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