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*31936Sbostic static char sccsid[] = "@(#)man.c 5.7 (Berkeley) 07/26/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" 22*31936Sbostic #define DEF_PATH "/usr/man:/usr/new/man:/usr/local/man" 23*31936Sbostic 2431713Sbostic #define LOCAL_PATH "/usr/local/man" 2531713Sbostic #define LOCAL_NAME "local" 26*31936Sbostic 27*31936Sbostic #define NEW_PATH "/usr/new/man" 28*31936Sbostic #define NEW_NAME "new" 29*31936Sbostic 3031713Sbostic #define NO 0 3131713Sbostic #define YES 1 3231703Sbostic 3331703Sbostic #define NO_SECTION 0 3431713Sbostic #define S_THREEF 9 35*31936Sbostic #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", 51*31936Sbostic "cat3f", "3rd (F)", "cat.old", "old", NULL, NULL, 5231778Sbostic }, sec1list[] = { /* section one list */ 5331778Sbostic "notused", "", "cat1", "1st", "cat8", "8th", 54*31936Sbostic "cat6", "6th", "cat.old", "old", NULL, NULL, 5531703Sbostic }; 5631703Sbostic 5731778Sbostic static DIR *dirlist; /* list of directories to search */ 5831713Sbostic static int nomore; /* copy file to stdout */ 5931713Sbostic static char *defpath, /* default search path */ 6031713Sbostic *locpath, /* local search path */ 6131713Sbostic *machine, /* machine type */ 6231713Sbostic *manpath, /* current search path */ 63*31936Sbostic *newpath, /* new search path */ 6431713Sbostic *pager; /* requested pager */ 6531703Sbostic 6631703Sbostic main(argc, argv) 6731703Sbostic int argc; 6831713Sbostic register char **argv; 6931703Sbostic { 7031703Sbostic int section; 7131703Sbostic char **arg_start, **arg, 7231779Sbostic *getenv(), *malloc(); 7331703Sbostic 7431703Sbostic arg_start = argv; 7531703Sbostic for (--argc, ++argv; argc && (*argv)[0] == '-'; --argc, ++argv) 7631703Sbostic switch((*argv)[1]) { 7731703Sbostic case 0: /* just write to stdout */ 7831703Sbostic nomore = YES; 7931703Sbostic break; 8031703Sbostic case 'M': 8131703Sbostic case 'P': /* backward compatibility */ 8231703Sbostic if ((*argv)[2]) 8331906Sbostic defpath = *argv + 2; 8431703Sbostic else { 8531703Sbostic if (argc < 2) { 8631703Sbostic fprintf(stderr, "%s: missing path\n", *argv); 8731703Sbostic exit(1); 8831703Sbostic } 8931703Sbostic --argc; 9031906Sbostic defpath = *++argv; 9131703Sbostic } 9231703Sbostic break; 9331703Sbostic /* 9431703Sbostic * "man -f" and "man -k" are undocumented ways of calling 9531703Sbostic * whatis(1) and apropos(1). Just strip out the flag 9631703Sbostic * argument and jump. 9731703Sbostic */ 9831703Sbostic case 'f': 9931703Sbostic for (arg = argv; arg[0] = arg[1]; ++arg); 10031703Sbostic *arg_start = "whatis"; 10131703Sbostic execvp(*arg_start, arg_start); 10231703Sbostic fputs("whatis: Command not found.\n", stderr); 10331703Sbostic exit(1); 10431703Sbostic case 'k': 10531703Sbostic for (arg = argv; *arg = arg[1]; ++arg); 10631703Sbostic *arg_start = "apropos"; 10731703Sbostic execvp(*arg_start, arg_start); 10831703Sbostic fputs("apropos: Command not found.\n", stderr); 10931703Sbostic exit(1); 11031703Sbostic case '?': 11131703Sbostic default: 11231703Sbostic usage(); 11331703Sbostic } 11431703Sbostic if (!argc) 11531703Sbostic usage(); 11631703Sbostic 11731703Sbostic if (!nomore) 11831703Sbostic if (!isatty(1)) 11931703Sbostic nomore = YES; 12031779Sbostic else if (pager = getenv("PAGER")) { 12131779Sbostic register char *C; 12231779Sbostic 12331779Sbostic /* 12431779Sbostic * if the user uses "more", we make it "more -s" 12531779Sbostic * watch out for PAGER = "mypager /usr/ucb/more" 12631779Sbostic */ 12731779Sbostic for (C = pager; *C && !isspace(*C); ++C); 12831779Sbostic for (; C > pager && *C != '/'; --C); 12931779Sbostic if (C != pager) 13031779Sbostic ++C; 13131779Sbostic /* make sure it's "more", not "morex" */ 13231779Sbostic if (!strncmp(C, "more", 4) && (!C[4] || isspace(C[4]))) { 13331779Sbostic C += 4; 13431779Sbostic /* 13531779Sbostic * sizeof is 1 more than # of chars, so, 13631779Sbostic * allocate for the rest of the PAGER 13731779Sbostic * environment variable, a space, and the EOS. 13831779Sbostic */ 13931779Sbostic if (!(pager = malloc((u_int)(strlen(C) + sizeof(DEF_PAGER) + 1)))) { 14031779Sbostic fputs("man: out of space.\n", stderr); 14131779Sbostic exit(1); 14231779Sbostic } 14331779Sbostic (void)sprintf(pager, "%s %s", DEF_PAGER, C); 14431779Sbostic } 14531779Sbostic } 14631779Sbostic else 14731703Sbostic pager = DEF_PAGER; 14831703Sbostic if (!(machine = getenv("MACHINE"))) 14931703Sbostic machine = MACHINE; 15031713Sbostic if (!defpath && !(defpath = getenv("MANPATH"))) 15131713Sbostic defpath = DEF_PATH; 15231713Sbostic locpath = LOCAL_PATH; 153*31936Sbostic newpath = NEW_PATH; 15431713Sbostic for (; *defpath && *defpath == ':'; ++defpath); 15531703Sbostic 15631778Sbostic /* Gentlemen... start your kludges! */ 15731703Sbostic for (; *argv; ++argv) { 15831703Sbostic section = NO_SECTION; 15931906Sbostic manpath = defpath; 16031703Sbostic switch(**argv) { 16131778Sbostic /* 162*31936Sbostic * Section 1 requests are really for section 1, 6, 8, in the 163*31936Sbostic * standard, local and new directories and section old. Since 164*31936Sbostic * old isn't broken up into a directory of cat[1-8], we just 165*31936Sbostic * treat it like a subdirectory of the others. 16631778Sbostic */ 167*31936Sbostic case '1': case '2': case '4': case '5': case '6': 16831713Sbostic case '7': case '8': 16931703Sbostic if (!(*argv)[1]) { 17031713Sbostic section = secno((*argv)[0]); 17131713Sbostic goto numtest; 17231713Sbostic } 17331713Sbostic break; 17431778Sbostic 17531778Sbostic /* sect. 3 requests are for either section 3, or section 3F. */ 17631713Sbostic case '3': 17731713Sbostic if (!(*argv)[1]) { /* "3" */ 17831713Sbostic section = secno((*argv)[0]); 17931713Sbostic numtest: if (!*++argv) { 18031778Sbostic fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[section].msg); 18131713Sbostic exit(1); 18231713Sbostic } 18331713Sbostic } /* "3[fF]" */ 184*31936Sbostic else if (((*argv)[1] == 'f' || (*argv)[1] == 'F') && !(*argv)[2]) { 18531713Sbostic section = S_THREEF; 18631703Sbostic if (!*++argv) { 18731778Sbostic fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[S_THREEF].msg); 18831703Sbostic exit(1); 18931703Sbostic } 19031703Sbostic } 19131703Sbostic break; 19231778Sbostic /* 193*31936Sbostic * Requests for the new or local sections can have subsection 194*31936Sbostic * numbers appended to them, i.e. "local3" is really 195*31936Sbostic * local/cat3. 19631778Sbostic */ 19731713Sbostic case 'l': /* local */ 19831713Sbostic if (!(*argv)[1]) /* "l" */ 19931713Sbostic section = NO_SECTION; /* "l2" */ 20031713Sbostic else if (isdigit((*argv)[1]) && !(*argv)[2]) 20131713Sbostic section = secno((*argv)[1]); 20231713Sbostic else { 20331713Sbostic int lex; 20431713Sbostic lex = strcmp(LOCAL_NAME, *argv); 20531713Sbostic if (!lex) /* "local" */ 20631713Sbostic section = NO_SECTION; /* "local2" */ 20731713Sbostic else if (lex < 0 && isdigit((*argv)[sizeof(LOCAL_NAME) - 1]) && !(*argv)[sizeof(LOCAL_NAME)]) 20831713Sbostic section = secno((*argv)[sizeof(LOCAL_NAME) - 1]); 20931713Sbostic else 21031713Sbostic break; 21131703Sbostic } 21231713Sbostic if (!*++argv) { 21331713Sbostic fputs("man: what do you want from the local section of the manual?\n", stderr); 21431713Sbostic exit(1); 21531713Sbostic } 21631713Sbostic manpath = locpath; 21731703Sbostic break; 21831713Sbostic case 'n': /* new */ 219*31936Sbostic if (!(*argv)[1]) /* "n" */ 220*31936Sbostic section = NO_SECTION; /* "n2" */ 221*31936Sbostic else if (isdigit((*argv)[1]) && !(*argv)[2]) 222*31936Sbostic section = secno((*argv)[1]); 223*31936Sbostic else { 224*31936Sbostic int lex; 225*31936Sbostic lex = strcmp(NEW_NAME, *argv); 226*31936Sbostic if (!lex) /* "new" */ 227*31936Sbostic section = NO_SECTION; /* "new2" */ 228*31936Sbostic else if (lex < 0 && isdigit((*argv)[sizeof(NEW_NAME) - 1]) && !(*argv)[sizeof(NEW_NAME)]) 229*31936Sbostic section = secno((*argv)[sizeof(NEW_NAME) - 1]); 230*31936Sbostic else 231*31936Sbostic break; 23231703Sbostic } 233*31936Sbostic if (!*++argv) { 234*31936Sbostic fputs("man: what do you want from the new section of the manual?\n", stderr); 235*31936Sbostic exit(1); 236*31936Sbostic } 237*31936Sbostic manpath = newpath; 23831703Sbostic break; 23931713Sbostic case 'o': /* old */ 24031778Sbostic if (!(*argv)[1] || !strcmp(*argv, stanlist[S_OLD].msg)) { 24131713Sbostic section = S_OLD; 242*31936Sbostic if (!*++argv) { 24331778Sbostic fprintf(stderr, "man: what do you want from the %s section of the manual?\n", stanlist[section].msg); 24431703Sbostic exit(1); 24531703Sbostic } 24631703Sbostic } 24731703Sbostic break; 24831703Sbostic } 249*31936Sbostic if (section == 1) { 250*31936Sbostic dirlist = sec1list; 251*31936Sbostic section = NO_SECTION; 252*31936Sbostic } 253*31936Sbostic else 254*31936Sbostic dirlist = stanlist; 25531778Sbostic /* 25631778Sbostic * This is really silly, but I wanted to put out rational 25731778Sbostic * errors, not just "I couldn't find it." This if statement 25831778Sbostic * knows an awful lot about what gets assigned to what in 25931778Sbostic * the switch statement we just passed through. Sorry. 26031778Sbostic */ 26131703Sbostic if (!manual(section, *argv)) 26231713Sbostic if (manpath == locpath) 263*31936Sbostic if (section == NO_SECTION) 264*31936Sbostic fprintf(stderr, "No entry for %s in the local manual.\n", *argv); 265*31936Sbostic else 266*31936Sbostic fprintf(stderr, "No entry for %s in the %s section of the local manual.\n", *argv, stanlist[section].msg); 267*31936Sbostic else if (manpath == newpath) 268*31936Sbostic if (section == NO_SECTION) 269*31936Sbostic fprintf(stderr, "No entry for %s in the new manual.\n", *argv); 270*31936Sbostic else 271*31936Sbostic fprintf(stderr, "No entry for %s in the %s section of the new manual.\n", *argv, stanlist[section].msg); 27231778Sbostic else if (dirlist == sec1list) 27331778Sbostic fprintf(stderr, "No entry for %s in the 1st section of the manual.\n", *argv); 27431713Sbostic else if (section == NO_SECTION) 27531713Sbostic fprintf(stderr, "No entry for %s in the manual.\n", *argv); 27631703Sbostic else 27731778Sbostic fprintf(stderr, "No entry for %s in the %s section of the manual.\n", *argv, stanlist[section].msg); 27831703Sbostic } 27931703Sbostic exit(0); 28031703Sbostic } 28131703Sbostic 28231778Sbostic /* 28331778Sbostic * manual -- 28431778Sbostic * given a section number and a file name go through the directory 28531778Sbostic * list and find a file that matches. 28631778Sbostic */ 28731703Sbostic static 28831703Sbostic manual(section, name) 28931703Sbostic int section; 29031703Sbostic char *name; 29131703Sbostic { 29231713Sbostic register DIR *dir; 29331713Sbostic register char *beg, *end; 29431703Sbostic char *index(); 29531703Sbostic 29631703Sbostic for (beg = manpath;; beg = end + 1) { 29731703Sbostic if (end = index(beg, ':')) 29831703Sbostic *end = '\0'; 29931778Sbostic if (section == NO_SECTION) 30031778Sbostic for (dir = dirlist; (++dir)->name;) { 30131713Sbostic if (find(beg, dir->name, name)) 30231703Sbostic return(YES); 30331778Sbostic } 30431778Sbostic else if (find(beg, stanlist[section].name, name)) 30531703Sbostic return(YES); 30631703Sbostic if (!end) 30731703Sbostic return(NO); 30831703Sbostic } 30931703Sbostic /*NOTREACHED*/ 31031703Sbostic } 31131703Sbostic 31231778Sbostic /* 31331778Sbostic * find -- 31431778Sbostic * given a directory path, a sub-directory and a file name, 31531778Sbostic * see if a file exists in ${directory}/${dir}/{file name} 31631778Sbostic * or in ${directory}/${dir}/${machine}/${file name}. 31731778Sbostic */ 31831703Sbostic static 31931703Sbostic find(beg, dir, name) 32031703Sbostic char *beg, *dir, *name; 32131703Sbostic { 32231703Sbostic char fname[MAXPATHLEN + 1]; 32331703Sbostic 32431713Sbostic (void)sprintf(fname, "%s/%s/%s.0", beg, dir, name); 32531703Sbostic if (!access(fname, R_OK)) { 32631703Sbostic show(fname); 32731703Sbostic return(YES); 32831703Sbostic } 32931713Sbostic (void)sprintf(fname, "%s/%s/%s/%s.0", beg, dir, machine, name); 33031703Sbostic if (!access(fname, R_OK)) { 33131703Sbostic show(fname); 33231703Sbostic return(YES); 33331703Sbostic } 33431703Sbostic return(NO); 33531703Sbostic } 33631703Sbostic 33731778Sbostic /* 33831778Sbostic * show -- 33931778Sbostic * display the file 34031778Sbostic */ 34131703Sbostic static 34231703Sbostic show(fname) 34331703Sbostic char *fname; 34431703Sbostic { 34531703Sbostic register int fd, n; 34631703Sbostic char buf[BUFSIZ]; 34731703Sbostic 34831703Sbostic if (nomore) { 34931703Sbostic if (!(fd = open(fname, O_RDONLY, 0))) { 35031703Sbostic perror("man: open"); 35131703Sbostic exit(1); 35231703Sbostic } 35331703Sbostic while ((n = read(fd, buf, sizeof(buf))) > 0) 35431703Sbostic if (write(1, buf, n) != n) { 35531703Sbostic perror("man: write"); 35631703Sbostic exit(1); 35731703Sbostic } 35831703Sbostic if (n == -1) { 35931703Sbostic perror("man: read"); 36031703Sbostic exit(1); 36131703Sbostic } 36231703Sbostic (void)close(fd); 36331703Sbostic } 36431703Sbostic else { 36531703Sbostic /* 36631703Sbostic * use system(2) in case someone's pager is 36731703Sbostic * "command arg1 arg2" 36831703Sbostic */ 36931703Sbostic (void)sprintf(buf, "%s %s", pager, fname); 37031703Sbostic (void)system(buf); 37131703Sbostic } 37231703Sbostic } 37331703Sbostic 37431778Sbostic /* 37531778Sbostic * usage -- 37631778Sbostic * print out a usage message and die 37731778Sbostic */ 37831703Sbostic static 37931703Sbostic usage() 38031703Sbostic { 38131703Sbostic fputs("usage: man [-] [-M path] [section] title ...\n", stderr); 38231703Sbostic exit(1); 38331703Sbostic } 384