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