131703Sbostic /* 231703Sbostic * Copyright (c) 1987 Regents of the University of California. 333054Sbostic * All rights reserved. 433054Sbostic * 542741Sbostic * %sccs.include.redist.c% 631703Sbostic */ 731703Sbostic 831703Sbostic #ifndef lint 931703Sbostic char copyright[] = 1031703Sbostic "@(#) Copyright (c) 1987 Regents of the University of California.\n\ 1131703Sbostic All rights reserved.\n"; 1233054Sbostic #endif /* not lint */ 1331703Sbostic 1431703Sbostic #ifndef lint 15*44938Sbostic static char sccsid[] = "@(#)man.c 5.24 (Berkeley) 07/21/90"; 1633054Sbostic #endif /* not lint */ 1731703Sbostic 1831703Sbostic #include <sys/param.h> 1931703Sbostic #include <sys/file.h> 2040390Sbostic #include <errno.h> 2131703Sbostic #include <ctype.h> 2240390Sbostic #include <string.h> 2342402Sbostic #include <stdlib.h> 2437892Sbostic #include "pathnames.h" 2531703Sbostic 2640390Sbostic extern int errno; 2731703Sbostic 28*44938Sbostic int f_all, f_cat, f_how, f_where; 2940390Sbostic char *command, *machine, *p_augment, *p_path, *pager, *progname; 3044597Strent extern char **arorder, *pathbuf; 3134057Sbostic 3231703Sbostic main(argc, argv) 3333354Sbostic int argc; 3433354Sbostic register char **argv; 3531703Sbostic { 3634057Sbostic extern char *optarg; 3734057Sbostic extern int optind; 3842402Sbostic int ch, res; 3944418Strent char *section[2], *check_pager(), *getpath(), **getorder(), *tmp; 4031703Sbostic 4140390Sbostic progname = "man"; 42*44938Sbostic while ((ch = getopt(argc, argv, "-acfhkM:m:P:w")) != EOF) 4334057Sbostic switch((char)ch) { 4440390Sbostic case 'a': 4540390Sbostic f_all = 1; 4631703Sbostic break; 4740390Sbostic case 'c': 4840390Sbostic case '-': /* deprecated */ 4940390Sbostic f_cat = 1; 5040390Sbostic break; 51*44938Sbostic case 'h': 52*44938Sbostic f_how = 1; 53*44938Sbostic break; 5440390Sbostic case 'm': 5540390Sbostic p_augment = optarg; 5640390Sbostic break; 5731703Sbostic case 'M': 5831703Sbostic case 'P': /* backward compatibility */ 5940390Sbostic p_path = optarg; 6031703Sbostic break; 6142402Sbostic /* 6242402Sbostic * "man -f" and "man -k" are backward compatible, undocumented 6342402Sbostic * ways of calling whatis(1) and apropos(1). 6442402Sbostic */ 6531703Sbostic case 'f': 6634057Sbostic jump(argv, "-f", "whatis"); 6740390Sbostic /* NOTREACHED */ 6831703Sbostic case 'k': 6934057Sbostic jump(argv, "-k", "apropos"); 7040390Sbostic /* NOTREACHED */ 7132565Sbostic case 'w': 7240390Sbostic f_all = f_where = 1; 7332565Sbostic break; 7431703Sbostic case '?': 7531703Sbostic default: 7634057Sbostic usage(); 7731703Sbostic } 7834057Sbostic argv += optind; 7931703Sbostic 8034057Sbostic if (!*argv) 8134057Sbostic usage(); 8233354Sbostic 83*44938Sbostic if (!f_cat && !f_how) 8431703Sbostic if (!isatty(1)) 8540390Sbostic f_cat = 1; 8640390Sbostic else if (pager = getenv("PAGER")) 8740390Sbostic pager = check_pager(pager); 8831779Sbostic else 8937892Sbostic pager = _PATH_PAGER; 9040390Sbostic 9131703Sbostic if (!(machine = getenv("MACHINE"))) 9231703Sbostic machine = MACHINE; 9340390Sbostic 9442402Sbostic /* see if checking in a specific section */ 9542402Sbostic if (argc > 1 && getsection(*argv)) { 9642402Sbostic section[0] = *argv++; 9742402Sbostic section[1] = (char *)NULL; 9842402Sbostic } else { 9942402Sbostic section[0] = "_default"; 10042402Sbostic section[1] = (char *)NULL; 10142402Sbostic } 10240390Sbostic 10344418Strent arorder = getorder(); 10444418Strent if (p_path || (p_path = getenv("MANPATH"))) { 10544418Strent char buf[MAXPATHLEN], **av; 10644418Strent 10744418Strent tmp = strtok(p_path, ":"); 10844418Strent while (tmp) { 10944418Strent (void)sprintf(buf, "%s/", tmp); 11044418Strent for (av = arorder; *av; ++av) 11144418Strent cadd(buf, strlen(buf), *av); 11244418Strent tmp = strtok((char *)NULL, ":"); 11344418Strent } 11444418Strent p_path = pathbuf; 11544418Strent } else if (!(p_path = getpath(section)) && !p_augment) { 11642402Sbostic (void)fprintf(stderr, 11744418Strent "man: no place to search for those manual pages.\n"); 11842402Sbostic exit(1); 11942402Sbostic } 12040390Sbostic 12142402Sbostic for (; *argv; ++argv) { 12242402Sbostic if (p_augment) 12342402Sbostic res = manual(p_augment, *argv); 12442402Sbostic res = manual(p_path, *argv); 12542402Sbostic if (res || f_where) 12642402Sbostic continue; 12742402Sbostic (void)fprintf(stderr, 12842402Sbostic "man: no entry for %s in the manual.\n", *argv); 12942402Sbostic exit(1); 13042402Sbostic } 13142402Sbostic 13234752Sbostic /* use system(3) in case someone's pager is "pager arg1 arg2" */ 13333809Sbostic if (command) 13433809Sbostic (void)system(command); 13533354Sbostic exit(0); 13633354Sbostic } 13731703Sbostic 13840390Sbostic /* 13931778Sbostic * manual -- 14040390Sbostic * given a path, a directory list and a file name, find a file 14140390Sbostic * that matches; check ${directory}/${dir}/{file name} and 14233809Sbostic * ${directory}/${dir}/${machine}/${file name}. 14331778Sbostic */ 14442402Sbostic manual(path, name) 14540390Sbostic char *path, *name; 14631703Sbostic { 14742402Sbostic register int res; 14840390Sbostic register char *end; 14940390Sbostic char fname[MAXPATHLEN + 1]; 15031703Sbostic 15140390Sbostic for (res = 0;; path = end + 1) { 15242402Sbostic if (!*path) /* foo: */ 15342402Sbostic break; 15442402Sbostic if (end = index(path, ':')) { 15542402Sbostic if (end == path + 1) /* foo::bar */ 15642402Sbostic continue; 15731703Sbostic *end = '\0'; 15833809Sbostic } 15942402Sbostic (void)sprintf(fname, "%s/%s.0", path, name); 16042402Sbostic if (access(fname, R_OK)) { 16142402Sbostic (void)sprintf(fname, "%s/%s/%s.0", path, machine, name); 16242402Sbostic if (access(fname, R_OK)) 16342402Sbostic continue; 16442402Sbostic } 16542402Sbostic 16642402Sbostic if (f_where) 16742402Sbostic (void)printf("man: found in %s.\n", fname); 16842402Sbostic else if (f_cat) 16942402Sbostic cat(fname); 170*44938Sbostic else if (f_how) 171*44938Sbostic how(fname); 17242402Sbostic else 17342402Sbostic add(fname); 17442402Sbostic if (!f_all) 17542402Sbostic return(1); 17642402Sbostic res = 1; 17731703Sbostic if (!end) 17842402Sbostic break; 17933297Sbostic *end = ':'; 18031703Sbostic } 18142402Sbostic return(res); 18231703Sbostic } 18331703Sbostic 18431778Sbostic /* 185*44938Sbostic * how -- 186*44938Sbostic * display how information 187*44938Sbostic */ 188*44938Sbostic how(fname) 189*44938Sbostic char *fname; 190*44938Sbostic { 191*44938Sbostic register FILE *fp; 192*44938Sbostic 193*44938Sbostic register int lcnt, print; 194*44938Sbostic register char *p; 195*44938Sbostic char buf[BUFSIZ]; 196*44938Sbostic 197*44938Sbostic if (!(fp = fopen(fname, "r"))) { 198*44938Sbostic (void)fprintf(stderr, "man: %s: %s\n", fname, strerror(errno)); 199*44938Sbostic exit(1); 200*44938Sbostic } 201*44938Sbostic #define S1 "SYNOPSIS" 202*44938Sbostic #define S2 "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS" 203*44938Sbostic #define D1 "DESCRIPTION" 204*44938Sbostic #define D2 "D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN" 205*44938Sbostic for (lcnt = print = 0; fgets(buf, sizeof(buf), fp);) { 206*44938Sbostic if (!strncmp(buf, S1, sizeof(S1) - 1) || 207*44938Sbostic !strncmp(buf, S2, sizeof(S2) - 1)) { 208*44938Sbostic print = 1; 209*44938Sbostic continue; 210*44938Sbostic } else if (!strncmp(buf, D1, sizeof(D1) - 1) || 211*44938Sbostic !strncmp(buf, D2, sizeof(D2) - 1)) 212*44938Sbostic return; 213*44938Sbostic if (!print) 214*44938Sbostic continue; 215*44938Sbostic if (*buf == '\n') 216*44938Sbostic ++lcnt; 217*44938Sbostic else { 218*44938Sbostic for(; lcnt; --lcnt) 219*44938Sbostic (void)putchar('\n'); 220*44938Sbostic for (p = buf; isspace(*p); ++p); 221*44938Sbostic (void)fputs(p, stdout); 222*44938Sbostic } 223*44938Sbostic } 224*44938Sbostic (void)fclose(fp); 225*44938Sbostic } 226*44938Sbostic /* 22733809Sbostic * cat -- 22833809Sbostic * cat out the file 22931778Sbostic */ 23033809Sbostic cat(fname) 23133809Sbostic char *fname; 23231703Sbostic { 23333809Sbostic register int fd, n; 23433809Sbostic char buf[BUFSIZ]; 23531703Sbostic 236*44938Sbostic if ((fd = open(fname, O_RDONLY, 0)) < 0) { 23740390Sbostic (void)fprintf(stderr, "man: %s: %s\n", fname, strerror(errno)); 23833809Sbostic exit(1); 23931703Sbostic } 24033809Sbostic while ((n = read(fd, buf, sizeof(buf))) > 0) 24133809Sbostic if (write(1, buf, n) != n) { 24240390Sbostic (void)fprintf(stderr, 24340390Sbostic "man: write: %s\n", strerror(errno)); 24433809Sbostic exit(1); 24533809Sbostic } 24633809Sbostic if (n == -1) { 24740390Sbostic (void)fprintf(stderr, "man: read: %s\n", strerror(errno)); 24833809Sbostic exit(1); 24933809Sbostic } 25033809Sbostic (void)close(fd); 25131703Sbostic } 25231703Sbostic 25331778Sbostic /* 25433809Sbostic * add -- 25533809Sbostic * add a file name to the list for future paging 25631778Sbostic */ 25733809Sbostic add(fname) 25833354Sbostic char *fname; 25931703Sbostic { 26033809Sbostic static u_int buflen; 26133809Sbostic static int len; 26233809Sbostic static char *cp; 26333809Sbostic int flen; 26431703Sbostic 26533809Sbostic if (!command) { 26640390Sbostic if (!(command = malloc(buflen = 1024))) 26740390Sbostic enomem(); 26833809Sbostic len = strlen(strcpy(command, pager)); 26933809Sbostic cp = command + len; 27033809Sbostic } 27133809Sbostic flen = strlen(fname); 27233809Sbostic if (len + flen + 2 > buflen) { /* +2 == space, EOS */ 27340390Sbostic if (!(command = realloc(command, buflen += 1024))) 27440390Sbostic enomem(); 27533809Sbostic cp = command + len; 27631703Sbostic } 27733809Sbostic *cp++ = ' '; 27833809Sbostic len += flen + 1; /* +1 = space */ 27933809Sbostic (void)strcpy(cp, fname); 28033809Sbostic cp += flen; 28131703Sbostic } 28231703Sbostic 28331778Sbostic /* 28440390Sbostic * check_pager -- 28540390Sbostic * check the user supplied page information 28640390Sbostic */ 28740390Sbostic char * 28840390Sbostic check_pager(name) 28940390Sbostic char *name; 29040390Sbostic { 29140390Sbostic register char *p; 29242402Sbostic char *save; 29340390Sbostic 29440390Sbostic /* 29540390Sbostic * if the user uses "more", we make it "more -s"; watch out for 29640390Sbostic * PAGER = "mypager /usr/ucb/more" 29740390Sbostic */ 29840390Sbostic for (p = name; *p && !isspace(*p); ++p); 29940390Sbostic for (; p > name && *p != '/'; --p); 30040390Sbostic if (p != name) 30140390Sbostic ++p; 30240390Sbostic 30340390Sbostic /* make sure it's "more", not "morex" */ 30440390Sbostic if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))){ 30540390Sbostic save = name; 30640390Sbostic /* allocate space to add the "-s" */ 30740390Sbostic if (!(name = 30840390Sbostic malloc((u_int)(strlen(save) + sizeof("-s") + 1)))) 30940390Sbostic enomem(); 31040390Sbostic (void)sprintf(name, "%s %s", save, "-s"); 31140390Sbostic } 31240390Sbostic return(name); 31340390Sbostic } 31440390Sbostic 31540390Sbostic /* 31634057Sbostic * jump -- 31734057Sbostic * strip out flag argument and jump 31834057Sbostic */ 31934057Sbostic jump(argv, flag, name) 32034057Sbostic char **argv, *name; 32134057Sbostic register char *flag; 32234057Sbostic { 32334057Sbostic register char **arg; 32434057Sbostic 32534057Sbostic argv[0] = name; 32634057Sbostic for (arg = argv + 1; *arg; ++arg) 32734057Sbostic if (!strcmp(*arg, flag)) 32834057Sbostic break; 32934057Sbostic for (; *arg; ++arg) 33034057Sbostic arg[0] = arg[1]; 33134057Sbostic execvp(name, argv); 33242402Sbostic (void)fprintf(stderr, "%s: Command not found.\n", name); 33334057Sbostic exit(1); 33434057Sbostic } 33534057Sbostic 33634057Sbostic /* 33734057Sbostic * usage -- 33840390Sbostic * print usage message and die 33934057Sbostic */ 34034057Sbostic usage() 34134057Sbostic { 34240390Sbostic (void)fprintf(stderr, 34340390Sbostic "usage: man [-ac] [-M path] [-m path] [section] title ...\n"); 34434057Sbostic exit(1); 34534057Sbostic } 346