xref: /csrg-svn/usr.bin/man/man.c (revision 31713)
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*31713Sbostic static char sccsid[] = "@(#)man.c	5.2 (Berkeley) 06/30/87";
1531703Sbostic #endif not lint
1631703Sbostic 
1731703Sbostic #include <sys/param.h>
1831703Sbostic #include <sys/file.h>
1931703Sbostic #include <ctype.h>
2031703Sbostic 
21*31713Sbostic #define	DEF_PAGER	"more -s"
22*31713Sbostic #define	DEF_PATH	"/usr/man:/usr/local/man"
23*31713Sbostic #define	LOCAL_PATH	"/usr/local/man"
24*31713Sbostic #define	LOCAL_NAME	"local"
25*31713Sbostic #define	NO		0
26*31713Sbostic #define	YES		1
2731703Sbostic 
2831703Sbostic #define	NO_SECTION	0
29*31713Sbostic #define	S_THREEF	9
30*31713Sbostic #define	S_NEW		10
31*31713Sbostic #define	S_OLD		11
32*31713Sbostic 
33*31713Sbostic /* this array maps a character (ex: '4') to an offset in dirlist */
34*31713Sbostic #define	secno(x)	(seclist[(int)(x - '0')])
35*31713Sbostic static int	seclist[] = { -1, 1, 4, 5, 6, 7, 3, 8, 2, -1, -1 };
36*31713Sbostic 
37*31713Sbostic /* sub directory list, ordered for searching */
38*31713Sbostic typedef struct something_meaningful {
39*31713Sbostic 	char	*name,
40*31713Sbostic 		*msg;
41*31713Sbostic } DIR;
42*31713Sbostic 
43*31713Sbostic DIR	dirlist[] = {		/* sub-directory list */
44*31713Sbostic 	"notused", "",		"cat1", "1st",		"cat8", "8th",
45*31713Sbostic 	"cat6", "6th",		"cat2", "2nd",		"cat3", "3rd",
46*31713Sbostic 	"cat4", "4th",		"cat5", "5th", 		"cat7", "7th",
47*31713Sbostic 	"cat3f", "3rd (F)",	"new", "new",		"old", "old",
48*31713Sbostic 	NULL, NULL,
4931703Sbostic };
5031703Sbostic 
51*31713Sbostic static int	nomore;			/* copy file to stdout */
52*31713Sbostic static char	*defpath,		/* default search path */
53*31713Sbostic 		*locpath,		/* local search path */
54*31713Sbostic 		*machine,		/* machine type */
55*31713Sbostic 		*manpath,		/* current search path */
56*31713Sbostic 		*pager;			/* requested pager */
5731703Sbostic 
5831703Sbostic main(argc, argv)
5931703Sbostic 	int	argc;
60*31713Sbostic 	register char	**argv;
6131703Sbostic {
6231703Sbostic 	int	section;
6331703Sbostic 	char	**arg_start, **arg,
6431703Sbostic 		*getenv();
6531703Sbostic 
6631703Sbostic 	arg_start = argv;
6731703Sbostic 	for (--argc, ++argv; argc && (*argv)[0] == '-'; --argc, ++argv)
6831703Sbostic 		switch((*argv)[1]) {
6931703Sbostic 		case 0:			/* just write to stdout */
7031703Sbostic 			nomore = YES;
7131703Sbostic 			break;
7231703Sbostic 		case 'M':
7331703Sbostic 		case 'P':		/* backward compatibility */
7431703Sbostic 			if ((*argv)[2])
7531703Sbostic 				manpath = *argv + 2;
7631703Sbostic 			else {
7731703Sbostic 				if (argc < 2) {
7831703Sbostic 					fprintf(stderr, "%s: missing path\n", *argv);
7931703Sbostic 					exit(1);
8031703Sbostic 				}
8131703Sbostic 				--argc;
8231703Sbostic 				manpath = *++argv;
8331703Sbostic 			}
8431703Sbostic 			break;
8531703Sbostic 		/*
8631703Sbostic 		 * "man -f" and "man -k" are undocumented ways of calling
8731703Sbostic 		 * whatis(1) and apropos(1).  Just strip out the flag
8831703Sbostic 		 * argument and jump.
8931703Sbostic 		 */
9031703Sbostic 		case 'f':
9131703Sbostic 			for (arg = argv; arg[0] = arg[1]; ++arg);
9231703Sbostic 			*arg_start = "whatis";
9331703Sbostic 			execvp(*arg_start, arg_start);
9431703Sbostic 			fputs("whatis: Command not found.\n", stderr);
9531703Sbostic 			exit(1);
9631703Sbostic 		case 'k':
9731703Sbostic 			for (arg = argv; *arg = arg[1]; ++arg);
9831703Sbostic 			*arg_start = "apropos";
9931703Sbostic 			execvp(*arg_start, arg_start);
10031703Sbostic 			fputs("apropos: Command not found.\n", stderr);
10131703Sbostic 			exit(1);
10231703Sbostic 		case '?':
10331703Sbostic 		default:
10431703Sbostic 			usage();
10531703Sbostic 		}
10631703Sbostic 	if (!argc)
10731703Sbostic 		usage();
10831703Sbostic 
10931703Sbostic 	if (!nomore)
11031703Sbostic 		if (!isatty(1))
11131703Sbostic 			nomore = YES;
11231703Sbostic 		else if (!(pager = getenv("PAGER")))
11331703Sbostic 			pager = DEF_PAGER;
11431703Sbostic 	if (!(machine = getenv("MACHINE")))
11531703Sbostic 		machine = MACHINE;
116*31713Sbostic 	if (!defpath && !(defpath = getenv("MANPATH")))
117*31713Sbostic 		defpath = DEF_PATH;
118*31713Sbostic 	locpath = LOCAL_PATH;
119*31713Sbostic 	for (; *defpath && *defpath == ':'; ++defpath);
12031703Sbostic 
12131703Sbostic 	for (; *argv; ++argv) {
12231703Sbostic 		section = NO_SECTION;
123*31713Sbostic 		manpath = DEF_PATH;
12431703Sbostic 		switch(**argv) {
125*31713Sbostic 		/* hardwired section numbers, fix here if they change */
126*31713Sbostic 		case '1': case '2': case '4': case '5': case '6':
127*31713Sbostic 		case '7': case '8':
12831703Sbostic 			if (!(*argv)[1]) {
129*31713Sbostic 				section = secno((*argv)[0]);
130*31713Sbostic 				goto numtest;
131*31713Sbostic 			}
132*31713Sbostic 			break;
133*31713Sbostic 		case '3':
134*31713Sbostic 			if (!(*argv)[1]) {			/* "3" */
135*31713Sbostic 				section = secno((*argv)[0]);
136*31713Sbostic numtest:			if (!*++argv) {
137*31713Sbostic 					fprintf(stderr, "man: what do you want from the %s section of the manual?\n", dirlist[section].msg);
138*31713Sbostic 					exit(1);
139*31713Sbostic 				}
140*31713Sbostic 			}					/* "3[fF]" */
141*31713Sbostic 			if (((*argv)[1] == 'f'  || (*argv)[1] == 'F') && !(*argv)[2]) {
142*31713Sbostic 				section = S_THREEF;
14331703Sbostic 				if (!*++argv) {
144*31713Sbostic 					fprintf(stderr, "man: what do you want from the %s section of the manual?\n", dirlist[S_THREEF].msg);
14531703Sbostic 					exit(1);
14631703Sbostic 				}
14731703Sbostic 			}
14831703Sbostic 			break;
149*31713Sbostic 		case 'l':					/* local */
150*31713Sbostic 			if (!(*argv)[1])			/* "l" */
151*31713Sbostic 				section = NO_SECTION;		/* "l2" */
152*31713Sbostic 			else if (isdigit((*argv)[1]) && !(*argv)[2])
153*31713Sbostic 				section = secno((*argv)[1]);
154*31713Sbostic 			else {
155*31713Sbostic 				int	lex;
156*31713Sbostic 				lex = strcmp(LOCAL_NAME, *argv);
157*31713Sbostic 				if (!lex)			/* "local" */
158*31713Sbostic 					section = NO_SECTION;	/* "local2" */
159*31713Sbostic 				else if (lex < 0 && isdigit((*argv)[sizeof(LOCAL_NAME) - 1]) && !(*argv)[sizeof(LOCAL_NAME)])
160*31713Sbostic 					section = secno((*argv)[sizeof(LOCAL_NAME) - 1]);
161*31713Sbostic 				else
162*31713Sbostic 					break;
16331703Sbostic 			}
164*31713Sbostic 			if (!*++argv) {
165*31713Sbostic 				fputs("man: what do you want from the local section of the manual?\n", stderr);
166*31713Sbostic 				exit(1);
167*31713Sbostic 			}
168*31713Sbostic 			manpath = locpath;
16931703Sbostic 			break;
170*31713Sbostic 		case 'n':					/* new */
171*31713Sbostic 			if (!*argv[1] || !strcmp(*argv, dirlist[S_NEW].name)) {
172*31713Sbostic 				section = S_NEW;
173*31713Sbostic 				goto strtest;
17431703Sbostic 			}
17531703Sbostic 			break;
176*31713Sbostic 		case 'o':					/* old */
177*31713Sbostic 			if (!*argv[1] || !strcmp(*argv, dirlist[S_OLD].name)) {
178*31713Sbostic 				section = S_OLD;
179*31713Sbostic strtest:			if (!*++argv) {
180*31713Sbostic 					fprintf(stderr, "man: what do you want from the %s section of the manual?\n", dirlist[section].msg);
18131703Sbostic 					exit(1);
18231703Sbostic 				}
18331703Sbostic 			}
18431703Sbostic 			break;
18531703Sbostic 		}
18631703Sbostic 		if (!manual(section, *argv))
187*31713Sbostic 			if (manpath == locpath)
188*31713Sbostic 				fprintf(stderr, "No entry for %s in the %s section of the local manual.\n", *argv, dirlist[section].msg);
189*31713Sbostic 			else if (section == NO_SECTION)
190*31713Sbostic 				fprintf(stderr, "No entry for %s in the manual.\n", *argv);
19131703Sbostic 			else
192*31713Sbostic 				fprintf(stderr, "No entry for %s in the %s section of the manual.\n", *argv, dirlist[section].msg);
19331703Sbostic 	}
19431703Sbostic 	exit(0);
19531703Sbostic }
19631703Sbostic 
19731703Sbostic static
19831703Sbostic manual(section, name)
19931703Sbostic 	int	section;
20031703Sbostic 	char	*name;
20131703Sbostic {
202*31713Sbostic 	register DIR	*dir;
203*31713Sbostic 	register char	*beg, *end;
20431703Sbostic 	char	*index();
20531703Sbostic 
20631703Sbostic 	for (beg = manpath;; beg = end + 1) {
20731703Sbostic 		if (end = index(beg, ':'))
20831703Sbostic 			*end = '\0';
20931703Sbostic 		if (section == NO_SECTION) {
210*31713Sbostic 			for (dir = dirlist + 1; dir->name; ++dir)
211*31713Sbostic 				if (find(beg, dir->name, name))
21231703Sbostic 					return(YES);
21331703Sbostic 		}
214*31713Sbostic 		else if (find(beg, dirlist[section].name, name))
21531703Sbostic 			return(YES);
21631703Sbostic 		if (!end)
21731703Sbostic 			return(NO);
21831703Sbostic 	}
21931703Sbostic 	/*NOTREACHED*/
22031703Sbostic }
22131703Sbostic 
22231703Sbostic static
22331703Sbostic find(beg, dir, name)
22431703Sbostic 	char	*beg, *dir, *name;
22531703Sbostic {
22631703Sbostic 	char	fname[MAXPATHLEN + 1];
22731703Sbostic 
228*31713Sbostic 	(void)sprintf(fname, "%s/%s/%s.0", beg, dir, name);
22931703Sbostic 	if (!access(fname, R_OK)) {
23031703Sbostic 		show(fname);
23131703Sbostic 		return(YES);
23231703Sbostic 	}
233*31713Sbostic 	(void)sprintf(fname, "%s/%s/%s/%s.0", beg, dir, machine, name);
23431703Sbostic 	if (!access(fname, R_OK)) {
23531703Sbostic 		show(fname);
23631703Sbostic 		return(YES);
23731703Sbostic 	}
23831703Sbostic 	return(NO);
23931703Sbostic }
24031703Sbostic 
24131703Sbostic static
24231703Sbostic show(fname)
24331703Sbostic 	char	*fname;
24431703Sbostic {
24531703Sbostic 	register int	fd, n;
24631703Sbostic 	char	buf[BUFSIZ];
24731703Sbostic 
24831703Sbostic 	if (nomore) {
24931703Sbostic 		if (!(fd = open(fname, O_RDONLY, 0))) {
25031703Sbostic 			perror("man: open");
25131703Sbostic 			exit(1);
25231703Sbostic 		}
25331703Sbostic 		while ((n = read(fd, buf, sizeof(buf))) > 0)
25431703Sbostic 			if (write(1, buf, n) != n) {
25531703Sbostic 				perror("man: write");
25631703Sbostic 				exit(1);
25731703Sbostic 			}
25831703Sbostic 		if (n == -1) {
25931703Sbostic 			perror("man: read");
26031703Sbostic 			exit(1);
26131703Sbostic 		}
26231703Sbostic 		(void)close(fd);
26331703Sbostic 	}
26431703Sbostic 	else {
26531703Sbostic 		/*
26631703Sbostic 		 * use system(2) in case someone's pager is
26731703Sbostic 		 * "command arg1 arg2"
26831703Sbostic 		 */
26931703Sbostic 		(void)sprintf(buf, "%s %s", pager, fname);
27031703Sbostic 		(void)system(buf);
27131703Sbostic 	}
27231703Sbostic }
27331703Sbostic 
27431703Sbostic static
27531703Sbostic usage()
27631703Sbostic {
27731703Sbostic 	fputs("usage: man [-] [-M path] [section] title ...\n", stderr);
27831703Sbostic 	exit(1);
27931703Sbostic }
280