xref: /csrg-svn/usr.bin/man/man.c (revision 40390)
131703Sbostic /*
231703Sbostic  * Copyright (c) 1987 Regents of the University of California.
333054Sbostic  * All rights reserved.
433054Sbostic  *
533054Sbostic  * Redistribution and use in source and binary forms are permitted
634886Sbostic  * provided that the above copyright notice and this paragraph are
734886Sbostic  * duplicated in all such forms and that any documentation,
834886Sbostic  * advertising materials, and other materials related to such
934886Sbostic  * distribution and use acknowledge that the software was developed
1034886Sbostic  * by the University of California, Berkeley.  The name of the
1134886Sbostic  * University may not be used to endorse or promote products derived
1234886Sbostic  * from this software without specific prior written permission.
1334886Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1434886Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1534886Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1631703Sbostic  */
1731703Sbostic 
1831703Sbostic #ifndef lint
1931703Sbostic char copyright[] =
2031703Sbostic "@(#) Copyright (c) 1987 Regents of the University of California.\n\
2131703Sbostic  All rights reserved.\n";
2233054Sbostic #endif /* not lint */
2331703Sbostic 
2431703Sbostic #ifndef lint
25*40390Sbostic static char sccsid[] = "@(#)man.c	5.19 (Berkeley) 03/11/90";
2633054Sbostic #endif /* not lint */
2731703Sbostic 
2831703Sbostic #include <sys/param.h>
2931703Sbostic #include <sys/file.h>
30*40390Sbostic #include <errno.h>
3131703Sbostic #include <ctype.h>
32*40390Sbostic #include <string.h>
3337892Sbostic #include "pathnames.h"
3431703Sbostic 
35*40390Sbostic extern int errno;
3631703Sbostic 
37*40390Sbostic char *command, *machine, *p_augment, *p_path, *pager, *progname;
38*40390Sbostic int f_all, f_cat, f_where;
3934057Sbostic 
4031703Sbostic main(argc, argv)
4133354Sbostic 	int argc;
4233354Sbostic 	register char **argv;
4331703Sbostic {
4434057Sbostic 	extern char *optarg;
4534057Sbostic 	extern int optind;
4634057Sbostic 	int ch;
47*40390Sbostic 	char *check_pager(), *config(), *getenv();
4831703Sbostic 
49*40390Sbostic 	progname = "man";
50*40390Sbostic 	while ((ch = getopt(argc, argv, "-acfkM:m:P:w")) != EOF)
5134057Sbostic 		switch((char)ch) {
52*40390Sbostic 		case 'a':
53*40390Sbostic 			f_all = 1;
5431703Sbostic 			break;
55*40390Sbostic 		case 'c':
56*40390Sbostic 		case '-':		/* deprecated */
57*40390Sbostic 			f_cat = 1;
58*40390Sbostic 			break;
59*40390Sbostic 		case 'm':
60*40390Sbostic 			p_augment = optarg;
61*40390Sbostic 			break;
6231703Sbostic 		case 'M':
6331703Sbostic 		case 'P':		/* backward compatibility */
64*40390Sbostic 			p_path = optarg;
6531703Sbostic 			break;
66*40390Sbostic 			/*
67*40390Sbostic 			 * "man -f" and "man -k" are backward compatible,
68*40390Sbostic 			 * undocumented ways of calling whatis(1) and
69*40390Sbostic 			 * apropos(1).
70*40390Sbostic 			 */
7131703Sbostic 		case 'f':
7234057Sbostic 			jump(argv, "-f", "whatis");
73*40390Sbostic 			/* NOTREACHED */
7431703Sbostic 		case 'k':
7534057Sbostic 			jump(argv, "-k", "apropos");
76*40390Sbostic 			/* NOTREACHED */
7732565Sbostic 		case 'w':
78*40390Sbostic 			f_all = f_where = 1;
7932565Sbostic 			break;
8031703Sbostic 		case '?':
8131703Sbostic 		default:
8234057Sbostic 			usage();
8331703Sbostic 		}
8434057Sbostic 	argv += optind;
8531703Sbostic 
8634057Sbostic 	if (!*argv)
8734057Sbostic 		usage();
8833354Sbostic 
89*40390Sbostic 	if (!f_cat)
9031703Sbostic 		if (!isatty(1))
91*40390Sbostic 			f_cat = 1;
92*40390Sbostic 		else if (pager = getenv("PAGER"))
93*40390Sbostic 			pager = check_pager(pager);
9431779Sbostic 		else
9537892Sbostic 			pager = _PATH_PAGER;
96*40390Sbostic 
9731703Sbostic 	if (!(machine = getenv("MACHINE")))
9831703Sbostic 		machine = MACHINE;
99*40390Sbostic 
100*40390Sbostic 	if (!p_path && !(p_path = getenv("MANPATH")))
101*40390Sbostic 		p_path = config();
102*40390Sbostic 
10333354Sbostic 	man(argv);
104*40390Sbostic 
10534752Sbostic 	/* use system(3) in case someone's pager is "pager arg1 arg2" */
10633809Sbostic 	if (command)
10733809Sbostic 		(void)system(command);
10833354Sbostic 	exit(0);
10933354Sbostic }
11031703Sbostic 
11133809Sbostic typedef struct {
11233809Sbostic 	char	*name, *msg;
11333809Sbostic } DIR;
11433354Sbostic static DIR	list1[] = {		/* section one list */
11533354Sbostic 	"cat1", "1st",		"cat8", "8th",		"cat6", "6th",
11633354Sbostic 	"cat.old", "old",	NULL, NULL,
11733354Sbostic },		list2[] = {		/* rest of the list */
11833354Sbostic 	"cat2", "2nd",		"cat3", "3rd",		"cat4", "4th",
11933354Sbostic 	"cat5", "5th", 		"cat7", "7th",		"cat3f", "3rd (F)",
12033354Sbostic 	NULL, NULL,
12133354Sbostic },		list3[2];		/* single section */
12233354Sbostic 
123*40390Sbostic /*
124*40390Sbostic  * man --
125*40390Sbostic  *	main loop to find the manual page and print it out.
126*40390Sbostic  */
12733354Sbostic man(argv)
12833354Sbostic 	char **argv;
12933354Sbostic {
13033354Sbostic 	DIR *section, *getsect();
13133354Sbostic 	int res;
13233354Sbostic 
13331703Sbostic 	for (; *argv; ++argv) {
134*40390Sbostic 		section = isdigit(**argv) ? getsect(*argv++) : NULL;
13534752Sbostic 		if (*argv) {
136*40390Sbostic 			if (p_augment)
137*40390Sbostic 				if (section)
138*40390Sbostic 					res = manual(p_augment, section, *argv);
139*40390Sbostic 				else {
140*40390Sbostic 					res = manual(p_augment, list1, *argv);
141*40390Sbostic 					if (!res || f_all)
142*40390Sbostic 						res += manual(p_augment, list2,
143*40390Sbostic 						    *argv);
144*40390Sbostic 				}
145*40390Sbostic 			if (p_path)
146*40390Sbostic 				if (section)
147*40390Sbostic 					res = manual(p_path, section, *argv);
148*40390Sbostic 				else {
149*40390Sbostic 					res = manual(p_path, list1, *argv);
150*40390Sbostic 					if (!res || f_all)
151*40390Sbostic 						res += manual(p_path, list2,
152*40390Sbostic 						    *argv);
153*40390Sbostic 				}
154*40390Sbostic 			if (res || f_where)
15534752Sbostic 				continue;
156*40390Sbostic 			(void)fprintf(stderr,
157*40390Sbostic 			    "man: no entry for %s in the ", *argv);
158*40390Sbostic 		} else
159*40390Sbostic 			(void)fprintf(stderr,
160*40390Sbostic 			    "man: what do you want from the ");
16133809Sbostic 		if (section)
162*40390Sbostic 			(void)fprintf(stderr,
163*40390Sbostic 			    "%s section of the ", section->msg);
16434752Sbostic 		if (*argv)
165*40390Sbostic 			(void)fprintf(stderr, "manual.\n");
16634752Sbostic 		else
167*40390Sbostic 			(void)fprintf(stderr, "manual?\n");
16834752Sbostic 		exit(1);
16931703Sbostic 	}
17031703Sbostic }
17131703Sbostic 
17231778Sbostic /*
17331778Sbostic  * manual --
174*40390Sbostic  *	given a path, a directory list and a file name, find a file
175*40390Sbostic  *	that matches; check ${directory}/${dir}/{file name} and
17633809Sbostic  *	${directory}/${dir}/${machine}/${file name}.
17731778Sbostic  */
178*40390Sbostic manual(path, section, name)
179*40390Sbostic 	char *path, *name;
18033354Sbostic 	DIR *section;
18131703Sbostic {
182*40390Sbostic 	register char *end;
18333354Sbostic 	register DIR *dp;
18433809Sbostic 	register int res;
185*40390Sbostic 	char fname[MAXPATHLEN + 1];
18631703Sbostic 
187*40390Sbostic 	for (res = 0;; path = end + 1) {
188*40390Sbostic 		if (end = index(path, ':'))
18931703Sbostic 			*end = '\0';
19033809Sbostic 		for (dp = section; dp->name; ++dp) {
191*40390Sbostic 			(void)sprintf(fname, "%s/%s/%s.0",
192*40390Sbostic 			    path, dp->name, name);
19333809Sbostic 			if (access(fname, R_OK)) {
194*40390Sbostic 				(void)sprintf(fname, "%s/%s/%s/%s.0", path,
19533809Sbostic 				    dp->name, machine, name);
19633809Sbostic 				if (access(fname, R_OK))
19733809Sbostic 					continue;
19831778Sbostic 			}
199*40390Sbostic 			if (f_where)
200*40390Sbostic 				(void)printf("man: found in %s.\n", fname);
201*40390Sbostic 			else if (f_cat)
20233809Sbostic 				cat(fname);
20333809Sbostic 			else
20433809Sbostic 				add(fname);
205*40390Sbostic 			if (!f_all)
20634057Sbostic 				return(1);
20733809Sbostic 			res = 1;
20833809Sbostic 		}
20931703Sbostic 		if (!end)
21033809Sbostic 			return(res);
21133297Sbostic 		*end = ':';
21231703Sbostic 	}
213*40390Sbostic 	/* NOTREACHED */
21431703Sbostic }
21531703Sbostic 
21631778Sbostic /*
21733809Sbostic  * cat --
21833809Sbostic  *	cat out the file
21931778Sbostic  */
22033809Sbostic cat(fname)
22133809Sbostic 	char *fname;
22231703Sbostic {
22333809Sbostic 	register int fd, n;
22433809Sbostic 	char buf[BUFSIZ];
22531703Sbostic 
22633809Sbostic 	if (!(fd = open(fname, O_RDONLY, 0))) {
227*40390Sbostic 		(void)fprintf(stderr, "man: %s: %s\n", fname, strerror(errno));
22833809Sbostic 		exit(1);
22931703Sbostic 	}
23033809Sbostic 	while ((n = read(fd, buf, sizeof(buf))) > 0)
23133809Sbostic 		if (write(1, buf, n) != n) {
232*40390Sbostic 			(void)fprintf(stderr,
233*40390Sbostic 			    "man: write: %s\n", strerror(errno));
23433809Sbostic 			exit(1);
23533809Sbostic 		}
23633809Sbostic 	if (n == -1) {
237*40390Sbostic 		(void)fprintf(stderr, "man: read: %s\n", strerror(errno));
23833809Sbostic 		exit(1);
23933809Sbostic 	}
24033809Sbostic 	(void)close(fd);
24131703Sbostic }
24231703Sbostic 
24331778Sbostic /*
24433809Sbostic  * add --
24533809Sbostic  *	add a file name to the list for future paging
24631778Sbostic  */
24733809Sbostic add(fname)
24833354Sbostic 	char *fname;
24931703Sbostic {
25033809Sbostic 	static u_int buflen;
25133809Sbostic 	static int len;
25233809Sbostic 	static char *cp;
25333809Sbostic 	int flen;
25433809Sbostic 	char *malloc(), *realloc(), *strcpy();
25531703Sbostic 
25633809Sbostic 	if (!command) {
257*40390Sbostic 		if (!(command = malloc(buflen = 1024)))
258*40390Sbostic 			enomem();
25933809Sbostic 		len = strlen(strcpy(command, pager));
26033809Sbostic 		cp = command + len;
26133809Sbostic 	}
26233809Sbostic 	flen = strlen(fname);
26333809Sbostic 	if (len + flen + 2 > buflen) {		/* +2 == space, EOS */
264*40390Sbostic 		if (!(command = realloc(command, buflen += 1024)))
265*40390Sbostic 			enomem();
26633809Sbostic 		cp = command + len;
26731703Sbostic 	}
26833809Sbostic 	*cp++ = ' ';
26933809Sbostic 	len += flen + 1;			/* +1 = space */
27033809Sbostic 	(void)strcpy(cp, fname);
27133809Sbostic 	cp += flen;
27231703Sbostic }
27331703Sbostic 
27431778Sbostic /*
27533354Sbostic  * getsect --
27633354Sbostic  *	return a point to the section structure for a particular suffix
27731778Sbostic  */
278*40390Sbostic DIR *
27933354Sbostic getsect(s)
28033354Sbostic 	char *s;
28131703Sbostic {
28233354Sbostic 	switch(*s++) {
28333354Sbostic 	case '1':
28433354Sbostic 		if (!*s)
28533354Sbostic 			return(list1);
28633354Sbostic 		break;
28733354Sbostic 	case '2':
28833354Sbostic 		if (!*s) {
28933354Sbostic 			list3[0] = list2[0];
29033354Sbostic 			return(list3);
29133354Sbostic 		}
29233354Sbostic 		break;
29333354Sbostic 	/* sect. 3 requests are for either section 3, or section 3[fF]. */
29433354Sbostic 	case '3':
29533354Sbostic 		if (!*s) {
29633354Sbostic 			list3[0] = list2[1];
29733354Sbostic 			return(list3);
29833354Sbostic 		}
29933354Sbostic 		else if ((*s == 'f'  || *s == 'F') && !*++s) {
30033354Sbostic 			list3[0] = list2[5];
30133354Sbostic 			return(list3);
30233354Sbostic 		}
30333354Sbostic 		break;
30433354Sbostic 	case '4':
30533354Sbostic 		if (!*s) {
30633354Sbostic 			list3[0] = list2[2];
30733354Sbostic 			return(list3);
30833354Sbostic 		}
30933354Sbostic 		break;
31033354Sbostic 	case '5':
31133354Sbostic 		if (!*s) {
31233354Sbostic 			list3[0] = list2[3];
31333354Sbostic 			return(list3);
31433354Sbostic 		}
31533354Sbostic 		break;
31633354Sbostic 	case '6':
31733354Sbostic 		if (!*s) {
31833354Sbostic 			list3[0] = list1[2];
31933354Sbostic 			return(list3);
32033354Sbostic 		}
32133354Sbostic 		break;
32233354Sbostic 	case '7':
32333354Sbostic 		if (!*s) {
32433354Sbostic 			list3[0] = list2[4];
32533354Sbostic 			return(list3);
32633354Sbostic 		}
32733354Sbostic 		break;
32833354Sbostic 	case '8':
32933354Sbostic 		if (!*s) {
33033354Sbostic 			list3[0] = list1[1];
33133354Sbostic 			return(list3);
33233354Sbostic 		}
333*40390Sbostic 		break;
33433354Sbostic 	}
335*40390Sbostic 	(void)fprintf(stderr, "man: unknown manual section.\n");
336*40390Sbostic 	exit(1);
337*40390Sbostic 	/* NOTREACHED */
33831703Sbostic }
33934057Sbostic 
34034057Sbostic /*
341*40390Sbostic  * check_pager --
342*40390Sbostic  *	check the user supplied page information
343*40390Sbostic  */
344*40390Sbostic char *
345*40390Sbostic check_pager(name)
346*40390Sbostic 	char *name;
347*40390Sbostic {
348*40390Sbostic 	register char *p;
349*40390Sbostic 	char *save, *malloc();
350*40390Sbostic 
351*40390Sbostic 	/*
352*40390Sbostic 	 * if the user uses "more", we make it "more -s"; watch out for
353*40390Sbostic 	 * PAGER = "mypager /usr/ucb/more"
354*40390Sbostic 	 */
355*40390Sbostic 	for (p = name; *p && !isspace(*p); ++p);
356*40390Sbostic 	for (; p > name && *p != '/'; --p);
357*40390Sbostic 	if (p != name)
358*40390Sbostic 		++p;
359*40390Sbostic 
360*40390Sbostic 	/* make sure it's "more", not "morex" */
361*40390Sbostic 	if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))){
362*40390Sbostic 		save = name;
363*40390Sbostic 		/* allocate space to add the "-s" */
364*40390Sbostic 		if (!(name =
365*40390Sbostic 		    malloc((u_int)(strlen(save) + sizeof("-s") + 1))))
366*40390Sbostic 			enomem();
367*40390Sbostic 		(void)sprintf(name, "%s %s", save, "-s");
368*40390Sbostic 	}
369*40390Sbostic 	return(name);
370*40390Sbostic }
371*40390Sbostic 
372*40390Sbostic /*
37334057Sbostic  * jump --
37434057Sbostic  *	strip out flag argument and jump
37534057Sbostic  */
37634057Sbostic jump(argv, flag, name)
37734057Sbostic 	char **argv, *name;
37834057Sbostic 	register char *flag;
37934057Sbostic {
38034057Sbostic 	register char **arg;
38134057Sbostic 
38234057Sbostic 	argv[0] = name;
38334057Sbostic 	for (arg = argv + 1; *arg; ++arg)
38434057Sbostic 		if (!strcmp(*arg, flag))
38534057Sbostic 			break;
38634057Sbostic 	for (; *arg; ++arg)
38734057Sbostic 		arg[0] = arg[1];
38834057Sbostic 	execvp(name, argv);
38934057Sbostic 	fprintf(stderr, "%s: Command not found.\n", name);
39034057Sbostic 	exit(1);
39134057Sbostic }
39234057Sbostic 
39334057Sbostic /*
39434057Sbostic  * usage --
395*40390Sbostic  *	print usage message and die
39634057Sbostic  */
39734057Sbostic usage()
39834057Sbostic {
399*40390Sbostic 	(void)fprintf(stderr,
400*40390Sbostic 	    "usage: man [-ac] [-M path] [-m path] [section] title ...\n");
40134057Sbostic 	exit(1);
40234057Sbostic }
403