1 /* $NetBSD: makewhatis.c,v 1.41 2008/04/28 20:23:04 martin 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 The NetBSD Foundation, Inc.\n\ 39 All rights reserved.\n"); 40 __RCSID("$NetBSD: makewhatis.c,v 1.41 2008/04/28 20:23:04 martin 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 "/usr/share/man", 111 NULL 112 }; 113 114 static const char *sectionext = "0123456789ln"; 115 static const char *whatisdb = _PATH_WHATIS; 116 static int dowarn = 0; 117 118 #define ISALPHA(c) isalpha((unsigned char)(c)) 119 #define ISDIGIT(c) isdigit((unsigned char)(c)) 120 #define ISSPACE(c) isspace((unsigned char)(c)) 121 122 int 123 main(int argc, char *const *argv) 124 { 125 char * const *manpath; 126 int c, dofork; 127 const char *conffile; 128 ENTRY *ep; 129 TAG *tp; 130 int rv, jobs, status; 131 glob_t pg; 132 char *paths[2], **p, *sl; 133 int retval; 134 135 dofork = 1; 136 conffile = NULL; 137 jobs = 0; 138 retval = EXIT_SUCCESS; 139 140 (void)setlocale(LC_ALL, ""); 141 142 while ((c = getopt(argc, argv, "C:fw")) != -1) { 143 switch (c) { 144 case 'C': 145 conffile = optarg; 146 break; 147 case 'f': 148 /* run all processing on foreground */ 149 dofork = 0; 150 break; 151 case 'w': 152 dowarn++; 153 break; 154 default: 155 fprintf(stderr, "Usage: %s [-fw] [-C file] [manpath ...]\n", 156 getprogname()); 157 exit(EXIT_FAILURE); 158 } 159 } 160 argc -= optind; 161 argv += optind; 162 163 if (argc >= 1) { 164 manpath = &argv[0]; 165 166 mkwhatis: 167 return makewhatis(manpath); 168 } 169 170 /* 171 * Try read config file, fallback to default_manpath[] 172 * if man.conf not available. 173 */ 174 config(conffile); 175 if ((tp = gettag("_whatdb", 0)) == NULL) { 176 manpath = default_manpath; 177 goto mkwhatis; 178 } 179 180 /* Build individual databases */ 181 paths[1] = NULL; 182 TAILQ_FOREACH(ep, &tp->entrylist, q) { 183 if ((rv = glob(ep->s, 184 GLOB_BRACE | GLOB_NOSORT | GLOB_ERR | GLOB_NOCHECK, 185 NULL, &pg)) != 0) 186 err(EXIT_FAILURE, "glob('%s')", ep->s); 187 188 /* We always have something to work with here */ 189 for (p = pg.gl_pathv; *p; p++) { 190 sl = strrchr(*p, '/'); 191 if (sl == NULL) { 192 err(EXIT_FAILURE, "glob: _whatdb entry '%s' " 193 "doesn't contain slash", ep->s); 194 } 195 196 /* 197 * Cut the last component of path, leaving just 198 * the directory. We will use the result as root 199 * for manpage search. 200 * glob malloc()s space for the paths, so it's 201 * okay to change it in-place. 202 */ 203 *sl = '\0'; 204 paths[0] = *p; 205 206 if (!dofork) { 207 /* Do not fork child */ 208 makewhatis(paths); 209 continue; 210 } 211 212 switch (fork()) { 213 case 0: 214 exit(makewhatis(paths)); 215 break; 216 case -1: 217 warn("fork"); 218 makewhatis(paths); 219 break; 220 default: 221 jobs++; 222 break; 223 } 224 225 } 226 227 globfree(&pg); 228 } 229 230 /* Wait for the childern to finish */ 231 while (jobs > 0) { 232 (void)wait(&status); 233 if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS) 234 retval = EXIT_FAILURE; 235 jobs--; 236 } 237 238 return retval; 239 } 240 241 static int 242 makewhatis(char * const * manpath) 243 { 244 FTS *fts; 245 FTSENT *fe; 246 manpage *source; 247 whatis *dest; 248 FILE *out; 249 size_t sdoff, sdlen; 250 251 if ((fts = fts_open(manpath, FTS_LOGICAL, NULL)) == NULL) 252 err(EXIT_FAILURE, "Cannot open `%s'", *manpath); 253 254 source = NULL; 255 while ((fe = fts_read(fts)) != NULL) { 256 switch (fe->fts_info) { 257 case FTS_F: 258 if (manpagesection(fe->fts_path) >= 0) { 259 /* 260 * Get manpage subdirectory prefix. Most 261 * commonly, this is arch-specific subdirectory. 262 */ 263 if (fe->fts_level >= 3) { 264 int sl; 265 const char *s, *lsl; 266 267 lsl = NULL; 268 s = &fe->fts_path[fe->fts_pathlen - 1]; 269 for(sl = fe->fts_level - 1; sl > 0; 270 sl--) { 271 s--; 272 while (s[0] != '/') 273 s--; 274 if (lsl == NULL) 275 lsl = s; 276 } 277 278 /* Include trailing '/', so we get 279 * 'arch/'. */ 280 sdoff = s + 1 - fe->fts_path; 281 sdlen = lsl - s + 1; 282 } else { 283 sdoff = 0; 284 sdlen = 0; 285 } 286 287 addmanpage(&source, fe->fts_statp->st_ino, 288 fe->fts_path, sdoff, sdlen); 289 } 290 /*FALLTHROUGH*/ 291 case FTS_D: 292 case FTS_DC: 293 case FTS_DEFAULT: 294 case FTS_DP: 295 case FTS_SL: 296 case FTS_DOT: 297 case FTS_W: 298 case FTS_NSOK: 299 case FTS_INIT: 300 break; 301 case FTS_SLNONE: 302 warnx("Symbolic link with no target: `%s'", 303 fe->fts_path); 304 break; 305 case FTS_DNR: 306 warnx("Unreadable directory: `%s'", fe->fts_path); 307 break; 308 case FTS_NS: 309 errno = fe->fts_errno; 310 warn("Cannot stat `%s'", fe->fts_path); 311 break; 312 case FTS_ERR: 313 errno = fe->fts_errno; 314 warn("Error reading `%s'", fe->fts_path); 315 break; 316 default: 317 errx(EXIT_FAILURE, "Unknown info %d returned from fts " 318 " for path: `%s'", fe->fts_info, fe->fts_path); 319 } 320 } 321 322 (void)fts_close(fts); 323 324 dest = NULL; 325 processmanpages(&source, &dest); 326 327 if (chdir(manpath[0]) == -1) 328 err(EXIT_FAILURE, "Cannot change dir to `%s'", manpath[0]); 329 330 (void)unlink(whatisdb); 331 if ((out = fopen(whatisdb, "w")) == NULL) 332 err(EXIT_FAILURE, "Cannot open `%s'", whatisdb); 333 334 dumpwhatis(out, dest); 335 if (fchmod(fileno(out), S_IRUSR|S_IRGRP|S_IROTH) == -1) 336 err(EXIT_FAILURE, "Cannot chmod `%s'", whatisdb); 337 if (fclose(out) != 0) 338 err(EXIT_FAILURE, "Cannot close `%s'", whatisdb); 339 340 return EXIT_SUCCESS; 341 } 342 343 static char * 344 findwhitespace(char *str) 345 { 346 while (!ISSPACE(*str)) 347 if (*str++ == '\0') { 348 str = NULL; 349 break; 350 } 351 352 return str; 353 } 354 355 static char 356 *strmove(char *dest,char *src) 357 { 358 return memmove(dest, src, strlen(src) + 1); 359 } 360 361 static char * 362 GetS(gzFile in, char *buffer, size_t length) 363 { 364 char *ptr; 365 366 if (((ptr = gzgets(in, buffer, (int)length)) != NULL) && (*ptr == '\0')) 367 ptr = NULL; 368 369 return ptr; 370 } 371 372 static char * 373 makesection(int s) 374 { 375 char sectionbuffer[24]; 376 if (s == -1) 377 return NULL; 378 (void)snprintf(sectionbuffer, sizeof(sectionbuffer), 379 " (%c) - ", sectionext[s]); 380 return estrdup(sectionbuffer); 381 } 382 383 static int 384 pathnamesection(const char *pat, const char *name) 385 { 386 char *ptr, *ext; 387 size_t len = strlen(pat); 388 389 390 while ((ptr = strstr(name, pat)) != NULL) { 391 if ((ext = strchr(sectionext, ptr[len])) != NULL) { 392 return ext - sectionext; 393 } 394 name = ptr + 1; 395 } 396 return -1; 397 } 398 399 400 static int 401 manpagesection(char *name) 402 { 403 char *ptr; 404 405 if ((ptr = strrchr(name, '/')) != NULL) 406 ptr++; 407 else 408 ptr = name; 409 410 while ((ptr = strchr(ptr, '.')) != NULL) { 411 int section; 412 413 ptr++; 414 section = 0; 415 while (sectionext[section] != '\0') 416 if (sectionext[section] == *ptr) 417 return section; 418 else 419 section++; 420 } 421 return -1; 422 } 423 424 static char * 425 createsectionstring(char *section_id) 426 { 427 char *section; 428 429 if (asprintf(§ion, " (%s) - ", section_id) < 0) 430 err(EXIT_FAILURE, "malloc failed"); 431 return section; 432 } 433 434 static void 435 addmanpage(manpage **tree,ino_t inode,char *name, size_t sdoff, size_t sdlen) 436 { 437 manpage *mp; 438 439 while ((mp = *tree) != NULL) { 440 if (mp->mp_inode == inode) 441 return; 442 tree = inode < mp->mp_inode ? &mp->mp_left : &mp->mp_right; 443 } 444 445 mp = emalloc(sizeof(manpage) + strlen(name)); 446 mp->mp_left = NULL; 447 mp->mp_right = NULL; 448 mp->mp_inode = inode; 449 mp->mp_sdoff = sdoff; 450 mp->mp_sdlen = sdlen; 451 (void)strcpy(mp->mp_name, name); 452 *tree = mp; 453 } 454 455 static void 456 addwhatis(whatis **tree, char *data, char *prefix) 457 { 458 whatis *wi; 459 int result; 460 461 while (ISSPACE(*data)) 462 data++; 463 464 if (*data == '/') { 465 char *ptr; 466 467 ptr = ++data; 468 while ((*ptr != '\0') && !ISSPACE(*ptr)) 469 if (*ptr++ == '/') 470 data = ptr; 471 } 472 473 while ((wi = *tree) != NULL) { 474 result = strcmp(data, wi->wi_data); 475 if (result == 0) return; 476 tree = result < 0 ? &wi->wi_left : &wi->wi_right; 477 } 478 479 wi = emalloc(sizeof(whatis) + strlen(prefix)); 480 481 wi->wi_left = NULL; 482 wi->wi_right = NULL; 483 wi->wi_data = data; 484 if (prefix[0] != '\0') 485 (void) strcpy(wi->wi_prefix, prefix); 486 else 487 wi->wi_prefix[0] = '\0'; 488 *tree = wi; 489 } 490 491 static void 492 catpreprocess(char *from) 493 { 494 char *to; 495 496 to = from; 497 while (ISSPACE(*from)) from++; 498 499 while (*from != '\0') 500 if (ISSPACE(*from)) { 501 while (ISSPACE(*++from)); 502 if (*from != '\0') 503 *to++ = ' '; 504 } 505 else if (*(from + 1) == '\b') 506 from += 2; 507 else 508 *to++ = *from++; 509 510 *to = '\0'; 511 } 512 513 static char * 514 makewhatisline(const char *file, const char *line, const char *section) 515 { 516 static const char *del[] = { 517 " - ", 518 " -- ", 519 "- ", 520 " -", 521 NULL 522 }; 523 size_t i, pos; 524 size_t llen, slen, dlen; 525 char *result, *ptr; 526 527 ptr = NULL; 528 if (section == NULL) { 529 if (dowarn) 530 warnx("%s: No section provided for `%s'", file, line); 531 return estrdup(line); 532 } 533 534 for (i = 0; del[i]; i++) 535 if ((ptr = strstr(line, del[i])) != NULL) 536 break; 537 538 if (del[i] == NULL) { 539 if (dowarn) 540 warnx("%s: Bad format line `%s'", file, line); 541 return estrdup(line); 542 } 543 544 slen = strlen(section); 545 llen = strlen(line); 546 dlen = strlen(del[i]); 547 548 result = emalloc(llen - dlen + slen + 1); 549 pos = ptr - line; 550 551 (void)memcpy(result, line, pos); 552 (void)memcpy(&result[pos], section, slen); 553 (void)strcpy(&result[pos + slen], &line[pos + dlen]); 554 return result; 555 } 556 557 static char * 558 parsecatpage(const char *name, gzFile *in) 559 { 560 char buffer[8192]; 561 char *section, *ptr, *last; 562 size_t size; 563 564 do { 565 if (GetS(in, buffer, sizeof(buffer)) == NULL) 566 return NULL; 567 } 568 while (buffer[0] == '\n'); 569 570 section = NULL; 571 if ((ptr = strchr(buffer, '(')) != NULL) { 572 if ((last = strchr(ptr + 1, ')')) !=NULL) { 573 size_t length; 574 575 length = last - ptr + 1; 576 section = emalloc(length + 5); 577 *section = ' '; 578 (void) memcpy(section + 1, ptr, length); 579 (void) strcpy(section + 1 + length, " - "); 580 } 581 } 582 583 for (;;) { 584 if (GetS(in, buffer, sizeof(buffer)) == NULL) { 585 free(section); 586 return NULL; 587 } 588 catpreprocess(buffer); 589 if (strncmp(buffer, "NAME", 4) == 0) 590 break; 591 } 592 if (section == NULL) 593 section = makesection(pathnamesection("/cat", name)); 594 595 ptr = last = buffer; 596 size = sizeof(buffer) - 1; 597 while ((size > 0) && (GetS(in, ptr, size) != NULL)) { 598 int length; 599 600 catpreprocess(ptr); 601 602 length = strlen(ptr); 603 if (length == 0) { 604 *last = '\0'; 605 606 ptr = makewhatisline(name, buffer, section); 607 free(section); 608 return ptr; 609 } 610 if ((length > 1) && (ptr[length - 1] == '-') && 611 ISALPHA(ptr[length - 2])) 612 last = &ptr[--length]; 613 else { 614 last = &ptr[length++]; 615 *last = ' '; 616 } 617 618 ptr += length; 619 size -= length; 620 } 621 622 free(section); 623 624 return NULL; 625 } 626 627 static int 628 manpreprocess(char *line) 629 { 630 char *from, *to; 631 632 to = from = line; 633 while (ISSPACE(*from)) 634 from++; 635 if (strncmp(from, ".\\\"", 3) == 0) 636 return 1; 637 638 while (*from != '\0') 639 if (ISSPACE(*from)) { 640 while (ISSPACE(*++from)); 641 if ((*from != '\0') && (*from != ',')) 642 *to++ = ' '; 643 } else if (*from == '\\') { 644 switch (*++from) { 645 case '\0': 646 case '-': 647 break; 648 case 'f': 649 case 's': 650 from++; 651 if ((*from=='+') || (*from=='-')) 652 from++; 653 while (ISDIGIT(*from)) 654 from++; 655 break; 656 default: 657 from++; 658 } 659 } else { 660 if (*from == '"') 661 from++; 662 else 663 *to++ = *from++; 664 } 665 666 *to = '\0'; 667 668 if (strncasecmp(line, ".Xr", 3) == 0) { 669 char *sect; 670 671 from = line + 3; 672 if (ISSPACE(*from)) 673 from++; 674 675 if ((sect = findwhitespace(from)) != NULL) { 676 size_t length; 677 char *trail; 678 679 *sect++ = '\0'; 680 if ((trail = findwhitespace(sect)) != NULL) 681 *trail++ = '\0'; 682 length = strlen(from); 683 (void) memmove(line, from, length); 684 line[length++] = '('; 685 to = &line[length]; 686 length = strlen(sect); 687 (void) memmove(to, sect, length); 688 if (trail == NULL) { 689 (void) strcpy(&to[length], ")"); 690 } else { 691 to += length; 692 *to++ = ')'; 693 length = strlen(trail); 694 (void) memmove(to, trail, length + 1); 695 } 696 } 697 } 698 699 return 0; 700 } 701 702 static char * 703 nroff(const char *inname, gzFile *in) 704 { 705 char tempname[MAXPATHLEN], buffer[65536], *data; 706 int tempfd, bytes, pipefd[2], status; 707 static int devnull = -1; 708 pid_t child; 709 710 if (gzrewind(in) < 0) 711 err(EXIT_FAILURE, "Cannot rewind pipe"); 712 713 if ((devnull < 0) && 714 ((devnull = open(_PATH_DEVNULL, O_WRONLY, 0)) < 0)) 715 err(EXIT_FAILURE, "Cannot open `/dev/null'"); 716 717 (void)strlcpy(tempname, _PATH_TMP "makewhatis.XXXXXX", 718 sizeof(tempname)); 719 if ((tempfd = mkstemp(tempname)) == -1) 720 err(EXIT_FAILURE, "Cannot create temp file"); 721 722 while ((bytes = gzread(in, buffer, sizeof(buffer))) > 0) 723 if (write(tempfd, buffer, (size_t)bytes) != bytes) { 724 bytes = -1; 725 break; 726 } 727 728 if (bytes < 0) { 729 (void)close(tempfd); 730 (void)unlink(tempname); 731 err(EXIT_FAILURE, "Read from pipe failed"); 732 } 733 if (lseek(tempfd, (off_t)0, SEEK_SET) == (off_t)-1) { 734 (void)close(tempfd); 735 (void)unlink(tempname); 736 err(EXIT_FAILURE, "Cannot rewind temp file"); 737 } 738 if (pipe(pipefd) == -1) { 739 (void)close(tempfd); 740 (void)unlink(tempname); 741 err(EXIT_FAILURE, "Cannot create pipe"); 742 } 743 744 switch (child = vfork()) { 745 case -1: 746 (void)close(pipefd[1]); 747 (void)close(pipefd[0]); 748 (void)close(tempfd); 749 (void)unlink(tempname); 750 err(EXIT_FAILURE, "Fork failed"); 751 /* NOTREACHED */ 752 case 0: 753 (void)close(pipefd[0]); 754 if (tempfd != STDIN_FILENO) { 755 (void)dup2(tempfd, STDIN_FILENO); 756 (void)close(tempfd); 757 } 758 if (pipefd[1] != STDOUT_FILENO) { 759 (void)dup2(pipefd[1], STDOUT_FILENO); 760 (void)close(pipefd[1]); 761 } 762 if (devnull != STDERR_FILENO) { 763 (void)dup2(devnull, STDERR_FILENO); 764 (void)close(devnull); 765 } 766 (void)execlp(NROFF, NROFF, "-S", "-man", NULL); 767 _exit(EXIT_FAILURE); 768 /*NOTREACHED*/ 769 default: 770 (void)close(pipefd[1]); 771 (void)close(tempfd); 772 break; 773 } 774 775 if ((in = gzdopen(pipefd[0], "r")) == NULL) { 776 if (errno == 0) 777 errno = ENOMEM; 778 (void)close(pipefd[0]); 779 (void)kill(child, SIGTERM); 780 while (waitpid(child, NULL, 0) != child); 781 (void)unlink(tempname); 782 err(EXIT_FAILURE, "Cannot read from pipe"); 783 } 784 785 data = parsecatpage(inname, in); 786 while (gzread(in, buffer, sizeof(buffer)) > 0); 787 (void)gzclose(in); 788 789 while (waitpid(child, &status, 0) != child); 790 if ((data != NULL) && 791 !(WIFEXITED(status) && (WEXITSTATUS(status) == 0))) { 792 free(data); 793 errx(EXIT_FAILURE, NROFF " on `%s' exited with %d status", 794 inname, WEXITSTATUS(status)); 795 } 796 797 (void)unlink(tempname); 798 return data; 799 } 800 801 static char * 802 parsemanpage(const char *name, gzFile *in, int defaultsection) 803 { 804 char *section, buffer[8192], *ptr; 805 806 section = NULL; 807 do { 808 if (GetS(in, buffer, sizeof(buffer) - 1) == NULL) { 809 free(section); 810 return NULL; 811 } 812 if (manpreprocess(buffer)) 813 continue; 814 if (strncasecmp(buffer, ".Dt", 3) == 0) { 815 char *end; 816 817 ptr = &buffer[3]; 818 if (ISSPACE(*ptr)) 819 ptr++; 820 if ((ptr = findwhitespace(ptr)) == NULL) 821 continue; 822 823 if ((end = findwhitespace(++ptr)) != NULL) 824 *end = '\0'; 825 826 free(section); 827 section = createsectionstring(ptr); 828 } 829 else if (strncasecmp(buffer, ".TH", 3) == 0) { 830 ptr = &buffer[3]; 831 while (ISSPACE(*ptr)) 832 ptr++; 833 if ((ptr = findwhitespace(ptr)) != NULL) { 834 char *next; 835 836 while (ISSPACE(*ptr)) 837 ptr++; 838 if ((next = findwhitespace(ptr)) != NULL) 839 *next = '\0'; 840 free(section); 841 section = createsectionstring(ptr); 842 } 843 } 844 else if (strncasecmp(buffer, ".Ds", 3) == 0) { 845 free(section); 846 return NULL; 847 } 848 } while (strncasecmp(buffer, ".Sh NAME", 8) != 0); 849 850 do { 851 if (GetS(in, buffer, sizeof(buffer) - 1) == NULL) { 852 free(section); 853 return NULL; 854 } 855 } while (manpreprocess(buffer)); 856 857 if (strncasecmp(buffer, ".Nm", 3) == 0) { 858 size_t length, offset; 859 860 ptr = &buffer[3]; 861 while (ISSPACE(*ptr)) 862 ptr++; 863 864 length = strlen(ptr); 865 if ((length > 1) && (ptr[length - 1] == ',') && 866 ISSPACE(ptr[length - 2])) { 867 ptr[--length] = '\0'; 868 ptr[length - 1] = ','; 869 } 870 (void) memmove(buffer, ptr, length + 1); 871 872 offset = length + 3; 873 ptr = &buffer[offset]; 874 for (;;) { 875 size_t more; 876 877 if ((sizeof(buffer) == offset) || 878 (GetS(in, ptr, sizeof(buffer) - offset) 879 == NULL)) { 880 free(section); 881 return NULL; 882 } 883 if (manpreprocess(ptr)) 884 continue; 885 886 if (strncasecmp(ptr, ".Nm", 3) != 0) break; 887 888 ptr += 3; 889 if (ISSPACE(*ptr)) 890 ptr++; 891 892 buffer[length++] = ' '; 893 more = strlen(ptr); 894 if ((more > 1) && (ptr[more - 1] == ',') && 895 ISSPACE(ptr[more - 2])) { 896 ptr[--more] = '\0'; 897 ptr[more - 1] = ','; 898 } 899 900 (void) memmove(&buffer[length], ptr, more + 1); 901 length += more; 902 offset = length + 3; 903 904 ptr = &buffer[offset]; 905 } 906 907 if (strncasecmp(ptr, ".Nd", 3) == 0) { 908 (void) strlcpy(&buffer[length], " -", 909 sizeof(buffer) - length); 910 911 while (strncasecmp(ptr, ".Sh", 3) != 0) { 912 int more; 913 914 if (*ptr == '.') { 915 char *space; 916 917 if (strncasecmp(ptr, ".Nd", 3) != 0 || 918 strchr(ptr, '[') != NULL) { 919 free(section); 920 return NULL; 921 } 922 space = findwhitespace(ptr); 923 if (space == NULL) { 924 ptr = ""; 925 } else { 926 space++; 927 (void) strmove(ptr, space); 928 } 929 } 930 931 if (*ptr != '\0') { 932 buffer[offset - 1] = ' '; 933 more = strlen(ptr) + 1; 934 offset += more; 935 } 936 ptr = &buffer[offset]; 937 if ((sizeof(buffer) == offset) || 938 (GetS(in, ptr, sizeof(buffer) - offset) 939 == NULL)) { 940 free(section); 941 return NULL; 942 } 943 if (manpreprocess(ptr)) 944 *ptr = '\0'; 945 } 946 } 947 } 948 else { 949 int offset; 950 951 if (*buffer == '.') { 952 char *space; 953 954 if ((space = findwhitespace(&buffer[1])) == NULL) { 955 free(section); 956 return NULL; 957 } 958 space++; 959 (void) strmove(buffer, space); 960 } 961 962 offset = strlen(buffer) + 1; 963 for (;;) { 964 int more; 965 966 ptr = &buffer[offset]; 967 if ((sizeof(buffer) == offset) || 968 (GetS(in, ptr, sizeof(buffer) - offset) 969 == NULL)) { 970 free(section); 971 return NULL; 972 } 973 if (manpreprocess(ptr) || (*ptr == '\0')) 974 continue; 975 976 if ((strncasecmp(ptr, ".Sh", 3) == 0) || 977 (strncasecmp(ptr, ".Ss", 3) == 0)) 978 break; 979 980 if (*ptr == '.') { 981 char *space; 982 983 if ((space = findwhitespace(ptr)) == NULL) { 984 continue; 985 } 986 987 space++; 988 (void) memmove(ptr, space, strlen(space) + 1); 989 } 990 991 buffer[offset - 1] = ' '; 992 more = strlen(ptr); 993 if ((more > 1) && (ptr[more - 1] == ',') && 994 ISSPACE(ptr[more - 2])) { 995 ptr[more - 1] = '\0'; 996 ptr[more - 2] = ','; 997 } 998 else more++; 999 offset += more; 1000 } 1001 } 1002 1003 if (section == NULL) 1004 section = makesection(defaultsection); 1005 1006 ptr = makewhatisline(name, buffer, section); 1007 free(section); 1008 return ptr; 1009 } 1010 1011 static char * 1012 getwhatisdata(char *name) 1013 { 1014 gzFile *in; 1015 char *data; 1016 int section; 1017 1018 if ((in = gzopen(name, "r")) == NULL) { 1019 if (errno == 0) 1020 errno = ENOMEM; 1021 err(EXIT_FAILURE, "Cannot open `%s'", name); 1022 /* NOTREACHED */ 1023 } 1024 1025 section = manpagesection(name); 1026 if (section == 0) { 1027 data = parsecatpage(name, in); 1028 } else { 1029 data = parsemanpage(name, in, section); 1030 if (data == NULL) 1031 data = nroff(name, in); 1032 } 1033 1034 (void) gzclose(in); 1035 return data; 1036 } 1037 1038 static void 1039 processmanpages(manpage **source, whatis **dest) 1040 { 1041 manpage *mp; 1042 char sd[128]; 1043 1044 mp = *source; 1045 *source = NULL; 1046 1047 while (mp != NULL) { 1048 manpage *obsolete; 1049 char *data; 1050 1051 if (mp->mp_left != NULL) 1052 processmanpages(&mp->mp_left,dest); 1053 1054 if ((data = getwhatisdata(mp->mp_name)) != NULL) { 1055 /* Pass eventual directory prefix to addwhatis() */ 1056 if (mp->mp_sdlen > 0 && mp->mp_sdlen < sizeof(sd)-1) 1057 strlcpy(sd, &mp->mp_name[mp->mp_sdoff], 1058 mp->mp_sdlen); 1059 else 1060 sd[0] = '\0'; 1061 1062 addwhatis(dest, data, sd); 1063 } 1064 1065 obsolete = mp; 1066 mp = mp->mp_right; 1067 free(obsolete); 1068 } 1069 } 1070 1071 static void 1072 dumpwhatis(FILE *out, whatis *tree) 1073 { 1074 while (tree != NULL) { 1075 if (tree->wi_left) 1076 dumpwhatis(out, tree->wi_left); 1077 1078 if ((tree->wi_data[0] && fputs(tree->wi_prefix, out) == EOF) || 1079 (fputs(tree->wi_data, out) == EOF) || 1080 (fputc('\n', out) == EOF)) 1081 err(EXIT_FAILURE, "Write failed"); 1082 1083 tree = tree->wi_right; 1084 } 1085 } 1086