1 /* $NetBSD: man.c,v 1.38 2009/10/06 06:43:15 cegger 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.38 2009/10/06 06:43:15 cegger Exp $");
47 #include <sys/param.h>
48 #include <sys/queue.h>
49 #include <sys/utsname.h>
65 #include "pathnames.h"
68 #define MAN_DEBUG 0 /* debug path output */
72 * manstate: structure collecting the current global state so we can
73 * easily identify it and pass it to helper functions in one arg.
76 /* command line flags */
77 int all
; /* -a: show all matches rather than first */
78 int cat
; /* -c: do not use a pager */
79 char *conffile
; /* -C: use alternate config file */
80 int how
; /* -h: show SYNOPSIS only */
81 char *manpath
; /* -M: alternate MANPATH */
82 char *addpath
; /* -m: add these dirs to front of manpath */
83 char *pathsearch
; /* -S: path of man must contain this string */
84 char *sectionname
; /* -s: limit search to a given man section */
85 int where
; /* -w: just show paths of all matching files */
87 /* important tags from the config file */
88 TAG
*defaultpath
; /* _default: default MANPATH */
89 TAG
*subdirs
; /* _subdir: default subdir search list */
90 TAG
*suffixlist
; /* _suffix: for files that can be cat()'d */
91 TAG
*buildlist
; /* _build: for files that must be built */
93 /* tags for internal use */
94 TAG
*intmp
; /* _intmp: tmp files we must cleanup */
95 TAG
*missinglist
; /* _missing: pages we couldn't find */
96 TAG
*mymanpath
; /* _new_path: final version of MANPATH */
97 TAG
*section
; /* <sec>: tag for m.sectionname */
99 /* other misc stuff */
100 const char *pager
; /* pager to use */
101 size_t pagerlen
; /* length of the above */
107 static void build_page(char *, char **, struct manstate
*);
108 static void cat(char *);
109 static const char *check_pager(const char *);
110 static int cleanup(void);
111 static void how(char *);
112 static void jump(char **, char *, char *);
113 static int manual(char *, struct manstate
*, glob_t
*);
114 static void onsig(int);
115 static void usage(void);
121 main(int argc
, char **argv
)
123 static struct manstate m
= { 0 }; /* init to zero */
124 int ch
, abs_section
, found
;
126 ENTRY
*esubd
, *epath
;
127 char *p
, **ap
, *cmd
, buf
[MAXPATHLEN
* 2];
132 * parse command line...
134 while ((ch
= getopt(argc
, argv
, "-aC:cfhkM:m:P:s:S:w")) != -1)
143 case '-': /* XXX: '-' is a deprecated version of '-c' */
153 case 'P': /* -P for backward compatibility */
154 m
.manpath
= strdup(optarg
);
157 * The -f and -k options are backward compatible,
158 * undocumented ways of calling whatis(1) and apropos(1).
161 jump(argv
, "-f", "whatis");
164 jump(argv
, "-k", "apropos");
167 if (m
.sectionname
!= NULL
)
169 m
.sectionname
= optarg
;
172 m
.pathsearch
= optarg
;
188 * read the configuration file and collect any other information
189 * we will need (machine type, pager, section [if specified
190 * without '-s'], and MANPATH through the environment).
192 config(m
.conffile
); /* exits on error ... */
194 if ((machine
= getenv("MACHINE")) == NULL
) {
195 struct utsname utsname
;
197 if (uname(&utsname
) == -1) {
201 machine
= utsname
.machine
;
204 if (!m
.cat
&& !m
.how
&& !m
.where
) { /* if we need a pager ... */
205 if (!isatty(STDOUT_FILENO
)) {
208 if ((m
.pager
= getenv("PAGER")) != NULL
&&
210 m
.pager
= check_pager(m
.pager
);
212 m
.pager
= _PATH_PAGER
;
213 m
.pagerlen
= strlen(m
.pager
);
217 /* do we need to set m.section to a non-null value? */
220 m
.section
= gettag(m
.sectionname
, 0); /* -s must be a section */
221 if (m
.section
== NULL
)
222 errx(EXIT_FAILURE
, "unknown section: %s", m
.sectionname
);
224 } else if (argc
> 1) {
226 m
.section
= gettag(*argv
, 0); /* might be a section? */
234 if (m
.manpath
== NULL
)
235 m
.manpath
= getenv("MANPATH"); /* note: -M overrides getenv */
239 * get default values from config file, plus create the tags we
240 * use for keeping internal state. make sure all our mallocs
244 m
.defaultpath
= gettag("_default", 1);
245 m
.subdirs
= gettag("_subdir", 1);
246 m
.suffixlist
= gettag("_suffix", 1);
247 m
.buildlist
= gettag("_build", 1);
249 m
.mymanpath
= gettag("_new_path", 1);
250 m
.missinglist
= gettag("_missing", 1);
251 m
.intmp
= gettag("_intmp", 1);
252 if (!m
.defaultpath
|| !m
.subdirs
|| !m
.suffixlist
|| !m
.buildlist
||
253 !m
.mymanpath
|| !m
.missinglist
|| !m
.intmp
)
254 errx(EXIT_FAILURE
, "malloc failed");
257 * are we using a section whose elements are all absolute paths?
258 * (we only need to look at the first entry on the section list,
259 * as config() will ensure that any additional entries will match
262 abs_section
= (m
.section
!= NULL
&&
263 !TAILQ_EMPTY(&m
.section
->entrylist
) &&
264 *(TAILQ_FIRST(&m
.section
->entrylist
)->s
) == '/');
267 * now that we have all the data we need, we must determine the
268 * manpath we are going to use to find the requested entries using
269 * the following steps...
271 * [1] if the user specified a section and that section's elements
272 * from the config file are all absolute paths, then we override
273 * defaultpath and -M/MANPATH with the section's absolute paths.
276 m
.manpath
= NULL
; /* ignore -M/MANPATH */
277 m
.defaultpath
= m
.section
; /* overwrite _default path */
278 m
.section
= NULL
; /* promoted to defaultpath */
282 * [2] section can now only be non-null if the user asked for
283 * a section and that section's elements did not have
284 * absolute paths. in this case we use the section's
285 * elements to override _subdir from the config file.
287 * after this step, we are done processing "m.section"...
290 m
.subdirs
= m
.section
;
293 * [3] we need to setup the path we want to use (m.mymanpath).
294 * if the user gave us a path (m.manpath) use it, otherwise
295 * go with the default. in either case we need to append
296 * the subdir and machine spec to each element of the path.
298 * for absolute section paths that come from the config file,
299 * we only append the subdir spec if the path ends in
300 * a '/' --- elements that do not end in '/' are assumed to
301 * not have subdirectories. this is mainly for backward compat,
302 * but it allows non-subdir configs like:
303 * sect3 /usr/share/man/{old/,}cat3
304 * doc /usr/{pkg,share}/doc/{sendmail/op,sendmail/intro}
306 * note that we try and be careful to not put double slashes
307 * in the path (e.g. we want /usr/share/man/man1, not
308 * /usr/share/man//man1) because "more" will put the filename
309 * we generate in its prompt and the double slashes look ugly.
313 /* note: strtok is going to destroy m.manpath */
314 for (p
= strtok(m
.manpath
, ":") ; p
; p
= strtok(NULL
, ":")) {
318 TAILQ_FOREACH(esubd
, &m
.subdirs
->entrylist
, q
) {
319 snprintf(buf
, sizeof(buf
), "%s%s%s{/%s,}",
320 p
, (p
[len
-1] == '/') ? "" : "/",
322 if (addentry(m
.mymanpath
, buf
, 0) < 0)
323 errx(EXIT_FAILURE
, "malloc failed");
329 TAILQ_FOREACH(epath
, &m
.defaultpath
->entrylist
, q
) {
330 /* handle trailing "/" magic here ... */
332 epath
->s
[epath
->len
- 1] != '/') {
334 (void)snprintf(buf
, sizeof(buf
),
335 "%s{/%s,}", epath
->s
, machine
);
336 if (addentry(m
.mymanpath
, buf
, 0) < 0)
337 errx(EXIT_FAILURE
, "malloc failed");
341 TAILQ_FOREACH(esubd
, &m
.subdirs
->entrylist
, q
) {
342 snprintf(buf
, sizeof(buf
), "%s%s%s{/%s,}",
344 (epath
->s
[epath
->len
-1] == '/') ? ""
347 if (addentry(m
.mymanpath
, buf
, 0) < 0)
348 errx(EXIT_FAILURE
, "malloc failed");
355 * [4] finally, prepend the "-m" m.addpath to mymanpath if it
356 * was specified. subdirs and machine are always applied to
361 /* note: strtok is going to destroy m.addpath */
362 for (p
= strtok(m
.addpath
, ":") ; p
; p
= strtok(NULL
, ":")) {
366 TAILQ_FOREACH(esubd
, &m
.subdirs
->entrylist
, q
) {
367 snprintf(buf
, sizeof(buf
), "%s%s%s{/%s,}",
368 p
, (p
[len
-1] == '/') ? "" : "/",
371 if (addentry(m
.mymanpath
, buf
, 1) < 0)
372 errx(EXIT_FAILURE
, "malloc failed");
379 * now m.mymanpath is complete!
382 printf("mymanpath:\n");
383 TAILQ_FOREACH(epath
, &m
.mymanpath
->entrylist
, q
) {
384 printf("\t%s\n", epath
->s
);
389 * start searching for matching files and format them if necessary.
390 * setup an interrupt handler so that we can ensure that temporary
393 (void)signal(SIGINT
, onsig
);
394 (void)signal(SIGHUP
, onsig
);
395 (void)signal(SIGPIPE
, onsig
);
397 memset(&pg
, 0, sizeof(pg
));
398 for (found
= 0; *argv
; ++argv
)
399 if (manual(*argv
, &m
, &pg
)) {
403 /* if nothing found, we're done. */
410 * handle the simple display cases first (m.cat, m.how, m.where)
413 for (ap
= pg
.gl_pathv
; *ap
!= NULL
; ++ap
) {
421 for (ap
= pg
.gl_pathv
; *ap
!= NULL
; ++ap
) {
429 for (ap
= pg
.gl_pathv
; *ap
!= NULL
; ++ap
) {
432 (void)printf("%s\n", *ap
);
438 * normal case - we display things in a single command, so
439 * build a list of things to display. first compute total
440 * length of buffer we will need so we can malloc it.
442 for (ap
= pg
.gl_pathv
, len
= m
.pagerlen
+ 1; *ap
!= NULL
; ++ap
) {
445 len
+= strlen(*ap
) + 1;
447 if ((cmd
= malloc(len
)) == NULL
) {
453 /* now build the command string... */
456 memcpy(p
, m
.pager
, len
);
459 for (ap
= pg
.gl_pathv
; *ap
!= NULL
; ++ap
) {
469 /* Use system(3) in case someone's pager is "pager arg1 arg2". */
476 manual_find_buildkeyword(char *escpage
, const char *fmt
,
477 struct manstate
*mp
, glob_t
*pg
, size_t cnt
)
481 char *p
, buf
[MAXPATHLEN
];
484 /* Try the _build key words next. */
485 TAILQ_FOREACH(suffix
, &mp
->buildlist
->entrylist
, q
) {
487 *p
!= '\0' && !isspace((unsigned char)*p
);
494 (void)snprintf(buf
, sizeof(buf
), fmt
, escpage
, suffix
->s
);
495 if (!fnmatch(buf
, pg
->gl_pathv
[cnt
], 0)) {
497 build_page(p
+ 1, &pg
->gl_pathv
[cnt
], mp
);
510 * Search the manuals for the pages.
513 manual(char *page
, struct manstate
*mp
, glob_t
*pg
)
515 ENTRY
*suffix
, *mdir
;
516 int anyfound
, error
, found
;
518 char *p
, buf
[MAXPATHLEN
], *escpage
, *eptr
;
519 static const char escglob
[] = "\\~?*{}[]";
524 * Fixup page which may contain glob(3) special characters, e.g.
525 * the famous "No man page for [" FAQ.
527 if ((escpage
= malloc((2 * strlen(page
)) + 1)) == NULL
) {
537 if (strchr(escglob
, *p
) != NULL
) {
547 * If 'page' is given with a full or relative path
548 * then interpret it as a file specification.
550 if ((page
[0] == '/') || (page
[0] == '.')) {
551 /* check if file actually exists */
552 (void)strlcpy(buf
, escpage
, sizeof(buf
));
553 error
= glob(buf
, GLOB_APPEND
| GLOB_BRACE
| GLOB_NOSORT
, NULL
, pg
);
555 if (error
== GLOB_NOMATCH
) {
558 errx(EXIT_FAILURE
, "glob failed");
562 if (pg
->gl_matchc
== 0)
565 /* clip suffix for the suffix check below */
566 p
= strrchr(escpage
, '.');
567 if (p
&& p
[0] == '.' && isdigit((unsigned char)p
[1]))
571 for (cnt
= pg
->gl_pathc
- pg
->gl_matchc
;
572 cnt
< pg
->gl_pathc
; ++cnt
)
574 found
= manual_find_buildkeyword(escpage
, "%s%s",
579 /* Delete any other matches. */
580 while (++cnt
< pg
->gl_pathc
)
581 pg
->gl_pathv
[cnt
] = "";
587 /* It's not a man page, forget about it. */
588 pg
->gl_pathv
[cnt
] = "";
593 if (addentry(mp
->missinglist
, page
, 0) < 0) {
603 /* For each man directory in mymanpath ... */
604 TAILQ_FOREACH(mdir
, &mp
->mymanpath
->entrylist
, q
) {
607 * use glob(3) to look in the filesystem for matching files.
608 * match any suffix here, as we will check that later.
610 (void)snprintf(buf
, sizeof(buf
), "%s/%s.*", mdir
->s
, escpage
);
611 if ((error
= glob(buf
,
612 GLOB_APPEND
| GLOB_BRACE
| GLOB_NOSORT
, NULL
, pg
)) != 0) {
613 if (error
== GLOB_NOMATCH
)
621 if (pg
->gl_matchc
== 0)
625 * start going through the matches glob(3) just found and
626 * use m.pathsearch (if present) to filter out pages we
627 * don't want. then verify the suffix is valid, and build
628 * the page if we have a _build suffix.
630 for (cnt
= pg
->gl_pathc
- pg
->gl_matchc
;
631 cnt
< pg
->gl_pathc
; ++cnt
) {
633 /* filter on directory path name */
634 if (mp
->pathsearch
) {
635 p
= strstr(pg
->gl_pathv
[cnt
], mp
->pathsearch
);
636 if (!p
|| strchr(p
, '/') == NULL
) {
637 pg
->gl_pathv
[cnt
] = ""; /* zap! */
643 * Try the _suffix key words first.
646 * Older versions of man.conf didn't have the suffix
647 * key words, it was assumed that everything was a .0.
648 * We just test for .0 first, it's fast and probably
651 (void)snprintf(buf
, sizeof(buf
), "*/%s.0", escpage
);
652 if (!fnmatch(buf
, pg
->gl_pathv
[cnt
], 0))
656 TAILQ_FOREACH(suffix
, &mp
->suffixlist
->entrylist
, q
) {
658 sizeof(buf
), "*/%s%s", escpage
,
660 if (!fnmatch(buf
, pg
->gl_pathv
[cnt
], 0)) {
668 /* Try the _build key words next. */
669 found
= manual_find_buildkeyword(escpage
, "*/%s%s",
674 /* Delete any other matches. */
675 while (++cnt
< pg
->gl_pathc
)
676 pg
->gl_pathv
[cnt
] = "";
682 /* It's not a man page, forget about it. */
683 pg
->gl_pathv
[cnt
] = "";
686 if (anyfound
&& !mp
->all
)
690 /* If not found, enter onto the missing list. */
692 if (addentry(mp
->missinglist
, page
, 0) < 0) {
705 * Build a man page for display.
708 build_page(char *fmt
, char **pathp
, struct manstate
*mp
)
711 int olddir
, fd
, n
, tmpdirlen
;
713 char buf
[MAXPATHLEN
], cmd
[MAXPATHLEN
], tpath
[MAXPATHLEN
];
716 /* Let the user know this may take awhile. */
719 warnx("Formatting manual page...");
723 * Historically man chdir'd to the root of the man tree.
724 * This was used in man pages that contained relative ".so"
725 * directives (including other man pages for command aliases etc.)
726 * It even went one step farther, by examining the first line
727 * of the man page and parsing the .so filename so it would
728 * make hard(?) links to the cat'ted man pages for space savings.
729 * (We don't do that here, but we could).
732 /* copy and find the end */
733 for (b
= buf
, p
= *pathp
; (*b
++ = *p
++) != '\0';)
737 * skip the last two path components, page name and man[n] ...
738 * (e.g. buf will be "/usr/share/man" and p will be "man1/man.1")
739 * we also save a pointer to our current directory so that we
740 * can fchdir() back to it. this allows relative MANDIR paths
741 * to work with multiple man pages... e.g. consider:
742 * cd /usr/share && man -M ./man cat ls
743 * when no "cat1" subdir files are present.
746 for (--b
, --p
, n
= 2; b
!= buf
; b
--, p
--)
750 olddir
= open(".", O_RDONLY
);
757 /* advance fmt pass the suffix spec to the printf format string */
758 for (; *fmt
&& isspace((unsigned char)*fmt
); ++fmt
)
762 * Get a temporary file and build a version of the file
763 * to display. Replace the old file name with the new one.
765 if ((tmpdir
= getenv("TMPDIR")) == NULL
)
767 tmpdirlen
= strlen(tmpdir
);
768 (void)snprintf(tpath
, sizeof (tpath
), "%s%s%s", tmpdir
,
769 (tmpdirlen
&& tmpdir
[tmpdirlen
-1] == '/') ? "" : "/", TMPFILE
);
770 if ((fd
= mkstemp(tpath
)) == -1) {
775 (void)snprintf(buf
, sizeof(buf
), "%s > %s", fmt
, tpath
);
776 (void)snprintf(cmd
, sizeof(cmd
), buf
, p
);
779 if ((*pathp
= strdup(tpath
)) == NULL
) {
785 /* Link the built file into the remove-when-done list. */
786 if (addentry(mp
->intmp
, *pathp
, 0) < 0) {
792 /* restore old directory so relative manpaths still work */
801 * display how information
811 if (!(fp
= fopen(fname
, "r"))) {
816 #define S1 "SYNOPSIS"
817 #define S2 "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS"
818 #define D1 "DESCRIPTION"
819 #define D2 "D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN"
820 for (lcnt
= print
= 0; fgets(buf
, sizeof(buf
), fp
);) {
821 if (!strncmp(buf
, S1
, sizeof(S1
) - 1) ||
822 !strncmp(buf
, S2
, sizeof(S2
) - 1)) {
825 } else if (!strncmp(buf
, D1
, sizeof(D1
) - 1) ||
826 !strncmp(buf
, D2
, sizeof(D2
) - 1)) {
838 for (p
= buf
; isspace((unsigned char)*p
); ++p
)
840 (void)fputs(p
, stdout
);
856 if ((fd
= open(fname
, O_RDONLY
, 0)) < 0) {
861 while ((n
= read(fd
, buf
, sizeof(buf
))) > 0)
862 if (write(STDOUT_FILENO
, buf
, n
) != n
) {
877 * check the user supplied page information
880 check_pager(const char *name
)
885 * if the user uses "more", we make it "more -s"; watch out for
886 * PAGER = "mypager /usr/ucb/more"
888 for (p
= name
; *p
&& !isspace((unsigned char)*p
); ++p
)
890 for (; p
> name
&& *p
!= '/'; --p
);
894 /* make sure it's "more", not "morex" */
895 if (!strncmp(p
, "more", 4) && (!p
[4] || isspace((unsigned char)p
[4]))){
897 (void)asprintf(&newname
, "%s %s", p
, "-s");
906 * strip out flag argument and jump
909 jump(char **argv
, char *flag
, char *name
)
914 for (arg
= argv
+ 1; *arg
; ++arg
)
915 if (!strcmp(*arg
, flag
))
920 (void)fprintf(stderr
, "%s: Command not found.\n", name
);
926 * If signaled, delete the temporary files.
934 (void)raise_default_signal(signo
);
942 * Clean up temporary files, show any error messages.
953 * note that _missing and _intmp were created by main(), so
954 * gettag() cannot return NULL here.
956 missp
= gettag("_missing", 0); /* missing man pages */
957 intmpp
= gettag("_intmp", 0); /* tmp files we need to unlink */
959 TAILQ_FOREACH(ep
, &missp
->entrylist
, q
) {
960 warnx("no entry for %s in the manual.", ep
->s
);
964 TAILQ_FOREACH(ep
, &intmpp
->entrylist
, q
)
972 * print usage message and die
977 (void)fprintf(stderr
, "usage: %s [-acw|-h] [-C cfg] [-M path] "
978 "[-m path] [-S srch] [[-s] sect] name ...\n", getprogname());
979 (void)fprintf(stderr
,
980 "usage: %s -k [-C cfg] [-M path] [-m path] keyword ...\n",