/* $Header:man.c 12.0$ */
/* $ACIS:man.c 12.0$ */
/* $Source: /ibm/acis/usr/src/ucb/RCS/man.c,v $ */

#ifndef lint
static char *rcsid = "$Header:man.c 12.0$";
#endif

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1980 Regents of the University of California.\n\
 All rights reserved.\n";
#endif not lint

#ifndef lint
static char sccsid[] = "@(#)man.c	5.12 (Berkeley) 3/20/86";
#endif not lint

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

/*
 * man
 * link also to apropos and whatis
 * This version uses more for underlining and paging.
 */
#define	NROFFCAT "nroff -h -man"	/* for nroffing to cat file */
#define	NROFF	"nroff -man"		/* for nroffing to tty */
#define	MORE	"more -s"		/* paging filter */
#define	CAT_	"/bin/cat"		/* for when output is not a tty */
#define	CAT_S	"/bin/cat -s"		/* for '-' opt (no more) */

#define TROFFCMD "vtroff -man %s"

#define	ALLSECT	"1nli6823457po"	/* order to look through sections */
#define	SECT1	"1nlio"		/* sections to look at if 1 is specified */
#define	SUBSEC1	"cgv"		/* subsections to try in section 1 */
#define SUBSEC2 "v"
#define	SUBSEC3	"gsxmncf"
#define	SUBSEC4	"pfn"
#define SUBSEC5 "v"
#define	SUBSEC8	"crv"

#define	WHATIS	"whatis"

int	nomore;
char	*CAT	= CAT_;
char	*manpath = "/usr/man";
char	*strcpy();
char	*strcat();
char	*getenv();
char	*calloc();
char	*trim();
char	*tail();
int	remove();
int	apropos();
int	whatis();
int	section;
int	subsec;
int	troffit;
int	mypid;

#define	eq(a,b)	(strcmp(a,b) == 0)
char *progname;

main(argc, argv)
	int argc;
	char *argv[];
{
	char *mp;
	progname = argv[0];

	if ((mp = getenv("MANPATH")) != NULL)
		manpath = mp;
	umask(0);
	mypid = getpid();
	if (strcmp(tail(argv[0]), "apropos") == 0) {
		runpath(argc-1, argv+1, apropos);
		exit(0);
	}
	if (strcmp(tail(argv[0]), "whatis") == 0) {
		runpath(argc-1, argv+1, whatis);
		exit(0);
	}
	if (argc <= 1) {
		fprintf(stderr, "Usage: %s [ section ] name ...\n",progname);
		exit(1);
	}
	argc--, argv++;
	while (argc > 0 && argv[0][0] == '-') {
		switch(argv[0][1]) {

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

		case 't':
			troffit++;
			break;

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

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

		case 'P':		/* backwards compatibility */
		case 'M':
			if (argc < 2) {
				fprintf(stderr, "%s: missing path\n", *argv);
				exit(1);
			}
			argc--, argv++;
			manpath = *argv;
			break;
		}
		argc--, argv++;
	}
	if (troffit == 0 && nomore == 0 && !isatty(1))
		nomore++;
	section = 0;
	do {
		if (eq(argv[0], "local")) {
			section = 'l';
			goto sectin;
		} else if (eq(argv[0], "ibmtools")) {
			section = 'i';
			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 (isdigit(argv[0][0]) &&
		    (argv[0][1] == 0 || argv[0][2] == 0)) {
			section = argv[0][0];
			subsec = argv[0][1];
sectin:
			argc--, argv++;
			if (argc == 0) {
				fprintf(stderr,
				    "But what do you want from section %s?\n",
				    argv[-1]);
				exit(1);
			}
			continue;
		}
		manual(section, argv[0]);
		argc--, argv++;
	} while (argc > 0);
	exit(0);
}

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) {
			fprintf(stderr, "%s: missing path\n", av[0]);
			exit(1);
		}
		manpath = av[1];
		ac -= 2, av += 2;
	}
	(*f)(ac, av);
	exit(0);
}

manual(sec, name)
	char sec, *name;
{
	char section = sec;
	char work[100], work2[100];
	char path[MAXPATHLEN+1], realname[MAXPATHLEN+1];
	char cmdbuf[150];
	int ss = 0;
	struct stat stbuf, stbuf2;
	int last;
	char *sp = ALLSECT;
	FILE *it;
	char abuf[BUFSIZ];

	strcpy(work, "manx/");
	strcat(work, name);
	strcat(work, ".x");
	last = strlen(work) - 1;
	if (section == '1') {
		sp = SECT1;
		section = 0;
	}
	if (section == 0) {		/* no section or section 1 given */
		for (section = *sp++; section; section = *sp++) {
			work[3] = section;
			work[last] = section;
			work[last+1] = 0;
			work[last+2] = 0;
			if (pathstat(work, path, &stbuf))
				break;
			if (work[last] >= '1' && work[last] <= '8') {
				char *cp;
search:
				switch (work[last]) {
				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 '8': cp = SUBSEC8; break;
				default:  cp = ""; break;
				}
				while (*cp) {
					work[last+1] = *cp++;
					if (pathstat(work, path, &stbuf)) {
						ss = work[last+1];
						goto found;
					}
				}
				if (ss == 0)
					work[last+1] = 0;
			}
		}
		if (section == 0) {
			if (sec == 0)
				printf("No manual entry for %s.\n", name);
			else
				printf("No entry for %s in section %c%s.\n",
				   name, sec, " of the manual");
			return;
		}
	} else {			/* section given */
		work[3] = section;
		work[last] = section;
		work[last+1] = subsec;
		work[last+2] = 0;
		if (!pathstat(work, path, &stbuf)) {
			if ((section >= '1' && section <= '8') && subsec == 0) {
				sp = "\0";
				goto search;
			}
			else if (section == 'o') {	/* XXX */
				char *cp;
				char sec;
				for (sec = '0'; sec <= '8'; sec++) {
					work[last] = sec;
					if (pathstat(work, path, &stbuf))
						goto found;
					switch (work[last]) {
					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 '8': cp = SUBSEC8; break;
					default:  cp = ""; break;
					}
					while (*cp) {
						work[last+1] = *cp++;
						if (pathstat(work, path, &stbuf)) {
							ss = work[last+1];
							goto found;
						}
					}
					if (ss == 0)
						work[last+1] = 0;
				}
			}
			printf("No entry for %s in section %c", name, section);
			if (subsec)
				putchar(subsec);
			printf(" of the manual.\n");
			return;
		}
	}
found:
	sprintf(realname, "%s/%s", path, work);
	if (troffit) {
		troff(path, work);
		return;
	}
	if (!nomore) {
		if ((it = fopen(realname, "r")) == NULL) {
			goto catit;
		}
		if (fgets(abuf, BUFSIZ-1, it) &&
		   strncmp(abuf, ".so ", 4) == 0) {
			register char *cp = abuf+4;
			char *dp;

			while (*cp && *cp != '\n')
				cp++;
			*cp = 0;
			while (cp > abuf && *--cp != '/')
				;
			dp = ".so man";
			if (cp != abuf+strlen(dp)+1) {
tohard:
				fclose(it);
				nomore = 1;
				strcpy(work, abuf+4);
				goto hardway;
			}
			for (cp = abuf; *cp == *dp && *cp; cp++, dp++)
				;
			if (*dp)
				goto tohard;
			strcpy(work, cp-3);
		}
		fclose(it);
	}
catit:
	strcpy(work2, "cat");
	work2[3] = work[3];
	work2[4] = 0;
	sprintf(realname, "%s/%s", path, work2);
	if (stat(realname, &stbuf2) < 0)
		goto hardway;
	strcpy(work2+4, work+4);
	sprintf(realname, "%s/%s", path, work2);
	if (stat(realname, &stbuf2) < 0 || stbuf2.st_mtime < stbuf.st_mtime) {
		if (nomore)
			goto hardway;
		printf("Reformatting page.  Wait...");
		fflush(stdout);
		unlink(work2);
		if (signal(SIGINT, SIG_IGN) == SIG_DFL) {
			(void) signal(SIGINT, remove);
			(void) signal(SIGQUIT, remove);
			(void) signal(SIGTERM, remove);
		}
		sprintf(cmdbuf, "%s %s/%s > /tmp/man%d; trap '' 1 15",
			NROFFCAT, path, work, mypid);
		if (system(cmdbuf)) {
			printf(" aborted (sorry)\n");
			remove();
			/*NOTREACHED*/
		}
		sprintf(cmdbuf, "/bin/mv -f /tmp/man%d %s/%s 2>/dev/null",
			mypid, path, work2);
		if (system(cmdbuf)) {
			sprintf(path,  "/");
			sprintf(work2, "tmp/man%d", mypid);
		}
		printf(" done\n");
	}
	strcpy(work, work2);
hardway:
	nroff(path, work);
	if (work2[0] == 't')
		remove();
}

/*
 * Use the manpath to look for
 * the file name.  The result of
 * stat is returned in stbuf, the
 * successful path in path.
 */
pathstat(name, path, stbuf)
	char *name, path[];
	struct stat *stbuf;
{
	char *cp, *tp, *ep;
	char **cpp;
	static char *manpaths[] = {"man", "cat", 0};
	static char *nopaths[]  = {"", 0};

	if (strncmp(name, "man", 3) == 0)
		cpp = manpaths;
	else
		cpp = nopaths;
	for ( ; *cpp ; cpp++) {
		for (cp = manpath; cp && *cp; cp = tp) {
			tp = index(cp, ':');
			if (tp) {
				if (tp == cp) {
					sprintf(path, "%s%s", *cpp,
						name+strlen(*cpp));
				}
				else {
					sprintf(path, "%.*s/%s%s", tp-cp, cp, 
						*cpp, name+strlen(*cpp));
				}
				ep = path + (tp-cp);
				tp++;
			} else {
				sprintf(path, "%s/%s%s", cp, *cpp,
					name+strlen(*cpp));
				ep = path + strlen(cp);
			}
			if (stat(path, stbuf) >= 0) {
				*ep = '\0';
				return (1);
			}
		}
	}
	return (0);
}

nroff(pp, wp)
	char *pp, *wp;
{
	char cmd[BUFSIZ];

	chdir(pp);
	if (wp[0] == 'c' || wp[0] == 't')
		sprintf(cmd, "%s %s", nomore? CAT : MORE, wp);
	else
		sprintf(cmd, nomore? "%s %s" : "%s %s|%s", NROFF, wp, MORE);
	(void) system(cmd);
}

troff(pp, wp)
	char *pp, *wp;
{
	char cmdbuf[BUFSIZ];

	chdir(pp);
	sprintf(cmdbuf, TROFFCMD, wp);
	(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);
}

remove()
{
	char name[15];

	sprintf(name, "/tmp/man%d", mypid);
	unlink(name);
	exit(1);
}

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

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

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

	if (argc == 0) {
		fprintf(stderr, "apropos what?\n");
		exit(1);
	}
	gotit = calloc(1, blklen(argv));
	for (cp = manpath; cp; cp = tp) {
		tp = index(cp, ':');
		if (tp) {
			if (tp == cp)
				strcpy(file, WHATIS);
			else
				sprintf(file, "%.*s/%s", tp-cp, cp, WHATIS);
			tp++;
		} else
			sprintf(file, "%s/%s", cp, WHATIS);
		if (freopen(file, "r", stdin) == NULL)
			continue;
		while (fgets(buf, sizeof buf, stdin) != NULL)
			for (vp = argv; *vp; vp++)
				if (match(buf, *vp)) {
					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)
			printf("%s: nothing appropriate\n", *vp);
}

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

	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)
	register int 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);
}

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

	if (argc == 0) {
		fprintf(stderr, "whatis what?\n");
		exit(1);
	}
	for (vp = argv; *vp; vp++)
		*vp = trim(*vp);
	gotit = calloc(1, blklen(argv));
	for (cp = manpath; cp; cp = tp) {
		tp = index(cp, ':');
		if (tp) {
			if (tp == cp)
				strcpy(file, WHATIS);
			else
				sprintf(file, "%.*s/%s", tp-cp, cp, WHATIS);
			tp++;
		} else
			sprintf(file, "%s/%s", cp, WHATIS);
		if (freopen(file, "r", stdin) == NULL)
			continue;
		while (fgets(buf, sizeof buf, stdin) != NULL)
			for (vp = argv; *vp; vp++)
				if (wmatch(buf, *vp)) {
					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)
			printf("%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 (isalpha(*bp) || isdigit(*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);
}

char *
tail(s)
	char *s;
{
	char *suffix;

	for (suffix=s+strlen(s); (suffix[0] != '/') && (suffix >= s) ; suffix--)
		/* find the last '/' if there is one */ ;
	return(suffix+1);
}
