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 634886Sbostic * provided that the above copyright notice and this paragraph are 734886Sbostic * duplicated in all such forms and that any documentation, 834886Sbostic * advertising materials, and other materials related to such 934886Sbostic * distribution and use acknowledge that the software was developed 1034886Sbostic * by the University of California, Berkeley. The name of the 1134886Sbostic * University may not be used to endorse or promote products derived 1234886Sbostic * from this software without specific prior written permission. 1334886Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1434886Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1534886Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1631703Sbostic */ 1731703Sbostic 1831703Sbostic #ifndef lint 1931703Sbostic char copyright[] = 2031703Sbostic "@(#) Copyright (c) 1987 Regents of the University of California.\n\ 2131703Sbostic All rights reserved.\n"; 2233054Sbostic #endif /* not lint */ 2331703Sbostic 2431703Sbostic #ifndef lint 25*40390Sbostic static char sccsid[] = "@(#)man.c 5.19 (Berkeley) 03/11/90"; 2633054Sbostic #endif /* not lint */ 2731703Sbostic 2831703Sbostic #include <sys/param.h> 2931703Sbostic #include <sys/file.h> 30*40390Sbostic #include <errno.h> 3131703Sbostic #include <ctype.h> 32*40390Sbostic #include <string.h> 3337892Sbostic #include "pathnames.h" 3431703Sbostic 35*40390Sbostic extern int errno; 3631703Sbostic 37*40390Sbostic char *command, *machine, *p_augment, *p_path, *pager, *progname; 38*40390Sbostic int f_all, f_cat, f_where; 3934057Sbostic 4031703Sbostic main(argc, argv) 4133354Sbostic int argc; 4233354Sbostic register char **argv; 4331703Sbostic { 4434057Sbostic extern char *optarg; 4534057Sbostic extern int optind; 4634057Sbostic int ch; 47*40390Sbostic char *check_pager(), *config(), *getenv(); 4831703Sbostic 49*40390Sbostic progname = "man"; 50*40390Sbostic while ((ch = getopt(argc, argv, "-acfkM:m:P:w")) != EOF) 5134057Sbostic switch((char)ch) { 52*40390Sbostic case 'a': 53*40390Sbostic f_all = 1; 5431703Sbostic break; 55*40390Sbostic case 'c': 56*40390Sbostic case '-': /* deprecated */ 57*40390Sbostic f_cat = 1; 58*40390Sbostic break; 59*40390Sbostic case 'm': 60*40390Sbostic p_augment = optarg; 61*40390Sbostic break; 6231703Sbostic case 'M': 6331703Sbostic case 'P': /* backward compatibility */ 64*40390Sbostic p_path = optarg; 6531703Sbostic break; 66*40390Sbostic /* 67*40390Sbostic * "man -f" and "man -k" are backward compatible, 68*40390Sbostic * undocumented ways of calling whatis(1) and 69*40390Sbostic * apropos(1). 70*40390Sbostic */ 7131703Sbostic case 'f': 7234057Sbostic jump(argv, "-f", "whatis"); 73*40390Sbostic /* NOTREACHED */ 7431703Sbostic case 'k': 7534057Sbostic jump(argv, "-k", "apropos"); 76*40390Sbostic /* NOTREACHED */ 7732565Sbostic case 'w': 78*40390Sbostic f_all = f_where = 1; 7932565Sbostic break; 8031703Sbostic case '?': 8131703Sbostic default: 8234057Sbostic usage(); 8331703Sbostic } 8434057Sbostic argv += optind; 8531703Sbostic 8634057Sbostic if (!*argv) 8734057Sbostic usage(); 8833354Sbostic 89*40390Sbostic if (!f_cat) 9031703Sbostic if (!isatty(1)) 91*40390Sbostic f_cat = 1; 92*40390Sbostic else if (pager = getenv("PAGER")) 93*40390Sbostic pager = check_pager(pager); 9431779Sbostic else 9537892Sbostic pager = _PATH_PAGER; 96*40390Sbostic 9731703Sbostic if (!(machine = getenv("MACHINE"))) 9831703Sbostic machine = MACHINE; 99*40390Sbostic 100*40390Sbostic if (!p_path && !(p_path = getenv("MANPATH"))) 101*40390Sbostic p_path = config(); 102*40390Sbostic 10333354Sbostic man(argv); 104*40390Sbostic 10534752Sbostic /* use system(3) in case someone's pager is "pager arg1 arg2" */ 10633809Sbostic if (command) 10733809Sbostic (void)system(command); 10833354Sbostic exit(0); 10933354Sbostic } 11031703Sbostic 11133809Sbostic typedef struct { 11233809Sbostic char *name, *msg; 11333809Sbostic } DIR; 11433354Sbostic static DIR list1[] = { /* section one list */ 11533354Sbostic "cat1", "1st", "cat8", "8th", "cat6", "6th", 11633354Sbostic "cat.old", "old", NULL, NULL, 11733354Sbostic }, list2[] = { /* rest of the list */ 11833354Sbostic "cat2", "2nd", "cat3", "3rd", "cat4", "4th", 11933354Sbostic "cat5", "5th", "cat7", "7th", "cat3f", "3rd (F)", 12033354Sbostic NULL, NULL, 12133354Sbostic }, list3[2]; /* single section */ 12233354Sbostic 123*40390Sbostic /* 124*40390Sbostic * man -- 125*40390Sbostic * main loop to find the manual page and print it out. 126*40390Sbostic */ 12733354Sbostic man(argv) 12833354Sbostic char **argv; 12933354Sbostic { 13033354Sbostic DIR *section, *getsect(); 13133354Sbostic int res; 13233354Sbostic 13331703Sbostic for (; *argv; ++argv) { 134*40390Sbostic section = isdigit(**argv) ? getsect(*argv++) : NULL; 13534752Sbostic if (*argv) { 136*40390Sbostic if (p_augment) 137*40390Sbostic if (section) 138*40390Sbostic res = manual(p_augment, section, *argv); 139*40390Sbostic else { 140*40390Sbostic res = manual(p_augment, list1, *argv); 141*40390Sbostic if (!res || f_all) 142*40390Sbostic res += manual(p_augment, list2, 143*40390Sbostic *argv); 144*40390Sbostic } 145*40390Sbostic if (p_path) 146*40390Sbostic if (section) 147*40390Sbostic res = manual(p_path, section, *argv); 148*40390Sbostic else { 149*40390Sbostic res = manual(p_path, list1, *argv); 150*40390Sbostic if (!res || f_all) 151*40390Sbostic res += manual(p_path, list2, 152*40390Sbostic *argv); 153*40390Sbostic } 154*40390Sbostic if (res || f_where) 15534752Sbostic continue; 156*40390Sbostic (void)fprintf(stderr, 157*40390Sbostic "man: no entry for %s in the ", *argv); 158*40390Sbostic } else 159*40390Sbostic (void)fprintf(stderr, 160*40390Sbostic "man: what do you want from the "); 16133809Sbostic if (section) 162*40390Sbostic (void)fprintf(stderr, 163*40390Sbostic "%s section of the ", section->msg); 16434752Sbostic if (*argv) 165*40390Sbostic (void)fprintf(stderr, "manual.\n"); 16634752Sbostic else 167*40390Sbostic (void)fprintf(stderr, "manual?\n"); 16834752Sbostic exit(1); 16931703Sbostic } 17031703Sbostic } 17131703Sbostic 17231778Sbostic /* 17331778Sbostic * manual -- 174*40390Sbostic * given a path, a directory list and a file name, find a file 175*40390Sbostic * that matches; check ${directory}/${dir}/{file name} and 17633809Sbostic * ${directory}/${dir}/${machine}/${file name}. 17731778Sbostic */ 178*40390Sbostic manual(path, section, name) 179*40390Sbostic char *path, *name; 18033354Sbostic DIR *section; 18131703Sbostic { 182*40390Sbostic register char *end; 18333354Sbostic register DIR *dp; 18433809Sbostic register int res; 185*40390Sbostic char fname[MAXPATHLEN + 1]; 18631703Sbostic 187*40390Sbostic for (res = 0;; path = end + 1) { 188*40390Sbostic if (end = index(path, ':')) 18931703Sbostic *end = '\0'; 19033809Sbostic for (dp = section; dp->name; ++dp) { 191*40390Sbostic (void)sprintf(fname, "%s/%s/%s.0", 192*40390Sbostic path, dp->name, name); 19333809Sbostic if (access(fname, R_OK)) { 194*40390Sbostic (void)sprintf(fname, "%s/%s/%s/%s.0", path, 19533809Sbostic dp->name, machine, name); 19633809Sbostic if (access(fname, R_OK)) 19733809Sbostic continue; 19831778Sbostic } 199*40390Sbostic if (f_where) 200*40390Sbostic (void)printf("man: found in %s.\n", fname); 201*40390Sbostic else if (f_cat) 20233809Sbostic cat(fname); 20333809Sbostic else 20433809Sbostic add(fname); 205*40390Sbostic if (!f_all) 20634057Sbostic return(1); 20733809Sbostic res = 1; 20833809Sbostic } 20931703Sbostic if (!end) 21033809Sbostic return(res); 21133297Sbostic *end = ':'; 21231703Sbostic } 213*40390Sbostic /* NOTREACHED */ 21431703Sbostic } 21531703Sbostic 21631778Sbostic /* 21733809Sbostic * cat -- 21833809Sbostic * cat out the file 21931778Sbostic */ 22033809Sbostic cat(fname) 22133809Sbostic char *fname; 22231703Sbostic { 22333809Sbostic register int fd, n; 22433809Sbostic char buf[BUFSIZ]; 22531703Sbostic 22633809Sbostic if (!(fd = open(fname, O_RDONLY, 0))) { 227*40390Sbostic (void)fprintf(stderr, "man: %s: %s\n", fname, strerror(errno)); 22833809Sbostic exit(1); 22931703Sbostic } 23033809Sbostic while ((n = read(fd, buf, sizeof(buf))) > 0) 23133809Sbostic if (write(1, buf, n) != n) { 232*40390Sbostic (void)fprintf(stderr, 233*40390Sbostic "man: write: %s\n", strerror(errno)); 23433809Sbostic exit(1); 23533809Sbostic } 23633809Sbostic if (n == -1) { 237*40390Sbostic (void)fprintf(stderr, "man: read: %s\n", strerror(errno)); 23833809Sbostic exit(1); 23933809Sbostic } 24033809Sbostic (void)close(fd); 24131703Sbostic } 24231703Sbostic 24331778Sbostic /* 24433809Sbostic * add -- 24533809Sbostic * add a file name to the list for future paging 24631778Sbostic */ 24733809Sbostic add(fname) 24833354Sbostic char *fname; 24931703Sbostic { 25033809Sbostic static u_int buflen; 25133809Sbostic static int len; 25233809Sbostic static char *cp; 25333809Sbostic int flen; 25433809Sbostic char *malloc(), *realloc(), *strcpy(); 25531703Sbostic 25633809Sbostic if (!command) { 257*40390Sbostic if (!(command = malloc(buflen = 1024))) 258*40390Sbostic enomem(); 25933809Sbostic len = strlen(strcpy(command, pager)); 26033809Sbostic cp = command + len; 26133809Sbostic } 26233809Sbostic flen = strlen(fname); 26333809Sbostic if (len + flen + 2 > buflen) { /* +2 == space, EOS */ 264*40390Sbostic if (!(command = realloc(command, buflen += 1024))) 265*40390Sbostic enomem(); 26633809Sbostic cp = command + len; 26731703Sbostic } 26833809Sbostic *cp++ = ' '; 26933809Sbostic len += flen + 1; /* +1 = space */ 27033809Sbostic (void)strcpy(cp, fname); 27133809Sbostic cp += flen; 27231703Sbostic } 27331703Sbostic 27431778Sbostic /* 27533354Sbostic * getsect -- 27633354Sbostic * return a point to the section structure for a particular suffix 27731778Sbostic */ 278*40390Sbostic DIR * 27933354Sbostic getsect(s) 28033354Sbostic char *s; 28131703Sbostic { 28233354Sbostic switch(*s++) { 28333354Sbostic case '1': 28433354Sbostic if (!*s) 28533354Sbostic return(list1); 28633354Sbostic break; 28733354Sbostic case '2': 28833354Sbostic if (!*s) { 28933354Sbostic list3[0] = list2[0]; 29033354Sbostic return(list3); 29133354Sbostic } 29233354Sbostic break; 29333354Sbostic /* sect. 3 requests are for either section 3, or section 3[fF]. */ 29433354Sbostic case '3': 29533354Sbostic if (!*s) { 29633354Sbostic list3[0] = list2[1]; 29733354Sbostic return(list3); 29833354Sbostic } 29933354Sbostic else if ((*s == 'f' || *s == 'F') && !*++s) { 30033354Sbostic list3[0] = list2[5]; 30133354Sbostic return(list3); 30233354Sbostic } 30333354Sbostic break; 30433354Sbostic case '4': 30533354Sbostic if (!*s) { 30633354Sbostic list3[0] = list2[2]; 30733354Sbostic return(list3); 30833354Sbostic } 30933354Sbostic break; 31033354Sbostic case '5': 31133354Sbostic if (!*s) { 31233354Sbostic list3[0] = list2[3]; 31333354Sbostic return(list3); 31433354Sbostic } 31533354Sbostic break; 31633354Sbostic case '6': 31733354Sbostic if (!*s) { 31833354Sbostic list3[0] = list1[2]; 31933354Sbostic return(list3); 32033354Sbostic } 32133354Sbostic break; 32233354Sbostic case '7': 32333354Sbostic if (!*s) { 32433354Sbostic list3[0] = list2[4]; 32533354Sbostic return(list3); 32633354Sbostic } 32733354Sbostic break; 32833354Sbostic case '8': 32933354Sbostic if (!*s) { 33033354Sbostic list3[0] = list1[1]; 33133354Sbostic return(list3); 33233354Sbostic } 333*40390Sbostic break; 33433354Sbostic } 335*40390Sbostic (void)fprintf(stderr, "man: unknown manual section.\n"); 336*40390Sbostic exit(1); 337*40390Sbostic /* NOTREACHED */ 33831703Sbostic } 33934057Sbostic 34034057Sbostic /* 341*40390Sbostic * check_pager -- 342*40390Sbostic * check the user supplied page information 343*40390Sbostic */ 344*40390Sbostic char * 345*40390Sbostic check_pager(name) 346*40390Sbostic char *name; 347*40390Sbostic { 348*40390Sbostic register char *p; 349*40390Sbostic char *save, *malloc(); 350*40390Sbostic 351*40390Sbostic /* 352*40390Sbostic * if the user uses "more", we make it "more -s"; watch out for 353*40390Sbostic * PAGER = "mypager /usr/ucb/more" 354*40390Sbostic */ 355*40390Sbostic for (p = name; *p && !isspace(*p); ++p); 356*40390Sbostic for (; p > name && *p != '/'; --p); 357*40390Sbostic if (p != name) 358*40390Sbostic ++p; 359*40390Sbostic 360*40390Sbostic /* make sure it's "more", not "morex" */ 361*40390Sbostic if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))){ 362*40390Sbostic save = name; 363*40390Sbostic /* allocate space to add the "-s" */ 364*40390Sbostic if (!(name = 365*40390Sbostic malloc((u_int)(strlen(save) + sizeof("-s") + 1)))) 366*40390Sbostic enomem(); 367*40390Sbostic (void)sprintf(name, "%s %s", save, "-s"); 368*40390Sbostic } 369*40390Sbostic return(name); 370*40390Sbostic } 371*40390Sbostic 372*40390Sbostic /* 37334057Sbostic * jump -- 37434057Sbostic * strip out flag argument and jump 37534057Sbostic */ 37634057Sbostic jump(argv, flag, name) 37734057Sbostic char **argv, *name; 37834057Sbostic register char *flag; 37934057Sbostic { 38034057Sbostic register char **arg; 38134057Sbostic 38234057Sbostic argv[0] = name; 38334057Sbostic for (arg = argv + 1; *arg; ++arg) 38434057Sbostic if (!strcmp(*arg, flag)) 38534057Sbostic break; 38634057Sbostic for (; *arg; ++arg) 38734057Sbostic arg[0] = arg[1]; 38834057Sbostic execvp(name, argv); 38934057Sbostic fprintf(stderr, "%s: Command not found.\n", name); 39034057Sbostic exit(1); 39134057Sbostic } 39234057Sbostic 39334057Sbostic /* 39434057Sbostic * usage -- 395*40390Sbostic * print usage message and die 39634057Sbostic */ 39734057Sbostic usage() 39834057Sbostic { 399*40390Sbostic (void)fprintf(stderr, 400*40390Sbostic "usage: man [-ac] [-M path] [-m path] [section] title ...\n"); 40134057Sbostic exit(1); 40234057Sbostic } 403