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*32565Sbostic static char sccsid[] = "@(#)man.c 5.8 (Berkeley) 10/29/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 */ 58*32565Sbostic static int nomore, /* copy file to stdout */ 59*32565Sbostic 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); 111*32565Sbostic case 'w': 112*32565Sbostic /* 113*32565Sbostic * Deliberately undocumented; really only useful when 114*32565Sbostic * you're moving man pages around. Not worth adding. 115*32565Sbostic */ 116*32565Sbostic where = YES; 117*32565Sbostic break; 11831703Sbostic case '?': 11931703Sbostic default: 12031703Sbostic usage(); 12131703Sbostic } 12231703Sbostic if (!argc) 12331703Sbostic usage(); 12431703Sbostic 12531703Sbostic if (!nomore) 12631703Sbostic if (!isatty(1)) 12731703Sbostic nomore = YES; 12831779Sbostic else if (pager = getenv("PAGER")) { 12931779Sbostic register char *C; 13031779Sbostic 13131779Sbostic /* 13231779Sbostic * if the user uses "more", we make it "more -s" 13331779Sbostic * watch out for PAGER = "mypager /usr/ucb/more" 13431779Sbostic */ 13531779Sbostic for (C = pager; *C && !isspace(*C); ++C); 13631779Sbostic for (; C > pager && *C != '/'; --C); 13731779Sbostic if (C != pager) 13831779Sbostic ++C; 13931779Sbostic /* make sure it's "more", not "morex" */ 14031779Sbostic if (!strncmp(C, "more", 4) && (!C[4] || isspace(C[4]))) { 14131779Sbostic C += 4; 14231779Sbostic /* 14331779Sbostic * sizeof is 1 more than # of chars, so, 14431779Sbostic * allocate for the rest of the PAGER 14531779Sbostic * environment variable, a space, and the EOS. 14631779Sbostic */ 14731779Sbostic if (!(pager = malloc((u_int)(strlen(C) + sizeof(DEF_PAGER) + 1)))) { 14831779Sbostic fputs("man: out of space.\n", stderr); 14931779Sbostic exit(1); 15031779Sbostic } 15131779Sbostic (void)sprintf(pager, "%s %s", DEF_PAGER, C); 15231779Sbostic } 15331779Sbostic } 15431779Sbostic else 15531703Sbostic pager = DEF_PAGER; 15631703Sbostic if (!(machine = getenv("MACHINE"))) 15731703Sbostic machine = MACHINE; 15831713Sbostic if (!defpath && !(defpath = getenv("MANPATH"))) 15931713Sbostic defpath = DEF_PATH; 16031713Sbostic locpath = LOCAL_PATH; 16131936Sbostic newpath = NEW_PATH; 16231713Sbostic for (; *defpath && *defpath == ':'; ++defpath); 16331703Sbostic 16431778Sbostic /* Gentlemen... start your kludges! */ 16531703Sbostic for (; *argv; ++argv) { 16631703Sbostic section = NO_SECTION; 16731906Sbostic manpath = defpath; 16831703Sbostic switch(**argv) { 16931778Sbostic /* 17031936Sbostic * Section 1 requests are really for section 1, 6, 8, in the 17131936Sbostic * standard, local and new directories and section old. Since 17231936Sbostic * old isn't broken up into a directory of cat[1-8], we just 17331936Sbostic * treat it like a subdirectory of the others. 17431778Sbostic */ 17531936Sbostic case '1': case '2': case '4': case '5': case '6': 17631713Sbostic case '7': case '8': 17731703Sbostic if (!(*argv)[1]) { 17831713Sbostic section = secno((*argv)[0]); 17931713Sbostic goto numtest; 18031713Sbostic } 18131713Sbostic break; 18231778Sbostic 18331778Sbostic /* sect. 3 requests are for either section 3, or section 3F. */ 18431713Sbostic case '3': 18531713Sbostic if (!(*argv)[1]) { /* "3" */ 18631713Sbostic section = secno((*argv)[0]); 18731713Sbostic numtest: if (!*++argv) { 18831778Sbostic fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[section].msg); 18931713Sbostic exit(1); 19031713Sbostic } 19131713Sbostic } /* "3[fF]" */ 19231936Sbostic else if (((*argv)[1] == 'f' || (*argv)[1] == 'F') && !(*argv)[2]) { 19331713Sbostic section = S_THREEF; 19431703Sbostic if (!*++argv) { 19531778Sbostic fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[S_THREEF].msg); 19631703Sbostic exit(1); 19731703Sbostic } 19831703Sbostic } 19931703Sbostic break; 20031778Sbostic /* 20131936Sbostic * Requests for the new or local sections can have subsection 20231936Sbostic * numbers appended to them, i.e. "local3" is really 20331936Sbostic * local/cat3. 20431778Sbostic */ 20531713Sbostic case 'l': /* local */ 20631713Sbostic if (!(*argv)[1]) /* "l" */ 20731713Sbostic section = NO_SECTION; /* "l2" */ 20831713Sbostic else if (isdigit((*argv)[1]) && !(*argv)[2]) 20931713Sbostic section = secno((*argv)[1]); 21031713Sbostic else { 21131713Sbostic int lex; 21231713Sbostic lex = strcmp(LOCAL_NAME, *argv); 21331713Sbostic if (!lex) /* "local" */ 21431713Sbostic section = NO_SECTION; /* "local2" */ 21531713Sbostic else if (lex < 0 && isdigit((*argv)[sizeof(LOCAL_NAME) - 1]) && !(*argv)[sizeof(LOCAL_NAME)]) 21631713Sbostic section = secno((*argv)[sizeof(LOCAL_NAME) - 1]); 21731713Sbostic else 21831713Sbostic break; 21931703Sbostic } 22031713Sbostic if (!*++argv) { 22131713Sbostic fputs("man: what do you want from the local section of the manual?\n", stderr); 22231713Sbostic exit(1); 22331713Sbostic } 22431713Sbostic manpath = locpath; 22531703Sbostic break; 22631713Sbostic case 'n': /* new */ 22731936Sbostic if (!(*argv)[1]) /* "n" */ 22831936Sbostic section = NO_SECTION; /* "n2" */ 22931936Sbostic else if (isdigit((*argv)[1]) && !(*argv)[2]) 23031936Sbostic section = secno((*argv)[1]); 23131936Sbostic else { 23231936Sbostic int lex; 23331936Sbostic lex = strcmp(NEW_NAME, *argv); 23431936Sbostic if (!lex) /* "new" */ 23531936Sbostic section = NO_SECTION; /* "new2" */ 23631936Sbostic else if (lex < 0 && isdigit((*argv)[sizeof(NEW_NAME) - 1]) && !(*argv)[sizeof(NEW_NAME)]) 23731936Sbostic section = secno((*argv)[sizeof(NEW_NAME) - 1]); 23831936Sbostic else 23931936Sbostic break; 24031703Sbostic } 24131936Sbostic if (!*++argv) { 24231936Sbostic fputs("man: what do you want from the new section of the manual?\n", stderr); 24331936Sbostic exit(1); 24431936Sbostic } 24531936Sbostic manpath = newpath; 24631703Sbostic break; 24731713Sbostic case 'o': /* old */ 24831778Sbostic if (!(*argv)[1] || !strcmp(*argv, stanlist[S_OLD].msg)) { 24931713Sbostic section = S_OLD; 25031936Sbostic if (!*++argv) { 25131778Sbostic fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[section].msg); 25231703Sbostic exit(1); 25331703Sbostic } 25431703Sbostic } 25531703Sbostic break; 25631703Sbostic } 25731936Sbostic if (section == 1) { 25831936Sbostic dirlist = sec1list; 25931936Sbostic section = NO_SECTION; 26031936Sbostic } 26131936Sbostic else 26231936Sbostic dirlist = stanlist; 26331778Sbostic /* 26431778Sbostic * This is really silly, but I wanted to put out rational 26531778Sbostic * errors, not just "I couldn't find it." This if statement 26631778Sbostic * knows an awful lot about what gets assigned to what in 26731778Sbostic * the switch statement we just passed through. Sorry. 26831778Sbostic */ 269*32565Sbostic if (!manual(section, *argv) && !where) 27031713Sbostic if (manpath == locpath) 27131936Sbostic if (section == NO_SECTION) 27231936Sbostic fprintf(stderr, "No entry for %s in the local manual.\n", *argv); 27331936Sbostic else 27431936Sbostic fprintf(stderr, "No entry for %s in the %s section of the local manual.\n", *argv, stanlist[section].msg); 27531936Sbostic else if (manpath == newpath) 27631936Sbostic if (section == NO_SECTION) 27731936Sbostic fprintf(stderr, "No entry for %s in the new manual.\n", *argv); 27831936Sbostic else 27931936Sbostic fprintf(stderr, "No entry for %s in the %s section of the new manual.\n", *argv, stanlist[section].msg); 28031778Sbostic else if (dirlist == sec1list) 28131778Sbostic fprintf(stderr, "No entry for %s in the 1st section of the manual.\n", *argv); 28231713Sbostic else if (section == NO_SECTION) 28331713Sbostic fprintf(stderr, "No entry for %s in the manual.\n", *argv); 28431703Sbostic else 28531778Sbostic fprintf(stderr, "No entry for %s in the %s section of the manual.\n", *argv, stanlist[section].msg); 28631703Sbostic } 28731703Sbostic exit(0); 28831703Sbostic } 28931703Sbostic 29031778Sbostic /* 29131778Sbostic * manual -- 29231778Sbostic * given a section number and a file name go through the directory 29331778Sbostic * list and find a file that matches. 29431778Sbostic */ 29531703Sbostic static 29631703Sbostic manual(section, name) 29731703Sbostic int section; 29831703Sbostic char *name; 29931703Sbostic { 30031713Sbostic register DIR *dir; 30131713Sbostic register char *beg, *end; 30231703Sbostic char *index(); 30331703Sbostic 30431703Sbostic for (beg = manpath;; beg = end + 1) { 30531703Sbostic if (end = index(beg, ':')) 30631703Sbostic *end = '\0'; 30731778Sbostic if (section == NO_SECTION) 30831778Sbostic for (dir = dirlist; (++dir)->name;) { 30931713Sbostic if (find(beg, dir->name, name)) 31031703Sbostic return(YES); 31131778Sbostic } 31231778Sbostic else if (find(beg, stanlist[section].name, name)) 31331703Sbostic return(YES); 31431703Sbostic if (!end) 31531703Sbostic return(NO); 31631703Sbostic } 31731703Sbostic /*NOTREACHED*/ 31831703Sbostic } 31931703Sbostic 32031778Sbostic /* 32131778Sbostic * find -- 32231778Sbostic * given a directory path, a sub-directory and a file name, 32331778Sbostic * see if a file exists in ${directory}/${dir}/{file name} 32431778Sbostic * or in ${directory}/${dir}/${machine}/${file name}. 32531778Sbostic */ 32631703Sbostic static 32731703Sbostic find(beg, dir, name) 32831703Sbostic char *beg, *dir, *name; 32931703Sbostic { 33031703Sbostic char fname[MAXPATHLEN + 1]; 33131703Sbostic 33231713Sbostic (void)sprintf(fname, "%s/%s/%s.0", beg, dir, name); 333*32565Sbostic if (access(fname, R_OK)) { 334*32565Sbostic (void)sprintf(fname, "%s/%s/%s/%s.0", beg, dir, machine, name); 335*32565Sbostic if (access(fname, R_OK)) 336*32565Sbostic return(NO); 33731703Sbostic } 338*32565Sbostic if (where) 339*32565Sbostic printf("man: found in %s.\n", fname); 340*32565Sbostic else 34131703Sbostic show(fname); 342*32565Sbostic return(!where); 34331703Sbostic } 34431703Sbostic 34531778Sbostic /* 34631778Sbostic * show -- 34731778Sbostic * display the file 34831778Sbostic */ 34931703Sbostic static 35031703Sbostic show(fname) 35131703Sbostic char *fname; 35231703Sbostic { 35331703Sbostic register int fd, n; 35431703Sbostic char buf[BUFSIZ]; 35531703Sbostic 35631703Sbostic if (nomore) { 35731703Sbostic if (!(fd = open(fname, O_RDONLY, 0))) { 35831703Sbostic perror("man: open"); 35931703Sbostic exit(1); 36031703Sbostic } 36131703Sbostic while ((n = read(fd, buf, sizeof(buf))) > 0) 36231703Sbostic if (write(1, buf, n) != n) { 36331703Sbostic perror("man: write"); 36431703Sbostic exit(1); 36531703Sbostic } 36631703Sbostic if (n == -1) { 36731703Sbostic perror("man: read"); 36831703Sbostic exit(1); 36931703Sbostic } 37031703Sbostic (void)close(fd); 37131703Sbostic } 37231703Sbostic else { 37331703Sbostic /* 37431703Sbostic * use system(2) in case someone's pager is 37531703Sbostic * "command arg1 arg2" 37631703Sbostic */ 37731703Sbostic (void)sprintf(buf, "%s %s", pager, fname); 37831703Sbostic (void)system(buf); 37931703Sbostic } 38031703Sbostic } 38131703Sbostic 38231778Sbostic /* 38331778Sbostic * usage -- 38431778Sbostic * print out a usage message and die 38531778Sbostic */ 38631703Sbostic static 38731703Sbostic usage() 38831703Sbostic { 38931703Sbostic fputs("usage: man [-] [-M path] [section] title ...\n", stderr); 39031703Sbostic exit(1); 39131703Sbostic } 392