Subject: bin/1904: changes to man programs
To: None <gnats-bugs@gnats.netbsd.org>
From: None <kashmir@umiacs.UMD.EDU>
List: netbsd-bugs
Date: 01/06/1996 15:31:23
>Number:         1904
>Category:       bin
>Synopsis:       changes to man programs
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    bin-bug-people (Utility Bug People)
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Sat Jan  6 15:50:01 1996
>Last-Modified:
>Originator:     Mike Grupenhoff
>Organization:
nyuk nyuk
>Release:        -current of 96-01-06
>Environment:
System: NetBSD snarf.umiacs.umd.edu 1.1A NetBSD 1.1A (SNARF) #7: Fri Dec 29 23:04:19 EST 1995 kashmir@snarf.umiacs.umd.edu:/usr/src/sys/arch/i386/compile/SNARF i386
>Description:

- apropos and whatis can't handle multiple whatis db's when they
  are specified in shell glob syntax.  For example, a line like:

  _whatdb         /usr/{share,local,afs,X11}/man/whatis.db

  in /etc/man.conf will not be handled properly.

- catman doesn't work with /etc/man.conf and linked manpages

- bsd.man.mk doesn't use the build rules listed in /etc/man.conf
  catman should be used in bsd.man.mk instead of nroff
  
>How-To-Repeat:
look at the source
>Fix:
4 patches are attached.

The first patch adds support to apropos for globbing _whatdb.

-----cut-here-----
Index: apropos.c
===================================================================
RCS file: /snarf/netbsd/master/src/usr.bin/apropos/apropos.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 apropos.c
--- apropos.c	1995/11/16 02:27:00	1.1.1.1
+++ apropos.c	1996/01/02 05:28:43
@@ -52,6 +52,7 @@
 
 #include <ctype.h>
 #include <err.h>
+#include <glob.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -77,6 +78,7 @@
 	TAG *tp;
 	int ch, rv;
 	char *conffile, **p, *p_augment, *p_path;
+	glob_t pg;
 
 	conffile = NULL;
 	p_augment = p_path = NULL;
@@ -117,8 +119,15 @@
 		config(conffile);
 		ep = (tp = getlist("_whatdb")) == NULL ?
 		    NULL : tp->list.tqh_first;
-		for (; ep != NULL; ep = ep->q.tqe_next)
-			apropos(argv, ep->s, 0);
+		for (; ep != NULL; ep = ep->q.tqe_next) {
+			if (glob(ep->s, GLOB_BRACE|GLOB_QUOTE, NULL, &pg) != 0)
+				err(1, "glob");
+			if (pg.gl_matchc == 0)
+				continue;
+			for (ch = 0; pg.gl_pathv[ch] != NULL; ch++)
+				apropos(argv, pg.gl_pathv[ch], 0);
+			globfree(&pg);
+		}
 	}
 
 	if (!foundman)
-----cut-here-----



The second patch adds glob support to whatis.

-----cut-here-----
Index: whatis.c
===================================================================
RCS file: /snarf/netbsd/master/src/usr.bin/whatis/whatis.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 whatis.c
--- whatis.c	1995/11/16 02:28:52	1.1.1.1
+++ whatis.c	1996/01/02 05:27:59
@@ -1,3 +1,5 @@
+/*	$NetBSD$	*/
+
 /*
  * Copyright (c) 1987, 1993
  *	The Regents of the University of California.  All rights reserved.
@@ -38,7 +40,11 @@
 #endif /* not lint */
 
 #ifndef lint
+#if 0
 static char sccsid[] = "@(#)whatis.c	8.5 (Berkeley) 11/26/93";
+#else
+static char rcsid[] = "$NetBSD$";
+#endif
 #endif /* not lint */
 
 #include <sys/param.h>
@@ -46,6 +52,7 @@
 
 #include <ctype.h>
 #include <err.h>
+#include <glob.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -71,6 +78,7 @@
 	TAG *tp;
 	int ch, rv;
 	char *beg, *conffile, **p, *p_augment, *p_path;
+	glob_t pg;
 
 	conffile = NULL;
 	p_augment = p_path = NULL;
@@ -112,8 +120,15 @@
 		config(conffile);
 		ep = (tp = getlist("_whatdb")) == NULL ?
 		   NULL : tp->list.tqh_first;
-		for (; ep != NULL; ep = ep->q.tqe_next)
-			whatis(argv, ep->s, 0);
+		for (; ep != NULL; ep = ep->q.tqe_next) {
+			if (glob(ep->s, GLOB_BRACE|GLOB_QUOTE, NULL, &pg) != 0)
+				err(1, "glob");
+			if (pg.gl_matchc == 0)
+				continue;
+			for (ch = 0; pg.gl_pathv[ch] != NULL; ch++)
+				whatis(argv, pg.gl_pathv[ch], 0);
+			globfree(&pg);
+		}
 	}
 
 	if (!foundman) {
-----cut-here-----



The third patch is for catman.  It does the following:

	-adds $NetBSD$ rcsids

	-adds support for parsing man.conf

	-adds support for handling hard and symlinked manpages (it hard
	 links the catpage together in both cases)

	-adds a second mode of operation:

		catman <manpage>

	 will format the manpage using the build rules in /etc/man.conf,
	 and dump it to stdout.  This can be used in bsd.man.mk to
	 format manpages instead of assuming everything works with
	 nroff -man.

After applying this patch, TODO and pathnames.h should be removed from the
catman directory.  Note that this patch also modifies pathnames.h in the man
src directory (it adds _PATH_MAKEWHATIS).


-----cut-here-----
Index: Makefile
===================================================================
RCS file: /snarf/netbsd/master/src/usr.sbin/catman/Makefile,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 Makefile
--- Makefile	1995/11/16 02:29:24	1.1.1.1
+++ Makefile	1996/01/02 02:45:05
@@ -1,7 +1,8 @@
-#	$Id: Makefile,v 1.1.1.1 1995/11/16 02:29:24 kashmir Exp $
+#	$NetBSD$
 
-BINDIR=		/usr/sbin
 PROG=		catman
+SRCS=		catman.c config.c
 MAN=		catman.8
+.PATH:		${.CURDIR}/../../usr.bin/man
 
 .include <bsd.prog.mk>
Index: catman.8
===================================================================
RCS file: /snarf/netbsd/master/src/usr.sbin/catman/catman.8,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 catman.8
--- catman.8	1995/11/16 02:29:24	1.1.1.1
+++ catman.8	1996/01/02 02:29:22
@@ -1,3 +1,4 @@
+.\"	$NetBSD$
 .\"
 .\" Copyright (c) 1993 Winning Strategies, Inc.
 .\" All rights reserved.
@@ -27,9 +28,7 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.\"	$Id: catman.8,v 1.1.1.1 1995/11/16 02:29:24 kashmir Exp $
-.\"
-.Dd July 30, 1993
+.Dd January 1, 1995
 .Dt CATMAN 8
 .Os
 .Sh NAME
@@ -38,13 +37,20 @@
 .Sh SYNOPSIS
 .Nm catman
 .Op Fl knpsw
+.Op Fl C Ar file
 .Op Fl M Ar directory
 .Op Ar sections
+.Nm catman
+.Op Fl C Ar file
+.Ar manpage
 .Sh DESCRIPTION
 .Nm Catman 
 creates formatted versions of the on-line manual pages from their 
 .Xr nroff 1
 source.
+.Pp
+The first form of the command operates on the man directories specified in
+/etc/man.conf.
 Manual pages whose formatted versions are missing or out of date are 
 regenerated.
 If manual pages are regenerated, 
@@ -66,6 +72,9 @@
 .Nm catman
 will try to operate on all of the known manual sections.
 .Pp
+The second form of the command takes a filename on the command line and
+generates formatted output to stdout.
+.Pp
 The options are as follows:
 .Bl -tag -width indent
 .It Fl k
@@ -86,15 +95,32 @@
 Only create the 
 .Nm whatis 
 database.
+.It Fl C Ar file
+Use the specified
+.Ar file
+instead of the default configuration file.
 .It Fl M Ar directory
 Update manual pages in 
 .Ar directory.
 .El
+.Sh ENVIRONMENT
+.Bl -tag -width MANPATH
+.It Ev MANPATH
+The standard search path may be overridden by specifying a path in the
+.Ev MANPATH
+environment variable.
+.El
+.Sh FILES
+.Bl -tag
+.It Pa /etc/man.conf
+default man configuration file.
+.El
+.Bl -tag
+.It Pa whatis.db
+name of the whatis database.
+.El
 .Sh SEE ALSO
 .Xr apropos 1 ,
 .Xr man 1 ,
 .Xr whatis 1
-.Sh BUGS
-Currently knows nothing about 
-.Pa /etc/man.conf 
-and machine specific man pages.
+.Xr man.conf 5
Index: catman.c
===================================================================
RCS file: /snarf/netbsd/master/src/usr.sbin/catman/catman.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 catman.c
--- catman.c	1995/11/16 02:29:24	1.1.1.1
+++ catman.c	1996/01/06 19:49:46
@@ -1,4 +1,7 @@
+/*	$NetBSD$	*/
+
 /*
+ * Copyright (c) 1996 Michael E. Grupenhoff.  All rights reserved.
  * Copyright (c) 1993 Winning Strategies, Inc.
  * All rights reserved.
  *
@@ -29,23 +32,28 @@
  */
 
 #ifndef lint
-static char rcsid[] = "$Id: catman.c,v 1.1.1.1 1995/11/16 02:29:24 kashmir Exp $";
+static char rcsid[] = "$NetBSD$";
 #endif /* not lint */
 
 #include <sys/types.h>
+#include <sys/queue.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
+
+#include <ctype.h>
 #include <dirent.h>
 #include <err.h>
 #include <errno.h>
+#include <fnmatch.h>
+#include <glob.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
-#include <paths.h>
 
-#include "pathnames.h"
+#include "../../usr.bin/man/config.h"
+#include "../../usr.bin/man/pathnames.h"
 
 int f_nowhatis;
 int f_noaction;
@@ -53,23 +61,29 @@
 int f_ignerr;
 int f_noprint;
 
-int dowhatis;
-
-char *mp = _PATH_MAN;
-char *sp = _MAN_SECTIONS;
-
-void usage __P((void));
-void catman __P((const char *, char *));
-void makewhatis __P((const char *));
+int catman __P((const char *));
+int dirformat __P((char *, char *, char *));
 void dosystem __P((const char *));
+void lncatpages __P((char *, char *, char *, char *, char *));
+void makedefault __P((char *));
+void makesubdir __P((const char *));
+void makewhatis __P((const char *));
+void mformat __P((char *, char *));
+void usage __P((void));
 
 int
 main(argc, argv)
 	int argc;
 	char **argv;
 {
-	int c;
+	int c, needwhatis;
+	char *conffile, *mp;
+	glob_t pg;
+	ENTRY *ep;
+	TAG *tp;
 
+	mp = NULL;
+	conffile = _PATH_MANCONF;
 	while ((c = getopt(argc, argv, "knpswM:")) != EOF) {
 		switch (c) {
 		case 'k':
@@ -87,6 +101,9 @@
 		case 'w':
 			f_noformat = 1;
 			break;
+		case 'C':
+			conffile = optarg;
+			break;
 		case 'M':
 			mp = optarg;
 			break;
@@ -105,37 +122,167 @@
 
 	if (argc > 1)
 		usage();
-	if (argc == 1)
-		sp = *argv;
 
-	if (f_noformat == 0 || f_nowhatis == 0)
-		catman(mp, sp);
-	if (f_nowhatis == 0 && dowhatis)
-		makewhatis(mp);
+	config(conffile);
+
+	/*
+	 * Remaining argument could either be an nroff source file, in
+	 * which case we format it to stdout, or a list of man sections.
+	 */
+	if (argc == 1)
+		if (access(*argv, F_OK) == 0) {
+			mformat(*argv, NULL);
+			exit(0);
+		} else
+			makesubdir(*argv);
+
+	if (mp != NULL || (mp = getenv("MANPATH")) != NULL)
+		makedefault(mp);
+
+	if ((tp = getlist("_default")) == NULL)
+		errx(1, "No manpath in %s.\n", _PATH_MANCONF);
+	for (ep = tp->list.tqh_first; ep != NULL; ep = ep->q.tqe_next) {
+		memset(&pg, 0, sizeof(pg));
+		if (glob(ep->s, GLOB_BRACE | GLOB_QUOTE, NULL, &pg) != 0)
+			err(1, "glob");
+		if (pg.gl_matchc == 0)
+			continue;
+		for (c = 0; pg.gl_pathv[c] != NULL; c++) {
+			if (f_noformat == 0 || f_nowhatis == 0)
+				needwhatis = catman(pg.gl_pathv[c]);
+			if (f_noformat || (needwhatis && f_nowhatis == 0))
+				makewhatis(pg.gl_pathv[c]);
+		}
+		globfree(&pg);
+	}
 
 	exit(0);
 }
 
-
-void
-catman(path, section)
+int
+catman(path)
 	const char *path;
-	char *section;
 {
+	int rval;
 	char mandir[PATH_MAX];
 	char catdir[PATH_MAX];
-	char manpage[PATH_MAX];
-	char catpage[PATH_MAX];
+	char *catsuffix, *slash;
+	TAG *subdirp, *sufp;
+	ENTRY *ep;
+
+	if (chdir(path) != 0) {
+		warn(path);
+		return (0);
+	}
+
+	if (path[strlen(path) - 1] == '/')
+		slash = "";
+	else
+		slash = "/";
+
+	if ((sufp = getlist("_suffix")) == NULL)
+		catsuffix = ".0";
+	else
+		catsuffix = sufp->list.tqh_first->s;
+
+	if ((subdirp = getlist("_subdir")) == NULL)
+		errx(1, "No man subdirectories to scan!\n");
+
+	rval = 0;
+	for (ep = subdirp->list.tqh_first; ep != NULL; ep = ep->q.tqe_next) {
+		if (strncmp(ep->s, "man", strlen("man")) != 0)
+			continue;
+		sprintf(mandir, "%s%s%s", path, slash, ep->s);
+		sprintf(catdir, "%s%scat%c", path, slash, ep->s[3]);
+		rval |= dirformat(mandir, catdir, catsuffix);
+	}
+
+	return (rval);
+}
+
+void
+makewhatis(path)
+	const char *path;
+{
 	char sysbuf[1024];
-	struct stat manstat;
-	struct stat catstat;
-	struct dirent *dp;
-	DIR *dirp;
-	char *s, *tmp;
-	int sectlen, error;
 
-	for (s = section; *s; s += sectlen) {
-#ifdef notdef
+	sprintf(sysbuf, "%s %s", _PATH_MAKEWHATIS, path);
+	if (f_noprint == 0)
+		printf("%s\n", sysbuf);
+	if (f_noaction == 0)
+		dosystem(sysbuf);
+}
+
+void
+dosystem(cmd)
+	const char *cmd;
+{
+	int status;
+
+	if ((status = system(cmd)) == 0)
+		return;
+
+	if (status == -1)
+		err(1, "cannot execute action");
+	if (WIFSIGNALED(status))
+		errx(1, "child was signaled to quit. aborting");
+	if (WIFSTOPPED(status))
+		errx(1, "child was stopped. aborting");
+	if (f_ignerr == 0)
+		errx(1, "*** Exited %d", status);
+	warnx("*** Exited %d (continuing)", status);
+}
+
+/*
+ * Replace the current _default entry
+ */
+void
+makedefault(mp)
+	char *mp;
+{
+	ENTRY *ep;
+	TAG *defp;
+	char *p;
+
+	if ((defp = getlist("_default")) == NULL)
+		defp = addlist("_default");
+	while ((ep = defp->list.tqh_first) != NULL) {
+		free(ep->s);
+		TAILQ_REMOVE(&defp->list, ep, q);
+		free(ep);
+	}
+
+	for (p = strsep(&mp, ":"); p != NULL; p = strsep(&mp, ":")) {
+		if (*p == '\0')
+			continue;
+		if ((ep = malloc(sizeof(ENTRY))) == NULL ||
+		    (ep->s = strdup(p)) == NULL)
+			errx(1, "Out of memory");
+		TAILQ_INSERT_TAIL(&defp->list, ep, q);
+	}
+}
+
+/*
+ * Replace the current _subdir entry
+ */
+void
+makesubdir(subsec)
+	const char *subsec;
+{
+	ENTRY *ep;
+	TAG *subdirp;
+	const char *s, *tmp;
+	int sectlen;
+
+	if ((subdirp = getlist("_subdir")) == NULL)
+		subdirp = addlist("_subdir");
+	while ((ep = subdirp->list.tqh_first) != NULL) {
+		free(ep->s);
+		TAILQ_REMOVE(&subdirp->list, ep, q);
+		free(ep);
+	}
+
+	for (s = subsec, sectlen = 0; *s; s += sectlen) {
 		tmp = s;
 		sectlen = 0;
 		if (isdigit(*tmp)) {
@@ -146,123 +293,225 @@
 				tmp++;
 			}
 		}
-#else
-		sectlen = 1;
-#endif
 		if (sectlen == 0)
-			errx(1, "malformed section string");
+			errx(1, "malformed section string \"%s\"", subsec);
 
-		sprintf(mandir, "%s/%s%.*s", path, _PATH_MANPAGES, sectlen, s);
-		sprintf(catdir, "%s/%s%.*s", path, _PATH_CATPAGES, sectlen, s);
+		if ((ep = malloc(sizeof(ENTRY))) == NULL ||
+		    (ep->s = malloc(sectlen + strlen("man") + 1)) == NULL)
+			err(1, "Out of memory");
+		sprintf(ep->s, "man%.*s", sectlen, s);
+		TAILQ_INSERT_TAIL(&subdirp->list, ep, q);
+	}
+}
 
-		if ((dirp = opendir(mandir)) == 0) {
-			warn("can't open %s", mandir);
+void
+lncatpages(mandir, manpage, catdir, catpage, catsuffix)
+	char *mandir, *manpage, *catdir, *catpage, *catsuffix;
+{
+	DIR *dirp;
+	struct dirent *dp;
+	struct stat st, manstat;
+	char manfile[PATH_MAX];
+	char catfile[PATH_MAX];
+	char *tmp;
+
+	if (stat(manpage, &manstat) < 0) {
+		warn("can't stat %s", manpage);
+		return;
+	}
+	if ((dirp = opendir(mandir)) == NULL) {
+		warn(mandir);
+		return;
+	}
+	while ((dp = readdir(dirp)) != NULL) {
+		if (strcmp(dp->d_name, ".") == 0 ||
+		    strcmp(dp->d_name, "..") == 0)
+			continue;
+		sprintf(manfile, "%s/%s", mandir, dp->d_name);
+		if (strcmp(manfile, manpage) == 0)
+			continue;
+		if (stat(manfile, &st) < 0) {
+			warn("can't stat %s", manfile);
 			continue;
 		}
-
-		if (stat(catdir, &catstat) < 0) {
-			if (errno != ENOENT) {
-				warn("can't stat %s", catdir);
-				closedir(dirp);
+		if (st.st_ino == manstat.st_ino) {
+			sprintf(catfile, "%s/%s", catdir, dp->d_name);
+			if ((tmp = strrchr(catfile, '.')) == NULL)
 				continue;
-			}
+			strcpy(tmp, catsuffix);
+			unlink(catfile);
 			if (f_noprint == 0)
-				printf("mkdir %s\n", catdir);
-			if (f_noaction == 0 && mkdir(catdir, 0755) < 0) {
-				warn("can't create %s", catdir);
-				closedir(dirp);
-				return;
+				printf("ln %s %s\n", catpage, catfile);
+			if (f_noaction == 0 && link(catpage, catfile) < 0) {
+				warn("can't link %s to %s", catpage, catfile);
+				continue;
 			}
+		}
+	}
+	closedir(dirp);
+}
 
+/*
+ * Format all pages in mandir, placing the output in catdir.
+ */
+int
+dirformat(mandir, catdir, catsuffix)
+	char *mandir, *catdir, *catsuffix;
+{
+	int error, rval, symlink;
+	char manpage[PATH_MAX];
+	char catpage[PATH_MAX];
+	char *tmp, *mansuffix;
+	DIR *dirp;
+	struct dirent *dp;
+	struct stat manstat;
+	struct stat catstat;
+
+	if (chdir(mandir) || (dirp = opendir(".")) == NULL) {
+		if (errno != ENOENT)
+			warn(mandir);
+		return (0);
+	}
+	if (stat(catdir, &catstat) < 0) {
+		if (errno != ENOENT) {
+			warn("can't stat %s", catdir);
+			closedir(dirp);
+			return (0);
+		}
+		if (f_noprint == 0)
+			printf("mkdir %s\n", catdir);
+		if (f_noaction == 0 && mkdir(catdir, 0755) < 0) {
+			warn("can't create %s", catdir);
+			closedir(dirp);
+			return (0);
 		}
 
-		while ((dp = readdir(dirp)) != NULL) {
-			if (strcmp(dp->d_name, ".") == 0 ||
-			    strcmp(dp->d_name, "..") == 0)
-				continue;
+	}
 
-			sprintf(manpage, "%s/%s", mandir, dp->d_name);
-			sprintf(catpage, "%s/%s", catdir, dp->d_name);
-			if ((tmp = strrchr(catpage, '.')) != NULL)
-				strcpy(tmp, ".0");
-			else
-				continue;
+	rval = 0;
+	while ((dp = readdir(dirp)) != NULL) {
+		if (strcmp(dp->d_name, ".") == 0 ||
+		    strcmp(dp->d_name, "..") == 0)
+			continue;
 
+		sprintf(manpage, "%s/%s", mandir, dp->d_name);
+		sprintf(catpage, "%s/%s", catdir, dp->d_name);
+
+		if (lstat(manpage, &manstat) < 0) {
+			warn("can't stat %s", manpage);
+			continue;
+		}
+
+		symlink = 0;
+		if (S_ISLNK(manstat.st_mode)) {
+			symlink = 1;
 			if (stat(manpage, &manstat) < 0) {
 				warn("can't stat %s", manpage);
 				continue;
 			}
+		}
 
-			if (!S_ISREG(manstat.st_mode)) {
+		if (!S_ISREG(manstat.st_mode)) {
+			/*
+			 * Subdirectories within man directories contain
+			 * architecture specific pages.
+			 */
+			if (S_ISDIR(manstat.st_mode))
+				dirformat(manpage, catpage, catsuffix);
+			else
 				warnx("not a regular file %s", manpage);
-				continue;
-			}
-			if ((error = stat(catpage, &catstat)) &&
-			    errno != ENOENT) {
-				warn("can't stat %s", catpage);
-				continue;
-			}
-
-			if ((error && errno == ENOENT) || 
-			    manstat.st_mtime >= catstat.st_mtime) {
-				if (f_noformat)
-					dowhatis = 1;
-				else {
-					/*
-					 * manpage is out of date,
-					 * reformat
-					 */
-					sprintf(sysbuf, "nroff -mandoc %s > %s",
-					    manpage, catpage);
-					if (f_noprint == 0)
-						printf("%s\n", sysbuf);
-					if (f_noaction == 0)
-						dosystem(sysbuf);
-					dowhatis = 1;
-				}
-			}
+			continue;
 		}
-		closedir(dirp);
-	}
-}
 
-void
-makewhatis(path)
-	const char *path;
-{
-	char sysbuf[1024];
+		if ((mansuffix = strrchr(manpage, '.')) == NULL)
+			continue;
+		if ((tmp = strrchr(catpage, '.')) == NULL)
+			continue;
+		else
+			strcpy(tmp, catsuffix);
 
-	sprintf(sysbuf, "%s %s", _PATH_MAKEWHATIS, path);
-	if (f_noprint == 0)
-		printf("%s\n", sysbuf);
-	if (f_noaction == 0)
-		dosystem(sysbuf);
+		if ((error = stat(catpage, &catstat)) &&
+		    errno != ENOENT) {
+			warn("can't stat %s", catpage);
+			continue;
+		}
+
+		/*
+		 * If manpage is out of date, reformat
+		 */
+		if ((error && errno == ENOENT) || 
+		    (manstat.st_mtime >= catstat.st_mtime &&
+		    f_noformat == 0)) {
+			mformat(manpage, catpage);
+			/*
+			 * If manpages hardlinked together, duplicate
+			 * with catpages.
+			 */
+			if (manstat.st_nlink > 1 || symlink)
+				lncatpages(mandir, manpage, catdir, catpage,
+				    catsuffix);
+			rval = 1;
+		}
+	}
+	closedir(dirp);
+	return (rval);
 }
 
 void
-dosystem(cmd)
-	const char *cmd;
+mformat(manpage, catpage)
+	char *manpage, *catpage;
 {
-	int status;
-
-	if ((status = system(cmd)) == 0)
-		return;
+	int fnd;
+	char buf[1024], fmtbuf[1024], *p, *sufent, *mansuffix;
+	TAG *sufp;
+	ENTRY *e_sufp;
+
+	if ((mansuffix = strrchr(manpage, '.')) == NULL)
+		mansuffix = ".1";
+
+	if ((sufp = getlist("_build")) == NULL)
+		errx(1, "No _build keywords found in %s", _PATH_MANCONF);
+
+	for (fnd = 0, e_sufp = sufp->list.tqh_first; e_sufp != NULL;
+	    e_sufp = e_sufp->q.tqe_next) {
+		if ((sufent = strdup(e_sufp->s)) == NULL)
+			errx(1, "Out of memory");
+		for (p = sufent; *p != '\0' && !isspace(*p); p++);
+		if (*p == '\0') {
+			free(sufent);
+			continue;
+		}
+		*p = '\0';
+		if (fnmatch(sufent, mansuffix, 0) != 0) {
+			free(sufent);
+			continue;
+		}
+		fnd = 1;
 
-	if (status == -1)
-		err(1, "cannot execute action");
-	if (WIFSIGNALED(status))
-		errx(1, "child was signaled to quit. aborting");
-	if (WIFSTOPPED(status))
-		errx(1, "child was stopped. aborting");
-	if (f_ignerr == 0)
-		errx(1,"*** Exited %d");
-	warnx("*** Exited %d (continuing)");
+		for (++p; isspace(*p); p++);
+		/*
+		 * If catpage is NULL, then we send it to stdout
+		 */
+		if (catpage == NULL)
+			strncpy(fmtbuf, p, sizeof(fmtbuf));
+		else
+			snprintf(fmtbuf, sizeof(fmtbuf), "%s > %s", p, catpage);
+		snprintf(buf, sizeof(buf), fmtbuf, manpage);
+		if (f_noprint == 0 && catpage != NULL)
+			printf("%s\n", buf);
+		if (f_noaction == 0)
+			dosystem(buf);
+		free(sufent);
+	}
+	if (fnd == 0)
+		warnx("Don't know how to format %s", manpage);
 }
 
 void
 usage()
 {
 	(void)fprintf(stderr,
-	    "usage: catman [-knpsw] [-M manpath] [sections]\n");
+	    "usage: catman [-knpsw] [-C file] [-M manpath] [sections]\n"
+	    "       catman [-C file] manpage\n");
 	exit(1);
 }
Index: ../../usr.bin/man/config.c
===================================================================
RCS file: /snarf/netbsd/master/src/usr.bin/man/config.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 config.c
--- config.c	1995/11/16 02:27:52	1.1.1.1
+++ config.c	1996/01/02 02:43:53
@@ -119,12 +119,12 @@
 			while (*++t && isspace(*t));
 			if ((ep = malloc(sizeof(ENTRY))) == NULL ||
 			    (ep->s = strdup(t)) == NULL)
-				err(1, NULL);
+				err(1, "Out of memory");
 			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);
+				err(1, "Out of memory");
 			TAILQ_INSERT_TAIL(&tp->list, ep, q);
 		}
 	}
@@ -144,7 +144,7 @@
 
 	if ((tp = calloc(1, sizeof(TAG))) == NULL ||
 	    (tp->s = strdup(name)) == NULL)
-		err(1, NULL);
+		err(1, "Out of memory");
 	TAILQ_INIT(&tp->list);
 	TAILQ_INSERT_TAIL(&head, tp, q);
 	return (tp);
Index: ../../usr.bin/man/pathnames.h
===================================================================
RCS file: /snarf/netbsd/master/src/usr.bin/man/pathnames.h,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 pathnames.h
--- pathnames.h	1995/11/16 02:27:53	1.1.1.1
+++ pathnames.h	1996/01/01 20:16:32
@@ -35,6 +35,7 @@
  *	@(#)pathnames.h	8.3 (Berkeley) 1/2/94
  */
 
+#define	_PATH_MAKEWHATIS	"/usr/libexec/makewhatis"
 #define	_PATH_MANCONF	"/etc/man.conf"
 #define	_PATH_PAGER	"/usr/bin/more -s"
 #define	_PATH_TMP	"/tmp/man.XXXXXX"
-----cut-here-----


This final patch changes bsd.man.mk to use catman instead of nroff.
-----cut-here-----
Index: bsd.man.mk
===================================================================
RCS file: /snarf/netbsd/master/src/share/mk/bsd.man.mk,v
retrieving revision 1.1.1.2
diff -u -r1.1.1.2 bsd.man.mk
--- bsd.man.mk	1995/12/15 12:08:10	1.1.1.2
+++ bsd.man.mk	1996/01/02 01:37:48
@@ -15,8 +15,8 @@
 	.cat7 .cat8 .cat9
 
 .9.cat9 .8.cat8 .7.cat7 .6.cat6 .5.cat5 .4.cat4 .3.cat3 .2.cat2 .1.cat1:
-	@echo "nroff -mandoc ${.IMPSRC} > ${.TARGET}"
-	@nroff -mandoc ${.IMPSRC} > ${.TARGET} || ( rm -f ${.TARGET} ; false )
+	@echo "catman ${.IMPSRC} > ${.TARGET}"
+	@catman ${.IMPSRC} > ${.TARGET} || ( rm -f ${.TARGET} ; false )
 
 .if defined(MAN) && !empty(MAN)
 MANALL=	${MAN:S/.1$/.cat1/g:S/.2$/.cat2/g:S/.3$/.cat3/g:S/.4$/.cat4/g:S/.5$/.cat5/g:S/.6$/.cat6/g:S/.7$/.cat7/g:S/.8$/.cat8/g:S/.9$/.cat9/g}
-----cut-here-----

>Audit-Trail:
>Unformatted: