From 4d7f45e2595cdda6921d9ed62f7ce9ba08453d13 Mon Sep 17 00:00:00 2001 From: bde Date: Sun, 6 Jul 1997 06:54:14 +0000 Subject: Finish importing Lite2's src/usr.bin, except for ex, diff, grep, mail, pascal and vmstat.sparc. All changed files on the vendor branch should already have been imported. --- usr.bin/man/Makefile | 8 + usr.bin/man/config.c | 176 ++++++++++++ usr.bin/man/config.h | 57 ++++ usr.bin/man/man.1 | 188 +++++++++++++ usr.bin/man/man.c | 712 ++++++++++++++++++++++++++++++++++++++++++++++++ usr.bin/man/man.conf | 46 ++++ usr.bin/man/man.conf.5 | 195 +++++++++++++ usr.bin/man/pathnames.h | 39 +++ 8 files changed, 1421 insertions(+) create mode 100644 usr.bin/man/Makefile create mode 100644 usr.bin/man/config.c create mode 100644 usr.bin/man/config.h create mode 100644 usr.bin/man/man.1 create mode 100644 usr.bin/man/man.c create mode 100644 usr.bin/man/man.conf create mode 100644 usr.bin/man/man.conf.5 create mode 100644 usr.bin/man/pathnames.h (limited to 'usr.bin/man') diff --git a/usr.bin/man/Makefile b/usr.bin/man/Makefile new file mode 100644 index 0000000..728d038 --- /dev/null +++ b/usr.bin/man/Makefile @@ -0,0 +1,8 @@ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +PROG= man +SRCS= config.c man.c +MAN1= man.0 +MAN5= man.conf.0 + +.include diff --git a/usr.bin/man/config.c b/usr.bin/man/config.c new file mode 100644 index 0000000..42780fa --- /dev/null +++ b/usr.bin/man/config.c @@ -0,0 +1,176 @@ +/* + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)config.c 8.8 (Berkeley) 1/31/95"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "pathnames.h" + +struct _head head; + +/* + * config -- + * + * Read the configuration file and build a doubly linked + * list that looks like: + * + * tag1 <-> record <-> record <-> record + * | + * tag2 <-> record <-> record <-> record + */ +void +config(fname) + char *fname; +{ + TAG *tp; + ENTRY *ep; + FILE *cfp; + size_t len; + int lcnt; + char *p, *t; + + if (fname == NULL) + fname = _PATH_MANCONF; + if ((cfp = fopen(fname, "r")) == NULL) + err(1, "%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 (; *p != '\0' && isspace(*p); ++p); + /* Skip empty/comment lines. */ + if (*p == '\0' || *p == '#') + continue; + /* Find first token. */ + for (t = p; *t && !isspace(*t); ++t); + if (*t == '\0') /* Need more than one token.*/ + continue; + *t = '\0'; + + for (tp = head.tqh_first; /* Find any matching tag. */ + tp != NULL && strcmp(p, tp->s); tp = tp->q.tqe_next); + + if (tp == NULL) /* Create a new tag. */ + tp = addlist(p); + + /* + * Attach new records. The keyword _build takes the rest of + * the line as a single entity, everything else is white + * space separated. 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. + */ + if (!strcmp(p, "_build")) { + while (*++t && isspace(*t)); + if ((ep = malloc(sizeof(ENTRY))) == NULL || + (ep->s = strdup(t)) == NULL) + err(1, NULL); + TAILQ_INSERT_TAIL(&tp->list, ep, q); + } else for (++t; (p = strtok(t, " \t\n")) != NULL; t = NULL) { + if ((ep = malloc(sizeof(ENTRY))) == NULL || + (ep->s = strdup(p)) == NULL) + err(1, NULL); + TAILQ_INSERT_TAIL(&tp->list, ep, q); + } + } + + fclose(cfp); +} + +/* + * addlist -- + * Add a tag to the list. + */ +TAG * +addlist(name) + char *name; +{ + TAG *tp; + + if ((tp = calloc(1, sizeof(TAG))) == NULL || + (tp->s = strdup(name)) == NULL) + err(1, NULL); + TAILQ_INIT(&tp->list); + TAILQ_INSERT_TAIL(&head, tp, q); + return (tp); +} + +/* + * getlist -- + * Return the linked list of entries for a tag if it exists. + */ +TAG * +getlist(name) + char *name; +{ + TAG *tp; + + for (tp = head.tqh_first; tp != NULL; tp = tp->q.tqe_next) + if (!strcmp(name, tp->s)) + return (tp); + return (NULL); +} + +void +debug(l) + char *l; +{ + TAG *tp; + ENTRY *ep; + + (void)printf("%s ===============\n", l); + for (tp = head.tqh_first; tp != NULL; tp = tp->q.tqe_next) { + printf("%s\n", tp->s); + for (ep = tp->list.tqh_first; ep != NULL; ep = ep->q.tqe_next) + printf("\t%s\n", ep->s); + } +} diff --git a/usr.bin/man/config.h b/usr.bin/man/config.h new file mode 100644 index 0000000..52ea969 --- /dev/null +++ b/usr.bin/man/config.h @@ -0,0 +1,57 @@ +/*- + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 + */ + +typedef struct _tag { + TAILQ_ENTRY(_tag) q; /* Queue of tags. */ + + TAILQ_HEAD(tqh, _entry) list; /* Queue of entries. */ + char *s; /* Associated string. */ + size_t len; /* Length of 's'. */ +} TAG; +typedef struct _entry { + TAILQ_ENTRY(_entry) q; /* Queue of entries. */ + + char *s; /* Associated string. */ + size_t len; /* Length of 's'. */ +} ENTRY; + +TAILQ_HEAD(_head, _tag); +extern struct _head head; + +TAG *addlist __P((char *)); +void config __P((char *)); +void debug __P((char *)); +TAG *getlist __P((char *)); diff --git a/usr.bin/man/man.1 b/usr.bin/man/man.1 new file mode 100644 index 0000000..081e204 --- /dev/null +++ b/usr.bin/man/man.1 @@ -0,0 +1,188 @@ +.\" 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. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. 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 January 2, 1994 +.Dt MAN 1 +.Os BSD 4 +.Sh NAME +.Nm man +.Nd display the on-line manual pages +.Sh SYNOPSIS +.Nm man +.Op Fl achw +.Op Fl C Ar file +.Op Fl M Ar path +.Op Fl m Ar path +.Op Ar section +.Ar name Ar ... +.Sh DESCRIPTION +The +.Nm man +utility +displays the +.Bx +manual pages entitled +.Ar name . +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl a +Display all of the manual pages for a specified +.Ar section +and +.Ar name +combination. +(Normally, only the first manual 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 manual environment. +See +.Xr man.conf 5 +for a description of the contents of this file. +.It Fl c +Copy the manual 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 manual pages. +.It Fl M +Override the list of standard directories which +.Nm man +searches for manual pages. +The supplied +.Ar path +must be a colon (``:'') 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 ``_subdir'' line in the +.Nm man +configuration file. +.It Fl m +Augment the list of standard directories which +.Nm man +searches for manual pages. +The supplied +.Ar path +must be a colon (``:'') 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 ``_subdir'' line in the +.Nm man +configuration file. +.It Fl w +List the pathnames of the manual pages which +.Nm man +would display for the specified +.Ar section +and +.Ar name +combination. +.El +.Pp +The optional +.Ar section +argument restricts the directories that +.Nm man +will search. +The +.Nm man +configuration file (see +.Xr man.conf 5 ) +specifies the possible +.Ar section +values that are currently available. +If only a single argument is specified or if the first argument is +not a valid section, +.Nm man +assumes that the argument is the name of a manual page to be displayed. +.Sh ENVIRONMENT +.Bl -tag -width MANPATHX +.It Ev MACHINE +As some manual pages are intended only for specific architectures, +.Nm man +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 man +may be overridden by specifying a path in the +.Ev MANPATH +environment +variable. +The format of the path is a colon (``:'') separated list of directories. +The subdirectories to be searched as well as their search order +is specified by the ``_subdir'' line in the +.Nm man +configuration file. +.It Ev PAGER +Any value of the environment variable +.Ev PAGER +will be used instead of the standard pagination program, +.Xr more 1 . +.El +.Sh FILES +.Bl -tag -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 +.Sh BUGS +The on-line manual pages are, by necessity, forgiving toward stupid +display devices, causing a few manual pages to not as nicely formatted +as their typeset counterparts. +.Sh HISTORY +A +.Nm +command appeared in +.At v6 . diff --git a/usr.bin/man/man.c b/usr.bin/man/man.c new file mode 100644 index 0000000..4f3b9e5 --- /dev/null +++ b/usr.bin/man/man.c @@ -0,0 +1,712 @@ +/* + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1987, 1993, 1994, 1995\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)man.c 8.17 (Berkeley) 1/31/95"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "pathnames.h" + +int f_all, f_where; + +static void build_page __P((char *, char **)); +static void cat __P((char *)); +static char *check_pager __P((char *)); +static int cleanup __P((void)); +static void how __P((char *)); +static void jump __P((char **, char *, char *)); +static int manual __P((char *, TAG *, glob_t *)); +static void onsig __P((int)); +static void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + extern int optind; + TAG *defp, *defnewp, *section, *sectnewp, *subp; + ENTRY *e_defp, *e_sectp, *e_subp, *ep; + glob_t pg; + size_t len; + int ch, f_cat, f_how, found; + char **ap, *cmd, *machine, *p, *p_add, *p_path, *pager, *slashp; + char *conffile, buf[MAXPATHLEN * 2]; + + f_cat = f_how = 0; + conffile = p_add = p_path = NULL; + while ((ch = getopt(argc, argv, "-aC:cfhkM:m:P:w")) != EOF) + switch (ch) { + case 'a': + f_all = 1; + break; + case 'C': + conffile = optarg; + break; + case 'c': + case '-': /* Deprecated. */ + f_cat = 1; + break; + case 'h': + f_how = 1; + break; + case 'm': + p_add = optarg; + break; + case 'M': + case 'P': /* Backward compatibility. */ + p_path = 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 'w': + f_all = f_where = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (!*argv) + usage(); + + if (!f_cat && !f_how && !f_where) + if (!isatty(1)) + f_cat = 1; + else if ((pager = getenv("PAGER")) != NULL) + pager = check_pager(pager); + else + pager = _PATH_PAGER; + + /* Read the configuration file. */ + config(conffile); + + /* Get the machine type. */ + if ((machine = getenv("MACHINE")) == NULL) + machine = MACHINE; + + /* If there's no _default list, create an empty one. */ + if ((defp = getlist("_default")) == NULL) + defp = addlist("_default"); + + /* + * 1: If the user specified a MANPATH variable, or set the -M + * option, we replace the _default list with the user's list, + * appending the entries in the _subdir list and the machine. + */ + if (p_path == NULL) + p_path = getenv("MANPATH"); + if (p_path != NULL) { + while ((e_defp = defp->list.tqh_first) != NULL) { + free(e_defp->s); + TAILQ_REMOVE(&defp->list, e_defp, q); + } + for (p = strtok(p_path, ":"); + p != NULL; p = strtok(NULL, ":")) { + slashp = p[strlen(p) - 1] == '/' ? "" : "/"; + e_subp = (subp = getlist("_subdir")) == NULL ? + NULL : subp->list.tqh_first; + for (; e_subp != NULL; e_subp = e_subp->q.tqe_next) { + (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,}", + p, slashp, e_subp->s, machine); + if ((ep = malloc(sizeof(ENTRY))) == NULL || + (ep->s = strdup(buf)) == NULL) + err(1, NULL); + TAILQ_INSERT_TAIL(&defp->list, ep, q); + } + } + } + + /* + * 2: If the user did not specify MANPATH, -M or a section, rewrite + * the _default list to include the _subdir list and the machine. + */ + if (argv[1] == NULL) + section = NULL; + else if ((section = getlist(*argv)) != NULL) + ++argv; + if (p_path == NULL && section == NULL) { + defnewp = addlist("_default_new"); + e_defp = + defp->list.tqh_first == NULL ? NULL : defp->list.tqh_first; + for (; e_defp != NULL; e_defp = e_defp->q.tqe_next) { + slashp = + e_defp->s[strlen(e_defp->s) - 1] == '/' ? "" : "/"; + e_subp = (subp = getlist("_subdir")) == NULL ? + NULL : subp->list.tqh_first; + for (; e_subp != NULL; e_subp = e_subp->q.tqe_next) { + (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,}", + e_defp->s, slashp, e_subp->s, machine); + if ((ep = malloc(sizeof(ENTRY))) == NULL || + (ep->s = strdup(buf)) == NULL) + err(1, NULL); + TAILQ_INSERT_TAIL(&defnewp->list, ep, q); + } + } + defp = getlist("_default"); + while ((e_defp = defp->list.tqh_first) != NULL) { + free(e_defp->s); + TAILQ_REMOVE(&defp->list, e_defp, q); + } + free(defp->s); + TAILQ_REMOVE(&head, defp, q); + defnewp = getlist("_default_new"); + free(defnewp->s); + defnewp->s = "_default"; + defp = defnewp; + } + + /* + * 3: If the user set the -m option, insert the user's list before + * whatever list we have, again appending the _subdir list and + * the machine. + */ + if (p_add != NULL) + for (p = strtok(p_add, ":"); p != NULL; p = strtok(NULL, ":")) { + slashp = p[strlen(p) - 1] == '/' ? "" : "/"; + e_subp = (subp = getlist("_subdir")) == NULL ? + NULL : subp->list.tqh_first; + for (; e_subp != NULL; e_subp = e_subp->q.tqe_next) { + (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,}", + p, slashp, e_subp->s, machine); + if ((ep = malloc(sizeof(ENTRY))) == NULL || + (ep->s = strdup(buf)) == NULL) + err(1, NULL); + TAILQ_INSERT_HEAD(&defp->list, ep, q); + } + } + + /* + * 4: If none of MANPATH, -M, or -m were specified, and a section was, + * rewrite the section's paths (if they have a trailing slash) to + * append the _subdir list and the machine. This then becomes the + * _default list. + */ + if (p_path == NULL && p_add == NULL && section != NULL) { + sectnewp = addlist("_section_new"); + for (e_sectp = section->list.tqh_first; + e_sectp != NULL; e_sectp = e_sectp->q.tqe_next) { + if (e_sectp->s[strlen(e_sectp->s) - 1] != '/') { + (void)snprintf(buf, sizeof(buf), + "%s{/%s,}", e_sectp->s, machine); + if ((ep = malloc(sizeof(ENTRY))) == NULL || + (ep->s = strdup(buf)) == NULL) + err(1, NULL); + TAILQ_INSERT_TAIL(§newp->list, ep, q); + continue; + } + e_subp = (subp = getlist("_subdir")) == NULL ? + NULL : subp->list.tqh_first; + for (; e_subp != NULL; e_subp = e_subp->q.tqe_next) { + (void)snprintf(buf, sizeof(buf), "%s%s{/%s,}", + e_sectp->s, e_subp->s, machine); + if ((ep = malloc(sizeof(ENTRY))) == NULL || + (ep->s = strdup(buf)) == NULL) + err(1, NULL); + TAILQ_INSERT_TAIL(§newp->list, ep, q); + } + } + sectnewp->s = section->s; + defp = sectnewp; + TAILQ_REMOVE(&head, section, q); + } + + /* + * 5: Search for the files. Set up an interrupt handler, so the + * temporary files go away. + */ + (void)signal(SIGINT, onsig); + (void)signal(SIGHUP, onsig); + + memset(&pg, 0, sizeof(pg)); + for (found = 0; *argv; ++argv) + if (manual(*argv, defp, &pg)) + found = 1; + + /* 6: If nothing found, we're done. */ + if (!found) { + (void)cleanup(); + exit (1); + } + + /* 7: If it's simple, display it fast. */ + if (f_cat) { + for (ap = pg.gl_pathv; *ap != NULL; ++ap) { + if (**ap == '\0') + continue; + cat(*ap); + } + exit (cleanup()); + } + if (f_how) { + for (ap = pg.gl_pathv; *ap != NULL; ++ap) { + if (**ap == '\0') + continue; + how(*ap); + } + exit(cleanup()); + } + if (f_where) { + for (ap = pg.gl_pathv; *ap != NULL; ++ap) { + if (**ap == '\0') + continue; + (void)printf("%s\n", *ap); + } + exit(cleanup()); + } + + /* + * 8: We display things in a single command; build a list of things + * to display. + */ + for (ap = pg.gl_pathv, len = strlen(pager) + 1; *ap != NULL; ++ap) { + if (**ap == '\0') + continue; + len += strlen(*ap) + 1; + } + if ((cmd = malloc(len)) == NULL) { + warn(NULL); + (void)cleanup(); + exit(1); + } + p = cmd; + len = strlen(pager); + memmove(p, pager, len); + p += len; + *p++ = ' '; + for (ap = pg.gl_pathv; *ap != NULL; ++ap) { + if (**ap == '\0') + continue; + len = strlen(*ap); + memmove(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(page, tag, pg) + char *page; + TAG *tag; + glob_t *pg; +{ + ENTRY *ep, *e_sufp, *e_tag; + TAG *missp, *sufp; + int anyfound, cnt, found; + char *p, buf[128]; + + anyfound = 0; + buf[0] = '*'; + + /* For each element in the list... */ + e_tag = tag == NULL ? NULL : tag->list.tqh_first; + for (; e_tag != NULL; e_tag = e_tag->q.tqe_next) { + (void)snprintf(buf, sizeof(buf), "%s/%s.*", e_tag->s, page); + if (glob(buf, + GLOB_APPEND | GLOB_BRACE | GLOB_NOSORT | GLOB_QUOTE, + NULL, pg)) { + warn("globbing"); + (void)cleanup(); + exit(1); + } + if (pg->gl_matchc == 0) + continue; + + /* Find out if it's really a man page. */ + for (cnt = pg->gl_pathc - pg->gl_matchc; + cnt < pg->gl_pathc; ++cnt) { + + /* + * 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", page); + if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) + goto next; + + e_sufp = (sufp = getlist("_suffix")) == NULL ? + NULL : sufp->list.tqh_first; + for (found = 0; + e_sufp != NULL; e_sufp = e_sufp->q.tqe_next) { + (void)snprintf(buf, + sizeof(buf), "*/%s%s", page, e_sufp->s); + if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) { + found = 1; + break; + } + } + if (found) + goto next; + + /* Try the _build key words next. */ + e_sufp = (sufp = getlist("_build")) == NULL ? + NULL : sufp->list.tqh_first; + for (found = 0; + e_sufp != NULL; e_sufp = e_sufp->q.tqe_next) { + for (p = e_sufp->s; + *p != '\0' && !isspace(*p); ++p); + if (*p == '\0') + continue; + *p = '\0'; + (void)snprintf(buf, + sizeof(buf), "*/%s%s", page, e_sufp->s); + if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) { + if (!f_where) + build_page(p + 1, + &pg->gl_pathv[cnt]); + *p = ' '; + found = 1; + break; + } + *p = ' '; + } + if (found) { +next: anyfound = 1; + if (!f_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 && !f_all) + break; + } + + /* If not found, enter onto the missing list. */ + if (!anyfound) { + if ((missp = getlist("_missing")) == NULL) + missp = addlist("_missing"); + if ((ep = malloc(sizeof(ENTRY))) == NULL || + (ep->s = strdup(page)) == NULL) { + warn(NULL); + (void)cleanup(); + exit(1); + } + TAILQ_INSERT_TAIL(&missp->list, ep, q); + } + return (anyfound); +} + +/* + * build_page -- + * Build a man page for display. + */ +static void +build_page(fmt, pathp) + char *fmt, **pathp; +{ + static int warned; + ENTRY *ep; + TAG *intmpp; + int fd; + char buf[MAXPATHLEN], cmd[MAXPATHLEN], tpath[sizeof(_PATH_TMP)]; + + /* Let the user know this may take awhile. */ + if (!warned) { + warned = 1; + warnx("Formatting manual page..."); + } + + /* Add a remove-when-done list. */ + if ((intmpp = getlist("_intmp")) == NULL) + intmpp = addlist("_intmp"); + + /* Move to the printf(3) format string. */ + for (; *fmt && isspace(*fmt); ++fmt); + + /* + * Get a temporary file and build a version of the file + * to display. Replace the old file name with the new one. + */ + (void)strcpy(tpath, _PATH_TMP); + 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, *pathp); + (void)system(cmd); + (void)close(fd); + if ((*pathp = strdup(tpath)) == NULL) { + warn(NULL); + (void)cleanup(); + exit(1); + } + + /* Link the built file into the remove-when-done list. */ + if ((ep = malloc(sizeof(ENTRY))) == NULL) { + warn(NULL); + (void)cleanup(); + exit(1); + } + ep->s = *pathp; + TAILQ_INSERT_TAIL(&intmpp->list, ep, q); +} + +/* + * how -- + * display how information + */ +static void +how(fname) + 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)) + return; + if (!print) + continue; + if (*buf == '\n') + ++lcnt; + else { + for(; lcnt; --lcnt) + (void)putchar('\n'); + for (p = buf; isspace(*p); ++p); + (void)fputs(p, stdout); + } + } + (void)fclose(fp); +} + +/* + * cat -- + * cat out the file + */ +static void +cat(fname) + 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 char * +check_pager(name) + char *name; +{ + char *p, *save; + + /* + * if the user uses "more", we make it "more -s"; watch out for + * PAGER = "mypager /usr/ucb/more" + */ + for (p = name; *p && !isspace(*p); ++p); + for (; p > name && *p != '/'; --p); + if (p != name) + ++p; + + /* make sure it's "more", not "morex" */ + if (!strncmp(p, "more", 4) && (!p[4] || isspace(p[4]))){ + save = name; + /* allocate space to add the "-s" */ + if (!(name = + malloc((u_int)(strlen(save) + sizeof("-s") + 1)))) + err(1, NULL); + (void)sprintf(name, "%s %s", save, "-s"); + } + return(name); +} + +/* + * jump -- + * strip out flag argument and jump + */ +static void +jump(argv, flag, name) + char **argv, *flag, *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(signo) + int signo; +{ + (void)cleanup(); + + (void)signal(signo, SIG_DFL); + (void)kill(getpid(), signo); + + /* NOTREACHED */ + exit (1); +} + +/* + * cleanup -- + * Clean up temporary files, show any error messages. + */ +static int +cleanup() +{ + TAG *intmpp, *missp; + ENTRY *ep; + int rval; + + rval = 0; + ep = (missp = getlist("_missing")) == NULL ? + NULL : missp->list.tqh_first; + if (ep != NULL) + for (; ep != NULL; ep = ep->q.tqe_next) { + warnx("no entry for %s in the manual.", ep->s); + rval = 1; + } + + ep = (intmpp = getlist("_intmp")) == NULL ? + NULL : intmpp->list.tqh_first; + for (; ep != NULL; ep = ep->q.tqe_next) + (void)unlink(ep->s); + return (rval); +} + +/* + * usage -- + * print usage message and die + */ +static void +usage() +{ + (void)fprintf(stderr, + "usage: man [-achw] [-C file] [-M path] [-m path] [section] title ...\n"); + exit(1); +} diff --git a/usr.bin/man/man.conf b/usr.bin/man/man.conf new file mode 100644 index 0000000..9faad52 --- /dev/null +++ b/usr.bin/man/man.conf @@ -0,0 +1,46 @@ +# Sheer, raging paranoia... +_version BSD.2 + +# The whatis/apropos database. +_whatdb /usr/share/man/whatis.db + +# Subdirectories for paths ending in '/', IN SEARCH ORDER. +_subdir cat{1,8,6,2,3,4,5,7,3f} + +# Files typed by suffix and their commands. +# Note the order, .Z must come after .[1-9].Z, or it will match first. +_suffix .0 +_build .[1-9] /usr/bin/nroff -man %s +_build .[1-9].Z /usr/bin/zcat %s | /usr/bin/nroff -man +_build .Z /usr/bin/zcat %s +_build .0.Z /usr/bin/zcat %s +_build .gz /usr/contrib/bin/gunzip %s +_build .z /usr/contrib/bin/gunzip %s +_build .nr /usr/bin/nroff -man %s + +# Sections and their directories. +# All paths ending in '/' are the equivalent of entries specifying that +# directory with all of the subdirectories listed for the keyword _subdir. + +# default +_default /usr/{share,X11,contrib,local}/{man,man/old}/ + +# Other sections that represent complete man subdirectories. +X11 /usr/X11R4/man/ +X11R4 /usr/X11R4/man/ +contrib /usr/contrib/man/ +local /usr/local/man/ +new /usr/contrib/man/ +old /usr/share/man/old/ + +# Specific section/directory combinations. +1 /usr/{share,X11R4,contrib,local}/{man/,man/old/}cat1 +2 /usr/{share,X11R4,contrib,local}/{man/,man/old/}cat2 +3 /usr/{share,X11R4,contrib,local}/{man/,man/old/}cat3 +3F /usr/share/man/cat3f +3f /usr/share/man/cat3f +4 /usr/{share,X11R4,contrib,local}/{man/,man/old/}cat4 +5 /usr/{share,X11R4,contrib,local}/{man/,man/old/}cat5 +6 /usr/{share,X11R4,contrib,local}/{man/,man/old/}cat6 +7 /usr/{share,X11R4,contrib,local}/{man/,man/old/}cat7 +8 /usr/{share,X11R4,contrib,local}/{man/,man/old/}cat8 diff --git a/usr.bin/man/man.conf.5 b/usr.bin/man/man.conf.5 new file mode 100644 index 0000000..353a302 --- /dev/null +++ b/usr.bin/man/man.conf.5 @@ -0,0 +1,195 @@ +.\" 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. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. 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 January 2, 1994 +.Dt MAN.CONF 5 +.Os +.Sh NAME +.Nm man.conf +.Nd configuration file for +.Xr man 1 +.Sh DESCRIPTION +The +.Xr man 1 , +.Xr apropos 1 , +and +.Xr whatis 1 +commands +search for manual pages or their database files as specified by the +.Nm man.conf +file. +Manual pages are normally expected to be preformatted (see +.Xr nroff 1 ) +and named with a trailing ``.0''. +.Pp +The +.Nm man.conf +file contains two types of lines. +.Pp +The first type of line is a ``section'' line, which contains a +section name followed by one or more directory paths. +The directory paths may contain the normal shell globbing characters, +including curly braces (``{}''); to escape a shell globbing character, +precede it with a backslash (``\e''). +Lines in this format specify that manual pages for the section +may be found in the following directories. +.Pp +Directories named with a trailing slash character (``/'') are expected +to contain subdirectories of manual pages, (see the keyword ``_subdir'' +below) instead of manual pages. +These subdirectories are searched instead of the directory. +.Pp +Before searching any directory for a manual page, the +.Xr man 1 +command always searches the subdirectory with the same name +as the current machine type, if it exists. +No specification of these subdirectories is necessary in the +.Nm man.conf +file. +.Pp +Section names are unrestricted except for the reserved words specified +below; in general, you should avoid anything with a leading underscore +(``_'') to avoid future incompatibilities. +.Pp +The section named ``_default'' is the list of directories that will +be searched if no section is specified by the user. +.Pp +The second type of line is preceded with a ``keyword''. +The possible keywords and their meanings are as follows: +.Pp +.Bl -tag -width "_version" +.It _build +Man file names, regardless of their format, are expected to end in +a ``.*'' pattern, i.e. a ``.'' followed by some suffix. +The first field of a _build line lists a suffix which indicates +files which need to be reformated or manipulated in some way before +being displayed to the user. +The suffix may contain the normal shell globbing characters (NOT +including curly braces (``{}'')). +The rest of the line must be a shell command line, the standard +output of which is the manual page in a format which may be directly +displayed to the user. +Any occurrences of the string ``%s'' in the shell command line will +be replaced by the name of the file which is being reformatted. +.It _subdir +The list (in search order) of subdirectories which will be searched in +any directory named with a trailing slash (``/'') character. +This list is also used when a path is specified to the +.Xr man 1 +utility by the user, using the +.Ev MANPATH +environment variable or the +.Fl M +and +.Fl m +options. +.It _suffix +Man file names, regardless of their format are expected to end in +a ``.*'' pattern, i.e. a ``.'' followed by some suffix. +Each field of a _suffix line is a suffix which indicates +files which do not need to be reformatted or manipulated +in any way, but which may be directly displayed to the user. +Each suffix may contain the normal shell globbing characters (NOT +including curly braces (``{}'')). +.It _version +The version of the configuration file. +.It _whatdb +The full pathname (not just a directory path) for a database to be used +by the +.Xr apropos 1 +and +.Xr whatis 1 +commands. +.El +.Pp +Multiple specifications for all types of lines are cumulative and the +entries are used in the order listed in the file; multiple entries may +be listed per line, as well. +.Pp +Empty lines or lines whose first non-whitespace character is a hash +mark (``#'') are ignored. +.Sh EXAMPLES +Given the following +.Nm man.conf +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 +``mktemp.'' and ``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 ``vax'', the subdirectory ``vax'' in each +directory would be searched as well, before the directory was +searched. +.Pp +If ``mktemp.tbl'' was found first, the command +.Dq Li tbl | 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 FILES +.Bl -tag -width /etc/man.conf -compact +.It Pa /etc/man.conf +Standard manual directory search path. +.El +.Sh SEE ALSO +.Xr apropos 1 , +.Xr machine 1 , +.Xr man 1 , +.Xr whatis 1 , +.Xr whereis 1 , +.Xr fnmatch 3 , +.Xr glob 3 diff --git a/usr.bin/man/pathnames.h b/usr.bin/man/pathnames.h new file mode 100644 index 0000000..17284fe --- /dev/null +++ b/usr.bin/man/pathnames.h @@ -0,0 +1,39 @@ +/* + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 + */ + +#define _PATH_MANCONF "/etc/man.conf" +#define _PATH_PAGER "/usr/bin/more -s" +#define _PATH_TMP "/tmp/man.XXXXXX" +#define _PATH_WHATIS "whatis.db" -- cgit v1.1