xref: /csrg-svn/usr.bin/man/man.c (revision 31936)
131703Sbostic /*
231703Sbostic  * Copyright (c) 1987 Regents of the University of California.
331703Sbostic  * All rights reserved.  The Berkeley software License Agreement
431703Sbostic  * specifies the terms and conditions for redistribution.
531703Sbostic  */
631703Sbostic 
731703Sbostic #ifndef lint
831703Sbostic char copyright[] =
931703Sbostic "@(#) Copyright (c) 1987 Regents of the University of California.\n\
1031703Sbostic  All rights reserved.\n";
1131703Sbostic #endif not lint
1231703Sbostic 
1331703Sbostic #ifndef lint
14*31936Sbostic static char sccsid[] = "@(#)man.c	5.7 (Berkeley) 07/26/87";
1531703Sbostic #endif not lint
1631703Sbostic 
1731703Sbostic #include <sys/param.h>
1831703Sbostic #include <sys/file.h>
1931703Sbostic #include <ctype.h>
2031703Sbostic 
2131779Sbostic #define	DEF_PAGER	"/usr/ucb/more -s"
22*31936Sbostic #define	DEF_PATH	"/usr/man:/usr/new/man:/usr/local/man"
23*31936Sbostic 
2431713Sbostic #define	LOCAL_PATH	"/usr/local/man"
2531713Sbostic #define	LOCAL_NAME	"local"
26*31936Sbostic 
27*31936Sbostic #define	NEW_PATH	"/usr/new/man"
28*31936Sbostic #define	NEW_NAME	"new"
29*31936Sbostic 
3031713Sbostic #define	NO		0
3131713Sbostic #define	YES		1
3231703Sbostic 
3331703Sbostic #define	NO_SECTION	0
3431713Sbostic #define	S_THREEF	9
35*31936Sbostic #define	S_OLD		10
3631713Sbostic 
3731778Sbostic /* this array maps a character (ex: '4') to an offset in stanlist */
3831713Sbostic #define	secno(x)	(seclist[(int)(x - '0')])
3931713Sbostic static int	seclist[] = { -1, 1, 4, 5, 6, 7, 3, 8, 2, -1, -1 };
4031713Sbostic 
4131713Sbostic /* sub directory list, ordered for searching */
4231778Sbostic typedef struct {
4331713Sbostic 	char	*name,
4431713Sbostic 		*msg;
4531713Sbostic } DIR;
4631713Sbostic 
4731778Sbostic DIR	stanlist[] = {		/* standard sub-directory list */
4831713Sbostic 	"notused", "",		"cat1", "1st",		"cat8", "8th",
4931713Sbostic 	"cat6", "6th",		"cat2", "2nd",		"cat3", "3rd",
5031713Sbostic 	"cat4", "4th",		"cat5", "5th", 		"cat7", "7th",
51*31936Sbostic 	"cat3f", "3rd (F)",	"cat.old", "old",	NULL, NULL,
5231778Sbostic },	sec1list[] = {		/* section one list */
5331778Sbostic 	"notused", "",		"cat1", "1st",		"cat8", "8th",
54*31936Sbostic 	"cat6", "6th",		"cat.old", "old",	NULL, NULL,
5531703Sbostic };
5631703Sbostic 
5731778Sbostic static DIR	*dirlist;		/* list of directories to search */
5831713Sbostic static int	nomore;			/* copy file to stdout */
5931713Sbostic static char	*defpath,		/* default search path */
6031713Sbostic 		*locpath,		/* local search path */
6131713Sbostic 		*machine,		/* machine type */
6231713Sbostic 		*manpath,		/* current search path */
63*31936Sbostic 		*newpath,		/* new search path */
6431713Sbostic 		*pager;			/* requested pager */
6531703Sbostic 
6631703Sbostic main(argc, argv)
6731703Sbostic 	int	argc;
6831713Sbostic 	register char	**argv;
6931703Sbostic {
7031703Sbostic 	int	section;
7131703Sbostic 	char	**arg_start, **arg,
7231779Sbostic 		*getenv(), *malloc();
7331703Sbostic 
7431703Sbostic 	arg_start = argv;
7531703Sbostic 	for (--argc, ++argv; argc && (*argv)[0] == '-'; --argc, ++argv)
7631703Sbostic 		switch((*argv)[1]) {
7731703Sbostic 		case 0:			/* just write to stdout */
7831703Sbostic 			nomore = YES;
7931703Sbostic 			break;
8031703Sbostic 		case 'M':
8131703Sbostic 		case 'P':		/* backward compatibility */
8231703Sbostic 			if ((*argv)[2])
8331906Sbostic 				defpath = *argv + 2;
8431703Sbostic 			else {
8531703Sbostic 				if (argc < 2) {
8631703Sbostic 					fprintf(stderr, "%s: missing path\n", *argv);
8731703Sbostic 					exit(1);
8831703Sbostic 				}
8931703Sbostic 				--argc;
9031906Sbostic 				defpath = *++argv;
9131703Sbostic 			}
9231703Sbostic 			break;
9331703Sbostic 		/*
9431703Sbostic 		 * "man -f" and "man -k" are undocumented ways of calling
9531703Sbostic 		 * whatis(1) and apropos(1).  Just strip out the flag
9631703Sbostic 		 * argument and jump.
9731703Sbostic 		 */
9831703Sbostic 		case 'f':
9931703Sbostic 			for (arg = argv; arg[0] = arg[1]; ++arg);
10031703Sbostic 			*arg_start = "whatis";
10131703Sbostic 			execvp(*arg_start, arg_start);
10231703Sbostic 			fputs("whatis: Command not found.\n", stderr);
10331703Sbostic 			exit(1);
10431703Sbostic 		case 'k':
10531703Sbostic 			for (arg = argv; *arg = arg[1]; ++arg);
10631703Sbostic 			*arg_start = "apropos";
10731703Sbostic 			execvp(*arg_start, arg_start);
10831703Sbostic 			fputs("apropos: Command not found.\n", stderr);
10931703Sbostic 			exit(1);
11031703Sbostic 		case '?':
11131703Sbostic 		default:
11231703Sbostic 			usage();
11331703Sbostic 		}
11431703Sbostic 	if (!argc)
11531703Sbostic 		usage();
11631703Sbostic 
11731703Sbostic 	if (!nomore)
11831703Sbostic 		if (!isatty(1))
11931703Sbostic 			nomore = YES;
12031779Sbostic 		else if (pager = getenv("PAGER")) {
12131779Sbostic 			register char	*C;
12231779Sbostic 
12331779Sbostic 			/*
12431779Sbostic 			 * if the user uses "more", we make it "more -s"
12531779Sbostic 			 * watch out for PAGER = "mypager /usr/ucb/more"
12631779Sbostic 			 */
12731779Sbostic 			for (C = pager; *C && !isspace(*C); ++C);
12831779Sbostic 			for (; C > pager && *C != '/'; --C);
12931779Sbostic 			if (C != pager)
13031779Sbostic 				++C;
13131779Sbostic 			/* make sure it's "more", not "morex" */
13231779Sbostic 			if (!strncmp(C, "more", 4) && (!C[4] || isspace(C[4]))) {
13331779Sbostic 				C += 4;
13431779Sbostic 				/*
13531779Sbostic 				 * sizeof is 1 more than # of chars, so,
13631779Sbostic 				 * allocate for the rest of the PAGER
13731779Sbostic 				 * environment variable, a space, and the EOS.
13831779Sbostic 				 */
13931779Sbostic 				if (!(pager = malloc((u_int)(strlen(C) + sizeof(DEF_PAGER) + 1)))) {
14031779Sbostic 					fputs("man: out of space.\n", stderr);
14131779Sbostic 					exit(1);
14231779Sbostic 				}
14331779Sbostic 				(void)sprintf(pager, "%s %s", DEF_PAGER, C);
14431779Sbostic 			}
14531779Sbostic 		}
14631779Sbostic 		else
14731703Sbostic 			pager = DEF_PAGER;
14831703Sbostic 	if (!(machine = getenv("MACHINE")))
14931703Sbostic 		machine = MACHINE;
15031713Sbostic 	if (!defpath && !(defpath = getenv("MANPATH")))
15131713Sbostic 		defpath = DEF_PATH;
15231713Sbostic 	locpath = LOCAL_PATH;
153*31936Sbostic 	newpath = NEW_PATH;
15431713Sbostic 	for (; *defpath && *defpath == ':'; ++defpath);
15531703Sbostic 
15631778Sbostic 	/* Gentlemen... start your kludges! */
15731703Sbostic 	for (; *argv; ++argv) {
15831703Sbostic 		section = NO_SECTION;
15931906Sbostic 		manpath = defpath;
16031703Sbostic 		switch(**argv) {
16131778Sbostic 		/*
162*31936Sbostic 		 * Section 1 requests are really for section 1, 6, 8, in the
163*31936Sbostic 		 * standard, local and new directories and section old. Since
164*31936Sbostic 		 * old isn't broken up into a directory of cat[1-8], we just
165*31936Sbostic 		 * treat it like a subdirectory of the others.
16631778Sbostic 		 */
167*31936Sbostic 		case '1': case '2': case '4': case '5': case '6':
16831713Sbostic 		case '7': case '8':
16931703Sbostic 			if (!(*argv)[1]) {
17031713Sbostic 				section = secno((*argv)[0]);
17131713Sbostic 				goto numtest;
17231713Sbostic 			}
17331713Sbostic 			break;
17431778Sbostic 
17531778Sbostic 		/* sect. 3 requests are for either section 3, or section 3F. */
17631713Sbostic 		case '3':
17731713Sbostic 			if (!(*argv)[1]) {			/* "3" */
17831713Sbostic 				section = secno((*argv)[0]);
17931713Sbostic numtest:			if (!*++argv) {
18031778Sbostic 					fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[section].msg);
18131713Sbostic 					exit(1);
18231713Sbostic 				}
18331713Sbostic 			}					/* "3[fF]" */
184*31936Sbostic 			else if (((*argv)[1] == 'f'  || (*argv)[1] == 'F') && !(*argv)[2]) {
18531713Sbostic 				section = S_THREEF;
18631703Sbostic 				if (!*++argv) {
18731778Sbostic 					fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[S_THREEF].msg);
18831703Sbostic 					exit(1);
18931703Sbostic 				}
19031703Sbostic 			}
19131703Sbostic 			break;
19231778Sbostic 		/*
193*31936Sbostic 		 * Requests for the new or local sections can have subsection
194*31936Sbostic 		 * numbers appended to them, i.e. "local3" is really
195*31936Sbostic 		 * local/cat3.
19631778Sbostic 		 */
19731713Sbostic 		case 'l':					/* local */
19831713Sbostic 			if (!(*argv)[1])			/* "l" */
19931713Sbostic 				section = NO_SECTION;		/* "l2" */
20031713Sbostic 			else if (isdigit((*argv)[1]) && !(*argv)[2])
20131713Sbostic 				section = secno((*argv)[1]);
20231713Sbostic 			else {
20331713Sbostic 				int	lex;
20431713Sbostic 				lex = strcmp(LOCAL_NAME, *argv);
20531713Sbostic 				if (!lex)			/* "local" */
20631713Sbostic 					section = NO_SECTION;	/* "local2" */
20731713Sbostic 				else if (lex < 0 && isdigit((*argv)[sizeof(LOCAL_NAME) - 1]) && !(*argv)[sizeof(LOCAL_NAME)])
20831713Sbostic 					section = secno((*argv)[sizeof(LOCAL_NAME) - 1]);
20931713Sbostic 				else
21031713Sbostic 					break;
21131703Sbostic 			}
21231713Sbostic 			if (!*++argv) {
21331713Sbostic 				fputs("man: what do you want from the local section of the manual?\n", stderr);
21431713Sbostic 				exit(1);
21531713Sbostic 			}
21631713Sbostic 			manpath = locpath;
21731703Sbostic 			break;
21831713Sbostic 		case 'n':					/* new */
219*31936Sbostic 			if (!(*argv)[1])			/* "n" */
220*31936Sbostic 				section = NO_SECTION;		/* "n2" */
221*31936Sbostic 			else if (isdigit((*argv)[1]) && !(*argv)[2])
222*31936Sbostic 				section = secno((*argv)[1]);
223*31936Sbostic 			else {
224*31936Sbostic 				int	lex;
225*31936Sbostic 				lex = strcmp(NEW_NAME, *argv);
226*31936Sbostic 				if (!lex)			/* "new" */
227*31936Sbostic 					section = NO_SECTION;	/* "new2" */
228*31936Sbostic 				else if (lex < 0 && isdigit((*argv)[sizeof(NEW_NAME) - 1]) && !(*argv)[sizeof(NEW_NAME)])
229*31936Sbostic 					section = secno((*argv)[sizeof(NEW_NAME) - 1]);
230*31936Sbostic 				else
231*31936Sbostic 					break;
23231703Sbostic 			}
233*31936Sbostic 			if (!*++argv) {
234*31936Sbostic 				fputs("man: what do you want from the new section of the manual?\n", stderr);
235*31936Sbostic 				exit(1);
236*31936Sbostic 			}
237*31936Sbostic 			manpath = newpath;
23831703Sbostic 			break;
23931713Sbostic 		case 'o':					/* old */
24031778Sbostic 			if (!(*argv)[1] || !strcmp(*argv, stanlist[S_OLD].msg)) {
24131713Sbostic 				section = S_OLD;
242*31936Sbostic 				if (!*++argv) {
24331778Sbostic 					fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[section].msg);
24431703Sbostic 					exit(1);
24531703Sbostic 				}
24631703Sbostic 			}
24731703Sbostic 			break;
24831703Sbostic 		}
249*31936Sbostic 		if (section == 1) {
250*31936Sbostic 			dirlist = sec1list;
251*31936Sbostic 			section = NO_SECTION;
252*31936Sbostic 		}
253*31936Sbostic 		else
254*31936Sbostic 			dirlist = stanlist;
25531778Sbostic 		/*
25631778Sbostic 		 * This is really silly, but I wanted to put out rational
25731778Sbostic 		 * errors, not just "I couldn't find it."  This if statement
25831778Sbostic 		 * knows an awful lot about what gets assigned to what in
25931778Sbostic 		 * the switch statement we just passed through.  Sorry.
26031778Sbostic 		 */
26131703Sbostic 		if (!manual(section, *argv))
26231713Sbostic 			if (manpath == locpath)
263*31936Sbostic 				if (section == NO_SECTION)
264*31936Sbostic 					fprintf(stderr, "No entry for %s in the local manual.\n", *argv);
265*31936Sbostic 				else
266*31936Sbostic 					fprintf(stderr, "No entry for %s in the %s section of the local manual.\n", *argv, stanlist[section].msg);
267*31936Sbostic 			else if (manpath == newpath)
268*31936Sbostic 				if (section == NO_SECTION)
269*31936Sbostic 					fprintf(stderr, "No entry for %s in the new manual.\n", *argv);
270*31936Sbostic 				else
271*31936Sbostic 					fprintf(stderr, "No entry for %s in the %s section of the new manual.\n", *argv, stanlist[section].msg);
27231778Sbostic 			else if (dirlist == sec1list)
27331778Sbostic 				fprintf(stderr, "No entry for %s in the 1st section of the manual.\n", *argv);
27431713Sbostic 			else if (section == NO_SECTION)
27531713Sbostic 				fprintf(stderr, "No entry for %s in the manual.\n", *argv);
27631703Sbostic 			else
27731778Sbostic 				fprintf(stderr, "No entry for %s in the %s section of the manual.\n", *argv, stanlist[section].msg);
27831703Sbostic 	}
27931703Sbostic 	exit(0);
28031703Sbostic }
28131703Sbostic 
28231778Sbostic /*
28331778Sbostic  * manual --
28431778Sbostic  *	given a section number and a file name go through the directory
28531778Sbostic  *	list and find a file that matches.
28631778Sbostic  */
28731703Sbostic static
28831703Sbostic manual(section, name)
28931703Sbostic 	int	section;
29031703Sbostic 	char	*name;
29131703Sbostic {
29231713Sbostic 	register DIR	*dir;
29331713Sbostic 	register char	*beg, *end;
29431703Sbostic 	char	*index();
29531703Sbostic 
29631703Sbostic 	for (beg = manpath;; beg = end + 1) {
29731703Sbostic 		if (end = index(beg, ':'))
29831703Sbostic 			*end = '\0';
29931778Sbostic 		if (section == NO_SECTION)
30031778Sbostic 			for (dir = dirlist; (++dir)->name;) {
30131713Sbostic 				if (find(beg, dir->name, name))
30231703Sbostic 					return(YES);
30331778Sbostic 			}
30431778Sbostic 		else if (find(beg, stanlist[section].name, name))
30531703Sbostic 			return(YES);
30631703Sbostic 		if (!end)
30731703Sbostic 			return(NO);
30831703Sbostic 	}
30931703Sbostic 	/*NOTREACHED*/
31031703Sbostic }
31131703Sbostic 
31231778Sbostic /*
31331778Sbostic  * find --
31431778Sbostic  *	given a directory path, a sub-directory and a file name,
31531778Sbostic  *	see if a file exists in ${directory}/${dir}/{file name}
31631778Sbostic  *	or in ${directory}/${dir}/${machine}/${file name}.
31731778Sbostic  */
31831703Sbostic static
31931703Sbostic find(beg, dir, name)
32031703Sbostic 	char	*beg, *dir, *name;
32131703Sbostic {
32231703Sbostic 	char	fname[MAXPATHLEN + 1];
32331703Sbostic 
32431713Sbostic 	(void)sprintf(fname, "%s/%s/%s.0", beg, dir, name);
32531703Sbostic 	if (!access(fname, R_OK)) {
32631703Sbostic 		show(fname);
32731703Sbostic 		return(YES);
32831703Sbostic 	}
32931713Sbostic 	(void)sprintf(fname, "%s/%s/%s/%s.0", beg, dir, machine, name);
33031703Sbostic 	if (!access(fname, R_OK)) {
33131703Sbostic 		show(fname);
33231703Sbostic 		return(YES);
33331703Sbostic 	}
33431703Sbostic 	return(NO);
33531703Sbostic }
33631703Sbostic 
33731778Sbostic /*
33831778Sbostic  * show --
33931778Sbostic  *	display the file
34031778Sbostic  */
34131703Sbostic static
34231703Sbostic show(fname)
34331703Sbostic 	char	*fname;
34431703Sbostic {
34531703Sbostic 	register int	fd, n;
34631703Sbostic 	char	buf[BUFSIZ];
34731703Sbostic 
34831703Sbostic 	if (nomore) {
34931703Sbostic 		if (!(fd = open(fname, O_RDONLY, 0))) {
35031703Sbostic 			perror("man: open");
35131703Sbostic 			exit(1);
35231703Sbostic 		}
35331703Sbostic 		while ((n = read(fd, buf, sizeof(buf))) > 0)
35431703Sbostic 			if (write(1, buf, n) != n) {
35531703Sbostic 				perror("man: write");
35631703Sbostic 				exit(1);
35731703Sbostic 			}
35831703Sbostic 		if (n == -1) {
35931703Sbostic 			perror("man: read");
36031703Sbostic 			exit(1);
36131703Sbostic 		}
36231703Sbostic 		(void)close(fd);
36331703Sbostic 	}
36431703Sbostic 	else {
36531703Sbostic 		/*
36631703Sbostic 		 * use system(2) in case someone's pager is
36731703Sbostic 		 * "command arg1 arg2"
36831703Sbostic 		 */
36931703Sbostic 		(void)sprintf(buf, "%s %s", pager, fname);
37031703Sbostic 		(void)system(buf);
37131703Sbostic 	}
37231703Sbostic }
37331703Sbostic 
37431778Sbostic /*
37531778Sbostic  * usage --
37631778Sbostic  *	print out a usage message and die
37731778Sbostic  */
37831703Sbostic static
37931703Sbostic usage()
38031703Sbostic {
38131703Sbostic 	fputs("usage: man [-] [-M path] [section] title ...\n", stderr);
38231703Sbostic 	exit(1);
38331703Sbostic }
384