131703Sbostic /* 231703Sbostic * Copyright (c) 1987 Regents of the University of California. 333054Sbostic * All rights reserved. 433054Sbostic * 533054Sbostic * Redistribution and use in source and binary forms are permitted 633054Sbostic * provided that this notice is preserved and that due credit is given 733054Sbostic * to the University of California at Berkeley. The name of the University 833054Sbostic * may not be used to endorse or promote products derived from this 933054Sbostic * software without specific prior written permission. This software 1033054Sbostic * is provided ``as is'' without express or implied warranty. 1131703Sbostic */ 1231703Sbostic 1331703Sbostic #ifndef lint 1431703Sbostic char copyright[] = 1531703Sbostic "@(#) Copyright (c) 1987 Regents of the University of California.\n\ 1631703Sbostic All rights reserved.\n"; 1733054Sbostic #endif /* not lint */ 1831703Sbostic 1931703Sbostic #ifndef lint 20*34057Sbostic static char sccsid[] = "@(#)man.c 5.14 (Berkeley) 04/22/88"; 2133054Sbostic #endif /* not lint */ 2231703Sbostic 2331703Sbostic #include <sys/param.h> 2431703Sbostic #include <sys/file.h> 2531703Sbostic #include <ctype.h> 2631703Sbostic 2731779Sbostic #define DEF_PAGER "/usr/ucb/more -s" 2831936Sbostic #define DEF_PATH "/usr/man:/usr/new/man:/usr/local/man" 2931713Sbostic #define LOCAL_PATH "/usr/local/man" 3031936Sbostic #define NEW_PATH "/usr/new/man" 3131936Sbostic 3233809Sbostic #define NO 0 3333809Sbostic #define YES 1 3431703Sbostic 3533809Sbostic static char *command, /* command buffer */ 3633809Sbostic *defpath, /* default search path */ 3731713Sbostic *locpath, /* local search path */ 3831713Sbostic *machine, /* machine type */ 3931713Sbostic *manpath, /* current search path */ 4031936Sbostic *newpath, /* new search path */ 41*34057Sbostic *pager, /* requested pager */ 42*34057Sbostic how; /* how to display */ 4331703Sbostic 44*34057Sbostic #define ALL 0x1 /* show all man pages */ 45*34057Sbostic #define CAT 0x2 /* copy file to stdout */ 46*34057Sbostic #define WHERE 0x4 /* just tell me where */ 47*34057Sbostic 4831703Sbostic main(argc, argv) 4933354Sbostic int argc; 5033354Sbostic register char **argv; 5131703Sbostic { 52*34057Sbostic extern char *optarg; 53*34057Sbostic extern int optind; 54*34057Sbostic int ch; 55*34057Sbostic char *getenv(), *malloc(); 5631703Sbostic 57*34057Sbostic while ((ch = getopt(argc, argv, "-M:P:afkw")) != EOF) 58*34057Sbostic switch((char)ch) { 59*34057Sbostic case '-': 60*34057Sbostic how |= CAT; 6131703Sbostic break; 6231703Sbostic case 'M': 6331703Sbostic case 'P': /* backward compatibility */ 64*34057Sbostic defpath = optarg; 6531703Sbostic break; 66*34057Sbostic case 'a': 67*34057Sbostic how |= ALL; 68*34057Sbostic break; 6931703Sbostic /* 7033809Sbostic * "man -f" and "man -k" are backward contemptible, 7133809Sbostic * undocumented ways of calling whatis(1) and apropos(1). 7231703Sbostic */ 7331703Sbostic case 'f': 74*34057Sbostic jump(argv, "-f", "whatis"); 75*34057Sbostic /*NOTREACHED*/ 7631703Sbostic case 'k': 77*34057Sbostic jump(argv, "-k", "apropos"); 78*34057Sbostic /*NOTREACHED*/ 7933354Sbostic /* 8033354Sbostic * Deliberately undocumented; really only useful when 8133354Sbostic * you're moving man pages around. Not worth adding. 8233354Sbostic */ 8332565Sbostic case 'w': 84*34057Sbostic how |= WHERE | ALL; 8532565Sbostic break; 8631703Sbostic case '?': 8731703Sbostic default: 88*34057Sbostic usage(); 8931703Sbostic } 90*34057Sbostic argv += optind; 9131703Sbostic 92*34057Sbostic if (!*argv) 93*34057Sbostic usage(); 9433354Sbostic 95*34057Sbostic if (!(how & CAT)) 9631703Sbostic if (!isatty(1)) 97*34057Sbostic how |= CAT; 9831779Sbostic else if (pager = getenv("PAGER")) { 9933354Sbostic register char *p; 10031779Sbostic 10131779Sbostic /* 10231779Sbostic * if the user uses "more", we make it "more -s" 10331779Sbostic * watch out for PAGER = "mypager /usr/ucb/more" 10431779Sbostic */ 10533354Sbostic for (p = pager; *p && !isspace(*p); ++p); 10633354Sbostic for (; p > pager && *p != '/'; --p); 10733354Sbostic if (p != pager) 10833354Sbostic ++p; 10931779Sbostic /* make sure it's "more", not "morex" */ 11033354Sbostic if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))) { 11133354Sbostic p += 4; 11231779Sbostic /* 11331779Sbostic * allocate for the rest of the PAGER 11431779Sbostic * environment variable, a space, and the EOS. 11531779Sbostic */ 11633354Sbostic if (!(pager = malloc((u_int)(strlen(p) + sizeof(DEF_PAGER) + 1)))) { 11731779Sbostic fputs("man: out of space.\n", stderr); 11831779Sbostic exit(1); 11931779Sbostic } 12033354Sbostic (void)sprintf(pager, "%s %s", DEF_PAGER, p); 12131779Sbostic } 12231779Sbostic } 12331779Sbostic else 12431703Sbostic pager = DEF_PAGER; 12531703Sbostic if (!(machine = getenv("MACHINE"))) 12631703Sbostic machine = MACHINE; 12731713Sbostic if (!defpath && !(defpath = getenv("MANPATH"))) 12831713Sbostic defpath = DEF_PATH; 12931713Sbostic locpath = LOCAL_PATH; 13031936Sbostic newpath = NEW_PATH; 13133354Sbostic man(argv); 13233809Sbostic /* use system(3) in case someone's pager is "foo arg1 arg2" */ 13333809Sbostic if (command) 13433809Sbostic (void)system(command); 13533354Sbostic exit(0); 13633354Sbostic } 13731703Sbostic 13833809Sbostic typedef struct { 13933809Sbostic char *name, *msg; 14033809Sbostic } DIR; 14133354Sbostic static DIR list1[] = { /* section one list */ 14233354Sbostic "cat1", "1st", "cat8", "8th", "cat6", "6th", 14333354Sbostic "cat.old", "old", NULL, NULL, 14433354Sbostic }, list2[] = { /* rest of the list */ 14533354Sbostic "cat2", "2nd", "cat3", "3rd", "cat4", "4th", 14633354Sbostic "cat5", "5th", "cat7", "7th", "cat3f", "3rd (F)", 14733354Sbostic NULL, NULL, 14833354Sbostic }, list3[2]; /* single section */ 14933354Sbostic 15033354Sbostic static 15133354Sbostic man(argv) 15233354Sbostic char **argv; 15333354Sbostic { 15433354Sbostic register char *p; 15533354Sbostic DIR *section, *getsect(); 15633354Sbostic int res; 15733354Sbostic 15831703Sbostic for (; *argv; ++argv) { 15931906Sbostic manpath = defpath; 16033354Sbostic section = NULL; 16131703Sbostic switch(**argv) { 16233354Sbostic case 'l': /* local */ 16333354Sbostic for (p = *argv; isalpha(*p); ++p); 16433354Sbostic if (!strncmp(*argv, "l", p - *argv) || 16533354Sbostic !strncmp(*argv, "local", p - *argv)) { 16633354Sbostic manpath = locpath; 16733354Sbostic if (section = getsect(p)) 16833354Sbostic goto argtest; 16931713Sbostic } 17031713Sbostic break; 17133354Sbostic case 'n': /* new */ 17233354Sbostic for (p = *argv; isalpha(*p); ++p); 17333354Sbostic if (!strncmp(*argv, "n", p - *argv) || 17433354Sbostic !strncmp(*argv, "new", p - *argv)) { 17533354Sbostic manpath = newpath; 17633354Sbostic if (section = getsect(p)) 17733354Sbostic goto argtest; 17831703Sbostic } 17931703Sbostic break; 18031778Sbostic /* 18133354Sbostic * old isn't really a separate section of the manual, 18233354Sbostic * and its entries are all in a single directory. 18331778Sbostic */ 18433354Sbostic case 'o': /* old */ 18533354Sbostic for (p = *argv; isalpha(*p); ++p); 18633354Sbostic if (!strncmp(*argv, "o", p - *argv) || 18733354Sbostic !strncmp(*argv, "old", p - *argv)) { 18833354Sbostic list3[0] = list1[3]; 18933354Sbostic section = list3; 19033354Sbostic goto argtest; 19131703Sbostic } 19231703Sbostic break; 19333354Sbostic case '1': case '2': case '3': case '4': 19433354Sbostic case '5': case '6': case '7': case '8': 19533354Sbostic if (!(section = getsect(*argv))) 19633354Sbostic break; 19733354Sbostic argtest: if (!*++argv) { 19833354Sbostic fprintf(stderr, "man: what do you want from the %s section of the manual?\n", section->msg); 19931936Sbostic exit(1); 20031936Sbostic } 20131703Sbostic } 20233354Sbostic 20333809Sbostic if (section) 20433809Sbostic res = manual(section, *argv); 20533809Sbostic else { 20633809Sbostic res = manual(list1, *argv); 207*34057Sbostic if (!res || (how & ALL)) 208*34057Sbostic res += manual(list2, *argv); 20933809Sbostic } 210*34057Sbostic if (!res && !(how & WHERE)) { 21131713Sbostic if (manpath == locpath) 21233354Sbostic if (section) 21333354Sbostic fprintf(stderr, "No entry for %s in the %s section of the local manual.\n", *argv, section->msg); 21433354Sbostic else 21531936Sbostic fprintf(stderr, "No entry for %s in the local manual.\n", *argv); 21633354Sbostic else if (manpath == newpath) 21733354Sbostic if (section) 21833354Sbostic fprintf(stderr, "No entry for %s in the %s section of the new manual.\n", *argv, section->msg); 21931936Sbostic else 22031936Sbostic fprintf(stderr, "No entry for %s in the new manual.\n", *argv); 22133354Sbostic else if (section) 22233354Sbostic fprintf(stderr, "No entry for %s in the %s section of the manual.\n", *argv, section->msg); 22333354Sbostic else 22431713Sbostic fprintf(stderr, "No entry for %s in the manual.\n", *argv); 22533809Sbostic exit(1); 22633809Sbostic } 22731703Sbostic } 22831703Sbostic } 22931703Sbostic 23031778Sbostic /* 23131778Sbostic * manual -- 23233809Sbostic * given a directory list and a file name find a file that 23333809Sbostic * matches; check ${directory}/${dir}/{file name} and 23433809Sbostic * ${directory}/${dir}/${machine}/${file name}. 23531778Sbostic */ 23631703Sbostic static 23731703Sbostic manual(section, name) 23833354Sbostic DIR *section; 23933354Sbostic char *name; 24031703Sbostic { 24133354Sbostic register char *beg, *end; 24233354Sbostic register DIR *dp; 24333809Sbostic register int res; 24433809Sbostic char fname[MAXPATHLEN + 1], *index(); 24531703Sbostic 24633809Sbostic for (beg = manpath, res = 0;; beg = end + 1) { 24731703Sbostic if (end = index(beg, ':')) 24831703Sbostic *end = '\0'; 24933809Sbostic for (dp = section; dp->name; ++dp) { 25033809Sbostic (void)sprintf(fname, "%s/%s/%s.0", beg, dp->name, name); 25133809Sbostic if (access(fname, R_OK)) { 25233809Sbostic (void)sprintf(fname, "%s/%s/%s/%s.0", beg, 25333809Sbostic dp->name, machine, name); 25433809Sbostic if (access(fname, R_OK)) 25533809Sbostic continue; 25631778Sbostic } 257*34057Sbostic if (how & WHERE) 25833809Sbostic printf("man: found in %s.\n", fname); 259*34057Sbostic else if (how & CAT) 26033809Sbostic cat(fname); 26133809Sbostic else 26233809Sbostic add(fname); 263*34057Sbostic if (!(how & ALL)) 264*34057Sbostic return(1); 26533809Sbostic res = 1; 26633809Sbostic } 26731703Sbostic if (!end) 26833809Sbostic return(res); 26933297Sbostic *end = ':'; 27031703Sbostic } 27131703Sbostic /*NOTREACHED*/ 27231703Sbostic } 27331703Sbostic 27431778Sbostic /* 27533809Sbostic * cat -- 27633809Sbostic * cat out the file 27731778Sbostic */ 27831703Sbostic static 27933809Sbostic cat(fname) 28033809Sbostic char *fname; 28131703Sbostic { 28233809Sbostic register int fd, n; 28333809Sbostic char buf[BUFSIZ]; 28431703Sbostic 28533809Sbostic if (!(fd = open(fname, O_RDONLY, 0))) { 28633809Sbostic perror("man: open"); 28733809Sbostic exit(1); 28831703Sbostic } 28933809Sbostic while ((n = read(fd, buf, sizeof(buf))) > 0) 29033809Sbostic if (write(1, buf, n) != n) { 29133809Sbostic perror("man: write"); 29233809Sbostic exit(1); 29333809Sbostic } 29433809Sbostic if (n == -1) { 29533809Sbostic perror("man: read"); 29633809Sbostic exit(1); 29733809Sbostic } 29833809Sbostic (void)close(fd); 29931703Sbostic } 30031703Sbostic 30131778Sbostic /* 30233809Sbostic * add -- 30333809Sbostic * add a file name to the list for future paging 30431778Sbostic */ 30531703Sbostic static 30633809Sbostic add(fname) 30733354Sbostic char *fname; 30831703Sbostic { 30933809Sbostic static u_int buflen; 31033809Sbostic static int len; 31133809Sbostic static char *cp; 31233809Sbostic int flen; 31333809Sbostic char *malloc(), *realloc(), *strcpy(); 31431703Sbostic 31533809Sbostic if (!command) { 31633809Sbostic if (!(command = malloc(buflen = 1024))) { 31733809Sbostic fputs("man: out of space.\n", stderr); 31831703Sbostic exit(1); 31931703Sbostic } 32033809Sbostic len = strlen(strcpy(command, pager)); 32133809Sbostic cp = command + len; 32233809Sbostic } 32333809Sbostic flen = strlen(fname); 32433809Sbostic if (len + flen + 2 > buflen) { /* +2 == space, EOS */ 32533809Sbostic if (!(command = realloc(command, buflen += 1024))) { 32633809Sbostic fputs("man: out of space.\n", stderr); 32731703Sbostic exit(1); 32831703Sbostic } 32933809Sbostic cp = command + len; 33031703Sbostic } 33133809Sbostic *cp++ = ' '; 33233809Sbostic len += flen + 1; /* +1 = space */ 33333809Sbostic (void)strcpy(cp, fname); 33433809Sbostic cp += flen; 33531703Sbostic } 33631703Sbostic 33731778Sbostic /* 33833354Sbostic * getsect -- 33933354Sbostic * return a point to the section structure for a particular suffix 34031778Sbostic */ 34133354Sbostic static DIR * 34233354Sbostic getsect(s) 34333354Sbostic char *s; 34431703Sbostic { 34533354Sbostic switch(*s++) { 34633354Sbostic case '1': 34733354Sbostic if (!*s) 34833354Sbostic return(list1); 34933354Sbostic break; 35033354Sbostic case '2': 35133354Sbostic if (!*s) { 35233354Sbostic list3[0] = list2[0]; 35333354Sbostic return(list3); 35433354Sbostic } 35533354Sbostic break; 35633354Sbostic /* sect. 3 requests are for either section 3, or section 3[fF]. */ 35733354Sbostic case '3': 35833354Sbostic if (!*s) { 35933354Sbostic list3[0] = list2[1]; 36033354Sbostic return(list3); 36133354Sbostic } 36233354Sbostic else if ((*s == 'f' || *s == 'F') && !*++s) { 36333354Sbostic list3[0] = list2[5]; 36433354Sbostic return(list3); 36533354Sbostic } 36633354Sbostic break; 36733354Sbostic case '4': 36833354Sbostic if (!*s) { 36933354Sbostic list3[0] = list2[2]; 37033354Sbostic return(list3); 37133354Sbostic } 37233354Sbostic break; 37333354Sbostic case '5': 37433354Sbostic if (!*s) { 37533354Sbostic list3[0] = list2[3]; 37633354Sbostic return(list3); 37733354Sbostic } 37833354Sbostic break; 37933354Sbostic case '6': 38033354Sbostic if (!*s) { 38133354Sbostic list3[0] = list1[2]; 38233354Sbostic return(list3); 38333354Sbostic } 38433354Sbostic break; 38533354Sbostic case '7': 38633354Sbostic if (!*s) { 38733354Sbostic list3[0] = list2[4]; 38833354Sbostic return(list3); 38933354Sbostic } 39033354Sbostic break; 39133354Sbostic case '8': 39233354Sbostic if (!*s) { 39333354Sbostic list3[0] = list1[1]; 39433354Sbostic return(list3); 39533354Sbostic } 39633354Sbostic } 39733354Sbostic return((DIR *)NULL); 39831703Sbostic } 399*34057Sbostic 400*34057Sbostic /* 401*34057Sbostic * jump -- 402*34057Sbostic * strip out flag argument and jump 403*34057Sbostic */ 404*34057Sbostic static 405*34057Sbostic jump(argv, flag, name) 406*34057Sbostic char **argv, *name; 407*34057Sbostic register char *flag; 408*34057Sbostic { 409*34057Sbostic register char **arg; 410*34057Sbostic 411*34057Sbostic argv[0] = name; 412*34057Sbostic for (arg = argv + 1; *arg; ++arg) 413*34057Sbostic if (!strcmp(*arg, flag)) 414*34057Sbostic break; 415*34057Sbostic for (; *arg; ++arg) 416*34057Sbostic arg[0] = arg[1]; 417*34057Sbostic execvp(name, argv); 418*34057Sbostic fprintf(stderr, "%s: Command not found.\n", name); 419*34057Sbostic exit(1); 420*34057Sbostic } 421*34057Sbostic 422*34057Sbostic /* 423*34057Sbostic * usage -- 424*34057Sbostic * print usage and die 425*34057Sbostic */ 426*34057Sbostic static 427*34057Sbostic usage() 428*34057Sbostic { 429*34057Sbostic fputs("usage: man [-] [-a] [-M path] [section] title ...\n", stderr); 430*34057Sbostic exit(1); 431*34057Sbostic } 432