xref: /csrg-svn/usr.bin/man/man.c (revision 42741)
1 /*
2  * Copyright (c) 1987 Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 char copyright[] =
10 "@(#) Copyright (c) 1987 Regents of the University of California.\n\
11  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)man.c	5.21 (Berkeley) 06/01/90";
16 #endif /* not lint */
17 
18 #include <sys/param.h>
19 #include <sys/file.h>
20 #include <errno.h>
21 #include <ctype.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include "pathnames.h"
25 
26 extern int errno;
27 
28 int f_all, f_cat, f_where;
29 char *command, *machine, *p_augment, *p_path, *pager, *progname;
30 
31 main(argc, argv)
32 	int argc;
33 	register char **argv;
34 {
35 	extern char *optarg;
36 	extern int optind;
37 	int ch, res;
38 	char *section[2], *check_pager(), *getpath();
39 
40 	progname = "man";
41 	while ((ch = getopt(argc, argv, "-acfkM:m:P:w")) != EOF)
42 		switch((char)ch) {
43 		case 'a':
44 			f_all = 1;
45 			break;
46 		case 'c':
47 		case '-':		/* deprecated */
48 			f_cat = 1;
49 			break;
50 		case 'm':
51 			p_augment = optarg;
52 			break;
53 		case 'M':
54 		case 'P':		/* backward compatibility */
55 			p_path = optarg;
56 			break;
57 		/*
58 		 * "man -f" and "man -k" are backward compatible, undocumented
59 		 * ways of calling whatis(1) and apropos(1).
60 		 */
61 		case 'f':
62 			jump(argv, "-f", "whatis");
63 			/* NOTREACHED */
64 		case 'k':
65 			jump(argv, "-k", "apropos");
66 			/* NOTREACHED */
67 		case 'w':
68 			f_all = f_where = 1;
69 			break;
70 		case '?':
71 		default:
72 			usage();
73 		}
74 	argv += optind;
75 
76 	if (!*argv)
77 		usage();
78 
79 	if (!f_cat)
80 		if (!isatty(1))
81 			f_cat = 1;
82 		else if (pager = getenv("PAGER"))
83 			pager = check_pager(pager);
84 		else
85 			pager = _PATH_PAGER;
86 
87 	if (!(machine = getenv("MACHINE")))
88 		machine = MACHINE;
89 
90 	/* see if checking in a specific section */
91 	if (argc > 1 && getsection(*argv)) {
92 		section[0] = *argv++;
93 		section[1] = (char *)NULL;
94 	} else {
95 		section[0] = "_default";
96 		section[1] = (char *)NULL;
97 	}
98 
99 	if (!p_path && !(p_path = getenv("MANPATH")) &&
100 	    !(p_path = getpath(section)) && !p_augment) {
101 		(void)fprintf(stderr,
102 		    "man: no place to search for those manual pages.\n");
103 		exit(1);
104 	}
105 
106 	for (; *argv; ++argv) {
107 		if (p_augment)
108 			res = manual(p_augment, *argv);
109 		res = manual(p_path, *argv);
110 		if (res || f_where)
111 			continue;
112 		(void)fprintf(stderr,
113 		    "man: no entry for %s in the manual.\n", *argv);
114 		exit(1);
115 	}
116 
117 	/* use system(3) in case someone's pager is "pager arg1 arg2" */
118 	if (command)
119 		(void)system(command);
120 	exit(0);
121 }
122 
123 /*
124  * manual --
125  *	given a path, a directory list and a file name, find a file
126  *	that matches; check ${directory}/${dir}/{file name} and
127  *	${directory}/${dir}/${machine}/${file name}.
128  */
129 manual(path, name)
130 	char *path, *name;
131 {
132 	register int res;
133 	register char *end;
134 	char fname[MAXPATHLEN + 1];
135 
136 	for (res = 0;; path = end + 1) {
137 		if (!*path)				/* foo: */
138 			break;
139 		if (end = index(path, ':')) {
140 			if (end == path + 1)		/* foo::bar */
141 				continue;
142 			*end = '\0';
143 		}
144 		(void)sprintf(fname, "%s/%s.0", path, name);
145 		if (access(fname, R_OK)) {
146 			(void)sprintf(fname, "%s/%s/%s.0", path, machine, name);
147 			if (access(fname, R_OK))
148 				continue;
149 		}
150 
151 		if (f_where)
152 			(void)printf("man: found in %s.\n", fname);
153 		else if (f_cat)
154 			cat(fname);
155 		else
156 			add(fname);
157 		if (!f_all)
158 			return(1);
159 		res = 1;
160 		if (!end)
161 			break;
162 		*end = ':';
163 	}
164 	return(res);
165 }
166 
167 /*
168  * cat --
169  *	cat out the file
170  */
171 cat(fname)
172 	char *fname;
173 {
174 	register int fd, n;
175 	char buf[BUFSIZ];
176 
177 	if (!(fd = open(fname, O_RDONLY, 0))) {
178 		(void)fprintf(stderr, "man: %s: %s\n", fname, strerror(errno));
179 		exit(1);
180 	}
181 	while ((n = read(fd, buf, sizeof(buf))) > 0)
182 		if (write(1, buf, n) != n) {
183 			(void)fprintf(stderr,
184 			    "man: write: %s\n", strerror(errno));
185 			exit(1);
186 		}
187 	if (n == -1) {
188 		(void)fprintf(stderr, "man: read: %s\n", strerror(errno));
189 		exit(1);
190 	}
191 	(void)close(fd);
192 }
193 
194 /*
195  * add --
196  *	add a file name to the list for future paging
197  */
198 add(fname)
199 	char *fname;
200 {
201 	static u_int buflen;
202 	static int len;
203 	static char *cp;
204 	int flen;
205 
206 	if (!command) {
207 		if (!(command = malloc(buflen = 1024)))
208 			enomem();
209 		len = strlen(strcpy(command, pager));
210 		cp = command + len;
211 	}
212 	flen = strlen(fname);
213 	if (len + flen + 2 > buflen) {		/* +2 == space, EOS */
214 		if (!(command = realloc(command, buflen += 1024)))
215 			enomem();
216 		cp = command + len;
217 	}
218 	*cp++ = ' ';
219 	len += flen + 1;			/* +1 = space */
220 	(void)strcpy(cp, fname);
221 	cp += flen;
222 }
223 
224 /*
225  * check_pager --
226  *	check the user supplied page information
227  */
228 char *
229 check_pager(name)
230 	char *name;
231 {
232 	register char *p;
233 	char *save;
234 
235 	/*
236 	 * if the user uses "more", we make it "more -s"; watch out for
237 	 * PAGER = "mypager /usr/ucb/more"
238 	 */
239 	for (p = name; *p && !isspace(*p); ++p);
240 	for (; p > name && *p != '/'; --p);
241 	if (p != name)
242 		++p;
243 
244 	/* make sure it's "more", not "morex" */
245 	if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))){
246 		save = name;
247 		/* allocate space to add the "-s" */
248 		if (!(name =
249 		    malloc((u_int)(strlen(save) + sizeof("-s") + 1))))
250 			enomem();
251 		(void)sprintf(name, "%s %s", save, "-s");
252 	}
253 	return(name);
254 }
255 
256 /*
257  * jump --
258  *	strip out flag argument and jump
259  */
260 jump(argv, flag, name)
261 	char **argv, *name;
262 	register char *flag;
263 {
264 	register char **arg;
265 
266 	argv[0] = name;
267 	for (arg = argv + 1; *arg; ++arg)
268 		if (!strcmp(*arg, flag))
269 			break;
270 	for (; *arg; ++arg)
271 		arg[0] = arg[1];
272 	execvp(name, argv);
273 	(void)fprintf(stderr, "%s: Command not found.\n", name);
274 	exit(1);
275 }
276 
277 /*
278  * usage --
279  *	print usage message and die
280  */
281 usage()
282 {
283 	(void)fprintf(stderr,
284 	    "usage: man [-ac] [-M path] [-m path] [section] title ...\n");
285 	exit(1);
286 }
287