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