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