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