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*33809Sbostic static char sccsid[] = "@(#)man.c 5.13 (Berkeley) 03/28/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 32*33809Sbostic #define NO 0 33*33809Sbostic #define YES 1 3431703Sbostic 3532565Sbostic static int nomore, /* copy file to stdout */ 3632565Sbostic where; /* just tell me where */ 37*33809Sbostic static char *command, /* command buffer */ 38*33809Sbostic *defpath, /* default search path */ 3931713Sbostic *locpath, /* local search path */ 4031713Sbostic *machine, /* machine type */ 4131713Sbostic *manpath, /* current search path */ 4231936Sbostic *newpath, /* new search path */ 4331713Sbostic *pager; /* requested pager */ 4431703Sbostic 4531703Sbostic main(argc, argv) 4633354Sbostic int argc; 4733354Sbostic register char **argv; 4831703Sbostic { 4933354Sbostic char **arg_start, **arg, *getenv(), *malloc(), *strcpy(); 5031703Sbostic 5131703Sbostic arg_start = argv; 5231703Sbostic for (--argc, ++argv; argc && (*argv)[0] == '-'; --argc, ++argv) 5331703Sbostic switch((*argv)[1]) { 5431703Sbostic case 0: /* just write to stdout */ 5531703Sbostic nomore = YES; 5631703Sbostic break; 5731703Sbostic case 'M': 5831703Sbostic case 'P': /* backward compatibility */ 5931703Sbostic if ((*argv)[2]) 6031906Sbostic defpath = *argv + 2; 6131703Sbostic else { 6231703Sbostic if (argc < 2) { 6331703Sbostic fprintf(stderr, "%s: missing path\n", *argv); 6431703Sbostic exit(1); 6531703Sbostic } 6631703Sbostic --argc; 6731906Sbostic defpath = *++argv; 6831703Sbostic } 6931703Sbostic break; 7031703Sbostic /* 71*33809Sbostic * "man -f" and "man -k" are backward contemptible, 72*33809Sbostic * undocumented ways of calling whatis(1) and apropos(1). 73*33809Sbostic * Just strip out the flag argument and jump. 7431703Sbostic */ 7531703Sbostic case 'f': 7631703Sbostic for (arg = argv; arg[0] = arg[1]; ++arg); 7731703Sbostic *arg_start = "whatis"; 7831703Sbostic execvp(*arg_start, arg_start); 7931703Sbostic fputs("whatis: Command not found.\n", stderr); 8031703Sbostic exit(1); 8131703Sbostic case 'k': 8231703Sbostic for (arg = argv; *arg = arg[1]; ++arg); 8331703Sbostic *arg_start = "apropos"; 8431703Sbostic execvp(*arg_start, arg_start); 8531703Sbostic fputs("apropos: Command not found.\n", stderr); 8631703Sbostic exit(1); 8733354Sbostic /* 8833354Sbostic * Deliberately undocumented; really only useful when 8933354Sbostic * you're moving man pages around. Not worth adding. 9033354Sbostic */ 9132565Sbostic case 'w': 9232565Sbostic where = YES; 9332565Sbostic break; 9431703Sbostic case '?': 9531703Sbostic default: 9632614Sbostic fprintf(stderr, "man: illegal option -- %c\n", (*argv)[1]); 9733354Sbostic goto usage; 9831703Sbostic } 9931703Sbostic 10033354Sbostic if (!argc) { 10133354Sbostic usage: fputs("usage: man [-] [-M path] [section] title ...\n", stderr); 10233354Sbostic exit(1); 10333354Sbostic } 10433354Sbostic 10531703Sbostic if (!nomore) 10631703Sbostic if (!isatty(1)) 10731703Sbostic nomore = YES; 10831779Sbostic else if (pager = getenv("PAGER")) { 10933354Sbostic register char *p; 11031779Sbostic 11131779Sbostic /* 11231779Sbostic * if the user uses "more", we make it "more -s" 11331779Sbostic * watch out for PAGER = "mypager /usr/ucb/more" 11431779Sbostic */ 11533354Sbostic for (p = pager; *p && !isspace(*p); ++p); 11633354Sbostic for (; p > pager && *p != '/'; --p); 11733354Sbostic if (p != pager) 11833354Sbostic ++p; 11931779Sbostic /* make sure it's "more", not "morex" */ 12033354Sbostic if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))) { 12133354Sbostic p += 4; 12231779Sbostic /* 12331779Sbostic * allocate for the rest of the PAGER 12431779Sbostic * environment variable, a space, and the EOS. 12531779Sbostic */ 12633354Sbostic if (!(pager = malloc((u_int)(strlen(p) + sizeof(DEF_PAGER) + 1)))) { 12731779Sbostic fputs("man: out of space.\n", stderr); 12831779Sbostic exit(1); 12931779Sbostic } 13033354Sbostic (void)sprintf(pager, "%s %s", DEF_PAGER, p); 13131779Sbostic } 13231779Sbostic } 13331779Sbostic else 13431703Sbostic pager = DEF_PAGER; 13531703Sbostic if (!(machine = getenv("MACHINE"))) 13631703Sbostic machine = MACHINE; 13731713Sbostic if (!defpath && !(defpath = getenv("MANPATH"))) 13831713Sbostic defpath = DEF_PATH; 13931713Sbostic locpath = LOCAL_PATH; 14031936Sbostic newpath = NEW_PATH; 14133354Sbostic man(argv); 142*33809Sbostic /* use system(3) in case someone's pager is "foo arg1 arg2" */ 143*33809Sbostic if (command) 144*33809Sbostic (void)system(command); 14533354Sbostic exit(0); 14633354Sbostic } 14731703Sbostic 148*33809Sbostic typedef struct { 149*33809Sbostic char *name, *msg; 150*33809Sbostic } DIR; 15133354Sbostic static DIR list1[] = { /* section one list */ 15233354Sbostic "cat1", "1st", "cat8", "8th", "cat6", "6th", 15333354Sbostic "cat.old", "old", NULL, NULL, 15433354Sbostic }, list2[] = { /* rest of the list */ 15533354Sbostic "cat2", "2nd", "cat3", "3rd", "cat4", "4th", 15633354Sbostic "cat5", "5th", "cat7", "7th", "cat3f", "3rd (F)", 15733354Sbostic NULL, NULL, 15833354Sbostic }, list3[2]; /* single section */ 15933354Sbostic 16033354Sbostic static 16133354Sbostic man(argv) 16233354Sbostic char **argv; 16333354Sbostic { 16433354Sbostic register char *p; 16533354Sbostic DIR *section, *getsect(); 16633354Sbostic int res; 16733354Sbostic 16831703Sbostic for (; *argv; ++argv) { 16931906Sbostic manpath = defpath; 17033354Sbostic section = NULL; 17131703Sbostic switch(**argv) { 17233354Sbostic case 'l': /* local */ 17333354Sbostic for (p = *argv; isalpha(*p); ++p); 17433354Sbostic if (!strncmp(*argv, "l", p - *argv) || 17533354Sbostic !strncmp(*argv, "local", p - *argv)) { 17633354Sbostic manpath = locpath; 17733354Sbostic if (section = getsect(p)) 17833354Sbostic goto argtest; 17931713Sbostic } 18031713Sbostic break; 18133354Sbostic case 'n': /* new */ 18233354Sbostic for (p = *argv; isalpha(*p); ++p); 18333354Sbostic if (!strncmp(*argv, "n", p - *argv) || 18433354Sbostic !strncmp(*argv, "new", p - *argv)) { 18533354Sbostic manpath = newpath; 18633354Sbostic if (section = getsect(p)) 18733354Sbostic goto argtest; 18831703Sbostic } 18931703Sbostic break; 19031778Sbostic /* 19133354Sbostic * old isn't really a separate section of the manual, 19233354Sbostic * and its entries are all in a single directory. 19331778Sbostic */ 19433354Sbostic case 'o': /* old */ 19533354Sbostic for (p = *argv; isalpha(*p); ++p); 19633354Sbostic if (!strncmp(*argv, "o", p - *argv) || 19733354Sbostic !strncmp(*argv, "old", p - *argv)) { 19833354Sbostic list3[0] = list1[3]; 19933354Sbostic section = list3; 20033354Sbostic goto argtest; 20131703Sbostic } 20231703Sbostic break; 20333354Sbostic case '1': case '2': case '3': case '4': 20433354Sbostic case '5': case '6': case '7': case '8': 20533354Sbostic if (!(section = getsect(*argv))) 20633354Sbostic break; 20733354Sbostic argtest: if (!*++argv) { 20833354Sbostic fprintf(stderr, "man: what do you want from the %s section of the manual?\n", section->msg); 20931936Sbostic exit(1); 21031936Sbostic } 21131703Sbostic } 21233354Sbostic 213*33809Sbostic if (section) 214*33809Sbostic res = manual(section, *argv); 215*33809Sbostic else { 216*33809Sbostic res = manual(list1, *argv); 217*33809Sbostic res += manual(list2, *argv); 218*33809Sbostic } 219*33809Sbostic if (!res && !where) { 22031713Sbostic if (manpath == locpath) 22133354Sbostic if (section) 22233354Sbostic fprintf(stderr, "No entry for %s in the %s section of the local manual.\n", *argv, section->msg); 22333354Sbostic else 22431936Sbostic fprintf(stderr, "No entry for %s in the local manual.\n", *argv); 22533354Sbostic else if (manpath == newpath) 22633354Sbostic if (section) 22733354Sbostic fprintf(stderr, "No entry for %s in the %s section of the new manual.\n", *argv, section->msg); 22831936Sbostic else 22931936Sbostic fprintf(stderr, "No entry for %s in the new manual.\n", *argv); 23033354Sbostic else if (section) 23133354Sbostic fprintf(stderr, "No entry for %s in the %s section of the manual.\n", *argv, section->msg); 23233354Sbostic else 23331713Sbostic fprintf(stderr, "No entry for %s in the manual.\n", *argv); 234*33809Sbostic exit(1); 235*33809Sbostic } 23631703Sbostic } 23731703Sbostic } 23831703Sbostic 23931778Sbostic /* 24031778Sbostic * manual -- 241*33809Sbostic * given a directory list and a file name find a file that 242*33809Sbostic * matches; check ${directory}/${dir}/{file name} and 243*33809Sbostic * ${directory}/${dir}/${machine}/${file name}. 24431778Sbostic */ 24531703Sbostic static 24631703Sbostic manual(section, name) 24733354Sbostic DIR *section; 24833354Sbostic char *name; 24931703Sbostic { 25033354Sbostic register char *beg, *end; 25133354Sbostic register DIR *dp; 252*33809Sbostic register int res; 253*33809Sbostic char fname[MAXPATHLEN + 1], *index(); 25431703Sbostic 255*33809Sbostic for (beg = manpath, res = 0;; beg = end + 1) { 25631703Sbostic if (end = index(beg, ':')) 25731703Sbostic *end = '\0'; 258*33809Sbostic for (dp = section; dp->name; ++dp) { 259*33809Sbostic (void)sprintf(fname, "%s/%s/%s.0", beg, dp->name, name); 260*33809Sbostic if (access(fname, R_OK)) { 261*33809Sbostic (void)sprintf(fname, "%s/%s/%s/%s.0", beg, 262*33809Sbostic dp->name, machine, name); 263*33809Sbostic if (access(fname, R_OK)) 264*33809Sbostic continue; 26531778Sbostic } 266*33809Sbostic if (where) 267*33809Sbostic printf("man: found in %s.\n", fname); 268*33809Sbostic else if (nomore) 269*33809Sbostic cat(fname); 270*33809Sbostic else 271*33809Sbostic add(fname); 272*33809Sbostic res = 1; 273*33809Sbostic } 27431703Sbostic if (!end) 275*33809Sbostic return(res); 27633297Sbostic *end = ':'; 27731703Sbostic } 27831703Sbostic /*NOTREACHED*/ 27931703Sbostic } 28031703Sbostic 28131778Sbostic /* 282*33809Sbostic * cat -- 283*33809Sbostic * cat out the file 28431778Sbostic */ 28531703Sbostic static 286*33809Sbostic cat(fname) 287*33809Sbostic char *fname; 28831703Sbostic { 289*33809Sbostic register int fd, n; 290*33809Sbostic char buf[BUFSIZ]; 29131703Sbostic 292*33809Sbostic if (!(fd = open(fname, O_RDONLY, 0))) { 293*33809Sbostic perror("man: open"); 294*33809Sbostic exit(1); 29531703Sbostic } 296*33809Sbostic while ((n = read(fd, buf, sizeof(buf))) > 0) 297*33809Sbostic if (write(1, buf, n) != n) { 298*33809Sbostic perror("man: write"); 299*33809Sbostic exit(1); 300*33809Sbostic } 301*33809Sbostic if (n == -1) { 302*33809Sbostic perror("man: read"); 303*33809Sbostic exit(1); 304*33809Sbostic } 305*33809Sbostic (void)close(fd); 30631703Sbostic } 30731703Sbostic 30831778Sbostic /* 309*33809Sbostic * add -- 310*33809Sbostic * add a file name to the list for future paging 31131778Sbostic */ 31231703Sbostic static 313*33809Sbostic add(fname) 31433354Sbostic char *fname; 31531703Sbostic { 316*33809Sbostic static u_int buflen; 317*33809Sbostic static int len; 318*33809Sbostic static char *cp; 319*33809Sbostic int flen; 320*33809Sbostic char *malloc(), *realloc(), *strcpy(); 32131703Sbostic 322*33809Sbostic if (!command) { 323*33809Sbostic if (!(command = malloc(buflen = 1024))) { 324*33809Sbostic fputs("man: out of space.\n", stderr); 32531703Sbostic exit(1); 32631703Sbostic } 327*33809Sbostic len = strlen(strcpy(command, pager)); 328*33809Sbostic cp = command + len; 329*33809Sbostic } 330*33809Sbostic flen = strlen(fname); 331*33809Sbostic if (len + flen + 2 > buflen) { /* +2 == space, EOS */ 332*33809Sbostic if (!(command = realloc(command, buflen += 1024))) { 333*33809Sbostic fputs("man: out of space.\n", stderr); 33431703Sbostic exit(1); 33531703Sbostic } 336*33809Sbostic cp = command + len; 33731703Sbostic } 338*33809Sbostic *cp++ = ' '; 339*33809Sbostic len += flen + 1; /* +1 = space */ 340*33809Sbostic (void)strcpy(cp, fname); 341*33809Sbostic cp += flen; 34231703Sbostic } 34331703Sbostic 34431778Sbostic /* 34533354Sbostic * getsect -- 34633354Sbostic * return a point to the section structure for a particular suffix 34731778Sbostic */ 34833354Sbostic static DIR * 34933354Sbostic getsect(s) 35033354Sbostic char *s; 35131703Sbostic { 35233354Sbostic switch(*s++) { 35333354Sbostic case '1': 35433354Sbostic if (!*s) 35533354Sbostic return(list1); 35633354Sbostic break; 35733354Sbostic case '2': 35833354Sbostic if (!*s) { 35933354Sbostic list3[0] = list2[0]; 36033354Sbostic return(list3); 36133354Sbostic } 36233354Sbostic break; 36333354Sbostic /* sect. 3 requests are for either section 3, or section 3[fF]. */ 36433354Sbostic case '3': 36533354Sbostic if (!*s) { 36633354Sbostic list3[0] = list2[1]; 36733354Sbostic return(list3); 36833354Sbostic } 36933354Sbostic else if ((*s == 'f' || *s == 'F') && !*++s) { 37033354Sbostic list3[0] = list2[5]; 37133354Sbostic return(list3); 37233354Sbostic } 37333354Sbostic break; 37433354Sbostic case '4': 37533354Sbostic if (!*s) { 37633354Sbostic list3[0] = list2[2]; 37733354Sbostic return(list3); 37833354Sbostic } 37933354Sbostic break; 38033354Sbostic case '5': 38133354Sbostic if (!*s) { 38233354Sbostic list3[0] = list2[3]; 38333354Sbostic return(list3); 38433354Sbostic } 38533354Sbostic break; 38633354Sbostic case '6': 38733354Sbostic if (!*s) { 38833354Sbostic list3[0] = list1[2]; 38933354Sbostic return(list3); 39033354Sbostic } 39133354Sbostic break; 39233354Sbostic case '7': 39333354Sbostic if (!*s) { 39433354Sbostic list3[0] = list2[4]; 39533354Sbostic return(list3); 39633354Sbostic } 39733354Sbostic break; 39833354Sbostic case '8': 39933354Sbostic if (!*s) { 40033354Sbostic list3[0] = list1[1]; 40133354Sbostic return(list3); 40233354Sbostic } 40333354Sbostic } 40433354Sbostic return((DIR *)NULL); 40531703Sbostic } 406