xref: /csrg-svn/usr.bin/man/man.c (revision 31906)
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*31906Sbostic static char sccsid[] = "@(#)man.c	5.6 (Berkeley) 07/20/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"
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 
3331778Sbostic /* 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 */
3831778Sbostic typedef struct {
3931713Sbostic 	char	*name,
4031713Sbostic 		*msg;
4131713Sbostic } DIR;
4231713Sbostic 
4331778Sbostic 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",
4731778Sbostic 	"cat3f", "3rd (F)",	"cat.new", "new",	"cat.old", "old",
4831713Sbostic 	NULL, NULL,
4931778Sbostic },	sec1list[] = {		/* section one list */
5031778Sbostic 	"notused", "",		"cat1", "1st",		"cat8", "8th",
5131778Sbostic 	"cat6", "6th",		"cat.new", "new",	"cat.old", "old",
5231778Sbostic 	NULL, NULL,
5331703Sbostic };
5431703Sbostic 
5531778Sbostic 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,
6931779Sbostic 		*getenv(), *malloc();
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])
80*31906Sbostic 				defpath = *argv + 2;
8131703Sbostic 			else {
8231703Sbostic 				if (argc < 2) {
8331703Sbostic 					fprintf(stderr, "%s: missing path\n", *argv);
8431703Sbostic 					exit(1);
8531703Sbostic 				}
8631703Sbostic 				--argc;
87*31906Sbostic 				defpath = *++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;
11731779Sbostic 		else if (pager = getenv("PAGER")) {
11831779Sbostic 			register char	*C;
11931779Sbostic 
12031779Sbostic 			/*
12131779Sbostic 			 * if the user uses "more", we make it "more -s"
12231779Sbostic 			 * watch out for PAGER = "mypager /usr/ucb/more"
12331779Sbostic 			 */
12431779Sbostic 			for (C = pager; *C && !isspace(*C); ++C);
12531779Sbostic 			for (; C > pager && *C != '/'; --C);
12631779Sbostic 			if (C != pager)
12731779Sbostic 				++C;
12831779Sbostic 			/* make sure it's "more", not "morex" */
12931779Sbostic 			if (!strncmp(C, "more", 4) && (!C[4] || isspace(C[4]))) {
13031779Sbostic 				C += 4;
13131779Sbostic 				/*
13231779Sbostic 				 * sizeof is 1 more than # of chars, so,
13331779Sbostic 				 * allocate for the rest of the PAGER
13431779Sbostic 				 * environment variable, a space, and the EOS.
13531779Sbostic 				 */
13631779Sbostic 				if (!(pager = malloc((u_int)(strlen(C) + sizeof(DEF_PAGER) + 1)))) {
13731779Sbostic 					fputs("man: out of space.\n", stderr);
13831779Sbostic 					exit(1);
13931779Sbostic 				}
14031779Sbostic 				(void)sprintf(pager, "%s %s", DEF_PAGER, C);
14131779Sbostic 			}
14231779Sbostic 		}
14331779Sbostic 		else
14431703Sbostic 			pager = DEF_PAGER;
14531703Sbostic 	if (!(machine = getenv("MACHINE")))
14631703Sbostic 		machine = MACHINE;
14731713Sbostic 	if (!defpath && !(defpath = getenv("MANPATH")))
14831713Sbostic 		defpath = DEF_PATH;
14931713Sbostic 	locpath = LOCAL_PATH;
15031713Sbostic 	for (; *defpath && *defpath == ':'; ++defpath);
15131703Sbostic 
15231778Sbostic 	/* Gentlemen... start your kludges! */
15331703Sbostic 	for (; *argv; ++argv) {
15431703Sbostic 		section = NO_SECTION;
155*31906Sbostic 		manpath = defpath;
15631778Sbostic 		dirlist = stanlist;
15731703Sbostic 		switch(**argv) {
15831778Sbostic 		/*
15931778Sbostic 		 * Section 1 requests are really for section 1, 6, 8, new and
16031778Sbostic 		 * old.  Since new and old aren't broken up into a directory
16131778Sbostic 		 * of cat[1-8], we just pretend that they are a subdirectory
16231778Sbostic 		 * of /usr/man.  Should be fixed -- make new and old full
16331778Sbostic 		 * structures just like local is, get rid of "sec1list" and
16431778Sbostic 		 * dirlist.
16531778Sbostic 		 */
16631778Sbostic 		case '1':
16731778Sbostic 			if (!(*argv)[1]) {
16831778Sbostic 				dirlist = sec1list;
16931778Sbostic 				goto numtest;
17031778Sbostic 			}
17131778Sbostic 			break;
17231778Sbostic 
17331778Sbostic 		case '2': case '4': case '5': case '6':
17431713Sbostic 		case '7': case '8':
17531703Sbostic 			if (!(*argv)[1]) {
17631713Sbostic 				section = secno((*argv)[0]);
17731713Sbostic 				goto numtest;
17831713Sbostic 			}
17931713Sbostic 			break;
18031778Sbostic 
18131778Sbostic 		/* sect. 3 requests are for either section 3, or section 3F. */
18231713Sbostic 		case '3':
18331713Sbostic 			if (!(*argv)[1]) {			/* "3" */
18431713Sbostic 				section = secno((*argv)[0]);
18531713Sbostic numtest:			if (!*++argv) {
18631778Sbostic 					fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[section].msg);
18731713Sbostic 					exit(1);
18831713Sbostic 				}
18931713Sbostic 			}					/* "3[fF]" */
19031713Sbostic 			if (((*argv)[1] == 'f'  || (*argv)[1] == 'F') && !(*argv)[2]) {
19131713Sbostic 				section = S_THREEF;
19231703Sbostic 				if (!*++argv) {
19331778Sbostic 					fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[S_THREEF].msg);
19431703Sbostic 					exit(1);
19531703Sbostic 				}
19631703Sbostic 			}
19731703Sbostic 			break;
19831778Sbostic 		/*
19931778Sbostic 		 * Requests for the local section can have subsection numbers
20031778Sbostic 		 * appended to them, i.e. "local3" is really local/cat3.
20131778Sbostic 		 */
20231713Sbostic 		case 'l':					/* local */
20331713Sbostic 			if (!(*argv)[1])			/* "l" */
20431713Sbostic 				section = NO_SECTION;		/* "l2" */
20531713Sbostic 			else if (isdigit((*argv)[1]) && !(*argv)[2])
20631713Sbostic 				section = secno((*argv)[1]);
20731713Sbostic 			else {
20831713Sbostic 				int	lex;
20931713Sbostic 				lex = strcmp(LOCAL_NAME, *argv);
21031713Sbostic 				if (!lex)			/* "local" */
21131713Sbostic 					section = NO_SECTION;	/* "local2" */
21231713Sbostic 				else if (lex < 0 && isdigit((*argv)[sizeof(LOCAL_NAME) - 1]) && !(*argv)[sizeof(LOCAL_NAME)])
21331713Sbostic 					section = secno((*argv)[sizeof(LOCAL_NAME) - 1]);
21431713Sbostic 				else
21531713Sbostic 					break;
21631703Sbostic 			}
21731713Sbostic 			if (!*++argv) {
21831713Sbostic 				fputs("man: what do you want from the local section of the manual?\n", stderr);
21931713Sbostic 				exit(1);
22031713Sbostic 			}
22131713Sbostic 			manpath = locpath;
22231703Sbostic 			break;
22331713Sbostic 		case 'n':					/* new */
22431778Sbostic 			if (!(*argv)[1] || !strcmp(*argv, stanlist[S_NEW].msg)) {
22531713Sbostic 				section = S_NEW;
22631713Sbostic 				goto strtest;
22731703Sbostic 			}
22831703Sbostic 			break;
22931713Sbostic 		case 'o':					/* old */
23031778Sbostic 			if (!(*argv)[1] || !strcmp(*argv, stanlist[S_OLD].msg)) {
23131713Sbostic 				section = S_OLD;
23231713Sbostic strtest:			if (!*++argv) {
23331778Sbostic 					fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[section].msg);
23431703Sbostic 					exit(1);
23531703Sbostic 				}
23631703Sbostic 			}
23731703Sbostic 			break;
23831703Sbostic 		}
23931778Sbostic 		/*
24031778Sbostic 		 * This is really silly, but I wanted to put out rational
24131778Sbostic 		 * errors, not just "I couldn't find it."  This if statement
24231778Sbostic 		 * knows an awful lot about what gets assigned to what in
24331778Sbostic 		 * the switch statement we just passed through.  Sorry.
24431778Sbostic 		 */
24531703Sbostic 		if (!manual(section, *argv))
24631713Sbostic 			if (manpath == locpath)
24731778Sbostic 				fprintf(stderr, "No entry for %s in the %s section of the local manual.\n", *argv, stanlist[section].msg);
24831778Sbostic 			else if (dirlist == sec1list)
24931778Sbostic 				fprintf(stderr, "No entry for %s in the 1st section of the manual.\n", *argv);
25031713Sbostic 			else if (section == NO_SECTION)
25131713Sbostic 				fprintf(stderr, "No entry for %s in the manual.\n", *argv);
25231703Sbostic 			else
25331778Sbostic 				fprintf(stderr, "No entry for %s in the %s section of the manual.\n", *argv, stanlist[section].msg);
25431703Sbostic 	}
25531703Sbostic 	exit(0);
25631703Sbostic }
25731703Sbostic 
25831778Sbostic /*
25931778Sbostic  * manual --
26031778Sbostic  *	given a section number and a file name go through the directory
26131778Sbostic  *	list and find a file that matches.
26231778Sbostic  */
26331703Sbostic static
26431703Sbostic manual(section, name)
26531703Sbostic 	int	section;
26631703Sbostic 	char	*name;
26731703Sbostic {
26831713Sbostic 	register DIR	*dir;
26931713Sbostic 	register char	*beg, *end;
27031703Sbostic 	char	*index();
27131703Sbostic 
27231703Sbostic 	for (beg = manpath;; beg = end + 1) {
27331703Sbostic 		if (end = index(beg, ':'))
27431703Sbostic 			*end = '\0';
27531778Sbostic 		if (section == NO_SECTION)
27631778Sbostic 			for (dir = dirlist; (++dir)->name;) {
27731713Sbostic 				if (find(beg, dir->name, name))
27831703Sbostic 					return(YES);
27931778Sbostic 			}
28031778Sbostic 		else if (find(beg, stanlist[section].name, name))
28131703Sbostic 			return(YES);
28231703Sbostic 		if (!end)
28331703Sbostic 			return(NO);
28431703Sbostic 	}
28531703Sbostic 	/*NOTREACHED*/
28631703Sbostic }
28731703Sbostic 
28831778Sbostic /*
28931778Sbostic  * find --
29031778Sbostic  *	given a directory path, a sub-directory and a file name,
29131778Sbostic  *	see if a file exists in ${directory}/${dir}/{file name}
29231778Sbostic  *	or in ${directory}/${dir}/${machine}/${file name}.
29331778Sbostic  */
29431703Sbostic static
29531703Sbostic find(beg, dir, name)
29631703Sbostic 	char	*beg, *dir, *name;
29731703Sbostic {
29831703Sbostic 	char	fname[MAXPATHLEN + 1];
29931703Sbostic 
30031713Sbostic 	(void)sprintf(fname, "%s/%s/%s.0", beg, dir, name);
30131703Sbostic 	if (!access(fname, R_OK)) {
30231703Sbostic 		show(fname);
30331703Sbostic 		return(YES);
30431703Sbostic 	}
30531713Sbostic 	(void)sprintf(fname, "%s/%s/%s/%s.0", beg, dir, machine, name);
30631703Sbostic 	if (!access(fname, R_OK)) {
30731703Sbostic 		show(fname);
30831703Sbostic 		return(YES);
30931703Sbostic 	}
31031703Sbostic 	return(NO);
31131703Sbostic }
31231703Sbostic 
31331778Sbostic /*
31431778Sbostic  * show --
31531778Sbostic  *	display the file
31631778Sbostic  */
31731703Sbostic static
31831703Sbostic show(fname)
31931703Sbostic 	char	*fname;
32031703Sbostic {
32131703Sbostic 	register int	fd, n;
32231703Sbostic 	char	buf[BUFSIZ];
32331703Sbostic 
32431703Sbostic 	if (nomore) {
32531703Sbostic 		if (!(fd = open(fname, O_RDONLY, 0))) {
32631703Sbostic 			perror("man: open");
32731703Sbostic 			exit(1);
32831703Sbostic 		}
32931703Sbostic 		while ((n = read(fd, buf, sizeof(buf))) > 0)
33031703Sbostic 			if (write(1, buf, n) != n) {
33131703Sbostic 				perror("man: write");
33231703Sbostic 				exit(1);
33331703Sbostic 			}
33431703Sbostic 		if (n == -1) {
33531703Sbostic 			perror("man: read");
33631703Sbostic 			exit(1);
33731703Sbostic 		}
33831703Sbostic 		(void)close(fd);
33931703Sbostic 	}
34031703Sbostic 	else {
34131703Sbostic 		/*
34231703Sbostic 		 * use system(2) in case someone's pager is
34331703Sbostic 		 * "command arg1 arg2"
34431703Sbostic 		 */
34531703Sbostic 		(void)sprintf(buf, "%s %s", pager, fname);
34631703Sbostic 		(void)system(buf);
34731703Sbostic 	}
34831703Sbostic }
34931703Sbostic 
35031778Sbostic /*
35131778Sbostic  * usage --
35231778Sbostic  *	print out a usage message and die
35331778Sbostic  */
35431703Sbostic static
35531703Sbostic usage()
35631703Sbostic {
35731703Sbostic 	fputs("usage: man [-] [-M path] [section] title ...\n", stderr);
35831703Sbostic 	exit(1);
35931703Sbostic }
360