[comp.sources.unix] v24i057: Purdue shell turbo charger and manual installer, Part01/06

rsalz@uunet.uu.net (Rich Salz) (03/20/91)

Submitted-by: Kevin Braunsdorf <ksb@cc.purdue.edu>
Posting-number: Volume 24, Issue 57
Archive-name: pucc-mk/part01

This six part archive contains the mk tool (a turbo charger for the
shell) and the manual page installer.  This might not be for everyone;
if you don't write or install manual pages you can skip it.  It needs
the libs to build and the install package to run.

Sometimes you want to hide a bit of shell code in a file, then later extract
and run that code on that file to produce an output.  A good example of this
is the manual system: it would be terrible to have to name a Makefile for
each manual page in /usr/man/man*, or keep said file(s) in sync with the
pages.

What we do instead is hide the formatting command in the comments of the
manual page, then extract it when we need to format the page.  Thus the
command moves with the page and cannot get lost.  If a page has no command
hidden in it we will fall back to a set formula, much as make(1) does.

The manual page installer complete replaces catman and makewhatis,
among others.

kayessbee
--
"This may be a new sense of the word `robust' for you."
Kevin Braunsdorf, ksb@cc.purdue.edu, pur-ee!ksb, purdue!ksb

#!/bin/sh
# This is pucc-1c, a shell archive (produced by shar 3.49)
# To extract the files from this archive, save it to a file, remove
# everything above the "!/bin/sh" line above, and type "sh file_name".
#
# made 11/29/1990 15:58 UTC by ksb@cc.purdue.edu (Kevin Braunsdorf)
# Source directory /ksb/c.s.u-2
#
# existing files will NOT be overwritten unless -c is specified
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#   1737 -rw-r--r-- INSTALL.03
#  32072 -r--r--r-- mkcat/mkcat.c
#  21421 -r--r--r-- mk/mk.c
#   2737 -rw-r--r-- mk/Makefile
#   1182 -rw-r--r-- mk/Tests/ExitCodes
#    376 -rw-r--r-- mk/Tests/compat
#    190 -r--r--r-- mk/rlimsys.h
#  27828 -r--r--r-- mkcat/scan.c
#  15821 -r--r--r-- mkcat/genwhatis.c
#  12839 -r--r--r-- mkcat/format.c
#   1292 -r--r--r-- mk/setenv.c
#    191 -r--r--r-- mk-lib/m-clean
#  11371 -r--r--r-- mkcat/readman.c
#  10944 -r--r--r-- mk/mk.1l
#   7922 -r--r--r-- mkcat/pt.c
#   4889 -r--r--r-- mk/rlimsys.c
#   6777 -r--r--r-- mk-lib/mk.5l
#   5555 -r--r--r-- mkcat/mkcat.8l
#   5149 -r--r--r-- mkcat/sym2hard.c
#   4487 -r--r--r-- mkcat/mkcat-opts.5l
#    725 -rw-r--r-- mk/Tests/file.x,y
#    159 -r--r--r-- mk-lib/m-info
#   4513 -rw-r--r-- mkcat/main.c
#   2823 -rw-r--r-- mk/Makefile.mkcmd
#   3495 -rw-r--r-- mk/main.c
#   3088 -rw-r--r-- mk-lib/Makefile
#   3766 -r--r--r-- mkcat/strcasecmp.c
#   3675 -rw-r--r-- mkcat/Makefile
#   3718 -rw-r--r-- mkcat/Makefile.mkcmd
#   2007 -r--r--r-- mk/mk.m
#   1954 -r--r--r-- mkcat/mkcat.h
#   2050 -r--r--r-- mkcat/pt.h
#   1815 -r--r--r-- mkcat/genwhatis.h
#   2423 -r--r--r-- mkcat/mkcat.m
#   1630 -rw-r--r-- mk/Tests/options
#   1101 -r--r--r-- mk/README
#   1123 -r--r--r-- mk-lib/file-valid
#   1168 -r--r--r-- mk-lib/pre-uu
#   1371 -r--r--r-- mk-lib/dot-m
#   1259 -r--r--r-- mk-lib/README
#   1493 -r--r--r-- mkcat/readman.h
#   1164 -r--r--r-- mkcat/scan.h
#   1525 -rw-r--r-- mkcat/machine.h
#   1204 -r--r--r-- mkcat/format.h
#    806 -rw-r--r-- mk/mk.h
#    825 -r--r--r-- mk/rcsname.c
#    977 -r--r--r-- mk/optaccum.c
#   1011 -r--r--r-- mk-lib/pre-dir
#    897 -r--r--r-- mk-lib/pre-Z
#   1049 -r--r--r-- mk-lib/pre-z
#    859 -r--r--r-- mk-lib/pre-zoo
#    830 -r--r--r-- mk-lib/dot-y
#   1019 -r--r--r-- mk-lib/pre-MW
#    911 -r--r--r-- mkcat/README
#    430 -r--r--r-- mk/optaccum.h
#    657 -r--r--r-- mk/INSTALL
#    610 -rw-r--r-- mk/machine.h
#    712 -r--r--r-- mk-lib/pre-out
#    533 -r--r--r-- mk-lib/dot-c
#    672 -r--r--r-- mk-lib/dot-e
#    530 -r--r--r-- mk-lib/dot-l
#    693 -r--r--r-- mk-lib/dot-man
#    601 -r--r--r-- mk-lib/dot-ps
#    705 -r--r--r-- mk-lib/dot-r
#    501 -r--r--r-- mk-lib/dot-shar
#    545 -r--r--r-- mk-lib/dot-t
#    605 -r--r--r-- mk-lib/comma-v
#    647 -r--r--r-- mkcat/INSTALL
#    471 -r--r--r-- mk-lib/pre-a
#    473 -r--r--r-- mk-lib/pre-C
#    485 -r--r--r-- mk-lib/pre-patch
#    481 -r--r--r-- mk-lib/dot-1
#    384 -r--r--r-- mk-lib/dot-dvi
#    441 -r--r--r-- mk-lib/dot-f
#    417 -r--r--r-- mk-lib/dot-m4
#    467 -r--r--r-- mk-lib/dot-mk
#    435 -r--r--r-- mk-lib/dot-p
#    426 -r--r--r-- mk-lib/pre-cpio
#    320 -rw-r--r-- mk/Tests/typenot
#    264 -rw-r--r-- mk/Tests/markers
#    275 -rw-r--r-- mk/Tests/params
#    314 -r--r--r-- mk-lib/m-mkcat
#    300 -r--r--r-- mk-lib/m-run
#    298 -r--r--r-- mk-lib/type-b
#    314 -r--r--r-- mk-lib/type-c
#    274 -r--r--r-- mk-lib/type-d
#    281 -r--r--r-- mk-lib/type-p
#    263 -r--r--r-- mk-lib/type-s
#    320 -r--r--r-- mk-lib/file-gmon.out
#    339 -r--r--r-- mk-lib/pre-o
#    296 -r--r--r-- mk-lib/pre-tar
#    263 -r--r--r-- mk-lib/dot-
#    257 -r--r--r-- mk-lib/dot-h
#    299 -r--r--r-- mk-lib/dot-sh
#    286 -rw-r--r-- mkcat/main.h
#    238 -rw-r--r-- mk/main.h
#    222 -r--r--r-- mk-lib/dot-xbm
#    205 -r--r--r-- mk-lib/m-compile
#    225 -r--r--r-- mk-lib/m-display
#    203 -r--r--r-- mk-lib/pre-ln
#    233 -r--r--r-- mk-lib/dot-C
#    239 -r--r--r-- mk-lib/dot-pl
#    199 -r--r--r-- mk-lib/dot-el
#    131 -rw-r--r-- mk/Tests/lines
#    111 -r--r--r-- mk-lib/dot-ph
#
# ============= INSTALL.03 ==============
if test -f 'INSTALL.03' -a X"$1" != X"-c"; then
	echo 'x - skipping INSTALL.03 (File already exists)'
else
echo 'x - extracting INSTALL.03 (Text)'
sed 's/^X//' << 'Purdue' > 'INSTALL.03' &&
Contains:
X	mk(1l)		- detect and execute shell commands in files
X	mk(5l)		- how to write and embed mk commands
X	mkcat(8l)	- update and format a manual page
X
X
Notes on depends:
X	- libopt.a and libsrtunq.a need not be installed, but it
X	  would make life easier if you want other PUCC tools.
X
X	- mk-lib needs a lib directory (like /usr/local/lib/mk)
X
X	- mk needs to know mk-lib's lib directory, and needs libopt
X
X	- mkcat needs mk(1L) and install(1L) to function at all,
X	  and needs libopt and libsrtunq
X
X
To install these tools:
X
0\ read the manual pages, see if you want any of them
X
1\ decide where to install all this stuff, change the destinations in
X   the Makefiles {BIN,LIB,ETC,HEADER}
X	vi */Makefile
X	
2\ edit mk-lib/Makefile and mk/mk.h to change `/usr/local/lib/mk' to
X   whatever lib directory you want mk to use
X	vi mk-lib/Makefile mk/mk.h
X
3\ edit */machine.h and set up and macros that look important to you
X	vi */machine.h
X
4\ remove strcasecmp.o from the OBJ list in mkcat/Makefile if you have
X    one in libc (or just ignore it)
X	nm /lib/libc.a | grep strcasecmp
X
5\ build mk, test it if you like
X	(cd mk && make all)
X	(cd mk && make self-test)
X
6\ build mkcat {it has some hard coded stuff you might edit later in mkcat.c}
X	(cd mkcat && make)
X
7\ install the tools, note that install must be first the others us it
X	su
X	(cd mk && make install)				#
X	(cd mk-lib && make install)			#
X	(cd mkcat && make install)			#
X	exit						# the root shell
X
8\ install the manual pages
X	su
X	mkcat -v mk/mk.1l mk-lib/mk.5l mkcat/mkcat.8l mkcat/mkcat-opts.5l
X	exit
X
9\ clean up the dirs
X	(cd mk && make clean)
X	(cd mk-lib && make clean)
X	(cd mkcat && make clean)
X
kayessbee
--
Kevin Braunsdorf, ksb@cc.purdue.edu, pur-ee!ksb, purdue!ksb
Purdue
chmod 0644 INSTALL.03 ||
echo 'restore of INSTALL.03 failed'
Wc_c="`wc -c < 'INSTALL.03'`"
test 1737 -eq "$Wc_c" ||
	echo 'INSTALL.03: original size 1737, current size' "$Wc_c"
fi
# ============= mkcat/mkcat.c ==============
if test ! -d 'mkcat'; then
    echo 'x - creating directory mkcat'
    mkdir 'mkcat'
fi
if test -f 'mkcat/mkcat.c' -a X"$1" != X"-c"; then
	echo 'x - skipping mkcat/mkcat.c (File already exists)'
else
echo 'x - extracting mkcat/mkcat.c (Text)'
sed 's/^X//' << 'Purdue' > 'mkcat/mkcat.c' &&
/*
X * Copyright 1990 Purdue Research Foundation, West Lafayette, Indiana
X * 47907.  All rights reserved.
X *
X * Written by Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb
X *
X * This software is not subject to any license of the American Telephone
X * and Telegraph Company or the Regents of the University of California.
X *
X * Permission is granted to anyone to use this software for any purpose on
X * any computer system, and to alter it and redistribute it freely, subject
X * to the following restrictions:
X *
X * 1. Neither the authors nor Purdue University are responsible for any
X *    consequences of the use of this software.
X *
X * 2. The origin of this software must not be misrepresented, either by
X *    explicit claim or by omission.  Credit to the authors and Purdue
X *    University must appear in documentation and sources.
X *
X * 3. Altered versions must be plainly marked as such, and must not be
X *    misrepresented as being the original software.
X *
X * 4. This notice may not be removed or altered.
X */
X
/*
X * this module sets some global variables that tell mkcat how this	(ksb)
X * system is aranged
X *
X * who are we today, parse options with mkcmd parsers and stuff
X */
#include "machine.h"
X
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/param.h>
#if defined(SYSV)
#include <ndir.h>
#else
#include <sys/dir.h>
#endif
#include <sys/stat.h>
X
#include <grp.h>
#include <pwd.h>
extern struct group *getgrgid(), *getgrnam();
extern struct passwd *getpwuid(), *getpwnam();
X
#if !defined(MAXPATHLEN)
#define MAXPATHLEN	1024
#endif
X
#include "main.h"
#include "pt.h"
#include "genwhatis.h"
#include "mkcat.h"
#include "srtunq.h"
X
extern int errno;
extern char *sys_errlist[];
#define strerror(Me) sys_errlist[Me]
extern char *strrchr(), *strchr(), *strcat(), *strcpy(), *strncpy();
extern char *strsave();
extern char *malloc(), *calloc(), *mktemp();
extern int strcmp(), strncmp();
X
#define HAVE_A(Mp)	((char *)0 != (Mp) && '\000' != *(Mp))
X
int fSawCfg;				/* we found/read a config file	*/
char acOpts[] = ".mkcat-opts";		/* file to read/write options	*/
int fUseHards = 1;			/* -H or -S			*/
int fCompress = 1;			/* or 0				*/
char *apcModes[] = {
X	/* owner      group       mode					*/
X	(char *)0, (char *)0, (char *)0,	/* dir			*/
X	(char *)0, (char *)0, (char *)0,	/* page			*/
X	(char *)0, (char *)0, (char *)0,	/* whatis		*/
X	(char *)0, (char *)0, (char *)0		/* man src		*/
};
char *pcMkToken = (char *)0;
X
typedef struct LInode {			/* linked list, extender combos	*/
X	struct LInode *pLInext;
X	SRTTABLE STall;
} LIST;
LIST *pLIExt;
SRTTABLE STBase;
X
static char
X	acZKey[] = "compress:",		/* use compress/zcat		*/
X	acLKey[] = "links:",		/* hard or soft flag		*/
X	acTKey[] = "token:",		/* `mk -m$token file' to format	*/
X	acBKey[] = "OKbase:",		/* intro page bases		*/
X	acSKey[] = "OKext:",		/* getc.3s !conflict gets.3f	*/
X	acPKey[] = "page",		/* optional			*/
X	acDKey[] = "dir",
X	acWKey[] = "whatis",
X	acNKey[] = "man",
X	acMKey[] = "mode:",		/* install opts			*/
X	acOKey[] = "owner:",
X	acGKey[] = "group:";
X
X
/* is this base in the list of legal dup bases?				(ksb)
X */
int
IsOKBase(pc)
char *pc;
{
X	return (char *)0 != srtmem(& STBase, pc, strcmp);
}
X
/* do the given extenders conflict?					(ksb)
X * find a list that contains both, then no conflict
X */
int
ExtConflict(pcE1, pcE2)
char *pcE1, *pcE2;
{
X	register LIST *pLI;
X
X	for (pLI = pLIExt; (LIST *)0 != pLI; pLI = pLI->pLInext) {
X		if ((char *)0 == srtmem(& pLI->STall, pcE1, strcmp))
X			continue;
X		if ((char *)0 != srtmem(& pLI->STall, pcE2, strcmp))
X			return 0;
X	}
X	return 1;
}
X
/*
X * learn these things by reading $ROOT/.mkcat-opt			(ksb)
X *	# comments
X *	compress: y|n
X *	links: H|S
X *	{dir,page,}{mode,group,owner}: string
X *	OKbase: intro
X *	OKext: {3f,3s}
X */
static void
PrepInfo()
{
X	register FILE *fpCnf;
X	register char *pcScan, *pcNl;
X	register int j;
X	auto char acConfig[MAXPATHLEN+1];
X	auto char acLine[BUFSIZ];
X
X	srtinit(&STBase);
X	pLIExt = (LIST *)0;
X
X	fSawCfg = 0;
X	(void)sprintf(acConfig, "%s/%s", pcRoot, acOpts);
X	if (NULL == (fpCnf = fopen(acConfig, "r"))) {
X		return;		/* use defaults		*/
X	}
X	fSawCfg = 1;
X	while ((char *)0 != (pcScan = fgets(acLine, BUFSIZ, fpCnf))) {
X		while (isspace(*pcScan)) {
X			if ('\n' == *pcScan)
X				break;
X			++pcScan;
X		}
X		if ('\n' == *pcScan || '#' == *pcScan) {
X			continue;
X		}
X		if (0 == strncasecmp(acZKey, pcScan, sizeof(acZKey)-1)) {
X			pcScan += sizeof(acZKey)-1;
X			while (isspace(*pcScan)) {
X				if ('\n' == *pcScan)
X					break;
X				++pcScan;
X			}
X			switch (*pcScan) {
X			case 'y':
X			case 'Y':
X				fCompress = 1;
X				break;
X			case 'n':
X			case 'N':
X				fCompress = 0;
X				break;
X			default:
X			case '\n':
X				break;
X			}
X			continue;
X		}
X		if (0 == strncasecmp(acLKey, pcScan, sizeof(acLKey)-1)) {
X			pcScan += sizeof(acLKey)-1;
X			while (isspace(*pcScan)) {
X				if ('\n' == *pcScan)
X					break;
X				++pcScan;
X			}
X			switch (*pcScan) {
X			case 'H':
X			case 'h':
X				fUseHards = 1;
X				break;
X			case 's':
X			case 'S':
X				fUseHards = 0;
X				break;
X			default:
X			case '\n':
X				break;
X			}
X			continue;
X		}
X
X		if (0 == strncasecmp(acBKey, pcScan, sizeof(acBKey)-1)) {
X			register char *pcLast;
X
X			pcScan += sizeof(acBKey)-1;
X			while (isspace(*pcScan)) {
X				if ('\n' == *pcScan)
X					break;
X				++pcScan;
X			}
X			switch (*pcScan) {
X			default:
X				if ((char *)0 == (pcLast = strchr(pcScan, '\n'))) {
X					fprintf(stderr, "%s: %s line too long\n", progname, acBKey);
X					break;
X				}
X				*pcLast = '\000';
X				(void)srtin(&STBase, pcScan, strcmp);
X				break;
X			case '\n':
X				fprintf(stderr, "%s: %s missing basename\n", progname, acBKey);
X				break;
X			}
X			continue;
X		}
X
X		if (0 == strncasecmp(acSKey, pcScan, sizeof(acSKey)-1)) {
X			register char *pcLast, cSave;
X			register LIST *pLITemp;
X
X			pLITemp = (LIST *)malloc(sizeof(LIST));
X			if ((LIST *)0 == pLITemp) {
X				write(2, "out of memory\n", 13);
X				exit(2);
X			}
X			pLITemp->pLInext = pLIExt;
X			pLIExt = pLITemp;
X			(void)srtinit(& pLITemp->STall);
X			pcScan += sizeof(acSKey)-1;
X			do {	/* {a,v,c} */
X				while (isspace(*pcScan)) {
X					if ('\n' == *pcScan)
X						break;
X					++pcScan;
X				}
X				switch (*pcScan) {
X				case ',':
X				case '{':
X					++pcScan;
X					/* fallthrough */
X				default:
X					pcLast = pcScan;
X					while (',' != *pcScan && '}' != *pcScan && '\000' != *pcScan && '\n' != *pcScan)
X						++pcScan;
X					cSave = *pcScan;
X					*pcScan = '\000';
X					(void)srtin(& pLITemp->STall, pcLast, strcmp);
X					*pcScan = cSave;
X					break;
X				case '}':
X					++pcScan;
X				case '\n':
X					break;
X				}
X			} while ('\n' != *pcScan && '\000' != *pcScan);
X			continue;
X		}
X
X		if ((char *)0 == (pcNl = strchr(pcScan, '\n'))) {
X			fprintf(stderr, "%s: %s: line too long?\n", progname, acConfig);
X			exit(1);
X		}
X		*pcNl = '\000';
X
X		if (0 == strncasecmp(acTKey, pcScan, sizeof(acTKey)-1)) {
X			pcScan += sizeof(acTKey)-1;
X			pcMkToken = strsave(pcScan);
X			continue;
X		}
X		if (0 == strncasecmp(acDKey, pcScan, sizeof(acDKey)-1)) {
X			j = DIRMODE-MINMODE;
X			pcScan += sizeof(acDKey)-1;
X		} else if (0 == strncasecmp(acPKey, pcScan, sizeof(acPKey)-1)) {
X			j = PAGEMODE-MINMODE;
X			pcScan += sizeof(acPKey)-1;
X		} else if (0 == strncasecmp(acWKey, pcScan, sizeof(acWKey)-1)) {
X			j = WHATISMODE-MINMODE;
X			pcScan += sizeof(acWKey)-1;
X		} else if (0 == strncasecmp(acNKey, pcScan, sizeof(acNKey)-1)) {
X			j = MANMODE-MINMODE;
X			pcScan += sizeof(acNKey)-1;
X		} else {
X			j = PAGEMODE-MINMODE;
X		}
X		while (isspace(*pcScan)) {
X			++pcScan;
X		}
X		if (':' == *pcScan) {
X			do {
X				++pcScan;
X			} while (isspace(*pcScan));
X			switch (j) {
X			case DIRMODE-MINMODE:
X				pcCat = strsave(pcScan);
X				break;
X			case WHATISMODE-MINMODE:
X				pcWhatis = strsave(pcScan);
X				break;
X			case MANMODE-MINMODE:
X				pcMan = strsave(pcScan);
X				break;
X			case PAGEMODE-MINMODE:
X			default:
X				fprintf(stderr, "%s: unknown config file option\n", progname);
X				exit(3);
X			}
X			continue;
X		}
X
X		if (0 == strncasecmp(acOKey, pcScan, sizeof(acOKey)-1)) {
X			pcScan += sizeof(acOKey)-1;
X			while (isspace(*pcScan)) {
X				++pcScan;
X			}
X			if ('\000' == pcScan)
X				continue;
X			apcModes[j+DIROWNER] = strsave(pcScan);
X			continue;
X		}
X		if (0 == strncasecmp(acGKey, pcScan, sizeof(acGKey)-1)) {
X			pcScan += sizeof(acGKey)-1;
X			while (isspace(*pcScan)) {
X				++pcScan;
X			}
X			if ('\000' == pcScan)
X				continue;
X			apcModes[j+DIRGROUP] = strsave(pcScan);
X			continue;
X		}
X		if (0 == strncasecmp(acMKey, pcScan, sizeof(acMKey)-1)) {
X			pcScan += sizeof(acMKey)-1;
X			while (isspace(*pcScan)) {
X				++pcScan;
X			}
X			if ('\000' == pcScan)
X				continue;
X			apcModes[j+DIRMODE] = strsave(pcScan);
X			continue;
X		}
X		printf("%s: %s: unknown: %s", progname, pcScan);
X	}
X	(void)fclose(fpCnf);
}
X
X
/*
X * select the cat directories to scan					(ksb)
X */
static int
rootSelect(pEnt)
struct direct *pEnt;
{
X	extern int strlen();
X	auto struct stat stDir;
X	register int len;
X
X	len = strlen(pcCat);
X	if (0 != strncmp(pcCat, pEnt->d_name, len) || !isdigit(pEnt->d_name[len])) {
X		return 0;
X	}
X	if (-1 == LSTAT(pEnt->d_name, & stDir)) {
X		fprintf(stderr, "%s: stat: %s: %s\n", progname, pEnt->d_name, strerror(errno));
X		return 0;
X	}
X	if ((stDir.st_mode & S_IFMT) != S_IFDIR) {
X		fprintf(stderr, "%s: `%s\' should be a directory?\n", progname, pEnt->d_name);
X		return 0;
X	}
X	return 1;
}
X
/*
X * do show config							(ksb)
X */
void
doShowCfg()
{
X	register int i;
X	register LIST *pLI;
X	register char chSep, *pc;
X	auto int fSeeInstall;
X
X	for (fSeeInstall = 0, i = 0; i < MAXMODE+1; ++i) {
X		if ((char *)0 == apcModes[i]) {
X			apcModes[i] = "<see install>";
X			fSeeInstall = 1;
X		}
X	}
X	printf("%s: version: $Id: mkcat.c,v 3.21 90/11/28 09:54:46 ksb Exp $\n", progname);
X	printf("%s: root = `%s\', cat = `%s\', whatis = `%s\'", progname, pcRoot, pcCat, pcWhatis);
X	if (HAVE_A(pcMan)) {
X		printf(", man = `%s\'", pcMan);
X	}
X	printf("\n%s: pages are%s compressed, links are %s", progname, fCompress ? "":" not", fUseHards ? "hard" : "symbolic");
X	if (fSawCfg) {
X		printf(", config in %s\n", acOpts);
X	} else {
X		printf(", no config file\n");
X	}
X	srtgti(& STBase);
X	while ((char *)0 != (pc = srtgets(& STBase)))
X		printf("%s: %s %s\n", progname, acBKey, pc);
X	for (pLI = pLIExt; 0 != pLI; pLI = pLI->pLInext) {
X		printf("%s: %s ", progname, acSKey);
X		chSep = '{';
X		srtgti(& pLI->STall);
X		while ((char *)0 != (pc = srtgets(& pLI->STall))) {
X			printf("%c%s", chSep, pc);
X			chSep = ',';
X		}
X		printf("}\n");
X	}
X	printf("%s: dirs: owner=%-16s group=%-16s mode=%-12s\n", progname, apcModes[DIROWNER], apcModes[DIRGROUP], apcModes[DIRMODE]);
X	printf("%s: page: owner=%-16s group=%-16s mode=%-12s\n", progname, apcModes[PAGEOWNER], apcModes[PAGEGROUP], apcModes[PAGEMODE]);
X	printf("%s: what: owner=%-16s group=%-16s mode=%-12s\n", progname, apcModes[WHATISOWNER], apcModes[WHATISGROUP], apcModes[WHATISMODE]);
X	printf("%s:  man: owner=%-16s group=%-16s mode=%-12s\n", progname, apcModes[MANOWNER], apcModes[MANGROUP], apcModes[MANMODE]);
X	if (fSeeInstall) {
X		printf("%s: use install -V to see install\'s defaults\n", progname);
X	}
X	if (fVerbose) {
X		printf("%s", copyright);
X	}
}
X
X
/*
X * build an instck(1L) list for this manual system from mkcat-opts	(ksb)
X */
void
doGen()
{
X	register int i;
X	auto char acPat[MAXPATHLEN+2];
X	static char acFmt[] = "%-31s %-17s %-11s %-11s -\n";
X	static char acQuote[] = "\"";
X
X	for (i = 0; i < MAXMODE+1; ++i) {
X		if ((char *)0 == apcModes[i]) {
X			apcModes[i] = "*";
X		}
X	}
X	printf("# instck list machine generated by mkcat\n");
X	/* handle all the dirs we can build
X	 */
X	printf(acFmt, pcRoot, apcModes[DIRMODE], apcModes[DIROWNER], apcModes[DIRGROUP]);
X	sprintf(acPat, "%s/%s", pcRoot, "OLD");
X	printf(acFmt, acPat, acQuote, acQuote, acQuote);
X	if ('/' == pcCat[0] || ('.' == pcCat[0] && '/' == pcCat[1]))
X		sprintf(acPat, "%s*", pcCat);
X	else
X		sprintf(acPat, "%s/%s*", pcRoot, pcCat);
X	printf(acFmt, acPat, acQuote, acQuote, acQuote);
X	(void)strcat(acPat, "/OLD");
X	printf(acFmt, acPat, acQuote, acQuote, acQuote);
X	if (HAVE_A(pcMan)) {
X		if ('/' == pcMan[0] || ('.' == pcMan[0] && '/' == pcMan[1]))
X			sprintf(acPat, "%s*", pcMan);
X		else
X			sprintf(acPat, "%s/%s*", pcRoot, pcMan);
X		printf(acFmt, acPat, acQuote, acQuote, acQuote);
X		(void)strcat(acPat, "/OLD");
X		printf(acFmt, acPat, acQuote, acQuote, acQuote);
X	}
X	/* now do all the files
X	 */
X	if ('/' == pcWhatis[0] || ('.' == pcWhatis[0] && '/' == pcWhatis[1]))
X		sprintf(acPat, "%s*", pcWhatis);
X	else
X		sprintf(acPat, "%s/%s", pcRoot, pcWhatis);
X	printf(acFmt, acPat, apcModes[WHATISMODE], apcModes[WHATISOWNER], apcModes[WHATISGROUP]);
X	if ('/' == pcCat[0] || ('.' == pcCat[0] && '/' == pcCat[1]))
X		sprintf(acPat, "%s*/*", pcCat);
X	else
X		sprintf(acPat, "%s/%s*/*", pcRoot, pcCat);
X	printf(acFmt, acPat, apcModes[PAGEMODE], apcModes[PAGEOWNER], apcModes[PAGEGROUP]);
X	if (HAVE_A(pcMan)) {
X		if ('/' == pcMan[0] || ('.' == pcMan[0] && '/' == pcMan[1]))
X			sprintf(acPat, "%s*/*", pcMan);
X		else
X			sprintf(acPat, "%s/%s*/*", pcRoot, pcMan);
X		printf(acFmt, acPat, apcModes[MANMODE], apcModes[MANOWNER], apcModes[MANGROUP]);
X	}
}
X
extern int alphasort();
X
/*
X * sync the whatis db with the cat pages				(ksb)
X */
static int
doOthers(argc, argv)
int argc;
char **argv;
{
X	static char acDir[] = ".";
X	register struct direct *pEnt;
X	register int i, ndir;
X	auto int iErrors, *piCounts;
X	auto struct direct **ppDECats;
X	auto WHATIS **ppWU;
X
X	if (fVerbose) {
X		fprintf(fpOut, "%s: cd %s\n", progname, pcRoot);
X	}
X	if (-1 == chdir(pcRoot)) {
X		fprintf(stderr, "%s: chdir: %s: %s\n", progname, pcRoot, strerror(errno));
X		return 0;
X	}
X
X	if (-1 == (ndir = scandir(acDir, & ppDECats, rootSelect, alphasort))) {
X		fprintf(stderr, "%s: scandir: %s: %s\n", progname, acDir, strerror(errno));
X		return 0;
X	}
X
X	if (0 == ndir) {
X		fprintf(fpOut, "%s: no cat directories?\n", progname);
X		return 0;
X	}
X
X	if (0 == (ppWU = (WHATIS **)malloc((unsigned)sizeof(WHATIS *)*ndir))) {
X		fprintf(stderr, acNoMem, progname);
X		return 0;
X	}
X	if (0 == (piCounts = (int *)malloc((unsigned)sizeof(int)*ndir))) {
X		fprintf(stderr, acNoMem, progname);
X		return 0;
X	}
X
X	iErrors = 0;
X	for (i = 0; i < ndir; ++i) {
X		pEnt = ppDECats[i];
X		if (-1 == chdir(pEnt->d_name)) {
X			fprintf(stderr, "%s: chdir: %s: %s\n", progname, pEnt->d_name, strerror(errno));
X			continue;
X		}
X		if (! AllDir(atoi(pEnt->d_name+strlen(pcCat)), ppWU+i, piCounts+i)) {
X			++iErrors;
X		}
X		if (fCkAlso) {
X			AlsoScan(ppWU[i], piCounts[i]);
X		}
X		/* if the .. pointer is missing come in from above
X		 * you know, I hope this never happens!
X		 */
X		if (-1 == chdir("..") && -1 == chdir(pcRoot)) {
X			fprintf(stderr, "%s: chdir: %s: %s\n", progname, pcRoot, strerror(errno));
X			exit(1);
X		}
X	}
X	if (fMkWhatis) {
X		(void)NewWhatis(ndir, ppWU, piCounts);
X	}
X	return iErrors;
}
X
/*
X * make a directory via install(1L)					(ksb)
X */
void
MakeDir(pcDir, wOwner, wGroup, wMode)
char *pcDir;
int wOwner, wGroup, wMode;
{
X	auto char acCmd[2*MAXPATHLEN+1];
X
X	(void)sprintf(acCmd, "INSTALL=\"%s\" install -d", fVerbose ? "-v" : "");
X	if ((char *)0 != apcModes[wOwner]) {
X		(void)strcat(acCmd, " -o");
X		(void)strcat(acCmd, apcModes[wOwner]);
X	}
X	if ((char *)0 != apcModes[wGroup]) {
X		(void)strcat(acCmd, " -g");
X		(void)strcat(acCmd, apcModes[wGroup]);
X	}
X	if ((char *)0 != apcModes[wMode]) {
X		(void)strcat(acCmd, " -m");
X		(void)strcat(acCmd, apcModes[wMode]);
X	}
X	(void)strcat(acCmd, " ");
X	(void)strcat(acCmd, pcDir);
X	if (fVerbose) {
X		fprintf(fpOut, "%s: %s\n", progname, acCmd);
X	}
X	(void)fflush(fpOut);
X	if (fExec && 0 != system(acCmd)) {
X		fprintf(stderr, "%s: install -d %s failed\n", progname, pcDir);
X	}
}
X
/*
X * use pcDest = NIL and pcFlags = "-R" for deinstall file		(ksb)
X */
void
InstFile(pcSrc, pcDest, wOwner, wGroup, wMode, pcFlags)
char *pcSrc, *pcDest, *pcFlags;
int wOwner, wGroup, wMode;
{
X	auto char acCmd[MAXPATHLEN*2+104];
X
X	sprintf(acCmd, "INSTALL=\"%s\" install", fVerbose ? "-v" : "");
X	if ((char *)0 != pcFlags) {
X		(void)strcat(acCmd, " ");
X		(void)strcat(acCmd, pcFlags);
X	}
X	if ((char *)0 != apcModes[wOwner]) {
X		(void)strcat(acCmd, " -o");
X		(void)strcat(acCmd, apcModes[wOwner]);
X	}
X	if ((char *)0 != apcModes[wGroup]) {
X		(void)strcat(acCmd, " -g");
X		(void)strcat(acCmd, apcModes[wGroup]);
X	}
X	if ((char *)0 != apcModes[wMode]) {
X		(void)strcat(acCmd, " -m");
X		(void)strcat(acCmd, apcModes[wMode]);
X	}
X	(void)strcat(acCmd, " ");
X	(void)strcat(acCmd, pcSrc);
X	if ((char *)0 != pcDest) {
X		(void)strcat(acCmd, " ");
X		(void)strcat(acCmd, pcDest);
X	}
X	if (fVerbose) {
X		fprintf(fpOut, "%s: %s\n", progname, acCmd);
X	}
X	(void)fflush(fpOut);
X	if (fExec && 0 != system(acCmd)) {
X		fprintf(stderr, "%s: install %s failed\n", progname, pcSrc);
X	}
}
X
/*
X * get the words of wisdom from the user				(ksb)
X * strip leading white space, etc
X */
char *
GetWord(pcBuf, iLen, pcDef)
char *pcBuf, *pcDef;
int iLen;
{
X	register char *pcWhite, *pcCopy;
X	register int l;
X
X	(void)fflush(stdout);
X	pcBuf[0] = '\n';
X	if ((char *)0 == fgets(pcBuf, iLen, stdin))
X		return (char *)0;
X	pcWhite = pcBuf;
X	while (isspace(*pcWhite) && '\n' != *pcWhite)
X		++pcWhite;
X
X	pcCopy = pcBuf;
X	l = iLen;
X	while (l-- > 0 && '\n' != (*pcCopy = *pcWhite))
X		++pcCopy, ++pcWhite;
X
X	if (pcCopy == pcBuf)
X		(void)strncpy(pcBuf, pcDef, iLen);
X	else
X		*pcCopy = '\000';
X	return pcBuf;
}
X
/*
X * edit the modes we have, let the user pick'm				(ksb)
X * this could be done better, but what the heck.
X */
void
EditModes(wOwner, wGroup, wMode, pcWhat)
int wOwner, wGroup, wMode;
char *pcWhat;
{
#define MAXOGM	128		/* max owner, group, or mode length	*/
X	static char acNotSet[] = "-";
X	auto char acMode[MAXOGM+1];
X	register char *pcDef;
X
X	for (;;) {
X		pcDef = apcModes[wOwner];
X		if ((char *)0 == pcDef)
X			pcDef = acNotSet;
X		printf("Owner for %s? [%s] ", pcWhat, pcDef);
X		if ((char *)0 == GetWord(acMode, MAXOGM, pcDef)) {
X			exit(0);
X		}
X		switch (acMode[0]) {
X		case '-':
X			apcModes[wOwner] = (char *)0;
X			break;
X		default:
X			if ((struct passwd *)0 == getpwnam(acMode)) {
X				fprintf(stderr, "%s: %s: unknown login name\n", progname, acMode);
X				continue;
X			}
X			apcModes[wOwner] = strsave(acMode);
X			break;
X		}
X		break;
X	}
X	for (;;) {
X		pcDef = apcModes[wGroup];
X		if ((char *)0 == pcDef)
X			pcDef = acNotSet;
X		printf("Group for %s? [%s] ", pcWhat, pcDef);
X		if ((char *)0 == GetWord(acMode, MAXOGM, pcDef)) {
X			exit(0);
X		}
X		switch (acMode[0]) {
X		case '-':
X			apcModes[wGroup] = (char *)0;
X			break;
X		default:
X			if ((struct group *)0 == getgrnam(acMode)) {
X				fprintf(stderr, "%s: %s: unknown group name\n", progname, acMode);
X				continue;
X			}
X			apcModes[wGroup] = strsave(acMode);
X			break;
X		}
X		break;
X	}
X	for (;;) {
X		pcDef = apcModes[wMode];
X		if ((char *)0 == pcDef)
X			pcDef = acNotSet;
X		printf("Mode for %s? [%s] ", pcWhat, pcDef);
X		if ((char *)0 == GetWord(acMode, MAXOGM, pcDef)) {
X			exit(0);
X		}
X		switch (acMode[0]) {
X		case '-':
X			apcModes[wMode] = (char *)0;
X			break;
X		default:
X			/* ZZZ we should check to see if this is a mode,
X			 * but that could be hard, as install takes about
X			 * 400 mode forms... (hehheh) [ksb]
X			 */
X			apcModes[wMode] = strsave(acMode);
X			break;
X		}
X		break;
X	}
}
X
static char acEditBlurb[] =
X	"Use a dash (-) to select install\'s default\n";
X
/*
X * ask the user how to setup the mkcat-opts file			(ksb)
X */
int
doInit()
{
#define MAXANS	80
X	register LIST *pLI;
X	auto char chSep, *pc;
X	auto struct stat stDir;
X	auto char acAns[MAXANS+1];
X	auto char acConfig[MAXPATHLEN+1];
X	static char acDefMan[MAXPATHLEN+1];	/* returned in a global */
X	auto char acTCnf[MAXPATHLEN+1];		/* temp configuration	*/
X	auto FILE *fpOpts;
X	auto char *pcUser;
X	auto int fOldCompress, fOldUseHards, fNew, fDidDir;
X	extern char *getenv(), **environ;
X
X	fDidDir = 0;
X	fNew = 1;
X	printf("\nBuilding `%s' in manual root `%s'\n", acOpts, pcRoot);
X
X	/* build the man dir if it isn't there and we can
X	 */
X	if (-1 == stat(pcRoot, & stDir)) {
X		if (ENOENT != errno) {
X			fprintf(stderr, "%s: stat: %s: %s\n", progname, pcRoot, strerror(errno));
X			exit(1);
X		}
X		for (;;) {
X			printf("\nBuild `%s\' [yn] ", pcRoot);
X			if ((char *)0 == GetWord(acAns, MAXANS, "yes"))
X				exit(0);
X			switch (acAns[0]) {
X			case 'q':
X			case 'Q':
X			case 'N':
X			case 'n':
X				exit(0);
X			case 'y':
X			case 'Y':
X				printf("%s\n", acEditBlurb);
X				EditModes(DIROWNER, DIRGROUP, DIRMODE, "all directories");
X				fDidDir = 1;
X				break;
X			default:
X				continue;
X			}
X			break;
X		}
X		MakeDir(pcRoot, DIROWNER, DIRGROUP, DIRMODE);
X	}
X
X	/* read the existing options file if it is there and that is OK
X	 */
X	sprintf(acConfig, "%s/%s", pcRoot, acOpts);
X	/* at least set up minimal stuff */
X	srtinit(& STBase);
X	pLIExt = (LIST *)0;
X	if (-1 != access(acConfig, 0)) {
X		for (;;) {
X			printf("\nRead existing `%s\' [yn] ", acConfig);
X			if ((char *)0 == GetWord(acAns, MAXANS, "yes"))
X				exit(0);
X			switch (acAns[0]) {
X			case 'q':
X			case 'Q':
X				exit(0);
X			case 'N':
X			case 'n':
X				break;
X			case 'y':
X			case 'Y':
X				PrepInfo();
X				fNew = 0;
X				break;
X			default:
X				continue;
X			}
X			break;
X		}
X	} else {
X		srtin(&STBase, "intro", strcmp);
X		apcModes[PAGEMODE] = "0644";
X		apcModes[WHATISMODE] = "0644";
X		apcModes[MANMODE] = "0444";
X	}
X
X	fOldCompress = fCompress;
X	fOldUseHards = fUseHards;
X	/* ask about the compress/links options
X	 */
X	for (;;) {
X		printf("\nShould fomatted pages be kept compressed? [%s] ", fCompress ? "yn" : "ny");
X		if ((char *)0 == GetWord(acAns, MAXANS, fCompress ? "yes" : "no"))
X			exit(0);
X		switch (acAns[0]) {
X		case 'q':
X		case 'Q':
X			exit(0);
X		case 'N':
X		case 'n':
X			fCompress = 0;
X			break;
X		case 'y':
X		case 'Y':
X			fCompress = 1;
X			break;
X		default:
X			continue;
X		}
X		break;
X	}
X	for (;;) {
X		printf("\nShould extra links be symbolic or hard? [%s] ", fUseHards ? "HS" : "SH");
X		if ((char *)0 == GetWord(acAns, MAXANS, fUseHards ? "Hard" : "Symbolic"))
X			exit(0);
X		switch (acAns[0]) {
X		case 'q':
X		case 'Q':
X			exit(0);
X		case 'H':
X		case 'h':
X			fUseHards = 1;
X			break;
X		case 'S':
X		case 's':
X			fUseHards = 0;
X			break;
X		default:
X			continue;
X		}
X		break;
X	}
X
X	/* should we auto-save manual source?
X	 */
X	for (;;) {
X		printf("\nKeep all manual source files? [%s] ", HAVE_A(pcMan)? "yn" : "ny");
X		if ((char *)0 == GetWord(acAns, MAXANS, HAVE_A(pcMan) ? "yes" : "no"))
X			exit(0);
X		switch (acAns[0]) {
X		case 'q':
X		case 'Q':
X			exit(0);
X		case 'N':
X		case 'n':
X			pcMan = (char *)0;
X			break;
X		case 'y':
X		case 'Y':
X			if ((char *)0 == pcMan)
X				pcMan = "man";
X			printf("Prefix for saved manual directories? [%s] ", pcMan);
X			if ((char *)0 == GetWord(acDefMan, MAXPATHLEN, pcMan))
X				exit(0);
X			pcMan = acDefMan;
X			break;
X		default:
X			continue;
X		}
X		break;
X	}
X
X	/* ask about OK base names
X	 */
X	printf("\nNames of pages that appear multiple times in a single section are common\nbasenames, like `intro\' (intro.3c, intro.3x,...)\n");
X	for (;;) {
X		auto char acBase[MAXPATHLEN+1];
X
X		srtgti(& STBase);
X		if ((char *)0 != (pc = srtgets(& STBase))) {
X			printf("These are the current common basenames:\n");
X			do
X				printf("\t%s\n", pc);
X			while ((char *)0 != (pc = srtgets(& STBase)));
X		} else {
X			printf("There are no current common basenames for manual pages\n");
X		}
X		printf("\nAdd, Clear all, Delete, Exit? [EADC] ");
X		if ((char *)0 == GetWord(acAns, MAXANS, HAVE_A(pcMan) ? "yes" : "no"))
X			exit(0);
X		switch (acAns[0]) {
X		case 'q':
X		case 'Q':
X			exit(0);
X		case 'e':
X		case 'E':
X			break;
X		case 'c':
X		case 'C':
X			srtdtree(& STBase);
X			continue;
X		case 'D':
X		case 'A':
X			acAns[0] = tolower(acAns[0]);
X			/* fall through */
X		case 'd':
X		case 'a':
X			printf("Base to %s? [intro] ", 'a' == acAns[0] ? "add" : "delete");
X			if ((char *)0 == GetWord(acBase, MAXPATHLEN, "intro"))
X				exit(0);
X			if ('a' == acAns[0]) {
X				if (NULL == srtin(&STBase, acBase, strcmp)) {
X					printf("Out of memory?\n");
X					exit(2);
X				}
X				continue;
X			}
X			if (!srtdel(&STBase, acBase, strcmp)) {
X				printf("Base `%s\' not found\n", acBase);
X			}
X			continue;
X		}
X		break;
X	}
X
X	/* ask about modes */
X	printf("\n%s\n", acEditBlurb);
X	if (!fDidDir) {
X		EditModes(DIROWNER, DIRGROUP, DIRMODE, "all directories");
X	}
X	if ((char *)0 != apcModes[DIROWNER] && (char *)0 == apcModes[PAGEOWNER])
X		apcModes[PAGEOWNER] = apcModes[DIROWNER];
X	if ((char *)0 != apcModes[DIRGROUP] && (char *)0 == apcModes[PAGEGROUP])
X		apcModes[PAGEGROUP] = apcModes[DIRGROUP];
X	EditModes(PAGEOWNER, PAGEGROUP, PAGEMODE, "cat pages");
X
X	if ((char *)0 != apcModes[PAGEOWNER] && (char *)0 == apcModes[WHATISOWNER])
X		apcModes[WHATISOWNER] = apcModes[PAGEOWNER];
X	if ((char *)0 != apcModes[PAGEGROUP] && (char *)0 == apcModes[WHATISGROUP])
X		apcModes[WHATISGROUP] = apcModes[PAGEGROUP];
X	EditModes(WHATISOWNER, WHATISGROUP, WHATISMODE, "the whatis database");
X
X	if (HAVE_A(pcMan)) {
X		EditModes(MANOWNER, MANGROUP, MANMODE, "saved manual sources");
X	}
X
X	/* ask about OK extensions ZZZ */
X
X	/* write what we have */
X	for (;;) {
X		printf("\nInstall this configuration in %s? [yn] ", acConfig);
X		if ((char *)0 == GetWord(acAns, MAXANS, "yes"))
X			exit(0);
X		switch (acAns[0]) {
X		case 'q':
X		case 'Q':
X		case 'N':
X		case 'n':
X			exit(0);
X		case 'y':
X		case 'Y':
X			break;
X		default:
X			continue;
X		}
X		break;
X	}
X	(void)strcpy(acTCnf, "/tmp/catcnfXXXXXX");
X	if ((char *)0 == mktemp(acTCnf)) {
X		fprintf(stderr, "%s: mktemp: %s\n", progname, acTCnf);
X		exit(3);
X	}
X	if (NULL == (fpOpts = fopen(acTCnf, "w"))) {
X		fprintf(stderr, "%s: fopen: %s: %s\n", progname, acConfig, strerror(errno));
X		exit(2);
X	}
X	pcUser = getenv("USER");
X	if ((char *)0 == pcUser || '\000' == pcUser[0]) {
X		pcUser = getenv("LOGNAME");
X	}
X	if ((char *)0 == pcUser || '\000' == pcUser[0]) {
X		pcUser = "an unknown user";
X	}
X
X	fprintf(fpOpts, "# mkcat options updated by %s\n", pcUser);
X	fprintf(fpOpts, "compress: %c\nlinks: %c\n",
X		fCompress ? 'y' : 'n',
X		fUseHards ? 'H' : 'S');
X	if (HAVE_A(pcMan)) {
X		fprintf(fpOpts, "man: %s\n", pcMan);
X	}
X	fprintf(fpOpts, "# Add {page,dir,man,whatis}{mode,owner,group}: value\n");
X	{	static char *apcHead[4], *apcTail[3];
X		register int t;
X
X		/* This code keeps #define renumberings from hurting us
X		 * (have care here, the ice can be thin).
X		 */
X		apcHead[PAGEOWNER/3] = acPKey;
X		apcHead[DIROWNER/3] = acDKey;
X		apcHead[WHATISOWNER/3] = acWKey;
X		apcHead[MANOWNER/3] = acNKey;
X		/* see which order stats are kept
X		 */
X		t = DIROWNER < DIRMODE && DIROWNER < DIRGROUP ?
X			DIROWNER :
X		    DIRMODE < DIRGROUP ?
X			DIRMODE : DIRGROUP;
X		apcTail[DIROWNER-t] = acOKey;
X		apcTail[DIRGROUP-t] = acGKey;
X		apcTail[DIRMODE-t] = acMKey;
X		for (t = 0; t < MAXMODE+1; ++t) {
X			if ((char *)0 == apcModes[t])
X				continue;
X			fprintf(fpOpts, "%s%s %s\n", apcHead[t/3], apcTail[t % 3], apcModes[t]);
X		}
X	}
X	fprintf(fpOpts, "# these base names may exist under all externders\n");
X	fprintf(fpOpts, "# like intro.3m, intro.3x, intro.3s, etc.\n");
X	srtgti(& STBase);
X	while ((char *)0 != (pc = srtgets(& STBase)))
X		fprintf(fpOpts, "%s %s\n", acBKey, pc);
X	fprintf(fpOpts, "# these extenders might have common basenames\n");
X	fprintf(fpOpts, "# e.g.: {3f,3s} for getc\n");
X	for (pLI = pLIExt; 0 != pLI; pLI = pLI->pLInext) {
X		fprintf(fpOpts, "%s ", acSKey);
X		chSep = '{';
X		srtgti(& pLI->STall);
X		while ((char *)0 != (pc = srtgets(& pLI->STall))) {
X			fprintf(fpOpts, "%c%s", chSep, pc);
X			chSep = ',';
X		}
X		fprintf(fpOpts, "}\n");
X	}
X	(void)fclose(fpOpts);
X
X	if (!CmpFile(acTCnf, acConfig)) {
X		InstFile(acTCnf, acConfig, WHATISOWNER, WHATISGROUP, WHATISMODE, (char *)0);
X	}
X
X	/* ask to build cat[1-9] */
X	if ((char *)0 != pcCat) {
X		auto char acNCat[MAXPATHLEN+2];
X		register int i;
X
X		for (i = 1; i < 10; ++i) {
X			if ('/' == pcCat[0] || ('.' == pcCat[0] && '/' == pcCat[1]))
X				(void)sprintf(acNCat, "%s%1d", pcCat, i);
X			else
X				(void)sprintf(acNCat, "%s/%s%1d", pcRoot, pcCat, i);
X			MakeDir(acNCat, DIROWNER, DIRGROUP, DIRMODE);
X		}
X	}
X	/* ask to build man[1-9] */
X	if (HAVE_A(pcMan)) {
X		auto char acNMan[MAXPATHLEN+2];
X		register int i;
X
X		for (i = 1; i < 10; ++i) {
X			if ('/' == pcMan[0] || ('.' == pcMan[0] && '/' == pcMan[1]))
X				(void)sprintf(acNMan, "%s%1d", pcMan, i);
X			else
X				(void)sprintf(acNMan, "%s/%s%1d", pcRoot, pcMan, i);
X			MakeDir(acNMan, DIROWNER, DIRGROUP, DIRMODE);
X		}
X	}
X	if (fNew || fCompress != fOldCompress) {
X		for (;;) {
X			printf("\nCompression flag changed! Use `%s -Z\' to restore order? [yn] ", progname);
X			if ((char *)0 == GetWord(acAns, MAXANS, "yes"))
X				exit(0);
X			switch (acAns[0]) {
X			case 'q':
X			case 'Q':
X				exit(0);
X			case 'N':
X			case 'n':
X				break;
X			case 'y':
X			case 'Y':
X				switch (fork()) {
X				case -1:
X					fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
X					break;
X				case 0:
X					/* execlp(progname, "(mkcat)", fVerbose ? "-vL" : "-L", (char *)0); */
X					fJustComp = 1;
X					fUseHards = fOldUseHards;
X					doOthers(0, &progname);
X					exit(0);
X				default:
X					break;
X				}
X				while (-1 != wait((int *)0))
X					;
X				break;
X			default:
X				continue;
X			}
X			break;
X		}
X	}
X	if (fNew || fUseHards != fOldUseHards) {
X		for (;;) {
X			printf("\nLink type changed! Use `%s -L\' to relink pages? [yn] ", progname);
X			if ((char *)0 == GetWord(acAns, MAXANS, "yes"))
X				exit(0);
X			switch (acAns[0]) {
X			case 'q':
X			case 'Q':
X				exit(0);
X			case 'N':
X			case 'n':
X				break;
X			case 'y':
X			case 'Y':
X				switch (fork()) {
X				case -1:
X					fprintf(stderr, "%s: fork: %s\n", progname, strerror(errno));
X					break;
X				case 0:
X					/* execlp(progname, "(mkcat)", fVerbose ? "-vH" : "-H", (char *)0); */
X					fMkLinks = 1;
X					doOthers(0, &progname);
X					exit(0);
X				default:
X					break;
X				}
X				while (-1 != wait((int *)0))
X					;
X				break;
X			default:
X				continue;
X			}
X			break;
X		}
X	}
}
X
/*
X * mkcat -- install a manual page in the std place			(ksb)
X */
int
doMkCat(argc, argv)
int argc;
char **argv;
{
X	auto int iErrors, i;
X	auto WHATIS *pWUAdd, *pWUDel;
X	auto PATH *pPTNames;
X	auto char acDir[MAXPATHLEN+2];
X	auto struct stat stSrc, stDest;
X
X	(void)umask(0022);
X	if (fInitNew) {
X		doInit();
X		exit(0);
X	}
X
X	PrepInfo();
X
X	if (fVersion) {
X		doShowCfg();
X		exit(0);
X	}
X	if (fGenInstck) {
X		doGen();
X		exit(0);
X	}
X
X	fInstall |= !(fMkWhatis||fCkAlso||fMkLinks||fJustComp) && !fDelete;
X	if (!fInstall && !fDelete) {
X		exit(doOthers(argc, argv));
X	}
X	if (0 == argc) {
X		fprintf(stderr, "%s: missing pages (see -h)\n", progname);
X		exit(0);
X	}
X
X	iErrors = 0;
X	if ((PATH *)0 == (pPTNames = (PATH *)calloc((unsigned)argc+2, sizeof(PATH)))) {
X		fprintf(stderr, acNoMem, progname);
X		exit(1);
X	}
X	if ((WHATIS *)0 == (pWUAdd = (WHATIS *)calloc((unsigned)argc+2, sizeof(WHATIS)))) {
X		fprintf(stderr, acNoMem, progname);
X		exit(1);
X	}
X
X	if ((WHATIS *)0 == (pWUDel = (WHATIS *)calloc((unsigned)argc+2, sizeof(WHATIS)))) {
X		fprintf(stderr, acNoMem, progname);
X		exit(1);
X	}
X
X	/* format or read the pages and read the links out of them
X	 */
X	for (i = 0; i < argc; ++i) {
X		PTInit(&pPTNames[i], argv[i]);
X		if (-1 == stat(argv[i], & stSrc)) {
X			fprintf(stderr, "%s: stat: %s: %s\n", progname, argv[i], strerror(errno));
X			continue;
X		}
X		iErrors += ModFmt(&pPTNames[i], &pWUAdd[i], &pWUDel[i]);
X
X		if (!HAVE_A(pcMan) || !fFormat) {
X			continue;
X		}
X
X		if ('/' == pcMan[0] || ('.' == pcMan[0] && '/' == pcMan[1])) {
X			sprintf(acDir, "%s%d", pcMan, pWUAdd[i].isection);
X		} else {
X			sprintf(acDir, "%s/%s%d", pcRoot, pcMan, pWUAdd[i].isection);
X		}
X		if (-1 == stat(acDir, &stDest)) {
X			MakeDir(acDir, DIROWNER, DIRGROUP, DIRMODE);
X		}
X
X		(void)strcat(acDir, "/");
X		(void)strcat(acDir, pWUAdd[i].pcbase);
X		(void)strcat(acDir, ".");
X		(void)strcat(acDir, pWUAdd[i].pcext);
X		if (-1 != stat(acDir, &stDest) && stDest.st_ino == stSrc.st_ino && stDest.st_dev == stSrc.st_dev) {
X			fprintf(stdout, "%s: given file is installed in %s\n", progname, acDir);
X			continue;
X		}
X		if (fDelete) {
X			InstFile(acDir, (char *)0, MANOWNER, MANGROUP, MANMODE, "-R");
X		} else {
X			InstFile(argv[i], acDir, MANOWNER, MANGROUP, MANMODE, "-c");
X		}
X	}
X
X	/* update the whatis database with all the pages
X	 * (this wastes the complex data struct we just built)
X	 */
X	if (argc - iErrors > 0) {
X		iErrors += ModWhatis(argc, pWUAdd, argc, pWUDel);
X	}
X
X	return iErrors;
}
Purdue
chmod 0444 mkcat/mkcat.c ||
echo 'restore of mkcat/mkcat.c failed'
Wc_c="`wc -c < 'mkcat/mkcat.c'`"
test 32072 -eq "$Wc_c" ||
	echo 'mkcat/mkcat.c: original size 32072, current size' "$Wc_c"
fi
true || echo 'restore of mk/mk.c failed'
echo End of part 1, continue with part 2
exit 0

exit 0 # Just in case...
-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.