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*52473Sbostic static char sccsid[] = "@(#)man.c 5.25 (Berkeley) 02/12/92"; 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 2844938Sbostic 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"; 4244938Sbostic 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; 5144938Sbostic case 'h': 5244938Sbostic f_how = 1; 5344938Sbostic 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 8344938Sbostic 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) { 109*52473Sbostic (void)snprintf(buf, sizeof(buf), "%s/", tmp); 11044418Strent for (av = arorder; *av; ++av) 11144418Strent cadd(buf, strlen(buf), *av); 112*52473Sbostic tmp = strtok(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); 17044938Sbostic else if (f_how) 17144938Sbostic 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 /* 18544938Sbostic * how -- 18644938Sbostic * display how information 18744938Sbostic */ 18844938Sbostic how(fname) 18944938Sbostic char *fname; 19044938Sbostic { 19144938Sbostic register FILE *fp; 19244938Sbostic 19344938Sbostic register int lcnt, print; 19444938Sbostic register char *p; 19544938Sbostic char buf[BUFSIZ]; 19644938Sbostic 19744938Sbostic if (!(fp = fopen(fname, "r"))) { 19844938Sbostic (void)fprintf(stderr, "man: %s: %s\n", fname, strerror(errno)); 19944938Sbostic exit(1); 20044938Sbostic } 20144938Sbostic #define S1 "SYNOPSIS" 20244938Sbostic #define S2 "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS" 20344938Sbostic #define D1 "DESCRIPTION" 20444938Sbostic #define D2 "D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN" 20544938Sbostic for (lcnt = print = 0; fgets(buf, sizeof(buf), fp);) { 20644938Sbostic if (!strncmp(buf, S1, sizeof(S1) - 1) || 20744938Sbostic !strncmp(buf, S2, sizeof(S2) - 1)) { 20844938Sbostic print = 1; 20944938Sbostic continue; 21044938Sbostic } else if (!strncmp(buf, D1, sizeof(D1) - 1) || 21144938Sbostic !strncmp(buf, D2, sizeof(D2) - 1)) 21244938Sbostic return; 21344938Sbostic if (!print) 21444938Sbostic continue; 21544938Sbostic if (*buf == '\n') 21644938Sbostic ++lcnt; 21744938Sbostic else { 21844938Sbostic for(; lcnt; --lcnt) 21944938Sbostic (void)putchar('\n'); 22044938Sbostic for (p = buf; isspace(*p); ++p); 22144938Sbostic (void)fputs(p, stdout); 22244938Sbostic } 22344938Sbostic } 22444938Sbostic (void)fclose(fp); 22544938Sbostic } 22644938Sbostic /* 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 23644938Sbostic 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