xref: /csrg-svn/usr.bin/man/man.c (revision 34057)
131703Sbostic /*
231703Sbostic  * Copyright (c) 1987 Regents of the University of California.
333054Sbostic  * All rights reserved.
433054Sbostic  *
533054Sbostic  * Redistribution and use in source and binary forms are permitted
633054Sbostic  * provided that this notice is preserved and that due credit is given
733054Sbostic  * to the University of California at Berkeley. The name of the University
833054Sbostic  * may not be used to endorse or promote products derived from this
933054Sbostic  * software without specific prior written permission. This software
1033054Sbostic  * is provided ``as is'' without express or implied warranty.
1131703Sbostic  */
1231703Sbostic 
1331703Sbostic #ifndef lint
1431703Sbostic char copyright[] =
1531703Sbostic "@(#) Copyright (c) 1987 Regents of the University of California.\n\
1631703Sbostic  All rights reserved.\n";
1733054Sbostic #endif /* not lint */
1831703Sbostic 
1931703Sbostic #ifndef lint
20*34057Sbostic static char sccsid[] = "@(#)man.c	5.14 (Berkeley) 04/22/88";
2133054Sbostic #endif /* not lint */
2231703Sbostic 
2331703Sbostic #include <sys/param.h>
2431703Sbostic #include <sys/file.h>
2531703Sbostic #include <ctype.h>
2631703Sbostic 
2731779Sbostic #define	DEF_PAGER	"/usr/ucb/more -s"
2831936Sbostic #define	DEF_PATH	"/usr/man:/usr/new/man:/usr/local/man"
2931713Sbostic #define	LOCAL_PATH	"/usr/local/man"
3031936Sbostic #define	NEW_PATH	"/usr/new/man"
3131936Sbostic 
3233809Sbostic #define	NO	0
3333809Sbostic #define	YES	1
3431703Sbostic 
3533809Sbostic static char	*command,		/* command buffer */
3633809Sbostic 		*defpath,		/* default search path */
3731713Sbostic 		*locpath,		/* local search path */
3831713Sbostic 		*machine,		/* machine type */
3931713Sbostic 		*manpath,		/* current search path */
4031936Sbostic 		*newpath,		/* new search path */
41*34057Sbostic 		*pager,			/* requested pager */
42*34057Sbostic 		how;			/* how to display */
4331703Sbostic 
44*34057Sbostic #define	ALL	0x1			/* show all man pages */
45*34057Sbostic #define	CAT	0x2			/* copy file to stdout */
46*34057Sbostic #define	WHERE	0x4			/* just tell me where */
47*34057Sbostic 
4831703Sbostic main(argc, argv)
4933354Sbostic 	int argc;
5033354Sbostic 	register char **argv;
5131703Sbostic {
52*34057Sbostic 	extern char *optarg;
53*34057Sbostic 	extern int optind;
54*34057Sbostic 	int ch;
55*34057Sbostic 	char *getenv(), *malloc();
5631703Sbostic 
57*34057Sbostic 	while ((ch = getopt(argc, argv, "-M:P:afkw")) != EOF)
58*34057Sbostic 		switch((char)ch) {
59*34057Sbostic 		case '-':
60*34057Sbostic 			how |= CAT;
6131703Sbostic 			break;
6231703Sbostic 		case 'M':
6331703Sbostic 		case 'P':		/* backward compatibility */
64*34057Sbostic 			defpath = optarg;
6531703Sbostic 			break;
66*34057Sbostic 		case 'a':
67*34057Sbostic 			how |= ALL;
68*34057Sbostic 			break;
6931703Sbostic 		/*
7033809Sbostic 		 * "man -f" and "man -k" are backward contemptible,
7133809Sbostic 		 * undocumented ways of calling whatis(1) and apropos(1).
7231703Sbostic 		 */
7331703Sbostic 		case 'f':
74*34057Sbostic 			jump(argv, "-f", "whatis");
75*34057Sbostic 			/*NOTREACHED*/
7631703Sbostic 		case 'k':
77*34057Sbostic 			jump(argv, "-k", "apropos");
78*34057Sbostic 			/*NOTREACHED*/
7933354Sbostic 		/*
8033354Sbostic 		 * Deliberately undocumented; really only useful when
8133354Sbostic 		 * you're moving man pages around.  Not worth adding.
8233354Sbostic 		 */
8332565Sbostic 		case 'w':
84*34057Sbostic 			how |= WHERE | ALL;
8532565Sbostic 			break;
8631703Sbostic 		case '?':
8731703Sbostic 		default:
88*34057Sbostic 			usage();
8931703Sbostic 		}
90*34057Sbostic 	argv += optind;
9131703Sbostic 
92*34057Sbostic 	if (!*argv)
93*34057Sbostic 		usage();
9433354Sbostic 
95*34057Sbostic 	if (!(how & CAT))
9631703Sbostic 		if (!isatty(1))
97*34057Sbostic 			how |= CAT;
9831779Sbostic 		else if (pager = getenv("PAGER")) {
9933354Sbostic 			register char *p;
10031779Sbostic 
10131779Sbostic 			/*
10231779Sbostic 			 * if the user uses "more", we make it "more -s"
10331779Sbostic 			 * watch out for PAGER = "mypager /usr/ucb/more"
10431779Sbostic 			 */
10533354Sbostic 			for (p = pager; *p && !isspace(*p); ++p);
10633354Sbostic 			for (; p > pager && *p != '/'; --p);
10733354Sbostic 			if (p != pager)
10833354Sbostic 				++p;
10931779Sbostic 			/* make sure it's "more", not "morex" */
11033354Sbostic 			if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))) {
11133354Sbostic 				p += 4;
11231779Sbostic 				/*
11331779Sbostic 				 * allocate for the rest of the PAGER
11431779Sbostic 				 * environment variable, a space, and the EOS.
11531779Sbostic 				 */
11633354Sbostic 				if (!(pager = malloc((u_int)(strlen(p) + sizeof(DEF_PAGER) + 1)))) {
11731779Sbostic 					fputs("man: out of space.\n", stderr);
11831779Sbostic 					exit(1);
11931779Sbostic 				}
12033354Sbostic 				(void)sprintf(pager, "%s %s", DEF_PAGER, p);
12131779Sbostic 			}
12231779Sbostic 		}
12331779Sbostic 		else
12431703Sbostic 			pager = DEF_PAGER;
12531703Sbostic 	if (!(machine = getenv("MACHINE")))
12631703Sbostic 		machine = MACHINE;
12731713Sbostic 	if (!defpath && !(defpath = getenv("MANPATH")))
12831713Sbostic 		defpath = DEF_PATH;
12931713Sbostic 	locpath = LOCAL_PATH;
13031936Sbostic 	newpath = NEW_PATH;
13133354Sbostic 	man(argv);
13233809Sbostic 	/* use system(3) in case someone's pager is "foo arg1 arg2" */
13333809Sbostic 	if (command)
13433809Sbostic 		(void)system(command);
13533354Sbostic 	exit(0);
13633354Sbostic }
13731703Sbostic 
13833809Sbostic typedef struct {
13933809Sbostic 	char	*name, *msg;
14033809Sbostic } DIR;
14133354Sbostic static DIR	list1[] = {		/* section one list */
14233354Sbostic 	"cat1", "1st",		"cat8", "8th",		"cat6", "6th",
14333354Sbostic 	"cat.old", "old",	NULL, NULL,
14433354Sbostic },		list2[] = {		/* rest of the list */
14533354Sbostic 	"cat2", "2nd",		"cat3", "3rd",		"cat4", "4th",
14633354Sbostic 	"cat5", "5th", 		"cat7", "7th",		"cat3f", "3rd (F)",
14733354Sbostic 	NULL, NULL,
14833354Sbostic },		list3[2];		/* single section */
14933354Sbostic 
15033354Sbostic static
15133354Sbostic man(argv)
15233354Sbostic 	char **argv;
15333354Sbostic {
15433354Sbostic 	register char *p;
15533354Sbostic 	DIR *section, *getsect();
15633354Sbostic 	int res;
15733354Sbostic 
15831703Sbostic 	for (; *argv; ++argv) {
15931906Sbostic 		manpath = defpath;
16033354Sbostic 		section = NULL;
16131703Sbostic 		switch(**argv) {
16233354Sbostic 		case 'l':				/* local */
16333354Sbostic 			for (p = *argv; isalpha(*p); ++p);
16433354Sbostic 			if (!strncmp(*argv, "l", p - *argv) ||
16533354Sbostic 			    !strncmp(*argv, "local", p - *argv)) {
16633354Sbostic 				manpath = locpath;
16733354Sbostic 				if (section = getsect(p))
16833354Sbostic 					goto argtest;
16931713Sbostic 			}
17031713Sbostic 			break;
17133354Sbostic 		case 'n':				/* new */
17233354Sbostic 			for (p = *argv; isalpha(*p); ++p);
17333354Sbostic 			if (!strncmp(*argv, "n", p - *argv) ||
17433354Sbostic 			    !strncmp(*argv, "new", p - *argv)) {
17533354Sbostic 				manpath = newpath;
17633354Sbostic 				if (section = getsect(p))
17733354Sbostic 					goto argtest;
17831703Sbostic 			}
17931703Sbostic 			break;
18031778Sbostic 		/*
18133354Sbostic 		 * old isn't really a separate section of the manual,
18233354Sbostic 		 * and its entries are all in a single directory.
18331778Sbostic 		 */
18433354Sbostic 		case 'o':				/* old */
18533354Sbostic 			for (p = *argv; isalpha(*p); ++p);
18633354Sbostic 			if (!strncmp(*argv, "o", p - *argv) ||
18733354Sbostic 			    !strncmp(*argv, "old", p - *argv)) {
18833354Sbostic 				list3[0] = list1[3];
18933354Sbostic 				section = list3;
19033354Sbostic 				goto argtest;
19131703Sbostic 			}
19231703Sbostic 			break;
19333354Sbostic 		case '1': case '2': case '3': case '4':
19433354Sbostic 		case '5': case '6': case '7': case '8':
19533354Sbostic 			if (!(section = getsect(*argv)))
19633354Sbostic 				break;
19733354Sbostic argtest:		if (!*++argv) {
19833354Sbostic 				fprintf(stderr, "man: what do you want from the %s section of the manual?\n", section->msg);
19931936Sbostic 				exit(1);
20031936Sbostic 			}
20131703Sbostic 		}
20233354Sbostic 
20333809Sbostic 		if (section)
20433809Sbostic 			res = manual(section, *argv);
20533809Sbostic 		else {
20633809Sbostic 			res = manual(list1, *argv);
207*34057Sbostic 			if (!res || (how & ALL))
208*34057Sbostic 				res += manual(list2, *argv);
20933809Sbostic 		}
210*34057Sbostic 		if (!res && !(how & WHERE)) {
21131713Sbostic 			if (manpath == locpath)
21233354Sbostic 				if (section)
21333354Sbostic 					fprintf(stderr, "No entry for %s in the %s section of the local manual.\n", *argv, section->msg);
21433354Sbostic 				else
21531936Sbostic 					fprintf(stderr, "No entry for %s in the local manual.\n", *argv);
21633354Sbostic 			else if (manpath == newpath)
21733354Sbostic 				if (section)
21833354Sbostic 					fprintf(stderr, "No entry for %s in the %s section of the new manual.\n", *argv, section->msg);
21931936Sbostic 				else
22031936Sbostic 					fprintf(stderr, "No entry for %s in the new manual.\n", *argv);
22133354Sbostic 			else if (section)
22233354Sbostic 				fprintf(stderr, "No entry for %s in the %s section of the manual.\n", *argv, section->msg);
22333354Sbostic 			else
22431713Sbostic 				fprintf(stderr, "No entry for %s in the manual.\n", *argv);
22533809Sbostic 			exit(1);
22633809Sbostic 		}
22731703Sbostic 	}
22831703Sbostic }
22931703Sbostic 
23031778Sbostic /*
23131778Sbostic  * manual --
23233809Sbostic  *	given a directory list and a file name find a file that
23333809Sbostic  *	matches; check ${directory}/${dir}/{file name} and
23433809Sbostic  *	${directory}/${dir}/${machine}/${file name}.
23531778Sbostic  */
23631703Sbostic static
23731703Sbostic manual(section, name)
23833354Sbostic 	DIR *section;
23933354Sbostic 	char *name;
24031703Sbostic {
24133354Sbostic 	register char *beg, *end;
24233354Sbostic 	register DIR *dp;
24333809Sbostic 	register int res;
24433809Sbostic 	char fname[MAXPATHLEN + 1], *index();
24531703Sbostic 
24633809Sbostic 	for (beg = manpath, res = 0;; beg = end + 1) {
24731703Sbostic 		if (end = index(beg, ':'))
24831703Sbostic 			*end = '\0';
24933809Sbostic 		for (dp = section; dp->name; ++dp) {
25033809Sbostic 			(void)sprintf(fname, "%s/%s/%s.0", beg, dp->name, name);
25133809Sbostic 			if (access(fname, R_OK)) {
25233809Sbostic 				(void)sprintf(fname, "%s/%s/%s/%s.0", beg,
25333809Sbostic 				    dp->name, machine, name);
25433809Sbostic 				if (access(fname, R_OK))
25533809Sbostic 					continue;
25631778Sbostic 			}
257*34057Sbostic 			if (how & WHERE)
25833809Sbostic 				printf("man: found in %s.\n", fname);
259*34057Sbostic 			else if (how & CAT)
26033809Sbostic 				cat(fname);
26133809Sbostic 			else
26233809Sbostic 				add(fname);
263*34057Sbostic 			if (!(how & ALL))
264*34057Sbostic 				return(1);
26533809Sbostic 			res = 1;
26633809Sbostic 		}
26731703Sbostic 		if (!end)
26833809Sbostic 			return(res);
26933297Sbostic 		*end = ':';
27031703Sbostic 	}
27131703Sbostic 	/*NOTREACHED*/
27231703Sbostic }
27331703Sbostic 
27431778Sbostic /*
27533809Sbostic  * cat --
27633809Sbostic  *	cat out the file
27731778Sbostic  */
27831703Sbostic static
27933809Sbostic cat(fname)
28033809Sbostic 	char *fname;
28131703Sbostic {
28233809Sbostic 	register int fd, n;
28333809Sbostic 	char buf[BUFSIZ];
28431703Sbostic 
28533809Sbostic 	if (!(fd = open(fname, O_RDONLY, 0))) {
28633809Sbostic 		perror("man: open");
28733809Sbostic 		exit(1);
28831703Sbostic 	}
28933809Sbostic 	while ((n = read(fd, buf, sizeof(buf))) > 0)
29033809Sbostic 		if (write(1, buf, n) != n) {
29133809Sbostic 			perror("man: write");
29233809Sbostic 			exit(1);
29333809Sbostic 		}
29433809Sbostic 	if (n == -1) {
29533809Sbostic 		perror("man: read");
29633809Sbostic 		exit(1);
29733809Sbostic 	}
29833809Sbostic 	(void)close(fd);
29931703Sbostic }
30031703Sbostic 
30131778Sbostic /*
30233809Sbostic  * add --
30333809Sbostic  *	add a file name to the list for future paging
30431778Sbostic  */
30531703Sbostic static
30633809Sbostic add(fname)
30733354Sbostic 	char *fname;
30831703Sbostic {
30933809Sbostic 	static u_int buflen;
31033809Sbostic 	static int len;
31133809Sbostic 	static char *cp;
31233809Sbostic 	int flen;
31333809Sbostic 	char *malloc(), *realloc(), *strcpy();
31431703Sbostic 
31533809Sbostic 	if (!command) {
31633809Sbostic 		if (!(command = malloc(buflen = 1024))) {
31733809Sbostic 			fputs("man: out of space.\n", stderr);
31831703Sbostic 			exit(1);
31931703Sbostic 		}
32033809Sbostic 		len = strlen(strcpy(command, pager));
32133809Sbostic 		cp = command + len;
32233809Sbostic 	}
32333809Sbostic 	flen = strlen(fname);
32433809Sbostic 	if (len + flen + 2 > buflen) {		/* +2 == space, EOS */
32533809Sbostic 		if (!(command = realloc(command, buflen += 1024))) {
32633809Sbostic 			fputs("man: out of space.\n", stderr);
32731703Sbostic 			exit(1);
32831703Sbostic 		}
32933809Sbostic 		cp = command + len;
33031703Sbostic 	}
33133809Sbostic 	*cp++ = ' ';
33233809Sbostic 	len += flen + 1;			/* +1 = space */
33333809Sbostic 	(void)strcpy(cp, fname);
33433809Sbostic 	cp += flen;
33531703Sbostic }
33631703Sbostic 
33731778Sbostic /*
33833354Sbostic  * getsect --
33933354Sbostic  *	return a point to the section structure for a particular suffix
34031778Sbostic  */
34133354Sbostic static DIR *
34233354Sbostic getsect(s)
34333354Sbostic 	char *s;
34431703Sbostic {
34533354Sbostic 	switch(*s++) {
34633354Sbostic 	case '1':
34733354Sbostic 		if (!*s)
34833354Sbostic 			return(list1);
34933354Sbostic 		break;
35033354Sbostic 	case '2':
35133354Sbostic 		if (!*s) {
35233354Sbostic 			list3[0] = list2[0];
35333354Sbostic 			return(list3);
35433354Sbostic 		}
35533354Sbostic 		break;
35633354Sbostic 	/* sect. 3 requests are for either section 3, or section 3[fF]. */
35733354Sbostic 	case '3':
35833354Sbostic 		if (!*s) {
35933354Sbostic 			list3[0] = list2[1];
36033354Sbostic 			return(list3);
36133354Sbostic 		}
36233354Sbostic 		else if ((*s == 'f'  || *s == 'F') && !*++s) {
36333354Sbostic 			list3[0] = list2[5];
36433354Sbostic 			return(list3);
36533354Sbostic 		}
36633354Sbostic 		break;
36733354Sbostic 	case '4':
36833354Sbostic 		if (!*s) {
36933354Sbostic 			list3[0] = list2[2];
37033354Sbostic 			return(list3);
37133354Sbostic 		}
37233354Sbostic 		break;
37333354Sbostic 	case '5':
37433354Sbostic 		if (!*s) {
37533354Sbostic 			list3[0] = list2[3];
37633354Sbostic 			return(list3);
37733354Sbostic 		}
37833354Sbostic 		break;
37933354Sbostic 	case '6':
38033354Sbostic 		if (!*s) {
38133354Sbostic 			list3[0] = list1[2];
38233354Sbostic 			return(list3);
38333354Sbostic 		}
38433354Sbostic 		break;
38533354Sbostic 	case '7':
38633354Sbostic 		if (!*s) {
38733354Sbostic 			list3[0] = list2[4];
38833354Sbostic 			return(list3);
38933354Sbostic 		}
39033354Sbostic 		break;
39133354Sbostic 	case '8':
39233354Sbostic 		if (!*s) {
39333354Sbostic 			list3[0] = list1[1];
39433354Sbostic 			return(list3);
39533354Sbostic 		}
39633354Sbostic 	}
39733354Sbostic 	return((DIR *)NULL);
39831703Sbostic }
399*34057Sbostic 
400*34057Sbostic /*
401*34057Sbostic  * jump --
402*34057Sbostic  *	strip out flag argument and jump
403*34057Sbostic  */
404*34057Sbostic static
405*34057Sbostic jump(argv, flag, name)
406*34057Sbostic 	char **argv, *name;
407*34057Sbostic 	register char *flag;
408*34057Sbostic {
409*34057Sbostic 	register char **arg;
410*34057Sbostic 
411*34057Sbostic 	argv[0] = name;
412*34057Sbostic 	for (arg = argv + 1; *arg; ++arg)
413*34057Sbostic 		if (!strcmp(*arg, flag))
414*34057Sbostic 			break;
415*34057Sbostic 	for (; *arg; ++arg)
416*34057Sbostic 		arg[0] = arg[1];
417*34057Sbostic 	execvp(name, argv);
418*34057Sbostic 	fprintf(stderr, "%s: Command not found.\n", name);
419*34057Sbostic 	exit(1);
420*34057Sbostic }
421*34057Sbostic 
422*34057Sbostic /*
423*34057Sbostic  * usage --
424*34057Sbostic  *	print usage and die
425*34057Sbostic  */
426*34057Sbostic static
427*34057Sbostic usage()
428*34057Sbostic {
429*34057Sbostic 	fputs("usage: man [-] [-a] [-M path] [section] title ...\n", stderr);
430*34057Sbostic 	exit(1);
431*34057Sbostic }
432