131703Sbostic /* 231703Sbostic * Copyright (c) 1987 Regents of the University of California. 333054Sbostic * All rights reserved. 433054Sbostic * 533054Sbostic * Redistribution and use in source and binary forms are permitted 633054Sbostic * provided that this notice is preserved and that due credit is given 733054Sbostic * to the University of California at Berkeley. The name of the University 833054Sbostic * may not be used to endorse or promote products derived from this 933054Sbostic * software without specific prior written permission. This software 1033054Sbostic * 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"; 1733054Sbostic #endif /* not lint */ 1831703Sbostic 1931703Sbostic #ifndef lint 20*33297Sbostic static char sccsid[] = "@(#)man.c 5.11 (Berkeley) 01/08/88"; 2133054Sbostic #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)) 317*33297Sbostic goto found; 31831778Sbostic } 319*33297Sbostic else if (find(beg, stanlist[section].name, name)) { 320*33297Sbostic found: if (end) 321*33297Sbostic *end = ':'; 32231703Sbostic return(YES); 323*33297Sbostic } 32431703Sbostic if (!end) 32531703Sbostic return(NO); 326*33297Sbostic *end = ':'; 32731703Sbostic } 32831703Sbostic /*NOTREACHED*/ 32931703Sbostic } 33031703Sbostic 33131778Sbostic /* 33231778Sbostic * find -- 33331778Sbostic * given a directory path, a sub-directory and a file name, 33431778Sbostic * see if a file exists in ${directory}/${dir}/{file name} 33531778Sbostic * or in ${directory}/${dir}/${machine}/${file name}. 33631778Sbostic */ 33731703Sbostic static 33831703Sbostic find(beg, dir, name) 33931703Sbostic char *beg, *dir, *name; 34031703Sbostic { 34131703Sbostic char fname[MAXPATHLEN + 1]; 34231703Sbostic 34331713Sbostic (void)sprintf(fname, "%s/%s/%s.0", beg, dir, name); 34432565Sbostic if (access(fname, R_OK)) { 34532565Sbostic (void)sprintf(fname, "%s/%s/%s/%s.0", beg, dir, machine, name); 34632565Sbostic if (access(fname, R_OK)) 34732565Sbostic return(NO); 34831703Sbostic } 34932565Sbostic if (where) 35032565Sbostic printf("man: found in %s.\n", fname); 35132565Sbostic else 35231703Sbostic show(fname); 35332565Sbostic return(!where); 35431703Sbostic } 35531703Sbostic 35631778Sbostic /* 35731778Sbostic * show -- 35831778Sbostic * display the file 35931778Sbostic */ 36031703Sbostic static 36131703Sbostic show(fname) 36231703Sbostic char *fname; 36331703Sbostic { 36431703Sbostic register int fd, n; 36531703Sbostic char buf[BUFSIZ]; 36631703Sbostic 36731703Sbostic if (nomore) { 36831703Sbostic if (!(fd = open(fname, O_RDONLY, 0))) { 36931703Sbostic perror("man: open"); 37031703Sbostic exit(1); 37131703Sbostic } 37231703Sbostic while ((n = read(fd, buf, sizeof(buf))) > 0) 37331703Sbostic if (write(1, buf, n) != n) { 37431703Sbostic perror("man: write"); 37531703Sbostic exit(1); 37631703Sbostic } 37731703Sbostic if (n == -1) { 37831703Sbostic perror("man: read"); 37931703Sbostic exit(1); 38031703Sbostic } 38131703Sbostic (void)close(fd); 38231703Sbostic } 38331703Sbostic else { 38431703Sbostic /* 38531703Sbostic * use system(2) in case someone's pager is 38631703Sbostic * "command arg1 arg2" 38731703Sbostic */ 38831703Sbostic (void)sprintf(buf, "%s %s", pager, fname); 38931703Sbostic (void)system(buf); 39031703Sbostic } 39131703Sbostic } 39231703Sbostic 39331778Sbostic /* 39431778Sbostic * usage -- 39531778Sbostic * print out a usage message and die 39631778Sbostic */ 39731703Sbostic static 39831703Sbostic usage() 39931703Sbostic { 40031703Sbostic fputs("usage: man [-] [-M path] [section] title ...\n", stderr); 40131703Sbostic exit(1); 40231703Sbostic } 403