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

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

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

#!/bin/sh
# This is part 04 of pucc-1c
# ============= mkcat/pt.c ==============
if test ! -d 'mkcat'; then
    echo 'x - creating directory mkcat'
    mkdir 'mkcat'
fi
if test -f 'mkcat/pt.c' -a X"$1" != X"-c"; then
	echo 'x - skipping mkcat/pt.c (File already exists)'
else
echo 'x - extracting mkcat/pt.c (Text)'
sed 's/^X//' << 'Purdue' > 'mkcat/pt.c' &&
/*
X * an abstraction for a UNIX path name
X * $Id: pt.c,v 3.1 90/11/28 09:44:01 ksb Exp $
X *
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 * pull a path in it's parts, bent toward what I needed a little	(ksb)
X *
X * $Compile: ${cc-cc} -DTEST ${cc_debug--g} %f -o %F
X * $Cc: ${cc-cc} ${cc_debug--O} -c %f
X */
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/param.h>
X
#if !defined(MAXPATHLEN)
#define MAXPATHLEN	1024
#endif
X
#include "machine.h"
#include "pt.h"
X
extern int strlen();
extern char *strrchr(), *strcpy();
X
static char acDotZ[] =		/* compresses extender			*/
X	".Z";
X
/*
X * build a path structure, a nice abstraction for a UNIX file name	(ksb)
X * if you pass pcFile a a null you'd better have filled acname with a
X * path name...  /a//b is OK now too (/a & b)
X */
void
PTInit(pPT, pcFile)
PATH *pPT;
char *pcFile;
{
X	register char *pcScan;
X	if ((char *)0 != pcFile) {
X		(void)strcpy(pPT->acname, pcFile);
X	}
X	pcScan = pPT->acname;
X	while ((char *)0 != (pPT->pcdir = strrchr(pcScan, '/'))) {
X		pcScan = pPT->pcdir+1;
X		if ('\000' != pcScan[1])
X			break;
X		*pcScan = '\000';
X		pcScan = pPT->acname;
X	}
X	while (pPT->pcdir > pPT->acname && '/' == pPT->pcdir[-1]) {
X		--pPT->pcdir;
X	}
X	pPT->pcbase = pcScan;
X	if ((char *)0 == (pPT->pccomp = strrchr(pcScan, acDotZ[0])) ||
X	    0 != strcmp(acDotZ, pPT->pccomp)) {
X		pPT->pccomp = (char *)0;
X		pPT->istate = PS_NONE;
X	} else {
X		*pPT->pccomp = '\000';
X		pPT->istate = PS_COMP;
X	}
X	pPT->pcext = strrchr(pcScan, '.');
}
X
/*
X * return the local path name of the file (no directory part)		(ksb)
X */
char *
PTLocal(pPT)
PATH *pPT;
{
X	if ((char *)0 != pPT->pcext && 0 != (pPT->istate & PS_EXT)) {
X		pPT->istate &= ~PS_EXT;
X		*pPT->pcext = '.';
X	}
X	if ((char *)0 != pPT->pccomp && 0 != (pPT->istate & PS_COMP)) {
X		pPT->istate &= ~PS_COMP;
X		*pPT->pccomp = acDotZ[0];
X	}
X	return pPT->pcbase;
}
X
/*
X * return the full path name 						(ksb)
X */
char *
PTFull(pPT)
PATH *pPT;
{
X	(void) PTLocal(pPT);
X	if ((char *)0 != pPT->pcdir && 0 != (pPT->istate & PS_DIR)) {
X		pPT->istate &= ~PS_DIR;
X		*pPT->pcdir = '/';
X	}
X	return pPT->acname;
}
X
static char acDot[] = ".";
static char acSlash[] = "/";
X
/*
X * return the directory prefix of the file name				(ksb)
X * handles no dir part (.), and /foo (/)
X * you can check for these cases with a pointer compare (acDot, acSlash)
X */
char *
PTDir(pPT)
PATH *pPT;
{
X	if ((char *)0 == pPT->pcdir) {
X		return acDot;
X	}
X	if (pPT->pcdir == pPT->acname) {
X		return acSlash;
X	}
X	if (0 == (pPT->istate & PS_DIR)) {
X		pPT->istate |= PS_DIR;
X		*pPT->pcdir = '\000';
X	}
X	return pPT->acname;
}
X
/*
X * return the base name, no extender					(ksb)
X */
char *
PTBase(pPT)
PATH *pPT;
{
X	if ((char *)0 != pPT->pcext) {
X		if (0 == (pPT->istate & PS_EXT)) {
X			pPT->istate |= PS_EXT;
X			*pPT->pcext = '\000';
X		}
X	} else if ((char *)0 != pPT->pccomp && 0 == (pPT->istate & PS_COMP)) {
X		pPT->istate |= PS_COMP;
X		*pPT->pccomp = '\000';
X	}
X	return pPT->pcbase;
}
X
/*
X * return the exender on the path					(ksb)
X */
char *
PTExt(pPT)
PATH *pPT;
{
X	if ((char *)0 != pPT->pccomp && 0 == (pPT->istate & PS_COMP)) {
X		pPT->istate |= PS_COMP;
X		*pPT->pccomp = '\000';
X	}
X	return (char *)0 != pPT->pcext ? pPT->pcext + 1 : "";
}
X
/*
X * make the file compress'd (add a .Z) return if there was one		(ksb)
X */
int
PTComp(pPT)
PATH *pPT;
{
X	if ((char *)0 != pPT->pccomp)
X		return 1;
X	pPT->pccomp = PTLocal(pPT);
X	pPT->pccomp += strlen(pPT->pccomp);
X	(void)strcpy(pPT->pccomp, acDotZ);
X	return 0;
}
X
/*
X * make the file name be uncompressed (remove the .Z)			(ksb)
X * return 1 if the file *was* uncompressed
X */
int
PTUnComp(pPT)
PATH *pPT;
{
X	if ((char *)0 == pPT->pccomp)
X		return 1;
X	if (0 == (pPT->istate & PS_COMP))
X		*pPT->pccomp = '\000';
X	pPT->pccomp = (char *)0;
X	return 0;
}
X
X
#if defined(TEST)
X
/* Crack, simple configuration file parser				(ksb)
X */
char *
Crack(pcText, ppcVar)
char *pcText;
char **ppcVar;
{
X	register char ***pppc;
X	register char **ppc;
X
X	pppc = & ppcVar;
X	while ((char **)0 != (ppc = *pppc++)) {
X		if ('\000' == *pcText) {
X			*ppc = (char *)0;
X			continue;
X		}
X		*ppc = pcText;
X		while (!isspace(*pcText) && '\000' != *pcText)
X			++pcText;
X		if (isspace(*pcText)) {
X			*pcText++ = '\000';
X		}
X		while (isspace(*pcText))
X			++pcText;
X	}
X	return pcText;
}
X
extern int errno;
extern char *sys_errlist[];
#define strerror(Me) (sys_errlist[Me])
X
X
/*
X * test driver for path splitting part					(ksb)
X */
int
main(argc, argv)
int argc;
char **argv;
{
X	auto char acLine[1045], *pc;
X	auto char *pcOrig, *pcDir, *pcLocal, *pcBase, *pcExt, *pcComp, *pcComm;
X	auto PATH PTTest;
X	register int e = 0;
X
X	switch (argc) {
X	case 1:
X		break;
X	case 2:
X		if (NULL == freopen(argv[1], "r", stdin)) {
X			fprintf(stderr, "%s: freopen: %s: %s\n", argv[0], argv[1], strerror(errno));
X			exit(1);
X		}
X		break;
X	default:
X		printf("%s: usage [file]\n", argv[0]);
X		exit(1);
X	}
X
X	while (NULL != gets(acLine)) {
X		pcOrig = acLine;
X		while (isspace(*pcOrig))
X			++pcOrig;
X		if ('#' == *pcOrig || '\000' == *pcOrig)
X			continue;
X		pcComm = Crack(pcOrig, &pcOrig, &pcDir, &pcLocal, &pcBase, &pcExt, &pcComp, (char **)0);
X		PTInit(pcOrig, & PTTest);
X		if (0 != strcmp(pcOrig, (pc = PTFull(& PTTest)))) {
X			printf("pt: Full: `%s\' is broken, `%s\' != `%s\'\n", pcOrig, pcOrig, pc);
X			++e;
X		}
X		if (0 != strcmp(pcDir, (pc = PTDir(& PTTest)))) {
X			printf("pt: Dir: `%s\' is broken, `%s\' != `%s\'\n", pcOrig, pcDir, pc);
X			++e;
X		}
X		if (0 != strcmp(pcLocal, (pc = PTLocal(& PTTest))) && '~' != pcLocal[0] && '\000' != pc[0]) {
X			printf("pt: Local: `%s\' is broken, `%s\' != `%s\'\n", pcOrig, pcLocal, pc);
X			++e;
X		}
X		if (0 != strcmp(pcBase, (pc = PTBase(& PTTest))) && '~' != pcBase[0] && '\000' != pc[0]) {
X			printf("pt: Base: `%s\' is broken, `%s\' != `%s\'\n", pcOrig, pcBase, pc);
X			++e;
X		}
X		if (0 != strcmp(pcExt, (pc = PTExt(& PTTest))) && '~' != pcExt[0] && '\000' != pc[0]) {
X			printf("pt: Ext: `%s\' is broken, `%s\' != `%s\'\n", pcOrig, pcExt, pc);
X			++e;
X		}
X		if (PTIsComp(&PTTest) != ('y' == *pcComp || 'Y' == *pcComp)) {
X			printf("pt: IsComp: broken\n", pcOrig);
X			++e;
X		}
X		if (0 != strcmp(pcOrig, (pc = PTFull(& PTTest)))) {
X			printf("pt: Full: `%s\' is broken, `%s\' != `%s\'\n", pcOrig, pcOrig, pc);
X			++e;
X		}
X		if (0 != strcmp(pcBase, (pc = PTBase(& PTTest))) && '~' != pcBase[0] && '\000' != pc[0]) {
X			printf("pt: Base: `%s\' is broken, `%s\' != `%s\'\n", pcOrig, pcBase, pc);
X			++e;
X		}
X		if (0 != strcmp(pcLocal, (pc = PTLocal(& PTTest))) && '~' != pcLocal[0] && '\000' != pc[0]) {
X			printf("pt: Local: `%s\' is broken, `%s\' != `%s\'\n", pcOrig, pcLocal, pc);
X			++e;
X		}
X		if (0 != strcmp(pcDir, (pc = PTDir(& PTTest)))) {
X			printf("pt: Dir: `%s\' is broken, `%s\' != `%s\'\n", pcOrig, pcDir, pc);
X			++e;
X		}
X		if (0 != strcmp(pcOrig, (pc = PTFull(& PTTest)))) {
X			printf("pt: Full: `%s\' is broken, `%s\' != `%s\'\n", pcOrig, pcOrig, pc);
X			++e;
X		}
X	}
X
X	exit(e != 0);
}
X
#endif	/* test driver code */
Purdue
chmod 0444 mkcat/pt.c ||
echo 'restore of mkcat/pt.c failed'
Wc_c="`wc -c < 'mkcat/pt.c'`"
test 7922 -eq "$Wc_c" ||
	echo 'mkcat/pt.c: original size 7922, current size' "$Wc_c"
fi
# ============= mk/rlimsys.c ==============
if test ! -d 'mk'; then
    echo 'x - creating directory mk'
    mkdir 'mk'
fi
if test -f 'mk/rlimsys.c' -a X"$1" != X"-c"; then
	echo 'x - skipping mk/rlimsys.c (File already exists)'
else
echo 'x - extracting mk/rlimsys.c (Text)'
sed 's/^X//' << 'Purdue' > 'mk/rlimsys.c' &&
/*
X * resource limited system	Kevin S Braunsdorf		(ksb)
X */
#include "machine.h"
X
#if RESOURCE
X
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#if defined(bsd)
#include <sys/wait.h>
#endif
#include <signal.h>
#include <stdio.h>
#include <ctype.h>
X
#include "mk.h"
#include "rlimsys.h"
X
X
/*
X * resource records
X */
typedef struct RRnode {
X	int rtype;		/* resource type from resource.h	*/
X	int fsys;		/* done by kernel or us?		*/
X	char *pchrname;		/* user name				*/
X	int rlen;		/* lenght of user name			*/
X	int fset;		/* was given by user?			*/
X	struct rlimit ourlim;	/* rlim_cur, rlim_max			*/
} RES_REC;
X
#if RLIM_NLIMITS != 6
error_more_or_fewer_resources error_more_or_fewer_resources
#endif
X
#define MY_CLOCK	0
/*
X * resources we know about in alpha order
X */
RES_REC sbRRLimits[] = {
X	{ MY_CLOCK,	0, "clock",	5 },
X	{ RLIMIT_CORE,	1, "core",	4 },
X	{ RLIMIT_CPU,	1, "cpu",	3 },
X	{ RLIMIT_DATA,	1, "data",	4 },
X	{ RLIMIT_FSIZE,	1, "fsize",	5 },
X	{ RLIMIT_RSS,	1, "rss",	3 },
X	{ RLIMIT_STACK,	1, "stack",	5 }
};
X
/*
X * we need to know how many limits we can set
X */
#define MY_NLIMITS	7
X
X
/*
X * init the resource limit stuff, make mongo syscalls			(ksb)
X */
void
rinit()
{
X	register int i;
X	register RES_REC *pRR;
X
X	for (i = 0; i < MY_NLIMITS; ++i) {
X		pRR = & sbRRLimits[i];
X		pRR->fset = 0;
X		if (pRR->fsys) {
X			getrlimit(pRR->rtype, & pRR->ourlim);
X		} else {
X			pRR->ourlim.rlim_cur = RLIM_INFINITY;
X			pRR->ourlim.rlim_max = RLIM_INFINITY;
X		}
X	}
}
X
/*
X * parse a resource limit statement of the form				(ksb)
X *	<resource>=number_cur/number_max
X * (we eat leading blanks, as all good cvt routines should)
X */
char *
rparse(pchState)
register char *pchState;
{
X	extern char *strchr();
X	extern long atol();
X	register int i;
X	register char *pchTemp;
X	register RES_REC *pRR;
X	register int fSetMax = 0, fSetCur = 0;
X
X	while (isspace(*pchState)) {
X		++pchState;
X	}
X	if ((char *)0 == (pchTemp = strchr(pchState, '='))) {
X		return pchState;
X	}
X
X	for (i = 0; i < MY_NLIMITS; ++i) {
X		pRR = & sbRRLimits[i];
X		if (0 == strncmp(pRR->pchrname, pchState, pRR->rlen)) {
X			break;
X		}
X	}
X	if (MY_NLIMITS == i) {
X		return pchState;
X	}
X
X	do {
X		++pchTemp;
X	} while (isspace(*pchTemp));
X	if (isdigit(*pchTemp)) {
X		pRR->fset = 1;
X		pRR->ourlim.rlim_cur = atol(pchTemp);
X		fSetCur = 1;
X		do {
X			++pchTemp;
X		} while (isdigit(*pchTemp));
X	}
X	if ('/' == *pchTemp)
X		++pchTemp;
X	while (isspace(*pchTemp))
X		++pchTemp;
X
X	if (isdigit(*pchTemp)) {
X		pRR->fset = 1;
X		pRR->ourlim.rlim_max = atol(pchTemp);
X		fSetMax = 1;
X		do {
X			++pchTemp;
X		} while (isdigit(*pchTemp));
X	}
X	while (isspace(*pchTemp))
X		++pchTemp;
X	if (fSetMax != fSetCur) {	/* only one set, set both same	*/
X		if (fSetMax) {
X			pRR->ourlim.rlim_cur = pRR->ourlim.rlim_max;
X		} else {
X			pRR->ourlim.rlim_max = pRR->ourlim.rlim_cur;
X		}
X	}
X	return pchTemp;
}
X
int r_fTrace = 0;
static int iChildPid, fWarned;
X
/*
X * trap a time out for a "real time" alarm
X */
int
do_alarm()
{
X	register unsigned next;
X	if (kill(iChildPid, fWarned++ ? SIGALRM : SIGKILL) < 0) {
X		fprintf(stderr, "%s: %d: ", "rlimsys", iChildPid);
X		perror("kill");
X		exit(1);
X	}
X	next = (unsigned)(sbRRLimits[MY_CLOCK].ourlim.rlim_max -
X		sbRRLimits[MY_CLOCK].ourlim.rlim_cur);
X	if (0 == next) {
X		next = 2;
X	}
X	alarm(next);
}
X
/*
X * emulate "system" with a CPU limit
X */
int
rlimsys(pchCmd)
char *pchCmd;
{
#if defined(bsd)
X	auto union wait waitbuf;
#else
X	auto int waitbuf;
#endif
X	register int wret;
#if SUNOS >= 40
X	register void (*atrap)(), (*itrap)(), (*qtrap)();
#else /* !SUNOS40 */
X	register int (*atrap)(), (*itrap)(), (*qtrap)();
#endif /* SUNOS40 */
X	register int i;
X	register RES_REC *pRR;
X	auto int iRetCode;
X
X	iChildPid = vfork();
X	switch (iChildPid) {
X	case 0:
X		for (i = 0; i < MY_NLIMITS; ++i) {
X			pRR = & sbRRLimits[i];
X			if (pRR->fset && pRR->fsys) {
X				setrlimit(pRR->rtype, & pRR->ourlim);
X			}
X		}
X		execl("/bin/sh", "sh", r_fTrace ? "-cx" : "-c", pchCmd, 0);
X		_exit(127);
X	case -1:
X		perror("rlimsys: fork");
X		return 1;
X	default:
X		break;
X	}
X
X	itrap = signal(SIGINT, SIG_IGN);
X	qtrap = signal(SIGQUIT, SIG_IGN);
X
X	if (sbRRLimits[MY_CLOCK].fset) {
X		atrap = signal(SIGALRM, do_alarm);
X		alarm((unsigned)sbRRLimits[MY_CLOCK].ourlim.rlim_cur);
X	}
X	for (;;) {
X		if (iChildPid == (wret = wait3(& waitbuf, WUNTRACED, (struct rusage *)0))) {
X			if (WIFSTOPPED(waitbuf)) {
X				if (kill(iChildPid, SIGCONT) < 0) {
X					break;
X				}
X				continue;
X			}
X			if (WIFSIGNALED(waitbuf)) {
X				iRetCode = waitbuf.w_termsig;
X			} else {
X				iRetCode = waitbuf.w_retcode;
X			}
X			break;
X		}
X		if (wret == -1) {
X			fprintf(stderr, "rlimsys: lost child %d\n", iChildPid);
X			perror("rlimsys: wait");
X			iRetCode = 1;
X			break;
X		}
X	}
X
X	(void) signal(SIGINT, itrap);
X	(void) signal(SIGQUIT, qtrap);
X	if (sbRRLimits[MY_CLOCK].fset) {
X		alarm((unsigned)0);
X		(void) signal(SIGALRM, atrap);
X	}
X	return iRetCode;
}
X
#endif /* resource usage */
Purdue
chmod 0444 mk/rlimsys.c ||
echo 'restore of mk/rlimsys.c failed'
Wc_c="`wc -c < 'mk/rlimsys.c'`"
test 4889 -eq "$Wc_c" ||
	echo 'mk/rlimsys.c: original size 4889, current size' "$Wc_c"
fi
# ============= mk-lib/mk.5l ==============
if test ! -d 'mk-lib'; then
    echo 'x - creating directory mk-lib'
    mkdir 'mk-lib'
fi
if test -f 'mk-lib/mk.5l' -a X"$1" != X"-c"; then
	echo 'x - skipping mk-lib/mk.5l (File already exists)'
else
echo 'x - extracting mk-lib/mk.5l (Text)'
sed 's/^X//' << 'Purdue' > 'mk-lib/mk.5l' &&
.\" how to code an mk marker line
.TH MK 5L LOCAL
.SH NAME
mk \- how to write and embed \fBmk\fP commands
.SH SYNOPSIS
\fB$\fP \fImarker\fP[\fB(\fP\fIsubmarker\fP\fB)\fP] [\fB=\fP[\fB~\fP]\fIexit\-status\fP] [\fB,\fP\fIresource\fP\fB=\fP[\fIlimit\fP][\fB/\fP\fImaximum\fP]] \fB:\fP \fIcommand\fP [ \fB$$\fP ]
.SH COMMANDS
.I Mk
is a utility for detecting and executing shell commands within files.
Normally, when the named files contain source language statements,
the commands are contained in lines that appear as comments to
the language processor.
This is merely a convention, however, and is not a
.I mk
requirement.
.PP
.I Mk
commands are always given to \fIsh\fP(1) for execution.
It is the convention of the authors to use an indirect
name for the \*(lqkey\*(rq UNIX tools in each command.
For example when a command requires the use of a language processor,
like cc(1), one might use the shell form
.sp 1
X	${cc-cc} ...
.sp 1
so that different versions of the compiler (cc or gcc) or cross
compilers might be used in place of the default compiler.
.PP
Some compilers required a special optimization level for debugging.
A hook for this option is usually provided by a variable whose
name ends in the string \*(lq_debug\*(rq
.sp 1
X	${cc-cc} ${cc_debug-\-O} ...
.sp 1
This allows the user to simply set an environment variable when
debugging a product.
.PP
The default load name, \*(lqa.out\*(rq, should \fBnot\fP be used as an
output file, if it can be avoided,
(such default actions are available via the compiler itself).
.I Mk
rules generally use the basename of the given file as an output
file (see \fImk\fP(1l) for a description of all the percent escapes).
.sp 1
X	${cc-cc} ${cc_debug-\-O} \-o %F ...
.sp 1
.PP
If any special flags must be included to compile this program,
or several options cause different versions to be built they should
be specified next:
.sp 1
X	${cc-cc} ${cc_debug-\-O} \-o %F ...
.br
X	${cc-cc} ${cc_debug-\-g} \-o %F \-DDEBUG ...
.sp 1
.PP
The name of the source file should be followed by any libraries
that the product needs to load against.
.sp 1
X	${cc-cc} ${cc_debug-\-O} \-o %F \-DNORMAL %f \-lm
.sp 1
.PP
Sometimes recursive calls to \fImk\fP are the best way to
solve a complex problem.
The escapes \fImk\fP provides for recursive calls (%b, %o, %\fIoption\fP)
are used to pass down the command line switches that might be important:
.sp 1
X	%b %o \-mStep1 %f && %b %o \-mStep2 %f
.sp 1
Sometimes one might want to force an option to a recursive call, but
leave the others as given (in this case we force \-a):
.sp 1
X	%b \-a%C%I%N%V \-m%m %f
.sp 1
.SH "MARKED LINES"
.PP
The default \fImarker\fP \fBmk\fP searches for is \*(lqCompile\*(rq.
A marker is, in effect, a verb that is applied to a file, these are
the markers \fImk\fP has conventions for (by default):
.sp 1
.RS
.TS
l l.
Clean	remove any junk this file might produce
Compile	produce an binary from this file
Display	produce an output from this file
Info	output a description of what \fBmk\fP thinks is in the file
Laser	format this file to produce a hard copy
Run	execute this file (if possible)
Mkcat	format a manual page for the \fImkcat\fP(8l) program
.TE
.RE
.sp 1
Other markers will be added as needed.
.PP
A submarker is a modifier on the verb.
These are used to choose marked lines when more than on marked
line might server.
Submarkers are still primitive.
.PP
Currently few submarkers have a defined meaning to \fImk\fP:
.RS
.TS
l l.
debug	generate a debugging version
test	generate a test program for this module
verbose	be more verbose
\fIos\fP	generate a version for the given operating system
X	SYSV, bsd, hpux, posix
.TE
.RE
.SH "MATCHING"
.PP
These rules cause a match for marked lines in any file \fImk\fP searches:
.RS
\(bu the special marker \*(lq*\*(rq matches any \fImarker\fP
.br
\(bu the special submarker \*(lq*\*(rq matches any \fIsubmarker\fP
.br
\(bu the marker from the line must match \fImarker\fP
.br
\(bu if a \fIsubmarker\fP was given on the command line it must be matched
.br
\(bu case differentiates markers and submarkers unless \-\fBi\fP was given
.br
.RE
.PP
Thus we have a table of \fImk\fP invokations versus markers:
.RS
.TS
l l l l
l c c c.
Marker	\-mTest	\-mTest \-ddebug	\-mTest -d'*'
$Fail	no	no	no
$Test	yes	no	no
$Test(bsd)	yes	no	yes
$Test(debug)	yes	yes	yes
$Test(*)	yes	yes	yes
$*	yes	no	no
$*(bsd)	yes	no	yes
$*(debug)	yes	yes	yes
$*(*)	yes	yes	yes
.TE
.RE
.PP
When coding a template file the $\fImarker\fP(*): form is used to
assure a match.
.SH TEMPLATES
.PP
By using \fImk\fP's template's options
the user might build a default rule for a given application.
\fIMk\fP will expand each element of the template path as if
it were a \fIcommand\fP, then try to read the generated filename.
If the file exists and contains a marked line that matches
the current marker \fImk\fP will use that command.
.PP
\fIMk\fP has a set of default templates under some directory
(likely \fI/usr/local/lib/mk\fP) these are scanned if no other
\-\fBe\fP or \-\fBt\fP options are given.
The root of this directory is provided by the %~ escape to
allow users to reference these templates.
.PP
The default list of templates may be examined with ``\fImk\fP \-\fBV\fP''.
.PP
The default \fImk\fP templates have a naming convention which also
determines the order in which they are searched.
.RS
.TS
l l.
type-%y	trap based on file type
pre-%x	trap extenders that represent nontext files
comma-%U	trap RCS files
file-%F	trap files with special names
dot-%x	trap text file extenders
m-%M	trap based on marker given
.TE
.RE
.SH FAILURE
.PP
Some markers can detect that they have failed and that the user might
want to take corrective or special action in this case.
The convention in such cases is to call the shell form:
.sp 1
X	${false-false}
.sp 1
to allow the user a ``hook'' with which to trap the error.
.SH EXAMPLES
.TP
$\&All: %b %o \-mCompile *.c
Compile all the C source files in this directory using their own
rules.
.TP
$\e&Compile: ...
A trick to avoid letting mk see a marked line in an examples
section of a manual page.
.TP
$\&Compile(debug): ${cc-cc} ${cc_debug-\-g} \-o %F \-DDEBUG %f \-lm
This line will match only if \-\fBd\fPdebug or \-\fBd\fP'*' were given.
.TP
$\&Compile(*): ${cc-cc} ${cc_debug-\-O} \-o %F %f \-lm
This line will match any default request.
.TP
$\&*(*): echo \'%B: %m: unknown marker\' && ${false-false}
This marked line will output an error message for any marker, and fail.
.SH BUGS
.PP
Use of \fIrcs\fP(1) keywords as markers
(Author, Date, Header, Id, Locker, Log, RCSfile, Revision, Source, State)
might give unexpected results.
.SH AUTHORS
S. McGeady, Intel, Inc., mcg@mipon2.intel.com
.sp 1
Kevin Braunsdorf, Purdue University Computing Center (ksb@cc.purdue.edu)
.SH "SEE ALSO"
mk(1l), sh(1),
printf(3)
Purdue
chmod 0444 mk-lib/mk.5l ||
echo 'restore of mk-lib/mk.5l failed'
Wc_c="`wc -c < 'mk-lib/mk.5l'`"
test 6777 -eq "$Wc_c" ||
	echo 'mk-lib/mk.5l: original size 6777, current size' "$Wc_c"
fi
# ============= mkcat/mkcat.8l ==============
if test -f 'mkcat/mkcat.8l' -a X"$1" != X"-c"; then
	echo 'x - skipping mkcat/mkcat.8l (File already exists)'
else
echo 'x - extracting mkcat/mkcat.8l (Text)'
sed 's/^X//' << 'Purdue' > 'mkcat/mkcat.8l' &&
.\" Copyright 1990 Purdue Research Foundation, West Lafayette, Indiana
.\" 47907.  All rights reserved.
.\"
.\" Written by Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb
.\"	       Jeff Smith, jsmith@cc.purdue.edu, purdue!jsmith
.\"
.\" This software is not subject to any license of the American Telephone
.\" and Telegraph Company or the Regents of the University of California.
.\"
.\" Permission is granted to anyone to use this software for any purpose on
.\" any computer system, and to alter it and redistribute it freely, subject
.\" to the following restrictions:
.\"
.\" 1. Neither the authors nor Purdue University are responsible for any
.\"    consequences of the use of this software.
.\"
.\" 2. The origin of this software must not be misrepresented, either by
.\"    explicit claim or by omission.  Credit to the authors and Purdue
.\"    University must appear in documentation and sources.
.\"
.\" 3. Altered versions must be plainly marked as such, and must not be
.\"    misrepresented as being the original software.
.\"
.\" 4. This notice may not be removed or altered.
.\"
.\" $Id: mkcat.8l,v 3.2 90/06/12 14:29:45 ksb Exp $
.\"
.\" $Laser: ${tbl-tbl} %f | ${ltroff-ltroff} -man
.\" $Compile: ${tbl-tbl} %f | ${nroff-nroff} -man | ${PAGER-${more-more}}
.TH MKCAT 8L LOCAL
.SH NAME
mkcat \- update and format a manual page
.SH SYNOPSIS
\fBmkcat\fP [-\fBDIfnsv\fP] [-\fBc\fP \fIcatdirs\fP] [-\fBm\fP \fIman\fP] [-\fBr\fP \fIroot\fP] [-\fBw\fP \fIdatabase\fP] \fIpages\fP
.br
\fBmkcat\fP [-\fBVLRWh\fP] [-\fBc\fP \fIcatdirs\fP] [-\fBm\fP \fIman\fP] [-\fBr\fP \fIroot\fP] [-\fBw\fP \fIdatabase\fP]
.SH DESCRIPTION
\fIMkcat\fP formats the given \fIpages\fP and installs them in the standard
manual structure.
\fIMkcat\fP updates the \fIwhatis\fP(1) database and creates links
to the installed page to represent alternate names for the page
given in the \fBNAME\fP section of the formatted page.
.PP
\fIMkcat\fP depends heavily on the \fBmk\fP(1l) program to extract
shell commands to format each page.
The \fImk\fP program extracts any special format directive from the first 99
lines of the manual page source file which are marked with the
\*(lqMkcat\*(rq marker.
See the examples below.
.PP
Under the \fB\-D\fP option \fImkcat\fP deletes antiquated pages.
.SH OPTIONS
.TP
.BI \-c catdirs
Specify \fIcatdirs\fP as the prefix for all the cat directories.
The default is \*(lqcat\*(rq which makes the standard manual system
directories \*(lqcat1\*(rq, \*(lqcat2\*(rq, \*(lqcat3\*(rq, ... etc.
These are used to store manual pages from the corresponding section of
the manual.
.TP
.BI \-D
Delete (rather than install) the given \fIpages\fP.
The associated \fIwhatis\fP(1) entries are removed as
well as any links.
.TP
.BI \-f
The given manual pages are already formatted, simply install (delete) them.
.TP
.BI \-h
Print a short help message.
.TP
.BI \-I
Install the given manual page and update the whatis database.
(This is the default action.)
.TP
.BI \-L
Only rebuild missing links to the cat pages.
.TP
.BI \-m man
When this option is specified \fImkcat\fP copies the manual page
source into a parallel \fIman\fP directory.
This mocks the old manual system.
.TP
.BI \-n
Do not really run any commands.
This option outputs the approximate actions of \fImkcat\fP in
shell commands.
.TP
.BI \-r root
Specify a user defined root for the manual system.
The default is \*(lq\fI/usr/man\fP\*(rq.
This allows users to make their own manual systems in their accounts.
.TP
.BI \-s
Do not output descriptions of the shell commands as they are run.
This is the default.
.TP
.BI \-v
Be verbose by displaying shell commands as they are run.
.TP
.BI \-V
Show the current configuration of the manual system.
.TP
.BI \-w database
Specify a \fIwhatis\fP(1) database to update.
The default \fIdatabase\fP is \*(lqwhatis\*(rq from the \fIroot\fP
of the manual system.
If this is a full path name \fIroot\fP is not prepended to it.
.TP
.BI \-R
Install a new root manual system, specify the new root with \-\fBr\fP.
If a options file exits in that directory it is used as the defaults
for the new system.
.TP
.BI \-W
Just rebuild the whatis database.
.TP
.BI \-Z
Verify that all the cat pages are (un)compressed.
.SH EXAMPLES
.TP
mkcat -v -r/usr/man -R
Install mkcat's configuration file in /usr/man.
.TP
mkcat ls.1
Update the \fIls\fP(1) manual page and \fIwhatis\fP(1) entry.
.TP
mkcat -cnew sleep.1 sleep.3
Update both \fIsleep\fP(1) manual pages and \fIwhatis\fP(1) entries
in the \*(lqnew\*(rq cat directory.
.TP
mkcat -D catman.8
Delete the outdated \fIcatman\fP(8) manual page.
.TP
mkcat -r $HOME/man myprog.1g
Update the \fImyprog\fP(1g) manual page in the user\'s own manual system.
X
.TP
\&.\e" $\&Mkcat: tbl %f | nroff -man.nopage
\fBUltrix\fP manual pages require the nopage macro package.
.TP
\&.\e" $\&Mkcat: neqn %f | tbl | nroff -man | colcrt
This line runs the \fIneqn\fP(1) processor in addition to \fItbl\fP(1) and
\fInroff\fP(1).
.TP
\&.\e" $\&Mkcat: nroff -mlocal %f | cat -s
This manual page uses a local macro package, and doesn't use \fItbl\fP.
.SH FILES
.TS
l l.
/usr/man/whatis	the default \fIwhatis\fP(1) database
/usr/man/cat[1-8]	the default cat directories
/usr/man/.mkcat-opts	the manual system configuration
.TE
.SH BUGS
.PP
Under the \fB\-n\fP option \fImkcat\fP cannot always predict the links
it would make.
.SH AUTHOR
Kevin Braunsdorf
.br
Purdue UNIX Group
.br
ksb@cc.purdue.edu, pur-ee!ksb, purdue!ksb
.SH "SEE ALSO"
apropos(1), colcrt(1), compress(1), man(1), mk(1l), neqn(1), nroff(1),
tbl(1), whatis(1),
catman(8)
Purdue
chmod 0444 mkcat/mkcat.8l ||
echo 'restore of mkcat/mkcat.8l failed'
Wc_c="`wc -c < 'mkcat/mkcat.8l'`"
test 5555 -eq "$Wc_c" ||
	echo 'mkcat/mkcat.8l: original size 5555, current size' "$Wc_c"
fi
# ============= mkcat/sym2hard.c ==============
if test -f 'mkcat/sym2hard.c' -a X"$1" != X"-c"; then
	echo 'x - skipping mkcat/sym2hard.c (File already exists)'
else
echo 'x - extracting mkcat/sym2hard.c (Text)'
sed 's/^X//' << 'Purdue' > 'mkcat/sym2hard.c' &&
/*
X * $Id: sym2hard.c,v 3.1 90/11/28 09:44:14 ksb Exp $
X *
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 code replaces a symbolic link with a hard link			(ksb)
X *
X * N.B. this is questionable code in any case....
X */
X
#include "machine.h"
X
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/file.h>
#include <sys/stat.h>
X
#include "main.h"
X
#if !defined(MAXPATHLEN)
#define MAXPATHLEN	1024
#endif
X
#if !defined(ELOOP)
#define ELOOP	ENOENT
#endif	/* ZZZZ ETA kludge ZZZZ */
X
#if HAVE_SLINKS
X
extern int errno;
extern char *sys_errlist[];
#define strerror(Me) sys_errlist[Me]
X
X
extern char *getwd();
typedef struct SRnode {
X	struct stat *pst;
X	struct SRnode *psr;
} STREC;
X
/*
X * copy the parrent directory of pcFile to pcDir, which is a buffer	(ksb)
X * given by the user. returns the `tail part'
X */
static char *
getparent(pcFile, pcDir)
char *pcFile, *pcDir;
{
X	register char *pcTail;
X	extern char *strcpy(), *strrchr();
X
X	if ((char *)0 == (pcTail = strrchr(pcFile, '/'))) {
X		(void)strcpy(pcDir, ".");
X		return pcFile;
X	} else if (pcFile == pcTail) {
X		(void)strcpy(pcDir, "/");
X		return pcFile+1;
X	}
X	*pcTail = '\000';
X	(void)strcpy(pcDir, pcFile);
X	*pcTail = '/';
X	return pcTail+1;
}
X
/*
X * do one level of sym2hard						(ksb)
X *
X * pcLink is a symbolic link that should be a hard link
X * (we convert all interveining symbolic links while we are at it)
X *
X * stat the symbolic link, is it a file? we are done.
X * check for loops, maybe return ELOOP
X * record pwd so we can get back
X * read the link into acRead
X * cd to acRead's parrent directory
X * recurse on acRead's tail
X * cd back
X * stat acRead, on the same device, and a plain file? not -> bomb out
X * remove pcLink, and re-link to acRead
X */
static int
sym1Hard(pcLink, pSRPrev)
char *pcLink;
STREC *pSRPrev;
{
X	auto char acRead[MAXPATHLEN+1];
X	auto char acNext[MAXPATHLEN+1];
X	auto char acPwd[MAXPATHLEN+1];
X	auto struct stat stLink, stRead;
X	auto STREC SR, *pSR;
X	register int i;
X	register char *pcTail;
X
X	if (0 != lstat(pcLink, & stLink)) {
X		fprintf(stderr, "%s: lstat: %s: %s\n", progname, pcLink, strerror(errno));
X		return -1;
X	}
X	for (pSR = pSRPrev; (STREC *)0 != pSR; pSR = pSR->psr) {
X		if (pSR->pst->st_dev == stLink.st_dev &&
X		    pSR->pst->st_ino == stLink.st_ino) {
X			errno = ELOOP;
X			return -1;
X		}
X	}
X	SR.pst = & stLink;
X	SR.psr = pSRPrev;
X	switch (stLink.st_mode & S_IFMT) {
X	default:
X		errno = EEXIST;
X		return -1;
X	case 0:
X	case S_IFREG:	/* regular */
X		return 0;
X	case S_IFLNK:	/* symbolic link */
X		break;
X	}
X
X	if (-1 == (i = readlink(pcLink, acRead, MAXPATHLEN))) {
X		fprintf(stderr, "%s: readlink: %s: %s\n", progname, pcLink, strerror(errno));
X		return -1;
X	}
X	acRead[i] = '\000';
X
X	if ((char *)0 == getwd(acPwd)) {
X		fprintf(stderr, "%s: getwd: %s\n", progname, acPwd);
X		return -1;
X	}
X
X	pcTail = getparent(acRead, acNext);
X	if (-1 == chdir(acNext)) {
X		return -1;
X	}
X	if (-1 == sym1Hard(pcTail, & SR)) {
X		return -1;
X	}
X	if (-1 == chdir(acPwd)) {
X		return -1;
X	}
X
X	if (0 != stat(acRead, & stRead)) {
X		fprintf(stderr, "%s: stat: %s: %s\n", progname, pcLink, strerror(errno));
X		return -1;
X	}
X	if (stLink.st_dev != stRead.st_dev || S_IFREG != (stRead.st_mode & S_IFMT)) {
X		errno = EXDEV;
X		return -1;
X	}
X
X	if (fVerbose) {
X		printf("%s: rm -f %s\n", progname, pcLink);
X	}
X	if (fExec && unlink(pcLink)) {
X		fprintf(stderr, "%s: unlink: %s: %s\n", progname, pcLink, strerror(errno));
X		return 	-1;
X	}
X	if (fVerbose) {
X		printf("%s: ln %s %s\n", progname, acRead, pcLink);
X	}
X	if (fExec && link(acRead, pcLink)) {
X		fprintf(stderr, "%s: link: %s to %s: %s\n", progname, acRead, pcLink, strerror(errno));
X		return 	-1;
X	}
X
X	return 0;
}
X
/*
X * we need a wrapper around sym1Hard to restore the top level directory	(ksb)
X */
int
sym2Hard(pcLink)
char *pcLink;
{
X	auto char acNext[MAXPATHLEN+1];
X	auto char acPwd[MAXPATHLEN+1];
X	register int i;
X	register char *pcTail;
X
X	if ((char *)0 == getwd(acPwd)) {
X		fprintf(stderr, "%s: getwd: %s\n", progname, acPwd);
X		return -1;
X	}
X
X	pcTail = getparent(pcLink, acNext);
X	if (-1 == chdir(acNext)) {
X		return -1;
X	}
X
X	i = sym1Hard(pcTail, (STREC *)0);
X
X	(void) chdir(acPwd);
X	return i;
}
X
#endif
Purdue
chmod 0444 mkcat/sym2hard.c ||
echo 'restore of mkcat/sym2hard.c failed'
Wc_c="`wc -c < 'mkcat/sym2hard.c'`"
test 5149 -eq "$Wc_c" ||
	echo 'mkcat/sym2hard.c: original size 5149, current size' "$Wc_c"
fi
# ============= mkcat/mkcat-opts.5l ==============
if test -f 'mkcat/mkcat-opts.5l' -a X"$1" != X"-c"; then
	echo 'x - skipping mkcat/mkcat-opts.5l (File already exists)'
else
echo 'x - extracting mkcat/mkcat-opts.5l (Text)'
sed 's/^X//' << 'Purdue' > 'mkcat/mkcat-opts.5l' &&
.\" Copyright 1990 Purdue Research Foundation, West Lafayette, Indiana
.\" 47907.  All rights reserved.
.\"
.\" Written by Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb
.\"
.\" This software is not subject to any license of the American Telephone
.\" and Telegraph Company or the Regents of the University of California.
.\"
.\" Permission is granted to anyone to use this software for any purpose on
.\" any computer system, and to alter it and redistribute it freely, subject
.\" to the following restrictions:
.\"
.\" 1. Neither the authors nor Purdue University are responsible for any
.\"    consequences of the use of this software.
.\"
.\" 2. The origin of this software must not be misrepresented, either by
.\"    explicit claim or by omission.  Credit to the authors and Purdue
.\"    University must appear in documentation and sources.
.\"
.\" 3. Altered versions must be plainly marked as such, and must not be
.\"    misrepresented as being the original software.
.\"
.\" 4. This notice may not be removed or altered.
.\"
.\" $Id: mkcat-opts.5l,v 3.0 90/06/01 11:47:31 ksb Exp $
.\"
.\" $Laser: ${tbl-tbl} %f | ${ltroff-ltroff} -man
.\" $Compile: ${tbl-tbl} %f | ${nroff-nroff} -man | ${PAGER-${more-more}}
.TH MKCAT-OPTS 5L PUCC
.SH NAME
mkcat-opts \- manual system configuration file
.SH SYNOPSIS
.B /usr/man/.mkcat-opts
.SH DESCRIPTION
.PP
The manual system is a complex database built on top of the UNIX file
system which is updated almost entirely by the \fImkcat\fP(8L) program.
So that each user of \fImkcat\fP doesn't have to specify many options
to update a single manual page, many defaults are kept in a single file
at the root of the manual system.
The paragraphs below describe the options available in this file.
.PP
Any line which begins with a pounds sign (`#') is taken to be a comment.
.PP
The formatted manual pages (called \*(lqcat\*(rq pages) may either be
stored compressed or as plain ASCII files.
Compressed files usually save disk space, uncompressed (ASCII) files
save a little CPU time when a user runs \fIman\fP(1).
To enable compression the options file may contain a line like:
.sp 1
X	compress: y
.sp 1
to keep the files as plain ASCII:
.sp 1
X	compress: n
.PP
Manual pages which contain descriptions of more than one command
(have multiple words before the `\-' in the NAME section)
are made available under all their names.
This service is provided by linking all the names to the formatted
manual page (see \fIln\fP(1)).
Either hard links or symbolic links may be used for the auxiliary
names for the pages.
For symbolic links:
.sp 1
X	links: S
.sp 1
for hard links:
.sp 1
X	links: H
.PP
When \fImkcat\fP installs a file in the manual system it
uses the \fIinstall\fP(1L) program.
If the system administrator prefers special modes on the manual
system's files, these modes may specified in the options file.
For example to change the mode and group on the installed cat pages:
.sp 1
X	pagemode: 0660
.br
X	pagegroup: mandoc
.sp 1
The \*(lqpage\*(rq above may be replaced by any of \*(lqwhatis\*(rq,
\*(lqdir\*(rq, or \*(lqman\*(rq which \fImkcat\fP uses respectively
for the whatis database, any directories, or the man page sources.
The owner may be specified as:
.sp 1
X	whatisowner: root
.br
X	dirowner: root
.sp 1
.PP
If the default \fB\-m\fP \fIman\fP directory compiled into \fImkcat\fP
is not acceptable one may be specified as:
.sp 1
X	man: \fIman\fP
.sp 1
likewise for the \fB\-w\fP \fIwhatis\fP and \fB\-c\fP \fIcat\fP options.
.PP
\fIMkcat\fP checks to be sure that each installed page is unique in its
section.
Some pages are purposely \fBnot\fP unique in a section; for example
getc.3f and getc.3s.
These may be explicitly exempted based on either the basename (getc)
or the extenders (3f, 3s).
To exempt all pages with conflict which end in 3f and 3s a line:
.sp 1
X	OKext: {3f,3s}
.sp 1
is placed in the options file.
To exempt just the getc pages:
.sp 1
X	OKbase: getc
.sp 1
.SH EXAMPLE
.RS
.nf
# mkcat install options (ksb)
compress: y
links: S
# allow group write, see install(1L)
pagemode: 0644/020
dirmode: 0755/020
whatismode: 0644/020
OKext: {3s,3f}
OKbase: intro
.fi
.RE
.SH FILES
.TS
l l.
/usr/man/.mkcat-opts	modes for installed files, link type, compression flag
.TE
.SH AUTHOR
Kevin Braunsdorf, PUCC UNIX Group, ksb@cc.purdue.edu, pur-ee!ksb, purdue!ksb
.br
Copyright \*(co 1990 Purdue Research Foundation.  All rights reserved.
.SH "SEE ALSO"
compress(1), install(1L), ln(1), man(1), mkcat(8L), uncompress(1), zcat(1)
Purdue
chmod 0444 mkcat/mkcat-opts.5l ||
echo 'restore of mkcat/mkcat-opts.5l failed'
Wc_c="`wc -c < 'mkcat/mkcat-opts.5l'`"
test 4487 -eq "$Wc_c" ||
	echo 'mkcat/mkcat-opts.5l: original size 4487, current size' "$Wc_c"
fi
# ============= mk/Tests/file.x,y ==============
if test ! -d 'mk/Tests'; then
    echo 'x - creating directory mk/Tests'
    mkdir 'mk/Tests'
fi
if test -f 'mk/Tests/file.x,y' -a X"$1" != X"-c"; then
	echo 'x - skipping mk/Tests/file.x,y (File already exists)'
else
echo 'x - extracting mk/Tests/file.x,y (Text)'
sed 's/^X//' << 'Purdue' > 'mk/Tests/file.x,y' &&
$Compile: [ %f = "file.x,y" ] && for marker in Base Ext Cut Type\n\tdo\n\t\t%b %o -a -m$marker %f && echo "$marker test OK"\n\tdone
X
# base names
$Base(1): [ "file" = "%F" ]
$Base(2): [ "file" = "%P" ]
$Base(3): [ "file" = "%p" ]
X
# extentions
$Ext(1): [ "x,y" = "%x" ]
$Ext(2): [ "x,y" = "%X" ]
X
# Cuts on non-dot extenders that fail
$Cut(1): [ -z "%u@" ]
$Cut(2): [ "file.x,y" = "%q@" ]
$Cut(3)=~*: exit 1 # %U@
$Cut(4)=~*: exit 1 # %Q@
X
# Cuts on non-dot extenders that work
$Cut(5): [ "file.x" = "%q," ]
$Cut(6): [ "y" = "%u," ]
$Cut(7): [ "file.x" = "%Q," ]
$Cut(8): [ "y" = "%U," ]
X
# file type flags
$Type(1): [ "f" = "%y" ]%Yf
$Type(2)=~*: exit 1 # %Yd should stop xlation
$Type(3)=~*: exit 1 # %Y. should stop error
Purdue
chmod 0644 mk/Tests/file.x,y ||
echo 'restore of mk/Tests/file.x,y failed'
Wc_c="`wc -c < 'mk/Tests/file.x,y'`"
test 725 -eq "$Wc_c" ||
	echo 'mk/Tests/file.x,y: original size 725, current size' "$Wc_c"
fi
# ============= mk-lib/m-info ==============
if test -f 'mk-lib/m-info' -a X"$1" != X"-c"; then
	echo 'x - skipping mk-lib/m-info (File already exists)'
else
echo 'x - extracting mk-lib/m-info (Text)'
sed 's/^X//' << 'Purdue' > 'mk-lib/m-info' &&
# the Info target outputs a descriptive message				(ksb)
X
$Info(*): ${file-file} %f
$Info(*): ${echo-echo} type %y, extender %X
$Info(*): ${echo-echo} type %y
Purdue
chmod 0444 mk-lib/m-info ||
echo 'restore of mk-lib/m-info failed'
Wc_c="`wc -c < 'mk-lib/m-info'`"
test 159 -eq "$Wc_c" ||
	echo 'mk-lib/m-info: original size 159, current size' "$Wc_c"
fi
# ============= mkcat/main.c ==============
if test -f 'mkcat/main.c' -a X"$1" != X"-c"; then
	echo 'x - skipping mkcat/main.c (File already exists)'
else
echo 'x - extracting mkcat/main.c (Text)'
sed 's/^X//' << 'Purdue' > 'mkcat/main.c' &&
/*
X * machine generated cmd line parser
X */
X
#include "getopt.h"
#include <stdio.h>
#include "machine.h"
#include "mkcat.h"
X
char
X	*progname = "$Id$",
X	u_terse[] = " [-ADILRVWZfhnsv] [-c cat] [-m man] [-r root] [-w whatis] [pages]",
X	*u_help[] = {
X		"A        scan the formatted pages for SEE ALSO errors",
X		"D        delete the given pages from the cat directories",
X		"I        install the given pages (default action)",
X		"L        rebuild auxiliary links to cat pages",
X		"R        install a new manual root",
X		"V        show the version and configuration of this program",
X		"W        rebuild the whatis database from the cat pages",
X		"Z        check file compression and update",
X		"c cat    specify the prefix for cat directories (cat1 cat2 cat3...)",
X		"f        the given pages are already formatted, just use them",
X		"h        print this help message",
X		"m man    save the manual page source in parallel with the cat page",
X		"n        do not really change the manual system",
X		"r root   specify a user defined root for the manual system",
X		"s        be silent",
X		"v        be verbose by displaying shell commands as they are run",
X		"w whatis specify a whatis database to update",
X		"pages    the manual pages to be updated",
X		(char *)0
X	};
int
X	iExit = 0,
X	fCkAlso = 0,
X	fDelete = 0,
X	fGenInstck = 0,
X	fInstall = 0,
X	fMkLinks = 0,
X	fInitNew = 0,
X	fVersion = 0,
X	fMkWhatis = 0,
X	fJustComp = 0;
char
X	copyright[] = "@(#) Copyright 1990 Purdue Research Foundation.\nAll rights reserved.\n",
X	*pcCat = acCat;
int
X	fFormat = 1;
char
X	*pcMan = (char *)0;
int
X	fExec = 1;
char
X	*pcRoot = acRoot;
int
X	fVerbose = 0;
char
X	*pcWhatis = acWhatis;
X
static int sbiOnly['w'-'*'+1];
static char sbForbid[] = "%s: option `%c\' forbidden by `%c\'\n";
X
static void
chkonly(chOpt, fDup)
int chOpt, fDup;
{
X	register int chWas;
X
X	chWas = sbiOnly[chOpt-'*'];
X	if (fDup && chOpt == chWas) {
X		fprintf(stderr, "%s: option `%c\' cannot be given more than once\n", progname, chWas);
X		exit(1);
X	} else if (chWas < 0) {
X		fprintf(stderr, sbForbid, progname, chOpt, -chWas);
X		exit(1);
X	}
X	sbiOnly[chOpt-'*'] = chOpt;
}
X
static void
chkforbid(chOpt, pchList)
int chOpt;
char *pchList;
{
X	register int chCur;
X
X	while ('\000' != (chCur = *pchList++)) {
X		if (sbiOnly[chCur-'*'] > 0) {
X			fprintf(stderr, sbForbid, progname, chCur, chOpt);
X			exit(1);
X		}		
X		sbiOnly[chCur-'*'] = -chOpt;
X	}
}
X
/*
X * parser
X */
int
main(argc, argv)
int argc;
char **argv;
{
X	static char
X		sbOpt[] = "ADGILRVWZc:fhm:nr:svw:",
X		*u_pch = (char *)0;
X	static int
X		u_loop = 0;
X	register int curopt;
X	register char *pchEnv;
X	extern char *getenv();
X	extern char *strncpy();
X	extern int atoi();
X	extern char *strrchr();
X
X	progname = strrchr(argv[0], '/');
X	if ((char *)0 == progname)
X		progname = argv[0];
X	else
X		++progname;
X	if ((char *)0 != (pchEnv = getenv("MKCAT")))
X		envopt(pchEnv);
X	while (EOF != (curopt = getopt(argc, argv, sbOpt))) {
X		switch (curopt) {
X		case BADARG:
X			fprintf(stderr, "%s: option %c needs a parameter\n", progname, optopt);
X			exit(1);
X		case BADCH:
X			fprintf(stderr, "%s: usage%s\n", progname, u_terse);
X			exit(1);
X		case 'A':
X			chkforbid('A', "IR");
X			fCkAlso = ! 0;
X			continue;
X		case 'D':
X			chkonly('D', 0);
X			chkforbid('D', "WIZLVR");
X			fDelete = ! 0;
X			continue;
X		case 'G':
X			fGenInstck = ! 0;
X			continue;
X		case 'I':
X			chkonly('I', 0);
X			chkforbid('I', "DLWZVR");
X			fInstall = ! 0;
X			continue;
X		case 'L':
X			chkonly('L', 0);
X			chkforbid('L', "DIZVR");
X			fMkLinks = ! 0;
X			continue;
X		case 'R':
X			chkonly('R', 0);
X			chkforbid('R', "WIZL");
X			fInitNew = ! 0;
X			continue;
X		case 'V':
X			chkonly('V', 0);
X			chkforbid('V', "WIZLD");
X			fVersion = ! 0;
X			continue;
X		case 'W':
X			chkonly('W', 0);
X			chkforbid('W', "DIZVR");
X			fMkWhatis = ! 0;
X			continue;
X		case 'Z':
X			chkonly('Z', 0);
X			chkforbid('Z', "DILWVR");
X			fJustComp = ! 0;
X			continue;
X		case 'c':
X			pcCat = optarg;
X			continue;
X		case 'f':
X			fFormat = ! 1;
X			continue;
X		case 'h':
X			fprintf(stdout, "%s: usage%s\n", progname, u_terse);
X			for (u_loop = 0; (char *)0 != (u_pch = u_help[u_loop]); ++u_loop) {
X				fprintf(stdout, "%s\n", u_pch);
X			}
X			exit(0);
X		case 'm':
X			pcMan = optarg;
X			continue;
X		case 'n':
X			fExec = 0;
X			fVerbose = 1;
X			continue;
X		case 'r':
X			pcRoot = optarg;
X			continue;
X		case 's':
X			fVerbose = 0;
X			continue;
X		case 'v':
X			fVerbose = 1;
X			continue;
X		case 'w':
X			pcWhatis = optarg;
X			continue;
X		}
X		break;
X	}
X	iExit = doMkCat(argc-optind, & argv[optind]);
X	return iExit;
}
Purdue
chmod 0644 mkcat/main.c ||
echo 'restore of mkcat/main.c failed'
Wc_c="`wc -c < 'mkcat/main.c'`"
test 4513 -eq "$Wc_c" ||
	echo 'mkcat/main.c: original size 4513, current size' "$Wc_c"
fi
# ============= mk/Makefile.mkcmd ==============
if test -f 'mk/Makefile.mkcmd' -a X"$1" != X"-c"; then
	echo 'x - skipping mk/Makefile.mkcmd (File already exists)'
else
echo 'x - extracting mk/Makefile.mkcmd (Text)'
sed 's/^X//' << 'Purdue' > 'mk/Makefile.mkcmd' &&
# Written by S. McGeady, Intel, Inc., mcg@mipon2.intel.com		(sm)
#	     Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb		(ksb)
#
# This software is not subject to any license of the American Telephone
# and Telegraph Company or the Regents of the University of California.
#
# Permission is granted to anyone to use this software for any purpose on
# any computer system, and to alter it and redistribute it freely, subject
# to the following restrictions:
#
# 1. The authors are not held responsible for any consequences of the
#    use of this software.
#
# 2. The origin of this software must not be misrepresented, either by
#    explicit claim or by omission.  Credit to the authors must appear
#    in documentation and sources.
#
# 3. Altered versions must be plainly marked as such, and must not be
#    misrepresented as being the original software.
#
# 4. This notice may not be removed or altered.
#
# Makefile for mk
#
# $Id: Makefile,v 4.6 90/11/19 13:56:36 ksb Exp $
X
PROG=	mk
BIN=	${DESTDIR}/usr/local/bin
X
L=../libopt
#L=/usr/include/local
I=/usr/include
S=/usr/include/sys
P=
X
INCLUDE= -I$L
DEBUG=	-O
CDEFS= 	
CFLAGS= ${DEBUG} ${CDEFS} ${INCLUDE}
X
HDR=	mk.h optaccum.h machine.h rlimsys.h
SRC=	mk.c rcsname.c setenv.c optaccum.c rlimsys.c
GENh=	main.h
GENc=	main.c
GEN=	${GENh} ${GENc}
DEP=	${SRC} ${GENc}
OBJ=	main.o mk.o rcsname.o setenv.o optaccum.o rlimsys.o
MAN=	mk.1l
SOURCE=	Makefile Makefile.plain README mk.m ${MAN} ${HDR} ${SRC}
X
all: ${PROG}
X
${PROG}:$P ${OBJ}
#	${CC} -o $@ ${CFLAGS} ${OBJ} -lopt
#	${CC} -o $@ ${CFLAGS} ${OBJ} -L /usr/local/lib -lopt
X	${CC} -o $@ ${CFLAGS} ${OBJ} ../libopt/libopt.a
X
main.h: main.c
X
main.c: mk.m
X	mkcmd std_help.m mk.m
X	-(cmp -s prog.c main.c || (mv prog.c main.c && echo main.c updated))
X	-(cmp -s prog.h main.h || (mv prog.h main.h && echo main.h updated))
X	rm -rf prog.[ch]
X
self-test: ${PROG}
X	cd Tests; ../${PROG} -mCompile *
X
swap: ${HDR} ${SRC} ${GEN} Makefile.plain
X	mv Makefile Makefile.mkcmd
X	mv Makefile.plain Makefile
X
clean: FRC
X	rm -f Makefile.bak *.o prog.[ch] ${GEN} ${PROG} a.out core errs tags
X
depend: ${HDR} ${SRC} ${GEN} FRC
X	maketd ${CDEFS} ${INCLUDE} ${DEP}
X
install: all FRC
X	install -cs ${PROG} ${BIN}/${PROG}
X
lint: ${HDR} ${SRC} ${GEN} FRC
X	lint -h ${CDEFS} ${INCLUDE} ${DEP}
X
mkcat: ${MAN}
X	mkcat ${MAN}
X
print: source FRC
X	lpr -J'${PROG} source' ${SOURCE}
X
source: ${SOURCE}
X
spotless: clean
X	rcsclean ${SOURCE}
X
tags: ${HDR} ${SRC} ${GEN}
X	ctags -t ${HDR} ${SRC} ${GEN}
X
${SOURCE}:
X	co -q $@
X
FRC:
X
# DO NOT DELETE THIS LINE - maketd DEPENDS ON IT
X
main.o: machine.h main.c optaccum.h
X
mk.o: machine.h main.h mk.c mk.h rlimsys.h
X
rcsname.o: machine.h rcsname.c
X
setenv.o: machine.h setenv.c
X
optaccum.o: machine.h optaccum.c
X
rlimsys.o: machine.h mk.h rlimsys.c rlimsys.h
X
# *** Do not add anything here - It will go away. ***
Purdue
chmod 0644 mk/Makefile.mkcmd ||
echo 'restore of mk/Makefile.mkcmd failed'
Wc_c="`wc -c < 'mk/Makefile.mkcmd'`"
test 2823 -eq "$Wc_c" ||
	echo 'mk/Makefile.mkcmd: original size 2823, current size' "$Wc_c"
fi
# ============= mk/main.c ==============
if test -f 'mk/main.c' -a X"$1" != X"-c"; then
	echo 'x - skipping mk/main.c (File already exists)'
else
echo 'x - extracting mk/main.c (Text)'
sed 's/^X//' << 'Purdue' > 'mk/main.c' &&
/*
X * machine generated cmd line parser
X */
X
#include "getopt.h"
#include <stdio.h>
#include "machine.h"
#include "optaccum.h"
#include "mk.h"
X
char
X	*progname = "$Id$",
X	uline[] = " [-Aachinsv] [-D defn] [-U undef] [-d submarker] [-e templates] [-l lines] [-m marker] [-t templates] [files]",
X	*help[] = {
X		"A           find the first matched command that succeeds",
X		"D defn      give a definition of an environment variable",
X		"U undef     remove a definition for an envoronment variable",
X		"a           find all matched commands",
X		"c           confirm the action before running it",
X		"d submarker look for the string \"$marker(submarker):\" in file",
X		"e templates scan templates before given files",
X		"h           print this help message",
X		"i           ignore case for markers and submarkers",
X		"l lines     specify the max number of lines to search",
X		"m marker    look for the string \"$marker:\" in file",
X		"n           do not really do it",
X		"s           silent operation",
X		"t templates look for marker in template file",
X		"v           be verbose",
X		"files       search for commands in these files",
X		(char *)0
X	},
X	*pathname = (char *)0;
int
X	fFirst = 0,
X	debug = 0,
X	fAll = 0,
X	fConfirm = 0;
char
X	*submark = (char *)0,
X	*pchExamine = (char *)0;
int
X	fCase = 0,
X	lines = 99;
char
X	*markstr = (char *)0;
int
X	fExec = 1;
char
X	*pchTemplates = (char *)0;
int
X	fVerbose = 1;
X
/*
X * a usage function
X */
int
usage()
{
X	register char **ppch;
X
X	for (ppch = help; (char *)0 != *ppch; ++ppch)
X		printf("%s\n", *ppch);
}
X
/*
X * parser
X */
int
main(argc, argv)
int argc;
char **argv;
{
X	static char
X		sbOpt[] = "AD:U:Vacd:e:hil:m:nst:v";
X	static int
X		retval = 0;
X	static char
X		*u_pch = (char *)0;
X	static int
X		u_loop = 0;
X	register int curopt, fNoArgs = 1;
X	register char *pchEnv;
X	extern char *getenv();
X	extern int atoi();
X	extern char *strrchr();
X
X	progname = strrchr(argv[0], '/');
X	if ((char *)0 == progname)
X		progname = argv[0];
X	else
X		++progname;
X	if ((char *)0 != (pchEnv = getenv("MK")))
X		envopt(pchEnv);
X	pathname = argv[0];
X	for (;;) {
X		if (EOF != (curopt = getopt(argc, argv, sbOpt))) {
X			/* fallthrough */;
X		} else if (EOF != getarg(argc, argv)) {
X			retval += process(optarg);
X			fNoArgs = 0;
X			continue;
X		} else {
X			break;
X		}
X		switch (curopt) {
X		case BADARG:
X			fprintf(stderr, "%s: option %c needs a parameter\n", progname, optopt);
X			exit(1);
X		case BADCH:
X			fprintf(stderr, "%s: usage%s\n", progname, uline);
X			exit(1);
X		case 'A':
X			fFirst = ! 0;
X			continue;
X		case 'D':
X			define(optarg);
X			continue;
X		case 'U':
X			undefine(optarg);
X			continue;
X		case 'V':
X			debug = ! 0;
X			continue;
X		case 'a':
X			fAll = ! 0;
X			continue;
X		case 'c':
X			fConfirm = ! 0;
X			continue;
X		case 'd':
X			submark = optarg;
X			continue;
X		case 'e':
X			pchExamine = OptAccum(pchExamine, optarg, ":");
X			continue;
X		case 'h':
X			fprintf(stdout, "%s: usage%s\n", progname, uline);
X			for (u_loop = 0; (char *)0 != (u_pch = help[u_loop]); ++u_loop) {
X				fprintf(stdout, "%s\n", u_pch);
X			}
X			exit(0);
X		case 'i':
X			fCase = ! 0;
X			continue;
X		case 'l':
X			lines = atoi(optarg);
X			continue;
X		case 'm':
X			markstr = optarg;
X			continue;
X		case 'n':
X			fExec = ! 1;
X			continue;
X		case 's':
X			fVerbose = 0;
X			continue;
X		case 't':
X			pchTemplates = OptAccum(pchTemplates, optarg, ":");
X			continue;
X		case 'v':
X			fVerbose = 1;
X			continue;
X		}
X		break;
X	}
X	if (fNoArgs) {
X		if (debug) {
X			Version();
X		}
X	}
X
X	/* we just exit now */
X	exit(retval);
}
Purdue
chmod 0644 mk/main.c ||
echo 'restore of mk/main.c failed'
Wc_c="`wc -c < 'mk/main.c'`"
test 3495 -eq "$Wc_c" ||
	echo 'mk/main.c: original size 3495, current size' "$Wc_c"
fi
# ============= mk-lib/Makefile ==============
if test -f 'mk-lib/Makefile' -a X"$1" != X"-c"; then
	echo 'x - skipping mk-lib/Makefile (File already exists)'
else
echo 'x - extracting mk-lib/Makefile (Text)'
sed 's/^X//' << 'Purdue' > 'mk-lib/Makefile' &&
# Makefile for mk default rules
#		Kevin S Braunsdorf, PUCC
X
LIB=	${DESTDIR}/usr/local/lib
X
MANUAL=	dot-1c dot-1g dot-1l dot-1u dot-1v \
X	dot-2 dot-2l \
X	dot-3 dot-3c dot-3f dot-3l dot-3m dot-3n dot-3s dot-3x \
X	dot-4 dot-4f dot-4l dot-4n dot-4p \
X	dot-5 dot-5l \
X	dot-6 dot-6l \
X	dot-7 dot-7l \
X	dot-8 dot-8c dot-8l dot-8v
USECC=	dot-s m-cc
USEFORTRAN=dot-f77 m-fc
USEMAKE=file-Makefile file-makefile dot-make
USEMAN=	dot-ms dot-me dot-mm
USEM2= 	dot-m2
USENDBM= pre-pag
USENONE=dot-i
USENROFF=dot-nro
USEPASCAL= m-pc
USEPATCH=pre-diff
USESH=	dot-csh dot-tcsh dot-ksh
USEUU= pre-uue
USEVALID=file-Valid dot-v
REAL=	m-clean m-compile m-display m-info m-mkcat m-run \
X	type-b type-c type-d type-p type-s \
X	file-valid file-gmon.out \
X	pre-a pre-C pre-dir pre-ln pre-o pre-out pre-patch \
X	pre-tar pre-uu pre-Z pre-z pre-zoo \
X	dot- dot-1 dot-c dot-C dot-dvi dot-e dot-el dot-f dot-h dot-l \
X	dot-m4 dot-m dot-man dot-mk dot-p dot-ph dot-pl dot-ps dot-r \
X	dot-sh dot-shar dot-t dot-xbm dot-y \
X	comma-v
IGNORE=	pre-MW pre-cpio
MAN=	mk.5l
SOURCE=	Makefile README ${MAN} ${REAL} ${IGNORE}
X
all: source
X
${LIB}/mk:
X	install -d -r $@
X
clean: FRC
X	rm -f Makefile.bak a.out core errs tags
X
depend: FRC
X	: 'no depend'
X
deinstall: ${MAN}
X	rm -rf ${LIB}/mk
X	mkcat -D ${MAN}
X
dirs: ${LIB}/mk
X
install: all dirs FRC
X	install -c -m 644 ${REAL} ${LIB}/mk
X	-cd ${LIB}/mk; for file in ${USEVALID}; do \
X		[ -f $$file ] || (ln -s file-valid $$file && echo $$file);\
X	done
X	-cd ${LIB}/mk; for file in ${USEPATCH}; do \
X		[ -f $$file ] || (ln -s pre-patch $$file && echo $$file);\
X	done
X	-cd ${LIB}/mk; for file in ${USENDBM}; do \
X		[ -f $$file ] || (ln -s pre-dir $$file && echo $$file);\
X	done
X	-cd ${LIB}/mk; for file in ${USEUU}; do \
X		[ -f $$file ] || (ln -s pre-uu $$file && echo $$file);\
X	done
X	-cd ${LIB}/mk; for file in ${MANUAL}; do \
X		[ -f $$file ] || (ln -s dot-1 $$file && echo $$file);\
X	done
X	-cd ${LIB}/mk; for file in ${USEMAN}; do \
X		[ -f $$file ] || (ln -s dot-man $$file && echo $$file);\
X	done
X	-cd ${LIB}/mk; for file in ${USESH}; do \
X		[ -f $$file ] || (ln -s dot-sh $$file && echo $$file);\
X	done
X	-cd ${LIB}/mk; for file in ${USECC}; do \
X		[ -f $$file ] || (ln -s dot-c $$file && echo $$file);\
X	done
X	-cd ${LIB}/mk; for file in ${USEMAKE}; do \
X		[ -f $$file ] || (ln -s dot-mk $$file && echo $$file);\
X	done
X	-cd ${LIB}/mk; for file in ${USENONE}; do \
X		[ -f $$file ] || (ln -s dot-h $$file && echo $$file);\
X	done
X	-cd ${LIB}/mk; for file in ${USEM2}; do \
X		[ -f $$file ] || (ln -s dot-m $$file && echo $$file);\
X	done
X	-cd ${LIB}/mk; for file in ${USEPASCAL}; do \
X		[ -f $$file ] || (ln -s dot-p $$file && echo $$file);\
X	done
X	-cd ${LIB}/mk; for file in ${USEFORTRAN}; do \
X		[ -f $$file ] || (ln -s dot-f $$file && echo $$file);\
X	done
X	-cd ${LIB}/mk; for file in ${USENROFF}; do \
X		[ -f $$file ] || (ln -s dot-t $$file && echo $$file);\
X	done
X
lint: FRC
X	: 'nothing to lint'
X
mkcat: ${MAN}
X	mkcat ${MAN}
X
print: source FRC
X	lpr -J'mk templates' ${SOURCE}
X
source: ${SOURCE}
X
spotless: clean
X	rcsclean ${SOURCE}
X
tags: FRC
X	: 'no tags'
X
${SOURCE}:
X	co -q $@
X
FRC:
X
Purdue
chmod 0644 mk-lib/Makefile ||
echo 'restore of mk-lib/Makefile failed'
Wc_c="`wc -c < 'mk-lib/Makefile'`"
test 3088 -eq "$Wc_c" ||
	echo 'mk-lib/Makefile: original size 3088, current size' "$Wc_c"
fi
true || echo 'restore of mkcat/strcasecmp.c failed'
echo End of part 4, continue with part 5
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.