xref: /csrg-svn/usr.bin/man/man.c (revision 31778)
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*31778Sbostic static char sccsid[] = "@(#)man.c	5.4 (Berkeley) 07/03/87";
1531703Sbostic #endif not lint
1631703Sbostic 
1731703Sbostic #include <sys/param.h>
1831703Sbostic #include <sys/file.h>
1931703Sbostic #include <ctype.h>
2031703Sbostic 
2131713Sbostic #define	DEF_PAGER	"more -s"
2231713Sbostic #define	DEF_PATH	"/usr/man:/usr/local/man"
2331713Sbostic #define	LOCAL_PATH	"/usr/local/man"
2431713Sbostic #define	LOCAL_NAME	"local"
2531713Sbostic #define	NO		0
2631713Sbostic #define	YES		1
2731703Sbostic 
2831703Sbostic #define	NO_SECTION	0
2931713Sbostic #define	S_THREEF	9
3031713Sbostic #define	S_NEW		10
3131713Sbostic #define	S_OLD		11
3231713Sbostic 
33*31778Sbostic /* this array maps a character (ex: '4') to an offset in stanlist */
3431713Sbostic #define	secno(x)	(seclist[(int)(x - '0')])
3531713Sbostic static int	seclist[] = { -1, 1, 4, 5, 6, 7, 3, 8, 2, -1, -1 };
3631713Sbostic 
3731713Sbostic /* sub directory list, ordered for searching */
38*31778Sbostic typedef struct {
3931713Sbostic 	char	*name,
4031713Sbostic 		*msg;
4131713Sbostic } DIR;
4231713Sbostic 
43*31778Sbostic DIR	stanlist[] = {		/* standard sub-directory list */
4431713Sbostic 	"notused", "",		"cat1", "1st",		"cat8", "8th",
4531713Sbostic 	"cat6", "6th",		"cat2", "2nd",		"cat3", "3rd",
4631713Sbostic 	"cat4", "4th",		"cat5", "5th", 		"cat7", "7th",
47*31778Sbostic 	"cat3f", "3rd (F)",	"cat.new", "new",	"cat.old", "old",
4831713Sbostic 	NULL, NULL,
49*31778Sbostic },	sec1list[] = {		/* section one list */
50*31778Sbostic 	"notused", "",		"cat1", "1st",		"cat8", "8th",
51*31778Sbostic 	"cat6", "6th",		"cat.new", "new",	"cat.old", "old",
52*31778Sbostic 	NULL, NULL,
5331703Sbostic };
5431703Sbostic 
55*31778Sbostic static DIR	*dirlist;		/* list of directories to search */
5631713Sbostic static int	nomore;			/* copy file to stdout */
5731713Sbostic static char	*defpath,		/* default search path */
5831713Sbostic 		*locpath,		/* local search path */
5931713Sbostic 		*machine,		/* machine type */
6031713Sbostic 		*manpath,		/* current search path */
6131713Sbostic 		*pager;			/* requested pager */
6231703Sbostic 
6331703Sbostic main(argc, argv)
6431703Sbostic 	int	argc;
6531713Sbostic 	register char	**argv;
6631703Sbostic {
6731703Sbostic 	int	section;
6831703Sbostic 	char	**arg_start, **arg,
6931703Sbostic 		*getenv();
7031703Sbostic 
7131703Sbostic 	arg_start = argv;
7231703Sbostic 	for (--argc, ++argv; argc && (*argv)[0] == '-'; --argc, ++argv)
7331703Sbostic 		switch((*argv)[1]) {
7431703Sbostic 		case 0:			/* just write to stdout */
7531703Sbostic 			nomore = YES;
7631703Sbostic 			break;
7731703Sbostic 		case 'M':
7831703Sbostic 		case 'P':		/* backward compatibility */
7931703Sbostic 			if ((*argv)[2])
8031703Sbostic 				manpath = *argv + 2;
8131703Sbostic 			else {
8231703Sbostic 				if (argc < 2) {
8331703Sbostic 					fprintf(stderr, "%s: missing path\n", *argv);
8431703Sbostic 					exit(1);
8531703Sbostic 				}
8631703Sbostic 				--argc;
8731703Sbostic 				manpath = *++argv;
8831703Sbostic 			}
8931703Sbostic 			break;
9031703Sbostic 		/*
9131703Sbostic 		 * "man -f" and "man -k" are undocumented ways of calling
9231703Sbostic 		 * whatis(1) and apropos(1).  Just strip out the flag
9331703Sbostic 		 * argument and jump.
9431703Sbostic 		 */
9531703Sbostic 		case 'f':
9631703Sbostic 			for (arg = argv; arg[0] = arg[1]; ++arg);
9731703Sbostic 			*arg_start = "whatis";
9831703Sbostic 			execvp(*arg_start, arg_start);
9931703Sbostic 			fputs("whatis: Command not found.\n", stderr);
10031703Sbostic 			exit(1);
10131703Sbostic 		case 'k':
10231703Sbostic 			for (arg = argv; *arg = arg[1]; ++arg);
10331703Sbostic 			*arg_start = "apropos";
10431703Sbostic 			execvp(*arg_start, arg_start);
10531703Sbostic 			fputs("apropos: Command not found.\n", stderr);
10631703Sbostic 			exit(1);
10731703Sbostic 		case '?':
10831703Sbostic 		default:
10931703Sbostic 			usage();
11031703Sbostic 		}
11131703Sbostic 	if (!argc)
11231703Sbostic 		usage();
11331703Sbostic 
11431703Sbostic 	if (!nomore)
11531703Sbostic 		if (!isatty(1))
11631703Sbostic 			nomore = YES;
11731703Sbostic 		else if (!(pager = getenv("PAGER")))
11831703Sbostic 			pager = DEF_PAGER;
11931703Sbostic 	if (!(machine = getenv("MACHINE")))
12031703Sbostic 		machine = MACHINE;
12131713Sbostic 	if (!defpath && !(defpath = getenv("MANPATH")))
12231713Sbostic 		defpath = DEF_PATH;
12331713Sbostic 	locpath = LOCAL_PATH;
12431713Sbostic 	for (; *defpath && *defpath == ':'; ++defpath);
12531703Sbostic 
126*31778Sbostic 	/* Gentlemen... start your kludges! */
12731703Sbostic 	for (; *argv; ++argv) {
12831703Sbostic 		section = NO_SECTION;
12931713Sbostic 		manpath = DEF_PATH;
130*31778Sbostic 		dirlist = stanlist;
13131703Sbostic 		switch(**argv) {
132*31778Sbostic 		/*
133*31778Sbostic 		 * Section 1 requests are really for section 1, 6, 8, new and
134*31778Sbostic 		 * old.  Since new and old aren't broken up into a directory
135*31778Sbostic 		 * of cat[1-8], we just pretend that they are a subdirectory
136*31778Sbostic 		 * of /usr/man.  Should be fixed -- make new and old full
137*31778Sbostic 		 * structures just like local is, get rid of "sec1list" and
138*31778Sbostic 		 * dirlist.
139*31778Sbostic 		 */
140*31778Sbostic 		case '1':
141*31778Sbostic 			if (!(*argv)[1]) {
142*31778Sbostic 				dirlist = sec1list;
143*31778Sbostic 				goto numtest;
144*31778Sbostic 			}
145*31778Sbostic 			break;
146*31778Sbostic 
147*31778Sbostic 		case '2': case '4': case '5': case '6':
14831713Sbostic 		case '7': case '8':
14931703Sbostic 			if (!(*argv)[1]) {
15031713Sbostic 				section = secno((*argv)[0]);
15131713Sbostic 				goto numtest;
15231713Sbostic 			}
15331713Sbostic 			break;
154*31778Sbostic 
155*31778Sbostic 		/* sect. 3 requests are for either section 3, or section 3F. */
15631713Sbostic 		case '3':
15731713Sbostic 			if (!(*argv)[1]) {			/* "3" */
15831713Sbostic 				section = secno((*argv)[0]);
15931713Sbostic numtest:			if (!*++argv) {
160*31778Sbostic 					fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[section].msg);
16131713Sbostic 					exit(1);
16231713Sbostic 				}
16331713Sbostic 			}					/* "3[fF]" */
16431713Sbostic 			if (((*argv)[1] == 'f'  || (*argv)[1] == 'F') && !(*argv)[2]) {
16531713Sbostic 				section = S_THREEF;
16631703Sbostic 				if (!*++argv) {
167*31778Sbostic 					fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[S_THREEF].msg);
16831703Sbostic 					exit(1);
16931703Sbostic 				}
17031703Sbostic 			}
17131703Sbostic 			break;
172*31778Sbostic 		/*
173*31778Sbostic 		 * Requests for the local section can have subsection numbers
174*31778Sbostic 		 * appended to them, i.e. "local3" is really local/cat3.
175*31778Sbostic 		 */
17631713Sbostic 		case 'l':					/* local */
17731713Sbostic 			if (!(*argv)[1])			/* "l" */
17831713Sbostic 				section = NO_SECTION;		/* "l2" */
17931713Sbostic 			else if (isdigit((*argv)[1]) && !(*argv)[2])
18031713Sbostic 				section = secno((*argv)[1]);
18131713Sbostic 			else {
18231713Sbostic 				int	lex;
18331713Sbostic 				lex = strcmp(LOCAL_NAME, *argv);
18431713Sbostic 				if (!lex)			/* "local" */
18531713Sbostic 					section = NO_SECTION;	/* "local2" */
18631713Sbostic 				else if (lex < 0 && isdigit((*argv)[sizeof(LOCAL_NAME) - 1]) && !(*argv)[sizeof(LOCAL_NAME)])
18731713Sbostic 					section = secno((*argv)[sizeof(LOCAL_NAME) - 1]);
18831713Sbostic 				else
18931713Sbostic 					break;
19031703Sbostic 			}
19131713Sbostic 			if (!*++argv) {
19231713Sbostic 				fputs("man: what do you want from the local section of the manual?\n", stderr);
19331713Sbostic 				exit(1);
19431713Sbostic 			}
19531713Sbostic 			manpath = locpath;
19631703Sbostic 			break;
19731713Sbostic 		case 'n':					/* new */
198*31778Sbostic 			if (!(*argv)[1] || !strcmp(*argv, stanlist[S_NEW].msg)) {
19931713Sbostic 				section = S_NEW;
20031713Sbostic 				goto strtest;
20131703Sbostic 			}
20231703Sbostic 			break;
20331713Sbostic 		case 'o':					/* old */
204*31778Sbostic 			if (!(*argv)[1] || !strcmp(*argv, stanlist[S_OLD].msg)) {
20531713Sbostic 				section = S_OLD;
20631713Sbostic strtest:			if (!*++argv) {
207*31778Sbostic 					fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[section].msg);
20831703Sbostic 					exit(1);
20931703Sbostic 				}
21031703Sbostic 			}
21131703Sbostic 			break;
21231703Sbostic 		}
213*31778Sbostic 		/*
214*31778Sbostic 		 * This is really silly, but I wanted to put out rational
215*31778Sbostic 		 * errors, not just "I couldn't find it."  This if statement
216*31778Sbostic 		 * knows an awful lot about what gets assigned to what in
217*31778Sbostic 		 * the switch statement we just passed through.  Sorry.
218*31778Sbostic 		 */
21931703Sbostic 		if (!manual(section, *argv))
22031713Sbostic 			if (manpath == locpath)
221*31778Sbostic 				fprintf(stderr, "No entry for %s in the %s section of the local manual.\n", *argv, stanlist[section].msg);
222*31778Sbostic 			else if (dirlist == sec1list)
223*31778Sbostic 				fprintf(stderr, "No entry for %s in the 1st section of the manual.\n", *argv);
22431713Sbostic 			else if (section == NO_SECTION)
22531713Sbostic 				fprintf(stderr, "No entry for %s in the manual.\n", *argv);
22631703Sbostic 			else
227*31778Sbostic 				fprintf(stderr, "No entry for %s in the %s section of the manual.\n", *argv, stanlist[section].msg);
22831703Sbostic 	}
22931703Sbostic 	exit(0);
23031703Sbostic }
23131703Sbostic 
232*31778Sbostic /*
233*31778Sbostic  * manual --
234*31778Sbostic  *	given a section number and a file name go through the directory
235*31778Sbostic  *	list and find a file that matches.
236*31778Sbostic  */
23731703Sbostic static
23831703Sbostic manual(section, name)
23931703Sbostic 	int	section;
24031703Sbostic 	char	*name;
24131703Sbostic {
24231713Sbostic 	register DIR	*dir;
24331713Sbostic 	register char	*beg, *end;
24431703Sbostic 	char	*index();
24531703Sbostic 
24631703Sbostic 	for (beg = manpath;; beg = end + 1) {
24731703Sbostic 		if (end = index(beg, ':'))
24831703Sbostic 			*end = '\0';
249*31778Sbostic 		if (section == NO_SECTION)
250*31778Sbostic 			for (dir = dirlist; (++dir)->name;) {
25131713Sbostic 				if (find(beg, dir->name, name))
25231703Sbostic 					return(YES);
253*31778Sbostic 			}
254*31778Sbostic 		else if (find(beg, stanlist[section].name, name))
25531703Sbostic 			return(YES);
25631703Sbostic 		if (!end)
25731703Sbostic 			return(NO);
25831703Sbostic 	}
25931703Sbostic 	/*NOTREACHED*/
26031703Sbostic }
26131703Sbostic 
262*31778Sbostic /*
263*31778Sbostic  * find --
264*31778Sbostic  *	given a directory path, a sub-directory and a file name,
265*31778Sbostic  *	see if a file exists in ${directory}/${dir}/{file name}
266*31778Sbostic  *	or in ${directory}/${dir}/${machine}/${file name}.
267*31778Sbostic  */
26831703Sbostic static
26931703Sbostic find(beg, dir, name)
27031703Sbostic 	char	*beg, *dir, *name;
27131703Sbostic {
27231703Sbostic 	char	fname[MAXPATHLEN + 1];
27331703Sbostic 
27431713Sbostic 	(void)sprintf(fname, "%s/%s/%s.0", beg, dir, name);
27531703Sbostic 	if (!access(fname, R_OK)) {
27631703Sbostic 		show(fname);
27731703Sbostic 		return(YES);
27831703Sbostic 	}
27931713Sbostic 	(void)sprintf(fname, "%s/%s/%s/%s.0", beg, dir, machine, name);
28031703Sbostic 	if (!access(fname, R_OK)) {
28131703Sbostic 		show(fname);
28231703Sbostic 		return(YES);
28331703Sbostic 	}
28431703Sbostic 	return(NO);
28531703Sbostic }
28631703Sbostic 
287*31778Sbostic /*
288*31778Sbostic  * show --
289*31778Sbostic  *	display the file
290*31778Sbostic  */
29131703Sbostic static
29231703Sbostic show(fname)
29331703Sbostic 	char	*fname;
29431703Sbostic {
29531703Sbostic 	register int	fd, n;
29631703Sbostic 	char	buf[BUFSIZ];
29731703Sbostic 
29831703Sbostic 	if (nomore) {
29931703Sbostic 		if (!(fd = open(fname, O_RDONLY, 0))) {
30031703Sbostic 			perror("man: open");
30131703Sbostic 			exit(1);
30231703Sbostic 		}
30331703Sbostic 		while ((n = read(fd, buf, sizeof(buf))) > 0)
30431703Sbostic 			if (write(1, buf, n) != n) {
30531703Sbostic 				perror("man: write");
30631703Sbostic 				exit(1);
30731703Sbostic 			}
30831703Sbostic 		if (n == -1) {
30931703Sbostic 			perror("man: read");
31031703Sbostic 			exit(1);
31131703Sbostic 		}
31231703Sbostic 		(void)close(fd);
31331703Sbostic 	}
31431703Sbostic 	else {
31531703Sbostic 		/*
31631703Sbostic 		 * use system(2) in case someone's pager is
31731703Sbostic 		 * "command arg1 arg2"
31831703Sbostic 		 */
31931703Sbostic 		(void)sprintf(buf, "%s %s", pager, fname);
32031703Sbostic 		(void)system(buf);
32131703Sbostic 	}
32231703Sbostic }
32331703Sbostic 
324*31778Sbostic /*
325*31778Sbostic  * usage --
326*31778Sbostic  *	print out a usage message and die
327*31778Sbostic  */
32831703Sbostic static
32931703Sbostic usage()
33031703Sbostic {
33131703Sbostic 	fputs("usage: man [-] [-M path] [section] title ...\n", stderr);
33231703Sbostic 	exit(1);
33331703Sbostic }
334