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*34752Sbostic static char sccsid[] = "@(#)man.c 5.16 (Berkeley) 06/17/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 */ 4134057Sbostic *pager, /* requested pager */ 4234057Sbostic how; /* how to display */ 4331703Sbostic 4434057Sbostic #define ALL 0x1 /* show all man pages */ 4534057Sbostic #define CAT 0x2 /* copy file to stdout */ 4634057Sbostic #define WHERE 0x4 /* just tell me where */ 4734057Sbostic 4831703Sbostic main(argc, argv) 4933354Sbostic int argc; 5033354Sbostic register char **argv; 5131703Sbostic { 5234057Sbostic extern char *optarg; 5334057Sbostic extern int optind; 5434057Sbostic int ch; 5534057Sbostic char *getenv(), *malloc(); 5631703Sbostic 5734057Sbostic while ((ch = getopt(argc, argv, "-M:P:afkw")) != EOF) 5834057Sbostic switch((char)ch) { 5934057Sbostic case '-': 6034057Sbostic how |= CAT; 6131703Sbostic break; 6231703Sbostic case 'M': 6331703Sbostic case 'P': /* backward compatibility */ 6434057Sbostic defpath = optarg; 6531703Sbostic break; 6634057Sbostic case 'a': 6734057Sbostic how |= ALL; 6834057Sbostic 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': 7434057Sbostic jump(argv, "-f", "whatis"); 7534057Sbostic /*NOTREACHED*/ 7631703Sbostic case 'k': 7734057Sbostic jump(argv, "-k", "apropos"); 7834057Sbostic /*NOTREACHED*/ 7933354Sbostic /* 8033354Sbostic * Deliberately undocumented; really only useful when 8133354Sbostic * you're moving man pages around. Not worth adding. 8233354Sbostic */ 8332565Sbostic case 'w': 8434057Sbostic how |= WHERE | ALL; 8532565Sbostic break; 8631703Sbostic case '?': 8731703Sbostic default: 8834057Sbostic usage(); 8931703Sbostic } 9034057Sbostic argv += optind; 9131703Sbostic 9234057Sbostic if (!*argv) 9334057Sbostic usage(); 9433354Sbostic 9534057Sbostic if (!(how & CAT)) 9631703Sbostic if (!isatty(1)) 9734057Sbostic 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" */ 11034058Smarc if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))){ 11134058Smarc char *opager = pager; 11231779Sbostic /* 11334058Smarc * allocate space to add the "-s" 11431779Sbostic */ 11534058Smarc if (!(pager = malloc((u_int)(strlen(opager) 11634058Smarc + sizeof("-s") + 1)))) { 11731779Sbostic fputs("man: out of space.\n", stderr); 11831779Sbostic exit(1); 11931779Sbostic } 12034058Smarc (void)sprintf(pager, "%s %s", opager, "-s"); 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); 132*34752Sbostic /* use system(3) in case someone's pager is "pager 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 */ 163*34752Sbostic /* support the "{l,local,n,new}###" syntax */ 16433354Sbostic for (p = *argv; isalpha(*p); ++p); 16533354Sbostic if (!strncmp(*argv, "l", p - *argv) || 16633354Sbostic !strncmp(*argv, "local", p - *argv)) { 167*34752Sbostic ++argv; 16833354Sbostic manpath = locpath; 169*34752Sbostic section = getsect(p); 17031713Sbostic } 17131713Sbostic break; 17233354Sbostic case 'n': /* new */ 17333354Sbostic for (p = *argv; isalpha(*p); ++p); 17433354Sbostic if (!strncmp(*argv, "n", p - *argv) || 17533354Sbostic !strncmp(*argv, "new", p - *argv)) { 176*34752Sbostic ++argv; 17733354Sbostic manpath = newpath; 178*34752Sbostic section = getsect(p); 17931703Sbostic } 18031703Sbostic break; 18131778Sbostic /* 18233354Sbostic * old isn't really a separate section of the manual, 18333354Sbostic * and its entries are all in a single directory. 18431778Sbostic */ 18533354Sbostic case 'o': /* old */ 18633354Sbostic for (p = *argv; isalpha(*p); ++p); 18733354Sbostic if (!strncmp(*argv, "o", p - *argv) || 18833354Sbostic !strncmp(*argv, "old", p - *argv)) { 189*34752Sbostic ++argv; 19033354Sbostic list3[0] = list1[3]; 19133354Sbostic section = list3; 19231703Sbostic } 19331703Sbostic break; 19433354Sbostic case '1': case '2': case '3': case '4': 19533354Sbostic case '5': case '6': case '7': case '8': 196*34752Sbostic if (section = getsect(*argv)) 197*34752Sbostic ++argv; 198*34752Sbostic } 199*34752Sbostic 200*34752Sbostic if (*argv) { 201*34752Sbostic if (section) 202*34752Sbostic res = manual(section, *argv); 203*34752Sbostic else { 204*34752Sbostic res = manual(list1, *argv); 205*34752Sbostic if (!res || (how & ALL)) 206*34752Sbostic res += manual(list2, *argv); 20731936Sbostic } 208*34752Sbostic if (res || how&WHERE) 209*34752Sbostic continue; 21031703Sbostic } 21133354Sbostic 212*34752Sbostic fputs("man: ", stderr); 213*34752Sbostic if (*argv) 214*34752Sbostic fprintf(stderr, "no entry for %s in the ", *argv); 215*34752Sbostic else 216*34752Sbostic fputs("what do you want from the ", stderr); 21733809Sbostic if (section) 218*34752Sbostic fprintf(stderr, "%s section of the ", section->msg); 219*34752Sbostic if (manpath == locpath) 220*34752Sbostic fputs("local ", stderr); 221*34752Sbostic else if (manpath == newpath) 222*34752Sbostic fputs("new ", stderr); 223*34752Sbostic if (*argv) 224*34752Sbostic fputs("manual.\n", stderr); 225*34752Sbostic else 226*34752Sbostic fputs("manual?\n", stderr); 227*34752Sbostic exit(1); 22831703Sbostic } 22931703Sbostic } 23031703Sbostic 23131778Sbostic /* 23231778Sbostic * manual -- 23333809Sbostic * given a directory list and a file name find a file that 23433809Sbostic * matches; check ${directory}/${dir}/{file name} and 23533809Sbostic * ${directory}/${dir}/${machine}/${file name}. 23631778Sbostic */ 23731703Sbostic static 23831703Sbostic manual(section, name) 23933354Sbostic DIR *section; 24033354Sbostic char *name; 24131703Sbostic { 24233354Sbostic register char *beg, *end; 24333354Sbostic register DIR *dp; 24433809Sbostic register int res; 24533809Sbostic char fname[MAXPATHLEN + 1], *index(); 24631703Sbostic 24733809Sbostic for (beg = manpath, res = 0;; beg = end + 1) { 24831703Sbostic if (end = index(beg, ':')) 24931703Sbostic *end = '\0'; 25033809Sbostic for (dp = section; dp->name; ++dp) { 25133809Sbostic (void)sprintf(fname, "%s/%s/%s.0", beg, dp->name, name); 25233809Sbostic if (access(fname, R_OK)) { 25333809Sbostic (void)sprintf(fname, "%s/%s/%s/%s.0", beg, 25433809Sbostic dp->name, machine, name); 25533809Sbostic if (access(fname, R_OK)) 25633809Sbostic continue; 25731778Sbostic } 25834057Sbostic if (how & WHERE) 25933809Sbostic printf("man: found in %s.\n", fname); 26034057Sbostic else if (how & CAT) 26133809Sbostic cat(fname); 26233809Sbostic else 26333809Sbostic add(fname); 26434057Sbostic if (!(how & ALL)) 26534057Sbostic return(1); 26633809Sbostic res = 1; 26733809Sbostic } 26831703Sbostic if (!end) 26933809Sbostic return(res); 27033297Sbostic *end = ':'; 27131703Sbostic } 27231703Sbostic /*NOTREACHED*/ 27331703Sbostic } 27431703Sbostic 27531778Sbostic /* 27633809Sbostic * cat -- 27733809Sbostic * cat out the file 27831778Sbostic */ 27931703Sbostic static 28033809Sbostic cat(fname) 28133809Sbostic char *fname; 28231703Sbostic { 28333809Sbostic register int fd, n; 28433809Sbostic char buf[BUFSIZ]; 28531703Sbostic 28633809Sbostic if (!(fd = open(fname, O_RDONLY, 0))) { 28733809Sbostic perror("man: open"); 28833809Sbostic exit(1); 28931703Sbostic } 29033809Sbostic while ((n = read(fd, buf, sizeof(buf))) > 0) 29133809Sbostic if (write(1, buf, n) != n) { 29233809Sbostic perror("man: write"); 29333809Sbostic exit(1); 29433809Sbostic } 29533809Sbostic if (n == -1) { 29633809Sbostic perror("man: read"); 29733809Sbostic exit(1); 29833809Sbostic } 29933809Sbostic (void)close(fd); 30031703Sbostic } 30131703Sbostic 30231778Sbostic /* 30333809Sbostic * add -- 30433809Sbostic * add a file name to the list for future paging 30531778Sbostic */ 30631703Sbostic static 30733809Sbostic add(fname) 30833354Sbostic char *fname; 30931703Sbostic { 31033809Sbostic static u_int buflen; 31133809Sbostic static int len; 31233809Sbostic static char *cp; 31333809Sbostic int flen; 31433809Sbostic char *malloc(), *realloc(), *strcpy(); 31531703Sbostic 31633809Sbostic if (!command) { 31733809Sbostic if (!(command = malloc(buflen = 1024))) { 31833809Sbostic fputs("man: out of space.\n", stderr); 31931703Sbostic exit(1); 32031703Sbostic } 32133809Sbostic len = strlen(strcpy(command, pager)); 32233809Sbostic cp = command + len; 32333809Sbostic } 32433809Sbostic flen = strlen(fname); 32533809Sbostic if (len + flen + 2 > buflen) { /* +2 == space, EOS */ 32633809Sbostic if (!(command = realloc(command, buflen += 1024))) { 32733809Sbostic fputs("man: out of space.\n", stderr); 32831703Sbostic exit(1); 32931703Sbostic } 33033809Sbostic cp = command + len; 33131703Sbostic } 33233809Sbostic *cp++ = ' '; 33333809Sbostic len += flen + 1; /* +1 = space */ 33433809Sbostic (void)strcpy(cp, fname); 33533809Sbostic cp += flen; 33631703Sbostic } 33731703Sbostic 33831778Sbostic /* 33933354Sbostic * getsect -- 34033354Sbostic * return a point to the section structure for a particular suffix 34131778Sbostic */ 34233354Sbostic static DIR * 34333354Sbostic getsect(s) 34433354Sbostic char *s; 34531703Sbostic { 34633354Sbostic switch(*s++) { 34733354Sbostic case '1': 34833354Sbostic if (!*s) 34933354Sbostic return(list1); 35033354Sbostic break; 35133354Sbostic case '2': 35233354Sbostic if (!*s) { 35333354Sbostic list3[0] = list2[0]; 35433354Sbostic return(list3); 35533354Sbostic } 35633354Sbostic break; 35733354Sbostic /* sect. 3 requests are for either section 3, or section 3[fF]. */ 35833354Sbostic case '3': 35933354Sbostic if (!*s) { 36033354Sbostic list3[0] = list2[1]; 36133354Sbostic return(list3); 36233354Sbostic } 36333354Sbostic else if ((*s == 'f' || *s == 'F') && !*++s) { 36433354Sbostic list3[0] = list2[5]; 36533354Sbostic return(list3); 36633354Sbostic } 36733354Sbostic break; 36833354Sbostic case '4': 36933354Sbostic if (!*s) { 37033354Sbostic list3[0] = list2[2]; 37133354Sbostic return(list3); 37233354Sbostic } 37333354Sbostic break; 37433354Sbostic case '5': 37533354Sbostic if (!*s) { 37633354Sbostic list3[0] = list2[3]; 37733354Sbostic return(list3); 37833354Sbostic } 37933354Sbostic break; 38033354Sbostic case '6': 38133354Sbostic if (!*s) { 38233354Sbostic list3[0] = list1[2]; 38333354Sbostic return(list3); 38433354Sbostic } 38533354Sbostic break; 38633354Sbostic case '7': 38733354Sbostic if (!*s) { 38833354Sbostic list3[0] = list2[4]; 38933354Sbostic return(list3); 39033354Sbostic } 39133354Sbostic break; 39233354Sbostic case '8': 39333354Sbostic if (!*s) { 39433354Sbostic list3[0] = list1[1]; 39533354Sbostic return(list3); 39633354Sbostic } 39733354Sbostic } 39833354Sbostic return((DIR *)NULL); 39931703Sbostic } 40034057Sbostic 40134057Sbostic /* 40234057Sbostic * jump -- 40334057Sbostic * strip out flag argument and jump 40434057Sbostic */ 40534057Sbostic static 40634057Sbostic jump(argv, flag, name) 40734057Sbostic char **argv, *name; 40834057Sbostic register char *flag; 40934057Sbostic { 41034057Sbostic register char **arg; 41134057Sbostic 41234057Sbostic argv[0] = name; 41334057Sbostic for (arg = argv + 1; *arg; ++arg) 41434057Sbostic if (!strcmp(*arg, flag)) 41534057Sbostic break; 41634057Sbostic for (; *arg; ++arg) 41734057Sbostic arg[0] = arg[1]; 41834057Sbostic execvp(name, argv); 41934057Sbostic fprintf(stderr, "%s: Command not found.\n", name); 42034057Sbostic exit(1); 42134057Sbostic } 42234057Sbostic 42334057Sbostic /* 42434057Sbostic * usage -- 42534057Sbostic * print usage and die 42634057Sbostic */ 42734057Sbostic static 42834057Sbostic usage() 42934057Sbostic { 43034057Sbostic fputs("usage: man [-] [-a] [-M path] [section] title ...\n", stderr); 43134057Sbostic exit(1); 43234057Sbostic } 433