xref: /minix3/libexec/makewhatis/makewhatis.c (revision 84d9c625bfea59e274550651111ae9edfdc40fbd)
1*84d9c625SLionel Sambuc /*	$NetBSD: makewhatis.c,v 1.49 2013/06/24 20:57:47 christos Exp $	*/
20c3983b2SBen Gras 
30c3983b2SBen Gras /*-
40c3983b2SBen Gras  * Copyright (c) 1999 The NetBSD Foundation, Inc.
50c3983b2SBen Gras  * All rights reserved.
60c3983b2SBen Gras  *
70c3983b2SBen Gras  * This code is derived from software contributed to The NetBSD Foundation
80c3983b2SBen Gras  * by Matthias Scheler.
90c3983b2SBen Gras  *
100c3983b2SBen Gras  * Redistribution and use in source and binary forms, with or without
110c3983b2SBen Gras  * modification, are permitted provided that the following conditions
120c3983b2SBen Gras  * are met:
130c3983b2SBen Gras  * 1. Redistributions of source code must retain the above copyright
140c3983b2SBen Gras  *    notice, this list of conditions and the following disclaimer.
150c3983b2SBen Gras  * 2. Redistributions in binary form must reproduce the above copyright
160c3983b2SBen Gras  *    notice, this list of conditions and the following disclaimer in the
170c3983b2SBen Gras  *    documentation and/or other materials provided with the distribution.
180c3983b2SBen Gras  *
190c3983b2SBen Gras  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
200c3983b2SBen Gras  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
210c3983b2SBen Gras  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
220c3983b2SBen Gras  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
230c3983b2SBen Gras  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
240c3983b2SBen Gras  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
250c3983b2SBen Gras  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
260c3983b2SBen Gras  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
270c3983b2SBen Gras  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
280c3983b2SBen Gras  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
290c3983b2SBen Gras  * POSSIBILITY OF SUCH DAMAGE.
300c3983b2SBen Gras  */
310c3983b2SBen Gras 
320c3983b2SBen Gras #if HAVE_NBTOOL_CONFIG_H
330c3983b2SBen Gras #include "nbtool_config.h"
340c3983b2SBen Gras #endif
350c3983b2SBen Gras 
360c3983b2SBen Gras #include <sys/cdefs.h>
370c3983b2SBen Gras #if !defined(lint)
380c3983b2SBen Gras __COPYRIGHT("@(#) Copyright (c) 1999\
390c3983b2SBen Gras  The NetBSD Foundation, Inc.  All rights reserved.");
40*84d9c625SLionel Sambuc __RCSID("$NetBSD: makewhatis.c,v 1.49 2013/06/24 20:57:47 christos Exp $");
410c3983b2SBen Gras #endif /* not lint */
420c3983b2SBen Gras 
430c3983b2SBen Gras #include <sys/types.h>
440c3983b2SBen Gras #include <sys/param.h>
450c3983b2SBen Gras #include <sys/queue.h>
460c3983b2SBen Gras #include <sys/stat.h>
470c3983b2SBen Gras #include <sys/wait.h>
480c3983b2SBen Gras 
490c3983b2SBen Gras #include <ctype.h>
500c3983b2SBen Gras #include <err.h>
510c3983b2SBen Gras #include <errno.h>
520c3983b2SBen Gras #include <fcntl.h>
530c3983b2SBen Gras #include <fts.h>
540c3983b2SBen Gras #include <glob.h>
550c3983b2SBen Gras #include <locale.h>
560c3983b2SBen Gras #include <paths.h>
570c3983b2SBen Gras #include <signal.h>
580c3983b2SBen Gras #include <stdio.h>
590c3983b2SBen Gras #include <stdlib.h>
600c3983b2SBen Gras #include <string.h>
610c3983b2SBen Gras #include <unistd.h>
620c3983b2SBen Gras #include <zlib.h>
630c3983b2SBen Gras #include <util.h>
640c3983b2SBen Gras 
650c3983b2SBen Gras #include <man/manconf.h>
660c3983b2SBen Gras #include <man/pathnames.h>
670c3983b2SBen Gras 
680c3983b2SBen Gras #ifndef NROFF
690c3983b2SBen Gras #define NROFF "nroff"
700c3983b2SBen Gras #endif
710c3983b2SBen Gras 
720c3983b2SBen Gras typedef struct manpagestruct manpage;
730c3983b2SBen Gras struct manpagestruct {
740c3983b2SBen Gras 	manpage *mp_left, *mp_right;
750c3983b2SBen Gras 	ino_t	 mp_inode;
760c3983b2SBen Gras 	size_t	 mp_sdoff;
770c3983b2SBen Gras 	size_t	 mp_sdlen;
780c3983b2SBen Gras 	char	 mp_name[1];
790c3983b2SBen Gras };
800c3983b2SBen Gras 
810c3983b2SBen Gras typedef struct whatisstruct whatis;
820c3983b2SBen Gras struct whatisstruct {
830c3983b2SBen Gras 	whatis	*wi_left, *wi_right;
840c3983b2SBen Gras 	char	*wi_data;
850c3983b2SBen Gras 	char	wi_prefix[1];
860c3983b2SBen Gras };
870c3983b2SBen Gras 
880c3983b2SBen Gras int		main(int, char * const *);
890c3983b2SBen Gras static char	*findwhitespace(char *);
900c3983b2SBen Gras static char	*strmove(char *, char *);
910c3983b2SBen Gras static char	*GetS(gzFile, char *, size_t);
920c3983b2SBen Gras static int	pathnamesection(const char *, const char *);
930c3983b2SBen Gras static int	manpagesection(char *);
940c3983b2SBen Gras static char	*createsectionstring(char *);
950c3983b2SBen Gras static void	addmanpage(manpage **, ino_t, char *, size_t, size_t);
960c3983b2SBen Gras static void	addwhatis(whatis **, char *, char *);
970c3983b2SBen Gras static char	*makesection(int);
980c3983b2SBen Gras static char	*makewhatisline(const char *, const char *, const char *);
990c3983b2SBen Gras static void	catpreprocess(char *);
1000c3983b2SBen Gras static char	*parsecatpage(const char *, gzFile *);
1010c3983b2SBen Gras static int	manpreprocess(char *);
1020c3983b2SBen Gras static char	*nroff(const char *, gzFile *);
1030c3983b2SBen Gras static char	*parsemanpage(const char *, gzFile *, int);
1040c3983b2SBen Gras static char	*getwhatisdata(char *);
1050c3983b2SBen Gras static void	processmanpages(manpage **, whatis **);
1060c3983b2SBen Gras static void	dumpwhatis(FILE *, whatis *);
1070c3983b2SBen Gras static int	makewhatis(char * const *manpath);
1080c3983b2SBen Gras 
1090c3983b2SBen Gras static char * const default_manpath[] = {
110*84d9c625SLionel Sambuc #if defined(__minix)
1110c3983b2SBen Gras 	"/usr/man",
112*84d9c625SLionel Sambuc #endif /* defined(__minix) */
113910825eeSBen Gras 	"/usr/share/man",
1140c3983b2SBen Gras 	NULL
1150c3983b2SBen Gras };
1160c3983b2SBen Gras 
1170c3983b2SBen Gras static const char	*sectionext = "0123456789ln";
1180c3983b2SBen Gras static const char	*whatisdb   = _PATH_WHATIS;
1190c3983b2SBen Gras static const char	*whatisdb_new = _PATH_WHATIS ".new";
1200c3983b2SBen Gras static int		dowarn      = 0;
1210c3983b2SBen Gras 
1220c3983b2SBen Gras #define	ISALPHA(c)	isalpha((unsigned char)(c))
1230c3983b2SBen Gras #define	ISDIGIT(c)	isdigit((unsigned char)(c))
1240c3983b2SBen Gras #define	ISSPACE(c)	isspace((unsigned char)(c))
1250c3983b2SBen Gras 
1260c3983b2SBen Gras int
main(int argc,char * const * argv)1270c3983b2SBen Gras main(int argc, char *const *argv)
1280c3983b2SBen Gras {
1290c3983b2SBen Gras 	char * const	*manpath;
1300c3983b2SBen Gras 	int		c, dofork;
1310c3983b2SBen Gras 	const char	*conffile;
1320c3983b2SBen Gras 	ENTRY		*ep;
1330c3983b2SBen Gras 	TAG		*tp;
1340c3983b2SBen Gras 	int		rv, jobs, status;
1350c3983b2SBen Gras 	glob_t		pg;
1360c3983b2SBen Gras 	char		*paths[2], **p, *sl;
1370c3983b2SBen Gras 	int		retval;
1380c3983b2SBen Gras 
1390c3983b2SBen Gras 	dofork = 1;
1400c3983b2SBen Gras 	conffile = NULL;
1410c3983b2SBen Gras 	jobs = 0;
1420c3983b2SBen Gras 	retval = EXIT_SUCCESS;
1430c3983b2SBen Gras 
1440c3983b2SBen Gras 	(void)setlocale(LC_ALL, "");
1450c3983b2SBen Gras 
1460c3983b2SBen Gras 	while ((c = getopt(argc, argv, "C:fw")) != -1) {
1470c3983b2SBen Gras 		switch (c) {
1480c3983b2SBen Gras 		case 'C':
1490c3983b2SBen Gras 			conffile = optarg;
1500c3983b2SBen Gras 			break;
1510c3983b2SBen Gras 		case 'f':
1520c3983b2SBen Gras 			/* run all processing on foreground */
1530c3983b2SBen Gras 			dofork = 0;
1540c3983b2SBen Gras 			break;
1550c3983b2SBen Gras 		case 'w':
1560c3983b2SBen Gras 			dowarn++;
1570c3983b2SBen Gras 			break;
1580c3983b2SBen Gras 		default:
1590c3983b2SBen Gras 			fprintf(stderr, "Usage: %s [-fw] [-C file] [manpath ...]\n",
1600c3983b2SBen Gras 				getprogname());
1610c3983b2SBen Gras 			exit(EXIT_FAILURE);
1620c3983b2SBen Gras 		}
1630c3983b2SBen Gras 	}
1640c3983b2SBen Gras 	argc -= optind;
1650c3983b2SBen Gras 	argv += optind;
1660c3983b2SBen Gras 
1670c3983b2SBen Gras 	if (argc >= 1) {
1680c3983b2SBen Gras 		manpath = &argv[0];
1690c3983b2SBen Gras 
1700c3983b2SBen Gras 	    mkwhatis:
1710c3983b2SBen Gras 		return makewhatis(manpath);
1720c3983b2SBen Gras 	}
1730c3983b2SBen Gras 
1740c3983b2SBen Gras 	/*
1750c3983b2SBen Gras 	 * Try read config file, fallback to default_manpath[]
1760c3983b2SBen Gras 	 * if man.conf not available.
1770c3983b2SBen Gras 	 */
1780c3983b2SBen Gras 	config(conffile);
1790c3983b2SBen Gras 	if ((tp = gettag("_whatdb", 0)) == NULL) {
1800c3983b2SBen Gras 		manpath = default_manpath;
1810c3983b2SBen Gras 		goto mkwhatis;
1820c3983b2SBen Gras 	}
1830c3983b2SBen Gras 
1840c3983b2SBen Gras 	/* Build individual databases */
1850c3983b2SBen Gras 	paths[1] = NULL;
1860c3983b2SBen Gras 	TAILQ_FOREACH(ep, &tp->entrylist, q) {
1870c3983b2SBen Gras 		if ((rv = glob(ep->s,
1880c3983b2SBen Gras 		    GLOB_BRACE | GLOB_NOSORT | GLOB_ERR | GLOB_NOCHECK,
1890c3983b2SBen Gras 		    NULL, &pg)) != 0)
1900c3983b2SBen Gras 			err(EXIT_FAILURE, "glob('%s')", ep->s);
1910c3983b2SBen Gras 
1920c3983b2SBen Gras 		/* We always have something to work with here */
1930c3983b2SBen Gras 		for (p = pg.gl_pathv; *p; p++) {
1940c3983b2SBen Gras 			sl = strrchr(*p, '/');
1950c3983b2SBen Gras 			if (sl == NULL) {
1960c3983b2SBen Gras 				err(EXIT_FAILURE, "glob: _whatdb entry '%s' "
1970c3983b2SBen Gras 				    "doesn't contain slash", ep->s);
1980c3983b2SBen Gras 			}
1990c3983b2SBen Gras 
2000c3983b2SBen Gras 			/*
2010c3983b2SBen Gras 			 * Cut the last component of path, leaving just
2020c3983b2SBen Gras 			 * the directory. We will use the result as root
2030c3983b2SBen Gras 			 * for manpage search.
2040c3983b2SBen Gras 			 * glob malloc()s space for the paths, so it's
2050c3983b2SBen Gras 			 * okay to change it in-place.
2060c3983b2SBen Gras 			 */
2070c3983b2SBen Gras 			*sl = '\0';
2080c3983b2SBen Gras 			paths[0] = *p;
2090c3983b2SBen Gras 
2100c3983b2SBen Gras 			if (!dofork) {
2110c3983b2SBen Gras 				/* Do not fork child */
2120c3983b2SBen Gras 				makewhatis(paths);
2130c3983b2SBen Gras 				continue;
2140c3983b2SBen Gras 			}
2150c3983b2SBen Gras 
2160c3983b2SBen Gras 			switch (fork()) {
2170c3983b2SBen Gras 			case 0:
2180c3983b2SBen Gras 				exit(makewhatis(paths));
2190c3983b2SBen Gras 				break;
2200c3983b2SBen Gras 			case -1:
2210c3983b2SBen Gras 				warn("fork");
2220c3983b2SBen Gras 				makewhatis(paths);
2230c3983b2SBen Gras 				break;
2240c3983b2SBen Gras 			default:
2250c3983b2SBen Gras 				jobs++;
2260c3983b2SBen Gras 				break;
2270c3983b2SBen Gras 			}
2280c3983b2SBen Gras 
2290c3983b2SBen Gras 		}
2300c3983b2SBen Gras 
2310c3983b2SBen Gras 		globfree(&pg);
2320c3983b2SBen Gras 	}
2330c3983b2SBen Gras 
2340c3983b2SBen Gras 	/* Wait for the childern to finish */
2350c3983b2SBen Gras 	while (jobs > 0) {
2360c3983b2SBen Gras 		(void)wait(&status);
2370c3983b2SBen Gras 		if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS)
2380c3983b2SBen Gras 			retval = EXIT_FAILURE;
2390c3983b2SBen Gras 		jobs--;
2400c3983b2SBen Gras 	}
2410c3983b2SBen Gras 
2420c3983b2SBen Gras 	return retval;
2430c3983b2SBen Gras }
2440c3983b2SBen Gras 
2450c3983b2SBen Gras static int
makewhatis(char * const * manpath)2460c3983b2SBen Gras makewhatis(char * const * manpath)
2470c3983b2SBen Gras {
2480c3983b2SBen Gras 	FTS	*fts;
2490c3983b2SBen Gras 	FTSENT	*fe;
2500c3983b2SBen Gras 	manpage *source;
2510c3983b2SBen Gras 	whatis	*dest;
2520c3983b2SBen Gras 	FILE	*out;
2530c3983b2SBen Gras 	size_t	sdoff, sdlen;
2540c3983b2SBen Gras 	int	outfd;
2550c3983b2SBen Gras 	struct stat st_before, st_after;
2560c3983b2SBen Gras 
2570c3983b2SBen Gras 	if ((fts = fts_open(manpath, FTS_LOGICAL, NULL)) == NULL)
2580c3983b2SBen Gras 		err(EXIT_FAILURE, "Cannot open `%s'", *manpath);
2590c3983b2SBen Gras 
2600c3983b2SBen Gras 	source = NULL;
2610c3983b2SBen Gras 	while ((fe = fts_read(fts)) != NULL) {
2620c3983b2SBen Gras 		switch (fe->fts_info) {
2630c3983b2SBen Gras 		case FTS_F:
2640c3983b2SBen Gras 			if (manpagesection(fe->fts_path) >= 0) {
2650c3983b2SBen Gras 				/*
2660c3983b2SBen Gras 				 * Get manpage subdirectory prefix. Most
2670c3983b2SBen Gras 				 * commonly, this is arch-specific subdirectory.
2680c3983b2SBen Gras 				 */
2690c3983b2SBen Gras 				if (fe->fts_level >= 3) {
2700c3983b2SBen Gras 					int		sl;
2710c3983b2SBen Gras 					const char	*s, *lsl;
2720c3983b2SBen Gras 
2730c3983b2SBen Gras 					lsl = NULL;
2740c3983b2SBen Gras 					s = &fe->fts_path[fe->fts_pathlen - 1];
2750c3983b2SBen Gras 					for(sl = fe->fts_level - 1; sl > 0;
2760c3983b2SBen Gras 					    sl--) {
2770c3983b2SBen Gras 						s--;
2780c3983b2SBen Gras 						while (s[0] != '/')
2790c3983b2SBen Gras 							s--;
2800c3983b2SBen Gras 						if (lsl == NULL)
2810c3983b2SBen Gras 							lsl = s;
2820c3983b2SBen Gras 					}
2830c3983b2SBen Gras 
2840c3983b2SBen Gras 					/*
2850c3983b2SBen Gras 					 * Include trailing '/', so we get
2860c3983b2SBen Gras 					 * 'arch/'.
2870c3983b2SBen Gras 					 */
2880c3983b2SBen Gras 					sdoff = s + 1 - fe->fts_path;
2890c3983b2SBen Gras 					sdlen = lsl - s + 1;
2900c3983b2SBen Gras 				} else {
2910c3983b2SBen Gras 					sdoff = 0;
2920c3983b2SBen Gras 					sdlen = 0;
2930c3983b2SBen Gras 				}
2940c3983b2SBen Gras 
2950c3983b2SBen Gras 				addmanpage(&source, fe->fts_statp->st_ino,
2960c3983b2SBen Gras 				    fe->fts_path, sdoff, sdlen);
2970c3983b2SBen Gras 			}
2980c3983b2SBen Gras 			/*FALLTHROUGH*/
2990c3983b2SBen Gras 		case FTS_D:
3000c3983b2SBen Gras 		case FTS_DC:
3010c3983b2SBen Gras 		case FTS_DEFAULT:
3020c3983b2SBen Gras 		case FTS_DP:
3030c3983b2SBen Gras 		case FTS_SL:
3040c3983b2SBen Gras 		case FTS_DOT:
3050c3983b2SBen Gras 		case FTS_W:
3060c3983b2SBen Gras 		case FTS_NSOK:
3070c3983b2SBen Gras 		case FTS_INIT:
3080c3983b2SBen Gras 			break;
3090c3983b2SBen Gras 		case FTS_SLNONE:
3100c3983b2SBen Gras 			warnx("Symbolic link with no target: `%s'",
3110c3983b2SBen Gras 			    fe->fts_path);
3120c3983b2SBen Gras 			break;
3130c3983b2SBen Gras 		case FTS_DNR:
3140c3983b2SBen Gras 			warnx("Unreadable directory: `%s'", fe->fts_path);
3150c3983b2SBen Gras 			break;
3160c3983b2SBen Gras 		case FTS_NS:
3170c3983b2SBen Gras 			errno = fe->fts_errno;
3180c3983b2SBen Gras 			warn("Cannot stat `%s'", fe->fts_path);
3190c3983b2SBen Gras 			break;
3200c3983b2SBen Gras 		case FTS_ERR:
3210c3983b2SBen Gras 			errno = fe->fts_errno;
3220c3983b2SBen Gras 			warn("Error reading `%s'", fe->fts_path);
3230c3983b2SBen Gras 			break;
3240c3983b2SBen Gras 		default:
3250c3983b2SBen Gras 			errx(EXIT_FAILURE, "Unknown info %d returned from fts "
3260c3983b2SBen Gras 			    " for path: `%s'", fe->fts_info, fe->fts_path);
3270c3983b2SBen Gras 		}
3280c3983b2SBen Gras 	}
3290c3983b2SBen Gras 
3300c3983b2SBen Gras 	(void)fts_close(fts);
3310c3983b2SBen Gras 
3320c3983b2SBen Gras 	dest = NULL;
3330c3983b2SBen Gras 	processmanpages(&source, &dest);
3340c3983b2SBen Gras 
3350c3983b2SBen Gras 	if (chdir(manpath[0]) == -1)
3360c3983b2SBen Gras 		err(EXIT_FAILURE, "Cannot change dir to `%s'", manpath[0]);
3370c3983b2SBen Gras 
3380c3983b2SBen Gras 	/*
3390c3983b2SBen Gras 	 * makewhatis runs unattended, so it needs to be able to
3400c3983b2SBen Gras 	 * recover if the last run crashed out. Therefore, if
3410c3983b2SBen Gras 	 * whatisdb_new exists and is more than (arbitrarily) sixteen
3420c3983b2SBen Gras 	 * hours old, nuke it. If it exists but is not so old, refuse
3430c3983b2SBen Gras 	 * to run until it's cleaned up, in case another makewhatis is
3440c3983b2SBen Gras 	 * already running. Also, open the output with O_EXCL to make
3450c3983b2SBen Gras 	 * sure we get our own, in case two copies start exactly at
3460c3983b2SBen Gras 	 * once. (Unlikely? Maybe, maybe not, if two copies of cron
3470c3983b2SBen Gras 	 * end up running.)
3480c3983b2SBen Gras 	 *
3490c3983b2SBen Gras 	 * Similarly, before renaming the file after we finish writing
3500c3983b2SBen Gras 	 * to it, make sure it's still the same file we opened. This
3510c3983b2SBen Gras 	 * can't be completely race-free, but getting caught by it
3520c3983b2SBen Gras 	 * would require an unexplained sixteen-hour-or-more lag
3530c3983b2SBen Gras 	 * between the last mtime update when we wrote to it and when
3540c3983b2SBen Gras 	 * we get to the stat call *and* another makewhatis starting
3550c3983b2SBen Gras 	 * out to write at exactly the wrong moment. Not impossible,
3560c3983b2SBen Gras 	 * but not likely enough to worry about.
3570c3983b2SBen Gras 	 *
3580c3983b2SBen Gras 	 * This is maybe unnecessarily elaborate, but generating
3590c3983b2SBen Gras 	 * corrupted output isn't so good either.
3600c3983b2SBen Gras 	 */
3610c3983b2SBen Gras 
3620c3983b2SBen Gras 	if (stat(whatisdb_new, &st_before) == 0) {
3630c3983b2SBen Gras 		if (st_before.st_mtime - time(NULL) > 16*60*60) {
3640c3983b2SBen Gras 			/* Don't complain if someone else just removed it. */
3650c3983b2SBen Gras 			if (unlink(whatisdb_new) == -1 && errno != ENOENT) {
3660c3983b2SBen Gras 				err(EXIT_FAILURE, "Could not remove `%s'",
3670c3983b2SBen Gras 				    whatisdb_new);
3680c3983b2SBen Gras 			} else {
3690c3983b2SBen Gras 				warnx("Removed stale `%s'", whatisdb_new);
3700c3983b2SBen Gras 			}
3710c3983b2SBen Gras 		} else {
3720c3983b2SBen Gras 			errx(EXIT_FAILURE, "The file `%s' already exists "
3730c3983b2SBen Gras 			    "-- am I already running?", whatisdb_new);
3740c3983b2SBen Gras 		}
3750c3983b2SBen Gras 	} else if (errno != ENOENT) {
3760c3983b2SBen Gras 		/* Something unexpected happened. */
3770c3983b2SBen Gras 		err(EXIT_FAILURE, "Cannot stat `%s'", whatisdb_new);
3780c3983b2SBen Gras 	}
3790c3983b2SBen Gras 
3800c3983b2SBen Gras 	outfd = open(whatisdb_new, O_WRONLY|O_CREAT|O_EXCL,
3810c3983b2SBen Gras 	    S_IRUSR|S_IRGRP|S_IROTH);
3820c3983b2SBen Gras 	if (outfd < 0)
3830c3983b2SBen Gras 		err(EXIT_FAILURE, "Cannot open `%s'", whatisdb_new);
3840c3983b2SBen Gras 
3850c3983b2SBen Gras 	if (fstat(outfd, &st_before) == -1)
3860c3983b2SBen Gras 		err(EXIT_FAILURE, "Cannot fstat `%s'", whatisdb_new);
3870c3983b2SBen Gras 
3880c3983b2SBen Gras 	if ((out = fdopen(outfd, "w")) == NULL)
3890c3983b2SBen Gras 		err(EXIT_FAILURE, "Cannot fdopen `%s'", whatisdb_new);
3900c3983b2SBen Gras 
3910c3983b2SBen Gras 	dumpwhatis(out, dest);
3920c3983b2SBen Gras 	if (fchmod(fileno(out), S_IRUSR|S_IRGRP|S_IROTH) == -1)
3930c3983b2SBen Gras 		err(EXIT_FAILURE, "Cannot chmod `%s'", whatisdb_new);
3940c3983b2SBen Gras 	if (fclose(out) != 0)
3950c3983b2SBen Gras 		err(EXIT_FAILURE, "Cannot close `%s'", whatisdb_new);
3960c3983b2SBen Gras 
3970c3983b2SBen Gras 	if (stat(whatisdb_new, &st_after) == -1)
3980c3983b2SBen Gras 		err(EXIT_FAILURE, "Cannot stat `%s' (after writing)",
3990c3983b2SBen Gras 		    whatisdb_new);
4000c3983b2SBen Gras 
4010c3983b2SBen Gras 	if (st_before.st_dev != st_after.st_dev ||
4020c3983b2SBen Gras 	    st_before.st_ino != st_after.st_ino) {
4030c3983b2SBen Gras 		errx(EXIT_FAILURE, "The file `%s' changed under me; giving up",
4040c3983b2SBen Gras 		    whatisdb_new);
4050c3983b2SBen Gras 	}
4060c3983b2SBen Gras 
4070c3983b2SBen Gras 	if (rename(whatisdb_new, whatisdb) == -1)
4080c3983b2SBen Gras 		err(EXIT_FAILURE, "Could not rename `%s' to `%s'",
4090c3983b2SBen Gras 		    whatisdb_new, whatisdb);
4100c3983b2SBen Gras 
4110c3983b2SBen Gras 	return EXIT_SUCCESS;
4120c3983b2SBen Gras }
4130c3983b2SBen Gras 
4140c3983b2SBen Gras static char *
findwhitespace(char * str)4150c3983b2SBen Gras findwhitespace(char *str)
4160c3983b2SBen Gras {
4170c3983b2SBen Gras 	while (!ISSPACE(*str))
4180c3983b2SBen Gras 		if (*str++ == '\0') {
4190c3983b2SBen Gras 			str = NULL;
4200c3983b2SBen Gras 			break;
4210c3983b2SBen Gras 		}
4220c3983b2SBen Gras 
4230c3983b2SBen Gras 	return str;
4240c3983b2SBen Gras }
4250c3983b2SBen Gras 
4260c3983b2SBen Gras static char *
strmove(char * dest,char * src)4270c3983b2SBen Gras strmove(char *dest, char *src)
4280c3983b2SBen Gras {
4290c3983b2SBen Gras 	return memmove(dest, src, strlen(src) + 1);
4300c3983b2SBen Gras }
4310c3983b2SBen Gras 
4320c3983b2SBen Gras static char *
GetS(gzFile in,char * buffer,size_t length)4330c3983b2SBen Gras GetS(gzFile in, char *buffer, size_t length)
4340c3983b2SBen Gras {
4350c3983b2SBen Gras 	char	*ptr;
4360c3983b2SBen Gras 
4370c3983b2SBen Gras 	if (((ptr = gzgets(in, buffer, (int)length)) != NULL) && (*ptr == '\0'))
4380c3983b2SBen Gras 		ptr = NULL;
4390c3983b2SBen Gras 
4400c3983b2SBen Gras 	return ptr;
4410c3983b2SBen Gras }
4420c3983b2SBen Gras 
4430c3983b2SBen Gras static char *
makesection(int s)4440c3983b2SBen Gras makesection(int s)
4450c3983b2SBen Gras {
4460c3983b2SBen Gras 	char sectionbuffer[24];
4470c3983b2SBen Gras 	if (s == -1)
4480c3983b2SBen Gras 		return NULL;
4490c3983b2SBen Gras 	(void)snprintf(sectionbuffer, sizeof(sectionbuffer),
4500c3983b2SBen Gras 		" (%c) - ", sectionext[s]);
4510c3983b2SBen Gras 	return estrdup(sectionbuffer);
4520c3983b2SBen Gras }
4530c3983b2SBen Gras 
4540c3983b2SBen Gras static int
pathnamesection(const char * pat,const char * name)4550c3983b2SBen Gras pathnamesection(const char *pat, const char *name)
4560c3983b2SBen Gras {
4570c3983b2SBen Gras 	char *ptr, *ext;
4580c3983b2SBen Gras 	size_t len = strlen(pat);
4590c3983b2SBen Gras 
4600c3983b2SBen Gras 
4610c3983b2SBen Gras 	while ((ptr = strstr(name, pat)) != NULL) {
4620c3983b2SBen Gras 		if ((ext = strchr(sectionext, ptr[len])) != NULL) {
4630c3983b2SBen Gras 			return ext - sectionext;
4640c3983b2SBen Gras 		}
4650c3983b2SBen Gras 		name = ptr + 1;
4660c3983b2SBen Gras 	}
4670c3983b2SBen Gras 	return -1;
4680c3983b2SBen Gras }
4690c3983b2SBen Gras 
4700c3983b2SBen Gras 
4710c3983b2SBen Gras static int
manpagesection(char * name)4720c3983b2SBen Gras manpagesection(char *name)
4730c3983b2SBen Gras {
4740c3983b2SBen Gras 	char	*ptr;
4750c3983b2SBen Gras 
4760c3983b2SBen Gras 	if ((ptr = strrchr(name, '/')) != NULL)
4770c3983b2SBen Gras 		ptr++;
4780c3983b2SBen Gras 	else
4790c3983b2SBen Gras 		ptr = name;
4800c3983b2SBen Gras 
4810c3983b2SBen Gras 	while ((ptr = strchr(ptr, '.')) != NULL) {
4820c3983b2SBen Gras 		int section;
4830c3983b2SBen Gras 
4840c3983b2SBen Gras 		ptr++;
4850c3983b2SBen Gras 		section = 0;
4860c3983b2SBen Gras 		while (sectionext[section] != '\0')
4870c3983b2SBen Gras 			if (sectionext[section] == *ptr)
4880c3983b2SBen Gras 				return section;
4890c3983b2SBen Gras 			else
4900c3983b2SBen Gras 				section++;
4910c3983b2SBen Gras 	}
4920c3983b2SBen Gras 	return -1;
4930c3983b2SBen Gras }
4940c3983b2SBen Gras 
4950c3983b2SBen Gras static char *
createsectionstring(char * section_id)4960c3983b2SBen Gras createsectionstring(char *section_id)
4970c3983b2SBen Gras {
4980c3983b2SBen Gras 	char *section;
4990c3983b2SBen Gras 
5000c3983b2SBen Gras 	if (asprintf(&section, " (%s) - ", section_id) < 0)
5010c3983b2SBen Gras 		err(EXIT_FAILURE, "malloc failed");
5020c3983b2SBen Gras 	return section;
5030c3983b2SBen Gras }
5040c3983b2SBen Gras 
5050c3983b2SBen Gras static void
addmanpage(manpage ** tree,ino_t inode,char * name,size_t sdoff,size_t sdlen)5060c3983b2SBen Gras addmanpage(manpage **tree, ino_t inode, char *name, size_t sdoff, size_t sdlen)
5070c3983b2SBen Gras {
5080c3983b2SBen Gras 	manpage *mp;
5090c3983b2SBen Gras 
5100c3983b2SBen Gras 	while ((mp = *tree) != NULL) {
5110c3983b2SBen Gras 		if (mp->mp_inode == inode)
5120c3983b2SBen Gras 			return;
5130c3983b2SBen Gras 		tree = inode < mp->mp_inode ? &mp->mp_left : &mp->mp_right;
5140c3983b2SBen Gras 	}
5150c3983b2SBen Gras 
5160c3983b2SBen Gras 	mp = emalloc(sizeof(manpage) + strlen(name));
5170c3983b2SBen Gras 	mp->mp_left = NULL;
5180c3983b2SBen Gras 	mp->mp_right = NULL;
5190c3983b2SBen Gras 	mp->mp_inode = inode;
5200c3983b2SBen Gras 	mp->mp_sdoff = sdoff;
5210c3983b2SBen Gras 	mp->mp_sdlen = sdlen;
5220c3983b2SBen Gras 	(void)strcpy(mp->mp_name, name);
5230c3983b2SBen Gras 	*tree = mp;
5240c3983b2SBen Gras }
5250c3983b2SBen Gras 
5260c3983b2SBen Gras static void
addwhatis(whatis ** tree,char * data,char * prefix)5270c3983b2SBen Gras addwhatis(whatis **tree, char *data, char *prefix)
5280c3983b2SBen Gras {
5290c3983b2SBen Gras 	whatis *wi;
5300c3983b2SBen Gras 	int result;
5310c3983b2SBen Gras 
5320c3983b2SBen Gras 	while (ISSPACE(*data))
5330c3983b2SBen Gras 		data++;
5340c3983b2SBen Gras 
5350c3983b2SBen Gras 	if (*data == '/') {
5360c3983b2SBen Gras 		char *ptr;
5370c3983b2SBen Gras 
5380c3983b2SBen Gras 		ptr = ++data;
5390c3983b2SBen Gras 		while ((*ptr != '\0') && !ISSPACE(*ptr))
5400c3983b2SBen Gras 			if (*ptr++ == '/')
5410c3983b2SBen Gras 				data = ptr;
5420c3983b2SBen Gras 	}
5430c3983b2SBen Gras 
5440c3983b2SBen Gras 	while ((wi = *tree) != NULL) {
5450c3983b2SBen Gras 		result = strcmp(data, wi->wi_data);
5460c3983b2SBen Gras 		if (result == 0) result = strcmp(prefix, wi->wi_prefix);
5470c3983b2SBen Gras 		if (result == 0) return;
5480c3983b2SBen Gras 		tree = result < 0 ? &wi->wi_left : &wi->wi_right;
5490c3983b2SBen Gras 	}
5500c3983b2SBen Gras 
5510c3983b2SBen Gras 	wi = emalloc(sizeof(whatis) + strlen(prefix));
5520c3983b2SBen Gras 
5530c3983b2SBen Gras 	wi->wi_left = NULL;
5540c3983b2SBen Gras 	wi->wi_right = NULL;
5550c3983b2SBen Gras 	wi->wi_data = data;
5560c3983b2SBen Gras 	if (prefix[0] != '\0')
5570c3983b2SBen Gras 		(void) strcpy(wi->wi_prefix, prefix);
5580c3983b2SBen Gras 	else
5590c3983b2SBen Gras 		wi->wi_prefix[0] = '\0';
5600c3983b2SBen Gras 	*tree = wi;
5610c3983b2SBen Gras }
5620c3983b2SBen Gras 
5630c3983b2SBen Gras static void
catpreprocess(char * from)5640c3983b2SBen Gras catpreprocess(char *from)
5650c3983b2SBen Gras {
5660c3983b2SBen Gras 	char	*to;
5670c3983b2SBen Gras 
5680c3983b2SBen Gras 	to = from;
5690c3983b2SBen Gras 	while (ISSPACE(*from)) from++;
5700c3983b2SBen Gras 
5710c3983b2SBen Gras 	while (*from != '\0')
5720c3983b2SBen Gras 		if (ISSPACE(*from)) {
5730c3983b2SBen Gras 			while (ISSPACE(*++from));
5740c3983b2SBen Gras 			if (*from != '\0')
5750c3983b2SBen Gras 				*to++ = ' ';
5760c3983b2SBen Gras 		}
5770c3983b2SBen Gras 		else if (*(from + 1) == '\b')
5780c3983b2SBen Gras 			from += 2;
5790c3983b2SBen Gras 		else
5800c3983b2SBen Gras 			*to++ = *from++;
5810c3983b2SBen Gras 
5820c3983b2SBen Gras 	*to = '\0';
5830c3983b2SBen Gras }
5840c3983b2SBen Gras 
5850c3983b2SBen Gras static char *
makewhatisline(const char * file,const char * line,const char * section)5860c3983b2SBen Gras makewhatisline(const char *file, const char *line, const char *section)
5870c3983b2SBen Gras {
5880c3983b2SBen Gras 	static const char *del[] = {
5890c3983b2SBen Gras 		" - ",
5900c3983b2SBen Gras 		" -- ",
5910c3983b2SBen Gras 		"- ",
5920c3983b2SBen Gras 		" -",
5930c3983b2SBen Gras 		NULL
5940c3983b2SBen Gras 	};
5950c3983b2SBen Gras 	size_t i, pos;
5960c3983b2SBen Gras 	size_t llen, slen, dlen;
5970c3983b2SBen Gras 	char *result, *ptr;
5980c3983b2SBen Gras 
5990c3983b2SBen Gras 	ptr = NULL;
6000c3983b2SBen Gras 	if (section == NULL) {
6010c3983b2SBen Gras 		if (dowarn)
6020c3983b2SBen Gras 			warnx("%s: No section provided for `%s'", file, line);
6030c3983b2SBen Gras 		return estrdup(line);
6040c3983b2SBen Gras 	}
6050c3983b2SBen Gras 
6060c3983b2SBen Gras 	for (i = 0; del[i]; i++)
6070c3983b2SBen Gras 		if ((ptr = strstr(line, del[i])) != NULL)
6080c3983b2SBen Gras 			break;
6090c3983b2SBen Gras 
6100c3983b2SBen Gras 	if (del[i] == NULL) {
6110c3983b2SBen Gras 		if (dowarn)
6120c3983b2SBen Gras 			warnx("%s: Bad format line `%s'", file, line);
6130c3983b2SBen Gras 		return estrdup(line);
6140c3983b2SBen Gras 	}
6150c3983b2SBen Gras 
6160c3983b2SBen Gras 	slen = strlen(section);
6170c3983b2SBen Gras 	llen = strlen(line);
6180c3983b2SBen Gras 	dlen = strlen(del[i]);
6190c3983b2SBen Gras 
6200c3983b2SBen Gras 	result = emalloc(llen - dlen + slen + 1);
6210c3983b2SBen Gras 	pos = ptr - line;
6220c3983b2SBen Gras 
6230c3983b2SBen Gras 	(void)memcpy(result, line, pos);
6240c3983b2SBen Gras 	(void)memcpy(&result[pos], section, slen);
6250c3983b2SBen Gras 	(void)strcpy(&result[pos + slen], &line[pos + dlen]);
6260c3983b2SBen Gras 	return result;
6270c3983b2SBen Gras }
6280c3983b2SBen Gras 
6290c3983b2SBen Gras static char *
parsecatpage(const char * name,gzFile * in)6300c3983b2SBen Gras parsecatpage(const char *name, gzFile *in)
6310c3983b2SBen Gras {
6320c3983b2SBen Gras 	char	 buffer[8192];
6330c3983b2SBen Gras 	char	*section, *ptr, *last;
6340c3983b2SBen Gras 	size_t	 size;
6350c3983b2SBen Gras 
6360c3983b2SBen Gras 	do {
6370c3983b2SBen Gras 		if (GetS(in, buffer, sizeof(buffer)) == NULL)
6380c3983b2SBen Gras 			return NULL;
6390c3983b2SBen Gras 	}
6400c3983b2SBen Gras 	while (buffer[0] == '\n');
6410c3983b2SBen Gras 
6420c3983b2SBen Gras 	section = NULL;
6430c3983b2SBen Gras 	if ((ptr = strchr(buffer, '(')) != NULL) {
6440c3983b2SBen Gras 		if ((last = strchr(ptr + 1, ')')) !=NULL) {
6450c3983b2SBen Gras 			size_t	length;
6460c3983b2SBen Gras 
6470c3983b2SBen Gras 			length = last - ptr + 1;
6480c3983b2SBen Gras 			section = emalloc(length + 5);
6490c3983b2SBen Gras 			*section = ' ';
6500c3983b2SBen Gras 			(void) memcpy(section + 1, ptr, length);
6510c3983b2SBen Gras 			(void) strcpy(section + 1 + length, " - ");
6520c3983b2SBen Gras 		}
6530c3983b2SBen Gras 	}
6540c3983b2SBen Gras 
6550c3983b2SBen Gras 	for (;;) {
6560c3983b2SBen Gras 		if (GetS(in, buffer, sizeof(buffer)) == NULL) {
6570c3983b2SBen Gras 			free(section);
6580c3983b2SBen Gras 			return NULL;
6590c3983b2SBen Gras 		}
6600c3983b2SBen Gras 		catpreprocess(buffer);
6610c3983b2SBen Gras 		if (strncmp(buffer, "NAME", 4) == 0)
6620c3983b2SBen Gras 			break;
6630c3983b2SBen Gras 	}
6640c3983b2SBen Gras 	if (section == NULL)
6650c3983b2SBen Gras 		section = makesection(pathnamesection("/cat", name));
6660c3983b2SBen Gras 
6670c3983b2SBen Gras 	ptr = last = buffer;
6680c3983b2SBen Gras 	size = sizeof(buffer) - 1;
6690c3983b2SBen Gras 	while ((size > 0) && (GetS(in, ptr, size) != NULL)) {
6700c3983b2SBen Gras 		int	 length;
6710c3983b2SBen Gras 
6720c3983b2SBen Gras 		catpreprocess(ptr);
6730c3983b2SBen Gras 
6740c3983b2SBen Gras 		length = strlen(ptr);
6750c3983b2SBen Gras 		if (length == 0) {
6760c3983b2SBen Gras 			*last = '\0';
6770c3983b2SBen Gras 
6780c3983b2SBen Gras 			ptr = makewhatisline(name, buffer, section);
6790c3983b2SBen Gras 			free(section);
6800c3983b2SBen Gras 			return ptr;
6810c3983b2SBen Gras 		}
6820c3983b2SBen Gras 		if ((length > 1) && (ptr[length - 1] == '-') &&
6830c3983b2SBen Gras 		    ISALPHA(ptr[length - 2]))
6840c3983b2SBen Gras 			last = &ptr[--length];
6850c3983b2SBen Gras 		else {
6860c3983b2SBen Gras 			last = &ptr[length++];
6870c3983b2SBen Gras 			*last = ' ';
6880c3983b2SBen Gras 		}
6890c3983b2SBen Gras 
6900c3983b2SBen Gras 		ptr += length;
6910c3983b2SBen Gras 		size -= length;
6920c3983b2SBen Gras 	}
6930c3983b2SBen Gras 
6940c3983b2SBen Gras 	free(section);
6950c3983b2SBen Gras 
6960c3983b2SBen Gras 	return NULL;
6970c3983b2SBen Gras }
6980c3983b2SBen Gras 
6990c3983b2SBen Gras static int
manpreprocess(char * line)7000c3983b2SBen Gras manpreprocess(char *line)
7010c3983b2SBen Gras {
7020c3983b2SBen Gras 	char	*from, *to;
7030c3983b2SBen Gras 
7040c3983b2SBen Gras 	to = from = line;
7050c3983b2SBen Gras 	while (ISSPACE(*from))
7060c3983b2SBen Gras 		from++;
7070c3983b2SBen Gras 	if (strncmp(from, ".\\\"", 3) == 0)
7080c3983b2SBen Gras 		return 1;
7090c3983b2SBen Gras 
7100c3983b2SBen Gras 	while (*from != '\0')
7110c3983b2SBen Gras 		if (ISSPACE(*from)) {
7120c3983b2SBen Gras 			while (ISSPACE(*++from));
7130c3983b2SBen Gras 			if ((*from != '\0') && (*from != ','))
7140c3983b2SBen Gras 				*to++ = ' ';
7150c3983b2SBen Gras 		} else if (*from == '\\') {
7160c3983b2SBen Gras 			switch (*++from) {
7170c3983b2SBen Gras 			case '\0':
7180c3983b2SBen Gras 			case '-':
7190c3983b2SBen Gras 				break;
7200c3983b2SBen Gras 			case 'f':
7210c3983b2SBen Gras 			case 's':
7220c3983b2SBen Gras 				from++;
7230c3983b2SBen Gras 				if ((*from=='+') || (*from=='-'))
7240c3983b2SBen Gras 					from++;
7250c3983b2SBen Gras 				while (ISDIGIT(*from))
7260c3983b2SBen Gras 					from++;
7270c3983b2SBen Gras 				break;
7280c3983b2SBen Gras 			default:
7290c3983b2SBen Gras 				from++;
7300c3983b2SBen Gras 			}
7310c3983b2SBen Gras 		} else {
7320c3983b2SBen Gras 			if (*from == '"')
7330c3983b2SBen Gras 				from++;
7340c3983b2SBen Gras 			else
7350c3983b2SBen Gras 				*to++ = *from++;
7360c3983b2SBen Gras 		}
7370c3983b2SBen Gras 
7380c3983b2SBen Gras 	*to = '\0';
7390c3983b2SBen Gras 
7400c3983b2SBen Gras 	if (strncasecmp(line, ".Xr", 3) == 0) {
7410c3983b2SBen Gras 		char	*sect;
7420c3983b2SBen Gras 
7430c3983b2SBen Gras 		from = line + 3;
7440c3983b2SBen Gras 		if (ISSPACE(*from))
7450c3983b2SBen Gras 			from++;
7460c3983b2SBen Gras 
7470c3983b2SBen Gras 		if ((sect = findwhitespace(from)) != NULL) {
7480c3983b2SBen Gras 			size_t	length;
7490c3983b2SBen Gras 			char	*trail;
7500c3983b2SBen Gras 
7510c3983b2SBen Gras 			*sect++ = '\0';
7520c3983b2SBen Gras 			if ((trail = findwhitespace(sect)) != NULL)
7530c3983b2SBen Gras 				*trail++ = '\0';
7540c3983b2SBen Gras 			length = strlen(from);
7550c3983b2SBen Gras 			(void) memmove(line, from, length);
7560c3983b2SBen Gras 			line[length++] = '(';
7570c3983b2SBen Gras 			to = &line[length];
7580c3983b2SBen Gras 			length = strlen(sect);
7590c3983b2SBen Gras 			(void) memmove(to, sect, length);
7600c3983b2SBen Gras 			if (trail == NULL) {
7610c3983b2SBen Gras 				(void) strcpy(&to[length], ")");
7620c3983b2SBen Gras 			} else {
7630c3983b2SBen Gras 				to += length;
7640c3983b2SBen Gras 				*to++ = ')';
7650c3983b2SBen Gras 				length = strlen(trail);
7660c3983b2SBen Gras 				(void) memmove(to, trail, length + 1);
7670c3983b2SBen Gras 			}
7680c3983b2SBen Gras 		}
7690c3983b2SBen Gras 	}
7700c3983b2SBen Gras 
7710c3983b2SBen Gras 	return 0;
7720c3983b2SBen Gras }
7730c3983b2SBen Gras 
7740c3983b2SBen Gras static char *
nroff(const char * inname,gzFile * in)7750c3983b2SBen Gras nroff(const char *inname, gzFile *in)
7760c3983b2SBen Gras {
7770c3983b2SBen Gras 	char tempname[MAXPATHLEN], buffer[65536], *data;
7780c3983b2SBen Gras 	int tempfd, bytes, pipefd[2], status;
7790c3983b2SBen Gras 	static int devnull = -1;
7800c3983b2SBen Gras 	pid_t child;
7810c3983b2SBen Gras 
7820c3983b2SBen Gras 	if (gzrewind(in) < 0)
7830c3983b2SBen Gras 		err(EXIT_FAILURE, "Cannot rewind pipe");
7840c3983b2SBen Gras 
7850c3983b2SBen Gras 	if ((devnull < 0) &&
7860c3983b2SBen Gras 	    ((devnull = open(_PATH_DEVNULL, O_WRONLY, 0)) < 0))
7870c3983b2SBen Gras 		err(EXIT_FAILURE, "Cannot open `/dev/null'");
7880c3983b2SBen Gras 
7890c3983b2SBen Gras 	(void)strlcpy(tempname, _PATH_TMP "makewhatis.XXXXXX",
7900c3983b2SBen Gras 	    sizeof(tempname));
7910c3983b2SBen Gras 	if ((tempfd = mkstemp(tempname)) == -1)
7920c3983b2SBen Gras 		err(EXIT_FAILURE, "Cannot create temp file");
7930c3983b2SBen Gras 
7940c3983b2SBen Gras 	while ((bytes = gzread(in, buffer, sizeof(buffer))) > 0)
7950c3983b2SBen Gras 		if (write(tempfd, buffer, (size_t)bytes) != bytes) {
7960c3983b2SBen Gras 			bytes = -1;
7970c3983b2SBen Gras 			break;
7980c3983b2SBen Gras 		}
7990c3983b2SBen Gras 
8000c3983b2SBen Gras 	if (bytes < 0) {
8010c3983b2SBen Gras 		(void)close(tempfd);
8020c3983b2SBen Gras 		(void)unlink(tempname);
8030c3983b2SBen Gras 		err(EXIT_FAILURE, "Read from pipe failed");
8040c3983b2SBen Gras 	}
8050c3983b2SBen Gras 	if (lseek(tempfd, (off_t)0, SEEK_SET) == (off_t)-1) {
8060c3983b2SBen Gras 		(void)close(tempfd);
8070c3983b2SBen Gras 		(void)unlink(tempname);
8080c3983b2SBen Gras 		err(EXIT_FAILURE, "Cannot rewind temp file");
8090c3983b2SBen Gras 	}
8100c3983b2SBen Gras 	if (pipe(pipefd) == -1) {
8110c3983b2SBen Gras 		(void)close(tempfd);
8120c3983b2SBen Gras 		(void)unlink(tempname);
8130c3983b2SBen Gras 		err(EXIT_FAILURE, "Cannot create pipe");
8140c3983b2SBen Gras 	}
8150c3983b2SBen Gras 
8160c3983b2SBen Gras 	switch (child = vfork()) {
8170c3983b2SBen Gras 	case -1:
8180c3983b2SBen Gras 		(void)close(pipefd[1]);
8190c3983b2SBen Gras 		(void)close(pipefd[0]);
8200c3983b2SBen Gras 		(void)close(tempfd);
8210c3983b2SBen Gras 		(void)unlink(tempname);
8220c3983b2SBen Gras 		err(EXIT_FAILURE, "Fork failed");
8230c3983b2SBen Gras 		/* NOTREACHED */
8240c3983b2SBen Gras 	case 0:
8250c3983b2SBen Gras 		(void)close(pipefd[0]);
8260c3983b2SBen Gras 		if (tempfd != STDIN_FILENO) {
8270c3983b2SBen Gras 			(void)dup2(tempfd, STDIN_FILENO);
8280c3983b2SBen Gras 			(void)close(tempfd);
8290c3983b2SBen Gras 		}
8300c3983b2SBen Gras 		if (pipefd[1] != STDOUT_FILENO) {
8310c3983b2SBen Gras 			(void)dup2(pipefd[1], STDOUT_FILENO);
8320c3983b2SBen Gras 			(void)close(pipefd[1]);
8330c3983b2SBen Gras 		}
8340c3983b2SBen Gras 		if (devnull != STDERR_FILENO) {
8350c3983b2SBen Gras 			(void)dup2(devnull, STDERR_FILENO);
8360c3983b2SBen Gras 			(void)close(devnull);
8370c3983b2SBen Gras 		}
8380c3983b2SBen Gras 		(void)execlp(NROFF, NROFF, "-S", "-man", NULL);
8390c3983b2SBen Gras 		_exit(EXIT_FAILURE);
8400c3983b2SBen Gras 		/*NOTREACHED*/
8410c3983b2SBen Gras 	default:
8420c3983b2SBen Gras 		(void)close(pipefd[1]);
8430c3983b2SBen Gras 		(void)close(tempfd);
8440c3983b2SBen Gras 		break;
8450c3983b2SBen Gras 	}
8460c3983b2SBen Gras 
8470c3983b2SBen Gras 	if ((in = gzdopen(pipefd[0], "r")) == NULL) {
8480c3983b2SBen Gras 		if (errno == 0)
8490c3983b2SBen Gras 			errno = ENOMEM;
8500c3983b2SBen Gras 		(void)close(pipefd[0]);
8510c3983b2SBen Gras 		(void)kill(child, SIGTERM);
8520c3983b2SBen Gras 		while (waitpid(child, NULL, 0) != child);
8530c3983b2SBen Gras 		(void)unlink(tempname);
8540c3983b2SBen Gras 		err(EXIT_FAILURE, "Cannot read from pipe");
8550c3983b2SBen Gras 	}
8560c3983b2SBen Gras 
8570c3983b2SBen Gras 	data = parsecatpage(inname, in);
8580c3983b2SBen Gras 	while (gzread(in, buffer, sizeof(buffer)) > 0);
8590c3983b2SBen Gras 	(void)gzclose(in);
8600c3983b2SBen Gras 
8610c3983b2SBen Gras 	while (waitpid(child, &status, 0) != child);
8620c3983b2SBen Gras 	if ((data != NULL) &&
8630c3983b2SBen Gras 	    !(WIFEXITED(status) && (WEXITSTATUS(status) == 0))) {
8640c3983b2SBen Gras 		free(data);
8650c3983b2SBen Gras 		errx(EXIT_FAILURE, NROFF " on `%s' exited with %d status",
8660c3983b2SBen Gras 		    inname, WEXITSTATUS(status));
8670c3983b2SBen Gras 	}
8680c3983b2SBen Gras 
8690c3983b2SBen Gras 	(void)unlink(tempname);
8700c3983b2SBen Gras 	return data;
8710c3983b2SBen Gras }
8720c3983b2SBen Gras 
8730c3983b2SBen Gras static char *
parsemanpage(const char * name,gzFile * in,int defaultsection)8740c3983b2SBen Gras parsemanpage(const char *name, gzFile *in, int defaultsection)
8750c3983b2SBen Gras {
8760c3983b2SBen Gras 	char	*section, buffer[8192], *ptr;
877910825eeSBen Gras 	static const char POD[] = ".\\\" Automatically generated by Pod";
878910825eeSBen Gras 	static const char IX[] = ".IX TITLE";
8790c3983b2SBen Gras 
8800c3983b2SBen Gras 	section = NULL;
8810c3983b2SBen Gras 	do {
8820c3983b2SBen Gras 		if (GetS(in, buffer, sizeof(buffer) - 1) == NULL) {
8830c3983b2SBen Gras 			free(section);
8840c3983b2SBen Gras 			return NULL;
8850c3983b2SBen Gras 		}
886910825eeSBen Gras 
887910825eeSBen Gras 		/*
888910825eeSBen Gras 		 * Skip over lines in man pages that have been generated
889*84d9c625SLionel Sambuc 		 * by Pod, until we find the TITLE.
890910825eeSBen Gras 		 */
891910825eeSBen Gras 		if (strncasecmp(buffer, POD, sizeof(POD) - 1) == 0) {
892910825eeSBen Gras 			do {
893910825eeSBen Gras 				if (GetS(in, buffer, sizeof(buffer) - 1)
894910825eeSBen Gras 				    == NULL) {
895910825eeSBen Gras 					free(section);
896910825eeSBen Gras 					return NULL;
897910825eeSBen Gras 				}
898910825eeSBen Gras 			} while (strncasecmp(buffer, IX, sizeof(IX) - 1) != 0);
899910825eeSBen Gras 		}
900910825eeSBen Gras 
9010c3983b2SBen Gras 		if (manpreprocess(buffer))
9020c3983b2SBen Gras 			continue;
9030c3983b2SBen Gras 		if (strncasecmp(buffer, ".Dt", 3) == 0) {
9040c3983b2SBen Gras 			char	*end;
9050c3983b2SBen Gras 
9060c3983b2SBen Gras 			ptr = &buffer[3];
9070c3983b2SBen Gras 			if (ISSPACE(*ptr))
9080c3983b2SBen Gras 				ptr++;
9090c3983b2SBen Gras 			if ((ptr = findwhitespace(ptr)) == NULL)
9100c3983b2SBen Gras 				continue;
9110c3983b2SBen Gras 
9120c3983b2SBen Gras 			if ((end = findwhitespace(++ptr)) != NULL)
9130c3983b2SBen Gras 				*end = '\0';
9140c3983b2SBen Gras 
9150c3983b2SBen Gras 			free(section);
9160c3983b2SBen Gras 			section = createsectionstring(ptr);
9170c3983b2SBen Gras 		}
9180c3983b2SBen Gras 		else if (strncasecmp(buffer, ".TH", 3) == 0) {
9190c3983b2SBen Gras 			ptr = &buffer[3];
9200c3983b2SBen Gras 			while (ISSPACE(*ptr))
9210c3983b2SBen Gras 				ptr++;
9220c3983b2SBen Gras 			if ((ptr = findwhitespace(ptr)) != NULL) {
9230c3983b2SBen Gras 				char *next;
9240c3983b2SBen Gras 
9250c3983b2SBen Gras 				while (ISSPACE(*ptr))
9260c3983b2SBen Gras 					ptr++;
9270c3983b2SBen Gras 				if ((next = findwhitespace(ptr)) != NULL)
9280c3983b2SBen Gras 					*next = '\0';
9290c3983b2SBen Gras 				free(section);
9300c3983b2SBen Gras 				section = createsectionstring(ptr);
9310c3983b2SBen Gras 			}
9320c3983b2SBen Gras 		}
9330c3983b2SBen Gras 		else if (strncasecmp(buffer, ".Ds", 3) == 0) {
9340c3983b2SBen Gras 			free(section);
9350c3983b2SBen Gras 			return NULL;
9360c3983b2SBen Gras 		}
9370c3983b2SBen Gras 	} while (strncasecmp(buffer, ".Sh NAME", 8) != 0);
9380c3983b2SBen Gras 
9390c3983b2SBen Gras 	do {
9400c3983b2SBen Gras 		if (GetS(in, buffer, sizeof(buffer) - 1) == NULL) {
9410c3983b2SBen Gras 			free(section);
9420c3983b2SBen Gras 			return NULL;
9430c3983b2SBen Gras 		}
9440c3983b2SBen Gras 	} while (manpreprocess(buffer));
9450c3983b2SBen Gras 
9460c3983b2SBen Gras 	if (strncasecmp(buffer, ".Nm", 3) == 0) {
9470c3983b2SBen Gras 		size_t	length, offset;
9480c3983b2SBen Gras 
9490c3983b2SBen Gras 		ptr = &buffer[3];
9500c3983b2SBen Gras 		while (ISSPACE(*ptr))
9510c3983b2SBen Gras 			ptr++;
9520c3983b2SBen Gras 
9530c3983b2SBen Gras 		length = strlen(ptr);
9540c3983b2SBen Gras 		if ((length > 1) && (ptr[length - 1] == ',') &&
9550c3983b2SBen Gras 		    ISSPACE(ptr[length - 2])) {
9560c3983b2SBen Gras 			ptr[--length] = '\0';
9570c3983b2SBen Gras 			ptr[length - 1] = ',';
9580c3983b2SBen Gras 		}
9590c3983b2SBen Gras 		(void) memmove(buffer, ptr, length + 1);
9600c3983b2SBen Gras 
9610c3983b2SBen Gras 		offset = length + 3;
9620c3983b2SBen Gras 		ptr = &buffer[offset];
9630c3983b2SBen Gras 		for (;;) {
9640c3983b2SBen Gras 			size_t	 more;
9650c3983b2SBen Gras 
9660c3983b2SBen Gras 			if ((sizeof(buffer) == offset) ||
9670c3983b2SBen Gras 			    (GetS(in, ptr, sizeof(buffer) - offset)
9680c3983b2SBen Gras 			       == NULL)) {
9690c3983b2SBen Gras 				free(section);
9700c3983b2SBen Gras 				return NULL;
9710c3983b2SBen Gras 			}
9720c3983b2SBen Gras 			if (manpreprocess(ptr))
9730c3983b2SBen Gras 				continue;
9740c3983b2SBen Gras 
9750c3983b2SBen Gras 			if (strncasecmp(ptr, ".Nm", 3) != 0) break;
9760c3983b2SBen Gras 
9770c3983b2SBen Gras 			ptr += 3;
9780c3983b2SBen Gras 			if (ISSPACE(*ptr))
9790c3983b2SBen Gras 				ptr++;
9800c3983b2SBen Gras 
9810c3983b2SBen Gras 			buffer[length++] = ' ';
9820c3983b2SBen Gras 			more = strlen(ptr);
9830c3983b2SBen Gras 			if ((more > 1) && (ptr[more - 1] == ',') &&
9840c3983b2SBen Gras 			    ISSPACE(ptr[more - 2])) {
9850c3983b2SBen Gras 				ptr[--more] = '\0';
9860c3983b2SBen Gras 				ptr[more - 1] = ',';
9870c3983b2SBen Gras 			}
9880c3983b2SBen Gras 
9890c3983b2SBen Gras 			(void) memmove(&buffer[length], ptr, more + 1);
9900c3983b2SBen Gras 			length += more;
9910c3983b2SBen Gras 			offset = length + 3;
9920c3983b2SBen Gras 
9930c3983b2SBen Gras 			ptr = &buffer[offset];
9940c3983b2SBen Gras 		}
9950c3983b2SBen Gras 
9960c3983b2SBen Gras 		if (strncasecmp(ptr, ".Nd", 3) == 0) {
9970c3983b2SBen Gras 			(void) strlcpy(&buffer[length], " -",
9980c3983b2SBen Gras 			    sizeof(buffer) - length);
9990c3983b2SBen Gras 
10000c3983b2SBen Gras 			while (strncasecmp(ptr, ".Sh", 3) != 0) {
10010c3983b2SBen Gras 				int	 more;
10020c3983b2SBen Gras 
10030c3983b2SBen Gras 				if (*ptr == '.') {
10040c3983b2SBen Gras 					char	*space;
10050c3983b2SBen Gras 
10060c3983b2SBen Gras 					if (strncasecmp(ptr, ".Nd", 3) != 0 ||
10070c3983b2SBen Gras 					    strchr(ptr, '[') != NULL) {
10080c3983b2SBen Gras 						free(section);
10090c3983b2SBen Gras 						return NULL;
10100c3983b2SBen Gras 					}
10110c3983b2SBen Gras 					space = findwhitespace(ptr);
10120c3983b2SBen Gras 					if (space == NULL) {
10130c3983b2SBen Gras 						ptr = "";
10140c3983b2SBen Gras 					} else {
10150c3983b2SBen Gras 						space++;
10160c3983b2SBen Gras 						(void) strmove(ptr, space);
10170c3983b2SBen Gras 					}
10180c3983b2SBen Gras 				}
10190c3983b2SBen Gras 
10200c3983b2SBen Gras 				if (*ptr != '\0') {
10210c3983b2SBen Gras 					buffer[offset - 1] = ' ';
10220c3983b2SBen Gras 					more = strlen(ptr) + 1;
10230c3983b2SBen Gras 					offset += more;
10240c3983b2SBen Gras 				}
10250c3983b2SBen Gras 				ptr = &buffer[offset];
10260c3983b2SBen Gras 				if ((sizeof(buffer) == offset) ||
10270c3983b2SBen Gras 				    (GetS(in, ptr, sizeof(buffer) - offset)
10280c3983b2SBen Gras 					== NULL)) {
10290c3983b2SBen Gras 					free(section);
10300c3983b2SBen Gras 					return NULL;
10310c3983b2SBen Gras 				}
10320c3983b2SBen Gras 				if (manpreprocess(ptr))
10330c3983b2SBen Gras 					*ptr = '\0';
10340c3983b2SBen Gras 			}
10350c3983b2SBen Gras 		}
10360c3983b2SBen Gras 	}
10370c3983b2SBen Gras 	else {
10380c3983b2SBen Gras 		int	 offset;
10390c3983b2SBen Gras 
10400c3983b2SBen Gras 		if (*buffer == '.') {
10410c3983b2SBen Gras 			char	*space;
10420c3983b2SBen Gras 
10430c3983b2SBen Gras 			if ((space = findwhitespace(&buffer[1])) == NULL) {
10440c3983b2SBen Gras 				free(section);
10450c3983b2SBen Gras 				return NULL;
10460c3983b2SBen Gras 			}
10470c3983b2SBen Gras 			space++;
10480c3983b2SBen Gras 			(void) strmove(buffer, space);
10490c3983b2SBen Gras 		}
10500c3983b2SBen Gras 
10510c3983b2SBen Gras 		offset = strlen(buffer) + 1;
10520c3983b2SBen Gras 		for (;;) {
10530c3983b2SBen Gras 			int	 more;
10540c3983b2SBen Gras 
10550c3983b2SBen Gras 			ptr = &buffer[offset];
10560c3983b2SBen Gras 			if ((sizeof(buffer) == offset) ||
10570c3983b2SBen Gras 			    (GetS(in, ptr, sizeof(buffer) - offset)
10580c3983b2SBen Gras 				== NULL)) {
10590c3983b2SBen Gras 				free(section);
10600c3983b2SBen Gras 				return NULL;
10610c3983b2SBen Gras 			}
10620c3983b2SBen Gras 			if (manpreprocess(ptr) || (*ptr == '\0'))
10630c3983b2SBen Gras 				continue;
10640c3983b2SBen Gras 
10650c3983b2SBen Gras 			if ((strncasecmp(ptr, ".Sh", 3) == 0) ||
10660c3983b2SBen Gras 			    (strncasecmp(ptr, ".Ss", 3) == 0))
10670c3983b2SBen Gras 				break;
10680c3983b2SBen Gras 
10690c3983b2SBen Gras 			if (*ptr == '.') {
10700c3983b2SBen Gras 				char	*space;
10710c3983b2SBen Gras 
10720c3983b2SBen Gras 				if ((space = findwhitespace(ptr)) == NULL) {
10730c3983b2SBen Gras 					continue;
10740c3983b2SBen Gras 				}
10750c3983b2SBen Gras 
10760c3983b2SBen Gras 				space++;
10770c3983b2SBen Gras 				(void) memmove(ptr, space, strlen(space) + 1);
10780c3983b2SBen Gras 			}
10790c3983b2SBen Gras 
10800c3983b2SBen Gras 			buffer[offset - 1] = ' ';
10810c3983b2SBen Gras 			more = strlen(ptr);
10820c3983b2SBen Gras 			if ((more > 1) && (ptr[more - 1] == ',') &&
10830c3983b2SBen Gras 			    ISSPACE(ptr[more - 2])) {
10840c3983b2SBen Gras 				ptr[more - 1] = '\0';
10850c3983b2SBen Gras 				ptr[more - 2] = ',';
10860c3983b2SBen Gras 			}
10870c3983b2SBen Gras 			else more++;
10880c3983b2SBen Gras 			offset += more;
10890c3983b2SBen Gras 		}
10900c3983b2SBen Gras 	}
10910c3983b2SBen Gras 
10920c3983b2SBen Gras 	if (section == NULL)
10930c3983b2SBen Gras 		section = makesection(defaultsection);
10940c3983b2SBen Gras 
10950c3983b2SBen Gras 	ptr = makewhatisline(name, buffer, section);
10960c3983b2SBen Gras 	free(section);
10970c3983b2SBen Gras 	return ptr;
10980c3983b2SBen Gras }
10990c3983b2SBen Gras 
11000c3983b2SBen Gras static char *
getwhatisdata(char * name)11010c3983b2SBen Gras getwhatisdata(char *name)
11020c3983b2SBen Gras {
11030c3983b2SBen Gras 	gzFile	*in;
11040c3983b2SBen Gras 	char	*data;
11050c3983b2SBen Gras 	int	 section;
11060c3983b2SBen Gras 
11070c3983b2SBen Gras 	if ((in = gzopen(name, "r")) == NULL) {
11080c3983b2SBen Gras 		if (errno == 0)
11090c3983b2SBen Gras 			errno = ENOMEM;
11100c3983b2SBen Gras 		err(EXIT_FAILURE, "Cannot open `%s'", name);
11110c3983b2SBen Gras 		/* NOTREACHED */
11120c3983b2SBen Gras 	}
11130c3983b2SBen Gras 
11140c3983b2SBen Gras 	section = manpagesection(name);
11150c3983b2SBen Gras 	if (section == 0) {
11160c3983b2SBen Gras 		data = parsecatpage(name, in);
11170c3983b2SBen Gras 	} else {
11180c3983b2SBen Gras 		data = parsemanpage(name, in, section);
11190c3983b2SBen Gras 		if (data == NULL)
11200c3983b2SBen Gras 			data = nroff(name, in);
11210c3983b2SBen Gras 	}
11220c3983b2SBen Gras 
11230c3983b2SBen Gras 	(void) gzclose(in);
11240c3983b2SBen Gras 	return data;
11250c3983b2SBen Gras }
11260c3983b2SBen Gras 
11270c3983b2SBen Gras static void
processmanpages(manpage ** source,whatis ** dest)11280c3983b2SBen Gras processmanpages(manpage **source, whatis **dest)
11290c3983b2SBen Gras {
11300c3983b2SBen Gras 	manpage *mp;
11310c3983b2SBen Gras 	char sd[128];
11320c3983b2SBen Gras 
11330c3983b2SBen Gras 	mp = *source;
11340c3983b2SBen Gras 	*source = NULL;
11350c3983b2SBen Gras 
11360c3983b2SBen Gras 	while (mp != NULL) {
11370c3983b2SBen Gras 		manpage *obsolete;
11380c3983b2SBen Gras 		char *data;
11390c3983b2SBen Gras 
11400c3983b2SBen Gras 		if (mp->mp_left != NULL)
11410c3983b2SBen Gras 			processmanpages(&mp->mp_left, dest);
11420c3983b2SBen Gras 
11430c3983b2SBen Gras 		if ((data = getwhatisdata(mp->mp_name)) != NULL) {
11440c3983b2SBen Gras 			/* Pass eventual directory prefix to addwhatis() */
11450c3983b2SBen Gras 			if (mp->mp_sdlen > 0 && mp->mp_sdlen < sizeof(sd)-1)
11460c3983b2SBen Gras 				strlcpy(sd, &mp->mp_name[mp->mp_sdoff],
11470c3983b2SBen Gras 					mp->mp_sdlen);
11480c3983b2SBen Gras 			else
11490c3983b2SBen Gras 				sd[0] = '\0';
11500c3983b2SBen Gras 
11510c3983b2SBen Gras 			addwhatis(dest, data, sd);
11520c3983b2SBen Gras 		}
11530c3983b2SBen Gras 
11540c3983b2SBen Gras 		obsolete = mp;
11550c3983b2SBen Gras 		mp = mp->mp_right;
11560c3983b2SBen Gras 		free(obsolete);
11570c3983b2SBen Gras 	}
11580c3983b2SBen Gras }
11590c3983b2SBen Gras 
11600c3983b2SBen Gras static void
dumpwhatis(FILE * out,whatis * tree)11610c3983b2SBen Gras dumpwhatis(FILE *out, whatis *tree)
11620c3983b2SBen Gras {
11630c3983b2SBen Gras 	while (tree != NULL) {
11640c3983b2SBen Gras 		if (tree->wi_left)
11650c3983b2SBen Gras 			dumpwhatis(out, tree->wi_left);
11660c3983b2SBen Gras 
11670c3983b2SBen Gras 		if ((tree->wi_data[0] && fputs(tree->wi_prefix, out) == EOF) ||
11680c3983b2SBen Gras 		    (fputs(tree->wi_data, out) == EOF) ||
11690c3983b2SBen Gras 		    (fputc('\n', out) == EOF))
11700c3983b2SBen Gras 			err(EXIT_FAILURE, "Write failed");
11710c3983b2SBen Gras 
11720c3983b2SBen Gras 		tree = tree->wi_right;
11730c3983b2SBen Gras 	}
11740c3983b2SBen Gras }
1175