131703Sbostic /* 263537Sbostic * Copyright (c) 1987, 1993 363537Sbostic * The Regents of the University of California. All rights reserved. 433054Sbostic * 542741Sbostic * %sccs.include.redist.c% 631703Sbostic */ 731703Sbostic 831703Sbostic #ifndef lint 963537Sbostic static char copyright[] = 1063537Sbostic "@(#) Copyright (c) 1987, 1993\n\ 1163537Sbostic The Regents of the University of California. All rights reserved.\n"; 1233054Sbostic #endif /* not lint */ 1331703Sbostic 1431703Sbostic #ifndef lint 15*65282Sbostic static char sccsid[] = "@(#)man.c 8.2 (Berkeley) 01/02/94"; 1633054Sbostic #endif /* not lint */ 1731703Sbostic 1831703Sbostic #include <sys/param.h> 1963536Sbostic 2063536Sbostic #include <ctype.h> 2140390Sbostic #include <errno.h> 2263536Sbostic #include <fcntl.h> 2363536Sbostic #include <stdio.h> 2463536Sbostic #include <stdlib.h> 2540390Sbostic #include <string.h> 2663536Sbostic #include <unistd.h> 2763536Sbostic 2837892Sbostic #include "pathnames.h" 2931703Sbostic 3040390Sbostic extern int errno; 3131703Sbostic 3244938Sbostic int f_all, f_cat, f_how, f_where; 3340390Sbostic char *command, *machine, *p_augment, *p_path, *pager, *progname; 3444597Strent extern char **arorder, *pathbuf; 3534057Sbostic 3631703Sbostic main(argc, argv) 3733354Sbostic int argc; 3833354Sbostic register char **argv; 3931703Sbostic { 4034057Sbostic extern char *optarg; 4134057Sbostic extern int optind; 4242402Sbostic int ch, res; 4344418Strent char *section[2], *check_pager(), *getpath(), **getorder(), *tmp; 4431703Sbostic 4540390Sbostic progname = "man"; 4644938Sbostic while ((ch = getopt(argc, argv, "-acfhkM:m:P:w")) != EOF) 4734057Sbostic switch((char)ch) { 4840390Sbostic case 'a': 4940390Sbostic f_all = 1; 5031703Sbostic break; 5140390Sbostic case 'c': 5240390Sbostic case '-': /* deprecated */ 5340390Sbostic f_cat = 1; 5440390Sbostic break; 5544938Sbostic case 'h': 5644938Sbostic f_how = 1; 5744938Sbostic break; 5840390Sbostic case 'm': 5940390Sbostic p_augment = optarg; 6040390Sbostic break; 6131703Sbostic case 'M': 6231703Sbostic case 'P': /* backward compatibility */ 6340390Sbostic p_path = optarg; 6431703Sbostic break; 6542402Sbostic /* 6642402Sbostic * "man -f" and "man -k" are backward compatible, undocumented 6742402Sbostic * ways of calling whatis(1) and apropos(1). 6842402Sbostic */ 6931703Sbostic case 'f': 7034057Sbostic jump(argv, "-f", "whatis"); 7140390Sbostic /* NOTREACHED */ 7231703Sbostic case 'k': 7334057Sbostic jump(argv, "-k", "apropos"); 7440390Sbostic /* NOTREACHED */ 7532565Sbostic case 'w': 7640390Sbostic f_all = f_where = 1; 7732565Sbostic break; 7831703Sbostic case '?': 7931703Sbostic default: 8034057Sbostic usage(); 8131703Sbostic } 82*65282Sbostic argc -= optind; 8334057Sbostic argv += optind; 8431703Sbostic 8534057Sbostic if (!*argv) 8634057Sbostic usage(); 8733354Sbostic 8844938Sbostic if (!f_cat && !f_how) 8931703Sbostic if (!isatty(1)) 9040390Sbostic f_cat = 1; 9140390Sbostic else if (pager = getenv("PAGER")) 9240390Sbostic pager = check_pager(pager); 9331779Sbostic else 9437892Sbostic pager = _PATH_PAGER; 9540390Sbostic 9631703Sbostic if (!(machine = getenv("MACHINE"))) 9731703Sbostic machine = MACHINE; 9840390Sbostic 9942402Sbostic /* see if checking in a specific section */ 10042402Sbostic if (argc > 1 && getsection(*argv)) { 10142402Sbostic section[0] = *argv++; 10242402Sbostic section[1] = (char *)NULL; 10342402Sbostic } else { 10442402Sbostic section[0] = "_default"; 10542402Sbostic section[1] = (char *)NULL; 10642402Sbostic } 10740390Sbostic 10844418Strent arorder = getorder(); 10944418Strent if (p_path || (p_path = getenv("MANPATH"))) { 11044418Strent char buf[MAXPATHLEN], **av; 11144418Strent 11263515Sbostic tmp = strtok(p_path, ":"); 11344418Strent while (tmp) { 11452473Sbostic (void)snprintf(buf, sizeof(buf), "%s/", tmp); 11544418Strent for (av = arorder; *av; ++av) 11663515Sbostic cadd(buf, strlen(buf), *av); 11763515Sbostic tmp = strtok(NULL, ":"); 11844418Strent } 11944418Strent p_path = pathbuf; 12044418Strent } else if (!(p_path = getpath(section)) && !p_augment) { 12142402Sbostic (void)fprintf(stderr, 12244418Strent "man: no place to search for those manual pages.\n"); 12342402Sbostic exit(1); 12442402Sbostic } 12540390Sbostic 12642402Sbostic for (; *argv; ++argv) { 12742402Sbostic if (p_augment) 12842402Sbostic res = manual(p_augment, *argv); 12942402Sbostic res = manual(p_path, *argv); 13054706Sbostic if (!res && !f_where) 13154706Sbostic (void)fprintf(stderr, 13254706Sbostic "man: no entry for %s in the manual.\n", *argv); 13342402Sbostic } 13442402Sbostic 13534752Sbostic /* use system(3) in case someone's pager is "pager arg1 arg2" */ 13633809Sbostic if (command) 13733809Sbostic (void)system(command); 13833354Sbostic exit(0); 13933354Sbostic } 14031703Sbostic 14140390Sbostic /* 14231778Sbostic * manual -- 14340390Sbostic * given a path, a directory list and a file name, find a file 14440390Sbostic * that matches; check ${directory}/${dir}/{file name} and 14533809Sbostic * ${directory}/${dir}/${machine}/${file name}. 14631778Sbostic */ 14742402Sbostic manual(path, name) 14840390Sbostic char *path, *name; 14931703Sbostic { 15042402Sbostic register int res; 15163515Sbostic register char *cp; 15240390Sbostic char fname[MAXPATHLEN + 1]; 15331703Sbostic 15463515Sbostic for (res = 0; path != NULL && *path != '\0'; path = cp) { 15563515Sbostic if (cp = strchr(path, ':')) { 15663515Sbostic if (cp == path + 1) { /* foo::bar */ 15763515Sbostic ++cp; 15842402Sbostic continue; 15963515Sbostic } 16063515Sbostic *cp = '\0'; 16133809Sbostic } 16263536Sbostic (void)snprintf(fname, sizeof(fname), "%s/%s.0", path, name); 16342402Sbostic if (access(fname, R_OK)) { 16463536Sbostic (void)snprintf(fname, sizeof(fname), 16563536Sbostic "%s/%s/%s.0", path, machine, name); 16663515Sbostic if (access(fname, R_OK)) { 16763536Sbostic if (cp != NULL) 16863536Sbostic *cp++ = ':'; 16942402Sbostic continue; 17063515Sbostic } 17142402Sbostic } 17242402Sbostic 17342402Sbostic if (f_where) 17442402Sbostic (void)printf("man: found in %s.\n", fname); 17542402Sbostic else if (f_cat) 17642402Sbostic cat(fname); 17744938Sbostic else if (f_how) 17844938Sbostic how(fname); 17942402Sbostic else 18042402Sbostic add(fname); 18142402Sbostic if (!f_all) 18242402Sbostic return(1); 18342402Sbostic res = 1; 18463536Sbostic if (cp != NULL) 18563515Sbostic *cp++ = ':'; 18631703Sbostic } 18742402Sbostic return(res); 18831703Sbostic } 18931703Sbostic 19031778Sbostic /* 19144938Sbostic * how -- 19244938Sbostic * display how information 19344938Sbostic */ 19444938Sbostic how(fname) 19544938Sbostic char *fname; 19644938Sbostic { 19744938Sbostic register FILE *fp; 19844938Sbostic 19944938Sbostic register int lcnt, print; 20044938Sbostic register char *p; 20144938Sbostic char buf[BUFSIZ]; 20244938Sbostic 20344938Sbostic if (!(fp = fopen(fname, "r"))) { 20444938Sbostic (void)fprintf(stderr, "man: %s: %s\n", fname, strerror(errno)); 20544938Sbostic exit(1); 20644938Sbostic } 20744938Sbostic #define S1 "SYNOPSIS" 20844938Sbostic #define S2 "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS" 20944938Sbostic #define D1 "DESCRIPTION" 21044938Sbostic #define D2 "D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN" 21144938Sbostic for (lcnt = print = 0; fgets(buf, sizeof(buf), fp);) { 21244938Sbostic if (!strncmp(buf, S1, sizeof(S1) - 1) || 21344938Sbostic !strncmp(buf, S2, sizeof(S2) - 1)) { 21444938Sbostic print = 1; 21544938Sbostic continue; 21644938Sbostic } else if (!strncmp(buf, D1, sizeof(D1) - 1) || 21744938Sbostic !strncmp(buf, D2, sizeof(D2) - 1)) 21844938Sbostic return; 21944938Sbostic if (!print) 22044938Sbostic continue; 22144938Sbostic if (*buf == '\n') 22244938Sbostic ++lcnt; 22344938Sbostic else { 22444938Sbostic for(; lcnt; --lcnt) 22544938Sbostic (void)putchar('\n'); 22644938Sbostic for (p = buf; isspace(*p); ++p); 22744938Sbostic (void)fputs(p, stdout); 22844938Sbostic } 22944938Sbostic } 23044938Sbostic (void)fclose(fp); 23144938Sbostic } 23244938Sbostic /* 23333809Sbostic * cat -- 23433809Sbostic * cat out the file 23531778Sbostic */ 23633809Sbostic cat(fname) 23733809Sbostic char *fname; 23831703Sbostic { 23933809Sbostic register int fd, n; 24033809Sbostic char buf[BUFSIZ]; 24131703Sbostic 24244938Sbostic if ((fd = open(fname, O_RDONLY, 0)) < 0) { 24340390Sbostic (void)fprintf(stderr, "man: %s: %s\n", fname, strerror(errno)); 24433809Sbostic exit(1); 24531703Sbostic } 24633809Sbostic while ((n = read(fd, buf, sizeof(buf))) > 0) 24733809Sbostic if (write(1, buf, n) != n) { 24840390Sbostic (void)fprintf(stderr, 24940390Sbostic "man: write: %s\n", strerror(errno)); 25033809Sbostic exit(1); 25133809Sbostic } 25233809Sbostic if (n == -1) { 25340390Sbostic (void)fprintf(stderr, "man: read: %s\n", strerror(errno)); 25433809Sbostic exit(1); 25533809Sbostic } 25633809Sbostic (void)close(fd); 25731703Sbostic } 25831703Sbostic 25931778Sbostic /* 26033809Sbostic * add -- 26133809Sbostic * add a file name to the list for future paging 26231778Sbostic */ 26333809Sbostic add(fname) 26433354Sbostic char *fname; 26531703Sbostic { 26633809Sbostic static u_int buflen; 26733809Sbostic static int len; 26833809Sbostic static char *cp; 26933809Sbostic int flen; 27031703Sbostic 27133809Sbostic if (!command) { 27240390Sbostic if (!(command = malloc(buflen = 1024))) 27340390Sbostic enomem(); 27433809Sbostic len = strlen(strcpy(command, pager)); 27533809Sbostic cp = command + len; 27633809Sbostic } 27733809Sbostic flen = strlen(fname); 27833809Sbostic if (len + flen + 2 > buflen) { /* +2 == space, EOS */ 27940390Sbostic if (!(command = realloc(command, buflen += 1024))) 28040390Sbostic enomem(); 28133809Sbostic cp = command + len; 28231703Sbostic } 28333809Sbostic *cp++ = ' '; 28433809Sbostic len += flen + 1; /* +1 = space */ 28533809Sbostic (void)strcpy(cp, fname); 28633809Sbostic cp += flen; 28731703Sbostic } 28831703Sbostic 28931778Sbostic /* 29040390Sbostic * check_pager -- 29140390Sbostic * check the user supplied page information 29240390Sbostic */ 29340390Sbostic char * 29440390Sbostic check_pager(name) 29540390Sbostic char *name; 29640390Sbostic { 29740390Sbostic register char *p; 29842402Sbostic char *save; 29940390Sbostic 30040390Sbostic /* 30140390Sbostic * if the user uses "more", we make it "more -s"; watch out for 30240390Sbostic * PAGER = "mypager /usr/ucb/more" 30340390Sbostic */ 30440390Sbostic for (p = name; *p && !isspace(*p); ++p); 30540390Sbostic for (; p > name && *p != '/'; --p); 30640390Sbostic if (p != name) 30740390Sbostic ++p; 30840390Sbostic 30940390Sbostic /* make sure it's "more", not "morex" */ 31040390Sbostic if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))){ 31140390Sbostic save = name; 31240390Sbostic /* allocate space to add the "-s" */ 31340390Sbostic if (!(name = 31440390Sbostic malloc((u_int)(strlen(save) + sizeof("-s") + 1)))) 31540390Sbostic enomem(); 31640390Sbostic (void)sprintf(name, "%s %s", save, "-s"); 31740390Sbostic } 31840390Sbostic return(name); 31940390Sbostic } 32040390Sbostic 32140390Sbostic /* 32234057Sbostic * jump -- 32334057Sbostic * strip out flag argument and jump 32434057Sbostic */ 32534057Sbostic jump(argv, flag, name) 32634057Sbostic char **argv, *name; 32734057Sbostic register char *flag; 32834057Sbostic { 32934057Sbostic register char **arg; 33034057Sbostic 33134057Sbostic argv[0] = name; 33234057Sbostic for (arg = argv + 1; *arg; ++arg) 33334057Sbostic if (!strcmp(*arg, flag)) 33434057Sbostic break; 33534057Sbostic for (; *arg; ++arg) 33634057Sbostic arg[0] = arg[1]; 33734057Sbostic execvp(name, argv); 33842402Sbostic (void)fprintf(stderr, "%s: Command not found.\n", name); 33934057Sbostic exit(1); 34034057Sbostic } 34134057Sbostic 34234057Sbostic /* 34334057Sbostic * usage -- 34440390Sbostic * print usage message and die 34534057Sbostic */ 34634057Sbostic usage() 34734057Sbostic { 34840390Sbostic (void)fprintf(stderr, 34940390Sbostic "usage: man [-ac] [-M path] [-m path] [section] title ...\n"); 35034057Sbostic exit(1); 35134057Sbostic } 352