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