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*32614Sbostic static char sccsid[] = "@(#)man.c 5.9 (Berkeley) 11/14/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" 2231936Sbostic #define DEF_PATH "/usr/man:/usr/new/man:/usr/local/man" 2331936Sbostic 2431713Sbostic #define LOCAL_PATH "/usr/local/man" 2531713Sbostic #define LOCAL_NAME "local" 2631936Sbostic 2731936Sbostic #define NEW_PATH "/usr/new/man" 2831936Sbostic #define NEW_NAME "new" 2931936Sbostic 3031713Sbostic #define NO 0 3131713Sbostic #define YES 1 3231703Sbostic 3331703Sbostic #define NO_SECTION 0 3431713Sbostic #define S_THREEF 9 3531936Sbostic #define S_OLD 10 3631713Sbostic 3731778Sbostic /* this array maps a character (ex: '4') to an offset in stanlist */ 3831713Sbostic #define secno(x) (seclist[(int)(x - '0')]) 3931713Sbostic static int seclist[] = { -1, 1, 4, 5, 6, 7, 3, 8, 2, -1, -1 }; 4031713Sbostic 4131713Sbostic /* sub directory list, ordered for searching */ 4231778Sbostic typedef struct { 4331713Sbostic char *name, 4431713Sbostic *msg; 4531713Sbostic } DIR; 4631713Sbostic 4731778Sbostic DIR stanlist[] = { /* standard sub-directory list */ 4831713Sbostic "notused", "", "cat1", "1st", "cat8", "8th", 4931713Sbostic "cat6", "6th", "cat2", "2nd", "cat3", "3rd", 5031713Sbostic "cat4", "4th", "cat5", "5th", "cat7", "7th", 5131936Sbostic "cat3f", "3rd (F)", "cat.old", "old", NULL, NULL, 5231778Sbostic }, sec1list[] = { /* section one list */ 5331778Sbostic "notused", "", "cat1", "1st", "cat8", "8th", 5431936Sbostic "cat6", "6th", "cat.old", "old", NULL, NULL, 5531703Sbostic }; 5631703Sbostic 5731778Sbostic static DIR *dirlist; /* list of directories to search */ 5832565Sbostic static int nomore, /* copy file to stdout */ 5932565Sbostic where; /* just tell me where */ 6031713Sbostic static char *defpath, /* default search path */ 6131713Sbostic *locpath, /* local search path */ 6231713Sbostic *machine, /* machine type */ 6331713Sbostic *manpath, /* current search path */ 6431936Sbostic *newpath, /* new search path */ 6531713Sbostic *pager; /* requested pager */ 6631703Sbostic 6731703Sbostic main(argc, argv) 6831703Sbostic int argc; 6931713Sbostic register char **argv; 7031703Sbostic { 7131703Sbostic int section; 7231703Sbostic char **arg_start, **arg, 7331779Sbostic *getenv(), *malloc(); 7431703Sbostic 7531703Sbostic arg_start = argv; 7631703Sbostic for (--argc, ++argv; argc && (*argv)[0] == '-'; --argc, ++argv) 7731703Sbostic switch((*argv)[1]) { 7831703Sbostic case 0: /* just write to stdout */ 7931703Sbostic nomore = YES; 8031703Sbostic break; 8131703Sbostic case 'M': 8231703Sbostic case 'P': /* backward compatibility */ 8331703Sbostic if ((*argv)[2]) 8431906Sbostic defpath = *argv + 2; 8531703Sbostic else { 8631703Sbostic if (argc < 2) { 8731703Sbostic fprintf(stderr, "%s: missing path\n", *argv); 8831703Sbostic exit(1); 8931703Sbostic } 9031703Sbostic --argc; 9131906Sbostic defpath = *++argv; 9231703Sbostic } 9331703Sbostic break; 9431703Sbostic /* 9531703Sbostic * "man -f" and "man -k" are undocumented ways of calling 9631703Sbostic * whatis(1) and apropos(1). Just strip out the flag 9731703Sbostic * argument and jump. 9831703Sbostic */ 9931703Sbostic case 'f': 10031703Sbostic for (arg = argv; arg[0] = arg[1]; ++arg); 10131703Sbostic *arg_start = "whatis"; 10231703Sbostic execvp(*arg_start, arg_start); 10331703Sbostic fputs("whatis: Command not found.\n", stderr); 10431703Sbostic exit(1); 10531703Sbostic case 'k': 10631703Sbostic for (arg = argv; *arg = arg[1]; ++arg); 10731703Sbostic *arg_start = "apropos"; 10831703Sbostic execvp(*arg_start, arg_start); 10931703Sbostic fputs("apropos: Command not found.\n", stderr); 11031703Sbostic exit(1); 11132565Sbostic case 'w': 11232565Sbostic /* 11332565Sbostic * Deliberately undocumented; really only useful when 11432565Sbostic * you're moving man pages around. Not worth adding. 11532565Sbostic */ 11632565Sbostic where = YES; 11732565Sbostic break; 11831703Sbostic case '?': 11931703Sbostic default: 120*32614Sbostic fprintf(stderr, "man: illegal option -- %c\n", (*argv)[1]); 12131703Sbostic usage(); 12231703Sbostic } 12331703Sbostic if (!argc) 12431703Sbostic usage(); 12531703Sbostic 12631703Sbostic if (!nomore) 12731703Sbostic if (!isatty(1)) 12831703Sbostic nomore = YES; 12931779Sbostic else if (pager = getenv("PAGER")) { 13031779Sbostic register char *C; 13131779Sbostic 13231779Sbostic /* 13331779Sbostic * if the user uses "more", we make it "more -s" 13431779Sbostic * watch out for PAGER = "mypager /usr/ucb/more" 13531779Sbostic */ 13631779Sbostic for (C = pager; *C && !isspace(*C); ++C); 13731779Sbostic for (; C > pager && *C != '/'; --C); 13831779Sbostic if (C != pager) 13931779Sbostic ++C; 14031779Sbostic /* make sure it's "more", not "morex" */ 14131779Sbostic if (!strncmp(C, "more", 4) && (!C[4] || isspace(C[4]))) { 14231779Sbostic C += 4; 14331779Sbostic /* 14431779Sbostic * sizeof is 1 more than # of chars, so, 14531779Sbostic * allocate for the rest of the PAGER 14631779Sbostic * environment variable, a space, and the EOS. 14731779Sbostic */ 14831779Sbostic if (!(pager = malloc((u_int)(strlen(C) + sizeof(DEF_PAGER) + 1)))) { 14931779Sbostic fputs("man: out of space.\n", stderr); 15031779Sbostic exit(1); 15131779Sbostic } 15231779Sbostic (void)sprintf(pager, "%s %s", DEF_PAGER, C); 15331779Sbostic } 15431779Sbostic } 15531779Sbostic else 15631703Sbostic pager = DEF_PAGER; 15731703Sbostic if (!(machine = getenv("MACHINE"))) 15831703Sbostic machine = MACHINE; 15931713Sbostic if (!defpath && !(defpath = getenv("MANPATH"))) 16031713Sbostic defpath = DEF_PATH; 16131713Sbostic locpath = LOCAL_PATH; 16231936Sbostic newpath = NEW_PATH; 16331713Sbostic for (; *defpath && *defpath == ':'; ++defpath); 16431703Sbostic 16531778Sbostic /* Gentlemen... start your kludges! */ 16631703Sbostic for (; *argv; ++argv) { 16731703Sbostic section = NO_SECTION; 16831906Sbostic manpath = defpath; 16931703Sbostic switch(**argv) { 17031778Sbostic /* 17131936Sbostic * Section 1 requests are really for section 1, 6, 8, in the 17231936Sbostic * standard, local and new directories and section old. Since 17331936Sbostic * old isn't broken up into a directory of cat[1-8], we just 17431936Sbostic * treat it like a subdirectory of the others. 17531778Sbostic */ 17631936Sbostic case '1': case '2': case '4': case '5': case '6': 17731713Sbostic case '7': case '8': 17831703Sbostic if (!(*argv)[1]) { 17931713Sbostic section = secno((*argv)[0]); 18031713Sbostic goto numtest; 18131713Sbostic } 18231713Sbostic break; 18331778Sbostic 18431778Sbostic /* sect. 3 requests are for either section 3, or section 3F. */ 18531713Sbostic case '3': 18631713Sbostic if (!(*argv)[1]) { /* "3" */ 18731713Sbostic section = secno((*argv)[0]); 18831713Sbostic numtest: if (!*++argv) { 18931778Sbostic fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[section].msg); 19031713Sbostic exit(1); 19131713Sbostic } 19231713Sbostic } /* "3[fF]" */ 19331936Sbostic else if (((*argv)[1] == 'f' || (*argv)[1] == 'F') && !(*argv)[2]) { 19431713Sbostic section = S_THREEF; 19531703Sbostic if (!*++argv) { 19631778Sbostic fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[S_THREEF].msg); 19731703Sbostic exit(1); 19831703Sbostic } 19931703Sbostic } 20031703Sbostic break; 20131778Sbostic /* 20231936Sbostic * Requests for the new or local sections can have subsection 20331936Sbostic * numbers appended to them, i.e. "local3" is really 20431936Sbostic * local/cat3. 20531778Sbostic */ 20631713Sbostic case 'l': /* local */ 20731713Sbostic if (!(*argv)[1]) /* "l" */ 20831713Sbostic section = NO_SECTION; /* "l2" */ 20931713Sbostic else if (isdigit((*argv)[1]) && !(*argv)[2]) 21031713Sbostic section = secno((*argv)[1]); 21131713Sbostic else { 21231713Sbostic int lex; 21331713Sbostic lex = strcmp(LOCAL_NAME, *argv); 21431713Sbostic if (!lex) /* "local" */ 21531713Sbostic section = NO_SECTION; /* "local2" */ 21631713Sbostic else if (lex < 0 && isdigit((*argv)[sizeof(LOCAL_NAME) - 1]) && !(*argv)[sizeof(LOCAL_NAME)]) 21731713Sbostic section = secno((*argv)[sizeof(LOCAL_NAME) - 1]); 21831713Sbostic else 21931713Sbostic break; 22031703Sbostic } 22131713Sbostic if (!*++argv) { 22231713Sbostic fputs("man: what do you want from the local section of the manual?\n", stderr); 22331713Sbostic exit(1); 22431713Sbostic } 22531713Sbostic manpath = locpath; 22631703Sbostic break; 22731713Sbostic case 'n': /* new */ 22831936Sbostic if (!(*argv)[1]) /* "n" */ 22931936Sbostic section = NO_SECTION; /* "n2" */ 23031936Sbostic else if (isdigit((*argv)[1]) && !(*argv)[2]) 23131936Sbostic section = secno((*argv)[1]); 23231936Sbostic else { 23331936Sbostic int lex; 23431936Sbostic lex = strcmp(NEW_NAME, *argv); 23531936Sbostic if (!lex) /* "new" */ 23631936Sbostic section = NO_SECTION; /* "new2" */ 23731936Sbostic else if (lex < 0 && isdigit((*argv)[sizeof(NEW_NAME) - 1]) && !(*argv)[sizeof(NEW_NAME)]) 23831936Sbostic section = secno((*argv)[sizeof(NEW_NAME) - 1]); 23931936Sbostic else 24031936Sbostic break; 24131703Sbostic } 24231936Sbostic if (!*++argv) { 24331936Sbostic fputs("man: what do you want from the new section of the manual?\n", stderr); 24431936Sbostic exit(1); 24531936Sbostic } 24631936Sbostic manpath = newpath; 24731703Sbostic break; 24831713Sbostic case 'o': /* old */ 24931778Sbostic if (!(*argv)[1] || !strcmp(*argv, stanlist[S_OLD].msg)) { 25031713Sbostic section = S_OLD; 25131936Sbostic if (!*++argv) { 25231778Sbostic fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[section].msg); 25331703Sbostic exit(1); 25431703Sbostic } 25531703Sbostic } 25631703Sbostic break; 25731703Sbostic } 25831936Sbostic if (section == 1) { 25931936Sbostic dirlist = sec1list; 26031936Sbostic section = NO_SECTION; 26131936Sbostic } 26231936Sbostic else 26331936Sbostic dirlist = stanlist; 26431778Sbostic /* 26531778Sbostic * This is really silly, but I wanted to put out rational 26631778Sbostic * errors, not just "I couldn't find it." This if statement 26731778Sbostic * knows an awful lot about what gets assigned to what in 26831778Sbostic * the switch statement we just passed through. Sorry. 26931778Sbostic */ 27032565Sbostic if (!manual(section, *argv) && !where) 27131713Sbostic if (manpath == locpath) 27231936Sbostic if (section == NO_SECTION) 27331936Sbostic fprintf(stderr, "No entry for %s in the local manual.\n", *argv); 27431936Sbostic else 27531936Sbostic fprintf(stderr, "No entry for %s in the %s section of the local manual.\n", *argv, stanlist[section].msg); 27631936Sbostic else if (manpath == newpath) 27731936Sbostic if (section == NO_SECTION) 27831936Sbostic fprintf(stderr, "No entry for %s in the new manual.\n", *argv); 27931936Sbostic else 28031936Sbostic fprintf(stderr, "No entry for %s in the %s section of the new manual.\n", *argv, stanlist[section].msg); 28131778Sbostic else if (dirlist == sec1list) 28231778Sbostic fprintf(stderr, "No entry for %s in the 1st section of the manual.\n", *argv); 28331713Sbostic else if (section == NO_SECTION) 28431713Sbostic fprintf(stderr, "No entry for %s in the manual.\n", *argv); 28531703Sbostic else 28631778Sbostic fprintf(stderr, "No entry for %s in the %s section of the manual.\n", *argv, stanlist[section].msg); 28731703Sbostic } 28831703Sbostic exit(0); 28931703Sbostic } 29031703Sbostic 29131778Sbostic /* 29231778Sbostic * manual -- 29331778Sbostic * given a section number and a file name go through the directory 29431778Sbostic * list and find a file that matches. 29531778Sbostic */ 29631703Sbostic static 29731703Sbostic manual(section, name) 29831703Sbostic int section; 29931703Sbostic char *name; 30031703Sbostic { 30131713Sbostic register DIR *dir; 30231713Sbostic register char *beg, *end; 30331703Sbostic char *index(); 30431703Sbostic 30531703Sbostic for (beg = manpath;; beg = end + 1) { 30631703Sbostic if (end = index(beg, ':')) 30731703Sbostic *end = '\0'; 30831778Sbostic if (section == NO_SECTION) 30931778Sbostic for (dir = dirlist; (++dir)->name;) { 31031713Sbostic if (find(beg, dir->name, name)) 31131703Sbostic return(YES); 31231778Sbostic } 31331778Sbostic else if (find(beg, stanlist[section].name, name)) 31431703Sbostic return(YES); 31531703Sbostic if (!end) 31631703Sbostic return(NO); 31731703Sbostic } 31831703Sbostic /*NOTREACHED*/ 31931703Sbostic } 32031703Sbostic 32131778Sbostic /* 32231778Sbostic * find -- 32331778Sbostic * given a directory path, a sub-directory and a file name, 32431778Sbostic * see if a file exists in ${directory}/${dir}/{file name} 32531778Sbostic * or in ${directory}/${dir}/${machine}/${file name}. 32631778Sbostic */ 32731703Sbostic static 32831703Sbostic find(beg, dir, name) 32931703Sbostic char *beg, *dir, *name; 33031703Sbostic { 33131703Sbostic char fname[MAXPATHLEN + 1]; 33231703Sbostic 33331713Sbostic (void)sprintf(fname, "%s/%s/%s.0", beg, dir, name); 33432565Sbostic if (access(fname, R_OK)) { 33532565Sbostic (void)sprintf(fname, "%s/%s/%s/%s.0", beg, dir, machine, name); 33632565Sbostic if (access(fname, R_OK)) 33732565Sbostic return(NO); 33831703Sbostic } 33932565Sbostic if (where) 34032565Sbostic printf("man: found in %s.\n", fname); 34132565Sbostic else 34231703Sbostic show(fname); 34332565Sbostic return(!where); 34431703Sbostic } 34531703Sbostic 34631778Sbostic /* 34731778Sbostic * show -- 34831778Sbostic * display the file 34931778Sbostic */ 35031703Sbostic static 35131703Sbostic show(fname) 35231703Sbostic char *fname; 35331703Sbostic { 35431703Sbostic register int fd, n; 35531703Sbostic char buf[BUFSIZ]; 35631703Sbostic 35731703Sbostic if (nomore) { 35831703Sbostic if (!(fd = open(fname, O_RDONLY, 0))) { 35931703Sbostic perror("man: open"); 36031703Sbostic exit(1); 36131703Sbostic } 36231703Sbostic while ((n = read(fd, buf, sizeof(buf))) > 0) 36331703Sbostic if (write(1, buf, n) != n) { 36431703Sbostic perror("man: write"); 36531703Sbostic exit(1); 36631703Sbostic } 36731703Sbostic if (n == -1) { 36831703Sbostic perror("man: read"); 36931703Sbostic exit(1); 37031703Sbostic } 37131703Sbostic (void)close(fd); 37231703Sbostic } 37331703Sbostic else { 37431703Sbostic /* 37531703Sbostic * use system(2) in case someone's pager is 37631703Sbostic * "command arg1 arg2" 37731703Sbostic */ 37831703Sbostic (void)sprintf(buf, "%s %s", pager, fname); 37931703Sbostic (void)system(buf); 38031703Sbostic } 38131703Sbostic } 38231703Sbostic 38331778Sbostic /* 38431778Sbostic * usage -- 38531778Sbostic * print out a usage message and die 38631778Sbostic */ 38731703Sbostic static 38831703Sbostic usage() 38931703Sbostic { 39031703Sbostic fputs("usage: man [-] [-M path] [section] title ...\n", stderr); 39131703Sbostic exit(1); 39231703Sbostic } 393