xref: /onnv-gate/usr/src/cmd/man/src/man.c (revision 13142:f04a293f3233)
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  * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989  AT&T.	*/
26 /*		All rights reserved.					*/
27 
28 /*
29  * University Copyright- Copyright (c) 1982, 1986, 1988
30  * The Regents of the University of California
31  * All Rights Reserved
32  *
33  * University Acknowledgment- Portions of this document are derived from
34  * software developed by the University of California, Berkeley, and its
35  * contributors.
36  */
37 
38 
39 /*
40  * man
41  * links to apropos, whatis, and catman
42  * This version uses more for underlining and paging.
43  */
44 
45 #include <stdio.h>
46 #include <ctype.h>
47 #include <sgtty.h>
48 #include <sys/param.h>
49 #include <sys/types.h>
50 #include <sys/stat.h>
51 #include <signal.h>
52 #include <string.h>
53 #include <malloc.h>
54 #include <dirent.h>
55 #include <errno.h>
56 #include <fcntl.h>
57 #include <locale.h>
58 #include <stdlib.h>
59 #include <unistd.h>
60 #include <memory.h>
61 #include <limits.h>
62 #include <wchar.h>
63 
64 #define	MACROF 	"tmac.an"		/* name of <locale> macro file */
65 #define	TMAC_AN	"-man"		/* default macro file */
66 
67 /*
68  * The default search path for man subtrees.
69  */
70 
71 #define	MANDIR		"/usr/share/man" 	/* default mandir */
72 #define	MAKEWHATIS	"/usr/lib/makewhatis"
73 #define	WHATIS		"windex"
74 #define	TEMPLATE	"/tmp/mpXXXXXX"
75 #define	CONFIG		"man.cf"
76 
77 /*
78  * Names for formatting and display programs.  The values given
79  * below are reasonable defaults, but sites with source may
80  * wish to modify them to match the local environment.  The
81  * value for TCAT is particularly problematic as there's no
82  * accepted standard value available for it.  (The definition
83  * below assumes C.A.T. troff output and prints it).
84  */
85 
86 #define	MORE	"more -s" 		/* default paging filter */
87 #define	CAT_S	"/usr/bin/cat -s"	/* for '-' opt (no more) */
88 #define	CAT_	"/usr/bin/cat"		/* for when output is not a tty */
89 #define	TROFF	"troff"			/* local name for troff */
90 #define	TCAT	"lp -c -T troff"	/* command to "display" troff output */
91 
92 #define	SOLIMIT		10	/* maximum allowed .so chain length */
93 #define	MAXDIRS		128	/* max # of subdirs per manpath */
94 #define	MAXPAGES	128	/* max # for multiple pages */
95 #define	PLEN		3	/* prefix length {man, cat, fmt} */
96 #define	TMPLEN		7	/* length of tmpfile prefix */
97 #define	MAXTOKENS 	64
98 
99 #define	DOT_SO		".so "
100 #define	PREPROC_SPEC	"'\\\" "
101 
102 #define	DPRINTF		if (debug && !catmando) \
103 				(void) printf
104 
105 #define	sys(s)		(debug ? ((void)puts(s), 0) : system(s))
106 #define	eq(a, b)	(strcmp(a, b) == 0)
107 #define	match(a, b, c)	(strncmp(a, b, c) == 0)
108 
109 #define	ISDIR(A)	((A.st_mode & S_IFMT) == S_IFDIR)
110 
111 #define	SROFF_CMD	"/usr/lib/sgml/sgml2roff" /* sgml converter */
112 #define	MANDIRNAME	"man"			  /* man directory */
113 #define	SGMLDIR		"sman"			  /* sman directory */
114 #define	SGML_SYMBOL	"<!DOCTYPE"	/* a sgml file should contain this */
115 #define	SGML_SYMBOL_LEN		9	/* length of SGML_SYMBOL */
116 
117 /*
118  * Directory mapping of old directories to new directories
119  */
120 
121 typedef struct {
122 	char *old_name;
123 	char *new_name;
124 } map_entry;
125 
126 static const map_entry map[] = {
127 					{ "3b", "3ucb" },
128 					{ "3e", "3elf" },
129 					{ "3g", "3gen" },
130 					{ "3k", "3kstat" },
131 					{ "3n", "3socket" },
132 					{ "3r", "3rt" },
133 					{ "3s", "3c" },
134 					{ "3t", "3thr" },
135 					{ "3x", "3curses" },
136 					{ "3xc", "3xcurses" },
137 					{ "3xn", "3xnet" }
138 };
139 
140 /*
141  * A list of known preprocessors to precede the formatter itself
142  * in the formatting pipeline.  Preprocessors are specified by
143  * starting a manual page with a line of the form:
144  *	'\" X
145  * where X is a string consisting of letters from the p_tag fields
146  * below.
147  */
148 static const struct preprocessor {
149 	char	p_tag;
150 	char	*p_nroff,
151 		*p_troff,
152 		*p_stdin_char;
153 } preprocessors [] = {
154 	{'c',	"cw",				"cw",		"-"},
155 	{'e',	"neqn /usr/share/lib/pub/eqnchar",
156 			"eqn /usr/share/lib/pub/eqnchar",	"-"},
157 	{'p',	"gpic",				"gpic",		"-"},
158 	{'r',	"refer",			"refer",	"-"},
159 	{'t',	"tbl",				"tbl",		""},
160 	{'v',	"vgrind -f",			"vgrind -f",	"-"},
161 	{0,	0,				0,		0}
162 };
163 
164 struct suffix {
165 	char *ds;
166 	char *fs;
167 };
168 
169 /*
170  * Flags that control behavior of build_manpath()
171  *
172  *   BMP_ISPATH 	pathv is a vector constructed from PATH.
173  *                	Perform appropriate path translations for
174  * 			manpath.
175  *   BMP_APPEND_MANDIR	Add /usr/share/man to the end if it
176  *			hasn't already appeared earlier.
177  *   BMP_FALLBACK_MANDIR Append /usr/share/man only if no other
178  *			manpath (including derived from PATH)
179  * 			elements are valid.
180  */
181 #define	BMP_ISPATH		1
182 #define	BMP_APPEND_MANDIR	2
183 #define	BMP_FALLBACK_MANDIR	4
184 
185 /*
186  * When doing equality comparisons of directories, device and inode
187  * comparisons are done.  The dupsec and dupnode structures are used
188  * to form a list of lists for this processing.
189  */
190 struct secnode {
191 	char		*secp;
192 	struct secnode	*next;
193 };
194 struct dupnode {
195 	dev_t		dev;	/* from struct stat st_dev */
196 	ino_t		ino;	/* from struct stat st_ino */
197 	struct secnode	*secl;	/* sections already considered */
198 	struct dupnode	*next;
199 };
200 
201 /*
202  * Map directories that may appear in PATH to the corresponding
203  * man directory
204  */
205 static struct pathmap {
206 	char	*bindir;
207 	char	*mandir;
208 	dev_t	dev;
209 	ino_t	ino;
210 } bintoman[] = {
211 	{"/sbin",		"/usr/share/man,1m",			0, 0},
212 	{"/usr/sbin",		"/usr/share/man,1m",			0, 0},
213 	{"/usr/ucb",		"/usr/share/man,1b",			0, 0},
214 	{"/usr/bin/X11",	"/usr/X11/share/man",			0, 0},
215 	/*
216 	 * Restrict to section 1 so that whatis /usr/{,xpg4,xpg6}/bin/ls
217 	 * does not confuse users with section 1 and 1b
218 	 */
219 	{"/usr/bin",		"/usr/share/man,1,1m,1s,1t,1c", 	0, 0},
220 	{"/usr/xpg4/bin",	"/usr/share/man,1",			0, 0},
221 	{"/usr/xpg6/bin",	"/usr/share/man,1",			0, 0},
222 	{NULL,			NULL,					0, 0}
223 };
224 
225 /*
226  * Subdirectories to search for unformatted/formatted man page
227  * versions, in nroff and troff variations.  The searching
228  * code in manual() is structured to expect there to be two
229  * subdirectories apiece, the first for unformatted files
230  * and the second for formatted ones.
231  */
232 static char	*nroffdirs[] = { "man", "cat", 0 };
233 static char	*troffdirs[] = { "man", "fmt", 0 };
234 
235 #define	MAN_USAGE "\
236 usage:\tman [-] [-adFlprt] [-M path] [-T macro-package ] [ -s section ] \
237 name ...\n\
238 \tman [-M path] -k keyword ...\n\tman [-M path] -f file ..."
239 #define	CATMAN_USAGE "\
240 usage:\tcatman [-p] [-c|-ntw] [-M path] [-T macro-package ] [sections]"
241 
242 static char *opts[] = {
243 	"FfkrpP:M:T:ts:lad",	/* man */
244 	"wpnP:M:T:tc"		/* catman */
245 };
246 
247 struct man_node {
248 	char *path;		/* mandir path */
249 	char **secv;		/* submandir suffices */
250 	int  defsrch;		/* hint for man -p to avoid section list */
251 	int  frompath;		/* hint for man -d and catman -p */
252 	struct man_node *next;
253 };
254 
255 static char	*pages[MAXPAGES];
256 static char	**endp = pages;
257 
258 /*
259  * flags (options)
260  */
261 static int	nomore;
262 static int	troffit;
263 static int	debug;
264 static int	Tflag;
265 static int	sargs;
266 static int	margs;
267 static int	force;
268 static int	found;
269 static int	list;
270 static int	all;
271 static int	whatis;
272 static int	apropos;
273 static int	catmando;
274 static int	nowhatis;
275 static int	whatonly;
276 static int	compargs;	/* -c option for catman */
277 static int	printmp;
278 
279 static char	*CAT	= CAT_;
280 static char	macros[MAXPATHLEN];
281 static char	*mansec;
282 static char	*pager;
283 static char	*troffcmd;
284 static char	*troffcat;
285 static char	**subdirs;
286 
287 static char *check_config(char *);
288 static struct man_node *build_manpath(char **, int);
289 static void getpath(struct man_node *, char **);
290 static void getsect(struct man_node *, char **);
291 static void get_all_sect(struct man_node *);
292 static void catman(struct man_node *, char **, int);
293 static int makecat(char *, char **, int);
294 static int getdirs(char *, char ***, short);
295 static void whatapro(struct man_node *, char *, int);
296 static void lookup_windex(char *, char *, char **);
297 static int icmp(wchar_t *, wchar_t *);
298 static void more(char **, int);
299 static void cleanup(char **);
300 static void bye(int);
301 static char **split(char *, char);
302 static void freev(char **);
303 static void fullpaths(struct man_node **);
304 static void lower(char *);
305 static int cmp(const void *, const void *);
306 static int manual(struct man_node *, char *);
307 static void mandir(char **, char *, char *);
308 static void sortdir(DIR *, char ***);
309 static int searchdir(char *, char *, char *);
310 static int windex(char **, char *, char *);
311 static void section(struct suffix *, char *);
312 static int bfsearch(FILE *, char **, char *, char **);
313 static int compare(char *, char *, char **);
314 static int format(char *, char *, char *, char *);
315 static char *addlocale(char *);
316 static int get_manconfig(FILE *, char *);
317 static void	malloc_error(void);
318 static int	sgmlcheck(const char *);
319 static char *map_section(char *, char *);
320 static void free_manp(struct man_node *manp);
321 static void init_bintoman(void);
322 static char *path_to_manpath(char *);
323 static int dupcheck(struct man_node *, struct dupnode **);
324 static void free_dupnode(struct dupnode *);
325 static void print_manpath(struct man_node *, char *);
326 
327 /*
328  * This flag is used when the SGML-to-troff converter
329  * is absent - all the SGML searches are bypassed.
330  */
331 static int no_sroff = 0;
332 
333 /*
334  * This flag is used to describe the case where we've found
335  * an SGML formatted manpage in the sman directory, we haven't
336  * found a troff formatted manpage, and we don't have the SGML to troff
337  * conversion utility on the system.
338  */
339 static int sman_no_man_no_sroff;
340 
341 static char language[PATH_MAX + 1]; 	/* LC_MESSAGES */
342 static char localedir[PATH_MAX + 1];	/* locale specific path component */
343 
344 static int	defaultmandir = 1;	/* if processing default mandir, 1 */
345 
346 static char *newsection = NULL;
347 
348 int
main(int argc,char * argv[])349 main(int argc, char *argv[])
350 {
351 	int badopts = 0;
352 	int c;
353 	char **pathv;
354 	char *cmdname;
355 	char *manpath = NULL;
356 	static struct man_node	*manpage = NULL;
357 	int bmp_flags = 0;
358 	int err = 0;
359 
360 	if (access(SROFF_CMD, F_OK | X_OK) != 0)
361 		no_sroff = 1;
362 
363 	(void) setlocale(LC_ALL, "");
364 	(void) strcpy(language, setlocale(LC_MESSAGES, (char *)0));
365 	if (strcmp("C", language) != 0)
366 		(void) sprintf(localedir, "%s", language);
367 
368 #if !defined(TEXT_DOMAIN)
369 #define	TEXT_DOMAIN "SYS_TEST"
370 #endif
371 	(void) textdomain(TEXT_DOMAIN);
372 
373 	(void) strcpy(macros, TMAC_AN);
374 
375 	/*
376 	 * get base part of command name
377 	 */
378 	if ((cmdname = strrchr(argv[0], '/')) != NULL)
379 		cmdname++;
380 	else
381 		cmdname = argv[0];
382 
383 	if (eq(cmdname, "apropos") || eq(cmdname, "whatis")) {
384 		whatis++;
385 		apropos = (*cmdname == 'a');
386 		if ((optind = 1) == argc) {
387 			(void) fprintf(stderr, gettext("%s what?\n"), cmdname);
388 			exit(2);
389 		}
390 		goto doargs;
391 	} else if (eq(cmdname, "catman"))
392 		catmando++;
393 
394 	opterr = 0;
395 	while ((c = getopt(argc, argv, opts[catmando])) != -1)
396 		switch (c) {
397 
398 		/*
399 		 * man specific options
400 		 */
401 		case 'k':
402 			apropos++;
403 			/*FALLTHROUGH*/
404 		case 'f':
405 			whatis++;
406 			break;
407 		case 'F':
408 			force++;	/* do lookups the hard way */
409 			break;
410 		case 's':
411 			mansec = optarg;
412 			sargs++;
413 			break;
414 		case 'r':
415 			nomore++, troffit++;
416 			break;
417 		case 'l':
418 			list++;		/* implies all */
419 			/*FALLTHROUGH*/
420 		case 'a':
421 			all++;
422 			break;
423 		case 'd':
424 			debug++;
425 			break;
426 		/*
427 		 * man and catman use -p differently.  In catman it
428 		 * enables debug mode and in man it prints the (possibly
429 		 * derived from PATH or name operand) MANPATH.
430 		 */
431 		case 'p':
432 			if (catmando == 0) {
433 				printmp++;
434 			} else {
435 				debug++;
436 			}
437 			break;
438 		case 'n':
439 			nowhatis++;
440 			break;
441 		case 'w':
442 			whatonly++;
443 			break;
444 		case 'c':	/* n|troff compatibility */
445 			if (no_sroff)
446 				(void) fprintf(stderr, gettext(
447 				    "catman: SGML conversion not "
448 				    "available -- -c flag ignored\n"));
449 			else
450 				compargs++;
451 			continue;
452 
453 		/*
454 		 * shared options
455 		 */
456 		case 'P':	/* Backwards compatibility */
457 		case 'M':	/* Respecify path for man pages. */
458 			manpath = optarg;
459 			margs++;
460 			break;
461 		case 'T':	/* Respecify man macros */
462 			(void) strcpy(macros, optarg);
463 			Tflag++;
464 			break;
465 		case 't':
466 			troffit++;
467 			break;
468 		case '?':
469 			badopts++;
470 		}
471 
472 	/*
473 	 *  Bad options or no args?
474 	 *	(man -p and catman don't need args)
475 	 */
476 	if (badopts || (!catmando && !printmp && optind == argc)) {
477 		(void) fprintf(stderr, "%s\n", catmando ?
478 		    gettext(CATMAN_USAGE) : gettext(MAN_USAGE));
479 		exit(2);
480 	}
481 
482 	if (compargs && (nowhatis || whatonly || troffit)) {
483 		(void) fprintf(stderr, "%s\n", gettext(CATMAN_USAGE));
484 		(void) fprintf(stderr, gettext(
485 		    "-c option cannot be used with [-w][-n][-t]\n"));
486 		exit(2);
487 	}
488 
489 	if (sargs && margs && catmando) {
490 		(void) fprintf(stderr, "%s\n", gettext(CATMAN_USAGE));
491 		exit(2);
492 	}
493 
494 	if (troffit == 0 && nomore == 0 && !isatty(fileno(stdout)))
495 		nomore++;
496 
497 	/*
498 	 * Collect environment information.
499 	 */
500 	if (troffit) {
501 		if ((troffcmd = getenv("TROFF")) == NULL)
502 			troffcmd = TROFF;
503 		if ((troffcat = getenv("TCAT")) == NULL)
504 			troffcat = TCAT;
505 	} else {
506 		if (((pager = getenv("PAGER")) == NULL) ||
507 		    (*pager == NULL))
508 			pager = MORE;
509 	}
510 
511 doargs:
512 	subdirs = troffit ? troffdirs : nroffdirs;
513 
514 	init_bintoman();
515 
516 	if (manpath == NULL && (manpath = getenv("MANPATH")) == NULL) {
517 		if ((manpath = getenv("PATH")) != NULL) {
518 			bmp_flags = BMP_ISPATH | BMP_APPEND_MANDIR;
519 		} else {
520 			manpath = MANDIR;
521 		}
522 	}
523 
524 	pathv = split(manpath, ':');
525 
526 	manpage = build_manpath(pathv, bmp_flags);
527 
528 	/* release pathv allocated by split() */
529 	freev(pathv);
530 
531 	fullpaths(&manpage);
532 
533 	if (catmando) {
534 		catman(manpage, argv+optind, argc-optind);
535 		exit(0);
536 	}
537 
538 	/*
539 	 * The manual routine contains windows during which
540 	 * termination would leave a temp file behind.  Thus
541 	 * we blanket the whole thing with a clean-up routine.
542 	 */
543 	if (signal(SIGINT, SIG_IGN) == SIG_DFL) {
544 		(void) signal(SIGINT, bye);
545 		(void) signal(SIGQUIT, bye);
546 		(void) signal(SIGTERM, bye);
547 	}
548 
549 	/*
550 	 * "man -p" without operands
551 	 */
552 	if ((printmp != 0) && (optind == argc)) {
553 		print_manpath(manpage, NULL);
554 		exit(0);
555 	}
556 
557 	for (; optind < argc; optind++) {
558 		if (strcmp(argv[optind], "-") == 0) {
559 			nomore++;
560 			CAT = CAT_S;
561 		} else {
562 			char *cmd;
563 			static struct man_node *mp;
564 			char *pv[2];
565 
566 			/*
567 			 * If full path to command specified, customize
568 			 * manpath accordingly
569 			 */
570 			if ((cmd = strrchr(argv[optind], '/')) != NULL) {
571 				*cmd = '\0';
572 				if ((pv[0] = strdup(argv[optind])) == NULL) {
573 					malloc_error();
574 				}
575 				pv[1] = NULL;
576 				*cmd = '/';
577 				mp = build_manpath(pv,
578 				    BMP_ISPATH|BMP_FALLBACK_MANDIR);
579 			} else {
580 				mp = manpage;
581 			}
582 
583 			if (whatis) {
584 				whatapro(mp, argv[optind], apropos);
585 			} else if (printmp != 0) {
586 				print_manpath(mp, argv[optind]);
587 			} else {
588 				err += manual(mp, argv[optind]);
589 			}
590 
591 			if (mp != NULL && mp != manpage) {
592 				free(pv[0]);
593 				free_manp(mp);
594 			}
595 		}
596 	}
597 	return (err == 0 ? 0 : 1);
598 	/*NOTREACHED*/
599 }
600 
601 /*
602  * This routine builds the manpage structure from MANPATH or PATH,
603  * depending on flags.  See BMP_* definitions above for valid
604  * flags.
605  *
606  * Assumes pathv elements were malloc'd, as done by split().
607  * Elements may be freed and reallocated to have different contents.
608  */
609 
610 static struct man_node *
build_manpath(char ** pathv,int flags)611 build_manpath(char **pathv, int flags)
612 {
613 	struct man_node *manpage = NULL;
614 	struct man_node *currp = NULL;
615 	struct man_node *lastp = NULL;
616 	char **p;
617 	char **q;
618 	char *mand = NULL;
619 	char *mandir = MANDIR;
620 	int s;
621 	struct dupnode *didup = NULL;
622 	struct stat sb;
623 
624 	s = sizeof (struct man_node);
625 	for (p = pathv; *p; ) {
626 
627 		if (flags & BMP_ISPATH) {
628 			if ((mand = path_to_manpath(*p)) == NULL) {
629 				goto next;
630 			}
631 			free(*p);
632 			*p = mand;
633 		}
634 		q = split(*p, ',');
635 		if (stat(q[0], &sb) != 0 || (sb.st_mode & S_IFDIR) == 0) {
636 			freev(q);
637 			goto next;
638 		}
639 
640 		if (access(q[0], R_OK|X_OK) != 0) {
641 			if (catmando) {
642 				(void) fprintf(stderr,
643 				    gettext("%s is not accessible.\n"),
644 				    q[0]);
645 				(void) fflush(stderr);
646 			}
647 		} else {
648 
649 			/*
650 			 * Some element exists.  Do not append MANDIR as a
651 			 * fallback.
652 			 */
653 			flags &= ~BMP_FALLBACK_MANDIR;
654 
655 			if ((currp = (struct man_node *)calloc(1, s)) == NULL) {
656 				malloc_error();
657 			}
658 
659 			currp->frompath = (flags & BMP_ISPATH);
660 
661 			if (manpage == NULL) {
662 				lastp = manpage = currp;
663 			}
664 
665 			getpath(currp, p);
666 			getsect(currp, p);
667 
668 			/*
669 			 * If there are no new elements in this path,
670 			 * do not add it to the manpage list
671 			 */
672 			if (dupcheck(currp, &didup) != 0) {
673 				freev(currp->secv);
674 				free(currp);
675 			} else {
676 				currp->next = NULL;
677 				if (currp != manpage) {
678 					lastp->next = currp;
679 				}
680 				lastp = currp;
681 			}
682 		}
683 		freev(q);
684 next:
685 		/*
686 		 * Special handling of appending MANDIR.
687 		 * After all pathv elements have been processed, append MANDIR
688 		 * if needed.
689 		 */
690 		if (p == &mandir) {
691 			break;
692 		}
693 		p++;
694 		if (*p != NULL) {
695 			continue;
696 		}
697 		if (flags & (BMP_APPEND_MANDIR|BMP_FALLBACK_MANDIR)) {
698 			p = &mandir;
699 			flags &= ~BMP_ISPATH;
700 		}
701 	}
702 
703 	free_dupnode(didup);
704 
705 	return (manpage);
706 }
707 
708 /*
709  * Stores the mandir path into the manp structure.
710  */
711 
712 static void
getpath(struct man_node * manp,char ** pv)713 getpath(struct man_node *manp, char **pv)
714 {
715 	char *s;
716 	int i = 0;
717 
718 	s = *pv;
719 
720 	while (*s != NULL && *s != ',')
721 		i++, s++;
722 
723 	manp->path = (char *)malloc(i+1);
724 	if (manp->path == NULL)
725 		malloc_error();
726 	(void) strncpy(manp->path, *pv, i);
727 	*(manp->path + i) = '\0';
728 }
729 
730 /*
731  * Stores the mandir's corresponding sections (submandir
732  * directories) into the manp structure.
733  */
734 
735 static void
getsect(struct man_node * manp,char ** pv)736 getsect(struct man_node *manp, char **pv)
737 {
738 	char *sections;
739 	char **sectp;
740 
741 	if (sargs) {
742 		manp->secv = split(mansec, ',');
743 
744 		for (sectp = manp->secv; *sectp; sectp++)
745 			lower(*sectp);
746 	} else if ((sections = strchr(*pv, ',')) != NULL) {
747 		if (debug) {
748 			if (manp->frompath != 0) {
749 /*
750  * TRANSLATION_NOTE - message for man -d or catman -p
751  * ex. /usr/share/man: derived from PATH, MANSECTS=,1b
752  */
753 				(void) printf(gettext(
754 				    "%s: derived from PATH, MANSECTS=%s\n"),
755 				    manp->path, sections);
756 			} else {
757 /*
758  * TRANSLATION_NOTE - message for man -d or catman -p
759  * ex. /usr/share/man: from -M option, MANSECTS=,1,2,3c
760  */
761 				(void) fprintf(stdout, gettext(
762 				    "%s: from -M option, MANSECTS=%s\n"),
763 				    manp->path, sections);
764 			}
765 		}
766 		manp->secv = split(++sections, ',');
767 		for (sectp = manp->secv; *sectp; sectp++)
768 			lower(*sectp);
769 
770 		if (*manp->secv == NULL)
771 			get_all_sect(manp);
772 	} else if ((sections = check_config(*pv)) != NULL) {
773 		manp->defsrch = 1;
774 /*
775  * TRANSLATION_NOTE - message for man -d or catman -p
776  * ex. /usr/share/man: from man.cf, MANSECTS=1,1m,1c
777  */
778 		if (debug)
779 			(void) fprintf(stdout, gettext(
780 			    "%s: from %s, MANSECTS=%s\n"),
781 			    manp->path, CONFIG, sections);
782 		manp->secv = split(sections, ',');
783 
784 		for (sectp = manp->secv; *sectp; sectp++)
785 			lower(*sectp);
786 
787 		if (*manp->secv == NULL)
788 			get_all_sect(manp);
789 	} else {
790 		manp->defsrch = 1;
791 /*
792  * TRANSLATION_NOTE - message for man -d or catman -p
793  * if man.cf has not been found or sections has not been specified
794  * man/catman searches the sections lexicographically.
795  */
796 		if (debug)
797 			(void) fprintf(stdout, gettext(
798 			    "%s: search the sections lexicographically\n"),
799 			    manp->path);
800 		manp->secv = NULL;
801 		get_all_sect(manp);
802 	}
803 }
804 
805 /*
806  * Get suffices of all sub-mandir directories in a mandir.
807  */
808 
809 static void
get_all_sect(struct man_node * manp)810 get_all_sect(struct man_node *manp)
811 {
812 	DIR *dp;
813 	char **dirv;
814 	char **dv;
815 	char **p;
816 	char *prev = NULL;
817 	char *tmp = NULL;
818 	int  plen;
819 	int	maxentries = MAXTOKENS;
820 	int	entries = 0;
821 
822 	if ((dp = opendir(manp->path)) == 0)
823 		return;
824 
825 	/*
826 	 * sortdir() allocates memory for dirv and dirv[].
827 	 */
828 	sortdir(dp, &dirv);
829 
830 	(void) closedir(dp);
831 
832 	if (manp->secv == NULL) {
833 		/*
834 		 * allocates memory for manp->secv only if it's NULL
835 		 */
836 		manp->secv = (char **)malloc(maxentries * sizeof (char *));
837 		if (manp->secv == NULL)
838 			malloc_error();
839 	}
840 
841 	for (dv = dirv, p = manp->secv; *dv; dv++) {
842 		plen = PLEN;
843 		if (match(*dv, SGMLDIR, PLEN+1))
844 			++plen;
845 
846 		if (strcmp(*dv, CONFIG) == 0) {
847 			/* release memory allocated by sortdir */
848 			free(*dv);
849 			continue;
850 		}
851 
852 		if (tmp != NULL)
853 			free(tmp);
854 		tmp = strdup(*dv + plen);
855 		if (tmp == NULL)
856 			malloc_error();
857 		(void) sprintf(tmp, "%s", *dv + plen);
858 
859 		if (prev != NULL) {
860 			if (strcmp(prev, tmp) == 0) {
861 				/* release memory allocated by sortdir */
862 				free(*dv);
863 				continue;
864 			}
865 		}
866 
867 		if (prev != NULL)
868 			free(prev);
869 		prev = strdup(*dv + plen);
870 		if (prev == NULL)
871 			malloc_error();
872 		(void) sprintf(prev, "%s", *dv + plen);
873 		/*
874 		 * copy the string in (*dv + plen) to *p
875 		 */
876 		*p = strdup(*dv + plen);
877 		if (*p == NULL)
878 			malloc_error();
879 		p++;
880 		entries++;
881 		if (entries == maxentries) {
882 			maxentries += MAXTOKENS;
883 			manp->secv = (char **)realloc(manp->secv,
884 			    sizeof (char *) * maxentries);
885 			if (manp->secv == NULL)
886 				malloc_error();
887 			p = manp->secv + entries;
888 		}
889 		/* release memory allocated by sortdir */
890 		free(*dv);
891 	}
892 	*p = 0;
893 	/* release memory allocated by sortdir */
894 	free(dirv);
895 }
896 
897 /*
898  * Format man pages (build cat pages); if no
899  * sections are specified, build all of them.
900  * When building cat pages:
901  *	catman() tries to build cat pages for locale specific
902  *	man dirs first.  Then, catman() tries to build cat pages
903  *	for the default man dir (for C locale like /usr/share/man)
904  *	regardless of the locale.
905  * When building windex file:
906  *	catman() tries to build windex file for locale specific
907  *	man dirs first.  Then, catman() tries to build windex file
908  *	for the default man dir (for C locale like /usr/share/man)
909  *	regardless of the locale.
910  */
911 
912 static void
catman(struct man_node * manp,char ** argv,int argc)913 catman(struct man_node *manp, char **argv, int argc)
914 {
915 	char cmdbuf[BUFSIZ];
916 	char **dv;
917 	int changed;
918 	struct man_node *p;
919 	int ndirs = 0;
920 	char *ldir;
921 	int	i;
922 	struct dupnode *dnp = NULL;
923 	char   **realsecv;
924 	char   *fakesecv[2] = { " catman ", NULL };
925 
926 	for (p = manp; p != NULL; p = p->next) {
927 		/*
928 		 * prevent catman from doing very heavy lifting multiple
929 		 * times on some directory
930 		 */
931 		realsecv = p->secv;
932 		p->secv = fakesecv;
933 		if (dupcheck(p, &dnp) != 0) {
934 			p->secv = realsecv;
935 			continue;
936 		}
937 
938 /*
939  * TRANSLATION_NOTE - message for catman -p
940  * ex. mandir path = /usr/share/man
941  */
942 		if (debug)
943 			(void) fprintf(stdout, gettext(
944 			    "\nmandir path = %s\n"), p->path);
945 		ndirs = 0;
946 
947 		/*
948 		 * Build cat pages
949 		 * addlocale() allocates memory and returns it
950 		 */
951 		ldir = addlocale(p->path);
952 		if (!whatonly) {
953 			if (*localedir != '\0') {
954 				if (defaultmandir)
955 					defaultmandir = 0;
956 				/* getdirs allocate memory for dv */
957 				ndirs = getdirs(ldir, &dv, 1);
958 				if (ndirs != 0) {
959 					changed = argc ?
960 					    makecat(ldir, argv, argc) :
961 					    makecat(ldir, dv, ndirs);
962 					/* release memory by getdirs */
963 					for (i = 0; i < ndirs; i++) {
964 						free(dv[i]);
965 					}
966 					free(dv);
967 				}
968 			}
969 
970 			/* default man dir is always processed */
971 			defaultmandir = 1;
972 			ndirs = getdirs(p->path, &dv, 1);
973 			changed = argc ?
974 			    makecat(p->path, argv, argc) :
975 			    makecat(p->path, dv, ndirs);
976 			/* release memory allocated by getdirs */
977 			for (i = 0; i < ndirs; i++) {
978 				free(dv[i]);
979 			}
980 			free(dv);
981 		}
982 		/*
983 		 * Build whatis database
984 		 *  print error message if locale is set and man dir not found
985 		 *  won't build it at all if -c option is on
986 		 */
987 		if (!compargs && (whatonly || (!nowhatis && changed))) {
988 			if (*localedir != '\0') {
989 				/* just count the number of ndirs */
990 				if ((ndirs = getdirs(ldir, NULL, 0)) != 0) {
991 					(void) sprintf(cmdbuf,
992 					    "/usr/bin/sh %s %s",
993 					    MAKEWHATIS, ldir);
994 					(void) sys(cmdbuf);
995 				}
996 			}
997 			/* whatis database of the default man dir */
998 			/* will be always built in C locale. */
999 			(void) sprintf(cmdbuf,
1000 			    "/usr/bin/sh %s %s",
1001 			    MAKEWHATIS, p->path);
1002 			(void) sys(cmdbuf);
1003 		}
1004 		/* release memory allocated by addlocale() */
1005 		free(ldir);
1006 	}
1007 	free_dupnode(dnp);
1008 }
1009 
1010 /*
1011  * Build cat pages for given sections
1012  */
1013 
1014 static int
makecat(char * path,char ** dv,int ndirs)1015 makecat(char *path, char **dv, int ndirs)
1016 {
1017 	DIR *dp, *sdp;
1018 	struct dirent *d;
1019 	struct stat sbuf;
1020 	char mandir[MAXPATHLEN+1];
1021 	char smandir[MAXPATHLEN+1];
1022 	char catdir[MAXPATHLEN+1];
1023 	char *dirp, *sdirp;
1024 	int i, fmt;
1025 	int manflag, smanflag;
1026 
1027 	for (i = fmt = 0; i < ndirs; i++) {
1028 		(void) snprintf(mandir, MAXPATHLEN, "%s/%s%s",
1029 		    path, MANDIRNAME, dv[i]);
1030 		(void) snprintf(smandir, MAXPATHLEN, "%s/%s%s",
1031 		    path, SGMLDIR, dv[i]);
1032 		(void) snprintf(catdir, MAXPATHLEN, "%s/%s%s",
1033 		    path, subdirs[1], dv[i]);
1034 		dirp = strrchr(mandir, '/') + 1;
1035 		sdirp = strrchr(smandir, '/') + 1;
1036 
1037 		manflag = smanflag = 0;
1038 
1039 		if ((dp = opendir(mandir)) != NULL)
1040 			manflag = 1;
1041 
1042 		if (!no_sroff && (sdp = opendir(smandir)) != NULL)
1043 			smanflag = 1;
1044 
1045 		if (dp == 0 && sdp == 0) {
1046 			if (strcmp(mandir, CONFIG) == 0)
1047 				perror(mandir);
1048 			continue;
1049 		}
1050 /*
1051  * TRANSLATION_NOTE - message for catman -p
1052  * ex. Building cat pages for mandir = /usr/share/man/ja
1053  */
1054 		if (debug)
1055 			(void) fprintf(stdout, gettext(
1056 			    "Building cat pages for mandir = %s\n"), path);
1057 
1058 		if (!compargs && stat(catdir, &sbuf) < 0) {
1059 			(void) umask(02);
1060 /*
1061  * TRANSLATION_NOTE - message for catman -p
1062  * ex. mkdir /usr/share/man/ja/cat3c
1063  */
1064 			if (debug)
1065 				(void) fprintf(stdout, gettext("mkdir %s\n"),
1066 				    catdir);
1067 			else {
1068 				if (mkdir(catdir, 0755) < 0) {
1069 					perror(catdir);
1070 					continue;
1071 				}
1072 				(void) chmod(catdir, 0755);
1073 			}
1074 		}
1075 
1076 		/*
1077 		 * if it is -c option of catman, if there is no
1078 		 * coresponding man dir for sman files to go to,
1079 		 * make the man dir
1080 		 */
1081 
1082 		if (compargs && !manflag) {
1083 			if (mkdir(mandir, 0755) < 0) {
1084 				perror(mandir);
1085 				continue;
1086 			}
1087 			(void) chmod(mandir, 0755);
1088 		}
1089 
1090 		if (smanflag) {
1091 			while ((d = readdir(sdp))) {
1092 				if (eq(".", d->d_name) || eq("..", d->d_name))
1093 					continue;
1094 
1095 				if (format(path, sdirp, (char *)0, d->d_name)
1096 				    > 0)
1097 					fmt++;
1098 			}
1099 		}
1100 
1101 		if (manflag && !compargs) {
1102 			while ((d = readdir(dp))) {
1103 				if (eq(".", d->d_name) || eq("..", d->d_name))
1104 					continue;
1105 
1106 				if (format(path, dirp, (char *)0, d->d_name)
1107 				    > 0)
1108 					fmt++;
1109 			}
1110 		}
1111 
1112 		if (manflag)
1113 			(void) closedir(dp);
1114 
1115 		if (smanflag)
1116 			(void) closedir(sdp);
1117 
1118 	}
1119 	return (fmt);
1120 }
1121 
1122 
1123 /*
1124  * Get all "man" and "sman" dirs under a given manpath
1125  * and return the number found
1126  * If -c option is on, only count sman dirs
1127  */
1128 
1129 static int
getdirs(char * path,char *** dirv,short flag)1130 getdirs(char *path, char ***dirv, short flag)
1131 {
1132 	DIR *dp;
1133 	struct dirent *d;
1134 	int n = 0;
1135 	int plen, sgml_flag, man_flag;
1136 	int i = 0;
1137 	int	maxentries = MAXDIRS;
1138 	char	**dv;
1139 
1140 	if ((dp = opendir(path)) == 0) {
1141 		if (debug) {
1142 			if (*localedir != '\0')
1143 				(void) printf(gettext("\
1144 locale is %s, search in %s\n"), localedir, path);
1145 			perror(path);
1146 		}
1147 		return (0);
1148 	}
1149 
1150 	if (flag) {
1151 		/* allocate memory for dirv */
1152 		*dirv = (char **)malloc(sizeof (char *) *
1153 		    maxentries);
1154 		if (*dirv == NULL)
1155 			malloc_error();
1156 		dv = *dirv;
1157 	}
1158 	while ((d = readdir(dp))) {
1159 		plen = PLEN;
1160 		man_flag = sgml_flag = 0;
1161 		if (match(d->d_name, SGMLDIR, PLEN+1)) {
1162 			plen = PLEN + 1;
1163 			sgml_flag = 1;
1164 			i++;
1165 		}
1166 
1167 		if (match(subdirs[0], d->d_name, PLEN))
1168 			man_flag = 1;
1169 
1170 		if (compargs && sgml_flag) {
1171 			if (flag) {
1172 				*dv = strdup(d->d_name+plen);
1173 				if (*dv == NULL)
1174 					malloc_error();
1175 				dv++;
1176 				n = i;
1177 			}
1178 		} else if (!compargs && (sgml_flag || man_flag)) {
1179 			if (flag) {
1180 				*dv = strdup(d->d_name+plen);
1181 				if (*dv == NULL)
1182 					malloc_error();
1183 				dv++;
1184 			}
1185 			n++;
1186 		}
1187 		if (flag) {
1188 			if ((dv - *dirv) == maxentries) {
1189 				int entries = maxentries;
1190 				maxentries += MAXTOKENS;
1191 				*dirv = (char **)realloc(*dirv,
1192 				    sizeof (char *) * maxentries);
1193 				if (*dirv == NULL)
1194 					malloc_error();
1195 				dv = *dirv + entries;
1196 			}
1197 		}
1198 	}
1199 
1200 	(void) closedir(dp);
1201 	return (n);
1202 }
1203 
1204 
1205 /*
1206  * Find matching whatis or apropos entries
1207  * whatapro() tries to handle the windex file of the locale specific
1208  * man dirs first, then tries to handle the windex file of the default
1209  * man dir (of C locale like /usr/share/man).
1210  */
1211 
1212 static void
whatapro(struct man_node * manp,char * word,int apropos)1213 whatapro(struct man_node *manp, char *word, int apropos)
1214 {
1215 	char whatpath[MAXPATHLEN+1];
1216 	char *p;
1217 	struct man_node *b;
1218 	int ndirs = 0;
1219 	char *ldir;
1220 
1221 
1222 /*
1223  * TRANSLATION_NOTE - message for man -d
1224  * %s takes a parameter to -k option.
1225  */
1226 	DPRINTF(gettext("word = %s \n"), word);
1227 
1228 	/*
1229 	 * get base part of name
1230 	 */
1231 	if (!apropos) {
1232 		if ((p = strrchr(word, '/')) == NULL)
1233 			p = word;
1234 		else
1235 			p++;
1236 	} else {
1237 		p = word;
1238 	}
1239 
1240 	for (b = manp; b != NULL; b = b->next) {
1241 
1242 		if (*localedir != '\0') {
1243 			/* addlocale() allocates memory and returns it */
1244 			ldir = addlocale(b->path);
1245 			if (defaultmandir)
1246 				defaultmandir = 0;
1247 			ndirs = getdirs(ldir, NULL, 0);
1248 			if (ndirs != 0) {
1249 				(void) sprintf(whatpath, "%s/%s", ldir, WHATIS);
1250 /*
1251  * TRANSLATION_NOTE - message for man -d
1252  * ex. mandir path = /usr/share/man/ja
1253  */
1254 				DPRINTF(gettext("\nmandir path = %s\n"), ldir);
1255 				lookup_windex(whatpath, p, b->secv);
1256 			}
1257 			/* release memory allocated by addlocale() */
1258 			free(ldir);
1259 		}
1260 
1261 		defaultmandir = 1;
1262 		(void) sprintf(whatpath, "%s/%s", b->path, WHATIS);
1263 /*
1264  * TRANSLATION_NOTE - message for man -d
1265  * ex. mandir path = /usr/share/man
1266  */
1267 		DPRINTF(gettext("\nmandir path = %s\n"), b->path);
1268 
1269 		lookup_windex(whatpath, p, b->secv);
1270 	}
1271 }
1272 
1273 
1274 static void
lookup_windex(char * whatpath,char * word,char ** secv)1275 lookup_windex(char *whatpath, char *word, char **secv)
1276 {
1277 	FILE *fp;
1278 	char *matches[MAXPAGES];
1279 	char **pp;
1280 	wchar_t	wbuf[BUFSIZ];
1281 	wchar_t *word_wchar = NULL;
1282 	wchar_t	*ws;
1283 	size_t	word_len, ret;
1284 
1285 	if ((fp = fopen(whatpath, "r")) == NULL) {
1286 		perror(whatpath);
1287 		return;
1288 	}
1289 
1290 	if (apropos) {
1291 		word_len = strlen(word) + 1;
1292 		if ((word_wchar = (wchar_t *)malloc(sizeof (wchar_t) *
1293 		    word_len)) == NULL) {
1294 			malloc_error();
1295 		}
1296 		ret = mbstowcs(word_wchar, (const char *)word, word_len);
1297 		if (ret == (size_t)-1) {
1298 			(void) fprintf(stderr, gettext(
1299 			    "Invalid character in keyword\n"));
1300 			exit(1);
1301 		}
1302 		while (fgetws(wbuf, BUFSIZ, fp) != NULL)
1303 			for (ws = wbuf; *ws; ws++)
1304 				if (icmp(word_wchar, ws) == 0) {
1305 					(void) printf("%ws", wbuf);
1306 					break;
1307 				}
1308 	} else {
1309 		if (bfsearch(fp, matches, word, secv))
1310 			for (pp = matches; *pp; pp++) {
1311 				(void) printf("%s", *pp);
1312 				/*
1313 				 * release memory allocated by
1314 				 * strdup() in bfsearch()
1315 				 */
1316 				free(*pp);
1317 			}
1318 	}
1319 	(void) fclose(fp);
1320 	if (word_wchar)
1321 		free(word_wchar);
1322 
1323 }
1324 
1325 
1326 /*
1327  * case-insensitive compare unless upper case is used
1328  * ie)	"mount" matches mount, Mount, MOUNT
1329  *	"Mount" matches Mount, MOUNT
1330  *	"MOUNT" matches MOUNT only
1331  *	If matched return 0.  Otherwise, return 1.
1332  */
1333 
1334 static int
icmp(wchar_t * ws,wchar_t * wt)1335 icmp(wchar_t *ws, wchar_t *wt)
1336 {
1337 	for (; (*ws == 0) ||
1338 	    (*ws == (iswupper(*ws) ? *wt: towlower(*wt)));
1339 	    ws++, wt++)
1340 		if (*ws == 0)
1341 			return (0);
1342 
1343 	return (1);
1344 }
1345 
1346 
1347 /*
1348  * Invoke PAGER with all matching man pages
1349  */
1350 
1351 static void
more(char ** pages,int plain)1352 more(char **pages, int plain)
1353 {
1354 	char cmdbuf[BUFSIZ];
1355 	char **vp;
1356 
1357 	/*
1358 	 * Dont bother.
1359 	 */
1360 	if (list || (*pages == 0))
1361 		return;
1362 
1363 	if (plain && troffit) {
1364 		cleanup(pages);
1365 		return;
1366 	}
1367 	(void) sprintf(cmdbuf, "%s", troffit ? troffcat :
1368 	    plain ? CAT : pager);
1369 
1370 	/*
1371 	 * Build arg list
1372 	 */
1373 	for (vp = pages; vp < endp; vp++) {
1374 		(void) strcat(cmdbuf, " ");
1375 		(void) strcat(cmdbuf, *vp);
1376 	}
1377 	(void) sys(cmdbuf);
1378 	cleanup(pages);
1379 }
1380 
1381 
1382 /*
1383  * Get rid of dregs.
1384  */
1385 
1386 static void
cleanup(char ** pages)1387 cleanup(char **pages)
1388 {
1389 	char **vp;
1390 
1391 	for (vp = pages; vp < endp; vp++) {
1392 		if (match(TEMPLATE, *vp, TMPLEN))
1393 			(void) unlink(*vp);
1394 		free(*vp);
1395 	}
1396 
1397 	endp = pages;	/* reset */
1398 }
1399 
1400 
1401 /*
1402  * Clean things up after receiving a signal.
1403  */
1404 
1405 /*ARGSUSED*/
1406 static void
bye(int sig)1407 bye(int sig)
1408 {
1409 	cleanup(pages);
1410 	exit(1);
1411 	/*NOTREACHED*/
1412 }
1413 
1414 
1415 /*
1416  * Split a string by specified separator.
1417  *    ignore empty components/adjacent separators.
1418  *    returns vector to all tokens
1419  */
1420 
1421 static char **
split(char * s1,char sep)1422 split(char *s1, char sep)
1423 {
1424 	char **tokv, **vp;
1425 	char *mp, *tp;
1426 	int maxentries = MAXTOKENS;
1427 	int entries = 0;
1428 
1429 	tokv = vp = (char **)malloc(maxentries * sizeof (char *));
1430 	if (tokv == NULL)
1431 		malloc_error();
1432 	mp = s1;
1433 	for (; mp && *mp; mp = tp) {
1434 		tp = strchr(mp, sep);
1435 		if (mp == tp) {		/* empty component */
1436 			tp++;			/* ignore */
1437 			continue;
1438 		}
1439 		if (tp) {
1440 			/* a component found */
1441 			size_t	len;
1442 
1443 			len = tp - mp;
1444 			*vp = (char *)malloc(sizeof (char) * len + 1);
1445 			if (*vp == NULL)
1446 				malloc_error();
1447 			(void) strncpy(*vp, mp, len);
1448 			*(*vp + len) = '\0';
1449 			tp++;
1450 			vp++;
1451 		} else {
1452 			/* the last component */
1453 			*vp = strdup(mp);
1454 			if (*vp == NULL)
1455 				malloc_error();
1456 			vp++;
1457 		}
1458 		entries++;
1459 		if (entries == maxentries) {
1460 			maxentries += MAXTOKENS;
1461 			tokv = (char **)realloc(tokv,
1462 			    maxentries * sizeof (char *));
1463 			if (tokv == NULL)
1464 				malloc_error();
1465 			vp = tokv + entries;
1466 		}
1467 	}
1468 	*vp = 0;
1469 	return (tokv);
1470 }
1471 
1472 /*
1473  * Free a vector allocated by split();
1474  */
1475 static void
freev(char ** v)1476 freev(char **v)
1477 {
1478 	int i;
1479 	if (v != NULL) {
1480 		for (i = 0; v[i] != NULL; i++) {
1481 			free(v[i]);
1482 		}
1483 		free(v);
1484 	}
1485 }
1486 
1487 /*
1488  * Convert paths to full paths if necessary
1489  *
1490  */
1491 
1492 static void
fullpaths(struct man_node ** manp_head)1493 fullpaths(struct man_node **manp_head)
1494 {
1495 	char *cwd = NULL;
1496 	char *p;
1497 	char cwd_gotten = 0;
1498 	struct man_node *manp = *manp_head;
1499 	struct man_node *b;
1500 	struct man_node *prev = NULL;
1501 
1502 	for (b = manp; b != NULL; b = b->next) {
1503 		if (*(b->path) == '/') {
1504 			prev = b;
1505 			continue;
1506 		}
1507 
1508 		/* try to get cwd if haven't already */
1509 		if (!cwd_gotten) {
1510 			cwd = getcwd(NULL, MAXPATHLEN+1);
1511 			cwd_gotten = 1;
1512 		}
1513 
1514 		if (cwd) {
1515 			/* case: relative manpath with cwd: make absolute */
1516 			if ((p = malloc(strlen(b->path)+strlen(cwd)+2)) ==
1517 			    NULL) {
1518 				malloc_error();
1519 			}
1520 			(void) sprintf(p, "%s/%s", cwd, b->path);
1521 			/*
1522 			 * resetting b->path
1523 			 */
1524 			free(b->path);
1525 			b->path = p;
1526 		} else {
1527 			/* case: relative manpath but no cwd: omit path entry */
1528 			if (prev)
1529 				prev->next = b->next;
1530 			else
1531 				*manp_head = b->next;
1532 
1533 			free_manp(b);
1534 		}
1535 	}
1536 	/*
1537 	 * release memory allocated by getcwd()
1538 	 */
1539 	free(cwd);
1540 }
1541 
1542 /*
1543  * Free a man_node structure and its contents
1544  */
1545 
1546 static void
free_manp(struct man_node * manp)1547 free_manp(struct man_node *manp)
1548 {
1549 	char **p;
1550 
1551 	free(manp->path);
1552 	p = manp->secv;
1553 	while ((p != NULL) && (*p != NULL)) {
1554 		free(*p);
1555 		p++;
1556 	}
1557 	free(manp->secv);
1558 	free(manp);
1559 }
1560 
1561 
1562 /*
1563  * Map (in place) to lower case
1564  */
1565 
1566 static void
lower(char * s)1567 lower(char *s)
1568 {
1569 	if (s == 0)
1570 		return;
1571 	while (*s) {
1572 		if (isupper(*s))
1573 			*s = tolower(*s);
1574 		s++;
1575 	}
1576 }
1577 
1578 
1579 /*
1580  * compare for sort()
1581  * sort first by section-spec, then by prefix {sman, man, cat, fmt}
1582  *	note: prefix is reverse sorted so that "sman" and "man" always
1583  * 	comes before {cat, fmt}
1584  */
1585 
1586 static int
cmp(const void * arg1,const void * arg2)1587 cmp(const void *arg1, const void *arg2)
1588 {
1589 	int n;
1590 	char **p1 = (char **)arg1;
1591 	char **p2 = (char **)arg2;
1592 
1593 
1594 	/* by section; sman always before man dirs */
1595 	if ((n = strcmp(*p1 + PLEN + (**p1 == 's' ? 1 : 0),
1596 	    *p2 + PLEN + (**p2 == 's' ? 1 : 0))))
1597 		return (n);
1598 
1599 	/* by prefix reversed */
1600 	return (strncmp(*p2, *p1, PLEN));
1601 }
1602 
1603 
1604 /*
1605  * Find a man page ...
1606  *   Loop through each path specified,
1607  *   first try the lookup method (whatis database),
1608  *   and if it doesn't exist, do the hard way.
1609  */
1610 
1611 static int
manual(struct man_node * manp,char * name)1612 manual(struct man_node *manp, char *name)
1613 {
1614 	struct man_node *p;
1615 	struct man_node *local;
1616 	int ndirs = 0;
1617 	char *ldir;
1618 	char *ldirs[2];
1619 	char *fullname = name;
1620 	char *slash;
1621 
1622 	if ((slash = strrchr(name, '/')) != NULL) {
1623 		name = slash + 1;
1624 	}
1625 
1626 	/*
1627 	 *  for each path in MANPATH
1628 	 */
1629 	found = 0;
1630 
1631 	for (p = manp; p != NULL; p = p->next) {
1632 /*
1633  * TRANSLATION_NOTE - message for man -d
1634  * ex. mandir path = /usr/share/man
1635  */
1636 		DPRINTF(gettext("\nmandir path = %s\n"), p->path);
1637 
1638 		if (*localedir != '\0') {
1639 			/* addlocale() allocates memory and returns it */
1640 			ldir = addlocale(p->path);
1641 			if (defaultmandir)
1642 				defaultmandir = 0;
1643 /*
1644  * TRANSLATION_NOTE - message for man -d
1645  * ex. localedir = ja, ldir = /usr/share/man/ja
1646  */
1647 			if (debug)
1648 				(void) printf(gettext(
1649 				    "localedir = %s, ldir = %s\n"),
1650 				    localedir, ldir);
1651 			ndirs = getdirs(ldir, NULL, 0);
1652 			if (ndirs != 0) {
1653 				ldirs[0] = ldir;
1654 				ldirs[1] = NULL;
1655 				local = build_manpath(ldirs, 0);
1656 				if (force ||
1657 				    windex(local->secv, ldir, name) < 0)
1658 					mandir(local->secv, ldir, name);
1659 				free_manp(local);
1660 			}
1661 			/* release memory allocated by addlocale() */
1662 			free(ldir);
1663 		}
1664 
1665 		defaultmandir = 1;
1666 		/*
1667 		 * locale mandir not valid, man page in locale
1668 		 * mandir not found, or -a option present
1669 		 */
1670 		if (ndirs == 0 || !found || all) {
1671 			if (force || windex(p->secv, p->path, name) < 0)
1672 				mandir(p->secv, p->path, name);
1673 		}
1674 
1675 		if (found && !all)
1676 			break;
1677 	}
1678 
1679 	if (found) {
1680 		more(pages, nomore);
1681 	} else {
1682 		if (sargs) {
1683 			(void) fprintf(stderr, gettext("No entry for %s in "
1684 			    "section(s) %s of the manual.\n"),
1685 			    fullname, mansec);
1686 		} else {
1687 			(void) fprintf(stderr, gettext(
1688 			    "No manual entry for %s.\n"), fullname, mansec);
1689 		}
1690 
1691 		if (sman_no_man_no_sroff)
1692 			(void) fprintf(stderr, gettext("(An SGML manpage was "
1693 			    "found for '%s' but it cannot be displayed.)\n"),
1694 			    fullname, mansec);
1695 	}
1696 	sman_no_man_no_sroff = 0;
1697 	return (!found);
1698 }
1699 
1700 
1701 /*
1702  * For a specified manual directory,
1703  *	read, store, & sort section subdirs,
1704  *	for each section specified
1705  *		find and search matching subdirs
1706  */
1707 
1708 static void
mandir(char ** secv,char * path,char * name)1709 mandir(char **secv, char *path, char *name)
1710 {
1711 	DIR *dp;
1712 	char **dirv;
1713 	char **dv, **pdv;
1714 	int len, dslen, plen = PLEN;
1715 
1716 	if ((dp = opendir(path)) == 0) {
1717 /*
1718  * TRANSLATION_NOTE - message for man -d or catman -p
1719  * opendir(%s) returned 0
1720  */
1721 		if (debug)
1722 			(void) fprintf(stdout, gettext(
1723 			    " opendir on %s failed\n"), path);
1724 		return;
1725 	}
1726 
1727 /*
1728  * TRANSLATION_NOTE - message for man -d or catman -p
1729  * ex. mandir path = /usr/share/man/ja
1730  */
1731 	if (debug)
1732 		(void) printf(gettext("mandir path = %s\n"), path);
1733 
1734 	/*
1735 	 * sordir() allocates memory for dirv and dirv[].
1736 	 */
1737 	sortdir(dp, &dirv);
1738 	/*
1739 	 * Search in the order specified by MANSECTS
1740 	 */
1741 	for (; *secv; secv++) {
1742 /*
1743  * TRANSLATION_NOTE - message for man -d or catman -p
1744  * ex.  section = 3c
1745  */
1746 		DPRINTF(gettext("  section = %s\n"), *secv);
1747 		len = strlen(*secv);
1748 		for (dv = dirv; *dv; dv++) {
1749 			plen = PLEN;
1750 			if (*dv[0] == 's')
1751 				plen++;
1752 			dslen = strlen(*dv+plen);
1753 			if (dslen > len)
1754 				len = dslen;
1755 			if (**secv == '\\') {
1756 				if (!eq(*secv + 1, *dv+plen))
1757 					continue;
1758 			} else if (strncasecmp(*secv, *dv+plen, len) != 0) {
1759 				/* check to see if directory name changed */
1760 				if (!all &&
1761 				    (newsection = map_section(*secv, path))
1762 				    == NULL) {
1763 					continue;
1764 				}
1765 				if (newsection == NULL)
1766 					newsection = "";
1767 				if (!match(newsection, *dv+plen, len)) {
1768 					continue;
1769 				}
1770 			}
1771 
1772 			if (searchdir(path, *dv, name) == 0)
1773 				continue;
1774 
1775 			if (!all) {
1776 				/* release memory allocated by sortdir() */
1777 				pdv = dirv;
1778 				while (*pdv) {
1779 					free(*pdv);
1780 					pdv++;
1781 				}
1782 				(void) closedir(dp);
1783 				/* release memory allocated by sortdir() */
1784 				free(dirv);
1785 				return;
1786 			}
1787 			/*
1788 			 * if we found a match in the man dir skip
1789 			 * the corresponding cat dir if it exists
1790 			 */
1791 			if (all && **dv == 'm' && *(dv+1) &&
1792 			    eq(*(dv+1)+plen, *dv+plen))
1793 					dv++;
1794 		}
1795 	}
1796 	/* release memory allocated by sortdir() */
1797 	pdv = dirv;
1798 	while (*pdv) {
1799 		free(*pdv);
1800 		pdv++;
1801 	}
1802 	free(dirv);
1803 	(void) closedir(dp);
1804 }
1805 
1806 /*
1807  * Sort directories.
1808  */
1809 
1810 static void
sortdir(DIR * dp,char *** dirv)1811 sortdir(DIR *dp, char ***dirv)
1812 {
1813 	struct dirent *d;
1814 	char **dv;
1815 	int	maxentries = MAXDIRS;
1816 	int	entries = 0;
1817 
1818 	*dirv = (char **)malloc(sizeof (char *) * maxentries);
1819 	dv = *dirv;
1820 	while ((d = readdir(dp))) {	/* store dirs */
1821 		if (eq(d->d_name, ".") || eq(d->d_name, ".."))	/* ignore */
1822 			continue;
1823 
1824 		/* check if it matches sman, man, cat format */
1825 		if (match(d->d_name, SGMLDIR, PLEN+1) ||
1826 		    match(d->d_name, subdirs[0], PLEN) ||
1827 		    match(d->d_name, subdirs[1], PLEN)) {
1828 			*dv = malloc(strlen(d->d_name) + 1);
1829 			if (*dv == NULL)
1830 				malloc_error();
1831 			(void) strcpy(*dv, d->d_name);
1832 			dv++;
1833 			entries++;
1834 			if (entries == maxentries) {
1835 				maxentries += MAXDIRS;
1836 				*dirv = (char **)realloc(*dirv,
1837 				    sizeof (char *) * maxentries);
1838 				if (*dirv == NULL)
1839 					malloc_error();
1840 				dv = *dirv + entries;
1841 			}
1842 		}
1843 	}
1844 	*dv = 0;
1845 
1846 	qsort((void *)*dirv, dv - *dirv, sizeof (char *), cmp);
1847 
1848 }
1849 
1850 
1851 /*
1852  * Search a section subdirectory for a
1853  * given man page, return 1 for success
1854  */
1855 
1856 static int
searchdir(char * path,char * dir,char * name)1857 searchdir(char *path, char *dir, char *name)
1858 {
1859 	DIR *sdp;
1860 	struct dirent *sd;
1861 	char sectpath[MAXPATHLEN+1];
1862 	char file[MAXNAMLEN+1];
1863 	char dname[MAXPATHLEN+1];
1864 	char *last;
1865 	int nlen;
1866 
1867 /*
1868  * TRANSLATION_NOTE - message for man -d or catman -p
1869  * ex.   scanning = man3c
1870  */
1871 	DPRINTF(gettext("    scanning = %s\n"), dir);
1872 	(void) sprintf(sectpath, "%s/%s", path, dir);
1873 	(void) snprintf(file, MAXPATHLEN, "%s.", name);
1874 
1875 	if ((sdp = opendir(sectpath)) == 0) {
1876 		if (errno != ENOTDIR)	/* ignore matching cruft */
1877 			perror(sectpath);
1878 		return (0);
1879 	}
1880 	while ((sd = readdir(sdp))) {
1881 		last = strrchr(sd->d_name, '.');
1882 		nlen = last - sd->d_name;
1883 		(void) sprintf(dname, "%.*s.", nlen, sd->d_name);
1884 		if (eq(dname, file) || eq(sd->d_name, name)) {
1885 			if (no_sroff && *dir == 's') {
1886 				sman_no_man_no_sroff = 1;
1887 				return (0);
1888 			}
1889 			(void) format(path, dir, name, sd->d_name);
1890 			(void) closedir(sdp);
1891 			return (1);
1892 		}
1893 	}
1894 	(void) closedir(sdp);
1895 	return (0);
1896 }
1897 
1898 /*
1899  * Check the hash table of old directory names to see if there is a
1900  * new directory name.
1901  * Returns new directory name if a match; after checking to be sure
1902  * directory exists.
1903  * Otherwise returns NULL
1904  */
1905 
1906 static char *
map_section(char * section,char * path)1907 map_section(char *section, char *path)
1908 {
1909 	int i;
1910 	int len;
1911 	char fullpath[MAXPATHLEN];
1912 
1913 	if (list)  /* -l option fall through */
1914 		return (NULL);
1915 
1916 	for (i = 0; i <= ((sizeof (map)/sizeof (map[0]) - 1)); i++) {
1917 		if (strlen(section) > strlen(map[i].new_name)) {
1918 			len = strlen(section);
1919 		} else {
1920 			len = strlen(map[i].new_name);
1921 		}
1922 		if (match(section, map[i].old_name, len)) {
1923 			(void) sprintf(fullpath,
1924 			    "%s/sman%s", path, map[i].new_name);
1925 			if (!access(fullpath, R_OK | X_OK)) {
1926 				return (map[i].new_name);
1927 			} else {
1928 				return (NULL);
1929 			}
1930 		}
1931 	}
1932 
1933 	return (NULL);
1934 }
1935 
1936 
1937 /*
1938  * Use windex database for quick lookup of man pages
1939  * instead of mandir() (brute force search)
1940  */
1941 
1942 static int
windex(char ** secv,char * path,char * name)1943 windex(char **secv, char *path, char *name)
1944 {
1945 	FILE *fp;
1946 	struct stat sbuf;
1947 	struct suffix *sp;
1948 	struct suffix	psecs[MAXPAGES+1];
1949 	char whatfile[MAXPATHLEN+1];
1950 	char page[MAXPATHLEN+1];
1951 	char *matches[MAXPAGES];
1952 	char *file, *dir;
1953 	char **sv, **vp;
1954 	int len, dslen, exist, i;
1955 	int	found_in_windex = 0;
1956 	char *tmp[] = {0, 0, 0, 0};
1957 
1958 
1959 	(void) sprintf(whatfile, "%s/%s", path, WHATIS);
1960 	if ((fp = fopen(whatfile, "r")) == NULL) {
1961 		if (errno == ENOENT)
1962 			return (-1);
1963 		return (0);
1964 	}
1965 
1966 /*
1967  * TRANSLATION_NOTE - message for man -d or catman -p
1968  * ex. search in = /usr/share/man/ja/windex file
1969  */
1970 	if (debug)
1971 		(void) fprintf(stdout, gettext(
1972 		    " search in = %s file\n"), whatfile);
1973 
1974 	if (bfsearch(fp, matches, name, NULL) == 0) {
1975 		(void) fclose(fp);
1976 		return (-1); /* force search in mandir */
1977 	}
1978 
1979 	(void) fclose(fp);
1980 
1981 	/*
1982 	 * Save and split sections
1983 	 * section() allocates memory for sp->ds
1984 	 */
1985 	for (sp = psecs, vp = matches; *vp; vp++, sp++) {
1986 		if ((sp - psecs) < MAXPAGES) {
1987 			section(sp, *vp);
1988 		} else {
1989 			if (debug)
1990 				(void) fprintf(stderr, gettext(
1991 				    "too many sections in %s windex entry\n"),
1992 				    name);
1993 
1994 			/* Setting sp->ds to NULL signifies end-of-data. */
1995 			sp->ds = 0;
1996 			goto finish;
1997 		}
1998 	}
1999 
2000 	sp->ds = 0;
2001 
2002 	/*
2003 	 * Search in the order specified
2004 	 * by MANSECTS
2005 	 */
2006 	for (; *secv; secv++) {
2007 		len = strlen(*secv);
2008 
2009 /*
2010  * TRANSLATION_NOTE - message for man -d or catman -p
2011  * ex.  search an entry to match printf.3c
2012  */
2013 		if (debug)
2014 			(void) fprintf(stdout, gettext(
2015 			    "  search an entry to match %s.%s\n"), name, *secv);
2016 		/*
2017 		 * For every whatis entry that
2018 		 * was matched
2019 		 */
2020 		for (sp = psecs; sp->ds; sp++) {
2021 			dslen = strlen(sp->ds);
2022 			if (dslen > len)
2023 				len = dslen;
2024 			if (**secv == '\\') {
2025 				if (!eq(*secv + 1, sp->ds))
2026 					continue;
2027 			} else if (!match(*secv, sp->ds, len)) {
2028 				/* check to see if directory name changed */
2029 				if (!all &&
2030 				    (newsection = map_section(*secv, path))
2031 				    == NULL) {
2032 					continue;
2033 				}
2034 				if (newsection == NULL)
2035 					newsection = "";
2036 				if (!match(newsection, sp->ds, len)) {
2037 					continue;
2038 				}
2039 			}
2040 			/*
2041 			 * here to form "sman", "man", "cat"|"fmt" in
2042 			 * order
2043 			 */
2044 			if (!no_sroff) {
2045 				tmp[0] = SGMLDIR;
2046 				for (i = 1; i < 4; i++)
2047 					tmp[i] = subdirs[i-1];
2048 			} else {
2049 				for (i = 0; i < 3; i++)
2050 					tmp[i] = subdirs[i];
2051 			}
2052 
2053 			for (sv = tmp; *sv; sv++) {
2054 				(void) sprintf(page,
2055 				    "%s/%s%s/%s%s%s", path, *sv,
2056 				    sp->ds, name, *sp->fs ? "." : "",
2057 				    sp->fs);
2058 				exist = (stat(page, &sbuf) == 0);
2059 				if (exist)
2060 					break;
2061 			}
2062 			if (!exist) {
2063 				(void) fprintf(stderr, gettext(
2064 				    "%s entry incorrect:  %s(%s) not found.\n"),
2065 				    WHATIS, name, sp->ds);
2066 				continue;
2067 			}
2068 
2069 			file = strrchr(page, '/'), *file = 0;
2070 			dir = strrchr(page, '/');
2071 
2072 			/*
2073 			 * By now we have a match
2074 			 */
2075 			found_in_windex = 1;
2076 			(void) format(path, ++dir, name, ++file);
2077 
2078 			if (!all)
2079 				goto finish;
2080 		}
2081 	}
2082 finish:
2083 	/*
2084 	 * release memory allocated by section()
2085 	 */
2086 	sp = psecs;
2087 	while (sp->ds) {
2088 		free(sp->ds);
2089 		sp->ds = NULL;
2090 		sp++;
2091 	}
2092 
2093 	/*
2094 	 * If we didn't find a match, return failure as if we didn't find
2095 	 * the windex at all. Why? Well, if you create a windex, then upgrade
2096 	 * to a later release that contains new man pages, and forget to
2097 	 * recreate the windex (since we don't do that automatically), you
2098 	 * won't see any new man pages since they aren't in the windex.
2099 	 * Pretending we didn't see a windex at all if there are no matches
2100 	 * forces a search of the underlying directory. After all, the
2101 	 * goal of the windex is to enable searches (man -k) and speed things
2102 	 * up, not to _prevent_ you from seeing new man pages, so this seems
2103 	 * ok. The only problem is when there are multiple entries (different
2104 	 * sections), and some are in and some are out. Say you do 'man ls',
2105 	 * and ls(1) isn't in the windex, but ls(1B) is. In that case, we
2106 	 * will find a match in ls(1B), and you'll see that man page.
2107 	 * That doesn't seem bad since if you specify the section the search
2108 	 * will be restricted too. So in the example above, if you do
2109 	 * 'man -s 1 ls' you'll get ls(1).
2110 	 */
2111 	if (found_in_windex)
2112 		return (0);
2113 	else
2114 		return (-1);
2115 }
2116 
2117 
2118 /*
2119  * Return pointers to the section-spec
2120  * and file-suffix of a whatis entry
2121  */
2122 
2123 static void
section(struct suffix * sp,char * s)2124 section(struct suffix *sp, char *s)
2125 {
2126 	char *lp, *p;
2127 
2128 	lp = strchr(s, '(');
2129 	p = strchr(s, ')');
2130 
2131 	if (++lp == 0 || p == 0 || lp == p) {
2132 		(void) fprintf(stderr,
2133 		    gettext("mangled windex entry:\n\t%s\n"), s);
2134 		return;
2135 	}
2136 	*p = 0;
2137 
2138 	/*
2139 	 * copy the string pointed to by lp
2140 	 */
2141 	lp = strdup(lp);
2142 	if (lp == NULL)
2143 		malloc_error();
2144 	/*
2145 	 * release memory in s
2146 	 * s has been allocated memory in bfsearch()
2147 	 */
2148 	free(s);
2149 
2150 	lower(lp);
2151 
2152 	/*
2153 	 * split section-specifier if file-name
2154 	 * suffix differs from section-suffix
2155 	 */
2156 	sp->ds = lp;
2157 	if ((p = strchr(lp, '/'))) {
2158 		*p++ = 0;
2159 		sp->fs = p;
2160 	} else
2161 		sp->fs = lp;
2162 }
2163 
2164 
2165 /*
2166  * Binary file search to find matching man
2167  *   pages in whatis database.
2168  */
2169 
2170 static int
bfsearch(FILE * fp,char ** matchv,char * key,char ** secv)2171 bfsearch(FILE *fp, char **matchv, char *key, char **secv)
2172 {
2173 	char entry[BUFSIZ];
2174 	char **vp;
2175 	long top, bot, mid;
2176 	int	c;
2177 
2178 	vp = matchv;
2179 	bot = 0;
2180 	(void) fseek(fp, 0L, 2);
2181 	top = ftell(fp);
2182 	for (;;) {
2183 		mid = (top+bot)/2;
2184 		(void) fseek(fp, mid, 0);
2185 		do {
2186 			c = getc(fp);
2187 			mid++;
2188 		} while (c != EOF && c != '\n');
2189 		if (fgets(entry, sizeof (entry), fp) == NULL)
2190 			break;
2191 		switch (compare(key, entry, secv)) {
2192 		case -2:
2193 		case -1:
2194 		case 0:
2195 			if (top <= mid)
2196 				break;
2197 			top = mid;
2198 			continue;
2199 		case 1:
2200 		case 2:
2201 			bot = mid;
2202 			continue;
2203 		}
2204 		break;
2205 	}
2206 	(void) fseek(fp, bot, 0);
2207 	while (ftell(fp) < top) {
2208 		if (fgets(entry, sizeof (entry), fp) == NULL) {
2209 			*matchv = 0;
2210 			return (matchv - vp);
2211 		}
2212 		switch (compare(key, entry, secv)) {
2213 		case -2:
2214 			*matchv = 0;
2215 			return (matchv - vp);
2216 		case -1:
2217 		case 0:
2218 			*matchv = strdup(entry);
2219 			if (*matchv == NULL)
2220 				malloc_error();
2221 			else
2222 				matchv++;
2223 			break;
2224 		case 1:
2225 		case 2:
2226 			continue;
2227 		}
2228 		break;
2229 	}
2230 	while (fgets(entry, sizeof (entry), fp)) {
2231 		switch (compare(key, entry, secv)) {
2232 		case -1:
2233 		case 0:
2234 			*matchv = strdup(entry);
2235 			if (*matchv == NULL)
2236 				malloc_error();
2237 			else
2238 				matchv++;
2239 			continue;
2240 		}
2241 		break;
2242 	}
2243 	*matchv = 0;
2244 	return (matchv - vp);
2245 }
2246 
2247 static int
compare(char * key,char * entry,char ** secv)2248 compare(char *key, char *entry, char **secv)
2249 {
2250 	char	*entbuf;
2251 	char	*s;
2252 	int	comp, mlen;
2253 	int	mbcurmax = MB_CUR_MAX;
2254 	char 	*secp = NULL;
2255 	int	rv;
2256 	int	eblen;
2257 
2258 	entbuf = strdup(entry);
2259 	if (entbuf == NULL) {
2260 		malloc_error();
2261 	}
2262 	eblen = strlen(entbuf);
2263 
2264 	s = entbuf;
2265 	while (*s) {
2266 		if (*s == '\t' || *s == ' ') {
2267 			*s = '\0';
2268 			break;
2269 		}
2270 		mlen = mblen(s, mbcurmax);
2271 		if (mlen == -1) {
2272 			(void) fprintf(stderr, gettext(
2273 			    "Invalid character in windex file.\n"));
2274 			exit(1);
2275 		}
2276 		s += mlen;
2277 	}
2278 	/*
2279 	 * Find the section within parantheses
2280 	 */
2281 	if (secv != NULL && (s - entbuf) < eblen) {
2282 		if ((secp = strchr(s + 1, ')')) != NULL) {
2283 			*secp = '\0';
2284 			if ((secp = strchr(s + 1, '(')) != NULL) {
2285 				secp++;
2286 			}
2287 		}
2288 	}
2289 
2290 	comp = strcmp(key, entbuf);
2291 	if (comp == 0) {
2292 		if (secp == NULL) {
2293 			rv = 0;
2294 		} else {
2295 			while (*secv != NULL) {
2296 				if ((strcmp(*secv, secp)) == 0) {
2297 					rv = 0;
2298 					break;
2299 				}
2300 				secv++;
2301 			}
2302 		}
2303 	} else if (comp < 0) {
2304 		rv = -2;
2305 	} else {
2306 		rv = 2;
2307 	}
2308 	free(entbuf);
2309 	return (rv);
2310 }
2311 
2312 
2313 /*
2314  * Format a man page and follow .so references
2315  * if necessary.
2316  */
2317 
2318 static int
format(char * path,char * dir,char * name,char * pg)2319 format(char *path, char *dir, char *name, char *pg)
2320 {
2321 	char manpname[MAXPATHLEN+1], catpname[MAXPATHLEN+1];
2322 	char manpname_sgml[MAXPATHLEN+1], smantmpname[MAXPATHLEN+1];
2323 	char soed[MAXPATHLEN+1], soref[MAXPATHLEN+1];
2324 	char manbuf[BUFSIZ], cmdbuf[BUFSIZ], tmpbuf[BUFSIZ];
2325 	char tmpdir[MAXPATHLEN+1];
2326 	int socount, updatedcat, regencat;
2327 	struct stat mansb, catsb, smansb;
2328 	char *tmpname;
2329 	int catonly = 0;
2330 	struct stat statb;
2331 	int plen = PLEN;
2332 	FILE *md;
2333 	int tempfd;
2334 	ssize_t	count;
2335 	int	temp, sgml_flag = 0, check_flag = 0;
2336 	char prntbuf[BUFSIZ + 1];
2337 	char *ptr;
2338 	char *new_m;
2339 	char	*tmpsubdir;
2340 
2341 	found++;
2342 
2343 	if (*dir != 'm' && *dir != 's')
2344 		catonly++;
2345 
2346 
2347 	if (*dir == 's') {
2348 		tmpsubdir = SGMLDIR;
2349 		++plen;
2350 		(void) sprintf(manpname_sgml, "%s/man%s/%s",
2351 		    path, dir+plen, pg);
2352 	} else
2353 		tmpsubdir = MANDIRNAME;
2354 
2355 	if (list) {
2356 		(void) printf(gettext("%s (%s)\t-M %s\n"),
2357 		    name, dir+plen, path);
2358 		return (-1);
2359 	}
2360 
2361 	(void) sprintf(manpname, "%s/%s%s/%s", path, tmpsubdir, dir+plen, pg);
2362 	(void) sprintf(catpname, "%s/%s%s/%s", path, subdirs[1], dir+plen, pg);
2363 
2364 	(void) sprintf(smantmpname, "%s/%s%s/%s", path, SGMLDIR, dir+plen, pg);
2365 
2366 /*
2367  * TRANSLATION_NOTE - message for man -d or catman -p
2368  * ex.  unformatted = /usr/share/man/ja/man3s/printf.3s
2369  */
2370 	DPRINTF(gettext(
2371 	    "      unformatted = %s\n"), catonly ? "" : manpname);
2372 /*
2373  * TRANSLATION_NOTE - message for man -d or catman -p
2374  * ex.  formatted = /usr/share/man/ja/cat3s/printf.3s
2375  */
2376 	DPRINTF(gettext(
2377 	    "      formatted = %s\n"), catpname);
2378 
2379 	/*
2380 	 * Take care of indirect references to other man pages;
2381 	 * i.e., resolve files containing only ".so manx/file.x".
2382 	 * We follow .so chains, replacing title with the .so'ed
2383 	 * file at each stage, and keeping track of how many times
2384 	 * we've done so, so that we can avoid looping.
2385 	 */
2386 	*soed = 0;
2387 	socount = 0;
2388 	for (;;) {
2389 		FILE *md;
2390 		char *cp;
2391 		char *s;
2392 		char *new_s;
2393 
2394 		if (catonly)
2395 			break;
2396 		/*
2397 		 * Grab manpname's first line, stashing it in manbuf.
2398 		 */
2399 
2400 
2401 		if ((md = fopen(manpname, "r")) == NULL) {
2402 			if (*soed && errno == ENOENT) {
2403 				(void) fprintf(stderr,
2404 				    gettext("Can't find referent of "
2405 				    ".so in %s\n"), soed);
2406 				(void) fflush(stderr);
2407 				return (-1);
2408 			}
2409 			perror(manpname);
2410 			return (-1);
2411 		}
2412 
2413 		/*
2414 		 * If this is a directory, just ignore it.
2415 		 */
2416 		if (fstat(fileno(md), &statb) == NULL) {
2417 			if (S_ISDIR(statb.st_mode)) {
2418 				if (debug) {
2419 					(void) fprintf(stderr,
2420 					    "\tignoring directory %s\n",
2421 					    manpname);
2422 					(void) fflush(stderr);
2423 				}
2424 				(void) fclose(md);
2425 				return (-1);
2426 			}
2427 		}
2428 
2429 		if (fgets(manbuf, BUFSIZ-1, md) == NULL) {
2430 			(void) fclose(md);
2431 			(void) fprintf(stderr, gettext("%s: null file\n"),
2432 			    manpname);
2433 			(void) fflush(stderr);
2434 			return (-1);
2435 		}
2436 		(void) fclose(md);
2437 
2438 		if (strncmp(manbuf, DOT_SO, sizeof (DOT_SO) - 1))
2439 			break;
2440 so_again:	if (++socount > SOLIMIT) {
2441 			(void) fprintf(stderr, gettext(".so chain too long\n"));
2442 			(void) fflush(stderr);
2443 			return (-1);
2444 		}
2445 		s = manbuf + sizeof (DOT_SO) - 1;
2446 		if ((check_flag == 1) && ((new_s = strrchr(s, '/')) != NULL)) {
2447 				new_s++;
2448 				(void) sprintf(s, "%s%s/%s",
2449 				    tmpsubdir, dir+plen, new_s);
2450 		}
2451 
2452 		cp = strrchr(s, '\n');
2453 		if (cp)
2454 			*cp = '\0';
2455 		/*
2456 		 * Compensate for sloppy typists by stripping
2457 		 * trailing white space.
2458 		 */
2459 		cp = s + strlen(s);
2460 		while (--cp >= s && (*cp == ' ' || *cp == '\t'))
2461 			*cp = '\0';
2462 
2463 		/*
2464 		 * Go off and find the next link in the chain.
2465 		 */
2466 		(void) strcpy(soed, manpname);
2467 		(void) strcpy(soref, s);
2468 		(void) sprintf(manpname, "%s/%s", path, s);
2469 /*
2470  * TRANSLATION_NOTE - message for man -d or catman -p
2471  * ex.  .so ref = man3c/string.3c
2472  */
2473 		DPRINTF(gettext(".so ref = %s\n"), s);
2474 	}
2475 
2476 	/*
2477 	 * Make symlinks if so'ed and cattin'
2478 	 */
2479 	if (socount && catmando) {
2480 		(void) sprintf(cmdbuf, "cd %s; rm -f %s; ln -s ../%s%s %s",
2481 		    path, catpname, subdirs[1], soref+plen, catpname);
2482 		(void) sys(cmdbuf);
2483 		return (1);
2484 	}
2485 
2486 	/*
2487 	 * Obtain the cat page that corresponds to the man page.
2488 	 * If it already exists, is up to date, and if we haven't
2489 	 * been told not to use it, use it as it stands.
2490 	 */
2491 	regencat = updatedcat = 0;
2492 	if (compargs || (!catonly && stat(manpname, &mansb) >= 0 &&
2493 	    (stat(catpname, &catsb) < 0 || catsb.st_mtime < mansb.st_mtime)) ||
2494 	    (access(catpname, R_OK) != 0)) {
2495 		/*
2496 		 * Construct a shell command line for formatting manpname.
2497 		 * The resulting file goes initially into /tmp.  If possible,
2498 		 * it will later be moved to catpname.
2499 		 */
2500 
2501 		int pipestage = 0;
2502 		int needcol = 0;
2503 		char *cbp = cmdbuf;
2504 
2505 		regencat = updatedcat = 1;
2506 
2507 		if (!catmando && !debug && !check_flag) {
2508 			(void) fprintf(stderr, gettext(
2509 			    "Reformatting page.  Please Wait..."));
2510 			if (sargs && (newsection != NULL) &&
2511 			    (*newsection != '\0')) {
2512 				(void) fprintf(stderr, gettext(
2513 				    "\nThe directory name has been changed "
2514 				    "to %s\n"), newsection);
2515 			}
2516 			(void) fflush(stderr);
2517 		}
2518 
2519 		/*
2520 		 * in catman command, if the file exists in sman dir already,
2521 		 * don't need to convert the file in man dir to cat dir
2522 		 */
2523 
2524 		if (!no_sroff && catmando &&
2525 		    match(tmpsubdir, MANDIRNAME, PLEN) &&
2526 		    stat(smantmpname, &smansb) >= 0)
2527 			return (1);
2528 
2529 		/*
2530 		 * cd to path so that relative .so commands will work
2531 		 * correctly
2532 		 */
2533 		(void) sprintf(cbp, "cd %s; ", path);
2534 		cbp += strlen(cbp);
2535 
2536 
2537 		/*
2538 		 * check to see whether it is a sgml file
2539 		 * assume sgml symbol(>!DOCTYPE) can be found in the first
2540 		 * BUFSIZ bytes
2541 		 */
2542 
2543 		if ((temp = open(manpname, 0)) == -1) {
2544 				perror(manpname);
2545 				return (-1);
2546 		}
2547 
2548 		if ((count = read(temp, prntbuf, BUFSIZ)) <= 0) {
2549 				perror(manpname);
2550 				return (-1);
2551 		}
2552 
2553 		prntbuf[count] = '\0';	/* null terminate */
2554 		ptr = prntbuf;
2555 		if (sgmlcheck((const char *)ptr) == 1) {
2556 			sgml_flag = 1;
2557 			if (defaultmandir && *localedir) {
2558 				(void) sprintf(cbp, "LC_MESSAGES=C %s %s ",
2559 				    SROFF_CMD, manpname);
2560 			} else {
2561 				(void) sprintf(cbp, "%s %s ",
2562 				    SROFF_CMD, manpname);
2563 			}
2564 			cbp += strlen(cbp);
2565 		} else if (*dir == 's') {
2566 			(void) close(temp);
2567 			return (-1);
2568 		}
2569 		(void) close(temp);
2570 
2571 		/*
2572 		 * Check for special formatting requirements by examining
2573 		 * manpname's first line preprocessor specifications.
2574 		 */
2575 
2576 		if (strncmp(manbuf, PREPROC_SPEC,
2577 		    sizeof (PREPROC_SPEC) - 1) == 0) {
2578 			char *ptp;
2579 
2580 			ptp = manbuf + sizeof (PREPROC_SPEC) - 1;
2581 			while (*ptp && *ptp != '\n') {
2582 				const struct preprocessor *pp;
2583 
2584 				/*
2585 				 * Check for a preprocessor we know about.
2586 				 */
2587 				for (pp = preprocessors; pp->p_tag; pp++) {
2588 					if (pp->p_tag == *ptp)
2589 						break;
2590 				}
2591 				if (pp->p_tag == 0) {
2592 					(void) fprintf(stderr,
2593 					    gettext("unknown preprocessor "
2594 					    "specifier %c\n"), *ptp);
2595 					(void) fflush(stderr);
2596 					return (-1);
2597 				}
2598 
2599 				/*
2600 				 * Add it to the pipeline.
2601 				 */
2602 				(void) sprintf(cbp, "%s %s |",
2603 				    troffit ? pp->p_troff : pp->p_nroff,
2604 				    pipestage++ == 0 ? manpname :
2605 				    pp->p_stdin_char);
2606 				cbp += strlen(cbp);
2607 
2608 				/*
2609 				 * Special treatment: if tbl is among the
2610 				 * preprocessors and we'll process with
2611 				 * nroff, we have to pass things through
2612 				 * col at the end of the pipeline.
2613 				 */
2614 				if (pp->p_tag == 't' && !troffit)
2615 					needcol++;
2616 
2617 				ptp++;
2618 			}
2619 		}
2620 
2621 		/*
2622 		 * if catman, use the cat page name
2623 		 * otherwise, dup template and create another
2624 		 * (needed for multiple pages)
2625 		 */
2626 		if (catmando)
2627 			tmpname = catpname;
2628 		else {
2629 			tmpname = strdup(TEMPLATE);
2630 			if (tmpname == NULL)
2631 				malloc_error();
2632 			(void) close(mkstemp(tmpname));
2633 		}
2634 
2635 		if (! Tflag) {
2636 			if (*localedir != '\0') {
2637 				(void) sprintf(macros, "%s/%s", path, MACROF);
2638 /*
2639  * TRANSLATION_NOTE - message for man -d or catman -p
2640  * ex.  locale macros = /usr/share/man/ja/tmac.an
2641  */
2642 				if (debug)
2643 					(void) printf(gettext(
2644 					    "\nlocale macros = %s "),
2645 					    macros);
2646 				if (stat(macros, &statb) < 0)
2647 					(void) strcpy(macros, TMAC_AN);
2648 /*
2649  * TRANSLATION_NOTE - message for man -d or catman -p
2650  * ex.  macros = /usr/share/man/ja/tman.an
2651  */
2652 				if (debug)
2653 					(void) printf(gettext(
2654 					    "\nmacros = %s\n"),
2655 					    macros);
2656 			}
2657 		}
2658 
2659 		tmpdir[0] = '\0';
2660 		if (sgml_flag == 1) {
2661 			if (check_flag == 0) {
2662 				strcpy(tmpdir, "/tmp/sman_XXXXXX");
2663 				if ((tempfd = mkstemp(tmpdir)) == -1) {
2664 					(void) fprintf(stderr, gettext(
2665 					    "%s: null file\n"), tmpdir);
2666 					(void) fflush(stderr);
2667 					return (-1);
2668 				}
2669 
2670 				if (debug)
2671 					close(tempfd);
2672 
2673 				(void) sprintf(tmpbuf, "%s > %s",
2674 				    cmdbuf, tmpdir);
2675 				if (sys(tmpbuf)) {
2676 /*
2677  * TRANSLATION_NOTE - message for man -d or catman -p
2678  * Error message if sys(%s) failed
2679  */
2680 					(void) fprintf(stderr, gettext(
2681 					    "sys(%s) fail!\n"), tmpbuf);
2682 					(void) fprintf(stderr,
2683 					    gettext(" aborted (sorry)\n"));
2684 					(void) fflush(stderr);
2685 					/* release memory for tmpname */
2686 					if (!catmando) {
2687 						(void) unlink(tmpdir);
2688 						(void) unlink(tmpname);
2689 						free(tmpname);
2690 					}
2691 					return (-1);
2692 				} else if (debug == 0) {
2693 					if ((md = fdopen(tempfd, "r"))
2694 					    == NULL) {
2695 						(void) fprintf(stderr, gettext(
2696 						    "%s: null file\n"), tmpdir);
2697 						(void) fflush(stderr);
2698 						close(tempfd);
2699 						/* release memory for tmpname */
2700 						if (!catmando)
2701 							free(tmpname);
2702 						return (-1);
2703 					}
2704 
2705 					/* if the file is empty, */
2706 					/* it's a fragment, do nothing */
2707 					if (fgets(manbuf, BUFSIZ-1, md)
2708 					    == NULL) {
2709 						(void) fclose(md);
2710 						/* release memory for tmpname */
2711 						if (!catmando)
2712 							free(tmpname);
2713 						return (1);
2714 					}
2715 					(void) fclose(md);
2716 
2717 					if (strncmp(manbuf, DOT_SO,
2718 					    sizeof (DOT_SO) - 1) == 0) {
2719 						if (!compargs) {
2720 						check_flag = 1;
2721 						(void) unlink(tmpdir);
2722 						(void) unlink(tmpname);
2723 						/* release memory for tmpname */
2724 						if (!catmando)
2725 							free(tmpname);
2726 						goto so_again;
2727 						} else {
2728 							(void) unlink(tmpdir);
2729 						strcpy(tmpdir,
2730 						    "/tmp/sman_XXXXXX");
2731 						tempfd = mkstemp(tmpdir);
2732 						if ((tempfd == -1) ||
2733 						    (md = fdopen(tempfd, "w"))
2734 						    == NULL) {
2735 							(void) fprintf(stderr,
2736 							    gettext(
2737 							    "%s: null file\n"),
2738 							    tmpdir);
2739 							(void) fflush(stderr);
2740 							if (tempfd != -1)
2741 								close(tempfd);
2742 						/* release memory for tmpname */
2743 							if (!catmando)
2744 								free(tmpname);
2745 							return (-1);
2746 						}
2747 				if ((new_m = strrchr(manbuf, '/')) != NULL) {
2748 		(void) fprintf(md, ".so man%s%s\n", dir+plen, new_m);
2749 							} else {
2750 /*
2751  * TRANSLATION_NOTE - message for catman -c
2752  * Error message if unable to get file name
2753  */
2754 				(void) fprintf(stderr,
2755 				    gettext("file not found\n"));
2756 				(void) fflush(stderr);
2757 				return (-1);
2758 				}
2759 							(void) fclose(md);
2760 						}
2761 					}
2762 				}
2763 				if (catmando && compargs)
2764 					(void) sprintf(cmdbuf, "cat %s > %s",
2765 					    tmpdir, manpname_sgml);
2766 				else
2767 	(void) sprintf(cmdbuf, " cat %s | tbl | eqn | %s %s - %s > %s",
2768 	    tmpdir, troffit ? troffcmd : "nroff -u0 -Tlp",
2769 	    macros, troffit ? "" : " | col -x", tmpname);
2770 			} else
2771 				if (catmando && compargs)
2772 					(void) sprintf(cbp, " > %s",
2773 					    manpname_sgml);
2774 				else
2775 	(void) sprintf(cbp, " | tbl | eqn | %s %s - %s > %s",
2776 	    troffit ? troffcmd : "nroff -u0 -Tlp",
2777 	    macros, troffit ? "" : " | col -x", tmpname);
2778 
2779 		} else
2780 	(void) sprintf(cbp, "%s %s %s%s > %s",
2781 	    troffit ? troffcmd : "nroff -u0 -Tlp",
2782 	    macros, pipestage == 0 ? manpname : "-",
2783 	    troffit ? "" : " | col -x", tmpname);
2784 
2785 		/* Reformat the page. */
2786 		if (sys(cmdbuf)) {
2787 /*
2788  * TRANSLATION_NOTE - message for man -d or catman -p
2789  * Error message if sys(%s) failed
2790  */
2791 			(void) fprintf(stderr, gettext(
2792 			    "sys(%s) fail!\n"), cmdbuf);
2793 			(void) fprintf(stderr, gettext(" aborted (sorry)\n"));
2794 			(void) fflush(stderr);
2795 			(void) unlink(tmpname);
2796 			/* release memory for tmpname */
2797 			if (!catmando)
2798 				free(tmpname);
2799 			return (-1);
2800 		}
2801 
2802 		if (tmpdir[0] != '\0')
2803 			(void) unlink(tmpdir);
2804 
2805 		if (catmando)
2806 			return (1);
2807 
2808 		/*
2809 		 * Attempt to move the cat page to its proper home.
2810 		 */
2811 		(void) sprintf(cmdbuf,
2812 		    "trap '' 1 15; /usr/bin/mv -f %s %s 2> /dev/null",
2813 		    tmpname,
2814 		    catpname);
2815 		if (sys(cmdbuf))
2816 			updatedcat = 0;
2817 		else if (debug == 0)
2818 			(void) chmod(catpname, 0644);
2819 
2820 		if (debug) {
2821 			/* release memory for tmpname */
2822 			if (!catmando)
2823 				free(tmpname);
2824 			(void) unlink(tmpname);
2825 			return (1);
2826 		}
2827 
2828 		(void) fprintf(stderr, gettext(" done\n"));
2829 		(void) fflush(stderr);
2830 	}
2831 
2832 	/*
2833 	 * Save file name (dup if necessary)
2834 	 * to view later
2835 	 * fix for 1123802 - don't save names if we are invoked as catman
2836 	 */
2837 	if (!catmando) {
2838 		char	**tmpp;
2839 		int	dup;
2840 		char	*newpage;
2841 
2842 		if (regencat && !updatedcat)
2843 			newpage = tmpname;
2844 		else {
2845 			newpage = strdup(catpname);
2846 			if (newpage == NULL)
2847 				malloc_error();
2848 		}
2849 		/* make sure we don't add a dup */
2850 		dup = 0;
2851 		for (tmpp = pages; tmpp < endp; tmpp++) {
2852 			if (strcmp(*tmpp, newpage) == 0) {
2853 				dup = 1;
2854 				break;
2855 			}
2856 		}
2857 		if (!dup)
2858 			*endp++ = newpage;
2859 		if (endp >= &pages[MAXPAGES]) {
2860 			fprintf(stderr,
2861 			    gettext("Internal pages array overflow!\n"));
2862 			exit(1);
2863 		}
2864 	}
2865 
2866 	return (regencat);
2867 }
2868 
2869 /*
2870  * Add <localedir> to the path.
2871  */
2872 
2873 static char *
addlocale(char * path)2874 addlocale(char *path)
2875 {
2876 
2877 	char *tmp;
2878 
2879 	tmp = malloc(strlen(path) + strlen(localedir) + 2);
2880 	if (tmp == NULL)
2881 		malloc_error();
2882 	(void) sprintf(tmp, "%s/%s", path, localedir);
2883 	return (tmp);
2884 
2885 }
2886 
2887 /*
2888  * From the configuration file "man.cf", get the order of suffices of
2889  * sub-mandirs to be used in the search path for a given mandir.
2890  */
2891 
2892 static char *
check_config(char * path)2893 check_config(char *path)
2894 {
2895 	FILE *fp;
2896 	static char submandir[BUFSIZ];
2897 	char *sect;
2898 	char fname[MAXPATHLEN];
2899 
2900 	(void) sprintf(fname, "%s/%s", path, CONFIG);
2901 
2902 	if ((fp = fopen(fname, "r")) == NULL)
2903 		return (NULL);
2904 	else {
2905 		if (get_manconfig(fp, submandir) == -1) {
2906 			(void) fclose(fp);
2907 			return (NULL);
2908 		}
2909 
2910 		(void) fclose(fp);
2911 
2912 		sect = strchr(submandir, '=');
2913 		if (sect != NULL)
2914 			return (++sect);
2915 		else
2916 			return (NULL);
2917 	}
2918 }
2919 
2920 /*
2921  *  This routine is for getting the MANSECTS entry from man.cf.
2922  *  It sets submandir to the line in man.cf that contains
2923  *	MANSECTS=sections[,sections]...
2924  */
2925 
2926 static int
get_manconfig(FILE * fp,char * submandir)2927 get_manconfig(FILE *fp, char *submandir)
2928 {
2929 	char *s, *t, *rc;
2930 	char buf[BUFSIZ];
2931 
2932 	while ((rc = fgets(buf, sizeof (buf), fp)) != NULL) {
2933 
2934 		/*
2935 		 * skip leading blanks
2936 		 */
2937 		for (t = buf; *t != '\0'; t++) {
2938 			if (!isspace(*t))
2939 				break;
2940 		}
2941 		/*
2942 		 * skip line that starts with '#' or empty line
2943 		 */
2944 		if (*t == '#' || *t == '\0')
2945 			continue;
2946 
2947 		if (strstr(buf, "MANSECTS") != NULL)
2948 			break;
2949 	}
2950 
2951 	/*
2952 	 * the man.cf file doesn't have a MANSECTS entry
2953 	 */
2954 	if (rc == NULL)
2955 		return (-1);
2956 
2957 	s = strchr(buf, '\n');
2958 	*s = '\0';	/* replace '\n' with '\0' */
2959 
2960 	(void) strcpy(submandir, buf);
2961 	return (0);
2962 }
2963 
2964 static void
malloc_error(void)2965 malloc_error(void)
2966 {
2967 	(void) fprintf(stderr, gettext(
2968 	    "Memory allocation failed.\n"));
2969 	exit(1);
2970 }
2971 
2972 static int
sgmlcheck(const char * s1)2973 sgmlcheck(const char *s1)
2974 {
2975 	const char	*s2 = SGML_SYMBOL;
2976 	int	len;
2977 
2978 	while (*s1) {
2979 		/*
2980 		 * Assume the first character of SGML_SYMBOL(*s2) is '<'.
2981 		 * Therefore, not necessary to do toupper(*s1) here.
2982 		 */
2983 		if (*s1 == *s2) {
2984 			/*
2985 			 * *s1 is '<'.  Check the following substring matches
2986 			 * with "!DOCTYPE".
2987 			 */
2988 			s1++;
2989 			if (strncasecmp(s1, s2 + 1, SGML_SYMBOL_LEN - 1)
2990 			    == 0) {
2991 				/*
2992 				 * SGML_SYMBOL found
2993 				 */
2994 				return (1);
2995 			}
2996 			continue;
2997 		} else if (isascii(*s1)) {
2998 			/*
2999 			 * *s1 is an ASCII char
3000 			 * Skip one character
3001 			 */
3002 			s1++;
3003 			continue;
3004 		} else {
3005 			/*
3006 			 * *s1 is a non-ASCII char or
3007 			 * the first byte of the multibyte char.
3008 			 * Skip one character
3009 			 */
3010 			len = mblen(s1, MB_CUR_MAX);
3011 			if (len == -1)
3012 				len = 1;
3013 			s1 += len;
3014 			continue;
3015 		}
3016 	}
3017 	/*
3018 	 * SGML_SYMBOL not found
3019 	 */
3020 	return (0);
3021 }
3022 
3023 /*
3024  * Initializes the bintoman array with appropriate device and inode info
3025  */
3026 
3027 static void
init_bintoman(void)3028 init_bintoman(void)
3029 {
3030 	int i;
3031 	struct stat sb;
3032 
3033 	for (i = 0; bintoman[i].bindir != NULL; i++) {
3034 		if (stat(bintoman[i].bindir, &sb) == 0) {
3035 			bintoman[i].dev = sb.st_dev;
3036 			bintoman[i].ino = sb.st_ino;
3037 		} else {
3038 			bintoman[i].dev = NODEV;
3039 		}
3040 	}
3041 }
3042 
3043 /*
3044  * If a duplicate is found, return 1
3045  * If a duplicate is not found, add it to the dupnode list and return 0
3046  */
3047 static int
dupcheck(struct man_node * mnp,struct dupnode ** dnp)3048 dupcheck(struct man_node *mnp, struct dupnode **dnp)
3049 {
3050 	struct dupnode	*curdnp;
3051 	struct secnode	*cursnp;
3052 	struct stat 	sb;
3053 	int 		i;
3054 	int		rv = 1;
3055 	int		dupfound;
3056 
3057 	/*
3058 	 * If the path doesn't exist, treat it as a duplicate
3059 	 */
3060 	if (stat(mnp->path, &sb) != 0) {
3061 		return (1);
3062 	}
3063 
3064 	/*
3065 	 * If no sections were found in the man dir, treat it as duplicate
3066 	 */
3067 	if (mnp->secv == NULL) {
3068 		return (1);
3069 	}
3070 
3071 	/*
3072 	 * Find the dupnode structure for the previous time this directory
3073 	 * was looked at.  Device and inode numbers are compared so that
3074 	 * directories that are reached via different paths (e.g. /usr/man vs.
3075 	 * /usr/share/man) are treated as equivalent.
3076 	 */
3077 	for (curdnp = *dnp; curdnp != NULL; curdnp = curdnp->next) {
3078 		if (curdnp->dev == sb.st_dev && curdnp->ino == sb.st_ino) {
3079 			break;
3080 		}
3081 	}
3082 
3083 	/*
3084 	 * First time this directory has been seen.  Add a new node to the
3085 	 * head of the list.  Since all entries are guaranteed to be unique
3086 	 * copy all sections to new node.
3087 	 */
3088 	if (curdnp == NULL) {
3089 		if ((curdnp = calloc(1, sizeof (struct dupnode))) == NULL) {
3090 			malloc_error();
3091 		}
3092 		for (i = 0; mnp->secv[i] != NULL; i++) {
3093 			if ((cursnp = calloc(1, sizeof (struct secnode)))
3094 			    == NULL) {
3095 				malloc_error();
3096 			}
3097 			cursnp->next = curdnp->secl;
3098 			curdnp->secl = cursnp;
3099 			if ((cursnp->secp = strdup(mnp->secv[i])) == NULL) {
3100 				malloc_error();
3101 			}
3102 		}
3103 		curdnp->dev = sb.st_dev;
3104 		curdnp->ino = sb.st_ino;
3105 		curdnp->next = *dnp;
3106 		*dnp = curdnp;
3107 		return (0);
3108 	}
3109 
3110 	/*
3111 	 * Traverse the section vector in the man_node and the section list
3112 	 * in dupnode cache to eliminate all duplicates from man_node
3113 	 */
3114 	for (i = 0; mnp->secv[i] != NULL; i++) {
3115 		dupfound = 0;
3116 		for (cursnp = curdnp->secl; cursnp != NULL;
3117 		    cursnp = cursnp->next) {
3118 			if (strcmp(mnp->secv[i], cursnp->secp) == 0) {
3119 				dupfound = 1;
3120 				break;
3121 			}
3122 		}
3123 		if (dupfound) {
3124 			mnp->secv[i][0] = '\0';
3125 			continue;
3126 		}
3127 
3128 
3129 		/*
3130 		 * Update curdnp and set return value to indicate that this
3131 		 * was not all duplicates.
3132 		 */
3133 		if ((cursnp = calloc(1, sizeof (struct secnode))) == NULL) {
3134 			malloc_error();
3135 		}
3136 		cursnp->next = curdnp->secl;
3137 		curdnp->secl = cursnp;
3138 		if ((cursnp->secp = strdup(mnp->secv[i])) == NULL) {
3139 			malloc_error();
3140 		}
3141 		rv = 0;
3142 	}
3143 
3144 	return (rv);
3145 }
3146 
3147 /*
3148  * Given a bin directory, return the corresponding man directory.
3149  * Return string must be free()d by the caller.
3150  *
3151  * NULL will be returned if no matching man directory can be found.
3152  */
3153 
3154 static char *
path_to_manpath(char * bindir)3155 path_to_manpath(char *bindir)
3156 {
3157 	char	*mand, *p;
3158 	int	i;
3159 	struct stat	sb;
3160 
3161 	/*
3162 	 * First look for known translations for specific bin paths
3163 	 */
3164 	if (stat(bindir, &sb) != 0) {
3165 		return (NULL);
3166 	}
3167 	for (i = 0; bintoman[i].bindir != NULL; i++) {
3168 		if (sb.st_dev == bintoman[i].dev &&
3169 		    sb.st_ino == bintoman[i].ino) {
3170 			if ((mand = strdup(bintoman[i].mandir)) == NULL) {
3171 				malloc_error();
3172 			}
3173 			if ((p = strchr(mand, ',')) != NULL) {
3174 				*p = '\0';
3175 			}
3176 			if (stat(mand, &sb) != 0) {
3177 				free(mand);
3178 				return (NULL);
3179 			}
3180 			if (p != NULL) {
3181 				*p = ',';
3182 			}
3183 			return (mand);
3184 		}
3185 	}
3186 
3187 	/*
3188 	 * No specific translation found.  Try `dirname $bindir`/man
3189 	 * and `dirname $bindir`/share/man
3190 	 */
3191 	if ((mand = malloc(PATH_MAX)) == NULL) {
3192 		malloc_error();
3193 	}
3194 
3195 	if (strlcpy(mand, bindir, PATH_MAX) >= PATH_MAX) {
3196 		free(mand);
3197 		return (NULL);
3198 	}
3199 
3200 	/*
3201 	 * Advance to end of buffer, strip trailing /'s then remove last
3202 	 * directory component.
3203 	 */
3204 	for (p = mand; *p != '\0'; p++)
3205 		;
3206 	for (; p > mand && *p == '/'; p--)
3207 		;
3208 	for (; p > mand && *p != '/'; p--)
3209 		;
3210 	if (p == mand && *p == '.') {
3211 		if (realpath("..", mand) == NULL) {
3212 			free(mand);
3213 			return (NULL);
3214 		}
3215 		for (; *p != '\0'; p++)
3216 			;
3217 	} else {
3218 		*p = '\0';
3219 	}
3220 
3221 	if (strlcat(mand, "/man", PATH_MAX) >= PATH_MAX) {
3222 		free(mand);
3223 		return (NULL);
3224 	}
3225 
3226 	if ((stat(mand, &sb) == 0) && S_ISDIR(sb.st_mode)) {
3227 		return (mand);
3228 	}
3229 
3230 	/*
3231 	 * Strip the /man off and try /share/man
3232 	 */
3233 	*p = '\0';
3234 	if (strlcat(mand, "/share/man", PATH_MAX) >= PATH_MAX) {
3235 		free(mand);
3236 		return (NULL);
3237 	}
3238 	if ((stat(mand, &sb) == 0) && S_ISDIR(sb.st_mode)) {
3239 		return (mand);
3240 	}
3241 
3242 	/*
3243 	 * No man or share/man directory found
3244 	 */
3245 	free(mand);
3246 	return (NULL);
3247 }
3248 
3249 /*
3250  * Free a linked list of dupnode structs
3251  */
3252 void
free_dupnode(struct dupnode * dnp)3253 free_dupnode(struct dupnode *dnp) {
3254 	struct dupnode *dnp2;
3255 	struct secnode *snp;
3256 
3257 	while (dnp != NULL) {
3258 		dnp2 = dnp;
3259 		dnp = dnp->next;
3260 		while (dnp2->secl != NULL) {
3261 			snp = dnp2->secl;
3262 			dnp2->secl = dnp2->secl->next;
3263 			free(snp->secp);
3264 			free(snp);
3265 		}
3266 		free(dnp2);
3267 	}
3268 }
3269 
3270 /*
3271  * prints manp linked list to stdout.
3272  *
3273  * If namep is NULL, output can be used for setting MANPATH.
3274  *
3275  * If namep is not NULL output is two columns.  First column is the string
3276  * pointed to by namep.  Second column is a MANPATH-compatible representation
3277  * of manp linked list.
3278  */
3279 void
print_manpath(struct man_node * manp,char * namep)3280 print_manpath(struct man_node *manp, char *namep)
3281 {
3282 	char colon[2];
3283 	char **secp;
3284 
3285 	if (namep != NULL) {
3286 		(void) printf("%s ", namep);
3287 	}
3288 
3289 	colon[0] = '\0';
3290 	colon[1] = '\0';
3291 
3292 	for (; manp != NULL; manp = manp->next) {
3293 		(void) printf("%s%s", colon, manp->path);
3294 		colon[0] = ':';
3295 
3296 		/*
3297 		 * If man.cf or a directory scan was used to create section
3298 		 * list, do not print section list again.  If the output of
3299 		 * man -p is used to set MANPATH, subsequent runs of man
3300 		 * will re-read man.cf and/or scan man directories as
3301 		 * required.
3302 		 */
3303 		if (manp->defsrch != 0) {
3304 			continue;
3305 		}
3306 
3307 		for (secp = manp->secv; *secp != NULL; secp++) {
3308 			/*
3309 			 * Section deduplication may have eliminated some
3310 			 * sections from the vector. Avoid displaying this
3311 			 * detail which would appear as ",," in output
3312 			 */
3313 			if ((*secp)[0] != '\0') {
3314 				(void) printf(",%s", *secp);
3315 			}
3316 		}
3317 	}
3318 	(void) printf("\n");
3319 }
3320