#ident	"@(#)man.c	1.2	92/02/20	JPB"
/*	Copyright (c) 1990 UNIX System Laboratories, Inc.	*/
/*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF     	*/
/*	UNIX System Laboratories, Inc.                     	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/


#ident	"@(#)ucbman:man.c	1.4.3.1"

/*******************************************************************

		PROPRIETARY NOTICE (Combined)

This source code is unpublished proprietary information
constituting, or derived under license from AT&T's UNIX(r) System V.
In addition, portions of such source code were derived from Berkeley
4.3 BSD under license from the Regents of the University of
California.



		Copyright Notice 

Notice of copyright on this source code product does not indicate 
publication.

	(c) 1986,1987,1988,1989  Sun Microsystems, Inc
	(c) 1983,1984,1985,1986,1987,1988,1989  AT&T.
	          All rights reserved.
********************************************************************/ 

/*
 * man
 * link also to apropos and whatis
 * This version uses more for underlining and paging.
 */

#include <stdio.h>
#include <ctype.h>
#include <sgtty.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <string.h>

/*
 * The default search path for man subtrees.  
 */

/* Paths for directory structure. 
 * System V sometimes may use a different directory structure as
 * follows:
 *    /usr/man/u_man:/usr/man/p_man:/usr/man/a_man:/usr/man/local
 * and MANDIR can be set to this string.
 */

#define	MANDIR	"/usr/share/man"

/*
 * Names for formatting and display programs.  The values given
 * below are reasonable defaults, but sites with source may
 * wish to modify them to match the local environment.  The
 * value for TCAT is particularly problematic as there's no
 * accepted standard value available for it.  (The definition
 * below assumes C.A.T. troff output and prints it).
 */


#define	MORE	"more -s" 	/* default paging filter */
#define	CAT_	"/bin/cat"	/* for when output is not a tty */
#define	CAT_S	"/bin/cat -s"	/* for '-' opt (no more) */
#define	TROFF	"troff"		/* local name for troff */
#define TCAT	"lp -T troff"	/* command to "display" troff output */


/*
 * These are the possible subsections of all known 4bsd-derived systems.
 * SVr4 may use upper case letters and pare down the number of
 * subsections and speed things up a bit. 
 */
#define	ALLSECT	"1nl68234570po"	/* order to look through sections */
#define	SECT1	"1nlo"		/* sections to look at if 1 is specified */
#define	SUBSEC1	"bcfgmprsvw"	/* subsections to try in section 1 */
#define	SUBSEC2	"bvw"
#define	SUBSEC3	"bcfjklmnprsvwx"
#define	SUBSEC4	"bfmnpsv"
#define	SUBSEC5	"bv"
#define	SUBSEC7	"bv"
#define	SUBSEC8	"bcvs"
#define SUBSEC0 ""

#define	WHATIS	"whatis"

#define	SOLIMIT	10		/* maximum allowed .so chain length */

/*
 * A list of known preprocessors to precede the formatter itself
 * in the formatting pipeline.  Preprocessors are specified by
 * starting a manual page with a line of the form:
 *	'\" X
 * where X is a string consisting of letters from the p_tag fields
 * below.
 */
struct preprocessor {
	char	p_tag;
	char	*p_nroff,
		*p_troff;
} preprocessors [] = {
	{'e',	"neqn /usr/ucblib/pub/eqnchar",	"eqn /usr/ucblib/pub/eqnchar"},
	{'r',	"refer",			"refer"},
	{'t',	"tbl",				"tbl"},
	{0,	0,				0}
};

/*
 * Subdirectories to search for unformatted/formatted man page
 * versions, in nroff and troff variations.  The searching
 * code in manual() is structured to expect there to be two
 * subdirectories apiece, the first for unformatted files
 * and the second for formatted ones.
 */
char	*nroffdirs[] = { "man", "cat", 0 };
char	*troffdirs[] = { "man", "fmt", 0 };

char	tmpname[15];		/* name of temporary file */

int	nomore;
int	troffit;
char	*CAT	= CAT_;
char	*manpath = MANDIR;
char	*macros	= "-man";
char	*pager;
char	*troffcmd;
char	*troffcat;
char	*calloc();
char	*getenv();
char	*trim();
char	*msuffix();
int	apropos();
int	whatis();
void	clean(); 

#define	eq(a,b)	(strcmp(a,b) == 0)

main(argc, argv)
	int argc;
	char *argv[];
{
	char section, subsec;
	char *mp;
	char *cmdname;

	/* If defined, use environment value as default manual path list. */
	if ((mp = getenv("MANPATH")) != NULL)
		manpath = mp;

	(void) sprintf(tmpname, "/tmp/man%d", getpid());
	(void) umask(0);

        /* get base part of command name */
        if ((cmdname = strrchr(argv[0], '/')) != NULL)
                cmdname++;
        else
                cmdname = argv[0];

        if (eq(cmdname, "apropos") || eq(cmdname, "whatis")) {
                runpath(argc-1, argv+1,
                        cmdname[0] == 'a' ? apropos : whatis);
                exit(0);
        }

	argc--, argv++;
	while (argc > 0 && argv[0][0] == '-') {
		switch(argv[0][1]) {

		case 0:
			nomore++;
			CAT = CAT_S;
			break;

		case 'f':
			whatis(argc-1, argv+1);
			exit(0);

		case 'k':
			apropos(argc-1, argv+1);
			exit(0);

		case 'P':	/* Backwards compatibility */
		case 'M':	/* Respecify path for man pages. */
			if (argc < 2) {
				(void) fprintf(stderr, "%s: missing path\n",
					*argv);
				exit(1);
			}
			argc--, argv++;
			manpath = *argv;
			break;

		case 'T':	/* Respecify man macros */
			argc--, argv++;
			macros = *argv;
			break;

		case 't':
			troffit++;
			break;

		default:
			goto usage;
		}
		argc--, argv++;
	}
	if (argc <= 0) {
usage:
		(void) fprintf(stderr,
		    "Usage:\tman [-] [-t] [-M path] [-T man] [ section ] name ...\n");
		(void) fprintf(stderr, "\tman -k keyword ...\n\tman -f file ...\n");
		exit(1);
	}

	if (troffit == 0 && nomore == 0 && !isatty(fileno(stdout)))
		nomore++;

	/*
	 * Collect environment information.
	 */
	if (troffit) {
		if ((troffcmd = getenv("TROFF")) == NULL)
			troffcmd = TROFF;
		if ((troffcat = getenv("TCAT")) == NULL)
			troffcat = TCAT;
	}
	else {
		if (((pager = getenv("PAGER")) == NULL) ||
		    (*pager == NULL))
			pager = MORE;
	}

	/*
	 * The manual routine contains windows during which
	 * termination would leave a temp file behind.  Thus
	 * we blanket the whole thing with a clean-up routine.
	 */
	if (signal(SIGINT, SIG_IGN) == SIG_DFL) {
		(void) signal(SIGINT, clean);
		(void) signal(SIGQUIT, clean);
		(void) signal(SIGTERM, clean);
	}

	section = 0;
	do {
		if (eq(argv[0], "local")) {
			section = 'l';
			goto sectin;
		} else if (eq(argv[0], "new")) {
			section = 'n';
			goto sectin;
		} else if (eq(argv[0], "old")) {
			section = 'o';
			goto sectin;
		} else if (eq(argv[0], "public")) {
			section = 'p';
			goto sectin;
		} else if (argv[0][0] >= '1' && argv[0][0] <= '8' &&
		    (argv[0][1] == 0 || argv[0][2] == 0)) {
			section = argv[0][0];
			subsec = argv[0][1];
sectin:
			argc--, argv++;
			if (argc == 0) {
				(void) fprintf(stderr,
					"But what do you want from section %s?\n",
					argv[-1]);
				exit(1);
			}
			continue;
		}
		manual(section, subsec, argv[0]);
		argc--, argv++;
	} while (argc > 0);
	exit(0);
	/* NOTREACHED */
}

runpath(ac, av, f)
	int ac;
	char *av[];
	int (*f)();
{

	if (ac > 0 && strcmp(av[0], "-M") == 0 || strcmp(av[0], "-P") == 0) {
		if (ac < 2) {
			(void) fprintf(stderr, "%s: missing path\n", av[0]);
			exit(1);
		}
		manpath = av[1];
		ac -= 2, av += 2;
	}
	(*f)(ac, av);
	exit(0);
}

/*
 * Process the title specified by the arguments.
 */
manual(sec, subsec, name)
	char sec, subsec;
	char *name;
{
	char section = sec;
	char *sp = ALLSECT;
	char **subdirs;
	int last, ss;
	int socount, regencat, updatedcat, catonly;
	struct stat mansb, catsb;
	char manbuf[BUFSIZ];
	char title[200], path[200];
	char manpname[MAXPATHLEN+1], catpname[MAXPATHLEN+1];

	/*
	 * Establish list of subdirectories to search
	 * for acceptable man page versions.
	 */
	subdirs = troffit ? troffdirs : nroffdirs;

	/*
	 * Note the side-effect in this definition: if the cat page
	 * exists, but the man page doesn't, catonly will be set nonzero.
	 */
#define	checkpage() ((catonly = pathfind(title, subdirs, path)) >= 0)

	/*
	 * Set title to contain the (relative) pathname
	 * of the unformatted manual page.
	 */
	(void) sprintf(title, "manx/%s.x", name);
	last = strlen(title) - 1;
	if (section == '1') {
		sp = SECT1;
		section = 0;
	}
	if (section == 0) {
		/* Any section will do... */

		ss = 0;
		for (section = *sp++; section; section = *sp++) {
			title[3] = section;
			title[last] = section;
			title[last+1] = 0;
			title[last+2] = 0;
			if (checkpage())
				break;
			if (title[last] >= '0' && title[last] <= '8') {
				register char *cp;
search:
				switch (title[last]) {
				case '0': cp = SUBSEC0; break;
				case '1': cp = SUBSEC1; break;
				case '2': cp = SUBSEC2; break;
				case '3': cp = SUBSEC3; break;
				case '4': cp = SUBSEC4; break;
				case '5': cp = SUBSEC5; break;
				case '7': cp = SUBSEC7; break;
				case '8': cp = SUBSEC8; break;
				default:  cp = ""; break;
				}
				while (*cp) {
					title[last+1] = *cp++;
					if (checkpage()) {
						ss = title[last+1];
						goto found;
					}
				}
				if (ss == 0)
					title[last+1] = 0;
			}
		}
		if (section == 0) {
			if (sec == 0)
				(void) printf("No manual entry for %s.\n",
					name);
			else
				(void) printf("No entry for %s in section %c of the manual.\n",
					name, sec);
			return;
		}
	} else {
		/* Explicit section specified. */
		title[3] = section;
		title[last] = section;
		title[last+1] = subsec;
		title[last+2] = 0;
		if (!checkpage()) {
			if ((section >= '0' && section <= '8') && subsec == 0) {
				sp = "\0";
				goto search;
			}
			else if (section == 'o') {
				char *cp;
				char secnum;

				for (secnum = '0'; secnum <= '8'; secnum++) {
					title[last] = secnum;
					if (checkpage())
						goto found;
					switch (title[last]) {
					case '0': cp = SUBSEC0; break;
					case '1': cp = SUBSEC1; break;
					case '2': cp = SUBSEC2; break;
					case '3': cp = SUBSEC3; break;
					case '4': cp = SUBSEC4; break;
					case '5': cp = SUBSEC5; break;
					case '7': cp = SUBSEC7; break;
					case '8': cp = SUBSEC8; break;
					default:  cp = ""; break;
					}
					while (*cp) {
						title[last+1] = *cp++;
						if (checkpage()) {
							ss = title[last+1];
							goto found;
						}
					}
					if (ss == 0)
						title[last+1] = 0;
				}
			}
			(void) printf("No entry for %s in section %c", name,
				section);
			if (subsec)
				(void) printf("%c", subsec);
			(void) printf(" of the manual.\n");
			return;
		}
	}

found:
        /*
         * At this point we've found a match for the man page specified
         * by our arguments.  However, it may be an indirect reference
         * to another man page.
         */
 
        /*
         * Take care of indirect references to other man pages;
         * i.e., resolve files containing only ".so manx/file.x".
         * We follow .so chains, replacing title with the .so'ed
         * file at each stage, and keeping track of how many times
         * we've done so, so that we can avoid looping.
         */
        for (socount = 0; ; ) {
                register FILE *md;
                register char *cp;
 
                if (catonly)
                        break;
 
                /*
                 * Construct the title's corresponding man page
                 * pathname.
                 */
                (void) sprintf(manpname, "%s/%s%s", path, subdirs[0],
                        msuffix(title));
 
                /*
                 * Grab manpname's first line, stashing it in manbuf.
                 */
                if ((md = fopen(manpname, "r")) == NULL) {
                        perror(manpname);
                        return;
                }
                if (fgets(manbuf, BUFSIZ-1, md) == NULL) {
                        (void) fclose(md);
                        (void) fprintf(stderr, "%s: null file\n",
manpname);
                        (void) fflush(stderr);
                        return;
                }
                (void) fclose(md);

                if (strncmp(manbuf, ".so ", sizeof ".so " - 1))
                        break;
                if (++socount > SOLIMIT) {
                        (void) fprintf(stderr, ".so chain too long\n");
                        (void) fflush(stderr);
                        return;
                }
                (void) strcpy(title, manbuf + sizeof ".so " - 1);
                cp = strrchr(title, '\n');
                if (cp)
                        *cp = '\0';
                /*
                 * Compensate for sloppy typists by stripping
                 * trailing white space.
                 */
                cp = title + strlen(title);
                while (--cp >= title && (*cp == ' ' || *cp == '\t'))
                        *cp = '\0';

                /*
                 * Go off and find the next link in the chain.
                 */
                if (!checkpage()) {
                        (void) fprintf(stderr,
                                "Can't find referent of .so in %s\n",
                                manpname);
                        (void) fflush(stderr);
                        return;
                }
        }
 

#undef	checkpage

	/*
	 * Get pathnames for the formatted and unformatted
	 * versions of title.
	 */
	(void) sprintf(catpname, "%s/%s%s", path, subdirs[1], msuffix(title));
	if (!catonly)
		(void) sprintf(manpname, "%s/%s%s", path, subdirs[0],
			msuffix(title));

#ifdef DEBUG
	(void) printf("title: %s, cat page: %s, man page: %s\n", title,
		catpname, catonly ? "" : manpname);
#endif DEBUG

	/*
	 * Obtain the cat page that corresponds to the man page.
	 * If it already exists, is up to date, and if we haven't
	 * been told not to use it, use it as it stands.
	 */
	regencat = updatedcat = 0;
	if (!catonly && stat(manpname, &mansb) >= 0 &&
			(stat(catpname, &catsb) < 0 ||
			 catsb.st_mtime < mansb.st_mtime)) {
		/*
		 * Construct a shell command line for formatting manpname.
		 * The resulting file goes initially into /tmp.  If possible,
		 * it will later be moved to catpname.
		 */

		int pipestage = 0;
		int needcol = 0;
		char cmdbuf[200];
		char *cbp = cmdbuf;
                register FILE *md;
		char *datestring;	/* date man page last changed */

		regencat = updatedcat = 1;

		(void) fprintf(stderr, "Reformatting page.  Wait...");
		(void) fflush(stderr);

                /*
                 * Recover the relevant component of manpath and
                 * add a cd to it at the front of the command line.
                 * Doing so allows embedded relative .so commands
                 * to work correctly.
                 *
                 * This code is uglier than it should be, but that's
                 * what comes of twisting the design around to add
                 * afterthoughts.  It's yet another place that knows
                 * that there's exactly one level of subdirectory in
                 * each man subtree.
                 */
                {
                        char    *slashp = strrchr(manpname, '/');
 
                        /* Back up to the previous slash. */
                        while (slashp && --slashp >= manpname && *slashp
!= '/')                                continue;
                        if (slashp > manpname) {
                                int len = slashp - manpname;
 
                                (void) sprintf(cbp, "cd %.*s; ",
                                        len, manpname);
                                cbp += strlen(cbp);
                        }
                }
 

		/*
		 * Check for special formatting requirements by examining
		 * manpname's first line preprocessor specifications.
		 */

		if (strncmp(manbuf, "'\\\" ", sizeof "'\\\" " - 1) == 0) {
			register char *ptp = manbuf + sizeof "'\\\" " - 1;

			while (*ptp && *ptp != '\n') {
				register struct preprocessor *pp;

				/*
				 * Check for a preprocessor we know about.
				 */
				for (pp = preprocessors; pp->p_tag; pp++) {
					if (pp->p_tag == *ptp)
						break;
				}
				if (pp->p_tag == 0) {
					(void) fprintf(stderr,
						"unknown preprocessor specifier %c\n",
						*ptp);
					(void) fflush(stderr);
					return;
				}

				/*
				 * Add it to the pipeline.
				 */
				(void) sprintf(cbp, "%s %s | ",
					troffit ? pp->p_troff : pp->p_nroff,
					pipestage++ == 0 ? manpname : "-");
				cbp += strlen(cbp);

				/*
				 * Special treatment: if tbl is among the
				 * preprocessors and we'll process with
				 * nroff, we have to pass things through
				 * col at the end of the pipeline.
				 */
				if (pp->p_tag == 't' && !troffit)
					needcol++;

				ptp++;
			}
		}

		/*
		 * Tack on the formatter specification.
		 */

		/* 
		 * May need to determine date of last access and
		 * pass this to the macro package, but this
		 * information is not known for SVr4.  Keeping
		 * datestring in to ease adding it in later.
		 */

		datestring = NULL;

		(void) sprintf(cbp, "%s %s %s %s%s > %s",
			troffit ? troffcmd : "nroff -Tlp",
			datestring,
			macros,
			pipestage == 0 ? manpname : "-",
			needcol ? " | col" : "",
			tmpname);

#ifdef	DEBUG
		(void) printf("\n%s\n", cmdbuf);
#endif	DEBUG

		/* Reformat the page. */
		if (system(cmdbuf)) {
			(void) fprintf(stderr, " aborted (sorry)\n");
			(void) fflush(stderr);
			(void) unlink(tmpname);
			return;
		}

		/*
		 * Attempt to move the cat page to its proper home.
		 */
		(void) sprintf(cmdbuf,
			"trap '' 1 15; /bin/mv -f %s %s 2> /dev/null",
			tmpname,
			catpname);
		if (system(cmdbuf))
			updatedcat = 0;

		(void) fprintf(stderr, " done\n");
		(void) fflush(stderr);
	}

	/*
	 * Reset catpname to name the formatted output,
	 * wherever it may be.
	 */
	if (regencat && !updatedcat)
		(void) strcpy(catpname, tmpname);

	/*
	 * Dispose of the formatted page,
	 * presenting it to our invoker, etc.
	 */
	if (troffit)
		troff(catpname, nomore);
	else
		nroff(catpname, nomore);

	/*
	 * Get rid of dregs.
	 */
	(void) unlink(tmpname);
}

/*
 * Generate and examine paths of the form `manpath[i]/subdirs[j]/nametail',
 * performing a depth-first search (varying j most rapidly) for the
 * first existent file.  Here, `name' is assumed to be of the form
 * `xxxn/tail', where `n' is a single character.
 *
 * Return -1 if not found.  Otherwise, copy manpath[i] into respath
 * and return j.
 *
 */
pathfind(name, subdirs, respath)
	char *name, **subdirs, respath[];
{
	register char *mp, *tp, *ntp, *ep;
	char **sdp;
	struct stat statb;

	/*
	 * Set ntp to point immediately before the slash preceding nametail.
	 * Thus, ntp will be of the form `n/tail'.
	 */
	if ((ntp = msuffix(name)) == name)
		return (-1);

	for (mp = manpath; mp && *mp; mp = tp) {
		/* Obtain position of end of current manpath component. */
		tp = strchr(mp, ':');

		for (sdp = subdirs; *sdp; sdp++) {
			if (tp) {
				if (tp == mp) {
					/* Null manpath component mapped to . */
					(void) sprintf(respath, "./%s%s", *sdp,
						ntp);
					ep = respath + 1;
				}
				else {
					/* Embedded manpath component. */
					(void) sprintf(respath, "%.*s/%s%s",
						tp - mp, mp, *sdp, ntp);
					ep = respath + (tp - mp);
				}
			}
			else {
				/* Last component in manpath. */
				(void) sprintf(respath, "%s/%s%s", mp, *sdp,
					ntp);
				ep = respath + strlen(mp);
			}

			/* Check the file. */
			if (stat(respath, &statb) >= 0) {
				*ep = '\0';
				return (sdp - subdirs);
			}
		}

		/* Move to beginning of next manpath component. */
		if (tp)
			tp++;
	}

	return (-1);
}

/*
 * Given a string of the form `xn/y', with n a single character
 * return a pointer to the substring starting at `n'.  If the
 * input string is ill-formed, return it as is.
 */
char *
msuffix(cp)
	char *cp;
{
	register char *sp = strchr(cp, '/');

	return (sp && sp > cp ? sp - 1 : cp);
}

/*
 * Arrange to display the nroffed man page we just generated.
 * If plain is true, use CAT instead of the pager.
 */
nroff(filep, plain)
	char *filep;
	int plain;
{
	char cmdbuf[BUFSIZ];

	(void) sprintf(cmdbuf, "%s %s", plain ? CAT : pager, filep);
	(void) system(cmdbuf);
}

/*
 * Arrange to display the troffed man page we just generated.
 * If plain is true, don't bother.
 */
troff(filep, plain)
	char *filep;
	int plain;
{
	char cmdbuf[BUFSIZ];

	if (!plain) {
		(void) sprintf(cmdbuf, "%s %s", troffcat, filep);
		(void) system(cmdbuf);
	}
}

any(c, sp)
	register int c;
	register char *sp;
{
	register int d;

	while (d = *sp++)
		if (c == d)
			return (1);
	return (0);
}

/*
 * Clean things up after receiving a signal.
 */
void
clean()
{
	(void) unlink(tmpname);
	exit(1);
}

apropos(argc, argv)
	int argc;
	char **argv;
{
	char buf[BUFSIZ], file[MAXPATHLEN+1];
	char *gotit, *cp, *tp;
	register char **vp;

	if (argc == 0) {
		(void) fprintf(stderr, "apropos what?\n");
		exit(1);
	}
	gotit = (char *) calloc(1, blklen(argv));
	for (cp = manpath; cp; cp = tp) {
		tp = strchr(cp, ':');
		if (tp) {
			if (tp == cp)
				(void) strcpy(file, WHATIS);
			else
				(void) sprintf(file, "%.*s/%s", tp-cp, cp,
					WHATIS);
			tp++;
		} else
			(void) sprintf(file, "%s/%s", cp, WHATIS);
		if (freopen(file, "r", stdin) == NULL) {
			perror(file);
			continue;
		}
		while (fgets(buf, sizeof buf, stdin) != NULL)
			for (vp = argv; *vp; vp++)
				if (match(buf, *vp)) {
					(void) printf("%s", buf);
					gotit[vp - argv] = 1;
					for (vp++; *vp; vp++)
						if (match(buf, *vp))
							gotit[vp - argv] = 1;
					break;
				}
	}
	for (vp = argv; *vp; vp++)
		if (gotit[vp - argv] == 0)
			(void) printf("%s: nothing appropriate\n", *vp);
}

match(buf, str)
	char *buf, *str;
{
	register char *bp;

	bp = buf;
	for (;;) {
		if (*bp == 0)
			return (0);
		if (amatch(bp, str))
			return (1);
		bp++;
	}
}

amatch(cp, dp)
	register char *cp, *dp;
{

	while (*cp && *dp && lmatch(*cp, *dp))
		cp++, dp++;
	if (*dp == 0)
		return (1);
	return (0);
}

lmatch(c, d)
	char c, d;
{

	if (c == d)
		return (1);
	if (!isalpha(c) || !isalpha(d))
		return (0);
	if (islower(c))
		c = toupper(c);
	if (islower(d))
		d = toupper(d);
	return (c == d);
}

blklen(ip)
	register int *ip;
{
	register int i = 0;

	while (*ip++)
		i++;
	return (i);
}

whatis(argc, argv)
	int argc;
	char **argv;
{
	register char *gotit, **vp;
	char buf[BUFSIZ], file[MAXPATHLEN+1], *cp, *tp;

	if (argc == 0) {
		(void) fprintf(stderr, "whatis what?\n");
		exit(1);
	}
	for (vp = argv; *vp; vp++)
		*vp = trim(*vp);
	gotit = (char *)calloc(1, blklen(argv));
	for (cp = manpath; cp; cp = tp) {
		tp = strchr(cp, ':');
		if (tp) {
			if (tp == cp)
				(void) strcpy(file, WHATIS);
			else
				(void) sprintf(file, "%.*s/%s", tp-cp, cp,
					WHATIS);
			tp++;
		} else
			(void) sprintf(file, "%s/%s", cp, WHATIS);
		if (freopen(file, "r", stdin) == NULL) {
			perror(file);
			continue;
		}
		while (fgets(buf, sizeof buf, stdin) != NULL)
			for (vp = argv; *vp; vp++)
				if (wmatch(buf, *vp)) {
					(void) printf("%s", buf);
					gotit[vp - argv] = 1;
					for (vp++; *vp; vp++)
						if (wmatch(buf, *vp))
							gotit[vp - argv] = 1;
					break;
				}
	}
	for (vp = argv; *vp; vp++)
		if (gotit[vp - argv] == 0)
			(void) printf("man: man entry for %s not found\n", *vp);
}

wmatch(buf, str)
	char *buf, *str;
{
	register char *bp, *cp;

	bp = buf;
again:
	cp = str;
	while (*bp && *cp && lmatch(*bp, *cp))
		bp++, cp++;
	if (*cp == 0 && (*bp == '(' || *bp == ',' || *bp == '\t' || *bp == ' '))
		return (1);
	while (*bp != '(' && *bp != ',' && *bp != '\t' && *bp != ' ' && *bp)
		bp++;
	if (*bp != ',')
		return (0);
	bp++;
	while (isspace(*bp))
		bp++;
	goto again;
}

char *
trim(cp)
	register char *cp;
{
	register char *dp;

	for (dp = cp; *dp; dp++)
		if (*dp == '/')
			cp = dp + 1;
	if (cp[0] != '.') {
		if (cp + 3 <= dp && dp[-2] == '.' &&
		    any(dp[-1], "cosa12345678npP"))
			dp[-2] = 0;
		if (cp + 4 <= dp && dp[-3] == '.' &&
		    any(dp[-2], "13") && isalpha(dp[-1]))
			dp[-3] = 0;
	}
	return (cp);
}


