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