131703Sbostic /* 231703Sbostic * Copyright (c) 1987 Regents of the University of California. 331703Sbostic * All rights reserved. The Berkeley software License Agreement 431703Sbostic * specifies the terms and conditions for redistribution. 531703Sbostic */ 631703Sbostic 731703Sbostic #ifndef lint 831703Sbostic char copyright[] = 931703Sbostic "@(#) Copyright (c) 1987 Regents of the University of California.\n\ 1031703Sbostic All rights reserved.\n"; 1131703Sbostic #endif not lint 1231703Sbostic 1331703Sbostic #ifndef lint 14*31778Sbostic static char sccsid[] = "@(#)man.c 5.4 (Berkeley) 07/03/87"; 1531703Sbostic #endif not lint 1631703Sbostic 1731703Sbostic #include <sys/param.h> 1831703Sbostic #include <sys/file.h> 1931703Sbostic #include <ctype.h> 2031703Sbostic 2131713Sbostic #define DEF_PAGER "more -s" 2231713Sbostic #define DEF_PATH "/usr/man:/usr/local/man" 2331713Sbostic #define LOCAL_PATH "/usr/local/man" 2431713Sbostic #define LOCAL_NAME "local" 2531713Sbostic #define NO 0 2631713Sbostic #define YES 1 2731703Sbostic 2831703Sbostic #define NO_SECTION 0 2931713Sbostic #define S_THREEF 9 3031713Sbostic #define S_NEW 10 3131713Sbostic #define S_OLD 11 3231713Sbostic 33*31778Sbostic /* this array maps a character (ex: '4') to an offset in stanlist */ 3431713Sbostic #define secno(x) (seclist[(int)(x - '0')]) 3531713Sbostic static int seclist[] = { -1, 1, 4, 5, 6, 7, 3, 8, 2, -1, -1 }; 3631713Sbostic 3731713Sbostic /* sub directory list, ordered for searching */ 38*31778Sbostic typedef struct { 3931713Sbostic char *name, 4031713Sbostic *msg; 4131713Sbostic } DIR; 4231713Sbostic 43*31778Sbostic DIR stanlist[] = { /* standard sub-directory list */ 4431713Sbostic "notused", "", "cat1", "1st", "cat8", "8th", 4531713Sbostic "cat6", "6th", "cat2", "2nd", "cat3", "3rd", 4631713Sbostic "cat4", "4th", "cat5", "5th", "cat7", "7th", 47*31778Sbostic "cat3f", "3rd (F)", "cat.new", "new", "cat.old", "old", 4831713Sbostic NULL, NULL, 49*31778Sbostic }, sec1list[] = { /* section one list */ 50*31778Sbostic "notused", "", "cat1", "1st", "cat8", "8th", 51*31778Sbostic "cat6", "6th", "cat.new", "new", "cat.old", "old", 52*31778Sbostic NULL, NULL, 5331703Sbostic }; 5431703Sbostic 55*31778Sbostic static DIR *dirlist; /* list of directories to search */ 5631713Sbostic static int nomore; /* copy file to stdout */ 5731713Sbostic static char *defpath, /* default search path */ 5831713Sbostic *locpath, /* local search path */ 5931713Sbostic *machine, /* machine type */ 6031713Sbostic *manpath, /* current search path */ 6131713Sbostic *pager; /* requested pager */ 6231703Sbostic 6331703Sbostic main(argc, argv) 6431703Sbostic int argc; 6531713Sbostic register char **argv; 6631703Sbostic { 6731703Sbostic int section; 6831703Sbostic char **arg_start, **arg, 6931703Sbostic *getenv(); 7031703Sbostic 7131703Sbostic arg_start = argv; 7231703Sbostic for (--argc, ++argv; argc && (*argv)[0] == '-'; --argc, ++argv) 7331703Sbostic switch((*argv)[1]) { 7431703Sbostic case 0: /* just write to stdout */ 7531703Sbostic nomore = YES; 7631703Sbostic break; 7731703Sbostic case 'M': 7831703Sbostic case 'P': /* backward compatibility */ 7931703Sbostic if ((*argv)[2]) 8031703Sbostic manpath = *argv + 2; 8131703Sbostic else { 8231703Sbostic if (argc < 2) { 8331703Sbostic fprintf(stderr, "%s: missing path\n", *argv); 8431703Sbostic exit(1); 8531703Sbostic } 8631703Sbostic --argc; 8731703Sbostic manpath = *++argv; 8831703Sbostic } 8931703Sbostic break; 9031703Sbostic /* 9131703Sbostic * "man -f" and "man -k" are undocumented ways of calling 9231703Sbostic * whatis(1) and apropos(1). Just strip out the flag 9331703Sbostic * argument and jump. 9431703Sbostic */ 9531703Sbostic case 'f': 9631703Sbostic for (arg = argv; arg[0] = arg[1]; ++arg); 9731703Sbostic *arg_start = "whatis"; 9831703Sbostic execvp(*arg_start, arg_start); 9931703Sbostic fputs("whatis: Command not found.\n", stderr); 10031703Sbostic exit(1); 10131703Sbostic case 'k': 10231703Sbostic for (arg = argv; *arg = arg[1]; ++arg); 10331703Sbostic *arg_start = "apropos"; 10431703Sbostic execvp(*arg_start, arg_start); 10531703Sbostic fputs("apropos: Command not found.\n", stderr); 10631703Sbostic exit(1); 10731703Sbostic case '?': 10831703Sbostic default: 10931703Sbostic usage(); 11031703Sbostic } 11131703Sbostic if (!argc) 11231703Sbostic usage(); 11331703Sbostic 11431703Sbostic if (!nomore) 11531703Sbostic if (!isatty(1)) 11631703Sbostic nomore = YES; 11731703Sbostic else if (!(pager = getenv("PAGER"))) 11831703Sbostic pager = DEF_PAGER; 11931703Sbostic if (!(machine = getenv("MACHINE"))) 12031703Sbostic machine = MACHINE; 12131713Sbostic if (!defpath && !(defpath = getenv("MANPATH"))) 12231713Sbostic defpath = DEF_PATH; 12331713Sbostic locpath = LOCAL_PATH; 12431713Sbostic for (; *defpath && *defpath == ':'; ++defpath); 12531703Sbostic 126*31778Sbostic /* Gentlemen... start your kludges! */ 12731703Sbostic for (; *argv; ++argv) { 12831703Sbostic section = NO_SECTION; 12931713Sbostic manpath = DEF_PATH; 130*31778Sbostic dirlist = stanlist; 13131703Sbostic switch(**argv) { 132*31778Sbostic /* 133*31778Sbostic * Section 1 requests are really for section 1, 6, 8, new and 134*31778Sbostic * old. Since new and old aren't broken up into a directory 135*31778Sbostic * of cat[1-8], we just pretend that they are a subdirectory 136*31778Sbostic * of /usr/man. Should be fixed -- make new and old full 137*31778Sbostic * structures just like local is, get rid of "sec1list" and 138*31778Sbostic * dirlist. 139*31778Sbostic */ 140*31778Sbostic case '1': 141*31778Sbostic if (!(*argv)[1]) { 142*31778Sbostic dirlist = sec1list; 143*31778Sbostic goto numtest; 144*31778Sbostic } 145*31778Sbostic break; 146*31778Sbostic 147*31778Sbostic case '2': case '4': case '5': case '6': 14831713Sbostic case '7': case '8': 14931703Sbostic if (!(*argv)[1]) { 15031713Sbostic section = secno((*argv)[0]); 15131713Sbostic goto numtest; 15231713Sbostic } 15331713Sbostic break; 154*31778Sbostic 155*31778Sbostic /* sect. 3 requests are for either section 3, or section 3F. */ 15631713Sbostic case '3': 15731713Sbostic if (!(*argv)[1]) { /* "3" */ 15831713Sbostic section = secno((*argv)[0]); 15931713Sbostic numtest: if (!*++argv) { 160*31778Sbostic fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[section].msg); 16131713Sbostic exit(1); 16231713Sbostic } 16331713Sbostic } /* "3[fF]" */ 16431713Sbostic if (((*argv)[1] == 'f' || (*argv)[1] == 'F') && !(*argv)[2]) { 16531713Sbostic section = S_THREEF; 16631703Sbostic if (!*++argv) { 167*31778Sbostic fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[S_THREEF].msg); 16831703Sbostic exit(1); 16931703Sbostic } 17031703Sbostic } 17131703Sbostic break; 172*31778Sbostic /* 173*31778Sbostic * Requests for the local section can have subsection numbers 174*31778Sbostic * appended to them, i.e. "local3" is really local/cat3. 175*31778Sbostic */ 17631713Sbostic case 'l': /* local */ 17731713Sbostic if (!(*argv)[1]) /* "l" */ 17831713Sbostic section = NO_SECTION; /* "l2" */ 17931713Sbostic else if (isdigit((*argv)[1]) && !(*argv)[2]) 18031713Sbostic section = secno((*argv)[1]); 18131713Sbostic else { 18231713Sbostic int lex; 18331713Sbostic lex = strcmp(LOCAL_NAME, *argv); 18431713Sbostic if (!lex) /* "local" */ 18531713Sbostic section = NO_SECTION; /* "local2" */ 18631713Sbostic else if (lex < 0 && isdigit((*argv)[sizeof(LOCAL_NAME) - 1]) && !(*argv)[sizeof(LOCAL_NAME)]) 18731713Sbostic section = secno((*argv)[sizeof(LOCAL_NAME) - 1]); 18831713Sbostic else 18931713Sbostic break; 19031703Sbostic } 19131713Sbostic if (!*++argv) { 19231713Sbostic fputs("man: what do you want from the local section of the manual?\n", stderr); 19331713Sbostic exit(1); 19431713Sbostic } 19531713Sbostic manpath = locpath; 19631703Sbostic break; 19731713Sbostic case 'n': /* new */ 198*31778Sbostic if (!(*argv)[1] || !strcmp(*argv, stanlist[S_NEW].msg)) { 19931713Sbostic section = S_NEW; 20031713Sbostic goto strtest; 20131703Sbostic } 20231703Sbostic break; 20331713Sbostic case 'o': /* old */ 204*31778Sbostic if (!(*argv)[1] || !strcmp(*argv, stanlist[S_OLD].msg)) { 20531713Sbostic section = S_OLD; 20631713Sbostic strtest: if (!*++argv) { 207*31778Sbostic fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[section].msg); 20831703Sbostic exit(1); 20931703Sbostic } 21031703Sbostic } 21131703Sbostic break; 21231703Sbostic } 213*31778Sbostic /* 214*31778Sbostic * This is really silly, but I wanted to put out rational 215*31778Sbostic * errors, not just "I couldn't find it." This if statement 216*31778Sbostic * knows an awful lot about what gets assigned to what in 217*31778Sbostic * the switch statement we just passed through. Sorry. 218*31778Sbostic */ 21931703Sbostic if (!manual(section, *argv)) 22031713Sbostic if (manpath == locpath) 221*31778Sbostic fprintf(stderr, "No entry for %s in the %s section of the local manual.\n", *argv, stanlist[section].msg); 222*31778Sbostic else if (dirlist == sec1list) 223*31778Sbostic fprintf(stderr, "No entry for %s in the 1st section of the manual.\n", *argv); 22431713Sbostic else if (section == NO_SECTION) 22531713Sbostic fprintf(stderr, "No entry for %s in the manual.\n", *argv); 22631703Sbostic else 227*31778Sbostic fprintf(stderr, "No entry for %s in the %s section of the manual.\n", *argv, stanlist[section].msg); 22831703Sbostic } 22931703Sbostic exit(0); 23031703Sbostic } 23131703Sbostic 232*31778Sbostic /* 233*31778Sbostic * manual -- 234*31778Sbostic * given a section number and a file name go through the directory 235*31778Sbostic * list and find a file that matches. 236*31778Sbostic */ 23731703Sbostic static 23831703Sbostic manual(section, name) 23931703Sbostic int section; 24031703Sbostic char *name; 24131703Sbostic { 24231713Sbostic register DIR *dir; 24331713Sbostic register char *beg, *end; 24431703Sbostic char *index(); 24531703Sbostic 24631703Sbostic for (beg = manpath;; beg = end + 1) { 24731703Sbostic if (end = index(beg, ':')) 24831703Sbostic *end = '\0'; 249*31778Sbostic if (section == NO_SECTION) 250*31778Sbostic for (dir = dirlist; (++dir)->name;) { 25131713Sbostic if (find(beg, dir->name, name)) 25231703Sbostic return(YES); 253*31778Sbostic } 254*31778Sbostic else if (find(beg, stanlist[section].name, name)) 25531703Sbostic return(YES); 25631703Sbostic if (!end) 25731703Sbostic return(NO); 25831703Sbostic } 25931703Sbostic /*NOTREACHED*/ 26031703Sbostic } 26131703Sbostic 262*31778Sbostic /* 263*31778Sbostic * find -- 264*31778Sbostic * given a directory path, a sub-directory and a file name, 265*31778Sbostic * see if a file exists in ${directory}/${dir}/{file name} 266*31778Sbostic * or in ${directory}/${dir}/${machine}/${file name}. 267*31778Sbostic */ 26831703Sbostic static 26931703Sbostic find(beg, dir, name) 27031703Sbostic char *beg, *dir, *name; 27131703Sbostic { 27231703Sbostic char fname[MAXPATHLEN + 1]; 27331703Sbostic 27431713Sbostic (void)sprintf(fname, "%s/%s/%s.0", beg, dir, name); 27531703Sbostic if (!access(fname, R_OK)) { 27631703Sbostic show(fname); 27731703Sbostic return(YES); 27831703Sbostic } 27931713Sbostic (void)sprintf(fname, "%s/%s/%s/%s.0", beg, dir, machine, name); 28031703Sbostic if (!access(fname, R_OK)) { 28131703Sbostic show(fname); 28231703Sbostic return(YES); 28331703Sbostic } 28431703Sbostic return(NO); 28531703Sbostic } 28631703Sbostic 287*31778Sbostic /* 288*31778Sbostic * show -- 289*31778Sbostic * display the file 290*31778Sbostic */ 29131703Sbostic static 29231703Sbostic show(fname) 29331703Sbostic char *fname; 29431703Sbostic { 29531703Sbostic register int fd, n; 29631703Sbostic char buf[BUFSIZ]; 29731703Sbostic 29831703Sbostic if (nomore) { 29931703Sbostic if (!(fd = open(fname, O_RDONLY, 0))) { 30031703Sbostic perror("man: open"); 30131703Sbostic exit(1); 30231703Sbostic } 30331703Sbostic while ((n = read(fd, buf, sizeof(buf))) > 0) 30431703Sbostic if (write(1, buf, n) != n) { 30531703Sbostic perror("man: write"); 30631703Sbostic exit(1); 30731703Sbostic } 30831703Sbostic if (n == -1) { 30931703Sbostic perror("man: read"); 31031703Sbostic exit(1); 31131703Sbostic } 31231703Sbostic (void)close(fd); 31331703Sbostic } 31431703Sbostic else { 31531703Sbostic /* 31631703Sbostic * use system(2) in case someone's pager is 31731703Sbostic * "command arg1 arg2" 31831703Sbostic */ 31931703Sbostic (void)sprintf(buf, "%s %s", pager, fname); 32031703Sbostic (void)system(buf); 32131703Sbostic } 32231703Sbostic } 32331703Sbostic 324*31778Sbostic /* 325*31778Sbostic * usage -- 326*31778Sbostic * print out a usage message and die 327*31778Sbostic */ 32831703Sbostic static 32931703Sbostic usage() 33031703Sbostic { 33131703Sbostic fputs("usage: man [-] [-M path] [section] title ...\n", stderr); 33231703Sbostic exit(1); 33331703Sbostic } 334