makewhatis.c revision 1.21 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