131703Sbostic /*
2*68213Spendry * Copyright (c) 1987, 1993, 1994, 1995
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[] =
10*68213Spendry "@(#) Copyright (c) 1987, 1993, 1994, 1995\n\
1163537Sbostic The Regents of the University of California. All rights reserved.\n";
1233054Sbostic #endif /* not lint */
1331703Sbostic
1431703Sbostic #ifndef lint
15*68213Spendry static char sccsid[] = "@(#)man.c 8.17 (Berkeley) 01/31/95";
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
main(argc,argv)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
11066379Sbostic if (!f_cat && !f_how && !f_where)
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
manual(page,tag,pg)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))
37766825Sbostic goto next;
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 }
39066825Sbostic if (found)
39166825Sbostic goto next;
39265283Sbostic
39365283Sbostic /* Try the _build key words next. */
39465291Sbostic e_sufp = (sufp = getlist("_build")) == NULL ?
39565291Sbostic NULL : sufp->list.tqh_first;
39665283Sbostic for (found = 0;
39765291Sbostic e_sufp != NULL; e_sufp = e_sufp->q.tqe_next) {
39865291Sbostic for (p = e_sufp->s;
39965283Sbostic *p != '\0' && !isspace(*p); ++p);
40065283Sbostic if (*p == '\0')
40165283Sbostic continue;
40265283Sbostic *p = '\0';
40365283Sbostic (void)snprintf(buf,
40465291Sbostic sizeof(buf), "*/%s%s", page, e_sufp->s);
40565287Sbostic if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) {
40665289Sbostic if (!f_where)
40765283Sbostic build_page(p + 1,
40865289Sbostic &pg->gl_pathv[cnt]);
40965283Sbostic *p = ' ';
41065283Sbostic found = 1;
41165283Sbostic break;
41265283Sbostic }
41365283Sbostic *p = ' ';
41465283Sbostic }
41565283Sbostic if (found) {
41666825Sbostic next: anyfound = 1;
41766825Sbostic if (!f_all) {
41866825Sbostic /* Delete any other matches. */
41966825Sbostic while (++cnt< pg->gl_pathc)
42066825Sbostic pg->gl_pathv[cnt] = "";
42165283Sbostic break;
42266825Sbostic }
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
build_page(fmt,pathp)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
how(fname)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
cat(fname)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 *
check_pager(name)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
jump(argv,flag,name)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
onsig(signo)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
cleanup()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
usage()68134057Sbostic usage()
68234057Sbostic {
68340390Sbostic (void)fprintf(stderr,
68466033Spendry "usage: man [-achw] [-C file] [-M path] [-m path] [section] title ...\n");
68534057Sbostic exit(1);
68634057Sbostic }
687