131703Sbostic /* 231703Sbostic * Copyright (c) 1987 Regents of the University of California. 3*33054Sbostic * All rights reserved. 4*33054Sbostic * 5*33054Sbostic * Redistribution and use in source and binary forms are permitted 6*33054Sbostic * provided that this notice is preserved and that due credit is given 7*33054Sbostic * to the University of California at Berkeley. The name of the University 8*33054Sbostic * may not be used to endorse or promote products derived from this 9*33054Sbostic * software without specific prior written permission. This software 10*33054Sbostic * 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"; 17*33054Sbostic #endif /* not lint */ 1831703Sbostic 1931703Sbostic #ifndef lint 20*33054Sbostic static char sccsid[] = "@(#)man.c 5.10 (Berkeley) 12/16/87"; 21*33054Sbostic #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" 2931936Sbostic 3031713Sbostic #define LOCAL_PATH "/usr/local/man" 3131713Sbostic #define LOCAL_NAME "local" 3231936Sbostic 3331936Sbostic #define NEW_PATH "/usr/new/man" 3431936Sbostic #define NEW_NAME "new" 3531936Sbostic 3631713Sbostic #define NO 0 3731713Sbostic #define YES 1 3831703Sbostic 3931703Sbostic #define NO_SECTION 0 4031713Sbostic #define S_THREEF 9 4131936Sbostic #define S_OLD 10 4231713Sbostic 4331778Sbostic /* this array maps a character (ex: '4') to an offset in stanlist */ 4431713Sbostic #define secno(x) (seclist[(int)(x - '0')]) 4531713Sbostic static int seclist[] = { -1, 1, 4, 5, 6, 7, 3, 8, 2, -1, -1 }; 4631713Sbostic 4731713Sbostic /* sub directory list, ordered for searching */ 4831778Sbostic typedef struct { 4931713Sbostic char *name, 5031713Sbostic *msg; 5131713Sbostic } DIR; 5231713Sbostic 5331778Sbostic DIR stanlist[] = { /* standard sub-directory list */ 5431713Sbostic "notused", "", "cat1", "1st", "cat8", "8th", 5531713Sbostic "cat6", "6th", "cat2", "2nd", "cat3", "3rd", 5631713Sbostic "cat4", "4th", "cat5", "5th", "cat7", "7th", 5731936Sbostic "cat3f", "3rd (F)", "cat.old", "old", NULL, NULL, 5831778Sbostic }, sec1list[] = { /* section one list */ 5931778Sbostic "notused", "", "cat1", "1st", "cat8", "8th", 6031936Sbostic "cat6", "6th", "cat.old", "old", NULL, NULL, 6131703Sbostic }; 6231703Sbostic 6331778Sbostic static DIR *dirlist; /* list of directories to search */ 6432565Sbostic static int nomore, /* copy file to stdout */ 6532565Sbostic where; /* just tell me where */ 6631713Sbostic static char *defpath, /* default search path */ 6731713Sbostic *locpath, /* local search path */ 6831713Sbostic *machine, /* machine type */ 6931713Sbostic *manpath, /* current search path */ 7031936Sbostic *newpath, /* new search path */ 7131713Sbostic *pager; /* requested pager */ 7231703Sbostic 7331703Sbostic main(argc, argv) 7431703Sbostic int argc; 7531713Sbostic register char **argv; 7631703Sbostic { 7731703Sbostic int section; 7831703Sbostic char **arg_start, **arg, 7931779Sbostic *getenv(), *malloc(); 8031703Sbostic 8131703Sbostic arg_start = argv; 8231703Sbostic for (--argc, ++argv; argc && (*argv)[0] == '-'; --argc, ++argv) 8331703Sbostic switch((*argv)[1]) { 8431703Sbostic case 0: /* just write to stdout */ 8531703Sbostic nomore = YES; 8631703Sbostic break; 8731703Sbostic case 'M': 8831703Sbostic case 'P': /* backward compatibility */ 8931703Sbostic if ((*argv)[2]) 9031906Sbostic defpath = *argv + 2; 9131703Sbostic else { 9231703Sbostic if (argc < 2) { 9331703Sbostic fprintf(stderr, "%s: missing path\n", *argv); 9431703Sbostic exit(1); 9531703Sbostic } 9631703Sbostic --argc; 9731906Sbostic defpath = *++argv; 9831703Sbostic } 9931703Sbostic break; 10031703Sbostic /* 10131703Sbostic * "man -f" and "man -k" are undocumented ways of calling 10231703Sbostic * whatis(1) and apropos(1). Just strip out the flag 10331703Sbostic * argument and jump. 10431703Sbostic */ 10531703Sbostic case 'f': 10631703Sbostic for (arg = argv; arg[0] = arg[1]; ++arg); 10731703Sbostic *arg_start = "whatis"; 10831703Sbostic execvp(*arg_start, arg_start); 10931703Sbostic fputs("whatis: Command not found.\n", stderr); 11031703Sbostic exit(1); 11131703Sbostic case 'k': 11231703Sbostic for (arg = argv; *arg = arg[1]; ++arg); 11331703Sbostic *arg_start = "apropos"; 11431703Sbostic execvp(*arg_start, arg_start); 11531703Sbostic fputs("apropos: Command not found.\n", stderr); 11631703Sbostic exit(1); 11732565Sbostic case 'w': 11832565Sbostic /* 11932565Sbostic * Deliberately undocumented; really only useful when 12032565Sbostic * you're moving man pages around. Not worth adding. 12132565Sbostic */ 12232565Sbostic where = YES; 12332565Sbostic break; 12431703Sbostic case '?': 12531703Sbostic default: 12632614Sbostic fprintf(stderr, "man: illegal option -- %c\n", (*argv)[1]); 12731703Sbostic usage(); 12831703Sbostic } 12931703Sbostic if (!argc) 13031703Sbostic usage(); 13131703Sbostic 13231703Sbostic if (!nomore) 13331703Sbostic if (!isatty(1)) 13431703Sbostic nomore = YES; 13531779Sbostic else if (pager = getenv("PAGER")) { 13631779Sbostic register char *C; 13731779Sbostic 13831779Sbostic /* 13931779Sbostic * if the user uses "more", we make it "more -s" 14031779Sbostic * watch out for PAGER = "mypager /usr/ucb/more" 14131779Sbostic */ 14231779Sbostic for (C = pager; *C && !isspace(*C); ++C); 14331779Sbostic for (; C > pager && *C != '/'; --C); 14431779Sbostic if (C != pager) 14531779Sbostic ++C; 14631779Sbostic /* make sure it's "more", not "morex" */ 14731779Sbostic if (!strncmp(C, "more", 4) && (!C[4] || isspace(C[4]))) { 14831779Sbostic C += 4; 14931779Sbostic /* 15031779Sbostic * sizeof is 1 more than # of chars, so, 15131779Sbostic * allocate for the rest of the PAGER 15231779Sbostic * environment variable, a space, and the EOS. 15331779Sbostic */ 15431779Sbostic if (!(pager = malloc((u_int)(strlen(C) + sizeof(DEF_PAGER) + 1)))) { 15531779Sbostic fputs("man: out of space.\n", stderr); 15631779Sbostic exit(1); 15731779Sbostic } 15831779Sbostic (void)sprintf(pager, "%s %s", DEF_PAGER, C); 15931779Sbostic } 16031779Sbostic } 16131779Sbostic else 16231703Sbostic pager = DEF_PAGER; 16331703Sbostic if (!(machine = getenv("MACHINE"))) 16431703Sbostic machine = MACHINE; 16531713Sbostic if (!defpath && !(defpath = getenv("MANPATH"))) 16631713Sbostic defpath = DEF_PATH; 16731713Sbostic locpath = LOCAL_PATH; 16831936Sbostic newpath = NEW_PATH; 16931713Sbostic for (; *defpath && *defpath == ':'; ++defpath); 17031703Sbostic 17131778Sbostic /* Gentlemen... start your kludges! */ 17231703Sbostic for (; *argv; ++argv) { 17331703Sbostic section = NO_SECTION; 17431906Sbostic manpath = defpath; 17531703Sbostic switch(**argv) { 17631778Sbostic /* 17731936Sbostic * Section 1 requests are really for section 1, 6, 8, in the 17831936Sbostic * standard, local and new directories and section old. Since 17931936Sbostic * old isn't broken up into a directory of cat[1-8], we just 18031936Sbostic * treat it like a subdirectory of the others. 18131778Sbostic */ 18231936Sbostic case '1': case '2': case '4': case '5': case '6': 18331713Sbostic case '7': case '8': 18431703Sbostic if (!(*argv)[1]) { 18531713Sbostic section = secno((*argv)[0]); 18631713Sbostic goto numtest; 18731713Sbostic } 18831713Sbostic break; 18931778Sbostic 19031778Sbostic /* sect. 3 requests are for either section 3, or section 3F. */ 19131713Sbostic case '3': 19231713Sbostic if (!(*argv)[1]) { /* "3" */ 19331713Sbostic section = secno((*argv)[0]); 19431713Sbostic numtest: if (!*++argv) { 19531778Sbostic fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[section].msg); 19631713Sbostic exit(1); 19731713Sbostic } 19831713Sbostic } /* "3[fF]" */ 19931936Sbostic else if (((*argv)[1] == 'f' || (*argv)[1] == 'F') && !(*argv)[2]) { 20031713Sbostic section = S_THREEF; 20131703Sbostic if (!*++argv) { 20231778Sbostic fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[S_THREEF].msg); 20331703Sbostic exit(1); 20431703Sbostic } 20531703Sbostic } 20631703Sbostic break; 20731778Sbostic /* 20831936Sbostic * Requests for the new or local sections can have subsection 20931936Sbostic * numbers appended to them, i.e. "local3" is really 21031936Sbostic * local/cat3. 21131778Sbostic */ 21231713Sbostic case 'l': /* local */ 21331713Sbostic if (!(*argv)[1]) /* "l" */ 21431713Sbostic section = NO_SECTION; /* "l2" */ 21531713Sbostic else if (isdigit((*argv)[1]) && !(*argv)[2]) 21631713Sbostic section = secno((*argv)[1]); 21731713Sbostic else { 21831713Sbostic int lex; 21931713Sbostic lex = strcmp(LOCAL_NAME, *argv); 22031713Sbostic if (!lex) /* "local" */ 22131713Sbostic section = NO_SECTION; /* "local2" */ 22231713Sbostic else if (lex < 0 && isdigit((*argv)[sizeof(LOCAL_NAME) - 1]) && !(*argv)[sizeof(LOCAL_NAME)]) 22331713Sbostic section = secno((*argv)[sizeof(LOCAL_NAME) - 1]); 22431713Sbostic else 22531713Sbostic break; 22631703Sbostic } 22731713Sbostic if (!*++argv) { 22831713Sbostic fputs("man: what do you want from the local section of the manual?\n", stderr); 22931713Sbostic exit(1); 23031713Sbostic } 23131713Sbostic manpath = locpath; 23231703Sbostic break; 23331713Sbostic case 'n': /* new */ 23431936Sbostic if (!(*argv)[1]) /* "n" */ 23531936Sbostic section = NO_SECTION; /* "n2" */ 23631936Sbostic else if (isdigit((*argv)[1]) && !(*argv)[2]) 23731936Sbostic section = secno((*argv)[1]); 23831936Sbostic else { 23931936Sbostic int lex; 24031936Sbostic lex = strcmp(NEW_NAME, *argv); 24131936Sbostic if (!lex) /* "new" */ 24231936Sbostic section = NO_SECTION; /* "new2" */ 24331936Sbostic else if (lex < 0 && isdigit((*argv)[sizeof(NEW_NAME) - 1]) && !(*argv)[sizeof(NEW_NAME)]) 24431936Sbostic section = secno((*argv)[sizeof(NEW_NAME) - 1]); 24531936Sbostic else 24631936Sbostic break; 24731703Sbostic } 24831936Sbostic if (!*++argv) { 24931936Sbostic fputs("man: what do you want from the new section of the manual?\n", stderr); 25031936Sbostic exit(1); 25131936Sbostic } 25231936Sbostic manpath = newpath; 25331703Sbostic break; 25431713Sbostic case 'o': /* old */ 25531778Sbostic if (!(*argv)[1] || !strcmp(*argv, stanlist[S_OLD].msg)) { 25631713Sbostic section = S_OLD; 25731936Sbostic if (!*++argv) { 25831778Sbostic fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[section].msg); 25931703Sbostic exit(1); 26031703Sbostic } 26131703Sbostic } 26231703Sbostic break; 26331703Sbostic } 26431936Sbostic if (section == 1) { 26531936Sbostic dirlist = sec1list; 26631936Sbostic section = NO_SECTION; 26731936Sbostic } 26831936Sbostic else 26931936Sbostic dirlist = stanlist; 27031778Sbostic /* 27131778Sbostic * This is really silly, but I wanted to put out rational 27231778Sbostic * errors, not just "I couldn't find it." This if statement 27331778Sbostic * knows an awful lot about what gets assigned to what in 27431778Sbostic * the switch statement we just passed through. Sorry. 27531778Sbostic */ 27632565Sbostic if (!manual(section, *argv) && !where) 27731713Sbostic if (manpath == locpath) 27831936Sbostic if (section == NO_SECTION) 27931936Sbostic fprintf(stderr, "No entry for %s in the local manual.\n", *argv); 28031936Sbostic else 28131936Sbostic fprintf(stderr, "No entry for %s in the %s section of the local manual.\n", *argv, stanlist[section].msg); 28231936Sbostic else if (manpath == newpath) 28331936Sbostic if (section == NO_SECTION) 28431936Sbostic fprintf(stderr, "No entry for %s in the new manual.\n", *argv); 28531936Sbostic else 28631936Sbostic fprintf(stderr, "No entry for %s in the %s section of the new manual.\n", *argv, stanlist[section].msg); 28731778Sbostic else if (dirlist == sec1list) 28831778Sbostic fprintf(stderr, "No entry for %s in the 1st section of the manual.\n", *argv); 28931713Sbostic else if (section == NO_SECTION) 29031713Sbostic fprintf(stderr, "No entry for %s in the manual.\n", *argv); 29131703Sbostic else 29231778Sbostic fprintf(stderr, "No entry for %s in the %s section of the manual.\n", *argv, stanlist[section].msg); 29331703Sbostic } 29431703Sbostic exit(0); 29531703Sbostic } 29631703Sbostic 29731778Sbostic /* 29831778Sbostic * manual -- 29931778Sbostic * given a section number and a file name go through the directory 30031778Sbostic * list and find a file that matches. 30131778Sbostic */ 30231703Sbostic static 30331703Sbostic manual(section, name) 30431703Sbostic int section; 30531703Sbostic char *name; 30631703Sbostic { 30731713Sbostic register DIR *dir; 30831713Sbostic register char *beg, *end; 30931703Sbostic char *index(); 31031703Sbostic 31131703Sbostic for (beg = manpath;; beg = end + 1) { 31231703Sbostic if (end = index(beg, ':')) 31331703Sbostic *end = '\0'; 31431778Sbostic if (section == NO_SECTION) 31531778Sbostic for (dir = dirlist; (++dir)->name;) { 31631713Sbostic if (find(beg, dir->name, name)) 31731703Sbostic return(YES); 31831778Sbostic } 31931778Sbostic else if (find(beg, stanlist[section].name, name)) 32031703Sbostic return(YES); 32131703Sbostic if (!end) 32231703Sbostic return(NO); 32331703Sbostic } 32431703Sbostic /*NOTREACHED*/ 32531703Sbostic } 32631703Sbostic 32731778Sbostic /* 32831778Sbostic * find -- 32931778Sbostic * given a directory path, a sub-directory and a file name, 33031778Sbostic * see if a file exists in ${directory}/${dir}/{file name} 33131778Sbostic * or in ${directory}/${dir}/${machine}/${file name}. 33231778Sbostic */ 33331703Sbostic static 33431703Sbostic find(beg, dir, name) 33531703Sbostic char *beg, *dir, *name; 33631703Sbostic { 33731703Sbostic char fname[MAXPATHLEN + 1]; 33831703Sbostic 33931713Sbostic (void)sprintf(fname, "%s/%s/%s.0", beg, dir, name); 34032565Sbostic if (access(fname, R_OK)) { 34132565Sbostic (void)sprintf(fname, "%s/%s/%s/%s.0", beg, dir, machine, name); 34232565Sbostic if (access(fname, R_OK)) 34332565Sbostic return(NO); 34431703Sbostic } 34532565Sbostic if (where) 34632565Sbostic printf("man: found in %s.\n", fname); 34732565Sbostic else 34831703Sbostic show(fname); 34932565Sbostic return(!where); 35031703Sbostic } 35131703Sbostic 35231778Sbostic /* 35331778Sbostic * show -- 35431778Sbostic * display the file 35531778Sbostic */ 35631703Sbostic static 35731703Sbostic show(fname) 35831703Sbostic char *fname; 35931703Sbostic { 36031703Sbostic register int fd, n; 36131703Sbostic char buf[BUFSIZ]; 36231703Sbostic 36331703Sbostic if (nomore) { 36431703Sbostic if (!(fd = open(fname, O_RDONLY, 0))) { 36531703Sbostic perror("man: open"); 36631703Sbostic exit(1); 36731703Sbostic } 36831703Sbostic while ((n = read(fd, buf, sizeof(buf))) > 0) 36931703Sbostic if (write(1, buf, n) != n) { 37031703Sbostic perror("man: write"); 37131703Sbostic exit(1); 37231703Sbostic } 37331703Sbostic if (n == -1) { 37431703Sbostic perror("man: read"); 37531703Sbostic exit(1); 37631703Sbostic } 37731703Sbostic (void)close(fd); 37831703Sbostic } 37931703Sbostic else { 38031703Sbostic /* 38131703Sbostic * use system(2) in case someone's pager is 38231703Sbostic * "command arg1 arg2" 38331703Sbostic */ 38431703Sbostic (void)sprintf(buf, "%s %s", pager, fname); 38531703Sbostic (void)system(buf); 38631703Sbostic } 38731703Sbostic } 38831703Sbostic 38931778Sbostic /* 39031778Sbostic * usage -- 39131778Sbostic * print out a usage message and die 39231778Sbostic */ 39331703Sbostic static 39431703Sbostic usage() 39531703Sbostic { 39631703Sbostic fputs("usage: man [-] [-M path] [section] title ...\n", stderr); 39731703Sbostic exit(1); 39831703Sbostic } 399