1 /* $NetBSD: makewhatis.c,v 1.21 2002/01/31 22:43:41 tv 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.21 2002/01/31 22:43:41 tv 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/stat.h> 56 #include <sys/wait.h> 57 58 #include <ctype.h> 59 #include <err.h> 60 #include <errno.h> 61 #include <fcntl.h> 62 #include <fts.h> 63 #include <locale.h> 64 #include <paths.h> 65 #include <signal.h> 66 #include <stdio.h> 67 #include <stdlib.h> 68 #include <string.h> 69 #include <unistd.h> 70 #include <zlib.h> 71 72 typedef struct manpagestruct manpage; 73 struct manpagestruct { 74 manpage *mp_left,*mp_right; 75 ino_t mp_inode; 76 char mp_name[1]; 77 }; 78 79 typedef struct whatisstruct whatis; 80 struct whatisstruct { 81 whatis *wi_left,*wi_right; 82 char *wi_data; 83 }; 84 85 int main(int, char **); 86 char *findwhitespace(char *); 87 char *strmove(char *,char *); 88 char *GetS(gzFile, char *, size_t); 89 int manpagesection(char *); 90 char *createsectionstring(char *); 91 void addmanpage(manpage **, ino_t, char *); 92 void addwhatis(whatis **, char *); 93 char *replacestring(char *, char *, char *); 94 void catpreprocess(char *); 95 char *parsecatpage(gzFile *); 96 int manpreprocess(char *); 97 char *nroff(gzFile *); 98 char *parsemanpage(gzFile *, int); 99 char *getwhatisdata(char *); 100 void processmanpages(manpage **,whatis **); 101 void dumpwhatis(FILE *, whatis *); 102 void *emalloc(size_t); 103 char *estrdup(const char *); 104 105 char *default_manpath[] = { 106 "/usr/share/man", 107 NULL 108 }; 109 110 char sectionext[] = "0123456789ln"; 111 char whatisdb[] = "whatis.db"; 112 113 int 114 main(int argc, char **argv) 115 { 116 char **manpath; 117 FTS *fts; 118 FTSENT *fe; 119 manpage *source; 120 whatis *dest; 121 FILE *out; 122 123 (void)setlocale(LC_ALL, ""); 124 125 manpath = (argc < 2) ? default_manpath : &argv[1]; 126 127 if ((fts = fts_open(manpath, FTS_LOGICAL, NULL)) == NULL) 128 err(EXIT_FAILURE, "Cannot open `%s'", *manpath); 129 130 source = NULL; 131 while ((fe = fts_read(fts)) != NULL) { 132 switch (fe->fts_info) { 133 case FTS_F: 134 if (manpagesection(fe->fts_path) >= 0) 135 addmanpage(&source, fe->fts_statp->st_ino, 136 fe->fts_path); 137 /*FALLTHROUGH*/ 138 case FTS_D: 139 case FTS_DC: 140 case FTS_DEFAULT: 141 case FTS_DP: 142 case FTS_SLNONE: 143 break; 144 default: 145 errno = fe->fts_errno; 146 err(EXIT_FAILURE, "Error reading `%s'", fe->fts_path); 147 } 148 } 149 150 (void)fts_close(fts); 151 152 dest = NULL; 153 processmanpages(&source, &dest); 154 155 if (chdir(manpath[0]) == -1) 156 err(EXIT_FAILURE, "Cannot change dir to `%s'", manpath[0]); 157 158 (void)unlink(whatisdb); 159 if ((out = fopen(whatisdb, "w")) == NULL) 160 err(EXIT_FAILURE, "Cannot open `%s'", whatisdb); 161 162 dumpwhatis(out, dest); 163 if (fchmod(fileno(out), S_IRUSR|S_IRGRP|S_IROTH) == -1) 164 err(EXIT_FAILURE, "Cannot chmod `%s'", whatisdb); 165 if (fclose(out) != 0) 166 err(EXIT_FAILURE, "Cannot close `%s'", whatisdb); 167 168 return EXIT_SUCCESS; 169 } 170 171 char * 172 findwhitespace(char *str) 173 { 174 while (!isspace((unsigned char)*str)) 175 if (*str++ == '\0') { 176 str = NULL; 177 break; 178 } 179 180 return str; 181 } 182 183 char 184 *strmove(char *dest,char *src) 185 { 186 return memmove(dest, src, strlen(src) + 1); 187 } 188 189 char * 190 GetS(gzFile in, char *buffer, size_t length) 191 { 192 char *ptr; 193 194 if (((ptr = gzgets(in, buffer, (int)length)) != NULL) && (*ptr == '\0')) 195 ptr = NULL; 196 197 return ptr; 198 } 199 200 int 201 manpagesection(char *name) 202 { 203 char *ptr; 204 205 if ((ptr = strrchr(name, '/')) != NULL) 206 ptr++; 207 else 208 ptr = name; 209 210 while ((ptr = strchr(ptr, '.')) != NULL) { 211 int section; 212 213 ptr++; 214 section=0; 215 while (sectionext[section] != '\0') 216 if (sectionext[section] == *ptr) 217 return section; 218 else 219 section++; 220 } 221 222 return -1; 223 } 224 225 char * 226 createsectionstring(char *section_id) 227 { 228 char *section = emalloc(strlen(section_id) + 7); 229 section[0] = ' '; 230 section[1] = '('; 231 (void)strcat(strcpy(§ion[2], section_id), ") - "); 232 return section; 233 } 234 235 void 236 addmanpage(manpage **tree,ino_t inode,char *name) 237 { 238 manpage *mp; 239 240 while ((mp = *tree) != NULL) { 241 if (mp->mp_inode == inode) 242 return; 243 tree = inode < mp->mp_inode ? &mp->mp_left : &mp->mp_right; 244 } 245 246 mp = emalloc(sizeof(manpage) + strlen(name)); 247 mp->mp_left = NULL; 248 mp->mp_right = NULL; 249 mp->mp_inode = inode; 250 (void)strcpy(mp->mp_name, name); 251 *tree = mp; 252 } 253 254 void 255 addwhatis(whatis **tree, char *data) 256 { 257 whatis *wi; 258 int result; 259 260 while (isspace((unsigned char)*data)) 261 data++; 262 263 if (*data == '/') { 264 char *ptr; 265 266 ptr = ++data; 267 while ((*ptr != '\0') && !isspace((unsigned char)*ptr)) 268 if (*ptr++ == '/') 269 data = ptr; 270 } 271 272 while ((wi = *tree) != NULL) { 273 result = strcmp(data, wi->wi_data); 274 if (result == 0) return; 275 tree = result < 0 ? &wi->wi_left : &wi->wi_right; 276 } 277 278 wi = emalloc(sizeof(whatis) + strlen(data)); 279 280 wi->wi_left = NULL; 281 wi->wi_right = NULL; 282 wi->wi_data = data; 283 *tree = wi; 284 } 285 286 void 287 catpreprocess(char *from) 288 { 289 char *to; 290 291 to = from; 292 while (isspace((unsigned char)*from)) from++; 293 294 while (*from != '\0') 295 if (isspace((unsigned char)*from)) { 296 while (isspace((unsigned char)*++from)); 297 if (*from != '\0') 298 *to++ = ' '; 299 } 300 else if (*(from + 1) == '\10') 301 from += 2; 302 else 303 *to++ = *from++; 304 305 *to = '\0'; 306 } 307 308 char * 309 replacestring(char *string, char *old, char *new) 310 311 { 312 char *ptr, *result; 313 size_t slength, olength, nlength, pos; 314 315 if (new == NULL) 316 return estrdup(string); 317 318 ptr = strstr(string, old); 319 if (ptr == NULL) 320 return estrdup(string); 321 322 slength = strlen(string); 323 olength = strlen(old); 324 nlength = strlen(new); 325 result = emalloc(slength - olength + nlength + 1); 326 327 pos = ptr - string; 328 (void)memcpy(result, string, pos); 329 (void)memcpy(&result[pos], new, nlength); 330 (void)strcpy(&result[pos + nlength], &string[pos + olength]); 331 332 return result; 333 } 334 335 char * 336 parsecatpage(gzFile *in) 337 { 338 char buffer[8192]; 339 char *section, *ptr, *last; 340 size_t size; 341 342 do { 343 if (GetS(in, buffer, sizeof(buffer)) == NULL) 344 return NULL; 345 } 346 while (buffer[0] == '\n'); 347 348 section = NULL; 349 if ((ptr = strchr(buffer, '(')) != NULL) { 350 if ((last = strchr(ptr + 1, ')')) !=NULL) { 351 size_t length; 352 353 length = last - ptr + 1; 354 section = emalloc(length + 5); 355 *section = ' '; 356 (void) memcpy(section + 1, ptr, length); 357 (void) strcpy(section + 1 + length, " - "); 358 } 359 } 360 361 for (;;) { 362 if (GetS(in, buffer, sizeof(buffer)) == NULL) { 363 free(section); 364 return NULL; 365 } 366 catpreprocess(buffer); 367 if (strncmp(buffer, "NAME", 4) == 0) 368 break; 369 } 370 371 ptr = last = buffer; 372 size = sizeof(buffer) - 1; 373 while ((size > 0) && (GetS(in, ptr, size) != NULL)) { 374 int length; 375 376 catpreprocess(ptr); 377 378 length = strlen(ptr); 379 if (length == 0) { 380 *last = '\0'; 381 382 ptr = replacestring(buffer, " - ", section); 383 free(section); 384 return ptr; 385 } 386 if ((length > 1) && (ptr[length - 1] == '-') && 387 isalpha(ptr[length - 2])) 388 last = &ptr[--length]; 389 else { 390 last = &ptr[length++]; 391 *last = ' '; 392 } 393 394 ptr += length; 395 size -= length; 396 } 397 398 free(section); 399 400 return NULL; 401 } 402 403 int 404 manpreprocess(char *line) 405 { 406 char *from, *to; 407 408 to = from = line; 409 while (isspace((unsigned char)*from)) from++; 410 if (strncmp(from, ".\\\"", 3) == 0) 411 return 1; 412 413 while (*from != '\0') 414 if (isspace((unsigned char)*from)) { 415 while (isspace((unsigned char)*++from)); 416 if ((*from != '\0') && (*from != ',')) 417 *to++ = ' '; 418 } 419 else if (*from == '\\') 420 switch (*++from) { 421 case '\0': 422 case '-': 423 break; 424 case 'f': 425 case 's': 426 from++; 427 if ((*from=='+') || (*from=='-')) 428 from++; 429 while (isdigit(*from)) 430 from++; 431 break; 432 default: 433 from++; 434 } 435 else 436 if (*from == '"') 437 from++; 438 else 439 *to++ = *from++; 440 441 *to = '\0'; 442 443 if (strncasecmp(line, ".Xr", 3) == 0) { 444 char *sect; 445 446 from = line + 3; 447 if (isspace((unsigned char)*from)) 448 from++; 449 450 if ((sect = findwhitespace(from)) != NULL) { 451 size_t length; 452 char *trail; 453 454 *sect++ = '\0'; 455 if ((trail = findwhitespace(sect)) != NULL) 456 *trail++ = '\0'; 457 length = strlen(from); 458 (void) memmove(line, from, length); 459 line[length++] = '('; 460 to = &line[length]; 461 length = strlen(sect); 462 (void) memmove(to, sect, length); 463 if (trail == NULL) { 464 (void) strcpy(&to[length], ")"); 465 } else { 466 to += length; 467 *to++ = ')'; 468 length = strlen(trail); 469 (void) memmove(to, trail, length + 1); 470 } 471 } 472 } 473 474 return 0; 475 } 476 477 char * 478 nroff(gzFile *in) 479 { 480 char tempname[MAXPATHLEN], buffer[65536], *data; 481 int tempfd, bytes, pipefd[2], status; 482 static int devnull = -1; 483 pid_t child; 484 485 if (gzrewind(in) < 0) 486 err(EXIT_FAILURE, "Cannot rewind pipe"); 487 488 if ((devnull < 0) && 489 ((devnull = open(_PATH_DEVNULL, O_WRONLY, 0)) < 0)) 490 err(EXIT_FAILURE, "Cannot open `/dev/null'"); 491 492 (void)strcpy(tempname, _PATH_TMP "makewhatis.XXXXXX"); 493 if ((tempfd = mkstemp(tempname)) == -1) 494 err(EXIT_FAILURE, "Cannot create temp file"); 495 496 while ((bytes = gzread(in, buffer, sizeof(buffer))) > 0) 497 if (write(tempfd, buffer, (size_t)bytes) != bytes) { 498 bytes = -1; 499 break; 500 } 501 502 if (bytes < 0) { 503 (void)close(tempfd); 504 (void)unlink(tempname); 505 err(EXIT_FAILURE, "Read from pipe failed"); 506 } 507 if (lseek(tempfd, (off_t)0, SEEK_SET) == (off_t)-1) { 508 (void)close(tempfd); 509 (void)unlink(tempname); 510 err(EXIT_FAILURE, "Cannot rewind temp file"); 511 } 512 if (pipe(pipefd) == -1) { 513 (void)close(tempfd); 514 (void)unlink(tempname); 515 err(EXIT_FAILURE, "Cannot create pipe"); 516 } 517 518 switch (child = vfork()) { 519 case -1: 520 (void)close(pipefd[1]); 521 (void)close(pipefd[0]); 522 (void)close(tempfd); 523 (void)unlink(tempname); 524 err(EXIT_FAILURE, "Fork failed"); 525 /* NOTREACHED */ 526 case 0: 527 (void)close(pipefd[0]); 528 if (tempfd != STDIN_FILENO) { 529 (void)dup2(tempfd, STDIN_FILENO); 530 (void)close(tempfd); 531 } 532 if (pipefd[1] != STDOUT_FILENO) { 533 (void)dup2(pipefd[1], STDOUT_FILENO); 534 (void)close(pipefd[1]); 535 } 536 if (devnull != STDERR_FILENO) { 537 (void)dup2(devnull, STDERR_FILENO); 538 (void)close(devnull); 539 } 540 (void)execlp("nroff", "nroff", "-S", "-man", NULL); 541 _exit(EXIT_FAILURE); 542 /*NOTREACHED*/ 543 default: 544 (void)close(pipefd[1]); 545 (void)close(tempfd); 546 break; 547 } 548 549 if ((in = gzdopen(pipefd[0], "r")) == NULL) { 550 if (errno == 0) 551 errno = ENOMEM; 552 (void)close(pipefd[0]); 553 (void)kill(child, SIGTERM); 554 while (waitpid(child, NULL, 0) != child); 555 (void)unlink(tempname); 556 err(EXIT_FAILURE, "Cannot read from pipe"); 557 } 558 559 data = parsecatpage(in); 560 while (gzread(in, buffer, sizeof(buffer)) > 0); 561 (void)gzclose(in); 562 563 while (waitpid(child, &status, 0) != child); 564 if ((data != NULL) && 565 !(WIFEXITED(status) && (WEXITSTATUS(status) == 0))) { 566 free(data); 567 errx(EXIT_FAILURE, "nroff exited with %d status", 568 WEXITSTATUS(status)); 569 } 570 571 (void)unlink(tempname); 572 return data; 573 } 574 575 char * 576 parsemanpage(gzFile *in, int defaultsection) 577 { 578 char *section, buffer[8192], *ptr; 579 580 section = NULL; 581 do { 582 if (GetS(in, buffer, sizeof(buffer) - 1) == NULL) { 583 free(section); 584 return NULL; 585 } 586 if (manpreprocess(buffer)) 587 continue; 588 if (strncasecmp(buffer, ".Dt", 3) == 0) { 589 char *end; 590 591 ptr = &buffer[3]; 592 if (isspace((unsigned char)*ptr)) 593 ptr++; 594 if ((ptr = findwhitespace(ptr)) == NULL) 595 continue; 596 597 if ((end = findwhitespace(++ptr)) != NULL) 598 *end = '\0'; 599 600 free(section); 601 section = createsectionstring(ptr); 602 } 603 else if (strncasecmp(buffer, ".TH", 3) == 0) { 604 ptr = &buffer[3]; 605 while (isspace((unsigned char)*ptr)) 606 ptr++; 607 if ((ptr = findwhitespace(ptr)) != NULL) { 608 char *next; 609 610 while (isspace((unsigned char)*ptr)) 611 ptr++; 612 if ((next = findwhitespace(ptr)) != NULL) 613 *next = '\0'; 614 free(section); 615 section = createsectionstring(ptr); 616 } 617 } 618 else if (strncasecmp(buffer, ".Ds", 3) == 0) { 619 free(section); 620 return NULL; 621 } 622 } while (strncasecmp(buffer, ".Sh NAME", 8) != 0); 623 624 do { 625 if (GetS(in, buffer, sizeof(buffer) - 1) == NULL) { 626 free(section); 627 return NULL; 628 } 629 } while (manpreprocess(buffer)); 630 631 if (strncasecmp(buffer, ".Nm", 3) == 0) { 632 size_t length, offset; 633 634 ptr = &buffer[3]; 635 while (isspace((unsigned char)*ptr)) 636 ptr++; 637 638 length = strlen(ptr); 639 if ((length > 1) && (ptr[length - 1] == ',') && 640 isspace((unsigned char)ptr[length - 2])) { 641 ptr[--length] = '\0'; 642 ptr[length - 1] = ','; 643 } 644 (void) memmove(buffer, ptr, length + 1); 645 646 offset = length + 3; 647 ptr = &buffer[offset]; 648 for (;;) { 649 size_t more; 650 651 if ((sizeof(buffer) == offset) || 652 (GetS(in, ptr, sizeof(buffer) - offset) 653 == NULL)) { 654 free(section); 655 return NULL; 656 } 657 if (manpreprocess(ptr)) 658 continue; 659 660 if (strncasecmp(ptr, ".Nm", 3) != 0) break; 661 662 ptr += 3; 663 if (isspace((unsigned char)*ptr)) 664 ptr++; 665 666 buffer[length++] = ' '; 667 more = strlen(ptr); 668 if ((more > 1) && (ptr[more - 1] == ',') && 669 isspace((unsigned char)ptr[more - 2])) { 670 ptr[--more] = '\0'; 671 ptr[more - 1] = ','; 672 } 673 674 (void) memmove(&buffer[length], ptr, more + 1); 675 length += more; 676 offset = length + 3; 677 678 ptr = &buffer[offset]; 679 } 680 681 if (strncasecmp(ptr, ".Nd", 3) == 0) { 682 (void) strcpy(&buffer[length], " -"); 683 684 while (strncasecmp(ptr, ".Sh", 3) != 0) { 685 int more; 686 687 if (*ptr == '.') { 688 char *space; 689 690 if (strncasecmp(ptr, ".Nd", 3) != 0) { 691 free(section); 692 return NULL; 693 } 694 space = findwhitespace(ptr); 695 if (space == NULL) 696 ptr = ""; 697 else { 698 space++; 699 (void) strmove(ptr, space); 700 } 701 } 702 703 if (*ptr != '\0') { 704 buffer[offset - 1] = ' '; 705 more = strlen(ptr) + 1; 706 offset += more; 707 } 708 ptr = &buffer[offset]; 709 if ((sizeof(buffer) == offset) || 710 (GetS(in, ptr, sizeof(buffer) - offset) 711 == NULL)) { 712 free(section); 713 return NULL; 714 } 715 if (manpreprocess(ptr)) 716 *ptr = '\0'; 717 } 718 } 719 } 720 else { 721 int offset; 722 723 if (*buffer == '.') { 724 char *space; 725 726 if ((space = findwhitespace(&buffer[1])) == NULL) { 727 free(section); 728 return NULL; 729 } 730 space++; 731 (void) strmove(buffer, space); 732 } 733 734 offset = strlen(buffer) + 1; 735 for (;;) { 736 int more; 737 738 ptr = &buffer[offset]; 739 if ((sizeof(buffer) == offset) || 740 (GetS(in, ptr, sizeof(buffer) - offset) 741 == NULL)) { 742 free(section); 743 return NULL; 744 } 745 if (manpreprocess(ptr) || (*ptr == '\0')) 746 continue; 747 748 if ((strncasecmp(ptr, ".Sh", 3) == 0) || 749 (strncasecmp(ptr, ".Ss", 3) == 0)) 750 break; 751 752 if (*ptr == '.') { 753 char *space; 754 755 if ((space = findwhitespace(ptr)) == NULL) { 756 continue; 757 } 758 759 space++; 760 (void) memmove(ptr, space, strlen(space) + 1); 761 } 762 763 buffer[offset - 1] = ' '; 764 more = strlen(ptr); 765 if ((more > 1) && (ptr[more - 1] == ',') && 766 isspace((unsigned char)ptr[more - 2])) { 767 ptr[more - 1] = '\0'; 768 ptr[more - 2] = ','; 769 } 770 else more++; 771 offset += more; 772 } 773 } 774 775 if (section == NULL) { 776 char sectionbuffer[24]; 777 778 (void) sprintf(sectionbuffer, " (%c) - ", 779 sectionext[defaultsection]); 780 ptr = replacestring(buffer, " - ", sectionbuffer); 781 } 782 else { 783 ptr = replacestring(buffer, " - ", section); 784 free(section); 785 } 786 return ptr; 787 } 788 789 char * 790 getwhatisdata(char *name) 791 { 792 gzFile *in; 793 char *data; 794 int section; 795 796 if ((in = gzopen(name, "r")) == NULL) { 797 if (errno == 0) 798 errno = ENOMEM; 799 err(EXIT_FAILURE, "Cannot open `%s'", name); 800 /* NOTREACHED */ 801 } 802 803 section = manpagesection(name); 804 if (section == 0) 805 data = parsecatpage(in); 806 else { 807 data = parsemanpage(in, section); 808 if (data == NULL) 809 data = nroff(in); 810 } 811 812 (void) gzclose(in); 813 return data; 814 } 815 816 void 817 processmanpages(manpage **source, whatis **dest) 818 { 819 manpage *mp; 820 821 mp = *source; 822 *source = NULL; 823 824 while (mp != NULL) { 825 manpage *obsolete; 826 char *data; 827 828 if (mp->mp_left != NULL) 829 processmanpages(&mp->mp_left,dest); 830 831 if ((data = getwhatisdata(mp->mp_name)) != NULL) 832 addwhatis(dest,data); 833 834 obsolete = mp; 835 mp = mp->mp_right; 836 free(obsolete); 837 } 838 } 839 840 void 841 dumpwhatis(FILE *out, whatis *tree) 842 { 843 while (tree != NULL) { 844 if (tree->wi_left) 845 dumpwhatis(out, tree->wi_left); 846 847 if ((fputs(tree->wi_data, out) == EOF) || 848 (fputc('\n', out) == EOF)) 849 err(EXIT_FAILURE, "Write failed"); 850 851 tree = tree->wi_right; 852 } 853 } 854 855 void * 856 emalloc(size_t len) 857 { 858 void *ptr; 859 if ((ptr = malloc(len)) == NULL) 860 err(EXIT_FAILURE, "malloc %lu failed", (unsigned long)len); 861 return ptr; 862 } 863 864 char * 865 estrdup(const char *str) 866 { 867 char *ptr; 868 if ((ptr = strdup(str)) == NULL) 869 err(EXIT_FAILURE, "strdup failed"); 870 return ptr; 871 } 872