1 /* $NetBSD: man.c,v 1.56 2013/07/30 15:10:04 joerg Exp $ */
4 * Copyright (c) 1987, 1993, 1994, 1995
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/cdefs.h>
35 __COPYRIGHT("@(#) Copyright (c) 1987, 1993, 1994, 1995\
36 The Regents of the University of California. All rights reserved.");
41 static char sccsid[] = "@(#)man.c 8.17 (Berkeley) 1/31/95";
43 __RCSID("$NetBSD: man.c,v 1.56 2013/07/30 15:10:04 joerg Exp $");
47 #include <sys/param.h>
48 #include <sys/queue.h>
50 #include <sys/utsname.h>
67 #include "pathnames.h"
70 #define MAN_DEBUG 0 /* debug path output */
74 * manstate: structure collecting the current global state so we can
75 * easily identify it and pass it to helper functions in one arg.
78 /* command line flags */
79 int all; /* -a: show all matches rather than first */
80 int cat; /* -c: do not use a pager */
81 char *conffile; /* -C: use alternate config file */
82 int how; /* -h: show SYNOPSIS only */
83 char *manpath; /* -M: alternate MANPATH */
84 char *addpath; /* -m: add these dirs to front of manpath */
85 char *pathsearch; /* -S: path of man must contain this string */
86 char *sectionname; /* -s: limit search to a given man section */
87 int where; /* -w: just show paths of all matching files */
88 int getpath; /* -p: print the path of directories containing man pages */
90 /* important tags from the config file */
91 TAG *defaultpath; /* _default: default MANPATH */
92 TAG *subdirs; /* _subdir: default subdir search list */
93 TAG *suffixlist; /* _suffix: for files that can be cat()'d */
94 TAG *buildlist; /* _build: for files that must be built */
96 /* tags for internal use */
97 TAG *intmp; /* _intmp: tmp files we must cleanup */
98 TAG *missinglist; /* _missing: pages we couldn't find */
99 TAG *mymanpath; /* _new_path: final version of MANPATH */
100 TAG *section; /* <sec>: tag for m.sectionname */
102 /* other misc stuff */
103 const char *pager; /* pager to use */
104 size_t pagerlen; /* length of the above */
105 const char *machine; /* machine */
106 const char *machclass; /* machine class */
112 static void build_page(const char *, char **, struct manstate *);
113 static void cat(const char *);
114 static const char *check_pager(const char *);
115 static int cleanup(void);
116 static void how(const char *);
117 static void jump(char **, const char *, const char *) __dead2;
118 static int manual(char *, struct manstate *, glob_t *);
119 static void onsig(int);
120 static void usage(void) __dead2;
121 static void addpath(struct manstate *, const char *, size_t, const char *);
122 static const char *getclass(const char *);
123 static void printmanpath(struct manstate *);
129 main(int argc, char **argv)
131 static struct manstate m;
132 int ch, abs_section, found;
133 ENTRY *esubd, *epath;
138 setprogname(argv[0]);
139 setlocale(LC_ALL, "");
141 * parse command line...
143 while ((ch = getopt(argc, argv, "-aC:cfhkM:m:P:ps:S:w")) != -1)
152 case '-': /* XXX: '-' is a deprecated version of '-c' */
162 case 'P': /* -P for backward compatibility */
163 m.manpath = strdup(optarg);
169 * The -f and -k options are backward compatible,
170 * undocumented ways of calling whatis(1) and apropos(1).
173 jump(argv, "-f", "whatis");
176 jump(argv, "-k", "apropos");
179 if (m.sectionname != NULL)
181 m.sectionname = optarg;
184 m.pathsearch = optarg;
196 if (!m.getpath && !argc)
200 * read the configuration file and collect any other information
201 * we will need (machine type, pager, section [if specified
202 * without '-s'], and MANPATH through the environment).
204 config(m.conffile); /* exits on error ... */
206 if ((m.machine = getenv("MACHINE")) == NULL) {
207 struct utsname utsname;
209 if (uname(&utsname) == -1)
210 err(EXIT_FAILURE, "uname");
211 m.machine = utsname.machine;
214 m.machclass = getclass(m.machine);
216 if (!m.cat && !m.how && !m.where) { /* if we need a pager ... */
217 if (!isatty(STDOUT_FILENO)) {
220 if ((m.pager = getenv("PAGER")) != NULL &&
222 m.pager = check_pager(m.pager);
224 m.pager = _PATH_PAGER;
225 m.pagerlen = strlen(m.pager);
229 /* do we need to set m.section to a non-null value? */
232 m.section = gettag(m.sectionname, 0); /* -s must be a section */
233 if (m.section == NULL)
234 errx(EXIT_FAILURE, "unknown section: %s", m.sectionname);
236 } else if (argc > 1) {
238 m.section = gettag(*argv, 0); /* might be a section? */
246 if (m.manpath == NULL)
247 m.manpath = getenv("MANPATH"); /* note: -M overrides getenv */
251 * get default values from config file, plus create the tags we
252 * use for keeping internal state. make sure all our mallocs
256 m.defaultpath = gettag("_default", 1);
257 m.subdirs = gettag("_subdir", 1);
258 m.suffixlist = gettag("_suffix", 1);
259 m.buildlist = gettag("_build", 1);
261 m.mymanpath = gettag("_new_path", 1);
262 m.missinglist = gettag("_missing", 1);
263 m.intmp = gettag("_intmp", 1);
264 if (!m.defaultpath || !m.subdirs || !m.suffixlist || !m.buildlist ||
265 !m.mymanpath || !m.missinglist || !m.intmp)
266 errx(EXIT_FAILURE, "malloc failed");
269 * are we using a section whose elements are all absolute paths?
270 * (we only need to look at the first entry on the section list,
271 * as config() will ensure that any additional entries will match
274 abs_section = (m.section != NULL &&
275 !TAILQ_EMPTY(&m.section->entrylist) &&
276 *(TAILQ_FIRST(&m.section->entrylist)->s) == '/');
279 * now that we have all the data we need, we must determine the
280 * manpath we are going to use to find the requested entries using
281 * the following steps...
283 * [1] if the user specified a section and that section's elements
284 * from the config file are all absolute paths, then we override
285 * defaultpath and -M/MANPATH with the section's absolute paths.
288 m.manpath = NULL; /* ignore -M/MANPATH */
289 m.defaultpath = m.section; /* overwrite _default path */
290 m.section = NULL; /* promoted to defaultpath */
294 * [2] section can now only be non-null if the user asked for
295 * a section and that section's elements did not have
296 * absolute paths. in this case we use the section's
297 * elements to override _subdir from the config file.
299 * after this step, we are done processing "m.section"...
302 m.subdirs = m.section;
305 * [3] we need to setup the path we want to use (m.mymanpath).
306 * if the user gave us a path (m.manpath) use it, otherwise
307 * go with the default. in either case we need to append
308 * the subdir and machine spec to each element of the path.
310 * for absolute section paths that come from the config file,
311 * we only append the subdir spec if the path ends in
312 * a '/' --- elements that do not end in '/' are assumed to
313 * not have subdirectories. this is mainly for backward compat,
314 * but it allows non-subdir configs like:
315 * sect3 /usr/share/man/{old/,}cat3
316 * doc /usr/{pkg,share}/doc/{sendmail/op,sendmail/intro}
318 * note that we try and be careful to not put double slashes
319 * in the path (e.g. we want /usr/share/man/man1, not
320 * /usr/share/man//man1) because "more" will put the filename
321 * we generate in its prompt and the double slashes look ugly.
325 /* note: strtok is going to destroy m.manpath */
326 for (p = strtok(m.manpath, ":") ; p ; p = strtok(NULL, ":")) {
330 TAILQ_FOREACH(esubd, &m.subdirs->entrylist, q)
331 addpath(&m, p, len, esubd->s);
336 TAILQ_FOREACH(epath, &m.defaultpath->entrylist, q) {
337 /* handle trailing "/" magic here ... */
338 if (abs_section && epath->s[epath->len - 1] != '/') {
339 addpath(&m, "", 1, epath->s);
343 TAILQ_FOREACH(esubd, &m.subdirs->entrylist, q)
344 addpath(&m, epath->s, epath->len, esubd->s);
350 * [4] finally, prepend the "-m" m.addpath to mymanpath if it
351 * was specified. subdirs and machine are always applied to
356 /* note: strtok is going to destroy m.addpath */
357 for (p = strtok(m.addpath, ":") ; p ; p = strtok(NULL, ":")) {
361 TAILQ_FOREACH(esubd, &m.subdirs->entrylist, q)
362 addpath(&m, p, len, esubd->s);
371 * now m.mymanpath is complete!
374 printf("mymanpath:\n");
375 TAILQ_FOREACH(epath, &m.mymanpath->entrylist, q) {
376 printf("\t%s\n", epath->s);
381 * start searching for matching files and format them if necessary.
382 * setup an interrupt handler so that we can ensure that temporary
385 (void)signal(SIGINT, onsig);
386 (void)signal(SIGHUP, onsig);
387 (void)signal(SIGPIPE, onsig);
389 memset(&pg, 0, sizeof(pg));
390 for (found = 0; *argv; ++argv)
391 if (manual(*argv, &m, &pg)) {
395 /* if nothing found, we're done. */
402 * handle the simple display cases first (m.cat, m.how, m.where)
405 for (ap = pg.gl_pathv; *ap != NULL; ++ap) {
413 for (ap = pg.gl_pathv; *ap != NULL; ++ap) {
421 for (ap = pg.gl_pathv; *ap != NULL; ++ap) {
424 (void)printf("%s\n", *ap);
430 * normal case - we display things in a single command, so
431 * build a list of things to display. first compute total
432 * length of buffer we will need so we can malloc it.
434 for (ap = pg.gl_pathv, len = m.pagerlen + 1; *ap != NULL; ++ap) {
437 len += strlen(*ap) + 1;
439 if ((cmd = malloc(len)) == NULL) {
445 /* now build the command string... */
448 memcpy(p, m.pager, len);
451 for (ap = pg.gl_pathv; *ap != NULL; ++ap) {
461 /* Use system(3) in case someone's pager is "pager arg1 arg2". */
468 manual_find_literalfile(struct manstate *mp, glob_t *pg, size_t cnt)
472 char buf[MAXPATHLEN];
479 * Expand both '*' and suffix to force an actual
480 * match via fnmatch(3). Since the only match in pg
481 * is the literal file, the match is genuine.
484 TAILQ_FOREACH(suffix, &mp->buildlist->entrylist, q) {
485 for (p = suffix->s, suflen = 0;
486 *p != '\0' && !isspace((unsigned char)*p);
492 (void)snprintf(buf, sizeof(buf), "*%.*s", suflen, suffix->s);
493 if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) {
495 build_page(p + 1, &pg->gl_pathv[cnt], mp);
505 manual_find_buildkeyword(const char *prefix, const char *escpage,
506 struct manstate *mp, glob_t *pg, size_t cnt)
510 char buf[MAXPATHLEN];
515 /* Try the _build keywords next. */
516 TAILQ_FOREACH(suffix, &mp->buildlist->entrylist, q) {
517 for (p = suffix->s, suflen = 0;
518 *p != '\0' && !isspace((unsigned char)*p);
524 (void)snprintf(buf, sizeof(buf), "%s%s%.*s",
525 prefix, escpage, suflen, suffix->s);
526 if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) {
528 build_page(p + 1, &pg->gl_pathv[cnt], mp);
539 * Search the manuals for the pages.
542 manual(char *page, struct manstate *mp, glob_t *pg)
544 ENTRY *suffix, *mdir;
545 int anyfound, error, found;
547 char *p, buf[MAXPATHLEN], *escpage, *eptr;
548 static const char escglob[] = "\\~?*{}[]";
553 * Fixup page which may contain glob(3) special characters, e.g.
554 * the famous "No man page for [" FAQ.
556 if ((escpage = malloc((2 * strlen(page)) + 1)) == NULL) {
566 if (strchr(escglob, *p) != NULL) {
576 * If 'page' contains a slash then it's
577 * interpreted as a file specification.
579 if (strchr(page, '/')) {
580 /* check if file actually exists */
581 (void)strlcpy(buf, escpage, sizeof(buf));
582 error = glob(buf, GLOB_APPEND | GLOB_BRACE | GLOB_NOSORT, NULL, pg);
584 if (error == GLOB_NOMATCH) {
587 errx(EXIT_FAILURE, "glob failed");
591 if (pg->gl_matchc == 0)
594 /* literal file only yields one match */
595 cnt = pg->gl_pathc - pg->gl_matchc;
597 if (manual_find_literalfile(mp, pg, cnt)) {
600 /* It's not a man page, forget about it. */
601 *pg->gl_pathv[cnt] = '\0';
606 if (addentry(mp->missinglist, page, 0) < 0) {
616 /* For each man directory in mymanpath ... */
617 TAILQ_FOREACH(mdir, &mp->mymanpath->entrylist, q) {
620 * use glob(3) to look in the filesystem for matching files.
621 * match any suffix here, as we will check that later.
623 (void)snprintf(buf, sizeof(buf), "%s/%s.*", mdir->s, escpage);
624 if ((error = glob(buf,
625 GLOB_APPEND | GLOB_BRACE | GLOB_NOSORT, NULL, pg)) != 0) {
626 if (error == GLOB_NOMATCH)
634 if (pg->gl_matchc == 0)
638 * start going through the matches glob(3) just found and
639 * use m.pathsearch (if present) to filter out pages we
640 * don't want. then verify the suffix is valid, and build
641 * the page if we have a _build suffix.
643 for (cnt = pg->gl_pathc - pg->gl_matchc;
644 cnt < pg->gl_pathc; ++cnt) {
646 /* filter on directory path name */
647 if (mp->pathsearch) {
648 p = strstr(pg->gl_pathv[cnt], mp->pathsearch);
649 if (!p || strchr(p, '/') == NULL) {
650 *pg->gl_pathv[cnt] = '\0'; /* zap! */
656 * Try the _suffix keywords first.
659 * Older versions of man.conf didn't have the _suffix
660 * keywords, it was assumed that everything was a .0.
661 * We just test for .0 first, it's fast and probably
664 (void)snprintf(buf, sizeof(buf), "*/%s.0", escpage);
665 if (!fnmatch(buf, pg->gl_pathv[cnt], 0))
669 TAILQ_FOREACH(suffix, &mp->suffixlist->entrylist, q) {
671 sizeof(buf), "*/%s%s", escpage,
673 if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) {
681 /* Try the _build keywords next. */
682 found = manual_find_buildkeyword("*/", escpage,
687 /* Delete any other matches. */
688 while (++cnt< pg->gl_pathc)
689 *pg->gl_pathv[cnt] = '\0';
695 /* It's not a man page, forget about it. */
696 *pg->gl_pathv[cnt] = '\0';
699 if (anyfound && !mp->all)
703 /* If not found, enter onto the missing list. */
705 if (addentry(mp->missinglist, page, 0) < 0) {
718 * Build a man page for display.
721 build_page(const char *fmt, char **pathp, struct manstate *mp)
727 char buf[MAXPATHLEN], cmd[MAXPATHLEN], tpath[MAXPATHLEN];
730 /* Let the user know this may take awhile. */
733 warnx("Formatting manual page...");
737 * Historically man chdir'd to the root of the man tree.
738 * This was used in man pages that contained relative ".so"
739 * directives (including other man pages for command aliases etc.)
740 * It even went one step farther, by examining the first line
741 * of the man page and parsing the .so filename so it would
742 * make hard(?) links to the cat'ted man pages for space savings.
743 * (We don't do that here, but we could).
746 /* copy and find the end */
747 for (b = buf, p = *pathp; (*b++ = *p++) != '\0';)
751 * skip the last two path components, page name and man[n] ...
752 * (e.g. buf will be "/usr/share/man" and p will be "man1/man.1")
753 * we also save a pointer to our current directory so that we
754 * can fchdir() back to it. this allows relative MANDIR paths
755 * to work with multiple man pages... e.g. consider:
756 * cd /usr/share && man -M ./man cat ls
757 * when no "cat1" subdir files are present.
760 for (--b, --p, n = 2; b != buf; b--, p--)
764 olddir = open(".", O_RDONLY);
771 /* advance fmt past the suffix spec to the printf format string */
772 for (; *fmt && isspace((unsigned char)*fmt); ++fmt)
776 * Get a temporary file and build a version of the file
777 * to display. Replace the old file name with the new one.
779 if ((tmpdir = getenv("TMPDIR")) == NULL)
781 tmpdirlen = strlen(tmpdir);
782 (void)snprintf(tpath, sizeof (tpath), "%s%s%s", tmpdir,
783 (tmpdirlen > 0 && tmpdir[tmpdirlen-1] == '/') ? "" : "/", TMPFILE);
784 if ((fd = mkstemp(tpath)) == -1) {
789 (void)snprintf(buf, sizeof(buf), "%s > %s", fmt, tpath);
790 (void)snprintf(cmd, sizeof(cmd), buf, p);
793 if ((*pathp = strdup(tpath)) == NULL) {
799 /* Link the built file into the remove-when-done list. */
800 if (addentry(mp->intmp, *pathp, 0) < 0) {
806 /* restore old directory so relative manpaths still work */
815 * display how information
818 how(const char *fname)
826 if (!(fp = fopen(fname, "r"))) {
831 #define S1 "SYNOPSIS"
832 #define S2 "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS"
833 #define D1 "DESCRIPTION"
834 #define D2 "D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN"
835 for (lcnt = print = 0; fgets(buf, sizeof(buf), fp);) {
836 if (!strncmp(buf, S1, sizeof(S1) - 1) ||
837 !strncmp(buf, S2, sizeof(S2) - 1)) {
840 } else if (!strncmp(buf, D1, sizeof(D1) - 1) ||
841 !strncmp(buf, D2, sizeof(D2) - 1)) {
853 for (p = buf; isspace((unsigned char)*p); ++p)
855 (void)fputs(p, stdout);
866 cat(const char *fname)
872 if ((fd = open(fname, O_RDONLY, 0)) < 0) {
877 while ((n = read(fd, buf, sizeof(buf))) > 0)
878 if (write(STDOUT_FILENO, buf, (size_t)n) != n) {
893 * check the user supplied page information
896 check_pager(const char *name)
901 * if the user uses "more", we make it "more -s"; watch out for
902 * PAGER = "mypager /usr/ucb/more"
904 for (p = name; *p && !isspace((unsigned char)*p); ++p)
906 for (; p > name && *p != '/'; --p);
910 /* make sure it's "more", not "morex" */
911 if (!strncmp(p, "more", 4) && (!p[4] || isspace((unsigned char)p[4]))){
913 (void)asprintf(&newname, "%s %s", p, "-s");
922 * strip out flag argument and jump
925 jump(char **argv, const char *flag, const char *name)
929 argv[0] = __DECONST(char *, name);
930 for (arg = argv + 1; *arg; ++arg)
931 if (!strcmp(*arg, flag))
936 err(EXIT_FAILURE, "Cannot execute `%s'", name);
941 * If signaled, delete the temporary files.
947 signal(signo, SIG_DFL);
953 * Clean up temporary files, show any error messages.
964 * note that _missing and _intmp were created by main(), so
965 * gettag() cannot return NULL here.
967 missp = gettag("_missing", 0); /* missing man pages */
968 intmpp = gettag("_intmp", 0); /* tmp files we need to unlink */
970 TAILQ_FOREACH(ep, &missp->entrylist, q) {
971 warnx("no entry for %s in the manual.", ep->s);
975 TAILQ_FOREACH(ep, &intmpp->entrylist, q)
982 getclass(const char *machine)
986 snprintf(buf, sizeof(buf), "_%s", machine);
988 return t != NULL && !TAILQ_EMPTY(&t->entrylist) ?
989 TAILQ_FIRST(&t->entrylist)->s : NULL;
993 addpath(struct manstate *m, const char *dir, size_t len, const char *sub)
995 char buf[2 * MAXPATHLEN + 1];
996 (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,%s%s%s}",
997 dir, (dir[len - 1] == '/') ? "" : "/", sub, m->machine,
998 m->machclass ? "/" : "", m->machclass ? m->machclass : "",
999 m->machclass ? "," : "");
1000 if (addentry(m->mymanpath, buf, 0) < 0)
1001 errx(EXIT_FAILURE, "malloc failed");
1006 * print usage message and die
1011 (void)fprintf(stderr, "Usage: %s [-acw|-h] [-C cfg] [-M path] "
1012 "[-m path] [-S srch] [[-s] sect] name ...\n", getprogname());
1013 (void)fprintf(stderr,
1014 "Usage: %s -k [-C cfg] [-M path] [-m path] keyword ...\n",
1016 (void)fprintf(stderr, "Usage: %s -p\n", getprogname());
1022 * Prints a list of directories containing man pages.
1025 printmanpath(struct manstate *m)
1028 char *defaultpath = NULL; /* _default tag value from man.conf. */
1029 char *buf; /* for storing temporary values */
1033 TAG *path = m->defaultpath;
1034 TAG *subdirs = m->subdirs;
1036 /* the tail queue is empty if no _default tag is defined in * man.conf */
1037 if (TAILQ_EMPTY(&path->entrylist))
1038 errx(EXIT_FAILURE, "Empty manpath");
1040 defaultpath = TAILQ_LAST(&path->entrylist, tqh)->s;
1042 if (glob(defaultpath, GLOB_BRACE | GLOB_NOSORT, NULL, &pg) != 0)
1043 err(EXIT_FAILURE, "glob failed");
1045 if (pg.gl_matchc == 0) {
1046 warnx("Default path in %s doesn't exist", _PATH_MANCONF);
1051 TAILQ_FOREACH(esubd, &subdirs->entrylist, q) {
1052 /* Drop cat page directory, only sources are relevant. */
1053 if (strncmp(esubd->s, "man", 3))
1056 for (ap = pg.gl_pathv; *ap != NULL; ++ap) {
1057 if (asprintf(&buf, "%s%s", *ap, esubd->s) == -1)
1058 err(EXIT_FAILURE, "memory allocation error");
1059 /* Skip non-directories. */
1060 if (stat(buf, &sb) == 0 && S_ISDIR(sb.st_mode))
1061 printf("%s\n", buf);