xref: /csrg-svn/usr.bin/man/man.c (revision 33354)
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*33354Sbostic static char sccsid[] = "@(#)man.c	5.12 (Berkeley) 01/14/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 
3231713Sbostic #define	NO		0
3331713Sbostic #define	YES		1
3431703Sbostic 
3531778Sbostic typedef struct {
36*33354Sbostic 	char	*name, *msg;
3731713Sbostic } DIR;
3831713Sbostic 
3932565Sbostic static int	nomore,			/* copy file to stdout */
4032565Sbostic 		where;			/* just tell me where */
4131713Sbostic static char	*defpath,		/* default search path */
4231713Sbostic 		*locpath,		/* local search path */
4331713Sbostic 		*machine,		/* machine type */
4431713Sbostic 		*manpath,		/* current search path */
4531936Sbostic 		*newpath,		/* new search path */
4631713Sbostic 		*pager;			/* requested pager */
4731703Sbostic 
4831703Sbostic main(argc, argv)
49*33354Sbostic 	int argc;
50*33354Sbostic 	register char **argv;
5131703Sbostic {
52*33354Sbostic 	char **arg_start, **arg, *getenv(), *malloc(), *strcpy();
5331703Sbostic 
5431703Sbostic 	arg_start = argv;
5531703Sbostic 	for (--argc, ++argv; argc && (*argv)[0] == '-'; --argc, ++argv)
5631703Sbostic 		switch((*argv)[1]) {
5731703Sbostic 		case 0:			/* just write to stdout */
5831703Sbostic 			nomore = YES;
5931703Sbostic 			break;
6031703Sbostic 		case 'M':
6131703Sbostic 		case 'P':		/* backward compatibility */
6231703Sbostic 			if ((*argv)[2])
6331906Sbostic 				defpath = *argv + 2;
6431703Sbostic 			else {
6531703Sbostic 				if (argc < 2) {
6631703Sbostic 					fprintf(stderr, "%s: missing path\n", *argv);
6731703Sbostic 					exit(1);
6831703Sbostic 				}
6931703Sbostic 				--argc;
7031906Sbostic 				defpath = *++argv;
7131703Sbostic 			}
7231703Sbostic 			break;
7331703Sbostic 		/*
7431703Sbostic 		 * "man -f" and "man -k" are undocumented ways of calling
7531703Sbostic 		 * whatis(1) and apropos(1).  Just strip out the flag
7631703Sbostic 		 * argument and jump.
7731703Sbostic 		 */
7831703Sbostic 		case 'f':
7931703Sbostic 			for (arg = argv; arg[0] = arg[1]; ++arg);
8031703Sbostic 			*arg_start = "whatis";
8131703Sbostic 			execvp(*arg_start, arg_start);
8231703Sbostic 			fputs("whatis: Command not found.\n", stderr);
8331703Sbostic 			exit(1);
8431703Sbostic 		case 'k':
8531703Sbostic 			for (arg = argv; *arg = arg[1]; ++arg);
8631703Sbostic 			*arg_start = "apropos";
8731703Sbostic 			execvp(*arg_start, arg_start);
8831703Sbostic 			fputs("apropos: Command not found.\n", stderr);
8931703Sbostic 			exit(1);
90*33354Sbostic 		/*
91*33354Sbostic 		 * Deliberately undocumented; really only useful when
92*33354Sbostic 		 * you're moving man pages around.  Not worth adding.
93*33354Sbostic 		 */
9432565Sbostic 		case 'w':
9532565Sbostic 			where = YES;
9632565Sbostic 			break;
9731703Sbostic 		case '?':
9831703Sbostic 		default:
9932614Sbostic 			fprintf(stderr, "man: illegal option -- %c\n", (*argv)[1]);
100*33354Sbostic 			goto usage;
10131703Sbostic 		}
10231703Sbostic 
103*33354Sbostic 	if (!argc) {
104*33354Sbostic usage:		fputs("usage: man [-] [-M path] [section] title ...\n", stderr);
105*33354Sbostic 		exit(1);
106*33354Sbostic 	}
107*33354Sbostic 
10831703Sbostic 	if (!nomore)
10931703Sbostic 		if (!isatty(1))
11031703Sbostic 			nomore = YES;
11131779Sbostic 		else if (pager = getenv("PAGER")) {
112*33354Sbostic 			register char *p;
11331779Sbostic 
11431779Sbostic 			/*
11531779Sbostic 			 * if the user uses "more", we make it "more -s"
11631779Sbostic 			 * watch out for PAGER = "mypager /usr/ucb/more"
11731779Sbostic 			 */
118*33354Sbostic 			for (p = pager; *p && !isspace(*p); ++p);
119*33354Sbostic 			for (; p > pager && *p != '/'; --p);
120*33354Sbostic 			if (p != pager)
121*33354Sbostic 				++p;
12231779Sbostic 			/* make sure it's "more", not "morex" */
123*33354Sbostic 			if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))) {
124*33354Sbostic 				p += 4;
12531779Sbostic 				/*
12631779Sbostic 				 * allocate for the rest of the PAGER
12731779Sbostic 				 * environment variable, a space, and the EOS.
12831779Sbostic 				 */
129*33354Sbostic 				if (!(pager = malloc((u_int)(strlen(p) + sizeof(DEF_PAGER) + 1)))) {
13031779Sbostic 					fputs("man: out of space.\n", stderr);
13131779Sbostic 					exit(1);
13231779Sbostic 				}
133*33354Sbostic 				(void)sprintf(pager, "%s %s", DEF_PAGER, p);
13431779Sbostic 			}
13531779Sbostic 		}
13631779Sbostic 		else
13731703Sbostic 			pager = DEF_PAGER;
13831703Sbostic 	if (!(machine = getenv("MACHINE")))
13931703Sbostic 		machine = MACHINE;
14031713Sbostic 	if (!defpath && !(defpath = getenv("MANPATH")))
14131713Sbostic 		defpath = DEF_PATH;
14231713Sbostic 	locpath = LOCAL_PATH;
14331936Sbostic 	newpath = NEW_PATH;
144*33354Sbostic 	man(argv);
145*33354Sbostic 	exit(0);
146*33354Sbostic }
14731703Sbostic 
148*33354Sbostic static DIR	list1[] = {		/* section one list */
149*33354Sbostic 	"cat1", "1st",		"cat8", "8th",		"cat6", "6th",
150*33354Sbostic 	"cat.old", "old",	NULL, NULL,
151*33354Sbostic },		list2[] = {		/* rest of the list */
152*33354Sbostic 	"cat2", "2nd",		"cat3", "3rd",		"cat4", "4th",
153*33354Sbostic 	"cat5", "5th", 		"cat7", "7th",		"cat3f", "3rd (F)",
154*33354Sbostic 	NULL, NULL,
155*33354Sbostic },		list3[2];		/* single section */
156*33354Sbostic 
157*33354Sbostic static
158*33354Sbostic man(argv)
159*33354Sbostic 	char **argv;
160*33354Sbostic {
161*33354Sbostic 	register char *p;
162*33354Sbostic 	DIR *section, *getsect();
163*33354Sbostic 	int res;
164*33354Sbostic 
16531703Sbostic 	for (; *argv; ++argv) {
16631906Sbostic 		manpath = defpath;
167*33354Sbostic 		section = NULL;
16831703Sbostic 		switch(**argv) {
169*33354Sbostic 		case 'l':				/* local */
170*33354Sbostic 			for (p = *argv; isalpha(*p); ++p);
171*33354Sbostic 			if (!strncmp(*argv, "l", p - *argv) ||
172*33354Sbostic 			    !strncmp(*argv, "local", p - *argv)) {
173*33354Sbostic 				manpath = locpath;
174*33354Sbostic 				if (section = getsect(p))
175*33354Sbostic 					goto argtest;
17631713Sbostic 			}
17731713Sbostic 			break;
178*33354Sbostic 		case 'n':				/* new */
179*33354Sbostic 			for (p = *argv; isalpha(*p); ++p);
180*33354Sbostic 			if (!strncmp(*argv, "n", p - *argv) ||
181*33354Sbostic 			    !strncmp(*argv, "new", p - *argv)) {
182*33354Sbostic 				manpath = newpath;
183*33354Sbostic 				if (section = getsect(p))
184*33354Sbostic 					goto argtest;
18531703Sbostic 			}
18631703Sbostic 			break;
18731778Sbostic 		/*
188*33354Sbostic 		 * old isn't really a separate section of the manual,
189*33354Sbostic 		 * and its entries are all in a single directory.
19031778Sbostic 		 */
191*33354Sbostic 		case 'o':				/* old */
192*33354Sbostic 			for (p = *argv; isalpha(*p); ++p);
193*33354Sbostic 			if (!strncmp(*argv, "o", p - *argv) ||
194*33354Sbostic 			    !strncmp(*argv, "old", p - *argv)) {
195*33354Sbostic 				list3[0] = list1[3];
196*33354Sbostic 				section = list3;
197*33354Sbostic 				goto argtest;
19831703Sbostic 			}
19931703Sbostic 			break;
200*33354Sbostic 		case '1': case '2': case '3': case '4':
201*33354Sbostic 		case '5': case '6': case '7': case '8':
202*33354Sbostic 			if (!(section = getsect(*argv)))
203*33354Sbostic 				break;
204*33354Sbostic argtest:		if (!*++argv) {
205*33354Sbostic 				fprintf(stderr, "man: what do you want from the %s section of the manual?\n", section->msg);
20631936Sbostic 				exit(1);
20731936Sbostic 			}
20831703Sbostic 		}
209*33354Sbostic 
210*33354Sbostic 		res = section ? manual(section, *argv) :
211*33354Sbostic 		    manual(list1, *argv) || manual(list2, *argv);
212*33354Sbostic 		if (!res && !where)
21331713Sbostic 			if (manpath == locpath)
214*33354Sbostic 				if (section)
215*33354Sbostic 					fprintf(stderr, "No entry for %s in the %s section of the local manual.\n", *argv, section->msg);
216*33354Sbostic 				else
21731936Sbostic 					fprintf(stderr, "No entry for %s in the local manual.\n", *argv);
218*33354Sbostic 			else if (manpath == newpath)
219*33354Sbostic 				if (section)
220*33354Sbostic 					fprintf(stderr, "No entry for %s in the %s section of the new manual.\n", *argv, section->msg);
22131936Sbostic 				else
22231936Sbostic 					fprintf(stderr, "No entry for %s in the new manual.\n", *argv);
223*33354Sbostic 			else if (section)
224*33354Sbostic 				fprintf(stderr, "No entry for %s in the %s section of the manual.\n", *argv, section->msg);
225*33354Sbostic 			else
22631713Sbostic 				fprintf(stderr, "No entry for %s in the manual.\n", *argv);
22731703Sbostic 	}
22831703Sbostic }
22931703Sbostic 
23031778Sbostic /*
23131778Sbostic  * manual --
23231778Sbostic  *	given a section number and a file name go through the directory
23331778Sbostic  *	list and find a file that matches.
23431778Sbostic  */
23531703Sbostic static
23631703Sbostic manual(section, name)
237*33354Sbostic 	DIR *section;
238*33354Sbostic 	char *name;
23931703Sbostic {
240*33354Sbostic 	register char *beg, *end;
241*33354Sbostic 	register DIR *dp;
242*33354Sbostic 	char *index();
24331703Sbostic 
24431703Sbostic 	for (beg = manpath;; beg = end + 1) {
24531703Sbostic 		if (end = index(beg, ':'))
24631703Sbostic 			*end = '\0';
247*33354Sbostic 		for (dp = section; dp->name; ++dp)
248*33354Sbostic 			if (find(beg, dp->name, name)) {
249*33354Sbostic 				if (end)
250*33354Sbostic 					*end = ':';
251*33354Sbostic 				return(YES);
25231778Sbostic 			}
25331703Sbostic 		if (!end)
25431703Sbostic 			return(NO);
25533297Sbostic 		*end = ':';
25631703Sbostic 	}
25731703Sbostic 	/*NOTREACHED*/
25831703Sbostic }
25931703Sbostic 
26031778Sbostic /*
26131778Sbostic  * find --
26231778Sbostic  *	given a directory path, a sub-directory and a file name,
26331778Sbostic  *	see if a file exists in ${directory}/${dir}/{file name}
26431778Sbostic  *	or in ${directory}/${dir}/${machine}/${file name}.
26531778Sbostic  */
26631703Sbostic static
26731703Sbostic find(beg, dir, name)
268*33354Sbostic 	char *beg, *dir, *name;
26931703Sbostic {
270*33354Sbostic 	char fname[MAXPATHLEN + 1];
27131703Sbostic 
27231713Sbostic 	(void)sprintf(fname, "%s/%s/%s.0", beg, dir, name);
27332565Sbostic 	if (access(fname, R_OK)) {
27432565Sbostic 		(void)sprintf(fname, "%s/%s/%s/%s.0", beg, dir, machine, name);
27532565Sbostic 		if (access(fname, R_OK))
27632565Sbostic 			return(NO);
27731703Sbostic 	}
27832565Sbostic 	if (where)
27932565Sbostic 		printf("man: found in %s.\n", fname);
28032565Sbostic 	else
28131703Sbostic 		show(fname);
28232565Sbostic 	return(!where);
28331703Sbostic }
28431703Sbostic 
28531778Sbostic /*
28631778Sbostic  * show --
28731778Sbostic  *	display the file
28831778Sbostic  */
28931703Sbostic static
29031703Sbostic show(fname)
291*33354Sbostic 	char *fname;
29231703Sbostic {
293*33354Sbostic 	register int fd, n;
294*33354Sbostic 	char buf[BUFSIZ];
29531703Sbostic 
29631703Sbostic 	if (nomore) {
29731703Sbostic 		if (!(fd = open(fname, O_RDONLY, 0))) {
29831703Sbostic 			perror("man: open");
29931703Sbostic 			exit(1);
30031703Sbostic 		}
30131703Sbostic 		while ((n = read(fd, buf, sizeof(buf))) > 0)
30231703Sbostic 			if (write(1, buf, n) != n) {
30331703Sbostic 				perror("man: write");
30431703Sbostic 				exit(1);
30531703Sbostic 			}
30631703Sbostic 		if (n == -1) {
30731703Sbostic 			perror("man: read");
30831703Sbostic 			exit(1);
30931703Sbostic 		}
31031703Sbostic 		(void)close(fd);
31131703Sbostic 	}
31231703Sbostic 	else {
31331703Sbostic 		/*
314*33354Sbostic 		 * use system(3) in case someone's pager is
31531703Sbostic 		 * "command arg1 arg2"
31631703Sbostic 		 */
31731703Sbostic 		(void)sprintf(buf, "%s %s", pager, fname);
31831703Sbostic 		(void)system(buf);
31931703Sbostic 	}
32031703Sbostic }
32131703Sbostic 
32231778Sbostic /*
323*33354Sbostic  * getsect --
324*33354Sbostic  *	return a point to the section structure for a particular suffix
32531778Sbostic  */
326*33354Sbostic static DIR *
327*33354Sbostic getsect(s)
328*33354Sbostic 	char *s;
32931703Sbostic {
330*33354Sbostic 	switch(*s++) {
331*33354Sbostic 	case '1':
332*33354Sbostic 		if (!*s)
333*33354Sbostic 			return(list1);
334*33354Sbostic 		break;
335*33354Sbostic 	case '2':
336*33354Sbostic 		if (!*s) {
337*33354Sbostic 			list3[0] = list2[0];
338*33354Sbostic 			return(list3);
339*33354Sbostic 		}
340*33354Sbostic 		break;
341*33354Sbostic 	/* sect. 3 requests are for either section 3, or section 3[fF]. */
342*33354Sbostic 	case '3':
343*33354Sbostic 		if (!*s) {
344*33354Sbostic 			list3[0] = list2[1];
345*33354Sbostic 			return(list3);
346*33354Sbostic 		}
347*33354Sbostic 		else if ((*s == 'f'  || *s == 'F') && !*++s) {
348*33354Sbostic 			list3[0] = list2[5];
349*33354Sbostic 			return(list3);
350*33354Sbostic 		}
351*33354Sbostic 		break;
352*33354Sbostic 	case '4':
353*33354Sbostic 		if (!*s) {
354*33354Sbostic 			list3[0] = list2[2];
355*33354Sbostic 			return(list3);
356*33354Sbostic 		}
357*33354Sbostic 		break;
358*33354Sbostic 	case '5':
359*33354Sbostic 		if (!*s) {
360*33354Sbostic 			list3[0] = list2[3];
361*33354Sbostic 			return(list3);
362*33354Sbostic 		}
363*33354Sbostic 		break;
364*33354Sbostic 	case '6':
365*33354Sbostic 		if (!*s) {
366*33354Sbostic 			list3[0] = list1[2];
367*33354Sbostic 			return(list3);
368*33354Sbostic 		}
369*33354Sbostic 		break;
370*33354Sbostic 	case '7':
371*33354Sbostic 		if (!*s) {
372*33354Sbostic 			list3[0] = list2[4];
373*33354Sbostic 			return(list3);
374*33354Sbostic 		}
375*33354Sbostic 		break;
376*33354Sbostic 	case '8':
377*33354Sbostic 		if (!*s) {
378*33354Sbostic 			list3[0] = list1[1];
379*33354Sbostic 			return(list3);
380*33354Sbostic 		}
381*33354Sbostic 	}
382*33354Sbostic 	return((DIR *)NULL);
38331703Sbostic }
384