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