=================================================================== RCS file: /cvs/mandoc/mandocdb.c,v retrieving revision 1.56 retrieving revision 1.65 diff -u -p -r1.56 -r1.65 --- mandoc/mandocdb.c 2013/06/03 19:01:31 1.56 +++ mandoc/mandocdb.c 2013/06/06 19:00:28 1.65 @@ -1,4 +1,4 @@ -/* $Id: mandocdb.c,v 1.56 2013/06/03 19:01:31 schwarze Exp $ */ +/* $Id: mandocdb.c,v 1.65 2013/06/06 19:00:28 schwarze Exp $ */ /* * Copyright (c) 2011, 2012 Kristaps Dzonsons * Copyright (c) 2011, 2012, 2013 Ingo Schwarze @@ -19,7 +19,6 @@ #include "config.h" #endif -#include #include #include @@ -28,7 +27,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -98,7 +99,7 @@ struct of { struct of *next; /* next in ofs */ enum form dform; /* path-cued form */ enum form sform; /* suffix-cued form */ - char file[MAXPATHLEN]; /* filename rel. to manpath */ + char file[PATH_MAX]; /* filename rel. to manpath */ const char *desc; /* parsed description */ const char *sec; /* suffix-cued section (or empty) */ const char *dsec; /* path-cued section (or empty) */ @@ -122,28 +123,24 @@ struct mdoc_handler { #define MDOCF_CHILD 0x01 /* automatically index child nodes */ }; -static void dbclose(const char *, int); -static void dbindex(struct mchars *, int, - const struct of *, const char *); -static int dbopen(const char *, int); -static void dbprune(const char *); +static void dbclose(int); +static void dbindex(struct mchars *, int, const struct of *); +static int dbopen(int); +static void dbprune(void); static void fileadd(struct of *); static int filecheck(const char *); -static void filescan(const char *, const char *); -static struct str *hashget(const char *, size_t); +static void filescan(const char *); static void *hash_alloc(size_t, void *); static void hash_free(void *, size_t, void *); static void *hash_halloc(size_t, void *); static void inoadd(const struct stat *, struct of *); static int inocheck(const struct stat *); -static void ofadd(const char *, int, const char *, - const char *, const char *, const char *, - const char *, const struct stat *); +static void ofadd(int, const char *, const char *, const char *, + const char *, const char *, const struct stat *); static void offree(void); -static int ofmerge(struct mchars *, struct mparse *, const char *); -static void parse_catpage(struct of *, const char *); -static int parse_man(struct of *, - const struct man_node *); +static void ofmerge(struct mchars *, struct mparse *); +static void parse_catpage(struct of *); +static void parse_man(struct of *, const struct man_node *); static void parse_mdoc(struct of *, const struct mdoc_node *); static int parse_mdoc_body(struct of *, const struct mdoc_node *); static int parse_mdoc_head(struct of *, const struct mdoc_node *); @@ -155,28 +152,28 @@ static int parse_mdoc_Nm(struct of *, const struct md static int parse_mdoc_Sh(struct of *, const struct mdoc_node *); static int parse_mdoc_St(struct of *, const struct mdoc_node *); static int parse_mdoc_Xr(struct of *, const struct mdoc_node *); -static int path_reset(const char *, int, const char *); +static int set_basedir(const char *); static void putkey(const struct of *, const char *, uint64_t); static void putkeys(const struct of *, - const char *, int, uint64_t); + const char *, size_t, uint64_t); static void putmdockey(const struct of *, const struct mdoc_node *, uint64_t); -static void say(const char *, const char *, const char *, ...); +static void say(const char *, const char *, ...); static char *stradd(const char *); -static char *straddbuf(const char *, size_t); -static int treescan(const char *); +static char *stradds(const char *, size_t); +static int treescan(void); static size_t utf8(unsigned int, char [7]); static void utf8key(struct mchars *, struct str *); -static void wordaddbuf(const struct of *, - const char *, size_t, uint64_t); static char *progname; static int use_all; /* use all found files */ static int nodb; /* no database changes */ static int verb; /* print what we're doing */ static int warnings; /* warn about crap */ +static int exitcode; /* to be returned by main */ static enum op op; /* operational mode */ +static char basedir[PATH_MAX]; /* current base directory */ static struct ohash inos; /* table of inodes/devices */ static struct ohash filenames; /* table of filenames */ static struct ohash strings; /* table of all strings */ @@ -313,11 +310,10 @@ static const struct mdoc_handler mdocs[MDOC_MAX] = { int main(int argc, char *argv[]) { - char cwd[MAXPATHLEN]; - int ch, rc, fd, i; - unsigned int index; + int ch, i; + unsigned int slot; size_t j, sz; - const char *dir; + const char *path_arg; struct str *s; struct mchars *mc; struct manpaths dirs; @@ -345,20 +341,6 @@ main(int argc, char *argv[]) ++progname; /* - * Remember where we started by keeping a fd open to the origin - * path component: throughout this utility, we chdir() a lot to - * handle relative paths, and by doing this, we can return to - * the starting point. - */ - if (NULL == getcwd(cwd, MAXPATHLEN)) { - perror(NULL); - return(EXIT_FAILURE); - } else if (-1 == (fd = open(cwd, O_RDONLY, 0))) { - perror(cwd); - return(EXIT_FAILURE); - } - - /* * We accept a few different invocations. * The CHECKOP macro makes sure that invocation styles don't * clobber each other. @@ -369,7 +351,7 @@ main(int argc, char *argv[]) goto usage; \ } while (/*CONSTCOND*/0) - dir = NULL; + path_arg = NULL; op = OP_DEFAULT; while (-1 != (ch = getopt(argc, argv, "aC:d:ntu:vW"))) @@ -379,12 +361,12 @@ main(int argc, char *argv[]) break; case ('C'): CHECKOP(op, ch); - dir = optarg; + path_arg = optarg; op = OP_CONFFILE; break; case ('d'): CHECKOP(op, ch); - dir = optarg; + path_arg = optarg; op = OP_UPDATE; break; case ('n'): @@ -398,7 +380,7 @@ main(int argc, char *argv[]) break; case ('u'): CHECKOP(op, ch); - dir = optarg; + path_arg = optarg; op = OP_DELETE; break; case ('v'): @@ -419,7 +401,7 @@ main(int argc, char *argv[]) goto usage; } - rc = 1; + exitcode = (int)MANDOCLEVEL_OK; mp = mparse_alloc(MPARSE_AUTO, MANDOCLEVEL_FATAL, NULL, NULL, NULL); mc = mchars_alloc(); @@ -433,24 +415,23 @@ main(int argc, char *argv[]) * Force processing all files. */ use_all = 1; - if (NULL == dir) - dir = cwd; + /* * All of these deal with a specific directory. * Jump into that directory then collect files specified * on the command-line. */ - if (0 == path_reset(cwd, fd, dir)) + if (0 == set_basedir(path_arg)) goto out; for (i = 0; i < argc; i++) - filescan(argv[i], dir); - if (0 == dbopen(dir, 1)) + filescan(argv[i]); + if (0 == dbopen(1)) goto out; if (OP_TEST != op) - dbprune(dir); + dbprune(); if (OP_DELETE != op) - rc = ofmerge(mc, mp, dir); - dbclose(dir, 1); + ofmerge(mc, mp); + dbclose(1); } else { /* * If we have arguments, use them as our manpaths. @@ -464,7 +445,7 @@ main(int argc, char *argv[]) for (i = 0; i < argc; i++) dirs.paths[i] = mandoc_strdup(argv[i]); } else - manpath_parse(&dirs, dir, NULL, NULL); + manpath_parse(&dirs, path_arg, NULL, NULL); /* * First scan the tree rooted at a base directory. @@ -479,13 +460,13 @@ main(int argc, char *argv[]) dirs.paths[j][--sz] = '\0'; if (0 == sz) continue; - if (0 == path_reset(cwd, fd, dirs.paths[j])) + if (0 == set_basedir(dirs.paths[j])) goto out; - if (0 == treescan(dirs.paths[j])) + if (0 == treescan()) goto out; - if (0 == path_reset(cwd, fd, dirs.paths[j])) + if (0 == set_basedir(dirs.paths[j])) goto out; - if (0 == dbopen(dirs.paths[j], 0)) + if (0 == dbopen(0)) goto out; /* @@ -497,9 +478,8 @@ main(int argc, char *argv[]) SQL_EXEC("PRAGMA synchronous = OFF"); #endif - if (0 == ofmerge(mc, mp, dirs.paths[j])) - goto out; - dbclose(dirs.paths[j], 0); + ofmerge(mc, mp); + dbclose(0); offree(); ohash_delete(&inos); ohash_init(&inos, 6, &ino_info); @@ -508,12 +488,12 @@ main(int argc, char *argv[]) } } out: - close(fd); + set_basedir(NULL); manpath_free(&dirs); mchars_free(mc); mparse_free(mp); - for (s = ohash_first(&strings, &index); - NULL != s; s = ohash_next(&strings, &index)) { + for (s = ohash_first(&strings, &slot); NULL != s; + s = ohash_next(&strings, &slot)) { if (s->utf8 != s->key) free(s->utf8); free(s); @@ -522,7 +502,7 @@ out: ohash_delete(&inos); ohash_delete(&filenames); offree(); - return(rc ? EXIT_SUCCESS : EXIT_FAILURE); + return(exitcode); usage: fprintf(stderr, "usage: %s [-anvW] [-C file]\n" " %s [-anvW] dir ...\n" @@ -532,11 +512,11 @@ usage: progname, progname, progname, progname, progname); - return(EXIT_FAILURE); + return((int)MANDOCLEVEL_BADARG); } /* - * Scan a directory tree rooted at "base" for manpages. + * Scan a directory tree rooted at "basedir" for manpages. * We use fts(), scanning directory parts along the way for clues to our * section and architecture. * @@ -550,7 +530,7 @@ usage: * TODO: accomodate for multi-language directories. */ static int -treescan(const char *base) +treescan(void) { FTS *f; FTSENT *ff; @@ -568,7 +548,8 @@ treescan(const char *base) */ f = fts_open((char * const *)argv, FTS_LOGICAL, NULL); if (NULL == f) { - perror(base); + exitcode = (int)MANDOCLEVEL_SYSERR; + say("", NULL); return(0); } @@ -583,53 +564,60 @@ treescan(const char *base) * Disallow duplicate (hard-linked) files. */ if (FTS_F == ff->fts_info) { + if (0 == strcmp(path, MANDOC_DB)) + continue; if ( ! use_all && ff->fts_level < 2) { if (warnings) - say(base, path, "Extraneous file"); + say(path, "Extraneous file"); continue; } else if (inocheck(ff->fts_statp)) { if (warnings) - say(base, path, "Duplicate file"); + say(path, "Duplicate file"); continue; - } - - cp = ff->fts_name; - - if (0 == strcmp(cp, "mandocdb.db")) { - if (warnings) - say(base, path, "Skip database"); - continue; - } else if (NULL != (cp = strrchr(cp, '.'))) { - if (0 == strcmp(cp + 1, "html")) { + } else if (NULL == (sec = + strrchr(ff->fts_name, '.'))) { + if ( ! use_all) { if (warnings) - say(base, path, "Skip html"); + say(path, + "No filename suffix"); continue; - } else if (0 == strcmp(cp + 1, "gz")) { - if (warnings) - say(base, path, "Skip gz"); - continue; - } else if (0 == strcmp(cp + 1, "ps")) { - if (warnings) - say(base, path, "Skip ps"); - continue; - } else if (0 == strcmp(cp + 1, "pdf")) { - if (warnings) - say(base, path, "Skip pdf"); - continue; } + } else if (0 == strcmp(++sec, "html")) { + if (warnings) + say(path, "Skip html"); + continue; + } else if (0 == strcmp(sec, "gz")) { + if (warnings) + say(path, "Skip gz"); + continue; + } else if (0 == strcmp(sec, "ps")) { + if (warnings) + say(path, "Skip ps"); + continue; + } else if (0 == strcmp(sec, "pdf")) { + if (warnings) + say(path, "Skip pdf"); + continue; + } else if ( ! use_all && + ((FORM_SRC == dform && strcmp(sec, dsec)) || + (FORM_CAT == dform && strcmp(sec, "0")))) { + if (warnings) + say(path, "Wrong filename suffix"); + continue; + } else { + sec[-1] = '\0'; + sec = stradd(sec); } - - if (NULL != (sec = strrchr(ff->fts_name, '.'))) { - *sec = '\0'; - sec = stradd(sec + 1); - } name = stradd(ff->fts_name); - ofadd(base, dform, path, + ofadd(dform, path, name, dsec, sec, arch, ff->fts_statp); continue; } else if (FTS_D != ff->fts_info && - FTS_DP != ff->fts_info) + FTS_DP != ff->fts_info) { + if (warnings) + say(path, "Not a regular file"); continue; + } switch (ff->fts_level) { case (0): @@ -659,7 +647,7 @@ treescan(const char *base) break; if (warnings) - say(base, path, "Unknown directory part"); + say(path, "Unknown directory part"); fts_set(f, ff, FTS_SKIP); break; case (2): @@ -675,7 +663,7 @@ treescan(const char *base) if (FTS_DP == ff->fts_info || use_all) break; if (warnings) - say(base, path, "Extraneous directory part"); + say(path, "Extraneous directory part"); fts_set(f, ff, FTS_SKIP); break; } @@ -701,10 +689,11 @@ treescan(const char *base) * See treescan() for the fts(3) version of this. */ static void -filescan(const char *file, const char *base) +filescan(const char *file) { + char buf[PATH_MAX]; const char *sec, *arch, *name, *dsec; - char *p, *start, *buf; + char *p, *start; int dform; struct stat st; @@ -713,22 +702,28 @@ filescan(const char *file, const char *base) if (0 == strncmp(file, "./", 2)) file += 2; - if (-1 == stat(file, &st)) { - if (warnings) - say(base, file, "%s", strerror(errno)); + if (NULL == realpath(file, buf)) { + exitcode = (int)MANDOCLEVEL_BADARG; + say(file, NULL); return; + } else if (strstr(buf, basedir) != buf) { + exitcode = (int)MANDOCLEVEL_BADARG; + say("", "%s: outside base directory", buf); + return; + } else if (-1 == stat(buf, &st)) { + exitcode = (int)MANDOCLEVEL_BADARG; + say(file, NULL); + return; } else if ( ! (S_IFREG & st.st_mode)) { - if (warnings) - say(base, file, "Not a regular file"); + exitcode = (int)MANDOCLEVEL_BADARG; + say(file, "Not a regular file"); return; } else if (inocheck(&st)) { if (warnings) - say(base, file, "Duplicate file"); + say(file, "Duplicate file"); return; } - - buf = mandoc_strdup(file); - start = buf; + start = buf + strlen(basedir); sec = arch = name = dsec = NULL; dform = FORM_NONE; @@ -779,8 +774,7 @@ filescan(const char *file, const char *base) *p = '\0'; } - ofadd(base, dform, file, name, dsec, sec, arch, &st); - free(buf); + ofadd(dform, file, name, dsec, sec, arch, &st); } /* @@ -789,10 +783,9 @@ filescan(const char *file, const char *base) static int filecheck(const char *name) { - unsigned int index; - index = ohash_qlookup(&filenames, name); - return(NULL != ohash_find(&filenames, index)); + return(NULL != ohash_find(&filenames, + ohash_qlookup(&filenames, name))); } /* @@ -802,11 +795,11 @@ filecheck(const char *name) static void fileadd(struct of *of) { - unsigned int index; + unsigned int slot; - index = ohash_qlookup(&filenames, of->file); - assert(NULL == ohash_find(&filenames, index)); - ohash_insert(&filenames, index, of); + slot = ohash_qlookup(&filenames, of->file); + assert(NULL == ohash_find(&filenames, slot)); + ohash_insert(&filenames, slot, of); } /* @@ -817,15 +810,13 @@ inocheck(const struct stat *st) { struct id id; uint32_t hash; - unsigned int index; memset(&id, 0, sizeof(id)); id.ino = hash = st->st_ino; id.dev = st->st_dev; - index = ohash_lookup_memory - (&inos, (char *)&id, sizeof(id), hash); - return(NULL != ohash_find(&inos, index)); + return(NULL != ohash_find(&inos, ohash_lookup_memory( + &inos, (char *)&id, sizeof(id), hash))); } /* @@ -837,21 +828,20 @@ static void inoadd(const struct stat *st, struct of *of) { uint32_t hash; - unsigned int index; + unsigned int slot; of->id.ino = hash = st->st_ino; of->id.dev = st->st_dev; - index = ohash_lookup_memory + slot = ohash_lookup_memory (&inos, (char *)&of->id, sizeof(of->id), hash); - assert(NULL == ohash_find(&inos, index)); - ohash_insert(&inos, index, of); + assert(NULL == ohash_find(&inos, slot)); + ohash_insert(&inos, slot, of); } static void -ofadd(const char *base, int dform, const char *file, - const char *name, const char *dsec, const char *sec, - const char *arch, const struct stat *st) +ofadd(int dform, const char *file, const char *name, const char *dsec, + const char *sec, const char *arch, const struct stat *st) { struct of *of; int sform; @@ -876,7 +866,7 @@ ofadd(const char *base, int dform, const char *file, } of = mandoc_calloc(1, sizeof(struct of)); - strlcpy(of->file, file, MAXPATHLEN); + strlcpy(of->file, file, PATH_MAX); of->name = name; of->sec = sec; of->dsec = dsec; @@ -908,19 +898,19 @@ offree(void) /* * Run through the files in the global vector "ofs" and add them to the - * database specified in "base". + * database specified in "basedir". * * This handles the parsing scheme itself, using the cues of directory * and filename to determine whether the file is parsable or not. */ -static int -ofmerge(struct mchars *mc, struct mparse *mp, const char *base) +static void +ofmerge(struct mchars *mc, struct mparse *mp) { int form; size_t sz; struct mdoc *mdoc; struct man *man; - char buf[MAXPATHLEN]; + char buf[PATH_MAX]; char *bufp; const char *msec, *march, *mtitle, *cp; struct of *of; @@ -935,11 +925,10 @@ ofmerge(struct mchars *mc, struct mparse *mp, const ch * own. */ if ( ! use_all && FORM_CAT == of->dform) { - sz = strlcpy(buf, of->file, MAXPATHLEN); - if (sz >= MAXPATHLEN) { + sz = strlcpy(buf, of->file, PATH_MAX); + if (sz >= PATH_MAX) { if (warnings) - say(base, of->file, - "Filename too long"); + say(of->file, "Filename too long"); continue; } bufp = strstr(buf, "cat"); @@ -947,10 +936,10 @@ ofmerge(struct mchars *mc, struct mparse *mp, const ch memcpy(bufp, "man", 3); if (NULL != (bufp = strrchr(buf, '.'))) *++bufp = '\0'; - strlcat(buf, of->dsec, MAXPATHLEN); + strlcat(buf, of->dsec, PATH_MAX); if (filecheck(buf)) { if (warnings) - say(base, of->file, "Man " + say(of->file, "Man " "source exists: %s", buf); continue; } @@ -1006,7 +995,7 @@ ofmerge(struct mchars *mc, struct mparse *mp, const ch */ if (warnings && !use_all && form && strcasecmp(msec, of->dsec)) - say(base, of->file, "Section \"%s\" " + say(of->file, "Section \"%s\" " "manual in %s directory", msec, of->dsec); @@ -1025,7 +1014,7 @@ ofmerge(struct mchars *mc, struct mparse *mp, const ch * but don't skip manuals for this reason. */ if (warnings && !use_all && strcasecmp(march, of->arch)) - say(base, of->file, "Architecture \"%s\" " + say(of->file, "Architecture \"%s\" " "manual in \"%s\" directory", march, of->arch); @@ -1038,16 +1027,14 @@ ofmerge(struct mchars *mc, struct mparse *mp, const ch } else if (NULL != man) parse_man(of, man_node(man)); else - parse_catpage(of, base); + parse_catpage(of); - dbindex(mc, form, of, base); + dbindex(mc, form, of); } - - return(1); } static void -parse_catpage(struct of *of, const char *base) +parse_catpage(struct of *of) { FILE *stream; char *line, *p, *title; @@ -1055,7 +1042,7 @@ parse_catpage(struct of *of, const char *base) if (NULL == (stream = fopen(of->file, "r"))) { if (warnings) - say(base, of->file, "%s", strerror(errno)); + say(of->file, NULL); return; } @@ -1108,7 +1095,8 @@ parse_catpage(struct of *of, const char *base) if (NULL == title || '\0' == *title) { if (warnings) - say(base, of->file, "Cannot find NAME section"); + say(of->file, "Cannot find NAME section"); + putkey(of, of->name, TYPE_Nd); fclose(stream); free(title); return; @@ -1128,7 +1116,7 @@ parse_catpage(struct of *of, const char *base) /* Skip to next word. */ ; } else { if (warnings) - say(base, of->file, "No dash in title line"); + say(of->file, "No dash in title line"); p = title; } @@ -1160,20 +1148,10 @@ putkey(const struct of *of, const char *value, uint64_ { assert(NULL != value); - wordaddbuf(of, value, strlen(value), type); + putkeys(of, value, strlen(value), type); } /* - * Like putkey() but for unterminated strings. - */ -static void -putkeys(const struct of *of, const char *value, int sz, uint64_t type) -{ - - wordaddbuf(of, value, sz, type); -} - -/* * Grok all nodes at or below a certain mdoc node into putkey(). */ static void @@ -1188,7 +1166,7 @@ putmdockey(const struct of *of, const struct mdoc_node } } -static int +static void parse_man(struct of *of, const struct man_node *n) { const struct man_node *head, *body; @@ -1197,7 +1175,7 @@ parse_man(struct of *of, const struct man_node *n) size_t sz, titlesz; if (NULL == n) - return(0); + return; /* * We're only searching for one thing: the first text child in @@ -1239,7 +1217,7 @@ parse_man(struct of *of, const struct man_node *n) title[titlesz - 1] = ' '; } if (NULL == title) - return(1); + return; title = mandoc_realloc(title, titlesz + 1); title[titlesz] = '\0'; @@ -1252,7 +1230,7 @@ parse_man(struct of *of, const struct man_node *n) if (0 == (sz = strlen(sv))) { free(title); - return(1); + return; } /* Erase trailing space. */ @@ -1263,7 +1241,7 @@ parse_man(struct of *of, const struct man_node *n) if (start == sv) { free(title); - return(1); + return; } start = sv; @@ -1300,7 +1278,7 @@ parse_man(struct of *of, const struct man_node *n) if (sv == start) { putkey(of, start, TYPE_Nm); free(title); - return(1); + return; } while (isspace((unsigned char)*start)) @@ -1324,15 +1302,12 @@ parse_man(struct of *of, const struct man_node *n) of->desc = stradd(start); putkey(of, start, TYPE_Nd); free(title); - return(1); + return; } } for (n = n->child; n; n = n->next) - if (parse_man(of, n)) - return(1); - - return(0); + parse_man(of, n); } static void @@ -1546,13 +1521,13 @@ parse_mdoc_body(struct of *of, const struct mdoc_node } /* - * See straddbuf(). + * See stradds(). */ static char * stradd(const char *cp) { - return(straddbuf(cp, strlen(cp))); + return(stradds(cp, strlen(cp))); } /* @@ -1562,39 +1537,26 @@ stradd(const char *cp) * In using it, we avoid having thousands of (e.g.) "cat1" string * allocations for the "of" table. * We also have a layer atop the string table for keeping track of words - * in a parse sequence (see wordaddbuf()). + * in a parse sequence (see putkeys()). */ static char * -straddbuf(const char *cp, size_t sz) +stradds(const char *cp, size_t sz) { struct str *s; - unsigned int index; + unsigned int slot; const char *end; - if (NULL != (s = hashget(cp, sz))) + end = cp + sz; + slot = ohash_qlookupi(&strings, cp, &end); + if (NULL != (s = ohash_find(&strings, slot))) return(s->key); s = mandoc_calloc(sizeof(struct str) + sz + 1, 1); memcpy(s->key, cp, sz); - - end = cp + sz; - index = ohash_qlookupi(&strings, cp, &end); - assert(NULL == ohash_find(&strings, index)); - ohash_insert(&strings, index, s); + ohash_insert(&strings, slot, s); return(s->key); } -static struct str * -hashget(const char *cp, size_t sz) -{ - unsigned int index; - const char *end; - - end = cp + sz; - index = ohash_qlookupi(&strings, cp, &end); - return(ohash_find(&strings, index)); -} - /* * Add a word to the current parse sequence. * Within the hashtable of strings, we maintain a list of strings that @@ -1605,17 +1567,18 @@ hashget(const char *cp, size_t sz) * of its entries without conflict. */ static void -wordaddbuf(const struct of *of, - const char *cp, size_t sz, uint64_t v) +putkeys(const struct of *of, const char *cp, size_t sz, uint64_t v) { struct str *s; - unsigned int index; + unsigned int slot; const char *end; if (0 == sz) return; - s = hashget(cp, sz); + end = cp + sz; + slot = ohash_qlookupi(&strings, cp, &end); + s = ohash_find(&strings, slot); if (NULL != s && of == s->of) { s->mask |= v; @@ -1623,10 +1586,7 @@ wordaddbuf(const struct of *of, } else if (NULL == s) { s = mandoc_calloc(sizeof(struct str) + sz + 1, 1); memcpy(s->key, cp, sz); - end = cp + sz; - index = ohash_qlookupi(&strings, cp, &end); - assert(NULL == ohash_find(&strings, index)); - ohash_insert(&strings, index, s); + ohash_insert(&strings, slot, s); } s->next = words; @@ -1798,8 +1758,7 @@ utf8key(struct mchars *mc, struct str *key) * Also, UTF-8-encode the description at the last possible moment. */ static void -dbindex(struct mchars *mc, int form, - const struct of *of, const char *base) +dbindex(struct mchars *mc, int form, const struct of *of) { struct str *key; const char *desc; @@ -1807,14 +1766,15 @@ dbindex(struct mchars *mc, int form, size_t i; if (verb) - say(base, of->file, "Adding to index"); + say(of->file, "Adding to index"); if (nodb) return; desc = ""; if (NULL != of->desc) { - key = hashget(of->desc, strlen(of->desc)); + key = ohash_find(&strings, + ohash_qlookup(&strings, of->desc)); assert(NULL != key); if (NULL == key->utf8) utf8key(mc, key); @@ -1849,7 +1809,7 @@ dbindex(struct mchars *mc, int form, } static void -dbprune(const char *base) +dbprune(void) { struct of *of; size_t i; @@ -1863,7 +1823,7 @@ dbprune(const char *base) SQL_STEP(stmts[STMT_DELETE]); sqlite3_reset(stmts[STMT_DELETE]); if (verb) - say(base, of->file, "Deleted from index"); + say(of->file, "Deleted from index"); } } @@ -1872,10 +1832,9 @@ dbprune(const char *base) * If "real" is not set, rename the temporary file into the real one. */ static void -dbclose(const char *base, int real) +dbclose(int real) { size_t i; - char file[MAXPATHLEN]; if (nodb) return; @@ -1891,10 +1850,10 @@ dbclose(const char *base, int real) if (real) return; - strlcpy(file, MANDOC_DB, MAXPATHLEN); - strlcat(file, "~", MAXPATHLEN); - if (-1 == rename(file, MANDOC_DB)) - perror(MANDOC_DB); + if (-1 == rename(MANDOC_DB "~", MANDOC_DB)) { + exitcode = (int)MANDOCLEVEL_SYSERR; + say(MANDOC_DB, NULL); + } } /* @@ -1906,36 +1865,32 @@ dbclose(const char *base, int real) * Must be matched by dbclose(). */ static int -dbopen(const char *base, int real) +dbopen(int real) { - char file[MAXPATHLEN]; - const char *sql; + const char *file, *sql; int rc, ofl; - size_t sz; if (nodb) return(1); - sz = strlcpy(file, MANDOC_DB, MAXPATHLEN); - if ( ! real) - sz = strlcat(file, "~", MAXPATHLEN); + ofl = SQLITE_OPEN_READWRITE; + if (0 == real) { + file = MANDOC_DB "~"; + if (-1 == remove(file) && ENOENT != errno) { + exitcode = (int)MANDOCLEVEL_SYSERR; + say(file, NULL); + return(0); + } + ofl |= SQLITE_OPEN_EXCLUSIVE; + } else + file = MANDOC_DB; - if (sz >= MAXPATHLEN) { - fprintf(stderr, "%s: Path too long\n", file); - return(0); - } - - if ( ! real) - remove(file); - - ofl = SQLITE_OPEN_READWRITE | - (0 == real ? SQLITE_OPEN_EXCLUSIVE : 0); - rc = sqlite3_open_v2(file, &db, ofl, NULL); if (SQLITE_OK == rc) - return(1); + goto prepare_statements; if (SQLITE_CANTOPEN != rc) { - perror(file); + exitcode = (int)MANDOCLEVEL_SYSERR; + say(file, NULL); return(0); } @@ -1943,7 +1898,8 @@ dbopen(const char *base, int real) db = NULL; if (SQLITE_OK != (rc = sqlite3_open(file, &db))) { - perror(file); + exitcode = (int)MANDOCLEVEL_SYSERR; + say(file, NULL); return(0); } @@ -1967,10 +1923,13 @@ dbopen(const char *base, int real) "CREATE INDEX \"key_index\" ON keys (key);\n"; if (SQLITE_OK != sqlite3_exec(db, sql, NULL, NULL, NULL)) { - perror(sqlite3_errmsg(db)); + exitcode = (int)MANDOCLEVEL_SYSERR; + say(file, "%s", sqlite3_errmsg(db)); return(0); } +prepare_statements: + SQL_EXEC("PRAGMA foreign_keys = ON"); sql = "DELETE FROM docs where file=?"; sqlite3_prepare_v2(db, sql, -1, &stmts[STMT_DELETE], NULL); sql = "INSERT INTO docs " @@ -2004,28 +1963,76 @@ hash_free(void *p, size_t sz, void *arg) } static int -path_reset(const char *cwd, int fd, const char *base) +set_basedir(const char *targetdir) { + static char startdir[PATH_MAX]; + static int fd; - if (-1 == fchdir(fd)) { - perror(cwd); + /* + * Remember where we started by keeping a fd open to the origin + * path component: throughout this utility, we chdir() a lot to + * handle relative paths, and by doing this, we can return to + * the starting point. + */ + if ('\0' == *startdir) { + if (NULL == getcwd(startdir, PATH_MAX)) { + exitcode = (int)MANDOCLEVEL_SYSERR; + if (NULL != targetdir) + say(".", NULL); + return(0); + } + if (-1 == (fd = open(startdir, O_RDONLY, 0))) { + exitcode = (int)MANDOCLEVEL_SYSERR; + say(startdir, NULL); + return(0); + } + if (NULL == targetdir) + targetdir = startdir; + } else { + if (-1 == fd) + return(0); + if (-1 == fchdir(fd)) { + close(fd); + basedir[0] = '\0'; + exitcode = (int)MANDOCLEVEL_SYSERR; + say(startdir, NULL); + return(0); + } + if (NULL == targetdir) { + close(fd); + return(1); + } + } + if (NULL == realpath(targetdir, basedir)) { + basedir[0] = '\0'; + exitcode = (int)MANDOCLEVEL_BADARG; + say(targetdir, NULL); return(0); - } else if (-1 == chdir(base)) { - perror(base); + } else if (-1 == chdir(basedir)) { + exitcode = (int)MANDOCLEVEL_BADARG; + say("", NULL); return(0); } return(1); } static void -say(const char *dir, const char *file, const char *format, ...) +say(const char *file, const char *format, ...) { va_list ap; - fprintf(stderr, "%s", dir); + if ('\0' != *basedir) + fprintf(stderr, "%s", basedir); + if ('\0' != *basedir && '\0' != *file) + fputs("//", stderr); if ('\0' != *file) - fprintf(stderr, "//%s", file); + fprintf(stderr, "%s", file); fputs(": ", stderr); + + if (NULL == format) { + perror(NULL); + return; + } va_start(ap, format); vfprintf(stderr, format, ap);