xref: /original-bsd/usr.bin/man/man.c (revision f95533f0)
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.24 (Berkeley) 07/21/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_how, f_where;
29 char *command, *machine, *p_augment, *p_path, *pager, *progname;
30 extern char **arorder, *pathbuf;
31 
32 main(argc, argv)
33 	int argc;
34 	register char **argv;
35 {
36 	extern char *optarg;
37 	extern int optind;
38 	int ch, res;
39 	char *section[2], *check_pager(), *getpath(), **getorder(), *tmp;
40 
41 	progname = "man";
42 	while ((ch = getopt(argc, argv, "-acfhkM:m:P:w")) != EOF)
43 		switch((char)ch) {
44 		case 'a':
45 			f_all = 1;
46 			break;
47 		case 'c':
48 		case '-':		/* deprecated */
49 			f_cat = 1;
50 			break;
51 		case 'h':
52 			f_how = 1;
53 			break;
54 		case 'm':
55 			p_augment = optarg;
56 			break;
57 		case 'M':
58 		case 'P':		/* backward compatibility */
59 			p_path = optarg;
60 			break;
61 		/*
62 		 * "man -f" and "man -k" are backward compatible, undocumented
63 		 * ways of calling whatis(1) and apropos(1).
64 		 */
65 		case 'f':
66 			jump(argv, "-f", "whatis");
67 			/* NOTREACHED */
68 		case 'k':
69 			jump(argv, "-k", "apropos");
70 			/* NOTREACHED */
71 		case 'w':
72 			f_all = f_where = 1;
73 			break;
74 		case '?':
75 		default:
76 			usage();
77 		}
78 	argv += optind;
79 
80 	if (!*argv)
81 		usage();
82 
83 	if (!f_cat && !f_how)
84 		if (!isatty(1))
85 			f_cat = 1;
86 		else if (pager = getenv("PAGER"))
87 			pager = check_pager(pager);
88 		else
89 			pager = _PATH_PAGER;
90 
91 	if (!(machine = getenv("MACHINE")))
92 		machine = MACHINE;
93 
94 	/* see if checking in a specific section */
95 	if (argc > 1 && getsection(*argv)) {
96 		section[0] = *argv++;
97 		section[1] = (char *)NULL;
98 	} else {
99 		section[0] = "_default";
100 		section[1] = (char *)NULL;
101 	}
102 
103 	arorder = getorder();
104 	if (p_path || (p_path = getenv("MANPATH"))) {
105 		char buf[MAXPATHLEN], **av;
106 
107 		tmp = strtok(p_path, ":");
108 		while (tmp) {
109 			(void)sprintf(buf, "%s/", tmp);
110 			for (av = arorder; *av; ++av)
111                 		cadd(buf, strlen(buf), *av);
112 			tmp = strtok((char *)NULL, ":");
113 		}
114 		p_path = pathbuf;
115 	} else if (!(p_path = getpath(section)) && !p_augment) {
116 		(void)fprintf(stderr,
117 			"man: no place to search for those manual pages.\n");
118 		exit(1);
119 	}
120 
121 	for (; *argv; ++argv) {
122 		if (p_augment)
123 			res = manual(p_augment, *argv);
124 		res = manual(p_path, *argv);
125 		if (res || f_where)
126 			continue;
127 		(void)fprintf(stderr,
128 		    "man: no entry for %s in the manual.\n", *argv);
129 		exit(1);
130 	}
131 
132 	/* use system(3) in case someone's pager is "pager arg1 arg2" */
133 	if (command)
134 		(void)system(command);
135 	exit(0);
136 }
137 
138 /*
139  * manual --
140  *	given a path, a directory list and a file name, find a file
141  *	that matches; check ${directory}/${dir}/{file name} and
142  *	${directory}/${dir}/${machine}/${file name}.
143  */
144 manual(path, name)
145 	char *path, *name;
146 {
147 	register int res;
148 	register char *end;
149 	char fname[MAXPATHLEN + 1];
150 
151 	for (res = 0;; path = end + 1) {
152 		if (!*path)				/* foo: */
153 			break;
154 		if (end = index(path, ':')) {
155 			if (end == path + 1)		/* foo::bar */
156 				continue;
157 			*end = '\0';
158 		}
159 		(void)sprintf(fname, "%s/%s.0", path, name);
160 		if (access(fname, R_OK)) {
161 			(void)sprintf(fname, "%s/%s/%s.0", path, machine, name);
162 			if (access(fname, R_OK))
163 				continue;
164 		}
165 
166 		if (f_where)
167 			(void)printf("man: found in %s.\n", fname);
168 		else if (f_cat)
169 			cat(fname);
170 		else if (f_how)
171 			how(fname);
172 		else
173 			add(fname);
174 		if (!f_all)
175 			return(1);
176 		res = 1;
177 		if (!end)
178 			break;
179 		*end = ':';
180 	}
181 	return(res);
182 }
183 
184 /*
185  * how --
186  *	display how information
187  */
188 how(fname)
189 	char *fname;
190 {
191 	register FILE *fp;
192 
193 	register int lcnt, print;
194 	register char *p;
195 	char buf[BUFSIZ];
196 
197 	if (!(fp = fopen(fname, "r"))) {
198 		(void)fprintf(stderr, "man: %s: %s\n", fname, strerror(errno));
199 		exit(1);
200 	}
201 #define	S1	"SYNOPSIS"
202 #define	S2	"S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS"
203 #define	D1	"DESCRIPTION"
204 #define	D2	"D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN"
205 	for (lcnt = print = 0; fgets(buf, sizeof(buf), fp);) {
206 		if (!strncmp(buf, S1, sizeof(S1) - 1) ||
207 		    !strncmp(buf, S2, sizeof(S2) - 1)) {
208 			print = 1;
209 			continue;
210 		} else if (!strncmp(buf, D1, sizeof(D1) - 1) ||
211 		    !strncmp(buf, D2, sizeof(D2) - 1))
212 			return;
213 		if (!print)
214 			continue;
215 		if (*buf == '\n')
216 			++lcnt;
217 		else {
218 			for(; lcnt; --lcnt)
219 				(void)putchar('\n');
220 			for (p = buf; isspace(*p); ++p);
221 			(void)fputs(p, stdout);
222 		}
223 	}
224 	(void)fclose(fp);
225 }
226 /*
227  * cat --
228  *	cat out the file
229  */
230 cat(fname)
231 	char *fname;
232 {
233 	register int fd, n;
234 	char buf[BUFSIZ];
235 
236 	if ((fd = open(fname, O_RDONLY, 0)) < 0) {
237 		(void)fprintf(stderr, "man: %s: %s\n", fname, strerror(errno));
238 		exit(1);
239 	}
240 	while ((n = read(fd, buf, sizeof(buf))) > 0)
241 		if (write(1, buf, n) != n) {
242 			(void)fprintf(stderr,
243 			    "man: write: %s\n", strerror(errno));
244 			exit(1);
245 		}
246 	if (n == -1) {
247 		(void)fprintf(stderr, "man: read: %s\n", strerror(errno));
248 		exit(1);
249 	}
250 	(void)close(fd);
251 }
252 
253 /*
254  * add --
255  *	add a file name to the list for future paging
256  */
257 add(fname)
258 	char *fname;
259 {
260 	static u_int buflen;
261 	static int len;
262 	static char *cp;
263 	int flen;
264 
265 	if (!command) {
266 		if (!(command = malloc(buflen = 1024)))
267 			enomem();
268 		len = strlen(strcpy(command, pager));
269 		cp = command + len;
270 	}
271 	flen = strlen(fname);
272 	if (len + flen + 2 > buflen) {		/* +2 == space, EOS */
273 		if (!(command = realloc(command, buflen += 1024)))
274 			enomem();
275 		cp = command + len;
276 	}
277 	*cp++ = ' ';
278 	len += flen + 1;			/* +1 = space */
279 	(void)strcpy(cp, fname);
280 	cp += flen;
281 }
282 
283 /*
284  * check_pager --
285  *	check the user supplied page information
286  */
287 char *
288 check_pager(name)
289 	char *name;
290 {
291 	register char *p;
292 	char *save;
293 
294 	/*
295 	 * if the user uses "more", we make it "more -s"; watch out for
296 	 * PAGER = "mypager /usr/ucb/more"
297 	 */
298 	for (p = name; *p && !isspace(*p); ++p);
299 	for (; p > name && *p != '/'; --p);
300 	if (p != name)
301 		++p;
302 
303 	/* make sure it's "more", not "morex" */
304 	if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))){
305 		save = name;
306 		/* allocate space to add the "-s" */
307 		if (!(name =
308 		    malloc((u_int)(strlen(save) + sizeof("-s") + 1))))
309 			enomem();
310 		(void)sprintf(name, "%s %s", save, "-s");
311 	}
312 	return(name);
313 }
314 
315 /*
316  * jump --
317  *	strip out flag argument and jump
318  */
319 jump(argv, flag, name)
320 	char **argv, *name;
321 	register char *flag;
322 {
323 	register char **arg;
324 
325 	argv[0] = name;
326 	for (arg = argv + 1; *arg; ++arg)
327 		if (!strcmp(*arg, flag))
328 			break;
329 	for (; *arg; ++arg)
330 		arg[0] = arg[1];
331 	execvp(name, argv);
332 	(void)fprintf(stderr, "%s: Command not found.\n", name);
333 	exit(1);
334 }
335 
336 /*
337  * usage --
338  *	print usage message and die
339  */
340 usage()
341 {
342 	(void)fprintf(stderr,
343 	    "usage: man [-ac] [-M path] [-m path] [section] title ...\n");
344 	exit(1);
345 }
346