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