xref: /csrg-svn/usr.bin/man/man.c (revision 32565)
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*32565Sbostic static char sccsid[] = "@(#)man.c	5.8 (Berkeley) 10/29/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"
2231936Sbostic #define	DEF_PATH	"/usr/man:/usr/new/man:/usr/local/man"
2331936Sbostic 
2431713Sbostic #define	LOCAL_PATH	"/usr/local/man"
2531713Sbostic #define	LOCAL_NAME	"local"
2631936Sbostic 
2731936Sbostic #define	NEW_PATH	"/usr/new/man"
2831936Sbostic #define	NEW_NAME	"new"
2931936Sbostic 
3031713Sbostic #define	NO		0
3131713Sbostic #define	YES		1
3231703Sbostic 
3331703Sbostic #define	NO_SECTION	0
3431713Sbostic #define	S_THREEF	9
3531936Sbostic #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",
5131936Sbostic 	"cat3f", "3rd (F)",	"cat.old", "old",	NULL, NULL,
5231778Sbostic },	sec1list[] = {		/* section one list */
5331778Sbostic 	"notused", "",		"cat1", "1st",		"cat8", "8th",
5431936Sbostic 	"cat6", "6th",		"cat.old", "old",	NULL, NULL,
5531703Sbostic };
5631703Sbostic 
5731778Sbostic static DIR	*dirlist;		/* list of directories to search */
58*32565Sbostic static int	nomore,			/* copy file to stdout */
59*32565Sbostic 		where;			/* just tell me where */
6031713Sbostic static char	*defpath,		/* default search path */
6131713Sbostic 		*locpath,		/* local search path */
6231713Sbostic 		*machine,		/* machine type */
6331713Sbostic 		*manpath,		/* current search path */
6431936Sbostic 		*newpath,		/* new search path */
6531713Sbostic 		*pager;			/* requested pager */
6631703Sbostic 
6731703Sbostic main(argc, argv)
6831703Sbostic 	int	argc;
6931713Sbostic 	register char	**argv;
7031703Sbostic {
7131703Sbostic 	int	section;
7231703Sbostic 	char	**arg_start, **arg,
7331779Sbostic 		*getenv(), *malloc();
7431703Sbostic 
7531703Sbostic 	arg_start = argv;
7631703Sbostic 	for (--argc, ++argv; argc && (*argv)[0] == '-'; --argc, ++argv)
7731703Sbostic 		switch((*argv)[1]) {
7831703Sbostic 		case 0:			/* just write to stdout */
7931703Sbostic 			nomore = YES;
8031703Sbostic 			break;
8131703Sbostic 		case 'M':
8231703Sbostic 		case 'P':		/* backward compatibility */
8331703Sbostic 			if ((*argv)[2])
8431906Sbostic 				defpath = *argv + 2;
8531703Sbostic 			else {
8631703Sbostic 				if (argc < 2) {
8731703Sbostic 					fprintf(stderr, "%s: missing path\n", *argv);
8831703Sbostic 					exit(1);
8931703Sbostic 				}
9031703Sbostic 				--argc;
9131906Sbostic 				defpath = *++argv;
9231703Sbostic 			}
9331703Sbostic 			break;
9431703Sbostic 		/*
9531703Sbostic 		 * "man -f" and "man -k" are undocumented ways of calling
9631703Sbostic 		 * whatis(1) and apropos(1).  Just strip out the flag
9731703Sbostic 		 * argument and jump.
9831703Sbostic 		 */
9931703Sbostic 		case 'f':
10031703Sbostic 			for (arg = argv; arg[0] = arg[1]; ++arg);
10131703Sbostic 			*arg_start = "whatis";
10231703Sbostic 			execvp(*arg_start, arg_start);
10331703Sbostic 			fputs("whatis: Command not found.\n", stderr);
10431703Sbostic 			exit(1);
10531703Sbostic 		case 'k':
10631703Sbostic 			for (arg = argv; *arg = arg[1]; ++arg);
10731703Sbostic 			*arg_start = "apropos";
10831703Sbostic 			execvp(*arg_start, arg_start);
10931703Sbostic 			fputs("apropos: Command not found.\n", stderr);
11031703Sbostic 			exit(1);
111*32565Sbostic 		case 'w':
112*32565Sbostic 			/*
113*32565Sbostic 			 * Deliberately undocumented; really only useful when
114*32565Sbostic 			 * you're moving man pages around.  Not worth adding.
115*32565Sbostic 			 */
116*32565Sbostic 			where = YES;
117*32565Sbostic 			break;
11831703Sbostic 		case '?':
11931703Sbostic 		default:
12031703Sbostic 			usage();
12131703Sbostic 		}
12231703Sbostic 	if (!argc)
12331703Sbostic 		usage();
12431703Sbostic 
12531703Sbostic 	if (!nomore)
12631703Sbostic 		if (!isatty(1))
12731703Sbostic 			nomore = YES;
12831779Sbostic 		else if (pager = getenv("PAGER")) {
12931779Sbostic 			register char	*C;
13031779Sbostic 
13131779Sbostic 			/*
13231779Sbostic 			 * if the user uses "more", we make it "more -s"
13331779Sbostic 			 * watch out for PAGER = "mypager /usr/ucb/more"
13431779Sbostic 			 */
13531779Sbostic 			for (C = pager; *C && !isspace(*C); ++C);
13631779Sbostic 			for (; C > pager && *C != '/'; --C);
13731779Sbostic 			if (C != pager)
13831779Sbostic 				++C;
13931779Sbostic 			/* make sure it's "more", not "morex" */
14031779Sbostic 			if (!strncmp(C, "more", 4) && (!C[4] || isspace(C[4]))) {
14131779Sbostic 				C += 4;
14231779Sbostic 				/*
14331779Sbostic 				 * sizeof is 1 more than # of chars, so,
14431779Sbostic 				 * allocate for the rest of the PAGER
14531779Sbostic 				 * environment variable, a space, and the EOS.
14631779Sbostic 				 */
14731779Sbostic 				if (!(pager = malloc((u_int)(strlen(C) + sizeof(DEF_PAGER) + 1)))) {
14831779Sbostic 					fputs("man: out of space.\n", stderr);
14931779Sbostic 					exit(1);
15031779Sbostic 				}
15131779Sbostic 				(void)sprintf(pager, "%s %s", DEF_PAGER, C);
15231779Sbostic 			}
15331779Sbostic 		}
15431779Sbostic 		else
15531703Sbostic 			pager = DEF_PAGER;
15631703Sbostic 	if (!(machine = getenv("MACHINE")))
15731703Sbostic 		machine = MACHINE;
15831713Sbostic 	if (!defpath && !(defpath = getenv("MANPATH")))
15931713Sbostic 		defpath = DEF_PATH;
16031713Sbostic 	locpath = LOCAL_PATH;
16131936Sbostic 	newpath = NEW_PATH;
16231713Sbostic 	for (; *defpath && *defpath == ':'; ++defpath);
16331703Sbostic 
16431778Sbostic 	/* Gentlemen... start your kludges! */
16531703Sbostic 	for (; *argv; ++argv) {
16631703Sbostic 		section = NO_SECTION;
16731906Sbostic 		manpath = defpath;
16831703Sbostic 		switch(**argv) {
16931778Sbostic 		/*
17031936Sbostic 		 * Section 1 requests are really for section 1, 6, 8, in the
17131936Sbostic 		 * standard, local and new directories and section old. Since
17231936Sbostic 		 * old isn't broken up into a directory of cat[1-8], we just
17331936Sbostic 		 * treat it like a subdirectory of the others.
17431778Sbostic 		 */
17531936Sbostic 		case '1': case '2': case '4': case '5': case '6':
17631713Sbostic 		case '7': case '8':
17731703Sbostic 			if (!(*argv)[1]) {
17831713Sbostic 				section = secno((*argv)[0]);
17931713Sbostic 				goto numtest;
18031713Sbostic 			}
18131713Sbostic 			break;
18231778Sbostic 
18331778Sbostic 		/* sect. 3 requests are for either section 3, or section 3F. */
18431713Sbostic 		case '3':
18531713Sbostic 			if (!(*argv)[1]) {			/* "3" */
18631713Sbostic 				section = secno((*argv)[0]);
18731713Sbostic numtest:			if (!*++argv) {
18831778Sbostic 					fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[section].msg);
18931713Sbostic 					exit(1);
19031713Sbostic 				}
19131713Sbostic 			}					/* "3[fF]" */
19231936Sbostic 			else if (((*argv)[1] == 'f'  || (*argv)[1] == 'F') && !(*argv)[2]) {
19331713Sbostic 				section = S_THREEF;
19431703Sbostic 				if (!*++argv) {
19531778Sbostic 					fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[S_THREEF].msg);
19631703Sbostic 					exit(1);
19731703Sbostic 				}
19831703Sbostic 			}
19931703Sbostic 			break;
20031778Sbostic 		/*
20131936Sbostic 		 * Requests for the new or local sections can have subsection
20231936Sbostic 		 * numbers appended to them, i.e. "local3" is really
20331936Sbostic 		 * local/cat3.
20431778Sbostic 		 */
20531713Sbostic 		case 'l':					/* local */
20631713Sbostic 			if (!(*argv)[1])			/* "l" */
20731713Sbostic 				section = NO_SECTION;		/* "l2" */
20831713Sbostic 			else if (isdigit((*argv)[1]) && !(*argv)[2])
20931713Sbostic 				section = secno((*argv)[1]);
21031713Sbostic 			else {
21131713Sbostic 				int	lex;
21231713Sbostic 				lex = strcmp(LOCAL_NAME, *argv);
21331713Sbostic 				if (!lex)			/* "local" */
21431713Sbostic 					section = NO_SECTION;	/* "local2" */
21531713Sbostic 				else if (lex < 0 && isdigit((*argv)[sizeof(LOCAL_NAME) - 1]) && !(*argv)[sizeof(LOCAL_NAME)])
21631713Sbostic 					section = secno((*argv)[sizeof(LOCAL_NAME) - 1]);
21731713Sbostic 				else
21831713Sbostic 					break;
21931703Sbostic 			}
22031713Sbostic 			if (!*++argv) {
22131713Sbostic 				fputs("man: what do you want from the local section of the manual?\n", stderr);
22231713Sbostic 				exit(1);
22331713Sbostic 			}
22431713Sbostic 			manpath = locpath;
22531703Sbostic 			break;
22631713Sbostic 		case 'n':					/* new */
22731936Sbostic 			if (!(*argv)[1])			/* "n" */
22831936Sbostic 				section = NO_SECTION;		/* "n2" */
22931936Sbostic 			else if (isdigit((*argv)[1]) && !(*argv)[2])
23031936Sbostic 				section = secno((*argv)[1]);
23131936Sbostic 			else {
23231936Sbostic 				int	lex;
23331936Sbostic 				lex = strcmp(NEW_NAME, *argv);
23431936Sbostic 				if (!lex)			/* "new" */
23531936Sbostic 					section = NO_SECTION;	/* "new2" */
23631936Sbostic 				else if (lex < 0 && isdigit((*argv)[sizeof(NEW_NAME) - 1]) && !(*argv)[sizeof(NEW_NAME)])
23731936Sbostic 					section = secno((*argv)[sizeof(NEW_NAME) - 1]);
23831936Sbostic 				else
23931936Sbostic 					break;
24031703Sbostic 			}
24131936Sbostic 			if (!*++argv) {
24231936Sbostic 				fputs("man: what do you want from the new section of the manual?\n", stderr);
24331936Sbostic 				exit(1);
24431936Sbostic 			}
24531936Sbostic 			manpath = newpath;
24631703Sbostic 			break;
24731713Sbostic 		case 'o':					/* old */
24831778Sbostic 			if (!(*argv)[1] || !strcmp(*argv, stanlist[S_OLD].msg)) {
24931713Sbostic 				section = S_OLD;
25031936Sbostic 				if (!*++argv) {
25131778Sbostic 					fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[section].msg);
25231703Sbostic 					exit(1);
25331703Sbostic 				}
25431703Sbostic 			}
25531703Sbostic 			break;
25631703Sbostic 		}
25731936Sbostic 		if (section == 1) {
25831936Sbostic 			dirlist = sec1list;
25931936Sbostic 			section = NO_SECTION;
26031936Sbostic 		}
26131936Sbostic 		else
26231936Sbostic 			dirlist = stanlist;
26331778Sbostic 		/*
26431778Sbostic 		 * This is really silly, but I wanted to put out rational
26531778Sbostic 		 * errors, not just "I couldn't find it."  This if statement
26631778Sbostic 		 * knows an awful lot about what gets assigned to what in
26731778Sbostic 		 * the switch statement we just passed through.  Sorry.
26831778Sbostic 		 */
269*32565Sbostic 		if (!manual(section, *argv) && !where)
27031713Sbostic 			if (manpath == locpath)
27131936Sbostic 				if (section == NO_SECTION)
27231936Sbostic 					fprintf(stderr, "No entry for %s in the local manual.\n", *argv);
27331936Sbostic 				else
27431936Sbostic 					fprintf(stderr, "No entry for %s in the %s section of the local manual.\n", *argv, stanlist[section].msg);
27531936Sbostic 			else if (manpath == newpath)
27631936Sbostic 				if (section == NO_SECTION)
27731936Sbostic 					fprintf(stderr, "No entry for %s in the new manual.\n", *argv);
27831936Sbostic 				else
27931936Sbostic 					fprintf(stderr, "No entry for %s in the %s section of the new manual.\n", *argv, stanlist[section].msg);
28031778Sbostic 			else if (dirlist == sec1list)
28131778Sbostic 				fprintf(stderr, "No entry for %s in the 1st section of the manual.\n", *argv);
28231713Sbostic 			else if (section == NO_SECTION)
28331713Sbostic 				fprintf(stderr, "No entry for %s in the manual.\n", *argv);
28431703Sbostic 			else
28531778Sbostic 				fprintf(stderr, "No entry for %s in the %s section of the manual.\n", *argv, stanlist[section].msg);
28631703Sbostic 	}
28731703Sbostic 	exit(0);
28831703Sbostic }
28931703Sbostic 
29031778Sbostic /*
29131778Sbostic  * manual --
29231778Sbostic  *	given a section number and a file name go through the directory
29331778Sbostic  *	list and find a file that matches.
29431778Sbostic  */
29531703Sbostic static
29631703Sbostic manual(section, name)
29731703Sbostic 	int	section;
29831703Sbostic 	char	*name;
29931703Sbostic {
30031713Sbostic 	register DIR	*dir;
30131713Sbostic 	register char	*beg, *end;
30231703Sbostic 	char	*index();
30331703Sbostic 
30431703Sbostic 	for (beg = manpath;; beg = end + 1) {
30531703Sbostic 		if (end = index(beg, ':'))
30631703Sbostic 			*end = '\0';
30731778Sbostic 		if (section == NO_SECTION)
30831778Sbostic 			for (dir = dirlist; (++dir)->name;) {
30931713Sbostic 				if (find(beg, dir->name, name))
31031703Sbostic 					return(YES);
31131778Sbostic 			}
31231778Sbostic 		else if (find(beg, stanlist[section].name, name))
31331703Sbostic 			return(YES);
31431703Sbostic 		if (!end)
31531703Sbostic 			return(NO);
31631703Sbostic 	}
31731703Sbostic 	/*NOTREACHED*/
31831703Sbostic }
31931703Sbostic 
32031778Sbostic /*
32131778Sbostic  * find --
32231778Sbostic  *	given a directory path, a sub-directory and a file name,
32331778Sbostic  *	see if a file exists in ${directory}/${dir}/{file name}
32431778Sbostic  *	or in ${directory}/${dir}/${machine}/${file name}.
32531778Sbostic  */
32631703Sbostic static
32731703Sbostic find(beg, dir, name)
32831703Sbostic 	char	*beg, *dir, *name;
32931703Sbostic {
33031703Sbostic 	char	fname[MAXPATHLEN + 1];
33131703Sbostic 
33231713Sbostic 	(void)sprintf(fname, "%s/%s/%s.0", beg, dir, name);
333*32565Sbostic 	if (access(fname, R_OK)) {
334*32565Sbostic 		(void)sprintf(fname, "%s/%s/%s/%s.0", beg, dir, machine, name);
335*32565Sbostic 		if (access(fname, R_OK))
336*32565Sbostic 			return(NO);
33731703Sbostic 	}
338*32565Sbostic 	if (where)
339*32565Sbostic 		printf("man: found in %s.\n", fname);
340*32565Sbostic 	else
34131703Sbostic 		show(fname);
342*32565Sbostic 	return(!where);
34331703Sbostic }
34431703Sbostic 
34531778Sbostic /*
34631778Sbostic  * show --
34731778Sbostic  *	display the file
34831778Sbostic  */
34931703Sbostic static
35031703Sbostic show(fname)
35131703Sbostic 	char	*fname;
35231703Sbostic {
35331703Sbostic 	register int	fd, n;
35431703Sbostic 	char	buf[BUFSIZ];
35531703Sbostic 
35631703Sbostic 	if (nomore) {
35731703Sbostic 		if (!(fd = open(fname, O_RDONLY, 0))) {
35831703Sbostic 			perror("man: open");
35931703Sbostic 			exit(1);
36031703Sbostic 		}
36131703Sbostic 		while ((n = read(fd, buf, sizeof(buf))) > 0)
36231703Sbostic 			if (write(1, buf, n) != n) {
36331703Sbostic 				perror("man: write");
36431703Sbostic 				exit(1);
36531703Sbostic 			}
36631703Sbostic 		if (n == -1) {
36731703Sbostic 			perror("man: read");
36831703Sbostic 			exit(1);
36931703Sbostic 		}
37031703Sbostic 		(void)close(fd);
37131703Sbostic 	}
37231703Sbostic 	else {
37331703Sbostic 		/*
37431703Sbostic 		 * use system(2) in case someone's pager is
37531703Sbostic 		 * "command arg1 arg2"
37631703Sbostic 		 */
37731703Sbostic 		(void)sprintf(buf, "%s %s", pager, fname);
37831703Sbostic 		(void)system(buf);
37931703Sbostic 	}
38031703Sbostic }
38131703Sbostic 
38231778Sbostic /*
38331778Sbostic  * usage --
38431778Sbostic  *	print out a usage message and die
38531778Sbostic  */
38631703Sbostic static
38731703Sbostic usage()
38831703Sbostic {
38931703Sbostic 	fputs("usage: man [-] [-M path] [section] title ...\n", stderr);
39031703Sbostic 	exit(1);
39131703Sbostic }
392