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*31906Sbostic static char sccsid[] = "@(#)man.c 5.6 (Berkeley) 07/20/87"; 1531703Sbostic #endif not lint 1631703Sbostic 1731703Sbostic #include <sys/param.h> 1831703Sbostic #include <sys/file.h> 1931703Sbostic #include <ctype.h> 2031703Sbostic 2131779Sbostic #define DEF_PAGER "/usr/ucb/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 3331778Sbostic /* 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 */ 3831778Sbostic typedef struct { 3931713Sbostic char *name, 4031713Sbostic *msg; 4131713Sbostic } DIR; 4231713Sbostic 4331778Sbostic 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", 4731778Sbostic "cat3f", "3rd (F)", "cat.new", "new", "cat.old", "old", 4831713Sbostic NULL, NULL, 4931778Sbostic }, sec1list[] = { /* section one list */ 5031778Sbostic "notused", "", "cat1", "1st", "cat8", "8th", 5131778Sbostic "cat6", "6th", "cat.new", "new", "cat.old", "old", 5231778Sbostic NULL, NULL, 5331703Sbostic }; 5431703Sbostic 5531778Sbostic 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, 6931779Sbostic *getenv(), *malloc(); 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]) 80*31906Sbostic defpath = *argv + 2; 8131703Sbostic else { 8231703Sbostic if (argc < 2) { 8331703Sbostic fprintf(stderr, "%s: missing path\n", *argv); 8431703Sbostic exit(1); 8531703Sbostic } 8631703Sbostic --argc; 87*31906Sbostic defpath = *++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; 11731779Sbostic else if (pager = getenv("PAGER")) { 11831779Sbostic register char *C; 11931779Sbostic 12031779Sbostic /* 12131779Sbostic * if the user uses "more", we make it "more -s" 12231779Sbostic * watch out for PAGER = "mypager /usr/ucb/more" 12331779Sbostic */ 12431779Sbostic for (C = pager; *C && !isspace(*C); ++C); 12531779Sbostic for (; C > pager && *C != '/'; --C); 12631779Sbostic if (C != pager) 12731779Sbostic ++C; 12831779Sbostic /* make sure it's "more", not "morex" */ 12931779Sbostic if (!strncmp(C, "more", 4) && (!C[4] || isspace(C[4]))) { 13031779Sbostic C += 4; 13131779Sbostic /* 13231779Sbostic * sizeof is 1 more than # of chars, so, 13331779Sbostic * allocate for the rest of the PAGER 13431779Sbostic * environment variable, a space, and the EOS. 13531779Sbostic */ 13631779Sbostic if (!(pager = malloc((u_int)(strlen(C) + sizeof(DEF_PAGER) + 1)))) { 13731779Sbostic fputs("man: out of space.\n", stderr); 13831779Sbostic exit(1); 13931779Sbostic } 14031779Sbostic (void)sprintf(pager, "%s %s", DEF_PAGER, C); 14131779Sbostic } 14231779Sbostic } 14331779Sbostic else 14431703Sbostic pager = DEF_PAGER; 14531703Sbostic if (!(machine = getenv("MACHINE"))) 14631703Sbostic machine = MACHINE; 14731713Sbostic if (!defpath && !(defpath = getenv("MANPATH"))) 14831713Sbostic defpath = DEF_PATH; 14931713Sbostic locpath = LOCAL_PATH; 15031713Sbostic for (; *defpath && *defpath == ':'; ++defpath); 15131703Sbostic 15231778Sbostic /* Gentlemen... start your kludges! */ 15331703Sbostic for (; *argv; ++argv) { 15431703Sbostic section = NO_SECTION; 155*31906Sbostic manpath = defpath; 15631778Sbostic dirlist = stanlist; 15731703Sbostic switch(**argv) { 15831778Sbostic /* 15931778Sbostic * Section 1 requests are really for section 1, 6, 8, new and 16031778Sbostic * old. Since new and old aren't broken up into a directory 16131778Sbostic * of cat[1-8], we just pretend that they are a subdirectory 16231778Sbostic * of /usr/man. Should be fixed -- make new and old full 16331778Sbostic * structures just like local is, get rid of "sec1list" and 16431778Sbostic * dirlist. 16531778Sbostic */ 16631778Sbostic case '1': 16731778Sbostic if (!(*argv)[1]) { 16831778Sbostic dirlist = sec1list; 16931778Sbostic goto numtest; 17031778Sbostic } 17131778Sbostic break; 17231778Sbostic 17331778Sbostic case '2': case '4': case '5': case '6': 17431713Sbostic case '7': case '8': 17531703Sbostic if (!(*argv)[1]) { 17631713Sbostic section = secno((*argv)[0]); 17731713Sbostic goto numtest; 17831713Sbostic } 17931713Sbostic break; 18031778Sbostic 18131778Sbostic /* sect. 3 requests are for either section 3, or section 3F. */ 18231713Sbostic case '3': 18331713Sbostic if (!(*argv)[1]) { /* "3" */ 18431713Sbostic section = secno((*argv)[0]); 18531713Sbostic numtest: if (!*++argv) { 18631778Sbostic fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[section].msg); 18731713Sbostic exit(1); 18831713Sbostic } 18931713Sbostic } /* "3[fF]" */ 19031713Sbostic if (((*argv)[1] == 'f' || (*argv)[1] == 'F') && !(*argv)[2]) { 19131713Sbostic section = S_THREEF; 19231703Sbostic if (!*++argv) { 19331778Sbostic fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[S_THREEF].msg); 19431703Sbostic exit(1); 19531703Sbostic } 19631703Sbostic } 19731703Sbostic break; 19831778Sbostic /* 19931778Sbostic * Requests for the local section can have subsection numbers 20031778Sbostic * appended to them, i.e. "local3" is really local/cat3. 20131778Sbostic */ 20231713Sbostic case 'l': /* local */ 20331713Sbostic if (!(*argv)[1]) /* "l" */ 20431713Sbostic section = NO_SECTION; /* "l2" */ 20531713Sbostic else if (isdigit((*argv)[1]) && !(*argv)[2]) 20631713Sbostic section = secno((*argv)[1]); 20731713Sbostic else { 20831713Sbostic int lex; 20931713Sbostic lex = strcmp(LOCAL_NAME, *argv); 21031713Sbostic if (!lex) /* "local" */ 21131713Sbostic section = NO_SECTION; /* "local2" */ 21231713Sbostic else if (lex < 0 && isdigit((*argv)[sizeof(LOCAL_NAME) - 1]) && !(*argv)[sizeof(LOCAL_NAME)]) 21331713Sbostic section = secno((*argv)[sizeof(LOCAL_NAME) - 1]); 21431713Sbostic else 21531713Sbostic break; 21631703Sbostic } 21731713Sbostic if (!*++argv) { 21831713Sbostic fputs("man: what do you want from the local section of the manual?\n", stderr); 21931713Sbostic exit(1); 22031713Sbostic } 22131713Sbostic manpath = locpath; 22231703Sbostic break; 22331713Sbostic case 'n': /* new */ 22431778Sbostic if (!(*argv)[1] || !strcmp(*argv, stanlist[S_NEW].msg)) { 22531713Sbostic section = S_NEW; 22631713Sbostic goto strtest; 22731703Sbostic } 22831703Sbostic break; 22931713Sbostic case 'o': /* old */ 23031778Sbostic if (!(*argv)[1] || !strcmp(*argv, stanlist[S_OLD].msg)) { 23131713Sbostic section = S_OLD; 23231713Sbostic strtest: if (!*++argv) { 23331778Sbostic fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[section].msg); 23431703Sbostic exit(1); 23531703Sbostic } 23631703Sbostic } 23731703Sbostic break; 23831703Sbostic } 23931778Sbostic /* 24031778Sbostic * This is really silly, but I wanted to put out rational 24131778Sbostic * errors, not just "I couldn't find it." This if statement 24231778Sbostic * knows an awful lot about what gets assigned to what in 24331778Sbostic * the switch statement we just passed through. Sorry. 24431778Sbostic */ 24531703Sbostic if (!manual(section, *argv)) 24631713Sbostic if (manpath == locpath) 24731778Sbostic fprintf(stderr, "No entry for %s in the %s section of the local manual.\n", *argv, stanlist[section].msg); 24831778Sbostic else if (dirlist == sec1list) 24931778Sbostic fprintf(stderr, "No entry for %s in the 1st section of the manual.\n", *argv); 25031713Sbostic else if (section == NO_SECTION) 25131713Sbostic fprintf(stderr, "No entry for %s in the manual.\n", *argv); 25231703Sbostic else 25331778Sbostic fprintf(stderr, "No entry for %s in the %s section of the manual.\n", *argv, stanlist[section].msg); 25431703Sbostic } 25531703Sbostic exit(0); 25631703Sbostic } 25731703Sbostic 25831778Sbostic /* 25931778Sbostic * manual -- 26031778Sbostic * given a section number and a file name go through the directory 26131778Sbostic * list and find a file that matches. 26231778Sbostic */ 26331703Sbostic static 26431703Sbostic manual(section, name) 26531703Sbostic int section; 26631703Sbostic char *name; 26731703Sbostic { 26831713Sbostic register DIR *dir; 26931713Sbostic register char *beg, *end; 27031703Sbostic char *index(); 27131703Sbostic 27231703Sbostic for (beg = manpath;; beg = end + 1) { 27331703Sbostic if (end = index(beg, ':')) 27431703Sbostic *end = '\0'; 27531778Sbostic if (section == NO_SECTION) 27631778Sbostic for (dir = dirlist; (++dir)->name;) { 27731713Sbostic if (find(beg, dir->name, name)) 27831703Sbostic return(YES); 27931778Sbostic } 28031778Sbostic else if (find(beg, stanlist[section].name, name)) 28131703Sbostic return(YES); 28231703Sbostic if (!end) 28331703Sbostic return(NO); 28431703Sbostic } 28531703Sbostic /*NOTREACHED*/ 28631703Sbostic } 28731703Sbostic 28831778Sbostic /* 28931778Sbostic * find -- 29031778Sbostic * given a directory path, a sub-directory and a file name, 29131778Sbostic * see if a file exists in ${directory}/${dir}/{file name} 29231778Sbostic * or in ${directory}/${dir}/${machine}/${file name}. 29331778Sbostic */ 29431703Sbostic static 29531703Sbostic find(beg, dir, name) 29631703Sbostic char *beg, *dir, *name; 29731703Sbostic { 29831703Sbostic char fname[MAXPATHLEN + 1]; 29931703Sbostic 30031713Sbostic (void)sprintf(fname, "%s/%s/%s.0", beg, dir, name); 30131703Sbostic if (!access(fname, R_OK)) { 30231703Sbostic show(fname); 30331703Sbostic return(YES); 30431703Sbostic } 30531713Sbostic (void)sprintf(fname, "%s/%s/%s/%s.0", beg, dir, machine, name); 30631703Sbostic if (!access(fname, R_OK)) { 30731703Sbostic show(fname); 30831703Sbostic return(YES); 30931703Sbostic } 31031703Sbostic return(NO); 31131703Sbostic } 31231703Sbostic 31331778Sbostic /* 31431778Sbostic * show -- 31531778Sbostic * display the file 31631778Sbostic */ 31731703Sbostic static 31831703Sbostic show(fname) 31931703Sbostic char *fname; 32031703Sbostic { 32131703Sbostic register int fd, n; 32231703Sbostic char buf[BUFSIZ]; 32331703Sbostic 32431703Sbostic if (nomore) { 32531703Sbostic if (!(fd = open(fname, O_RDONLY, 0))) { 32631703Sbostic perror("man: open"); 32731703Sbostic exit(1); 32831703Sbostic } 32931703Sbostic while ((n = read(fd, buf, sizeof(buf))) > 0) 33031703Sbostic if (write(1, buf, n) != n) { 33131703Sbostic perror("man: write"); 33231703Sbostic exit(1); 33331703Sbostic } 33431703Sbostic if (n == -1) { 33531703Sbostic perror("man: read"); 33631703Sbostic exit(1); 33731703Sbostic } 33831703Sbostic (void)close(fd); 33931703Sbostic } 34031703Sbostic else { 34131703Sbostic /* 34231703Sbostic * use system(2) in case someone's pager is 34331703Sbostic * "command arg1 arg2" 34431703Sbostic */ 34531703Sbostic (void)sprintf(buf, "%s %s", pager, fname); 34631703Sbostic (void)system(buf); 34731703Sbostic } 34831703Sbostic } 34931703Sbostic 35031778Sbostic /* 35131778Sbostic * usage -- 35231778Sbostic * print out a usage message and die 35331778Sbostic */ 35431703Sbostic static 35531703Sbostic usage() 35631703Sbostic { 35731703Sbostic fputs("usage: man [-] [-M path] [section] title ...\n", stderr); 35831703Sbostic exit(1); 35931703Sbostic } 360