xref: /original-bsd/usr.bin/man/man.c (revision cc54e209)
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 this notice is preserved and that due credit is given
7  * to the University of California at Berkeley. The name of the University
8  * may not be used to endorse or promote products derived from this
9  * software without specific prior written permission. This software
10  * is provided ``as is'' without express or implied warranty.
11  */
12 
13 #ifndef lint
14 char copyright[] =
15 "@(#) Copyright (c) 1987 Regents of the University of California.\n\
16  All rights reserved.\n";
17 #endif /* not lint */
18 
19 #ifndef lint
20 static char sccsid[] = "@(#)man.c	5.12 (Berkeley) 01/14/88";
21 #endif /* not lint */
22 
23 #include <sys/param.h>
24 #include <sys/file.h>
25 #include <ctype.h>
26 
27 #define	DEF_PAGER	"/usr/ucb/more -s"
28 #define	DEF_PATH	"/usr/man:/usr/new/man:/usr/local/man"
29 #define	LOCAL_PATH	"/usr/local/man"
30 #define	NEW_PATH	"/usr/new/man"
31 
32 #define	NO		0
33 #define	YES		1
34 
35 typedef struct {
36 	char	*name, *msg;
37 } DIR;
38 
39 static int	nomore,			/* copy file to stdout */
40 		where;			/* just tell me where */
41 static char	*defpath,		/* default search path */
42 		*locpath,		/* local search path */
43 		*machine,		/* machine type */
44 		*manpath,		/* current search path */
45 		*newpath,		/* new search path */
46 		*pager;			/* requested pager */
47 
48 main(argc, argv)
49 	int argc;
50 	register char **argv;
51 {
52 	char **arg_start, **arg, *getenv(), *malloc(), *strcpy();
53 
54 	arg_start = argv;
55 	for (--argc, ++argv; argc && (*argv)[0] == '-'; --argc, ++argv)
56 		switch((*argv)[1]) {
57 		case 0:			/* just write to stdout */
58 			nomore = YES;
59 			break;
60 		case 'M':
61 		case 'P':		/* backward compatibility */
62 			if ((*argv)[2])
63 				defpath = *argv + 2;
64 			else {
65 				if (argc < 2) {
66 					fprintf(stderr, "%s: missing path\n", *argv);
67 					exit(1);
68 				}
69 				--argc;
70 				defpath = *++argv;
71 			}
72 			break;
73 		/*
74 		 * "man -f" and "man -k" are undocumented ways of calling
75 		 * whatis(1) and apropos(1).  Just strip out the flag
76 		 * argument and jump.
77 		 */
78 		case 'f':
79 			for (arg = argv; arg[0] = arg[1]; ++arg);
80 			*arg_start = "whatis";
81 			execvp(*arg_start, arg_start);
82 			fputs("whatis: Command not found.\n", stderr);
83 			exit(1);
84 		case 'k':
85 			for (arg = argv; *arg = arg[1]; ++arg);
86 			*arg_start = "apropos";
87 			execvp(*arg_start, arg_start);
88 			fputs("apropos: Command not found.\n", stderr);
89 			exit(1);
90 		/*
91 		 * Deliberately undocumented; really only useful when
92 		 * you're moving man pages around.  Not worth adding.
93 		 */
94 		case 'w':
95 			where = YES;
96 			break;
97 		case '?':
98 		default:
99 			fprintf(stderr, "man: illegal option -- %c\n", (*argv)[1]);
100 			goto usage;
101 		}
102 
103 	if (!argc) {
104 usage:		fputs("usage: man [-] [-M path] [section] title ...\n", stderr);
105 		exit(1);
106 	}
107 
108 	if (!nomore)
109 		if (!isatty(1))
110 			nomore = YES;
111 		else if (pager = getenv("PAGER")) {
112 			register char *p;
113 
114 			/*
115 			 * if the user uses "more", we make it "more -s"
116 			 * watch out for PAGER = "mypager /usr/ucb/more"
117 			 */
118 			for (p = pager; *p && !isspace(*p); ++p);
119 			for (; p > pager && *p != '/'; --p);
120 			if (p != pager)
121 				++p;
122 			/* make sure it's "more", not "morex" */
123 			if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))) {
124 				p += 4;
125 				/*
126 				 * allocate for the rest of the PAGER
127 				 * environment variable, a space, and the EOS.
128 				 */
129 				if (!(pager = malloc((u_int)(strlen(p) + sizeof(DEF_PAGER) + 1)))) {
130 					fputs("man: out of space.\n", stderr);
131 					exit(1);
132 				}
133 				(void)sprintf(pager, "%s %s", DEF_PAGER, p);
134 			}
135 		}
136 		else
137 			pager = DEF_PAGER;
138 	if (!(machine = getenv("MACHINE")))
139 		machine = MACHINE;
140 	if (!defpath && !(defpath = getenv("MANPATH")))
141 		defpath = DEF_PATH;
142 	locpath = LOCAL_PATH;
143 	newpath = NEW_PATH;
144 	man(argv);
145 	exit(0);
146 }
147 
148 static DIR	list1[] = {		/* section one list */
149 	"cat1", "1st",		"cat8", "8th",		"cat6", "6th",
150 	"cat.old", "old",	NULL, NULL,
151 },		list2[] = {		/* rest of the list */
152 	"cat2", "2nd",		"cat3", "3rd",		"cat4", "4th",
153 	"cat5", "5th", 		"cat7", "7th",		"cat3f", "3rd (F)",
154 	NULL, NULL,
155 },		list3[2];		/* single section */
156 
157 static
158 man(argv)
159 	char **argv;
160 {
161 	register char *p;
162 	DIR *section, *getsect();
163 	int res;
164 
165 	for (; *argv; ++argv) {
166 		manpath = defpath;
167 		section = NULL;
168 		switch(**argv) {
169 		case 'l':				/* local */
170 			for (p = *argv; isalpha(*p); ++p);
171 			if (!strncmp(*argv, "l", p - *argv) ||
172 			    !strncmp(*argv, "local", p - *argv)) {
173 				manpath = locpath;
174 				if (section = getsect(p))
175 					goto argtest;
176 			}
177 			break;
178 		case 'n':				/* new */
179 			for (p = *argv; isalpha(*p); ++p);
180 			if (!strncmp(*argv, "n", p - *argv) ||
181 			    !strncmp(*argv, "new", p - *argv)) {
182 				manpath = newpath;
183 				if (section = getsect(p))
184 					goto argtest;
185 			}
186 			break;
187 		/*
188 		 * old isn't really a separate section of the manual,
189 		 * and its entries are all in a single directory.
190 		 */
191 		case 'o':				/* old */
192 			for (p = *argv; isalpha(*p); ++p);
193 			if (!strncmp(*argv, "o", p - *argv) ||
194 			    !strncmp(*argv, "old", p - *argv)) {
195 				list3[0] = list1[3];
196 				section = list3;
197 				goto argtest;
198 			}
199 			break;
200 		case '1': case '2': case '3': case '4':
201 		case '5': case '6': case '7': case '8':
202 			if (!(section = getsect(*argv)))
203 				break;
204 argtest:		if (!*++argv) {
205 				fprintf(stderr, "man: what do you want from the %s section of the manual?\n", section->msg);
206 				exit(1);
207 			}
208 		}
209 
210 		res = section ? manual(section, *argv) :
211 		    manual(list1, *argv) || manual(list2, *argv);
212 		if (!res && !where)
213 			if (manpath == locpath)
214 				if (section)
215 					fprintf(stderr, "No entry for %s in the %s section of the local manual.\n", *argv, section->msg);
216 				else
217 					fprintf(stderr, "No entry for %s in the local manual.\n", *argv);
218 			else if (manpath == newpath)
219 				if (section)
220 					fprintf(stderr, "No entry for %s in the %s section of the new manual.\n", *argv, section->msg);
221 				else
222 					fprintf(stderr, "No entry for %s in the new manual.\n", *argv);
223 			else if (section)
224 				fprintf(stderr, "No entry for %s in the %s section of the manual.\n", *argv, section->msg);
225 			else
226 				fprintf(stderr, "No entry for %s in the manual.\n", *argv);
227 	}
228 }
229 
230 /*
231  * manual --
232  *	given a section number and a file name go through the directory
233  *	list and find a file that matches.
234  */
235 static
236 manual(section, name)
237 	DIR *section;
238 	char *name;
239 {
240 	register char *beg, *end;
241 	register DIR *dp;
242 	char *index();
243 
244 	for (beg = manpath;; beg = end + 1) {
245 		if (end = index(beg, ':'))
246 			*end = '\0';
247 		for (dp = section; dp->name; ++dp)
248 			if (find(beg, dp->name, name)) {
249 				if (end)
250 					*end = ':';
251 				return(YES);
252 			}
253 		if (!end)
254 			return(NO);
255 		*end = ':';
256 	}
257 	/*NOTREACHED*/
258 }
259 
260 /*
261  * find --
262  *	given a directory path, a sub-directory and a file name,
263  *	see if a file exists in ${directory}/${dir}/{file name}
264  *	or in ${directory}/${dir}/${machine}/${file name}.
265  */
266 static
267 find(beg, dir, name)
268 	char *beg, *dir, *name;
269 {
270 	char fname[MAXPATHLEN + 1];
271 
272 	(void)sprintf(fname, "%s/%s/%s.0", beg, dir, name);
273 	if (access(fname, R_OK)) {
274 		(void)sprintf(fname, "%s/%s/%s/%s.0", beg, dir, machine, name);
275 		if (access(fname, R_OK))
276 			return(NO);
277 	}
278 	if (where)
279 		printf("man: found in %s.\n", fname);
280 	else
281 		show(fname);
282 	return(!where);
283 }
284 
285 /*
286  * show --
287  *	display the file
288  */
289 static
290 show(fname)
291 	char *fname;
292 {
293 	register int fd, n;
294 	char buf[BUFSIZ];
295 
296 	if (nomore) {
297 		if (!(fd = open(fname, O_RDONLY, 0))) {
298 			perror("man: open");
299 			exit(1);
300 		}
301 		while ((n = read(fd, buf, sizeof(buf))) > 0)
302 			if (write(1, buf, n) != n) {
303 				perror("man: write");
304 				exit(1);
305 			}
306 		if (n == -1) {
307 			perror("man: read");
308 			exit(1);
309 		}
310 		(void)close(fd);
311 	}
312 	else {
313 		/*
314 		 * use system(3) in case someone's pager is
315 		 * "command arg1 arg2"
316 		 */
317 		(void)sprintf(buf, "%s %s", pager, fname);
318 		(void)system(buf);
319 	}
320 }
321 
322 /*
323  * getsect --
324  *	return a point to the section structure for a particular suffix
325  */
326 static DIR *
327 getsect(s)
328 	char *s;
329 {
330 	switch(*s++) {
331 	case '1':
332 		if (!*s)
333 			return(list1);
334 		break;
335 	case '2':
336 		if (!*s) {
337 			list3[0] = list2[0];
338 			return(list3);
339 		}
340 		break;
341 	/* sect. 3 requests are for either section 3, or section 3[fF]. */
342 	case '3':
343 		if (!*s) {
344 			list3[0] = list2[1];
345 			return(list3);
346 		}
347 		else if ((*s == 'f'  || *s == 'F') && !*++s) {
348 			list3[0] = list2[5];
349 			return(list3);
350 		}
351 		break;
352 	case '4':
353 		if (!*s) {
354 			list3[0] = list2[2];
355 			return(list3);
356 		}
357 		break;
358 	case '5':
359 		if (!*s) {
360 			list3[0] = list2[3];
361 			return(list3);
362 		}
363 		break;
364 	case '6':
365 		if (!*s) {
366 			list3[0] = list1[2];
367 			return(list3);
368 		}
369 		break;
370 	case '7':
371 		if (!*s) {
372 			list3[0] = list2[4];
373 			return(list3);
374 		}
375 		break;
376 	case '8':
377 		if (!*s) {
378 			list3[0] = list1[1];
379 			return(list3);
380 		}
381 	}
382 	return((DIR *)NULL);
383 }
384