xref: /csrg-svn/usr.bin/man/man.c (revision 34886)
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
6*34886Sbostic  * provided that the above copyright notice and this paragraph are
7*34886Sbostic  * duplicated in all such forms and that any documentation,
8*34886Sbostic  * advertising materials, and other materials related to such
9*34886Sbostic  * distribution and use acknowledge that the software was developed
10*34886Sbostic  * by the University of California, Berkeley.  The name of the
11*34886Sbostic  * University may not be used to endorse or promote products derived
12*34886Sbostic  * from this software without specific prior written permission.
13*34886Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14*34886Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15*34886Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1631703Sbostic  */
1731703Sbostic 
1831703Sbostic #ifndef lint
1931703Sbostic char copyright[] =
2031703Sbostic "@(#) Copyright (c) 1987 Regents of the University of California.\n\
2131703Sbostic  All rights reserved.\n";
2233054Sbostic #endif /* not lint */
2331703Sbostic 
2431703Sbostic #ifndef lint
25*34886Sbostic static char sccsid[] = "@(#)man.c	5.17 (Berkeley) 06/29/88";
2633054Sbostic #endif /* not lint */
2731703Sbostic 
2831703Sbostic #include <sys/param.h>
2931703Sbostic #include <sys/file.h>
3031703Sbostic #include <ctype.h>
3131703Sbostic 
3231779Sbostic #define	DEF_PAGER	"/usr/ucb/more -s"
3331936Sbostic #define	DEF_PATH	"/usr/man:/usr/new/man:/usr/local/man"
3431713Sbostic #define	LOCAL_PATH	"/usr/local/man"
3531936Sbostic #define	NEW_PATH	"/usr/new/man"
3631936Sbostic 
3733809Sbostic #define	NO	0
3833809Sbostic #define	YES	1
3931703Sbostic 
4033809Sbostic static char	*command,		/* command buffer */
4133809Sbostic 		*defpath,		/* default search path */
4231713Sbostic 		*locpath,		/* local search path */
4331713Sbostic 		*machine,		/* machine type */
4431713Sbostic 		*manpath,		/* current search path */
4531936Sbostic 		*newpath,		/* new search path */
4634057Sbostic 		*pager,			/* requested pager */
4734057Sbostic 		how;			/* how to display */
4831703Sbostic 
4934057Sbostic #define	ALL	0x1			/* show all man pages */
5034057Sbostic #define	CAT	0x2			/* copy file to stdout */
5134057Sbostic #define	WHERE	0x4			/* just tell me where */
5234057Sbostic 
5331703Sbostic main(argc, argv)
5433354Sbostic 	int argc;
5533354Sbostic 	register char **argv;
5631703Sbostic {
5734057Sbostic 	extern char *optarg;
5834057Sbostic 	extern int optind;
5934057Sbostic 	int ch;
6034057Sbostic 	char *getenv(), *malloc();
6131703Sbostic 
6234057Sbostic 	while ((ch = getopt(argc, argv, "-M:P:afkw")) != EOF)
6334057Sbostic 		switch((char)ch) {
6434057Sbostic 		case '-':
6534057Sbostic 			how |= CAT;
6631703Sbostic 			break;
6731703Sbostic 		case 'M':
6831703Sbostic 		case 'P':		/* backward compatibility */
6934057Sbostic 			defpath = optarg;
7031703Sbostic 			break;
7134057Sbostic 		case 'a':
7234057Sbostic 			how |= ALL;
7334057Sbostic 			break;
7431703Sbostic 		/*
7533809Sbostic 		 * "man -f" and "man -k" are backward contemptible,
7633809Sbostic 		 * undocumented ways of calling whatis(1) and apropos(1).
7731703Sbostic 		 */
7831703Sbostic 		case 'f':
7934057Sbostic 			jump(argv, "-f", "whatis");
8034057Sbostic 			/*NOTREACHED*/
8131703Sbostic 		case 'k':
8234057Sbostic 			jump(argv, "-k", "apropos");
8334057Sbostic 			/*NOTREACHED*/
8433354Sbostic 		/*
8533354Sbostic 		 * Deliberately undocumented; really only useful when
8633354Sbostic 		 * you're moving man pages around.  Not worth adding.
8733354Sbostic 		 */
8832565Sbostic 		case 'w':
8934057Sbostic 			how |= WHERE | ALL;
9032565Sbostic 			break;
9131703Sbostic 		case '?':
9231703Sbostic 		default:
9334057Sbostic 			usage();
9431703Sbostic 		}
9534057Sbostic 	argv += optind;
9631703Sbostic 
9734057Sbostic 	if (!*argv)
9834057Sbostic 		usage();
9933354Sbostic 
10034057Sbostic 	if (!(how & CAT))
10131703Sbostic 		if (!isatty(1))
10234057Sbostic 			how |= CAT;
10331779Sbostic 		else if (pager = getenv("PAGER")) {
10433354Sbostic 			register char *p;
10531779Sbostic 
10631779Sbostic 			/*
10731779Sbostic 			 * if the user uses "more", we make it "more -s"
10831779Sbostic 			 * watch out for PAGER = "mypager /usr/ucb/more"
10931779Sbostic 			 */
11033354Sbostic 			for (p = pager; *p && !isspace(*p); ++p);
11133354Sbostic 			for (; p > pager && *p != '/'; --p);
11233354Sbostic 			if (p != pager)
11333354Sbostic 				++p;
11431779Sbostic 			/* make sure it's "more", not "morex" */
11534058Smarc 			if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))){
11634058Smarc 				char *opager = pager;
11731779Sbostic 				/*
11834058Smarc 				 * allocate space to add the "-s"
11931779Sbostic 				 */
12034058Smarc 				if (!(pager = malloc((u_int)(strlen(opager)
12134058Smarc 				    + sizeof("-s") + 1)))) {
12231779Sbostic 					fputs("man: out of space.\n", stderr);
12331779Sbostic 					exit(1);
12431779Sbostic 				}
12534058Smarc 				(void)sprintf(pager, "%s %s", opager, "-s");
12631779Sbostic 			}
12731779Sbostic 		}
12831779Sbostic 		else
12931703Sbostic 			pager = DEF_PAGER;
13031703Sbostic 	if (!(machine = getenv("MACHINE")))
13131703Sbostic 		machine = MACHINE;
13231713Sbostic 	if (!defpath && !(defpath = getenv("MANPATH")))
13331713Sbostic 		defpath = DEF_PATH;
13431713Sbostic 	locpath = LOCAL_PATH;
13531936Sbostic 	newpath = NEW_PATH;
13633354Sbostic 	man(argv);
13734752Sbostic 	/* use system(3) in case someone's pager is "pager arg1 arg2" */
13833809Sbostic 	if (command)
13933809Sbostic 		(void)system(command);
14033354Sbostic 	exit(0);
14133354Sbostic }
14231703Sbostic 
14333809Sbostic typedef struct {
14433809Sbostic 	char	*name, *msg;
14533809Sbostic } DIR;
14633354Sbostic static DIR	list1[] = {		/* section one list */
14733354Sbostic 	"cat1", "1st",		"cat8", "8th",		"cat6", "6th",
14833354Sbostic 	"cat.old", "old",	NULL, NULL,
14933354Sbostic },		list2[] = {		/* rest of the list */
15033354Sbostic 	"cat2", "2nd",		"cat3", "3rd",		"cat4", "4th",
15133354Sbostic 	"cat5", "5th", 		"cat7", "7th",		"cat3f", "3rd (F)",
15233354Sbostic 	NULL, NULL,
15333354Sbostic },		list3[2];		/* single section */
15433354Sbostic 
15533354Sbostic static
15633354Sbostic man(argv)
15733354Sbostic 	char **argv;
15833354Sbostic {
15933354Sbostic 	register char *p;
16033354Sbostic 	DIR *section, *getsect();
16133354Sbostic 	int res;
16233354Sbostic 
16331703Sbostic 	for (; *argv; ++argv) {
16431906Sbostic 		manpath = defpath;
16533354Sbostic 		section = NULL;
16631703Sbostic 		switch(**argv) {
16733354Sbostic 		case 'l':				/* local */
16834752Sbostic 			/* support the "{l,local,n,new}###"  syntax */
16933354Sbostic 			for (p = *argv; isalpha(*p); ++p);
17033354Sbostic 			if (!strncmp(*argv, "l", p - *argv) ||
17133354Sbostic 			    !strncmp(*argv, "local", p - *argv)) {
17234752Sbostic 				++argv;
17333354Sbostic 				manpath = locpath;
17434752Sbostic 				section = getsect(p);
17531713Sbostic 			}
17631713Sbostic 			break;
17733354Sbostic 		case 'n':				/* new */
17833354Sbostic 			for (p = *argv; isalpha(*p); ++p);
17933354Sbostic 			if (!strncmp(*argv, "n", p - *argv) ||
18033354Sbostic 			    !strncmp(*argv, "new", p - *argv)) {
18134752Sbostic 				++argv;
18233354Sbostic 				manpath = newpath;
18334752Sbostic 				section = getsect(p);
18431703Sbostic 			}
18531703Sbostic 			break;
18631778Sbostic 		/*
18733354Sbostic 		 * old isn't really a separate section of the manual,
18833354Sbostic 		 * and its entries are all in a single directory.
18931778Sbostic 		 */
19033354Sbostic 		case 'o':				/* old */
19133354Sbostic 			for (p = *argv; isalpha(*p); ++p);
19233354Sbostic 			if (!strncmp(*argv, "o", p - *argv) ||
19333354Sbostic 			    !strncmp(*argv, "old", p - *argv)) {
19434752Sbostic 				++argv;
19533354Sbostic 				list3[0] = list1[3];
19633354Sbostic 				section = list3;
19731703Sbostic 			}
19831703Sbostic 			break;
19933354Sbostic 		case '1': case '2': case '3': case '4':
20033354Sbostic 		case '5': case '6': case '7': case '8':
20134752Sbostic 			if (section = getsect(*argv))
20234752Sbostic 				++argv;
20334752Sbostic 		}
20434752Sbostic 
20534752Sbostic 		if (*argv) {
20634752Sbostic 			if (section)
20734752Sbostic 				res = manual(section, *argv);
20834752Sbostic 			else {
20934752Sbostic 				res = manual(list1, *argv);
21034752Sbostic 				if (!res || (how & ALL))
21134752Sbostic 					res += manual(list2, *argv);
21231936Sbostic 			}
21334752Sbostic 			if (res || how&WHERE)
21434752Sbostic 				continue;
21531703Sbostic 		}
21633354Sbostic 
21734752Sbostic 		fputs("man: ", stderr);
21834752Sbostic 		if (*argv)
21934752Sbostic 			fprintf(stderr, "no entry for %s in the ", *argv);
22034752Sbostic 		else
22134752Sbostic 			fputs("what do you want from the ", stderr);
22233809Sbostic 		if (section)
22334752Sbostic 			fprintf(stderr, "%s section of the ", section->msg);
22434752Sbostic 		if (manpath == locpath)
22534752Sbostic 			fputs("local ", stderr);
22634752Sbostic 		else if (manpath == newpath)
22734752Sbostic 			fputs("new ", stderr);
22834752Sbostic 		if (*argv)
22934752Sbostic 			fputs("manual.\n", stderr);
23034752Sbostic 		else
23134752Sbostic 			fputs("manual?\n", stderr);
23234752Sbostic 		exit(1);
23331703Sbostic 	}
23431703Sbostic }
23531703Sbostic 
23631778Sbostic /*
23731778Sbostic  * manual --
23833809Sbostic  *	given a directory list and a file name find a file that
23933809Sbostic  *	matches; check ${directory}/${dir}/{file name} and
24033809Sbostic  *	${directory}/${dir}/${machine}/${file name}.
24131778Sbostic  */
24231703Sbostic static
24331703Sbostic manual(section, name)
24433354Sbostic 	DIR *section;
24533354Sbostic 	char *name;
24631703Sbostic {
24733354Sbostic 	register char *beg, *end;
24833354Sbostic 	register DIR *dp;
24933809Sbostic 	register int res;
25033809Sbostic 	char fname[MAXPATHLEN + 1], *index();
25131703Sbostic 
25233809Sbostic 	for (beg = manpath, res = 0;; beg = end + 1) {
25331703Sbostic 		if (end = index(beg, ':'))
25431703Sbostic 			*end = '\0';
25533809Sbostic 		for (dp = section; dp->name; ++dp) {
25633809Sbostic 			(void)sprintf(fname, "%s/%s/%s.0", beg, dp->name, name);
25733809Sbostic 			if (access(fname, R_OK)) {
25833809Sbostic 				(void)sprintf(fname, "%s/%s/%s/%s.0", beg,
25933809Sbostic 				    dp->name, machine, name);
26033809Sbostic 				if (access(fname, R_OK))
26133809Sbostic 					continue;
26231778Sbostic 			}
26334057Sbostic 			if (how & WHERE)
26433809Sbostic 				printf("man: found in %s.\n", fname);
26534057Sbostic 			else if (how & CAT)
26633809Sbostic 				cat(fname);
26733809Sbostic 			else
26833809Sbostic 				add(fname);
26934057Sbostic 			if (!(how & ALL))
27034057Sbostic 				return(1);
27133809Sbostic 			res = 1;
27233809Sbostic 		}
27331703Sbostic 		if (!end)
27433809Sbostic 			return(res);
27533297Sbostic 		*end = ':';
27631703Sbostic 	}
27731703Sbostic 	/*NOTREACHED*/
27831703Sbostic }
27931703Sbostic 
28031778Sbostic /*
28133809Sbostic  * cat --
28233809Sbostic  *	cat out the file
28331778Sbostic  */
28431703Sbostic static
28533809Sbostic cat(fname)
28633809Sbostic 	char *fname;
28731703Sbostic {
28833809Sbostic 	register int fd, n;
28933809Sbostic 	char buf[BUFSIZ];
29031703Sbostic 
29133809Sbostic 	if (!(fd = open(fname, O_RDONLY, 0))) {
29233809Sbostic 		perror("man: open");
29333809Sbostic 		exit(1);
29431703Sbostic 	}
29533809Sbostic 	while ((n = read(fd, buf, sizeof(buf))) > 0)
29633809Sbostic 		if (write(1, buf, n) != n) {
29733809Sbostic 			perror("man: write");
29833809Sbostic 			exit(1);
29933809Sbostic 		}
30033809Sbostic 	if (n == -1) {
30133809Sbostic 		perror("man: read");
30233809Sbostic 		exit(1);
30333809Sbostic 	}
30433809Sbostic 	(void)close(fd);
30531703Sbostic }
30631703Sbostic 
30731778Sbostic /*
30833809Sbostic  * add --
30933809Sbostic  *	add a file name to the list for future paging
31031778Sbostic  */
31131703Sbostic static
31233809Sbostic add(fname)
31333354Sbostic 	char *fname;
31431703Sbostic {
31533809Sbostic 	static u_int buflen;
31633809Sbostic 	static int len;
31733809Sbostic 	static char *cp;
31833809Sbostic 	int flen;
31933809Sbostic 	char *malloc(), *realloc(), *strcpy();
32031703Sbostic 
32133809Sbostic 	if (!command) {
32233809Sbostic 		if (!(command = malloc(buflen = 1024))) {
32333809Sbostic 			fputs("man: out of space.\n", stderr);
32431703Sbostic 			exit(1);
32531703Sbostic 		}
32633809Sbostic 		len = strlen(strcpy(command, pager));
32733809Sbostic 		cp = command + len;
32833809Sbostic 	}
32933809Sbostic 	flen = strlen(fname);
33033809Sbostic 	if (len + flen + 2 > buflen) {		/* +2 == space, EOS */
33133809Sbostic 		if (!(command = realloc(command, buflen += 1024))) {
33233809Sbostic 			fputs("man: out of space.\n", stderr);
33331703Sbostic 			exit(1);
33431703Sbostic 		}
33533809Sbostic 		cp = command + len;
33631703Sbostic 	}
33733809Sbostic 	*cp++ = ' ';
33833809Sbostic 	len += flen + 1;			/* +1 = space */
33933809Sbostic 	(void)strcpy(cp, fname);
34033809Sbostic 	cp += flen;
34131703Sbostic }
34231703Sbostic 
34331778Sbostic /*
34433354Sbostic  * getsect --
34533354Sbostic  *	return a point to the section structure for a particular suffix
34631778Sbostic  */
34733354Sbostic static DIR *
34833354Sbostic getsect(s)
34933354Sbostic 	char *s;
35031703Sbostic {
35133354Sbostic 	switch(*s++) {
35233354Sbostic 	case '1':
35333354Sbostic 		if (!*s)
35433354Sbostic 			return(list1);
35533354Sbostic 		break;
35633354Sbostic 	case '2':
35733354Sbostic 		if (!*s) {
35833354Sbostic 			list3[0] = list2[0];
35933354Sbostic 			return(list3);
36033354Sbostic 		}
36133354Sbostic 		break;
36233354Sbostic 	/* sect. 3 requests are for either section 3, or section 3[fF]. */
36333354Sbostic 	case '3':
36433354Sbostic 		if (!*s) {
36533354Sbostic 			list3[0] = list2[1];
36633354Sbostic 			return(list3);
36733354Sbostic 		}
36833354Sbostic 		else if ((*s == 'f'  || *s == 'F') && !*++s) {
36933354Sbostic 			list3[0] = list2[5];
37033354Sbostic 			return(list3);
37133354Sbostic 		}
37233354Sbostic 		break;
37333354Sbostic 	case '4':
37433354Sbostic 		if (!*s) {
37533354Sbostic 			list3[0] = list2[2];
37633354Sbostic 			return(list3);
37733354Sbostic 		}
37833354Sbostic 		break;
37933354Sbostic 	case '5':
38033354Sbostic 		if (!*s) {
38133354Sbostic 			list3[0] = list2[3];
38233354Sbostic 			return(list3);
38333354Sbostic 		}
38433354Sbostic 		break;
38533354Sbostic 	case '6':
38633354Sbostic 		if (!*s) {
38733354Sbostic 			list3[0] = list1[2];
38833354Sbostic 			return(list3);
38933354Sbostic 		}
39033354Sbostic 		break;
39133354Sbostic 	case '7':
39233354Sbostic 		if (!*s) {
39333354Sbostic 			list3[0] = list2[4];
39433354Sbostic 			return(list3);
39533354Sbostic 		}
39633354Sbostic 		break;
39733354Sbostic 	case '8':
39833354Sbostic 		if (!*s) {
39933354Sbostic 			list3[0] = list1[1];
40033354Sbostic 			return(list3);
40133354Sbostic 		}
40233354Sbostic 	}
40333354Sbostic 	return((DIR *)NULL);
40431703Sbostic }
40534057Sbostic 
40634057Sbostic /*
40734057Sbostic  * jump --
40834057Sbostic  *	strip out flag argument and jump
40934057Sbostic  */
41034057Sbostic static
41134057Sbostic jump(argv, flag, name)
41234057Sbostic 	char **argv, *name;
41334057Sbostic 	register char *flag;
41434057Sbostic {
41534057Sbostic 	register char **arg;
41634057Sbostic 
41734057Sbostic 	argv[0] = name;
41834057Sbostic 	for (arg = argv + 1; *arg; ++arg)
41934057Sbostic 		if (!strcmp(*arg, flag))
42034057Sbostic 			break;
42134057Sbostic 	for (; *arg; ++arg)
42234057Sbostic 		arg[0] = arg[1];
42334057Sbostic 	execvp(name, argv);
42434057Sbostic 	fprintf(stderr, "%s: Command not found.\n", name);
42534057Sbostic 	exit(1);
42634057Sbostic }
42734057Sbostic 
42834057Sbostic /*
42934057Sbostic  * usage --
43034057Sbostic  *	print usage and die
43134057Sbostic  */
43234057Sbostic static
43334057Sbostic usage()
43434057Sbostic {
43534057Sbostic 	fputs("usage: man [-] [-a] [-M path] [section] title ...\n", stderr);
43634057Sbostic 	exit(1);
43734057Sbostic }
438