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