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*66033Spendry static char sccsid[] = "@(#)man.c 8.14 (Berkeley) 02/08/94"; 1633054Sbostic #endif /* not lint */ 1731703Sbostic 1831703Sbostic #include <sys/param.h> 1965283Sbostic #include <sys/queue.h> 2063536Sbostic 2163536Sbostic #include <ctype.h> 2265283Sbostic #include <err.h> 2340390Sbostic #include <errno.h> 2463536Sbostic #include <fcntl.h> 2565283Sbostic #include <fnmatch.h> 2665283Sbostic #include <glob.h> 2765283Sbostic #include <signal.h> 2863536Sbostic #include <stdio.h> 2963536Sbostic #include <stdlib.h> 3040390Sbostic #include <string.h> 3163536Sbostic #include <unistd.h> 3263536Sbostic 3365283Sbostic #include "config.h" 3437892Sbostic #include "pathnames.h" 3531703Sbostic 3665283Sbostic int f_all, f_where; 3731703Sbostic 3865289Sbostic static void build_page __P((char *, char **)); 3965283Sbostic static void cat __P((char *)); 4065283Sbostic static char *check_pager __P((char *)); 4165291Sbostic static int cleanup __P((void)); 4265283Sbostic static void how __P((char *)); 4365283Sbostic static void jump __P((char **, char *, char *)); 4465291Sbostic static int manual __P((char *, TAG *, glob_t *)); 4565283Sbostic static void onsig __P((int)); 4665283Sbostic static void usage __P((void)); 4734057Sbostic 4865283Sbostic int 4931703Sbostic main(argc, argv) 5033354Sbostic int argc; 5165283Sbostic char *argv[]; 5231703Sbostic { 5334057Sbostic extern char *optarg; 5434057Sbostic extern int optind; 5565291Sbostic TAG *defp, *defnewp, *section, *sectnewp, *subp; 5665291Sbostic ENTRY *e_defp, *e_sectp, *e_subp, *ep; 5765283Sbostic glob_t pg; 5865283Sbostic size_t len; 5965283Sbostic int ch, f_cat, f_how, found; 6065283Sbostic char **ap, *cmd, *machine, *p, *p_add, *p_path, *pager, *slashp; 6165284Sbostic char *conffile, buf[MAXPATHLEN * 2]; 6231703Sbostic 6365283Sbostic f_cat = f_how = 0; 6465292Sbostic conffile = p_add = p_path = NULL; 6565284Sbostic while ((ch = getopt(argc, argv, "-aC:cfhkM:m:P:w")) != EOF) 6665283Sbostic switch (ch) { 6740390Sbostic case 'a': 6840390Sbostic f_all = 1; 6931703Sbostic break; 7065284Sbostic case 'C': 7165284Sbostic conffile = optarg; 7265284Sbostic break; 7340390Sbostic case 'c': 7465283Sbostic case '-': /* Deprecated. */ 7540390Sbostic f_cat = 1; 7640390Sbostic break; 7744938Sbostic case 'h': 7844938Sbostic f_how = 1; 7944938Sbostic break; 8040390Sbostic case 'm': 8165283Sbostic p_add = optarg; 8240390Sbostic break; 8331703Sbostic case 'M': 8465283Sbostic case 'P': /* Backward compatibility. */ 8540390Sbostic p_path = optarg; 8631703Sbostic break; 8742402Sbostic /* 8865283Sbostic * The -f and -k options are backward compatible, 8965283Sbostic * undocumented ways of calling whatis(1) and apropos(1). 9042402Sbostic */ 9131703Sbostic case 'f': 9234057Sbostic jump(argv, "-f", "whatis"); 9340390Sbostic /* NOTREACHED */ 9431703Sbostic case 'k': 9534057Sbostic jump(argv, "-k", "apropos"); 9640390Sbostic /* NOTREACHED */ 9732565Sbostic case 'w': 9840390Sbostic f_all = f_where = 1; 9932565Sbostic break; 10031703Sbostic case '?': 10131703Sbostic default: 10234057Sbostic usage(); 10331703Sbostic } 10465282Sbostic argc -= optind; 10534057Sbostic argv += optind; 10631703Sbostic 10734057Sbostic if (!*argv) 10834057Sbostic usage(); 10933354Sbostic 11044938Sbostic if (!f_cat && !f_how) 11131703Sbostic if (!isatty(1)) 11240390Sbostic f_cat = 1; 11365289Sbostic else if ((pager = getenv("PAGER")) != NULL) 11440390Sbostic pager = check_pager(pager); 11531779Sbostic else 11637892Sbostic pager = _PATH_PAGER; 11740390Sbostic 11865283Sbostic /* Read the configuration file. */ 11965284Sbostic config(conffile); 12065283Sbostic 12165291Sbostic /* Get the machine type. */ 12265291Sbostic if ((machine = getenv("MACHINE")) == NULL) 12365291Sbostic machine = MACHINE; 12465291Sbostic 12565283Sbostic /* If there's no _default list, create an empty one. */ 12665283Sbostic if ((defp = getlist("_default")) == NULL) 12765283Sbostic defp = addlist("_default"); 12865283Sbostic 12965283Sbostic /* 13065283Sbostic * 1: If the user specified a MANPATH variable, or set the -M 13165283Sbostic * option, we replace the _default list with the user's list, 13265283Sbostic * appending the entries in the _subdir list and the machine. 13365283Sbostic */ 13465283Sbostic if (p_path == NULL) 13565283Sbostic p_path = getenv("MANPATH"); 13665283Sbostic if (p_path != NULL) { 13765291Sbostic while ((e_defp = defp->list.tqh_first) != NULL) { 13865291Sbostic free(e_defp->s); 13965291Sbostic TAILQ_REMOVE(&defp->list, e_defp, q); 14065283Sbostic } 14165283Sbostic for (p = strtok(p_path, ":"); 14265283Sbostic p != NULL; p = strtok(NULL, ":")) { 14365283Sbostic slashp = p[strlen(p) - 1] == '/' ? "" : "/"; 14465291Sbostic e_subp = (subp = getlist("_subdir")) == NULL ? 14565291Sbostic NULL : subp->list.tqh_first; 14665291Sbostic for (; e_subp != NULL; e_subp = e_subp->q.tqe_next) { 14765283Sbostic (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,}", 14865291Sbostic p, slashp, e_subp->s, machine); 14965291Sbostic if ((ep = malloc(sizeof(ENTRY))) == NULL || 15065291Sbostic (ep->s = strdup(buf)) == NULL) 15165283Sbostic err(1, NULL); 15265291Sbostic TAILQ_INSERT_TAIL(&defp->list, ep, q); 15365283Sbostic } 15465283Sbostic } 15542402Sbostic } 15640390Sbostic 15765283Sbostic /* 15865283Sbostic * 2: If the user did not specify MANPATH, -M or a section, rewrite 15965283Sbostic * the _default list to include the _subdir list and the machine. 16065283Sbostic */ 16165293Sbostic if (argv[1] == NULL) 16265293Sbostic section = NULL; 16365293Sbostic else if ((section = getlist(*argv)) != NULL) 16465283Sbostic ++argv; 16565283Sbostic if (p_path == NULL && section == NULL) { 16665283Sbostic defnewp = addlist("_default_new"); 16765291Sbostic e_defp = 16865291Sbostic defp->list.tqh_first == NULL ? NULL : defp->list.tqh_first; 16965291Sbostic for (; e_defp != NULL; e_defp = e_defp->q.tqe_next) { 17065291Sbostic slashp = 17165291Sbostic e_defp->s[strlen(e_defp->s) - 1] == '/' ? "" : "/"; 17265291Sbostic e_subp = (subp = getlist("_subdir")) == NULL ? 17365291Sbostic NULL : subp->list.tqh_first; 17465291Sbostic for (; e_subp != NULL; e_subp = e_subp->q.tqe_next) { 17565283Sbostic (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,}", 17665291Sbostic e_defp->s, slashp, e_subp->s, machine); 17765291Sbostic if ((ep = malloc(sizeof(ENTRY))) == NULL || 17865291Sbostic (ep->s = strdup(buf)) == NULL) 17965283Sbostic err(1, NULL); 18065291Sbostic TAILQ_INSERT_TAIL(&defnewp->list, ep, q); 18165283Sbostic } 18265283Sbostic } 18365283Sbostic defp = getlist("_default"); 18465291Sbostic while ((e_defp = defp->list.tqh_first) != NULL) { 18565291Sbostic free(e_defp->s); 18665291Sbostic TAILQ_REMOVE(&defp->list, e_defp, q); 18765283Sbostic } 18865283Sbostic free(defp->s); 18965291Sbostic TAILQ_REMOVE(&head, defp, q); 19065283Sbostic defnewp = getlist("_default_new"); 19165283Sbostic free(defnewp->s); 19265283Sbostic defnewp->s = "_default"; 19365283Sbostic defp = defnewp; 19465283Sbostic } 19544418Strent 19665283Sbostic /* 19765283Sbostic * 3: If the user set the -m option, insert the user's list before 19865283Sbostic * whatever list we have, again appending the _subdir list and 19965283Sbostic * the machine. 20065283Sbostic */ 20165283Sbostic if (p_add != NULL) 20265283Sbostic for (p = strtok(p_add, ":"); p != NULL; p = strtok(NULL, ":")) { 20365283Sbostic slashp = p[strlen(p) - 1] == '/' ? "" : "/"; 20465291Sbostic e_subp = (subp = getlist("_subdir")) == NULL ? 20565291Sbostic NULL : subp->list.tqh_first; 20665291Sbostic for (; e_subp != NULL; e_subp = e_subp->q.tqe_next) { 20765283Sbostic (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,}", 20865291Sbostic p, slashp, e_subp->s, machine); 20965291Sbostic if ((ep = malloc(sizeof(ENTRY))) == NULL || 21065291Sbostic (ep->s = strdup(buf)) == NULL) 21165283Sbostic err(1, NULL); 21265291Sbostic TAILQ_INSERT_HEAD(&defp->list, ep, q); 21365283Sbostic } 21444418Strent } 21565283Sbostic 21665283Sbostic /* 21765283Sbostic * 4: If none of MANPATH, -M, or -m were specified, and a section was, 21865283Sbostic * rewrite the section's paths (if they have a trailing slash) to 21965283Sbostic * append the _subdir list and the machine. This then becomes the 22065283Sbostic * _default list. 22165283Sbostic */ 22265283Sbostic if (p_path == NULL && p_add == NULL && section != NULL) { 22365283Sbostic sectnewp = addlist("_section_new"); 22465291Sbostic for (e_sectp = section->list.tqh_first; 22565291Sbostic e_sectp != NULL; e_sectp = e_sectp->q.tqe_next) { 22665291Sbostic if (e_sectp->s[strlen(e_sectp->s) - 1] != '/') { 22765285Sbostic (void)snprintf(buf, sizeof(buf), 22865291Sbostic "%s{/%s,}", e_sectp->s, machine); 22965291Sbostic if ((ep = malloc(sizeof(ENTRY))) == NULL || 23065291Sbostic (ep->s = strdup(buf)) == NULL) 23165285Sbostic err(1, NULL); 23265291Sbostic TAILQ_INSERT_TAIL(§newp->list, ep, q); 23365283Sbostic continue; 23465283Sbostic } 23565291Sbostic e_subp = (subp = getlist("_subdir")) == NULL ? 23665291Sbostic NULL : subp->list.tqh_first; 23765291Sbostic for (; e_subp != NULL; e_subp = e_subp->q.tqe_next) { 23865291Sbostic (void)snprintf(buf, sizeof(buf), "%s%s{/%s,}", 23965291Sbostic e_sectp->s, e_subp->s, machine); 24065291Sbostic if ((ep = malloc(sizeof(ENTRY))) == NULL || 24165291Sbostic (ep->s = strdup(buf)) == NULL) 24265283Sbostic err(1, NULL); 24365291Sbostic TAILQ_INSERT_TAIL(§newp->list, ep, q); 24465283Sbostic } 24565283Sbostic } 24665283Sbostic sectnewp->s = section->s; 24765283Sbostic defp = sectnewp; 24865291Sbostic TAILQ_REMOVE(&head, section, q); 24942402Sbostic } 25040390Sbostic 25165283Sbostic /* 25265283Sbostic * 5: Search for the files. Set up an interrupt handler, so the 25365283Sbostic * temporary files go away. 25465283Sbostic */ 25565283Sbostic (void)signal(SIGINT, onsig); 25665291Sbostic (void)signal(SIGHUP, onsig); 25765283Sbostic 25865283Sbostic memset(&pg, 0, sizeof(pg)); 25965283Sbostic for (found = 0; *argv; ++argv) 26065283Sbostic if (manual(*argv, defp, &pg)) 26165283Sbostic found = 1; 26265283Sbostic 26365290Sbostic /* 6: If nothing found, we're done. */ 26465283Sbostic if (!found) { 26565291Sbostic (void)cleanup(); 26665283Sbostic exit (1); 26742402Sbostic } 26842402Sbostic 26965290Sbostic /* 7: If it's simple, display it fast. */ 27065283Sbostic if (f_cat) { 27165283Sbostic for (ap = pg.gl_pathv; *ap != NULL; ++ap) { 27265287Sbostic if (**ap == '\0') 27365283Sbostic continue; 27465283Sbostic cat(*ap); 27565283Sbostic } 27665291Sbostic exit (cleanup()); 27765283Sbostic } 27865283Sbostic if (f_how) { 27965283Sbostic for (ap = pg.gl_pathv; *ap != NULL; ++ap) { 28065287Sbostic if (**ap == '\0') 28165283Sbostic continue; 28265283Sbostic how(*ap); 28365283Sbostic } 28465291Sbostic exit(cleanup()); 28565283Sbostic } 28665283Sbostic if (f_where) { 28765283Sbostic for (ap = pg.gl_pathv; *ap != NULL; ++ap) { 28865287Sbostic if (**ap == '\0') 28965283Sbostic continue; 29065283Sbostic (void)printf("%s\n", *ap); 29165283Sbostic } 29265291Sbostic exit(cleanup()); 29365283Sbostic } 29465283Sbostic 29565283Sbostic /* 29665290Sbostic * 8: We display things in a single command; build a list of things 29765283Sbostic * to display. 29865283Sbostic */ 29965283Sbostic for (ap = pg.gl_pathv, len = strlen(pager) + 1; *ap != NULL; ++ap) { 30065283Sbostic if (**ap == '\0') 30165283Sbostic continue; 30265283Sbostic len += strlen(*ap) + 1; 30365283Sbostic } 30465283Sbostic if ((cmd = malloc(len)) == NULL) { 30565291Sbostic warn(NULL); 30665291Sbostic (void)cleanup(); 30765291Sbostic exit(1); 30865283Sbostic } 30965283Sbostic p = cmd; 31065283Sbostic len = strlen(pager); 31165283Sbostic memmove(p, pager, len); 31265283Sbostic p += len; 31365283Sbostic *p++ = ' '; 31465283Sbostic for (ap = pg.gl_pathv; *ap != NULL; ++ap) { 31565283Sbostic if (**ap == '\0') 31665283Sbostic continue; 31765283Sbostic len = strlen(*ap); 31865283Sbostic memmove(p, *ap, len); 31965283Sbostic p += len; 32065283Sbostic *p++ = ' '; 32165283Sbostic } 32265283Sbostic *p = '\0'; 32365283Sbostic 32465283Sbostic /* Use system(3) in case someone's pager is "pager arg1 arg2". */ 32565283Sbostic (void)system(cmd); 32665283Sbostic 32765291Sbostic exit(cleanup()); 32833354Sbostic } 32931703Sbostic 33040390Sbostic /* 33131778Sbostic * manual -- 33265283Sbostic * Search the manuals for the pages. 33331778Sbostic */ 33465283Sbostic static int 33565291Sbostic manual(page, tag, pg) 33665283Sbostic char *page; 33765291Sbostic TAG *tag; 33865283Sbostic glob_t *pg; 33931703Sbostic { 34065291Sbostic ENTRY *ep, *e_sufp, *e_tag; 34165291Sbostic TAG *missp, *sufp; 34265283Sbostic int anyfound, cnt, found; 34365283Sbostic char *p, buf[128]; 34431703Sbostic 34565283Sbostic anyfound = 0; 34665283Sbostic buf[0] = '*'; 34765283Sbostic 34865283Sbostic /* For each element in the list... */ 34965292Sbostic e_tag = tag == NULL ? NULL : tag->list.tqh_first; 35065291Sbostic for (; e_tag != NULL; e_tag = e_tag->q.tqe_next) { 35165291Sbostic (void)snprintf(buf, sizeof(buf), "%s/%s.*", e_tag->s, page); 35265283Sbostic if (glob(buf, 35365286Sbostic GLOB_APPEND | GLOB_BRACE | GLOB_NOSORT | GLOB_QUOTE, 35465286Sbostic NULL, pg)) { 35565291Sbostic warn("globbing"); 35665291Sbostic (void)cleanup(); 35765291Sbostic exit(1); 35865283Sbostic } 35965283Sbostic if (pg->gl_matchc == 0) 36065283Sbostic continue; 36165283Sbostic 36265283Sbostic /* Find out if it's really a man page. */ 36365287Sbostic for (cnt = pg->gl_pathc - pg->gl_matchc; 36465287Sbostic cnt < pg->gl_pathc; ++cnt) { 36565283Sbostic 36665285Sbostic /* 36765285Sbostic * Try the _suffix key words first. 36865285Sbostic * 36965285Sbostic * XXX 37065285Sbostic * Older versions of man.conf didn't have the suffix 37165285Sbostic * key words, it was assumed that everything was a .0. 37265285Sbostic * We just test for .0 first, it's fast and probably 37365285Sbostic * going to hit. 37465285Sbostic */ 37565287Sbostic (void)snprintf(buf, sizeof(buf), "*/%s.0", page); 37665287Sbostic if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) 37765285Sbostic goto easy; 37865285Sbostic 37965291Sbostic e_sufp = (sufp = getlist("_suffix")) == NULL ? 38065291Sbostic NULL : sufp->list.tqh_first; 38165283Sbostic for (found = 0; 38265291Sbostic e_sufp != NULL; e_sufp = e_sufp->q.tqe_next) { 38365283Sbostic (void)snprintf(buf, 38465291Sbostic sizeof(buf), "*/%s%s", page, e_sufp->s); 38565287Sbostic if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) { 38665283Sbostic found = 1; 38765283Sbostic break; 38865283Sbostic } 38965283Sbostic } 39065283Sbostic if (found) { 39165285Sbostic easy: anyfound = 1; 39265283Sbostic if (!f_all) 39365283Sbostic break; 39442402Sbostic continue; 39563515Sbostic } 39665283Sbostic 39765283Sbostic /* Try the _build key words next. */ 39865291Sbostic e_sufp = (sufp = getlist("_build")) == NULL ? 39965291Sbostic NULL : sufp->list.tqh_first; 40065283Sbostic for (found = 0; 40165291Sbostic e_sufp != NULL; e_sufp = e_sufp->q.tqe_next) { 40265291Sbostic for (p = e_sufp->s; 40365283Sbostic *p != '\0' && !isspace(*p); ++p); 40465283Sbostic if (*p == '\0') 40565283Sbostic continue; 40665283Sbostic *p = '\0'; 40765283Sbostic (void)snprintf(buf, 40865291Sbostic sizeof(buf), "*/%s%s", page, e_sufp->s); 40965287Sbostic if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) { 41065289Sbostic if (!f_where) 41165283Sbostic build_page(p + 1, 41265289Sbostic &pg->gl_pathv[cnt]); 41365283Sbostic *p = ' '; 41465283Sbostic found = 1; 41565283Sbostic break; 41665283Sbostic } 41765283Sbostic *p = ' '; 41865283Sbostic } 41965283Sbostic if (found) { 42065283Sbostic anyfound = 1; 42165283Sbostic if (!f_all) 42265283Sbostic break; 42342402Sbostic continue; 42463515Sbostic } 42565283Sbostic 42665283Sbostic /* It's not a man page, forget about it. */ 42765287Sbostic pg->gl_pathv[cnt] = ""; 42842402Sbostic } 42942402Sbostic 43065283Sbostic if (anyfound && !f_all) 43165283Sbostic break; 43231703Sbostic } 43365283Sbostic 43465283Sbostic /* If not found, enter onto the missing list. */ 43565283Sbostic if (!anyfound) { 43665283Sbostic if ((missp = getlist("_missing")) == NULL) 43765283Sbostic missp = addlist("_missing"); 43865291Sbostic if ((ep = malloc(sizeof(ENTRY))) == NULL || 43965291Sbostic (ep->s = strdup(page)) == NULL) { 44065291Sbostic warn(NULL); 44165291Sbostic (void)cleanup(); 44265291Sbostic exit(1); 44365283Sbostic } 44465291Sbostic TAILQ_INSERT_TAIL(&missp->list, ep, q); 44565283Sbostic } 44665283Sbostic return (anyfound); 44731703Sbostic } 44831703Sbostic 44965283Sbostic /* 45065283Sbostic * build_page -- 45165283Sbostic * Build a man page for display. 45265283Sbostic */ 45365283Sbostic static void 45465289Sbostic build_page(fmt, pathp) 45565289Sbostic char *fmt, **pathp; 45665283Sbostic { 45765283Sbostic static int warned; 45865291Sbostic ENTRY *ep; 45965291Sbostic TAG *intmpp; 46065283Sbostic int fd; 46165283Sbostic char buf[MAXPATHLEN], cmd[MAXPATHLEN], tpath[sizeof(_PATH_TMP)]; 46265283Sbostic 46365283Sbostic /* Let the user know this may take awhile. */ 46465283Sbostic if (!warned) { 46565283Sbostic warned = 1; 46665283Sbostic warnx("Formatting manual page..."); 46765283Sbostic } 46865283Sbostic 46965289Sbostic /* Add a remove-when-done list. */ 47065283Sbostic if ((intmpp = getlist("_intmp")) == NULL) 47165283Sbostic intmpp = addlist("_intmp"); 47265283Sbostic 47365283Sbostic /* Move to the printf(3) format string. */ 47465283Sbostic for (; *fmt && isspace(*fmt); ++fmt); 47565283Sbostic 47665283Sbostic /* 47765289Sbostic * Get a temporary file and build a version of the file 47865289Sbostic * to display. Replace the old file name with the new one. 47965283Sbostic */ 48065283Sbostic (void)strcpy(tpath, _PATH_TMP); 48165283Sbostic if ((fd = mkstemp(tpath)) == -1) { 48265291Sbostic warn("%s", tpath); 48365291Sbostic (void)cleanup(); 48465291Sbostic exit(1); 48565283Sbostic } 48665283Sbostic (void)snprintf(buf, sizeof(buf), "%s > %s", fmt, tpath); 48765289Sbostic (void)snprintf(cmd, sizeof(cmd), buf, *pathp); 48865283Sbostic (void)system(cmd); 48965283Sbostic (void)close(fd); 49065289Sbostic if ((*pathp = strdup(tpath)) == NULL) { 49165291Sbostic warn(NULL); 49265291Sbostic (void)cleanup(); 49365291Sbostic exit(1); 49465283Sbostic } 49565289Sbostic 49665289Sbostic /* Link the built file into the remove-when-done list. */ 49765291Sbostic if ((ep = malloc(sizeof(ENTRY))) == NULL) { 49865291Sbostic warn(NULL); 49965291Sbostic (void)cleanup(); 50065291Sbostic exit(1); 50165289Sbostic } 50265291Sbostic ep->s = *pathp; 50365291Sbostic TAILQ_INSERT_TAIL(&intmpp->list, ep, q); 50465283Sbostic } 50565283Sbostic 50631778Sbostic /* 50744938Sbostic * how -- 50844938Sbostic * display how information 50944938Sbostic */ 51065283Sbostic static void 51144938Sbostic how(fname) 51244938Sbostic char *fname; 51344938Sbostic { 51465291Sbostic FILE *fp; 51544938Sbostic 51665291Sbostic int lcnt, print; 51765291Sbostic char *p, buf[256]; 51844938Sbostic 51944938Sbostic if (!(fp = fopen(fname, "r"))) { 52065291Sbostic warn("%s", fname); 52165291Sbostic (void)cleanup(); 52265291Sbostic exit (1); 52344938Sbostic } 52444938Sbostic #define S1 "SYNOPSIS" 52544938Sbostic #define S2 "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS" 52644938Sbostic #define D1 "DESCRIPTION" 52744938Sbostic #define D2 "D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN" 52844938Sbostic for (lcnt = print = 0; fgets(buf, sizeof(buf), fp);) { 52944938Sbostic if (!strncmp(buf, S1, sizeof(S1) - 1) || 53044938Sbostic !strncmp(buf, S2, sizeof(S2) - 1)) { 53144938Sbostic print = 1; 53244938Sbostic continue; 53344938Sbostic } else if (!strncmp(buf, D1, sizeof(D1) - 1) || 53444938Sbostic !strncmp(buf, D2, sizeof(D2) - 1)) 53544938Sbostic return; 53644938Sbostic if (!print) 53744938Sbostic continue; 53844938Sbostic if (*buf == '\n') 53944938Sbostic ++lcnt; 54044938Sbostic else { 54144938Sbostic for(; lcnt; --lcnt) 54244938Sbostic (void)putchar('\n'); 54344938Sbostic for (p = buf; isspace(*p); ++p); 54444938Sbostic (void)fputs(p, stdout); 54544938Sbostic } 54644938Sbostic } 54744938Sbostic (void)fclose(fp); 54844938Sbostic } 54965283Sbostic 55044938Sbostic /* 55133809Sbostic * cat -- 55233809Sbostic * cat out the file 55331778Sbostic */ 55465283Sbostic static void 55533809Sbostic cat(fname) 55633809Sbostic char *fname; 55731703Sbostic { 55865291Sbostic int fd, n; 55965291Sbostic char buf[2048]; 56031703Sbostic 56144938Sbostic if ((fd = open(fname, O_RDONLY, 0)) < 0) { 56265291Sbostic warn("%s", fname); 56365291Sbostic (void)cleanup(); 56465291Sbostic exit(1); 56531703Sbostic } 56633809Sbostic while ((n = read(fd, buf, sizeof(buf))) > 0) 56765283Sbostic if (write(STDOUT_FILENO, buf, n) != n) { 56865291Sbostic warn("write"); 56965291Sbostic (void)cleanup(); 57065291Sbostic exit (1); 57133809Sbostic } 57233809Sbostic if (n == -1) { 57365291Sbostic warn("read"); 57465291Sbostic (void)cleanup(); 57565291Sbostic exit(1); 57633809Sbostic } 57733809Sbostic (void)close(fd); 57831703Sbostic } 57931703Sbostic 58031778Sbostic /* 58140390Sbostic * check_pager -- 58240390Sbostic * check the user supplied page information 58340390Sbostic */ 58465283Sbostic static char * 58540390Sbostic check_pager(name) 58640390Sbostic char *name; 58740390Sbostic { 58865291Sbostic char *p, *save; 58940390Sbostic 59040390Sbostic /* 59140390Sbostic * if the user uses "more", we make it "more -s"; watch out for 59240390Sbostic * PAGER = "mypager /usr/ucb/more" 59340390Sbostic */ 59440390Sbostic for (p = name; *p && !isspace(*p); ++p); 59540390Sbostic for (; p > name && *p != '/'; --p); 59640390Sbostic if (p != name) 59740390Sbostic ++p; 59840390Sbostic 59940390Sbostic /* make sure it's "more", not "morex" */ 60040390Sbostic if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))){ 60140390Sbostic save = name; 60240390Sbostic /* allocate space to add the "-s" */ 60340390Sbostic if (!(name = 60440390Sbostic malloc((u_int)(strlen(save) + sizeof("-s") + 1)))) 60565283Sbostic err(1, NULL); 60640390Sbostic (void)sprintf(name, "%s %s", save, "-s"); 60740390Sbostic } 60840390Sbostic return(name); 60940390Sbostic } 61040390Sbostic 61140390Sbostic /* 61234057Sbostic * jump -- 61334057Sbostic * strip out flag argument and jump 61434057Sbostic */ 61565283Sbostic static void 61634057Sbostic jump(argv, flag, name) 61765283Sbostic char **argv, *flag, *name; 61834057Sbostic { 61965283Sbostic char **arg; 62034057Sbostic 62134057Sbostic argv[0] = name; 62234057Sbostic for (arg = argv + 1; *arg; ++arg) 62334057Sbostic if (!strcmp(*arg, flag)) 62434057Sbostic break; 62534057Sbostic for (; *arg; ++arg) 62634057Sbostic arg[0] = arg[1]; 62734057Sbostic execvp(name, argv); 62842402Sbostic (void)fprintf(stderr, "%s: Command not found.\n", name); 62934057Sbostic exit(1); 63034057Sbostic } 63134057Sbostic 63265283Sbostic /* 63365283Sbostic * onsig -- 63465283Sbostic * If signaled, delete the temporary files. 63565283Sbostic */ 63665283Sbostic static void 63765283Sbostic onsig(signo) 63865283Sbostic int signo; 63965283Sbostic { 64065291Sbostic (void)cleanup(); 64165283Sbostic 64265291Sbostic (void)signal(signo, SIG_DFL); 64365291Sbostic (void)kill(getpid(), signo); 64465291Sbostic 64565291Sbostic /* NOTREACHED */ 64665291Sbostic exit (1); 64765283Sbostic } 64865283Sbostic 64934057Sbostic /* 65065283Sbostic * cleanup -- 65165283Sbostic * Clean up temporary files, show any error messages. 65265283Sbostic */ 65365291Sbostic static int 65465283Sbostic cleanup() 65565283Sbostic { 65665291Sbostic TAG *intmpp, *missp; 65765291Sbostic ENTRY *ep; 65865291Sbostic int rval; 65965283Sbostic 66065291Sbostic rval = 0; 66165291Sbostic ep = (missp = getlist("_missing")) == NULL ? 66265291Sbostic NULL : missp->list.tqh_first; 66365291Sbostic if (ep != NULL) 66465291Sbostic for (; ep != NULL; ep = ep->q.tqe_next) { 66565291Sbostic warnx("no entry for %s in the manual.", ep->s); 66665291Sbostic rval = 1; 66765291Sbostic } 66865283Sbostic 66965291Sbostic ep = (intmpp = getlist("_intmp")) == NULL ? 67065291Sbostic NULL : intmpp->list.tqh_first; 67165291Sbostic for (; ep != NULL; ep = ep->q.tqe_next) 67265291Sbostic (void)unlink(ep->s); 67365291Sbostic return (rval); 67465283Sbostic } 67565283Sbostic 67665283Sbostic /* 67734057Sbostic * usage -- 67840390Sbostic * print usage message and die 67934057Sbostic */ 68065283Sbostic static void 68134057Sbostic usage() 68234057Sbostic { 68340390Sbostic (void)fprintf(stderr, 684*66033Spendry "usage: man [-achw] [-C file] [-M path] [-m path] [section] title ...\n"); 68534057Sbostic exit(1); 68634057Sbostic } 687