diff options
author | Kiyoshi Aman <kiyoshi.aman+adelie@gmail.com> | 2019-02-01 22:55:37 +0000 |
---|---|---|
committer | Kiyoshi Aman <kiyoshi.aman+adelie@gmail.com> | 2019-02-03 18:22:05 -0600 |
commit | 5b57d28ffb6e1ef86b50f7d05d977826eae89bfe (patch) | |
tree | 154a22fe556b49e6927197336f8bf91b12eacd5e /usr.bin/man | |
download | userland-5b57d28ffb6e1ef86b50f7d05d977826eae89bfe.tar.gz userland-5b57d28ffb6e1ef86b50f7d05d977826eae89bfe.tar.bz2 userland-5b57d28ffb6e1ef86b50f7d05d977826eae89bfe.tar.xz userland-5b57d28ffb6e1ef86b50f7d05d977826eae89bfe.zip |
initial population
Diffstat (limited to 'usr.bin/man')
-rw-r--r-- | usr.bin/man/man.1 | 269 | ||||
-rw-r--r-- | usr.bin/man/man.c | 1077 | ||||
-rw-r--r-- | usr.bin/man/man.conf.5 | 286 | ||||
-rw-r--r-- | usr.bin/man/manconf.c | 272 | ||||
-rw-r--r-- | usr.bin/man/manconf.h | 60 | ||||
-rw-r--r-- | usr.bin/man/pathnames.h | 39 |
6 files changed, 2003 insertions, 0 deletions
diff --git a/usr.bin/man/man.1 b/usr.bin/man/man.1 new file mode 100644 index 0000000..49b3bf3 --- /dev/null +++ b/usr.bin/man/man.1 @@ -0,0 +1,269 @@ +.\" $NetBSD: man.1,v 1.29 2016/06/16 15:10:58 abhinav 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 June 16, 2016 +.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 C Ar file +.Fl f +.Ar command Ar ... +.Nm +.Op Fl C Ar file +.Fl k +.Ar keyword Ar ... +.Nm +.Fl p +.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 f +Synonym for +.Xr whatis 1 . +It searches man pages for +.Ar command +in their names and displays header lines from all matching pages. +.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 +Search man pages for +.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 p +Print the search path for the manual pages. +.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. +.Pp +If +.Ar name +is given with a full path (beginning with +.Ql Pa \&/ ) +or a relative path that begins with +.Ql Pa .\&/ +or +.Ql Pa .\&./ , +then +.Nm +interprets it as a file specification, so that you can do +.Nm +.Cm ./foo.5 +or even +.Nm +.Cm /cd/foo/bar.1.gz . +If +.Ar name +contains +.Ql Pa / +but does not match one of the above cases, then the +search path is used; this allows you to request +machine-specific man pages, such as +.Nm Cm vax/boot . +.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. +Machine-specific man pages may also be requested by +prepending the relevant subdirectory name to the page name, +separated by +.Ql Pa \&/ . +.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. +.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. diff --git a/usr.bin/man/man.c b/usr.bin/man/man.c new file mode 100644 index 0000000..cba36bc --- /dev/null +++ b/usr.bin/man/man.c @@ -0,0 +1,1077 @@ +/* $NetBSD: man.c,v 1.67 2018/06/15 20:16:35 mrg 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> + +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1987, 1993, 1994, 1995\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)man.c 8.17 (Berkeley) 1/31/95"; +#else +__RCSID("$NetBSD: man.c,v 1.67 2018/06/15 20:16:35 mrg Exp $"); +#endif +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/stat.h> +#include <sys/utsname.h> + +#include <ctype.h> +#include <err.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 <locale.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 */ + int getpath; /* -p: print the path of directories containing man pages */ + + /* 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 */ + const char *machine; /* machine */ + const char *machclass; /* machine class */ +}; + +/* + * prototypes + */ +static void build_page(const char *, char **, struct manstate *); +static void cat(const char *); +static const char *check_pager(const char *); +static int cleanup(void); +static void how(const char *); +static void jump(char **, const char *, const char *) __dead; +static int manual(char *, struct manstate *, glob_t *); +static void onsig(int) __dead; +static void usage(void) __dead; +static void addpath(struct manstate *, const char *, size_t, const char *); +static const char *getclass(const char *); +static void printmanpath(struct manstate *); + +/* + * main function + */ +int +main(int argc, char **argv) +{ + static struct manstate m; + struct utsname utsname; + int ch, abs_section, found; + ENTRY *esubd, *epath; + char *p, **ap, *cmd; + size_t len; + glob_t pg; + + setprogname(argv[0]); + setlocale(LC_ALL, ""); + /* + * parse command line... + */ + while ((ch = getopt(argc, argv, "-aC:cfhkM:m:P:ps: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 */ + if ((m.manpath = strdup(optarg)) == NULL) + err(EXIT_FAILURE, "malloc failed"); + break; + case 'p': + m.getpath = 1; + 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 (!m.getpath && !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 ((m.machine = getenv("MACHINE")) == NULL) { + if (uname(&utsname) == -1) + err(EXIT_FAILURE, "uname"); + m.machine = utsname.machine; + } + + m.machclass = getclass(m.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(EXIT_FAILURE, "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(EXIT_FAILURE, "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) + addpath(&m, p, len, esubd->s); + } + + } else { + + TAILQ_FOREACH(epath, &m.defaultpath->entrylist, q) { + /* handle trailing "/" magic here ... */ + if (abs_section && epath->s[epath->len - 1] != '/') { + addpath(&m, "", 1, epath->s); + continue; + } + + TAILQ_FOREACH(esubd, &m.subdirs->entrylist, q) + addpath(&m, epath->s, epath->len, esubd->s); + } + + } + + /* + * [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) + addpath(&m, p, len, esubd->s); + } + + } + + if (m.getpath) + printmanpath(&m); + + /* + * 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(EXIT_FAILURE); + } + + /* + * 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(EXIT_FAILURE); + } + + /* 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()); +} + +static int +manual_find_literalfile(struct manstate *mp, char **pv) +{ + ENTRY *suffix; + int found; + char buf[MAXPATHLEN]; + const char *p; + int suflen; + + found = 0; + + /* + * Expand both '*' and suffix to force an actual + * match via fnmatch(3). Since the only match in pg + * is the literal file, the match is genuine. + */ + + TAILQ_FOREACH(suffix, &mp->buildlist->entrylist, q) { + for (p = suffix->s, suflen = 0; + *p != '\0' && !isspace((unsigned char)*p); + ++p) + ++suflen; + if (*p == '\0') + continue; + + (void)snprintf(buf, sizeof(buf), "*%.*s", suflen, suffix->s); + + if (!fnmatch(buf, *pv, 0)) { + if (!mp->where) + build_page(p + 1, pv, mp); + found = 1; + break; + } + } + + return found; +} + +static int +manual_find_buildkeyword(const char *prefix, const char *escpage, + struct manstate *mp, char **pv) +{ + ENTRY *suffix; + int found; + char buf[MAXPATHLEN]; + const char *p; + int suflen; + + found = 0; + /* Try the _build keywords next. */ + TAILQ_FOREACH(suffix, &mp->buildlist->entrylist, q) { + for (p = suffix->s, suflen = 0; + *p != '\0' && !isspace((unsigned char)*p); + ++p) + ++suflen; + if (*p == '\0') + continue; + + (void)snprintf(buf, sizeof(buf), "%s%s%.*s", + prefix, escpage, suflen, suffix->s); + if (!fnmatch(buf, *pv, 0)) { + if (!mp->where) + build_page(p + 1, pv, mp); + found = 1; + break; + } + } + + return found; +} + +/* + * 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(EXIT_FAILURE); + } + + p = page; + eptr = escpage; + + while (*p) { + if (strchr(escglob, *p) != NULL) { + *eptr++ = '\\'; + *eptr++ = *p++; + } else + *eptr++ = *p++; + } + + *eptr = '\0'; + + /* + * If 'page' is given with an absolute path, + * or a relative path explicitly beginning with "./" + * or "../", then interpret it as a file specification. + */ + if ((page[0] == '/') + || (page[0] == '.' && page[1] == '/') + || (page[0] == '.' && page[1] == '.' && page[2] == '/') + ) { + /* check if file actually exists */ + (void)strlcpy(buf, escpage, sizeof(buf)); + error = glob(buf, GLOB_APPEND | GLOB_BRACE | GLOB_NOSORT, NULL, pg); + if (error != 0) { + if (error == GLOB_NOMATCH) { + goto notfound; + } else { + errx(EXIT_FAILURE, "glob failed"); + } + } + + if (pg->gl_matchc == 0) + goto notfound; + + /* literal file only yields one match */ + cnt = pg->gl_pathc - pg->gl_matchc; + + if (manual_find_literalfile(mp, &pg->gl_pathv[cnt])) { + anyfound = 1; + } else { + /* It's not a man page, forget about it. */ + *pg->gl_pathv[cnt] = '\0'; + } + + notfound: + if (!anyfound) { + if (addentry(mp->missinglist, page, 0) < 0) { + warn("malloc"); + (void)cleanup(); + exit(EXIT_FAILURE); + } + } + free(escpage); + return anyfound; + } + + /* 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(EXIT_FAILURE); + } + } + 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] = '\0'; /* zap! */ + continue; + } + } + + /* + * Try the _suffix keywords first. + * + * XXX + * Older versions of man.conf didn't have the _suffix + * keywords, 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 keywords next. */ + found = manual_find_buildkeyword("*/", escpage, + mp, &pg->gl_pathv[cnt]); + if (found) { +next: anyfound = 1; + if (!mp->all) { + /* Delete any other matches. */ + while (++cnt< pg->gl_pathc) + *pg->gl_pathv[cnt] = '\0'; + break; + } + continue; + } + + /* It's not a man page, forget about it. */ + *pg->gl_pathv[cnt] = '\0'; + } + + 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(EXIT_FAILURE); + } + } + + free(escpage); + return anyfound; +} + +/* + * A do-nothing counterpart to fmtcheck(3) that only supplies the + * __format_arg marker. Actual fmtcheck(3) call is done once in + * config(). + */ +__always_inline __format_arg(2) +static inline const char * +fmtcheck_ok(const char *userfmt, const char *template) +{ + return userfmt; +} + +/* + * build_page -- + * Build a man page for display. + */ +static void +build_page(const char *fmt, char **pathp, struct manstate *mp) +{ + static int warned; + int olddir, fd, n; + size_t 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 past 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 > 0 && tmpdir[tmpdirlen-1] == '/') ? "" : "/", TMPFILE); + if ((fd = mkstemp(tpath)) == -1) { + warn("%s", tpath); + (void)cleanup(); + exit(EXIT_FAILURE); + } + (void)snprintf(buf, sizeof(buf), "%s > %s", fmt, tpath); + (void)snprintf(cmd, sizeof(cmd), fmtcheck_ok(buf, "%s"), p); + (void)system(cmd); + (void)close(fd); + if ((*pathp = strdup(tpath)) == NULL) { + warn("malloc"); + (void)cleanup(); + exit(EXIT_FAILURE); + } + + /* Link the built file into the remove-when-done list. */ + if (addentry(mp->intmp, *pathp, 0) < 0) { + warn("malloc"); + (void)cleanup(); + exit(EXIT_FAILURE); + } + + /* restore old directory so relative manpaths still work */ + if (olddir != -1) { + fchdir(olddir); + close(olddir); + } +} + +/* + * how -- + * display how information + */ +static void +how(const char *fname) +{ + FILE *fp; + + int lcnt, print; + char buf[256]; + const char *p; + + if (!(fp = fopen(fname, "r"))) { + warn("%s", fname); + (void)cleanup(); + exit(EXIT_FAILURE); + } +#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(const char *fname) +{ + int fd; + ssize_t n; + char buf[2048]; + + if ((fd = open(fname, O_RDONLY, 0)) < 0) { + warn("%s", fname); + (void)cleanup(); + exit(EXIT_FAILURE); + } + while ((n = read(fd, buf, sizeof(buf))) > 0) + if (write(STDOUT_FILENO, buf, (size_t)n) != n) { + warn("write"); + (void)cleanup(); + exit(EXIT_FAILURE); + } + if (n == -1) { + warn("read"); + (void)cleanup(); + exit(EXIT_FAILURE); + } + (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; + (void)asprintf(&newname, "%s %s", p, "-s"); + name = newname; + } + + return name; +} + +/* + * jump -- + * strip out flag argument and jump + */ +static void +jump(char **argv, const char *flag, const char *name) +{ + char **arg; + + argv[0] = __UNCONST(name); + for (arg = argv + 1; *arg; ++arg) + if (!strcmp(*arg, flag)) + break; + for (; *arg; ++arg) + arg[0] = arg[1]; + execvp(name, argv); + err(EXIT_FAILURE, "Cannot execute `%s'", name); +} + +/* + * onsig -- + * If signaled, delete the temporary files. + */ +static void +onsig(int signo) +{ + + (void)cleanup(); + + (void)raise_default_signal(signo); + + /* NOTREACHED */ + exit(EXIT_FAILURE); +} + +/* + * cleanup -- + * Clean up temporary files, show any error messages. + */ +static int +cleanup(void) +{ + TAG *intmpp, *missp; + ENTRY *ep; + int rval; + + rval = EXIT_SUCCESS; + /* + * 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 = EXIT_FAILURE; + } + + TAILQ_FOREACH(ep, &intmpp->entrylist, q) + (void)unlink(ep->s); + + return rval; +} + +static const char * +getclass(const char *machine) +{ + char buf[BUFSIZ]; + TAG *t; + snprintf(buf, sizeof(buf), "_%s", machine); + t = gettag(buf, 0); + return t != NULL && !TAILQ_EMPTY(&t->entrylist) ? + TAILQ_FIRST(&t->entrylist)->s : NULL; +} + +static void +addpath(struct manstate *m, const char *dir, size_t len, const char *sub) +{ + char buf[2 * MAXPATHLEN + 1]; + (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,%s%s%s}", + dir, (dir[len - 1] == '/') ? "" : "/", sub, m->machine, + m->machclass ? "/" : "", m->machclass ? m->machclass : "", + m->machclass ? "," : ""); + if (addentry(m->mymanpath, buf, 0) < 0) + errx(EXIT_FAILURE, "malloc failed"); +} + +/* + * usage -- + * print usage message and die + */ +static void +usage(void) +{ + (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 [-C file] -f command ...\n", getprogname()); + (void)fprintf(stderr, + "Usage: %s [-C file] -k keyword ...\n", + getprogname()); + (void)fprintf(stderr, "Usage: %s -p\n", getprogname()); + exit(EXIT_FAILURE); +} + +/* + * printmanpath -- + * Prints a list of directories containing man pages. + */ +static void +printmanpath(struct manstate *m) +{ + ENTRY *epath; + char **ap; + glob_t pg; + struct stat sb; + TAG *path = m->mymanpath; + + /* the tail queue is empty if no _default tag is defined in * man.conf */ + if (TAILQ_EMPTY(&path->entrylist)) + errx(EXIT_FAILURE, "Empty manpath"); + + TAILQ_FOREACH(epath, &path->entrylist, q) { + if (glob(epath->s, GLOB_BRACE | GLOB_NOSORT, NULL, &pg) != 0) + err(EXIT_FAILURE, "glob failed"); + + if (pg.gl_matchc == 0) { + globfree(&pg); + continue; + } + + for (ap = pg.gl_pathv; *ap != NULL; ++ap) { + /* Skip cat page directories */ + if (strstr(*ap, "/cat") != NULL) + continue; + /* Skip non-directories. */ + if (stat(*ap, &sb) == 0 && S_ISDIR(sb.st_mode)) + printf("%s\n", *ap); + } + globfree(&pg); + } +} diff --git a/usr.bin/man/man.conf.5 b/usr.bin/man/man.conf.5 new file mode 100644 index 0000000..b89c74b --- /dev/null +++ b/usr.bin/man/man.conf.5 @@ -0,0 +1,286 @@ +.\" $NetBSD: man.conf.5,v 1.27 2016/06/16 15:11:43 abhinav 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 June 16, 2016 +.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 makemandb 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 XXmachineX +.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. +There should be exactly one occurrence of the string +.Dq %s +in the shell command line, and it 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. +There should be exactly one occurrence of the string +.Dq %s +in the shell command line, and it will +be replaced by the name of the output file. +.It _default +Contains the system-wide default man path used to search for man pages. +.It _mandb +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 . +.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 _ Ns Aq machine +Defines additional paths to be searched for the particular +.Dv machine +whose literal value is taken from +.Xr uname 1 +.Fl m . +For example on an +.Dv amd64 , +.Dv _amd64 +is used. +.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 +_i386 x86 +_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. Ns Aq any_digit +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 Ao manual page Ac | 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 makemandb 8 diff --git a/usr.bin/man/manconf.c b/usr.bin/man/manconf.c new file mode 100644 index 0000000..f12d031 --- /dev/null +++ b/usr.bin/man/manconf.c @@ -0,0 +1,272 @@ +/* $NetBSD: manconf.c,v 1.8 2014/02/17 02:53:48 uwe 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> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)config.c 8.8 (Berkeley) 1/31/95"; +#else +__RCSID("$NetBSD: manconf.c,v 1.8 2014/02/17 02:53:48 uwe Exp $"); +#endif +#endif /* not lint */ + +#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")) { + const char *u; + + /* + * 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)); +#ifndef HAVE_NBTOOL_CONFIG_H + /* pre-verify user-supplied command format */ + u = t; + while (*u && !isspace((unsigned char)*u)) + ++u; + while (*u && isspace((unsigned char)*u)) + ++u; + if (fmtcheck(u, "%s") != u) { + warnx("%s:%d: invalid %s command ignored", + fname, lcnt, p); + continue; + } +#endif /* !HAVE_NBTOOL_CONFIG_H */ + 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 = (char)((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; +} diff --git a/usr.bin/man/manconf.h b/usr.bin/man/manconf.h new file mode 100644 index 0000000..ab87728 --- /dev/null +++ b/usr.bin/man/manconf.h @@ -0,0 +1,60 @@ +/* $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); diff --git a/usr.bin/man/pathnames.h b/usr.bin/man/pathnames.h new file mode 100644 index 0000000..15aa211 --- /dev/null +++ b/usr.bin/man/pathnames.h @@ -0,0 +1,39 @@ +/* $NetBSD: pathnames.h,v 1.7 2016/05/21 20:54:34 christos 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 <paths.h> + +#define _PATH_MANCONF "/etc/man.conf" +#define _PATH_PAGER "/usr/bin/more -s" +#define _PATH_WHATIS "whatis.db" +#define TMPFILE "man.XXXXXX" |