Commit | Line | Data |
---|---|---|
371bcb36 RM |
1 | /* |
2 | * Copyright (c) 1980 Regents of the University of California. | |
3 | * All rights reserved. The Berkeley software License Agreement | |
4 | * specifies the terms and conditions for redistribution. | |
5 | */ | |
6 | ||
7 | #ifndef lint | |
8 | char copyright[] = | |
9 | "@(#) Copyright (c) 1980 Regents of the University of California.\n\ | |
10 | All rights reserved.\n"; | |
11 | #endif not lint | |
12 | ||
13 | #ifndef lint | |
14 | static char sccsid[] = "@(#)man.c 5.12 (Berkeley) 3/20/86"; | |
15 | #endif not lint | |
16 | ||
17 | #include <stdio.h> | |
18 | #include <ctype.h> | |
19 | #include <sgtty.h> | |
20 | #include <sys/param.h> | |
21 | #include <sys/stat.h> | |
22 | #include <signal.h> | |
23 | #include <strings.h> | |
24 | ||
25 | /* | |
26 | * man | |
27 | * link also to apropos and whatis | |
28 | * This version uses more for underlining and paging. | |
29 | */ | |
30 | #define NROFFCAT "nroff -mandoc" /* for nroffing to cat file */ | |
31 | #define NROFF "nroff -mandoc" /* for nroffing to tty */ | |
32 | #define MORE "more -s" /* paging filter */ | |
33 | #define CAT_ "/bin/cat" /* for when output is not a tty */ | |
34 | #define CAT_S "/bin/cat -s" /* for '-' opt (no more) */ | |
35 | ||
36 | #define TROFFCMD "troff -man %s" | |
37 | ||
38 | #define ALLSECT "1nl6823457po" /* order to look through sections */ | |
39 | #define SECT1 "1nlo" /* sections to look at if 1 is specified */ | |
40 | #define SUBSEC1 "cg" /* subsections to try in section 1 */ | |
41 | #define SUBSEC3 "sxmncf" | |
42 | #define SUBSEC4 "pfn" | |
43 | #define SUBSEC8 "cv" | |
44 | ||
ec4cd07e | 45 | #define WHATIS "whatis.db" |
371bcb36 RM |
46 | |
47 | int nomore; | |
48 | char *CAT = CAT_; | |
49 | char *manpath = "/usr/share/man:/usr/local/man:/usr/gnu/man:/usr/X386/man"; | |
50 | char *strcpy(); | |
51 | char *strcat(); | |
52 | char *getenv(); | |
53 | char *calloc(); | |
54 | char *trim(); | |
55 | void nuke(int); | |
56 | int apropos(); | |
57 | int whatis(); | |
58 | int section; | |
59 | int subsec; | |
60 | int troffit; | |
61 | int mypid; | |
62 | ||
63 | #define eq(a,b) (strcmp(a,b) == 0) | |
64 | ||
65 | main(argc, argv) | |
66 | int argc; | |
67 | char *argv[]; | |
68 | { | |
69 | char *mp; | |
70 | ||
71 | if ((mp = getenv("MANPATH")) != NULL) | |
72 | manpath = mp; | |
73 | umask(0); | |
74 | mypid = getpid(); | |
75 | if (strcmp(argv[0], "apropos") == 0) { | |
76 | runpath(argc-1, argv+1, apropos); | |
77 | exit(0); | |
78 | } | |
79 | if (strcmp(argv[0], "whatis") == 0) { | |
80 | runpath(argc-1, argv+1, whatis); | |
81 | exit(0); | |
82 | } | |
83 | if (argc <= 1) { | |
84 | fprintf(stderr, "Usage: man [ section ] name ...\n"); | |
85 | exit(1); | |
86 | } | |
87 | argc--, argv++; | |
88 | while (argc > 0 && argv[0][0] == '-') { | |
89 | switch(argv[0][1]) { | |
90 | ||
91 | case 0: | |
92 | nomore++; | |
93 | CAT = CAT_S; | |
94 | break; | |
95 | ||
96 | case 't': | |
97 | troffit++; | |
98 | break; | |
99 | ||
100 | case 'k': | |
101 | apropos(argc-1, argv+1); | |
102 | exit(0); | |
103 | ||
104 | case 'f': | |
105 | whatis(argc-1, argv+1); | |
106 | exit(0); | |
107 | ||
108 | case 'P': /* backwards compatibility */ | |
109 | case 'M': | |
110 | if (argc < 2) { | |
111 | fprintf(stderr, "%s: missing path\n", *argv); | |
112 | exit(1); | |
113 | } | |
114 | argc--, argv++; | |
115 | manpath = *argv; | |
116 | break; | |
117 | } | |
118 | argc--, argv++; | |
119 | } | |
120 | if (troffit == 0 && nomore == 0 && !isatty(1)) | |
121 | nomore++; | |
122 | section = 0; | |
123 | do { | |
124 | if (eq(argv[0], "local")) { | |
125 | section = 'l'; | |
126 | goto sectin; | |
127 | } else if (eq(argv[0], "new")) { | |
128 | section = 'n'; | |
129 | goto sectin; | |
130 | } else if (eq(argv[0], "old")) { | |
131 | section = 'o'; | |
132 | goto sectin; | |
133 | } else if (eq(argv[0], "public")) { | |
134 | section = 'p'; | |
135 | goto sectin; | |
136 | } else if (isdigit(argv[0][0]) && | |
137 | (argv[0][1] == 0 || argv[0][2] == 0)) { | |
138 | section = argv[0][0]; | |
139 | subsec = argv[0][1]; | |
140 | sectin: | |
141 | argc--, argv++; | |
142 | if (argc == 0) { | |
143 | fprintf(stderr, | |
144 | "But what do you want from section %s?\n", | |
145 | argv[-1]); | |
146 | exit(1); | |
147 | } | |
148 | continue; | |
149 | } | |
150 | manual(section, argv[0]); | |
151 | argc--, argv++; | |
152 | } while (argc > 0); | |
153 | exit(0); | |
154 | } | |
155 | ||
156 | runpath(ac, av, f) | |
157 | int ac; | |
158 | char *av[]; | |
159 | int (*f)(); | |
160 | { | |
161 | ||
162 | if (ac > 0 && strcmp(av[0], "-M") == 0 || strcmp(av[0], "-P") == 0) { | |
163 | if (ac < 2) { | |
164 | fprintf(stderr, "%s: missing path\n", av[0]); | |
165 | exit(1); | |
166 | } | |
167 | manpath = av[1]; | |
168 | ac -= 2, av += 2; | |
169 | } | |
170 | (*f)(ac, av); | |
171 | exit(0); | |
172 | } | |
173 | ||
174 | manual(sec, name) | |
175 | char sec, *name; | |
176 | { | |
177 | char section = sec; | |
178 | char work[100], work2[100]; | |
179 | char path[MAXPATHLEN+1], realname[MAXPATHLEN+1]; | |
180 | char cmdbuf[150]; | |
181 | int ss = 0; | |
182 | struct stat stbuf, stbuf2; | |
183 | int last; | |
184 | char *sp = ALLSECT; | |
185 | FILE *it; | |
186 | char abuf[BUFSIZ]; | |
187 | ||
188 | strcpy(work, "manx/"); | |
189 | strcat(work, name); | |
190 | strcat(work, ".x"); | |
191 | last = strlen(work) - 1; | |
192 | if (section == '1') { | |
193 | sp = SECT1; | |
194 | section = 0; | |
195 | } | |
196 | if (section == 0) { /* no section or section 1 given */ | |
197 | for (section = *sp++; section; section = *sp++) { | |
198 | work[3] = section; | |
199 | work[last] = section; | |
200 | work[last+1] = 0; | |
201 | work[last+2] = 0; | |
202 | if (pathstat(work, path, &stbuf)) | |
203 | break; | |
204 | if (work[last] >= '1' && work[last] <= '8') { | |
205 | char *cp; | |
206 | search: | |
207 | switch (work[last]) { | |
208 | case '1': cp = SUBSEC1; break; | |
209 | case '3': cp = SUBSEC3; break; | |
210 | case '4': cp = SUBSEC4; break; | |
211 | case '8': cp = SUBSEC8; break; | |
212 | default: cp = "0"; break; | |
213 | } | |
214 | while (*cp) { | |
215 | work[last+1] = *cp++; | |
216 | if (pathstat(work, path, &stbuf)) { | |
217 | ss = work[last+1]; | |
218 | goto found; | |
219 | } | |
220 | } | |
221 | if (ss == 0) | |
222 | work[last+1] = 0; | |
223 | } | |
224 | } | |
225 | if (section == 0) { | |
226 | if (sec == 0) | |
227 | printf("No manual entry for %s.\n", name); | |
228 | else | |
229 | printf("No entry for %s in section %c%s.\n", | |
230 | name, sec, " of the manual"); | |
231 | return; | |
232 | } | |
233 | } else { /* section given */ | |
234 | work[3] = section; | |
235 | work[last] = section; | |
236 | work[last+1] = subsec; | |
237 | work[last+2] = 0; | |
238 | if (!pathstat(work, path, &stbuf)) { | |
239 | if ((section >= '1' && section <= '8') && subsec == 0) { | |
240 | sp = "\0"; | |
241 | goto search; | |
242 | } | |
243 | else if (section == 'o') { /* XXX */ | |
244 | char *cp; | |
245 | char sec; | |
246 | for (sec = '0'; sec <= '8'; sec++) { | |
247 | work[last] = sec; | |
248 | if (pathstat(work, path, &stbuf)) | |
249 | goto found; | |
250 | switch (work[last]) { | |
251 | case '1': cp = SUBSEC1; break; | |
252 | case '3': cp = SUBSEC3; break; | |
253 | case '4': cp = SUBSEC4; break; | |
254 | case '8': cp = SUBSEC8; break; | |
255 | default: cp = ""; break; | |
256 | } | |
257 | while (*cp) { | |
258 | work[last+1] = *cp++; | |
259 | if (pathstat(work, path, &stbuf)) { | |
260 | ss = work[last+1]; | |
261 | goto found; | |
262 | } | |
263 | } | |
264 | if (ss == 0) | |
265 | work[last+1] = 0; | |
266 | } | |
267 | } | |
268 | printf("No entry for %s in section %c", name, section); | |
269 | if (subsec) | |
270 | putchar(subsec); | |
271 | printf(" of the manual.\n"); | |
272 | return; | |
273 | } | |
274 | } | |
275 | found: | |
276 | sprintf(realname, "%s/%s", path, work); | |
277 | if (troffit) { | |
278 | troff(path, work); | |
279 | return; | |
280 | } | |
281 | if (!nomore) { | |
282 | if ((it = fopen(realname, "r")) == NULL) { | |
283 | goto catit; | |
284 | } | |
285 | if (fgets(abuf, BUFSIZ-1, it) && | |
286 | strncmp(abuf, ".so ", 4) == 0) { | |
287 | register char *cp = abuf+4; | |
288 | char *dp; | |
289 | ||
290 | while (*cp && *cp != '\n') | |
291 | cp++; | |
292 | *cp = 0; | |
293 | while (cp > abuf && *--cp != '/') | |
294 | ; | |
295 | dp = ".so man"; | |
296 | if (cp != abuf+strlen(dp)+1) { | |
297 | tohard: | |
298 | fclose(it); | |
299 | nomore = 1; | |
300 | strcpy(work, abuf+4); | |
301 | goto hardway; | |
302 | } | |
303 | for (cp = abuf; *cp == *dp && *cp; cp++, dp++) | |
304 | ; | |
305 | if (*dp) | |
306 | goto tohard; | |
307 | strcpy(work, cp-3); | |
308 | } | |
309 | fclose(it); | |
310 | } | |
311 | catit: | |
312 | strcpy(work2, "cat"); | |
313 | work2[3] = work[3]; | |
314 | work2[4] = 0; | |
315 | sprintf(realname, "%s/%s", path, work2); | |
316 | if (stat(realname, &stbuf2) < 0) | |
317 | goto hardway; | |
318 | strcpy(work2+4, work+4); | |
319 | sprintf(realname, "%s/%s", path, work2); | |
320 | if (stat(realname, &stbuf2) < 0 || stbuf2.st_mtime < stbuf.st_mtime) { | |
321 | if (nomore) | |
322 | goto hardway; | |
323 | printf("Reformatting page. Wait..."); | |
324 | fflush(stdout); | |
325 | unlink(work2); | |
326 | if (signal(SIGINT, SIG_IGN) == SIG_DFL) { | |
327 | (void) signal(SIGINT, nuke); | |
328 | (void) signal(SIGQUIT, nuke); | |
329 | (void) signal(SIGTERM, nuke); | |
330 | } | |
331 | sprintf(cmdbuf, "%s %s/%s > /tmp/man%d; trap '' 1 15", | |
332 | NROFFCAT, path, work, mypid); | |
333 | if (system(cmdbuf)) { | |
334 | printf(" aborted (sorry)\n"); | |
335 | nuke(0); | |
336 | /*NOTREACHED*/ | |
337 | } | |
338 | sprintf(cmdbuf, "/bin/mv -f /tmp/man%d %s/%s 2>/dev/null", | |
339 | mypid, path, work2); | |
340 | if (system(cmdbuf)) { | |
341 | sprintf(path, "/"); | |
342 | sprintf(work2, "tmp/man%d", mypid); | |
343 | } | |
344 | printf(" done\n"); | |
345 | } | |
346 | strcpy(work, work2); | |
347 | hardway: | |
348 | nroff(path, work); | |
349 | if (work2[0] == 't') | |
350 | nuke(0); | |
351 | } | |
352 | ||
353 | /* | |
354 | * Use the manpath to look for | |
355 | * the file name. The result of | |
356 | * stat is returned in stbuf, the | |
357 | * successful path in path. | |
358 | */ | |
359 | pathstat(name, path, stbuf) | |
360 | char *name, path[]; | |
361 | struct stat *stbuf; | |
362 | { | |
363 | char *cp, *tp, *ep; | |
364 | char **cpp; | |
365 | static char *manpaths[] = {"man", "cat", 0}; | |
366 | static char *nopaths[] = {"", 0}; | |
367 | ||
368 | if (strncmp(name, "man", 3) == 0) | |
369 | cpp = manpaths; | |
370 | else | |
371 | cpp = nopaths; | |
372 | for ( ; *cpp ; cpp++) { | |
373 | for (cp = manpath; cp && *cp; cp = tp) { | |
374 | tp = index(cp, ':'); | |
375 | if (tp) { | |
376 | if (tp == cp) { | |
377 | sprintf(path, "%s%s", *cpp, | |
378 | name+strlen(*cpp)); | |
379 | } | |
380 | else { | |
381 | sprintf(path, "%.*s/%s%s", tp-cp, cp, | |
382 | *cpp, name+strlen(*cpp)); | |
383 | } | |
384 | ep = path + (tp-cp); | |
385 | tp++; | |
386 | } else { | |
387 | sprintf(path, "%s/%s%s", cp, *cpp, | |
388 | name+strlen(*cpp)); | |
389 | ep = path + strlen(cp); | |
390 | } | |
391 | if (stat(path, stbuf) >= 0) { | |
392 | *ep = '\0'; | |
393 | return (1); | |
394 | } | |
395 | } | |
396 | } | |
397 | return (0); | |
398 | } | |
399 | ||
400 | nroff(pp, wp) | |
401 | char *pp, *wp; | |
402 | { | |
403 | char cmd[BUFSIZ]; | |
404 | ||
405 | chdir(pp); | |
406 | if (wp[0] == 'c' || wp[0] == 't') | |
407 | sprintf(cmd, "%s %s", nomore? CAT : MORE, wp); | |
408 | else | |
409 | sprintf(cmd, nomore? "%s %s" : "%s %s|%s", NROFF, wp, MORE); | |
410 | (void) system(cmd); | |
411 | } | |
412 | ||
413 | troff(pp, wp) | |
414 | char *pp, *wp; | |
415 | { | |
416 | char cmdbuf[BUFSIZ]; | |
417 | ||
418 | chdir(pp); | |
419 | sprintf(cmdbuf, TROFFCMD, wp); | |
420 | (void) system(cmdbuf); | |
421 | } | |
422 | ||
423 | any(c, sp) | |
424 | register int c; | |
425 | register char *sp; | |
426 | { | |
427 | register int d; | |
428 | ||
429 | while (d = *sp++) | |
430 | if (c == d) | |
431 | return (1); | |
432 | return (0); | |
433 | } | |
434 | ||
435 | void | |
436 | nuke(sig) | |
437 | int sig; | |
438 | { | |
439 | char name[15]; | |
440 | ||
441 | sprintf(name, "/tmp/man%d", mypid); | |
442 | unlink(name); | |
443 | exit(1); | |
444 | } | |
445 | ||
446 | unsigned int | |
447 | blklen(ip) | |
448 | register char **ip; | |
449 | { | |
450 | register unsigned int i = 0; | |
451 | ||
452 | while (*ip++) | |
453 | i++; | |
454 | return (i); | |
455 | } | |
456 | ||
457 | apropos(argc, argv) | |
458 | int argc; | |
459 | char **argv; | |
460 | { | |
461 | char buf[BUFSIZ], file[MAXPATHLEN+1]; | |
462 | char *gotit, *cp, *tp; | |
463 | register char **vp; | |
464 | ||
465 | if (argc == 0) { | |
466 | fprintf(stderr, "apropos what?\n"); | |
467 | exit(1); | |
468 | } | |
469 | gotit = calloc(1, blklen(argv)); | |
470 | for (cp = manpath; cp; cp = tp) { | |
471 | tp = index(cp, ':'); | |
472 | if (tp) { | |
473 | if (tp == cp) | |
474 | strcpy(file, WHATIS); | |
475 | else | |
476 | sprintf(file, "%.*s/%s", tp-cp, cp, WHATIS); | |
477 | tp++; | |
478 | } else | |
479 | sprintf(file, "%s/%s", cp, WHATIS); | |
480 | if (freopen(file, "r", stdin) == NULL) | |
481 | continue; | |
482 | while (fgets(buf, sizeof buf, stdin) != NULL) | |
483 | for (vp = argv; *vp; vp++) | |
484 | if (match(buf, *vp)) { | |
485 | printf("%s", buf); | |
486 | gotit[vp - argv] = 1; | |
487 | for (vp++; *vp; vp++) | |
488 | if (match(buf, *vp)) | |
489 | gotit[vp - argv] = 1; | |
490 | break; | |
491 | } | |
492 | } | |
493 | for (vp = argv; *vp; vp++) | |
494 | if (gotit[vp - argv] == 0) | |
495 | printf("%s: nothing appropriate\n", *vp); | |
496 | } | |
497 | ||
498 | match(bp, str) | |
499 | register char *bp; | |
500 | char *str; | |
501 | { | |
502 | ||
503 | for (;;) { | |
504 | if (*bp == 0) | |
505 | return (0); | |
506 | if (amatch(bp, str)) | |
507 | return (1); | |
508 | bp++; | |
509 | } | |
510 | } | |
511 | ||
512 | amatch(cp, dp) | |
513 | register char *cp, *dp; | |
514 | { | |
515 | ||
516 | while (*cp && *dp && lmatch(*cp, *dp)) | |
517 | cp++, dp++; | |
518 | if (*dp == 0) | |
519 | return (1); | |
520 | return (0); | |
521 | } | |
522 | ||
523 | lmatch(c, d) | |
524 | register int c, d; | |
525 | { | |
526 | ||
527 | if (c == d) | |
528 | return (1); | |
529 | if (!isalpha(c) || !isalpha(d)) | |
530 | return (0); | |
531 | if (islower(c)) | |
532 | c = toupper(c); | |
533 | if (islower(d)) | |
534 | d = toupper(d); | |
535 | return (c == d); | |
536 | } | |
537 | ||
538 | whatis(argc, argv) | |
539 | int argc; | |
540 | char **argv; | |
541 | { | |
542 | register char *gotit, **vp; | |
543 | char buf[BUFSIZ], file[MAXPATHLEN+1], *cp, *tp; | |
544 | ||
545 | if (argc == 0) { | |
546 | fprintf(stderr, "whatis what?\n"); | |
547 | exit(1); | |
548 | } | |
549 | for (vp = argv; *vp; vp++) | |
550 | *vp = trim(*vp); | |
551 | gotit = calloc(1, blklen(argv)); | |
552 | for (cp = manpath; cp; cp = tp) { | |
553 | tp = index(cp, ':'); | |
554 | if (tp) { | |
555 | if (tp == cp) | |
556 | strcpy(file, WHATIS); | |
557 | else | |
558 | sprintf(file, "%.*s/%s", tp-cp, cp, WHATIS); | |
559 | tp++; | |
560 | } else | |
561 | sprintf(file, "%s/%s", cp, WHATIS); | |
562 | if (freopen(file, "r", stdin) == NULL) | |
563 | continue; | |
564 | while (fgets(buf, sizeof buf, stdin) != NULL) | |
565 | for (vp = argv; *vp; vp++) | |
566 | if (wmatch(buf, *vp)) { | |
567 | printf("%s", buf); | |
568 | gotit[vp - argv] = 1; | |
569 | for (vp++; *vp; vp++) | |
570 | if (wmatch(buf, *vp)) | |
571 | gotit[vp - argv] = 1; | |
572 | break; | |
573 | } | |
574 | } | |
575 | for (vp = argv; *vp; vp++) | |
576 | if (gotit[vp - argv] == 0) | |
577 | printf("%s: not found\n", *vp); | |
578 | } | |
579 | ||
580 | wmatch(buf, str) | |
581 | char *buf, *str; | |
582 | { | |
583 | register char *bp, *cp; | |
584 | ||
585 | bp = buf; | |
586 | again: | |
587 | cp = str; | |
588 | while (*bp && *cp && lmatch(*bp, *cp)) | |
589 | bp++, cp++; | |
590 | if (*cp == 0 && (*bp == '(' || *bp == ',' || *bp == '\t' || *bp == ' ')) | |
591 | return (1); | |
592 | while (isalpha(*bp) || isdigit(*bp)) | |
593 | bp++; | |
594 | if (*bp != ',') | |
595 | return (0); | |
596 | bp++; | |
597 | while (isspace(*bp)) | |
598 | bp++; | |
599 | goto again; | |
600 | } | |
601 | ||
602 | char * | |
603 | trim(cp) | |
604 | register char *cp; | |
605 | { | |
606 | register char *dp; | |
607 | ||
608 | for (dp = cp; *dp; dp++) | |
609 | if (*dp == '/') | |
610 | cp = dp + 1; | |
611 | if (cp[0] != '.') { | |
612 | if (cp + 3 <= dp && dp[-2] == '.' && | |
613 | any(dp[-1], "cosa12345678npP")) | |
614 | dp[-2] = 0; | |
615 | if (cp + 4 <= dp && dp[-3] == '.' && | |
616 | any(dp[-2], "13") && isalpha(dp[-1])) | |
617 | dp[-3] = 0; | |
618 | } | |
619 | return (cp); | |
620 | } |