makewhatis.c revision 1.37 1 /* $NetBSD: makewhatis.c,v 1.37 2005/09/15 14:20:01 tron 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.37 2005/09/15 14:20:01 tron 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_SLNONE:
304 break;
305 default:
306 errno = fe->fts_errno;
307 err(EXIT_FAILURE, "Error reading `%s'", fe->fts_path);
308 }
309 }
310
311 (void)fts_close(fts);
312
313 dest = NULL;
314 processmanpages(&source, &dest);
315
316 if (chdir(manpath[0]) == -1)
317 err(EXIT_FAILURE, "Cannot change dir to `%s'", manpath[0]);
318
319 (void)unlink(whatisdb);
320 if ((out = fopen(whatisdb, "w")) == NULL)
321 err(EXIT_FAILURE, "Cannot open `%s'", whatisdb);
322
323 dumpwhatis(out, dest);
324 if (fchmod(fileno(out), S_IRUSR|S_IRGRP|S_IROTH) == -1)
325 err(EXIT_FAILURE, "Cannot chmod `%s'", whatisdb);
326 if (fclose(out) != 0)
327 err(EXIT_FAILURE, "Cannot close `%s'", whatisdb);
328
329 return EXIT_SUCCESS;
330 }
331
332 static char *
333 findwhitespace(char *str)
334 {
335 while (!ISSPACE(*str))
336 if (*str++ == '\0') {
337 str = NULL;
338 break;
339 }
340
341 return str;
342 }
343
344 static char
345 *strmove(char *dest,char *src)
346 {
347 return memmove(dest, src, strlen(src) + 1);
348 }
349
350 static char *
351 GetS(gzFile in, char *buffer, size_t length)
352 {
353 char *ptr;
354
355 if (((ptr = gzgets(in, buffer, (int)length)) != NULL) && (*ptr == '\0'))
356 ptr = NULL;
357
358 return ptr;
359 }
360
361 static char *
362 makesection(int s)
363 {
364 char sectionbuffer[24];
365 if (s == -1)
366 return NULL;
367 (void)snprintf(sectionbuffer, sizeof(sectionbuffer),
368 " (%c) - ", sectionext[s]);
369 return estrdup(sectionbuffer);
370 }
371
372 static int
373 pathnamesection(const char *pat, const char *name)
374 {
375 char *ptr, *ext;
376 size_t len = strlen(pat);
377
378
379 while ((ptr = strstr(name, pat)) != NULL) {
380 if ((ext = strchr(sectionext, ptr[len])) != NULL) {
381 return ext - sectionext;
382 }
383 name = ptr + 1;
384 }
385 return -1;
386 }
387
388
389 static int
390 manpagesection(char *name)
391 {
392 char *ptr;
393
394 if ((ptr = strrchr(name, '/')) != NULL)
395 ptr++;
396 else
397 ptr = name;
398
399 while ((ptr = strchr(ptr, '.')) != NULL) {
400 int section;
401
402 ptr++;
403 section = 0;
404 while (sectionext[section] != '\0')
405 if (sectionext[section] == *ptr)
406 return section;
407 else
408 section++;
409 }
410 return -1;
411 }
412
413 static char *
414 createsectionstring(char *section_id)
415 {
416 char *section;
417
418 if (asprintf(§ion, " (%s) - ", section_id) < 0)
419 err(EXIT_FAILURE, "malloc failed");
420 return section;
421 }
422
423 static void
424 addmanpage(manpage **tree,ino_t inode,char *name, size_t sdoff, size_t sdlen)
425 {
426 manpage *mp;
427
428 while ((mp = *tree) != NULL) {
429 if (mp->mp_inode == inode)
430 return;
431 tree = inode < mp->mp_inode ? &mp->mp_left : &mp->mp_right;
432 }
433
434 mp = emalloc(sizeof(manpage) + strlen(name));
435 mp->mp_left = NULL;
436 mp->mp_right = NULL;
437 mp->mp_inode = inode;
438 mp->mp_sdoff = sdoff;
439 mp->mp_sdlen = sdlen;
440 (void)strcpy(mp->mp_name, name);
441 *tree = mp;
442 }
443
444 static void
445 addwhatis(whatis **tree, char *data, char *prefix)
446 {
447 whatis *wi;
448 int result;
449
450 while (ISSPACE(*data))
451 data++;
452
453 if (*data == '/') {
454 char *ptr;
455
456 ptr = ++data;
457 while ((*ptr != '\0') && !ISSPACE(*ptr))
458 if (*ptr++ == '/')
459 data = ptr;
460 }
461
462 while ((wi = *tree) != NULL) {
463 result = strcmp(data, wi->wi_data);
464 if (result == 0) return;
465 tree = result < 0 ? &wi->wi_left : &wi->wi_right;
466 }
467
468 wi = emalloc(sizeof(whatis) + strlen(prefix));
469
470 wi->wi_left = NULL;
471 wi->wi_right = NULL;
472 wi->wi_data = data;
473 if (prefix[0] != '\0')
474 (void) strcpy(wi->wi_prefix, prefix);
475 else
476 wi->wi_prefix[0] = '\0';
477 *tree = wi;
478 }
479
480 static void
481 catpreprocess(char *from)
482 {
483 char *to;
484
485 to = from;
486 while (ISSPACE(*from)) from++;
487
488 while (*from != '\0')
489 if (ISSPACE(*from)) {
490 while (ISSPACE(*++from));
491 if (*from != '\0')
492 *to++ = ' ';
493 }
494 else if (*(from + 1) == '\b')
495 from += 2;
496 else
497 *to++ = *from++;
498
499 *to = '\0';
500 }
501
502 static char *
503 makewhatisline(const char *file, const char *line, const char *section)
504 {
505 static const char *del[] = {
506 " - ",
507 " -- ",
508 "- ",
509 " -",
510 NULL
511 };
512 size_t i, pos;
513 size_t llen, slen, dlen;
514 char *result, *ptr;
515
516 ptr = NULL;
517 if (section == NULL) {
518 if (dowarn)
519 warnx("%s: No section provided for `%s'", file, line);
520 return estrdup(line);
521 }
522
523 for (i = 0; del[i]; i++)
524 if ((ptr = strstr(line, del[i])) != NULL)
525 break;
526
527 if (del[i] == NULL) {
528 if (dowarn)
529 warnx("%s: Bad format line `%s'", file, line);
530 return estrdup(line);
531 }
532
533 slen = strlen(section);
534 llen = strlen(line);
535 dlen = strlen(del[i]);
536
537 result = emalloc(llen - dlen + slen + 1);
538 pos = ptr - line;
539
540 (void)memcpy(result, line, pos);
541 (void)memcpy(&result[pos], section, slen);
542 (void)strcpy(&result[pos + slen], &line[pos + dlen]);
543 return result;
544 }
545
546 static char *
547 parsecatpage(const char *name, gzFile *in)
548 {
549 char buffer[8192];
550 char *section, *ptr, *last;
551 size_t size;
552
553 do {
554 if (GetS(in, buffer, sizeof(buffer)) == NULL)
555 return NULL;
556 }
557 while (buffer[0] == '\n');
558
559 section = NULL;
560 if ((ptr = strchr(buffer, '(')) != NULL) {
561 if ((last = strchr(ptr + 1, ')')) !=NULL) {
562 size_t length;
563
564 length = last - ptr + 1;
565 section = emalloc(length + 5);
566 *section = ' ';
567 (void) memcpy(section + 1, ptr, length);
568 (void) strcpy(section + 1 + length, " - ");
569 }
570 }
571
572 for (;;) {
573 if (GetS(in, buffer, sizeof(buffer)) == NULL) {
574 free(section);
575 return NULL;
576 }
577 catpreprocess(buffer);
578 if (strncmp(buffer, "NAME", 4) == 0)
579 break;
580 }
581 if (section == NULL)
582 section = makesection(pathnamesection("/cat", name));
583
584 ptr = last = buffer;
585 size = sizeof(buffer) - 1;
586 while ((size > 0) && (GetS(in, ptr, size) != NULL)) {
587 int length;
588
589 catpreprocess(ptr);
590
591 length = strlen(ptr);
592 if (length == 0) {
593 *last = '\0';
594
595 ptr = makewhatisline(name, buffer, section);
596 free(section);
597 return ptr;
598 }
599 if ((length > 1) && (ptr[length - 1] == '-') &&
600 ISALPHA(ptr[length - 2]))
601 last = &ptr[--length];
602 else {
603 last = &ptr[length++];
604 *last = ' ';
605 }
606
607 ptr += length;
608 size -= length;
609 }
610
611 free(section);
612
613 return NULL;
614 }
615
616 static int
617 manpreprocess(char *line)
618 {
619 char *from, *to;
620
621 to = from = line;
622 while (ISSPACE(*from))
623 from++;
624 if (strncmp(from, ".\\\"", 3) == 0)
625 return 1;
626
627 while (*from != '\0')
628 if (ISSPACE(*from)) {
629 while (ISSPACE(*++from));
630 if ((*from != '\0') && (*from != ','))
631 *to++ = ' ';
632 } else if (*from == '\\') {
633 switch (*++from) {
634 case '\0':
635 case '-':
636 break;
637 case 'f':
638 case 's':
639 from++;
640 if ((*from=='+') || (*from=='-'))
641 from++;
642 while (ISDIGIT(*from))
643 from++;
644 break;
645 default:
646 from++;
647 }
648 } else {
649 if (*from == '"')
650 from++;
651 else
652 *to++ = *from++;
653 }
654
655 *to = '\0';
656
657 if (strncasecmp(line, ".Xr", 3) == 0) {
658 char *sect;
659
660 from = line + 3;
661 if (ISSPACE(*from))
662 from++;
663
664 if ((sect = findwhitespace(from)) != NULL) {
665 size_t length;
666 char *trail;
667
668 *sect++ = '\0';
669 if ((trail = findwhitespace(sect)) != NULL)
670 *trail++ = '\0';
671 length = strlen(from);
672 (void) memmove(line, from, length);
673 line[length++] = '(';
674 to = &line[length];
675 length = strlen(sect);
676 (void) memmove(to, sect, length);
677 if (trail == NULL) {
678 (void) strcpy(&to[length], ")");
679 } else {
680 to += length;
681 *to++ = ')';
682 length = strlen(trail);
683 (void) memmove(to, trail, length + 1);
684 }
685 }
686 }
687
688 return 0;
689 }
690
691 static char *
692 nroff(const char *inname, gzFile *in)
693 {
694 char tempname[MAXPATHLEN], buffer[65536], *data;
695 int tempfd, bytes, pipefd[2], status;
696 static int devnull = -1;
697 pid_t child;
698
699 if (gzrewind(in) < 0)
700 err(EXIT_FAILURE, "Cannot rewind pipe");
701
702 if ((devnull < 0) &&
703 ((devnull = open(_PATH_DEVNULL, O_WRONLY, 0)) < 0))
704 err(EXIT_FAILURE, "Cannot open `/dev/null'");
705
706 (void)strlcpy(tempname, _PATH_TMP "makewhatis.XXXXXX",
707 sizeof(tempname));
708 if ((tempfd = mkstemp(tempname)) == -1)
709 err(EXIT_FAILURE, "Cannot create temp file");
710
711 while ((bytes = gzread(in, buffer, sizeof(buffer))) > 0)
712 if (write(tempfd, buffer, (size_t)bytes) != bytes) {
713 bytes = -1;
714 break;
715 }
716
717 if (bytes < 0) {
718 (void)close(tempfd);
719 (void)unlink(tempname);
720 err(EXIT_FAILURE, "Read from pipe failed");
721 }
722 if (lseek(tempfd, (off_t)0, SEEK_SET) == (off_t)-1) {
723 (void)close(tempfd);
724 (void)unlink(tempname);
725 err(EXIT_FAILURE, "Cannot rewind temp file");
726 }
727 if (pipe(pipefd) == -1) {
728 (void)close(tempfd);
729 (void)unlink(tempname);
730 err(EXIT_FAILURE, "Cannot create pipe");
731 }
732
733 switch (child = vfork()) {
734 case -1:
735 (void)close(pipefd[1]);
736 (void)close(pipefd[0]);
737 (void)close(tempfd);
738 (void)unlink(tempname);
739 err(EXIT_FAILURE, "Fork failed");
740 /* NOTREACHED */
741 case 0:
742 (void)close(pipefd[0]);
743 if (tempfd != STDIN_FILENO) {
744 (void)dup2(tempfd, STDIN_FILENO);
745 (void)close(tempfd);
746 }
747 if (pipefd[1] != STDOUT_FILENO) {
748 (void)dup2(pipefd[1], STDOUT_FILENO);
749 (void)close(pipefd[1]);
750 }
751 if (devnull != STDERR_FILENO) {
752 (void)dup2(devnull, STDERR_FILENO);
753 (void)close(devnull);
754 }
755 (void)execlp(NROFF, NROFF, "-S", "-man", NULL);
756 _exit(EXIT_FAILURE);
757 /*NOTREACHED*/
758 default:
759 (void)close(pipefd[1]);
760 (void)close(tempfd);
761 break;
762 }
763
764 if ((in = gzdopen(pipefd[0], "r")) == NULL) {
765 if (errno == 0)
766 errno = ENOMEM;
767 (void)close(pipefd[0]);
768 (void)kill(child, SIGTERM);
769 while (waitpid(child, NULL, 0) != child);
770 (void)unlink(tempname);
771 err(EXIT_FAILURE, "Cannot read from pipe");
772 }
773
774 data = parsecatpage(inname, in);
775 while (gzread(in, buffer, sizeof(buffer)) > 0);
776 (void)gzclose(in);
777
778 while (waitpid(child, &status, 0) != child);
779 if ((data != NULL) &&
780 !(WIFEXITED(status) && (WEXITSTATUS(status) == 0))) {
781 free(data);
782 errx(EXIT_FAILURE, NROFF " on `%s' exited with %d status",
783 inname, WEXITSTATUS(status));
784 }
785
786 (void)unlink(tempname);
787 return data;
788 }
789
790 static char *
791 parsemanpage(const char *name, gzFile *in, int defaultsection)
792 {
793 char *section, buffer[8192], *ptr;
794
795 section = NULL;
796 do {
797 if (GetS(in, buffer, sizeof(buffer) - 1) == NULL) {
798 free(section);
799 return NULL;
800 }
801 if (manpreprocess(buffer))
802 continue;
803 if (strncasecmp(buffer, ".Dt", 3) == 0) {
804 char *end;
805
806 ptr = &buffer[3];
807 if (ISSPACE(*ptr))
808 ptr++;
809 if ((ptr = findwhitespace(ptr)) == NULL)
810 continue;
811
812 if ((end = findwhitespace(++ptr)) != NULL)
813 *end = '\0';
814
815 free(section);
816 section = createsectionstring(ptr);
817 }
818 else if (strncasecmp(buffer, ".TH", 3) == 0) {
819 ptr = &buffer[3];
820 while (ISSPACE(*ptr))
821 ptr++;
822 if ((ptr = findwhitespace(ptr)) != NULL) {
823 char *next;
824
825 while (ISSPACE(*ptr))
826 ptr++;
827 if ((next = findwhitespace(ptr)) != NULL)
828 *next = '\0';
829 free(section);
830 section = createsectionstring(ptr);
831 }
832 }
833 else if (strncasecmp(buffer, ".Ds", 3) == 0) {
834 free(section);
835 return NULL;
836 }
837 } while (strncasecmp(buffer, ".Sh NAME", 8) != 0);
838
839 do {
840 if (GetS(in, buffer, sizeof(buffer) - 1) == NULL) {
841 free(section);
842 return NULL;
843 }
844 } while (manpreprocess(buffer));
845
846 if (strncasecmp(buffer, ".Nm", 3) == 0) {
847 size_t length, offset;
848
849 ptr = &buffer[3];
850 while (ISSPACE(*ptr))
851 ptr++;
852
853 length = strlen(ptr);
854 if ((length > 1) && (ptr[length - 1] == ',') &&
855 ISSPACE(ptr[length - 2])) {
856 ptr[--length] = '\0';
857 ptr[length - 1] = ',';
858 }
859 (void) memmove(buffer, ptr, length + 1);
860
861 offset = length + 3;
862 ptr = &buffer[offset];
863 for (;;) {
864 size_t more;
865
866 if ((sizeof(buffer) == offset) ||
867 (GetS(in, ptr, sizeof(buffer) - offset)
868 == NULL)) {
869 free(section);
870 return NULL;
871 }
872 if (manpreprocess(ptr))
873 continue;
874
875 if (strncasecmp(ptr, ".Nm", 3) != 0) break;
876
877 ptr += 3;
878 if (ISSPACE(*ptr))
879 ptr++;
880
881 buffer[length++] = ' ';
882 more = strlen(ptr);
883 if ((more > 1) && (ptr[more - 1] == ',') &&
884 ISSPACE(ptr[more - 2])) {
885 ptr[--more] = '\0';
886 ptr[more - 1] = ',';
887 }
888
889 (void) memmove(&buffer[length], ptr, more + 1);
890 length += more;
891 offset = length + 3;
892
893 ptr = &buffer[offset];
894 }
895
896 if (strncasecmp(ptr, ".Nd", 3) == 0) {
897 (void) strlcpy(&buffer[length], " -",
898 sizeof(buffer) - length);
899
900 while (strncasecmp(ptr, ".Sh", 3) != 0) {
901 int more;
902
903 if (*ptr == '.') {
904 char *space;
905
906 if (strncasecmp(ptr, ".Nd", 3) != 0 ||
907 strchr(ptr, '[') != NULL) {
908 free(section);
909 return NULL;
910 }
911 space = findwhitespace(ptr);
912 if (space == NULL) {
913 ptr = "";
914 } else {
915 space++;
916 (void) strmove(ptr, space);
917 }
918 }
919
920 if (*ptr != '\0') {
921 buffer[offset - 1] = ' ';
922 more = strlen(ptr) + 1;
923 offset += more;
924 }
925 ptr = &buffer[offset];
926 if ((sizeof(buffer) == offset) ||
927 (GetS(in, ptr, sizeof(buffer) - offset)
928 == NULL)) {
929 free(section);
930 return NULL;
931 }
932 if (manpreprocess(ptr))
933 *ptr = '\0';
934 }
935 }
936 }
937 else {
938 int offset;
939
940 if (*buffer == '.') {
941 char *space;
942
943 if ((space = findwhitespace(&buffer[1])) == NULL) {
944 free(section);
945 return NULL;
946 }
947 space++;
948 (void) strmove(buffer, space);
949 }
950
951 offset = strlen(buffer) + 1;
952 for (;;) {
953 int more;
954
955 ptr = &buffer[offset];
956 if ((sizeof(buffer) == offset) ||
957 (GetS(in, ptr, sizeof(buffer) - offset)
958 == NULL)) {
959 free(section);
960 return NULL;
961 }
962 if (manpreprocess(ptr) || (*ptr == '\0'))
963 continue;
964
965 if ((strncasecmp(ptr, ".Sh", 3) == 0) ||
966 (strncasecmp(ptr, ".Ss", 3) == 0))
967 break;
968
969 if (*ptr == '.') {
970 char *space;
971
972 if ((space = findwhitespace(ptr)) == NULL) {
973 continue;
974 }
975
976 space++;
977 (void) memmove(ptr, space, strlen(space) + 1);
978 }
979
980 buffer[offset - 1] = ' ';
981 more = strlen(ptr);
982 if ((more > 1) && (ptr[more - 1] == ',') &&
983 ISSPACE(ptr[more - 2])) {
984 ptr[more - 1] = '\0';
985 ptr[more - 2] = ',';
986 }
987 else more++;
988 offset += more;
989 }
990 }
991
992 if (section == NULL)
993 section = makesection(defaultsection);
994
995 ptr = makewhatisline(name, buffer, section);
996 free(section);
997 return ptr;
998 }
999
1000 static char *
1001 getwhatisdata(char *name)
1002 {
1003 gzFile *in;
1004 char *data;
1005 int section;
1006
1007 if ((in = gzopen(name, "r")) == NULL) {
1008 if (errno == 0)
1009 errno = ENOMEM;
1010 err(EXIT_FAILURE, "Cannot open `%s'", name);
1011 /* NOTREACHED */
1012 }
1013
1014 section = manpagesection(name);
1015 if (section == 0) {
1016 data = parsecatpage(name, in);
1017 } else {
1018 data = parsemanpage(name, in, section);
1019 if (data == NULL)
1020 data = nroff(name, in);
1021 }
1022
1023 (void) gzclose(in);
1024 return data;
1025 }
1026
1027 static void
1028 processmanpages(manpage **source, whatis **dest)
1029 {
1030 manpage *mp;
1031 char sd[128];
1032
1033 mp = *source;
1034 *source = NULL;
1035
1036 while (mp != NULL) {
1037 manpage *obsolete;
1038 char *data;
1039
1040 if (mp->mp_left != NULL)
1041 processmanpages(&mp->mp_left,dest);
1042
1043 if ((data = getwhatisdata(mp->mp_name)) != NULL) {
1044 /* Pass eventual directory prefix to addwhatis() */
1045 if (mp->mp_sdlen > 0 && mp->mp_sdlen < sizeof(sd)-1)
1046 strlcpy(sd, &mp->mp_name[mp->mp_sdoff],
1047 mp->mp_sdlen);
1048 else
1049 sd[0] = '\0';
1050
1051 addwhatis(dest, data, sd);
1052 }
1053
1054 obsolete = mp;
1055 mp = mp->mp_right;
1056 free(obsolete);
1057 }
1058 }
1059
1060 static void
1061 dumpwhatis(FILE *out, whatis *tree)
1062 {
1063 while (tree != NULL) {
1064 if (tree->wi_left)
1065 dumpwhatis(out, tree->wi_left);
1066
1067 if ((tree->wi_data[0] && fputs(tree->wi_prefix, out) == EOF) ||
1068 (fputs(tree->wi_data, out) == EOF) ||
1069 (fputc('\n', out) == EOF))
1070 err(EXIT_FAILURE, "Write failed");
1071
1072 tree = tree->wi_right;
1073 }
1074 }
1075
1076 static void *
1077 emalloc(size_t len)
1078 {
1079 void *ptr;
1080 if ((ptr = malloc(len)) == NULL)
1081 err(EXIT_FAILURE, "malloc %lu failed", (unsigned long)len);
1082 return ptr;
1083 }
1084
1085 static char *
1086 estrdup(const char *str)
1087 {
1088 char *ptr;
1089 if ((ptr = strdup(str)) == NULL)
1090 err(EXIT_FAILURE, "strdup failed");
1091 return ptr;
1092 }
1093