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 634886Sbostic * provided that the above copyright notice and this paragraph are 734886Sbostic * duplicated in all such forms and that any documentation, 834886Sbostic * advertising materials, and other materials related to such 934886Sbostic * distribution and use acknowledge that the software was developed 1034886Sbostic * by the University of California, Berkeley. The name of the 1134886Sbostic * University may not be used to endorse or promote products derived 1234886Sbostic * from this software without specific prior written permission. 1334886Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1434886Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1534886Sbostic * 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*37892Sbostic static char sccsid[] = "@(#)man.c 5.18 (Berkeley) 05/11/89"; 2633054Sbostic #endif /* not lint */ 2731703Sbostic 2831703Sbostic #include <sys/param.h> 2931703Sbostic #include <sys/file.h> 3031703Sbostic #include <ctype.h> 31*37892Sbostic #include "pathnames.h" 3231703Sbostic 3333809Sbostic static char *command, /* command buffer */ 3433809Sbostic *defpath, /* default search path */ 3531713Sbostic *locpath, /* local search path */ 3631713Sbostic *machine, /* machine type */ 3731713Sbostic *manpath, /* current search path */ 3831936Sbostic *newpath, /* new search path */ 3934057Sbostic *pager, /* requested pager */ 4034057Sbostic how; /* how to display */ 4131703Sbostic 4234057Sbostic #define ALL 0x1 /* show all man pages */ 4334057Sbostic #define CAT 0x2 /* copy file to stdout */ 4434057Sbostic #define WHERE 0x4 /* just tell me where */ 4534057Sbostic 4631703Sbostic main(argc, argv) 4733354Sbostic int argc; 4833354Sbostic register char **argv; 4931703Sbostic { 5034057Sbostic extern char *optarg; 5134057Sbostic extern int optind; 5234057Sbostic int ch; 5334057Sbostic char *getenv(), *malloc(); 5431703Sbostic 5534057Sbostic while ((ch = getopt(argc, argv, "-M:P:afkw")) != EOF) 5634057Sbostic switch((char)ch) { 5734057Sbostic case '-': 5834057Sbostic how |= CAT; 5931703Sbostic break; 6031703Sbostic case 'M': 6131703Sbostic case 'P': /* backward compatibility */ 6234057Sbostic defpath = optarg; 6331703Sbostic break; 6434057Sbostic case 'a': 6534057Sbostic how |= ALL; 6634057Sbostic break; 6731703Sbostic /* 6833809Sbostic * "man -f" and "man -k" are backward contemptible, 6933809Sbostic * undocumented ways of calling whatis(1) and apropos(1). 7031703Sbostic */ 7131703Sbostic case 'f': 7234057Sbostic jump(argv, "-f", "whatis"); 7334057Sbostic /*NOTREACHED*/ 7431703Sbostic case 'k': 7534057Sbostic jump(argv, "-k", "apropos"); 7634057Sbostic /*NOTREACHED*/ 7733354Sbostic /* 7833354Sbostic * Deliberately undocumented; really only useful when 7933354Sbostic * you're moving man pages around. Not worth adding. 8033354Sbostic */ 8132565Sbostic case 'w': 8234057Sbostic how |= WHERE | ALL; 8332565Sbostic break; 8431703Sbostic case '?': 8531703Sbostic default: 8634057Sbostic usage(); 8731703Sbostic } 8834057Sbostic argv += optind; 8931703Sbostic 9034057Sbostic if (!*argv) 9134057Sbostic usage(); 9233354Sbostic 9334057Sbostic if (!(how & CAT)) 9431703Sbostic if (!isatty(1)) 9534057Sbostic how |= CAT; 9631779Sbostic else if (pager = getenv("PAGER")) { 9733354Sbostic register char *p; 9831779Sbostic 9931779Sbostic /* 10031779Sbostic * if the user uses "more", we make it "more -s" 10131779Sbostic * watch out for PAGER = "mypager /usr/ucb/more" 10231779Sbostic */ 10333354Sbostic for (p = pager; *p && !isspace(*p); ++p); 10433354Sbostic for (; p > pager && *p != '/'; --p); 10533354Sbostic if (p != pager) 10633354Sbostic ++p; 10731779Sbostic /* make sure it's "more", not "morex" */ 10834058Smarc if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))){ 10934058Smarc char *opager = pager; 11031779Sbostic /* 11134058Smarc * allocate space to add the "-s" 11231779Sbostic */ 11334058Smarc if (!(pager = malloc((u_int)(strlen(opager) 11434058Smarc + sizeof("-s") + 1)))) { 11531779Sbostic fputs("man: out of space.\n", stderr); 11631779Sbostic exit(1); 11731779Sbostic } 11834058Smarc (void)sprintf(pager, "%s %s", opager, "-s"); 11931779Sbostic } 12031779Sbostic } 12131779Sbostic else 122*37892Sbostic pager = _PATH_PAGER; 12331703Sbostic if (!(machine = getenv("MACHINE"))) 12431703Sbostic machine = MACHINE; 12531713Sbostic if (!defpath && !(defpath = getenv("MANPATH"))) 126*37892Sbostic defpath = _PATH_DEFAULT; 127*37892Sbostic locpath = _PATH_LOCAL; 128*37892Sbostic newpath = _PATH_NEW; 12933354Sbostic man(argv); 13034752Sbostic /* use system(3) in case someone's pager is "pager arg1 arg2" */ 13133809Sbostic if (command) 13233809Sbostic (void)system(command); 13333354Sbostic exit(0); 13433354Sbostic } 13531703Sbostic 13633809Sbostic typedef struct { 13733809Sbostic char *name, *msg; 13833809Sbostic } DIR; 13933354Sbostic static DIR list1[] = { /* section one list */ 14033354Sbostic "cat1", "1st", "cat8", "8th", "cat6", "6th", 14133354Sbostic "cat.old", "old", NULL, NULL, 14233354Sbostic }, list2[] = { /* rest of the list */ 14333354Sbostic "cat2", "2nd", "cat3", "3rd", "cat4", "4th", 14433354Sbostic "cat5", "5th", "cat7", "7th", "cat3f", "3rd (F)", 14533354Sbostic NULL, NULL, 14633354Sbostic }, list3[2]; /* single section */ 14733354Sbostic 14833354Sbostic static 14933354Sbostic man(argv) 15033354Sbostic char **argv; 15133354Sbostic { 15233354Sbostic register char *p; 15333354Sbostic DIR *section, *getsect(); 15433354Sbostic int res; 15533354Sbostic 15631703Sbostic for (; *argv; ++argv) { 15731906Sbostic manpath = defpath; 15833354Sbostic section = NULL; 15931703Sbostic switch(**argv) { 16033354Sbostic case 'l': /* local */ 16134752Sbostic /* support the "{l,local,n,new}###" syntax */ 16233354Sbostic for (p = *argv; isalpha(*p); ++p); 16333354Sbostic if (!strncmp(*argv, "l", p - *argv) || 16433354Sbostic !strncmp(*argv, "local", p - *argv)) { 16534752Sbostic ++argv; 16633354Sbostic manpath = locpath; 16734752Sbostic section = getsect(p); 16831713Sbostic } 16931713Sbostic break; 17033354Sbostic case 'n': /* new */ 17133354Sbostic for (p = *argv; isalpha(*p); ++p); 17233354Sbostic if (!strncmp(*argv, "n", p - *argv) || 17333354Sbostic !strncmp(*argv, "new", p - *argv)) { 17434752Sbostic ++argv; 17533354Sbostic manpath = newpath; 17634752Sbostic section = getsect(p); 17731703Sbostic } 17831703Sbostic break; 17931778Sbostic /* 18033354Sbostic * old isn't really a separate section of the manual, 18133354Sbostic * and its entries are all in a single directory. 18231778Sbostic */ 18333354Sbostic case 'o': /* old */ 18433354Sbostic for (p = *argv; isalpha(*p); ++p); 18533354Sbostic if (!strncmp(*argv, "o", p - *argv) || 18633354Sbostic !strncmp(*argv, "old", p - *argv)) { 18734752Sbostic ++argv; 18833354Sbostic list3[0] = list1[3]; 18933354Sbostic section = list3; 19031703Sbostic } 19131703Sbostic break; 19233354Sbostic case '1': case '2': case '3': case '4': 19333354Sbostic case '5': case '6': case '7': case '8': 19434752Sbostic if (section = getsect(*argv)) 19534752Sbostic ++argv; 19634752Sbostic } 19734752Sbostic 19834752Sbostic if (*argv) { 19934752Sbostic if (section) 20034752Sbostic res = manual(section, *argv); 20134752Sbostic else { 20234752Sbostic res = manual(list1, *argv); 20334752Sbostic if (!res || (how & ALL)) 20434752Sbostic res += manual(list2, *argv); 20531936Sbostic } 20634752Sbostic if (res || how&WHERE) 20734752Sbostic continue; 20831703Sbostic } 20933354Sbostic 21034752Sbostic fputs("man: ", stderr); 21134752Sbostic if (*argv) 21234752Sbostic fprintf(stderr, "no entry for %s in the ", *argv); 21334752Sbostic else 21434752Sbostic fputs("what do you want from the ", stderr); 21533809Sbostic if (section) 21634752Sbostic fprintf(stderr, "%s section of the ", section->msg); 21734752Sbostic if (manpath == locpath) 21834752Sbostic fputs("local ", stderr); 21934752Sbostic else if (manpath == newpath) 22034752Sbostic fputs("new ", stderr); 22134752Sbostic if (*argv) 22234752Sbostic fputs("manual.\n", stderr); 22334752Sbostic else 22434752Sbostic fputs("manual?\n", stderr); 22534752Sbostic exit(1); 22631703Sbostic } 22731703Sbostic } 22831703Sbostic 22931778Sbostic /* 23031778Sbostic * manual -- 23133809Sbostic * given a directory list and a file name find a file that 23233809Sbostic * matches; check ${directory}/${dir}/{file name} and 23333809Sbostic * ${directory}/${dir}/${machine}/${file name}. 23431778Sbostic */ 23531703Sbostic static 23631703Sbostic manual(section, name) 23733354Sbostic DIR *section; 23833354Sbostic char *name; 23931703Sbostic { 24033354Sbostic register char *beg, *end; 24133354Sbostic register DIR *dp; 24233809Sbostic register int res; 24333809Sbostic char fname[MAXPATHLEN + 1], *index(); 24431703Sbostic 24533809Sbostic for (beg = manpath, res = 0;; beg = end + 1) { 24631703Sbostic if (end = index(beg, ':')) 24731703Sbostic *end = '\0'; 24833809Sbostic for (dp = section; dp->name; ++dp) { 24933809Sbostic (void)sprintf(fname, "%s/%s/%s.0", beg, dp->name, name); 25033809Sbostic if (access(fname, R_OK)) { 25133809Sbostic (void)sprintf(fname, "%s/%s/%s/%s.0", beg, 25233809Sbostic dp->name, machine, name); 25333809Sbostic if (access(fname, R_OK)) 25433809Sbostic continue; 25531778Sbostic } 25634057Sbostic if (how & WHERE) 25733809Sbostic printf("man: found in %s.\n", fname); 25834057Sbostic else if (how & CAT) 25933809Sbostic cat(fname); 26033809Sbostic else 26133809Sbostic add(fname); 26234057Sbostic if (!(how & ALL)) 26334057Sbostic return(1); 26433809Sbostic res = 1; 26533809Sbostic } 26631703Sbostic if (!end) 26733809Sbostic return(res); 26833297Sbostic *end = ':'; 26931703Sbostic } 27031703Sbostic /*NOTREACHED*/ 27131703Sbostic } 27231703Sbostic 27331778Sbostic /* 27433809Sbostic * cat -- 27533809Sbostic * cat out the file 27631778Sbostic */ 27731703Sbostic static 27833809Sbostic cat(fname) 27933809Sbostic char *fname; 28031703Sbostic { 28133809Sbostic register int fd, n; 28233809Sbostic char buf[BUFSIZ]; 28331703Sbostic 28433809Sbostic if (!(fd = open(fname, O_RDONLY, 0))) { 28533809Sbostic perror("man: open"); 28633809Sbostic exit(1); 28731703Sbostic } 28833809Sbostic while ((n = read(fd, buf, sizeof(buf))) > 0) 28933809Sbostic if (write(1, buf, n) != n) { 29033809Sbostic perror("man: write"); 29133809Sbostic exit(1); 29233809Sbostic } 29333809Sbostic if (n == -1) { 29433809Sbostic perror("man: read"); 29533809Sbostic exit(1); 29633809Sbostic } 29733809Sbostic (void)close(fd); 29831703Sbostic } 29931703Sbostic 30031778Sbostic /* 30133809Sbostic * add -- 30233809Sbostic * add a file name to the list for future paging 30331778Sbostic */ 30431703Sbostic static 30533809Sbostic add(fname) 30633354Sbostic char *fname; 30731703Sbostic { 30833809Sbostic static u_int buflen; 30933809Sbostic static int len; 31033809Sbostic static char *cp; 31133809Sbostic int flen; 31233809Sbostic char *malloc(), *realloc(), *strcpy(); 31331703Sbostic 31433809Sbostic if (!command) { 31533809Sbostic if (!(command = malloc(buflen = 1024))) { 31633809Sbostic fputs("man: out of space.\n", stderr); 31731703Sbostic exit(1); 31831703Sbostic } 31933809Sbostic len = strlen(strcpy(command, pager)); 32033809Sbostic cp = command + len; 32133809Sbostic } 32233809Sbostic flen = strlen(fname); 32333809Sbostic if (len + flen + 2 > buflen) { /* +2 == space, EOS */ 32433809Sbostic if (!(command = realloc(command, buflen += 1024))) { 32533809Sbostic fputs("man: out of space.\n", stderr); 32631703Sbostic exit(1); 32731703Sbostic } 32833809Sbostic cp = command + len; 32931703Sbostic } 33033809Sbostic *cp++ = ' '; 33133809Sbostic len += flen + 1; /* +1 = space */ 33233809Sbostic (void)strcpy(cp, fname); 33333809Sbostic cp += flen; 33431703Sbostic } 33531703Sbostic 33631778Sbostic /* 33733354Sbostic * getsect -- 33833354Sbostic * return a point to the section structure for a particular suffix 33931778Sbostic */ 34033354Sbostic static DIR * 34133354Sbostic getsect(s) 34233354Sbostic char *s; 34331703Sbostic { 34433354Sbostic switch(*s++) { 34533354Sbostic case '1': 34633354Sbostic if (!*s) 34733354Sbostic return(list1); 34833354Sbostic break; 34933354Sbostic case '2': 35033354Sbostic if (!*s) { 35133354Sbostic list3[0] = list2[0]; 35233354Sbostic return(list3); 35333354Sbostic } 35433354Sbostic break; 35533354Sbostic /* sect. 3 requests are for either section 3, or section 3[fF]. */ 35633354Sbostic case '3': 35733354Sbostic if (!*s) { 35833354Sbostic list3[0] = list2[1]; 35933354Sbostic return(list3); 36033354Sbostic } 36133354Sbostic else if ((*s == 'f' || *s == 'F') && !*++s) { 36233354Sbostic list3[0] = list2[5]; 36333354Sbostic return(list3); 36433354Sbostic } 36533354Sbostic break; 36633354Sbostic case '4': 36733354Sbostic if (!*s) { 36833354Sbostic list3[0] = list2[2]; 36933354Sbostic return(list3); 37033354Sbostic } 37133354Sbostic break; 37233354Sbostic case '5': 37333354Sbostic if (!*s) { 37433354Sbostic list3[0] = list2[3]; 37533354Sbostic return(list3); 37633354Sbostic } 37733354Sbostic break; 37833354Sbostic case '6': 37933354Sbostic if (!*s) { 38033354Sbostic list3[0] = list1[2]; 38133354Sbostic return(list3); 38233354Sbostic } 38333354Sbostic break; 38433354Sbostic case '7': 38533354Sbostic if (!*s) { 38633354Sbostic list3[0] = list2[4]; 38733354Sbostic return(list3); 38833354Sbostic } 38933354Sbostic break; 39033354Sbostic case '8': 39133354Sbostic if (!*s) { 39233354Sbostic list3[0] = list1[1]; 39333354Sbostic return(list3); 39433354Sbostic } 39533354Sbostic } 39633354Sbostic return((DIR *)NULL); 39731703Sbostic } 39834057Sbostic 39934057Sbostic /* 40034057Sbostic * jump -- 40134057Sbostic * strip out flag argument and jump 40234057Sbostic */ 40334057Sbostic static 40434057Sbostic jump(argv, flag, name) 40534057Sbostic char **argv, *name; 40634057Sbostic register char *flag; 40734057Sbostic { 40834057Sbostic register char **arg; 40934057Sbostic 41034057Sbostic argv[0] = name; 41134057Sbostic for (arg = argv + 1; *arg; ++arg) 41234057Sbostic if (!strcmp(*arg, flag)) 41334057Sbostic break; 41434057Sbostic for (; *arg; ++arg) 41534057Sbostic arg[0] = arg[1]; 41634057Sbostic execvp(name, argv); 41734057Sbostic fprintf(stderr, "%s: Command not found.\n", name); 41834057Sbostic exit(1); 41934057Sbostic } 42034057Sbostic 42134057Sbostic /* 42234057Sbostic * usage -- 42334057Sbostic * print usage and die 42434057Sbostic */ 42534057Sbostic static 42634057Sbostic usage() 42734057Sbostic { 42834057Sbostic fputs("usage: man [-] [-a] [-M path] [section] title ...\n", stderr); 42934057Sbostic exit(1); 43034057Sbostic } 431