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*65291Sbostic static char sccsid[] = "@(#)man.c 8.11 (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 3865289Sbostic static void build_page __P((char *, char **)); 3965283Sbostic static void cat __P((char *)); 4065283Sbostic static char *check_pager __P((char *)); 41*65291Sbostic static int cleanup __P((void)); 4265283Sbostic static void how __P((char *)); 4365283Sbostic static void jump __P((char **, char *, char *)); 44*65291Sbostic 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; 55*65291Sbostic TAG *defp, *defnewp, *section, *sectnewp, *subp; 56*65291Sbostic 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 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; 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 121*65291Sbostic /* Get the machine type. */ 122*65291Sbostic if ((machine = getenv("MACHINE")) == NULL) 123*65291Sbostic machine = MACHINE; 124*65291Sbostic 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) { 137*65291Sbostic while ((e_defp = defp->list.tqh_first) != NULL) { 138*65291Sbostic free(e_defp->s); 139*65291Sbostic 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] == '/' ? "" : "/"; 144*65291Sbostic e_subp = (subp = getlist("_subdir")) == NULL ? 145*65291Sbostic NULL : subp->list.tqh_first; 146*65291Sbostic for (; e_subp != NULL; e_subp = e_subp->q.tqe_next) { 14765283Sbostic (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,}", 148*65291Sbostic p, slashp, e_subp->s, machine); 149*65291Sbostic if ((ep = malloc(sizeof(ENTRY))) == NULL || 150*65291Sbostic (ep->s = strdup(buf)) == NULL) 15165283Sbostic err(1, NULL); 152*65291Sbostic 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 */ 16165283Sbostic if ((section = getlist(*argv)) != NULL) 16265283Sbostic ++argv; 16365283Sbostic if (p_path == NULL && section == NULL) { 16465283Sbostic defnewp = addlist("_default_new"); 165*65291Sbostic e_defp = 166*65291Sbostic defp->list.tqh_first == NULL ? NULL : defp->list.tqh_first; 167*65291Sbostic for (; e_defp != NULL; e_defp = e_defp->q.tqe_next) { 168*65291Sbostic slashp = 169*65291Sbostic e_defp->s[strlen(e_defp->s) - 1] == '/' ? "" : "/"; 170*65291Sbostic e_subp = (subp = getlist("_subdir")) == NULL ? 171*65291Sbostic NULL : subp->list.tqh_first; 172*65291Sbostic for (; e_subp != NULL; e_subp = e_subp->q.tqe_next) { 17365283Sbostic (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,}", 174*65291Sbostic e_defp->s, slashp, e_subp->s, machine); 175*65291Sbostic if ((ep = malloc(sizeof(ENTRY))) == NULL || 176*65291Sbostic (ep->s = strdup(buf)) == NULL) 17765283Sbostic err(1, NULL); 178*65291Sbostic TAILQ_INSERT_TAIL(&defnewp->list, ep, q); 17965283Sbostic } 18065283Sbostic } 18165283Sbostic defp = getlist("_default"); 182*65291Sbostic while ((e_defp = defp->list.tqh_first) != NULL) { 183*65291Sbostic free(e_defp->s); 184*65291Sbostic TAILQ_REMOVE(&defp->list, e_defp, q); 18565283Sbostic } 18665283Sbostic free(defp->s); 187*65291Sbostic TAILQ_REMOVE(&head, defp, q); 18865283Sbostic defnewp = getlist("_default_new"); 18965283Sbostic free(defnewp->s); 19065283Sbostic defnewp->s = "_default"; 19165283Sbostic defp = defnewp; 19265283Sbostic } 19344418Strent 19465283Sbostic /* 19565283Sbostic * 3: If the user set the -m option, insert the user's list before 19665283Sbostic * whatever list we have, again appending the _subdir list and 19765283Sbostic * the machine. 19865283Sbostic */ 19965283Sbostic if (p_add != NULL) 20065283Sbostic for (p = strtok(p_add, ":"); p != NULL; p = strtok(NULL, ":")) { 20165283Sbostic slashp = p[strlen(p) - 1] == '/' ? "" : "/"; 202*65291Sbostic e_subp = (subp = getlist("_subdir")) == NULL ? 203*65291Sbostic NULL : subp->list.tqh_first; 204*65291Sbostic for (; e_subp != NULL; e_subp = e_subp->q.tqe_next) { 20565283Sbostic (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,}", 206*65291Sbostic p, slashp, e_subp->s, machine); 207*65291Sbostic if ((ep = malloc(sizeof(ENTRY))) == NULL || 208*65291Sbostic (ep->s = strdup(buf)) == NULL) 20965283Sbostic err(1, NULL); 210*65291Sbostic TAILQ_INSERT_HEAD(&defp->list, ep, q); 21165283Sbostic } 21244418Strent } 21365283Sbostic 21465283Sbostic /* 21565283Sbostic * 4: If none of MANPATH, -M, or -m were specified, and a section was, 21665283Sbostic * rewrite the section's paths (if they have a trailing slash) to 21765283Sbostic * append the _subdir list and the machine. This then becomes the 21865283Sbostic * _default list. 21965283Sbostic */ 22065283Sbostic if (p_path == NULL && p_add == NULL && section != NULL) { 22165283Sbostic sectnewp = addlist("_section_new"); 222*65291Sbostic for (e_sectp = section->list.tqh_first; 223*65291Sbostic e_sectp != NULL; e_sectp = e_sectp->q.tqe_next) { 224*65291Sbostic if (e_sectp->s[strlen(e_sectp->s) - 1] != '/') { 22565285Sbostic (void)snprintf(buf, sizeof(buf), 226*65291Sbostic "%s{/%s,}", e_sectp->s, machine); 227*65291Sbostic if ((ep = malloc(sizeof(ENTRY))) == NULL || 228*65291Sbostic (ep->s = strdup(buf)) == NULL) 22965285Sbostic err(1, NULL); 230*65291Sbostic TAILQ_INSERT_TAIL(§newp->list, ep, q); 23165283Sbostic continue; 23265283Sbostic } 233*65291Sbostic e_subp = (subp = getlist("_subdir")) == NULL ? 234*65291Sbostic NULL : subp->list.tqh_first; 235*65291Sbostic for (; e_subp != NULL; e_subp = e_subp->q.tqe_next) { 236*65291Sbostic (void)snprintf(buf, sizeof(buf), "%s%s{/%s,}", 237*65291Sbostic e_sectp->s, e_subp->s, machine); 238*65291Sbostic if ((ep = malloc(sizeof(ENTRY))) == NULL || 239*65291Sbostic (ep->s = strdup(buf)) == NULL) 24065283Sbostic err(1, NULL); 241*65291Sbostic TAILQ_INSERT_TAIL(§newp->list, ep, q); 24265283Sbostic } 24365283Sbostic } 24465283Sbostic sectnewp->s = section->s; 24565283Sbostic defp = sectnewp; 246*65291Sbostic TAILQ_REMOVE(&head, section, q); 24742402Sbostic } 24840390Sbostic 24965283Sbostic /* 25065283Sbostic * 5: Search for the files. Set up an interrupt handler, so the 25165283Sbostic * temporary files go away. 25265283Sbostic */ 25365283Sbostic (void)signal(SIGINT, onsig); 254*65291Sbostic (void)signal(SIGHUP, onsig); 25565283Sbostic 25665283Sbostic memset(&pg, 0, sizeof(pg)); 25765283Sbostic for (found = 0; *argv; ++argv) 25865283Sbostic if (manual(*argv, defp, &pg)) 25965283Sbostic found = 1; 26065283Sbostic 26165290Sbostic /* 6: If nothing found, we're done. */ 26265283Sbostic if (!found) { 263*65291Sbostic (void)cleanup(); 26465283Sbostic exit (1); 26542402Sbostic } 26642402Sbostic 26765290Sbostic /* 7: If it's simple, display it fast. */ 26865283Sbostic if (f_cat) { 26965283Sbostic for (ap = pg.gl_pathv; *ap != NULL; ++ap) { 27065287Sbostic if (**ap == '\0') 27165283Sbostic continue; 27265283Sbostic cat(*ap); 27365283Sbostic } 274*65291Sbostic exit (cleanup()); 27565283Sbostic } 27665283Sbostic if (f_how) { 27765283Sbostic for (ap = pg.gl_pathv; *ap != NULL; ++ap) { 27865287Sbostic if (**ap == '\0') 27965283Sbostic continue; 28065283Sbostic how(*ap); 28165283Sbostic } 282*65291Sbostic exit(cleanup()); 28365283Sbostic } 28465283Sbostic if (f_where) { 28565283Sbostic for (ap = pg.gl_pathv; *ap != NULL; ++ap) { 28665287Sbostic if (**ap == '\0') 28765283Sbostic continue; 28865283Sbostic (void)printf("%s\n", *ap); 28965283Sbostic } 290*65291Sbostic exit(cleanup()); 29165283Sbostic } 29265283Sbostic 29365283Sbostic /* 29465290Sbostic * 8: We display things in a single command; build a list of things 29565283Sbostic * to display. 29665283Sbostic */ 29765283Sbostic for (ap = pg.gl_pathv, len = strlen(pager) + 1; *ap != NULL; ++ap) { 29865283Sbostic if (**ap == '\0') 29965283Sbostic continue; 30065283Sbostic len += strlen(*ap) + 1; 30165283Sbostic } 30265283Sbostic if ((cmd = malloc(len)) == NULL) { 303*65291Sbostic warn(NULL); 304*65291Sbostic (void)cleanup(); 305*65291Sbostic exit(1); 30665283Sbostic } 30765283Sbostic p = cmd; 30865283Sbostic len = strlen(pager); 30965283Sbostic memmove(p, pager, len); 31065283Sbostic p += len; 31165283Sbostic *p++ = ' '; 31265283Sbostic for (ap = pg.gl_pathv; *ap != NULL; ++ap) { 31365283Sbostic if (**ap == '\0') 31465283Sbostic continue; 31565283Sbostic len = strlen(*ap); 31665283Sbostic memmove(p, *ap, len); 31765283Sbostic p += len; 31865283Sbostic *p++ = ' '; 31965283Sbostic } 32065283Sbostic *p = '\0'; 32165283Sbostic 32265283Sbostic /* Use system(3) in case someone's pager is "pager arg1 arg2". */ 32365283Sbostic (void)system(cmd); 32465283Sbostic 325*65291Sbostic exit(cleanup()); 32633354Sbostic } 32731703Sbostic 32840390Sbostic /* 32931778Sbostic * manual -- 33065283Sbostic * Search the manuals for the pages. 33131778Sbostic */ 33265283Sbostic static int 333*65291Sbostic manual(page, tag, pg) 33465283Sbostic char *page; 335*65291Sbostic TAG *tag; 33665283Sbostic glob_t *pg; 33731703Sbostic { 338*65291Sbostic ENTRY *ep, *e_sufp, *e_tag; 339*65291Sbostic TAG *missp, *sufp; 34065283Sbostic int anyfound, cnt, found; 34165283Sbostic char *p, buf[128]; 34231703Sbostic 34365283Sbostic anyfound = 0; 34465283Sbostic buf[0] = '*'; 34565283Sbostic 34665283Sbostic /* For each element in the list... */ 347*65291Sbostic if (tag != NULL) 348*65291Sbostic e_tag = tag->list.tqh_first; 349*65291Sbostic for (; e_tag != NULL; e_tag = e_tag->q.tqe_next) { 350*65291Sbostic (void)snprintf(buf, sizeof(buf), "%s/%s.*", e_tag->s, page); 35165283Sbostic if (glob(buf, 35265286Sbostic GLOB_APPEND | GLOB_BRACE | GLOB_NOSORT | GLOB_QUOTE, 35365286Sbostic NULL, pg)) { 354*65291Sbostic warn("globbing"); 355*65291Sbostic (void)cleanup(); 356*65291Sbostic exit(1); 35765283Sbostic } 35865283Sbostic if (pg->gl_matchc == 0) 35965283Sbostic continue; 36065283Sbostic 36165283Sbostic /* Find out if it's really a man page. */ 36265287Sbostic for (cnt = pg->gl_pathc - pg->gl_matchc; 36365287Sbostic cnt < pg->gl_pathc; ++cnt) { 36465283Sbostic 36565285Sbostic /* 36665285Sbostic * Try the _suffix key words first. 36765285Sbostic * 36865285Sbostic * XXX 36965285Sbostic * Older versions of man.conf didn't have the suffix 37065285Sbostic * key words, it was assumed that everything was a .0. 37165285Sbostic * We just test for .0 first, it's fast and probably 37265285Sbostic * going to hit. 37365285Sbostic */ 37465287Sbostic (void)snprintf(buf, sizeof(buf), "*/%s.0", page); 37565287Sbostic if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) 37665285Sbostic goto easy; 37765285Sbostic 378*65291Sbostic e_sufp = (sufp = getlist("_suffix")) == NULL ? 379*65291Sbostic NULL : sufp->list.tqh_first; 38065283Sbostic for (found = 0; 381*65291Sbostic e_sufp != NULL; e_sufp = e_sufp->q.tqe_next) { 38265283Sbostic (void)snprintf(buf, 383*65291Sbostic sizeof(buf), "*/%s%s", page, e_sufp->s); 38465287Sbostic if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) { 38565283Sbostic found = 1; 38665283Sbostic break; 38765283Sbostic } 38865283Sbostic } 38965283Sbostic if (found) { 39065285Sbostic easy: anyfound = 1; 39165283Sbostic if (!f_all) 39265283Sbostic break; 39342402Sbostic continue; 39463515Sbostic } 39565283Sbostic 39665283Sbostic /* Try the _build key words next. */ 397*65291Sbostic e_sufp = (sufp = getlist("_build")) == NULL ? 398*65291Sbostic NULL : sufp->list.tqh_first; 39965283Sbostic for (found = 0; 400*65291Sbostic e_sufp != NULL; e_sufp = e_sufp->q.tqe_next) { 401*65291Sbostic for (p = e_sufp->s; 40265283Sbostic *p != '\0' && !isspace(*p); ++p); 40365283Sbostic if (*p == '\0') 40465283Sbostic continue; 40565283Sbostic *p = '\0'; 40665283Sbostic (void)snprintf(buf, 407*65291Sbostic sizeof(buf), "*/%s%s", page, e_sufp->s); 40865287Sbostic if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) { 40965289Sbostic if (!f_where) 41065283Sbostic build_page(p + 1, 41165289Sbostic &pg->gl_pathv[cnt]); 41265283Sbostic *p = ' '; 41365283Sbostic found = 1; 41465283Sbostic break; 41565283Sbostic } 41665283Sbostic *p = ' '; 41765283Sbostic } 41865283Sbostic if (found) { 41965283Sbostic anyfound = 1; 42065283Sbostic if (!f_all) 42165283Sbostic break; 42242402Sbostic continue; 42363515Sbostic } 42465283Sbostic 42565283Sbostic /* It's not a man page, forget about it. */ 42665287Sbostic pg->gl_pathv[cnt] = ""; 42742402Sbostic } 42842402Sbostic 42965283Sbostic if (anyfound && !f_all) 43065283Sbostic break; 43131703Sbostic } 43265283Sbostic 43365283Sbostic /* If not found, enter onto the missing list. */ 43465283Sbostic if (!anyfound) { 43565283Sbostic if ((missp = getlist("_missing")) == NULL) 43665283Sbostic missp = addlist("_missing"); 437*65291Sbostic if ((ep = malloc(sizeof(ENTRY))) == NULL || 438*65291Sbostic (ep->s = strdup(page)) == NULL) { 439*65291Sbostic warn(NULL); 440*65291Sbostic (void)cleanup(); 441*65291Sbostic exit(1); 44265283Sbostic } 443*65291Sbostic TAILQ_INSERT_TAIL(&missp->list, ep, q); 44465283Sbostic } 44565283Sbostic return (anyfound); 44631703Sbostic } 44731703Sbostic 44865283Sbostic /* 44965283Sbostic * build_page -- 45065283Sbostic * Build a man page for display. 45165283Sbostic */ 45265283Sbostic static void 45365289Sbostic build_page(fmt, pathp) 45465289Sbostic char *fmt, **pathp; 45565283Sbostic { 45665283Sbostic static int warned; 457*65291Sbostic ENTRY *ep; 458*65291Sbostic TAG *intmpp; 45965283Sbostic int fd; 46065283Sbostic char buf[MAXPATHLEN], cmd[MAXPATHLEN], tpath[sizeof(_PATH_TMP)]; 46165283Sbostic 46265283Sbostic /* Let the user know this may take awhile. */ 46365283Sbostic if (!warned) { 46465283Sbostic warned = 1; 46565283Sbostic warnx("Formatting manual page..."); 46665283Sbostic } 46765283Sbostic 46865289Sbostic /* Add a remove-when-done list. */ 46965283Sbostic if ((intmpp = getlist("_intmp")) == NULL) 47065283Sbostic intmpp = addlist("_intmp"); 47165283Sbostic 47265283Sbostic /* Move to the printf(3) format string. */ 47365283Sbostic for (; *fmt && isspace(*fmt); ++fmt); 47465283Sbostic 47565283Sbostic /* 47665289Sbostic * Get a temporary file and build a version of the file 47765289Sbostic * to display. Replace the old file name with the new one. 47865283Sbostic */ 47965283Sbostic (void)strcpy(tpath, _PATH_TMP); 48065283Sbostic if ((fd = mkstemp(tpath)) == -1) { 481*65291Sbostic warn("%s", tpath); 482*65291Sbostic (void)cleanup(); 483*65291Sbostic exit(1); 48465283Sbostic } 48565283Sbostic (void)snprintf(buf, sizeof(buf), "%s > %s", fmt, tpath); 48665289Sbostic (void)snprintf(cmd, sizeof(cmd), buf, *pathp); 48765283Sbostic (void)system(cmd); 48865283Sbostic (void)close(fd); 48965289Sbostic if ((*pathp = strdup(tpath)) == NULL) { 490*65291Sbostic warn(NULL); 491*65291Sbostic (void)cleanup(); 492*65291Sbostic exit(1); 49365283Sbostic } 49465289Sbostic 49565289Sbostic /* Link the built file into the remove-when-done list. */ 496*65291Sbostic if ((ep = malloc(sizeof(ENTRY))) == NULL) { 497*65291Sbostic warn(NULL); 498*65291Sbostic (void)cleanup(); 499*65291Sbostic exit(1); 50065289Sbostic } 501*65291Sbostic ep->s = *pathp; 502*65291Sbostic TAILQ_INSERT_TAIL(&intmpp->list, ep, q); 50365283Sbostic } 50465283Sbostic 50531778Sbostic /* 50644938Sbostic * how -- 50744938Sbostic * display how information 50844938Sbostic */ 50965283Sbostic static void 51044938Sbostic how(fname) 51144938Sbostic char *fname; 51244938Sbostic { 513*65291Sbostic FILE *fp; 51444938Sbostic 515*65291Sbostic int lcnt, print; 516*65291Sbostic char *p, buf[256]; 51744938Sbostic 51844938Sbostic if (!(fp = fopen(fname, "r"))) { 519*65291Sbostic warn("%s", fname); 520*65291Sbostic (void)cleanup(); 521*65291Sbostic exit (1); 52244938Sbostic } 52344938Sbostic #define S1 "SYNOPSIS" 52444938Sbostic #define S2 "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS" 52544938Sbostic #define D1 "DESCRIPTION" 52644938Sbostic #define D2 "D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN" 52744938Sbostic for (lcnt = print = 0; fgets(buf, sizeof(buf), fp);) { 52844938Sbostic if (!strncmp(buf, S1, sizeof(S1) - 1) || 52944938Sbostic !strncmp(buf, S2, sizeof(S2) - 1)) { 53044938Sbostic print = 1; 53144938Sbostic continue; 53244938Sbostic } else if (!strncmp(buf, D1, sizeof(D1) - 1) || 53344938Sbostic !strncmp(buf, D2, sizeof(D2) - 1)) 53444938Sbostic return; 53544938Sbostic if (!print) 53644938Sbostic continue; 53744938Sbostic if (*buf == '\n') 53844938Sbostic ++lcnt; 53944938Sbostic else { 54044938Sbostic for(; lcnt; --lcnt) 54144938Sbostic (void)putchar('\n'); 54244938Sbostic for (p = buf; isspace(*p); ++p); 54344938Sbostic (void)fputs(p, stdout); 54444938Sbostic } 54544938Sbostic } 54644938Sbostic (void)fclose(fp); 54744938Sbostic } 54865283Sbostic 54944938Sbostic /* 55033809Sbostic * cat -- 55133809Sbostic * cat out the file 55231778Sbostic */ 55365283Sbostic static void 55433809Sbostic cat(fname) 55533809Sbostic char *fname; 55631703Sbostic { 557*65291Sbostic int fd, n; 558*65291Sbostic char buf[2048]; 55931703Sbostic 56044938Sbostic if ((fd = open(fname, O_RDONLY, 0)) < 0) { 561*65291Sbostic warn("%s", fname); 562*65291Sbostic (void)cleanup(); 563*65291Sbostic exit(1); 56431703Sbostic } 56533809Sbostic while ((n = read(fd, buf, sizeof(buf))) > 0) 56665283Sbostic if (write(STDOUT_FILENO, buf, n) != n) { 567*65291Sbostic warn("write"); 568*65291Sbostic (void)cleanup(); 569*65291Sbostic exit (1); 57033809Sbostic } 57133809Sbostic if (n == -1) { 572*65291Sbostic warn("read"); 573*65291Sbostic (void)cleanup(); 574*65291Sbostic exit(1); 57533809Sbostic } 57633809Sbostic (void)close(fd); 57731703Sbostic } 57831703Sbostic 57931778Sbostic /* 58040390Sbostic * check_pager -- 58140390Sbostic * check the user supplied page information 58240390Sbostic */ 58365283Sbostic static char * 58440390Sbostic check_pager(name) 58540390Sbostic char *name; 58640390Sbostic { 587*65291Sbostic char *p, *save; 58840390Sbostic 58940390Sbostic /* 59040390Sbostic * if the user uses "more", we make it "more -s"; watch out for 59140390Sbostic * PAGER = "mypager /usr/ucb/more" 59240390Sbostic */ 59340390Sbostic for (p = name; *p && !isspace(*p); ++p); 59440390Sbostic for (; p > name && *p != '/'; --p); 59540390Sbostic if (p != name) 59640390Sbostic ++p; 59740390Sbostic 59840390Sbostic /* make sure it's "more", not "morex" */ 59940390Sbostic if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))){ 60040390Sbostic save = name; 60140390Sbostic /* allocate space to add the "-s" */ 60240390Sbostic if (!(name = 60340390Sbostic malloc((u_int)(strlen(save) + sizeof("-s") + 1)))) 60465283Sbostic err(1, NULL); 60540390Sbostic (void)sprintf(name, "%s %s", save, "-s"); 60640390Sbostic } 60740390Sbostic return(name); 60840390Sbostic } 60940390Sbostic 61040390Sbostic /* 61134057Sbostic * jump -- 61234057Sbostic * strip out flag argument and jump 61334057Sbostic */ 61465283Sbostic static void 61534057Sbostic jump(argv, flag, name) 61665283Sbostic char **argv, *flag, *name; 61734057Sbostic { 61865283Sbostic char **arg; 61934057Sbostic 62034057Sbostic argv[0] = name; 62134057Sbostic for (arg = argv + 1; *arg; ++arg) 62234057Sbostic if (!strcmp(*arg, flag)) 62334057Sbostic break; 62434057Sbostic for (; *arg; ++arg) 62534057Sbostic arg[0] = arg[1]; 62634057Sbostic execvp(name, argv); 62742402Sbostic (void)fprintf(stderr, "%s: Command not found.\n", name); 62834057Sbostic exit(1); 62934057Sbostic } 63034057Sbostic 63165283Sbostic /* 63265283Sbostic * onsig -- 63365283Sbostic * If signaled, delete the temporary files. 63465283Sbostic */ 63565283Sbostic static void 63665283Sbostic onsig(signo) 63765283Sbostic int signo; 63865283Sbostic { 639*65291Sbostic (void)cleanup(); 64065283Sbostic 641*65291Sbostic (void)signal(signo, SIG_DFL); 642*65291Sbostic (void)kill(getpid(), signo); 643*65291Sbostic 644*65291Sbostic /* NOTREACHED */ 645*65291Sbostic exit (1); 64665283Sbostic } 64765283Sbostic 64834057Sbostic /* 64965283Sbostic * cleanup -- 65065283Sbostic * Clean up temporary files, show any error messages. 65165283Sbostic */ 652*65291Sbostic static int 65365283Sbostic cleanup() 65465283Sbostic { 655*65291Sbostic TAG *intmpp, *missp; 656*65291Sbostic ENTRY *ep; 657*65291Sbostic int rval; 65865283Sbostic 659*65291Sbostic rval = 0; 660*65291Sbostic ep = (missp = getlist("_missing")) == NULL ? 661*65291Sbostic NULL : missp->list.tqh_first; 662*65291Sbostic if (ep != NULL) 663*65291Sbostic for (; ep != NULL; ep = ep->q.tqe_next) { 664*65291Sbostic warnx("no entry for %s in the manual.", ep->s); 665*65291Sbostic rval = 1; 666*65291Sbostic } 66765283Sbostic 668*65291Sbostic ep = (intmpp = getlist("_intmp")) == NULL ? 669*65291Sbostic NULL : intmpp->list.tqh_first; 670*65291Sbostic for (; ep != NULL; ep = ep->q.tqe_next) 671*65291Sbostic (void)unlink(ep->s); 672*65291Sbostic return (rval); 67365283Sbostic } 67465283Sbostic 67565283Sbostic /* 67634057Sbostic * usage -- 67740390Sbostic * print usage message and die 67834057Sbostic */ 67965283Sbostic static void 68034057Sbostic usage() 68134057Sbostic { 68240390Sbostic (void)fprintf(stderr, 68365284Sbostic "usage: man [-ac] [-C file] [-M path] [-m path] [section] title ...\n"); 68434057Sbostic exit(1); 68534057Sbostic } 686