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*63515Sbostic static char sccsid[] = "@(#)man.c 5.27 (Berkeley) 06/17/93"; 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 107*63515Sbostic tmp = strtok(p_path, ":"); 10844418Strent while (tmp) { 10952473Sbostic (void)snprintf(buf, sizeof(buf), "%s/", tmp); 11044418Strent for (av = arorder; *av; ++av) 111*63515Sbostic cadd(buf, strlen(buf), *av); 112*63515Sbostic 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); 12554706Sbostic if (!res && !f_where) 12654706Sbostic (void)fprintf(stderr, 12754706Sbostic "man: no entry for %s in the manual.\n", *argv); 12842402Sbostic } 12942402Sbostic 13034752Sbostic /* use system(3) in case someone's pager is "pager arg1 arg2" */ 13133809Sbostic if (command) 13233809Sbostic (void)system(command); 13333354Sbostic exit(0); 13433354Sbostic } 13531703Sbostic 13640390Sbostic /* 13731778Sbostic * manual -- 13840390Sbostic * given a path, a directory list and a file name, find a file 13940390Sbostic * that matches; check ${directory}/${dir}/{file name} and 14033809Sbostic * ${directory}/${dir}/${machine}/${file name}. 14131778Sbostic */ 14242402Sbostic manual(path, name) 14340390Sbostic char *path, *name; 14431703Sbostic { 14542402Sbostic register int res; 146*63515Sbostic register char *cp; 14740390Sbostic char fname[MAXPATHLEN + 1]; 14831703Sbostic 149*63515Sbostic for (res = 0; path != NULL && *path != '\0'; path = cp) { 150*63515Sbostic if (cp = strchr(path, ':')) { 151*63515Sbostic if (cp == path + 1) { /* foo::bar */ 152*63515Sbostic ++cp; 15342402Sbostic continue; 154*63515Sbostic } 155*63515Sbostic *cp = '\0'; 15633809Sbostic } 15742402Sbostic (void)sprintf(fname, "%s/%s.0", path, name); 15842402Sbostic if (access(fname, R_OK)) { 15942402Sbostic (void)sprintf(fname, "%s/%s/%s.0", path, machine, name); 160*63515Sbostic if (access(fname, R_OK)) { 161*63515Sbostic ++cp; 16242402Sbostic continue; 163*63515Sbostic } 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; 177*63515Sbostic if (cp) 178*63515Sbostic *cp++ = ':'; 17931703Sbostic } 18042402Sbostic return(res); 18131703Sbostic } 18231703Sbostic 18331778Sbostic /* 18444938Sbostic * how -- 18544938Sbostic * display how information 18644938Sbostic */ 18744938Sbostic how(fname) 18844938Sbostic char *fname; 18944938Sbostic { 19044938Sbostic register FILE *fp; 19144938Sbostic 19244938Sbostic register int lcnt, print; 19344938Sbostic register char *p; 19444938Sbostic char buf[BUFSIZ]; 19544938Sbostic 19644938Sbostic if (!(fp = fopen(fname, "r"))) { 19744938Sbostic (void)fprintf(stderr, "man: %s: %s\n", fname, strerror(errno)); 19844938Sbostic exit(1); 19944938Sbostic } 20044938Sbostic #define S1 "SYNOPSIS" 20144938Sbostic #define S2 "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS" 20244938Sbostic #define D1 "DESCRIPTION" 20344938Sbostic #define D2 "D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN" 20444938Sbostic for (lcnt = print = 0; fgets(buf, sizeof(buf), fp);) { 20544938Sbostic if (!strncmp(buf, S1, sizeof(S1) - 1) || 20644938Sbostic !strncmp(buf, S2, sizeof(S2) - 1)) { 20744938Sbostic print = 1; 20844938Sbostic continue; 20944938Sbostic } else if (!strncmp(buf, D1, sizeof(D1) - 1) || 21044938Sbostic !strncmp(buf, D2, sizeof(D2) - 1)) 21144938Sbostic return; 21244938Sbostic if (!print) 21344938Sbostic continue; 21444938Sbostic if (*buf == '\n') 21544938Sbostic ++lcnt; 21644938Sbostic else { 21744938Sbostic for(; lcnt; --lcnt) 21844938Sbostic (void)putchar('\n'); 21944938Sbostic for (p = buf; isspace(*p); ++p); 22044938Sbostic (void)fputs(p, stdout); 22144938Sbostic } 22244938Sbostic } 22344938Sbostic (void)fclose(fp); 22444938Sbostic } 22544938Sbostic /* 22633809Sbostic * cat -- 22733809Sbostic * cat out the file 22831778Sbostic */ 22933809Sbostic cat(fname) 23033809Sbostic char *fname; 23131703Sbostic { 23233809Sbostic register int fd, n; 23333809Sbostic char buf[BUFSIZ]; 23431703Sbostic 23544938Sbostic if ((fd = open(fname, O_RDONLY, 0)) < 0) { 23640390Sbostic (void)fprintf(stderr, "man: %s: %s\n", fname, strerror(errno)); 23733809Sbostic exit(1); 23831703Sbostic } 23933809Sbostic while ((n = read(fd, buf, sizeof(buf))) > 0) 24033809Sbostic if (write(1, buf, n) != n) { 24140390Sbostic (void)fprintf(stderr, 24240390Sbostic "man: write: %s\n", strerror(errno)); 24333809Sbostic exit(1); 24433809Sbostic } 24533809Sbostic if (n == -1) { 24640390Sbostic (void)fprintf(stderr, "man: read: %s\n", strerror(errno)); 24733809Sbostic exit(1); 24833809Sbostic } 24933809Sbostic (void)close(fd); 25031703Sbostic } 25131703Sbostic 25231778Sbostic /* 25333809Sbostic * add -- 25433809Sbostic * add a file name to the list for future paging 25531778Sbostic */ 25633809Sbostic add(fname) 25733354Sbostic char *fname; 25831703Sbostic { 25933809Sbostic static u_int buflen; 26033809Sbostic static int len; 26133809Sbostic static char *cp; 26233809Sbostic int flen; 26331703Sbostic 26433809Sbostic if (!command) { 26540390Sbostic if (!(command = malloc(buflen = 1024))) 26640390Sbostic enomem(); 26733809Sbostic len = strlen(strcpy(command, pager)); 26833809Sbostic cp = command + len; 26933809Sbostic } 27033809Sbostic flen = strlen(fname); 27133809Sbostic if (len + flen + 2 > buflen) { /* +2 == space, EOS */ 27240390Sbostic if (!(command = realloc(command, buflen += 1024))) 27340390Sbostic enomem(); 27433809Sbostic cp = command + len; 27531703Sbostic } 27633809Sbostic *cp++ = ' '; 27733809Sbostic len += flen + 1; /* +1 = space */ 27833809Sbostic (void)strcpy(cp, fname); 27933809Sbostic cp += flen; 28031703Sbostic } 28131703Sbostic 28231778Sbostic /* 28340390Sbostic * check_pager -- 28440390Sbostic * check the user supplied page information 28540390Sbostic */ 28640390Sbostic char * 28740390Sbostic check_pager(name) 28840390Sbostic char *name; 28940390Sbostic { 29040390Sbostic register char *p; 29142402Sbostic char *save; 29240390Sbostic 29340390Sbostic /* 29440390Sbostic * if the user uses "more", we make it "more -s"; watch out for 29540390Sbostic * PAGER = "mypager /usr/ucb/more" 29640390Sbostic */ 29740390Sbostic for (p = name; *p && !isspace(*p); ++p); 29840390Sbostic for (; p > name && *p != '/'; --p); 29940390Sbostic if (p != name) 30040390Sbostic ++p; 30140390Sbostic 30240390Sbostic /* make sure it's "more", not "morex" */ 30340390Sbostic if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))){ 30440390Sbostic save = name; 30540390Sbostic /* allocate space to add the "-s" */ 30640390Sbostic if (!(name = 30740390Sbostic malloc((u_int)(strlen(save) + sizeof("-s") + 1)))) 30840390Sbostic enomem(); 30940390Sbostic (void)sprintf(name, "%s %s", save, "-s"); 31040390Sbostic } 31140390Sbostic return(name); 31240390Sbostic } 31340390Sbostic 31440390Sbostic /* 31534057Sbostic * jump -- 31634057Sbostic * strip out flag argument and jump 31734057Sbostic */ 31834057Sbostic jump(argv, flag, name) 31934057Sbostic char **argv, *name; 32034057Sbostic register char *flag; 32134057Sbostic { 32234057Sbostic register char **arg; 32334057Sbostic 32434057Sbostic argv[0] = name; 32534057Sbostic for (arg = argv + 1; *arg; ++arg) 32634057Sbostic if (!strcmp(*arg, flag)) 32734057Sbostic break; 32834057Sbostic for (; *arg; ++arg) 32934057Sbostic arg[0] = arg[1]; 33034057Sbostic execvp(name, argv); 33142402Sbostic (void)fprintf(stderr, "%s: Command not found.\n", name); 33234057Sbostic exit(1); 33334057Sbostic } 33434057Sbostic 33534057Sbostic /* 33634057Sbostic * usage -- 33740390Sbostic * print usage message and die 33834057Sbostic */ 33934057Sbostic usage() 34034057Sbostic { 34140390Sbostic (void)fprintf(stderr, 34240390Sbostic "usage: man [-ac] [-M path] [-m path] [section] title ...\n"); 34334057Sbostic exit(1); 34434057Sbostic } 345