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