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