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