xref: /titanic_50/usr/src/cmd/man/man.c (revision 95c635efb7c3b86efc493e0447eaec7aecca3f0f)
1*95c635efSGarrett D'Amore /*
2*95c635efSGarrett D'Amore  * CDDL HEADER START
3*95c635efSGarrett D'Amore  *
4*95c635efSGarrett D'Amore  * The contents of this file are subject to the terms of the
5*95c635efSGarrett D'Amore  * Common Development and Distribution License (the "License").
6*95c635efSGarrett D'Amore  * You may not use this file except in compliance with the License.
7*95c635efSGarrett D'Amore  *
8*95c635efSGarrett D'Amore  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*95c635efSGarrett D'Amore  * or http://www.opensolaris.org/os/licensing.
10*95c635efSGarrett D'Amore  * See the License for the specific language governing permissions
11*95c635efSGarrett D'Amore  * and limitations under the License.
12*95c635efSGarrett D'Amore  *
13*95c635efSGarrett D'Amore  * When distributing Covered Code, include this CDDL HEADER in each
14*95c635efSGarrett D'Amore  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*95c635efSGarrett D'Amore  * If applicable, add the following below this CDDL HEADER, with the
16*95c635efSGarrett D'Amore  * fields enclosed by brackets "[]" replaced with your own identifying
17*95c635efSGarrett D'Amore  * information: Portions Copyright [yyyy] [name of copyright owner]
18*95c635efSGarrett D'Amore  *
19*95c635efSGarrett D'Amore  * CDDL HEADER END
20*95c635efSGarrett D'Amore  */
21*95c635efSGarrett D'Amore 
22*95c635efSGarrett D'Amore /*
23*95c635efSGarrett D'Amore  * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
24*95c635efSGarrett D'Amore  * Copyright 2012, Josef 'Jeff' Sipek <jeffpc@31bits.net>. All rights reserved.
25*95c635efSGarrett D'Amore  * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
26*95c635efSGarrett D'Amore  * Copyright 2014 Garrett D'Amore <garrett@damore.org>
27*95c635efSGarrett D'Amore  */
28*95c635efSGarrett D'Amore 
29*95c635efSGarrett D'Amore /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989  AT&T.	*/
30*95c635efSGarrett D'Amore /*		All rights reserved.					*/
31*95c635efSGarrett D'Amore 
32*95c635efSGarrett D'Amore /*
33*95c635efSGarrett D'Amore  * University Copyright- Copyright (c) 1982, 1986, 1988
34*95c635efSGarrett D'Amore  * The Regents of the University of California
35*95c635efSGarrett D'Amore  * All Rights Reserved
36*95c635efSGarrett D'Amore  *
37*95c635efSGarrett D'Amore  * University Acknowledgment- Portions of this document are derived from
38*95c635efSGarrett D'Amore  * software developed by the University of California, Berkeley, and its
39*95c635efSGarrett D'Amore  * contributors.
40*95c635efSGarrett D'Amore  */
41*95c635efSGarrett D'Amore 
42*95c635efSGarrett D'Amore /*
43*95c635efSGarrett D'Amore  * Find and display reference manual pages. This version includes makewhatis
44*95c635efSGarrett D'Amore  * functionality as well.
45*95c635efSGarrett D'Amore  */
46*95c635efSGarrett D'Amore 
47*95c635efSGarrett D'Amore #include <sys/param.h>
48*95c635efSGarrett D'Amore #include <sys/stat.h>
49*95c635efSGarrett D'Amore #include <sys/termios.h>
50*95c635efSGarrett D'Amore #include <sys/types.h>
51*95c635efSGarrett D'Amore 
52*95c635efSGarrett D'Amore #include <ctype.h>
53*95c635efSGarrett D'Amore #include <dirent.h>
54*95c635efSGarrett D'Amore #include <err.h>
55*95c635efSGarrett D'Amore #include <errno.h>
56*95c635efSGarrett D'Amore #include <fcntl.h>
57*95c635efSGarrett D'Amore #include <fnmatch.h>
58*95c635efSGarrett D'Amore #include <limits.h>
59*95c635efSGarrett D'Amore #include <locale.h>
60*95c635efSGarrett D'Amore #include <malloc.h>
61*95c635efSGarrett D'Amore #include <memory.h>
62*95c635efSGarrett D'Amore #include <regex.h>
63*95c635efSGarrett D'Amore #include <stdio.h>
64*95c635efSGarrett D'Amore #include <stdlib.h>
65*95c635efSGarrett D'Amore #include <string.h>
66*95c635efSGarrett D'Amore #include <unistd.h>
67*95c635efSGarrett D'Amore 
68*95c635efSGarrett D'Amore #include "man.h"
69*95c635efSGarrett D'Amore 
70*95c635efSGarrett D'Amore 
71*95c635efSGarrett D'Amore /* Mapping of old directories to new directories */
72*95c635efSGarrett D'Amore static const struct map_entry {
73*95c635efSGarrett D'Amore 	char	*old_name;
74*95c635efSGarrett D'Amore 	char	*new_name;
75*95c635efSGarrett D'Amore } map[] = {
76*95c635efSGarrett D'Amore 	{ "3b",		"3ucb"		},
77*95c635efSGarrett D'Amore 	{ "3e",		"3elf"		},
78*95c635efSGarrett D'Amore 	{ "3g",		"3gen"		},
79*95c635efSGarrett D'Amore 	{ "3k",		"3kstat"	},
80*95c635efSGarrett D'Amore 	{ "3n",		"3socket"	},
81*95c635efSGarrett D'Amore 	{ "3r",		"3rt"		},
82*95c635efSGarrett D'Amore 	{ "3s",		"3c"		},
83*95c635efSGarrett D'Amore 	{ "3t",		"3thr"		},
84*95c635efSGarrett D'Amore 	{ "3x",		"3curses"	},
85*95c635efSGarrett D'Amore 	{ "3xc",	"3xcurses"	},
86*95c635efSGarrett D'Amore 	{ "3xn",	"3xnet"		},
87*95c635efSGarrett D'Amore 	{ NULL,		NULL		}
88*95c635efSGarrett D'Amore };
89*95c635efSGarrett D'Amore 
90*95c635efSGarrett D'Amore struct suffix {
91*95c635efSGarrett D'Amore 	char *ds;
92*95c635efSGarrett D'Amore 	char *fs;
93*95c635efSGarrett D'Amore };
94*95c635efSGarrett D'Amore 
95*95c635efSGarrett D'Amore /*
96*95c635efSGarrett D'Amore  * Flags that control behavior of build_manpath()
97*95c635efSGarrett D'Amore  *
98*95c635efSGarrett D'Amore  *   BMP_ISPATH 		pathv is a vector constructed from PATH.
99*95c635efSGarrett D'Amore  *                		Perform appropriate path translations for
100*95c635efSGarrett D'Amore  * 				manpath.
101*95c635efSGarrett D'Amore  *   BMP_APPEND_DEFMANDIR	Add DEFMANDIR to the end if it hasn't
102*95c635efSGarrett D'Amore  *				already appeared earlier.
103*95c635efSGarrett D'Amore  *   BMP_FALLBACK_DEFMANDIR	Append /usr/share/man only if no other
104*95c635efSGarrett D'Amore  *				manpath (including derived from PATH)
105*95c635efSGarrett D'Amore  * 				elements are valid.
106*95c635efSGarrett D'Amore  */
107*95c635efSGarrett D'Amore #define	BMP_ISPATH		1
108*95c635efSGarrett D'Amore #define	BMP_APPEND_DEFMANDIR	2
109*95c635efSGarrett D'Amore #define	BMP_FALLBACK_DEFMANDIR	4
110*95c635efSGarrett D'Amore 
111*95c635efSGarrett D'Amore /*
112*95c635efSGarrett D'Amore  * When doing equality comparisons of directories, device and inode
113*95c635efSGarrett D'Amore  * comparisons are done.  The secnode and dupnode structures are used
114*95c635efSGarrett D'Amore  * to form a list of lists for this processing.
115*95c635efSGarrett D'Amore  */
116*95c635efSGarrett D'Amore struct secnode {
117*95c635efSGarrett D'Amore 	char		*secp;
118*95c635efSGarrett D'Amore 	struct secnode	*next;
119*95c635efSGarrett D'Amore };
120*95c635efSGarrett D'Amore struct dupnode {
121*95c635efSGarrett D'Amore 	dev_t		dev;	/* from struct stat st_dev */
122*95c635efSGarrett D'Amore 	ino_t		ino;	/* from struct stat st_ino */
123*95c635efSGarrett D'Amore 	struct secnode	*secl;	/* sections already considered */
124*95c635efSGarrett D'Amore 	struct dupnode	*next;
125*95c635efSGarrett D'Amore };
126*95c635efSGarrett D'Amore 
127*95c635efSGarrett D'Amore /*
128*95c635efSGarrett D'Amore  * Map directories that may appear in PATH to the corresponding
129*95c635efSGarrett D'Amore  * man directory.
130*95c635efSGarrett D'Amore  */
131*95c635efSGarrett D'Amore static struct pathmap {
132*95c635efSGarrett D'Amore 	char	*bindir;
133*95c635efSGarrett D'Amore 	char	*mandir;
134*95c635efSGarrett D'Amore 	dev_t	dev;
135*95c635efSGarrett D'Amore 	ino_t	ino;
136*95c635efSGarrett D'Amore } bintoman[] = {
137*95c635efSGarrett D'Amore 	{ "/sbin",		"/usr/share/man,1m",		0, 0 },
138*95c635efSGarrett D'Amore 	{ "/usr/sbin",		"/usr/share/man,1m",		0, 0 },
139*95c635efSGarrett D'Amore 	{ "/usr/ucb",		"/usr/share/man,1b",		0, 0 },
140*95c635efSGarrett D'Amore 	{ "/usr/bin",		"/usr/share/man,1,1m,1s,1t,1c", 0, 0 },
141*95c635efSGarrett D'Amore 	{ "/usr/xpg4/bin",	"/usr/share/man,1",		0, 0 },
142*95c635efSGarrett D'Amore 	{ "/usr/xpg6/bin",	"/usr/share/man,1",		0, 0 },
143*95c635efSGarrett D'Amore 	{ NULL,			NULL,				0, 0 }
144*95c635efSGarrett D'Amore };
145*95c635efSGarrett D'Amore 
146*95c635efSGarrett D'Amore struct man_node {
147*95c635efSGarrett D'Amore 	char		*path;		/* mandir path */
148*95c635efSGarrett D'Amore 	char		**secv;		/* submandir suffices */
149*95c635efSGarrett D'Amore 	int		defsrch;	/* hint for man -p */
150*95c635efSGarrett D'Amore 	int		frompath;	/* hint for man -d */
151*95c635efSGarrett D'Amore 	struct man_node *next;
152*95c635efSGarrett D'Amore };
153*95c635efSGarrett D'Amore 
154*95c635efSGarrett D'Amore static int	all = 0;
155*95c635efSGarrett D'Amore static int	apropos = 0;
156*95c635efSGarrett D'Amore static int	debug = 0;
157*95c635efSGarrett D'Amore static int	found = 0;
158*95c635efSGarrett D'Amore static int	list = 0;
159*95c635efSGarrett D'Amore static int	makewhatis = 0;
160*95c635efSGarrett D'Amore static int	printmp = 0;
161*95c635efSGarrett D'Amore static int	sargs = 0;
162*95c635efSGarrett D'Amore static int	psoutput = 0;
163*95c635efSGarrett D'Amore static int	lintout = 0;
164*95c635efSGarrett D'Amore static int	whatis = 0;
165*95c635efSGarrett D'Amore static int	makewhatishere = 0;
166*95c635efSGarrett D'Amore 
167*95c635efSGarrett D'Amore static char	*mansec;
168*95c635efSGarrett D'Amore static char	*pager = NULL;
169*95c635efSGarrett D'Amore 
170*95c635efSGarrett D'Amore static char	*addlocale(char *);
171*95c635efSGarrett D'Amore static struct man_node *build_manpath(char **, int);
172*95c635efSGarrett D'Amore static void	do_makewhatis(struct man_node *);
173*95c635efSGarrett D'Amore static char	*check_config(char *);
174*95c635efSGarrett D'Amore static int	cmp(const void *, const void *);
175*95c635efSGarrett D'Amore static int	dupcheck(struct man_node *, struct dupnode **);
176*95c635efSGarrett D'Amore static int	format(char *, char *, char *, char *);
177*95c635efSGarrett D'Amore static void	free_dupnode(struct dupnode *);
178*95c635efSGarrett D'Amore static void	free_manp(struct man_node *manp);
179*95c635efSGarrett D'Amore static void	freev(char **);
180*95c635efSGarrett D'Amore static void	fullpaths(struct man_node **);
181*95c635efSGarrett D'Amore static void	get_all_sect(struct man_node *);
182*95c635efSGarrett D'Amore static int	getdirs(char *, char ***, int);
183*95c635efSGarrett D'Amore static void	getpath(struct man_node *, char **);
184*95c635efSGarrett D'Amore static void	getsect(struct man_node *, char **);
185*95c635efSGarrett D'Amore static void	init_bintoman(void);
186*95c635efSGarrett D'Amore static void	lower(char *);
187*95c635efSGarrett D'Amore static void	mandir(char **, char *, char *, int);
188*95c635efSGarrett D'Amore static int	manual(struct man_node *, char *);
189*95c635efSGarrett D'Amore static char	*map_section(char *, char *);
190*95c635efSGarrett D'Amore static char	*path_to_manpath(char *);
191*95c635efSGarrett D'Amore static void	print_manpath(struct man_node *);
192*95c635efSGarrett D'Amore static void	search_whatis(char *, char *);
193*95c635efSGarrett D'Amore static int	searchdir(char *, char *, char *);
194*95c635efSGarrett D'Amore static void	sortdir(DIR *, char ***);
195*95c635efSGarrett D'Amore static char	**split(char *, char);
196*95c635efSGarrett D'Amore static void	usage_man(void);
197*95c635efSGarrett D'Amore static void	usage_whatapro(void);
198*95c635efSGarrett D'Amore static void	usage_catman(void);
199*95c635efSGarrett D'Amore static void	usage_makewhatis(void);
200*95c635efSGarrett D'Amore static void	whatapro(struct man_node *, char *);
201*95c635efSGarrett D'Amore 
202*95c635efSGarrett D'Amore static char	language[MAXPATHLEN]; 	/* LC_MESSAGES */
203*95c635efSGarrett D'Amore static char	localedir[MAXPATHLEN];	/* locale specific path component */
204*95c635efSGarrett D'Amore 
205*95c635efSGarrett D'Amore static char	*newsection = NULL;
206*95c635efSGarrett D'Amore 
207*95c635efSGarrett D'Amore static int	manwidth = 0;
208*95c635efSGarrett D'Amore 
209*95c635efSGarrett D'Amore extern const char	*__progname;
210*95c635efSGarrett D'Amore 
211*95c635efSGarrett D'Amore int
212*95c635efSGarrett D'Amore main(int argc, char **argv)
213*95c635efSGarrett D'Amore {
214*95c635efSGarrett D'Amore 	int		c, i;
215*95c635efSGarrett D'Amore 	char		**pathv;
216*95c635efSGarrett D'Amore 	char		*manpath = NULL;
217*95c635efSGarrett D'Amore 	static struct man_node *mandirs = NULL;
218*95c635efSGarrett D'Amore 	int		bmp_flags = 0;
219*95c635efSGarrett D'Amore 	int		ret = 0;
220*95c635efSGarrett D'Amore 	char		*opts;
221*95c635efSGarrett D'Amore 	char		*mwstr;
222*95c635efSGarrett D'Amore 	int		catman = 0;
223*95c635efSGarrett D'Amore 
224*95c635efSGarrett D'Amore 	(void) setlocale(LC_ALL, "");
225*95c635efSGarrett D'Amore 	(void) strcpy(language, setlocale(LC_MESSAGES, (char *)NULL));
226*95c635efSGarrett D'Amore 	if (strcmp("C", language) != 0)
227*95c635efSGarrett D'Amore 		(void) strlcpy(localedir, language, MAXPATHLEN);
228*95c635efSGarrett D'Amore 
229*95c635efSGarrett D'Amore #if !defined(TEXT_DOMAIN)
230*95c635efSGarrett D'Amore #define	TEXT_DOMAIN "SYS_TEST"
231*95c635efSGarrett D'Amore #endif
232*95c635efSGarrett D'Amore 	(void) textdomain(TEXT_DOMAIN);
233*95c635efSGarrett D'Amore 
234*95c635efSGarrett D'Amore 	if (strcmp(__progname, "apropos") == 0) {
235*95c635efSGarrett D'Amore 		apropos++;
236*95c635efSGarrett D'Amore 		opts = "M:ds:";
237*95c635efSGarrett D'Amore 	} else if (strcmp(__progname, "whatis") == 0) {
238*95c635efSGarrett D'Amore 		apropos++;
239*95c635efSGarrett D'Amore 		whatis++;
240*95c635efSGarrett D'Amore 		opts = "M:ds:";
241*95c635efSGarrett D'Amore 	} else if (strcmp(__progname, "catman") == 0) {
242*95c635efSGarrett D'Amore 		catman++;
243*95c635efSGarrett D'Amore 		makewhatis++;
244*95c635efSGarrett D'Amore 		opts = "P:M:w";
245*95c635efSGarrett D'Amore 	} else if (strcmp(__progname, "makewhatis") == 0) {
246*95c635efSGarrett D'Amore 		makewhatis++;
247*95c635efSGarrett D'Amore 		makewhatishere++;
248*95c635efSGarrett D'Amore 		manpath = ".";
249*95c635efSGarrett D'Amore 		opts = "";
250*95c635efSGarrett D'Amore 	} else {
251*95c635efSGarrett D'Amore 		opts = "FM:P:T:adfklprs:tw";
252*95c635efSGarrett D'Amore 		if (argc > 1 && strcmp(argv[1], "-") == 0) {
253*95c635efSGarrett D'Amore 			pager = "cat";
254*95c635efSGarrett D'Amore 			optind++;
255*95c635efSGarrett D'Amore 		}
256*95c635efSGarrett D'Amore 	}
257*95c635efSGarrett D'Amore 
258*95c635efSGarrett D'Amore 	opterr = 0;
259*95c635efSGarrett D'Amore 	while ((c = getopt(argc, argv, opts)) != -1) {
260*95c635efSGarrett D'Amore 		switch (c) {
261*95c635efSGarrett D'Amore 		case 'M':	/* Respecify path for man pages */
262*95c635efSGarrett D'Amore 			manpath = optarg;
263*95c635efSGarrett D'Amore 			break;
264*95c635efSGarrett D'Amore 		case 'a':
265*95c635efSGarrett D'Amore 			all++;
266*95c635efSGarrett D'Amore 			break;
267*95c635efSGarrett D'Amore 		case 'd':
268*95c635efSGarrett D'Amore 			debug++;
269*95c635efSGarrett D'Amore 			break;
270*95c635efSGarrett D'Amore 		case 'f':
271*95c635efSGarrett D'Amore 			whatis++;
272*95c635efSGarrett D'Amore 			/*FALLTHROUGH*/
273*95c635efSGarrett D'Amore 		case 'k':
274*95c635efSGarrett D'Amore 			apropos++;
275*95c635efSGarrett D'Amore 			break;
276*95c635efSGarrett D'Amore 		case 'l':
277*95c635efSGarrett D'Amore 			list++;
278*95c635efSGarrett D'Amore 			all++;
279*95c635efSGarrett D'Amore 			break;
280*95c635efSGarrett D'Amore 		case 'p':
281*95c635efSGarrett D'Amore 			printmp++;
282*95c635efSGarrett D'Amore 			break;
283*95c635efSGarrett D'Amore 		case 's':
284*95c635efSGarrett D'Amore 			mansec = optarg;
285*95c635efSGarrett D'Amore 			sargs++;
286*95c635efSGarrett D'Amore 			break;
287*95c635efSGarrett D'Amore 		case 'r':
288*95c635efSGarrett D'Amore 			lintout++;
289*95c635efSGarrett D'Amore 			break;
290*95c635efSGarrett D'Amore 		case 't':
291*95c635efSGarrett D'Amore 			psoutput++;
292*95c635efSGarrett D'Amore 			break;
293*95c635efSGarrett D'Amore 		case 'T':
294*95c635efSGarrett D'Amore 		case 'P':
295*95c635efSGarrett D'Amore 		case 'F':
296*95c635efSGarrett D'Amore 			/* legacy options, compatibility only and ignored */
297*95c635efSGarrett D'Amore 			break;
298*95c635efSGarrett D'Amore 		case 'w':
299*95c635efSGarrett D'Amore 			makewhatis++;
300*95c635efSGarrett D'Amore 			break;
301*95c635efSGarrett D'Amore 		case '?':
302*95c635efSGarrett D'Amore 		default:
303*95c635efSGarrett D'Amore 			if (apropos)
304*95c635efSGarrett D'Amore 				usage_whatapro();
305*95c635efSGarrett D'Amore 			else if (catman)
306*95c635efSGarrett D'Amore 				usage_catman();
307*95c635efSGarrett D'Amore 			else if (makewhatishere)
308*95c635efSGarrett D'Amore 				usage_makewhatis();
309*95c635efSGarrett D'Amore 			else
310*95c635efSGarrett D'Amore 				usage_man();
311*95c635efSGarrett D'Amore 		}
312*95c635efSGarrett D'Amore 	}
313*95c635efSGarrett D'Amore 	argc -= optind;
314*95c635efSGarrett D'Amore 	argv += optind;
315*95c635efSGarrett D'Amore 
316*95c635efSGarrett D'Amore 	if (argc == 0) {
317*95c635efSGarrett D'Amore 		if (apropos) {
318*95c635efSGarrett D'Amore 			(void) fprintf(stderr, gettext("%s what?\n"),
319*95c635efSGarrett D'Amore 			    __progname);
320*95c635efSGarrett D'Amore 			exit(1);
321*95c635efSGarrett D'Amore 		} else if (!printmp && !makewhatis) {
322*95c635efSGarrett D'Amore 			(void) fprintf(stderr,
323*95c635efSGarrett D'Amore 			    gettext("What manual page do you want?\n"));
324*95c635efSGarrett D'Amore 			exit(1);
325*95c635efSGarrett D'Amore 		}
326*95c635efSGarrett D'Amore 	}
327*95c635efSGarrett D'Amore 
328*95c635efSGarrett D'Amore 	init_bintoman();
329*95c635efSGarrett D'Amore 	if (manpath == NULL && (manpath = getenv("MANPATH")) == NULL) {
330*95c635efSGarrett D'Amore 		if ((manpath = getenv("PATH")) != NULL)
331*95c635efSGarrett D'Amore 			bmp_flags = BMP_ISPATH | BMP_APPEND_DEFMANDIR;
332*95c635efSGarrett D'Amore 		else
333*95c635efSGarrett D'Amore 			manpath = DEFMANDIR;
334*95c635efSGarrett D'Amore 	}
335*95c635efSGarrett D'Amore 	pathv = split(manpath, ':');
336*95c635efSGarrett D'Amore 	mandirs = build_manpath(pathv, bmp_flags);
337*95c635efSGarrett D'Amore 	freev(pathv);
338*95c635efSGarrett D'Amore 	fullpaths(&mandirs);
339*95c635efSGarrett D'Amore 
340*95c635efSGarrett D'Amore 	if (makewhatis) {
341*95c635efSGarrett D'Amore 		do_makewhatis(mandirs);
342*95c635efSGarrett D'Amore 		exit(0);
343*95c635efSGarrett D'Amore 	}
344*95c635efSGarrett D'Amore 
345*95c635efSGarrett D'Amore 	if (printmp) {
346*95c635efSGarrett D'Amore 		print_manpath(mandirs);
347*95c635efSGarrett D'Amore 		exit(0);
348*95c635efSGarrett D'Amore 	}
349*95c635efSGarrett D'Amore 
350*95c635efSGarrett D'Amore 	/* Collect environment information */
351*95c635efSGarrett D'Amore 	if (isatty(STDOUT_FILENO) && (mwstr = getenv("MANWIDTH")) != NULL &&
352*95c635efSGarrett D'Amore 	    *mwstr != '\0') {
353*95c635efSGarrett D'Amore 		if (strcasecmp(mwstr, "tty") == 0) {
354*95c635efSGarrett D'Amore 			struct winsize	ws;
355*95c635efSGarrett D'Amore 
356*95c635efSGarrett D'Amore 			if (ioctl(0, TIOCGWINSZ, &ws) != 0)
357*95c635efSGarrett D'Amore 				warn("TIOCGWINSZ");
358*95c635efSGarrett D'Amore 			else
359*95c635efSGarrett D'Amore 				manwidth = ws.ws_col;
360*95c635efSGarrett D'Amore 		} else {
361*95c635efSGarrett D'Amore 			manwidth = (int)strtol(mwstr, (char **)NULL, 10);
362*95c635efSGarrett D'Amore 			if (manwidth < 0)
363*95c635efSGarrett D'Amore 				manwidth = 0;
364*95c635efSGarrett D'Amore 		}
365*95c635efSGarrett D'Amore 	}
366*95c635efSGarrett D'Amore 	if (manwidth != 0) {
367*95c635efSGarrett D'Amore 		DPRINTF("-- Using non-standard page width: %d\n", manwidth);
368*95c635efSGarrett D'Amore 	}
369*95c635efSGarrett D'Amore 
370*95c635efSGarrett D'Amore 	if (pager == NULL) {
371*95c635efSGarrett D'Amore 		if ((pager = getenv("PAGER")) == NULL || *pager == '\0')
372*95c635efSGarrett D'Amore 			pager = PAGER;
373*95c635efSGarrett D'Amore 	}
374*95c635efSGarrett D'Amore 	DPRINTF("-- Using pager: %s\n", pager);
375*95c635efSGarrett D'Amore 
376*95c635efSGarrett D'Amore 	for (i = 0; i < argc; i++) {
377*95c635efSGarrett D'Amore 		char		*cmd;
378*95c635efSGarrett D'Amore 		static struct man_node *mp;
379*95c635efSGarrett D'Amore 		char		*pv[2];
380*95c635efSGarrett D'Amore 
381*95c635efSGarrett D'Amore 		/*
382*95c635efSGarrett D'Amore 		 * If full path to command specified, customize
383*95c635efSGarrett D'Amore 		 * the manpath accordingly.
384*95c635efSGarrett D'Amore 		 */
385*95c635efSGarrett D'Amore 		if ((cmd = strrchr(argv[i], '/')) != NULL) {
386*95c635efSGarrett D'Amore 			*cmd = '\0';
387*95c635efSGarrett D'Amore 			if ((pv[0] = strdup(argv[i])) == NULL)
388*95c635efSGarrett D'Amore 				err(1, "strdup");
389*95c635efSGarrett D'Amore 			pv[1] = NULL;
390*95c635efSGarrett D'Amore 			*cmd = '/';
391*95c635efSGarrett D'Amore 			mp = build_manpath(pv,
392*95c635efSGarrett D'Amore 			    BMP_ISPATH | BMP_FALLBACK_DEFMANDIR);
393*95c635efSGarrett D'Amore 		} else {
394*95c635efSGarrett D'Amore 			mp = mandirs;
395*95c635efSGarrett D'Amore 		}
396*95c635efSGarrett D'Amore 
397*95c635efSGarrett D'Amore 		if (apropos)
398*95c635efSGarrett D'Amore 			whatapro(mp, argv[i]);
399*95c635efSGarrett D'Amore 		else
400*95c635efSGarrett D'Amore 			ret += manual(mp, argv[i]);
401*95c635efSGarrett D'Amore 
402*95c635efSGarrett D'Amore 		if (mp != NULL && mp != mandirs) {
403*95c635efSGarrett D'Amore 			free(pv[0]);
404*95c635efSGarrett D'Amore 			free_manp(mp);
405*95c635efSGarrett D'Amore 		}
406*95c635efSGarrett D'Amore 	}
407*95c635efSGarrett D'Amore 
408*95c635efSGarrett D'Amore 	return (ret == 0 ? 0 : 1);
409*95c635efSGarrett D'Amore }
410*95c635efSGarrett D'Amore 
411*95c635efSGarrett D'Amore /*
412*95c635efSGarrett D'Amore  * This routine builds the manpage structure from MANPATH or PATH,
413*95c635efSGarrett D'Amore  * depending on flags.  See BMP_* definitions above for valid
414*95c635efSGarrett D'Amore  * flags.
415*95c635efSGarrett D'Amore  */
416*95c635efSGarrett D'Amore static struct man_node *
417*95c635efSGarrett D'Amore build_manpath(char **pathv, int flags)
418*95c635efSGarrett D'Amore {
419*95c635efSGarrett D'Amore 	struct man_node *manpage = NULL;
420*95c635efSGarrett D'Amore 	struct man_node *currp = NULL;
421*95c635efSGarrett D'Amore 	struct man_node *lastp = NULL;
422*95c635efSGarrett D'Amore 	char		**p;
423*95c635efSGarrett D'Amore 	char		**q;
424*95c635efSGarrett D'Amore 	char		*mand = NULL;
425*95c635efSGarrett D'Amore 	char		*mandir = DEFMANDIR;
426*95c635efSGarrett D'Amore 	int		s;
427*95c635efSGarrett D'Amore 	struct dupnode	*didup = NULL;
428*95c635efSGarrett D'Amore 	struct stat	sb;
429*95c635efSGarrett D'Amore 
430*95c635efSGarrett D'Amore 	s = sizeof (struct man_node);
431*95c635efSGarrett D'Amore 	for (p = pathv; *p != NULL; ) {
432*95c635efSGarrett D'Amore 		if (flags & BMP_ISPATH) {
433*95c635efSGarrett D'Amore 			if ((mand = path_to_manpath(*p)) == NULL)
434*95c635efSGarrett D'Amore 				goto next;
435*95c635efSGarrett D'Amore 			free(*p);
436*95c635efSGarrett D'Amore 			*p = mand;
437*95c635efSGarrett D'Amore 		}
438*95c635efSGarrett D'Amore 		q = split(*p, ',');
439*95c635efSGarrett D'Amore 		if (stat(q[0], &sb) != 0 || (sb.st_mode & S_IFDIR) == 0) {
440*95c635efSGarrett D'Amore 			freev(q);
441*95c635efSGarrett D'Amore 			goto next;
442*95c635efSGarrett D'Amore 		}
443*95c635efSGarrett D'Amore 
444*95c635efSGarrett D'Amore 		if (access(q[0], R_OK | X_OK) == 0) {
445*95c635efSGarrett D'Amore 			/*
446*95c635efSGarrett D'Amore 			 * Some element exists.  Do not append DEFMANDIR as a
447*95c635efSGarrett D'Amore 			 * fallback.
448*95c635efSGarrett D'Amore 			 */
449*95c635efSGarrett D'Amore 			flags &= ~BMP_FALLBACK_DEFMANDIR;
450*95c635efSGarrett D'Amore 
451*95c635efSGarrett D'Amore 			if ((currp = (struct man_node *)calloc(1, s)) == NULL)
452*95c635efSGarrett D'Amore 				err(1, "calloc");
453*95c635efSGarrett D'Amore 
454*95c635efSGarrett D'Amore 			currp->frompath = (flags & BMP_ISPATH);
455*95c635efSGarrett D'Amore 
456*95c635efSGarrett D'Amore 			if (manpage == NULL)
457*95c635efSGarrett D'Amore 				lastp = manpage = currp;
458*95c635efSGarrett D'Amore 
459*95c635efSGarrett D'Amore 			getpath(currp, p);
460*95c635efSGarrett D'Amore 			getsect(currp, p);
461*95c635efSGarrett D'Amore 
462*95c635efSGarrett D'Amore 			/*
463*95c635efSGarrett D'Amore 			 * If there are no new elements in this path,
464*95c635efSGarrett D'Amore 			 * do not add it to the manpage list.
465*95c635efSGarrett D'Amore 			 */
466*95c635efSGarrett D'Amore 			if (dupcheck(currp, &didup) != 0) {
467*95c635efSGarrett D'Amore 				freev(currp->secv);
468*95c635efSGarrett D'Amore 				free(currp);
469*95c635efSGarrett D'Amore 			} else {
470*95c635efSGarrett D'Amore 				currp->next = NULL;
471*95c635efSGarrett D'Amore 				if (currp != manpage)
472*95c635efSGarrett D'Amore 					lastp->next = currp;
473*95c635efSGarrett D'Amore 				lastp = currp;
474*95c635efSGarrett D'Amore 			}
475*95c635efSGarrett D'Amore 		}
476*95c635efSGarrett D'Amore 		freev(q);
477*95c635efSGarrett D'Amore next:
478*95c635efSGarrett D'Amore 		/*
479*95c635efSGarrett D'Amore 		 * Special handling of appending DEFMANDIR. After all pathv
480*95c635efSGarrett D'Amore 		 * elements have been processed, append DEFMANDIR if needed.
481*95c635efSGarrett D'Amore 		 */
482*95c635efSGarrett D'Amore 		if (p == &mandir)
483*95c635efSGarrett D'Amore 			break;
484*95c635efSGarrett D'Amore 		p++;
485*95c635efSGarrett D'Amore 		if (*p != NULL)
486*95c635efSGarrett D'Amore 			continue;
487*95c635efSGarrett D'Amore 		if (flags & (BMP_APPEND_DEFMANDIR | BMP_FALLBACK_DEFMANDIR)) {
488*95c635efSGarrett D'Amore 			p = &mandir;
489*95c635efSGarrett D'Amore 			flags &= ~BMP_ISPATH;
490*95c635efSGarrett D'Amore 		}
491*95c635efSGarrett D'Amore 	}
492*95c635efSGarrett D'Amore 
493*95c635efSGarrett D'Amore 	free_dupnode(didup);
494*95c635efSGarrett D'Amore 
495*95c635efSGarrett D'Amore 	return (manpage);
496*95c635efSGarrett D'Amore }
497*95c635efSGarrett D'Amore 
498*95c635efSGarrett D'Amore /*
499*95c635efSGarrett D'Amore  * Store the mandir path into the manp structure.
500*95c635efSGarrett D'Amore  */
501*95c635efSGarrett D'Amore static void
502*95c635efSGarrett D'Amore getpath(struct man_node *manp, char **pv)
503*95c635efSGarrett D'Amore {
504*95c635efSGarrett D'Amore 	char	*s = *pv;
505*95c635efSGarrett D'Amore 	int	i = 0;
506*95c635efSGarrett D'Amore 
507*95c635efSGarrett D'Amore 	while (*s != '\0' && *s != ',')
508*95c635efSGarrett D'Amore 		i++, s++;
509*95c635efSGarrett D'Amore 
510*95c635efSGarrett D'Amore 	if ((manp->path = (char *)malloc(i + 1)) == NULL)
511*95c635efSGarrett D'Amore 		err(1, "malloc");
512*95c635efSGarrett D'Amore 	(void) strlcpy(manp->path, *pv, i + 1);
513*95c635efSGarrett D'Amore }
514*95c635efSGarrett D'Amore 
515*95c635efSGarrett D'Amore /*
516*95c635efSGarrett D'Amore  * Store the mandir's corresponding sections (submandir
517*95c635efSGarrett D'Amore  * directories) into the manp structure.
518*95c635efSGarrett D'Amore  */
519*95c635efSGarrett D'Amore static void
520*95c635efSGarrett D'Amore getsect(struct man_node *manp, char **pv)
521*95c635efSGarrett D'Amore {
522*95c635efSGarrett D'Amore 	char	*sections;
523*95c635efSGarrett D'Amore 	char	**sectp;
524*95c635efSGarrett D'Amore 
525*95c635efSGarrett D'Amore 	/* Just store all sections when doing makewhatis or apropos/whatis */
526*95c635efSGarrett D'Amore 	if (makewhatis || apropos) {
527*95c635efSGarrett D'Amore 		manp->defsrch = 1;
528*95c635efSGarrett D'Amore 		DPRINTF("-- Adding %s\n", manp->path);
529*95c635efSGarrett D'Amore 		manp->secv = NULL;
530*95c635efSGarrett D'Amore 		get_all_sect(manp);
531*95c635efSGarrett D'Amore 	} else if (sargs) {
532*95c635efSGarrett D'Amore 		manp->secv = split(mansec, ',');
533*95c635efSGarrett D'Amore 		for (sectp = manp->secv; *sectp; sectp++)
534*95c635efSGarrett D'Amore 			lower(*sectp);
535*95c635efSGarrett D'Amore 	} else if ((sections = strchr(*pv, ',')) != NULL) {
536*95c635efSGarrett D'Amore 		DPRINTF("-- Adding %s: MANSECTS=%s\n", manp->path, sections);
537*95c635efSGarrett D'Amore 		manp->secv = split(++sections, ',');
538*95c635efSGarrett D'Amore 		for (sectp = manp->secv; *sectp; sectp++)
539*95c635efSGarrett D'Amore 			lower(*sectp);
540*95c635efSGarrett D'Amore 		if (*manp->secv == NULL)
541*95c635efSGarrett D'Amore 			get_all_sect(manp);
542*95c635efSGarrett D'Amore 	} else if ((sections = check_config(*pv)) != NULL) {
543*95c635efSGarrett D'Amore 		manp->defsrch = 1;
544*95c635efSGarrett D'Amore 		DPRINTF("-- Adding %s: from %s, MANSECTS=%s\n", manp->path,
545*95c635efSGarrett D'Amore 		    CONFIG, sections);
546*95c635efSGarrett D'Amore 		manp->secv = split(sections, ',');
547*95c635efSGarrett D'Amore 		for (sectp = manp->secv; *sectp; sectp++)
548*95c635efSGarrett D'Amore 			lower(*sectp);
549*95c635efSGarrett D'Amore 		if (*manp->secv == NULL)
550*95c635efSGarrett D'Amore 			get_all_sect(manp);
551*95c635efSGarrett D'Amore 	} else {
552*95c635efSGarrett D'Amore 		manp->defsrch = 1;
553*95c635efSGarrett D'Amore 		DPRINTF("-- Adding %s: default sort order\n", manp->path);
554*95c635efSGarrett D'Amore 		manp->secv = NULL;
555*95c635efSGarrett D'Amore 		get_all_sect(manp);
556*95c635efSGarrett D'Amore 	}
557*95c635efSGarrett D'Amore }
558*95c635efSGarrett D'Amore 
559*95c635efSGarrett D'Amore /*
560*95c635efSGarrett D'Amore  * Get suffices of all sub-mandir directories in a mandir.
561*95c635efSGarrett D'Amore  */
562*95c635efSGarrett D'Amore static void
563*95c635efSGarrett D'Amore get_all_sect(struct man_node *manp)
564*95c635efSGarrett D'Amore {
565*95c635efSGarrett D'Amore 	DIR	*dp;
566*95c635efSGarrett D'Amore 	char	**dirv;
567*95c635efSGarrett D'Amore 	char	**dv;
568*95c635efSGarrett D'Amore 	char	**p;
569*95c635efSGarrett D'Amore 	char	*prev = NULL;
570*95c635efSGarrett D'Amore 	char 	*tmp = NULL;
571*95c635efSGarrett D'Amore 	int	maxentries = MAXTOKENS;
572*95c635efSGarrett D'Amore 	int	entries = 0;
573*95c635efSGarrett D'Amore 
574*95c635efSGarrett D'Amore 	if ((dp = opendir(manp->path)) == 0)
575*95c635efSGarrett D'Amore 		return;
576*95c635efSGarrett D'Amore 
577*95c635efSGarrett D'Amore 	sortdir(dp, &dirv);
578*95c635efSGarrett D'Amore 
579*95c635efSGarrett D'Amore 	(void) closedir(dp);
580*95c635efSGarrett D'Amore 
581*95c635efSGarrett D'Amore 	if (manp->secv == NULL) {
582*95c635efSGarrett D'Amore 		if ((manp->secv = malloc(maxentries * sizeof (char *))) == NULL)
583*95c635efSGarrett D'Amore 			err(1, "malloc");
584*95c635efSGarrett D'Amore 	}
585*95c635efSGarrett D'Amore 
586*95c635efSGarrett D'Amore 	for (dv = dirv, p = manp->secv; *dv; dv++) {
587*95c635efSGarrett D'Amore 		if (strcmp(*dv, CONFIG) == 0) {
588*95c635efSGarrett D'Amore 			free(*dv);
589*95c635efSGarrett D'Amore 			continue;
590*95c635efSGarrett D'Amore 		}
591*95c635efSGarrett D'Amore 
592*95c635efSGarrett D'Amore 		free(tmp);
593*95c635efSGarrett D'Amore 		if ((tmp = strdup(*dv + 3)) == NULL)
594*95c635efSGarrett D'Amore 			err(1, "strdup");
595*95c635efSGarrett D'Amore 
596*95c635efSGarrett D'Amore 		if (prev != NULL && strcmp(prev, tmp) == 0) {
597*95c635efSGarrett D'Amore 			free(*dv);
598*95c635efSGarrett D'Amore 			continue;
599*95c635efSGarrett D'Amore 		}
600*95c635efSGarrett D'Amore 
601*95c635efSGarrett D'Amore 		free(prev);
602*95c635efSGarrett D'Amore 		if ((prev = strdup(*dv + 3)) == NULL)
603*95c635efSGarrett D'Amore 			err(1, "strdup");
604*95c635efSGarrett D'Amore 
605*95c635efSGarrett D'Amore 		if ((*p = strdup(*dv + 3)) == NULL)
606*95c635efSGarrett D'Amore 			err(1, "strdup");
607*95c635efSGarrett D'Amore 
608*95c635efSGarrett D'Amore 		p++; entries++;
609*95c635efSGarrett D'Amore 
610*95c635efSGarrett D'Amore 		if (entries == maxentries) {
611*95c635efSGarrett D'Amore 			maxentries += MAXTOKENS;
612*95c635efSGarrett D'Amore 			if ((manp->secv = realloc(manp->secv,
613*95c635efSGarrett D'Amore 			    sizeof (char *) * maxentries)) == NULL)
614*95c635efSGarrett D'Amore 				err(1, "realloc");
615*95c635efSGarrett D'Amore 			p = manp->secv + entries;
616*95c635efSGarrett D'Amore 		}
617*95c635efSGarrett D'Amore 		free(*dv);
618*95c635efSGarrett D'Amore 	}
619*95c635efSGarrett D'Amore 	free(tmp);
620*95c635efSGarrett D'Amore 	free(prev);
621*95c635efSGarrett D'Amore 	*p = NULL;
622*95c635efSGarrett D'Amore 	free(dirv);
623*95c635efSGarrett D'Amore }
624*95c635efSGarrett D'Amore 
625*95c635efSGarrett D'Amore /*
626*95c635efSGarrett D'Amore  * Build whatis databases.
627*95c635efSGarrett D'Amore  */
628*95c635efSGarrett D'Amore static void
629*95c635efSGarrett D'Amore do_makewhatis(struct man_node *manp)
630*95c635efSGarrett D'Amore {
631*95c635efSGarrett D'Amore 	struct man_node *p;
632*95c635efSGarrett D'Amore 	char		*ldir;
633*95c635efSGarrett D'Amore 
634*95c635efSGarrett D'Amore 	for (p = manp; p != NULL; p = p->next) {
635*95c635efSGarrett D'Amore 		ldir = addlocale(p->path);
636*95c635efSGarrett D'Amore 		if (*localedir != '\0' && getdirs(ldir, NULL, 0) > 0)
637*95c635efSGarrett D'Amore 			mwpath(ldir);
638*95c635efSGarrett D'Amore 		free(ldir);
639*95c635efSGarrett D'Amore 		mwpath(p->path);
640*95c635efSGarrett D'Amore 	}
641*95c635efSGarrett D'Amore }
642*95c635efSGarrett D'Amore 
643*95c635efSGarrett D'Amore /*
644*95c635efSGarrett D'Amore  * Count mandirs under the given manpath
645*95c635efSGarrett D'Amore  */
646*95c635efSGarrett D'Amore static int
647*95c635efSGarrett D'Amore getdirs(char *path, char ***dirv, int flag)
648*95c635efSGarrett D'Amore {
649*95c635efSGarrett D'Amore 	DIR		*dp;
650*95c635efSGarrett D'Amore 	struct dirent	*d;
651*95c635efSGarrett D'Amore 	int		n = 0;
652*95c635efSGarrett D'Amore 	int		maxentries = MAXDIRS;
653*95c635efSGarrett D'Amore 	char		**dv = NULL;
654*95c635efSGarrett D'Amore 
655*95c635efSGarrett D'Amore 	if ((dp = opendir(path)) == NULL)
656*95c635efSGarrett D'Amore 		return (0);
657*95c635efSGarrett D'Amore 
658*95c635efSGarrett D'Amore 	if (flag) {
659*95c635efSGarrett D'Amore 		if ((*dirv = malloc(sizeof (char *) *
660*95c635efSGarrett D'Amore 		    maxentries)) == NULL)
661*95c635efSGarrett D'Amore 			err(1, "malloc");
662*95c635efSGarrett D'Amore 		dv = *dirv;
663*95c635efSGarrett D'Amore 	}
664*95c635efSGarrett D'Amore 	while ((d = readdir(dp))) {
665*95c635efSGarrett D'Amore 		if (strncmp(d->d_name, "man", 3) != 0)
666*95c635efSGarrett D'Amore 			continue;
667*95c635efSGarrett D'Amore 		n++;
668*95c635efSGarrett D'Amore 
669*95c635efSGarrett D'Amore 		if (flag) {
670*95c635efSGarrett D'Amore 			if ((*dv = strdup(d->d_name + 3)) == NULL)
671*95c635efSGarrett D'Amore 				err(1, "strdup");
672*95c635efSGarrett D'Amore 			dv++;
673*95c635efSGarrett D'Amore 			if ((dv - *dirv) == maxentries) {
674*95c635efSGarrett D'Amore 				int	entries = maxentries;
675*95c635efSGarrett D'Amore 
676*95c635efSGarrett D'Amore 				maxentries += MAXTOKENS;
677*95c635efSGarrett D'Amore 				if ((*dirv = realloc(*dirv,
678*95c635efSGarrett D'Amore 				    sizeof (char *) * maxentries)) == NULL)
679*95c635efSGarrett D'Amore 					err(1, "realloc");
680*95c635efSGarrett D'Amore 				dv = *dirv + entries;
681*95c635efSGarrett D'Amore 			}
682*95c635efSGarrett D'Amore 		}
683*95c635efSGarrett D'Amore 	}
684*95c635efSGarrett D'Amore 
685*95c635efSGarrett D'Amore 	(void) closedir(dp);
686*95c635efSGarrett D'Amore 	return (n);
687*95c635efSGarrett D'Amore }
688*95c635efSGarrett D'Amore 
689*95c635efSGarrett D'Amore 
690*95c635efSGarrett D'Amore /*
691*95c635efSGarrett D'Amore  * Find matching whatis or apropos entries.
692*95c635efSGarrett D'Amore  */
693*95c635efSGarrett D'Amore static void
694*95c635efSGarrett D'Amore whatapro(struct man_node *manp, char *word)
695*95c635efSGarrett D'Amore {
696*95c635efSGarrett D'Amore 	char		whatpath[MAXPATHLEN];
697*95c635efSGarrett D'Amore 	struct man_node *b;
698*95c635efSGarrett D'Amore 	char		*ldir;
699*95c635efSGarrett D'Amore 
700*95c635efSGarrett D'Amore 	for (b = manp; b != NULL; b = b->next) {
701*95c635efSGarrett D'Amore 		if (*localedir != '\0') {
702*95c635efSGarrett D'Amore 			ldir = addlocale(b->path);
703*95c635efSGarrett D'Amore 			if (getdirs(ldir, NULL, 0) != 0) {
704*95c635efSGarrett D'Amore 				(void) snprintf(whatpath, sizeof (whatpath),
705*95c635efSGarrett D'Amore 				    "%s/%s", ldir, WHATIS);
706*95c635efSGarrett D'Amore 				search_whatis(whatpath, word);
707*95c635efSGarrett D'Amore 			}
708*95c635efSGarrett D'Amore 			free(ldir);
709*95c635efSGarrett D'Amore 		}
710*95c635efSGarrett D'Amore 		(void) snprintf(whatpath, sizeof (whatpath), "%s/%s", b->path,
711*95c635efSGarrett D'Amore 		    WHATIS);
712*95c635efSGarrett D'Amore 		search_whatis(whatpath, word);
713*95c635efSGarrett D'Amore 	}
714*95c635efSGarrett D'Amore }
715*95c635efSGarrett D'Amore 
716*95c635efSGarrett D'Amore static void
717*95c635efSGarrett D'Amore search_whatis(char *whatpath, char *word)
718*95c635efSGarrett D'Amore {
719*95c635efSGarrett D'Amore 	FILE		*fp;
720*95c635efSGarrett D'Amore 	char		*line = NULL;
721*95c635efSGarrett D'Amore 	size_t		linecap = 0;
722*95c635efSGarrett D'Amore 	char		*pkwd;
723*95c635efSGarrett D'Amore 	regex_t		preg;
724*95c635efSGarrett D'Amore 	char		**ss = NULL;
725*95c635efSGarrett D'Amore 	char		s[MAXNAMELEN];
726*95c635efSGarrett D'Amore 	int		i;
727*95c635efSGarrett D'Amore 
728*95c635efSGarrett D'Amore 	if ((fp = fopen(whatpath, "r")) == NULL) {
729*95c635efSGarrett D'Amore 		perror(whatpath);
730*95c635efSGarrett D'Amore 		return;
731*95c635efSGarrett D'Amore 	}
732*95c635efSGarrett D'Amore 
733*95c635efSGarrett D'Amore 	DPRINTF("-- Found %s: %s\n", WHATIS, whatpath);
734*95c635efSGarrett D'Amore 
735*95c635efSGarrett D'Amore 	/* Build keyword regex */
736*95c635efSGarrett D'Amore 	if (asprintf(&pkwd, "%s%s%s", (whatis) ? "\\<" : "",
737*95c635efSGarrett D'Amore 	    word, (whatis) ? "\\>" : "") == -1)
738*95c635efSGarrett D'Amore 		err(1, "asprintf");
739*95c635efSGarrett D'Amore 
740*95c635efSGarrett D'Amore 	if (regcomp(&preg, pkwd, REG_BASIC | REG_ICASE | REG_NOSUB) != 0)
741*95c635efSGarrett D'Amore 		err(1, "regcomp");
742*95c635efSGarrett D'Amore 
743*95c635efSGarrett D'Amore 	if (sargs)
744*95c635efSGarrett D'Amore 		ss = split(mansec, ',');
745*95c635efSGarrett D'Amore 
746*95c635efSGarrett D'Amore 	while (getline(&line, &linecap, fp) > 0) {
747*95c635efSGarrett D'Amore 		if (regexec(&preg, line, 0, NULL, 0) == 0) {
748*95c635efSGarrett D'Amore 			if (sargs) {
749*95c635efSGarrett D'Amore 				/* Section-restricted search */
750*95c635efSGarrett D'Amore 				for (i = 0; ss[i] != NULL; i++) {
751*95c635efSGarrett D'Amore 					(void) snprintf(s, sizeof (s), "(%s)",
752*95c635efSGarrett D'Amore 					    ss[i]);
753*95c635efSGarrett D'Amore 					if (strstr(line, s) != NULL) {
754*95c635efSGarrett D'Amore 						(void) printf("%s", line);
755*95c635efSGarrett D'Amore 						break;
756*95c635efSGarrett D'Amore 					}
757*95c635efSGarrett D'Amore 				}
758*95c635efSGarrett D'Amore 			} else {
759*95c635efSGarrett D'Amore 				(void) printf("%s", line);
760*95c635efSGarrett D'Amore 			}
761*95c635efSGarrett D'Amore 		}
762*95c635efSGarrett D'Amore 	}
763*95c635efSGarrett D'Amore 
764*95c635efSGarrett D'Amore 	if (ss != NULL)
765*95c635efSGarrett D'Amore 		freev(ss);
766*95c635efSGarrett D'Amore 	free(pkwd);
767*95c635efSGarrett D'Amore 	(void) fclose(fp);
768*95c635efSGarrett D'Amore }
769*95c635efSGarrett D'Amore 
770*95c635efSGarrett D'Amore 
771*95c635efSGarrett D'Amore /*
772*95c635efSGarrett D'Amore  * Split a string by specified separator.
773*95c635efSGarrett D'Amore  */
774*95c635efSGarrett D'Amore static char **
775*95c635efSGarrett D'Amore split(char *s1, char sep)
776*95c635efSGarrett D'Amore {
777*95c635efSGarrett D'Amore 	char	**tokv, **vp;
778*95c635efSGarrett D'Amore 	char	*mp = s1, *tp;
779*95c635efSGarrett D'Amore 	int	maxentries = MAXTOKENS;
780*95c635efSGarrett D'Amore 	int	entries = 0;
781*95c635efSGarrett D'Amore 
782*95c635efSGarrett D'Amore 	if ((tokv = vp = malloc(maxentries * sizeof (char *))) == NULL)
783*95c635efSGarrett D'Amore 		err(1, "malloc");
784*95c635efSGarrett D'Amore 
785*95c635efSGarrett D'Amore 	for (; mp && *mp; mp = tp) {
786*95c635efSGarrett D'Amore 		tp = strchr(mp, sep);
787*95c635efSGarrett D'Amore 		if (mp == tp) {
788*95c635efSGarrett D'Amore 			tp++;
789*95c635efSGarrett D'Amore 			continue;
790*95c635efSGarrett D'Amore 		}
791*95c635efSGarrett D'Amore 		if (tp) {
792*95c635efSGarrett D'Amore 			size_t	len;
793*95c635efSGarrett D'Amore 
794*95c635efSGarrett D'Amore 			len = tp - mp;
795*95c635efSGarrett D'Amore 			if ((*vp = (char *)malloc(sizeof (char) *
796*95c635efSGarrett D'Amore 			    len + 1)) == NULL)
797*95c635efSGarrett D'Amore 				err(1, "malloc");
798*95c635efSGarrett D'Amore 			(void) strncpy(*vp, mp, len);
799*95c635efSGarrett D'Amore 			*(*vp + len) = '\0';
800*95c635efSGarrett D'Amore 			tp++;
801*95c635efSGarrett D'Amore 			vp++;
802*95c635efSGarrett D'Amore 		} else {
803*95c635efSGarrett D'Amore 			if ((*vp = strdup(mp)) == NULL)
804*95c635efSGarrett D'Amore 				err(1, "strdup");
805*95c635efSGarrett D'Amore 			vp++;
806*95c635efSGarrett D'Amore 		}
807*95c635efSGarrett D'Amore 		entries++;
808*95c635efSGarrett D'Amore 		if (entries == maxentries) {
809*95c635efSGarrett D'Amore 			maxentries += MAXTOKENS;
810*95c635efSGarrett D'Amore 			if ((tokv = realloc(tokv,
811*95c635efSGarrett D'Amore 			    maxentries * sizeof (char *))) == NULL)
812*95c635efSGarrett D'Amore 				err(1, "realloc");
813*95c635efSGarrett D'Amore 			vp = tokv + entries;
814*95c635efSGarrett D'Amore 		}
815*95c635efSGarrett D'Amore 	}
816*95c635efSGarrett D'Amore 	*vp = 0;
817*95c635efSGarrett D'Amore 
818*95c635efSGarrett D'Amore 	return (tokv);
819*95c635efSGarrett D'Amore }
820*95c635efSGarrett D'Amore 
821*95c635efSGarrett D'Amore /*
822*95c635efSGarrett D'Amore  * Free a vector allocated by split()
823*95c635efSGarrett D'Amore  */
824*95c635efSGarrett D'Amore static void
825*95c635efSGarrett D'Amore freev(char **v)
826*95c635efSGarrett D'Amore {
827*95c635efSGarrett D'Amore 	int i;
828*95c635efSGarrett D'Amore 	if (v != NULL) {
829*95c635efSGarrett D'Amore 		for (i = 0; v[i] != NULL; i++) {
830*95c635efSGarrett D'Amore 			free(v[i]);
831*95c635efSGarrett D'Amore 		}
832*95c635efSGarrett D'Amore 		free(v);
833*95c635efSGarrett D'Amore 	}
834*95c635efSGarrett D'Amore }
835*95c635efSGarrett D'Amore 
836*95c635efSGarrett D'Amore /*
837*95c635efSGarrett D'Amore  * Convert paths to full paths if necessary
838*95c635efSGarrett D'Amore  */
839*95c635efSGarrett D'Amore static void
840*95c635efSGarrett D'Amore fullpaths(struct man_node **manp_head)
841*95c635efSGarrett D'Amore {
842*95c635efSGarrett D'Amore 	char		*cwd = NULL;
843*95c635efSGarrett D'Amore 	char		*p;
844*95c635efSGarrett D'Amore 	int		cwd_gotten = 0;
845*95c635efSGarrett D'Amore 	struct man_node *manp = *manp_head;
846*95c635efSGarrett D'Amore 	struct man_node *b;
847*95c635efSGarrett D'Amore 	struct man_node *prev = NULL;
848*95c635efSGarrett D'Amore 
849*95c635efSGarrett D'Amore 	for (b = manp; b != NULL; b = b->next) {
850*95c635efSGarrett D'Amore 		if (*(b->path) == '/') {
851*95c635efSGarrett D'Amore 			prev = b;
852*95c635efSGarrett D'Amore 			continue;
853*95c635efSGarrett D'Amore 		}
854*95c635efSGarrett D'Amore 
855*95c635efSGarrett D'Amore 		if (!cwd_gotten) {
856*95c635efSGarrett D'Amore 			cwd = getcwd(NULL, MAXPATHLEN);
857*95c635efSGarrett D'Amore 			cwd_gotten = 1;
858*95c635efSGarrett D'Amore 		}
859*95c635efSGarrett D'Amore 
860*95c635efSGarrett D'Amore 		if (cwd) {
861*95c635efSGarrett D'Amore 			/* Relative manpath with cwd: make absolute */
862*95c635efSGarrett D'Amore 			if (asprintf(&p, "%s/%s", cwd, b->path) == -1)
863*95c635efSGarrett D'Amore 				err(1, "asprintf");
864*95c635efSGarrett D'Amore 			free(b->path);
865*95c635efSGarrett D'Amore 			b->path = p;
866*95c635efSGarrett D'Amore 		} else {
867*95c635efSGarrett D'Amore 			/* Relative manpath but no cwd: omit path entry */
868*95c635efSGarrett D'Amore 			if (prev)
869*95c635efSGarrett D'Amore 				prev->next = b->next;
870*95c635efSGarrett D'Amore 			else
871*95c635efSGarrett D'Amore 				*manp_head = b->next;
872*95c635efSGarrett D'Amore 
873*95c635efSGarrett D'Amore 			free_manp(b);
874*95c635efSGarrett D'Amore 		}
875*95c635efSGarrett D'Amore 	}
876*95c635efSGarrett D'Amore 	free(cwd);
877*95c635efSGarrett D'Amore }
878*95c635efSGarrett D'Amore 
879*95c635efSGarrett D'Amore /*
880*95c635efSGarrett D'Amore  * Free a man_node structure and its contents
881*95c635efSGarrett D'Amore  */
882*95c635efSGarrett D'Amore static void
883*95c635efSGarrett D'Amore free_manp(struct man_node *manp)
884*95c635efSGarrett D'Amore {
885*95c635efSGarrett D'Amore 	char	**p;
886*95c635efSGarrett D'Amore 
887*95c635efSGarrett D'Amore 	free(manp->path);
888*95c635efSGarrett D'Amore 	p = manp->secv;
889*95c635efSGarrett D'Amore 	while ((p != NULL) && (*p != NULL)) {
890*95c635efSGarrett D'Amore 		free(*p);
891*95c635efSGarrett D'Amore 		p++;
892*95c635efSGarrett D'Amore 	}
893*95c635efSGarrett D'Amore 	free(manp->secv);
894*95c635efSGarrett D'Amore 	free(manp);
895*95c635efSGarrett D'Amore }
896*95c635efSGarrett D'Amore 
897*95c635efSGarrett D'Amore 
898*95c635efSGarrett D'Amore /*
899*95c635efSGarrett D'Amore  * Map (in place) to lower case.
900*95c635efSGarrett D'Amore  */
901*95c635efSGarrett D'Amore static void
902*95c635efSGarrett D'Amore lower(char *s)
903*95c635efSGarrett D'Amore {
904*95c635efSGarrett D'Amore 
905*95c635efSGarrett D'Amore 	if (s == 0)
906*95c635efSGarrett D'Amore 		return;
907*95c635efSGarrett D'Amore 	while (*s) {
908*95c635efSGarrett D'Amore 		if (isupper(*s))
909*95c635efSGarrett D'Amore 			*s = tolower(*s);
910*95c635efSGarrett D'Amore 		s++;
911*95c635efSGarrett D'Amore 	}
912*95c635efSGarrett D'Amore }
913*95c635efSGarrett D'Amore 
914*95c635efSGarrett D'Amore 
915*95c635efSGarrett D'Amore /*
916*95c635efSGarrett D'Amore  * Compare function for qsort().
917*95c635efSGarrett D'Amore  * Sort first by section, then by prefix.
918*95c635efSGarrett D'Amore  */
919*95c635efSGarrett D'Amore static int
920*95c635efSGarrett D'Amore cmp(const void *arg1, const void *arg2)
921*95c635efSGarrett D'Amore {
922*95c635efSGarrett D'Amore 	int	n;
923*95c635efSGarrett D'Amore 	char	**p1 = (char **)arg1;
924*95c635efSGarrett D'Amore 	char	**p2 = (char **)arg2;
925*95c635efSGarrett D'Amore 
926*95c635efSGarrett D'Amore 	/* By section */
927*95c635efSGarrett D'Amore 	if ((n = strcmp(*p1 + 3, *p2 + 3)) != 0)
928*95c635efSGarrett D'Amore 		return (n);
929*95c635efSGarrett D'Amore 
930*95c635efSGarrett D'Amore 	/* By prefix reversed */
931*95c635efSGarrett D'Amore 	return (strncmp(*p2, *p1, 3));
932*95c635efSGarrett D'Amore }
933*95c635efSGarrett D'Amore 
934*95c635efSGarrett D'Amore 
935*95c635efSGarrett D'Amore /*
936*95c635efSGarrett D'Amore  * Find a manpage.
937*95c635efSGarrett D'Amore  */
938*95c635efSGarrett D'Amore static int
939*95c635efSGarrett D'Amore manual(struct man_node *manp, char *name)
940*95c635efSGarrett D'Amore {
941*95c635efSGarrett D'Amore 	struct man_node *p;
942*95c635efSGarrett D'Amore 	struct man_node *local;
943*95c635efSGarrett D'Amore 	int		ndirs = 0;
944*95c635efSGarrett D'Amore 	char		*ldir;
945*95c635efSGarrett D'Amore 	char		*ldirs[2];
946*95c635efSGarrett D'Amore 	char		*fullname = name;
947*95c635efSGarrett D'Amore 	char		*slash;
948*95c635efSGarrett D'Amore 
949*95c635efSGarrett D'Amore 	if ((slash = strrchr(name, '/')) != NULL)
950*95c635efSGarrett D'Amore 		name = slash + 1;
951*95c635efSGarrett D'Amore 
952*95c635efSGarrett D'Amore 	/* For each path in MANPATH */
953*95c635efSGarrett D'Amore 	found = 0;
954*95c635efSGarrett D'Amore 
955*95c635efSGarrett D'Amore 	for (p = manp; p != NULL; p = p->next) {
956*95c635efSGarrett D'Amore 		DPRINTF("-- Searching mandir: %s\n", p->path);
957*95c635efSGarrett D'Amore 
958*95c635efSGarrett D'Amore 		if (*localedir != '\0') {
959*95c635efSGarrett D'Amore 			ldir = addlocale(p->path);
960*95c635efSGarrett D'Amore 			ndirs = getdirs(ldir, NULL, 0);
961*95c635efSGarrett D'Amore 			if (ndirs != 0) {
962*95c635efSGarrett D'Amore 				ldirs[0] = ldir;
963*95c635efSGarrett D'Amore 				ldirs[1] = NULL;
964*95c635efSGarrett D'Amore 				local = build_manpath(ldirs, 0);
965*95c635efSGarrett D'Amore 				DPRINTF("-- Locale specific subdir: %s\n",
966*95c635efSGarrett D'Amore 				    ldir);
967*95c635efSGarrett D'Amore 				mandir(local->secv, ldir, name, 1);
968*95c635efSGarrett D'Amore 				free_manp(local);
969*95c635efSGarrett D'Amore 			}
970*95c635efSGarrett D'Amore 			free(ldir);
971*95c635efSGarrett D'Amore 		}
972*95c635efSGarrett D'Amore 
973*95c635efSGarrett D'Amore 		/*
974*95c635efSGarrett D'Amore 		 * Locale mandir not valid, man page in locale
975*95c635efSGarrett D'Amore 		 * mandir not found, or -a option present
976*95c635efSGarrett D'Amore 		 */
977*95c635efSGarrett D'Amore 		if (ndirs == 0 || !found || all)
978*95c635efSGarrett D'Amore 			mandir(p->secv, p->path, name, 0);
979*95c635efSGarrett D'Amore 
980*95c635efSGarrett D'Amore 		if (found && !all)
981*95c635efSGarrett D'Amore 			break;
982*95c635efSGarrett D'Amore 	}
983*95c635efSGarrett D'Amore 
984*95c635efSGarrett D'Amore 	if (!found) {
985*95c635efSGarrett D'Amore 		if (sargs) {
986*95c635efSGarrett D'Amore 			(void) fprintf(stderr, gettext(
987*95c635efSGarrett D'Amore 			    "No manual entry for %s in section(s) %s\n"),
988*95c635efSGarrett D'Amore 			    fullname, mansec);
989*95c635efSGarrett D'Amore 		} else {
990*95c635efSGarrett D'Amore 			(void) fprintf(stderr,
991*95c635efSGarrett D'Amore 			    gettext("No manual entry for %s\n"), fullname);
992*95c635efSGarrett D'Amore 		}
993*95c635efSGarrett D'Amore 
994*95c635efSGarrett D'Amore 	}
995*95c635efSGarrett D'Amore 
996*95c635efSGarrett D'Amore 	return (!found);
997*95c635efSGarrett D'Amore }
998*95c635efSGarrett D'Amore 
999*95c635efSGarrett D'Amore 
1000*95c635efSGarrett D'Amore /*
1001*95c635efSGarrett D'Amore  * For a specified manual directory, read, store and sort section subdirs.
1002*95c635efSGarrett D'Amore  * For each section specified, find and search matching subdirs.
1003*95c635efSGarrett D'Amore  */
1004*95c635efSGarrett D'Amore static void
1005*95c635efSGarrett D'Amore mandir(char **secv, char *path, char *name, int lspec)
1006*95c635efSGarrett D'Amore {
1007*95c635efSGarrett D'Amore 	DIR	*dp;
1008*95c635efSGarrett D'Amore 	char	**dirv;
1009*95c635efSGarrett D'Amore 	char	**dv, **pdv;
1010*95c635efSGarrett D'Amore 	int	len, dslen;
1011*95c635efSGarrett D'Amore 
1012*95c635efSGarrett D'Amore 	if ((dp = opendir(path)) == NULL)
1013*95c635efSGarrett D'Amore 		return;
1014*95c635efSGarrett D'Amore 
1015*95c635efSGarrett D'Amore 	if (lspec)
1016*95c635efSGarrett D'Amore 		DPRINTF("-- Searching mandir: %s\n", path);
1017*95c635efSGarrett D'Amore 
1018*95c635efSGarrett D'Amore 	sortdir(dp, &dirv);
1019*95c635efSGarrett D'Amore 
1020*95c635efSGarrett D'Amore 	/* Search in the order specified by MANSECTS */
1021*95c635efSGarrett D'Amore 	for (; *secv; secv++) {
1022*95c635efSGarrett D'Amore 		len = strlen(*secv);
1023*95c635efSGarrett D'Amore 		for (dv = dirv; *dv; dv++) {
1024*95c635efSGarrett D'Amore 			dslen = strlen(*dv + 3);
1025*95c635efSGarrett D'Amore 			if (dslen > len)
1026*95c635efSGarrett D'Amore 				len = dslen;
1027*95c635efSGarrett D'Amore 			if (**secv == '\\') {
1028*95c635efSGarrett D'Amore 				if (strcmp(*secv + 1, *dv + 3) != 0)
1029*95c635efSGarrett D'Amore 					continue;
1030*95c635efSGarrett D'Amore 			} else if (strncasecmp(*secv, *dv + 3, len) != 0) {
1031*95c635efSGarrett D'Amore 				if (!all &&
1032*95c635efSGarrett D'Amore 				    (newsection = map_section(*secv, path))
1033*95c635efSGarrett D'Amore 				    == NULL) {
1034*95c635efSGarrett D'Amore 					continue;
1035*95c635efSGarrett D'Amore 				}
1036*95c635efSGarrett D'Amore 				if (newsection == NULL)
1037*95c635efSGarrett D'Amore 					newsection = "";
1038*95c635efSGarrett D'Amore 				if (strncmp(newsection, *dv + 3, len) != 0) {
1039*95c635efSGarrett D'Amore 					continue;
1040*95c635efSGarrett D'Amore 				}
1041*95c635efSGarrett D'Amore 			}
1042*95c635efSGarrett D'Amore 
1043*95c635efSGarrett D'Amore 			if (searchdir(path, *dv, name) == 0)
1044*95c635efSGarrett D'Amore 				continue;
1045*95c635efSGarrett D'Amore 
1046*95c635efSGarrett D'Amore 			if (!all) {
1047*95c635efSGarrett D'Amore 				pdv = dirv;
1048*95c635efSGarrett D'Amore 				while (*pdv) {
1049*95c635efSGarrett D'Amore 					free(*pdv);
1050*95c635efSGarrett D'Amore 					pdv++;
1051*95c635efSGarrett D'Amore 				}
1052*95c635efSGarrett D'Amore 				(void) closedir(dp);
1053*95c635efSGarrett D'Amore 				free(dirv);
1054*95c635efSGarrett D'Amore 				return;
1055*95c635efSGarrett D'Amore 			}
1056*95c635efSGarrett D'Amore 
1057*95c635efSGarrett D'Amore 			if (all && **dv == 'm' && *(dv + 1) &&
1058*95c635efSGarrett D'Amore 			    strcmp(*(dv + 1) + 3, *dv + 3) == 0)
1059*95c635efSGarrett D'Amore 					dv++;
1060*95c635efSGarrett D'Amore 		}
1061*95c635efSGarrett D'Amore 	}
1062*95c635efSGarrett D'Amore 	pdv = dirv;
1063*95c635efSGarrett D'Amore 	while (*pdv != NULL) {
1064*95c635efSGarrett D'Amore 		free(*pdv);
1065*95c635efSGarrett D'Amore 		pdv++;
1066*95c635efSGarrett D'Amore 	}
1067*95c635efSGarrett D'Amore 	free(dirv);
1068*95c635efSGarrett D'Amore 	(void) closedir(dp);
1069*95c635efSGarrett D'Amore }
1070*95c635efSGarrett D'Amore 
1071*95c635efSGarrett D'Amore /*
1072*95c635efSGarrett D'Amore  * Sort directories.
1073*95c635efSGarrett D'Amore  */
1074*95c635efSGarrett D'Amore static void
1075*95c635efSGarrett D'Amore sortdir(DIR *dp, char ***dirv)
1076*95c635efSGarrett D'Amore {
1077*95c635efSGarrett D'Amore 	struct dirent	*d;
1078*95c635efSGarrett D'Amore 	char		**dv;
1079*95c635efSGarrett D'Amore 	int		maxentries = MAXDIRS;
1080*95c635efSGarrett D'Amore 	int		entries = 0;
1081*95c635efSGarrett D'Amore 
1082*95c635efSGarrett D'Amore 	if ((dv = *dirv = malloc(sizeof (char *) *
1083*95c635efSGarrett D'Amore 	    maxentries)) == NULL)
1084*95c635efSGarrett D'Amore 		err(1, "malloc");
1085*95c635efSGarrett D'Amore 	dv = *dirv;
1086*95c635efSGarrett D'Amore 
1087*95c635efSGarrett D'Amore 	while ((d = readdir(dp))) {
1088*95c635efSGarrett D'Amore 		if (strcmp(d->d_name, ".") == 0 ||
1089*95c635efSGarrett D'Amore 		    strcmp(d->d_name, "..") == 0)
1090*95c635efSGarrett D'Amore 			continue;
1091*95c635efSGarrett D'Amore 
1092*95c635efSGarrett D'Amore 		if (strncmp(d->d_name, "man", 3) == 0 ||
1093*95c635efSGarrett D'Amore 		    strncmp(d->d_name, "cat", 3) == 0) {
1094*95c635efSGarrett D'Amore 			if ((*dv = strdup(d->d_name)) == NULL)
1095*95c635efSGarrett D'Amore 				err(1, "strdup");
1096*95c635efSGarrett D'Amore 			dv++;
1097*95c635efSGarrett D'Amore 			entries++;
1098*95c635efSGarrett D'Amore 			if (entries == maxentries) {
1099*95c635efSGarrett D'Amore 				maxentries += MAXDIRS;
1100*95c635efSGarrett D'Amore 				if ((*dirv = realloc(*dirv,
1101*95c635efSGarrett D'Amore 				    sizeof (char *) * maxentries)) == NULL)
1102*95c635efSGarrett D'Amore 					err(1, "realloc");
1103*95c635efSGarrett D'Amore 				dv = *dirv + entries;
1104*95c635efSGarrett D'Amore 			}
1105*95c635efSGarrett D'Amore 		}
1106*95c635efSGarrett D'Amore 	}
1107*95c635efSGarrett D'Amore 	*dv = 0;
1108*95c635efSGarrett D'Amore 
1109*95c635efSGarrett D'Amore 	qsort((void *)*dirv, dv - *dirv, sizeof (char *), cmp);
1110*95c635efSGarrett D'Amore 
1111*95c635efSGarrett D'Amore }
1112*95c635efSGarrett D'Amore 
1113*95c635efSGarrett D'Amore 
1114*95c635efSGarrett D'Amore /*
1115*95c635efSGarrett D'Amore  * Search a section subdir for a given manpage.
1116*95c635efSGarrett D'Amore  */
1117*95c635efSGarrett D'Amore static int
1118*95c635efSGarrett D'Amore searchdir(char *path, char *dir, char *name)
1119*95c635efSGarrett D'Amore {
1120*95c635efSGarrett D'Amore 	DIR		*sdp;
1121*95c635efSGarrett D'Amore 	struct dirent	*sd;
1122*95c635efSGarrett D'Amore 	char		sectpath[MAXPATHLEN];
1123*95c635efSGarrett D'Amore 	char		file[MAXNAMLEN];
1124*95c635efSGarrett D'Amore 	char		dname[MAXPATHLEN];
1125*95c635efSGarrett D'Amore 	char		*last;
1126*95c635efSGarrett D'Amore 	int		nlen;
1127*95c635efSGarrett D'Amore 
1128*95c635efSGarrett D'Amore 	(void) snprintf(sectpath, sizeof (sectpath), "%s/%s", path, dir);
1129*95c635efSGarrett D'Amore 	(void) snprintf(file, sizeof (file), "%s.", name);
1130*95c635efSGarrett D'Amore 
1131*95c635efSGarrett D'Amore 	if ((sdp = opendir(sectpath)) == NULL)
1132*95c635efSGarrett D'Amore 		return (0);
1133*95c635efSGarrett D'Amore 
1134*95c635efSGarrett D'Amore 	while ((sd = readdir(sdp))) {
1135*95c635efSGarrett D'Amore 		char	*pname;
1136*95c635efSGarrett D'Amore 
1137*95c635efSGarrett D'Amore 		if ((pname = strdup(sd->d_name)) == NULL)
1138*95c635efSGarrett D'Amore 			err(1, "strdup");
1139*95c635efSGarrett D'Amore 		if ((last = strrchr(pname, '.')) != NULL &&
1140*95c635efSGarrett D'Amore 		    (strcmp(last, ".gz") == 0 || strcmp(last, ".bz2") == 0))
1141*95c635efSGarrett D'Amore 			*last = '\0';
1142*95c635efSGarrett D'Amore 		last = strrchr(pname, '.');
1143*95c635efSGarrett D'Amore 		nlen = last - pname;
1144*95c635efSGarrett D'Amore 		(void) snprintf(dname, sizeof (dname), "%.*s.", nlen, pname);
1145*95c635efSGarrett D'Amore 		if (strcmp(dname, file) == 0 ||
1146*95c635efSGarrett D'Amore 		    strcmp(pname, name) == 0) {
1147*95c635efSGarrett D'Amore 			(void) format(path, dir, name, sd->d_name);
1148*95c635efSGarrett D'Amore 			(void) closedir(sdp);
1149*95c635efSGarrett D'Amore 			free(pname);
1150*95c635efSGarrett D'Amore 			return (1);
1151*95c635efSGarrett D'Amore 		}
1152*95c635efSGarrett D'Amore 		free(pname);
1153*95c635efSGarrett D'Amore 	}
1154*95c635efSGarrett D'Amore 	(void) closedir(sdp);
1155*95c635efSGarrett D'Amore 
1156*95c635efSGarrett D'Amore 	return (0);
1157*95c635efSGarrett D'Amore }
1158*95c635efSGarrett D'Amore 
1159*95c635efSGarrett D'Amore /*
1160*95c635efSGarrett D'Amore  * Check the hash table of old directory names to see if there is a
1161*95c635efSGarrett D'Amore  * new directory name.
1162*95c635efSGarrett D'Amore  */
1163*95c635efSGarrett D'Amore static char *
1164*95c635efSGarrett D'Amore map_section(char *section, char *path)
1165*95c635efSGarrett D'Amore {
1166*95c635efSGarrett D'Amore 	int	i;
1167*95c635efSGarrett D'Amore 	char	fullpath[MAXPATHLEN];
1168*95c635efSGarrett D'Amore 
1169*95c635efSGarrett D'Amore 	if (list)  /* -l option fall through */
1170*95c635efSGarrett D'Amore 		return (NULL);
1171*95c635efSGarrett D'Amore 
1172*95c635efSGarrett D'Amore 	for (i = 0; map[i].new_name != NULL; i++) {
1173*95c635efSGarrett D'Amore 		if (strcmp(section, map[i].old_name) == 0) {
1174*95c635efSGarrett D'Amore 			(void) snprintf(fullpath, sizeof (fullpath),
1175*95c635efSGarrett D'Amore 			    "%s/man%s", path, map[i].new_name);
1176*95c635efSGarrett D'Amore 			if (!access(fullpath, R_OK | X_OK)) {
1177*95c635efSGarrett D'Amore 				return (map[i].new_name);
1178*95c635efSGarrett D'Amore 			} else {
1179*95c635efSGarrett D'Amore 				return (NULL);
1180*95c635efSGarrett D'Amore 			}
1181*95c635efSGarrett D'Amore 		}
1182*95c635efSGarrett D'Amore 	}
1183*95c635efSGarrett D'Amore 
1184*95c635efSGarrett D'Amore 	return (NULL);
1185*95c635efSGarrett D'Amore }
1186*95c635efSGarrett D'Amore 
1187*95c635efSGarrett D'Amore /*
1188*95c635efSGarrett D'Amore  * Format the manpage.
1189*95c635efSGarrett D'Amore  */
1190*95c635efSGarrett D'Amore static int
1191*95c635efSGarrett D'Amore format(char *path, char *dir, char *name, char *pg)
1192*95c635efSGarrett D'Amore {
1193*95c635efSGarrett D'Amore 	char		manpname[MAXPATHLEN], catpname[MAXPATHLEN];
1194*95c635efSGarrett D'Amore 	char		cmdbuf[BUFSIZ], tmpbuf[BUFSIZ];
1195*95c635efSGarrett D'Amore 	char		*cattool;
1196*95c635efSGarrett D'Amore 	int		utf8 = 0;
1197*95c635efSGarrett D'Amore 	struct stat	sbman, sbcat;
1198*95c635efSGarrett D'Amore 
1199*95c635efSGarrett D'Amore 	found++;
1200*95c635efSGarrett D'Amore 
1201*95c635efSGarrett D'Amore 	if (list) {
1202*95c635efSGarrett D'Amore 		(void) printf(gettext("%s(%s)\t-M %s\n"), name, dir + 3, path);
1203*95c635efSGarrett D'Amore 		return (-1);
1204*95c635efSGarrett D'Amore 	}
1205*95c635efSGarrett D'Amore 
1206*95c635efSGarrett D'Amore 	(void) snprintf(manpname, sizeof (manpname), "%s/man%s/%s", path,
1207*95c635efSGarrett D'Amore 	    dir + 3, pg);
1208*95c635efSGarrett D'Amore 	(void) snprintf(catpname, sizeof (catpname), "%s/cat%s/%s", path,
1209*95c635efSGarrett D'Amore 	    dir + 3, pg);
1210*95c635efSGarrett D'Amore 
1211*95c635efSGarrett D'Amore 	/* Can't do PS output if manpage doesn't exist */
1212*95c635efSGarrett D'Amore 	if (stat(manpname, &sbman) != 0 && (psoutput|lintout))
1213*95c635efSGarrett D'Amore 		return (-1);
1214*95c635efSGarrett D'Amore 
1215*95c635efSGarrett D'Amore 	/*
1216*95c635efSGarrett D'Amore 	 * If both manpage and catpage do not exist, manpname is
1217*95c635efSGarrett D'Amore 	 * broken symlink, most likely.
1218*95c635efSGarrett D'Amore 	 */
1219*95c635efSGarrett D'Amore 	if (stat(catpname, &sbcat) != 0 && stat(manpname, &sbman) != 0)
1220*95c635efSGarrett D'Amore 		err(1, "%s", manpname);
1221*95c635efSGarrett D'Amore 
1222*95c635efSGarrett D'Amore 	/* Setup cattool */
1223*95c635efSGarrett D'Amore 	if (fnmatch("*.gz", manpname, 0) == 0)
1224*95c635efSGarrett D'Amore 		cattool = "gzcat";
1225*95c635efSGarrett D'Amore 	else if (fnmatch("*.bz2", manpname, 0) == 0)
1226*95c635efSGarrett D'Amore 		cattool = "bzcat";
1227*95c635efSGarrett D'Amore 	else
1228*95c635efSGarrett D'Amore 		cattool = "cat";
1229*95c635efSGarrett D'Amore 
1230*95c635efSGarrett D'Amore 	/* Preprocess UTF-8 input with preconv (could be smarter) */
1231*95c635efSGarrett D'Amore 	if (strstr(path, "UTF-8") != NULL)
1232*95c635efSGarrett D'Amore 		utf8 = 1;
1233*95c635efSGarrett D'Amore 
1234*95c635efSGarrett D'Amore 	if (psoutput) {
1235*95c635efSGarrett D'Amore 		(void) snprintf(cmdbuf, BUFSIZ,
1236*95c635efSGarrett D'Amore 		    "cd %s; %s %s%s | mandoc -Tps | lp -Tpostscript",
1237*95c635efSGarrett D'Amore 		    path, cattool, manpname,
1238*95c635efSGarrett D'Amore 		    utf8 ? " | " PRECONV " -e UTF-8" : "");
1239*95c635efSGarrett D'Amore 		DPRINTF("-- Using manpage: %s\n", manpname);
1240*95c635efSGarrett D'Amore 		goto cmd;
1241*95c635efSGarrett D'Amore 	} else if (lintout) {
1242*95c635efSGarrett D'Amore 		(void) snprintf(cmdbuf, BUFSIZ,
1243*95c635efSGarrett D'Amore 		    "cd %s; %s %s%s | mandoc -Tlint",
1244*95c635efSGarrett D'Amore 		    path, cattool, manpname,
1245*95c635efSGarrett D'Amore 		    utf8 ? " | " PRECONV " -e UTF-8" : "");
1246*95c635efSGarrett D'Amore 		DPRINTF("-- Linting manpage: %s\n", manpname);
1247*95c635efSGarrett D'Amore 		goto cmd;
1248*95c635efSGarrett D'Amore 	}
1249*95c635efSGarrett D'Amore 
1250*95c635efSGarrett D'Amore 	/*
1251*95c635efSGarrett D'Amore 	 * Output catpage if:
1252*95c635efSGarrett D'Amore 	 * - manpage doesn't exist
1253*95c635efSGarrett D'Amore 	 * - output width is standard and catpage is recent enough
1254*95c635efSGarrett D'Amore 	 */
1255*95c635efSGarrett D'Amore 	if (stat(manpname, &sbman) != 0 || (manwidth == 0 &&
1256*95c635efSGarrett D'Amore 	    stat(catpname, &sbcat) == 0 && sbcat.st_mtime >= sbman.st_mtime)) {
1257*95c635efSGarrett D'Amore 		DPRINTF("-- Using catpage: %s\n", catpname);
1258*95c635efSGarrett D'Amore 		(void) snprintf(cmdbuf, BUFSIZ, "%s %s", pager, catpname);
1259*95c635efSGarrett D'Amore 		goto cmd;
1260*95c635efSGarrett D'Amore 	}
1261*95c635efSGarrett D'Amore 
1262*95c635efSGarrett D'Amore 	DPRINTF("-- Using manpage: %s\n", manpname);
1263*95c635efSGarrett D'Amore 	if (manwidth > 0)
1264*95c635efSGarrett D'Amore 		(void) snprintf(tmpbuf, BUFSIZ, "-Owidth=%d ", manwidth);
1265*95c635efSGarrett D'Amore 	(void) snprintf(cmdbuf, BUFSIZ, "cd %s; %s %s%s | mandoc -T%s %s| %s",
1266*95c635efSGarrett D'Amore 	    path, cattool, manpname,
1267*95c635efSGarrett D'Amore 	    utf8 ? " | " PRECONV " -e UTF-8 " : "",
1268*95c635efSGarrett D'Amore 	    utf8 ? "utf8" : "ascii", (manwidth > 0) ? tmpbuf : "", pager);
1269*95c635efSGarrett D'Amore 
1270*95c635efSGarrett D'Amore cmd:
1271*95c635efSGarrett D'Amore 	DPRINTF("-- Command: %s\n", cmdbuf);
1272*95c635efSGarrett D'Amore 
1273*95c635efSGarrett D'Amore 	if (!debug)
1274*95c635efSGarrett D'Amore 		return (system(cmdbuf) == 0);
1275*95c635efSGarrett D'Amore 	else
1276*95c635efSGarrett D'Amore 		return (0);
1277*95c635efSGarrett D'Amore }
1278*95c635efSGarrett D'Amore 
1279*95c635efSGarrett D'Amore /*
1280*95c635efSGarrett D'Amore  * Add <localedir> to the path.
1281*95c635efSGarrett D'Amore  */
1282*95c635efSGarrett D'Amore static char *
1283*95c635efSGarrett D'Amore addlocale(char *path)
1284*95c635efSGarrett D'Amore {
1285*95c635efSGarrett D'Amore 	char	*tmp;
1286*95c635efSGarrett D'Amore 
1287*95c635efSGarrett D'Amore 	if (asprintf(&tmp, "%s/%s", path, localedir) == -1)
1288*95c635efSGarrett D'Amore 		err(1, "asprintf");
1289*95c635efSGarrett D'Amore 
1290*95c635efSGarrett D'Amore 	return (tmp);
1291*95c635efSGarrett D'Amore }
1292*95c635efSGarrett D'Amore 
1293*95c635efSGarrett D'Amore /*
1294*95c635efSGarrett D'Amore  * Get the order of sections from man.cf.
1295*95c635efSGarrett D'Amore  */
1296*95c635efSGarrett D'Amore static char *
1297*95c635efSGarrett D'Amore check_config(char *path)
1298*95c635efSGarrett D'Amore {
1299*95c635efSGarrett D'Amore 	FILE		*fp;
1300*95c635efSGarrett D'Amore 	char		*rc = NULL;
1301*95c635efSGarrett D'Amore 	char		*sect;
1302*95c635efSGarrett D'Amore 	char		fname[MAXPATHLEN];
1303*95c635efSGarrett D'Amore 	char		*line = NULL;
1304*95c635efSGarrett D'Amore 	size_t		linecap = 0;
1305*95c635efSGarrett D'Amore 
1306*95c635efSGarrett D'Amore 	(void) snprintf(fname, MAXPATHLEN, "%s/%s", path, CONFIG);
1307*95c635efSGarrett D'Amore 
1308*95c635efSGarrett D'Amore 	if ((fp = fopen(fname, "r")) == NULL)
1309*95c635efSGarrett D'Amore 		return (NULL);
1310*95c635efSGarrett D'Amore 
1311*95c635efSGarrett D'Amore 	while (getline(&line, &linecap, fp) > 0) {
1312*95c635efSGarrett D'Amore 		if ((rc = strstr(line, "MANSECTS")) != NULL)
1313*95c635efSGarrett D'Amore 			break;
1314*95c635efSGarrett D'Amore 	}
1315*95c635efSGarrett D'Amore 
1316*95c635efSGarrett D'Amore 	(void) fclose(fp);
1317*95c635efSGarrett D'Amore 
1318*95c635efSGarrett D'Amore 	if (rc == NULL || (sect = strchr(line, '=')) == NULL)
1319*95c635efSGarrett D'Amore 		return (NULL);
1320*95c635efSGarrett D'Amore 	else
1321*95c635efSGarrett D'Amore 		return (++sect);
1322*95c635efSGarrett D'Amore }
1323*95c635efSGarrett D'Amore 
1324*95c635efSGarrett D'Amore 
1325*95c635efSGarrett D'Amore /*
1326*95c635efSGarrett D'Amore  * Initialize the bintoman array with appropriate device and inode info.
1327*95c635efSGarrett D'Amore  */
1328*95c635efSGarrett D'Amore static void
1329*95c635efSGarrett D'Amore init_bintoman(void)
1330*95c635efSGarrett D'Amore {
1331*95c635efSGarrett D'Amore 	int i;
1332*95c635efSGarrett D'Amore 	struct stat sb;
1333*95c635efSGarrett D'Amore 
1334*95c635efSGarrett D'Amore 	for (i = 0; bintoman[i].bindir != NULL; i++) {
1335*95c635efSGarrett D'Amore 		if (stat(bintoman[i].bindir, &sb) == 0) {
1336*95c635efSGarrett D'Amore 			bintoman[i].dev = sb.st_dev;
1337*95c635efSGarrett D'Amore 			bintoman[i].ino = sb.st_ino;
1338*95c635efSGarrett D'Amore 		} else {
1339*95c635efSGarrett D'Amore 			bintoman[i].dev = NODEV;
1340*95c635efSGarrett D'Amore 		}
1341*95c635efSGarrett D'Amore 	}
1342*95c635efSGarrett D'Amore }
1343*95c635efSGarrett D'Amore 
1344*95c635efSGarrett D'Amore /*
1345*95c635efSGarrett D'Amore  * If a duplicate is found, return 1.
1346*95c635efSGarrett D'Amore  * If a duplicate is not found, add it to the dupnode list and return 0.
1347*95c635efSGarrett D'Amore  */
1348*95c635efSGarrett D'Amore static int
1349*95c635efSGarrett D'Amore dupcheck(struct man_node *mnp, struct dupnode **dnp)
1350*95c635efSGarrett D'Amore {
1351*95c635efSGarrett D'Amore 	struct dupnode	*curdnp;
1352*95c635efSGarrett D'Amore 	struct secnode	*cursnp;
1353*95c635efSGarrett D'Amore 	struct stat 	sb;
1354*95c635efSGarrett D'Amore 	int 		i;
1355*95c635efSGarrett D'Amore 	int		rv = 1;
1356*95c635efSGarrett D'Amore 	int		dupfound;
1357*95c635efSGarrett D'Amore 
1358*95c635efSGarrett D'Amore 	/* If the path doesn't exist, treat it as a duplicate */
1359*95c635efSGarrett D'Amore 	if (stat(mnp->path, &sb) != 0)
1360*95c635efSGarrett D'Amore 		return (1);
1361*95c635efSGarrett D'Amore 
1362*95c635efSGarrett D'Amore 	/* If no sections were found in the man dir, treat it as duplicate */
1363*95c635efSGarrett D'Amore 	if (mnp->secv == NULL)
1364*95c635efSGarrett D'Amore 		return (1);
1365*95c635efSGarrett D'Amore 
1366*95c635efSGarrett D'Amore 	/*
1367*95c635efSGarrett D'Amore 	 * Find the dupnode structure for the previous time this directory
1368*95c635efSGarrett D'Amore 	 * was looked at.  Device and inode numbers are compared so that
1369*95c635efSGarrett D'Amore 	 * directories that are reached via different paths (e.g. /usr/man and
1370*95c635efSGarrett D'Amore 	 * /usr/share/man) are treated as equivalent.
1371*95c635efSGarrett D'Amore 	 */
1372*95c635efSGarrett D'Amore 	for (curdnp = *dnp; curdnp != NULL; curdnp = curdnp->next) {
1373*95c635efSGarrett D'Amore 		if (curdnp->dev == sb.st_dev && curdnp->ino == sb.st_ino)
1374*95c635efSGarrett D'Amore 			break;
1375*95c635efSGarrett D'Amore 	}
1376*95c635efSGarrett D'Amore 
1377*95c635efSGarrett D'Amore 	/*
1378*95c635efSGarrett D'Amore 	 * First time this directory has been seen. Add a new node to the
1379*95c635efSGarrett D'Amore 	 * head of the list. Since all entries are guaranteed to be unique
1380*95c635efSGarrett D'Amore 	 * copy all sections to new node.
1381*95c635efSGarrett D'Amore 	 */
1382*95c635efSGarrett D'Amore 	if (curdnp == NULL) {
1383*95c635efSGarrett D'Amore 		if ((curdnp = calloc(1, sizeof (struct dupnode))) == NULL)
1384*95c635efSGarrett D'Amore 			err(1, "calloc");
1385*95c635efSGarrett D'Amore 		for (i = 0; mnp->secv[i] != NULL; i++) {
1386*95c635efSGarrett D'Amore 			if ((cursnp = calloc(1, sizeof (struct secnode)))
1387*95c635efSGarrett D'Amore 			    == NULL)
1388*95c635efSGarrett D'Amore 				err(1, "calloc");
1389*95c635efSGarrett D'Amore 			cursnp->next = curdnp->secl;
1390*95c635efSGarrett D'Amore 			curdnp->secl = cursnp;
1391*95c635efSGarrett D'Amore 			if ((cursnp->secp = strdup(mnp->secv[i])) == NULL)
1392*95c635efSGarrett D'Amore 				err(1, "strdup");
1393*95c635efSGarrett D'Amore 		}
1394*95c635efSGarrett D'Amore 		curdnp->dev = sb.st_dev;
1395*95c635efSGarrett D'Amore 		curdnp->ino = sb.st_ino;
1396*95c635efSGarrett D'Amore 		curdnp->next = *dnp;
1397*95c635efSGarrett D'Amore 		*dnp = curdnp;
1398*95c635efSGarrett D'Amore 		return (0);
1399*95c635efSGarrett D'Amore 	}
1400*95c635efSGarrett D'Amore 
1401*95c635efSGarrett D'Amore 	/*
1402*95c635efSGarrett D'Amore 	 * Traverse the section vector in the man_node and the section list
1403*95c635efSGarrett D'Amore 	 * in dupnode cache to eliminate all duplicates from man_node.
1404*95c635efSGarrett D'Amore 	 */
1405*95c635efSGarrett D'Amore 	for (i = 0; mnp->secv[i] != NULL; i++) {
1406*95c635efSGarrett D'Amore 		dupfound = 0;
1407*95c635efSGarrett D'Amore 		for (cursnp = curdnp->secl; cursnp != NULL;
1408*95c635efSGarrett D'Amore 		    cursnp = cursnp->next) {
1409*95c635efSGarrett D'Amore 			if (strcmp(mnp->secv[i], cursnp->secp) == 0) {
1410*95c635efSGarrett D'Amore 				dupfound = 1;
1411*95c635efSGarrett D'Amore 				break;
1412*95c635efSGarrett D'Amore 			}
1413*95c635efSGarrett D'Amore 		}
1414*95c635efSGarrett D'Amore 		if (dupfound) {
1415*95c635efSGarrett D'Amore 			mnp->secv[i][0] = '\0';
1416*95c635efSGarrett D'Amore 			continue;
1417*95c635efSGarrett D'Amore 		}
1418*95c635efSGarrett D'Amore 
1419*95c635efSGarrett D'Amore 
1420*95c635efSGarrett D'Amore 		/*
1421*95c635efSGarrett D'Amore 		 * Update curdnp and set return value to indicate that this
1422*95c635efSGarrett D'Amore 		 * was not all duplicates.
1423*95c635efSGarrett D'Amore 		 */
1424*95c635efSGarrett D'Amore 		if ((cursnp = calloc(1, sizeof (struct secnode))) == NULL)
1425*95c635efSGarrett D'Amore 			err(1, "calloc");
1426*95c635efSGarrett D'Amore 		cursnp->next = curdnp->secl;
1427*95c635efSGarrett D'Amore 		curdnp->secl = cursnp;
1428*95c635efSGarrett D'Amore 		if ((cursnp->secp = strdup(mnp->secv[i])) == NULL)
1429*95c635efSGarrett D'Amore 			err(1, "strdup");
1430*95c635efSGarrett D'Amore 		rv = 0;
1431*95c635efSGarrett D'Amore 	}
1432*95c635efSGarrett D'Amore 
1433*95c635efSGarrett D'Amore 	return (rv);
1434*95c635efSGarrett D'Amore }
1435*95c635efSGarrett D'Amore 
1436*95c635efSGarrett D'Amore /*
1437*95c635efSGarrett D'Amore  * Given a bindir, return corresponding mandir.
1438*95c635efSGarrett D'Amore  */
1439*95c635efSGarrett D'Amore static char *
1440*95c635efSGarrett D'Amore path_to_manpath(char *bindir)
1441*95c635efSGarrett D'Amore {
1442*95c635efSGarrett D'Amore 	char		*mand, *p;
1443*95c635efSGarrett D'Amore 	int		i;
1444*95c635efSGarrett D'Amore 	struct stat	sb;
1445*95c635efSGarrett D'Amore 
1446*95c635efSGarrett D'Amore 	/* First look for known translations for specific bin paths */
1447*95c635efSGarrett D'Amore 	if (stat(bindir, &sb) != 0) {
1448*95c635efSGarrett D'Amore 		return (NULL);
1449*95c635efSGarrett D'Amore 	}
1450*95c635efSGarrett D'Amore 	for (i = 0; bintoman[i].bindir != NULL; i++) {
1451*95c635efSGarrett D'Amore 		if (sb.st_dev == bintoman[i].dev &&
1452*95c635efSGarrett D'Amore 		    sb.st_ino == bintoman[i].ino) {
1453*95c635efSGarrett D'Amore 			if ((mand = strdup(bintoman[i].mandir)) == NULL)
1454*95c635efSGarrett D'Amore 				err(1, "strdup");
1455*95c635efSGarrett D'Amore 			if ((p = strchr(mand, ',')) != NULL)
1456*95c635efSGarrett D'Amore 				*p = '\0';
1457*95c635efSGarrett D'Amore 			if (stat(mand, &sb) != 0) {
1458*95c635efSGarrett D'Amore 				free(mand);
1459*95c635efSGarrett D'Amore 				return (NULL);
1460*95c635efSGarrett D'Amore 			}
1461*95c635efSGarrett D'Amore 			if (p != NULL)
1462*95c635efSGarrett D'Amore 				*p = ',';
1463*95c635efSGarrett D'Amore 			return (mand);
1464*95c635efSGarrett D'Amore 		}
1465*95c635efSGarrett D'Amore 	}
1466*95c635efSGarrett D'Amore 
1467*95c635efSGarrett D'Amore 	/*
1468*95c635efSGarrett D'Amore 	 * No specific translation found.  Try `dirname $bindir`/share/man
1469*95c635efSGarrett D'Amore 	 * and `dirname $bindir`/man
1470*95c635efSGarrett D'Amore 	 */
1471*95c635efSGarrett D'Amore 	if ((mand = malloc(MAXPATHLEN)) == NULL)
1472*95c635efSGarrett D'Amore 		err(1, "malloc");
1473*95c635efSGarrett D'Amore 	if (strlcpy(mand, bindir, MAXPATHLEN) >= MAXPATHLEN) {
1474*95c635efSGarrett D'Amore 		free(mand);
1475*95c635efSGarrett D'Amore 		return (NULL);
1476*95c635efSGarrett D'Amore 	}
1477*95c635efSGarrett D'Amore 
1478*95c635efSGarrett D'Amore 	/*
1479*95c635efSGarrett D'Amore 	 * Advance to end of buffer, strip trailing /'s then remove last
1480*95c635efSGarrett D'Amore 	 * directory component.
1481*95c635efSGarrett D'Amore 	 */
1482*95c635efSGarrett D'Amore 	for (p = mand; *p != '\0'; p++)
1483*95c635efSGarrett D'Amore 		;
1484*95c635efSGarrett D'Amore 	for (; p > mand && *p == '/'; p--)
1485*95c635efSGarrett D'Amore 		;
1486*95c635efSGarrett D'Amore 	for (; p > mand && *p != '/'; p--)
1487*95c635efSGarrett D'Amore 		;
1488*95c635efSGarrett D'Amore 	if (p == mand && *p == '.') {
1489*95c635efSGarrett D'Amore 		if (realpath("..", mand) == NULL) {
1490*95c635efSGarrett D'Amore 			free(mand);
1491*95c635efSGarrett D'Amore 			return (NULL);
1492*95c635efSGarrett D'Amore 		}
1493*95c635efSGarrett D'Amore 		for (; *p != '\0'; p++)
1494*95c635efSGarrett D'Amore 			;
1495*95c635efSGarrett D'Amore 	} else {
1496*95c635efSGarrett D'Amore 		*p = '\0';
1497*95c635efSGarrett D'Amore 	}
1498*95c635efSGarrett D'Amore 
1499*95c635efSGarrett D'Amore 	if (strlcat(mand, "/share/man", MAXPATHLEN) >= MAXPATHLEN) {
1500*95c635efSGarrett D'Amore 		free(mand);
1501*95c635efSGarrett D'Amore 		return (NULL);
1502*95c635efSGarrett D'Amore 	}
1503*95c635efSGarrett D'Amore 
1504*95c635efSGarrett D'Amore 	if ((stat(mand, &sb) == 0) && S_ISDIR(sb.st_mode)) {
1505*95c635efSGarrett D'Amore 		return (mand);
1506*95c635efSGarrett D'Amore 	}
1507*95c635efSGarrett D'Amore 
1508*95c635efSGarrett D'Amore 	/*
1509*95c635efSGarrett D'Amore 	 * Strip the /share/man off and try /man
1510*95c635efSGarrett D'Amore 	 */
1511*95c635efSGarrett D'Amore 	*p = '\0';
1512*95c635efSGarrett D'Amore 	if (strlcat(mand, "/man", MAXPATHLEN) >= MAXPATHLEN) {
1513*95c635efSGarrett D'Amore 		free(mand);
1514*95c635efSGarrett D'Amore 		return (NULL);
1515*95c635efSGarrett D'Amore 	}
1516*95c635efSGarrett D'Amore 	if ((stat(mand, &sb) == 0) && S_ISDIR(sb.st_mode)) {
1517*95c635efSGarrett D'Amore 		return (mand);
1518*95c635efSGarrett D'Amore 	}
1519*95c635efSGarrett D'Amore 
1520*95c635efSGarrett D'Amore 	/*
1521*95c635efSGarrett D'Amore 	 * No man or share/man directory found
1522*95c635efSGarrett D'Amore 	 */
1523*95c635efSGarrett D'Amore 	free(mand);
1524*95c635efSGarrett D'Amore 	return (NULL);
1525*95c635efSGarrett D'Amore }
1526*95c635efSGarrett D'Amore 
1527*95c635efSGarrett D'Amore /*
1528*95c635efSGarrett D'Amore  * Free a linked list of dupnode structs.
1529*95c635efSGarrett D'Amore  */
1530*95c635efSGarrett D'Amore void
1531*95c635efSGarrett D'Amore free_dupnode(struct dupnode *dnp) {
1532*95c635efSGarrett D'Amore 	struct dupnode *dnp2;
1533*95c635efSGarrett D'Amore 	struct secnode *snp;
1534*95c635efSGarrett D'Amore 
1535*95c635efSGarrett D'Amore 	while (dnp != NULL) {
1536*95c635efSGarrett D'Amore 		dnp2 = dnp;
1537*95c635efSGarrett D'Amore 		dnp = dnp->next;
1538*95c635efSGarrett D'Amore 		while (dnp2->secl != NULL) {
1539*95c635efSGarrett D'Amore 			snp = dnp2->secl;
1540*95c635efSGarrett D'Amore 			dnp2->secl = dnp2->secl->next;
1541*95c635efSGarrett D'Amore 			free(snp->secp);
1542*95c635efSGarrett D'Amore 			free(snp);
1543*95c635efSGarrett D'Amore 		}
1544*95c635efSGarrett D'Amore 		free(dnp2);
1545*95c635efSGarrett D'Amore 	}
1546*95c635efSGarrett D'Amore }
1547*95c635efSGarrett D'Amore 
1548*95c635efSGarrett D'Amore /*
1549*95c635efSGarrett D'Amore  * Print manp linked list to stdout.
1550*95c635efSGarrett D'Amore  */
1551*95c635efSGarrett D'Amore void
1552*95c635efSGarrett D'Amore print_manpath(struct man_node *manp)
1553*95c635efSGarrett D'Amore {
1554*95c635efSGarrett D'Amore 	char	colon[2] = "\0\0";
1555*95c635efSGarrett D'Amore 	char	**secp;
1556*95c635efSGarrett D'Amore 
1557*95c635efSGarrett D'Amore 	for (; manp != NULL; manp = manp->next) {
1558*95c635efSGarrett D'Amore 		(void) printf("%s%s", colon, manp->path);
1559*95c635efSGarrett D'Amore 		colon[0] = ':';
1560*95c635efSGarrett D'Amore 
1561*95c635efSGarrett D'Amore 		/*
1562*95c635efSGarrett D'Amore 		 * If man.cf or a directory scan was used to create section
1563*95c635efSGarrett D'Amore 		 * list, do not print section list again.  If the output of
1564*95c635efSGarrett D'Amore 		 * man -p is used to set MANPATH, subsequent runs of man
1565*95c635efSGarrett D'Amore 		 * will re-read man.cf and/or scan man directories as
1566*95c635efSGarrett D'Amore 		 * required.
1567*95c635efSGarrett D'Amore 		 */
1568*95c635efSGarrett D'Amore 		if (manp->defsrch != 0)
1569*95c635efSGarrett D'Amore 			continue;
1570*95c635efSGarrett D'Amore 
1571*95c635efSGarrett D'Amore 		for (secp = manp->secv; *secp != NULL; secp++) {
1572*95c635efSGarrett D'Amore 			/*
1573*95c635efSGarrett D'Amore 			 * Section deduplication may have eliminated some
1574*95c635efSGarrett D'Amore 			 * sections from the vector. Avoid displaying this
1575*95c635efSGarrett D'Amore 			 * detail which would appear as ",," in output
1576*95c635efSGarrett D'Amore 			 */
1577*95c635efSGarrett D'Amore 			if ((*secp)[0] != '\0')
1578*95c635efSGarrett D'Amore 				(void) printf(",%s", *secp);
1579*95c635efSGarrett D'Amore 		}
1580*95c635efSGarrett D'Amore 	}
1581*95c635efSGarrett D'Amore 	(void) printf("\n");
1582*95c635efSGarrett D'Amore }
1583*95c635efSGarrett D'Amore 
1584*95c635efSGarrett D'Amore static void
1585*95c635efSGarrett D'Amore usage_man(void)
1586*95c635efSGarrett D'Amore {
1587*95c635efSGarrett D'Amore 
1588*95c635efSGarrett D'Amore 	(void) fprintf(stderr, gettext(
1589*95c635efSGarrett D'Amore "usage: man [-alptw] [-M path] [-s section] name ...\n"
1590*95c635efSGarrett D'Amore "       man [-M path] [-s section] -k keyword ...\n"
1591*95c635efSGarrett D'Amore "       man [-M path] [-s section] -f keyword ...\n"));
1592*95c635efSGarrett D'Amore 
1593*95c635efSGarrett D'Amore 	exit(1);
1594*95c635efSGarrett D'Amore }
1595*95c635efSGarrett D'Amore 
1596*95c635efSGarrett D'Amore static void
1597*95c635efSGarrett D'Amore usage_whatapro(void)
1598*95c635efSGarrett D'Amore {
1599*95c635efSGarrett D'Amore 
1600*95c635efSGarrett D'Amore 	(void) fprintf(stderr, gettext(
1601*95c635efSGarrett D'Amore "usage: %s [-M path] [-s section] keyword ...\n"),
1602*95c635efSGarrett D'Amore 	    whatis ? "whatis" : "apropos");
1603*95c635efSGarrett D'Amore 
1604*95c635efSGarrett D'Amore 	exit(1);
1605*95c635efSGarrett D'Amore }
1606*95c635efSGarrett D'Amore 
1607*95c635efSGarrett D'Amore static void
1608*95c635efSGarrett D'Amore usage_catman(void)
1609*95c635efSGarrett D'Amore {
1610*95c635efSGarrett D'Amore 	(void) fprintf(stderr, gettext(
1611*95c635efSGarrett D'Amore "usage: catman [-M path] [-w]\n"));
1612*95c635efSGarrett D'Amore 
1613*95c635efSGarrett D'Amore 	exit(1);
1614*95c635efSGarrett D'Amore }
1615*95c635efSGarrett D'Amore 
1616*95c635efSGarrett D'Amore static void
1617*95c635efSGarrett D'Amore usage_makewhatis(void)
1618*95c635efSGarrett D'Amore {
1619*95c635efSGarrett D'Amore 	(void) fprintf(stderr, gettext("usage: makewhatis\n"));
1620*95c635efSGarrett D'Amore 
1621*95c635efSGarrett D'Amore 	exit(1);
1622*95c635efSGarrett D'Amore }
1623