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