=================================================================== RCS file: /cvs/mandoc/mansearch.c,v retrieving revision 1.51 retrieving revision 1.57 diff -u -p -r1.51 -r1.57 --- mandoc/mansearch.c 2014/11/27 01:58:21 1.51 +++ mandoc/mansearch.c 2015/04/01 12:48:33 1.57 @@ -1,15 +1,15 @@ -/* $Id: mansearch.c,v 1.51 2014/11/27 01:58:21 schwarze Exp $ */ +/* $Id: mansearch.c,v 1.57 2015/04/01 12:48:33 schwarze Exp $ */ /* * Copyright (c) 2012 Kristaps Dzonsons - * Copyright (c) 2013, 2014 Ingo Schwarze + * Copyright (c) 2013, 2014, 2015 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF @@ -21,8 +21,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -44,7 +46,7 @@ #include "mandoc.h" #include "mandoc_aux.h" -#include "manpath.h" +#include "manconf.h" #include "mansearch.h" extern int mansearch_keymax; @@ -84,7 +86,8 @@ struct match { int form; /* bit field: formatted, zipped? */ }; -static void buildnames(struct manpage *, sqlite3 *, +static void buildnames(const struct mansearch *, + struct manpage *, sqlite3 *, sqlite3_stmt *, uint64_t, const char *, int form); static char *buildoutput(sqlite3 *, sqlite3_stmt *, @@ -95,8 +98,6 @@ static void *hash_calloc(size_t, size_t, void *); static struct expr *exprcomp(const struct mansearch *, int, char *[]); static void exprfree(struct expr *); -static struct expr *exprspec(struct expr *, uint64_t, - const char *, const char *); static struct expr *exprterm(const struct mansearch *, char *, int); static int manpage_compare(const void *, const void *); static void sql_append(char **sql, size_t *sz, @@ -162,7 +163,6 @@ mansearch(const struct mansearch *search, int argc, char *argv[], struct manpage **res, size_t *sz) { - int fd, rc, c, indexbit; int64_t pageid; uint64_t outbit, iterbit; char buf[PATH_MAX]; @@ -176,26 +176,23 @@ mansearch(const struct mansearch *search, struct ohash htab; unsigned int idx; size_t i, j, cur, maxres; + int c, chdir_status, getcwd_status, indexbit; + if (argc == 0 || (e = exprcomp(search, argc, argv)) == NULL) { + *sz = 0; + return(0); + } + info.calloc = hash_calloc; info.alloc = hash_alloc; info.free = hash_free; info.key_offset = offsetof(struct match, pageid); - *sz = cur = maxres = 0; - sql = NULL; + cur = maxres = 0; *res = NULL; - fd = -1; - e = NULL; - rc = 0; - if (0 == argc) - goto out; - if (NULL == (e = exprcomp(search, argc, argv))) - goto out; - - outbit = 0; if (NULL != search->outkey) { + outbit = TYPE_Nd; for (indexbit = 0, iterbit = 1; indexbit < mansearch_keymax; indexbit++, iterbit <<= 1) { @@ -205,22 +202,22 @@ mansearch(const struct mansearch *search, break; } } - } + } else + outbit = 0; /* - * Save a descriptor to the current working directory. - * Since pathnames in the "paths" variable might be relative, - * and we'll be chdir()ing into them, we need to keep a handle - * on our current directory from which to start the chdir(). + * Remember the original working directory, if possible. + * This will be needed if the second or a later directory + * is given as a relative path. + * Do not error out if the current directory is not + * searchable: Maybe it won't be needed after all. */ - if (NULL == getcwd(buf, PATH_MAX)) { - perror("getcwd"); - goto out; - } else if (-1 == (fd = open(buf, O_RDONLY, 0))) { - perror(buf); - goto out; - } + if (getcwd(buf, PATH_MAX) == NULL) { + getcwd_status = 0; + (void)strlcpy(buf, strerror(errno), sizeof(buf)); + } else + getcwd_status = 1; sql = sql_statement(e); @@ -232,21 +229,30 @@ mansearch(const struct mansearch *search, * scan it for our match expression. */ + chdir_status = 0; for (i = 0; i < paths->sz; i++) { - if (-1 == fchdir(fd)) { - perror(buf); - free(*res); - break; - } else if (-1 == chdir(paths->paths[i])) { + if (chdir_status && paths->paths[i][0] != '/') { + if ( ! getcwd_status) { + fprintf(stderr, "%s: getcwd: %s\n", + paths->paths[i], buf); + continue; + } else if (chdir(buf) == -1) { + perror(buf); + continue; + } + } + if (chdir(paths->paths[i]) == -1) { perror(paths->paths[i]); continue; } + chdir_status = 1; c = sqlite3_open_v2(MANDOC_DB, &db, SQLITE_OPEN_READONLY, NULL); if (SQLITE_OK != c) { - perror(MANDOC_DB); + fprintf(stderr, "%s/%s: %s\n", + paths->paths[i], MANDOC_DB, strerror(errno)); sqlite3_close(db); continue; } @@ -341,14 +347,16 @@ mansearch(const struct mansearch *search, mpage->bits = mp->bits; mpage->sec = 10; mpage->form = mp->form; - buildnames(mpage, db, s, mp->pageid, + buildnames(search, mpage, db, s, mp->pageid, paths->paths[i], mp->form); - mpage->output = TYPE_Nd & outbit ? - mp->desc : outbit ? - buildoutput(db, s2, mp->pageid, outbit) : NULL; - + if (mpage->names != NULL) { + mpage->output = TYPE_Nd & outbit ? + mp->desc : outbit ? + buildoutput(db, s2, mp->pageid, outbit) : + NULL; + cur++; + } free(mp); - cur++; } sqlite3_finalize(s); @@ -365,17 +373,12 @@ mansearch(const struct mansearch *search, break; } qsort(*res, cur, sizeof(struct manpage), manpage_compare); - rc = 1; -out: - if (-1 != fd) { - if (-1 == fchdir(fd)) - perror(buf); - close(fd); - } + if (chdir_status && getcwd_status && chdir(buf) == -1) + perror(buf); exprfree(e); free(sql); *sz = cur; - return(rc); + return(1); } void @@ -405,17 +408,19 @@ manpage_compare(const void *vp1, const void *vp2) } static void -buildnames(struct manpage *mpage, sqlite3 *db, sqlite3_stmt *s, +buildnames(const struct mansearch *search, struct manpage *mpage, + sqlite3 *db, sqlite3_stmt *s, uint64_t pageid, const char *path, int form) { - char *newnames, *prevsec, *prevarch; + glob_t globinfo; + char *firstname, *newnames, *prevsec, *prevarch; const char *oldnames, *sep1, *name, *sec, *sep2, *arch, *fsec; size_t i; - int c; + int c, globres; mpage->file = NULL; mpage->names = NULL; - prevsec = prevarch = NULL; + firstname = prevsec = prevarch = NULL; i = 1; SQL_BIND_INT64(db, s, i, pageid); while (SQLITE_ROW == (c = sqlite3_step(s))) { @@ -430,10 +435,15 @@ buildnames(struct manpage *mpage, sqlite3 *db, sqlite3 sep1 = ", "; } - /* Fetch the next name. */ + /* Fetch the next name, rejecting sec/arch mismatches. */ sec = (const char *)sqlite3_column_text(s, 0); + if (search->sec != NULL && strcasecmp(sec, search->sec)) + continue; arch = (const char *)sqlite3_column_text(s, 1); + if (search->arch != NULL && *arch != '\0' && + strcasecmp(arch, search->arch)) + continue; name = (const char *)sqlite3_column_text(s, 2); /* Remember the first section found. */ @@ -485,11 +495,34 @@ buildnames(struct manpage *mpage, sqlite3 *db, sqlite3 sep2 = *arch == '\0' ? "" : "/"; mandoc_asprintf(&mpage->file, "%s/%s%s%s%s/%s.%s", path, sep1, sec, sep2, arch, name, fsec); + if (access(mpage->file, R_OK) != -1) + continue; + + /* Handle unusual file name extensions. */ + + if (firstname == NULL) + firstname = mpage->file; + else + free(mpage->file); + mandoc_asprintf(&mpage->file, "%s/%s%s%s%s/%s.*", + path, sep1, sec, sep2, arch, name); + globres = glob(mpage->file, 0, NULL, &globinfo); + free(mpage->file); + mpage->file = globres ? NULL : + mandoc_strdup(*globinfo.gl_pathv); + globfree(&globinfo); } if (c != SQLITE_DONE) fprintf(stderr, "%s\n", sqlite3_errmsg(db)); sqlite3_reset(s); + /* If none of the files is usable, use the first name. */ + + if (mpage->file == NULL) + mpage->file = firstname; + else if (mpage->file != firstname) + free(firstname); + /* Append one final section to the names. */ if (prevsec != NULL) { @@ -643,8 +676,7 @@ exprcomp(const struct mansearch *search, int argc, cha struct expr *first, *prev, *cur, *next; first = cur = NULL; - logic = igncase = toclose = 0; - toopen = NULL != search->sec || NULL != search->arch; + logic = igncase = toopen = toclose = 0; for (i = 0; i < argc; i++) { if (0 == strcmp("(", argv[i])) { @@ -710,45 +742,13 @@ exprcomp(const struct mansearch *search, int argc, cha toopen = logic = igncase = 0; } - if (toopen || logic || igncase || toclose) - goto fail; + if ( ! (toopen || logic || igncase || toclose)) + return(first); - if (NULL != search->sec || NULL != search->arch) - cur->close++; - if (NULL != search->arch) - cur = exprspec(cur, TYPE_arch, search->arch, "^(%s|any)$"); - if (NULL != search->sec) - exprspec(cur, TYPE_sec, search->sec, "^%s$"); - - return(first); - fail: if (NULL != first) exprfree(first); return(NULL); -} - -static struct expr * -exprspec(struct expr *cur, uint64_t key, const char *value, - const char *format) -{ - char errbuf[BUFSIZ]; - char *cp; - int irc; - - mandoc_asprintf(&cp, format, value); - cur->next = mandoc_calloc(1, sizeof(struct expr)); - cur = cur->next; - cur->and = 1; - cur->bits = key; - if (0 != (irc = regcomp(&cur->regexp, cp, - REG_EXTENDED | REG_NOSUB | REG_ICASE))) { - regerror(irc, &cur->regexp, errbuf, sizeof(errbuf)); - fprintf(stderr, "regcomp: %s\n", errbuf); - cur->substr = value; - } - free(cp); - return(cur); } static struct expr *