makewhatis.c revision 1.36 1 /* $NetBSD: makewhatis.c,v 1.36 2005/06/01 15:53:31 lukem Exp $ */
2
3 /*-
4 * Copyright (c) 1999 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Matthias Scheler.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 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.36 2005/06/01 15:53:31 lukem 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 ptr = NULL;
503 if (section == NULL) {
504 if (dowarn)
505 warnx("%s: No section provided for `%s'", file, line);
506 return estrdup(line);
507 }
508
509 for (i = 0; del[i]; i++)
510 if ((ptr = strstr(line, del[i])) != NULL)
511 break;
512
513 if (del[i] == NULL) {
514 if (dowarn)
515 warnx("%s: Bad format line `%s'", file, line);
516 return estrdup(line);
517 }
518
519 slen = strlen(section);
520 llen = strlen(line);
521 dlen = strlen(del[i]);
522
523 result = emalloc(llen - dlen + slen + 1);
524 pos = ptr - line;
525
526 (void)memcpy(result, line, pos);
527 (void)memcpy(&result[pos], section, slen);
528 (void)strcpy(&result[pos + slen], &line[pos + dlen]);
529 return result;
530 }
531
532 char *
533 parsecatpage(const char *name, gzFile *in)
534 {
535 char buffer[8192];
536 char *section, *ptr, *last;
537 size_t size;
538
539 do {
540 if (GetS(in, buffer, sizeof(buffer)) == NULL)
541 return NULL;
542 }
543 while (buffer[0] == '\n');
544
545 section = NULL;
546 if ((ptr = strchr(buffer, '(')) != NULL) {
547 if ((last = strchr(ptr + 1, ')')) !=NULL) {
548 size_t length;
549
550 length = last - ptr + 1;
551 section = emalloc(length + 5);
552 *section = ' ';
553 (void) memcpy(section + 1, ptr, length);
554 (void) strcpy(section + 1 + length, " - ");
555 }
556 }
557
558 for (;;) {
559 if (GetS(in, buffer, sizeof(buffer)) == NULL) {
560 free(section);
561 return NULL;
562 }
563 catpreprocess(buffer);
564 if (strncmp(buffer, "NAME", 4) == 0)
565 break;
566 }
567 if (section == NULL)
568 section = makesection(pathnamesection("/cat", name));
569
570 ptr = last = buffer;
571 size = sizeof(buffer) - 1;
572 while ((size > 0) && (GetS(in, ptr, size) != NULL)) {
573 int length;
574
575 catpreprocess(ptr);
576
577 length = strlen(ptr);
578 if (length == 0) {
579 *last = '\0';
580
581 ptr = makewhatisline(name, buffer, section);
582 free(section);
583 return ptr;
584 }
585 if ((length > 1) && (ptr[length - 1] == '-') &&
586 isalpha((unsigned char)ptr[length - 2]))
587 last = &ptr[--length];
588 else {
589 last = &ptr[length++];
590 *last = ' ';
591 }
592
593 ptr += length;
594 size -= length;
595 }
596
597 free(section);
598
599 return NULL;
600 }
601
602 int
603 manpreprocess(char *line)
604 {
605 char *from, *to;
606
607 to = from = line;
608 while (isspace((unsigned char)*from)) from++;
609 if (strncmp(from, ".\\\"", 3) == 0)
610 return 1;
611
612 while (*from != '\0')
613 if (isspace((unsigned char)*from)) {
614 while (isspace((unsigned char)*++from));
615 if ((*from != '\0') && (*from != ','))
616 *to++ = ' ';
617 }
618 else if (*from == '\\')
619 switch (*++from) {
620 case '\0':
621 case '-':
622 break;
623 case 'f':
624 case 's':
625 from++;
626 if ((*from=='+') || (*from=='-'))
627 from++;
628 while (isdigit((unsigned char)*from))
629 from++;
630 break;
631 default:
632 from++;
633 }
634 else
635 if (*from == '"')
636 from++;
637 else
638 *to++ = *from++;
639
640 *to = '\0';
641
642 if (strncasecmp(line, ".Xr", 3) == 0) {
643 char *sect;
644
645 from = line + 3;
646 if (isspace((unsigned char)*from))
647 from++;
648
649 if ((sect = findwhitespace(from)) != NULL) {
650 size_t length;
651 char *trail;
652
653 *sect++ = '\0';
654 if ((trail = findwhitespace(sect)) != NULL)
655 *trail++ = '\0';
656 length = strlen(from);
657 (void) memmove(line, from, length);
658 line[length++] = '(';
659 to = &line[length];
660 length = strlen(sect);
661 (void) memmove(to, sect, length);
662 if (trail == NULL) {
663 (void) strcpy(&to[length], ")");
664 } else {
665 to += length;
666 *to++ = ')';
667 length = strlen(trail);
668 (void) memmove(to, trail, length + 1);
669 }
670 }
671 }
672
673 return 0;
674 }
675
676 char *
677 nroff(const char *inname, gzFile *in)
678 {
679 char tempname[MAXPATHLEN], buffer[65536], *data;
680 int tempfd, bytes, pipefd[2], status;
681 static int devnull = -1;
682 pid_t child;
683
684 if (gzrewind(in) < 0)
685 err(EXIT_FAILURE, "Cannot rewind pipe");
686
687 if ((devnull < 0) &&
688 ((devnull = open(_PATH_DEVNULL, O_WRONLY, 0)) < 0))
689 err(EXIT_FAILURE, "Cannot open `/dev/null'");
690
691 (void)strlcpy(tempname, _PATH_TMP "makewhatis.XXXXXX",
692 sizeof(tempname));
693 if ((tempfd = mkstemp(tempname)) == -1)
694 err(EXIT_FAILURE, "Cannot create temp file");
695
696 while ((bytes = gzread(in, buffer, sizeof(buffer))) > 0)
697 if (write(tempfd, buffer, (size_t)bytes) != bytes) {
698 bytes = -1;
699 break;
700 }
701
702 if (bytes < 0) {
703 (void)close(tempfd);
704 (void)unlink(tempname);
705 err(EXIT_FAILURE, "Read from pipe failed");
706 }
707 if (lseek(tempfd, (off_t)0, SEEK_SET) == (off_t)-1) {
708 (void)close(tempfd);
709 (void)unlink(tempname);
710 err(EXIT_FAILURE, "Cannot rewind temp file");
711 }
712 if (pipe(pipefd) == -1) {
713 (void)close(tempfd);
714 (void)unlink(tempname);
715 err(EXIT_FAILURE, "Cannot create pipe");
716 }
717
718 switch (child = vfork()) {
719 case -1:
720 (void)close(pipefd[1]);
721 (void)close(pipefd[0]);
722 (void)close(tempfd);
723 (void)unlink(tempname);
724 err(EXIT_FAILURE, "Fork failed");
725 /* NOTREACHED */
726 case 0:
727 (void)close(pipefd[0]);
728 if (tempfd != STDIN_FILENO) {
729 (void)dup2(tempfd, STDIN_FILENO);
730 (void)close(tempfd);
731 }
732 if (pipefd[1] != STDOUT_FILENO) {
733 (void)dup2(pipefd[1], STDOUT_FILENO);
734 (void)close(pipefd[1]);
735 }
736 if (devnull != STDERR_FILENO) {
737 (void)dup2(devnull, STDERR_FILENO);
738 (void)close(devnull);
739 }
740 (void)execlp(NROFF, NROFF, "-S", "-man", NULL);
741 _exit(EXIT_FAILURE);
742 /*NOTREACHED*/
743 default:
744 (void)close(pipefd[1]);
745 (void)close(tempfd);
746 break;
747 }
748
749 if ((in = gzdopen(pipefd[0], "r")) == NULL) {
750 if (errno == 0)
751 errno = ENOMEM;
752 (void)close(pipefd[0]);
753 (void)kill(child, SIGTERM);
754 while (waitpid(child, NULL, 0) != child);
755 (void)unlink(tempname);
756 err(EXIT_FAILURE, "Cannot read from pipe");
757 }
758
759 data = parsecatpage(inname, in);
760 while (gzread(in, buffer, sizeof(buffer)) > 0);
761 (void)gzclose(in);
762
763 while (waitpid(child, &status, 0) != child);
764 if ((data != NULL) &&
765 !(WIFEXITED(status) && (WEXITSTATUS(status) == 0))) {
766 free(data);
767 errx(EXIT_FAILURE, NROFF " on `%s' exited with %d status",
768 inname, WEXITSTATUS(status));
769 }
770
771 (void)unlink(tempname);
772 return data;
773 }
774
775 char *
776 parsemanpage(const char *name, gzFile *in, int defaultsection)
777 {
778 char *section, buffer[8192], *ptr;
779
780 section = NULL;
781 do {
782 if (GetS(in, buffer, sizeof(buffer) - 1) == NULL) {
783 free(section);
784 return NULL;
785 }
786 if (manpreprocess(buffer))
787 continue;
788 if (strncasecmp(buffer, ".Dt", 3) == 0) {
789 char *end;
790
791 ptr = &buffer[3];
792 if (isspace((unsigned char)*ptr))
793 ptr++;
794 if ((ptr = findwhitespace(ptr)) == NULL)
795 continue;
796
797 if ((end = findwhitespace(++ptr)) != NULL)
798 *end = '\0';
799
800 free(section);
801 section = createsectionstring(ptr);
802 }
803 else if (strncasecmp(buffer, ".TH", 3) == 0) {
804 ptr = &buffer[3];
805 while (isspace((unsigned char)*ptr))
806 ptr++;
807 if ((ptr = findwhitespace(ptr)) != NULL) {
808 char *next;
809
810 while (isspace((unsigned char)*ptr))
811 ptr++;
812 if ((next = findwhitespace(ptr)) != NULL)
813 *next = '\0';
814 free(section);
815 section = createsectionstring(ptr);
816 }
817 }
818 else if (strncasecmp(buffer, ".Ds", 3) == 0) {
819 free(section);
820 return NULL;
821 }
822 } while (strncasecmp(buffer, ".Sh NAME", 8) != 0);
823
824 do {
825 if (GetS(in, buffer, sizeof(buffer) - 1) == NULL) {
826 free(section);
827 return NULL;
828 }
829 } while (manpreprocess(buffer));
830
831 if (strncasecmp(buffer, ".Nm", 3) == 0) {
832 size_t length, offset;
833
834 ptr = &buffer[3];
835 while (isspace((unsigned char)*ptr))
836 ptr++;
837
838 length = strlen(ptr);
839 if ((length > 1) && (ptr[length - 1] == ',') &&
840 isspace((unsigned char)ptr[length - 2])) {
841 ptr[--length] = '\0';
842 ptr[length - 1] = ',';
843 }
844 (void) memmove(buffer, ptr, length + 1);
845
846 offset = length + 3;
847 ptr = &buffer[offset];
848 for (;;) {
849 size_t more;
850
851 if ((sizeof(buffer) == offset) ||
852 (GetS(in, ptr, sizeof(buffer) - offset)
853 == NULL)) {
854 free(section);
855 return NULL;
856 }
857 if (manpreprocess(ptr))
858 continue;
859
860 if (strncasecmp(ptr, ".Nm", 3) != 0) break;
861
862 ptr += 3;
863 if (isspace((unsigned char)*ptr))
864 ptr++;
865
866 buffer[length++] = ' ';
867 more = strlen(ptr);
868 if ((more > 1) && (ptr[more - 1] == ',') &&
869 isspace((unsigned char)ptr[more - 2])) {
870 ptr[--more] = '\0';
871 ptr[more - 1] = ',';
872 }
873
874 (void) memmove(&buffer[length], ptr, more + 1);
875 length += more;
876 offset = length + 3;
877
878 ptr = &buffer[offset];
879 }
880
881 if (strncasecmp(ptr, ".Nd", 3) == 0) {
882 (void) strlcpy(&buffer[length], " -",
883 sizeof(buffer) - length);
884
885 while (strncasecmp(ptr, ".Sh", 3) != 0) {
886 int more;
887
888 if (*ptr == '.') {
889 char *space;
890
891 if (strncasecmp(ptr, ".Nd", 3) != 0) {
892 free(section);
893 return NULL;
894 }
895 space = findwhitespace(ptr);
896 if (space == NULL)
897 ptr = "";
898 else {
899 space++;
900 (void) strmove(ptr, space);
901 }
902 }
903
904 if (*ptr != '\0') {
905 buffer[offset - 1] = ' ';
906 more = strlen(ptr) + 1;
907 offset += more;
908 }
909 ptr = &buffer[offset];
910 if ((sizeof(buffer) == offset) ||
911 (GetS(in, ptr, sizeof(buffer) - offset)
912 == NULL)) {
913 free(section);
914 return NULL;
915 }
916 if (manpreprocess(ptr))
917 *ptr = '\0';
918 }
919 }
920 }
921 else {
922 int offset;
923
924 if (*buffer == '.') {
925 char *space;
926
927 if ((space = findwhitespace(&buffer[1])) == NULL) {
928 free(section);
929 return NULL;
930 }
931 space++;
932 (void) strmove(buffer, space);
933 }
934
935 offset = strlen(buffer) + 1;
936 for (;;) {
937 int more;
938
939 ptr = &buffer[offset];
940 if ((sizeof(buffer) == offset) ||
941 (GetS(in, ptr, sizeof(buffer) - offset)
942 == NULL)) {
943 free(section);
944 return NULL;
945 }
946 if (manpreprocess(ptr) || (*ptr == '\0'))
947 continue;
948
949 if ((strncasecmp(ptr, ".Sh", 3) == 0) ||
950 (strncasecmp(ptr, ".Ss", 3) == 0))
951 break;
952
953 if (*ptr == '.') {
954 char *space;
955
956 if ((space = findwhitespace(ptr)) == NULL) {
957 continue;
958 }
959
960 space++;
961 (void) memmove(ptr, space, strlen(space) + 1);
962 }
963
964 buffer[offset - 1] = ' ';
965 more = strlen(ptr);
966 if ((more > 1) && (ptr[more - 1] == ',') &&
967 isspace((unsigned char)ptr[more - 2])) {
968 ptr[more - 1] = '\0';
969 ptr[more - 2] = ',';
970 }
971 else more++;
972 offset += more;
973 }
974 }
975
976 if (section == NULL)
977 section = makesection(defaultsection);
978
979 ptr = makewhatisline(name, buffer, section);
980 free(section);
981 return ptr;
982 }
983
984 char *
985 getwhatisdata(char *name)
986 {
987 gzFile *in;
988 char *data;
989 int section;
990
991 if ((in = gzopen(name, "r")) == NULL) {
992 if (errno == 0)
993 errno = ENOMEM;
994 err(EXIT_FAILURE, "Cannot open `%s'", name);
995 /* NOTREACHED */
996 }
997
998 section = manpagesection(name);
999 if (section == 0) {
1000 data = parsecatpage(name, in);
1001 } else {
1002 data = parsemanpage(name, in, section);
1003 if (data == NULL)
1004 data = nroff(name, in);
1005 }
1006
1007 (void) gzclose(in);
1008 return data;
1009 }
1010
1011 void
1012 processmanpages(manpage **source, whatis **dest)
1013 {
1014 manpage *mp;
1015 char sd[128];
1016
1017 mp = *source;
1018 *source = NULL;
1019
1020 while (mp != NULL) {
1021 manpage *obsolete;
1022 char *data;
1023
1024 if (mp->mp_left != NULL)
1025 processmanpages(&mp->mp_left,dest);
1026
1027 if ((data = getwhatisdata(mp->mp_name)) != NULL) {
1028 /* Pass eventual directory prefix to addwhatis() */
1029 if (mp->mp_sdlen > 0 && mp->mp_sdlen < sizeof(sd)-1)
1030 strlcpy(sd, &mp->mp_name[mp->mp_sdoff],
1031 mp->mp_sdlen);
1032 else
1033 sd[0] = '\0';
1034
1035 addwhatis(dest, data, sd);
1036 }
1037
1038 obsolete = mp;
1039 mp = mp->mp_right;
1040 free(obsolete);
1041 }
1042 }
1043
1044 void
1045 dumpwhatis(FILE *out, whatis *tree)
1046 {
1047 while (tree != NULL) {
1048 if (tree->wi_left)
1049 dumpwhatis(out, tree->wi_left);
1050
1051 if ((tree->wi_data[0] && fputs(tree->wi_prefix, out) == EOF) ||
1052 (fputs(tree->wi_data, out) == EOF) ||
1053 (fputc('\n', out) == EOF))
1054 err(EXIT_FAILURE, "Write failed");
1055
1056 tree = tree->wi_right;
1057 }
1058 }
1059
1060 void *
1061 emalloc(size_t len)
1062 {
1063 void *ptr;
1064 if ((ptr = malloc(len)) == NULL)
1065 err(EXIT_FAILURE, "malloc %lu failed", (unsigned long)len);
1066 return ptr;
1067 }
1068
1069 char *
1070 estrdup(const char *str)
1071 {
1072 char *ptr;
1073 if ((ptr = strdup(str)) == NULL)
1074 err(EXIT_FAILURE, "strdup failed");
1075 return ptr;
1076 }
1077