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