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*65287Sbostic static char sccsid[] = "@(#)man.c 8.7 (Berkeley) 01/02/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 3865283Sbostic static void build_page __P((char *, char *)); 3965283Sbostic static void cat __P((char *)); 4065283Sbostic static char *check_pager __P((char *)); 4165283Sbostic static void cleanup __P((void)); 4265283Sbostic static void how __P((char *)); 4365283Sbostic static void jump __P((char **, char *, char *)); 4465283Sbostic static int manual __P((char *, ENTRY *, 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; 5565283Sbostic ENTRY *defp, *defnewp, *intmpp; 5665283Sbostic ENTRY *section, *sectp, *sectnewp, *subp, *tp; 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 6365284Sbostic conffile = NULL; 6465283Sbostic f_cat = f_how = 0; 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; 11340390Sbostic else if (pager = getenv("PAGER")) 11440390Sbostic pager = check_pager(pager); 11531779Sbostic else 11637892Sbostic pager = _PATH_PAGER; 11740390Sbostic 11865283Sbostic /* Read the configuration file. */ 11965284Sbostic config(conffile); 12065283Sbostic 12165283Sbostic /* If there's no _default list, create an empty one. */ 12265283Sbostic if ((defp = getlist("_default")) == NULL) 12365283Sbostic defp = addlist("_default"); 12465283Sbostic 12565283Sbostic /* Get the machine type. */ 12665283Sbostic if ((machine = getenv("MACHINE")) == NULL) 12731703Sbostic machine = MACHINE; 12840390Sbostic 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) { 13765283Sbostic while ((tp = defp->list.qe_next) != NULL) { 13865283Sbostic free(tp->s); 13965283Sbostic queue_remove(&defp->list, tp, ENTRY *, list); 14065283Sbostic } 14165283Sbostic for (p = strtok(p_path, ":"); 14265283Sbostic p != NULL; p = strtok(NULL, ":")) { 14365283Sbostic slashp = p[strlen(p) - 1] == '/' ? "" : "/"; 14465283Sbostic subp = getlist("_subdir"); 14565283Sbostic if (subp != NULL) 14665283Sbostic subp = subp->list.qe_next; 14765283Sbostic for (; subp != NULL; subp = subp->list.qe_next) { 14865283Sbostic (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,}", 14965283Sbostic p, slashp, subp->s, machine); 15065283Sbostic if ((tp = malloc(sizeof(ENTRY))) == NULL || 15165283Sbostic (tp->s = strdup(buf)) == NULL) 15265283Sbostic err(1, NULL); 15365283Sbostic queue_enter_tail(&defp->list, 15465283Sbostic tp, ENTRY *, list); 15565283Sbostic } 15665283Sbostic } 15742402Sbostic } 15840390Sbostic 15965283Sbostic /* 16065283Sbostic * 2: If the user did not specify MANPATH, -M or a section, rewrite 16165283Sbostic * the _default list to include the _subdir list and the machine. 16265283Sbostic */ 16365283Sbostic if ((section = getlist(*argv)) != NULL) 16465283Sbostic ++argv; 16565283Sbostic if (p_path == NULL && section == NULL) { 16665283Sbostic defnewp = addlist("_default_new"); 16765283Sbostic if (defp->list.qe_next != NULL) 16865283Sbostic defp = defp->list.qe_next; 16965283Sbostic for (; defp != NULL; defp = defp->list.qe_next) { 17065283Sbostic slashp = defp->s[strlen(defp->s) - 1] == '/' ? "" : "/"; 17165283Sbostic subp = getlist("_subdir"); 17265283Sbostic if (subp != NULL) 17365283Sbostic subp = subp->list.qe_next; 17465283Sbostic for (; subp != NULL; subp = subp->list.qe_next) { 17565283Sbostic (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,}", 17665283Sbostic defp->s, slashp, subp->s, machine); 17765283Sbostic if ((tp = malloc(sizeof(ENTRY))) == NULL || 17865283Sbostic (tp->s = strdup(buf)) == NULL) 17965283Sbostic err(1, NULL); 18065283Sbostic queue_enter_tail(&defnewp->list, 18165283Sbostic tp, ENTRY *, list); 18265283Sbostic } 18365283Sbostic } 18465283Sbostic defp = getlist("_default"); 18565283Sbostic while ((tp = defp->list.qe_next) != NULL) { 18665283Sbostic free(tp->s); 18765283Sbostic queue_remove(&defp->list, tp, ENTRY *, list); 18865283Sbostic } 18965283Sbostic free(defp->s); 19065283Sbostic queue_remove(&defp->tags, defp, ENTRY *, tags); 19165283Sbostic defnewp = getlist("_default_new"); 19265283Sbostic free(defnewp->s); 19365283Sbostic defnewp->s = "_default"; 19465283Sbostic defp = defnewp; 19565283Sbostic } 19644418Strent 19765283Sbostic /* 19865283Sbostic * 3: If the user set the -m option, insert the user's list before 19965283Sbostic * whatever list we have, again appending the _subdir list and 20065283Sbostic * the machine. 20165283Sbostic */ 20265283Sbostic if (p_add != NULL) 20365283Sbostic for (p = strtok(p_add, ":"); p != NULL; p = strtok(NULL, ":")) { 20465283Sbostic slashp = p[strlen(p) - 1] == '/' ? "" : "/"; 20565283Sbostic subp = getlist("_subdir"); 20665283Sbostic if (subp != NULL) 20765283Sbostic subp = subp->list.qe_next; 20865283Sbostic for (; subp != NULL; subp = subp->list.qe_next) { 20965283Sbostic (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,}", 21065283Sbostic p, slashp, subp->s, machine); 21165283Sbostic if ((tp = malloc(sizeof(ENTRY))) == NULL || 21265283Sbostic (tp->s = strdup(buf)) == NULL) 21365283Sbostic err(1, NULL); 21465283Sbostic queue_enter_head(&defp->list, 21565283Sbostic tp, ENTRY *, list); 21665283Sbostic } 21744418Strent } 21865283Sbostic 21965283Sbostic /* 22065283Sbostic * 4: If none of MANPATH, -M, or -m were specified, and a section was, 22165283Sbostic * rewrite the section's paths (if they have a trailing slash) to 22265283Sbostic * append the _subdir list and the machine. This then becomes the 22365283Sbostic * _default list. 22465283Sbostic */ 22565283Sbostic if (p_path == NULL && p_add == NULL && section != NULL) { 22665283Sbostic sectnewp = addlist("_section_new"); 22765283Sbostic if ((sectp = section)->list.qe_next != NULL) 22865283Sbostic sectp = sectp->list.qe_next; 22965285Sbostic for (; sectp != NULL; sectp = sectp->list.qe_next) { 23065283Sbostic if (sectp->s[strlen(sectp->s) - 1] != '/') { 23165285Sbostic (void)snprintf(buf, sizeof(buf), 23265285Sbostic "%s{/%s,}", sectp->s, machine); 23365285Sbostic if ((tp = malloc(sizeof(ENTRY))) == NULL || 23465285Sbostic (tp->s = strdup(buf)) == NULL) 23565285Sbostic err(1, NULL); 23665283Sbostic queue_enter_tail(§newp->list, 23765283Sbostic tp, ENTRY *, list); 23865283Sbostic continue; 23965283Sbostic } 24065283Sbostic subp = getlist("_subdir"); 24165283Sbostic if (subp != NULL) 24265283Sbostic subp = subp->list.qe_next; 24365283Sbostic for (; subp != NULL; subp = subp->list.qe_next) { 24465283Sbostic (void)snprintf(buf, sizeof(buf), 24565283Sbostic "%s%s{/%s,}", sectp->s, subp->s, machine); 24665283Sbostic if ((tp = malloc(sizeof(ENTRY))) == NULL || 24765283Sbostic (tp->s = strdup(buf)) == NULL) 24865283Sbostic err(1, NULL); 24965283Sbostic queue_enter_tail(§newp->list, 25065283Sbostic tp, ENTRY *, list); 25165283Sbostic } 25265283Sbostic } 25365283Sbostic sectnewp->s = section->s; 25465283Sbostic defp = sectnewp; 25565283Sbostic queue_remove(§ion->tags, section, ENTRY *, tags); 25642402Sbostic } 25740390Sbostic 25865283Sbostic /* 25965283Sbostic * 5: Search for the files. Set up an interrupt handler, so the 26065283Sbostic * temporary files go away. 26165283Sbostic */ 26265283Sbostic (void)signal(SIGINT, onsig); 26365283Sbostic 26465283Sbostic memset(&pg, 0, sizeof(pg)); 26565283Sbostic for (found = 0; *argv; ++argv) 26665283Sbostic if (manual(*argv, defp, &pg)) 26765283Sbostic found = 1; 26865283Sbostic 26965283Sbostic /* 27065283Sbostic * 7: If nothing found, we're done. 27165283Sbostic */ 27265283Sbostic if (!found) { 27365283Sbostic cleanup(); 27465283Sbostic exit (1); 27542402Sbostic } 27642402Sbostic 27765283Sbostic /* 8: If it's simple, display it fast. */ 27865283Sbostic if (f_cat) { 27965283Sbostic found = 0; 28065283Sbostic for (ap = pg.gl_pathv; *ap != NULL; ++ap) { 281*65287Sbostic if (**ap == '\0') 28265283Sbostic continue; 28365283Sbostic cat(*ap); 28465283Sbostic if (!f_all) { 28565283Sbostic found = 1; 28665283Sbostic break; 28765283Sbostic } 28865283Sbostic } 28965283Sbostic if (!found) { 29065283Sbostic if (intmpp != NULL) 29165283Sbostic intmpp = intmpp->list.qe_next; 29265283Sbostic for (; intmpp != NULL; intmpp = intmpp->list.qe_next) { 29365283Sbostic cat(intmpp->s); 29465283Sbostic if (!f_all) 29565283Sbostic break; 29665283Sbostic } 29765283Sbostic } 29865283Sbostic cleanup(); 29965283Sbostic exit (0); 30065283Sbostic } 30165283Sbostic if (f_how) { 30265283Sbostic found = 0; 30365283Sbostic for (ap = pg.gl_pathv; *ap != NULL; ++ap) { 304*65287Sbostic if (**ap == '\0') 30565283Sbostic continue; 30665283Sbostic how(*ap); 30765283Sbostic if (!f_all) { 30865283Sbostic found = 1; 30965283Sbostic break; 31065283Sbostic } 31165283Sbostic } 31265283Sbostic if (!found) { 31365283Sbostic intmpp = getlist("_intmp"); 31465283Sbostic if (intmpp != NULL) 31565283Sbostic intmpp = intmpp->list.qe_next; 31665283Sbostic for (; intmpp != NULL; intmpp = intmpp->list.qe_next) { 31765283Sbostic how(intmpp->s); 31865283Sbostic if (!f_all) 31965283Sbostic break; 32065283Sbostic } 32165283Sbostic } 32265283Sbostic cleanup(); 32365283Sbostic exit (0); 32465283Sbostic } 32565283Sbostic if (f_where) { 32665283Sbostic for (ap = pg.gl_pathv; *ap != NULL; ++ap) { 327*65287Sbostic if (**ap == '\0') 32865283Sbostic continue; 32965283Sbostic (void)printf("%s\n", *ap); 33065283Sbostic } 33165283Sbostic intmpp = getlist("_intmp"); 33265283Sbostic if (intmpp != NULL) 33365283Sbostic intmpp = intmpp->list.qe_next; 33465283Sbostic for (; intmpp != NULL; intmpp = intmpp->list.qe_next) 33565283Sbostic (void)printf("%s\n", intmpp->s); 33665283Sbostic cleanup(); 33765283Sbostic exit (0); 33865283Sbostic } 33965283Sbostic 34065283Sbostic /* 34165283Sbostic * 9: We display things in a single command; build a list of things 34265283Sbostic * to display. 34365283Sbostic */ 34465283Sbostic found = 0; 34565283Sbostic for (ap = pg.gl_pathv, len = strlen(pager) + 1; *ap != NULL; ++ap) { 34665283Sbostic if (**ap == '\0') 34765283Sbostic continue; 34865283Sbostic len += strlen(*ap) + 1; 34965283Sbostic if (!f_all) { 35065283Sbostic found = 1; 35165283Sbostic break; 35265283Sbostic } 35365283Sbostic } 35465283Sbostic if (!found) { 35565283Sbostic intmpp = getlist("_intmp"); 35665283Sbostic if (intmpp != NULL) 35765283Sbostic intmpp = intmpp->list.qe_next; 35865283Sbostic for (; intmpp != NULL; intmpp = intmpp->list.qe_next) { 35965283Sbostic len += strlen(intmpp->s); 36065283Sbostic if (!f_all) 36165283Sbostic break; 36265283Sbostic } 36365283Sbostic } 36465283Sbostic 36565283Sbostic if ((cmd = malloc(len)) == NULL) { 36665283Sbostic cleanup(); 36765283Sbostic err(1, NULL); 36865283Sbostic } 36965283Sbostic p = cmd; 37065283Sbostic len = strlen(pager); 37165283Sbostic memmove(p, pager, len); 37265283Sbostic p += len; 37365283Sbostic *p++ = ' '; 37465283Sbostic found = 0; 37565283Sbostic for (ap = pg.gl_pathv; *ap != NULL; ++ap) { 37665283Sbostic if (**ap == '\0') 37765283Sbostic continue; 37865283Sbostic len = strlen(*ap); 37965283Sbostic memmove(p, *ap, len); 38065283Sbostic p += len; 38165283Sbostic *p++ = ' '; 38265283Sbostic if (!f_all) { 38365283Sbostic found = 1; 38465283Sbostic break; 38565283Sbostic } 38665283Sbostic } 38765283Sbostic if (!found) { 38865283Sbostic intmpp = getlist("_intmp"); 38965283Sbostic if (intmpp != NULL) 39065283Sbostic intmpp = intmpp->list.qe_next; 39165283Sbostic for (; intmpp != NULL; intmpp = intmpp->list.qe_next) { 39265283Sbostic len = strlen(intmpp->s); 39365283Sbostic memmove(p, intmpp->s, len); 39465283Sbostic p += len; 39565283Sbostic *p++ = ' '; 39665283Sbostic } 39765283Sbostic } 39865283Sbostic *p = '\0'; 39965283Sbostic 40065283Sbostic /* Use system(3) in case someone's pager is "pager arg1 arg2". */ 40165283Sbostic (void)system(cmd); 40265283Sbostic 40365283Sbostic cleanup(); 40433354Sbostic exit(0); 40533354Sbostic } 40631703Sbostic 40740390Sbostic /* 40831778Sbostic * manual -- 40965283Sbostic * Search the manuals for the pages. 41031778Sbostic */ 41165283Sbostic static int 41265283Sbostic manual(page, list, pg) 41365283Sbostic char *page; 41465283Sbostic ENTRY *list; 41565283Sbostic glob_t *pg; 41631703Sbostic { 41765283Sbostic ENTRY *listp, *missp, *sufp, *tp; 41865283Sbostic int anyfound, cnt, found; 41965283Sbostic char *p, buf[128]; 42031703Sbostic 42165283Sbostic anyfound = 0; 42265283Sbostic buf[0] = '*'; 42365283Sbostic 42465283Sbostic /* For each element in the list... */ 42565283Sbostic if (list != NULL) 42665283Sbostic list = list->list.qe_next; 42765283Sbostic for (listp = list; listp != NULL; listp = listp->list.qe_next) { 42865283Sbostic (void)snprintf(buf, sizeof(buf), "%s/%s.*", listp->s, page); 42965283Sbostic if (glob(buf, 43065286Sbostic GLOB_APPEND | GLOB_BRACE | GLOB_NOSORT | GLOB_QUOTE, 43165286Sbostic NULL, pg)) { 43265283Sbostic cleanup(); 43365283Sbostic err(1, "globbing"); 43465283Sbostic } 43565283Sbostic if (pg->gl_matchc == 0) 43665283Sbostic continue; 43765283Sbostic 43865283Sbostic /* Find out if it's really a man page. */ 439*65287Sbostic for (cnt = pg->gl_pathc - pg->gl_matchc; 440*65287Sbostic cnt < pg->gl_pathc; ++cnt) { 44165283Sbostic 44265285Sbostic /* 44365285Sbostic * Try the _suffix key words first. 44465285Sbostic * 44565285Sbostic * XXX 44665285Sbostic * Older versions of man.conf didn't have the suffix 44765285Sbostic * key words, it was assumed that everything was a .0. 44865285Sbostic * We just test for .0 first, it's fast and probably 44965285Sbostic * going to hit. 45065285Sbostic */ 451*65287Sbostic (void)snprintf(buf, sizeof(buf), "*/%s.0", page); 452*65287Sbostic if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) 45365285Sbostic goto easy; 45465285Sbostic 45565283Sbostic sufp = getlist("_suffix"); 45665283Sbostic if (sufp != NULL) 45765283Sbostic sufp = sufp->list.qe_next; 45865283Sbostic for (found = 0; 45965283Sbostic sufp != NULL; sufp = sufp->list.qe_next) { 46065283Sbostic (void)snprintf(buf, 461*65287Sbostic sizeof(buf), "*/%s%s", page, sufp->s); 462*65287Sbostic if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) { 46365283Sbostic found = 1; 46465283Sbostic break; 46565283Sbostic } 46665283Sbostic } 46765283Sbostic if (found) { 46865285Sbostic easy: anyfound = 1; 46965283Sbostic if (!f_all) 47065283Sbostic break; 47142402Sbostic continue; 47263515Sbostic } 47365283Sbostic 47465283Sbostic /* Try the _build key words next. */ 47565283Sbostic sufp = getlist("_build"); 47665283Sbostic if (sufp != NULL) 47765283Sbostic sufp = sufp->list.qe_next; 47865283Sbostic for (found = 0; 47965283Sbostic sufp != NULL; sufp = sufp->list.qe_next) { 48065283Sbostic for (p = sufp->s; 48165283Sbostic *p != '\0' && !isspace(*p); ++p); 48265283Sbostic if (*p == '\0') 48365283Sbostic continue; 48465283Sbostic *p = '\0'; 48565283Sbostic (void)snprintf(buf, 486*65287Sbostic sizeof(buf), "*/%s%s", page, sufp->s); 487*65287Sbostic if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) { 48865283Sbostic if (!f_where) { 48965283Sbostic build_page(p + 1, 490*65287Sbostic pg->gl_pathv[cnt]); 491*65287Sbostic pg->gl_pathv[cnt] = ""; 49265283Sbostic } 49365283Sbostic *p = ' '; 49465283Sbostic found = 1; 49565283Sbostic break; 49665283Sbostic } 49765283Sbostic *p = ' '; 49865283Sbostic } 49965283Sbostic if (found) { 50065283Sbostic anyfound = 1; 50165283Sbostic if (!f_all) 50265283Sbostic break; 50342402Sbostic continue; 50463515Sbostic } 50565283Sbostic 50665283Sbostic /* It's not a man page, forget about it. */ 507*65287Sbostic pg->gl_pathv[cnt] = ""; 50842402Sbostic } 50942402Sbostic 51065283Sbostic if (anyfound && !f_all) 51165283Sbostic break; 51231703Sbostic } 51365283Sbostic 51465283Sbostic /* If not found, enter onto the missing list. */ 51565283Sbostic if (!anyfound) { 51665283Sbostic if ((missp = getlist("_missing")) == NULL) 51765283Sbostic missp = addlist("_missing"); 51865283Sbostic if ((tp = malloc(sizeof(ENTRY))) == NULL || 51965283Sbostic (tp->s = strdup(page)) == NULL) { 52065283Sbostic cleanup(); 52165283Sbostic err(1, NULL); 52265283Sbostic } 52365283Sbostic queue_enter_tail(&missp->list, tp, ENTRY *, list); 52465283Sbostic } 52565283Sbostic return (anyfound); 52631703Sbostic } 52731703Sbostic 52865283Sbostic /* 52965283Sbostic * build_page -- 53065283Sbostic * Build a man page for display. 53165283Sbostic */ 53265283Sbostic static void 53365283Sbostic build_page(fmt, path) 53465283Sbostic char *fmt, *path; 53565283Sbostic { 53665283Sbostic static int warned; 53765283Sbostic ENTRY *intmpp, *tp; 53865283Sbostic int fd; 53965283Sbostic char buf[MAXPATHLEN], cmd[MAXPATHLEN], tpath[sizeof(_PATH_TMP)]; 54065283Sbostic 54165283Sbostic /* Let the user know this may take awhile. */ 54265283Sbostic if (!warned) { 54365283Sbostic warned = 1; 54465283Sbostic warnx("Formatting manual page..."); 54565283Sbostic } 54665283Sbostic 54765283Sbostic /* Add an "in tmp" list. */ 54865283Sbostic if ((intmpp = getlist("_intmp")) == NULL) 54965283Sbostic intmpp = addlist("_intmp"); 55065283Sbostic 55165283Sbostic /* Move to the printf(3) format string. */ 55265283Sbostic for (; *fmt && isspace(*fmt); ++fmt); 55365283Sbostic 55465283Sbostic /* 55565283Sbostic * Get a temporary file and build a version of the file to display. 55665283Sbostic * Link the built file into the list. 55765283Sbostic */ 55865283Sbostic (void)strcpy(tpath, _PATH_TMP); 55965283Sbostic if ((fd = mkstemp(tpath)) == -1) { 56065283Sbostic cleanup(); 56165283Sbostic err(1, "%s", tpath); 56265283Sbostic } 56365283Sbostic (void)snprintf(buf, sizeof(buf), "%s > %s", fmt, tpath); 56465283Sbostic (void)snprintf(cmd, sizeof(cmd), buf, path); 56565283Sbostic (void)system(cmd); 56665283Sbostic (void)close(fd); 56765283Sbostic if ((tp = malloc(sizeof(ENTRY))) == NULL || 56865283Sbostic (tp->s = strdup(tpath)) == NULL) { 56965283Sbostic cleanup(); 57065283Sbostic err(1, NULL); 57165283Sbostic } 57265283Sbostic queue_enter_tail(&intmpp->list, tp, ENTRY *, list); 57365283Sbostic } 57465283Sbostic 57531778Sbostic /* 57644938Sbostic * how -- 57744938Sbostic * display how information 57844938Sbostic */ 57965283Sbostic static void 58044938Sbostic how(fname) 58144938Sbostic char *fname; 58244938Sbostic { 58344938Sbostic register FILE *fp; 58444938Sbostic 58544938Sbostic register int lcnt, print; 58644938Sbostic register char *p; 58744938Sbostic char buf[BUFSIZ]; 58844938Sbostic 58944938Sbostic if (!(fp = fopen(fname, "r"))) { 59065283Sbostic cleanup(); 59165283Sbostic err(1, "%s", fname); 59244938Sbostic } 59344938Sbostic #define S1 "SYNOPSIS" 59444938Sbostic #define S2 "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS" 59544938Sbostic #define D1 "DESCRIPTION" 59644938Sbostic #define D2 "D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN" 59744938Sbostic for (lcnt = print = 0; fgets(buf, sizeof(buf), fp);) { 59844938Sbostic if (!strncmp(buf, S1, sizeof(S1) - 1) || 59944938Sbostic !strncmp(buf, S2, sizeof(S2) - 1)) { 60044938Sbostic print = 1; 60144938Sbostic continue; 60244938Sbostic } else if (!strncmp(buf, D1, sizeof(D1) - 1) || 60344938Sbostic !strncmp(buf, D2, sizeof(D2) - 1)) 60444938Sbostic return; 60544938Sbostic if (!print) 60644938Sbostic continue; 60744938Sbostic if (*buf == '\n') 60844938Sbostic ++lcnt; 60944938Sbostic else { 61044938Sbostic for(; lcnt; --lcnt) 61144938Sbostic (void)putchar('\n'); 61244938Sbostic for (p = buf; isspace(*p); ++p); 61344938Sbostic (void)fputs(p, stdout); 61444938Sbostic } 61544938Sbostic } 61644938Sbostic (void)fclose(fp); 61744938Sbostic } 61865283Sbostic 61944938Sbostic /* 62033809Sbostic * cat -- 62133809Sbostic * cat out the file 62231778Sbostic */ 62365283Sbostic static void 62433809Sbostic cat(fname) 62533809Sbostic char *fname; 62631703Sbostic { 62733809Sbostic register int fd, n; 62833809Sbostic char buf[BUFSIZ]; 62931703Sbostic 63044938Sbostic if ((fd = open(fname, O_RDONLY, 0)) < 0) { 63165283Sbostic cleanup(); 63265283Sbostic err(1, "%s", fname); 63331703Sbostic } 63433809Sbostic while ((n = read(fd, buf, sizeof(buf))) > 0) 63565283Sbostic if (write(STDOUT_FILENO, buf, n) != n) { 63665283Sbostic cleanup(); 63765283Sbostic err(1, "write"); 63833809Sbostic } 63933809Sbostic if (n == -1) { 64065283Sbostic cleanup(); 64165283Sbostic err(1, "read"); 64233809Sbostic } 64333809Sbostic (void)close(fd); 64431703Sbostic } 64531703Sbostic 64631778Sbostic /* 64740390Sbostic * check_pager -- 64840390Sbostic * check the user supplied page information 64940390Sbostic */ 65065283Sbostic static char * 65140390Sbostic check_pager(name) 65240390Sbostic char *name; 65340390Sbostic { 65440390Sbostic register char *p; 65542402Sbostic char *save; 65640390Sbostic 65740390Sbostic /* 65840390Sbostic * if the user uses "more", we make it "more -s"; watch out for 65940390Sbostic * PAGER = "mypager /usr/ucb/more" 66040390Sbostic */ 66140390Sbostic for (p = name; *p && !isspace(*p); ++p); 66240390Sbostic for (; p > name && *p != '/'; --p); 66340390Sbostic if (p != name) 66440390Sbostic ++p; 66540390Sbostic 66640390Sbostic /* make sure it's "more", not "morex" */ 66740390Sbostic if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))){ 66840390Sbostic save = name; 66940390Sbostic /* allocate space to add the "-s" */ 67040390Sbostic if (!(name = 67140390Sbostic malloc((u_int)(strlen(save) + sizeof("-s") + 1)))) 67265283Sbostic err(1, NULL); 67340390Sbostic (void)sprintf(name, "%s %s", save, "-s"); 67440390Sbostic } 67540390Sbostic return(name); 67640390Sbostic } 67740390Sbostic 67840390Sbostic /* 67934057Sbostic * jump -- 68034057Sbostic * strip out flag argument and jump 68134057Sbostic */ 68265283Sbostic static void 68334057Sbostic jump(argv, flag, name) 68465283Sbostic char **argv, *flag, *name; 68534057Sbostic { 68665283Sbostic char **arg; 68734057Sbostic 68834057Sbostic argv[0] = name; 68934057Sbostic for (arg = argv + 1; *arg; ++arg) 69034057Sbostic if (!strcmp(*arg, flag)) 69134057Sbostic break; 69234057Sbostic for (; *arg; ++arg) 69334057Sbostic arg[0] = arg[1]; 69434057Sbostic execvp(name, argv); 69542402Sbostic (void)fprintf(stderr, "%s: Command not found.\n", name); 69634057Sbostic exit(1); 69734057Sbostic } 69834057Sbostic 69965283Sbostic /* 70065283Sbostic * onsig -- 70165283Sbostic * If signaled, delete the temporary files. 70265283Sbostic */ 70365283Sbostic static void 70465283Sbostic onsig(signo) 70565283Sbostic int signo; 70665283Sbostic { 70765283Sbostic cleanup(); 70865283Sbostic 70965283Sbostic (void)signal(SIGINT, SIG_DFL); 71065283Sbostic (void)kill(getpid(), SIGINT); 71165283Sbostic } 71265283Sbostic 71334057Sbostic /* 71465283Sbostic * cleanup -- 71565283Sbostic * Clean up temporary files, show any error messages. 71665283Sbostic */ 71765283Sbostic static void 71865283Sbostic cleanup() 71965283Sbostic { 72065283Sbostic ENTRY *intmpp, *missp; 72165283Sbostic int sverrno; 72265283Sbostic 72365283Sbostic sverrno = errno; 72465283Sbostic 72565283Sbostic missp = getlist("_missing"); 72665283Sbostic if (missp != NULL) 72765283Sbostic missp = missp->list.qe_next; 72865283Sbostic if (missp != NULL) 72965283Sbostic for (; missp != NULL; missp = missp->list.qe_next) 73065283Sbostic warnx("no entry for %s in the manual.", missp->s); 73165283Sbostic 73265283Sbostic intmpp = getlist("_intmp"); 73365283Sbostic if (intmpp != NULL) 73465283Sbostic intmpp = intmpp->list.qe_next; 73565283Sbostic for (; intmpp != NULL; intmpp = intmpp->list.qe_next) 73665283Sbostic (void)unlink(intmpp->s); 73765283Sbostic 73865283Sbostic errno = sverrno; 73965283Sbostic } 74065283Sbostic 74165283Sbostic /* 74234057Sbostic * usage -- 74340390Sbostic * print usage message and die 74434057Sbostic */ 74565283Sbostic static void 74634057Sbostic usage() 74734057Sbostic { 74840390Sbostic (void)fprintf(stderr, 74965284Sbostic "usage: man [-ac] [-C file] [-M path] [-m path] [section] title ...\n"); 75034057Sbostic exit(1); 75134057Sbostic } 752