xref: /csrg-svn/usr.bin/man/man.c (revision 37892)
1 /*
2  * Copyright (c) 1987 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 char copyright[] =
20 "@(#) Copyright (c) 1987 Regents of the University of California.\n\
21  All rights reserved.\n";
22 #endif /* not lint */
23 
24 #ifndef lint
25 static char sccsid[] = "@(#)man.c	5.18 (Berkeley) 05/11/89";
26 #endif /* not lint */
27 
28 #include <sys/param.h>
29 #include <sys/file.h>
30 #include <ctype.h>
31 #include "pathnames.h"
32 
33 static char	*command,		/* command buffer */
34 		*defpath,		/* default search path */
35 		*locpath,		/* local search path */
36 		*machine,		/* machine type */
37 		*manpath,		/* current search path */
38 		*newpath,		/* new search path */
39 		*pager,			/* requested pager */
40 		how;			/* how to display */
41 
42 #define	ALL	0x1			/* show all man pages */
43 #define	CAT	0x2			/* copy file to stdout */
44 #define	WHERE	0x4			/* just tell me where */
45 
46 main(argc, argv)
47 	int argc;
48 	register char **argv;
49 {
50 	extern char *optarg;
51 	extern int optind;
52 	int ch;
53 	char *getenv(), *malloc();
54 
55 	while ((ch = getopt(argc, argv, "-M:P:afkw")) != EOF)
56 		switch((char)ch) {
57 		case '-':
58 			how |= CAT;
59 			break;
60 		case 'M':
61 		case 'P':		/* backward compatibility */
62 			defpath = optarg;
63 			break;
64 		case 'a':
65 			how |= ALL;
66 			break;
67 		/*
68 		 * "man -f" and "man -k" are backward contemptible,
69 		 * undocumented ways of calling whatis(1) and apropos(1).
70 		 */
71 		case 'f':
72 			jump(argv, "-f", "whatis");
73 			/*NOTREACHED*/
74 		case 'k':
75 			jump(argv, "-k", "apropos");
76 			/*NOTREACHED*/
77 		/*
78 		 * Deliberately undocumented; really only useful when
79 		 * you're moving man pages around.  Not worth adding.
80 		 */
81 		case 'w':
82 			how |= WHERE | ALL;
83 			break;
84 		case '?':
85 		default:
86 			usage();
87 		}
88 	argv += optind;
89 
90 	if (!*argv)
91 		usage();
92 
93 	if (!(how & CAT))
94 		if (!isatty(1))
95 			how |= CAT;
96 		else if (pager = getenv("PAGER")) {
97 			register char *p;
98 
99 			/*
100 			 * if the user uses "more", we make it "more -s"
101 			 * watch out for PAGER = "mypager /usr/ucb/more"
102 			 */
103 			for (p = pager; *p && !isspace(*p); ++p);
104 			for (; p > pager && *p != '/'; --p);
105 			if (p != pager)
106 				++p;
107 			/* make sure it's "more", not "morex" */
108 			if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))){
109 				char *opager = pager;
110 				/*
111 				 * allocate space to add the "-s"
112 				 */
113 				if (!(pager = malloc((u_int)(strlen(opager)
114 				    + sizeof("-s") + 1)))) {
115 					fputs("man: out of space.\n", stderr);
116 					exit(1);
117 				}
118 				(void)sprintf(pager, "%s %s", opager, "-s");
119 			}
120 		}
121 		else
122 			pager = _PATH_PAGER;
123 	if (!(machine = getenv("MACHINE")))
124 		machine = MACHINE;
125 	if (!defpath && !(defpath = getenv("MANPATH")))
126 		defpath = _PATH_DEFAULT;
127 	locpath = _PATH_LOCAL;
128 	newpath = _PATH_NEW;
129 	man(argv);
130 	/* use system(3) in case someone's pager is "pager arg1 arg2" */
131 	if (command)
132 		(void)system(command);
133 	exit(0);
134 }
135 
136 typedef struct {
137 	char	*name, *msg;
138 } DIR;
139 static DIR	list1[] = {		/* section one list */
140 	"cat1", "1st",		"cat8", "8th",		"cat6", "6th",
141 	"cat.old", "old",	NULL, NULL,
142 },		list2[] = {		/* rest of the list */
143 	"cat2", "2nd",		"cat3", "3rd",		"cat4", "4th",
144 	"cat5", "5th", 		"cat7", "7th",		"cat3f", "3rd (F)",
145 	NULL, NULL,
146 },		list3[2];		/* single section */
147 
148 static
149 man(argv)
150 	char **argv;
151 {
152 	register char *p;
153 	DIR *section, *getsect();
154 	int res;
155 
156 	for (; *argv; ++argv) {
157 		manpath = defpath;
158 		section = NULL;
159 		switch(**argv) {
160 		case 'l':				/* local */
161 			/* support the "{l,local,n,new}###"  syntax */
162 			for (p = *argv; isalpha(*p); ++p);
163 			if (!strncmp(*argv, "l", p - *argv) ||
164 			    !strncmp(*argv, "local", p - *argv)) {
165 				++argv;
166 				manpath = locpath;
167 				section = getsect(p);
168 			}
169 			break;
170 		case 'n':				/* new */
171 			for (p = *argv; isalpha(*p); ++p);
172 			if (!strncmp(*argv, "n", p - *argv) ||
173 			    !strncmp(*argv, "new", p - *argv)) {
174 				++argv;
175 				manpath = newpath;
176 				section = getsect(p);
177 			}
178 			break;
179 		/*
180 		 * old isn't really a separate section of the manual,
181 		 * and its entries are all in a single directory.
182 		 */
183 		case 'o':				/* old */
184 			for (p = *argv; isalpha(*p); ++p);
185 			if (!strncmp(*argv, "o", p - *argv) ||
186 			    !strncmp(*argv, "old", p - *argv)) {
187 				++argv;
188 				list3[0] = list1[3];
189 				section = list3;
190 			}
191 			break;
192 		case '1': case '2': case '3': case '4':
193 		case '5': case '6': case '7': case '8':
194 			if (section = getsect(*argv))
195 				++argv;
196 		}
197 
198 		if (*argv) {
199 			if (section)
200 				res = manual(section, *argv);
201 			else {
202 				res = manual(list1, *argv);
203 				if (!res || (how & ALL))
204 					res += manual(list2, *argv);
205 			}
206 			if (res || how&WHERE)
207 				continue;
208 		}
209 
210 		fputs("man: ", stderr);
211 		if (*argv)
212 			fprintf(stderr, "no entry for %s in the ", *argv);
213 		else
214 			fputs("what do you want from the ", stderr);
215 		if (section)
216 			fprintf(stderr, "%s section of the ", section->msg);
217 		if (manpath == locpath)
218 			fputs("local ", stderr);
219 		else if (manpath == newpath)
220 			fputs("new ", stderr);
221 		if (*argv)
222 			fputs("manual.\n", stderr);
223 		else
224 			fputs("manual?\n", stderr);
225 		exit(1);
226 	}
227 }
228 
229 /*
230  * manual --
231  *	given a directory list and a file name find a file that
232  *	matches; check ${directory}/${dir}/{file name} and
233  *	${directory}/${dir}/${machine}/${file name}.
234  */
235 static
236 manual(section, name)
237 	DIR *section;
238 	char *name;
239 {
240 	register char *beg, *end;
241 	register DIR *dp;
242 	register int res;
243 	char fname[MAXPATHLEN + 1], *index();
244 
245 	for (beg = manpath, res = 0;; beg = end + 1) {
246 		if (end = index(beg, ':'))
247 			*end = '\0';
248 		for (dp = section; dp->name; ++dp) {
249 			(void)sprintf(fname, "%s/%s/%s.0", beg, dp->name, name);
250 			if (access(fname, R_OK)) {
251 				(void)sprintf(fname, "%s/%s/%s/%s.0", beg,
252 				    dp->name, machine, name);
253 				if (access(fname, R_OK))
254 					continue;
255 			}
256 			if (how & WHERE)
257 				printf("man: found in %s.\n", fname);
258 			else if (how & CAT)
259 				cat(fname);
260 			else
261 				add(fname);
262 			if (!(how & ALL))
263 				return(1);
264 			res = 1;
265 		}
266 		if (!end)
267 			return(res);
268 		*end = ':';
269 	}
270 	/*NOTREACHED*/
271 }
272 
273 /*
274  * cat --
275  *	cat out the file
276  */
277 static
278 cat(fname)
279 	char *fname;
280 {
281 	register int fd, n;
282 	char buf[BUFSIZ];
283 
284 	if (!(fd = open(fname, O_RDONLY, 0))) {
285 		perror("man: open");
286 		exit(1);
287 	}
288 	while ((n = read(fd, buf, sizeof(buf))) > 0)
289 		if (write(1, buf, n) != n) {
290 			perror("man: write");
291 			exit(1);
292 		}
293 	if (n == -1) {
294 		perror("man: read");
295 		exit(1);
296 	}
297 	(void)close(fd);
298 }
299 
300 /*
301  * add --
302  *	add a file name to the list for future paging
303  */
304 static
305 add(fname)
306 	char *fname;
307 {
308 	static u_int buflen;
309 	static int len;
310 	static char *cp;
311 	int flen;
312 	char *malloc(), *realloc(), *strcpy();
313 
314 	if (!command) {
315 		if (!(command = malloc(buflen = 1024))) {
316 			fputs("man: out of space.\n", stderr);
317 			exit(1);
318 		}
319 		len = strlen(strcpy(command, pager));
320 		cp = command + len;
321 	}
322 	flen = strlen(fname);
323 	if (len + flen + 2 > buflen) {		/* +2 == space, EOS */
324 		if (!(command = realloc(command, buflen += 1024))) {
325 			fputs("man: out of space.\n", stderr);
326 			exit(1);
327 		}
328 		cp = command + len;
329 	}
330 	*cp++ = ' ';
331 	len += flen + 1;			/* +1 = space */
332 	(void)strcpy(cp, fname);
333 	cp += flen;
334 }
335 
336 /*
337  * getsect --
338  *	return a point to the section structure for a particular suffix
339  */
340 static DIR *
341 getsect(s)
342 	char *s;
343 {
344 	switch(*s++) {
345 	case '1':
346 		if (!*s)
347 			return(list1);
348 		break;
349 	case '2':
350 		if (!*s) {
351 			list3[0] = list2[0];
352 			return(list3);
353 		}
354 		break;
355 	/* sect. 3 requests are for either section 3, or section 3[fF]. */
356 	case '3':
357 		if (!*s) {
358 			list3[0] = list2[1];
359 			return(list3);
360 		}
361 		else if ((*s == 'f'  || *s == 'F') && !*++s) {
362 			list3[0] = list2[5];
363 			return(list3);
364 		}
365 		break;
366 	case '4':
367 		if (!*s) {
368 			list3[0] = list2[2];
369 			return(list3);
370 		}
371 		break;
372 	case '5':
373 		if (!*s) {
374 			list3[0] = list2[3];
375 			return(list3);
376 		}
377 		break;
378 	case '6':
379 		if (!*s) {
380 			list3[0] = list1[2];
381 			return(list3);
382 		}
383 		break;
384 	case '7':
385 		if (!*s) {
386 			list3[0] = list2[4];
387 			return(list3);
388 		}
389 		break;
390 	case '8':
391 		if (!*s) {
392 			list3[0] = list1[1];
393 			return(list3);
394 		}
395 	}
396 	return((DIR *)NULL);
397 }
398 
399 /*
400  * jump --
401  *	strip out flag argument and jump
402  */
403 static
404 jump(argv, flag, name)
405 	char **argv, *name;
406 	register char *flag;
407 {
408 	register char **arg;
409 
410 	argv[0] = name;
411 	for (arg = argv + 1; *arg; ++arg)
412 		if (!strcmp(*arg, flag))
413 			break;
414 	for (; *arg; ++arg)
415 		arg[0] = arg[1];
416 	execvp(name, argv);
417 	fprintf(stderr, "%s: Command not found.\n", name);
418 	exit(1);
419 }
420 
421 /*
422  * usage --
423  *	print usage and die
424  */
425 static
426 usage()
427 {
428 	fputs("usage: man [-] [-a] [-M path] [section] title ...\n", stderr);
429 	exit(1);
430 }
431