hostaddr id ifconfig ifdef indent install \
intr ipcrm ipcs irdpd isoread join kill last leave \
less lex life loadkeys loadramdisk logger login look lp \
- lpd ls lspci M m4 mail make MAKEDEV \
+ lpd ls lspci M m4 mail make MAKEDEV man \
mdb mdocml mesg mined mkdep mkdir mkdist mkfifo mkfs mknod \
mkproto modem mount mt netconf newroot nice nm nohup \
nonamed od packit packman passwd paste patch pax \
--- /dev/null
+# $NetBSD: Makefile,v 1.10 2007/10/05 07:38:52 lukem Exp $
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= man
+SRCS= man.c manconf.c
+MAN= man.1 man.conf.5
+
+DPADD+= ${LIBUTIL}
+LDADD+= -lutil
+
+.include <bsd.prog.mk>
--- /dev/null
+.\" $NetBSD: man.1,v 1.20 2006/04/13 21:10:44 wiz Exp $
+.\"
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)man.1 8.2 (Berkeley) 1/2/94
+.\"
+.Dd April 10, 2006
+.Dt MAN 1
+.Os
+.Sh NAME
+.Nm man
+.Nd display the on-line manual pages
+.Pq aka Dq Em man pages
+.Sh SYNOPSIS
+.Nm
+.Oo Fl acw Ns \&| Ns Fl h Oc
+.Op Fl C Ar file
+.Op Fl M Ar path
+.Op Fl m Ar path
+.Op Fl S Ar srch
+.Oo
+.Op Fl s
+.Ar section
+.Oc
+.Ar name Ar ...
+.Nm
+.Op Fl k
+.Op Fl C Ar file
+.Op Fl M Ar path
+.Op Fl m Ar path
+.Ar keyword Ar ...
+.Sh DESCRIPTION
+The
+.Nm
+utility displays the manual pages named on the command line.
+Its options are as follows:
+.Bl -tag -width indent
+.It Fl a
+Display all of the man pages for a specified
+.Ar section
+and
+.Ar name
+combination.
+(Normally, only the first man page found is displayed.)
+.It Fl C
+Use the specified
+.Ar file
+instead of the default configuration file.
+This permits users to configure their own man environment.
+See
+.Xr man.conf 5
+for a description of the contents of this file.
+.It Fl c
+Copy the man page to the standard output instead of using
+.Xr more 1
+to paginate it.
+This is done by default if the standard output is not a terminal device.
+.It Fl h
+Display only the
+.Dq Tn SYNOPSIS
+lines of the requested man pages.
+For commands, this is typically the command line usage information.
+For library functions, this usually contains the required include
+files and function prototypes.
+.It Fl k
+Display the header lines for any man pages matching
+.Ar keyword Ns Pq s ,
+in the same manner as
+.Xr apropos 1 .
+.It Fl M
+Override the list of standard directories which
+.Nm
+searches for man pages.
+The supplied
+.Ar path
+must be a colon
+.Pq Dq \&:
+separated list of directories.
+This search path may also be set using the environment variable
+.Ev MANPATH .
+The subdirectories to be searched, and their search order,
+is specified by the
+.Dq _subdir
+line in the
+.Nm
+configuration file.
+.It Fl m
+Augment the list of standard directories which
+.Nm
+searches for man pages.
+The supplied
+.Ar path
+must be a colon
+.Pq Dq \&:
+separated list of directories.
+These directories will be searched before the standard directories or
+the directories specified using the
+.Fl M
+option or the
+.Ev MANPATH
+environment variable.
+The subdirectories to be searched, and their search order,
+is specified by the
+.Dq _subdir
+line in the
+.Nm
+configuration file.
+.It Fl s
+Restrict the directories that
+.Nm
+will search to the specified section.
+The
+.Nm
+configuration file (see
+.Xr man.conf 5 )
+specifies the possible
+.Ar section
+values that are currently available.
+.It Fl S
+Display only man pages that have the specified string in the directory
+part of their filenames.
+This allows the man page search process criteria to be
+narrowed without having to change the MANPATH or
+.Dq _default
+variables.
+.It Fl w
+List the pathnames of the man pages which
+.Nm
+would display for the specified
+.Ar section
+and
+.Ar name
+combination.
+.El
+.Pp
+If the
+.Ql Fl s
+option is not specified,
+there is more than one argument,
+the
+.Ql Fl k
+option is not used, and the first argument is a valid section, then that
+argument will be used as if specified by the
+.Ql Fl s
+option.
+.Sh ENVIRONMENT
+.Bl -tag -width MANPATHX
+.It Ev MACHINE
+As some man pages are intended only for specific architectures,
+.Nm
+searches any subdirectories,
+with the same name as the current architecture,
+in every directory which it searches.
+Machine specific areas are checked before general areas.
+The current machine type may be overridden by setting the environment
+variable
+.Ev MACHINE
+to the name of a specific architecture.
+.It Ev MANPATH
+The standard search path used by
+.Nm
+may be overridden by specifying a path in the
+.Ev MANPATH
+environment
+variable.
+The format of the path is a colon
+.Pq Dq \&:
+separated list of directories.
+The subdirectories to be searched as well as their search order
+is specified by the
+.Dq _subdir
+line in the
+.Nm
+configuration file.
+.It Ev PAGER
+The pagination command used for writing the output.
+If the
+.Ev PAGER
+environment variable is null or not set, the standard pagination program
+.Xr more 1
+will be used.
+.El
+.Sh FILES
+.Bl -hang -width /etc/man.conf -compact
+.It Pa /etc/man.conf
+default man configuration file.
+.It Pa /usr/{share,X11R6,pkg,local}/man/whatis.db
+standard whatis/apropos database search path,
+set in
+.Pa /etc/man.conf .
+.El
+.Sh SEE ALSO
+.Xr apropos 1 ,
+.Xr whatis 1 ,
+.Xr whereis 1 ,
+.Xr man.conf 5 ,
+.Xr mdoc 7 ,
+.Xr mdoc.samples 7
+.Sh STANDARDS
+.Nm
+conforms to
+.St -xcu5 .
+.\"and is expected to conform to
+.\".St -p1003.2-?? .
+.Sh BUGS
+The on-line man pages are, by necessity, forgiving toward stupid
+display devices, causing a few man pages to be not as nicely formatted
+as their typeset counterparts.
--- /dev/null
+/* $NetBSD: man.c,v 1.37 2008/07/21 14:19:24 lukem Exp $ */
+
+/*
+ * Copyright (c) 1987, 1993, 1994, 1995
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/utsname.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <glob.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "manconf.h"
+#include "pathnames.h"
+
+#ifndef MAN_DEBUG
+#define MAN_DEBUG 0 /* debug path output */
+#endif
+
+/*
+ * manstate: structure collecting the current global state so we can
+ * easily identify it and pass it to helper functions in one arg.
+ */
+struct manstate {
+ /* command line flags */
+ int all; /* -a: show all matches rather than first */
+ int cat; /* -c: do not use a pager */
+ char *conffile; /* -C: use alternate config file */
+ int how; /* -h: show SYNOPSIS only */
+ char *manpath; /* -M: alternate MANPATH */
+ char *addpath; /* -m: add these dirs to front of manpath */
+ char *pathsearch; /* -S: path of man must contain this string */
+ char *sectionname; /* -s: limit search to a given man section */
+ int where; /* -w: just show paths of all matching files */
+
+ /* important tags from the config file */
+ TAG *defaultpath; /* _default: default MANPATH */
+ TAG *subdirs; /* _subdir: default subdir search list */
+ TAG *suffixlist; /* _suffix: for files that can be cat()'d */
+ TAG *buildlist; /* _build: for files that must be built */
+
+ /* tags for internal use */
+ TAG *intmp; /* _intmp: tmp files we must cleanup */
+ TAG *missinglist; /* _missing: pages we couldn't find */
+ TAG *mymanpath; /* _new_path: final version of MANPATH */
+ TAG *section; /* <sec>: tag for m.sectionname */
+
+ /* other misc stuff */
+ const char *pager; /* pager to use */
+ size_t pagerlen; /* length of the above */
+};
+
+/*
+ * prototypes
+ */
+int main(int, char **);
+static void build_page(char *, char **, struct manstate *);
+static void cat(char *);
+static const char *check_pager(const char *);
+static int cleanup(void);
+static void how(char *);
+static void jump(char **, char *, char *);
+static int manual(char *, struct manstate *, glob_t *);
+static void onsig(int);
+static void usage(void);
+
+/*
+ * main function
+ */
+int
+main(int argc, char **argv)
+{
+ static struct manstate m = { 0 }; /* init to zero */
+ int ch, abs_section, found;
+ const char *machine;
+ ENTRY *esubd, *epath;
+ char *p, **ap, *cmd, buf[MAXPATHLEN * 2];
+ size_t len;
+ glob_t pg;
+
+ /*
+ * parse command line...
+ */
+ while ((ch = getopt(argc, argv, "-aC:cfhkM:m:P:s:S:w")) != -1)
+ switch (ch) {
+ case 'a':
+ m.all = 1;
+ break;
+ case 'C':
+ m.conffile = optarg;
+ break;
+ case 'c':
+ case '-': /* XXX: '-' is a deprecated version of '-c' */
+ m.cat = 1;
+ break;
+ case 'h':
+ m.how = 1;
+ break;
+ case 'm':
+ m.addpath = optarg;
+ break;
+ case 'M':
+ case 'P': /* -P for backward compatibility */
+ m.manpath = strdup(optarg);
+ break;
+ /*
+ * The -f and -k options are backward compatible,
+ * undocumented ways of calling whatis(1) and apropos(1).
+ */
+ case 'f':
+ jump(argv, "-f", "whatis");
+ /* NOTREACHED */
+ case 'k':
+ jump(argv, "-k", "apropos");
+ /* NOTREACHED */
+ case 's':
+ if (m.sectionname != NULL)
+ usage();
+ m.sectionname = optarg;
+ break;
+ case 'S':
+ m.pathsearch = optarg;
+ break;
+ case 'w':
+ m.all = m.where = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!argc)
+ usage();
+
+ /*
+ * read the configuration file and collect any other information
+ * we will need (machine type, pager, section [if specified
+ * without '-s'], and MANPATH through the environment).
+ */
+ config(m.conffile); /* exits on error ... */
+
+ if ((machine = getenv("MACHINE")) == NULL) {
+ struct utsname utsname;
+
+ if (uname(&utsname) == -1) {
+ perror("uname");
+ exit(1);
+ }
+ machine = utsname.machine;
+ }
+
+ if (!m.cat && !m.how && !m.where) { /* if we need a pager ... */
+ if (!isatty(STDOUT_FILENO)) {
+ m.cat = 1;
+ } else {
+ if ((m.pager = getenv("PAGER")) != NULL &&
+ m.pager[0] != '\0')
+ m.pager = check_pager(m.pager);
+ else
+ m.pager = _PATH_PAGER;
+ m.pagerlen = strlen(m.pager);
+ }
+ }
+
+ /* do we need to set m.section to a non-null value? */
+ if (m.sectionname) {
+
+ m.section = gettag(m.sectionname, 0); /* -s must be a section */
+ if (m.section == NULL)
+ errx(1, "unknown section: %s", m.sectionname);
+
+ } else if (argc > 1) {
+
+ m.section = gettag(*argv, 0); /* might be a section? */
+ if (m.section) {
+ argv++;
+ argc--;
+ }
+
+ }
+
+ if (m.manpath == NULL)
+ m.manpath = getenv("MANPATH"); /* note: -M overrides getenv */
+
+
+ /*
+ * get default values from config file, plus create the tags we
+ * use for keeping internal state. make sure all our mallocs
+ * go through.
+ */
+ /* from cfg file */
+ m.defaultpath = gettag("_default", 1);
+ m.subdirs = gettag("_subdir", 1);
+ m.suffixlist = gettag("_suffix", 1);
+ m.buildlist = gettag("_build", 1);
+ /* internal use */
+ m.mymanpath = gettag("_new_path", 1);
+ m.missinglist = gettag("_missing", 1);
+ m.intmp = gettag("_intmp", 1);
+ if (!m.defaultpath || !m.subdirs || !m.suffixlist || !m.buildlist ||
+ !m.mymanpath || !m.missinglist || !m.intmp)
+ errx(1, "malloc failed");
+
+ /*
+ * are we using a section whose elements are all absolute paths?
+ * (we only need to look at the first entry on the section list,
+ * as config() will ensure that any additional entries will match
+ * the first one.)
+ */
+ abs_section = (m.section != NULL &&
+ !TAILQ_EMPTY(&m.section->entrylist) &&
+ *(TAILQ_FIRST(&m.section->entrylist)->s) == '/');
+
+ /*
+ * now that we have all the data we need, we must determine the
+ * manpath we are going to use to find the requested entries using
+ * the following steps...
+ *
+ * [1] if the user specified a section and that section's elements
+ * from the config file are all absolute paths, then we override
+ * defaultpath and -M/MANPATH with the section's absolute paths.
+ */
+ if (abs_section) {
+ m.manpath = NULL; /* ignore -M/MANPATH */
+ m.defaultpath = m.section; /* overwrite _default path */
+ m.section = NULL; /* promoted to defaultpath */
+ }
+
+ /*
+ * [2] section can now only be non-null if the user asked for
+ * a section and that section's elements did not have
+ * absolute paths. in this case we use the section's
+ * elements to override _subdir from the config file.
+ *
+ * after this step, we are done processing "m.section"...
+ */
+ if (m.section)
+ m.subdirs = m.section;
+
+ /*
+ * [3] we need to setup the path we want to use (m.mymanpath).
+ * if the user gave us a path (m.manpath) use it, otherwise
+ * go with the default. in either case we need to append
+ * the subdir and machine spec to each element of the path.
+ *
+ * for absolute section paths that come from the config file,
+ * we only append the subdir spec if the path ends in
+ * a '/' --- elements that do not end in '/' are assumed to
+ * not have subdirectories. this is mainly for backward compat,
+ * but it allows non-subdir configs like:
+ * sect3 /usr/share/man/{old/,}cat3
+ * doc /usr/{pkg,share}/doc/{sendmail/op,sendmail/intro}
+ *
+ * note that we try and be careful to not put double slashes
+ * in the path (e.g. we want /usr/share/man/man1, not
+ * /usr/share/man//man1) because "more" will put the filename
+ * we generate in its prompt and the double slashes look ugly.
+ */
+ if (m.manpath) {
+
+ /* note: strtok is going to destroy m.manpath */
+ for (p = strtok(m.manpath, ":") ; p ; p = strtok(NULL, ":")) {
+ len = strlen(p);
+ if (len < 1)
+ continue;
+ TAILQ_FOREACH(esubd, &m.subdirs->entrylist, q) {
+ snprintf(buf, sizeof(buf), "%s%s%s{/%s,}",
+ p, (p[len-1] == '/') ? "" : "/",
+ esubd->s, machine);
+ if (addentry(m.mymanpath, buf, 0) < 0)
+ errx(1, "malloc failed");
+ }
+ }
+
+ } else {
+
+ TAILQ_FOREACH(epath, &m.defaultpath->entrylist, q) {
+ /* handle trailing "/" magic here ... */
+ if (abs_section &&
+ epath->s[epath->len - 1] != '/') {
+
+ (void)snprintf(buf, sizeof(buf),
+ "%s{/%s,}", epath->s, machine);
+ if (addentry(m.mymanpath, buf, 0) < 0)
+ errx(1, "malloc failed");
+ continue;
+ }
+
+ TAILQ_FOREACH(esubd, &m.subdirs->entrylist, q) {
+ snprintf(buf, sizeof(buf), "%s%s%s{/%s,}",
+ epath->s,
+ (epath->s[epath->len-1] == '/') ? ""
+ : "/",
+ esubd->s, machine);
+ if (addentry(m.mymanpath, buf, 0) < 0)
+ errx(1, "malloc failed");
+ }
+ }
+
+ }
+
+ /*
+ * [4] finally, prepend the "-m" m.addpath to mymanpath if it
+ * was specified. subdirs and machine are always applied to
+ * m.addpath.
+ */
+ if (m.addpath) {
+
+ /* note: strtok is going to destroy m.addpath */
+ for (p = strtok(m.addpath, ":") ; p ; p = strtok(NULL, ":")) {
+ len = strlen(p);
+ if (len < 1)
+ continue;
+ TAILQ_FOREACH(esubd, &m.subdirs->entrylist, q) {
+ snprintf(buf, sizeof(buf), "%s%s%s{/%s,}",
+ p, (p[len-1] == '/') ? "" : "/",
+ esubd->s, machine);
+ /* add at front */
+ if (addentry(m.mymanpath, buf, 1) < 0)
+ errx(1, "malloc failed");
+ }
+ }
+
+ }
+
+ /*
+ * now m.mymanpath is complete!
+ */
+#if MAN_DEBUG
+ printf("mymanpath:\n");
+ TAILQ_FOREACH(epath, &m.mymanpath->entrylist, q) {
+ printf("\t%s\n", epath->s);
+ }
+#endif
+
+ /*
+ * start searching for matching files and format them if necessary.
+ * setup an interrupt handler so that we can ensure that temporary
+ * files go away.
+ */
+ (void)signal(SIGINT, onsig);
+ (void)signal(SIGHUP, onsig);
+ (void)signal(SIGPIPE, onsig);
+
+ memset(&pg, 0, sizeof(pg));
+ for (found = 0; *argv; ++argv)
+ if (manual(*argv, &m, &pg)) {
+ found = 1;
+ }
+
+ /* if nothing found, we're done. */
+ if (!found) {
+ (void)cleanup();
+ exit (1);
+ }
+
+ /*
+ * handle the simple display cases first (m.cat, m.how, m.where)
+ */
+ if (m.cat) {
+ for (ap = pg.gl_pathv; *ap != NULL; ++ap) {
+ if (**ap == '\0')
+ continue;
+ cat(*ap);
+ }
+ exit (cleanup());
+ }
+ if (m.how) {
+ for (ap = pg.gl_pathv; *ap != NULL; ++ap) {
+ if (**ap == '\0')
+ continue;
+ how(*ap);
+ }
+ exit(cleanup());
+ }
+ if (m.where) {
+ for (ap = pg.gl_pathv; *ap != NULL; ++ap) {
+ if (**ap == '\0')
+ continue;
+ (void)printf("%s\n", *ap);
+ }
+ exit(cleanup());
+ }
+
+ /*
+ * normal case - we display things in a single command, so
+ * build a list of things to display. first compute total
+ * length of buffer we will need so we can malloc it.
+ */
+ for (ap = pg.gl_pathv, len = m.pagerlen + 1; *ap != NULL; ++ap) {
+ if (**ap == '\0')
+ continue;
+ len += strlen(*ap) + 1;
+ }
+ if ((cmd = malloc(len)) == NULL) {
+ warn("malloc");
+ (void)cleanup();
+ exit(1);
+ }
+
+ /* now build the command string... */
+ p = cmd;
+ len = m.pagerlen;
+ memcpy(p, m.pager, len);
+ p += len;
+ *p++ = ' ';
+ for (ap = pg.gl_pathv; *ap != NULL; ++ap) {
+ if (**ap == '\0')
+ continue;
+ len = strlen(*ap);
+ memcpy(p, *ap, len);
+ p += len;
+ *p++ = ' ';
+ }
+ *--p = '\0';
+
+ /* Use system(3) in case someone's pager is "pager arg1 arg2". */
+ (void)system(cmd);
+
+ exit(cleanup());
+}
+
+/*
+ * manual --
+ * Search the manuals for the pages.
+ */
+static int
+manual(char *page, struct manstate *mp, glob_t *pg)
+{
+ ENTRY *suffix, *mdir;
+ int anyfound, error, found;
+ size_t cnt;
+ char *p, buf[MAXPATHLEN], *escpage, *eptr;
+ static const char escglob[] = "\\~?*{}[]";
+
+ anyfound = 0;
+
+ /*
+ * Fixup page which may contain glob(3) special characters, e.g.
+ * the famous "No man page for [" FAQ.
+ */
+ if ((escpage = malloc((2 * strlen(page)) + 1)) == NULL) {
+ warn("malloc");
+ (void)cleanup();
+ exit(1);
+ }
+
+ p = page;
+ eptr = escpage;
+
+ while (*p) {
+ if (strchr(escglob, *p) != NULL) {
+ *eptr++ = '\\';
+ *eptr++ = *p++;
+ } else
+ *eptr++ = *p++;
+ }
+
+ *eptr = '\0';
+
+ /* For each man directory in mymanpath ... */
+ TAILQ_FOREACH(mdir, &mp->mymanpath->entrylist, q) {
+
+ /*
+ * use glob(3) to look in the filesystem for matching files.
+ * match any suffix here, as we will check that later.
+ */
+ (void)snprintf(buf, sizeof(buf), "%s/%s.*", mdir->s, escpage);
+ if ((error = glob(buf,
+ GLOB_APPEND | GLOB_BRACE | GLOB_NOSORT, NULL, pg)) != 0) {
+ if (error == GLOB_NOMATCH)
+ continue;
+ else {
+ warn("globbing");
+ (void)cleanup();
+ exit(1);
+ }
+ }
+ if (pg->gl_matchc == 0)
+ continue;
+
+ /*
+ * start going through the matches glob(3) just found and
+ * use m.pathsearch (if present) to filter out pages we
+ * don't want. then verify the suffix is valid, and build
+ * the page if we have a _build suffix.
+ */
+ for (cnt = pg->gl_pathc - pg->gl_matchc;
+ cnt < pg->gl_pathc; ++cnt) {
+
+ /* filter on directory path name */
+ if (mp->pathsearch) {
+ p = strstr(pg->gl_pathv[cnt], mp->pathsearch);
+ if (!p || strchr(p, '/') == NULL) {
+ pg->gl_pathv[cnt] = ""; /* zap! */
+ continue;
+ }
+ }
+
+ /*
+ * Try the _suffix key words first.
+ *
+ * XXX
+ * Older versions of man.conf didn't have the suffix
+ * key words, it was assumed that everything was a .0.
+ * We just test for .0 first, it's fast and probably
+ * going to hit.
+ */
+ (void)snprintf(buf, sizeof(buf), "*/%s.0", escpage);
+ if (!fnmatch(buf, pg->gl_pathv[cnt], 0))
+ goto next;
+
+ found = 0;
+ TAILQ_FOREACH(suffix, &mp->suffixlist->entrylist, q) {
+ (void)snprintf(buf,
+ sizeof(buf), "*/%s%s", escpage,
+ suffix->s);
+ if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) {
+ found = 1;
+ break;
+ }
+ }
+ if (found)
+ goto next;
+
+ /* Try the _build key words next. */
+ found = 0;
+ TAILQ_FOREACH(suffix, &mp->buildlist->entrylist, q) {
+ for (p = suffix->s;
+ *p != '\0' && !isspace((unsigned char)*p);
+ ++p)
+ continue;
+ if (*p == '\0')
+ continue;
+ *p = '\0';
+ (void)snprintf(buf,
+ sizeof(buf), "*/%s%s", escpage,
+ suffix->s);
+ if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) {
+ if (!mp->where)
+ build_page(p + 1,
+ &pg->gl_pathv[cnt], mp);
+ *p = ' ';
+ found = 1;
+ break;
+ }
+ *p = ' ';
+ }
+ if (found) {
+next: anyfound = 1;
+ if (!mp->all) {
+ /* Delete any other matches. */
+ while (++cnt< pg->gl_pathc)
+ pg->gl_pathv[cnt] = "";
+ break;
+ }
+ continue;
+ }
+
+ /* It's not a man page, forget about it. */
+ pg->gl_pathv[cnt] = "";
+ }
+
+ if (anyfound && !mp->all)
+ break;
+ }
+
+ /* If not found, enter onto the missing list. */
+ if (!anyfound) {
+ if (addentry(mp->missinglist, page, 0) < 0) {
+ warn("malloc");
+ (void)cleanup();
+ exit(1);
+ }
+ }
+
+ free(escpage);
+ return (anyfound);
+}
+
+/*
+ * build_page --
+ * Build a man page for display.
+ */
+static void
+build_page(char *fmt, char **pathp, struct manstate *mp)
+{
+ static int warned;
+ int olddir, fd, n, tmpdirlen;
+ char *p, *b;
+ char buf[MAXPATHLEN], cmd[MAXPATHLEN], tpath[MAXPATHLEN];
+ const char *tmpdir;
+
+ /* Let the user know this may take awhile. */
+ if (!warned) {
+ warned = 1;
+ warnx("Formatting manual page...");
+ }
+
+ /*
+ * Historically man chdir'd to the root of the man tree.
+ * This was used in man pages that contained relative ".so"
+ * directives (including other man pages for command aliases etc.)
+ * It even went one step farther, by examining the first line
+ * of the man page and parsing the .so filename so it would
+ * make hard(?) links to the cat'ted man pages for space savings.
+ * (We don't do that here, but we could).
+ */
+
+ /* copy and find the end */
+ for (b = buf, p = *pathp; (*b++ = *p++) != '\0';)
+ continue;
+
+ /*
+ * skip the last two path components, page name and man[n] ...
+ * (e.g. buf will be "/usr/share/man" and p will be "man1/man.1")
+ * we also save a pointer to our current directory so that we
+ * can fchdir() back to it. this allows relative MANDIR paths
+ * to work with multiple man pages... e.g. consider:
+ * cd /usr/share && man -M ./man cat ls
+ * when no "cat1" subdir files are present.
+ */
+ olddir = -1;
+ for (--b, --p, n = 2; b != buf; b--, p--)
+ if (*b == '/')
+ if (--n == 0) {
+ *b = '\0';
+ olddir = open(".", O_RDONLY);
+ (void) chdir(buf);
+ p++;
+ break;
+ }
+
+
+ /* advance fmt pass the suffix spec to the printf format string */
+ for (; *fmt && isspace((unsigned char)*fmt); ++fmt)
+ continue;
+
+ /*
+ * Get a temporary file and build a version of the file
+ * to display. Replace the old file name with the new one.
+ */
+ if ((tmpdir = getenv("TMPDIR")) == NULL)
+ tmpdir = _PATH_TMP;
+ tmpdirlen = strlen(tmpdir);
+ (void)snprintf(tpath, sizeof (tpath), "%s%s%s", tmpdir,
+ (tmpdirlen && tmpdir[tmpdirlen-1] == '/') ? "" : "/", TMPFILE);
+ if ((fd = mkstemp(tpath)) == -1) {
+ warn("%s", tpath);
+ (void)cleanup();
+ exit(1);
+ }
+ (void)snprintf(buf, sizeof(buf), "%s > %s", fmt, tpath);
+ (void)snprintf(cmd, sizeof(cmd), buf, p);
+ (void)system(cmd);
+ (void)close(fd);
+ if ((*pathp = strdup(tpath)) == NULL) {
+ warn("malloc");
+ (void)cleanup();
+ exit(1);
+ }
+
+ /* Link the built file into the remove-when-done list. */
+ if (addentry(mp->intmp, *pathp, 0) < 0) {
+ warn("malloc");
+ (void)cleanup();
+ exit(1);
+ }
+
+ /* restore old directory so relative manpaths still work */
+ if (olddir != -1) {
+ fchdir(olddir);
+ close(olddir);
+ }
+}
+
+/*
+ * how --
+ * display how information
+ */
+static void
+how(char *fname)
+{
+ FILE *fp;
+
+ int lcnt, print;
+ char *p, buf[256];
+
+ if (!(fp = fopen(fname, "r"))) {
+ warn("%s", fname);
+ (void)cleanup();
+ exit (1);
+ }
+#define S1 "SYNOPSIS"
+#define S2 "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS"
+#define D1 "DESCRIPTION"
+#define D2 "D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN"
+ for (lcnt = print = 0; fgets(buf, sizeof(buf), fp);) {
+ if (!strncmp(buf, S1, sizeof(S1) - 1) ||
+ !strncmp(buf, S2, sizeof(S2) - 1)) {
+ print = 1;
+ continue;
+ } else if (!strncmp(buf, D1, sizeof(D1) - 1) ||
+ !strncmp(buf, D2, sizeof(D2) - 1)) {
+ if (fp)
+ (void)fclose(fp);
+ return;
+ }
+ if (!print)
+ continue;
+ if (*buf == '\n')
+ ++lcnt;
+ else {
+ for(; lcnt; --lcnt)
+ (void)putchar('\n');
+ for (p = buf; isspace((unsigned char)*p); ++p)
+ continue;
+ (void)fputs(p, stdout);
+ }
+ }
+ (void)fclose(fp);
+}
+
+/*
+ * cat --
+ * cat out the file
+ */
+static void
+cat(char *fname)
+{
+ int fd, n;
+ char buf[2048];
+
+ if ((fd = open(fname, O_RDONLY, 0)) < 0) {
+ warn("%s", fname);
+ (void)cleanup();
+ exit(1);
+ }
+ while ((n = read(fd, buf, sizeof(buf))) > 0)
+ if (write(STDOUT_FILENO, buf, n) != n) {
+ warn("write");
+ (void)cleanup();
+ exit (1);
+ }
+ if (n == -1) {
+ warn("read");
+ (void)cleanup();
+ exit(1);
+ }
+ (void)close(fd);
+}
+
+/*
+ * check_pager --
+ * check the user supplied page information
+ */
+static const char *
+check_pager(const char *name)
+{
+ const char *p;
+
+ /*
+ * if the user uses "more", we make it "more -s"; watch out for
+ * PAGER = "mypager /usr/ucb/more"
+ */
+ for (p = name; *p && !isspace((unsigned char)*p); ++p)
+ continue;
+ for (; p > name && *p != '/'; --p);
+ if (p != name)
+ ++p;
+
+ /* make sure it's "more", not "morex" */
+ if (!strncmp(p, "more", 4) && (!p[4] || isspace((unsigned char)p[4]))){
+ char *newname;
+ if(!(newname = malloc(strlen(p) + 20))) {
+ errx(1, "malloc failed");
+ }
+ (void)sprintf(newname, "%s %s", p, "-s");
+ name = newname;
+ }
+
+ return (name);
+}
+
+/*
+ * jump --
+ * strip out flag argument and jump
+ */
+static void
+jump(char **argv, char *flag, char *name)
+{
+ char **arg;
+
+ argv[0] = name;
+ for (arg = argv + 1; *arg; ++arg)
+ if (!strcmp(*arg, flag))
+ break;
+ for (; *arg; ++arg)
+ arg[0] = arg[1];
+ execvp(name, argv);
+ (void)fprintf(stderr, "%s: Command not found.\n", name);
+ exit(1);
+}
+
+/*
+ * onsig --
+ * If signaled, delete the temporary files.
+ */
+static void
+onsig(int signo)
+{
+
+ (void)cleanup();
+
+#if 0
+ (void)raise_default_signal(signo);
+#endif
+
+ /* NOTREACHED */
+ exit (1);
+}
+
+/*
+ * cleanup --
+ * Clean up temporary files, show any error messages.
+ */
+static int
+cleanup()
+{
+ TAG *intmpp, *missp;
+ ENTRY *ep;
+ int rval;
+
+ rval = 0;
+ /*
+ * note that _missing and _intmp were created by main(), so
+ * gettag() cannot return NULL here.
+ */
+ missp = gettag("_missing", 0); /* missing man pages */
+ intmpp = gettag("_intmp", 0); /* tmp files we need to unlink */
+
+ TAILQ_FOREACH(ep, &missp->entrylist, q) {
+ warnx("no entry for %s in the manual.", ep->s);
+ rval = 1;
+ }
+
+ TAILQ_FOREACH(ep, &intmpp->entrylist, q)
+ (void)unlink(ep->s);
+
+ return (rval);
+}
+
+/*
+ * usage --
+ * print usage message and die
+ */
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage: %s [-acw|-h] [-C cfg] [-M path] "
+ "[-m path] [-S srch] [[-s] sect] name ...\n", getprogname());
+ (void)fprintf(stderr,
+ "usage: %s -k [-C cfg] [-M path] [-m path] keyword ...\n",
+ getprogname());
+ exit(1);
+}
--- /dev/null
+.\" $NetBSD: man.conf.5,v 1.20 2007/02/10 19:27:39 reed Exp $
+.\"
+.\" Copyright (c) 1989, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)man.conf.5 8.5 (Berkeley) 1/2/94
+.\"
+.Dd April 10, 2006
+.Dt MAN.CONF 5
+.Os
+.Sh NAME
+.Nm man.conf
+.Nd configuration file for manual pages
+.Sh DESCRIPTION
+The
+.Nm
+file contains the default configuration used by
+.Xr man 1 ,
+.Xr apropos 1 ,
+.Xr whatis 1 ,
+.Xr catman 8 ,
+and
+.Xr makewhatis 8
+to find manual pages and information about manual pages (e.g. the
+whatis database).
+.Pp
+Manual pages are located by searching an ordered set of directories
+called the
+.Dq man path
+for a file that matches the name of the requested page.
+Each directory in the search path usually has a set of subdirectories
+in it (though this is not required).
+When subdirectories are used, there are normally two subdirectories
+for each section of the manual.
+One subdirectory contains formatted copies of that section's manual
+pages that can be directly displayed to a terminal, while the other
+section subdirectory contains unformatted copies of the pages (see
+.Xr nroff 1
+and
+.Xr mdoc 7 ) .
+Formatted manual pages are normally named with a trailing
+.Dq \.0
+suffix.
+.Pp
+The
+.Nm
+file contains comment and configuration lines.
+Comment lines start with the
+.Dq #
+character.
+Blank lines are also treated as comment lines.
+Configuration lines consist of a configuration keyword followed by a
+configuration string.
+There are two types of configuration keywords: control keywords and
+section keywords.
+Control keywords must start with the
+.Dq _
+character.
+The following control keywords are currently defined:
+.Bl -tag -width "_version"
+.It _build
+identifies the set of suffixes used for manual pages that must be
+formatted for display and the command that should be used to format
+them.
+Manual file names, regardless of their format, are expected to end in a
+.Dq \.*
+pattern, i.e. a
+.Dq \&\.
+followed by some suffix.
+The first field of a _build line contains a man page suffix specification.
+The suffix specification may contain the normal shell globbing characters
+(NOT including curly braces
+.Pq Dq {} ) .
+The rest of the _build line is a shell command line whose standard
+output is a formatted manual page that can be directly displayed to
+the user.
+Any occurrences of the string
+.Dq %s
+in the shell command line will
+be replaced by the name of the file which is being formatted.
+.It _crunch
+used by
+.Xr catman 8
+to determine how to crunch formatted pages
+which originally were compressed man pages: The first field lists a suffix
+which indicates what kind of compression were used to compress the man page.
+The rest of the line must be a shell command line, used to compress the
+formatted pages.
+.It _default
+contains the system-wide default man path used to search for man pages.
+.It _subdir
+contains the list (in search order) of section subdirectories which will
+be searched in any man path directory named with a trailing slash
+.Pq Dq /
+character.
+This list is also used, even if there is no trailing slash character,
+when a path is specified to the
+.Xr man 1
+utility by the user, by the
+.Ev MANPATH
+environment variable, or by the
+.Fl M
+and
+.Fl m
+options.
+.It _suffix
+identifies the set of suffixes used for formatted man pages
+(the
+.Dq \.0
+suffix is normally used here).
+Formatted man pages can be directly displayed to the user.
+Each suffix may contain the normal shell globbing characters (NOT
+including curly braces
+.Pq Dq {} ) .
+.It _version
+contains the version of the configuration file.
+.It _whatdb
+defines the full pathname (not just a directory path) for a database to
+be used
+by the
+.Xr apropos 1
+and
+.Xr whatis 1
+commands.
+The pathname may contain the normal shell globbing characters,
+including curly braces
+.Pq Dq {} ;
+to escape a shell globbing character,
+precede it with a backslash
+.Pq Dq \e .
+.El
+.Pp
+Section configuration lines in
+.Nm
+consist of a section keyword naming the section and a configuration
+string that defines the directory or subdirectory path that the section's
+manual pages are located in.
+The path may contain the normal shell globbing characters,
+including curly braces
+.Pq Dq {} ;
+to escape a shell globbing character,
+precede it with a backslash
+.Pq Dq \e .
+Section keywords must not start with the
+.Dq _
+character.
+.Pp
+A section path may contain either a list of absolute directories or
+a list of or relative directories (but not both).
+Relative directory paths are treated as a list of subdirectories that
+are appended to the current man path directory being searched.
+Section configuration lines with absolute directory paths (starting with
+.Dq / )
+completely replace the current man search path directory with their
+content.
+.Pp
+Section configuration lines with absolute directory paths ending
+with a trailing slash character are expected to contain subdirectories
+of manual pages, (see the keyword
+.Dq _subdir
+above).
+The
+.Dq _subdir
+subdirectory list is not applied to absolute section directories
+if there is no trailing slash.
+.Pp
+In addition to the above rules, the
+.Xr man 1
+command also always checks in each directory that it searches for
+a subdirectory with the same name as the current machine type.
+If the machine-specific directory is found, it is also searched.
+This allows the manual to contain machine-specific man pages.
+Note that the machine subdirectory does not need to be specified
+in the
+.Nm
+file.
+.Pp
+Multiple specifications for all types of
+.Nm
+configuration lines are
+cumulative and the entries are used in the order listed in the file;
+multiple entries may be listed per line, as well.
+.Sh FILES
+.Bl -tag -width /etc/man.conf -compact
+.It Pa /etc/man.conf
+Standard manual configuration file.
+.El
+.Sh EXAMPLES
+Given the following
+.Nm
+file:
+.Bd -literal -offset indent
+_version BSD.2
+_subdir cat[123]
+_suffix .0
+_build .[1-9] nroff -man %s
+_build .tbl tbl %s | nroff -man
+_default /usr/share/man/
+sect3 /usr/share/man/{old/,}cat3
+.Ed
+.Pp
+By default, the command
+.Dq Li man mktemp
+will search for
+.Dq mktemp.\*[Lt]any_digit\*[Gt]
+and
+.Dq mktemp.tbl
+in the directories
+.Dq Pa /usr/share/man/cat1 ,
+.Dq Pa /usr/share/man/cat2 ,
+and
+.Dq Pa /usr/share/man/cat3 .
+If on a machine of type
+.Dq vax ,
+the subdirectory
+.Dq vax
+in each
+directory would be searched as well, before the directory was
+searched.
+.Pp
+If
+.Dq mktemp.tbl
+was found first, the command
+.Dq Li tbl \*[Lt]manual page\*[Gt] | nroff -man
+would be run to build a man page for display to the user.
+.Pp
+The command
+.Dq Li man sect3 mktemp
+would search the directories
+.Dq Pa /usr/share/man/old/cat3
+and
+.Dq Pa /usr/share/man/cat3 ,
+in that order, for
+the mktemp manual page.
+If a subdirectory with the same name as the current machine type
+existed in any of them, it would be searched as well, before each
+of them were searched.
+.Sh SEE ALSO
+.Xr apropos 1 ,
+.Xr machine 1 ,
+.Xr man 1 ,
+.Xr whatis 1 ,
+.Xr whereis 1 ,
+.Xr fnmatch 3 ,
+.Xr glob 3 ,
+.Xr catman 8 ,
+.Xr makewhatis 8
--- /dev/null
+/* $NetBSD: manconf.c,v 1.6 2008/03/08 15:48:27 christos Exp $ */
+
+/*
+ * Copyright (c) 1989, 1993, 1995
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * manconf.c: provides interface for reading man.conf files
+ *
+ * note that this code is shared across all programs that read man.conf.
+ * (currently: apropos, catman, makewhatis, man, and whatis...)
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "manconf.h"
+#include "pathnames.h"
+
+TAILQ_HEAD(_head, _tag);
+static struct _head head; /* 'head' -- top level data structure */
+
+/*
+ * xstrdup: like strdup, but also returns length of string in lenp
+ */
+static char *
+xstrdup(const char *str, size_t *lenp)
+{
+ size_t len;
+ char *copy;
+
+ len = strlen(str) + 1;
+ copy = malloc(len);
+ if (!copy)
+ return NULL;
+ (void)memcpy(copy, str, len);
+ if (lenp)
+ *lenp = len - 1; /* subtract out the null */
+ return copy;
+}
+
+/*
+ * config --
+ *
+ * Read the configuration file and build a doubly linked
+ * list off of "head" that looks like:
+ *
+ * tag1 <-> entry <-> entry <-> entry
+ * |
+ * tag2 <-> entry <-> entry <-> entry
+ *
+ * note: will err/errx out on error (fopen or malloc failure)
+ */
+void
+config(const char *fname)
+{
+ TAG *tp;
+ FILE *cfp;
+ size_t len;
+ int lcnt;
+ char *p, *t, type;
+
+ if (fname == NULL)
+ fname = _PATH_MANCONF;
+ if ((cfp = fopen(fname, "r")) == NULL)
+ err(EXIT_FAILURE, "%s", fname);
+ TAILQ_INIT(&head);
+ for (lcnt = 1; (p = fgetln(cfp, &len)) != NULL; ++lcnt) {
+ if (len == 1) /* Skip empty lines. */
+ continue;
+ if (p[len - 1] != '\n') { /* Skip corrupted lines. */
+ warnx("%s: line %d corrupted", fname, lcnt);
+ continue;
+ }
+ p[len - 1] = '\0'; /* Terminate the line. */
+
+ /* Skip leading space. */
+ for (/*EMPTY*/; *p != '\0' && isspace((unsigned char)*p); ++p)
+ continue;
+ /* Skip empty/comment lines. */
+ if (*p == '\0' || *p == '#')
+ continue;
+ /* Find first token. */
+ for (t = p; *t && !isspace((unsigned char)*t); ++t)
+ continue;
+ if (*t == '\0') /* Need more than one token.*/
+ continue;
+ *t = '\0';
+
+ tp = gettag(p, 1);
+ if (!tp)
+ errx(EXIT_FAILURE, "gettag: malloc failed");
+
+ /*
+ * Attach new records. Check to see if it is a
+ * section record or not.
+ */
+
+ if (*p == '_') { /* not a section record */
+ /*
+ * Special cases: _build and _crunch take the
+ * rest of the line as a single entry.
+ */
+ if (!strcmp(p, "_build") || !strcmp(p, "_crunch")) {
+ /*
+ * The reason we're not just using
+ * strtok(3) for all of the parsing is
+ * so we don't get caught if a line
+ * has only a single token on it.
+ */
+ while (*++t && isspace((unsigned char)*t));
+ if (addentry(tp, t, 0) == -1)
+ errx(EXIT_FAILURE,
+ "addentry: malloc failed");
+ } else {
+ for (++t; (p = strtok(t, " \t\n")) != NULL;
+ t = NULL) {
+ if (addentry(tp, p, 0) == -1)
+ errx(EXIT_FAILURE,
+ "addentry: malloc failed");
+ }
+ }
+
+ } else { /* section record */
+
+ /*
+ * section entries can either be all absolute
+ * paths or all relative paths, but not both.
+ */
+ type = (TAILQ_FIRST(&tp->entrylist) != NULL) ?
+ *(TAILQ_FIRST(&tp->entrylist)->s) : 0;
+
+ for (++t; (p = strtok(t, " \t\n")) != NULL; t = NULL) {
+
+ /* ensure an assigned type */
+ if (type == 0)
+ type = *p;
+
+ /* check for illegal mix */
+ if (*p != type) {
+ warnx("section %s: %s: invalid entry, does not match previous types",
+ tp->s, p);
+ warnx("man.conf cannot mix absolute and relative paths in an entry");
+ continue;
+ }
+ if (addentry(tp, p, 0) == -1)
+ errx(EXIT_FAILURE,
+ "addentry: malloc failed");
+ }
+ }
+ }
+ (void)fclose(cfp);
+}
+
+/*
+ * gettag --
+ * if (!create) return tag for given name if it exists, or NULL otherwise
+ *
+ * if (create) return tag for given name if it exists, try and create
+ * a new tag if it does not exist. return NULL if unable to create new
+ * tag.
+ */
+TAG *
+gettag(const char *name, int create)
+{
+ TAG *tp;
+
+ TAILQ_FOREACH(tp, &head, q)
+ if (!strcmp(name, tp->s))
+ return tp;
+ if (!create)
+ return NULL;
+
+ /* try and add it in */
+ tp = malloc(sizeof(*tp));
+ if (tp)
+ tp->s = xstrdup(name, &tp->len);
+ if (!tp || !tp->s) {
+ if (tp)
+ free(tp);
+ return NULL;
+ }
+ TAILQ_INIT(&tp->entrylist);
+ TAILQ_INSERT_TAIL(&head, tp, q);
+ return tp;
+}
+
+/*
+ * addentry --
+ * add an entry to a list.
+ * returns -1 if malloc failed, otherwise 0.
+ */
+int
+addentry(TAG *tp, const char *newent, int ishead)
+{
+ ENTRY *ep;
+
+ ep = malloc(sizeof(*ep));
+ if (ep)
+ ep->s = xstrdup(newent, &ep->len);
+ if (!ep || !ep->s) {
+ if (ep)
+ free(ep);
+ return -1;
+ }
+ if (ishead)
+ TAILQ_INSERT_HEAD(&tp->entrylist, ep, q);
+ else
+ TAILQ_INSERT_TAIL(&tp->entrylist, ep, q);
+
+ return 0;
+}
--- /dev/null
+/* $NetBSD: manconf.h,v 1.3 2006/04/10 14:39:06 chuck Exp $ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)config.h 8.4 (Berkeley) 12/18/93
+ */
+
+/*
+ * manconf.h: common data structures and APIs shared across all programs
+ * that access man.conf (currently: apropos, catman, makewhatis, man, and
+ * whatis).
+ */
+
+/* TAG: top-level structure (one per section/reserved word) */
+typedef struct _tag {
+ TAILQ_ENTRY(_tag) q; /* Queue of tags */
+
+ TAILQ_HEAD(tqh, _entry) entrylist; /* Queue of entries */
+ char *s; /* Associated string */
+ size_t len; /* Length of 's' */
+} TAG;
+
+/* ENTRY: each TAG has one or more ENTRY strings linked off of it */
+typedef struct _entry {
+ TAILQ_ENTRY(_entry) q; /* Queue of entries */
+
+ char *s; /* Associated string */
+ size_t len; /* Length of 's' */
+} ENTRY;
+
+int addentry(TAG *, const char *, int);
+void config(const char *);
+TAG *gettag(const char *, int);
--- /dev/null
+/* $NetBSD: pathnames.h,v 1.5 2003/08/07 11:15:11 agc Exp $ */
+
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.3 (Berkeley) 1/2/94
+ */
+
+#include <minix/paths.h>
+
+#define _PATH_MANCONF "/etc/man.conf"
+#define _PATH_PAGER "/usr/bin/more -s"
+#define _PATH_WHATIS "whatis.db"
+#define TMPFILE "man.XXXXXX"