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