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