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