1 /* Id: mandocdb.c,v 1.101 2014/01/05 04:48:40 schwarze Exp */ 2 /* 3 * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> 4 * Copyright (c) 2011, 2012, 2013, 2014 Ingo Schwarze <schwarze@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 #ifdef HAVE_CONFIG_H 19 #include "config.h" 20 #endif 21 22 #include <sys/stat.h> 23 24 #include <assert.h> 25 #include <ctype.h> 26 #include <errno.h> 27 #include <fcntl.h> 28 #include <fts.h> 29 #include <getopt.h> 30 #include <limits.h> 31 #include <stddef.h> 32 #include <stdio.h> 33 #include <stdint.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <unistd.h> 37 38 #ifdef HAVE_OHASH 39 #include <ohash.h> 40 #else 41 #include "compat_ohash.h" 42 #endif 43 #include <sqlite3.h> 44 45 #include "mdoc.h" 46 #include "man.h" 47 #include "mandoc.h" 48 #include "manpath.h" 49 #include "mansearch.h" 50 51 #define SQL_EXEC(_v) \ 52 if (SQLITE_OK != sqlite3_exec(db, (_v), NULL, NULL, NULL)) \ 53 fprintf(stderr, "%s\n", sqlite3_errmsg(db)) 54 #define SQL_BIND_TEXT(_s, _i, _v) \ 55 if (SQLITE_OK != sqlite3_bind_text \ 56 ((_s), (_i)++, (_v), -1, SQLITE_STATIC)) \ 57 fprintf(stderr, "%s\n", sqlite3_errmsg(db)) 58 #define SQL_BIND_INT(_s, _i, _v) \ 59 if (SQLITE_OK != sqlite3_bind_int \ 60 ((_s), (_i)++, (_v))) \ 61 fprintf(stderr, "%s\n", sqlite3_errmsg(db)) 62 #define SQL_BIND_INT64(_s, _i, _v) \ 63 if (SQLITE_OK != sqlite3_bind_int64 \ 64 ((_s), (_i)++, (_v))) \ 65 fprintf(stderr, "%s\n", sqlite3_errmsg(db)) 66 #define SQL_STEP(_s) \ 67 if (SQLITE_DONE != sqlite3_step((_s))) \ 68 fprintf(stderr, "%s\n", sqlite3_errmsg(db)) 69 70 enum op { 71 OP_DEFAULT = 0, /* new dbs from dir list or default config */ 72 OP_CONFFILE, /* new databases from custom config file */ 73 OP_UPDATE, /* delete/add entries in existing database */ 74 OP_DELETE, /* delete entries from existing database */ 75 OP_TEST /* change no databases, report potential problems */ 76 }; 77 78 enum form { 79 FORM_NONE, /* format is unknown */ 80 FORM_SRC, /* format is -man or -mdoc */ 81 FORM_CAT /* format is cat */ 82 }; 83 84 struct str { 85 char *rendered; /* key in UTF-8 or ASCII form */ 86 const struct mpage *mpage; /* if set, the owning parse */ 87 uint64_t mask; /* bitmask in sequence */ 88 char key[]; /* may contain escape sequences */ 89 }; 90 91 struct inodev { 92 ino_t st_ino; 93 dev_t st_dev; 94 }; 95 96 struct mpage { 97 struct inodev inodev; /* used for hashing routine */ 98 enum form form; /* format from file content */ 99 char *sec; /* section from file content */ 100 char *arch; /* architecture from file content */ 101 char *title; /* title from file content */ 102 char *desc; /* description from file content */ 103 struct mlink *mlinks; /* singly linked list */ 104 }; 105 106 struct mlink { 107 char file[PATH_MAX]; /* filename rel. to manpath */ 108 enum form dform; /* format from directory */ 109 enum form fform; /* format from file name suffix */ 110 char *dsec; /* section from directory */ 111 char *arch; /* architecture from directory */ 112 char *name; /* name from file name (not empty) */ 113 char *fsec; /* section from file name suffix */ 114 struct mlink *next; /* singly linked list */ 115 }; 116 117 enum stmt { 118 STMT_DELETE_PAGE = 0, /* delete mpage */ 119 STMT_INSERT_PAGE, /* insert mpage */ 120 STMT_INSERT_LINK, /* insert mlink */ 121 STMT_INSERT_KEY, /* insert parsed key */ 122 STMT__MAX 123 }; 124 125 typedef int (*mdoc_fp)(struct mpage *, const struct mdoc_node *); 126 127 struct mdoc_handler { 128 mdoc_fp fp; /* optional handler */ 129 uint64_t mask; /* set unless handler returns 0 */ 130 }; 131 132 static void dbclose(int); 133 static void dbindex(const struct mpage *, struct mchars *); 134 static int dbopen(int); 135 static void dbprune(void); 136 static void filescan(const char *); 137 static void *hash_alloc(size_t, void *); 138 static void hash_free(void *, size_t, void *); 139 static void *hash_halloc(size_t, void *); 140 static void mlink_add(struct mlink *, const struct stat *); 141 static int mlink_check(struct mpage *, struct mlink *); 142 static void mlink_free(struct mlink *); 143 static void mlinks_undupe(struct mpage *); 144 static void mpages_free(void); 145 static void mpages_merge(struct mchars *, struct mparse *); 146 static void parse_cat(struct mpage *); 147 static void parse_man(struct mpage *, const struct man_node *); 148 static void parse_mdoc(struct mpage *, const struct mdoc_node *); 149 static int parse_mdoc_body(struct mpage *, const struct mdoc_node *); 150 static int parse_mdoc_head(struct mpage *, const struct mdoc_node *); 151 static int parse_mdoc_Fd(struct mpage *, const struct mdoc_node *); 152 static int parse_mdoc_Fn(struct mpage *, const struct mdoc_node *); 153 static int parse_mdoc_Nd(struct mpage *, const struct mdoc_node *); 154 static int parse_mdoc_Nm(struct mpage *, const struct mdoc_node *); 155 static int parse_mdoc_Sh(struct mpage *, const struct mdoc_node *); 156 static int parse_mdoc_Xr(struct mpage *, const struct mdoc_node *); 157 static void putkey(const struct mpage *, 158 const char *, uint64_t); 159 static void putkeys(const struct mpage *, 160 const char *, size_t, uint64_t); 161 static void putmdockey(const struct mpage *, 162 const struct mdoc_node *, uint64_t); 163 static void render_key(struct mchars *, struct str *); 164 static void say(const char *, const char *, ...); 165 static int set_basedir(const char *); 166 static int treescan(void); 167 static size_t utf8(unsigned int, char [7]); 168 169 static char *progname; 170 static int use_all; /* use all found files */ 171 static int nodb; /* no database changes */ 172 static int verb; /* print what we're doing */ 173 static int warnings; /* warn about crap */ 174 static int write_utf8; /* write UTF-8 output; else ASCII */ 175 static int exitcode; /* to be returned by main */ 176 static enum op op; /* operational mode */ 177 static char basedir[PATH_MAX]; /* current base directory */ 178 static struct ohash mpages; /* table of distinct manual pages */ 179 static struct ohash mlinks; /* table of directory entries */ 180 static struct ohash strings; /* table of all strings */ 181 static sqlite3 *db = NULL; /* current database */ 182 static sqlite3_stmt *stmts[STMT__MAX]; /* current statements */ 183 184 static const struct mdoc_handler mdocs[MDOC_MAX] = { 185 { NULL, 0 }, /* Ap */ 186 { NULL, 0 }, /* Dd */ 187 { NULL, 0 }, /* Dt */ 188 { NULL, 0 }, /* Os */ 189 { parse_mdoc_Sh, TYPE_Sh }, /* Sh */ 190 { parse_mdoc_head, TYPE_Ss }, /* Ss */ 191 { NULL, 0 }, /* Pp */ 192 { NULL, 0 }, /* D1 */ 193 { NULL, 0 }, /* Dl */ 194 { NULL, 0 }, /* Bd */ 195 { NULL, 0 }, /* Ed */ 196 { NULL, 0 }, /* Bl */ 197 { NULL, 0 }, /* El */ 198 { NULL, 0 }, /* It */ 199 { NULL, 0 }, /* Ad */ 200 { NULL, TYPE_An }, /* An */ 201 { NULL, TYPE_Ar }, /* Ar */ 202 { NULL, TYPE_Cd }, /* Cd */ 203 { NULL, TYPE_Cm }, /* Cm */ 204 { NULL, TYPE_Dv }, /* Dv */ 205 { NULL, TYPE_Er }, /* Er */ 206 { NULL, TYPE_Ev }, /* Ev */ 207 { NULL, 0 }, /* Ex */ 208 { NULL, TYPE_Fa }, /* Fa */ 209 { parse_mdoc_Fd, 0 }, /* Fd */ 210 { NULL, TYPE_Fl }, /* Fl */ 211 { parse_mdoc_Fn, 0 }, /* Fn */ 212 { NULL, TYPE_Ft }, /* Ft */ 213 { NULL, TYPE_Ic }, /* Ic */ 214 { NULL, TYPE_In }, /* In */ 215 { NULL, TYPE_Li }, /* Li */ 216 { parse_mdoc_Nd, TYPE_Nd }, /* Nd */ 217 { parse_mdoc_Nm, TYPE_Nm }, /* Nm */ 218 { NULL, 0 }, /* Op */ 219 { NULL, 0 }, /* Ot */ 220 { NULL, TYPE_Pa }, /* Pa */ 221 { NULL, 0 }, /* Rv */ 222 { NULL, TYPE_St }, /* St */ 223 { NULL, TYPE_Va }, /* Va */ 224 { parse_mdoc_body, TYPE_Va }, /* Vt */ 225 { parse_mdoc_Xr, 0 }, /* Xr */ 226 { NULL, 0 }, /* %A */ 227 { NULL, 0 }, /* %B */ 228 { NULL, 0 }, /* %D */ 229 { NULL, 0 }, /* %I */ 230 { NULL, 0 }, /* %J */ 231 { NULL, 0 }, /* %N */ 232 { NULL, 0 }, /* %O */ 233 { NULL, 0 }, /* %P */ 234 { NULL, 0 }, /* %R */ 235 { NULL, 0 }, /* %T */ 236 { NULL, 0 }, /* %V */ 237 { NULL, 0 }, /* Ac */ 238 { NULL, 0 }, /* Ao */ 239 { NULL, 0 }, /* Aq */ 240 { NULL, TYPE_At }, /* At */ 241 { NULL, 0 }, /* Bc */ 242 { NULL, 0 }, /* Bf */ 243 { NULL, 0 }, /* Bo */ 244 { NULL, 0 }, /* Bq */ 245 { NULL, TYPE_Bsx }, /* Bsx */ 246 { NULL, TYPE_Bx }, /* Bx */ 247 { NULL, 0 }, /* Db */ 248 { NULL, 0 }, /* Dc */ 249 { NULL, 0 }, /* Do */ 250 { NULL, 0 }, /* Dq */ 251 { NULL, 0 }, /* Ec */ 252 { NULL, 0 }, /* Ef */ 253 { NULL, TYPE_Em }, /* Em */ 254 { NULL, 0 }, /* Eo */ 255 { NULL, TYPE_Fx }, /* Fx */ 256 { NULL, TYPE_Ms }, /* Ms */ 257 { NULL, 0 }, /* No */ 258 { NULL, 0 }, /* Ns */ 259 { NULL, TYPE_Nx }, /* Nx */ 260 { NULL, TYPE_Ox }, /* Ox */ 261 { NULL, 0 }, /* Pc */ 262 { NULL, 0 }, /* Pf */ 263 { NULL, 0 }, /* Po */ 264 { NULL, 0 }, /* Pq */ 265 { NULL, 0 }, /* Qc */ 266 { NULL, 0 }, /* Ql */ 267 { NULL, 0 }, /* Qo */ 268 { NULL, 0 }, /* Qq */ 269 { NULL, 0 }, /* Re */ 270 { NULL, 0 }, /* Rs */ 271 { NULL, 0 }, /* Sc */ 272 { NULL, 0 }, /* So */ 273 { NULL, 0 }, /* Sq */ 274 { NULL, 0 }, /* Sm */ 275 { NULL, 0 }, /* Sx */ 276 { NULL, TYPE_Sy }, /* Sy */ 277 { NULL, TYPE_Tn }, /* Tn */ 278 { NULL, 0 }, /* Ux */ 279 { NULL, 0 }, /* Xc */ 280 { NULL, 0 }, /* Xo */ 281 { parse_mdoc_head, 0 }, /* Fo */ 282 { NULL, 0 }, /* Fc */ 283 { NULL, 0 }, /* Oo */ 284 { NULL, 0 }, /* Oc */ 285 { NULL, 0 }, /* Bk */ 286 { NULL, 0 }, /* Ek */ 287 { NULL, 0 }, /* Bt */ 288 { NULL, 0 }, /* Hf */ 289 { NULL, 0 }, /* Fr */ 290 { NULL, 0 }, /* Ud */ 291 { NULL, TYPE_Lb }, /* Lb */ 292 { NULL, 0 }, /* Lp */ 293 { NULL, TYPE_Lk }, /* Lk */ 294 { NULL, TYPE_Mt }, /* Mt */ 295 { NULL, 0 }, /* Brq */ 296 { NULL, 0 }, /* Bro */ 297 { NULL, 0 }, /* Brc */ 298 { NULL, 0 }, /* %C */ 299 { NULL, 0 }, /* Es */ 300 { NULL, 0 }, /* En */ 301 { NULL, TYPE_Dx }, /* Dx */ 302 { NULL, 0 }, /* %Q */ 303 { NULL, 0 }, /* br */ 304 { NULL, 0 }, /* sp */ 305 { NULL, 0 }, /* %U */ 306 { NULL, 0 }, /* Ta */ 307 }; 308 309 int 310 main(int argc, char *argv[]) 311 { 312 int ch, i; 313 size_t j, sz; 314 const char *path_arg; 315 struct mchars *mc; 316 struct manpaths dirs; 317 struct mparse *mp; 318 struct ohash_info mpages_info, mlinks_info; 319 320 memset(stmts, 0, STMT__MAX * sizeof(sqlite3_stmt *)); 321 memset(&dirs, 0, sizeof(struct manpaths)); 322 323 mpages_info.alloc = mlinks_info.alloc = hash_alloc; 324 mpages_info.halloc = mlinks_info.halloc = hash_halloc; 325 mpages_info.hfree = mlinks_info.hfree = hash_free; 326 327 mpages_info.key_offset = offsetof(struct mpage, inodev); 328 mlinks_info.key_offset = offsetof(struct mlink, file); 329 330 progname = strrchr(argv[0], '/'); 331 if (progname == NULL) 332 progname = argv[0]; 333 else 334 ++progname; 335 336 /* 337 * We accept a few different invocations. 338 * The CHECKOP macro makes sure that invocation styles don't 339 * clobber each other. 340 */ 341 #define CHECKOP(_op, _ch) do \ 342 if (OP_DEFAULT != (_op)) { \ 343 fprintf(stderr, "-%c: Conflicting option\n", (_ch)); \ 344 goto usage; \ 345 } while (/*CONSTCOND*/0) 346 347 path_arg = NULL; 348 op = OP_DEFAULT; 349 350 while (-1 != (ch = getopt(argc, argv, "aC:d:nT:tu:vW"))) 351 switch (ch) { 352 case ('a'): 353 use_all = 1; 354 break; 355 case ('C'): 356 CHECKOP(op, ch); 357 path_arg = optarg; 358 op = OP_CONFFILE; 359 break; 360 case ('d'): 361 CHECKOP(op, ch); 362 path_arg = optarg; 363 op = OP_UPDATE; 364 break; 365 case ('n'): 366 nodb = 1; 367 break; 368 case ('T'): 369 if (strcmp(optarg, "utf8")) { 370 fprintf(stderr, "-T%s: Unsupported " 371 "output format\n", optarg); 372 goto usage; 373 } 374 write_utf8 = 1; 375 break; 376 case ('t'): 377 CHECKOP(op, ch); 378 dup2(STDOUT_FILENO, STDERR_FILENO); 379 op = OP_TEST; 380 nodb = warnings = 1; 381 break; 382 case ('u'): 383 CHECKOP(op, ch); 384 path_arg = optarg; 385 op = OP_DELETE; 386 break; 387 case ('v'): 388 verb++; 389 break; 390 case ('W'): 391 warnings = 1; 392 break; 393 default: 394 goto usage; 395 } 396 397 argc -= optind; 398 argv += optind; 399 400 if (OP_CONFFILE == op && argc > 0) { 401 fprintf(stderr, "-C: Too many arguments\n"); 402 goto usage; 403 } 404 405 exitcode = (int)MANDOCLEVEL_OK; 406 mp = mparse_alloc(MPARSE_AUTO, 407 MANDOCLEVEL_FATAL, NULL, NULL, NULL); 408 mc = mchars_alloc(); 409 410 ohash_init(&mpages, 6, &mpages_info); 411 ohash_init(&mlinks, 6, &mlinks_info); 412 413 if (OP_UPDATE == op || OP_DELETE == op || OP_TEST == op) { 414 /* 415 * Force processing all files. 416 */ 417 use_all = 1; 418 419 /* 420 * All of these deal with a specific directory. 421 * Jump into that directory then collect files specified 422 * on the command-line. 423 */ 424 if (0 == set_basedir(path_arg)) 425 goto out; 426 for (i = 0; i < argc; i++) 427 filescan(argv[i]); 428 if (0 == dbopen(1)) 429 goto out; 430 if (OP_TEST != op) 431 dbprune(); 432 if (OP_DELETE != op) 433 mpages_merge(mc, mp); 434 dbclose(1); 435 } else { 436 /* 437 * If we have arguments, use them as our manpaths. 438 * If we don't, grok from manpath(1) or however else 439 * manpath_parse() wants to do it. 440 */ 441 if (argc > 0) { 442 dirs.paths = mandoc_calloc 443 (argc, sizeof(char *)); 444 dirs.sz = (size_t)argc; 445 for (i = 0; i < argc; i++) 446 dirs.paths[i] = mandoc_strdup(argv[i]); 447 } else 448 manpath_parse(&dirs, path_arg, NULL, NULL); 449 450 /* 451 * First scan the tree rooted at a base directory, then 452 * build a new database and finally move it into place. 453 * Ignore zero-length directories and strip trailing 454 * slashes. 455 */ 456 for (j = 0; j < dirs.sz; j++) { 457 sz = strlen(dirs.paths[j]); 458 if (sz && '/' == dirs.paths[j][sz - 1]) 459 dirs.paths[j][--sz] = '\0'; 460 if (0 == sz) 461 continue; 462 463 if (j) { 464 ohash_init(&mpages, 6, &mpages_info); 465 ohash_init(&mlinks, 6, &mlinks_info); 466 } 467 468 if (0 == set_basedir(dirs.paths[j])) 469 goto out; 470 if (0 == treescan()) 471 goto out; 472 if (0 == set_basedir(dirs.paths[j])) 473 goto out; 474 if (0 == dbopen(0)) 475 goto out; 476 477 mpages_merge(mc, mp); 478 dbclose(0); 479 480 if (j + 1 < dirs.sz) { 481 mpages_free(); 482 ohash_delete(&mpages); 483 ohash_delete(&mlinks); 484 } 485 } 486 } 487 out: 488 set_basedir(NULL); 489 manpath_free(&dirs); 490 mchars_free(mc); 491 mparse_free(mp); 492 mpages_free(); 493 ohash_delete(&mpages); 494 ohash_delete(&mlinks); 495 return(exitcode); 496 usage: 497 fprintf(stderr, "usage: %s [-anvW] [-C file] [-Tutf8]\n" 498 " %s [-anvW] [-Tutf8] dir ...\n" 499 " %s [-nvW] [-Tutf8] -d dir [file ...]\n" 500 " %s [-nvW] -u dir [file ...]\n" 501 " %s -t file ...\n", 502 progname, progname, progname, 503 progname, progname); 504 505 return((int)MANDOCLEVEL_BADARG); 506 } 507 508 /* 509 * Scan a directory tree rooted at "basedir" for manpages. 510 * We use fts(), scanning directory parts along the way for clues to our 511 * section and architecture. 512 * 513 * If use_all has been specified, grok all files. 514 * If not, sanitise paths to the following: 515 * 516 * [./]man*[/<arch>]/<name>.<section> 517 * or 518 * [./]cat<section>[/<arch>]/<name>.0 519 * 520 * TODO: accomodate for multi-language directories. 521 */ 522 static int 523 treescan(void) 524 { 525 FTS *f; 526 FTSENT *ff; 527 struct mlink *mlink; 528 int dform; 529 char *dsec, *arch, *fsec, *cp; 530 const char *path; 531 const char *argv[2]; 532 533 argv[0] = "."; 534 argv[1] = (char *)NULL; 535 536 /* 537 * Walk through all components under the directory, using the 538 * logical descent of files. 539 */ 540 f = fts_open((char * const *)argv, FTS_LOGICAL, NULL); 541 if (NULL == f) { 542 exitcode = (int)MANDOCLEVEL_SYSERR; 543 say("", NULL); 544 return(0); 545 } 546 547 dsec = arch = NULL; 548 dform = FORM_NONE; 549 550 while (NULL != (ff = fts_read(f))) { 551 path = ff->fts_path + 2; 552 /* 553 * If we're a regular file, add an mlink by using the 554 * stored directory data and handling the filename. 555 */ 556 if (FTS_F == ff->fts_info) { 557 if (0 == strcmp(path, MANDOC_DB)) 558 continue; 559 if ( ! use_all && ff->fts_level < 2) { 560 if (warnings) 561 say(path, "Extraneous file"); 562 continue; 563 } else if (NULL == (fsec = 564 strrchr(ff->fts_name, '.'))) { 565 if ( ! use_all) { 566 if (warnings) 567 say(path, 568 "No filename suffix"); 569 continue; 570 } 571 } else if (0 == strcmp(++fsec, "html")) { 572 if (warnings) 573 say(path, "Skip html"); 574 continue; 575 } else if (0 == strcmp(fsec, "gz")) { 576 if (warnings) 577 say(path, "Skip gz"); 578 continue; 579 } else if (0 == strcmp(fsec, "ps")) { 580 if (warnings) 581 say(path, "Skip ps"); 582 continue; 583 } else if (0 == strcmp(fsec, "pdf")) { 584 if (warnings) 585 say(path, "Skip pdf"); 586 continue; 587 } else if ( ! use_all && 588 ((FORM_SRC == dform && strcmp(fsec, dsec)) || 589 (FORM_CAT == dform && strcmp(fsec, "0")))) { 590 if (warnings) 591 say(path, "Wrong filename suffix"); 592 continue; 593 } else 594 fsec[-1] = '\0'; 595 596 mlink = mandoc_calloc(1, sizeof(struct mlink)); 597 strlcpy(mlink->file, path, sizeof(mlink->file)); 598 mlink->dform = dform; 599 mlink->dsec = dsec; 600 mlink->arch = arch; 601 mlink->name = ff->fts_name; 602 mlink->fsec = fsec; 603 mlink_add(mlink, ff->fts_statp); 604 continue; 605 } else if (FTS_D != ff->fts_info && 606 FTS_DP != ff->fts_info) { 607 if (warnings) 608 say(path, "Not a regular file"); 609 continue; 610 } 611 612 switch (ff->fts_level) { 613 case (0): 614 /* Ignore the root directory. */ 615 break; 616 case (1): 617 /* 618 * This might contain manX/ or catX/. 619 * Try to infer this from the name. 620 * If we're not in use_all, enforce it. 621 */ 622 cp = ff->fts_name; 623 if (FTS_DP == ff->fts_info) 624 break; 625 626 if (0 == strncmp(cp, "man", 3)) { 627 dform = FORM_SRC; 628 dsec = cp + 3; 629 } else if (0 == strncmp(cp, "cat", 3)) { 630 dform = FORM_CAT; 631 dsec = cp + 3; 632 } else { 633 dform = FORM_NONE; 634 dsec = NULL; 635 } 636 637 if (NULL != dsec || use_all) 638 break; 639 640 if (warnings) 641 say(path, "Unknown directory part"); 642 fts_set(f, ff, FTS_SKIP); 643 break; 644 case (2): 645 /* 646 * Possibly our architecture. 647 * If we're descending, keep tabs on it. 648 */ 649 if (FTS_DP != ff->fts_info && NULL != dsec) 650 arch = ff->fts_name; 651 else 652 arch = NULL; 653 break; 654 default: 655 if (FTS_DP == ff->fts_info || use_all) 656 break; 657 if (warnings) 658 say(path, "Extraneous directory part"); 659 fts_set(f, ff, FTS_SKIP); 660 break; 661 } 662 } 663 664 fts_close(f); 665 return(1); 666 } 667 668 /* 669 * Add a file to the mlinks table. 670 * Do not verify that it's a "valid" looking manpage (we'll do that 671 * later). 672 * 673 * Try to infer the manual section, architecture, and page name from the 674 * path, assuming it looks like 675 * 676 * [./]man*[/<arch>]/<name>.<section> 677 * or 678 * [./]cat<section>[/<arch>]/<name>.0 679 * 680 * See treescan() for the fts(3) version of this. 681 */ 682 static void 683 filescan(const char *file) 684 { 685 char buf[PATH_MAX]; 686 struct stat st; 687 struct mlink *mlink; 688 char *p, *start; 689 690 assert(use_all); 691 692 if (0 == strncmp(file, "./", 2)) 693 file += 2; 694 695 if (NULL == realpath(file, buf)) { 696 exitcode = (int)MANDOCLEVEL_BADARG; 697 say(file, NULL); 698 return; 699 } else if (OP_TEST != op && strstr(buf, basedir) != buf) { 700 exitcode = (int)MANDOCLEVEL_BADARG; 701 say("", "%s: outside base directory", buf); 702 return; 703 } else if (-1 == stat(buf, &st)) { 704 exitcode = (int)MANDOCLEVEL_BADARG; 705 say(file, NULL); 706 return; 707 } else if ( ! (S_IFREG & st.st_mode)) { 708 exitcode = (int)MANDOCLEVEL_BADARG; 709 say(file, "Not a regular file"); 710 return; 711 } 712 start = buf + strlen(basedir); 713 mlink = mandoc_calloc(1, sizeof(struct mlink)); 714 strlcpy(mlink->file, start, sizeof(mlink->file)); 715 716 /* 717 * First try to guess our directory structure. 718 * If we find a separator, try to look for man* or cat*. 719 * If we find one of these and what's underneath is a directory, 720 * assume it's an architecture. 721 */ 722 if (NULL != (p = strchr(start, '/'))) { 723 *p++ = '\0'; 724 if (0 == strncmp(start, "man", 3)) { 725 mlink->dform = FORM_SRC; 726 mlink->dsec = start + 3; 727 } else if (0 == strncmp(start, "cat", 3)) { 728 mlink->dform = FORM_CAT; 729 mlink->dsec = start + 3; 730 } 731 732 start = p; 733 if (NULL != mlink->dsec && NULL != (p = strchr(start, '/'))) { 734 *p++ = '\0'; 735 mlink->arch = start; 736 start = p; 737 } 738 } 739 740 /* 741 * Now check the file suffix. 742 * Suffix of `.0' indicates a catpage, `.1-9' is a manpage. 743 */ 744 p = strrchr(start, '\0'); 745 while (p-- > start && '/' != *p && '.' != *p) 746 /* Loop. */ ; 747 748 if ('.' == *p) { 749 *p++ = '\0'; 750 mlink->fsec = p; 751 } 752 753 /* 754 * Now try to parse the name. 755 * Use the filename portion of the path. 756 */ 757 mlink->name = start; 758 if (NULL != (p = strrchr(start, '/'))) { 759 mlink->name = p + 1; 760 *p = '\0'; 761 } 762 mlink_add(mlink, &st); 763 } 764 765 static void 766 mlink_add(struct mlink *mlink, const struct stat *st) 767 { 768 struct inodev inodev; 769 struct mpage *mpage; 770 unsigned int slot; 771 772 assert(NULL != mlink->file); 773 774 mlink->dsec = mandoc_strdup(mlink->dsec ? mlink->dsec : ""); 775 mlink->arch = mandoc_strdup(mlink->arch ? mlink->arch : ""); 776 mlink->name = mandoc_strdup(mlink->name ? mlink->name : ""); 777 mlink->fsec = mandoc_strdup(mlink->fsec ? mlink->fsec : ""); 778 779 if ('0' == *mlink->fsec) { 780 free(mlink->fsec); 781 mlink->fsec = mandoc_strdup(mlink->dsec); 782 mlink->fform = FORM_CAT; 783 } else if ('1' <= *mlink->fsec && '9' >= *mlink->fsec) 784 mlink->fform = FORM_SRC; 785 else 786 mlink->fform = FORM_NONE; 787 788 slot = ohash_qlookup(&mlinks, mlink->file); 789 assert(NULL == ohash_find(&mlinks, slot)); 790 ohash_insert(&mlinks, slot, mlink); 791 792 inodev.st_ino = st->st_ino; 793 inodev.st_dev = st->st_dev; 794 slot = ohash_lookup_memory(&mpages, (char *)&inodev, 795 sizeof(struct inodev), inodev.st_ino); 796 mpage = ohash_find(&mpages, slot); 797 if (NULL == mpage) { 798 mpage = mandoc_calloc(1, sizeof(struct mpage)); 799 mpage->inodev.st_ino = inodev.st_ino; 800 mpage->inodev.st_dev = inodev.st_dev; 801 ohash_insert(&mpages, slot, mpage); 802 } else 803 mlink->next = mpage->mlinks; 804 mpage->mlinks = mlink; 805 } 806 807 static void 808 mlink_free(struct mlink *mlink) 809 { 810 811 free(mlink->dsec); 812 free(mlink->arch); 813 free(mlink->name); 814 free(mlink->fsec); 815 free(mlink); 816 } 817 818 static void 819 mpages_free(void) 820 { 821 struct mpage *mpage; 822 struct mlink *mlink; 823 unsigned int slot; 824 825 mpage = ohash_first(&mpages, &slot); 826 while (NULL != mpage) { 827 while (NULL != (mlink = mpage->mlinks)) { 828 mpage->mlinks = mlink->next; 829 mlink_free(mlink); 830 } 831 free(mpage->sec); 832 free(mpage->arch); 833 free(mpage->title); 834 free(mpage->desc); 835 free(mpage); 836 mpage = ohash_next(&mpages, &slot); 837 } 838 } 839 840 /* 841 * For each mlink to the mpage, check whether the path looks like 842 * it is formatted, and if it does, check whether a source manual 843 * exists by the same name, ignoring the suffix. 844 * If both conditions hold, drop the mlink. 845 */ 846 static void 847 mlinks_undupe(struct mpage *mpage) 848 { 849 char buf[PATH_MAX]; 850 struct mlink **prev; 851 struct mlink *mlink; 852 char *bufp; 853 854 mpage->form = FORM_CAT; 855 prev = &mpage->mlinks; 856 while (NULL != (mlink = *prev)) { 857 if (FORM_CAT != mlink->dform) { 858 mpage->form = FORM_NONE; 859 goto nextlink; 860 } 861 if (strlcpy(buf, mlink->file, PATH_MAX) >= PATH_MAX) { 862 if (warnings) 863 say(mlink->file, "Filename too long"); 864 goto nextlink; 865 } 866 bufp = strstr(buf, "cat"); 867 assert(NULL != bufp); 868 memcpy(bufp, "man", 3); 869 if (NULL != (bufp = strrchr(buf, '.'))) 870 *++bufp = '\0'; 871 strlcat(buf, mlink->dsec, PATH_MAX); 872 if (NULL == ohash_find(&mlinks, 873 ohash_qlookup(&mlinks, buf))) 874 goto nextlink; 875 if (warnings) 876 say(mlink->file, "Man source exists: %s", buf); 877 if (use_all) 878 goto nextlink; 879 *prev = mlink->next; 880 mlink_free(mlink); 881 continue; 882 nextlink: 883 prev = &(*prev)->next; 884 } 885 } 886 887 static int 888 mlink_check(struct mpage *mpage, struct mlink *mlink) 889 { 890 int match; 891 892 match = 1; 893 894 /* 895 * Check whether the manual section given in a file 896 * agrees with the directory where the file is located. 897 * Some manuals have suffixes like (3p) on their 898 * section number either inside the file or in the 899 * directory name, some are linked into more than one 900 * section, like encrypt(1) = makekey(8). 901 */ 902 903 if (FORM_SRC == mpage->form && 904 strcasecmp(mpage->sec, mlink->dsec)) { 905 match = 0; 906 say(mlink->file, "Section \"%s\" manual in %s directory", 907 mpage->sec, mlink->dsec); 908 } 909 910 /* 911 * Manual page directories exist for each kernel 912 * architecture as returned by machine(1). 913 * However, many manuals only depend on the 914 * application architecture as returned by arch(1). 915 * For example, some (2/ARM) manuals are shared 916 * across the "armish" and "zaurus" kernel 917 * architectures. 918 * A few manuals are even shared across completely 919 * different architectures, for example fdformat(1) 920 * on amd64, i386, sparc, and sparc64. 921 */ 922 923 if (strcasecmp(mpage->arch, mlink->arch)) { 924 match = 0; 925 say(mlink->file, "Architecture \"%s\" manual in " 926 "\"%s\" directory", mpage->arch, mlink->arch); 927 } 928 929 if (strcasecmp(mpage->title, mlink->name)) 930 match = 0; 931 932 return(match); 933 } 934 935 /* 936 * Run through the files in the global vector "mpages" 937 * and add them to the database specified in "basedir". 938 * 939 * This handles the parsing scheme itself, using the cues of directory 940 * and filename to determine whether the file is parsable or not. 941 */ 942 static void 943 mpages_merge(struct mchars *mc, struct mparse *mp) 944 { 945 struct ohash_info str_info; 946 struct mpage *mpage; 947 struct mlink *mlink; 948 struct mdoc *mdoc; 949 struct man *man; 950 const char *cp; 951 int match; 952 unsigned int pslot; 953 enum mandoclevel lvl; 954 955 str_info.alloc = hash_alloc; 956 str_info.halloc = hash_halloc; 957 str_info.hfree = hash_free; 958 str_info.key_offset = offsetof(struct str, key); 959 960 mpage = ohash_first(&mpages, &pslot); 961 while (NULL != mpage) { 962 mlinks_undupe(mpage); 963 if (NULL == mpage->mlinks) { 964 mpage = ohash_next(&mpages, &pslot); 965 continue; 966 } 967 968 ohash_init(&strings, 6, &str_info); 969 mparse_reset(mp); 970 mdoc = NULL; 971 man = NULL; 972 973 /* 974 * Try interpreting the file as mdoc(7) or man(7) 975 * source code, unless it is already known to be 976 * formatted. Fall back to formatted mode. 977 */ 978 if (FORM_CAT != mpage->mlinks->dform || 979 FORM_CAT != mpage->mlinks->fform) { 980 lvl = mparse_readfd(mp, -1, mpage->mlinks->file); 981 if (lvl < MANDOCLEVEL_FATAL) 982 mparse_result(mp, &mdoc, &man); 983 } 984 985 if (NULL != mdoc) { 986 mpage->form = FORM_SRC; 987 mpage->sec = 988 mandoc_strdup(mdoc_meta(mdoc)->msec); 989 mpage->arch = mdoc_meta(mdoc)->arch; 990 mpage->arch = mandoc_strdup( 991 NULL == mpage->arch ? "" : mpage->arch); 992 mpage->title = 993 mandoc_strdup(mdoc_meta(mdoc)->title); 994 } else if (NULL != man) { 995 mpage->form = FORM_SRC; 996 mpage->sec = 997 mandoc_strdup(man_meta(man)->msec); 998 mpage->arch = 999 mandoc_strdup(mpage->mlinks->arch); 1000 mpage->title = 1001 mandoc_strdup(man_meta(man)->title); 1002 } else { 1003 mpage->form = FORM_CAT; 1004 mpage->sec = 1005 mandoc_strdup(mpage->mlinks->dsec); 1006 mpage->arch = 1007 mandoc_strdup(mpage->mlinks->arch); 1008 mpage->title = 1009 mandoc_strdup(mpage->mlinks->name); 1010 } 1011 putkey(mpage, mpage->sec, TYPE_sec); 1012 putkey(mpage, '\0' == *mpage->arch ? 1013 "any" : mpage->arch, TYPE_arch); 1014 1015 for (mlink = mpage->mlinks; mlink; mlink = mlink->next) { 1016 if ('\0' != *mlink->dsec) 1017 putkey(mpage, mlink->dsec, TYPE_sec); 1018 if ('\0' != *mlink->fsec) 1019 putkey(mpage, mlink->fsec, TYPE_sec); 1020 putkey(mpage, '\0' == *mlink->arch ? 1021 "any" : mlink->arch, TYPE_arch); 1022 putkey(mpage, mlink->name, TYPE_Nm); 1023 } 1024 1025 if (warnings && !use_all) { 1026 match = 0; 1027 for (mlink = mpage->mlinks; mlink; 1028 mlink = mlink->next) 1029 if (mlink_check(mpage, mlink)) 1030 match = 1; 1031 } else 1032 match = 1; 1033 1034 if (NULL != mdoc) { 1035 if (NULL != (cp = mdoc_meta(mdoc)->name)) 1036 putkey(mpage, cp, TYPE_Nm); 1037 assert(NULL == mpage->desc); 1038 parse_mdoc(mpage, mdoc_node(mdoc)); 1039 putkey(mpage, NULL != mpage->desc ? 1040 mpage->desc : mpage->mlinks->name, TYPE_Nd); 1041 } else if (NULL != man) 1042 parse_man(mpage, man_node(man)); 1043 else 1044 parse_cat(mpage); 1045 1046 dbindex(mpage, mc); 1047 ohash_delete(&strings); 1048 mpage = ohash_next(&mpages, &pslot); 1049 } 1050 } 1051 1052 static void 1053 parse_cat(struct mpage *mpage) 1054 { 1055 FILE *stream; 1056 char *line, *p, *title; 1057 size_t len, plen, titlesz; 1058 1059 if (NULL == (stream = fopen(mpage->mlinks->file, "r"))) { 1060 if (warnings) 1061 say(mpage->mlinks->file, NULL); 1062 return; 1063 } 1064 1065 /* Skip to first blank line. */ 1066 1067 while (NULL != (line = fgetln(stream, &len))) 1068 if ('\n' == *line) 1069 break; 1070 1071 /* 1072 * Assume the first line that is not indented 1073 * is the first section header. Skip to it. 1074 */ 1075 1076 while (NULL != (line = fgetln(stream, &len))) 1077 if ('\n' != *line && ' ' != *line) 1078 break; 1079 1080 /* 1081 * Read up until the next section into a buffer. 1082 * Strip the leading and trailing newline from each read line, 1083 * appending a trailing space. 1084 * Ignore empty (whitespace-only) lines. 1085 */ 1086 1087 titlesz = 0; 1088 title = NULL; 1089 1090 while (NULL != (line = fgetln(stream, &len))) { 1091 if (' ' != *line || '\n' != line[len - 1]) 1092 break; 1093 while (len > 0 && isspace((unsigned char)*line)) { 1094 line++; 1095 len--; 1096 } 1097 if (1 == len) 1098 continue; 1099 title = mandoc_realloc(title, titlesz + len); 1100 memcpy(title + titlesz, line, len); 1101 titlesz += len; 1102 title[titlesz - 1] = ' '; 1103 } 1104 1105 /* 1106 * If no page content can be found, or the input line 1107 * is already the next section header, or there is no 1108 * trailing newline, reuse the page title as the page 1109 * description. 1110 */ 1111 1112 if (NULL == title || '\0' == *title) { 1113 if (warnings) 1114 say(mpage->mlinks->file, 1115 "Cannot find NAME section"); 1116 assert(NULL == mpage->desc); 1117 mpage->desc = mandoc_strdup(mpage->mlinks->name); 1118 putkey(mpage, mpage->mlinks->name, TYPE_Nd); 1119 fclose(stream); 1120 free(title); 1121 return; 1122 } 1123 1124 title = mandoc_realloc(title, titlesz + 1); 1125 title[titlesz] = '\0'; 1126 1127 /* 1128 * Skip to the first dash. 1129 * Use the remaining line as the description (no more than 70 1130 * bytes). 1131 */ 1132 1133 if (NULL != (p = strstr(title, "- "))) { 1134 for (p += 2; ' ' == *p || '\b' == *p; p++) 1135 /* Skip to next word. */ ; 1136 } else { 1137 if (warnings) 1138 say(mpage->mlinks->file, 1139 "No dash in title line"); 1140 p = title; 1141 } 1142 1143 plen = strlen(p); 1144 1145 /* Strip backspace-encoding from line. */ 1146 1147 while (NULL != (line = memchr(p, '\b', plen))) { 1148 len = line - p; 1149 if (0 == len) { 1150 memmove(line, line + 1, plen--); 1151 continue; 1152 } 1153 memmove(line - 1, line + 1, plen - len); 1154 plen -= 2; 1155 } 1156 1157 assert(NULL == mpage->desc); 1158 mpage->desc = mandoc_strdup(p); 1159 putkey(mpage, mpage->desc, TYPE_Nd); 1160 fclose(stream); 1161 free(title); 1162 } 1163 1164 /* 1165 * Put a type/word pair into the word database for this particular file. 1166 */ 1167 static void 1168 putkey(const struct mpage *mpage, const char *value, uint64_t type) 1169 { 1170 1171 assert(NULL != value); 1172 putkeys(mpage, value, strlen(value), type); 1173 } 1174 1175 /* 1176 * Grok all nodes at or below a certain mdoc node into putkey(). 1177 */ 1178 static void 1179 putmdockey(const struct mpage *mpage, 1180 const struct mdoc_node *n, uint64_t m) 1181 { 1182 1183 for ( ; NULL != n; n = n->next) { 1184 if (NULL != n->child) 1185 putmdockey(mpage, n->child, m); 1186 if (MDOC_TEXT == n->type) 1187 putkey(mpage, n->string, m); 1188 } 1189 } 1190 1191 static void 1192 parse_man(struct mpage *mpage, const struct man_node *n) 1193 { 1194 const struct man_node *head, *body; 1195 char *start, *sv, *title; 1196 char byte; 1197 size_t sz, titlesz; 1198 1199 if (NULL == n) 1200 return; 1201 1202 /* 1203 * We're only searching for one thing: the first text child in 1204 * the BODY of a NAME section. Since we don't keep track of 1205 * sections in -man, run some hoops to find out whether we're in 1206 * the correct section or not. 1207 */ 1208 1209 if (MAN_BODY == n->type && MAN_SH == n->tok) { 1210 body = n; 1211 assert(body->parent); 1212 if (NULL != (head = body->parent->head) && 1213 1 == head->nchild && 1214 NULL != (head = (head->child)) && 1215 MAN_TEXT == head->type && 1216 0 == strcmp(head->string, "NAME") && 1217 NULL != (body = body->child) && 1218 MAN_TEXT == body->type) { 1219 1220 title = NULL; 1221 titlesz = 0; 1222 1223 /* 1224 * Suck the entire NAME section into memory. 1225 * Yes, we might run away. 1226 * But too many manuals have big, spread-out 1227 * NAME sections over many lines. 1228 */ 1229 1230 for ( ; NULL != body; body = body->next) { 1231 if (MAN_TEXT != body->type) 1232 break; 1233 if (0 == (sz = strlen(body->string))) 1234 continue; 1235 title = mandoc_realloc 1236 (title, titlesz + sz + 1); 1237 memcpy(title + titlesz, body->string, sz); 1238 titlesz += sz + 1; 1239 title[titlesz - 1] = ' '; 1240 } 1241 if (NULL == title) 1242 return; 1243 1244 title = mandoc_realloc(title, titlesz + 1); 1245 title[titlesz] = '\0'; 1246 1247 /* Skip leading space. */ 1248 1249 sv = title; 1250 while (isspace((unsigned char)*sv)) 1251 sv++; 1252 1253 if (0 == (sz = strlen(sv))) { 1254 free(title); 1255 return; 1256 } 1257 1258 /* Erase trailing space. */ 1259 1260 start = &sv[sz - 1]; 1261 while (start > sv && isspace((unsigned char)*start)) 1262 *start-- = '\0'; 1263 1264 if (start == sv) { 1265 free(title); 1266 return; 1267 } 1268 1269 start = sv; 1270 1271 /* 1272 * Go through a special heuristic dance here. 1273 * Conventionally, one or more manual names are 1274 * comma-specified prior to a whitespace, then a 1275 * dash, then a description. Try to puzzle out 1276 * the name parts here. 1277 */ 1278 1279 for ( ;; ) { 1280 sz = strcspn(start, " ,"); 1281 if ('\0' == start[sz]) 1282 break; 1283 1284 byte = start[sz]; 1285 start[sz] = '\0'; 1286 1287 putkey(mpage, start, TYPE_Nm); 1288 1289 if (' ' == byte) { 1290 start += sz + 1; 1291 break; 1292 } 1293 1294 assert(',' == byte); 1295 start += sz + 1; 1296 while (' ' == *start) 1297 start++; 1298 } 1299 1300 if (sv == start) { 1301 putkey(mpage, start, TYPE_Nm); 1302 free(title); 1303 return; 1304 } 1305 1306 while (isspace((unsigned char)*start)) 1307 start++; 1308 1309 if (0 == strncmp(start, "-", 1)) 1310 start += 1; 1311 else if (0 == strncmp(start, "\\-\\-", 4)) 1312 start += 4; 1313 else if (0 == strncmp(start, "\\-", 2)) 1314 start += 2; 1315 else if (0 == strncmp(start, "\\(en", 4)) 1316 start += 4; 1317 else if (0 == strncmp(start, "\\(em", 4)) 1318 start += 4; 1319 1320 while (' ' == *start) 1321 start++; 1322 1323 assert(NULL == mpage->desc); 1324 mpage->desc = mandoc_strdup(start); 1325 putkey(mpage, mpage->desc, TYPE_Nd); 1326 free(title); 1327 return; 1328 } 1329 } 1330 1331 for (n = n->child; n; n = n->next) { 1332 if (NULL != mpage->desc) 1333 break; 1334 parse_man(mpage, n); 1335 } 1336 } 1337 1338 static void 1339 parse_mdoc(struct mpage *mpage, const struct mdoc_node *n) 1340 { 1341 1342 assert(NULL != n); 1343 for (n = n->child; NULL != n; n = n->next) { 1344 switch (n->type) { 1345 case (MDOC_ELEM): 1346 /* FALLTHROUGH */ 1347 case (MDOC_BLOCK): 1348 /* FALLTHROUGH */ 1349 case (MDOC_HEAD): 1350 /* FALLTHROUGH */ 1351 case (MDOC_BODY): 1352 /* FALLTHROUGH */ 1353 case (MDOC_TAIL): 1354 if (NULL != mdocs[n->tok].fp) 1355 if (0 == (*mdocs[n->tok].fp)(mpage, n)) 1356 break; 1357 if (mdocs[n->tok].mask) 1358 putmdockey(mpage, n->child, 1359 mdocs[n->tok].mask); 1360 break; 1361 default: 1362 assert(MDOC_ROOT != n->type); 1363 continue; 1364 } 1365 if (NULL != n->child) 1366 parse_mdoc(mpage, n); 1367 } 1368 } 1369 1370 static int 1371 parse_mdoc_Fd(struct mpage *mpage, const struct mdoc_node *n) 1372 { 1373 const char *start, *end; 1374 size_t sz; 1375 1376 if (SEC_SYNOPSIS != n->sec || 1377 NULL == (n = n->child) || 1378 MDOC_TEXT != n->type) 1379 return(0); 1380 1381 /* 1382 * Only consider those `Fd' macro fields that begin with an 1383 * "inclusion" token (versus, e.g., #define). 1384 */ 1385 1386 if (strcmp("#include", n->string)) 1387 return(0); 1388 1389 if (NULL == (n = n->next) || MDOC_TEXT != n->type) 1390 return(0); 1391 1392 /* 1393 * Strip away the enclosing angle brackets and make sure we're 1394 * not zero-length. 1395 */ 1396 1397 start = n->string; 1398 if ('<' == *start || '"' == *start) 1399 start++; 1400 1401 if (0 == (sz = strlen(start))) 1402 return(0); 1403 1404 end = &start[(int)sz - 1]; 1405 if ('>' == *end || '"' == *end) 1406 end--; 1407 1408 if (end > start) 1409 putkeys(mpage, start, end - start + 1, TYPE_In); 1410 return(0); 1411 } 1412 1413 static int 1414 parse_mdoc_Fn(struct mpage *mpage, const struct mdoc_node *n) 1415 { 1416 const char *cp; 1417 1418 if (NULL == (n = n->child) || MDOC_TEXT != n->type) 1419 return(0); 1420 1421 /* 1422 * Parse: .Fn "struct type *name" "char *arg". 1423 * First strip away pointer symbol. 1424 * Then store the function name, then type. 1425 * Finally, store the arguments. 1426 */ 1427 1428 if (NULL == (cp = strrchr(n->string, ' '))) 1429 cp = n->string; 1430 1431 while ('*' == *cp) 1432 cp++; 1433 1434 putkey(mpage, cp, TYPE_Fn); 1435 1436 if (n->string < cp) 1437 putkeys(mpage, n->string, cp - n->string, TYPE_Ft); 1438 1439 for (n = n->next; NULL != n; n = n->next) 1440 if (MDOC_TEXT == n->type) 1441 putkey(mpage, n->string, TYPE_Fa); 1442 1443 return(0); 1444 } 1445 1446 static int 1447 parse_mdoc_Xr(struct mpage *mpage, const struct mdoc_node *n) 1448 { 1449 char *cp; 1450 1451 if (NULL == (n = n->child)) 1452 return(0); 1453 1454 if (NULL == n->next) { 1455 putkey(mpage, n->string, TYPE_Xr); 1456 return(0); 1457 } 1458 1459 if (-1 == asprintf(&cp, "%s(%s)", n->string, n->next->string)) { 1460 perror(NULL); 1461 exit((int)MANDOCLEVEL_SYSERR); 1462 } 1463 putkey(mpage, cp, TYPE_Xr); 1464 free(cp); 1465 return(0); 1466 } 1467 1468 static int 1469 parse_mdoc_Nd(struct mpage *mpage, const struct mdoc_node *n) 1470 { 1471 size_t sz; 1472 1473 if (MDOC_BODY != n->type) 1474 return(0); 1475 1476 /* 1477 * Special-case the `Nd' because we need to put the description 1478 * into the document table. 1479 */ 1480 1481 for (n = n->child; NULL != n; n = n->next) { 1482 if (MDOC_TEXT == n->type) { 1483 if (NULL != mpage->desc) { 1484 sz = strlen(mpage->desc) + 1485 strlen(n->string) + 2; 1486 mpage->desc = mandoc_realloc( 1487 mpage->desc, sz); 1488 strlcat(mpage->desc, " ", sz); 1489 strlcat(mpage->desc, n->string, sz); 1490 } else 1491 mpage->desc = mandoc_strdup(n->string); 1492 } 1493 if (NULL != n->child) 1494 parse_mdoc_Nd(mpage, n); 1495 } 1496 return(1); 1497 } 1498 1499 static int 1500 parse_mdoc_Nm(struct mpage *mpage, const struct mdoc_node *n) 1501 { 1502 1503 return(SEC_NAME == n->sec || 1504 (SEC_SYNOPSIS == n->sec && MDOC_HEAD == n->type)); 1505 } 1506 1507 static int 1508 parse_mdoc_Sh(struct mpage *mpage, const struct mdoc_node *n) 1509 { 1510 1511 return(SEC_CUSTOM == n->sec && MDOC_HEAD == n->type); 1512 } 1513 1514 static int 1515 parse_mdoc_head(struct mpage *mpage, const struct mdoc_node *n) 1516 { 1517 1518 return(MDOC_HEAD == n->type); 1519 } 1520 1521 static int 1522 parse_mdoc_body(struct mpage *mpage, const struct mdoc_node *n) 1523 { 1524 1525 return(MDOC_BODY == n->type); 1526 } 1527 1528 /* 1529 * Add a string to the hash table for the current manual. 1530 * Each string has a bitmask telling which macros it belongs to. 1531 * When we finish the manual, we'll dump the table. 1532 */ 1533 static void 1534 putkeys(const struct mpage *mpage, 1535 const char *cp, size_t sz, uint64_t v) 1536 { 1537 struct str *s; 1538 unsigned int slot; 1539 const char *end; 1540 1541 if (0 == sz) 1542 return; 1543 1544 end = cp + sz; 1545 slot = ohash_qlookupi(&strings, cp, &end); 1546 s = ohash_find(&strings, slot); 1547 1548 if (NULL != s && mpage == s->mpage) { 1549 s->mask |= v; 1550 return; 1551 } else if (NULL == s) { 1552 s = mandoc_calloc(sizeof(struct str) + sz + 1, 1); 1553 memcpy(s->key, cp, sz); 1554 ohash_insert(&strings, slot, s); 1555 } 1556 s->mpage = mpage; 1557 s->mask = v; 1558 } 1559 1560 /* 1561 * Take a Unicode codepoint and produce its UTF-8 encoding. 1562 * This isn't the best way to do this, but it works. 1563 * The magic numbers are from the UTF-8 packaging. 1564 * They're not as scary as they seem: read the UTF-8 spec for details. 1565 */ 1566 static size_t 1567 utf8(unsigned int cp, char out[7]) 1568 { 1569 size_t rc; 1570 1571 rc = 0; 1572 if (cp <= 0x0000007F) { 1573 rc = 1; 1574 out[0] = (char)cp; 1575 } else if (cp <= 0x000007FF) { 1576 rc = 2; 1577 out[0] = (cp >> 6 & 31) | 192; 1578 out[1] = (cp & 63) | 128; 1579 } else if (cp <= 0x0000FFFF) { 1580 rc = 3; 1581 out[0] = (cp >> 12 & 15) | 224; 1582 out[1] = (cp >> 6 & 63) | 128; 1583 out[2] = (cp & 63) | 128; 1584 } else if (cp <= 0x001FFFFF) { 1585 rc = 4; 1586 out[0] = (cp >> 18 & 7) | 240; 1587 out[1] = (cp >> 12 & 63) | 128; 1588 out[2] = (cp >> 6 & 63) | 128; 1589 out[3] = (cp & 63) | 128; 1590 } else if (cp <= 0x03FFFFFF) { 1591 rc = 5; 1592 out[0] = (cp >> 24 & 3) | 248; 1593 out[1] = (cp >> 18 & 63) | 128; 1594 out[2] = (cp >> 12 & 63) | 128; 1595 out[3] = (cp >> 6 & 63) | 128; 1596 out[4] = (cp & 63) | 128; 1597 } else if (cp <= 0x7FFFFFFF) { 1598 rc = 6; 1599 out[0] = (cp >> 30 & 1) | 252; 1600 out[1] = (cp >> 24 & 63) | 128; 1601 out[2] = (cp >> 18 & 63) | 128; 1602 out[3] = (cp >> 12 & 63) | 128; 1603 out[4] = (cp >> 6 & 63) | 128; 1604 out[5] = (cp & 63) | 128; 1605 } else 1606 return(0); 1607 1608 out[rc] = '\0'; 1609 return(rc); 1610 } 1611 1612 /* 1613 * Store the rendered version of a key, or alias the pointer 1614 * if the key contains no escape sequences. 1615 */ 1616 static void 1617 render_key(struct mchars *mc, struct str *key) 1618 { 1619 size_t sz, bsz, pos; 1620 char utfbuf[7], res[5]; 1621 char *buf; 1622 const char *seq, *cpp, *val; 1623 int len, u; 1624 enum mandoc_esc esc; 1625 1626 assert(NULL == key->rendered); 1627 1628 res[0] = '\\'; 1629 res[1] = '\t'; 1630 res[2] = ASCII_NBRSP; 1631 res[3] = ASCII_HYPH; 1632 res[4] = '\0'; 1633 1634 val = key->key; 1635 bsz = strlen(val); 1636 1637 /* 1638 * Pre-check: if we have no stop-characters, then set the 1639 * pointer as ourselvse and get out of here. 1640 */ 1641 if (strcspn(val, res) == bsz) { 1642 key->rendered = key->key; 1643 return; 1644 } 1645 1646 /* Pre-allocate by the length of the input */ 1647 1648 buf = mandoc_malloc(++bsz); 1649 pos = 0; 1650 1651 while ('\0' != *val) { 1652 /* 1653 * Halt on the first escape sequence. 1654 * This also halts on the end of string, in which case 1655 * we just copy, fallthrough, and exit the loop. 1656 */ 1657 if ((sz = strcspn(val, res)) > 0) { 1658 memcpy(&buf[pos], val, sz); 1659 pos += sz; 1660 val += sz; 1661 } 1662 1663 if (ASCII_HYPH == *val) { 1664 buf[pos++] = '-'; 1665 val++; 1666 continue; 1667 } else if ('\t' == *val || ASCII_NBRSP == *val) { 1668 buf[pos++] = ' '; 1669 val++; 1670 continue; 1671 } else if ('\\' != *val) 1672 break; 1673 1674 /* Read past the slash. */ 1675 1676 val++; 1677 1678 /* 1679 * Parse the escape sequence and see if it's a 1680 * predefined character or special character. 1681 */ 1682 1683 esc = mandoc_escape 1684 ((const char **)&val, &seq, &len); 1685 if (ESCAPE_ERROR == esc) 1686 break; 1687 if (ESCAPE_SPECIAL != esc) 1688 continue; 1689 1690 /* 1691 * Render the special character 1692 * as either UTF-8 or ASCII. 1693 */ 1694 1695 if (write_utf8) { 1696 if (0 == (u = mchars_spec2cp(mc, seq, len))) 1697 continue; 1698 cpp = utfbuf; 1699 if (0 == (sz = utf8(u, utfbuf))) 1700 continue; 1701 sz = strlen(cpp); 1702 } else { 1703 cpp = mchars_spec2str(mc, seq, len, &sz); 1704 if (NULL == cpp) 1705 continue; 1706 if (ASCII_NBRSP == *cpp) { 1707 cpp = " "; 1708 sz = 1; 1709 } 1710 } 1711 1712 /* Copy the rendered glyph into the stream. */ 1713 1714 bsz += sz; 1715 buf = mandoc_realloc(buf, bsz); 1716 memcpy(&buf[pos], cpp, sz); 1717 pos += sz; 1718 } 1719 1720 buf[pos] = '\0'; 1721 key->rendered = buf; 1722 } 1723 1724 /* 1725 * Flush the current page's terms (and their bits) into the database. 1726 * Wrap the entire set of additions in a transaction to make sqlite be a 1727 * little faster. 1728 * Also, handle escape sequences at the last possible moment. 1729 */ 1730 static void 1731 dbindex(const struct mpage *mpage, struct mchars *mc) 1732 { 1733 struct mlink *mlink; 1734 struct str *key; 1735 const char *desc; 1736 int64_t recno; 1737 size_t i; 1738 unsigned int slot; 1739 1740 if (verb) 1741 say(mpage->mlinks->file, "Adding to index"); 1742 1743 if (nodb) 1744 return; 1745 1746 desc = ""; 1747 if (NULL != mpage->desc && '\0' != *mpage->desc) { 1748 key = ohash_find(&strings, 1749 ohash_qlookup(&strings, mpage->desc)); 1750 assert(NULL != key); 1751 if (NULL == key->rendered) 1752 render_key(mc, key); 1753 desc = key->rendered; 1754 } 1755 1756 SQL_EXEC("BEGIN TRANSACTION"); 1757 1758 i = 1; 1759 SQL_BIND_TEXT(stmts[STMT_INSERT_PAGE], i, desc); 1760 SQL_BIND_INT(stmts[STMT_INSERT_PAGE], i, FORM_SRC == mpage->form); 1761 SQL_STEP(stmts[STMT_INSERT_PAGE]); 1762 recno = sqlite3_last_insert_rowid(db); 1763 sqlite3_reset(stmts[STMT_INSERT_PAGE]); 1764 1765 for (mlink = mpage->mlinks; mlink; mlink = mlink->next) { 1766 i = 1; 1767 SQL_BIND_TEXT(stmts[STMT_INSERT_LINK], i, mlink->file); 1768 SQL_BIND_TEXT(stmts[STMT_INSERT_LINK], i, mlink->dsec); 1769 SQL_BIND_TEXT(stmts[STMT_INSERT_LINK], i, mlink->arch); 1770 SQL_BIND_TEXT(stmts[STMT_INSERT_LINK], i, mlink->name); 1771 SQL_BIND_INT64(stmts[STMT_INSERT_LINK], i, recno); 1772 SQL_STEP(stmts[STMT_INSERT_LINK]); 1773 sqlite3_reset(stmts[STMT_INSERT_LINK]); 1774 } 1775 1776 for (key = ohash_first(&strings, &slot); NULL != key; 1777 key = ohash_next(&strings, &slot)) { 1778 assert(key->mpage == mpage); 1779 if (NULL == key->rendered) 1780 render_key(mc, key); 1781 i = 1; 1782 SQL_BIND_INT64(stmts[STMT_INSERT_KEY], i, key->mask); 1783 SQL_BIND_TEXT(stmts[STMT_INSERT_KEY], i, key->rendered); 1784 SQL_BIND_INT64(stmts[STMT_INSERT_KEY], i, recno); 1785 SQL_STEP(stmts[STMT_INSERT_KEY]); 1786 sqlite3_reset(stmts[STMT_INSERT_KEY]); 1787 if (key->rendered != key->key) 1788 free(key->rendered); 1789 free(key); 1790 } 1791 1792 SQL_EXEC("END TRANSACTION"); 1793 } 1794 1795 static void 1796 dbprune(void) 1797 { 1798 struct mpage *mpage; 1799 struct mlink *mlink; 1800 size_t i; 1801 unsigned int slot; 1802 1803 if (nodb) 1804 return; 1805 1806 mpage = ohash_first(&mpages, &slot); 1807 while (NULL != mpage) { 1808 mlink = mpage->mlinks; 1809 i = 1; 1810 SQL_BIND_TEXT(stmts[STMT_DELETE_PAGE], i, mlink->file); 1811 SQL_STEP(stmts[STMT_DELETE_PAGE]); 1812 sqlite3_reset(stmts[STMT_DELETE_PAGE]); 1813 if (verb) 1814 say(mlink->file, "Deleted from index"); 1815 mpage = ohash_next(&mpages, &slot); 1816 } 1817 } 1818 1819 /* 1820 * Close an existing database and its prepared statements. 1821 * If "real" is not set, rename the temporary file into the real one. 1822 */ 1823 static void 1824 dbclose(int real) 1825 { 1826 size_t i; 1827 1828 if (nodb) 1829 return; 1830 1831 for (i = 0; i < STMT__MAX; i++) { 1832 sqlite3_finalize(stmts[i]); 1833 stmts[i] = NULL; 1834 } 1835 1836 sqlite3_close(db); 1837 db = NULL; 1838 1839 if (real) 1840 return; 1841 1842 if (-1 == rename(MANDOC_DB "~", MANDOC_DB)) { 1843 exitcode = (int)MANDOCLEVEL_SYSERR; 1844 say(MANDOC_DB, NULL); 1845 } 1846 } 1847 1848 /* 1849 * This is straightforward stuff. 1850 * Open a database connection to a "temporary" database, then open a set 1851 * of prepared statements we'll use over and over again. 1852 * If "real" is set, we use the existing database; if not, we truncate a 1853 * temporary one. 1854 * Must be matched by dbclose(). 1855 */ 1856 static int 1857 dbopen(int real) 1858 { 1859 const char *file, *sql; 1860 int rc, ofl; 1861 1862 if (nodb) 1863 return(1); 1864 1865 ofl = SQLITE_OPEN_READWRITE; 1866 if (0 == real) { 1867 file = MANDOC_DB "~"; 1868 if (-1 == remove(file) && ENOENT != errno) { 1869 exitcode = (int)MANDOCLEVEL_SYSERR; 1870 say(file, NULL); 1871 return(0); 1872 } 1873 ofl |= SQLITE_OPEN_EXCLUSIVE; 1874 } else 1875 file = MANDOC_DB; 1876 1877 rc = sqlite3_open_v2(file, &db, ofl, NULL); 1878 if (SQLITE_OK == rc) 1879 goto prepare_statements; 1880 if (SQLITE_CANTOPEN != rc) { 1881 exitcode = (int)MANDOCLEVEL_SYSERR; 1882 say(file, NULL); 1883 return(0); 1884 } 1885 1886 sqlite3_close(db); 1887 db = NULL; 1888 1889 if (SQLITE_OK != (rc = sqlite3_open(file, &db))) { 1890 exitcode = (int)MANDOCLEVEL_SYSERR; 1891 say(file, NULL); 1892 return(0); 1893 } 1894 1895 sql = "CREATE TABLE \"mpages\" (\n" 1896 " \"desc\" TEXT NOT NULL,\n" 1897 " \"form\" INTEGER NOT NULL,\n" 1898 " \"id\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL\n" 1899 ");\n" 1900 "\n" 1901 "CREATE TABLE \"mlinks\" (\n" 1902 " \"file\" TEXT NOT NULL,\n" 1903 " \"sec\" TEXT NOT NULL,\n" 1904 " \"arch\" TEXT NOT NULL,\n" 1905 " \"name\" TEXT NOT NULL,\n" 1906 " \"pageid\" INTEGER NOT NULL REFERENCES mpages(id) " 1907 "ON DELETE CASCADE,\n" 1908 " \"id\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL\n" 1909 ");\n" 1910 "\n" 1911 "CREATE TABLE \"keys\" (\n" 1912 " \"bits\" INTEGER NOT NULL,\n" 1913 " \"key\" TEXT NOT NULL,\n" 1914 " \"pageid\" INTEGER NOT NULL REFERENCES mpages(id) " 1915 "ON DELETE CASCADE,\n" 1916 " \"id\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL\n" 1917 ");\n" 1918 "\n" 1919 "CREATE INDEX \"key_index\" ON keys (key);\n"; 1920 1921 if (SQLITE_OK != sqlite3_exec(db, sql, NULL, NULL, NULL)) { 1922 exitcode = (int)MANDOCLEVEL_SYSERR; 1923 say(file, "%s", sqlite3_errmsg(db)); 1924 return(0); 1925 } 1926 1927 prepare_statements: 1928 SQL_EXEC("PRAGMA foreign_keys = ON"); 1929 sql = "DELETE FROM mpages where file=?"; 1930 sqlite3_prepare_v2(db, sql, -1, &stmts[STMT_DELETE_PAGE], NULL); 1931 sql = "INSERT INTO mpages " 1932 "(desc,form) VALUES (?,?)"; 1933 sqlite3_prepare_v2(db, sql, -1, &stmts[STMT_INSERT_PAGE], NULL); 1934 sql = "INSERT INTO mlinks " 1935 "(file,sec,arch,name,pageid) VALUES (?,?,?,?,?)"; 1936 sqlite3_prepare_v2(db, sql, -1, &stmts[STMT_INSERT_LINK], NULL); 1937 sql = "INSERT INTO keys " 1938 "(bits,key,pageid) VALUES (?,?,?)"; 1939 sqlite3_prepare_v2(db, sql, -1, &stmts[STMT_INSERT_KEY], NULL); 1940 1941 #ifndef __APPLE__ 1942 /* 1943 * When opening a new database, we can turn off 1944 * synchronous mode for much better performance. 1945 */ 1946 1947 if (real) 1948 SQL_EXEC("PRAGMA synchronous = OFF"); 1949 #endif 1950 1951 return(1); 1952 } 1953 1954 static void * 1955 hash_halloc(size_t sz, void *arg) 1956 { 1957 1958 return(mandoc_calloc(sz, 1)); 1959 } 1960 1961 static void * 1962 hash_alloc(size_t sz, void *arg) 1963 { 1964 1965 return(mandoc_malloc(sz)); 1966 } 1967 1968 static void 1969 hash_free(void *p, size_t sz, void *arg) 1970 { 1971 1972 free(p); 1973 } 1974 1975 static int 1976 set_basedir(const char *targetdir) 1977 { 1978 static char startdir[PATH_MAX]; 1979 static int fd; 1980 1981 /* 1982 * Remember where we started by keeping a fd open to the origin 1983 * path component: throughout this utility, we chdir() a lot to 1984 * handle relative paths, and by doing this, we can return to 1985 * the starting point. 1986 */ 1987 if ('\0' == *startdir) { 1988 if (NULL == getcwd(startdir, PATH_MAX)) { 1989 exitcode = (int)MANDOCLEVEL_SYSERR; 1990 if (NULL != targetdir) 1991 say(".", NULL); 1992 return(0); 1993 } 1994 if (-1 == (fd = open(startdir, O_RDONLY, 0))) { 1995 exitcode = (int)MANDOCLEVEL_SYSERR; 1996 say(startdir, NULL); 1997 return(0); 1998 } 1999 if (NULL == targetdir) 2000 targetdir = startdir; 2001 } else { 2002 if (-1 == fd) 2003 return(0); 2004 if (-1 == fchdir(fd)) { 2005 close(fd); 2006 basedir[0] = '\0'; 2007 exitcode = (int)MANDOCLEVEL_SYSERR; 2008 say(startdir, NULL); 2009 return(0); 2010 } 2011 if (NULL == targetdir) { 2012 close(fd); 2013 return(1); 2014 } 2015 } 2016 if (NULL == realpath(targetdir, basedir)) { 2017 basedir[0] = '\0'; 2018 exitcode = (int)MANDOCLEVEL_BADARG; 2019 say(targetdir, NULL); 2020 return(0); 2021 } else if (-1 == chdir(basedir)) { 2022 exitcode = (int)MANDOCLEVEL_BADARG; 2023 say("", NULL); 2024 return(0); 2025 } 2026 return(1); 2027 } 2028 2029 static void 2030 say(const char *file, const char *format, ...) 2031 { 2032 va_list ap; 2033 2034 if ('\0' != *basedir) 2035 fprintf(stderr, "%s", basedir); 2036 if ('\0' != *basedir && '\0' != *file) 2037 fputs("//", stderr); 2038 if ('\0' != *file) 2039 fprintf(stderr, "%s", file); 2040 fputs(": ", stderr); 2041 2042 if (NULL == format) { 2043 perror(NULL); 2044 return; 2045 } 2046 2047 va_start(ap, format); 2048 vfprintf(stderr, format, ap); 2049 va_end(ap); 2050 2051 fputc('\n', stderr); 2052 } 2053