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 6*34886Sbostic * provided that the above copyright notice and this paragraph are 7*34886Sbostic * duplicated in all such forms and that any documentation, 8*34886Sbostic * advertising materials, and other materials related to such 9*34886Sbostic * distribution and use acknowledge that the software was developed 10*34886Sbostic * by the University of California, Berkeley. The name of the 11*34886Sbostic * University may not be used to endorse or promote products derived 12*34886Sbostic * from this software without specific prior written permission. 13*34886Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14*34886Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15*34886Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1631703Sbostic */ 1731703Sbostic 1831703Sbostic #ifndef lint 1931703Sbostic char copyright[] = 2031703Sbostic "@(#) Copyright (c) 1987 Regents of the University of California.\n\ 2131703Sbostic All rights reserved.\n"; 2233054Sbostic #endif /* not lint */ 2331703Sbostic 2431703Sbostic #ifndef lint 25*34886Sbostic static char sccsid[] = "@(#)man.c 5.17 (Berkeley) 06/29/88"; 2633054Sbostic #endif /* not lint */ 2731703Sbostic 2831703Sbostic #include <sys/param.h> 2931703Sbostic #include <sys/file.h> 3031703Sbostic #include <ctype.h> 3131703Sbostic 3231779Sbostic #define DEF_PAGER "/usr/ucb/more -s" 3331936Sbostic #define DEF_PATH "/usr/man:/usr/new/man:/usr/local/man" 3431713Sbostic #define LOCAL_PATH "/usr/local/man" 3531936Sbostic #define NEW_PATH "/usr/new/man" 3631936Sbostic 3733809Sbostic #define NO 0 3833809Sbostic #define YES 1 3931703Sbostic 4033809Sbostic static char *command, /* command buffer */ 4133809Sbostic *defpath, /* default search path */ 4231713Sbostic *locpath, /* local search path */ 4331713Sbostic *machine, /* machine type */ 4431713Sbostic *manpath, /* current search path */ 4531936Sbostic *newpath, /* new search path */ 4634057Sbostic *pager, /* requested pager */ 4734057Sbostic how; /* how to display */ 4831703Sbostic 4934057Sbostic #define ALL 0x1 /* show all man pages */ 5034057Sbostic #define CAT 0x2 /* copy file to stdout */ 5134057Sbostic #define WHERE 0x4 /* just tell me where */ 5234057Sbostic 5331703Sbostic main(argc, argv) 5433354Sbostic int argc; 5533354Sbostic register char **argv; 5631703Sbostic { 5734057Sbostic extern char *optarg; 5834057Sbostic extern int optind; 5934057Sbostic int ch; 6034057Sbostic char *getenv(), *malloc(); 6131703Sbostic 6234057Sbostic while ((ch = getopt(argc, argv, "-M:P:afkw")) != EOF) 6334057Sbostic switch((char)ch) { 6434057Sbostic case '-': 6534057Sbostic how |= CAT; 6631703Sbostic break; 6731703Sbostic case 'M': 6831703Sbostic case 'P': /* backward compatibility */ 6934057Sbostic defpath = optarg; 7031703Sbostic break; 7134057Sbostic case 'a': 7234057Sbostic how |= ALL; 7334057Sbostic break; 7431703Sbostic /* 7533809Sbostic * "man -f" and "man -k" are backward contemptible, 7633809Sbostic * undocumented ways of calling whatis(1) and apropos(1). 7731703Sbostic */ 7831703Sbostic case 'f': 7934057Sbostic jump(argv, "-f", "whatis"); 8034057Sbostic /*NOTREACHED*/ 8131703Sbostic case 'k': 8234057Sbostic jump(argv, "-k", "apropos"); 8334057Sbostic /*NOTREACHED*/ 8433354Sbostic /* 8533354Sbostic * Deliberately undocumented; really only useful when 8633354Sbostic * you're moving man pages around. Not worth adding. 8733354Sbostic */ 8832565Sbostic case 'w': 8934057Sbostic how |= WHERE | ALL; 9032565Sbostic break; 9131703Sbostic case '?': 9231703Sbostic default: 9334057Sbostic usage(); 9431703Sbostic } 9534057Sbostic argv += optind; 9631703Sbostic 9734057Sbostic if (!*argv) 9834057Sbostic usage(); 9933354Sbostic 10034057Sbostic if (!(how & CAT)) 10131703Sbostic if (!isatty(1)) 10234057Sbostic how |= CAT; 10331779Sbostic else if (pager = getenv("PAGER")) { 10433354Sbostic register char *p; 10531779Sbostic 10631779Sbostic /* 10731779Sbostic * if the user uses "more", we make it "more -s" 10831779Sbostic * watch out for PAGER = "mypager /usr/ucb/more" 10931779Sbostic */ 11033354Sbostic for (p = pager; *p && !isspace(*p); ++p); 11133354Sbostic for (; p > pager && *p != '/'; --p); 11233354Sbostic if (p != pager) 11333354Sbostic ++p; 11431779Sbostic /* make sure it's "more", not "morex" */ 11534058Smarc if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))){ 11634058Smarc char *opager = pager; 11731779Sbostic /* 11834058Smarc * allocate space to add the "-s" 11931779Sbostic */ 12034058Smarc if (!(pager = malloc((u_int)(strlen(opager) 12134058Smarc + sizeof("-s") + 1)))) { 12231779Sbostic fputs("man: out of space.\n", stderr); 12331779Sbostic exit(1); 12431779Sbostic } 12534058Smarc (void)sprintf(pager, "%s %s", opager, "-s"); 12631779Sbostic } 12731779Sbostic } 12831779Sbostic else 12931703Sbostic pager = DEF_PAGER; 13031703Sbostic if (!(machine = getenv("MACHINE"))) 13131703Sbostic machine = MACHINE; 13231713Sbostic if (!defpath && !(defpath = getenv("MANPATH"))) 13331713Sbostic defpath = DEF_PATH; 13431713Sbostic locpath = LOCAL_PATH; 13531936Sbostic newpath = NEW_PATH; 13633354Sbostic man(argv); 13734752Sbostic /* use system(3) in case someone's pager is "pager arg1 arg2" */ 13833809Sbostic if (command) 13933809Sbostic (void)system(command); 14033354Sbostic exit(0); 14133354Sbostic } 14231703Sbostic 14333809Sbostic typedef struct { 14433809Sbostic char *name, *msg; 14533809Sbostic } DIR; 14633354Sbostic static DIR list1[] = { /* section one list */ 14733354Sbostic "cat1", "1st", "cat8", "8th", "cat6", "6th", 14833354Sbostic "cat.old", "old", NULL, NULL, 14933354Sbostic }, list2[] = { /* rest of the list */ 15033354Sbostic "cat2", "2nd", "cat3", "3rd", "cat4", "4th", 15133354Sbostic "cat5", "5th", "cat7", "7th", "cat3f", "3rd (F)", 15233354Sbostic NULL, NULL, 15333354Sbostic }, list3[2]; /* single section */ 15433354Sbostic 15533354Sbostic static 15633354Sbostic man(argv) 15733354Sbostic char **argv; 15833354Sbostic { 15933354Sbostic register char *p; 16033354Sbostic DIR *section, *getsect(); 16133354Sbostic int res; 16233354Sbostic 16331703Sbostic for (; *argv; ++argv) { 16431906Sbostic manpath = defpath; 16533354Sbostic section = NULL; 16631703Sbostic switch(**argv) { 16733354Sbostic case 'l': /* local */ 16834752Sbostic /* support the "{l,local,n,new}###" syntax */ 16933354Sbostic for (p = *argv; isalpha(*p); ++p); 17033354Sbostic if (!strncmp(*argv, "l", p - *argv) || 17133354Sbostic !strncmp(*argv, "local", p - *argv)) { 17234752Sbostic ++argv; 17333354Sbostic manpath = locpath; 17434752Sbostic section = getsect(p); 17531713Sbostic } 17631713Sbostic break; 17733354Sbostic case 'n': /* new */ 17833354Sbostic for (p = *argv; isalpha(*p); ++p); 17933354Sbostic if (!strncmp(*argv, "n", p - *argv) || 18033354Sbostic !strncmp(*argv, "new", p - *argv)) { 18134752Sbostic ++argv; 18233354Sbostic manpath = newpath; 18334752Sbostic section = getsect(p); 18431703Sbostic } 18531703Sbostic break; 18631778Sbostic /* 18733354Sbostic * old isn't really a separate section of the manual, 18833354Sbostic * and its entries are all in a single directory. 18931778Sbostic */ 19033354Sbostic case 'o': /* old */ 19133354Sbostic for (p = *argv; isalpha(*p); ++p); 19233354Sbostic if (!strncmp(*argv, "o", p - *argv) || 19333354Sbostic !strncmp(*argv, "old", p - *argv)) { 19434752Sbostic ++argv; 19533354Sbostic list3[0] = list1[3]; 19633354Sbostic section = list3; 19731703Sbostic } 19831703Sbostic break; 19933354Sbostic case '1': case '2': case '3': case '4': 20033354Sbostic case '5': case '6': case '7': case '8': 20134752Sbostic if (section = getsect(*argv)) 20234752Sbostic ++argv; 20334752Sbostic } 20434752Sbostic 20534752Sbostic if (*argv) { 20634752Sbostic if (section) 20734752Sbostic res = manual(section, *argv); 20834752Sbostic else { 20934752Sbostic res = manual(list1, *argv); 21034752Sbostic if (!res || (how & ALL)) 21134752Sbostic res += manual(list2, *argv); 21231936Sbostic } 21334752Sbostic if (res || how&WHERE) 21434752Sbostic continue; 21531703Sbostic } 21633354Sbostic 21734752Sbostic fputs("man: ", stderr); 21834752Sbostic if (*argv) 21934752Sbostic fprintf(stderr, "no entry for %s in the ", *argv); 22034752Sbostic else 22134752Sbostic fputs("what do you want from the ", stderr); 22233809Sbostic if (section) 22334752Sbostic fprintf(stderr, "%s section of the ", section->msg); 22434752Sbostic if (manpath == locpath) 22534752Sbostic fputs("local ", stderr); 22634752Sbostic else if (manpath == newpath) 22734752Sbostic fputs("new ", stderr); 22834752Sbostic if (*argv) 22934752Sbostic fputs("manual.\n", stderr); 23034752Sbostic else 23134752Sbostic fputs("manual?\n", stderr); 23234752Sbostic exit(1); 23331703Sbostic } 23431703Sbostic } 23531703Sbostic 23631778Sbostic /* 23731778Sbostic * manual -- 23833809Sbostic * given a directory list and a file name find a file that 23933809Sbostic * matches; check ${directory}/${dir}/{file name} and 24033809Sbostic * ${directory}/${dir}/${machine}/${file name}. 24131778Sbostic */ 24231703Sbostic static 24331703Sbostic manual(section, name) 24433354Sbostic DIR *section; 24533354Sbostic char *name; 24631703Sbostic { 24733354Sbostic register char *beg, *end; 24833354Sbostic register DIR *dp; 24933809Sbostic register int res; 25033809Sbostic char fname[MAXPATHLEN + 1], *index(); 25131703Sbostic 25233809Sbostic for (beg = manpath, res = 0;; beg = end + 1) { 25331703Sbostic if (end = index(beg, ':')) 25431703Sbostic *end = '\0'; 25533809Sbostic for (dp = section; dp->name; ++dp) { 25633809Sbostic (void)sprintf(fname, "%s/%s/%s.0", beg, dp->name, name); 25733809Sbostic if (access(fname, R_OK)) { 25833809Sbostic (void)sprintf(fname, "%s/%s/%s/%s.0", beg, 25933809Sbostic dp->name, machine, name); 26033809Sbostic if (access(fname, R_OK)) 26133809Sbostic continue; 26231778Sbostic } 26334057Sbostic if (how & WHERE) 26433809Sbostic printf("man: found in %s.\n", fname); 26534057Sbostic else if (how & CAT) 26633809Sbostic cat(fname); 26733809Sbostic else 26833809Sbostic add(fname); 26934057Sbostic if (!(how & ALL)) 27034057Sbostic return(1); 27133809Sbostic res = 1; 27233809Sbostic } 27331703Sbostic if (!end) 27433809Sbostic return(res); 27533297Sbostic *end = ':'; 27631703Sbostic } 27731703Sbostic /*NOTREACHED*/ 27831703Sbostic } 27931703Sbostic 28031778Sbostic /* 28133809Sbostic * cat -- 28233809Sbostic * cat out the file 28331778Sbostic */ 28431703Sbostic static 28533809Sbostic cat(fname) 28633809Sbostic char *fname; 28731703Sbostic { 28833809Sbostic register int fd, n; 28933809Sbostic char buf[BUFSIZ]; 29031703Sbostic 29133809Sbostic if (!(fd = open(fname, O_RDONLY, 0))) { 29233809Sbostic perror("man: open"); 29333809Sbostic exit(1); 29431703Sbostic } 29533809Sbostic while ((n = read(fd, buf, sizeof(buf))) > 0) 29633809Sbostic if (write(1, buf, n) != n) { 29733809Sbostic perror("man: write"); 29833809Sbostic exit(1); 29933809Sbostic } 30033809Sbostic if (n == -1) { 30133809Sbostic perror("man: read"); 30233809Sbostic exit(1); 30333809Sbostic } 30433809Sbostic (void)close(fd); 30531703Sbostic } 30631703Sbostic 30731778Sbostic /* 30833809Sbostic * add -- 30933809Sbostic * add a file name to the list for future paging 31031778Sbostic */ 31131703Sbostic static 31233809Sbostic add(fname) 31333354Sbostic char *fname; 31431703Sbostic { 31533809Sbostic static u_int buflen; 31633809Sbostic static int len; 31733809Sbostic static char *cp; 31833809Sbostic int flen; 31933809Sbostic char *malloc(), *realloc(), *strcpy(); 32031703Sbostic 32133809Sbostic if (!command) { 32233809Sbostic if (!(command = malloc(buflen = 1024))) { 32333809Sbostic fputs("man: out of space.\n", stderr); 32431703Sbostic exit(1); 32531703Sbostic } 32633809Sbostic len = strlen(strcpy(command, pager)); 32733809Sbostic cp = command + len; 32833809Sbostic } 32933809Sbostic flen = strlen(fname); 33033809Sbostic if (len + flen + 2 > buflen) { /* +2 == space, EOS */ 33133809Sbostic if (!(command = realloc(command, buflen += 1024))) { 33233809Sbostic fputs("man: out of space.\n", stderr); 33331703Sbostic exit(1); 33431703Sbostic } 33533809Sbostic cp = command + len; 33631703Sbostic } 33733809Sbostic *cp++ = ' '; 33833809Sbostic len += flen + 1; /* +1 = space */ 33933809Sbostic (void)strcpy(cp, fname); 34033809Sbostic cp += flen; 34131703Sbostic } 34231703Sbostic 34331778Sbostic /* 34433354Sbostic * getsect -- 34533354Sbostic * return a point to the section structure for a particular suffix 34631778Sbostic */ 34733354Sbostic static DIR * 34833354Sbostic getsect(s) 34933354Sbostic char *s; 35031703Sbostic { 35133354Sbostic switch(*s++) { 35233354Sbostic case '1': 35333354Sbostic if (!*s) 35433354Sbostic return(list1); 35533354Sbostic break; 35633354Sbostic case '2': 35733354Sbostic if (!*s) { 35833354Sbostic list3[0] = list2[0]; 35933354Sbostic return(list3); 36033354Sbostic } 36133354Sbostic break; 36233354Sbostic /* sect. 3 requests are for either section 3, or section 3[fF]. */ 36333354Sbostic case '3': 36433354Sbostic if (!*s) { 36533354Sbostic list3[0] = list2[1]; 36633354Sbostic return(list3); 36733354Sbostic } 36833354Sbostic else if ((*s == 'f' || *s == 'F') && !*++s) { 36933354Sbostic list3[0] = list2[5]; 37033354Sbostic return(list3); 37133354Sbostic } 37233354Sbostic break; 37333354Sbostic case '4': 37433354Sbostic if (!*s) { 37533354Sbostic list3[0] = list2[2]; 37633354Sbostic return(list3); 37733354Sbostic } 37833354Sbostic break; 37933354Sbostic case '5': 38033354Sbostic if (!*s) { 38133354Sbostic list3[0] = list2[3]; 38233354Sbostic return(list3); 38333354Sbostic } 38433354Sbostic break; 38533354Sbostic case '6': 38633354Sbostic if (!*s) { 38733354Sbostic list3[0] = list1[2]; 38833354Sbostic return(list3); 38933354Sbostic } 39033354Sbostic break; 39133354Sbostic case '7': 39233354Sbostic if (!*s) { 39333354Sbostic list3[0] = list2[4]; 39433354Sbostic return(list3); 39533354Sbostic } 39633354Sbostic break; 39733354Sbostic case '8': 39833354Sbostic if (!*s) { 39933354Sbostic list3[0] = list1[1]; 40033354Sbostic return(list3); 40133354Sbostic } 40233354Sbostic } 40333354Sbostic return((DIR *)NULL); 40431703Sbostic } 40534057Sbostic 40634057Sbostic /* 40734057Sbostic * jump -- 40834057Sbostic * strip out flag argument and jump 40934057Sbostic */ 41034057Sbostic static 41134057Sbostic jump(argv, flag, name) 41234057Sbostic char **argv, *name; 41334057Sbostic register char *flag; 41434057Sbostic { 41534057Sbostic register char **arg; 41634057Sbostic 41734057Sbostic argv[0] = name; 41834057Sbostic for (arg = argv + 1; *arg; ++arg) 41934057Sbostic if (!strcmp(*arg, flag)) 42034057Sbostic break; 42134057Sbostic for (; *arg; ++arg) 42234057Sbostic arg[0] = arg[1]; 42334057Sbostic execvp(name, argv); 42434057Sbostic fprintf(stderr, "%s: Command not found.\n", name); 42534057Sbostic exit(1); 42634057Sbostic } 42734057Sbostic 42834057Sbostic /* 42934057Sbostic * usage -- 43034057Sbostic * print usage and die 43134057Sbostic */ 43234057Sbostic static 43334057Sbostic usage() 43434057Sbostic { 43534057Sbostic fputs("usage: man [-] [-a] [-M path] [section] title ...\n", stderr); 43634057Sbostic exit(1); 43734057Sbostic } 438