Print this page
9718 update mandoc to 1.14.4
   1 /*      $Id: mandocdb.c,v 1.253 2017/07/28 14:48:25 schwarze Exp $ */
   2 /*
   3  * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
   4  * Copyright (c) 2011-2017 Ingo Schwarze <schwarze@openbsd.org>
   5  * Copyright (c) 2016 Ed Maste <emaste@freebsd.org>
   6  *
   7  * Permission to use, copy, modify, and distribute this software for any
   8  * purpose with or without fee is hereby granted, provided that the above
   9  * copyright notice and this permission notice appear in all copies.
  10  *
  11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
  12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
  14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  18  */
  19 #include "config.h"
  20 
  21 #include <sys/types.h>

  22 #include <sys/stat.h>
  23 #include <sys/wait.h>
  24 
  25 #include <assert.h>
  26 #include <ctype.h>
  27 #if HAVE_ERR
  28 #include <err.h>
  29 #endif
  30 #include <errno.h>
  31 #include <fcntl.h>
  32 #if HAVE_FTS
  33 #include <fts.h>
  34 #else
  35 #include "compat_fts.h"
  36 #endif
  37 #include <limits.h>
  38 #if HAVE_SANDBOX_INIT
  39 #include <sandbox.h>
  40 #endif
  41 #include <stdarg.h>
  42 #include <stddef.h>
  43 #include <stdio.h>


 122 static  void     dbwrite(struct dba *);
 123 static  void     filescan(const char *);
 124 #if HAVE_FTS_COMPARE_CONST
 125 static  int      fts_compare(const FTSENT *const *, const FTSENT *const *);
 126 #else
 127 static  int      fts_compare(const FTSENT **, const FTSENT **);
 128 #endif
 129 static  void     mlink_add(struct mlink *, const struct stat *);
 130 static  void     mlink_check(struct mpage *, struct mlink *);
 131 static  void     mlink_free(struct mlink *);
 132 static  void     mlinks_undupe(struct mpage *);
 133 static  void     mpages_free(void);
 134 static  void     mpages_merge(struct dba *, struct mparse *);
 135 static  void     parse_cat(struct mpage *, int);
 136 static  void     parse_man(struct mpage *, const struct roff_meta *,
 137                         const struct roff_node *);
 138 static  void     parse_mdoc(struct mpage *, const struct roff_meta *,
 139                         const struct roff_node *);
 140 static  int      parse_mdoc_head(struct mpage *, const struct roff_meta *,
 141                         const struct roff_node *);


 142 static  int      parse_mdoc_Fd(struct mpage *, const struct roff_meta *,
 143                         const struct roff_node *);
 144 static  void     parse_mdoc_fname(struct mpage *, const struct roff_node *);
 145 static  int      parse_mdoc_Fn(struct mpage *, const struct roff_meta *,
 146                         const struct roff_node *);
 147 static  int      parse_mdoc_Fo(struct mpage *, const struct roff_meta *,
 148                         const struct roff_node *);
 149 static  int      parse_mdoc_Nd(struct mpage *, const struct roff_meta *,
 150                         const struct roff_node *);
 151 static  int      parse_mdoc_Nm(struct mpage *, const struct roff_meta *,
 152                         const struct roff_node *);
 153 static  int      parse_mdoc_Sh(struct mpage *, const struct roff_meta *,
 154                         const struct roff_node *);
 155 static  int      parse_mdoc_Va(struct mpage *, const struct roff_meta *,
 156                         const struct roff_node *);
 157 static  int      parse_mdoc_Xr(struct mpage *, const struct roff_meta *,
 158                         const struct roff_node *);
 159 static  void     putkey(const struct mpage *, char *, uint64_t);
 160 static  void     putkeys(const struct mpage *, char *, size_t, uint64_t);
 161 static  void     putmdockey(const struct mpage *,


 190         { parse_mdoc_Sh, TYPE_Sh, 0 }, /* Sh */
 191         { parse_mdoc_head, TYPE_Ss, 0 }, /* Ss */
 192         { NULL, 0, 0 },  /* Pp */
 193         { NULL, 0, 0 },  /* D1 */
 194         { NULL, 0, 0 },  /* Dl */
 195         { NULL, 0, 0 },  /* Bd */
 196         { NULL, 0, 0 },  /* Ed */
 197         { NULL, 0, 0 },  /* Bl */
 198         { NULL, 0, 0 },  /* El */
 199         { NULL, 0, 0 },  /* It */
 200         { NULL, 0, 0 },  /* Ad */
 201         { NULL, TYPE_An, 0 },  /* An */
 202         { NULL, 0, 0 },  /* Ap */
 203         { NULL, TYPE_Ar, 0 },  /* Ar */
 204         { NULL, TYPE_Cd, 0 },  /* Cd */
 205         { NULL, TYPE_Cm, 0 },  /* Cm */
 206         { NULL, TYPE_Dv, 0 },  /* Dv */
 207         { NULL, TYPE_Er, 0 },  /* Er */
 208         { NULL, TYPE_Ev, 0 },  /* Ev */
 209         { NULL, 0, 0 },  /* Ex */
 210         { NULL, TYPE_Fa, 0 },  /* Fa */
 211         { parse_mdoc_Fd, 0, 0 },  /* Fd */
 212         { NULL, TYPE_Fl, 0 },  /* Fl */
 213         { parse_mdoc_Fn, 0, 0 },  /* Fn */
 214         { NULL, TYPE_Ft, 0 },  /* Ft */
 215         { NULL, TYPE_Ic, 0 },  /* Ic */
 216         { NULL, TYPE_In, 0 },  /* In */
 217         { NULL, TYPE_Li, 0 },  /* Li */
 218         { parse_mdoc_Nd, 0, 0 },  /* Nd */
 219         { parse_mdoc_Nm, 0, 0 },  /* Nm */
 220         { NULL, 0, 0 },  /* Op */
 221         { NULL, 0, 0 },  /* Ot */
 222         { NULL, TYPE_Pa, NODE_NOSRC },  /* Pa */
 223         { NULL, 0, 0 },  /* Rv */
 224         { NULL, TYPE_St, 0 },  /* St */
 225         { parse_mdoc_Va, TYPE_Va, 0 },  /* Va */
 226         { parse_mdoc_Va, TYPE_Vt, 0 },  /* Vt */
 227         { parse_mdoc_Xr, 0, 0 },  /* Xr */
 228         { NULL, 0, 0 },  /* %A */
 229         { NULL, 0, 0 },  /* %B */
 230         { NULL, 0, 0 },  /* %D */
 231         { NULL, 0, 0 },  /* %I */
 232         { NULL, 0, 0 },  /* %J */
 233         { NULL, 0, 0 },  /* %N */
 234         { NULL, 0, 0 },  /* %O */


 302         { NULL, 0, 0 },  /* En */
 303         { NULL, TYPE_Dx, NODE_NOSRC },  /* Dx */
 304         { NULL, 0, 0 },  /* %Q */
 305         { NULL, 0, 0 },  /* %U */
 306         { NULL, 0, 0 },  /* Ta */
 307 };
 308 static  const struct mdoc_handler *const mdocs = __mdocs - MDOC_Dd;
 309 
 310 
 311 int
 312 mandocdb(int argc, char *argv[])
 313 {
 314         struct manconf    conf;
 315         struct mparse    *mp;
 316         struct dba       *dba;
 317         const char       *path_arg, *progname;
 318         size_t            j, sz;
 319         int               ch, i;
 320 
 321 #if HAVE_PLEDGE
 322         if (pledge("stdio rpath wpath cpath fattr flock proc exec", NULL) == -1) {
 323                 warn("pledge");
 324                 return (int)MANDOCLEVEL_SYSERR;
 325         }
 326 #endif
 327 
 328 #if HAVE_SANDBOX_INIT
 329         if (sandbox_init(kSBXProfileNoInternet, SANDBOX_NAMED, NULL) == -1) {
 330                 warnx("sandbox_init");
 331                 return (int)MANDOCLEVEL_SYSERR;
 332         }
 333 #endif
 334 
 335         memset(&conf, 0, sizeof(conf));
 336 
 337         /*
 338          * We accept a few different invocations.
 339          * The CHECKOP macro makes sure that invocation styles don't
 340          * clobber each other.
 341          */
 342 #define CHECKOP(_op, _ch) do \


 423         mp = mparse_alloc(mparse_options, MANDOCERR_MAX, NULL,
 424             MANDOC_OS_OTHER, NULL);
 425         mandoc_ohash_init(&mpages, 6, offsetof(struct mpage, inodev));
 426         mandoc_ohash_init(&mlinks, 6, offsetof(struct mlink, file));
 427 
 428         if (OP_UPDATE == op || OP_DELETE == op || OP_TEST == op) {
 429 
 430                 /*
 431                  * Most of these deal with a specific directory.
 432                  * Jump into that directory first.
 433                  */
 434                 if (OP_TEST != op && 0 == set_basedir(path_arg, 1))
 435                         goto out;
 436 
 437                 dba = nodb ? dba_new(128) : dba_read(MANDOC_DB);
 438                 if (dba != NULL) {
 439                         /*
 440                          * The existing database is usable.  Process
 441                          * all files specified on the command-line.
 442                          */
 443 #if HAVE_PLEDGE
 444                         if (!nodb) {
 445                                 if (pledge("stdio rpath wpath cpath fattr flock", NULL) == -1) {
 446                                         warn("pledge");
 447                                         exitcode = (int)MANDOCLEVEL_SYSERR;
 448                                         goto out;
 449                                 }
 450                         }
 451 #endif
 452                         use_all = 1;
 453                         for (i = 0; i < argc; i++)
 454                                 filescan(argv[i]);
 455                         if (nodb == 0)
 456                                 dbprune(dba);
 457                 } else {
 458                         /* Database missing or corrupt. */
 459                         if (op != OP_UPDATE || errno != ENOENT)
 460                                 say(MANDOC_DB, "%s: Automatically recreating"
 461                                     " from scratch", strerror(errno));
 462                         exitcode = (int)MANDOCLEVEL_OK;
 463                         op = OP_DEFAULT;
 464                         if (0 == treescan())
 465                                 goto out;
 466                         dba = dba_new(128);
 467                 }
 468                 if (OP_DELETE != op)
 469                         mpages_merge(dba, mp);
 470                 if (nodb == 0)
 471                         dbwrite(dba);


1365                 if (warnings)
1366                         say(mlink->file, "No dash in title line, "
1367                             "reusing \"%s\" as one-line description", title);
1368                 p = title;
1369         }
1370 
1371         plen = strlen(p);
1372 
1373         /* Strip backspace-encoding from line. */
1374 
1375         while (NULL != (line = memchr(p, '\b', plen))) {
1376                 len = line - p;
1377                 if (0 == len) {
1378                         memmove(line, line + 1, plen--);
1379                         continue;
1380                 }
1381                 memmove(line - 1, line + 1, plen - len);
1382                 plen -= 2;
1383         }
1384 
1385         mpage->desc = mandoc_strdup(p);





1386         fclose(stream);
1387         free(title);
1388 }
1389 
1390 /*
1391  * Put a type/word pair into the word database for this particular file.
1392  */
1393 static void
1394 putkey(const struct mpage *mpage, char *value, uint64_t type)
1395 {
1396         putkeys(mpage, value, strlen(value), type);
1397 }
1398 
1399 /*
1400  * Grok all nodes at or below a certain mdoc node into putkey().
1401  */
1402 static void
1403 putmdockey(const struct mpage *mpage,
1404         const struct roff_node *n, uint64_t m, int taboo)
1405 {


1509                                 return;
1510                         }
1511 
1512                         while (isspace((unsigned char)*start))
1513                                 start++;
1514 
1515                         if (0 == strncmp(start, "-", 1))
1516                                 start += 1;
1517                         else if (0 == strncmp(start, "\\-\\-", 4))
1518                                 start += 4;
1519                         else if (0 == strncmp(start, "\\-", 2))
1520                                 start += 2;
1521                         else if (0 == strncmp(start, "\\(en", 4))
1522                                 start += 4;
1523                         else if (0 == strncmp(start, "\\(em", 4))
1524                                 start += 4;
1525 
1526                         while (' ' == *start)
1527                                 start++;
1528 
1529                         mpage->desc = mandoc_strdup(start);





1530                         free(title);
1531                         return;
1532                 }
1533         }
1534 
1535         for (n = n->child; n; n = n->next) {
1536                 if (NULL != mpage->desc)
1537                         break;
1538                 parse_man(mpage, meta, n);
1539         }
1540 }
1541 
1542 static void
1543 parse_mdoc(struct mpage *mpage, const struct roff_meta *meta,
1544         const struct roff_node *n)
1545 {
1546 
1547         for (n = n->child; n != NULL; n = n->next) {
1548                 if (n->tok == TOKEN_NONE ||
1549                     n->tok < ROFF_MAX ||


1555                 case ROFFT_BLOCK:
1556                 case ROFFT_HEAD:
1557                 case ROFFT_BODY:
1558                 case ROFFT_TAIL:
1559                         if (mdocs[n->tok].fp != NULL &&
1560                             (*mdocs[n->tok].fp)(mpage, meta, n) == 0)
1561                                 break;
1562                         if (mdocs[n->tok].mask)
1563                                 putmdockey(mpage, n->child,
1564                                     mdocs[n->tok].mask, mdocs[n->tok].taboo);
1565                         break;
1566                 default:
1567                         continue;
1568                 }
1569                 if (NULL != n->child)
1570                         parse_mdoc(mpage, meta, n);
1571         }
1572 }
1573 
1574 static int














1575 parse_mdoc_Fd(struct mpage *mpage, const struct roff_meta *meta,
1576         const struct roff_node *n)
1577 {
1578         char            *start, *end;
1579         size_t           sz;
1580 
1581         if (SEC_SYNOPSIS != n->sec ||
1582             NULL == (n = n->child) ||
1583             n->type != ROFFT_TEXT)
1584                 return 0;
1585 
1586         /*
1587          * Only consider those `Fd' macro fields that begin with an
1588          * "inclusion" token (versus, e.g., #define).
1589          */
1590 
1591         if (strcmp("#include", n->string))
1592                 return 0;
1593 
1594         if ((n = n->next) == NULL || n->type != ROFFT_TEXT)


1623 
1624         if (n->type != ROFFT_TEXT)
1625                 return;
1626 
1627         /* Skip function pointer punctuation. */
1628 
1629         cp = n->string;
1630         while (*cp == '(' || *cp == '*')
1631                 cp++;
1632         sz = strcspn(cp, "()");
1633 
1634         putkeys(mpage, cp, sz, TYPE_Fn);
1635         if (n->sec == SEC_SYNOPSIS)
1636                 putkeys(mpage, cp, sz, NAME_SYN);
1637 }
1638 
1639 static int
1640 parse_mdoc_Fn(struct mpage *mpage, const struct roff_meta *meta,
1641         const struct roff_node *n)
1642 {

1643 
1644         if (n->child == NULL)
1645                 return 0;
1646 
1647         parse_mdoc_fname(mpage, n->child);
1648 
1649         for (n = n->child->next; n != NULL; n = n->next)
1650                 if (n->type == ROFFT_TEXT)
1651                         putkey(mpage, n->string, TYPE_Fa);




1652 
1653         return 0;
1654 }
1655 
1656 static int
1657 parse_mdoc_Fo(struct mpage *mpage, const struct roff_meta *meta,
1658         const struct roff_node *n)
1659 {
1660 
1661         if (n->type != ROFFT_HEAD)
1662                 return 1;
1663 
1664         if (n->child != NULL)
1665                 parse_mdoc_fname(mpage, n->child);
1666 
1667         return 0;
1668 }
1669 
1670 static int
1671 parse_mdoc_Va(struct mpage *mpage, const struct roff_meta *meta,


2102                 dba_array_FOREACH(files, file) {
2103                         if (*file < ' ')
2104                                 file++;
2105                         if (ohash_find(&mlinks, ohash_qlookup(&mlinks,
2106                             file)) != NULL) {
2107                                 if (debug)
2108                                         say(file, "Deleting from database");
2109                                 dba_array_del(dba->pages);
2110                                 break;
2111                         }
2112                 }
2113         }
2114 }
2115 
2116 /*
2117  * Write the database from memory to disk.
2118  */
2119 static void
2120 dbwrite(struct dba *dba)
2121 {
2122         char             tfn[32];
2123         int              status;
2124         pid_t            child;

2125 
2126         /*
2127          * Do not write empty databases, and delete existing ones
2128          * when makewhatis -u causes them to become empty.
2129          */
2130 
2131         dba_array_start(dba->pages);
2132         if (dba_array_next(dba->pages) == NULL) {
2133                 if (unlink(MANDOC_DB) == -1 && errno != ENOENT)
2134                         say(MANDOC_DB, "&unlink");
2135                 return;
2136         }
2137 
2138         /*
2139          * Build the database in a temporary file,
2140          * then atomically move it into place.
2141          */
2142 
2143         if (dba_write(MANDOC_DB "~", dba) != -1) {
2144                 if (rename(MANDOC_DB "~", MANDOC_DB) == -1) {
2145                         exitcode = (int)MANDOCLEVEL_SYSERR;
2146                         say(MANDOC_DB, "&rename");
2147                         unlink(MANDOC_DB "~");
2148                 }
2149                 return;
2150         }
2151 
2152         /*
2153          * We lack write permission and cannot replace the database
2154          * file, but let's at least check whether the data changed.
2155          */
2156 
2157         (void)strlcpy(tfn, "/tmp/mandocdb.XXXXXXXX", sizeof(tfn));
2158         if (mkdtemp(tfn) == NULL) {
2159                 exitcode = (int)MANDOCLEVEL_SYSERR;
2160                 say("", "&%s", tfn);
2161                 return;
2162         }
2163 

2164         (void)strlcat(tfn, "/" MANDOC_DB, sizeof(tfn));
2165         if (dba_write(tfn, dba) == -1) {
2166                 exitcode = (int)MANDOCLEVEL_SYSERR;
2167                 say(tfn, "&dba_write");
2168                 goto out;
2169         }
2170 
2171         switch (child = fork()) {
2172         case -1:
2173                 exitcode = (int)MANDOCLEVEL_SYSERR;
2174                 say("", "&fork cmp");
2175                 return;
2176         case 0:
2177                 execlp("cmp", "cmp", "-s", tfn, MANDOC_DB, (char *)NULL);
2178                 say("", "&exec cmp");
2179                 exit(0);
2180         default:
2181                 break;
2182         }
2183         if (waitpid(child, &status, 0) == -1) {
2184                 exitcode = (int)MANDOCLEVEL_SYSERR;
2185                 say("", "&wait cmp");
2186         } else if (WIFSIGNALED(status)) {
2187                 exitcode = (int)MANDOCLEVEL_SYSERR;
2188                 say("", "cmp died from signal %d", WTERMSIG(status));
2189         } else if (WEXITSTATUS(status)) {
2190                 exitcode = (int)MANDOCLEVEL_SYSERR;
2191                 say(MANDOC_DB,
2192                     "Data changed, but cannot replace database");
2193         }
























2194 




2195 out:









2196         *strrchr(tfn, '/') = '\0';
2197         switch (child = fork()) {
2198         case -1:
2199                 exitcode = (int)MANDOCLEVEL_SYSERR;
2200                 say("", "&fork rm");
2201                 return;
2202         case 0:
2203                 execlp("rm", "rm", "-rf", tfn, (char *)NULL);
2204                 say("", "&exec rm");
2205                 exit((int)MANDOCLEVEL_SYSERR);
2206         default:
2207                 break;
2208         }
2209         if (waitpid(child, &status, 0) == -1) {
2210                 exitcode = (int)MANDOCLEVEL_SYSERR;
2211                 say("", "&wait rm");
2212         } else if (WIFSIGNALED(status) || WEXITSTATUS(status)) {
2213                 exitcode = (int)MANDOCLEVEL_SYSERR;
2214                 say("", "%s: Cannot remove temporary directory", tfn);
2215         }
2216 }
2217 
2218 static int
2219 set_basedir(const char *targetdir, int report_baddir)
2220 {
2221         static char      startdir[PATH_MAX];
2222         static int       getcwd_status;  /* 1 = ok, 2 = failure */
2223         static int       chdir_status;  /* 1 = changed directory */
2224         char            *cp;
2225 
2226         /*
2227          * Remember the original working directory, if possible.
2228          * This will be needed if the second or a later directory
2229          * on the command line is given as a relative path.
2230          * Do not error out if the current directory is not
2231          * searchable: Maybe it won't be needed after all.
2232          */
2233         if (0 == getcwd_status) {
2234                 if (NULL == getcwd(startdir, sizeof(startdir))) {
2235                         getcwd_status = 2;


   1 /*      $Id: mandocdb.c,v 1.258 2018/02/23 18:25:57 schwarze Exp $ */
   2 /*
   3  * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
   4  * Copyright (c) 2011-2017 Ingo Schwarze <schwarze@openbsd.org>
   5  * Copyright (c) 2016 Ed Maste <emaste@freebsd.org>
   6  *
   7  * Permission to use, copy, modify, and distribute this software for any
   8  * purpose with or without fee is hereby granted, provided that the above
   9  * copyright notice and this permission notice appear in all copies.
  10  *
  11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
  12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
  14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  18  */
  19 #include "config.h"
  20 
  21 #include <sys/types.h>
  22 #include <sys/mman.h>
  23 #include <sys/stat.h>

  24 
  25 #include <assert.h>
  26 #include <ctype.h>
  27 #if HAVE_ERR
  28 #include <err.h>
  29 #endif
  30 #include <errno.h>
  31 #include <fcntl.h>
  32 #if HAVE_FTS
  33 #include <fts.h>
  34 #else
  35 #include "compat_fts.h"
  36 #endif
  37 #include <limits.h>
  38 #if HAVE_SANDBOX_INIT
  39 #include <sandbox.h>
  40 #endif
  41 #include <stdarg.h>
  42 #include <stddef.h>
  43 #include <stdio.h>


 122 static  void     dbwrite(struct dba *);
 123 static  void     filescan(const char *);
 124 #if HAVE_FTS_COMPARE_CONST
 125 static  int      fts_compare(const FTSENT *const *, const FTSENT *const *);
 126 #else
 127 static  int      fts_compare(const FTSENT **, const FTSENT **);
 128 #endif
 129 static  void     mlink_add(struct mlink *, const struct stat *);
 130 static  void     mlink_check(struct mpage *, struct mlink *);
 131 static  void     mlink_free(struct mlink *);
 132 static  void     mlinks_undupe(struct mpage *);
 133 static  void     mpages_free(void);
 134 static  void     mpages_merge(struct dba *, struct mparse *);
 135 static  void     parse_cat(struct mpage *, int);
 136 static  void     parse_man(struct mpage *, const struct roff_meta *,
 137                         const struct roff_node *);
 138 static  void     parse_mdoc(struct mpage *, const struct roff_meta *,
 139                         const struct roff_node *);
 140 static  int      parse_mdoc_head(struct mpage *, const struct roff_meta *,
 141                         const struct roff_node *);
 142 static  int      parse_mdoc_Fa(struct mpage *, const struct roff_meta *,
 143                         const struct roff_node *);
 144 static  int      parse_mdoc_Fd(struct mpage *, const struct roff_meta *,
 145                         const struct roff_node *);
 146 static  void     parse_mdoc_fname(struct mpage *, const struct roff_node *);
 147 static  int      parse_mdoc_Fn(struct mpage *, const struct roff_meta *,
 148                         const struct roff_node *);
 149 static  int      parse_mdoc_Fo(struct mpage *, const struct roff_meta *,
 150                         const struct roff_node *);
 151 static  int      parse_mdoc_Nd(struct mpage *, const struct roff_meta *,
 152                         const struct roff_node *);
 153 static  int      parse_mdoc_Nm(struct mpage *, const struct roff_meta *,
 154                         const struct roff_node *);
 155 static  int      parse_mdoc_Sh(struct mpage *, const struct roff_meta *,
 156                         const struct roff_node *);
 157 static  int      parse_mdoc_Va(struct mpage *, const struct roff_meta *,
 158                         const struct roff_node *);
 159 static  int      parse_mdoc_Xr(struct mpage *, const struct roff_meta *,
 160                         const struct roff_node *);
 161 static  void     putkey(const struct mpage *, char *, uint64_t);
 162 static  void     putkeys(const struct mpage *, char *, size_t, uint64_t);
 163 static  void     putmdockey(const struct mpage *,


 192         { parse_mdoc_Sh, TYPE_Sh, 0 }, /* Sh */
 193         { parse_mdoc_head, TYPE_Ss, 0 }, /* Ss */
 194         { NULL, 0, 0 },  /* Pp */
 195         { NULL, 0, 0 },  /* D1 */
 196         { NULL, 0, 0 },  /* Dl */
 197         { NULL, 0, 0 },  /* Bd */
 198         { NULL, 0, 0 },  /* Ed */
 199         { NULL, 0, 0 },  /* Bl */
 200         { NULL, 0, 0 },  /* El */
 201         { NULL, 0, 0 },  /* It */
 202         { NULL, 0, 0 },  /* Ad */
 203         { NULL, TYPE_An, 0 },  /* An */
 204         { NULL, 0, 0 },  /* Ap */
 205         { NULL, TYPE_Ar, 0 },  /* Ar */
 206         { NULL, TYPE_Cd, 0 },  /* Cd */
 207         { NULL, TYPE_Cm, 0 },  /* Cm */
 208         { NULL, TYPE_Dv, 0 },  /* Dv */
 209         { NULL, TYPE_Er, 0 },  /* Er */
 210         { NULL, TYPE_Ev, 0 },  /* Ev */
 211         { NULL, 0, 0 },  /* Ex */
 212         { parse_mdoc_Fa, 0, 0 },  /* Fa */
 213         { parse_mdoc_Fd, 0, 0 },  /* Fd */
 214         { NULL, TYPE_Fl, 0 },  /* Fl */
 215         { parse_mdoc_Fn, 0, 0 },  /* Fn */
 216         { NULL, TYPE_Ft | TYPE_Vt, 0 },  /* Ft */
 217         { NULL, TYPE_Ic, 0 },  /* Ic */
 218         { NULL, TYPE_In, 0 },  /* In */
 219         { NULL, TYPE_Li, 0 },  /* Li */
 220         { parse_mdoc_Nd, 0, 0 },  /* Nd */
 221         { parse_mdoc_Nm, 0, 0 },  /* Nm */
 222         { NULL, 0, 0 },  /* Op */
 223         { NULL, 0, 0 },  /* Ot */
 224         { NULL, TYPE_Pa, NODE_NOSRC },  /* Pa */
 225         { NULL, 0, 0 },  /* Rv */
 226         { NULL, TYPE_St, 0 },  /* St */
 227         { parse_mdoc_Va, TYPE_Va, 0 },  /* Va */
 228         { parse_mdoc_Va, TYPE_Vt, 0 },  /* Vt */
 229         { parse_mdoc_Xr, 0, 0 },  /* Xr */
 230         { NULL, 0, 0 },  /* %A */
 231         { NULL, 0, 0 },  /* %B */
 232         { NULL, 0, 0 },  /* %D */
 233         { NULL, 0, 0 },  /* %I */
 234         { NULL, 0, 0 },  /* %J */
 235         { NULL, 0, 0 },  /* %N */
 236         { NULL, 0, 0 },  /* %O */


 304         { NULL, 0, 0 },  /* En */
 305         { NULL, TYPE_Dx, NODE_NOSRC },  /* Dx */
 306         { NULL, 0, 0 },  /* %Q */
 307         { NULL, 0, 0 },  /* %U */
 308         { NULL, 0, 0 },  /* Ta */
 309 };
 310 static  const struct mdoc_handler *const mdocs = __mdocs - MDOC_Dd;
 311 
 312 
 313 int
 314 mandocdb(int argc, char *argv[])
 315 {
 316         struct manconf    conf;
 317         struct mparse    *mp;
 318         struct dba       *dba;
 319         const char       *path_arg, *progname;
 320         size_t            j, sz;
 321         int               ch, i;
 322 
 323 #if HAVE_PLEDGE
 324         if (pledge("stdio rpath wpath cpath", NULL) == -1) {
 325                 warn("pledge");
 326                 return (int)MANDOCLEVEL_SYSERR;
 327         }
 328 #endif
 329 
 330 #if HAVE_SANDBOX_INIT
 331         if (sandbox_init(kSBXProfileNoInternet, SANDBOX_NAMED, NULL) == -1) {
 332                 warnx("sandbox_init");
 333                 return (int)MANDOCLEVEL_SYSERR;
 334         }
 335 #endif
 336 
 337         memset(&conf, 0, sizeof(conf));
 338 
 339         /*
 340          * We accept a few different invocations.
 341          * The CHECKOP macro makes sure that invocation styles don't
 342          * clobber each other.
 343          */
 344 #define CHECKOP(_op, _ch) do \


 425         mp = mparse_alloc(mparse_options, MANDOCERR_MAX, NULL,
 426             MANDOC_OS_OTHER, NULL);
 427         mandoc_ohash_init(&mpages, 6, offsetof(struct mpage, inodev));
 428         mandoc_ohash_init(&mlinks, 6, offsetof(struct mlink, file));
 429 
 430         if (OP_UPDATE == op || OP_DELETE == op || OP_TEST == op) {
 431 
 432                 /*
 433                  * Most of these deal with a specific directory.
 434                  * Jump into that directory first.
 435                  */
 436                 if (OP_TEST != op && 0 == set_basedir(path_arg, 1))
 437                         goto out;
 438 
 439                 dba = nodb ? dba_new(128) : dba_read(MANDOC_DB);
 440                 if (dba != NULL) {
 441                         /*
 442                          * The existing database is usable.  Process
 443                          * all files specified on the command-line.
 444                          */









 445                         use_all = 1;
 446                         for (i = 0; i < argc; i++)
 447                                 filescan(argv[i]);
 448                         if (nodb == 0)
 449                                 dbprune(dba);
 450                 } else {
 451                         /* Database missing or corrupt. */
 452                         if (op != OP_UPDATE || errno != ENOENT)
 453                                 say(MANDOC_DB, "%s: Automatically recreating"
 454                                     " from scratch", strerror(errno));
 455                         exitcode = (int)MANDOCLEVEL_OK;
 456                         op = OP_DEFAULT;
 457                         if (0 == treescan())
 458                                 goto out;
 459                         dba = dba_new(128);
 460                 }
 461                 if (OP_DELETE != op)
 462                         mpages_merge(dba, mp);
 463                 if (nodb == 0)
 464                         dbwrite(dba);


1358                 if (warnings)
1359                         say(mlink->file, "No dash in title line, "
1360                             "reusing \"%s\" as one-line description", title);
1361                 p = title;
1362         }
1363 
1364         plen = strlen(p);
1365 
1366         /* Strip backspace-encoding from line. */
1367 
1368         while (NULL != (line = memchr(p, '\b', plen))) {
1369                 len = line - p;
1370                 if (0 == len) {
1371                         memmove(line, line + 1, plen--);
1372                         continue;
1373                 }
1374                 memmove(line - 1, line + 1, plen - len);
1375                 plen -= 2;
1376         }
1377 
1378         /*
1379          * Cut off excessive one-line descriptions.
1380          * Bad pages are not worth better heuristics.
1381          */
1382 
1383         mpage->desc = mandoc_strndup(p, 150);
1384         fclose(stream);
1385         free(title);
1386 }
1387 
1388 /*
1389  * Put a type/word pair into the word database for this particular file.
1390  */
1391 static void
1392 putkey(const struct mpage *mpage, char *value, uint64_t type)
1393 {
1394         putkeys(mpage, value, strlen(value), type);
1395 }
1396 
1397 /*
1398  * Grok all nodes at or below a certain mdoc node into putkey().
1399  */
1400 static void
1401 putmdockey(const struct mpage *mpage,
1402         const struct roff_node *n, uint64_t m, int taboo)
1403 {


1507                                 return;
1508                         }
1509 
1510                         while (isspace((unsigned char)*start))
1511                                 start++;
1512 
1513                         if (0 == strncmp(start, "-", 1))
1514                                 start += 1;
1515                         else if (0 == strncmp(start, "\\-\\-", 4))
1516                                 start += 4;
1517                         else if (0 == strncmp(start, "\\-", 2))
1518                                 start += 2;
1519                         else if (0 == strncmp(start, "\\(en", 4))
1520                                 start += 4;
1521                         else if (0 == strncmp(start, "\\(em", 4))
1522                                 start += 4;
1523 
1524                         while (' ' == *start)
1525                                 start++;
1526 
1527                         /*
1528                          * Cut off excessive one-line descriptions.
1529                          * Bad pages are not worth better heuristics.
1530                          */
1531 
1532                         mpage->desc = mandoc_strndup(start, 150);
1533                         free(title);
1534                         return;
1535                 }
1536         }
1537 
1538         for (n = n->child; n; n = n->next) {
1539                 if (NULL != mpage->desc)
1540                         break;
1541                 parse_man(mpage, meta, n);
1542         }
1543 }
1544 
1545 static void
1546 parse_mdoc(struct mpage *mpage, const struct roff_meta *meta,
1547         const struct roff_node *n)
1548 {
1549 
1550         for (n = n->child; n != NULL; n = n->next) {
1551                 if (n->tok == TOKEN_NONE ||
1552                     n->tok < ROFF_MAX ||


1558                 case ROFFT_BLOCK:
1559                 case ROFFT_HEAD:
1560                 case ROFFT_BODY:
1561                 case ROFFT_TAIL:
1562                         if (mdocs[n->tok].fp != NULL &&
1563                             (*mdocs[n->tok].fp)(mpage, meta, n) == 0)
1564                                 break;
1565                         if (mdocs[n->tok].mask)
1566                                 putmdockey(mpage, n->child,
1567                                     mdocs[n->tok].mask, mdocs[n->tok].taboo);
1568                         break;
1569                 default:
1570                         continue;
1571                 }
1572                 if (NULL != n->child)
1573                         parse_mdoc(mpage, meta, n);
1574         }
1575 }
1576 
1577 static int
1578 parse_mdoc_Fa(struct mpage *mpage, const struct roff_meta *meta,
1579         const struct roff_node *n)
1580 {
1581         uint64_t mask;
1582 
1583         mask = TYPE_Fa;
1584         if (n->sec == SEC_SYNOPSIS)
1585                 mask |= TYPE_Vt;
1586 
1587         putmdockey(mpage, n->child, mask, 0);
1588         return 0;
1589 }
1590 
1591 static int
1592 parse_mdoc_Fd(struct mpage *mpage, const struct roff_meta *meta,
1593         const struct roff_node *n)
1594 {
1595         char            *start, *end;
1596         size_t           sz;
1597 
1598         if (SEC_SYNOPSIS != n->sec ||
1599             NULL == (n = n->child) ||
1600             n->type != ROFFT_TEXT)
1601                 return 0;
1602 
1603         /*
1604          * Only consider those `Fd' macro fields that begin with an
1605          * "inclusion" token (versus, e.g., #define).
1606          */
1607 
1608         if (strcmp("#include", n->string))
1609                 return 0;
1610 
1611         if ((n = n->next) == NULL || n->type != ROFFT_TEXT)


1640 
1641         if (n->type != ROFFT_TEXT)
1642                 return;
1643 
1644         /* Skip function pointer punctuation. */
1645 
1646         cp = n->string;
1647         while (*cp == '(' || *cp == '*')
1648                 cp++;
1649         sz = strcspn(cp, "()");
1650 
1651         putkeys(mpage, cp, sz, TYPE_Fn);
1652         if (n->sec == SEC_SYNOPSIS)
1653                 putkeys(mpage, cp, sz, NAME_SYN);
1654 }
1655 
1656 static int
1657 parse_mdoc_Fn(struct mpage *mpage, const struct roff_meta *meta,
1658         const struct roff_node *n)
1659 {
1660         uint64_t mask;
1661 
1662         if (n->child == NULL)
1663                 return 0;
1664 
1665         parse_mdoc_fname(mpage, n->child);
1666 
1667         n = n->child->next;
1668         if (n != NULL && n->type == ROFFT_TEXT) {
1669                 mask = TYPE_Fa;
1670                 if (n->sec == SEC_SYNOPSIS)
1671                         mask |= TYPE_Vt;
1672                 putmdockey(mpage, n, mask, 0);
1673         }
1674 
1675         return 0;
1676 }
1677 
1678 static int
1679 parse_mdoc_Fo(struct mpage *mpage, const struct roff_meta *meta,
1680         const struct roff_node *n)
1681 {
1682 
1683         if (n->type != ROFFT_HEAD)
1684                 return 1;
1685 
1686         if (n->child != NULL)
1687                 parse_mdoc_fname(mpage, n->child);
1688 
1689         return 0;
1690 }
1691 
1692 static int
1693 parse_mdoc_Va(struct mpage *mpage, const struct roff_meta *meta,


2124                 dba_array_FOREACH(files, file) {
2125                         if (*file < ' ')
2126                                 file++;
2127                         if (ohash_find(&mlinks, ohash_qlookup(&mlinks,
2128                             file)) != NULL) {
2129                                 if (debug)
2130                                         say(file, "Deleting from database");
2131                                 dba_array_del(dba->pages);
2132                                 break;
2133                         }
2134                 }
2135         }
2136 }
2137 
2138 /*
2139  * Write the database from memory to disk.
2140  */
2141 static void
2142 dbwrite(struct dba *dba)
2143 {
2144         struct stat      sb1, sb2;
2145         char             tfn[33], *cp1, *cp2;
2146         off_t            i;
2147         int              fd1, fd2;
2148 
2149         /*
2150          * Do not write empty databases, and delete existing ones
2151          * when makewhatis -u causes them to become empty.
2152          */
2153 
2154         dba_array_start(dba->pages);
2155         if (dba_array_next(dba->pages) == NULL) {
2156                 if (unlink(MANDOC_DB) == -1 && errno != ENOENT)
2157                         say(MANDOC_DB, "&unlink");
2158                 return;
2159         }
2160 
2161         /*
2162          * Build the database in a temporary file,
2163          * then atomically move it into place.
2164          */
2165 
2166         if (dba_write(MANDOC_DB "~", dba) != -1) {
2167                 if (rename(MANDOC_DB "~", MANDOC_DB) == -1) {
2168                         exitcode = (int)MANDOCLEVEL_SYSERR;
2169                         say(MANDOC_DB, "&rename");
2170                         unlink(MANDOC_DB "~");
2171                 }
2172                 return;
2173         }
2174 
2175         /*
2176          * We lack write permission and cannot replace the database
2177          * file, but let's at least check whether the data changed.
2178          */
2179 
2180         (void)strlcpy(tfn, "/tmp/mandocdb.XXXXXXXX", sizeof(tfn));
2181         if (mkdtemp(tfn) == NULL) {
2182                 exitcode = (int)MANDOCLEVEL_SYSERR;
2183                 say("", "&%s", tfn);
2184                 return;
2185         }
2186         cp1 = cp2 = MAP_FAILED;
2187         fd1 = fd2 = -1;
2188         (void)strlcat(tfn, "/" MANDOC_DB, sizeof(tfn));
2189         if (dba_write(tfn, dba) == -1) {

2190                 say(tfn, "&dba_write");
2191                 goto err;
2192         }
2193         if ((fd1 = open(MANDOC_DB, O_RDONLY, 0)) == -1) {
2194                 say(MANDOC_DB, "&open");
2195                 goto err;









2196         }
2197         if ((fd2 = open(tfn, O_RDONLY, 0)) == -1) {
2198                 say(tfn, "&open");
2199                 goto err;







2200         }
2201         if (fstat(fd1, &sb1) == -1) {
2202                 say(MANDOC_DB, "&fstat");
2203                 goto err;
2204         }
2205         if (fstat(fd2, &sb2) == -1) {
2206                 say(tfn, "&fstat");
2207                 goto err;
2208         }
2209         if (sb1.st_size != sb2.st_size)
2210                 goto err;
2211         if ((cp1 = mmap(NULL, sb1.st_size, PROT_READ, MAP_PRIVATE,
2212             fd1, 0)) == MAP_FAILED) {
2213                 say(MANDOC_DB, "&mmap");
2214                 goto err;
2215         }
2216         if ((cp2 = mmap(NULL, sb2.st_size, PROT_READ, MAP_PRIVATE,
2217             fd2, 0)) == MAP_FAILED) {
2218                 say(tfn, "&mmap");
2219                 goto err;
2220         }
2221         for (i = 0; i < sb1.st_size; i++)
2222                 if (cp1[i] != cp2[i])
2223                         goto err;
2224         goto out;
2225 
2226 err:
2227         exitcode = (int)MANDOCLEVEL_SYSERR;
2228         say(MANDOC_DB, "Data changed, but cannot replace database");
2229 
2230 out:
2231         if (cp1 != MAP_FAILED)
2232                 munmap(cp1, sb1.st_size);
2233         if (cp2 != MAP_FAILED)
2234                 munmap(cp2, sb2.st_size);
2235         if (fd1 != -1)
2236                 close(fd1);
2237         if (fd2 != -1)
2238                 close(fd2);
2239         unlink(tfn);
2240         *strrchr(tfn, '/') = '\0';
2241         rmdir(tfn);


















2242 }
2243 
2244 static int
2245 set_basedir(const char *targetdir, int report_baddir)
2246 {
2247         static char      startdir[PATH_MAX];
2248         static int       getcwd_status;  /* 1 = ok, 2 = failure */
2249         static int       chdir_status;  /* 1 = changed directory */
2250         char            *cp;
2251 
2252         /*
2253          * Remember the original working directory, if possible.
2254          * This will be needed if the second or a later directory
2255          * on the command line is given as a relative path.
2256          * Do not error out if the current directory is not
2257          * searchable: Maybe it won't be needed after all.
2258          */
2259         if (0 == getcwd_status) {
2260                 if (NULL == getcwd(startdir, sizeof(startdir))) {
2261                         getcwd_status = 2;