1 /* $NetBSD: man.c,v 1.41 2010/07/07 21:24:34 christos 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.41 2010/07/07 21:24:34 christos Exp $");
47 #include <sys/param.h>
48 #include <sys/queue.h>
49 #include <sys/utsname.h>
66 #include "pathnames.h"
69 #define MAN_DEBUG 0 /* debug path output */
73 * manstate: structure collecting the current global state so we can
74 * easily identify it and pass it to helper functions in one arg.
77 /* command line flags */
78 int all
; /* -a: show all matches rather than first */
79 int cat
; /* -c: do not use a pager */
80 char *conffile
; /* -C: use alternate config file */
81 int how
; /* -h: show SYNOPSIS only */
82 char *manpath
; /* -M: alternate MANPATH */
83 char *addpath
; /* -m: add these dirs to front of manpath */
84 char *pathsearch
; /* -S: path of man must contain this string */
85 char *sectionname
; /* -s: limit search to a given man section */
86 int where
; /* -w: just show paths of all matching files */
88 /* important tags from the config file */
89 TAG
*defaultpath
; /* _default: default MANPATH */
90 TAG
*subdirs
; /* _subdir: default subdir search list */
91 TAG
*suffixlist
; /* _suffix: for files that can be cat()'d */
92 TAG
*buildlist
; /* _build: for files that must be built */
94 /* tags for internal use */
95 TAG
*intmp
; /* _intmp: tmp files we must cleanup */
96 TAG
*missinglist
; /* _missing: pages we couldn't find */
97 TAG
*mymanpath
; /* _new_path: final version of MANPATH */
98 TAG
*section
; /* <sec>: tag for m.sectionname */
100 /* other misc stuff */
101 const char *pager
; /* pager to use */
102 const char *machine
; /* machine */
103 const char *machclass
; /* machine class */
104 size_t pagerlen
; /* length of the above */
110 static void build_page(char *, char **, struct manstate
*);
111 static void cat(char *);
112 static const char *check_pager(const char *);
113 static int cleanup(void);
114 static void how(char *);
115 static void jump(char **, char *, char *);
116 static int manual(char *, struct manstate
*, glob_t
*);
117 static void onsig(int);
118 static void usage(void) __attribute__((__noreturn__
));
119 static void addpath(struct manstate
*, const char *, size_t, const char *);
120 static const char *getclass(const char *);
126 main(int argc
, char **argv
)
128 static struct manstate m
= { 0 }; /* init to zero */
129 int ch
, abs_section
, found
;
130 ENTRY
*esubd
, *epath
;
135 setprogname(argv
[0]);
136 setlocale(LC_ALL
, "");
138 * parse command line...
140 while ((ch
= getopt(argc
, argv
, "-aC:cfhkM:m:P:s:S:w")) != -1)
149 case '-': /* XXX: '-' is a deprecated version of '-c' */
159 case 'P': /* -P for backward compatibility */
160 m
.manpath
= strdup(optarg
);
163 * The -f and -k options are backward compatible,
164 * undocumented ways of calling whatis(1) and apropos(1).
167 jump(argv
, "-f", "whatis");
170 jump(argv
, "-k", "apropos");
173 if (m
.sectionname
!= NULL
)
175 m
.sectionname
= optarg
;
178 m
.pathsearch
= optarg
;
194 * read the configuration file and collect any other information
195 * we will need (machine type, pager, section [if specified
196 * without '-s'], and MANPATH through the environment).
198 config(m
.conffile
); /* exits on error ... */
200 if ((m
.machine
= getenv("MACHINE")) == NULL
) {
201 struct utsname utsname
;
203 if (uname(&utsname
) == -1)
204 err(EXIT_FAILURE
, "uname");
205 m
.machine
= utsname
.machine
;
208 m
.machclass
= getclass(m
.machine
);
210 if (!m
.cat
&& !m
.how
&& !m
.where
) { /* if we need a pager ... */
211 if (!isatty(STDOUT_FILENO
)) {
214 if ((m
.pager
= getenv("PAGER")) != NULL
&&
216 m
.pager
= check_pager(m
.pager
);
218 m
.pager
= _PATH_PAGER
;
219 m
.pagerlen
= strlen(m
.pager
);
223 /* do we need to set m.section to a non-null value? */
226 m
.section
= gettag(m
.sectionname
, 0); /* -s must be a section */
227 if (m
.section
== NULL
)
228 errx(EXIT_FAILURE
, "unknown section: %s", m
.sectionname
);
230 } else if (argc
> 1) {
232 m
.section
= gettag(*argv
, 0); /* might be a section? */
240 if (m
.manpath
== NULL
)
241 m
.manpath
= getenv("MANPATH"); /* note: -M overrides getenv */
245 * get default values from config file, plus create the tags we
246 * use for keeping internal state. make sure all our mallocs
250 m
.defaultpath
= gettag("_default", 1);
251 m
.subdirs
= gettag("_subdir", 1);
252 m
.suffixlist
= gettag("_suffix", 1);
253 m
.buildlist
= gettag("_build", 1);
255 m
.mymanpath
= gettag("_new_path", 1);
256 m
.missinglist
= gettag("_missing", 1);
257 m
.intmp
= gettag("_intmp", 1);
258 if (!m
.defaultpath
|| !m
.subdirs
|| !m
.suffixlist
|| !m
.buildlist
||
259 !m
.mymanpath
|| !m
.missinglist
|| !m
.intmp
)
260 errx(EXIT_FAILURE
, "malloc failed");
263 * are we using a section whose elements are all absolute paths?
264 * (we only need to look at the first entry on the section list,
265 * as config() will ensure that any additional entries will match
268 abs_section
= (m
.section
!= NULL
&&
269 !TAILQ_EMPTY(&m
.section
->entrylist
) &&
270 *(TAILQ_FIRST(&m
.section
->entrylist
)->s
) == '/');
273 * now that we have all the data we need, we must determine the
274 * manpath we are going to use to find the requested entries using
275 * the following steps...
277 * [1] if the user specified a section and that section's elements
278 * from the config file are all absolute paths, then we override
279 * defaultpath and -M/MANPATH with the section's absolute paths.
282 m
.manpath
= NULL
; /* ignore -M/MANPATH */
283 m
.defaultpath
= m
.section
; /* overwrite _default path */
284 m
.section
= NULL
; /* promoted to defaultpath */
288 * [2] section can now only be non-null if the user asked for
289 * a section and that section's elements did not have
290 * absolute paths. in this case we use the section's
291 * elements to override _subdir from the config file.
293 * after this step, we are done processing "m.section"...
296 m
.subdirs
= m
.section
;
299 * [3] we need to setup the path we want to use (m.mymanpath).
300 * if the user gave us a path (m.manpath) use it, otherwise
301 * go with the default. in either case we need to append
302 * the subdir and machine spec to each element of the path.
304 * for absolute section paths that come from the config file,
305 * we only append the subdir spec if the path ends in
306 * a '/' --- elements that do not end in '/' are assumed to
307 * not have subdirectories. this is mainly for backward compat,
308 * but it allows non-subdir configs like:
309 * sect3 /usr/share/man/{old/,}cat3
310 * doc /usr/{pkg,share}/doc/{sendmail/op,sendmail/intro}
312 * note that we try and be careful to not put double slashes
313 * in the path (e.g. we want /usr/share/man/man1, not
314 * /usr/share/man//man1) because "more" will put the filename
315 * we generate in its prompt and the double slashes look ugly.
319 /* note: strtok is going to destroy m.manpath */
320 for (p
= strtok(m
.manpath
, ":") ; p
; p
= strtok(NULL
, ":")) {
324 TAILQ_FOREACH(esubd
, &m
.subdirs
->entrylist
, q
)
325 addpath(&m
, p
, len
, esubd
->s
);
330 TAILQ_FOREACH(epath
, &m
.defaultpath
->entrylist
, q
) {
331 /* handle trailing "/" magic here ... */
332 if (abs_section
&& epath
->s
[epath
->len
- 1] != '/') {
333 addpath(&m
, "", 1, epath
->s
);
337 TAILQ_FOREACH(esubd
, &m
.subdirs
->entrylist
, q
)
338 addpath(&m
, epath
->s
, epath
->len
, esubd
->s
);
344 * [4] finally, prepend the "-m" m.addpath to mymanpath if it
345 * was specified. subdirs and machine are always applied to
350 /* note: strtok is going to destroy m.addpath */
351 for (p
= strtok(m
.addpath
, ":") ; p
; p
= strtok(NULL
, ":")) {
355 TAILQ_FOREACH(esubd
, &m
.subdirs
->entrylist
, q
)
356 addpath(&m
, p
, len
, esubd
->s
);
362 * now m.mymanpath is complete!
365 printf("mymanpath:\n");
366 TAILQ_FOREACH(epath
, &m
.mymanpath
->entrylist
, q
) {
367 printf("\t%s\n", epath
->s
);
372 * start searching for matching files and format them if necessary.
373 * setup an interrupt handler so that we can ensure that temporary
376 (void)signal(SIGINT
, onsig
);
377 (void)signal(SIGHUP
, onsig
);
378 (void)signal(SIGPIPE
, onsig
);
380 memset(&pg
, 0, sizeof(pg
));
381 for (found
= 0; *argv
; ++argv
)
382 if (manual(*argv
, &m
, &pg
)) {
386 /* if nothing found, we're done. */
393 * handle the simple display cases first (m.cat, m.how, m.where)
396 for (ap
= pg
.gl_pathv
; *ap
!= NULL
; ++ap
) {
404 for (ap
= pg
.gl_pathv
; *ap
!= NULL
; ++ap
) {
412 for (ap
= pg
.gl_pathv
; *ap
!= NULL
; ++ap
) {
415 (void)printf("%s\n", *ap
);
421 * normal case - we display things in a single command, so
422 * build a list of things to display. first compute total
423 * length of buffer we will need so we can malloc it.
425 for (ap
= pg
.gl_pathv
, len
= m
.pagerlen
+ 1; *ap
!= NULL
; ++ap
) {
428 len
+= strlen(*ap
) + 1;
430 if ((cmd
= malloc(len
)) == NULL
) {
436 /* now build the command string... */
439 memcpy(p
, m
.pager
, len
);
442 for (ap
= pg
.gl_pathv
; *ap
!= NULL
; ++ap
) {
452 /* Use system(3) in case someone's pager is "pager arg1 arg2". */
459 manual_find_buildkeyword(char *escpage
, const char *fmt
,
460 struct manstate
*mp
, glob_t
*pg
, size_t cnt
)
464 char *p
, buf
[MAXPATHLEN
];
467 /* Try the _build key words next. */
468 TAILQ_FOREACH(suffix
, &mp
->buildlist
->entrylist
, q
) {
470 *p
!= '\0' && !isspace((unsigned char)*p
);
477 (void)snprintf(buf
, sizeof(buf
), fmt
, escpage
, suffix
->s
);
478 if (!fnmatch(buf
, pg
->gl_pathv
[cnt
], 0)) {
480 build_page(p
+ 1, &pg
->gl_pathv
[cnt
], mp
);
493 * Search the manuals for the pages.
496 manual(char *page
, struct manstate
*mp
, glob_t
*pg
)
498 ENTRY
*suffix
, *mdir
;
499 int anyfound
, error
, found
;
501 char *p
, buf
[MAXPATHLEN
], *escpage
, *eptr
;
502 static const char escglob
[] = "\\~?*{}[]";
507 * Fixup page which may contain glob(3) special characters, e.g.
508 * the famous "No man page for [" FAQ.
510 if ((escpage
= malloc((2 * strlen(page
)) + 1)) == NULL
) {
520 if (strchr(escglob
, *p
) != NULL
) {
530 * If 'page' is given with a full or relative path
531 * then interpret it as a file specification.
533 if ((page
[0] == '/') || (page
[0] == '.')) {
534 /* check if file actually exists */
535 (void)strlcpy(buf
, escpage
, sizeof(buf
));
536 error
= glob(buf
, GLOB_APPEND
| GLOB_BRACE
| GLOB_NOSORT
, NULL
, pg
);
538 if (error
== GLOB_NOMATCH
) {
541 errx(EXIT_FAILURE
, "glob failed");
545 if (pg
->gl_matchc
== 0)
548 /* clip suffix for the suffix check below */
549 p
= strrchr(escpage
, '.');
550 if (p
&& p
[0] == '.' && isdigit((unsigned char)p
[1]))
554 for (cnt
= pg
->gl_pathc
- pg
->gl_matchc
;
555 cnt
< pg
->gl_pathc
; ++cnt
)
557 found
= manual_find_buildkeyword(escpage
, "%s%s",
562 /* Delete any other matches. */
563 while (++cnt
< pg
->gl_pathc
)
564 pg
->gl_pathv
[cnt
] = "";
570 /* It's not a man page, forget about it. */
571 pg
->gl_pathv
[cnt
] = "";
576 if (addentry(mp
->missinglist
, page
, 0) < 0) {
586 /* For each man directory in mymanpath ... */
587 TAILQ_FOREACH(mdir
, &mp
->mymanpath
->entrylist
, q
) {
590 * use glob(3) to look in the filesystem for matching files.
591 * match any suffix here, as we will check that later.
593 (void)snprintf(buf
, sizeof(buf
), "%s/%s.*", mdir
->s
, escpage
);
594 if ((error
= glob(buf
,
595 GLOB_APPEND
| GLOB_BRACE
| GLOB_NOSORT
, NULL
, pg
)) != 0) {
596 if (error
== GLOB_NOMATCH
)
604 if (pg
->gl_matchc
== 0)
608 * start going through the matches glob(3) just found and
609 * use m.pathsearch (if present) to filter out pages we
610 * don't want. then verify the suffix is valid, and build
611 * the page if we have a _build suffix.
613 for (cnt
= pg
->gl_pathc
- pg
->gl_matchc
;
614 cnt
< pg
->gl_pathc
; ++cnt
) {
616 /* filter on directory path name */
617 if (mp
->pathsearch
) {
618 p
= strstr(pg
->gl_pathv
[cnt
], mp
->pathsearch
);
619 if (!p
|| strchr(p
, '/') == NULL
) {
620 pg
->gl_pathv
[cnt
] = ""; /* zap! */
626 * Try the _suffix key words first.
629 * Older versions of man.conf didn't have the suffix
630 * key words, it was assumed that everything was a .0.
631 * We just test for .0 first, it's fast and probably
634 (void)snprintf(buf
, sizeof(buf
), "*/%s.0", escpage
);
635 if (!fnmatch(buf
, pg
->gl_pathv
[cnt
], 0))
639 TAILQ_FOREACH(suffix
, &mp
->suffixlist
->entrylist
, q
) {
641 sizeof(buf
), "*/%s%s", escpage
,
643 if (!fnmatch(buf
, pg
->gl_pathv
[cnt
], 0)) {
651 /* Try the _build key words next. */
652 found
= manual_find_buildkeyword(escpage
, "*/%s%s",
657 /* Delete any other matches. */
658 while (++cnt
< pg
->gl_pathc
)
659 pg
->gl_pathv
[cnt
] = "";
665 /* It's not a man page, forget about it. */
666 pg
->gl_pathv
[cnt
] = "";
669 if (anyfound
&& !mp
->all
)
673 /* If not found, enter onto the missing list. */
675 if (addentry(mp
->missinglist
, page
, 0) < 0) {
688 * Build a man page for display.
691 build_page(char *fmt
, char **pathp
, struct manstate
*mp
)
694 int olddir
, fd
, n
, tmpdirlen
;
696 char buf
[MAXPATHLEN
], cmd
[MAXPATHLEN
], tpath
[MAXPATHLEN
];
699 /* Let the user know this may take awhile. */
702 warnx("Formatting manual page...");
706 * Historically man chdir'd to the root of the man tree.
707 * This was used in man pages that contained relative ".so"
708 * directives (including other man pages for command aliases etc.)
709 * It even went one step farther, by examining the first line
710 * of the man page and parsing the .so filename so it would
711 * make hard(?) links to the cat'ted man pages for space savings.
712 * (We don't do that here, but we could).
715 /* copy and find the end */
716 for (b
= buf
, p
= *pathp
; (*b
++ = *p
++) != '\0';)
720 * skip the last two path components, page name and man[n] ...
721 * (e.g. buf will be "/usr/share/man" and p will be "man1/man.1")
722 * we also save a pointer to our current directory so that we
723 * can fchdir() back to it. this allows relative MANDIR paths
724 * to work with multiple man pages... e.g. consider:
725 * cd /usr/share && man -M ./man cat ls
726 * when no "cat1" subdir files are present.
729 for (--b
, --p
, n
= 2; b
!= buf
; b
--, p
--)
733 olddir
= open(".", O_RDONLY
);
740 /* advance fmt pass the suffix spec to the printf format string */
741 for (; *fmt
&& isspace((unsigned char)*fmt
); ++fmt
)
745 * Get a temporary file and build a version of the file
746 * to display. Replace the old file name with the new one.
748 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
750 tmpdirlen
= strlen(tmpdir
);
751 (void)snprintf(tpath
, sizeof (tpath
), "%s%s%s", tmpdir
,
752 (tmpdirlen
&& tmpdir
[tmpdirlen
-1] == '/') ? "" : "/", TMPFILE
);
753 if ((fd
= mkstemp(tpath
)) == -1) {
758 (void)snprintf(buf
, sizeof(buf
), "%s > %s", fmt
, tpath
);
759 (void)snprintf(cmd
, sizeof(cmd
), buf
, p
);
762 if ((*pathp
= strdup(tpath
)) == NULL
) {
768 /* Link the built file into the remove-when-done list. */
769 if (addentry(mp
->intmp
, *pathp
, 0) < 0) {
775 /* restore old directory so relative manpaths still work */
784 * display how information
794 if (!(fp
= fopen(fname
, "r"))) {
799 #define S1 "SYNOPSIS"
800 #define S2 "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS"
801 #define D1 "DESCRIPTION"
802 #define D2 "D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN"
803 for (lcnt
= print
= 0; fgets(buf
, sizeof(buf
), fp
);) {
804 if (!strncmp(buf
, S1
, sizeof(S1
) - 1) ||
805 !strncmp(buf
, S2
, sizeof(S2
) - 1)) {
808 } else if (!strncmp(buf
, D1
, sizeof(D1
) - 1) ||
809 !strncmp(buf
, D2
, sizeof(D2
) - 1)) {
821 for (p
= buf
; isspace((unsigned char)*p
); ++p
)
823 (void)fputs(p
, stdout
);
839 if ((fd
= open(fname
, O_RDONLY
, 0)) < 0) {
844 while ((n
= read(fd
, buf
, sizeof(buf
))) > 0)
845 if (write(STDOUT_FILENO
, buf
, n
) != n
) {
860 * check the user supplied page information
863 check_pager(const char *name
)
868 * if the user uses "more", we make it "more -s"; watch out for
869 * PAGER = "mypager /usr/ucb/more"
871 for (p
= name
; *p
&& !isspace((unsigned char)*p
); ++p
)
873 for (; p
> name
&& *p
!= '/'; --p
);
877 /* make sure it's "more", not "morex" */
878 if (!strncmp(p
, "more", 4) && (!p
[4] || isspace((unsigned char)p
[4]))){
880 (void)asprintf(&newname
, "%s %s", p
, "-s");
889 * strip out flag argument and jump
892 jump(char **argv
, char *flag
, char *name
)
897 for (arg
= argv
+ 1; *arg
; ++arg
)
898 if (!strcmp(*arg
, flag
))
903 err(EXIT_FAILURE
, "Cannot execute `%s'", name
);
908 * If signaled, delete the temporary files.
916 (void)raise_default_signal(signo
);
924 * Clean up temporary files, show any error messages.
935 * note that _missing and _intmp were created by main(), so
936 * gettag() cannot return NULL here.
938 missp
= gettag("_missing", 0); /* missing man pages */
939 intmpp
= gettag("_intmp", 0); /* tmp files we need to unlink */
941 TAILQ_FOREACH(ep
, &missp
->entrylist
, q
) {
942 warnx("no entry for %s in the manual.", ep
->s
);
946 TAILQ_FOREACH(ep
, &intmpp
->entrylist
, q
)
953 getclass(const char *machine
)
957 snprintf(buf
, sizeof(buf
), "_%s", machine
);
959 return t
!= NULL
&& !TAILQ_EMPTY(&t
->entrylist
) ?
960 TAILQ_FIRST(&t
->entrylist
)->s
: NULL
;
964 addpath(struct manstate
*m
, const char *dir
, size_t len
, const char *sub
)
966 char buf
[2 * MAXPATHLEN
+ 1];
967 (void)snprintf(buf
, sizeof(buf
), "%s%s%s{/%s,%s%s%s}",
968 dir
, (dir
[len
- 1] == '/') ? "" : "/", sub
, m
->machine
,
969 m
->machclass
? "/" : "", m
->machclass
? m
->machclass
: "",
970 m
->machclass
? "," : "");
971 if (addentry(m
->mymanpath
, buf
, 0) < 0)
972 errx(EXIT_FAILURE
, "malloc failed");
977 * print usage message and die
982 (void)fprintf(stderr
, "Usage: %s [-acw|-h] [-C cfg] [-M path] "
983 "[-m path] [-S srch] [[-s] sect] name ...\n", getprogname());
984 (void)fprintf(stderr
,
985 "Usage: %s -k [-C cfg] [-M path] [-m path] keyword ...\n",