This commit was generated by cvs2svn to track changes on a CVS vendor
[unix-history] / usr.bin / man / man.c
CommitLineData
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
8char 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
14static 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
47int nomore;
48char *CAT = CAT_;
49char *manpath = "/usr/share/man:/usr/local/man:/usr/gnu/man:/usr/X386/man";
50char *strcpy();
51char *strcat();
52char *getenv();
53char *calloc();
54char *trim();
55void nuke(int);
56int apropos();
57int whatis();
58int section;
59int subsec;
60int troffit;
61int mypid;
62
63#define eq(a,b) (strcmp(a,b) == 0)
64
65main(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];
140sectin:
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
156runpath(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
174manual(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;
206search:
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 }
275found:
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) {
297tohard:
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 }
311catit:
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);
347hardway:
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 */
359pathstat(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
400nroff(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
413troff(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
423any(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
435void
436nuke(sig)
437int sig;
438{
439 char name[15];
440
441 sprintf(name, "/tmp/man%d", mypid);
442 unlink(name);
443 exit(1);
444}
445
446unsigned int
447blklen(ip)
448 register char **ip;
449{
450 register unsigned int i = 0;
451
452 while (*ip++)
453 i++;
454 return (i);
455}
456
457apropos(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
498match(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
512amatch(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
523lmatch(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
538whatis(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
580wmatch(buf, str)
581 char *buf, *str;
582{
583 register char *bp, *cp;
584
585 bp = buf;
586again:
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
602char *
603trim(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}