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