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*65285Sbostic static char sccsid[] = "@(#)man.c 8.5 (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; 229*65285Sbostic for (; sectp != NULL; sectp = sectp->list.qe_next) { 23065283Sbostic if (sectp->s[strlen(sectp->s) - 1] != '/') { 231*65285Sbostic (void)snprintf(buf, sizeof(buf), 232*65285Sbostic "%s{/%s,}", sectp->s, machine); 233*65285Sbostic if ((tp = malloc(sizeof(ENTRY))) == NULL || 234*65285Sbostic (tp->s = strdup(buf)) == NULL) 235*65285Sbostic 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) { 28165283Sbostic 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) { 30465283Sbostic 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) { 32765283Sbostic 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, 43065283Sbostic GLOB_APPEND | GLOB_NOSORT | GLOB_BRACE, NULL, pg)) { 43165283Sbostic cleanup(); 43265283Sbostic err(1, "globbing"); 43365283Sbostic } 43465283Sbostic if (pg->gl_matchc == 0) 43565283Sbostic continue; 43665283Sbostic 43765283Sbostic /* Find out if it's really a man page. */ 43865283Sbostic for (cnt = 1; cnt <= pg->gl_matchc; ++cnt) { 43965283Sbostic 440*65285Sbostic /* 441*65285Sbostic * Try the _suffix key words first. 442*65285Sbostic * 443*65285Sbostic * XXX 444*65285Sbostic * Older versions of man.conf didn't have the suffix 445*65285Sbostic * key words, it was assumed that everything was a .0. 446*65285Sbostic * We just test for .0 first, it's fast and probably 447*65285Sbostic * going to hit. 448*65285Sbostic */ 449*65285Sbostic if (!fnmatch("*.0", 450*65285Sbostic pg->gl_pathv[pg->gl_pathc - cnt], 0)) 451*65285Sbostic goto easy; 452*65285Sbostic 45365283Sbostic sufp = getlist("_suffix"); 45465283Sbostic if (sufp != NULL) 45565283Sbostic sufp = sufp->list.qe_next; 45665283Sbostic for (found = 0; 45765283Sbostic sufp != NULL; sufp = sufp->list.qe_next) { 45865283Sbostic (void)snprintf(buf, 45965283Sbostic sizeof(buf), "*%s", sufp->s); 46065283Sbostic if (!fnmatch(buf, 46165283Sbostic pg->gl_pathv[pg->gl_pathc - cnt], 0)) { 46265283Sbostic found = 1; 46365283Sbostic break; 46465283Sbostic } 46565283Sbostic } 46665283Sbostic if (found) { 467*65285Sbostic easy: anyfound = 1; 46865283Sbostic if (!f_all) 46965283Sbostic break; 47042402Sbostic continue; 47163515Sbostic } 47265283Sbostic 47365283Sbostic /* Try the _build key words next. */ 47465283Sbostic sufp = getlist("_build"); 47565283Sbostic if (sufp != NULL) 47665283Sbostic sufp = sufp->list.qe_next; 47765283Sbostic for (found = 0; 47865283Sbostic sufp != NULL; sufp = sufp->list.qe_next) { 47965283Sbostic for (p = sufp->s; 48065283Sbostic *p != '\0' && !isspace(*p); ++p); 48165283Sbostic if (*p == '\0') 48265283Sbostic continue; 48365283Sbostic *p = '\0'; 48465283Sbostic (void)snprintf(buf, 48565283Sbostic sizeof(buf), "*%s", sufp->s); 48665283Sbostic if (!fnmatch(buf, 48765283Sbostic pg->gl_pathv[pg->gl_pathc - cnt], 0)) { 48865283Sbostic if (!f_where) { 48965283Sbostic build_page(p + 1, 49065283Sbostic pg->gl_pathv[pg->gl_pathc - 49165283Sbostic cnt]); 49265283Sbostic pg->gl_pathv[pg->gl_pathc - 49365283Sbostic cnt] = ""; 49465283Sbostic } 49565283Sbostic *p = ' '; 49665283Sbostic found = 1; 49765283Sbostic break; 49865283Sbostic } 49965283Sbostic *p = ' '; 50065283Sbostic } 50165283Sbostic if (found) { 50265283Sbostic anyfound = 1; 50365283Sbostic if (!f_all) 50465283Sbostic break; 50542402Sbostic continue; 50663515Sbostic } 50765283Sbostic 50865283Sbostic /* It's not a man page, forget about it. */ 50965283Sbostic pg->gl_pathv[pg->gl_pathc - cnt] = ""; 51042402Sbostic } 51142402Sbostic 51265283Sbostic if (anyfound && !f_all) 51365283Sbostic break; 51431703Sbostic } 51565283Sbostic 51665283Sbostic /* If not found, enter onto the missing list. */ 51765283Sbostic if (!anyfound) { 51865283Sbostic if ((missp = getlist("_missing")) == NULL) 51965283Sbostic missp = addlist("_missing"); 52065283Sbostic if ((tp = malloc(sizeof(ENTRY))) == NULL || 52165283Sbostic (tp->s = strdup(page)) == NULL) { 52265283Sbostic cleanup(); 52365283Sbostic err(1, NULL); 52465283Sbostic } 52565283Sbostic queue_enter_tail(&missp->list, tp, ENTRY *, list); 52665283Sbostic } 52765283Sbostic return (anyfound); 52831703Sbostic } 52931703Sbostic 53065283Sbostic /* 53165283Sbostic * build_page -- 53265283Sbostic * Build a man page for display. 53365283Sbostic */ 53465283Sbostic static void 53565283Sbostic build_page(fmt, path) 53665283Sbostic char *fmt, *path; 53765283Sbostic { 53865283Sbostic static int warned; 53965283Sbostic ENTRY *intmpp, *tp; 54065283Sbostic int fd; 54165283Sbostic char buf[MAXPATHLEN], cmd[MAXPATHLEN], tpath[sizeof(_PATH_TMP)]; 54265283Sbostic 54365283Sbostic /* Let the user know this may take awhile. */ 54465283Sbostic if (!warned) { 54565283Sbostic warned = 1; 54665283Sbostic warnx("Formatting manual page..."); 54765283Sbostic } 54865283Sbostic 54965283Sbostic /* Add an "in tmp" list. */ 55065283Sbostic if ((intmpp = getlist("_intmp")) == NULL) 55165283Sbostic intmpp = addlist("_intmp"); 55265283Sbostic 55365283Sbostic /* Move to the printf(3) format string. */ 55465283Sbostic for (; *fmt && isspace(*fmt); ++fmt); 55565283Sbostic 55665283Sbostic /* 55765283Sbostic * Get a temporary file and build a version of the file to display. 55865283Sbostic * Link the built file into the list. 55965283Sbostic */ 56065283Sbostic (void)strcpy(tpath, _PATH_TMP); 56165283Sbostic if ((fd = mkstemp(tpath)) == -1) { 56265283Sbostic cleanup(); 56365283Sbostic err(1, "%s", tpath); 56465283Sbostic } 56565283Sbostic (void)snprintf(buf, sizeof(buf), "%s > %s", fmt, tpath); 56665283Sbostic (void)snprintf(cmd, sizeof(cmd), buf, path); 56765283Sbostic (void)system(cmd); 56865283Sbostic (void)close(fd); 56965283Sbostic if ((tp = malloc(sizeof(ENTRY))) == NULL || 57065283Sbostic (tp->s = strdup(tpath)) == NULL) { 57165283Sbostic cleanup(); 57265283Sbostic err(1, NULL); 57365283Sbostic } 57465283Sbostic queue_enter_tail(&intmpp->list, tp, ENTRY *, list); 57565283Sbostic } 57665283Sbostic 57731778Sbostic /* 57844938Sbostic * how -- 57944938Sbostic * display how information 58044938Sbostic */ 58165283Sbostic static void 58244938Sbostic how(fname) 58344938Sbostic char *fname; 58444938Sbostic { 58544938Sbostic register FILE *fp; 58644938Sbostic 58744938Sbostic register int lcnt, print; 58844938Sbostic register char *p; 58944938Sbostic char buf[BUFSIZ]; 59044938Sbostic 59144938Sbostic if (!(fp = fopen(fname, "r"))) { 59265283Sbostic cleanup(); 59365283Sbostic err(1, "%s", fname); 59444938Sbostic } 59544938Sbostic #define S1 "SYNOPSIS" 59644938Sbostic #define S2 "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS" 59744938Sbostic #define D1 "DESCRIPTION" 59844938Sbostic #define D2 "D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN" 59944938Sbostic for (lcnt = print = 0; fgets(buf, sizeof(buf), fp);) { 60044938Sbostic if (!strncmp(buf, S1, sizeof(S1) - 1) || 60144938Sbostic !strncmp(buf, S2, sizeof(S2) - 1)) { 60244938Sbostic print = 1; 60344938Sbostic continue; 60444938Sbostic } else if (!strncmp(buf, D1, sizeof(D1) - 1) || 60544938Sbostic !strncmp(buf, D2, sizeof(D2) - 1)) 60644938Sbostic return; 60744938Sbostic if (!print) 60844938Sbostic continue; 60944938Sbostic if (*buf == '\n') 61044938Sbostic ++lcnt; 61144938Sbostic else { 61244938Sbostic for(; lcnt; --lcnt) 61344938Sbostic (void)putchar('\n'); 61444938Sbostic for (p = buf; isspace(*p); ++p); 61544938Sbostic (void)fputs(p, stdout); 61644938Sbostic } 61744938Sbostic } 61844938Sbostic (void)fclose(fp); 61944938Sbostic } 62065283Sbostic 62144938Sbostic /* 62233809Sbostic * cat -- 62333809Sbostic * cat out the file 62431778Sbostic */ 62565283Sbostic static void 62633809Sbostic cat(fname) 62733809Sbostic char *fname; 62831703Sbostic { 62933809Sbostic register int fd, n; 63033809Sbostic char buf[BUFSIZ]; 63131703Sbostic 63244938Sbostic if ((fd = open(fname, O_RDONLY, 0)) < 0) { 63365283Sbostic cleanup(); 63465283Sbostic err(1, "%s", fname); 63531703Sbostic } 63633809Sbostic while ((n = read(fd, buf, sizeof(buf))) > 0) 63765283Sbostic if (write(STDOUT_FILENO, buf, n) != n) { 63865283Sbostic cleanup(); 63965283Sbostic err(1, "write"); 64033809Sbostic } 64133809Sbostic if (n == -1) { 64265283Sbostic cleanup(); 64365283Sbostic err(1, "read"); 64433809Sbostic } 64533809Sbostic (void)close(fd); 64631703Sbostic } 64731703Sbostic 64831778Sbostic /* 64940390Sbostic * check_pager -- 65040390Sbostic * check the user supplied page information 65140390Sbostic */ 65265283Sbostic static char * 65340390Sbostic check_pager(name) 65440390Sbostic char *name; 65540390Sbostic { 65640390Sbostic register char *p; 65742402Sbostic char *save; 65840390Sbostic 65940390Sbostic /* 66040390Sbostic * if the user uses "more", we make it "more -s"; watch out for 66140390Sbostic * PAGER = "mypager /usr/ucb/more" 66240390Sbostic */ 66340390Sbostic for (p = name; *p && !isspace(*p); ++p); 66440390Sbostic for (; p > name && *p != '/'; --p); 66540390Sbostic if (p != name) 66640390Sbostic ++p; 66740390Sbostic 66840390Sbostic /* make sure it's "more", not "morex" */ 66940390Sbostic if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))){ 67040390Sbostic save = name; 67140390Sbostic /* allocate space to add the "-s" */ 67240390Sbostic if (!(name = 67340390Sbostic malloc((u_int)(strlen(save) + sizeof("-s") + 1)))) 67465283Sbostic err(1, NULL); 67540390Sbostic (void)sprintf(name, "%s %s", save, "-s"); 67640390Sbostic } 67740390Sbostic return(name); 67840390Sbostic } 67940390Sbostic 68040390Sbostic /* 68134057Sbostic * jump -- 68234057Sbostic * strip out flag argument and jump 68334057Sbostic */ 68465283Sbostic static void 68534057Sbostic jump(argv, flag, name) 68665283Sbostic char **argv, *flag, *name; 68734057Sbostic { 68865283Sbostic char **arg; 68934057Sbostic 69034057Sbostic argv[0] = name; 69134057Sbostic for (arg = argv + 1; *arg; ++arg) 69234057Sbostic if (!strcmp(*arg, flag)) 69334057Sbostic break; 69434057Sbostic for (; *arg; ++arg) 69534057Sbostic arg[0] = arg[1]; 69634057Sbostic execvp(name, argv); 69742402Sbostic (void)fprintf(stderr, "%s: Command not found.\n", name); 69834057Sbostic exit(1); 69934057Sbostic } 70034057Sbostic 70165283Sbostic /* 70265283Sbostic * onsig -- 70365283Sbostic * If signaled, delete the temporary files. 70465283Sbostic */ 70565283Sbostic static void 70665283Sbostic onsig(signo) 70765283Sbostic int signo; 70865283Sbostic { 70965283Sbostic cleanup(); 71065283Sbostic 71165283Sbostic (void)signal(SIGINT, SIG_DFL); 71265283Sbostic (void)kill(getpid(), SIGINT); 71365283Sbostic } 71465283Sbostic 71534057Sbostic /* 71665283Sbostic * cleanup -- 71765283Sbostic * Clean up temporary files, show any error messages. 71865283Sbostic */ 71965283Sbostic static void 72065283Sbostic cleanup() 72165283Sbostic { 72265283Sbostic ENTRY *intmpp, *missp; 72365283Sbostic int sverrno; 72465283Sbostic 72565283Sbostic sverrno = errno; 72665283Sbostic 72765283Sbostic missp = getlist("_missing"); 72865283Sbostic if (missp != NULL) 72965283Sbostic missp = missp->list.qe_next; 73065283Sbostic if (missp != NULL) 73165283Sbostic for (; missp != NULL; missp = missp->list.qe_next) 73265283Sbostic warnx("no entry for %s in the manual.", missp->s); 73365283Sbostic 73465283Sbostic intmpp = getlist("_intmp"); 73565283Sbostic if (intmpp != NULL) 73665283Sbostic intmpp = intmpp->list.qe_next; 73765283Sbostic for (; intmpp != NULL; intmpp = intmpp->list.qe_next) 73865283Sbostic (void)unlink(intmpp->s); 73965283Sbostic 74065283Sbostic errno = sverrno; 74165283Sbostic } 74265283Sbostic 74365283Sbostic /* 74434057Sbostic * usage -- 74540390Sbostic * print usage message and die 74634057Sbostic */ 74765283Sbostic static void 74834057Sbostic usage() 74934057Sbostic { 75040390Sbostic (void)fprintf(stderr, 75165284Sbostic "usage: man [-ac] [-C file] [-M path] [-m path] [section] title ...\n"); 75234057Sbostic exit(1); 75334057Sbostic } 754