xref: /csrg-svn/usr.bin/man/man.c (revision 33809)
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*33809Sbostic static char sccsid[] = "@(#)man.c	5.13 (Berkeley) 03/28/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 
32*33809Sbostic #define	NO	0
33*33809Sbostic #define	YES	1
3431703Sbostic 
3532565Sbostic static int	nomore,			/* copy file to stdout */
3632565Sbostic 		where;			/* just tell me where */
37*33809Sbostic static char	*command,		/* command buffer */
38*33809Sbostic 		*defpath,		/* default search path */
3931713Sbostic 		*locpath,		/* local search path */
4031713Sbostic 		*machine,		/* machine type */
4131713Sbostic 		*manpath,		/* current search path */
4231936Sbostic 		*newpath,		/* new search path */
4331713Sbostic 		*pager;			/* requested pager */
4431703Sbostic 
4531703Sbostic main(argc, argv)
4633354Sbostic 	int argc;
4733354Sbostic 	register char **argv;
4831703Sbostic {
4933354Sbostic 	char **arg_start, **arg, *getenv(), *malloc(), *strcpy();
5031703Sbostic 
5131703Sbostic 	arg_start = argv;
5231703Sbostic 	for (--argc, ++argv; argc && (*argv)[0] == '-'; --argc, ++argv)
5331703Sbostic 		switch((*argv)[1]) {
5431703Sbostic 		case 0:			/* just write to stdout */
5531703Sbostic 			nomore = YES;
5631703Sbostic 			break;
5731703Sbostic 		case 'M':
5831703Sbostic 		case 'P':		/* backward compatibility */
5931703Sbostic 			if ((*argv)[2])
6031906Sbostic 				defpath = *argv + 2;
6131703Sbostic 			else {
6231703Sbostic 				if (argc < 2) {
6331703Sbostic 					fprintf(stderr, "%s: missing path\n", *argv);
6431703Sbostic 					exit(1);
6531703Sbostic 				}
6631703Sbostic 				--argc;
6731906Sbostic 				defpath = *++argv;
6831703Sbostic 			}
6931703Sbostic 			break;
7031703Sbostic 		/*
71*33809Sbostic 		 * "man -f" and "man -k" are backward contemptible,
72*33809Sbostic 		 * undocumented ways of calling whatis(1) and apropos(1).
73*33809Sbostic 		 * Just strip out the flag argument and jump.
7431703Sbostic 		 */
7531703Sbostic 		case 'f':
7631703Sbostic 			for (arg = argv; arg[0] = arg[1]; ++arg);
7731703Sbostic 			*arg_start = "whatis";
7831703Sbostic 			execvp(*arg_start, arg_start);
7931703Sbostic 			fputs("whatis: Command not found.\n", stderr);
8031703Sbostic 			exit(1);
8131703Sbostic 		case 'k':
8231703Sbostic 			for (arg = argv; *arg = arg[1]; ++arg);
8331703Sbostic 			*arg_start = "apropos";
8431703Sbostic 			execvp(*arg_start, arg_start);
8531703Sbostic 			fputs("apropos: Command not found.\n", stderr);
8631703Sbostic 			exit(1);
8733354Sbostic 		/*
8833354Sbostic 		 * Deliberately undocumented; really only useful when
8933354Sbostic 		 * you're moving man pages around.  Not worth adding.
9033354Sbostic 		 */
9132565Sbostic 		case 'w':
9232565Sbostic 			where = YES;
9332565Sbostic 			break;
9431703Sbostic 		case '?':
9531703Sbostic 		default:
9632614Sbostic 			fprintf(stderr, "man: illegal option -- %c\n", (*argv)[1]);
9733354Sbostic 			goto usage;
9831703Sbostic 		}
9931703Sbostic 
10033354Sbostic 	if (!argc) {
10133354Sbostic usage:		fputs("usage: man [-] [-M path] [section] title ...\n", stderr);
10233354Sbostic 		exit(1);
10333354Sbostic 	}
10433354Sbostic 
10531703Sbostic 	if (!nomore)
10631703Sbostic 		if (!isatty(1))
10731703Sbostic 			nomore = YES;
10831779Sbostic 		else if (pager = getenv("PAGER")) {
10933354Sbostic 			register char *p;
11031779Sbostic 
11131779Sbostic 			/*
11231779Sbostic 			 * if the user uses "more", we make it "more -s"
11331779Sbostic 			 * watch out for PAGER = "mypager /usr/ucb/more"
11431779Sbostic 			 */
11533354Sbostic 			for (p = pager; *p && !isspace(*p); ++p);
11633354Sbostic 			for (; p > pager && *p != '/'; --p);
11733354Sbostic 			if (p != pager)
11833354Sbostic 				++p;
11931779Sbostic 			/* make sure it's "more", not "morex" */
12033354Sbostic 			if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))) {
12133354Sbostic 				p += 4;
12231779Sbostic 				/*
12331779Sbostic 				 * allocate for the rest of the PAGER
12431779Sbostic 				 * environment variable, a space, and the EOS.
12531779Sbostic 				 */
12633354Sbostic 				if (!(pager = malloc((u_int)(strlen(p) + sizeof(DEF_PAGER) + 1)))) {
12731779Sbostic 					fputs("man: out of space.\n", stderr);
12831779Sbostic 					exit(1);
12931779Sbostic 				}
13033354Sbostic 				(void)sprintf(pager, "%s %s", DEF_PAGER, p);
13131779Sbostic 			}
13231779Sbostic 		}
13331779Sbostic 		else
13431703Sbostic 			pager = DEF_PAGER;
13531703Sbostic 	if (!(machine = getenv("MACHINE")))
13631703Sbostic 		machine = MACHINE;
13731713Sbostic 	if (!defpath && !(defpath = getenv("MANPATH")))
13831713Sbostic 		defpath = DEF_PATH;
13931713Sbostic 	locpath = LOCAL_PATH;
14031936Sbostic 	newpath = NEW_PATH;
14133354Sbostic 	man(argv);
142*33809Sbostic 	/* use system(3) in case someone's pager is "foo arg1 arg2" */
143*33809Sbostic 	if (command)
144*33809Sbostic 		(void)system(command);
14533354Sbostic 	exit(0);
14633354Sbostic }
14731703Sbostic 
148*33809Sbostic typedef struct {
149*33809Sbostic 	char	*name, *msg;
150*33809Sbostic } DIR;
15133354Sbostic static DIR	list1[] = {		/* section one list */
15233354Sbostic 	"cat1", "1st",		"cat8", "8th",		"cat6", "6th",
15333354Sbostic 	"cat.old", "old",	NULL, NULL,
15433354Sbostic },		list2[] = {		/* rest of the list */
15533354Sbostic 	"cat2", "2nd",		"cat3", "3rd",		"cat4", "4th",
15633354Sbostic 	"cat5", "5th", 		"cat7", "7th",		"cat3f", "3rd (F)",
15733354Sbostic 	NULL, NULL,
15833354Sbostic },		list3[2];		/* single section */
15933354Sbostic 
16033354Sbostic static
16133354Sbostic man(argv)
16233354Sbostic 	char **argv;
16333354Sbostic {
16433354Sbostic 	register char *p;
16533354Sbostic 	DIR *section, *getsect();
16633354Sbostic 	int res;
16733354Sbostic 
16831703Sbostic 	for (; *argv; ++argv) {
16931906Sbostic 		manpath = defpath;
17033354Sbostic 		section = NULL;
17131703Sbostic 		switch(**argv) {
17233354Sbostic 		case 'l':				/* local */
17333354Sbostic 			for (p = *argv; isalpha(*p); ++p);
17433354Sbostic 			if (!strncmp(*argv, "l", p - *argv) ||
17533354Sbostic 			    !strncmp(*argv, "local", p - *argv)) {
17633354Sbostic 				manpath = locpath;
17733354Sbostic 				if (section = getsect(p))
17833354Sbostic 					goto argtest;
17931713Sbostic 			}
18031713Sbostic 			break;
18133354Sbostic 		case 'n':				/* new */
18233354Sbostic 			for (p = *argv; isalpha(*p); ++p);
18333354Sbostic 			if (!strncmp(*argv, "n", p - *argv) ||
18433354Sbostic 			    !strncmp(*argv, "new", p - *argv)) {
18533354Sbostic 				manpath = newpath;
18633354Sbostic 				if (section = getsect(p))
18733354Sbostic 					goto argtest;
18831703Sbostic 			}
18931703Sbostic 			break;
19031778Sbostic 		/*
19133354Sbostic 		 * old isn't really a separate section of the manual,
19233354Sbostic 		 * and its entries are all in a single directory.
19331778Sbostic 		 */
19433354Sbostic 		case 'o':				/* old */
19533354Sbostic 			for (p = *argv; isalpha(*p); ++p);
19633354Sbostic 			if (!strncmp(*argv, "o", p - *argv) ||
19733354Sbostic 			    !strncmp(*argv, "old", p - *argv)) {
19833354Sbostic 				list3[0] = list1[3];
19933354Sbostic 				section = list3;
20033354Sbostic 				goto argtest;
20131703Sbostic 			}
20231703Sbostic 			break;
20333354Sbostic 		case '1': case '2': case '3': case '4':
20433354Sbostic 		case '5': case '6': case '7': case '8':
20533354Sbostic 			if (!(section = getsect(*argv)))
20633354Sbostic 				break;
20733354Sbostic argtest:		if (!*++argv) {
20833354Sbostic 				fprintf(stderr, "man: what do you want from the %s section of the manual?\n", section->msg);
20931936Sbostic 				exit(1);
21031936Sbostic 			}
21131703Sbostic 		}
21233354Sbostic 
213*33809Sbostic 		if (section)
214*33809Sbostic 			res = manual(section, *argv);
215*33809Sbostic 		else {
216*33809Sbostic 			res = manual(list1, *argv);
217*33809Sbostic 			res += manual(list2, *argv);
218*33809Sbostic 		}
219*33809Sbostic 		if (!res && !where) {
22031713Sbostic 			if (manpath == locpath)
22133354Sbostic 				if (section)
22233354Sbostic 					fprintf(stderr, "No entry for %s in the %s section of the local manual.\n", *argv, section->msg);
22333354Sbostic 				else
22431936Sbostic 					fprintf(stderr, "No entry for %s in the local manual.\n", *argv);
22533354Sbostic 			else if (manpath == newpath)
22633354Sbostic 				if (section)
22733354Sbostic 					fprintf(stderr, "No entry for %s in the %s section of the new manual.\n", *argv, section->msg);
22831936Sbostic 				else
22931936Sbostic 					fprintf(stderr, "No entry for %s in the new manual.\n", *argv);
23033354Sbostic 			else if (section)
23133354Sbostic 				fprintf(stderr, "No entry for %s in the %s section of the manual.\n", *argv, section->msg);
23233354Sbostic 			else
23331713Sbostic 				fprintf(stderr, "No entry for %s in the manual.\n", *argv);
234*33809Sbostic 			exit(1);
235*33809Sbostic 		}
23631703Sbostic 	}
23731703Sbostic }
23831703Sbostic 
23931778Sbostic /*
24031778Sbostic  * manual --
241*33809Sbostic  *	given a directory list and a file name find a file that
242*33809Sbostic  *	matches; check ${directory}/${dir}/{file name} and
243*33809Sbostic  *	${directory}/${dir}/${machine}/${file name}.
24431778Sbostic  */
24531703Sbostic static
24631703Sbostic manual(section, name)
24733354Sbostic 	DIR *section;
24833354Sbostic 	char *name;
24931703Sbostic {
25033354Sbostic 	register char *beg, *end;
25133354Sbostic 	register DIR *dp;
252*33809Sbostic 	register int res;
253*33809Sbostic 	char fname[MAXPATHLEN + 1], *index();
25431703Sbostic 
255*33809Sbostic 	for (beg = manpath, res = 0;; beg = end + 1) {
25631703Sbostic 		if (end = index(beg, ':'))
25731703Sbostic 			*end = '\0';
258*33809Sbostic 		for (dp = section; dp->name; ++dp) {
259*33809Sbostic 			(void)sprintf(fname, "%s/%s/%s.0", beg, dp->name, name);
260*33809Sbostic 			if (access(fname, R_OK)) {
261*33809Sbostic 				(void)sprintf(fname, "%s/%s/%s/%s.0", beg,
262*33809Sbostic 				    dp->name, machine, name);
263*33809Sbostic 				if (access(fname, R_OK))
264*33809Sbostic 					continue;
26531778Sbostic 			}
266*33809Sbostic 			if (where)
267*33809Sbostic 				printf("man: found in %s.\n", fname);
268*33809Sbostic 			else if (nomore)
269*33809Sbostic 				cat(fname);
270*33809Sbostic 			else
271*33809Sbostic 				add(fname);
272*33809Sbostic 			res = 1;
273*33809Sbostic 		}
27431703Sbostic 		if (!end)
275*33809Sbostic 			return(res);
27633297Sbostic 		*end = ':';
27731703Sbostic 	}
27831703Sbostic 	/*NOTREACHED*/
27931703Sbostic }
28031703Sbostic 
28131778Sbostic /*
282*33809Sbostic  * cat --
283*33809Sbostic  *	cat out the file
28431778Sbostic  */
28531703Sbostic static
286*33809Sbostic cat(fname)
287*33809Sbostic 	char *fname;
28831703Sbostic {
289*33809Sbostic 	register int fd, n;
290*33809Sbostic 	char buf[BUFSIZ];
29131703Sbostic 
292*33809Sbostic 	if (!(fd = open(fname, O_RDONLY, 0))) {
293*33809Sbostic 		perror("man: open");
294*33809Sbostic 		exit(1);
29531703Sbostic 	}
296*33809Sbostic 	while ((n = read(fd, buf, sizeof(buf))) > 0)
297*33809Sbostic 		if (write(1, buf, n) != n) {
298*33809Sbostic 			perror("man: write");
299*33809Sbostic 			exit(1);
300*33809Sbostic 		}
301*33809Sbostic 	if (n == -1) {
302*33809Sbostic 		perror("man: read");
303*33809Sbostic 		exit(1);
304*33809Sbostic 	}
305*33809Sbostic 	(void)close(fd);
30631703Sbostic }
30731703Sbostic 
30831778Sbostic /*
309*33809Sbostic  * add --
310*33809Sbostic  *	add a file name to the list for future paging
31131778Sbostic  */
31231703Sbostic static
313*33809Sbostic add(fname)
31433354Sbostic 	char *fname;
31531703Sbostic {
316*33809Sbostic 	static u_int buflen;
317*33809Sbostic 	static int len;
318*33809Sbostic 	static char *cp;
319*33809Sbostic 	int flen;
320*33809Sbostic 	char *malloc(), *realloc(), *strcpy();
32131703Sbostic 
322*33809Sbostic 	if (!command) {
323*33809Sbostic 		if (!(command = malloc(buflen = 1024))) {
324*33809Sbostic 			fputs("man: out of space.\n", stderr);
32531703Sbostic 			exit(1);
32631703Sbostic 		}
327*33809Sbostic 		len = strlen(strcpy(command, pager));
328*33809Sbostic 		cp = command + len;
329*33809Sbostic 	}
330*33809Sbostic 	flen = strlen(fname);
331*33809Sbostic 	if (len + flen + 2 > buflen) {		/* +2 == space, EOS */
332*33809Sbostic 		if (!(command = realloc(command, buflen += 1024))) {
333*33809Sbostic 			fputs("man: out of space.\n", stderr);
33431703Sbostic 			exit(1);
33531703Sbostic 		}
336*33809Sbostic 		cp = command + len;
33731703Sbostic 	}
338*33809Sbostic 	*cp++ = ' ';
339*33809Sbostic 	len += flen + 1;			/* +1 = space */
340*33809Sbostic 	(void)strcpy(cp, fname);
341*33809Sbostic 	cp += flen;
34231703Sbostic }
34331703Sbostic 
34431778Sbostic /*
34533354Sbostic  * getsect --
34633354Sbostic  *	return a point to the section structure for a particular suffix
34731778Sbostic  */
34833354Sbostic static DIR *
34933354Sbostic getsect(s)
35033354Sbostic 	char *s;
35131703Sbostic {
35233354Sbostic 	switch(*s++) {
35333354Sbostic 	case '1':
35433354Sbostic 		if (!*s)
35533354Sbostic 			return(list1);
35633354Sbostic 		break;
35733354Sbostic 	case '2':
35833354Sbostic 		if (!*s) {
35933354Sbostic 			list3[0] = list2[0];
36033354Sbostic 			return(list3);
36133354Sbostic 		}
36233354Sbostic 		break;
36333354Sbostic 	/* sect. 3 requests are for either section 3, or section 3[fF]. */
36433354Sbostic 	case '3':
36533354Sbostic 		if (!*s) {
36633354Sbostic 			list3[0] = list2[1];
36733354Sbostic 			return(list3);
36833354Sbostic 		}
36933354Sbostic 		else if ((*s == 'f'  || *s == 'F') && !*++s) {
37033354Sbostic 			list3[0] = list2[5];
37133354Sbostic 			return(list3);
37233354Sbostic 		}
37333354Sbostic 		break;
37433354Sbostic 	case '4':
37533354Sbostic 		if (!*s) {
37633354Sbostic 			list3[0] = list2[2];
37733354Sbostic 			return(list3);
37833354Sbostic 		}
37933354Sbostic 		break;
38033354Sbostic 	case '5':
38133354Sbostic 		if (!*s) {
38233354Sbostic 			list3[0] = list2[3];
38333354Sbostic 			return(list3);
38433354Sbostic 		}
38533354Sbostic 		break;
38633354Sbostic 	case '6':
38733354Sbostic 		if (!*s) {
38833354Sbostic 			list3[0] = list1[2];
38933354Sbostic 			return(list3);
39033354Sbostic 		}
39133354Sbostic 		break;
39233354Sbostic 	case '7':
39333354Sbostic 		if (!*s) {
39433354Sbostic 			list3[0] = list2[4];
39533354Sbostic 			return(list3);
39633354Sbostic 		}
39733354Sbostic 		break;
39833354Sbostic 	case '8':
39933354Sbostic 		if (!*s) {
40033354Sbostic 			list3[0] = list1[1];
40133354Sbostic 			return(list3);
40233354Sbostic 		}
40333354Sbostic 	}
40433354Sbostic 	return((DIR *)NULL);
40531703Sbostic }
406