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