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