=================================================================== RCS file: /cvs/mandoc/mandocdb.c,v retrieving revision 1.239 retrieving revision 1.255 diff -u -p -r1.239 -r1.255 --- mandoc/mandocdb.c 2017/01/27 01:04:25 1.239 +++ mandoc/mandocdb.c 2017/08/26 15:55:46 1.255 @@ -1,4 +1,4 @@ -/* $Id: mandocdb.c,v 1.239 2017/01/27 01:04:25 schwarze Exp $ */ +/* $Id: mandocdb.c,v 1.255 2017/08/26 15:55:46 schwarze Exp $ */ /* * Copyright (c) 2011, 2012 Kristaps Dzonsons * Copyright (c) 2011-2017 Ingo Schwarze @@ -19,8 +19,8 @@ #include "config.h" #include +#include #include -#include #include #include @@ -162,7 +162,7 @@ static void putmdockey(const struct mpage *, const struct roff_node *, uint64_t, int); static int render_string(char **, size_t *); static void say(const char *, const char *, ...) - __attribute__((__format__ (printf, 2, 3))); + __attribute__((__format__ (__printf__, 2, 3))); static int set_basedir(const char *, int); static int treescan(void); static size_t utf8(unsigned int, char [7]); @@ -183,8 +183,7 @@ static struct ohash names; /* table of all names */ static struct ohash strings; /* table of all strings */ static uint64_t name_mask; -static const struct mdoc_handler mdocs[MDOC_MAX] = { - { NULL, 0, 0 }, /* Ap */ +static const struct mdoc_handler __mdocs[MDOC_MAX - MDOC_Dd] = { { NULL, 0, NODE_NOPRT }, /* Dd */ { NULL, 0, NODE_NOPRT }, /* Dt */ { NULL, 0, NODE_NOPRT }, /* Os */ @@ -200,6 +199,7 @@ static const struct mdoc_handler mdocs[MDOC_MAX] = { { NULL, 0, 0 }, /* It */ { NULL, 0, 0 }, /* Ad */ { NULL, TYPE_An, 0 }, /* An */ + { NULL, 0, 0 }, /* Ap */ { NULL, TYPE_Ar, 0 }, /* Ar */ { NULL, TYPE_Cd, 0 }, /* Cd */ { NULL, TYPE_Cm, 0 }, /* Cm */ @@ -302,12 +302,10 @@ static const struct mdoc_handler mdocs[MDOC_MAX] = { { NULL, 0, 0 }, /* En */ { NULL, TYPE_Dx, NODE_NOSRC }, /* Dx */ { NULL, 0, 0 }, /* %Q */ - { NULL, 0, 0 }, /* br */ - { NULL, 0, 0 }, /* sp */ { NULL, 0, 0 }, /* %U */ { NULL, 0, 0 }, /* Ta */ - { NULL, 0, 0 }, /* ll */ }; +static const struct mdoc_handler *const mdocs = __mdocs - MDOC_Dd; int @@ -321,7 +319,7 @@ mandocdb(int argc, char *argv[]) int ch, i; #if HAVE_PLEDGE - if (pledge("stdio rpath wpath cpath fattr flock proc exec", NULL) == -1) { + if (pledge("stdio rpath wpath cpath", NULL) == -1) { warn("pledge"); return (int)MANDOCLEVEL_SYSERR; } @@ -422,7 +420,8 @@ mandocdb(int argc, char *argv[]) exitcode = (int)MANDOCLEVEL_OK; mchars_alloc(); - mp = mparse_alloc(mparse_options, MANDOCLEVEL_BADARG, NULL, NULL); + mp = mparse_alloc(mparse_options, MANDOCERR_MAX, NULL, + MANDOC_OS_OTHER, NULL); mandoc_ohash_init(&mpages, 6, offsetof(struct mpage, inodev)); mandoc_ohash_init(&mlinks, 6, offsetof(struct mlink, file)); @@ -441,15 +440,6 @@ mandocdb(int argc, char *argv[]) * The existing database is usable. Process * all files specified on the command-line. */ -#if HAVE_PLEDGE - if (!nodb) { - if (pledge("stdio rpath wpath cpath fattr flock", NULL) == -1) { - warn("pledge"); - exitcode = (int)MANDOCLEVEL_SYSERR; - goto out; - } - } -#endif use_all = 1; for (i = 0; i < argc; i++) filescan(argv[i]); @@ -589,7 +579,7 @@ treescan(void) const char *argv[2]; argv[0] = "."; - argv[1] = (char *)NULL; + argv[1] = NULL; f = fts_open((char * const *)argv, FTS_PHYSICAL | FTS_NOCHDIR, fts_compare); @@ -1153,6 +1143,7 @@ mpages_merge(struct dba *dba, struct mparse *mp) if (mlink->dform != FORM_CAT || mlink->fform != FORM_CAT) { mparse_readfd(mp, fd, mlink->file); close(fd); + fd = -1; mparse_result(mp, &man, &sodest); } @@ -1209,26 +1200,33 @@ mpages_merge(struct dba *dba, struct mparse *mp) mpage->title = mandoc_strdup(man->meta.title); } else if (man != NULL && man->macroset == MACROSET_MAN) { man_validate(man); - mpage->form = FORM_SRC; - mpage->sec = mandoc_strdup(man->meta.msec); - mpage->arch = mandoc_strdup(mlink->arch); - mpage->title = mandoc_strdup(man->meta.title); - } else { + if (*man->meta.msec != '\0' || + *man->meta.title != '\0') { + mpage->form = FORM_SRC; + mpage->sec = mandoc_strdup(man->meta.msec); + mpage->arch = mandoc_strdup(mlink->arch); + mpage->title = mandoc_strdup(man->meta.title); + } else + man = NULL; + } + + assert(mpage->desc == NULL); + if (man == NULL) { mpage->form = FORM_CAT; mpage->sec = mandoc_strdup(mlink->dsec); mpage->arch = mandoc_strdup(mlink->arch); mpage->title = mandoc_strdup(mlink->name); - } - - assert(mpage->desc == NULL); - if (man != NULL && man->macroset == MACROSET_MDOC) + parse_cat(mpage, fd); + } else if (man->macroset == MACROSET_MDOC) parse_mdoc(mpage, &man->meta, man->first); - else if (man != NULL) - parse_man(mpage, &man->meta, man->first); else - parse_cat(mpage, fd); - if (mpage->desc == NULL) - mpage->desc = mandoc_strdup(mpage->mlinks->name); + parse_man(mpage, &man->meta, man->first); + if (mpage->desc == NULL) { + mpage->desc = mandoc_strdup(mlink->name); + if (warnings) + say(mlink->file, "No one-line description, " + "using filename \"%s\"", mlink->name); + } for (mlink = mpage->mlinks; mlink != NULL; @@ -1250,31 +1248,50 @@ static void parse_cat(struct mpage *mpage, int fd) { FILE *stream; - char *line, *p, *title; + struct mlink *mlink; + char *line, *p, *title, *sec; size_t linesz, plen, titlesz; ssize_t len; int offs; - stream = (-1 == fd) ? - fopen(mpage->mlinks->file, "r") : - fdopen(fd, "r"); - if (NULL == stream) { - if (-1 != fd) + mlink = mpage->mlinks; + stream = fd == -1 ? fopen(mlink->file, "r") : fdopen(fd, "r"); + if (stream == NULL) { + if (fd != -1) close(fd); if (warnings) - say(mpage->mlinks->file, "&fopen"); + say(mlink->file, "&fopen"); return; } line = NULL; linesz = 0; - /* Skip to first blank line. */ + /* Parse the section number from the header line. */ - while (getline(&line, &linesz, stream) != -1) + while (getline(&line, &linesz, stream) != -1) { if (*line == '\n') + continue; + if ((sec = strchr(line, '(')) == NULL) break; + if ((p = strchr(++sec, ')')) == NULL) + break; + free(mpage->sec); + mpage->sec = mandoc_strndup(sec, p - sec); + if (warnings && *mlink->dsec != '\0' && + strcasecmp(mpage->sec, mlink->dsec)) + say(mlink->file, + "Section \"%s\" manual in %s directory", + mpage->sec, mlink->dsec); + break; + } + /* Skip to first blank line. */ + + while (line == NULL || *line != '\n') + if (getline(&line, &linesz, stream) == -1) + break; + /* * Assume the first line that is not indented * is the first section header. Skip to it. @@ -1318,8 +1335,7 @@ parse_cat(struct mpage *mpage, int fd) if (NULL == title || '\0' == *title) { if (warnings) - say(mpage->mlinks->file, - "Cannot find NAME section"); + say(mlink->file, "Cannot find NAME section"); fclose(stream); free(title); return; @@ -1338,8 +1354,8 @@ parse_cat(struct mpage *mpage, int fd) /* Skip to next word. */ ; } else { if (warnings) - say(mpage->mlinks->file, - "No dash in title line"); + say(mlink->file, "No dash in title line, " + "reusing \"%s\" as one-line description", title); p = title; } @@ -1519,25 +1535,26 @@ parse_mdoc(struct mpage *mpage, const struct roff_meta const struct roff_node *n) { - assert(NULL != n); - for (n = n->child; NULL != n; n = n->next) { - if (n->flags & mdocs[n->tok].taboo) + for (n = n->child; n != NULL; n = n->next) { + if (n->tok == TOKEN_NONE || + n->tok < ROFF_MAX || + n->flags & mdocs[n->tok].taboo) continue; + assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX); switch (n->type) { case ROFFT_ELEM: case ROFFT_BLOCK: case ROFFT_HEAD: case ROFFT_BODY: case ROFFT_TAIL: - if (NULL != mdocs[n->tok].fp) - if (0 == (*mdocs[n->tok].fp)(mpage, meta, n)) - break; + if (mdocs[n->tok].fp != NULL && + (*mdocs[n->tok].fp)(mpage, meta, n) == 0) + break; if (mdocs[n->tok].mask) putmdockey(mpage, n->child, mdocs[n->tok].mask, mdocs[n->tok].taboo); break; default: - assert(n->type != ROFFT_ROOT); continue; } if (NULL != n->child) @@ -2093,10 +2110,28 @@ dbprune(struct dba *dba) static void dbwrite(struct dba *dba) { - char tfn[32]; - int status; - pid_t child; + struct stat sb1, sb2; + char tfn[33], *cp1, *cp2; + off_t i; + int fd1, fd2; + /* + * Do not write empty databases, and delete existing ones + * when makewhatis -u causes them to become empty. + */ + + dba_array_start(dba->pages); + if (dba_array_next(dba->pages) == NULL) { + if (unlink(MANDOC_DB) == -1 && errno != ENOENT) + say(MANDOC_DB, "&unlink"); + return; + } + + /* + * Build the database in a temporary file, + * then atomically move it into place. + */ + if (dba_write(MANDOC_DB "~", dba) != -1) { if (rename(MANDOC_DB "~", MANDOC_DB) == -1) { exitcode = (int)MANDOCLEVEL_SYSERR; @@ -2106,65 +2141,73 @@ dbwrite(struct dba *dba) return; } + /* + * We lack write permission and cannot replace the database + * file, but let's at least check whether the data changed. + */ + (void)strlcpy(tfn, "/tmp/mandocdb.XXXXXXXX", sizeof(tfn)); if (mkdtemp(tfn) == NULL) { exitcode = (int)MANDOCLEVEL_SYSERR; say("", "&%s", tfn); return; } - + cp1 = cp2 = NULL; + fd1 = fd2 = -1; (void)strlcat(tfn, "/" MANDOC_DB, sizeof(tfn)); if (dba_write(tfn, dba) == -1) { - exitcode = (int)MANDOCLEVEL_SYSERR; say(tfn, "&dba_write"); - goto out; + goto err; } - - switch (child = fork()) { - case -1: - exitcode = (int)MANDOCLEVEL_SYSERR; - say("", "&fork cmp"); - return; - case 0: - execlp("cmp", "cmp", "-s", tfn, MANDOC_DB, (char *)NULL); - say("", "&exec cmp"); - exit(0); - default: - break; + if ((fd1 = open(MANDOC_DB, O_RDONLY, 0)) == -1) { + say(MANDOC_DB, "&open"); + goto err; } - if (waitpid(child, &status, 0) == -1) { - exitcode = (int)MANDOCLEVEL_SYSERR; - say("", "&wait cmp"); - } else if (WIFSIGNALED(status)) { - exitcode = (int)MANDOCLEVEL_SYSERR; - say("", "cmp died from signal %d", WTERMSIG(status)); - } else if (WEXITSTATUS(status)) { - exitcode = (int)MANDOCLEVEL_SYSERR; - say(MANDOC_DB, - "Data changed, but cannot replace database"); + if ((fd2 = open(tfn, O_RDONLY, 0)) == -1) { + say(tfn, "&open"); + goto err; } + if (fstat(fd1, &sb1) == -1) { + say(MANDOC_DB, "&fstat"); + goto err; + } + if (fstat(fd2, &sb2) == -1) { + say(tfn, "&fstat"); + goto err; + } + if (sb1.st_size != sb2.st_size) + goto err; + if ((cp1 = mmap(NULL, sb1.st_size, PROT_READ, MAP_PRIVATE, + fd1, 0)) == NULL) { + say(MANDOC_DB, "&mmap"); + goto err; + } + if ((cp2 = mmap(NULL, sb2.st_size, PROT_READ, MAP_PRIVATE, + fd2, 0)) == NULL) { + say(tfn, "&mmap"); + goto err; + } + for (i = 0; i < sb1.st_size; i++) + if (cp1[i] != cp2[i]) + goto err; + goto out; +err: + exitcode = (int)MANDOCLEVEL_SYSERR; + say(MANDOC_DB, "Data changed, but cannot replace database"); + out: + if (cp1 != NULL) + munmap(cp1, sb1.st_size); + if (cp2 != NULL) + munmap(cp2, sb2.st_size); + if (fd1 != -1) + close(fd1); + if (fd2 != -1) + close(fd2); + unlink(tfn); *strrchr(tfn, '/') = '\0'; - switch (child = fork()) { - case -1: - exitcode = (int)MANDOCLEVEL_SYSERR; - say("", "&fork rm"); - return; - case 0: - execlp("rm", "rm", "-rf", tfn, (char *)NULL); - say("", "&exec rm"); - exit((int)MANDOCLEVEL_SYSERR); - default: - break; - } - if (waitpid(child, &status, 0) == -1) { - exitcode = (int)MANDOCLEVEL_SYSERR; - say("", "&wait rm"); - } else if (WIFSIGNALED(status) || WEXITSTATUS(status)) { - exitcode = (int)MANDOCLEVEL_SYSERR; - say("", "%s: Cannot remove temporary directory", tfn); - } + rmdir(tfn); } static int