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 -h -man" /* for nroffing to cat file */ 31: #define NROFF "nroff -man" /* 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 "vtroff -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: 45: #define WHATIS "whatis" 46: 47: int nomore; 48: char *CAT = CAT_; 49: char *manpath = "/usr/man"; 50: char *strcpy(); 51: char *strcat(); 52: char *getenv(); 53: char *calloc(); 54: char *trim(); 55: int remove(); 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 = ""; 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, remove); 328: (void) signal(SIGQUIT, remove); 329: (void) signal(SIGTERM, remove); 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: remove(); 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: remove(); 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: remove() 436: { 437: char name[15]; 438: 439: sprintf(name, "/tmp/man%d", mypid); 440: unlink(name); 441: exit(1); 442: } 443: 444: unsigned int 445: blklen(ip) 446: register char **ip; 447: { 448: register unsigned int i = 0; 449: 450: while (*ip++) 451: i++; 452: return (i); 453: } 454: 455: apropos(argc, argv) 456: int argc; 457: char **argv; 458: { 459: char buf[BUFSIZ], file[MAXPATHLEN+1]; 460: char *gotit, *cp, *tp; 461: register char **vp; 462: 463: if (argc == 0) { 464: fprintf(stderr, "apropos what?\n"); 465: exit(1); 466: } 467: gotit = calloc(1, blklen(argv)); 468: for (cp = manpath; cp; cp = tp) { 469: tp = index(cp, ':'); 470: if (tp) { 471: if (tp == cp) 472: strcpy(file, WHATIS); 473: else 474: sprintf(file, "%.*s/%s", tp-cp, cp, WHATIS); 475: tp++; 476: } else 477: sprintf(file, "%s/%s", cp, WHATIS); 478: if (freopen(file, "r", stdin) == NULL) 479: continue; 480: while (fgets(buf, sizeof buf, stdin) != NULL) 481: for (vp = argv; *vp; vp++) 482: if (match(buf, *vp)) { 483: printf("%s", buf); 484: gotit[vp - argv] = 1; 485: for (vp++; *vp; vp++) 486: if (match(buf, *vp)) 487: gotit[vp - argv] = 1; 488: break; 489: } 490: } 491: for (vp = argv; *vp; vp++) 492: if (gotit[vp - argv] == 0) 493: printf("%s: nothing appropriate\n", *vp); 494: } 495: 496: match(bp, str) 497: register char *bp; 498: char *str; 499: { 500: 501: for (;;) { 502: if (*bp == 0) 503: return (0); 504: if (amatch(bp, str)) 505: return (1); 506: bp++; 507: } 508: } 509: 510: amatch(cp, dp) 511: register char *cp, *dp; 512: { 513: 514: while (*cp && *dp && lmatch(*cp, *dp)) 515: cp++, dp++; 516: if (*dp == 0) 517: return (1); 518: return (0); 519: } 520: 521: lmatch(c, d) 522: register int c, d; 523: { 524: 525: if (c == d) 526: return (1); 527: if (!isalpha(c) || !isalpha(d)) 528: return (0); 529: if (islower(c)) 530: c = toupper(c); 531: if (islower(d)) 532: d = toupper(d); 533: return (c == d); 534: } 535: 536: whatis(argc, argv) 537: int argc; 538: char **argv; 539: { 540: register char *gotit, **vp; 541: char buf[BUFSIZ], file[MAXPATHLEN+1], *cp, *tp; 542: 543: if (argc == 0) { 544: fprintf(stderr, "whatis what?\n"); 545: exit(1); 546: } 547: for (vp = argv; *vp; vp++) 548: *vp = trim(*vp); 549: gotit = calloc(1, blklen(argv)); 550: for (cp = manpath; cp; cp = tp) { 551: tp = index(cp, ':'); 552: if (tp) { 553: if (tp == cp) 554: strcpy(file, WHATIS); 555: else 556: sprintf(file, "%.*s/%s", tp-cp, cp, WHATIS); 557: tp++; 558: } else 559: sprintf(file, "%s/%s", cp, WHATIS); 560: if (freopen(file, "r", stdin) == NULL) 561: continue; 562: while (fgets(buf, sizeof buf, stdin) != NULL) 563: for (vp = argv; *vp; vp++) 564: if (wmatch(buf, *vp)) { 565: printf("%s", buf); 566: gotit[vp - argv] = 1; 567: for (vp++; *vp; vp++) 568: if (wmatch(buf, *vp)) 569: gotit[vp - argv] = 1; 570: break; 571: } 572: } 573: for (vp = argv; *vp; vp++) 574: if (gotit[vp - argv] == 0) 575: printf("%s: not found\n", *vp); 576: } 577: 578: wmatch(buf, str) 579: char *buf, *str; 580: { 581: register char *bp, *cp; 582: 583: bp = buf; 584: again: 585: cp = str; 586: while (*bp && *cp && lmatch(*bp, *cp)) 587: bp++, cp++; 588: if (*cp == 0 && (*bp == '(' || *bp == ',' || *bp == '\t' || *bp == ' ')) 589: return (1); 590: while (isalpha(*bp) || isdigit(*bp)) 591: bp++; 592: if (*bp != ',') 593: return (0); 594: bp++; 595: while (isspace(*bp)) 596: bp++; 597: goto again; 598: } 599: 600: char * 601: trim(cp) 602: register char *cp; 603: { 604: register char *dp; 605: 606: for (dp = cp; *dp; dp++) 607: if (*dp == '/') 608: cp = dp + 1; 609: if (cp[0] != '.') { 610: if (cp + 3 <= dp && dp[-2] == '.' && 611: any(dp[-1], "cosa12345678npP")) 612: dp[-2] = 0; 613: if (cp + 4 <= dp && dp[-3] == '.' && 614: any(dp[-2], "13") && isalpha(dp[-1])) 615: dp[-3] = 0; 616: } 617: return (cp); 618: }