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*33354Sbostic static char sccsid[] = "@(#)man.c 5.12 (Berkeley) 01/14/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" 2931713Sbostic #define LOCAL_PATH "/usr/local/man" 3031936Sbostic #define NEW_PATH "/usr/new/man" 3131936Sbostic 3231713Sbostic #define NO 0 3331713Sbostic #define YES 1 3431703Sbostic 3531778Sbostic typedef struct { 36*33354Sbostic char *name, *msg; 3731713Sbostic } DIR; 3831713Sbostic 3932565Sbostic static int nomore, /* copy file to stdout */ 4032565Sbostic where; /* just tell me where */ 4131713Sbostic static char *defpath, /* default search path */ 4231713Sbostic *locpath, /* local search path */ 4331713Sbostic *machine, /* machine type */ 4431713Sbostic *manpath, /* current search path */ 4531936Sbostic *newpath, /* new search path */ 4631713Sbostic *pager; /* requested pager */ 4731703Sbostic 4831703Sbostic main(argc, argv) 49*33354Sbostic int argc; 50*33354Sbostic register char **argv; 5131703Sbostic { 52*33354Sbostic char **arg_start, **arg, *getenv(), *malloc(), *strcpy(); 5331703Sbostic 5431703Sbostic arg_start = argv; 5531703Sbostic for (--argc, ++argv; argc && (*argv)[0] == '-'; --argc, ++argv) 5631703Sbostic switch((*argv)[1]) { 5731703Sbostic case 0: /* just write to stdout */ 5831703Sbostic nomore = YES; 5931703Sbostic break; 6031703Sbostic case 'M': 6131703Sbostic case 'P': /* backward compatibility */ 6231703Sbostic if ((*argv)[2]) 6331906Sbostic defpath = *argv + 2; 6431703Sbostic else { 6531703Sbostic if (argc < 2) { 6631703Sbostic fprintf(stderr, "%s: missing path\n", *argv); 6731703Sbostic exit(1); 6831703Sbostic } 6931703Sbostic --argc; 7031906Sbostic defpath = *++argv; 7131703Sbostic } 7231703Sbostic break; 7331703Sbostic /* 7431703Sbostic * "man -f" and "man -k" are undocumented ways of calling 7531703Sbostic * whatis(1) and apropos(1). Just strip out the flag 7631703Sbostic * argument and jump. 7731703Sbostic */ 7831703Sbostic case 'f': 7931703Sbostic for (arg = argv; arg[0] = arg[1]; ++arg); 8031703Sbostic *arg_start = "whatis"; 8131703Sbostic execvp(*arg_start, arg_start); 8231703Sbostic fputs("whatis: Command not found.\n", stderr); 8331703Sbostic exit(1); 8431703Sbostic case 'k': 8531703Sbostic for (arg = argv; *arg = arg[1]; ++arg); 8631703Sbostic *arg_start = "apropos"; 8731703Sbostic execvp(*arg_start, arg_start); 8831703Sbostic fputs("apropos: Command not found.\n", stderr); 8931703Sbostic exit(1); 90*33354Sbostic /* 91*33354Sbostic * Deliberately undocumented; really only useful when 92*33354Sbostic * you're moving man pages around. Not worth adding. 93*33354Sbostic */ 9432565Sbostic case 'w': 9532565Sbostic where = YES; 9632565Sbostic break; 9731703Sbostic case '?': 9831703Sbostic default: 9932614Sbostic fprintf(stderr, "man: illegal option -- %c\n", (*argv)[1]); 100*33354Sbostic goto usage; 10131703Sbostic } 10231703Sbostic 103*33354Sbostic if (!argc) { 104*33354Sbostic usage: fputs("usage: man [-] [-M path] [section] title ...\n", stderr); 105*33354Sbostic exit(1); 106*33354Sbostic } 107*33354Sbostic 10831703Sbostic if (!nomore) 10931703Sbostic if (!isatty(1)) 11031703Sbostic nomore = YES; 11131779Sbostic else if (pager = getenv("PAGER")) { 112*33354Sbostic register char *p; 11331779Sbostic 11431779Sbostic /* 11531779Sbostic * if the user uses "more", we make it "more -s" 11631779Sbostic * watch out for PAGER = "mypager /usr/ucb/more" 11731779Sbostic */ 118*33354Sbostic for (p = pager; *p && !isspace(*p); ++p); 119*33354Sbostic for (; p > pager && *p != '/'; --p); 120*33354Sbostic if (p != pager) 121*33354Sbostic ++p; 12231779Sbostic /* make sure it's "more", not "morex" */ 123*33354Sbostic if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))) { 124*33354Sbostic p += 4; 12531779Sbostic /* 12631779Sbostic * allocate for the rest of the PAGER 12731779Sbostic * environment variable, a space, and the EOS. 12831779Sbostic */ 129*33354Sbostic if (!(pager = malloc((u_int)(strlen(p) + sizeof(DEF_PAGER) + 1)))) { 13031779Sbostic fputs("man: out of space.\n", stderr); 13131779Sbostic exit(1); 13231779Sbostic } 133*33354Sbostic (void)sprintf(pager, "%s %s", DEF_PAGER, p); 13431779Sbostic } 13531779Sbostic } 13631779Sbostic else 13731703Sbostic pager = DEF_PAGER; 13831703Sbostic if (!(machine = getenv("MACHINE"))) 13931703Sbostic machine = MACHINE; 14031713Sbostic if (!defpath && !(defpath = getenv("MANPATH"))) 14131713Sbostic defpath = DEF_PATH; 14231713Sbostic locpath = LOCAL_PATH; 14331936Sbostic newpath = NEW_PATH; 144*33354Sbostic man(argv); 145*33354Sbostic exit(0); 146*33354Sbostic } 14731703Sbostic 148*33354Sbostic static DIR list1[] = { /* section one list */ 149*33354Sbostic "cat1", "1st", "cat8", "8th", "cat6", "6th", 150*33354Sbostic "cat.old", "old", NULL, NULL, 151*33354Sbostic }, list2[] = { /* rest of the list */ 152*33354Sbostic "cat2", "2nd", "cat3", "3rd", "cat4", "4th", 153*33354Sbostic "cat5", "5th", "cat7", "7th", "cat3f", "3rd (F)", 154*33354Sbostic NULL, NULL, 155*33354Sbostic }, list3[2]; /* single section */ 156*33354Sbostic 157*33354Sbostic static 158*33354Sbostic man(argv) 159*33354Sbostic char **argv; 160*33354Sbostic { 161*33354Sbostic register char *p; 162*33354Sbostic DIR *section, *getsect(); 163*33354Sbostic int res; 164*33354Sbostic 16531703Sbostic for (; *argv; ++argv) { 16631906Sbostic manpath = defpath; 167*33354Sbostic section = NULL; 16831703Sbostic switch(**argv) { 169*33354Sbostic case 'l': /* local */ 170*33354Sbostic for (p = *argv; isalpha(*p); ++p); 171*33354Sbostic if (!strncmp(*argv, "l", p - *argv) || 172*33354Sbostic !strncmp(*argv, "local", p - *argv)) { 173*33354Sbostic manpath = locpath; 174*33354Sbostic if (section = getsect(p)) 175*33354Sbostic goto argtest; 17631713Sbostic } 17731713Sbostic break; 178*33354Sbostic case 'n': /* new */ 179*33354Sbostic for (p = *argv; isalpha(*p); ++p); 180*33354Sbostic if (!strncmp(*argv, "n", p - *argv) || 181*33354Sbostic !strncmp(*argv, "new", p - *argv)) { 182*33354Sbostic manpath = newpath; 183*33354Sbostic if (section = getsect(p)) 184*33354Sbostic goto argtest; 18531703Sbostic } 18631703Sbostic break; 18731778Sbostic /* 188*33354Sbostic * old isn't really a separate section of the manual, 189*33354Sbostic * and its entries are all in a single directory. 19031778Sbostic */ 191*33354Sbostic case 'o': /* old */ 192*33354Sbostic for (p = *argv; isalpha(*p); ++p); 193*33354Sbostic if (!strncmp(*argv, "o", p - *argv) || 194*33354Sbostic !strncmp(*argv, "old", p - *argv)) { 195*33354Sbostic list3[0] = list1[3]; 196*33354Sbostic section = list3; 197*33354Sbostic goto argtest; 19831703Sbostic } 19931703Sbostic break; 200*33354Sbostic case '1': case '2': case '3': case '4': 201*33354Sbostic case '5': case '6': case '7': case '8': 202*33354Sbostic if (!(section = getsect(*argv))) 203*33354Sbostic break; 204*33354Sbostic argtest: if (!*++argv) { 205*33354Sbostic fprintf(stderr, "man: what do you want from the %s section of the manual?\n", section->msg); 20631936Sbostic exit(1); 20731936Sbostic } 20831703Sbostic } 209*33354Sbostic 210*33354Sbostic res = section ? manual(section, *argv) : 211*33354Sbostic manual(list1, *argv) || manual(list2, *argv); 212*33354Sbostic if (!res && !where) 21331713Sbostic if (manpath == locpath) 214*33354Sbostic if (section) 215*33354Sbostic fprintf(stderr, "No entry for %s in the %s section of the local manual.\n", *argv, section->msg); 216*33354Sbostic else 21731936Sbostic fprintf(stderr, "No entry for %s in the local manual.\n", *argv); 218*33354Sbostic else if (manpath == newpath) 219*33354Sbostic if (section) 220*33354Sbostic fprintf(stderr, "No entry for %s in the %s section of the new manual.\n", *argv, section->msg); 22131936Sbostic else 22231936Sbostic fprintf(stderr, "No entry for %s in the new manual.\n", *argv); 223*33354Sbostic else if (section) 224*33354Sbostic fprintf(stderr, "No entry for %s in the %s section of the manual.\n", *argv, section->msg); 225*33354Sbostic else 22631713Sbostic fprintf(stderr, "No entry for %s in the manual.\n", *argv); 22731703Sbostic } 22831703Sbostic } 22931703Sbostic 23031778Sbostic /* 23131778Sbostic * manual -- 23231778Sbostic * given a section number and a file name go through the directory 23331778Sbostic * list and find a file that matches. 23431778Sbostic */ 23531703Sbostic static 23631703Sbostic manual(section, name) 237*33354Sbostic DIR *section; 238*33354Sbostic char *name; 23931703Sbostic { 240*33354Sbostic register char *beg, *end; 241*33354Sbostic register DIR *dp; 242*33354Sbostic char *index(); 24331703Sbostic 24431703Sbostic for (beg = manpath;; beg = end + 1) { 24531703Sbostic if (end = index(beg, ':')) 24631703Sbostic *end = '\0'; 247*33354Sbostic for (dp = section; dp->name; ++dp) 248*33354Sbostic if (find(beg, dp->name, name)) { 249*33354Sbostic if (end) 250*33354Sbostic *end = ':'; 251*33354Sbostic return(YES); 25231778Sbostic } 25331703Sbostic if (!end) 25431703Sbostic return(NO); 25533297Sbostic *end = ':'; 25631703Sbostic } 25731703Sbostic /*NOTREACHED*/ 25831703Sbostic } 25931703Sbostic 26031778Sbostic /* 26131778Sbostic * find -- 26231778Sbostic * given a directory path, a sub-directory and a file name, 26331778Sbostic * see if a file exists in ${directory}/${dir}/{file name} 26431778Sbostic * or in ${directory}/${dir}/${machine}/${file name}. 26531778Sbostic */ 26631703Sbostic static 26731703Sbostic find(beg, dir, name) 268*33354Sbostic char *beg, *dir, *name; 26931703Sbostic { 270*33354Sbostic char fname[MAXPATHLEN + 1]; 27131703Sbostic 27231713Sbostic (void)sprintf(fname, "%s/%s/%s.0", beg, dir, name); 27332565Sbostic if (access(fname, R_OK)) { 27432565Sbostic (void)sprintf(fname, "%s/%s/%s/%s.0", beg, dir, machine, name); 27532565Sbostic if (access(fname, R_OK)) 27632565Sbostic return(NO); 27731703Sbostic } 27832565Sbostic if (where) 27932565Sbostic printf("man: found in %s.\n", fname); 28032565Sbostic else 28131703Sbostic show(fname); 28232565Sbostic return(!where); 28331703Sbostic } 28431703Sbostic 28531778Sbostic /* 28631778Sbostic * show -- 28731778Sbostic * display the file 28831778Sbostic */ 28931703Sbostic static 29031703Sbostic show(fname) 291*33354Sbostic char *fname; 29231703Sbostic { 293*33354Sbostic register int fd, n; 294*33354Sbostic char buf[BUFSIZ]; 29531703Sbostic 29631703Sbostic if (nomore) { 29731703Sbostic if (!(fd = open(fname, O_RDONLY, 0))) { 29831703Sbostic perror("man: open"); 29931703Sbostic exit(1); 30031703Sbostic } 30131703Sbostic while ((n = read(fd, buf, sizeof(buf))) > 0) 30231703Sbostic if (write(1, buf, n) != n) { 30331703Sbostic perror("man: write"); 30431703Sbostic exit(1); 30531703Sbostic } 30631703Sbostic if (n == -1) { 30731703Sbostic perror("man: read"); 30831703Sbostic exit(1); 30931703Sbostic } 31031703Sbostic (void)close(fd); 31131703Sbostic } 31231703Sbostic else { 31331703Sbostic /* 314*33354Sbostic * use system(3) in case someone's pager is 31531703Sbostic * "command arg1 arg2" 31631703Sbostic */ 31731703Sbostic (void)sprintf(buf, "%s %s", pager, fname); 31831703Sbostic (void)system(buf); 31931703Sbostic } 32031703Sbostic } 32131703Sbostic 32231778Sbostic /* 323*33354Sbostic * getsect -- 324*33354Sbostic * return a point to the section structure for a particular suffix 32531778Sbostic */ 326*33354Sbostic static DIR * 327*33354Sbostic getsect(s) 328*33354Sbostic char *s; 32931703Sbostic { 330*33354Sbostic switch(*s++) { 331*33354Sbostic case '1': 332*33354Sbostic if (!*s) 333*33354Sbostic return(list1); 334*33354Sbostic break; 335*33354Sbostic case '2': 336*33354Sbostic if (!*s) { 337*33354Sbostic list3[0] = list2[0]; 338*33354Sbostic return(list3); 339*33354Sbostic } 340*33354Sbostic break; 341*33354Sbostic /* sect. 3 requests are for either section 3, or section 3[fF]. */ 342*33354Sbostic case '3': 343*33354Sbostic if (!*s) { 344*33354Sbostic list3[0] = list2[1]; 345*33354Sbostic return(list3); 346*33354Sbostic } 347*33354Sbostic else if ((*s == 'f' || *s == 'F') && !*++s) { 348*33354Sbostic list3[0] = list2[5]; 349*33354Sbostic return(list3); 350*33354Sbostic } 351*33354Sbostic break; 352*33354Sbostic case '4': 353*33354Sbostic if (!*s) { 354*33354Sbostic list3[0] = list2[2]; 355*33354Sbostic return(list3); 356*33354Sbostic } 357*33354Sbostic break; 358*33354Sbostic case '5': 359*33354Sbostic if (!*s) { 360*33354Sbostic list3[0] = list2[3]; 361*33354Sbostic return(list3); 362*33354Sbostic } 363*33354Sbostic break; 364*33354Sbostic case '6': 365*33354Sbostic if (!*s) { 366*33354Sbostic list3[0] = list1[2]; 367*33354Sbostic return(list3); 368*33354Sbostic } 369*33354Sbostic break; 370*33354Sbostic case '7': 371*33354Sbostic if (!*s) { 372*33354Sbostic list3[0] = list2[4]; 373*33354Sbostic return(list3); 374*33354Sbostic } 375*33354Sbostic break; 376*33354Sbostic case '8': 377*33354Sbostic if (!*s) { 378*33354Sbostic list3[0] = list1[1]; 379*33354Sbostic return(list3); 380*33354Sbostic } 381*33354Sbostic } 382*33354Sbostic return((DIR *)NULL); 38331703Sbostic } 384