xref: /titanic_50/usr/src/cmd/man/man.c (revision d456640d8a14f0ba35a7ec232f0ce9ebd34541cb)
195c635efSGarrett D'Amore /*
295c635efSGarrett D'Amore  * CDDL HEADER START
395c635efSGarrett D'Amore  *
495c635efSGarrett D'Amore  * The contents of this file are subject to the terms of the
595c635efSGarrett D'Amore  * Common Development and Distribution License (the "License").
695c635efSGarrett D'Amore  * You may not use this file except in compliance with the License.
795c635efSGarrett D'Amore  *
895c635efSGarrett D'Amore  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
995c635efSGarrett D'Amore  * or http://www.opensolaris.org/os/licensing.
1095c635efSGarrett D'Amore  * See the License for the specific language governing permissions
1195c635efSGarrett D'Amore  * and limitations under the License.
1295c635efSGarrett D'Amore  *
1395c635efSGarrett D'Amore  * When distributing Covered Code, include this CDDL HEADER in each
1495c635efSGarrett D'Amore  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1595c635efSGarrett D'Amore  * If applicable, add the following below this CDDL HEADER, with the
1695c635efSGarrett D'Amore  * fields enclosed by brackets "[]" replaced with your own identifying
1795c635efSGarrett D'Amore  * information: Portions Copyright [yyyy] [name of copyright owner]
1895c635efSGarrett D'Amore  *
1995c635efSGarrett D'Amore  * CDDL HEADER END
2095c635efSGarrett D'Amore  */
2195c635efSGarrett D'Amore 
2295c635efSGarrett D'Amore /*
2395c635efSGarrett D'Amore  * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
2495c635efSGarrett D'Amore  * Copyright 2012, Josef 'Jeff' Sipek <jeffpc@31bits.net>. All rights reserved.
2595c635efSGarrett D'Amore  * Copyright 2014 Garrett D'Amore <garrett@damore.org>
26*d456640dSYuri Pankov  * Copyright 2016 Nexenta Systems, Inc.
2795c635efSGarrett D'Amore  */
2895c635efSGarrett D'Amore 
2995c635efSGarrett D'Amore /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989  AT&T.	*/
3095c635efSGarrett D'Amore /*		All rights reserved.					*/
3195c635efSGarrett D'Amore 
3295c635efSGarrett D'Amore /*
3395c635efSGarrett D'Amore  * University Copyright- Copyright (c) 1982, 1986, 1988
3495c635efSGarrett D'Amore  * The Regents of the University of California
3595c635efSGarrett D'Amore  * All Rights Reserved
3695c635efSGarrett D'Amore  *
3795c635efSGarrett D'Amore  * University Acknowledgment- Portions of this document are derived from
3895c635efSGarrett D'Amore  * software developed by the University of California, Berkeley, and its
3995c635efSGarrett D'Amore  * contributors.
4095c635efSGarrett D'Amore  */
4195c635efSGarrett D'Amore 
4295c635efSGarrett D'Amore /*
4395c635efSGarrett D'Amore  * Find and display reference manual pages. This version includes makewhatis
4495c635efSGarrett D'Amore  * functionality as well.
4595c635efSGarrett D'Amore  */
4695c635efSGarrett D'Amore 
4795c635efSGarrett D'Amore #include <sys/param.h>
4895c635efSGarrett D'Amore #include <sys/stat.h>
4995c635efSGarrett D'Amore #include <sys/termios.h>
5095c635efSGarrett D'Amore #include <sys/types.h>
5195c635efSGarrett D'Amore 
5295c635efSGarrett D'Amore #include <ctype.h>
5395c635efSGarrett D'Amore #include <dirent.h>
5495c635efSGarrett D'Amore #include <err.h>
5595c635efSGarrett D'Amore #include <errno.h>
5695c635efSGarrett D'Amore #include <fcntl.h>
5795c635efSGarrett D'Amore #include <fnmatch.h>
5895c635efSGarrett D'Amore #include <limits.h>
5995c635efSGarrett D'Amore #include <locale.h>
6095c635efSGarrett D'Amore #include <malloc.h>
6195c635efSGarrett D'Amore #include <memory.h>
6295c635efSGarrett D'Amore #include <regex.h>
6395c635efSGarrett D'Amore #include <stdio.h>
6495c635efSGarrett D'Amore #include <stdlib.h>
6595c635efSGarrett D'Amore #include <string.h>
6695c635efSGarrett D'Amore #include <unistd.h>
6795c635efSGarrett D'Amore 
6895c635efSGarrett D'Amore #include "man.h"
6995c635efSGarrett D'Amore 
7095c635efSGarrett D'Amore 
7195c635efSGarrett D'Amore /* Mapping of old directories to new directories */
7295c635efSGarrett D'Amore static const struct map_entry {
7395c635efSGarrett D'Amore 	char	*old_name;
7495c635efSGarrett D'Amore 	char	*new_name;
7595c635efSGarrett D'Amore } map[] = {
7695c635efSGarrett D'Amore 	{ "3b",		"3ucb"		},
7795c635efSGarrett D'Amore 	{ "3e",		"3elf"		},
7895c635efSGarrett D'Amore 	{ "3g",		"3gen"		},
7995c635efSGarrett D'Amore 	{ "3k",		"3kstat"	},
8095c635efSGarrett D'Amore 	{ "3n",		"3socket"	},
8195c635efSGarrett D'Amore 	{ "3r",		"3rt"		},
8295c635efSGarrett D'Amore 	{ "3s",		"3c"		},
8395c635efSGarrett D'Amore 	{ "3t",		"3thr"		},
8495c635efSGarrett D'Amore 	{ "3x",		"3curses"	},
8595c635efSGarrett D'Amore 	{ "3xc",	"3xcurses"	},
8695c635efSGarrett D'Amore 	{ "3xn",	"3xnet"		},
8795c635efSGarrett D'Amore 	{ NULL,		NULL		}
8895c635efSGarrett D'Amore };
8995c635efSGarrett D'Amore 
9095c635efSGarrett D'Amore struct suffix {
9195c635efSGarrett D'Amore 	char *ds;
9295c635efSGarrett D'Amore 	char *fs;
9395c635efSGarrett D'Amore };
9495c635efSGarrett D'Amore 
9595c635efSGarrett D'Amore /*
9695c635efSGarrett D'Amore  * Flags that control behavior of build_manpath()
9795c635efSGarrett D'Amore  *
9895c635efSGarrett D'Amore  *   BMP_ISPATH 		pathv is a vector constructed from PATH.
9995c635efSGarrett D'Amore  *                		Perform appropriate path translations for
10095c635efSGarrett D'Amore  * 				manpath.
10195c635efSGarrett D'Amore  *   BMP_APPEND_DEFMANDIR	Add DEFMANDIR to the end if it hasn't
10295c635efSGarrett D'Amore  *				already appeared earlier.
10395c635efSGarrett D'Amore  *   BMP_FALLBACK_DEFMANDIR	Append /usr/share/man only if no other
10495c635efSGarrett D'Amore  *				manpath (including derived from PATH)
10595c635efSGarrett D'Amore  * 				elements are valid.
10695c635efSGarrett D'Amore  */
10795c635efSGarrett D'Amore #define	BMP_ISPATH		1
10895c635efSGarrett D'Amore #define	BMP_APPEND_DEFMANDIR	2
10995c635efSGarrett D'Amore #define	BMP_FALLBACK_DEFMANDIR	4
11095c635efSGarrett D'Amore 
11195c635efSGarrett D'Amore /*
11295c635efSGarrett D'Amore  * When doing equality comparisons of directories, device and inode
11395c635efSGarrett D'Amore  * comparisons are done.  The secnode and dupnode structures are used
11495c635efSGarrett D'Amore  * to form a list of lists for this processing.
11595c635efSGarrett D'Amore  */
11695c635efSGarrett D'Amore struct secnode {
11795c635efSGarrett D'Amore 	char		*secp;
11895c635efSGarrett D'Amore 	struct secnode	*next;
11995c635efSGarrett D'Amore };
12095c635efSGarrett D'Amore struct dupnode {
12195c635efSGarrett D'Amore 	dev_t		dev;	/* from struct stat st_dev */
12295c635efSGarrett D'Amore 	ino_t		ino;	/* from struct stat st_ino */
12395c635efSGarrett D'Amore 	struct secnode	*secl;	/* sections already considered */
12495c635efSGarrett D'Amore 	struct dupnode	*next;
12595c635efSGarrett D'Amore };
12695c635efSGarrett D'Amore 
12795c635efSGarrett D'Amore /*
12895c635efSGarrett D'Amore  * Map directories that may appear in PATH to the corresponding
12995c635efSGarrett D'Amore  * man directory.
13095c635efSGarrett D'Amore  */
13195c635efSGarrett D'Amore static struct pathmap {
13295c635efSGarrett D'Amore 	char	*bindir;
13395c635efSGarrett D'Amore 	char	*mandir;
13495c635efSGarrett D'Amore 	dev_t	dev;
13595c635efSGarrett D'Amore 	ino_t	ino;
13695c635efSGarrett D'Amore } bintoman[] = {
13795c635efSGarrett D'Amore 	{ "/sbin",		"/usr/share/man,1m",		0, 0 },
13895c635efSGarrett D'Amore 	{ "/usr/sbin",		"/usr/share/man,1m",		0, 0 },
13995c635efSGarrett D'Amore 	{ "/usr/ucb",		"/usr/share/man,1b",		0, 0 },
14095c635efSGarrett D'Amore 	{ "/usr/bin",		"/usr/share/man,1,1m,1s,1t,1c", 0, 0 },
14195c635efSGarrett D'Amore 	{ "/usr/xpg4/bin",	"/usr/share/man,1",		0, 0 },
14295c635efSGarrett D'Amore 	{ "/usr/xpg6/bin",	"/usr/share/man,1",		0, 0 },
14395c635efSGarrett D'Amore 	{ NULL,			NULL,				0, 0 }
14495c635efSGarrett D'Amore };
14595c635efSGarrett D'Amore 
14695c635efSGarrett D'Amore struct man_node {
14795c635efSGarrett D'Amore 	char		*path;		/* mandir path */
14895c635efSGarrett D'Amore 	char		**secv;		/* submandir suffices */
14995c635efSGarrett D'Amore 	int		defsrch;	/* hint for man -p */
15095c635efSGarrett D'Amore 	int		frompath;	/* hint for man -d */
15195c635efSGarrett D'Amore 	struct man_node *next;
15295c635efSGarrett D'Amore };
15395c635efSGarrett D'Amore 
15495c635efSGarrett D'Amore static int	all = 0;
15595c635efSGarrett D'Amore static int	apropos = 0;
15695c635efSGarrett D'Amore static int	debug = 0;
15795c635efSGarrett D'Amore static int	found = 0;
15895c635efSGarrett D'Amore static int	list = 0;
15995c635efSGarrett D'Amore static int	makewhatis = 0;
16095c635efSGarrett D'Amore static int	printmp = 0;
16195c635efSGarrett D'Amore static int	sargs = 0;
16295c635efSGarrett D'Amore static int	psoutput = 0;
16395c635efSGarrett D'Amore static int	lintout = 0;
16495c635efSGarrett D'Amore static int	whatis = 0;
16595c635efSGarrett D'Amore static int	makewhatishere = 0;
16695c635efSGarrett D'Amore 
16795c635efSGarrett D'Amore static char	*mansec;
16895c635efSGarrett D'Amore static char	*pager = NULL;
16995c635efSGarrett D'Amore 
17095c635efSGarrett D'Amore static char	*addlocale(char *);
17195c635efSGarrett D'Amore static struct man_node *build_manpath(char **, int);
17295c635efSGarrett D'Amore static void	do_makewhatis(struct man_node *);
17395c635efSGarrett D'Amore static char	*check_config(char *);
17495c635efSGarrett D'Amore static int	cmp(const void *, const void *);
17595c635efSGarrett D'Amore static int	dupcheck(struct man_node *, struct dupnode **);
17695c635efSGarrett D'Amore static int	format(char *, char *, char *, char *);
17795c635efSGarrett D'Amore static void	free_dupnode(struct dupnode *);
17895c635efSGarrett D'Amore static void	free_manp(struct man_node *manp);
17995c635efSGarrett D'Amore static void	freev(char **);
18095c635efSGarrett D'Amore static void	fullpaths(struct man_node **);
18195c635efSGarrett D'Amore static void	get_all_sect(struct man_node *);
18295c635efSGarrett D'Amore static int	getdirs(char *, char ***, int);
18395c635efSGarrett D'Amore static void	getpath(struct man_node *, char **);
18495c635efSGarrett D'Amore static void	getsect(struct man_node *, char **);
18595c635efSGarrett D'Amore static void	init_bintoman(void);
18695c635efSGarrett D'Amore static void	lower(char *);
18795c635efSGarrett D'Amore static void	mandir(char **, char *, char *, int);
18895c635efSGarrett D'Amore static int	manual(struct man_node *, char *);
18995c635efSGarrett D'Amore static char	*map_section(char *, char *);
19095c635efSGarrett D'Amore static char	*path_to_manpath(char *);
19195c635efSGarrett D'Amore static void	print_manpath(struct man_node *);
19295c635efSGarrett D'Amore static void	search_whatis(char *, char *);
19395c635efSGarrett D'Amore static int	searchdir(char *, char *, char *);
19495c635efSGarrett D'Amore static void	sortdir(DIR *, char ***);
19595c635efSGarrett D'Amore static char	**split(char *, char);
19695c635efSGarrett D'Amore static void	usage_man(void);
19795c635efSGarrett D'Amore static void	usage_whatapro(void);
19895c635efSGarrett D'Amore static void	usage_catman(void);
19995c635efSGarrett D'Amore static void	usage_makewhatis(void);
20095c635efSGarrett D'Amore static void	whatapro(struct man_node *, char *);
20195c635efSGarrett D'Amore 
20295c635efSGarrett D'Amore static char	language[MAXPATHLEN]; 	/* LC_MESSAGES */
20395c635efSGarrett D'Amore static char	localedir[MAXPATHLEN];	/* locale specific path component */
20495c635efSGarrett D'Amore 
20595c635efSGarrett D'Amore static char	*newsection = NULL;
20695c635efSGarrett D'Amore 
20795c635efSGarrett D'Amore static int	manwidth = 0;
20895c635efSGarrett D'Amore 
20995c635efSGarrett D'Amore extern const char	*__progname;
21095c635efSGarrett D'Amore 
21195c635efSGarrett D'Amore int
main(int argc,char ** argv)21295c635efSGarrett D'Amore main(int argc, char **argv)
21395c635efSGarrett D'Amore {
21495c635efSGarrett D'Amore 	int		c, i;
21595c635efSGarrett D'Amore 	char		**pathv;
21695c635efSGarrett D'Amore 	char		*manpath = NULL;
21795c635efSGarrett D'Amore 	static struct man_node *mandirs = NULL;
21895c635efSGarrett D'Amore 	int		bmp_flags = 0;
21995c635efSGarrett D'Amore 	int		ret = 0;
22095c635efSGarrett D'Amore 	char		*opts;
22195c635efSGarrett D'Amore 	char		*mwstr;
22295c635efSGarrett D'Amore 	int		catman = 0;
22395c635efSGarrett D'Amore 
22495c635efSGarrett D'Amore 	(void) setlocale(LC_ALL, "");
22595c635efSGarrett D'Amore 	(void) strcpy(language, setlocale(LC_MESSAGES, (char *)NULL));
22695c635efSGarrett D'Amore 	if (strcmp("C", language) != 0)
22795c635efSGarrett D'Amore 		(void) strlcpy(localedir, language, MAXPATHLEN);
22895c635efSGarrett D'Amore 
22995c635efSGarrett D'Amore #if !defined(TEXT_DOMAIN)
23095c635efSGarrett D'Amore #define	TEXT_DOMAIN "SYS_TEST"
23195c635efSGarrett D'Amore #endif
23295c635efSGarrett D'Amore 	(void) textdomain(TEXT_DOMAIN);
23395c635efSGarrett D'Amore 
23495c635efSGarrett D'Amore 	if (strcmp(__progname, "apropos") == 0) {
23595c635efSGarrett D'Amore 		apropos++;
23695c635efSGarrett D'Amore 		opts = "M:ds:";
23795c635efSGarrett D'Amore 	} else if (strcmp(__progname, "whatis") == 0) {
23895c635efSGarrett D'Amore 		apropos++;
23995c635efSGarrett D'Amore 		whatis++;
24095c635efSGarrett D'Amore 		opts = "M:ds:";
24195c635efSGarrett D'Amore 	} else if (strcmp(__progname, "catman") == 0) {
24295c635efSGarrett D'Amore 		catman++;
24395c635efSGarrett D'Amore 		makewhatis++;
24495c635efSGarrett D'Amore 		opts = "P:M:w";
24595c635efSGarrett D'Amore 	} else if (strcmp(__progname, "makewhatis") == 0) {
24695c635efSGarrett D'Amore 		makewhatis++;
24795c635efSGarrett D'Amore 		makewhatishere++;
24895c635efSGarrett D'Amore 		manpath = ".";
24995c635efSGarrett D'Amore 		opts = "";
25095c635efSGarrett D'Amore 	} else {
25195c635efSGarrett D'Amore 		opts = "FM:P:T:adfklprs:tw";
25295c635efSGarrett D'Amore 		if (argc > 1 && strcmp(argv[1], "-") == 0) {
25395c635efSGarrett D'Amore 			pager = "cat";
25495c635efSGarrett D'Amore 			optind++;
25595c635efSGarrett D'Amore 		}
25695c635efSGarrett D'Amore 	}
25795c635efSGarrett D'Amore 
25895c635efSGarrett D'Amore 	opterr = 0;
25995c635efSGarrett D'Amore 	while ((c = getopt(argc, argv, opts)) != -1) {
26095c635efSGarrett D'Amore 		switch (c) {
26195c635efSGarrett D'Amore 		case 'M':	/* Respecify path for man pages */
26295c635efSGarrett D'Amore 			manpath = optarg;
26395c635efSGarrett D'Amore 			break;
26495c635efSGarrett D'Amore 		case 'a':
26595c635efSGarrett D'Amore 			all++;
26695c635efSGarrett D'Amore 			break;
26795c635efSGarrett D'Amore 		case 'd':
26895c635efSGarrett D'Amore 			debug++;
26995c635efSGarrett D'Amore 			break;
27095c635efSGarrett D'Amore 		case 'f':
27195c635efSGarrett D'Amore 			whatis++;
27295c635efSGarrett D'Amore 			/*FALLTHROUGH*/
27395c635efSGarrett D'Amore 		case 'k':
27495c635efSGarrett D'Amore 			apropos++;
27595c635efSGarrett D'Amore 			break;
27695c635efSGarrett D'Amore 		case 'l':
27795c635efSGarrett D'Amore 			list++;
27895c635efSGarrett D'Amore 			all++;
27995c635efSGarrett D'Amore 			break;
28095c635efSGarrett D'Amore 		case 'p':
28195c635efSGarrett D'Amore 			printmp++;
28295c635efSGarrett D'Amore 			break;
28395c635efSGarrett D'Amore 		case 's':
28495c635efSGarrett D'Amore 			mansec = optarg;
28595c635efSGarrett D'Amore 			sargs++;
28695c635efSGarrett D'Amore 			break;
28795c635efSGarrett D'Amore 		case 'r':
28895c635efSGarrett D'Amore 			lintout++;
28995c635efSGarrett D'Amore 			break;
29095c635efSGarrett D'Amore 		case 't':
29195c635efSGarrett D'Amore 			psoutput++;
29295c635efSGarrett D'Amore 			break;
29395c635efSGarrett D'Amore 		case 'T':
29495c635efSGarrett D'Amore 		case 'P':
29595c635efSGarrett D'Amore 		case 'F':
29695c635efSGarrett D'Amore 			/* legacy options, compatibility only and ignored */
29795c635efSGarrett D'Amore 			break;
29895c635efSGarrett D'Amore 		case 'w':
29995c635efSGarrett D'Amore 			makewhatis++;
30095c635efSGarrett D'Amore 			break;
30195c635efSGarrett D'Amore 		case '?':
30295c635efSGarrett D'Amore 		default:
30395c635efSGarrett D'Amore 			if (apropos)
30495c635efSGarrett D'Amore 				usage_whatapro();
30595c635efSGarrett D'Amore 			else if (catman)
30695c635efSGarrett D'Amore 				usage_catman();
30795c635efSGarrett D'Amore 			else if (makewhatishere)
30895c635efSGarrett D'Amore 				usage_makewhatis();
30995c635efSGarrett D'Amore 			else
31095c635efSGarrett D'Amore 				usage_man();
31195c635efSGarrett D'Amore 		}
31295c635efSGarrett D'Amore 	}
31395c635efSGarrett D'Amore 	argc -= optind;
31495c635efSGarrett D'Amore 	argv += optind;
31595c635efSGarrett D'Amore 
31695c635efSGarrett D'Amore 	if (argc == 0) {
31795c635efSGarrett D'Amore 		if (apropos) {
31895c635efSGarrett D'Amore 			(void) fprintf(stderr, gettext("%s what?\n"),
31995c635efSGarrett D'Amore 			    __progname);
32095c635efSGarrett D'Amore 			exit(1);
32195c635efSGarrett D'Amore 		} else if (!printmp && !makewhatis) {
32295c635efSGarrett D'Amore 			(void) fprintf(stderr,
32395c635efSGarrett D'Amore 			    gettext("What manual page do you want?\n"));
32495c635efSGarrett D'Amore 			exit(1);
32595c635efSGarrett D'Amore 		}
32695c635efSGarrett D'Amore 	}
32795c635efSGarrett D'Amore 
32895c635efSGarrett D'Amore 	init_bintoman();
32995c635efSGarrett D'Amore 	if (manpath == NULL && (manpath = getenv("MANPATH")) == NULL) {
33095c635efSGarrett D'Amore 		if ((manpath = getenv("PATH")) != NULL)
33195c635efSGarrett D'Amore 			bmp_flags = BMP_ISPATH | BMP_APPEND_DEFMANDIR;
33295c635efSGarrett D'Amore 		else
33395c635efSGarrett D'Amore 			manpath = DEFMANDIR;
33495c635efSGarrett D'Amore 	}
33595c635efSGarrett D'Amore 	pathv = split(manpath, ':');
33695c635efSGarrett D'Amore 	mandirs = build_manpath(pathv, bmp_flags);
33795c635efSGarrett D'Amore 	freev(pathv);
33895c635efSGarrett D'Amore 	fullpaths(&mandirs);
33995c635efSGarrett D'Amore 
34095c635efSGarrett D'Amore 	if (makewhatis) {
34195c635efSGarrett D'Amore 		do_makewhatis(mandirs);
34295c635efSGarrett D'Amore 		exit(0);
34395c635efSGarrett D'Amore 	}
34495c635efSGarrett D'Amore 
34595c635efSGarrett D'Amore 	if (printmp) {
34695c635efSGarrett D'Amore 		print_manpath(mandirs);
34795c635efSGarrett D'Amore 		exit(0);
34895c635efSGarrett D'Amore 	}
34995c635efSGarrett D'Amore 
35095c635efSGarrett D'Amore 	/* Collect environment information */
35195c635efSGarrett D'Amore 	if (isatty(STDOUT_FILENO) && (mwstr = getenv("MANWIDTH")) != NULL &&
35295c635efSGarrett D'Amore 	    *mwstr != '\0') {
35395c635efSGarrett D'Amore 		if (strcasecmp(mwstr, "tty") == 0) {
35495c635efSGarrett D'Amore 			struct winsize	ws;
35595c635efSGarrett D'Amore 
35695c635efSGarrett D'Amore 			if (ioctl(0, TIOCGWINSZ, &ws) != 0)
35795c635efSGarrett D'Amore 				warn("TIOCGWINSZ");
35895c635efSGarrett D'Amore 			else
35995c635efSGarrett D'Amore 				manwidth = ws.ws_col;
36095c635efSGarrett D'Amore 		} else {
36195c635efSGarrett D'Amore 			manwidth = (int)strtol(mwstr, (char **)NULL, 10);
36295c635efSGarrett D'Amore 			if (manwidth < 0)
36395c635efSGarrett D'Amore 				manwidth = 0;
36495c635efSGarrett D'Amore 		}
36595c635efSGarrett D'Amore 	}
36695c635efSGarrett D'Amore 	if (manwidth != 0) {
36795c635efSGarrett D'Amore 		DPRINTF("-- Using non-standard page width: %d\n", manwidth);
36895c635efSGarrett D'Amore 	}
36995c635efSGarrett D'Amore 
37095c635efSGarrett D'Amore 	if (pager == NULL) {
37195c635efSGarrett D'Amore 		if ((pager = getenv("PAGER")) == NULL || *pager == '\0')
37295c635efSGarrett D'Amore 			pager = PAGER;
37395c635efSGarrett D'Amore 	}
37495c635efSGarrett D'Amore 	DPRINTF("-- Using pager: %s\n", pager);
37595c635efSGarrett D'Amore 
37695c635efSGarrett D'Amore 	for (i = 0; i < argc; i++) {
37795c635efSGarrett D'Amore 		char		*cmd;
37895c635efSGarrett D'Amore 		static struct man_node *mp;
37995c635efSGarrett D'Amore 		char		*pv[2];
38095c635efSGarrett D'Amore 
38195c635efSGarrett D'Amore 		/*
38295c635efSGarrett D'Amore 		 * If full path to command specified, customize
38395c635efSGarrett D'Amore 		 * the manpath accordingly.
38495c635efSGarrett D'Amore 		 */
38595c635efSGarrett D'Amore 		if ((cmd = strrchr(argv[i], '/')) != NULL) {
38695c635efSGarrett D'Amore 			*cmd = '\0';
38795c635efSGarrett D'Amore 			if ((pv[0] = strdup(argv[i])) == NULL)
38895c635efSGarrett D'Amore 				err(1, "strdup");
38995c635efSGarrett D'Amore 			pv[1] = NULL;
39095c635efSGarrett D'Amore 			*cmd = '/';
39195c635efSGarrett D'Amore 			mp = build_manpath(pv,
39295c635efSGarrett D'Amore 			    BMP_ISPATH | BMP_FALLBACK_DEFMANDIR);
39395c635efSGarrett D'Amore 		} else {
39495c635efSGarrett D'Amore 			mp = mandirs;
39595c635efSGarrett D'Amore 		}
39695c635efSGarrett D'Amore 
39795c635efSGarrett D'Amore 		if (apropos)
39895c635efSGarrett D'Amore 			whatapro(mp, argv[i]);
39995c635efSGarrett D'Amore 		else
40095c635efSGarrett D'Amore 			ret += manual(mp, argv[i]);
40195c635efSGarrett D'Amore 
40295c635efSGarrett D'Amore 		if (mp != NULL && mp != mandirs) {
40395c635efSGarrett D'Amore 			free(pv[0]);
40495c635efSGarrett D'Amore 			free_manp(mp);
40595c635efSGarrett D'Amore 		}
40695c635efSGarrett D'Amore 	}
40795c635efSGarrett D'Amore 
40895c635efSGarrett D'Amore 	return (ret == 0 ? 0 : 1);
40995c635efSGarrett D'Amore }
41095c635efSGarrett D'Amore 
41195c635efSGarrett D'Amore /*
41295c635efSGarrett D'Amore  * This routine builds the manpage structure from MANPATH or PATH,
41395c635efSGarrett D'Amore  * depending on flags.  See BMP_* definitions above for valid
41495c635efSGarrett D'Amore  * flags.
41595c635efSGarrett D'Amore  */
41695c635efSGarrett D'Amore static struct man_node *
build_manpath(char ** pathv,int flags)41795c635efSGarrett D'Amore build_manpath(char **pathv, int flags)
41895c635efSGarrett D'Amore {
41995c635efSGarrett D'Amore 	struct man_node *manpage = NULL;
42095c635efSGarrett D'Amore 	struct man_node *currp = NULL;
42195c635efSGarrett D'Amore 	struct man_node *lastp = NULL;
42295c635efSGarrett D'Amore 	char		**p;
42395c635efSGarrett D'Amore 	char		**q;
42495c635efSGarrett D'Amore 	char		*mand = NULL;
42595c635efSGarrett D'Amore 	char		*mandir = DEFMANDIR;
42695c635efSGarrett D'Amore 	int		s;
42795c635efSGarrett D'Amore 	struct dupnode	*didup = NULL;
42895c635efSGarrett D'Amore 	struct stat	sb;
42995c635efSGarrett D'Amore 
43095c635efSGarrett D'Amore 	s = sizeof (struct man_node);
43195c635efSGarrett D'Amore 	for (p = pathv; *p != NULL; ) {
43295c635efSGarrett D'Amore 		if (flags & BMP_ISPATH) {
43395c635efSGarrett D'Amore 			if ((mand = path_to_manpath(*p)) == NULL)
43495c635efSGarrett D'Amore 				goto next;
43595c635efSGarrett D'Amore 			free(*p);
43695c635efSGarrett D'Amore 			*p = mand;
43795c635efSGarrett D'Amore 		}
43895c635efSGarrett D'Amore 		q = split(*p, ',');
43995c635efSGarrett D'Amore 		if (stat(q[0], &sb) != 0 || (sb.st_mode & S_IFDIR) == 0) {
44095c635efSGarrett D'Amore 			freev(q);
44195c635efSGarrett D'Amore 			goto next;
44295c635efSGarrett D'Amore 		}
44395c635efSGarrett D'Amore 
44495c635efSGarrett D'Amore 		if (access(q[0], R_OK | X_OK) == 0) {
44595c635efSGarrett D'Amore 			/*
44695c635efSGarrett D'Amore 			 * Some element exists.  Do not append DEFMANDIR as a
44795c635efSGarrett D'Amore 			 * fallback.
44895c635efSGarrett D'Amore 			 */
44995c635efSGarrett D'Amore 			flags &= ~BMP_FALLBACK_DEFMANDIR;
45095c635efSGarrett D'Amore 
45195c635efSGarrett D'Amore 			if ((currp = (struct man_node *)calloc(1, s)) == NULL)
45295c635efSGarrett D'Amore 				err(1, "calloc");
45395c635efSGarrett D'Amore 
45495c635efSGarrett D'Amore 			currp->frompath = (flags & BMP_ISPATH);
45595c635efSGarrett D'Amore 
45695c635efSGarrett D'Amore 			if (manpage == NULL)
45795c635efSGarrett D'Amore 				lastp = manpage = currp;
45895c635efSGarrett D'Amore 
45995c635efSGarrett D'Amore 			getpath(currp, p);
46095c635efSGarrett D'Amore 			getsect(currp, p);
46195c635efSGarrett D'Amore 
46295c635efSGarrett D'Amore 			/*
46395c635efSGarrett D'Amore 			 * If there are no new elements in this path,
46495c635efSGarrett D'Amore 			 * do not add it to the manpage list.
46595c635efSGarrett D'Amore 			 */
46695c635efSGarrett D'Amore 			if (dupcheck(currp, &didup) != 0) {
46795c635efSGarrett D'Amore 				freev(currp->secv);
46895c635efSGarrett D'Amore 				free(currp);
46995c635efSGarrett D'Amore 			} else {
47095c635efSGarrett D'Amore 				currp->next = NULL;
47195c635efSGarrett D'Amore 				if (currp != manpage)
47295c635efSGarrett D'Amore 					lastp->next = currp;
47395c635efSGarrett D'Amore 				lastp = currp;
47495c635efSGarrett D'Amore 			}
47595c635efSGarrett D'Amore 		}
47695c635efSGarrett D'Amore 		freev(q);
47795c635efSGarrett D'Amore next:
47895c635efSGarrett D'Amore 		/*
47995c635efSGarrett D'Amore 		 * Special handling of appending DEFMANDIR. After all pathv
48095c635efSGarrett D'Amore 		 * elements have been processed, append DEFMANDIR if needed.
48195c635efSGarrett D'Amore 		 */
48295c635efSGarrett D'Amore 		if (p == &mandir)
48395c635efSGarrett D'Amore 			break;
48495c635efSGarrett D'Amore 		p++;
48595c635efSGarrett D'Amore 		if (*p != NULL)
48695c635efSGarrett D'Amore 			continue;
48795c635efSGarrett D'Amore 		if (flags & (BMP_APPEND_DEFMANDIR | BMP_FALLBACK_DEFMANDIR)) {
48895c635efSGarrett D'Amore 			p = &mandir;
48995c635efSGarrett D'Amore 			flags &= ~BMP_ISPATH;
49095c635efSGarrett D'Amore 		}
49195c635efSGarrett D'Amore 	}
49295c635efSGarrett D'Amore 
49395c635efSGarrett D'Amore 	free_dupnode(didup);
49495c635efSGarrett D'Amore 
49595c635efSGarrett D'Amore 	return (manpage);
49695c635efSGarrett D'Amore }
49795c635efSGarrett D'Amore 
49895c635efSGarrett D'Amore /*
49995c635efSGarrett D'Amore  * Store the mandir path into the manp structure.
50095c635efSGarrett D'Amore  */
50195c635efSGarrett D'Amore static void
getpath(struct man_node * manp,char ** pv)50295c635efSGarrett D'Amore getpath(struct man_node *manp, char **pv)
50395c635efSGarrett D'Amore {
50495c635efSGarrett D'Amore 	char	*s = *pv;
50595c635efSGarrett D'Amore 	int	i = 0;
50695c635efSGarrett D'Amore 
50795c635efSGarrett D'Amore 	while (*s != '\0' && *s != ',')
50895c635efSGarrett D'Amore 		i++, s++;
50995c635efSGarrett D'Amore 
51095c635efSGarrett D'Amore 	if ((manp->path = (char *)malloc(i + 1)) == NULL)
51195c635efSGarrett D'Amore 		err(1, "malloc");
51295c635efSGarrett D'Amore 	(void) strlcpy(manp->path, *pv, i + 1);
51395c635efSGarrett D'Amore }
51495c635efSGarrett D'Amore 
51595c635efSGarrett D'Amore /*
51695c635efSGarrett D'Amore  * Store the mandir's corresponding sections (submandir
51795c635efSGarrett D'Amore  * directories) into the manp structure.
51895c635efSGarrett D'Amore  */
51995c635efSGarrett D'Amore static void
getsect(struct man_node * manp,char ** pv)52095c635efSGarrett D'Amore getsect(struct man_node *manp, char **pv)
52195c635efSGarrett D'Amore {
52295c635efSGarrett D'Amore 	char	*sections;
52395c635efSGarrett D'Amore 	char	**sectp;
52495c635efSGarrett D'Amore 
52595c635efSGarrett D'Amore 	/* Just store all sections when doing makewhatis or apropos/whatis */
52695c635efSGarrett D'Amore 	if (makewhatis || apropos) {
52795c635efSGarrett D'Amore 		manp->defsrch = 1;
52895c635efSGarrett D'Amore 		DPRINTF("-- Adding %s\n", manp->path);
52995c635efSGarrett D'Amore 		manp->secv = NULL;
53095c635efSGarrett D'Amore 		get_all_sect(manp);
53195c635efSGarrett D'Amore 	} else if (sargs) {
532*d456640dSYuri Pankov 		DPRINTF("-- Adding %s: sections=%s\n", manp->path, mansec);
53395c635efSGarrett D'Amore 		manp->secv = split(mansec, ',');
53495c635efSGarrett D'Amore 		for (sectp = manp->secv; *sectp; sectp++)
53595c635efSGarrett D'Amore 			lower(*sectp);
53695c635efSGarrett D'Amore 	} else if ((sections = strchr(*pv, ',')) != NULL) {
537*d456640dSYuri Pankov 		sections++;
538*d456640dSYuri Pankov 		DPRINTF("-- Adding %s: sections=%s\n", manp->path, sections);
539*d456640dSYuri Pankov 		manp->secv = split(sections, ',');
54095c635efSGarrett D'Amore 		for (sectp = manp->secv; *sectp; sectp++)
54195c635efSGarrett D'Amore 			lower(*sectp);
54295c635efSGarrett D'Amore 		if (*manp->secv == NULL)
54395c635efSGarrett D'Amore 			get_all_sect(manp);
54495c635efSGarrett D'Amore 	} else if ((sections = check_config(*pv)) != NULL) {
54595c635efSGarrett D'Amore 		manp->defsrch = 1;
546*d456640dSYuri Pankov 		DPRINTF("-- Adding %s: sections=%s (from %s)\n", manp->path,
547*d456640dSYuri Pankov 		    sections, CONFIG);
54895c635efSGarrett D'Amore 		manp->secv = split(sections, ',');
54995c635efSGarrett D'Amore 		for (sectp = manp->secv; *sectp; sectp++)
55095c635efSGarrett D'Amore 			lower(*sectp);
55195c635efSGarrett D'Amore 		if (*manp->secv == NULL)
55295c635efSGarrett D'Amore 			get_all_sect(manp);
55395c635efSGarrett D'Amore 	} else {
55495c635efSGarrett D'Amore 		manp->defsrch = 1;
555*d456640dSYuri Pankov 		DPRINTF("-- Adding %s: default search order\n", manp->path);
55695c635efSGarrett D'Amore 		manp->secv = NULL;
55795c635efSGarrett D'Amore 		get_all_sect(manp);
55895c635efSGarrett D'Amore 	}
55995c635efSGarrett D'Amore }
56095c635efSGarrett D'Amore 
56195c635efSGarrett D'Amore /*
56295c635efSGarrett D'Amore  * Get suffices of all sub-mandir directories in a mandir.
56395c635efSGarrett D'Amore  */
56495c635efSGarrett D'Amore static void
get_all_sect(struct man_node * manp)56595c635efSGarrett D'Amore get_all_sect(struct man_node *manp)
56695c635efSGarrett D'Amore {
56795c635efSGarrett D'Amore 	DIR	*dp;
56895c635efSGarrett D'Amore 	char	**dirv;
56995c635efSGarrett D'Amore 	char	**dv;
57095c635efSGarrett D'Amore 	char	**p;
57195c635efSGarrett D'Amore 	char	*prev = NULL;
57295c635efSGarrett D'Amore 	char 	*tmp = NULL;
57395c635efSGarrett D'Amore 	int	maxentries = MAXTOKENS;
57495c635efSGarrett D'Amore 	int	entries = 0;
57595c635efSGarrett D'Amore 
57695c635efSGarrett D'Amore 	if ((dp = opendir(manp->path)) == 0)
57795c635efSGarrett D'Amore 		return;
57895c635efSGarrett D'Amore 
57995c635efSGarrett D'Amore 	sortdir(dp, &dirv);
58095c635efSGarrett D'Amore 
58195c635efSGarrett D'Amore 	(void) closedir(dp);
58295c635efSGarrett D'Amore 
58395c635efSGarrett D'Amore 	if (manp->secv == NULL) {
58495c635efSGarrett D'Amore 		if ((manp->secv = malloc(maxentries * sizeof (char *))) == NULL)
58595c635efSGarrett D'Amore 			err(1, "malloc");
58695c635efSGarrett D'Amore 	}
58795c635efSGarrett D'Amore 
58895c635efSGarrett D'Amore 	for (dv = dirv, p = manp->secv; *dv; dv++) {
58995c635efSGarrett D'Amore 		if (strcmp(*dv, CONFIG) == 0) {
59095c635efSGarrett D'Amore 			free(*dv);
59195c635efSGarrett D'Amore 			continue;
59295c635efSGarrett D'Amore 		}
59395c635efSGarrett D'Amore 
59495c635efSGarrett D'Amore 		free(tmp);
59595c635efSGarrett D'Amore 		if ((tmp = strdup(*dv + 3)) == NULL)
59695c635efSGarrett D'Amore 			err(1, "strdup");
59795c635efSGarrett D'Amore 
59895c635efSGarrett D'Amore 		if (prev != NULL && strcmp(prev, tmp) == 0) {
59995c635efSGarrett D'Amore 			free(*dv);
60095c635efSGarrett D'Amore 			continue;
60195c635efSGarrett D'Amore 		}
60295c635efSGarrett D'Amore 
60395c635efSGarrett D'Amore 		free(prev);
60495c635efSGarrett D'Amore 		if ((prev = strdup(*dv + 3)) == NULL)
60595c635efSGarrett D'Amore 			err(1, "strdup");
60695c635efSGarrett D'Amore 
60795c635efSGarrett D'Amore 		if ((*p = strdup(*dv + 3)) == NULL)
60895c635efSGarrett D'Amore 			err(1, "strdup");
60995c635efSGarrett D'Amore 
61095c635efSGarrett D'Amore 		p++; entries++;
61195c635efSGarrett D'Amore 
61295c635efSGarrett D'Amore 		if (entries == maxentries) {
61395c635efSGarrett D'Amore 			maxentries += MAXTOKENS;
61495c635efSGarrett D'Amore 			if ((manp->secv = realloc(manp->secv,
61595c635efSGarrett D'Amore 			    sizeof (char *) * maxentries)) == NULL)
61695c635efSGarrett D'Amore 				err(1, "realloc");
61795c635efSGarrett D'Amore 			p = manp->secv + entries;
61895c635efSGarrett D'Amore 		}
61995c635efSGarrett D'Amore 		free(*dv);
62095c635efSGarrett D'Amore 	}
62195c635efSGarrett D'Amore 	free(tmp);
62295c635efSGarrett D'Amore 	free(prev);
62395c635efSGarrett D'Amore 	*p = NULL;
62495c635efSGarrett D'Amore 	free(dirv);
62595c635efSGarrett D'Amore }
62695c635efSGarrett D'Amore 
62795c635efSGarrett D'Amore /*
62895c635efSGarrett D'Amore  * Build whatis databases.
62995c635efSGarrett D'Amore  */
63095c635efSGarrett D'Amore static void
do_makewhatis(struct man_node * manp)63195c635efSGarrett D'Amore do_makewhatis(struct man_node *manp)
63295c635efSGarrett D'Amore {
63395c635efSGarrett D'Amore 	struct man_node *p;
63495c635efSGarrett D'Amore 	char		*ldir;
63595c635efSGarrett D'Amore 
63695c635efSGarrett D'Amore 	for (p = manp; p != NULL; p = p->next) {
63795c635efSGarrett D'Amore 		ldir = addlocale(p->path);
63895c635efSGarrett D'Amore 		if (*localedir != '\0' && getdirs(ldir, NULL, 0) > 0)
63995c635efSGarrett D'Amore 			mwpath(ldir);
64095c635efSGarrett D'Amore 		free(ldir);
64195c635efSGarrett D'Amore 		mwpath(p->path);
64295c635efSGarrett D'Amore 	}
64395c635efSGarrett D'Amore }
64495c635efSGarrett D'Amore 
64595c635efSGarrett D'Amore /*
64695c635efSGarrett D'Amore  * Count mandirs under the given manpath
64795c635efSGarrett D'Amore  */
64895c635efSGarrett D'Amore static int
getdirs(char * path,char *** dirv,int flag)64995c635efSGarrett D'Amore getdirs(char *path, char ***dirv, int flag)
65095c635efSGarrett D'Amore {
65195c635efSGarrett D'Amore 	DIR		*dp;
65295c635efSGarrett D'Amore 	struct dirent	*d;
65395c635efSGarrett D'Amore 	int		n = 0;
65495c635efSGarrett D'Amore 	int		maxentries = MAXDIRS;
65595c635efSGarrett D'Amore 	char		**dv = NULL;
65695c635efSGarrett D'Amore 
65795c635efSGarrett D'Amore 	if ((dp = opendir(path)) == NULL)
65895c635efSGarrett D'Amore 		return (0);
65995c635efSGarrett D'Amore 
66095c635efSGarrett D'Amore 	if (flag) {
66195c635efSGarrett D'Amore 		if ((*dirv = malloc(sizeof (char *) *
66295c635efSGarrett D'Amore 		    maxentries)) == NULL)
66395c635efSGarrett D'Amore 			err(1, "malloc");
66495c635efSGarrett D'Amore 		dv = *dirv;
66595c635efSGarrett D'Amore 	}
66695c635efSGarrett D'Amore 	while ((d = readdir(dp))) {
66795c635efSGarrett D'Amore 		if (strncmp(d->d_name, "man", 3) != 0)
66895c635efSGarrett D'Amore 			continue;
66995c635efSGarrett D'Amore 		n++;
67095c635efSGarrett D'Amore 
67195c635efSGarrett D'Amore 		if (flag) {
67295c635efSGarrett D'Amore 			if ((*dv = strdup(d->d_name + 3)) == NULL)
67395c635efSGarrett D'Amore 				err(1, "strdup");
67495c635efSGarrett D'Amore 			dv++;
67595c635efSGarrett D'Amore 			if ((dv - *dirv) == maxentries) {
67695c635efSGarrett D'Amore 				int	entries = maxentries;
67795c635efSGarrett D'Amore 
67895c635efSGarrett D'Amore 				maxentries += MAXTOKENS;
67995c635efSGarrett D'Amore 				if ((*dirv = realloc(*dirv,
68095c635efSGarrett D'Amore 				    sizeof (char *) * maxentries)) == NULL)
68195c635efSGarrett D'Amore 					err(1, "realloc");
68295c635efSGarrett D'Amore 				dv = *dirv + entries;
68395c635efSGarrett D'Amore 			}
68495c635efSGarrett D'Amore 		}
68595c635efSGarrett D'Amore 	}
68695c635efSGarrett D'Amore 
68795c635efSGarrett D'Amore 	(void) closedir(dp);
68895c635efSGarrett D'Amore 	return (n);
68995c635efSGarrett D'Amore }
69095c635efSGarrett D'Amore 
69195c635efSGarrett D'Amore 
69295c635efSGarrett D'Amore /*
69395c635efSGarrett D'Amore  * Find matching whatis or apropos entries.
69495c635efSGarrett D'Amore  */
69595c635efSGarrett D'Amore static void
whatapro(struct man_node * manp,char * word)69695c635efSGarrett D'Amore whatapro(struct man_node *manp, char *word)
69795c635efSGarrett D'Amore {
69895c635efSGarrett D'Amore 	char		whatpath[MAXPATHLEN];
69995c635efSGarrett D'Amore 	struct man_node *b;
70095c635efSGarrett D'Amore 	char		*ldir;
70195c635efSGarrett D'Amore 
70295c635efSGarrett D'Amore 	for (b = manp; b != NULL; b = b->next) {
70395c635efSGarrett D'Amore 		if (*localedir != '\0') {
70495c635efSGarrett D'Amore 			ldir = addlocale(b->path);
70595c635efSGarrett D'Amore 			if (getdirs(ldir, NULL, 0) != 0) {
70695c635efSGarrett D'Amore 				(void) snprintf(whatpath, sizeof (whatpath),
70795c635efSGarrett D'Amore 				    "%s/%s", ldir, WHATIS);
70895c635efSGarrett D'Amore 				search_whatis(whatpath, word);
70995c635efSGarrett D'Amore 			}
71095c635efSGarrett D'Amore 			free(ldir);
71195c635efSGarrett D'Amore 		}
71295c635efSGarrett D'Amore 		(void) snprintf(whatpath, sizeof (whatpath), "%s/%s", b->path,
71395c635efSGarrett D'Amore 		    WHATIS);
71495c635efSGarrett D'Amore 		search_whatis(whatpath, word);
71595c635efSGarrett D'Amore 	}
71695c635efSGarrett D'Amore }
71795c635efSGarrett D'Amore 
71895c635efSGarrett D'Amore static void
search_whatis(char * whatpath,char * word)71995c635efSGarrett D'Amore search_whatis(char *whatpath, char *word)
72095c635efSGarrett D'Amore {
72195c635efSGarrett D'Amore 	FILE		*fp;
72295c635efSGarrett D'Amore 	char		*line = NULL;
72395c635efSGarrett D'Amore 	size_t		linecap = 0;
72495c635efSGarrett D'Amore 	char		*pkwd;
72595c635efSGarrett D'Amore 	regex_t		preg;
72695c635efSGarrett D'Amore 	char		**ss = NULL;
72795c635efSGarrett D'Amore 	char		s[MAXNAMELEN];
72895c635efSGarrett D'Amore 	int		i;
72995c635efSGarrett D'Amore 
73095c635efSGarrett D'Amore 	if ((fp = fopen(whatpath, "r")) == NULL) {
73195c635efSGarrett D'Amore 		perror(whatpath);
73295c635efSGarrett D'Amore 		return;
73395c635efSGarrett D'Amore 	}
73495c635efSGarrett D'Amore 
73595c635efSGarrett D'Amore 	DPRINTF("-- Found %s: %s\n", WHATIS, whatpath);
73695c635efSGarrett D'Amore 
73795c635efSGarrett D'Amore 	/* Build keyword regex */
73895c635efSGarrett D'Amore 	if (asprintf(&pkwd, "%s%s%s", (whatis) ? "\\<" : "",
73995c635efSGarrett D'Amore 	    word, (whatis) ? "\\>" : "") == -1)
74095c635efSGarrett D'Amore 		err(1, "asprintf");
74195c635efSGarrett D'Amore 
74295c635efSGarrett D'Amore 	if (regcomp(&preg, pkwd, REG_BASIC | REG_ICASE | REG_NOSUB) != 0)
74395c635efSGarrett D'Amore 		err(1, "regcomp");
74495c635efSGarrett D'Amore 
74595c635efSGarrett D'Amore 	if (sargs)
74695c635efSGarrett D'Amore 		ss = split(mansec, ',');
74795c635efSGarrett D'Amore 
74895c635efSGarrett D'Amore 	while (getline(&line, &linecap, fp) > 0) {
74995c635efSGarrett D'Amore 		if (regexec(&preg, line, 0, NULL, 0) == 0) {
75095c635efSGarrett D'Amore 			if (sargs) {
75195c635efSGarrett D'Amore 				/* Section-restricted search */
75295c635efSGarrett D'Amore 				for (i = 0; ss[i] != NULL; i++) {
75395c635efSGarrett D'Amore 					(void) snprintf(s, sizeof (s), "(%s)",
75495c635efSGarrett D'Amore 					    ss[i]);
75595c635efSGarrett D'Amore 					if (strstr(line, s) != NULL) {
75695c635efSGarrett D'Amore 						(void) printf("%s", line);
75795c635efSGarrett D'Amore 						break;
75895c635efSGarrett D'Amore 					}
75995c635efSGarrett D'Amore 				}
76095c635efSGarrett D'Amore 			} else {
76195c635efSGarrett D'Amore 				(void) printf("%s", line);
76295c635efSGarrett D'Amore 			}
76395c635efSGarrett D'Amore 		}
76495c635efSGarrett D'Amore 	}
76595c635efSGarrett D'Amore 
76695c635efSGarrett D'Amore 	if (ss != NULL)
76795c635efSGarrett D'Amore 		freev(ss);
76895c635efSGarrett D'Amore 	free(pkwd);
76995c635efSGarrett D'Amore 	(void) fclose(fp);
77095c635efSGarrett D'Amore }
77195c635efSGarrett D'Amore 
77295c635efSGarrett D'Amore 
77395c635efSGarrett D'Amore /*
77495c635efSGarrett D'Amore  * Split a string by specified separator.
77595c635efSGarrett D'Amore  */
77695c635efSGarrett D'Amore static char **
split(char * s1,char sep)77795c635efSGarrett D'Amore split(char *s1, char sep)
77895c635efSGarrett D'Amore {
77995c635efSGarrett D'Amore 	char	**tokv, **vp;
78095c635efSGarrett D'Amore 	char	*mp = s1, *tp;
78195c635efSGarrett D'Amore 	int	maxentries = MAXTOKENS;
78295c635efSGarrett D'Amore 	int	entries = 0;
78395c635efSGarrett D'Amore 
78495c635efSGarrett D'Amore 	if ((tokv = vp = malloc(maxentries * sizeof (char *))) == NULL)
78595c635efSGarrett D'Amore 		err(1, "malloc");
78695c635efSGarrett D'Amore 
78795c635efSGarrett D'Amore 	for (; mp && *mp; mp = tp) {
78895c635efSGarrett D'Amore 		tp = strchr(mp, sep);
78995c635efSGarrett D'Amore 		if (mp == tp) {
79095c635efSGarrett D'Amore 			tp++;
79195c635efSGarrett D'Amore 			continue;
79295c635efSGarrett D'Amore 		}
79395c635efSGarrett D'Amore 		if (tp) {
79495c635efSGarrett D'Amore 			size_t	len;
79595c635efSGarrett D'Amore 
79695c635efSGarrett D'Amore 			len = tp - mp;
79795c635efSGarrett D'Amore 			if ((*vp = (char *)malloc(sizeof (char) *
79895c635efSGarrett D'Amore 			    len + 1)) == NULL)
79995c635efSGarrett D'Amore 				err(1, "malloc");
80095c635efSGarrett D'Amore 			(void) strncpy(*vp, mp, len);
80195c635efSGarrett D'Amore 			*(*vp + len) = '\0';
80295c635efSGarrett D'Amore 			tp++;
80395c635efSGarrett D'Amore 			vp++;
80495c635efSGarrett D'Amore 		} else {
80595c635efSGarrett D'Amore 			if ((*vp = strdup(mp)) == NULL)
80695c635efSGarrett D'Amore 				err(1, "strdup");
80795c635efSGarrett D'Amore 			vp++;
80895c635efSGarrett D'Amore 		}
80995c635efSGarrett D'Amore 		entries++;
81095c635efSGarrett D'Amore 		if (entries == maxentries) {
81195c635efSGarrett D'Amore 			maxentries += MAXTOKENS;
81295c635efSGarrett D'Amore 			if ((tokv = realloc(tokv,
81395c635efSGarrett D'Amore 			    maxentries * sizeof (char *))) == NULL)
81495c635efSGarrett D'Amore 				err(1, "realloc");
81595c635efSGarrett D'Amore 			vp = tokv + entries;
81695c635efSGarrett D'Amore 		}
81795c635efSGarrett D'Amore 	}
81895c635efSGarrett D'Amore 	*vp = 0;
81995c635efSGarrett D'Amore 
82095c635efSGarrett D'Amore 	return (tokv);
82195c635efSGarrett D'Amore }
82295c635efSGarrett D'Amore 
82395c635efSGarrett D'Amore /*
82495c635efSGarrett D'Amore  * Free a vector allocated by split()
82595c635efSGarrett D'Amore  */
82695c635efSGarrett D'Amore static void
freev(char ** v)82795c635efSGarrett D'Amore freev(char **v)
82895c635efSGarrett D'Amore {
82995c635efSGarrett D'Amore 	int i;
83095c635efSGarrett D'Amore 	if (v != NULL) {
83195c635efSGarrett D'Amore 		for (i = 0; v[i] != NULL; i++) {
83295c635efSGarrett D'Amore 			free(v[i]);
83395c635efSGarrett D'Amore 		}
83495c635efSGarrett D'Amore 		free(v);
83595c635efSGarrett D'Amore 	}
83695c635efSGarrett D'Amore }
83795c635efSGarrett D'Amore 
83895c635efSGarrett D'Amore /*
83995c635efSGarrett D'Amore  * Convert paths to full paths if necessary
84095c635efSGarrett D'Amore  */
84195c635efSGarrett D'Amore static void
fullpaths(struct man_node ** manp_head)84295c635efSGarrett D'Amore fullpaths(struct man_node **manp_head)
84395c635efSGarrett D'Amore {
84495c635efSGarrett D'Amore 	char		*cwd = NULL;
84595c635efSGarrett D'Amore 	char		*p;
84695c635efSGarrett D'Amore 	int		cwd_gotten = 0;
84795c635efSGarrett D'Amore 	struct man_node *manp = *manp_head;
84895c635efSGarrett D'Amore 	struct man_node *b;
84995c635efSGarrett D'Amore 	struct man_node *prev = NULL;
85095c635efSGarrett D'Amore 
85195c635efSGarrett D'Amore 	for (b = manp; b != NULL; b = b->next) {
85295c635efSGarrett D'Amore 		if (*(b->path) == '/') {
85395c635efSGarrett D'Amore 			prev = b;
85495c635efSGarrett D'Amore 			continue;
85595c635efSGarrett D'Amore 		}
85695c635efSGarrett D'Amore 
85795c635efSGarrett D'Amore 		if (!cwd_gotten) {
85895c635efSGarrett D'Amore 			cwd = getcwd(NULL, MAXPATHLEN);
85995c635efSGarrett D'Amore 			cwd_gotten = 1;
86095c635efSGarrett D'Amore 		}
86195c635efSGarrett D'Amore 
86295c635efSGarrett D'Amore 		if (cwd) {
86395c635efSGarrett D'Amore 			/* Relative manpath with cwd: make absolute */
86495c635efSGarrett D'Amore 			if (asprintf(&p, "%s/%s", cwd, b->path) == -1)
86595c635efSGarrett D'Amore 				err(1, "asprintf");
86695c635efSGarrett D'Amore 			free(b->path);
86795c635efSGarrett D'Amore 			b->path = p;
86895c635efSGarrett D'Amore 		} else {
86995c635efSGarrett D'Amore 			/* Relative manpath but no cwd: omit path entry */
87095c635efSGarrett D'Amore 			if (prev)
87195c635efSGarrett D'Amore 				prev->next = b->next;
87295c635efSGarrett D'Amore 			else
87395c635efSGarrett D'Amore 				*manp_head = b->next;
87495c635efSGarrett D'Amore 
87595c635efSGarrett D'Amore 			free_manp(b);
87695c635efSGarrett D'Amore 		}
87795c635efSGarrett D'Amore 	}
87895c635efSGarrett D'Amore 	free(cwd);
87995c635efSGarrett D'Amore }
88095c635efSGarrett D'Amore 
88195c635efSGarrett D'Amore /*
88295c635efSGarrett D'Amore  * Free a man_node structure and its contents
88395c635efSGarrett D'Amore  */
88495c635efSGarrett D'Amore static void
free_manp(struct man_node * manp)88595c635efSGarrett D'Amore free_manp(struct man_node *manp)
88695c635efSGarrett D'Amore {
88795c635efSGarrett D'Amore 	char	**p;
88895c635efSGarrett D'Amore 
88995c635efSGarrett D'Amore 	free(manp->path);
89095c635efSGarrett D'Amore 	p = manp->secv;
89195c635efSGarrett D'Amore 	while ((p != NULL) && (*p != NULL)) {
89295c635efSGarrett D'Amore 		free(*p);
89395c635efSGarrett D'Amore 		p++;
89495c635efSGarrett D'Amore 	}
89595c635efSGarrett D'Amore 	free(manp->secv);
89695c635efSGarrett D'Amore 	free(manp);
89795c635efSGarrett D'Amore }
89895c635efSGarrett D'Amore 
89995c635efSGarrett D'Amore 
90095c635efSGarrett D'Amore /*
90195c635efSGarrett D'Amore  * Map (in place) to lower case.
90295c635efSGarrett D'Amore  */
90395c635efSGarrett D'Amore static void
lower(char * s)90495c635efSGarrett D'Amore lower(char *s)
90595c635efSGarrett D'Amore {
90695c635efSGarrett D'Amore 
90795c635efSGarrett D'Amore 	if (s == 0)
90895c635efSGarrett D'Amore 		return;
90995c635efSGarrett D'Amore 	while (*s) {
91095c635efSGarrett D'Amore 		if (isupper(*s))
91195c635efSGarrett D'Amore 			*s = tolower(*s);
91295c635efSGarrett D'Amore 		s++;
91395c635efSGarrett D'Amore 	}
91495c635efSGarrett D'Amore }
91595c635efSGarrett D'Amore 
91695c635efSGarrett D'Amore 
91795c635efSGarrett D'Amore /*
91895c635efSGarrett D'Amore  * Compare function for qsort().
91995c635efSGarrett D'Amore  * Sort first by section, then by prefix.
92095c635efSGarrett D'Amore  */
92195c635efSGarrett D'Amore static int
cmp(const void * arg1,const void * arg2)92295c635efSGarrett D'Amore cmp(const void *arg1, const void *arg2)
92395c635efSGarrett D'Amore {
92495c635efSGarrett D'Amore 	int	n;
92595c635efSGarrett D'Amore 	char	**p1 = (char **)arg1;
92695c635efSGarrett D'Amore 	char	**p2 = (char **)arg2;
92795c635efSGarrett D'Amore 
92895c635efSGarrett D'Amore 	/* By section */
92995c635efSGarrett D'Amore 	if ((n = strcmp(*p1 + 3, *p2 + 3)) != 0)
93095c635efSGarrett D'Amore 		return (n);
93195c635efSGarrett D'Amore 
93295c635efSGarrett D'Amore 	/* By prefix reversed */
93395c635efSGarrett D'Amore 	return (strncmp(*p2, *p1, 3));
93495c635efSGarrett D'Amore }
93595c635efSGarrett D'Amore 
93695c635efSGarrett D'Amore 
93795c635efSGarrett D'Amore /*
93895c635efSGarrett D'Amore  * Find a manpage.
93995c635efSGarrett D'Amore  */
94095c635efSGarrett D'Amore static int
manual(struct man_node * manp,char * name)94195c635efSGarrett D'Amore manual(struct man_node *manp, char *name)
94295c635efSGarrett D'Amore {
94395c635efSGarrett D'Amore 	struct man_node *p;
94495c635efSGarrett D'Amore 	struct man_node *local;
94595c635efSGarrett D'Amore 	int		ndirs = 0;
94695c635efSGarrett D'Amore 	char		*ldir;
94795c635efSGarrett D'Amore 	char		*ldirs[2];
94895c635efSGarrett D'Amore 	char		*fullname = name;
94995c635efSGarrett D'Amore 	char		*slash;
95095c635efSGarrett D'Amore 
95195c635efSGarrett D'Amore 	if ((slash = strrchr(name, '/')) != NULL)
95295c635efSGarrett D'Amore 		name = slash + 1;
95395c635efSGarrett D'Amore 
95495c635efSGarrett D'Amore 	/* For each path in MANPATH */
95595c635efSGarrett D'Amore 	found = 0;
95695c635efSGarrett D'Amore 
95795c635efSGarrett D'Amore 	for (p = manp; p != NULL; p = p->next) {
95895c635efSGarrett D'Amore 		DPRINTF("-- Searching mandir: %s\n", p->path);
95995c635efSGarrett D'Amore 
96095c635efSGarrett D'Amore 		if (*localedir != '\0') {
96195c635efSGarrett D'Amore 			ldir = addlocale(p->path);
96295c635efSGarrett D'Amore 			ndirs = getdirs(ldir, NULL, 0);
96395c635efSGarrett D'Amore 			if (ndirs != 0) {
96495c635efSGarrett D'Amore 				ldirs[0] = ldir;
96595c635efSGarrett D'Amore 				ldirs[1] = NULL;
96695c635efSGarrett D'Amore 				local = build_manpath(ldirs, 0);
96795c635efSGarrett D'Amore 				DPRINTF("-- Locale specific subdir: %s\n",
96895c635efSGarrett D'Amore 				    ldir);
96995c635efSGarrett D'Amore 				mandir(local->secv, ldir, name, 1);
97095c635efSGarrett D'Amore 				free_manp(local);
97195c635efSGarrett D'Amore 			}
97295c635efSGarrett D'Amore 			free(ldir);
97395c635efSGarrett D'Amore 		}
97495c635efSGarrett D'Amore 
97595c635efSGarrett D'Amore 		/*
97695c635efSGarrett D'Amore 		 * Locale mandir not valid, man page in locale
97795c635efSGarrett D'Amore 		 * mandir not found, or -a option present
97895c635efSGarrett D'Amore 		 */
97995c635efSGarrett D'Amore 		if (ndirs == 0 || !found || all)
98095c635efSGarrett D'Amore 			mandir(p->secv, p->path, name, 0);
98195c635efSGarrett D'Amore 
98295c635efSGarrett D'Amore 		if (found && !all)
98395c635efSGarrett D'Amore 			break;
98495c635efSGarrett D'Amore 	}
98595c635efSGarrett D'Amore 
98695c635efSGarrett D'Amore 	if (!found) {
98795c635efSGarrett D'Amore 		if (sargs) {
98895c635efSGarrett D'Amore 			(void) fprintf(stderr, gettext(
98995c635efSGarrett D'Amore 			    "No manual entry for %s in section(s) %s\n"),
99095c635efSGarrett D'Amore 			    fullname, mansec);
99195c635efSGarrett D'Amore 		} else {
99295c635efSGarrett D'Amore 			(void) fprintf(stderr,
99395c635efSGarrett D'Amore 			    gettext("No manual entry for %s\n"), fullname);
99495c635efSGarrett D'Amore 		}
99595c635efSGarrett D'Amore 
99695c635efSGarrett D'Amore 	}
99795c635efSGarrett D'Amore 
99895c635efSGarrett D'Amore 	return (!found);
99995c635efSGarrett D'Amore }
100095c635efSGarrett D'Amore 
100195c635efSGarrett D'Amore 
100295c635efSGarrett D'Amore /*
100395c635efSGarrett D'Amore  * For a specified manual directory, read, store and sort section subdirs.
100495c635efSGarrett D'Amore  * For each section specified, find and search matching subdirs.
100595c635efSGarrett D'Amore  */
100695c635efSGarrett D'Amore static void
mandir(char ** secv,char * path,char * name,int lspec)100795c635efSGarrett D'Amore mandir(char **secv, char *path, char *name, int lspec)
100895c635efSGarrett D'Amore {
100995c635efSGarrett D'Amore 	DIR	*dp;
101095c635efSGarrett D'Amore 	char	**dirv;
101195c635efSGarrett D'Amore 	char	**dv, **pdv;
101295c635efSGarrett D'Amore 	int	len, dslen;
101395c635efSGarrett D'Amore 
101495c635efSGarrett D'Amore 	if ((dp = opendir(path)) == NULL)
101595c635efSGarrett D'Amore 		return;
101695c635efSGarrett D'Amore 
101795c635efSGarrett D'Amore 	if (lspec)
101895c635efSGarrett D'Amore 		DPRINTF("-- Searching mandir: %s\n", path);
101995c635efSGarrett D'Amore 
102095c635efSGarrett D'Amore 	sortdir(dp, &dirv);
102195c635efSGarrett D'Amore 
102295c635efSGarrett D'Amore 	/* Search in the order specified by MANSECTS */
102395c635efSGarrett D'Amore 	for (; *secv; secv++) {
102495c635efSGarrett D'Amore 		len = strlen(*secv);
102595c635efSGarrett D'Amore 		for (dv = dirv; *dv; dv++) {
102695c635efSGarrett D'Amore 			dslen = strlen(*dv + 3);
102795c635efSGarrett D'Amore 			if (dslen > len)
102895c635efSGarrett D'Amore 				len = dslen;
102995c635efSGarrett D'Amore 			if (**secv == '\\') {
103095c635efSGarrett D'Amore 				if (strcmp(*secv + 1, *dv + 3) != 0)
103195c635efSGarrett D'Amore 					continue;
103295c635efSGarrett D'Amore 			} else if (strncasecmp(*secv, *dv + 3, len) != 0) {
103395c635efSGarrett D'Amore 				if (!all &&
103495c635efSGarrett D'Amore 				    (newsection = map_section(*secv, path))
103595c635efSGarrett D'Amore 				    == NULL) {
103695c635efSGarrett D'Amore 					continue;
103795c635efSGarrett D'Amore 				}
103895c635efSGarrett D'Amore 				if (newsection == NULL)
103995c635efSGarrett D'Amore 					newsection = "";
104095c635efSGarrett D'Amore 				if (strncmp(newsection, *dv + 3, len) != 0) {
104195c635efSGarrett D'Amore 					continue;
104295c635efSGarrett D'Amore 				}
104395c635efSGarrett D'Amore 			}
104495c635efSGarrett D'Amore 
104595c635efSGarrett D'Amore 			if (searchdir(path, *dv, name) == 0)
104695c635efSGarrett D'Amore 				continue;
104795c635efSGarrett D'Amore 
104895c635efSGarrett D'Amore 			if (!all) {
104995c635efSGarrett D'Amore 				pdv = dirv;
105095c635efSGarrett D'Amore 				while (*pdv) {
105195c635efSGarrett D'Amore 					free(*pdv);
105295c635efSGarrett D'Amore 					pdv++;
105395c635efSGarrett D'Amore 				}
105495c635efSGarrett D'Amore 				(void) closedir(dp);
105595c635efSGarrett D'Amore 				free(dirv);
105695c635efSGarrett D'Amore 				return;
105795c635efSGarrett D'Amore 			}
105895c635efSGarrett D'Amore 
105995c635efSGarrett D'Amore 			if (all && **dv == 'm' && *(dv + 1) &&
106095c635efSGarrett D'Amore 			    strcmp(*(dv + 1) + 3, *dv + 3) == 0)
106195c635efSGarrett D'Amore 					dv++;
106295c635efSGarrett D'Amore 		}
106395c635efSGarrett D'Amore 	}
106495c635efSGarrett D'Amore 	pdv = dirv;
106595c635efSGarrett D'Amore 	while (*pdv != NULL) {
106695c635efSGarrett D'Amore 		free(*pdv);
106795c635efSGarrett D'Amore 		pdv++;
106895c635efSGarrett D'Amore 	}
106995c635efSGarrett D'Amore 	free(dirv);
107095c635efSGarrett D'Amore 	(void) closedir(dp);
107195c635efSGarrett D'Amore }
107295c635efSGarrett D'Amore 
107395c635efSGarrett D'Amore /*
107495c635efSGarrett D'Amore  * Sort directories.
107595c635efSGarrett D'Amore  */
107695c635efSGarrett D'Amore static void
sortdir(DIR * dp,char *** dirv)107795c635efSGarrett D'Amore sortdir(DIR *dp, char ***dirv)
107895c635efSGarrett D'Amore {
107995c635efSGarrett D'Amore 	struct dirent	*d;
108095c635efSGarrett D'Amore 	char		**dv;
108195c635efSGarrett D'Amore 	int		maxentries = MAXDIRS;
108295c635efSGarrett D'Amore 	int		entries = 0;
108395c635efSGarrett D'Amore 
108495c635efSGarrett D'Amore 	if ((dv = *dirv = malloc(sizeof (char *) *
108595c635efSGarrett D'Amore 	    maxentries)) == NULL)
108695c635efSGarrett D'Amore 		err(1, "malloc");
108795c635efSGarrett D'Amore 	dv = *dirv;
108895c635efSGarrett D'Amore 
108995c635efSGarrett D'Amore 	while ((d = readdir(dp))) {
109095c635efSGarrett D'Amore 		if (strcmp(d->d_name, ".") == 0 ||
109195c635efSGarrett D'Amore 		    strcmp(d->d_name, "..") == 0)
109295c635efSGarrett D'Amore 			continue;
109395c635efSGarrett D'Amore 
109495c635efSGarrett D'Amore 		if (strncmp(d->d_name, "man", 3) == 0 ||
109595c635efSGarrett D'Amore 		    strncmp(d->d_name, "cat", 3) == 0) {
109695c635efSGarrett D'Amore 			if ((*dv = strdup(d->d_name)) == NULL)
109795c635efSGarrett D'Amore 				err(1, "strdup");
109895c635efSGarrett D'Amore 			dv++;
109995c635efSGarrett D'Amore 			entries++;
110095c635efSGarrett D'Amore 			if (entries == maxentries) {
110195c635efSGarrett D'Amore 				maxentries += MAXDIRS;
110295c635efSGarrett D'Amore 				if ((*dirv = realloc(*dirv,
110395c635efSGarrett D'Amore 				    sizeof (char *) * maxentries)) == NULL)
110495c635efSGarrett D'Amore 					err(1, "realloc");
110595c635efSGarrett D'Amore 				dv = *dirv + entries;
110695c635efSGarrett D'Amore 			}
110795c635efSGarrett D'Amore 		}
110895c635efSGarrett D'Amore 	}
110995c635efSGarrett D'Amore 	*dv = 0;
111095c635efSGarrett D'Amore 
111195c635efSGarrett D'Amore 	qsort((void *)*dirv, dv - *dirv, sizeof (char *), cmp);
111295c635efSGarrett D'Amore 
111395c635efSGarrett D'Amore }
111495c635efSGarrett D'Amore 
111595c635efSGarrett D'Amore 
111695c635efSGarrett D'Amore /*
111795c635efSGarrett D'Amore  * Search a section subdir for a given manpage.
111895c635efSGarrett D'Amore  */
111995c635efSGarrett D'Amore static int
searchdir(char * path,char * dir,char * name)112095c635efSGarrett D'Amore searchdir(char *path, char *dir, char *name)
112195c635efSGarrett D'Amore {
112295c635efSGarrett D'Amore 	DIR		*sdp;
112395c635efSGarrett D'Amore 	struct dirent	*sd;
112495c635efSGarrett D'Amore 	char		sectpath[MAXPATHLEN];
112595c635efSGarrett D'Amore 	char		file[MAXNAMLEN];
112695c635efSGarrett D'Amore 	char		dname[MAXPATHLEN];
112795c635efSGarrett D'Amore 	char		*last;
112895c635efSGarrett D'Amore 	int		nlen;
112995c635efSGarrett D'Amore 
113095c635efSGarrett D'Amore 	(void) snprintf(sectpath, sizeof (sectpath), "%s/%s", path, dir);
113195c635efSGarrett D'Amore 	(void) snprintf(file, sizeof (file), "%s.", name);
113295c635efSGarrett D'Amore 
113395c635efSGarrett D'Amore 	if ((sdp = opendir(sectpath)) == NULL)
113495c635efSGarrett D'Amore 		return (0);
113595c635efSGarrett D'Amore 
113695c635efSGarrett D'Amore 	while ((sd = readdir(sdp))) {
113795c635efSGarrett D'Amore 		char	*pname;
113895c635efSGarrett D'Amore 
113995c635efSGarrett D'Amore 		if ((pname = strdup(sd->d_name)) == NULL)
114095c635efSGarrett D'Amore 			err(1, "strdup");
114195c635efSGarrett D'Amore 		if ((last = strrchr(pname, '.')) != NULL &&
114295c635efSGarrett D'Amore 		    (strcmp(last, ".gz") == 0 || strcmp(last, ".bz2") == 0))
114395c635efSGarrett D'Amore 			*last = '\0';
114495c635efSGarrett D'Amore 		last = strrchr(pname, '.');
114595c635efSGarrett D'Amore 		nlen = last - pname;
114695c635efSGarrett D'Amore 		(void) snprintf(dname, sizeof (dname), "%.*s.", nlen, pname);
114795c635efSGarrett D'Amore 		if (strcmp(dname, file) == 0 ||
114895c635efSGarrett D'Amore 		    strcmp(pname, name) == 0) {
114995c635efSGarrett D'Amore 			(void) format(path, dir, name, sd->d_name);
115095c635efSGarrett D'Amore 			(void) closedir(sdp);
115195c635efSGarrett D'Amore 			free(pname);
115295c635efSGarrett D'Amore 			return (1);
115395c635efSGarrett D'Amore 		}
115495c635efSGarrett D'Amore 		free(pname);
115595c635efSGarrett D'Amore 	}
115695c635efSGarrett D'Amore 	(void) closedir(sdp);
115795c635efSGarrett D'Amore 
115895c635efSGarrett D'Amore 	return (0);
115995c635efSGarrett D'Amore }
116095c635efSGarrett D'Amore 
116195c635efSGarrett D'Amore /*
116295c635efSGarrett D'Amore  * Check the hash table of old directory names to see if there is a
116395c635efSGarrett D'Amore  * new directory name.
116495c635efSGarrett D'Amore  */
116595c635efSGarrett D'Amore static char *
map_section(char * section,char * path)116695c635efSGarrett D'Amore map_section(char *section, char *path)
116795c635efSGarrett D'Amore {
116895c635efSGarrett D'Amore 	int	i;
116995c635efSGarrett D'Amore 	char	fullpath[MAXPATHLEN];
117095c635efSGarrett D'Amore 
117195c635efSGarrett D'Amore 	if (list)  /* -l option fall through */
117295c635efSGarrett D'Amore 		return (NULL);
117395c635efSGarrett D'Amore 
117495c635efSGarrett D'Amore 	for (i = 0; map[i].new_name != NULL; i++) {
117595c635efSGarrett D'Amore 		if (strcmp(section, map[i].old_name) == 0) {
117695c635efSGarrett D'Amore 			(void) snprintf(fullpath, sizeof (fullpath),
117795c635efSGarrett D'Amore 			    "%s/man%s", path, map[i].new_name);
117895c635efSGarrett D'Amore 			if (!access(fullpath, R_OK | X_OK)) {
117995c635efSGarrett D'Amore 				return (map[i].new_name);
118095c635efSGarrett D'Amore 			} else {
118195c635efSGarrett D'Amore 				return (NULL);
118295c635efSGarrett D'Amore 			}
118395c635efSGarrett D'Amore 		}
118495c635efSGarrett D'Amore 	}
118595c635efSGarrett D'Amore 
118695c635efSGarrett D'Amore 	return (NULL);
118795c635efSGarrett D'Amore }
118895c635efSGarrett D'Amore 
118995c635efSGarrett D'Amore /*
119095c635efSGarrett D'Amore  * Format the manpage.
119195c635efSGarrett D'Amore  */
119295c635efSGarrett D'Amore static int
format(char * path,char * dir,char * name,char * pg)119395c635efSGarrett D'Amore format(char *path, char *dir, char *name, char *pg)
119495c635efSGarrett D'Amore {
119595c635efSGarrett D'Amore 	char		manpname[MAXPATHLEN], catpname[MAXPATHLEN];
119695c635efSGarrett D'Amore 	char		cmdbuf[BUFSIZ], tmpbuf[BUFSIZ];
119795c635efSGarrett D'Amore 	char		*cattool;
119895c635efSGarrett D'Amore 	struct stat	sbman, sbcat;
119995c635efSGarrett D'Amore 
120095c635efSGarrett D'Amore 	found++;
120195c635efSGarrett D'Amore 
120295c635efSGarrett D'Amore 	if (list) {
120395c635efSGarrett D'Amore 		(void) printf(gettext("%s(%s)\t-M %s\n"), name, dir + 3, path);
120495c635efSGarrett D'Amore 		return (-1);
120595c635efSGarrett D'Amore 	}
120695c635efSGarrett D'Amore 
120795c635efSGarrett D'Amore 	(void) snprintf(manpname, sizeof (manpname), "%s/man%s/%s", path,
120895c635efSGarrett D'Amore 	    dir + 3, pg);
120995c635efSGarrett D'Amore 	(void) snprintf(catpname, sizeof (catpname), "%s/cat%s/%s", path,
121095c635efSGarrett D'Amore 	    dir + 3, pg);
121195c635efSGarrett D'Amore 
121295c635efSGarrett D'Amore 	/* Can't do PS output if manpage doesn't exist */
121395c635efSGarrett D'Amore 	if (stat(manpname, &sbman) != 0 && (psoutput|lintout))
121495c635efSGarrett D'Amore 		return (-1);
121595c635efSGarrett D'Amore 
121695c635efSGarrett D'Amore 	/*
121795c635efSGarrett D'Amore 	 * If both manpage and catpage do not exist, manpname is
121895c635efSGarrett D'Amore 	 * broken symlink, most likely.
121995c635efSGarrett D'Amore 	 */
122095c635efSGarrett D'Amore 	if (stat(catpname, &sbcat) != 0 && stat(manpname, &sbman) != 0)
122195c635efSGarrett D'Amore 		err(1, "%s", manpname);
122295c635efSGarrett D'Amore 
122395c635efSGarrett D'Amore 	/* Setup cattool */
122495c635efSGarrett D'Amore 	if (fnmatch("*.gz", manpname, 0) == 0)
122595c635efSGarrett D'Amore 		cattool = "gzcat";
122695c635efSGarrett D'Amore 	else if (fnmatch("*.bz2", manpname, 0) == 0)
122795c635efSGarrett D'Amore 		cattool = "bzcat";
122895c635efSGarrett D'Amore 	else
122995c635efSGarrett D'Amore 		cattool = "cat";
123095c635efSGarrett D'Amore 
123195c635efSGarrett D'Amore 	if (psoutput) {
123295c635efSGarrett D'Amore 		(void) snprintf(cmdbuf, BUFSIZ,
1233260e9a87SYuri Pankov 		    "cd %s; %s %s | mandoc -Tps | lp -Tpostscript",
1234260e9a87SYuri Pankov 		    path, cattool, manpname);
123595c635efSGarrett D'Amore 		DPRINTF("-- Using manpage: %s\n", manpname);
123695c635efSGarrett D'Amore 		goto cmd;
123795c635efSGarrett D'Amore 	} else if (lintout) {
123895c635efSGarrett D'Amore 		(void) snprintf(cmdbuf, BUFSIZ,
1239260e9a87SYuri Pankov 		    "cd %s; %s %s | mandoc -Tlint",
1240260e9a87SYuri Pankov 		    path, cattool, manpname);
124195c635efSGarrett D'Amore 		DPRINTF("-- Linting manpage: %s\n", manpname);
124295c635efSGarrett D'Amore 		goto cmd;
124395c635efSGarrett D'Amore 	}
124495c635efSGarrett D'Amore 
124595c635efSGarrett D'Amore 	/*
124695c635efSGarrett D'Amore 	 * Output catpage if:
124795c635efSGarrett D'Amore 	 * - manpage doesn't exist
124895c635efSGarrett D'Amore 	 * - output width is standard and catpage is recent enough
124995c635efSGarrett D'Amore 	 */
125095c635efSGarrett D'Amore 	if (stat(manpname, &sbman) != 0 || (manwidth == 0 &&
125195c635efSGarrett D'Amore 	    stat(catpname, &sbcat) == 0 && sbcat.st_mtime >= sbman.st_mtime)) {
125295c635efSGarrett D'Amore 		DPRINTF("-- Using catpage: %s\n", catpname);
125395c635efSGarrett D'Amore 		(void) snprintf(cmdbuf, BUFSIZ, "%s %s", pager, catpname);
125495c635efSGarrett D'Amore 		goto cmd;
125595c635efSGarrett D'Amore 	}
125695c635efSGarrett D'Amore 
125795c635efSGarrett D'Amore 	DPRINTF("-- Using manpage: %s\n", manpname);
125895c635efSGarrett D'Amore 	if (manwidth > 0)
125995c635efSGarrett D'Amore 		(void) snprintf(tmpbuf, BUFSIZ, "-Owidth=%d ", manwidth);
1260260e9a87SYuri Pankov 	(void) snprintf(cmdbuf, BUFSIZ, "cd %s; %s %s | mandoc %s| %s",
1261260e9a87SYuri Pankov 	    path, cattool, manpname, (manwidth > 0) ? tmpbuf : "", pager);
126295c635efSGarrett D'Amore 
126395c635efSGarrett D'Amore cmd:
126495c635efSGarrett D'Amore 	DPRINTF("-- Command: %s\n", cmdbuf);
126595c635efSGarrett D'Amore 
126695c635efSGarrett D'Amore 	if (!debug)
126795c635efSGarrett D'Amore 		return (system(cmdbuf) == 0);
126895c635efSGarrett D'Amore 	else
126995c635efSGarrett D'Amore 		return (0);
127095c635efSGarrett D'Amore }
127195c635efSGarrett D'Amore 
127295c635efSGarrett D'Amore /*
127395c635efSGarrett D'Amore  * Add <localedir> to the path.
127495c635efSGarrett D'Amore  */
127595c635efSGarrett D'Amore static char *
addlocale(char * path)127695c635efSGarrett D'Amore addlocale(char *path)
127795c635efSGarrett D'Amore {
127895c635efSGarrett D'Amore 	char	*tmp;
127995c635efSGarrett D'Amore 
128095c635efSGarrett D'Amore 	if (asprintf(&tmp, "%s/%s", path, localedir) == -1)
128195c635efSGarrett D'Amore 		err(1, "asprintf");
128295c635efSGarrett D'Amore 
128395c635efSGarrett D'Amore 	return (tmp);
128495c635efSGarrett D'Amore }
128595c635efSGarrett D'Amore 
128695c635efSGarrett D'Amore /*
128795c635efSGarrett D'Amore  * Get the order of sections from man.cf.
128895c635efSGarrett D'Amore  */
128995c635efSGarrett D'Amore static char *
check_config(char * path)129095c635efSGarrett D'Amore check_config(char *path)
129195c635efSGarrett D'Amore {
129295c635efSGarrett D'Amore 	FILE		*fp;
129395c635efSGarrett D'Amore 	char		*rc = NULL;
1294*d456640dSYuri Pankov 	char		*sect = NULL;
129595c635efSGarrett D'Amore 	char		fname[MAXPATHLEN];
129695c635efSGarrett D'Amore 	char		*line = NULL;
1297*d456640dSYuri Pankov 	char		*nl;
129895c635efSGarrett D'Amore 	size_t		linecap = 0;
129995c635efSGarrett D'Amore 
130095c635efSGarrett D'Amore 	(void) snprintf(fname, MAXPATHLEN, "%s/%s", path, CONFIG);
130195c635efSGarrett D'Amore 
130295c635efSGarrett D'Amore 	if ((fp = fopen(fname, "r")) == NULL)
130395c635efSGarrett D'Amore 		return (NULL);
130495c635efSGarrett D'Amore 
130595c635efSGarrett D'Amore 	while (getline(&line, &linecap, fp) > 0) {
1306*d456640dSYuri Pankov 		if ((rc = strstr(line, "MANSECTS=")) != NULL)
130795c635efSGarrett D'Amore 			break;
130895c635efSGarrett D'Amore 	}
130995c635efSGarrett D'Amore 
131095c635efSGarrett D'Amore 	(void) fclose(fp);
131195c635efSGarrett D'Amore 
1312*d456640dSYuri Pankov 	if (rc != NULL) {
1313*d456640dSYuri Pankov 		if ((nl = strchr(rc, '\n')) != NULL)
1314*d456640dSYuri Pankov 			*nl = '\0';
1315*d456640dSYuri Pankov 		sect = strchr(rc, '=') + 1;
131695c635efSGarrett D'Amore 	}
131795c635efSGarrett D'Amore 
1318*d456640dSYuri Pankov 	return (sect);
1319*d456640dSYuri Pankov }
132095c635efSGarrett D'Amore 
132195c635efSGarrett D'Amore /*
132295c635efSGarrett D'Amore  * Initialize the bintoman array with appropriate device and inode info.
132395c635efSGarrett D'Amore  */
132495c635efSGarrett D'Amore static void
init_bintoman(void)132595c635efSGarrett D'Amore init_bintoman(void)
132695c635efSGarrett D'Amore {
132795c635efSGarrett D'Amore 	int i;
132895c635efSGarrett D'Amore 	struct stat sb;
132995c635efSGarrett D'Amore 
133095c635efSGarrett D'Amore 	for (i = 0; bintoman[i].bindir != NULL; i++) {
133195c635efSGarrett D'Amore 		if (stat(bintoman[i].bindir, &sb) == 0) {
133295c635efSGarrett D'Amore 			bintoman[i].dev = sb.st_dev;
133395c635efSGarrett D'Amore 			bintoman[i].ino = sb.st_ino;
133495c635efSGarrett D'Amore 		} else {
133595c635efSGarrett D'Amore 			bintoman[i].dev = NODEV;
133695c635efSGarrett D'Amore 		}
133795c635efSGarrett D'Amore 	}
133895c635efSGarrett D'Amore }
133995c635efSGarrett D'Amore 
134095c635efSGarrett D'Amore /*
134195c635efSGarrett D'Amore  * If a duplicate is found, return 1.
134295c635efSGarrett D'Amore  * If a duplicate is not found, add it to the dupnode list and return 0.
134395c635efSGarrett D'Amore  */
134495c635efSGarrett D'Amore static int
dupcheck(struct man_node * mnp,struct dupnode ** dnp)134595c635efSGarrett D'Amore dupcheck(struct man_node *mnp, struct dupnode **dnp)
134695c635efSGarrett D'Amore {
134795c635efSGarrett D'Amore 	struct dupnode	*curdnp;
134895c635efSGarrett D'Amore 	struct secnode	*cursnp;
134995c635efSGarrett D'Amore 	struct stat 	sb;
135095c635efSGarrett D'Amore 	int 		i;
135195c635efSGarrett D'Amore 	int		rv = 1;
135295c635efSGarrett D'Amore 	int		dupfound;
135395c635efSGarrett D'Amore 
135495c635efSGarrett D'Amore 	/* If the path doesn't exist, treat it as a duplicate */
135595c635efSGarrett D'Amore 	if (stat(mnp->path, &sb) != 0)
135695c635efSGarrett D'Amore 		return (1);
135795c635efSGarrett D'Amore 
135895c635efSGarrett D'Amore 	/* If no sections were found in the man dir, treat it as duplicate */
135995c635efSGarrett D'Amore 	if (mnp->secv == NULL)
136095c635efSGarrett D'Amore 		return (1);
136195c635efSGarrett D'Amore 
136295c635efSGarrett D'Amore 	/*
136395c635efSGarrett D'Amore 	 * Find the dupnode structure for the previous time this directory
136495c635efSGarrett D'Amore 	 * was looked at.  Device and inode numbers are compared so that
136595c635efSGarrett D'Amore 	 * directories that are reached via different paths (e.g. /usr/man and
136695c635efSGarrett D'Amore 	 * /usr/share/man) are treated as equivalent.
136795c635efSGarrett D'Amore 	 */
136895c635efSGarrett D'Amore 	for (curdnp = *dnp; curdnp != NULL; curdnp = curdnp->next) {
136995c635efSGarrett D'Amore 		if (curdnp->dev == sb.st_dev && curdnp->ino == sb.st_ino)
137095c635efSGarrett D'Amore 			break;
137195c635efSGarrett D'Amore 	}
137295c635efSGarrett D'Amore 
137395c635efSGarrett D'Amore 	/*
137495c635efSGarrett D'Amore 	 * First time this directory has been seen. Add a new node to the
137595c635efSGarrett D'Amore 	 * head of the list. Since all entries are guaranteed to be unique
137695c635efSGarrett D'Amore 	 * copy all sections to new node.
137795c635efSGarrett D'Amore 	 */
137895c635efSGarrett D'Amore 	if (curdnp == NULL) {
137995c635efSGarrett D'Amore 		if ((curdnp = calloc(1, sizeof (struct dupnode))) == NULL)
138095c635efSGarrett D'Amore 			err(1, "calloc");
138195c635efSGarrett D'Amore 		for (i = 0; mnp->secv[i] != NULL; i++) {
138295c635efSGarrett D'Amore 			if ((cursnp = calloc(1, sizeof (struct secnode)))
138395c635efSGarrett D'Amore 			    == NULL)
138495c635efSGarrett D'Amore 				err(1, "calloc");
138595c635efSGarrett D'Amore 			cursnp->next = curdnp->secl;
138695c635efSGarrett D'Amore 			curdnp->secl = cursnp;
138795c635efSGarrett D'Amore 			if ((cursnp->secp = strdup(mnp->secv[i])) == NULL)
138895c635efSGarrett D'Amore 				err(1, "strdup");
138995c635efSGarrett D'Amore 		}
139095c635efSGarrett D'Amore 		curdnp->dev = sb.st_dev;
139195c635efSGarrett D'Amore 		curdnp->ino = sb.st_ino;
139295c635efSGarrett D'Amore 		curdnp->next = *dnp;
139395c635efSGarrett D'Amore 		*dnp = curdnp;
139495c635efSGarrett D'Amore 		return (0);
139595c635efSGarrett D'Amore 	}
139695c635efSGarrett D'Amore 
139795c635efSGarrett D'Amore 	/*
139895c635efSGarrett D'Amore 	 * Traverse the section vector in the man_node and the section list
139995c635efSGarrett D'Amore 	 * in dupnode cache to eliminate all duplicates from man_node.
140095c635efSGarrett D'Amore 	 */
140195c635efSGarrett D'Amore 	for (i = 0; mnp->secv[i] != NULL; i++) {
140295c635efSGarrett D'Amore 		dupfound = 0;
140395c635efSGarrett D'Amore 		for (cursnp = curdnp->secl; cursnp != NULL;
140495c635efSGarrett D'Amore 		    cursnp = cursnp->next) {
140595c635efSGarrett D'Amore 			if (strcmp(mnp->secv[i], cursnp->secp) == 0) {
140695c635efSGarrett D'Amore 				dupfound = 1;
140795c635efSGarrett D'Amore 				break;
140895c635efSGarrett D'Amore 			}
140995c635efSGarrett D'Amore 		}
141095c635efSGarrett D'Amore 		if (dupfound) {
141195c635efSGarrett D'Amore 			mnp->secv[i][0] = '\0';
141295c635efSGarrett D'Amore 			continue;
141395c635efSGarrett D'Amore 		}
141495c635efSGarrett D'Amore 
141595c635efSGarrett D'Amore 
141695c635efSGarrett D'Amore 		/*
141795c635efSGarrett D'Amore 		 * Update curdnp and set return value to indicate that this
141895c635efSGarrett D'Amore 		 * was not all duplicates.
141995c635efSGarrett D'Amore 		 */
142095c635efSGarrett D'Amore 		if ((cursnp = calloc(1, sizeof (struct secnode))) == NULL)
142195c635efSGarrett D'Amore 			err(1, "calloc");
142295c635efSGarrett D'Amore 		cursnp->next = curdnp->secl;
142395c635efSGarrett D'Amore 		curdnp->secl = cursnp;
142495c635efSGarrett D'Amore 		if ((cursnp->secp = strdup(mnp->secv[i])) == NULL)
142595c635efSGarrett D'Amore 			err(1, "strdup");
142695c635efSGarrett D'Amore 		rv = 0;
142795c635efSGarrett D'Amore 	}
142895c635efSGarrett D'Amore 
142995c635efSGarrett D'Amore 	return (rv);
143095c635efSGarrett D'Amore }
143195c635efSGarrett D'Amore 
143295c635efSGarrett D'Amore /*
143395c635efSGarrett D'Amore  * Given a bindir, return corresponding mandir.
143495c635efSGarrett D'Amore  */
143595c635efSGarrett D'Amore static char *
path_to_manpath(char * bindir)143695c635efSGarrett D'Amore path_to_manpath(char *bindir)
143795c635efSGarrett D'Amore {
143895c635efSGarrett D'Amore 	char		*mand, *p;
143995c635efSGarrett D'Amore 	int		i;
144095c635efSGarrett D'Amore 	struct stat	sb;
144195c635efSGarrett D'Amore 
144295c635efSGarrett D'Amore 	/* First look for known translations for specific bin paths */
144395c635efSGarrett D'Amore 	if (stat(bindir, &sb) != 0) {
144495c635efSGarrett D'Amore 		return (NULL);
144595c635efSGarrett D'Amore 	}
144695c635efSGarrett D'Amore 	for (i = 0; bintoman[i].bindir != NULL; i++) {
144795c635efSGarrett D'Amore 		if (sb.st_dev == bintoman[i].dev &&
144895c635efSGarrett D'Amore 		    sb.st_ino == bintoman[i].ino) {
144995c635efSGarrett D'Amore 			if ((mand = strdup(bintoman[i].mandir)) == NULL)
145095c635efSGarrett D'Amore 				err(1, "strdup");
145195c635efSGarrett D'Amore 			if ((p = strchr(mand, ',')) != NULL)
145295c635efSGarrett D'Amore 				*p = '\0';
145395c635efSGarrett D'Amore 			if (stat(mand, &sb) != 0) {
145495c635efSGarrett D'Amore 				free(mand);
145595c635efSGarrett D'Amore 				return (NULL);
145695c635efSGarrett D'Amore 			}
145795c635efSGarrett D'Amore 			if (p != NULL)
145895c635efSGarrett D'Amore 				*p = ',';
145995c635efSGarrett D'Amore 			return (mand);
146095c635efSGarrett D'Amore 		}
146195c635efSGarrett D'Amore 	}
146295c635efSGarrett D'Amore 
146395c635efSGarrett D'Amore 	/*
146495c635efSGarrett D'Amore 	 * No specific translation found.  Try `dirname $bindir`/share/man
146595c635efSGarrett D'Amore 	 * and `dirname $bindir`/man
146695c635efSGarrett D'Amore 	 */
146795c635efSGarrett D'Amore 	if ((mand = malloc(MAXPATHLEN)) == NULL)
146895c635efSGarrett D'Amore 		err(1, "malloc");
146995c635efSGarrett D'Amore 	if (strlcpy(mand, bindir, MAXPATHLEN) >= MAXPATHLEN) {
147095c635efSGarrett D'Amore 		free(mand);
147195c635efSGarrett D'Amore 		return (NULL);
147295c635efSGarrett D'Amore 	}
147395c635efSGarrett D'Amore 
147495c635efSGarrett D'Amore 	/*
147595c635efSGarrett D'Amore 	 * Advance to end of buffer, strip trailing /'s then remove last
147695c635efSGarrett D'Amore 	 * directory component.
147795c635efSGarrett D'Amore 	 */
147895c635efSGarrett D'Amore 	for (p = mand; *p != '\0'; p++)
147995c635efSGarrett D'Amore 		;
148095c635efSGarrett D'Amore 	for (; p > mand && *p == '/'; p--)
148195c635efSGarrett D'Amore 		;
148295c635efSGarrett D'Amore 	for (; p > mand && *p != '/'; p--)
148395c635efSGarrett D'Amore 		;
148495c635efSGarrett D'Amore 	if (p == mand && *p == '.') {
148595c635efSGarrett D'Amore 		if (realpath("..", mand) == NULL) {
148695c635efSGarrett D'Amore 			free(mand);
148795c635efSGarrett D'Amore 			return (NULL);
148895c635efSGarrett D'Amore 		}
148995c635efSGarrett D'Amore 		for (; *p != '\0'; p++)
149095c635efSGarrett D'Amore 			;
149195c635efSGarrett D'Amore 	} else {
149295c635efSGarrett D'Amore 		*p = '\0';
149395c635efSGarrett D'Amore 	}
149495c635efSGarrett D'Amore 
149595c635efSGarrett D'Amore 	if (strlcat(mand, "/share/man", MAXPATHLEN) >= MAXPATHLEN) {
149695c635efSGarrett D'Amore 		free(mand);
149795c635efSGarrett D'Amore 		return (NULL);
149895c635efSGarrett D'Amore 	}
149995c635efSGarrett D'Amore 
150095c635efSGarrett D'Amore 	if ((stat(mand, &sb) == 0) && S_ISDIR(sb.st_mode)) {
150195c635efSGarrett D'Amore 		return (mand);
150295c635efSGarrett D'Amore 	}
150395c635efSGarrett D'Amore 
150495c635efSGarrett D'Amore 	/*
150595c635efSGarrett D'Amore 	 * Strip the /share/man off and try /man
150695c635efSGarrett D'Amore 	 */
150795c635efSGarrett D'Amore 	*p = '\0';
150895c635efSGarrett D'Amore 	if (strlcat(mand, "/man", MAXPATHLEN) >= MAXPATHLEN) {
150995c635efSGarrett D'Amore 		free(mand);
151095c635efSGarrett D'Amore 		return (NULL);
151195c635efSGarrett D'Amore 	}
151295c635efSGarrett D'Amore 	if ((stat(mand, &sb) == 0) && S_ISDIR(sb.st_mode)) {
151395c635efSGarrett D'Amore 		return (mand);
151495c635efSGarrett D'Amore 	}
151595c635efSGarrett D'Amore 
151695c635efSGarrett D'Amore 	/*
151795c635efSGarrett D'Amore 	 * No man or share/man directory found
151895c635efSGarrett D'Amore 	 */
151995c635efSGarrett D'Amore 	free(mand);
152095c635efSGarrett D'Amore 	return (NULL);
152195c635efSGarrett D'Amore }
152295c635efSGarrett D'Amore 
152395c635efSGarrett D'Amore /*
152495c635efSGarrett D'Amore  * Free a linked list of dupnode structs.
152595c635efSGarrett D'Amore  */
152695c635efSGarrett D'Amore void
free_dupnode(struct dupnode * dnp)1527*d456640dSYuri Pankov free_dupnode(struct dupnode *dnp)
1528*d456640dSYuri Pankov {
152995c635efSGarrett D'Amore 	struct dupnode *dnp2;
153095c635efSGarrett D'Amore 	struct secnode *snp;
153195c635efSGarrett D'Amore 
153295c635efSGarrett D'Amore 	while (dnp != NULL) {
153395c635efSGarrett D'Amore 		dnp2 = dnp;
153495c635efSGarrett D'Amore 		dnp = dnp->next;
153595c635efSGarrett D'Amore 		while (dnp2->secl != NULL) {
153695c635efSGarrett D'Amore 			snp = dnp2->secl;
153795c635efSGarrett D'Amore 			dnp2->secl = dnp2->secl->next;
153895c635efSGarrett D'Amore 			free(snp->secp);
153995c635efSGarrett D'Amore 			free(snp);
154095c635efSGarrett D'Amore 		}
154195c635efSGarrett D'Amore 		free(dnp2);
154295c635efSGarrett D'Amore 	}
154395c635efSGarrett D'Amore }
154495c635efSGarrett D'Amore 
154595c635efSGarrett D'Amore /*
154695c635efSGarrett D'Amore  * Print manp linked list to stdout.
154795c635efSGarrett D'Amore  */
154895c635efSGarrett D'Amore void
print_manpath(struct man_node * manp)154995c635efSGarrett D'Amore print_manpath(struct man_node *manp)
155095c635efSGarrett D'Amore {
155195c635efSGarrett D'Amore 	char	colon[2] = "\0\0";
155295c635efSGarrett D'Amore 	char	**secp;
155395c635efSGarrett D'Amore 
155495c635efSGarrett D'Amore 	for (; manp != NULL; manp = manp->next) {
155595c635efSGarrett D'Amore 		(void) printf("%s%s", colon, manp->path);
155695c635efSGarrett D'Amore 		colon[0] = ':';
155795c635efSGarrett D'Amore 
155895c635efSGarrett D'Amore 		/*
155995c635efSGarrett D'Amore 		 * If man.cf or a directory scan was used to create section
156095c635efSGarrett D'Amore 		 * list, do not print section list again.  If the output of
156195c635efSGarrett D'Amore 		 * man -p is used to set MANPATH, subsequent runs of man
156295c635efSGarrett D'Amore 		 * will re-read man.cf and/or scan man directories as
156395c635efSGarrett D'Amore 		 * required.
156495c635efSGarrett D'Amore 		 */
156595c635efSGarrett D'Amore 		if (manp->defsrch != 0)
156695c635efSGarrett D'Amore 			continue;
156795c635efSGarrett D'Amore 
156895c635efSGarrett D'Amore 		for (secp = manp->secv; *secp != NULL; secp++) {
156995c635efSGarrett D'Amore 			/*
157095c635efSGarrett D'Amore 			 * Section deduplication may have eliminated some
157195c635efSGarrett D'Amore 			 * sections from the vector. Avoid displaying this
157295c635efSGarrett D'Amore 			 * detail which would appear as ",," in output
157395c635efSGarrett D'Amore 			 */
157495c635efSGarrett D'Amore 			if ((*secp)[0] != '\0')
157595c635efSGarrett D'Amore 				(void) printf(",%s", *secp);
157695c635efSGarrett D'Amore 		}
157795c635efSGarrett D'Amore 	}
157895c635efSGarrett D'Amore 	(void) printf("\n");
157995c635efSGarrett D'Amore }
158095c635efSGarrett D'Amore 
158195c635efSGarrett D'Amore static void
usage_man(void)158295c635efSGarrett D'Amore usage_man(void)
158395c635efSGarrett D'Amore {
158495c635efSGarrett D'Amore 
158595c635efSGarrett D'Amore 	(void) fprintf(stderr, gettext(
158695c635efSGarrett D'Amore "usage: man [-alptw] [-M path] [-s section] name ...\n"
158795c635efSGarrett D'Amore "       man [-M path] [-s section] -k keyword ...\n"
158895c635efSGarrett D'Amore "       man [-M path] [-s section] -f keyword ...\n"));
158995c635efSGarrett D'Amore 
159095c635efSGarrett D'Amore 	exit(1);
159195c635efSGarrett D'Amore }
159295c635efSGarrett D'Amore 
159395c635efSGarrett D'Amore static void
usage_whatapro(void)159495c635efSGarrett D'Amore usage_whatapro(void)
159595c635efSGarrett D'Amore {
159695c635efSGarrett D'Amore 
159795c635efSGarrett D'Amore 	(void) fprintf(stderr, gettext(
159895c635efSGarrett D'Amore "usage: %s [-M path] [-s section] keyword ...\n"),
159995c635efSGarrett D'Amore 	    whatis ? "whatis" : "apropos");
160095c635efSGarrett D'Amore 
160195c635efSGarrett D'Amore 	exit(1);
160295c635efSGarrett D'Amore }
160395c635efSGarrett D'Amore 
160495c635efSGarrett D'Amore static void
usage_catman(void)160595c635efSGarrett D'Amore usage_catman(void)
160695c635efSGarrett D'Amore {
160795c635efSGarrett D'Amore 	(void) fprintf(stderr, gettext(
160895c635efSGarrett D'Amore "usage: catman [-M path] [-w]\n"));
160995c635efSGarrett D'Amore 
161095c635efSGarrett D'Amore 	exit(1);
161195c635efSGarrett D'Amore }
161295c635efSGarrett D'Amore 
161395c635efSGarrett D'Amore static void
usage_makewhatis(void)161495c635efSGarrett D'Amore usage_makewhatis(void)
161595c635efSGarrett D'Amore {
161695c635efSGarrett D'Amore 	(void) fprintf(stderr, gettext("usage: makewhatis\n"));
161795c635efSGarrett D'Amore 
161895c635efSGarrett D'Amore 	exit(1);
161995c635efSGarrett D'Amore }
1620