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