xref: /titanic_50/usr/src/cmd/man/man.c (revision d456640d8a14f0ba35a7ec232f0ce9ebd34541cb)
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  */
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
main(int argc,char ** argv)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 *
build_manpath(char ** pathv,int flags)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
getpath(struct man_node * manp,char ** pv)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
getsect(struct man_node * manp,char ** pv)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 		DPRINTF("-- Adding %s: sections=%s\n", manp->path, mansec);
533 		manp->secv = split(mansec, ',');
534 		for (sectp = manp->secv; *sectp; sectp++)
535 			lower(*sectp);
536 	} else if ((sections = strchr(*pv, ',')) != NULL) {
537 		sections++;
538 		DPRINTF("-- Adding %s: sections=%s\n", manp->path, sections);
539 		manp->secv = split(sections, ',');
540 		for (sectp = manp->secv; *sectp; sectp++)
541 			lower(*sectp);
542 		if (*manp->secv == NULL)
543 			get_all_sect(manp);
544 	} else if ((sections = check_config(*pv)) != NULL) {
545 		manp->defsrch = 1;
546 		DPRINTF("-- Adding %s: sections=%s (from %s)\n", manp->path,
547 		    sections, CONFIG);
548 		manp->secv = split(sections, ',');
549 		for (sectp = manp->secv; *sectp; sectp++)
550 			lower(*sectp);
551 		if (*manp->secv == NULL)
552 			get_all_sect(manp);
553 	} else {
554 		manp->defsrch = 1;
555 		DPRINTF("-- Adding %s: default search order\n", manp->path);
556 		manp->secv = NULL;
557 		get_all_sect(manp);
558 	}
559 }
560 
561 /*
562  * Get suffices of all sub-mandir directories in a mandir.
563  */
564 static void
get_all_sect(struct man_node * manp)565 get_all_sect(struct man_node *manp)
566 {
567 	DIR	*dp;
568 	char	**dirv;
569 	char	**dv;
570 	char	**p;
571 	char	*prev = NULL;
572 	char 	*tmp = NULL;
573 	int	maxentries = MAXTOKENS;
574 	int	entries = 0;
575 
576 	if ((dp = opendir(manp->path)) == 0)
577 		return;
578 
579 	sortdir(dp, &dirv);
580 
581 	(void) closedir(dp);
582 
583 	if (manp->secv == NULL) {
584 		if ((manp->secv = malloc(maxentries * sizeof (char *))) == NULL)
585 			err(1, "malloc");
586 	}
587 
588 	for (dv = dirv, p = manp->secv; *dv; dv++) {
589 		if (strcmp(*dv, CONFIG) == 0) {
590 			free(*dv);
591 			continue;
592 		}
593 
594 		free(tmp);
595 		if ((tmp = strdup(*dv + 3)) == NULL)
596 			err(1, "strdup");
597 
598 		if (prev != NULL && strcmp(prev, tmp) == 0) {
599 			free(*dv);
600 			continue;
601 		}
602 
603 		free(prev);
604 		if ((prev = strdup(*dv + 3)) == NULL)
605 			err(1, "strdup");
606 
607 		if ((*p = strdup(*dv + 3)) == NULL)
608 			err(1, "strdup");
609 
610 		p++; entries++;
611 
612 		if (entries == maxentries) {
613 			maxentries += MAXTOKENS;
614 			if ((manp->secv = realloc(manp->secv,
615 			    sizeof (char *) * maxentries)) == NULL)
616 				err(1, "realloc");
617 			p = manp->secv + entries;
618 		}
619 		free(*dv);
620 	}
621 	free(tmp);
622 	free(prev);
623 	*p = NULL;
624 	free(dirv);
625 }
626 
627 /*
628  * Build whatis databases.
629  */
630 static void
do_makewhatis(struct man_node * manp)631 do_makewhatis(struct man_node *manp)
632 {
633 	struct man_node *p;
634 	char		*ldir;
635 
636 	for (p = manp; p != NULL; p = p->next) {
637 		ldir = addlocale(p->path);
638 		if (*localedir != '\0' && getdirs(ldir, NULL, 0) > 0)
639 			mwpath(ldir);
640 		free(ldir);
641 		mwpath(p->path);
642 	}
643 }
644 
645 /*
646  * Count mandirs under the given manpath
647  */
648 static int
getdirs(char * path,char *** dirv,int flag)649 getdirs(char *path, char ***dirv, int flag)
650 {
651 	DIR		*dp;
652 	struct dirent	*d;
653 	int		n = 0;
654 	int		maxentries = MAXDIRS;
655 	char		**dv = NULL;
656 
657 	if ((dp = opendir(path)) == NULL)
658 		return (0);
659 
660 	if (flag) {
661 		if ((*dirv = malloc(sizeof (char *) *
662 		    maxentries)) == NULL)
663 			err(1, "malloc");
664 		dv = *dirv;
665 	}
666 	while ((d = readdir(dp))) {
667 		if (strncmp(d->d_name, "man", 3) != 0)
668 			continue;
669 		n++;
670 
671 		if (flag) {
672 			if ((*dv = strdup(d->d_name + 3)) == NULL)
673 				err(1, "strdup");
674 			dv++;
675 			if ((dv - *dirv) == maxentries) {
676 				int	entries = maxentries;
677 
678 				maxentries += MAXTOKENS;
679 				if ((*dirv = realloc(*dirv,
680 				    sizeof (char *) * maxentries)) == NULL)
681 					err(1, "realloc");
682 				dv = *dirv + entries;
683 			}
684 		}
685 	}
686 
687 	(void) closedir(dp);
688 	return (n);
689 }
690 
691 
692 /*
693  * Find matching whatis or apropos entries.
694  */
695 static void
whatapro(struct man_node * manp,char * word)696 whatapro(struct man_node *manp, char *word)
697 {
698 	char		whatpath[MAXPATHLEN];
699 	struct man_node *b;
700 	char		*ldir;
701 
702 	for (b = manp; b != NULL; b = b->next) {
703 		if (*localedir != '\0') {
704 			ldir = addlocale(b->path);
705 			if (getdirs(ldir, NULL, 0) != 0) {
706 				(void) snprintf(whatpath, sizeof (whatpath),
707 				    "%s/%s", ldir, WHATIS);
708 				search_whatis(whatpath, word);
709 			}
710 			free(ldir);
711 		}
712 		(void) snprintf(whatpath, sizeof (whatpath), "%s/%s", b->path,
713 		    WHATIS);
714 		search_whatis(whatpath, word);
715 	}
716 }
717 
718 static void
search_whatis(char * whatpath,char * word)719 search_whatis(char *whatpath, char *word)
720 {
721 	FILE		*fp;
722 	char		*line = NULL;
723 	size_t		linecap = 0;
724 	char		*pkwd;
725 	regex_t		preg;
726 	char		**ss = NULL;
727 	char		s[MAXNAMELEN];
728 	int		i;
729 
730 	if ((fp = fopen(whatpath, "r")) == NULL) {
731 		perror(whatpath);
732 		return;
733 	}
734 
735 	DPRINTF("-- Found %s: %s\n", WHATIS, whatpath);
736 
737 	/* Build keyword regex */
738 	if (asprintf(&pkwd, "%s%s%s", (whatis) ? "\\<" : "",
739 	    word, (whatis) ? "\\>" : "") == -1)
740 		err(1, "asprintf");
741 
742 	if (regcomp(&preg, pkwd, REG_BASIC | REG_ICASE | REG_NOSUB) != 0)
743 		err(1, "regcomp");
744 
745 	if (sargs)
746 		ss = split(mansec, ',');
747 
748 	while (getline(&line, &linecap, fp) > 0) {
749 		if (regexec(&preg, line, 0, NULL, 0) == 0) {
750 			if (sargs) {
751 				/* Section-restricted search */
752 				for (i = 0; ss[i] != NULL; i++) {
753 					(void) snprintf(s, sizeof (s), "(%s)",
754 					    ss[i]);
755 					if (strstr(line, s) != NULL) {
756 						(void) printf("%s", line);
757 						break;
758 					}
759 				}
760 			} else {
761 				(void) printf("%s", line);
762 			}
763 		}
764 	}
765 
766 	if (ss != NULL)
767 		freev(ss);
768 	free(pkwd);
769 	(void) fclose(fp);
770 }
771 
772 
773 /*
774  * Split a string by specified separator.
775  */
776 static char **
split(char * s1,char sep)777 split(char *s1, char sep)
778 {
779 	char	**tokv, **vp;
780 	char	*mp = s1, *tp;
781 	int	maxentries = MAXTOKENS;
782 	int	entries = 0;
783 
784 	if ((tokv = vp = malloc(maxentries * sizeof (char *))) == NULL)
785 		err(1, "malloc");
786 
787 	for (; mp && *mp; mp = tp) {
788 		tp = strchr(mp, sep);
789 		if (mp == tp) {
790 			tp++;
791 			continue;
792 		}
793 		if (tp) {
794 			size_t	len;
795 
796 			len = tp - mp;
797 			if ((*vp = (char *)malloc(sizeof (char) *
798 			    len + 1)) == NULL)
799 				err(1, "malloc");
800 			(void) strncpy(*vp, mp, len);
801 			*(*vp + len) = '\0';
802 			tp++;
803 			vp++;
804 		} else {
805 			if ((*vp = strdup(mp)) == NULL)
806 				err(1, "strdup");
807 			vp++;
808 		}
809 		entries++;
810 		if (entries == maxentries) {
811 			maxentries += MAXTOKENS;
812 			if ((tokv = realloc(tokv,
813 			    maxentries * sizeof (char *))) == NULL)
814 				err(1, "realloc");
815 			vp = tokv + entries;
816 		}
817 	}
818 	*vp = 0;
819 
820 	return (tokv);
821 }
822 
823 /*
824  * Free a vector allocated by split()
825  */
826 static void
freev(char ** v)827 freev(char **v)
828 {
829 	int i;
830 	if (v != NULL) {
831 		for (i = 0; v[i] != NULL; i++) {
832 			free(v[i]);
833 		}
834 		free(v);
835 	}
836 }
837 
838 /*
839  * Convert paths to full paths if necessary
840  */
841 static void
fullpaths(struct man_node ** manp_head)842 fullpaths(struct man_node **manp_head)
843 {
844 	char		*cwd = NULL;
845 	char		*p;
846 	int		cwd_gotten = 0;
847 	struct man_node *manp = *manp_head;
848 	struct man_node *b;
849 	struct man_node *prev = NULL;
850 
851 	for (b = manp; b != NULL; b = b->next) {
852 		if (*(b->path) == '/') {
853 			prev = b;
854 			continue;
855 		}
856 
857 		if (!cwd_gotten) {
858 			cwd = getcwd(NULL, MAXPATHLEN);
859 			cwd_gotten = 1;
860 		}
861 
862 		if (cwd) {
863 			/* Relative manpath with cwd: make absolute */
864 			if (asprintf(&p, "%s/%s", cwd, b->path) == -1)
865 				err(1, "asprintf");
866 			free(b->path);
867 			b->path = p;
868 		} else {
869 			/* Relative manpath but no cwd: omit path entry */
870 			if (prev)
871 				prev->next = b->next;
872 			else
873 				*manp_head = b->next;
874 
875 			free_manp(b);
876 		}
877 	}
878 	free(cwd);
879 }
880 
881 /*
882  * Free a man_node structure and its contents
883  */
884 static void
free_manp(struct man_node * manp)885 free_manp(struct man_node *manp)
886 {
887 	char	**p;
888 
889 	free(manp->path);
890 	p = manp->secv;
891 	while ((p != NULL) && (*p != NULL)) {
892 		free(*p);
893 		p++;
894 	}
895 	free(manp->secv);
896 	free(manp);
897 }
898 
899 
900 /*
901  * Map (in place) to lower case.
902  */
903 static void
lower(char * s)904 lower(char *s)
905 {
906 
907 	if (s == 0)
908 		return;
909 	while (*s) {
910 		if (isupper(*s))
911 			*s = tolower(*s);
912 		s++;
913 	}
914 }
915 
916 
917 /*
918  * Compare function for qsort().
919  * Sort first by section, then by prefix.
920  */
921 static int
cmp(const void * arg1,const void * arg2)922 cmp(const void *arg1, const void *arg2)
923 {
924 	int	n;
925 	char	**p1 = (char **)arg1;
926 	char	**p2 = (char **)arg2;
927 
928 	/* By section */
929 	if ((n = strcmp(*p1 + 3, *p2 + 3)) != 0)
930 		return (n);
931 
932 	/* By prefix reversed */
933 	return (strncmp(*p2, *p1, 3));
934 }
935 
936 
937 /*
938  * Find a manpage.
939  */
940 static int
manual(struct man_node * manp,char * name)941 manual(struct man_node *manp, char *name)
942 {
943 	struct man_node *p;
944 	struct man_node *local;
945 	int		ndirs = 0;
946 	char		*ldir;
947 	char		*ldirs[2];
948 	char		*fullname = name;
949 	char		*slash;
950 
951 	if ((slash = strrchr(name, '/')) != NULL)
952 		name = slash + 1;
953 
954 	/* For each path in MANPATH */
955 	found = 0;
956 
957 	for (p = manp; p != NULL; p = p->next) {
958 		DPRINTF("-- Searching mandir: %s\n", p->path);
959 
960 		if (*localedir != '\0') {
961 			ldir = addlocale(p->path);
962 			ndirs = getdirs(ldir, NULL, 0);
963 			if (ndirs != 0) {
964 				ldirs[0] = ldir;
965 				ldirs[1] = NULL;
966 				local = build_manpath(ldirs, 0);
967 				DPRINTF("-- Locale specific subdir: %s\n",
968 				    ldir);
969 				mandir(local->secv, ldir, name, 1);
970 				free_manp(local);
971 			}
972 			free(ldir);
973 		}
974 
975 		/*
976 		 * Locale mandir not valid, man page in locale
977 		 * mandir not found, or -a option present
978 		 */
979 		if (ndirs == 0 || !found || all)
980 			mandir(p->secv, p->path, name, 0);
981 
982 		if (found && !all)
983 			break;
984 	}
985 
986 	if (!found) {
987 		if (sargs) {
988 			(void) fprintf(stderr, gettext(
989 			    "No manual entry for %s in section(s) %s\n"),
990 			    fullname, mansec);
991 		} else {
992 			(void) fprintf(stderr,
993 			    gettext("No manual entry for %s\n"), fullname);
994 		}
995 
996 	}
997 
998 	return (!found);
999 }
1000 
1001 
1002 /*
1003  * For a specified manual directory, read, store and sort section subdirs.
1004  * For each section specified, find and search matching subdirs.
1005  */
1006 static void
mandir(char ** secv,char * path,char * name,int lspec)1007 mandir(char **secv, char *path, char *name, int lspec)
1008 {
1009 	DIR	*dp;
1010 	char	**dirv;
1011 	char	**dv, **pdv;
1012 	int	len, dslen;
1013 
1014 	if ((dp = opendir(path)) == NULL)
1015 		return;
1016 
1017 	if (lspec)
1018 		DPRINTF("-- Searching mandir: %s\n", path);
1019 
1020 	sortdir(dp, &dirv);
1021 
1022 	/* Search in the order specified by MANSECTS */
1023 	for (; *secv; secv++) {
1024 		len = strlen(*secv);
1025 		for (dv = dirv; *dv; dv++) {
1026 			dslen = strlen(*dv + 3);
1027 			if (dslen > len)
1028 				len = dslen;
1029 			if (**secv == '\\') {
1030 				if (strcmp(*secv + 1, *dv + 3) != 0)
1031 					continue;
1032 			} else if (strncasecmp(*secv, *dv + 3, len) != 0) {
1033 				if (!all &&
1034 				    (newsection = map_section(*secv, path))
1035 				    == NULL) {
1036 					continue;
1037 				}
1038 				if (newsection == NULL)
1039 					newsection = "";
1040 				if (strncmp(newsection, *dv + 3, len) != 0) {
1041 					continue;
1042 				}
1043 			}
1044 
1045 			if (searchdir(path, *dv, name) == 0)
1046 				continue;
1047 
1048 			if (!all) {
1049 				pdv = dirv;
1050 				while (*pdv) {
1051 					free(*pdv);
1052 					pdv++;
1053 				}
1054 				(void) closedir(dp);
1055 				free(dirv);
1056 				return;
1057 			}
1058 
1059 			if (all && **dv == 'm' && *(dv + 1) &&
1060 			    strcmp(*(dv + 1) + 3, *dv + 3) == 0)
1061 					dv++;
1062 		}
1063 	}
1064 	pdv = dirv;
1065 	while (*pdv != NULL) {
1066 		free(*pdv);
1067 		pdv++;
1068 	}
1069 	free(dirv);
1070 	(void) closedir(dp);
1071 }
1072 
1073 /*
1074  * Sort directories.
1075  */
1076 static void
sortdir(DIR * dp,char *** dirv)1077 sortdir(DIR *dp, char ***dirv)
1078 {
1079 	struct dirent	*d;
1080 	char		**dv;
1081 	int		maxentries = MAXDIRS;
1082 	int		entries = 0;
1083 
1084 	if ((dv = *dirv = malloc(sizeof (char *) *
1085 	    maxentries)) == NULL)
1086 		err(1, "malloc");
1087 	dv = *dirv;
1088 
1089 	while ((d = readdir(dp))) {
1090 		if (strcmp(d->d_name, ".") == 0 ||
1091 		    strcmp(d->d_name, "..") == 0)
1092 			continue;
1093 
1094 		if (strncmp(d->d_name, "man", 3) == 0 ||
1095 		    strncmp(d->d_name, "cat", 3) == 0) {
1096 			if ((*dv = strdup(d->d_name)) == NULL)
1097 				err(1, "strdup");
1098 			dv++;
1099 			entries++;
1100 			if (entries == maxentries) {
1101 				maxentries += MAXDIRS;
1102 				if ((*dirv = realloc(*dirv,
1103 				    sizeof (char *) * maxentries)) == NULL)
1104 					err(1, "realloc");
1105 				dv = *dirv + entries;
1106 			}
1107 		}
1108 	}
1109 	*dv = 0;
1110 
1111 	qsort((void *)*dirv, dv - *dirv, sizeof (char *), cmp);
1112 
1113 }
1114 
1115 
1116 /*
1117  * Search a section subdir for a given manpage.
1118  */
1119 static int
searchdir(char * path,char * dir,char * name)1120 searchdir(char *path, char *dir, char *name)
1121 {
1122 	DIR		*sdp;
1123 	struct dirent	*sd;
1124 	char		sectpath[MAXPATHLEN];
1125 	char		file[MAXNAMLEN];
1126 	char		dname[MAXPATHLEN];
1127 	char		*last;
1128 	int		nlen;
1129 
1130 	(void) snprintf(sectpath, sizeof (sectpath), "%s/%s", path, dir);
1131 	(void) snprintf(file, sizeof (file), "%s.", name);
1132 
1133 	if ((sdp = opendir(sectpath)) == NULL)
1134 		return (0);
1135 
1136 	while ((sd = readdir(sdp))) {
1137 		char	*pname;
1138 
1139 		if ((pname = strdup(sd->d_name)) == NULL)
1140 			err(1, "strdup");
1141 		if ((last = strrchr(pname, '.')) != NULL &&
1142 		    (strcmp(last, ".gz") == 0 || strcmp(last, ".bz2") == 0))
1143 			*last = '\0';
1144 		last = strrchr(pname, '.');
1145 		nlen = last - pname;
1146 		(void) snprintf(dname, sizeof (dname), "%.*s.", nlen, pname);
1147 		if (strcmp(dname, file) == 0 ||
1148 		    strcmp(pname, name) == 0) {
1149 			(void) format(path, dir, name, sd->d_name);
1150 			(void) closedir(sdp);
1151 			free(pname);
1152 			return (1);
1153 		}
1154 		free(pname);
1155 	}
1156 	(void) closedir(sdp);
1157 
1158 	return (0);
1159 }
1160 
1161 /*
1162  * Check the hash table of old directory names to see if there is a
1163  * new directory name.
1164  */
1165 static char *
map_section(char * section,char * path)1166 map_section(char *section, char *path)
1167 {
1168 	int	i;
1169 	char	fullpath[MAXPATHLEN];
1170 
1171 	if (list)  /* -l option fall through */
1172 		return (NULL);
1173 
1174 	for (i = 0; map[i].new_name != NULL; i++) {
1175 		if (strcmp(section, map[i].old_name) == 0) {
1176 			(void) snprintf(fullpath, sizeof (fullpath),
1177 			    "%s/man%s", path, map[i].new_name);
1178 			if (!access(fullpath, R_OK | X_OK)) {
1179 				return (map[i].new_name);
1180 			} else {
1181 				return (NULL);
1182 			}
1183 		}
1184 	}
1185 
1186 	return (NULL);
1187 }
1188 
1189 /*
1190  * Format the manpage.
1191  */
1192 static int
format(char * path,char * dir,char * name,char * pg)1193 format(char *path, char *dir, char *name, char *pg)
1194 {
1195 	char		manpname[MAXPATHLEN], catpname[MAXPATHLEN];
1196 	char		cmdbuf[BUFSIZ], tmpbuf[BUFSIZ];
1197 	char		*cattool;
1198 	struct stat	sbman, sbcat;
1199 
1200 	found++;
1201 
1202 	if (list) {
1203 		(void) printf(gettext("%s(%s)\t-M %s\n"), name, dir + 3, path);
1204 		return (-1);
1205 	}
1206 
1207 	(void) snprintf(manpname, sizeof (manpname), "%s/man%s/%s", path,
1208 	    dir + 3, pg);
1209 	(void) snprintf(catpname, sizeof (catpname), "%s/cat%s/%s", path,
1210 	    dir + 3, pg);
1211 
1212 	/* Can't do PS output if manpage doesn't exist */
1213 	if (stat(manpname, &sbman) != 0 && (psoutput|lintout))
1214 		return (-1);
1215 
1216 	/*
1217 	 * If both manpage and catpage do not exist, manpname is
1218 	 * broken symlink, most likely.
1219 	 */
1220 	if (stat(catpname, &sbcat) != 0 && stat(manpname, &sbman) != 0)
1221 		err(1, "%s", manpname);
1222 
1223 	/* Setup cattool */
1224 	if (fnmatch("*.gz", manpname, 0) == 0)
1225 		cattool = "gzcat";
1226 	else if (fnmatch("*.bz2", manpname, 0) == 0)
1227 		cattool = "bzcat";
1228 	else
1229 		cattool = "cat";
1230 
1231 	if (psoutput) {
1232 		(void) snprintf(cmdbuf, BUFSIZ,
1233 		    "cd %s; %s %s | mandoc -Tps | lp -Tpostscript",
1234 		    path, cattool, manpname);
1235 		DPRINTF("-- Using manpage: %s\n", manpname);
1236 		goto cmd;
1237 	} else if (lintout) {
1238 		(void) snprintf(cmdbuf, BUFSIZ,
1239 		    "cd %s; %s %s | mandoc -Tlint",
1240 		    path, cattool, manpname);
1241 		DPRINTF("-- Linting manpage: %s\n", manpname);
1242 		goto cmd;
1243 	}
1244 
1245 	/*
1246 	 * Output catpage if:
1247 	 * - manpage doesn't exist
1248 	 * - output width is standard and catpage is recent enough
1249 	 */
1250 	if (stat(manpname, &sbman) != 0 || (manwidth == 0 &&
1251 	    stat(catpname, &sbcat) == 0 && sbcat.st_mtime >= sbman.st_mtime)) {
1252 		DPRINTF("-- Using catpage: %s\n", catpname);
1253 		(void) snprintf(cmdbuf, BUFSIZ, "%s %s", pager, catpname);
1254 		goto cmd;
1255 	}
1256 
1257 	DPRINTF("-- Using manpage: %s\n", manpname);
1258 	if (manwidth > 0)
1259 		(void) snprintf(tmpbuf, BUFSIZ, "-Owidth=%d ", manwidth);
1260 	(void) snprintf(cmdbuf, BUFSIZ, "cd %s; %s %s | mandoc %s| %s",
1261 	    path, cattool, manpname, (manwidth > 0) ? tmpbuf : "", pager);
1262 
1263 cmd:
1264 	DPRINTF("-- Command: %s\n", cmdbuf);
1265 
1266 	if (!debug)
1267 		return (system(cmdbuf) == 0);
1268 	else
1269 		return (0);
1270 }
1271 
1272 /*
1273  * Add <localedir> to the path.
1274  */
1275 static char *
addlocale(char * path)1276 addlocale(char *path)
1277 {
1278 	char	*tmp;
1279 
1280 	if (asprintf(&tmp, "%s/%s", path, localedir) == -1)
1281 		err(1, "asprintf");
1282 
1283 	return (tmp);
1284 }
1285 
1286 /*
1287  * Get the order of sections from man.cf.
1288  */
1289 static char *
check_config(char * path)1290 check_config(char *path)
1291 {
1292 	FILE		*fp;
1293 	char		*rc = NULL;
1294 	char		*sect = NULL;
1295 	char		fname[MAXPATHLEN];
1296 	char		*line = NULL;
1297 	char		*nl;
1298 	size_t		linecap = 0;
1299 
1300 	(void) snprintf(fname, MAXPATHLEN, "%s/%s", path, CONFIG);
1301 
1302 	if ((fp = fopen(fname, "r")) == NULL)
1303 		return (NULL);
1304 
1305 	while (getline(&line, &linecap, fp) > 0) {
1306 		if ((rc = strstr(line, "MANSECTS=")) != NULL)
1307 			break;
1308 	}
1309 
1310 	(void) fclose(fp);
1311 
1312 	if (rc != NULL) {
1313 		if ((nl = strchr(rc, '\n')) != NULL)
1314 			*nl = '\0';
1315 		sect = strchr(rc, '=') + 1;
1316 	}
1317 
1318 	return (sect);
1319 }
1320 
1321 /*
1322  * Initialize the bintoman array with appropriate device and inode info.
1323  */
1324 static void
init_bintoman(void)1325 init_bintoman(void)
1326 {
1327 	int i;
1328 	struct stat sb;
1329 
1330 	for (i = 0; bintoman[i].bindir != NULL; i++) {
1331 		if (stat(bintoman[i].bindir, &sb) == 0) {
1332 			bintoman[i].dev = sb.st_dev;
1333 			bintoman[i].ino = sb.st_ino;
1334 		} else {
1335 			bintoman[i].dev = NODEV;
1336 		}
1337 	}
1338 }
1339 
1340 /*
1341  * If a duplicate is found, return 1.
1342  * If a duplicate is not found, add it to the dupnode list and return 0.
1343  */
1344 static int
dupcheck(struct man_node * mnp,struct dupnode ** dnp)1345 dupcheck(struct man_node *mnp, struct dupnode **dnp)
1346 {
1347 	struct dupnode	*curdnp;
1348 	struct secnode	*cursnp;
1349 	struct stat 	sb;
1350 	int 		i;
1351 	int		rv = 1;
1352 	int		dupfound;
1353 
1354 	/* If the path doesn't exist, treat it as a duplicate */
1355 	if (stat(mnp->path, &sb) != 0)
1356 		return (1);
1357 
1358 	/* If no sections were found in the man dir, treat it as duplicate */
1359 	if (mnp->secv == NULL)
1360 		return (1);
1361 
1362 	/*
1363 	 * Find the dupnode structure for the previous time this directory
1364 	 * was looked at.  Device and inode numbers are compared so that
1365 	 * directories that are reached via different paths (e.g. /usr/man and
1366 	 * /usr/share/man) are treated as equivalent.
1367 	 */
1368 	for (curdnp = *dnp; curdnp != NULL; curdnp = curdnp->next) {
1369 		if (curdnp->dev == sb.st_dev && curdnp->ino == sb.st_ino)
1370 			break;
1371 	}
1372 
1373 	/*
1374 	 * First time this directory has been seen. Add a new node to the
1375 	 * head of the list. Since all entries are guaranteed to be unique
1376 	 * copy all sections to new node.
1377 	 */
1378 	if (curdnp == NULL) {
1379 		if ((curdnp = calloc(1, sizeof (struct dupnode))) == NULL)
1380 			err(1, "calloc");
1381 		for (i = 0; mnp->secv[i] != NULL; i++) {
1382 			if ((cursnp = calloc(1, sizeof (struct secnode)))
1383 			    == NULL)
1384 				err(1, "calloc");
1385 			cursnp->next = curdnp->secl;
1386 			curdnp->secl = cursnp;
1387 			if ((cursnp->secp = strdup(mnp->secv[i])) == NULL)
1388 				err(1, "strdup");
1389 		}
1390 		curdnp->dev = sb.st_dev;
1391 		curdnp->ino = sb.st_ino;
1392 		curdnp->next = *dnp;
1393 		*dnp = curdnp;
1394 		return (0);
1395 	}
1396 
1397 	/*
1398 	 * Traverse the section vector in the man_node and the section list
1399 	 * in dupnode cache to eliminate all duplicates from man_node.
1400 	 */
1401 	for (i = 0; mnp->secv[i] != NULL; i++) {
1402 		dupfound = 0;
1403 		for (cursnp = curdnp->secl; cursnp != NULL;
1404 		    cursnp = cursnp->next) {
1405 			if (strcmp(mnp->secv[i], cursnp->secp) == 0) {
1406 				dupfound = 1;
1407 				break;
1408 			}
1409 		}
1410 		if (dupfound) {
1411 			mnp->secv[i][0] = '\0';
1412 			continue;
1413 		}
1414 
1415 
1416 		/*
1417 		 * Update curdnp and set return value to indicate that this
1418 		 * was not all duplicates.
1419 		 */
1420 		if ((cursnp = calloc(1, sizeof (struct secnode))) == NULL)
1421 			err(1, "calloc");
1422 		cursnp->next = curdnp->secl;
1423 		curdnp->secl = cursnp;
1424 		if ((cursnp->secp = strdup(mnp->secv[i])) == NULL)
1425 			err(1, "strdup");
1426 		rv = 0;
1427 	}
1428 
1429 	return (rv);
1430 }
1431 
1432 /*
1433  * Given a bindir, return corresponding mandir.
1434  */
1435 static char *
path_to_manpath(char * bindir)1436 path_to_manpath(char *bindir)
1437 {
1438 	char		*mand, *p;
1439 	int		i;
1440 	struct stat	sb;
1441 
1442 	/* First look for known translations for specific bin paths */
1443 	if (stat(bindir, &sb) != 0) {
1444 		return (NULL);
1445 	}
1446 	for (i = 0; bintoman[i].bindir != NULL; i++) {
1447 		if (sb.st_dev == bintoman[i].dev &&
1448 		    sb.st_ino == bintoman[i].ino) {
1449 			if ((mand = strdup(bintoman[i].mandir)) == NULL)
1450 				err(1, "strdup");
1451 			if ((p = strchr(mand, ',')) != NULL)
1452 				*p = '\0';
1453 			if (stat(mand, &sb) != 0) {
1454 				free(mand);
1455 				return (NULL);
1456 			}
1457 			if (p != NULL)
1458 				*p = ',';
1459 			return (mand);
1460 		}
1461 	}
1462 
1463 	/*
1464 	 * No specific translation found.  Try `dirname $bindir`/share/man
1465 	 * and `dirname $bindir`/man
1466 	 */
1467 	if ((mand = malloc(MAXPATHLEN)) == NULL)
1468 		err(1, "malloc");
1469 	if (strlcpy(mand, bindir, MAXPATHLEN) >= MAXPATHLEN) {
1470 		free(mand);
1471 		return (NULL);
1472 	}
1473 
1474 	/*
1475 	 * Advance to end of buffer, strip trailing /'s then remove last
1476 	 * directory component.
1477 	 */
1478 	for (p = mand; *p != '\0'; p++)
1479 		;
1480 	for (; p > mand && *p == '/'; p--)
1481 		;
1482 	for (; p > mand && *p != '/'; p--)
1483 		;
1484 	if (p == mand && *p == '.') {
1485 		if (realpath("..", mand) == NULL) {
1486 			free(mand);
1487 			return (NULL);
1488 		}
1489 		for (; *p != '\0'; p++)
1490 			;
1491 	} else {
1492 		*p = '\0';
1493 	}
1494 
1495 	if (strlcat(mand, "/share/man", MAXPATHLEN) >= MAXPATHLEN) {
1496 		free(mand);
1497 		return (NULL);
1498 	}
1499 
1500 	if ((stat(mand, &sb) == 0) && S_ISDIR(sb.st_mode)) {
1501 		return (mand);
1502 	}
1503 
1504 	/*
1505 	 * Strip the /share/man off and try /man
1506 	 */
1507 	*p = '\0';
1508 	if (strlcat(mand, "/man", MAXPATHLEN) >= MAXPATHLEN) {
1509 		free(mand);
1510 		return (NULL);
1511 	}
1512 	if ((stat(mand, &sb) == 0) && S_ISDIR(sb.st_mode)) {
1513 		return (mand);
1514 	}
1515 
1516 	/*
1517 	 * No man or share/man directory found
1518 	 */
1519 	free(mand);
1520 	return (NULL);
1521 }
1522 
1523 /*
1524  * Free a linked list of dupnode structs.
1525  */
1526 void
free_dupnode(struct dupnode * dnp)1527 free_dupnode(struct dupnode *dnp)
1528 {
1529 	struct dupnode *dnp2;
1530 	struct secnode *snp;
1531 
1532 	while (dnp != NULL) {
1533 		dnp2 = dnp;
1534 		dnp = dnp->next;
1535 		while (dnp2->secl != NULL) {
1536 			snp = dnp2->secl;
1537 			dnp2->secl = dnp2->secl->next;
1538 			free(snp->secp);
1539 			free(snp);
1540 		}
1541 		free(dnp2);
1542 	}
1543 }
1544 
1545 /*
1546  * Print manp linked list to stdout.
1547  */
1548 void
print_manpath(struct man_node * manp)1549 print_manpath(struct man_node *manp)
1550 {
1551 	char	colon[2] = "\0\0";
1552 	char	**secp;
1553 
1554 	for (; manp != NULL; manp = manp->next) {
1555 		(void) printf("%s%s", colon, manp->path);
1556 		colon[0] = ':';
1557 
1558 		/*
1559 		 * If man.cf or a directory scan was used to create section
1560 		 * list, do not print section list again.  If the output of
1561 		 * man -p is used to set MANPATH, subsequent runs of man
1562 		 * will re-read man.cf and/or scan man directories as
1563 		 * required.
1564 		 */
1565 		if (manp->defsrch != 0)
1566 			continue;
1567 
1568 		for (secp = manp->secv; *secp != NULL; secp++) {
1569 			/*
1570 			 * Section deduplication may have eliminated some
1571 			 * sections from the vector. Avoid displaying this
1572 			 * detail which would appear as ",," in output
1573 			 */
1574 			if ((*secp)[0] != '\0')
1575 				(void) printf(",%s", *secp);
1576 		}
1577 	}
1578 	(void) printf("\n");
1579 }
1580 
1581 static void
usage_man(void)1582 usage_man(void)
1583 {
1584 
1585 	(void) fprintf(stderr, gettext(
1586 "usage: man [-alptw] [-M path] [-s section] name ...\n"
1587 "       man [-M path] [-s section] -k keyword ...\n"
1588 "       man [-M path] [-s section] -f keyword ...\n"));
1589 
1590 	exit(1);
1591 }
1592 
1593 static void
usage_whatapro(void)1594 usage_whatapro(void)
1595 {
1596 
1597 	(void) fprintf(stderr, gettext(
1598 "usage: %s [-M path] [-s section] keyword ...\n"),
1599 	    whatis ? "whatis" : "apropos");
1600 
1601 	exit(1);
1602 }
1603 
1604 static void
usage_catman(void)1605 usage_catman(void)
1606 {
1607 	(void) fprintf(stderr, gettext(
1608 "usage: catman [-M path] [-w]\n"));
1609 
1610 	exit(1);
1611 }
1612 
1613 static void
usage_makewhatis(void)1614 usage_makewhatis(void)
1615 {
1616 	(void) fprintf(stderr, gettext("usage: makewhatis\n"));
1617 
1618 	exit(1);
1619 }
1620