xref: /original-bsd/usr.bin/man/man.c (revision 9cc8b07d)
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.26 (Berkeley) 07/06/92";
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)snprintf(buf, sizeof(buf), "%s/", tmp);
110 			for (av = arorder; *av; ++av)
111                 		cadd(buf, strlen(buf), *av);
112 			tmp = strtok(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 			(void)fprintf(stderr,
127 			    "man: no entry for %s in the manual.\n", *argv);
128 	}
129 
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 /*
137  * manual --
138  *	given a path, a directory list and a file name, find a file
139  *	that matches; check ${directory}/${dir}/{file name} and
140  *	${directory}/${dir}/${machine}/${file name}.
141  */
142 manual(path, name)
143 	char *path, *name;
144 {
145 	register int res;
146 	register char *end;
147 	char fname[MAXPATHLEN + 1];
148 
149 	for (res = 0;; path = end + 1) {
150 		if (!*path)				/* foo: */
151 			break;
152 		if (end = index(path, ':')) {
153 			if (end == path + 1)		/* foo::bar */
154 				continue;
155 			*end = '\0';
156 		}
157 		(void)sprintf(fname, "%s/%s.0", path, name);
158 		if (access(fname, R_OK)) {
159 			(void)sprintf(fname, "%s/%s/%s.0", path, machine, name);
160 			if (access(fname, R_OK))
161 				continue;
162 		}
163 
164 		if (f_where)
165 			(void)printf("man: found in %s.\n", fname);
166 		else if (f_cat)
167 			cat(fname);
168 		else if (f_how)
169 			how(fname);
170 		else
171 			add(fname);
172 		if (!f_all)
173 			return(1);
174 		res = 1;
175 		if (!end)
176 			break;
177 		*end = ':';
178 	}
179 	return(res);
180 }
181 
182 /*
183  * how --
184  *	display how information
185  */
186 how(fname)
187 	char *fname;
188 {
189 	register FILE *fp;
190 
191 	register int lcnt, print;
192 	register char *p;
193 	char buf[BUFSIZ];
194 
195 	if (!(fp = fopen(fname, "r"))) {
196 		(void)fprintf(stderr, "man: %s: %s\n", fname, strerror(errno));
197 		exit(1);
198 	}
199 #define	S1	"SYNOPSIS"
200 #define	S2	"S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS"
201 #define	D1	"DESCRIPTION"
202 #define	D2	"D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN"
203 	for (lcnt = print = 0; fgets(buf, sizeof(buf), fp);) {
204 		if (!strncmp(buf, S1, sizeof(S1) - 1) ||
205 		    !strncmp(buf, S2, sizeof(S2) - 1)) {
206 			print = 1;
207 			continue;
208 		} else if (!strncmp(buf, D1, sizeof(D1) - 1) ||
209 		    !strncmp(buf, D2, sizeof(D2) - 1))
210 			return;
211 		if (!print)
212 			continue;
213 		if (*buf == '\n')
214 			++lcnt;
215 		else {
216 			for(; lcnt; --lcnt)
217 				(void)putchar('\n');
218 			for (p = buf; isspace(*p); ++p);
219 			(void)fputs(p, stdout);
220 		}
221 	}
222 	(void)fclose(fp);
223 }
224 /*
225  * cat --
226  *	cat out the file
227  */
228 cat(fname)
229 	char *fname;
230 {
231 	register int fd, n;
232 	char buf[BUFSIZ];
233 
234 	if ((fd = open(fname, O_RDONLY, 0)) < 0) {
235 		(void)fprintf(stderr, "man: %s: %s\n", fname, strerror(errno));
236 		exit(1);
237 	}
238 	while ((n = read(fd, buf, sizeof(buf))) > 0)
239 		if (write(1, buf, n) != n) {
240 			(void)fprintf(stderr,
241 			    "man: write: %s\n", strerror(errno));
242 			exit(1);
243 		}
244 	if (n == -1) {
245 		(void)fprintf(stderr, "man: read: %s\n", strerror(errno));
246 		exit(1);
247 	}
248 	(void)close(fd);
249 }
250 
251 /*
252  * add --
253  *	add a file name to the list for future paging
254  */
255 add(fname)
256 	char *fname;
257 {
258 	static u_int buflen;
259 	static int len;
260 	static char *cp;
261 	int flen;
262 
263 	if (!command) {
264 		if (!(command = malloc(buflen = 1024)))
265 			enomem();
266 		len = strlen(strcpy(command, pager));
267 		cp = command + len;
268 	}
269 	flen = strlen(fname);
270 	if (len + flen + 2 > buflen) {		/* +2 == space, EOS */
271 		if (!(command = realloc(command, buflen += 1024)))
272 			enomem();
273 		cp = command + len;
274 	}
275 	*cp++ = ' ';
276 	len += flen + 1;			/* +1 = space */
277 	(void)strcpy(cp, fname);
278 	cp += flen;
279 }
280 
281 /*
282  * check_pager --
283  *	check the user supplied page information
284  */
285 char *
286 check_pager(name)
287 	char *name;
288 {
289 	register char *p;
290 	char *save;
291 
292 	/*
293 	 * if the user uses "more", we make it "more -s"; watch out for
294 	 * PAGER = "mypager /usr/ucb/more"
295 	 */
296 	for (p = name; *p && !isspace(*p); ++p);
297 	for (; p > name && *p != '/'; --p);
298 	if (p != name)
299 		++p;
300 
301 	/* make sure it's "more", not "morex" */
302 	if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))){
303 		save = name;
304 		/* allocate space to add the "-s" */
305 		if (!(name =
306 		    malloc((u_int)(strlen(save) + sizeof("-s") + 1))))
307 			enomem();
308 		(void)sprintf(name, "%s %s", save, "-s");
309 	}
310 	return(name);
311 }
312 
313 /*
314  * jump --
315  *	strip out flag argument and jump
316  */
317 jump(argv, flag, name)
318 	char **argv, *name;
319 	register char *flag;
320 {
321 	register char **arg;
322 
323 	argv[0] = name;
324 	for (arg = argv + 1; *arg; ++arg)
325 		if (!strcmp(*arg, flag))
326 			break;
327 	for (; *arg; ++arg)
328 		arg[0] = arg[1];
329 	execvp(name, argv);
330 	(void)fprintf(stderr, "%s: Command not found.\n", name);
331 	exit(1);
332 }
333 
334 /*
335  * usage --
336  *	print usage message and die
337  */
338 usage()
339 {
340 	(void)fprintf(stderr,
341 	    "usage: man [-ac] [-M path] [-m path] [section] title ...\n");
342 	exit(1);
343 }
344