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