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