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