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