1 /* $NetBSD: makewhatis.c,v 1.49 2013/06/24 20:57:47 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 1999 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Matthias Scheler. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #if HAVE_NBTOOL_CONFIG_H 33 #include "nbtool_config.h" 34 #endif 35 36 #include <sys/cdefs.h> 37 #if !defined(lint) 38 __COPYRIGHT("@(#) Copyright (c) 1999\ 39 The NetBSD Foundation, Inc. All rights reserved."); 40 __RCSID("$NetBSD: makewhatis.c,v 1.49 2013/06/24 20:57:47 christos Exp $"); 41 #endif /* not lint */ 42 43 #include <sys/types.h> 44 #include <sys/param.h> 45 #include <sys/queue.h> 46 #include <sys/stat.h> 47 #include <sys/wait.h> 48 49 #include <ctype.h> 50 #include <err.h> 51 #include <errno.h> 52 #include <fcntl.h> 53 #include <fts.h> 54 #include <glob.h> 55 #include <locale.h> 56 #include <paths.h> 57 #include <signal.h> 58 #include <stdio.h> 59 #include <stdlib.h> 60 #include <string.h> 61 #include <unistd.h> 62 #include <zlib.h> 63 #include <util.h> 64 65 #include <man/manconf.h> 66 #include <man/pathnames.h> 67 68 #ifndef NROFF 69 #define NROFF "nroff" 70 #endif 71 72 typedef struct manpagestruct manpage; 73 struct manpagestruct { 74 manpage *mp_left, *mp_right; 75 ino_t mp_inode; 76 size_t mp_sdoff; 77 size_t mp_sdlen; 78 char mp_name[1]; 79 }; 80 81 typedef struct whatisstruct whatis; 82 struct whatisstruct { 83 whatis *wi_left, *wi_right; 84 char *wi_data; 85 char wi_prefix[1]; 86 }; 87 88 int main(int, char * const *); 89 static char *findwhitespace(char *); 90 static char *strmove(char *, char *); 91 static char *GetS(gzFile, char *, size_t); 92 static int pathnamesection(const char *, const char *); 93 static int manpagesection(char *); 94 static char *createsectionstring(char *); 95 static void addmanpage(manpage **, ino_t, char *, size_t, size_t); 96 static void addwhatis(whatis **, char *, char *); 97 static char *makesection(int); 98 static char *makewhatisline(const char *, const char *, const char *); 99 static void catpreprocess(char *); 100 static char *parsecatpage(const char *, gzFile *); 101 static int manpreprocess(char *); 102 static char *nroff(const char *, gzFile *); 103 static char *parsemanpage(const char *, gzFile *, int); 104 static char *getwhatisdata(char *); 105 static void processmanpages(manpage **, whatis **); 106 static void dumpwhatis(FILE *, whatis *); 107 static int makewhatis(char * const *manpath); 108 109 static char * const default_manpath[] = { 110 #if defined(__minix) 111 "/usr/man", 112 #endif /* defined(__minix) */ 113 "/usr/share/man", 114 NULL 115 }; 116 117 static const char *sectionext = "0123456789ln"; 118 static const char *whatisdb = _PATH_WHATIS; 119 static const char *whatisdb_new = _PATH_WHATIS ".new"; 120 static int dowarn = 0; 121 122 #define ISALPHA(c) isalpha((unsigned char)(c)) 123 #define ISDIGIT(c) isdigit((unsigned char)(c)) 124 #define ISSPACE(c) isspace((unsigned char)(c)) 125 126 int 127 main(int argc, char *const *argv) 128 { 129 char * const *manpath; 130 int c, dofork; 131 const char *conffile; 132 ENTRY *ep; 133 TAG *tp; 134 int rv, jobs, status; 135 glob_t pg; 136 char *paths[2], **p, *sl; 137 int retval; 138 139 dofork = 1; 140 conffile = NULL; 141 jobs = 0; 142 retval = EXIT_SUCCESS; 143 144 (void)setlocale(LC_ALL, ""); 145 146 while ((c = getopt(argc, argv, "C:fw")) != -1) { 147 switch (c) { 148 case 'C': 149 conffile = optarg; 150 break; 151 case 'f': 152 /* run all processing on foreground */ 153 dofork = 0; 154 break; 155 case 'w': 156 dowarn++; 157 break; 158 default: 159 fprintf(stderr, "Usage: %s [-fw] [-C file] [manpath ...]\n", 160 getprogname()); 161 exit(EXIT_FAILURE); 162 } 163 } 164 argc -= optind; 165 argv += optind; 166 167 if (argc >= 1) { 168 manpath = &argv[0]; 169 170 mkwhatis: 171 return makewhatis(manpath); 172 } 173 174 /* 175 * Try read config file, fallback to default_manpath[] 176 * if man.conf not available. 177 */ 178 config(conffile); 179 if ((tp = gettag("_whatdb", 0)) == NULL) { 180 manpath = default_manpath; 181 goto mkwhatis; 182 } 183 184 /* Build individual databases */ 185 paths[1] = NULL; 186 TAILQ_FOREACH(ep, &tp->entrylist, q) { 187 if ((rv = glob(ep->s, 188 GLOB_BRACE | GLOB_NOSORT | GLOB_ERR | GLOB_NOCHECK, 189 NULL, &pg)) != 0) 190 err(EXIT_FAILURE, "glob('%s')", ep->s); 191 192 /* We always have something to work with here */ 193 for (p = pg.gl_pathv; *p; p++) { 194 sl = strrchr(*p, '/'); 195 if (sl == NULL) { 196 err(EXIT_FAILURE, "glob: _whatdb entry '%s' " 197 "doesn't contain slash", ep->s); 198 } 199 200 /* 201 * Cut the last component of path, leaving just 202 * the directory. We will use the result as root 203 * for manpage search. 204 * glob malloc()s space for the paths, so it's 205 * okay to change it in-place. 206 */ 207 *sl = '\0'; 208 paths[0] = *p; 209 210 if (!dofork) { 211 /* Do not fork child */ 212 makewhatis(paths); 213 continue; 214 } 215 216 switch (fork()) { 217 case 0: 218 exit(makewhatis(paths)); 219 break; 220 case -1: 221 warn("fork"); 222 makewhatis(paths); 223 break; 224 default: 225 jobs++; 226 break; 227 } 228 229 } 230 231 globfree(&pg); 232 } 233 234 /* Wait for the childern to finish */ 235 while (jobs > 0) { 236 (void)wait(&status); 237 if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS) 238 retval = EXIT_FAILURE; 239 jobs--; 240 } 241 242 return retval; 243 } 244 245 static int 246 makewhatis(char * const * manpath) 247 { 248 FTS *fts; 249 FTSENT *fe; 250 manpage *source; 251 whatis *dest; 252 FILE *out; 253 size_t sdoff, sdlen; 254 int outfd; 255 struct stat st_before, st_after; 256 257 if ((fts = fts_open(manpath, FTS_LOGICAL, NULL)) == NULL) 258 err(EXIT_FAILURE, "Cannot open `%s'", *manpath); 259 260 source = NULL; 261 while ((fe = fts_read(fts)) != NULL) { 262 switch (fe->fts_info) { 263 case FTS_F: 264 if (manpagesection(fe->fts_path) >= 0) { 265 /* 266 * Get manpage subdirectory prefix. Most 267 * commonly, this is arch-specific subdirectory. 268 */ 269 if (fe->fts_level >= 3) { 270 int sl; 271 const char *s, *lsl; 272 273 lsl = NULL; 274 s = &fe->fts_path[fe->fts_pathlen - 1]; 275 for(sl = fe->fts_level - 1; sl > 0; 276 sl--) { 277 s--; 278 while (s[0] != '/') 279 s--; 280 if (lsl == NULL) 281 lsl = s; 282 } 283 284 /* 285 * Include trailing '/', so we get 286 * 'arch/'. 287 */ 288 sdoff = s + 1 - fe->fts_path; 289 sdlen = lsl - s + 1; 290 } else { 291 sdoff = 0; 292 sdlen = 0; 293 } 294 295 addmanpage(&source, fe->fts_statp->st_ino, 296 fe->fts_path, sdoff, sdlen); 297 } 298 /*FALLTHROUGH*/ 299 case FTS_D: 300 case FTS_DC: 301 case FTS_DEFAULT: 302 case FTS_DP: 303 case FTS_SL: 304 case FTS_DOT: 305 case FTS_W: 306 case FTS_NSOK: 307 case FTS_INIT: 308 break; 309 case FTS_SLNONE: 310 warnx("Symbolic link with no target: `%s'", 311 fe->fts_path); 312 break; 313 case FTS_DNR: 314 warnx("Unreadable directory: `%s'", fe->fts_path); 315 break; 316 case FTS_NS: 317 errno = fe->fts_errno; 318 warn("Cannot stat `%s'", fe->fts_path); 319 break; 320 case FTS_ERR: 321 errno = fe->fts_errno; 322 warn("Error reading `%s'", fe->fts_path); 323 break; 324 default: 325 errx(EXIT_FAILURE, "Unknown info %d returned from fts " 326 " for path: `%s'", fe->fts_info, fe->fts_path); 327 } 328 } 329 330 (void)fts_close(fts); 331 332 dest = NULL; 333 processmanpages(&source, &dest); 334 335 if (chdir(manpath[0]) == -1) 336 err(EXIT_FAILURE, "Cannot change dir to `%s'", manpath[0]); 337 338 /* 339 * makewhatis runs unattended, so it needs to be able to 340 * recover if the last run crashed out. Therefore, if 341 * whatisdb_new exists and is more than (arbitrarily) sixteen 342 * hours old, nuke it. If it exists but is not so old, refuse 343 * to run until it's cleaned up, in case another makewhatis is 344 * already running. Also, open the output with O_EXCL to make 345 * sure we get our own, in case two copies start exactly at 346 * once. (Unlikely? Maybe, maybe not, if two copies of cron 347 * end up running.) 348 * 349 * Similarly, before renaming the file after we finish writing 350 * to it, make sure it's still the same file we opened. This 351 * can't be completely race-free, but getting caught by it 352 * would require an unexplained sixteen-hour-or-more lag 353 * between the last mtime update when we wrote to it and when 354 * we get to the stat call *and* another makewhatis starting 355 * out to write at exactly the wrong moment. Not impossible, 356 * but not likely enough to worry about. 357 * 358 * This is maybe unnecessarily elaborate, but generating 359 * corrupted output isn't so good either. 360 */ 361 362 if (stat(whatisdb_new, &st_before) == 0) { 363 if (st_before.st_mtime - time(NULL) > 16*60*60) { 364 /* Don't complain if someone else just removed it. */ 365 if (unlink(whatisdb_new) == -1 && errno != ENOENT) { 366 err(EXIT_FAILURE, "Could not remove `%s'", 367 whatisdb_new); 368 } else { 369 warnx("Removed stale `%s'", whatisdb_new); 370 } 371 } else { 372 errx(EXIT_FAILURE, "The file `%s' already exists " 373 "-- am I already running?", whatisdb_new); 374 } 375 } else if (errno != ENOENT) { 376 /* Something unexpected happened. */ 377 err(EXIT_FAILURE, "Cannot stat `%s'", whatisdb_new); 378 } 379 380 outfd = open(whatisdb_new, O_WRONLY|O_CREAT|O_EXCL, 381 S_IRUSR|S_IRGRP|S_IROTH); 382 if (outfd < 0) 383 err(EXIT_FAILURE, "Cannot open `%s'", whatisdb_new); 384 385 if (fstat(outfd, &st_before) == -1) 386 err(EXIT_FAILURE, "Cannot fstat `%s'", whatisdb_new); 387 388 if ((out = fdopen(outfd, "w")) == NULL) 389 err(EXIT_FAILURE, "Cannot fdopen `%s'", whatisdb_new); 390 391 dumpwhatis(out, dest); 392 if (fchmod(fileno(out), S_IRUSR|S_IRGRP|S_IROTH) == -1) 393 err(EXIT_FAILURE, "Cannot chmod `%s'", whatisdb_new); 394 if (fclose(out) != 0) 395 err(EXIT_FAILURE, "Cannot close `%s'", whatisdb_new); 396 397 if (stat(whatisdb_new, &st_after) == -1) 398 err(EXIT_FAILURE, "Cannot stat `%s' (after writing)", 399 whatisdb_new); 400 401 if (st_before.st_dev != st_after.st_dev || 402 st_before.st_ino != st_after.st_ino) { 403 errx(EXIT_FAILURE, "The file `%s' changed under me; giving up", 404 whatisdb_new); 405 } 406 407 if (rename(whatisdb_new, whatisdb) == -1) 408 err(EXIT_FAILURE, "Could not rename `%s' to `%s'", 409 whatisdb_new, whatisdb); 410 411 return EXIT_SUCCESS; 412 } 413 414 static char * 415 findwhitespace(char *str) 416 { 417 while (!ISSPACE(*str)) 418 if (*str++ == '\0') { 419 str = NULL; 420 break; 421 } 422 423 return str; 424 } 425 426 static char * 427 strmove(char *dest, char *src) 428 { 429 return memmove(dest, src, strlen(src) + 1); 430 } 431 432 static char * 433 GetS(gzFile in, char *buffer, size_t length) 434 { 435 char *ptr; 436 437 if (((ptr = gzgets(in, buffer, (int)length)) != NULL) && (*ptr == '\0')) 438 ptr = NULL; 439 440 return ptr; 441 } 442 443 static char * 444 makesection(int s) 445 { 446 char sectionbuffer[24]; 447 if (s == -1) 448 return NULL; 449 (void)snprintf(sectionbuffer, sizeof(sectionbuffer), 450 " (%c) - ", sectionext[s]); 451 return estrdup(sectionbuffer); 452 } 453 454 static int 455 pathnamesection(const char *pat, const char *name) 456 { 457 char *ptr, *ext; 458 size_t len = strlen(pat); 459 460 461 while ((ptr = strstr(name, pat)) != NULL) { 462 if ((ext = strchr(sectionext, ptr[len])) != NULL) { 463 return ext - sectionext; 464 } 465 name = ptr + 1; 466 } 467 return -1; 468 } 469 470 471 static int 472 manpagesection(char *name) 473 { 474 char *ptr; 475 476 if ((ptr = strrchr(name, '/')) != NULL) 477 ptr++; 478 else 479 ptr = name; 480 481 while ((ptr = strchr(ptr, '.')) != NULL) { 482 int section; 483 484 ptr++; 485 section = 0; 486 while (sectionext[section] != '\0') 487 if (sectionext[section] == *ptr) 488 return section; 489 else 490 section++; 491 } 492 return -1; 493 } 494 495 static char * 496 createsectionstring(char *section_id) 497 { 498 char *section; 499 500 if (asprintf(§ion, " (%s) - ", section_id) < 0) 501 err(EXIT_FAILURE, "malloc failed"); 502 return section; 503 } 504 505 static void 506 addmanpage(manpage **tree, ino_t inode, char *name, size_t sdoff, size_t sdlen) 507 { 508 manpage *mp; 509 510 while ((mp = *tree) != NULL) { 511 if (mp->mp_inode == inode) 512 return; 513 tree = inode < mp->mp_inode ? &mp->mp_left : &mp->mp_right; 514 } 515 516 mp = emalloc(sizeof(manpage) + strlen(name)); 517 mp->mp_left = NULL; 518 mp->mp_right = NULL; 519 mp->mp_inode = inode; 520 mp->mp_sdoff = sdoff; 521 mp->mp_sdlen = sdlen; 522 (void)strcpy(mp->mp_name, name); 523 *tree = mp; 524 } 525 526 static void 527 addwhatis(whatis **tree, char *data, char *prefix) 528 { 529 whatis *wi; 530 int result; 531 532 while (ISSPACE(*data)) 533 data++; 534 535 if (*data == '/') { 536 char *ptr; 537 538 ptr = ++data; 539 while ((*ptr != '\0') && !ISSPACE(*ptr)) 540 if (*ptr++ == '/') 541 data = ptr; 542 } 543 544 while ((wi = *tree) != NULL) { 545 result = strcmp(data, wi->wi_data); 546 if (result == 0) result = strcmp(prefix, wi->wi_prefix); 547 if (result == 0) return; 548 tree = result < 0 ? &wi->wi_left : &wi->wi_right; 549 } 550 551 wi = emalloc(sizeof(whatis) + strlen(prefix)); 552 553 wi->wi_left = NULL; 554 wi->wi_right = NULL; 555 wi->wi_data = data; 556 if (prefix[0] != '\0') 557 (void) strcpy(wi->wi_prefix, prefix); 558 else 559 wi->wi_prefix[0] = '\0'; 560 *tree = wi; 561 } 562 563 static void 564 catpreprocess(char *from) 565 { 566 char *to; 567 568 to = from; 569 while (ISSPACE(*from)) from++; 570 571 while (*from != '\0') 572 if (ISSPACE(*from)) { 573 while (ISSPACE(*++from)); 574 if (*from != '\0') 575 *to++ = ' '; 576 } 577 else if (*(from + 1) == '\b') 578 from += 2; 579 else 580 *to++ = *from++; 581 582 *to = '\0'; 583 } 584 585 static char * 586 makewhatisline(const char *file, const char *line, const char *section) 587 { 588 static const char *del[] = { 589 " - ", 590 " -- ", 591 "- ", 592 " -", 593 NULL 594 }; 595 size_t i, pos; 596 size_t llen, slen, dlen; 597 char *result, *ptr; 598 599 ptr = NULL; 600 if (section == NULL) { 601 if (dowarn) 602 warnx("%s: No section provided for `%s'", file, line); 603 return estrdup(line); 604 } 605 606 for (i = 0; del[i]; i++) 607 if ((ptr = strstr(line, del[i])) != NULL) 608 break; 609 610 if (del[i] == NULL) { 611 if (dowarn) 612 warnx("%s: Bad format line `%s'", file, line); 613 return estrdup(line); 614 } 615 616 slen = strlen(section); 617 llen = strlen(line); 618 dlen = strlen(del[i]); 619 620 result = emalloc(llen - dlen + slen + 1); 621 pos = ptr - line; 622 623 (void)memcpy(result, line, pos); 624 (void)memcpy(&result[pos], section, slen); 625 (void)strcpy(&result[pos + slen], &line[pos + dlen]); 626 return result; 627 } 628 629 static char * 630 parsecatpage(const char *name, gzFile *in) 631 { 632 char buffer[8192]; 633 char *section, *ptr, *last; 634 size_t size; 635 636 do { 637 if (GetS(in, buffer, sizeof(buffer)) == NULL) 638 return NULL; 639 } 640 while (buffer[0] == '\n'); 641 642 section = NULL; 643 if ((ptr = strchr(buffer, '(')) != NULL) { 644 if ((last = strchr(ptr + 1, ')')) !=NULL) { 645 size_t length; 646 647 length = last - ptr + 1; 648 section = emalloc(length + 5); 649 *section = ' '; 650 (void) memcpy(section + 1, ptr, length); 651 (void) strcpy(section + 1 + length, " - "); 652 } 653 } 654 655 for (;;) { 656 if (GetS(in, buffer, sizeof(buffer)) == NULL) { 657 free(section); 658 return NULL; 659 } 660 catpreprocess(buffer); 661 if (strncmp(buffer, "NAME", 4) == 0) 662 break; 663 } 664 if (section == NULL) 665 section = makesection(pathnamesection("/cat", name)); 666 667 ptr = last = buffer; 668 size = sizeof(buffer) - 1; 669 while ((size > 0) && (GetS(in, ptr, size) != NULL)) { 670 int length; 671 672 catpreprocess(ptr); 673 674 length = strlen(ptr); 675 if (length == 0) { 676 *last = '\0'; 677 678 ptr = makewhatisline(name, buffer, section); 679 free(section); 680 return ptr; 681 } 682 if ((length > 1) && (ptr[length - 1] == '-') && 683 ISALPHA(ptr[length - 2])) 684 last = &ptr[--length]; 685 else { 686 last = &ptr[length++]; 687 *last = ' '; 688 } 689 690 ptr += length; 691 size -= length; 692 } 693 694 free(section); 695 696 return NULL; 697 } 698 699 static int 700 manpreprocess(char *line) 701 { 702 char *from, *to; 703 704 to = from = line; 705 while (ISSPACE(*from)) 706 from++; 707 if (strncmp(from, ".\\\"", 3) == 0) 708 return 1; 709 710 while (*from != '\0') 711 if (ISSPACE(*from)) { 712 while (ISSPACE(*++from)); 713 if ((*from != '\0') && (*from != ',')) 714 *to++ = ' '; 715 } else if (*from == '\\') { 716 switch (*++from) { 717 case '\0': 718 case '-': 719 break; 720 case 'f': 721 case 's': 722 from++; 723 if ((*from=='+') || (*from=='-')) 724 from++; 725 while (ISDIGIT(*from)) 726 from++; 727 break; 728 default: 729 from++; 730 } 731 } else { 732 if (*from == '"') 733 from++; 734 else 735 *to++ = *from++; 736 } 737 738 *to = '\0'; 739 740 if (strncasecmp(line, ".Xr", 3) == 0) { 741 char *sect; 742 743 from = line + 3; 744 if (ISSPACE(*from)) 745 from++; 746 747 if ((sect = findwhitespace(from)) != NULL) { 748 size_t length; 749 char *trail; 750 751 *sect++ = '\0'; 752 if ((trail = findwhitespace(sect)) != NULL) 753 *trail++ = '\0'; 754 length = strlen(from); 755 (void) memmove(line, from, length); 756 line[length++] = '('; 757 to = &line[length]; 758 length = strlen(sect); 759 (void) memmove(to, sect, length); 760 if (trail == NULL) { 761 (void) strcpy(&to[length], ")"); 762 } else { 763 to += length; 764 *to++ = ')'; 765 length = strlen(trail); 766 (void) memmove(to, trail, length + 1); 767 } 768 } 769 } 770 771 return 0; 772 } 773 774 static char * 775 nroff(const char *inname, gzFile *in) 776 { 777 char tempname[MAXPATHLEN], buffer[65536], *data; 778 int tempfd, bytes, pipefd[2], status; 779 static int devnull = -1; 780 pid_t child; 781 782 if (gzrewind(in) < 0) 783 err(EXIT_FAILURE, "Cannot rewind pipe"); 784 785 if ((devnull < 0) && 786 ((devnull = open(_PATH_DEVNULL, O_WRONLY, 0)) < 0)) 787 err(EXIT_FAILURE, "Cannot open `/dev/null'"); 788 789 (void)strlcpy(tempname, _PATH_TMP "makewhatis.XXXXXX", 790 sizeof(tempname)); 791 if ((tempfd = mkstemp(tempname)) == -1) 792 err(EXIT_FAILURE, "Cannot create temp file"); 793 794 while ((bytes = gzread(in, buffer, sizeof(buffer))) > 0) 795 if (write(tempfd, buffer, (size_t)bytes) != bytes) { 796 bytes = -1; 797 break; 798 } 799 800 if (bytes < 0) { 801 (void)close(tempfd); 802 (void)unlink(tempname); 803 err(EXIT_FAILURE, "Read from pipe failed"); 804 } 805 if (lseek(tempfd, (off_t)0, SEEK_SET) == (off_t)-1) { 806 (void)close(tempfd); 807 (void)unlink(tempname); 808 err(EXIT_FAILURE, "Cannot rewind temp file"); 809 } 810 if (pipe(pipefd) == -1) { 811 (void)close(tempfd); 812 (void)unlink(tempname); 813 err(EXIT_FAILURE, "Cannot create pipe"); 814 } 815 816 switch (child = vfork()) { 817 case -1: 818 (void)close(pipefd[1]); 819 (void)close(pipefd[0]); 820 (void)close(tempfd); 821 (void)unlink(tempname); 822 err(EXIT_FAILURE, "Fork failed"); 823 /* NOTREACHED */ 824 case 0: 825 (void)close(pipefd[0]); 826 if (tempfd != STDIN_FILENO) { 827 (void)dup2(tempfd, STDIN_FILENO); 828 (void)close(tempfd); 829 } 830 if (pipefd[1] != STDOUT_FILENO) { 831 (void)dup2(pipefd[1], STDOUT_FILENO); 832 (void)close(pipefd[1]); 833 } 834 if (devnull != STDERR_FILENO) { 835 (void)dup2(devnull, STDERR_FILENO); 836 (void)close(devnull); 837 } 838 (void)execlp(NROFF, NROFF, "-S", "-man", NULL); 839 _exit(EXIT_FAILURE); 840 /*NOTREACHED*/ 841 default: 842 (void)close(pipefd[1]); 843 (void)close(tempfd); 844 break; 845 } 846 847 if ((in = gzdopen(pipefd[0], "r")) == NULL) { 848 if (errno == 0) 849 errno = ENOMEM; 850 (void)close(pipefd[0]); 851 (void)kill(child, SIGTERM); 852 while (waitpid(child, NULL, 0) != child); 853 (void)unlink(tempname); 854 err(EXIT_FAILURE, "Cannot read from pipe"); 855 } 856 857 data = parsecatpage(inname, in); 858 while (gzread(in, buffer, sizeof(buffer)) > 0); 859 (void)gzclose(in); 860 861 while (waitpid(child, &status, 0) != child); 862 if ((data != NULL) && 863 !(WIFEXITED(status) && (WEXITSTATUS(status) == 0))) { 864 free(data); 865 errx(EXIT_FAILURE, NROFF " on `%s' exited with %d status", 866 inname, WEXITSTATUS(status)); 867 } 868 869 (void)unlink(tempname); 870 return data; 871 } 872 873 static char * 874 parsemanpage(const char *name, gzFile *in, int defaultsection) 875 { 876 char *section, buffer[8192], *ptr; 877 static const char POD[] = ".\\\" Automatically generated by Pod"; 878 static const char IX[] = ".IX TITLE"; 879 880 section = NULL; 881 do { 882 if (GetS(in, buffer, sizeof(buffer) - 1) == NULL) { 883 free(section); 884 return NULL; 885 } 886 887 /* 888 * Skip over lines in man pages that have been generated 889 * by Pod, until we find the TITLE. 890 */ 891 if (strncasecmp(buffer, POD, sizeof(POD) - 1) == 0) { 892 do { 893 if (GetS(in, buffer, sizeof(buffer) - 1) 894 == NULL) { 895 free(section); 896 return NULL; 897 } 898 } while (strncasecmp(buffer, IX, sizeof(IX) - 1) != 0); 899 } 900 901 if (manpreprocess(buffer)) 902 continue; 903 if (strncasecmp(buffer, ".Dt", 3) == 0) { 904 char *end; 905 906 ptr = &buffer[3]; 907 if (ISSPACE(*ptr)) 908 ptr++; 909 if ((ptr = findwhitespace(ptr)) == NULL) 910 continue; 911 912 if ((end = findwhitespace(++ptr)) != NULL) 913 *end = '\0'; 914 915 free(section); 916 section = createsectionstring(ptr); 917 } 918 else if (strncasecmp(buffer, ".TH", 3) == 0) { 919 ptr = &buffer[3]; 920 while (ISSPACE(*ptr)) 921 ptr++; 922 if ((ptr = findwhitespace(ptr)) != NULL) { 923 char *next; 924 925 while (ISSPACE(*ptr)) 926 ptr++; 927 if ((next = findwhitespace(ptr)) != NULL) 928 *next = '\0'; 929 free(section); 930 section = createsectionstring(ptr); 931 } 932 } 933 else if (strncasecmp(buffer, ".Ds", 3) == 0) { 934 free(section); 935 return NULL; 936 } 937 } while (strncasecmp(buffer, ".Sh NAME", 8) != 0); 938 939 do { 940 if (GetS(in, buffer, sizeof(buffer) - 1) == NULL) { 941 free(section); 942 return NULL; 943 } 944 } while (manpreprocess(buffer)); 945 946 if (strncasecmp(buffer, ".Nm", 3) == 0) { 947 size_t length, offset; 948 949 ptr = &buffer[3]; 950 while (ISSPACE(*ptr)) 951 ptr++; 952 953 length = strlen(ptr); 954 if ((length > 1) && (ptr[length - 1] == ',') && 955 ISSPACE(ptr[length - 2])) { 956 ptr[--length] = '\0'; 957 ptr[length - 1] = ','; 958 } 959 (void) memmove(buffer, ptr, length + 1); 960 961 offset = length + 3; 962 ptr = &buffer[offset]; 963 for (;;) { 964 size_t more; 965 966 if ((sizeof(buffer) == offset) || 967 (GetS(in, ptr, sizeof(buffer) - offset) 968 == NULL)) { 969 free(section); 970 return NULL; 971 } 972 if (manpreprocess(ptr)) 973 continue; 974 975 if (strncasecmp(ptr, ".Nm", 3) != 0) break; 976 977 ptr += 3; 978 if (ISSPACE(*ptr)) 979 ptr++; 980 981 buffer[length++] = ' '; 982 more = strlen(ptr); 983 if ((more > 1) && (ptr[more - 1] == ',') && 984 ISSPACE(ptr[more - 2])) { 985 ptr[--more] = '\0'; 986 ptr[more - 1] = ','; 987 } 988 989 (void) memmove(&buffer[length], ptr, more + 1); 990 length += more; 991 offset = length + 3; 992 993 ptr = &buffer[offset]; 994 } 995 996 if (strncasecmp(ptr, ".Nd", 3) == 0) { 997 (void) strlcpy(&buffer[length], " -", 998 sizeof(buffer) - length); 999 1000 while (strncasecmp(ptr, ".Sh", 3) != 0) { 1001 int more; 1002 1003 if (*ptr == '.') { 1004 char *space; 1005 1006 if (strncasecmp(ptr, ".Nd", 3) != 0 || 1007 strchr(ptr, '[') != NULL) { 1008 free(section); 1009 return NULL; 1010 } 1011 space = findwhitespace(ptr); 1012 if (space == NULL) { 1013 ptr = ""; 1014 } else { 1015 space++; 1016 (void) strmove(ptr, space); 1017 } 1018 } 1019 1020 if (*ptr != '\0') { 1021 buffer[offset - 1] = ' '; 1022 more = strlen(ptr) + 1; 1023 offset += more; 1024 } 1025 ptr = &buffer[offset]; 1026 if ((sizeof(buffer) == offset) || 1027 (GetS(in, ptr, sizeof(buffer) - offset) 1028 == NULL)) { 1029 free(section); 1030 return NULL; 1031 } 1032 if (manpreprocess(ptr)) 1033 *ptr = '\0'; 1034 } 1035 } 1036 } 1037 else { 1038 int offset; 1039 1040 if (*buffer == '.') { 1041 char *space; 1042 1043 if ((space = findwhitespace(&buffer[1])) == NULL) { 1044 free(section); 1045 return NULL; 1046 } 1047 space++; 1048 (void) strmove(buffer, space); 1049 } 1050 1051 offset = strlen(buffer) + 1; 1052 for (;;) { 1053 int more; 1054 1055 ptr = &buffer[offset]; 1056 if ((sizeof(buffer) == offset) || 1057 (GetS(in, ptr, sizeof(buffer) - offset) 1058 == NULL)) { 1059 free(section); 1060 return NULL; 1061 } 1062 if (manpreprocess(ptr) || (*ptr == '\0')) 1063 continue; 1064 1065 if ((strncasecmp(ptr, ".Sh", 3) == 0) || 1066 (strncasecmp(ptr, ".Ss", 3) == 0)) 1067 break; 1068 1069 if (*ptr == '.') { 1070 char *space; 1071 1072 if ((space = findwhitespace(ptr)) == NULL) { 1073 continue; 1074 } 1075 1076 space++; 1077 (void) memmove(ptr, space, strlen(space) + 1); 1078 } 1079 1080 buffer[offset - 1] = ' '; 1081 more = strlen(ptr); 1082 if ((more > 1) && (ptr[more - 1] == ',') && 1083 ISSPACE(ptr[more - 2])) { 1084 ptr[more - 1] = '\0'; 1085 ptr[more - 2] = ','; 1086 } 1087 else more++; 1088 offset += more; 1089 } 1090 } 1091 1092 if (section == NULL) 1093 section = makesection(defaultsection); 1094 1095 ptr = makewhatisline(name, buffer, section); 1096 free(section); 1097 return ptr; 1098 } 1099 1100 static char * 1101 getwhatisdata(char *name) 1102 { 1103 gzFile *in; 1104 char *data; 1105 int section; 1106 1107 if ((in = gzopen(name, "r")) == NULL) { 1108 if (errno == 0) 1109 errno = ENOMEM; 1110 err(EXIT_FAILURE, "Cannot open `%s'", name); 1111 /* NOTREACHED */ 1112 } 1113 1114 section = manpagesection(name); 1115 if (section == 0) { 1116 data = parsecatpage(name, in); 1117 } else { 1118 data = parsemanpage(name, in, section); 1119 if (data == NULL) 1120 data = nroff(name, in); 1121 } 1122 1123 (void) gzclose(in); 1124 return data; 1125 } 1126 1127 static void 1128 processmanpages(manpage **source, whatis **dest) 1129 { 1130 manpage *mp; 1131 char sd[128]; 1132 1133 mp = *source; 1134 *source = NULL; 1135 1136 while (mp != NULL) { 1137 manpage *obsolete; 1138 char *data; 1139 1140 if (mp->mp_left != NULL) 1141 processmanpages(&mp->mp_left, dest); 1142 1143 if ((data = getwhatisdata(mp->mp_name)) != NULL) { 1144 /* Pass eventual directory prefix to addwhatis() */ 1145 if (mp->mp_sdlen > 0 && mp->mp_sdlen < sizeof(sd)-1) 1146 strlcpy(sd, &mp->mp_name[mp->mp_sdoff], 1147 mp->mp_sdlen); 1148 else 1149 sd[0] = '\0'; 1150 1151 addwhatis(dest, data, sd); 1152 } 1153 1154 obsolete = mp; 1155 mp = mp->mp_right; 1156 free(obsolete); 1157 } 1158 } 1159 1160 static void 1161 dumpwhatis(FILE *out, whatis *tree) 1162 { 1163 while (tree != NULL) { 1164 if (tree->wi_left) 1165 dumpwhatis(out, tree->wi_left); 1166 1167 if ((tree->wi_data[0] && fputs(tree->wi_prefix, out) == EOF) || 1168 (fputs(tree->wi_data, out) == EOF) || 1169 (fputc('\n', out) == EOF)) 1170 err(EXIT_FAILURE, "Write failed"); 1171 1172 tree = tree->wi_right; 1173 } 1174 } 1175