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

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

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

#!/bin/sh
# This is part 03 of pucc-1c
# ============= mkcat/genwhatis.c ==============
if test ! -d 'mkcat'; then
    echo 'x - creating directory mkcat'
    mkdir 'mkcat'
fi
if test -f 'mkcat/genwhatis.c' -a X"$1" != X"-c"; then
	echo 'x - skipping mkcat/genwhatis.c (File already exists)'
else
echo 'x - extracting mkcat/genwhatis.c (Text)'
sed 's/^X//' << 'Purdue' > 'mkcat/genwhatis.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
/*  $Id: genwhatis.c,v 3.3 90/08/31 12:45:46 ksb Exp $
X *
X * genwhatis -- update the whatis database
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>
#include <fcntl.h>
#if defined(SYSV)
#include <ndir.h>
#else
#include <sys/dir.h>
#endif
#include <sys/stat.h>
X
#if !defined(MAXPATHLEN)
#define MAXPATHLEN	1024
#endif
X
#include "main.h"
#include "pt.h"
#include "genwhatis.h"
#include "mkcat.h"
X
extern char *sys_errlist[];
#define strerror(Me) sys_errlist[Me]
extern char *strrchr(), *strcpy();
X
X
extern char *malloc(), *realloc(), *calloc();
extern char *mktemp(), *strcat(), *strcpy();
extern int errno;
extern int strlen();
X
static char acLockExt[] =	/* lock extender on db			*/
X	".lock";
X
/*
X * compare to whatis entries (the lowest left in each really)		(ksb)
X */
static int
wucmp(pWULeft, pWURight)
register WHATIS *pWULeft, *pWURight;
{
X	register int i;
X
X	i = pWULeft->ppclist[0][0] - pWURight->ppclist[0][0];
X	if (i != 0)
X		return i;
X	i = strcmp(pWULeft->ppclist[0], pWURight->ppclist[0]);
X	if (i != 0)
X		return i;
X	i = pWULeft->isection - pWURight->isection;
X	if (i != 0)
X		return i;
X	return strcmp(pWULeft->pcext, pWURight->pcext);
}
X
/*
X * remove the first whatis entry in a queue				(ksb)
X */
static void
wudequeue(pWU)
WHATIS *pWU;
{
X	if (0 == pWU->ilen) {
X		fprintf(stderr, "%s: internal error\n", progname);
X		exit(20);
X	}
X	pWU->ilen -= 1;
X	pWU->ppclist += 1;
}
X
/*
X * pull the lowest string off the queues				(ksb)
X */
static WHATIS *
wufindmin(iLen, pWUWhats)
int iLen;
WHATIS *pWUWhats;
{
X	register WHATIS *pWULow;
X	register int i, cp;
X
X	pWULow = (WHATIS *)0;
X	for (i = 0; i < iLen; ++i) {
X		/* spent or error page -- ignore it now
X		 */
X		if (0 == pWUWhats[i].ilen)
X			continue;
X		if ((WHATIS *)0 == pWULow) {
X			pWULow = & pWUWhats[i];
X			continue;
X		}
X		/* this first case can happen if we were given two
X		 * copies of the same page to update... *sigh*
X		 */
X		if (0 == (cp = wucmp(& pWUWhats[i], pWULow))) {
X			wudequeue(& pWUWhats[i]);
X		} else if (cp < 0) {
X			pWULow = & pWUWhats[i];
X		}
X	}
X	return pWULow;
}
X
/*
X * output a whatis entry						(ksb)
X */
static void
wudump(fp, pWU)
FILE *fp;
WHATIS *pWU;
{
X	fprintf(fp, "%s (%s)\t- %s\n", pWU->ppclist[0], pWU->pcext, pWU->pcsummary);
}
X
/*
X * read a whatis record from a file, cannot be longer than 1024		(ksb)
X * characters
X */
static WHATIS *
nextwu(fp)
register FILE *fp;
{
X	static WHATIS WURet;
X	static char *apcFake[2];
X	static char acLine[1024];
X	register char *pcLine;
X
X	if (feof(fp) || (char *)0 == (pcLine = fgets(acLine, 1024, fp))) {
X		return (WHATIS *)0;
X	}
X
X	/* we do not copy bogus entries that the old makewhatis program
X	 * would generate... " (5) 	-devices - UUCP ...."
X	 * (for L-devices)
X	 */
X	while (isspace(*pcLine))
X		++pcLine;
X	apcFake[0] = pcLine;
X	while (! isspace(*pcLine) && '\000' != *pcLine)
X		++pcLine;
X
X	do {
X		*pcLine++ = '\000';
X	} while (isspace(*pcLine));
X
X	/* should have "(Xn) - <stuff>" now */
X	if ('('/*)*/ != *pcLine)
X		return nextwu(fp);
X	while ('\b' == pcLine[1] && '(' == pcLine[2]) /*)*/
X		pcLine += 2;
X	++pcLine;
X	WURet.isection = atoi(pcLine);
X	WURet.pcext = pcLine;
X	while (/*(*/ ')' != *pcLine && '\000' != *pcLine)
X		++pcLine;
X	if (')' != *pcLine)
X		return nextwu(fp);
X	*pcLine++ = '\000';
X
X	while ('-' != *pcLine && '\000' != *pcLine)
X		++pcLine;
X	if ('-' != *pcLine)
X		return nextwu(fp);
X
X	do {
X		++pcLine;
X	} while (isspace(*pcLine) && '\000' != *pcLine);
X	WURet.pcsummary = pcLine;
X
X	if ((char *)0 != (pcLine = strrchr(pcLine, '\n'))) {
X		*pcLine = '\000';
X	}
X
X	WURet.ppclist = & apcFake[0];
X	WURet.ilen = 1;
X
X	return & WURet;
}
X
/*
X * update the whatis database, use install (of course)			(ksb)
X * we flock the whatis database so two people can run this at
X * about the same time...
X *
X * Take	Old	Del	Add
X * ~	n	n	n
X * Old	y	n	n
X * ~	n	y	n
X * ~	y	y	n
X * Add	n	n	y
X * Add	y	n	y
X * Add	n	y	y
X * Add	y	y	y
X */
int
MergeWhatis(fpOldWhatis, iLenNew, pWUWhatsNew, iLenDel, pWUWhatsGone, fpNewWhatis)
FILE *fpOldWhatis, *fpNewWhatis;
int iLenNew, iLenDel;
WHATIS *pWUWhatsNew, *pWUWhatsGone;
{
X	register WHATIS *pWUAdd, *pWUDel, *pWUOld;
X	register int i, a;
X
X	pWUOld = nextwu(fpOldWhatis);
X	pWUAdd = wufindmin(iLenNew, pWUWhatsNew);
X	pWUDel = wufindmin(iLenDel, pWUWhatsGone);
X	while ((WHATIS *)0 != pWUOld) {
X		while ((WHATIS *)0 != pWUAdd && (i = wucmp(pWUAdd, pWUOld)) < 0) {
X			wudump(fpNewWhatis, pWUAdd);
X			wudequeue(pWUAdd);
X			pWUAdd = wufindmin(iLenNew, pWUWhatsNew);
X		}
X		while ((WHATIS *)0 != pWUDel && (a = wucmp(pWUDel, pWUOld)) < 0) {
X			wudequeue(pWUDel);
X			pWUDel = wufindmin(iLenDel, pWUWhatsGone);
X		}
X		if ((WHATIS *)0 != pWUAdd && 0 == i) {
X			wudump(fpNewWhatis, pWUAdd);
X			wudequeue(pWUAdd);
X			pWUAdd = wufindmin(iLenNew, pWUWhatsNew);
X		} else if ((WHATIS *)0 != pWUDel && 0 == a) {
X			wudequeue(pWUDel);
X			pWUDel = wufindmin(iLenDel, pWUWhatsGone);
X		} else {
X			wudump(fpNewWhatis, pWUOld);
X		}
X		pWUOld = nextwu(fpOldWhatis);
X	}
X	while ((WHATIS *)0 != pWUAdd) {
X		wudump(fpNewWhatis, pWUAdd);
X		wudequeue(pWUAdd);
X		pWUAdd = wufindmin(iLenNew, pWUWhatsNew);
X	}
X	return 0;
}
X
/*
X * modify the whatis database (add or delete lines)
X */
int
ModWhatis(iLenAdd, pWUAdd, iLenDel, pWUDel)
int iLenAdd, iLenDel;
WHATIS *pWUAdd, *pWUDel;
{
X	register FILE *fpWhat, *fpNew;
X	register int i;
X	auto int fdLock;			/* for a flock		*/
X	auto char *pcNew, *pcEnd;
X	auto char acNew[MAXPATHLEN+1];
X	auto char acDest[MAXPATHLEN+1];
X	auto char acCmd[4*MAXPATHLEN+200];
#define PDELAY	2	/* sleep between peeks at the lock		*/
#define MAXLOOP	30	/* wait 60 seconds for another update		*/
X
X	if ('/' == pcWhatis[0] || ('.' == pcWhatis[0] && '/' == pcWhatis[1])) {
X		sprintf(acDest, "%s", pcWhatis);
X	} else {
X		sprintf(acDest, "%s/%s", pcRoot, pcWhatis);
X	}
X	pcEnd = acDest+strlen(acDest);
X	(void)strcpy(pcEnd, acLockExt);
X	if (fExec) {
X		for (i = 0; i < MAXLOOP; ++i) {
X			fdLock = open(acDest, O_EXCL|O_CREAT|O_WRONLY, 0660);
X			if (-1 == fdLock && EEXIST == errno) {
X				if (0 == i) {
X					fprintf(stderr, "%s: contending for database\n", progname);
X					(void)fflush(stderr);
X				}
X				sleep(PDELAY);
X				continue;
X			}
X			if (-1 == fdLock) {
X				fprintf(stderr, "%s: open: %s: %s\n", progname, acDest, strerror(errno));
X				exit(1);
X			}
X			(void)close(fdLock);
X			break;
X		}
X		if (MAXLOOP == i) {
X			fprintf(stderr, "%s: ignoring lock file\n", progname);
X		} else if (fVerbose) {
X			fprintf(fpOut, "%s: locked %s\n", progname, acDest);
X		}
X	}
X	*pcEnd = '\000';
X
X	if (NULL == (fpWhat = fopen(acDest, "r"))) {
X		fprintf(stderr, "%s: fopen: %s: %s\n", progname, acDest, strerror(errno));
X		if (NULL == (fpWhat = fopen("/dev/null", "r"))) {
X			fprintf(stderr, "%s: fopen: /dev/null: %s\n", progname, strerror(errno));
X			exit(1);
X		}
X	}
X
X	(void)strcpy(acNew, "/usr/tmp/mkwhatXXXXXX");
X	if ((char *)0 == (pcNew = mktemp(acNew))) {
X		fprintf(stderr, "%s: mktemp: no temp file\n", progname);
X		exit(1);
X	}
X	if (NULL == (fpNew = fopen(pcNew, "w"))) {
X		fprintf(stderr, "%s: fopen: %s: %s\n", progname, pcNew, strerror(errno));
X		exit(1);
X	}
X
X	i = MergeWhatis(fpWhat, iLenAdd, pWUAdd, iLenDel, pWUDel, fpNew);
X	(void)fclose(fpWhat);
X	(void)fclose(fpNew);
X	if (0 != i) {
X		return 1;
X	}
X
X	(void) sprintf(acCmd, "cmp -s %s %s || INSTALL=\"%s\" install", acDest, pcNew, fVerbose ? "-v" : "");
X	if ((char *)0 != apcModes[WHATISOWNER]) {
X		(void)strcat(acCmd, " -o");
X		(void)strcat(acCmd, apcModes[WHATISOWNER]);
X	}
X	if ((char *)0 != apcModes[WHATISGROUP]) {
X		(void)strcat(acCmd, " -g");
X		(void)strcat(acCmd, apcModes[WHATISGROUP]);
X	}
X	if ((char *)0 != apcModes[WHATISMODE]) {
X		(void)strcat(acCmd, " -m");
X		(void)strcat(acCmd, apcModes[WHATISMODE]);
X	}
X	(void)strcat(acCmd, " ");
X	(void)strcat(acCmd, pcNew);
X	(void)strcat(acCmd, " ");
X	(void)strcat(acCmd, acDest);
X
X	if (fVerbose) {
X		fprintf(fpOut, "%s: %s\n", progname, acCmd);
X	}
X	(void)fflush(stdout);
X	if (fExec && 0 != system(acCmd)) {
X		return 1;
X	}
X	if (fVerbose) {
X		fprintf(fpOut, "%s: rm -f %s\n", progname, pcNew);
X	}
X	if (fExec) {
X		(void) unlink(pcNew);
X	}
X	*pcEnd = acLockExt[0];
X	if (fExec) {
X		if (-1 == unlink(acDest)) {
X			fprintf(stderr, "%s: unlink: %s: %s\n", progname, acDest, strerror(errno));
X		} else if (fVerbose) {
X			fprintf(fpOut, "%s: unlocked %s\n", progname, acDest);
X		}
X	}
X
X	return 0;
}
X
/*
X * make a new whatis database from all the cat info			(ksb)
X */
int
NewWhatis(n, ppWU, piCounts)
int n;
WHATIS **ppWU;
int *piCounts;
{
X	register WHATIS **ppWUNew;
X	register FILE *fpNew;
X	register int i, f;
X	auto int fdLock;			/* for a create lock	*/
X	auto char *pcNew, *pcEnd;
X	auto char acNew[MAXPATHLEN+1];
X	auto char acDest[MAXPATHLEN+1];
X	auto char acCmd[4*MAXPATHLEN+200];
X
X	fprintf(fpOut, "%s: %d sections ready for whatis\n", progname, n);
X
X	if ('/' == pcWhatis[0] || ('.' == pcWhatis[0] && '/' == pcWhatis[1])) {
X		sprintf(acDest, "%s", pcWhatis);
X	} else {
X		sprintf(acDest, "%s/%s", pcRoot, pcWhatis);
X	}
X	pcEnd = acDest+strlen(acDest);
X	(void)strcpy(pcEnd, acLockExt);
X	if (fExec) {
X		for (i = 0; i < MAXLOOP; ++i) {
X			fdLock = open(acDest, O_EXCL|O_CREAT|O_WRONLY, 0660);
X			if (-1 == fdLock && EEXIST == errno) {
X				if (0 == i) {
X					fprintf(stderr, "%s: contending for database\n", progname);
X					(void)fflush(stderr);
X				}
X				sleep(PDELAY);
X				continue;
X			}
X			if (-1 == fdLock) {
X				fprintf(stderr, "%s: open: %s: %s\n", progname, acDest, strerror(errno));
X				exit(1);
X			}
X			(void)close(fdLock);
X			break;
X		}
X		if (MAXLOOP == i) {
X			fprintf(stderr, "%s: ignoring lock file\n", progname);
X		} else if (fVerbose) {
X			fprintf(fpOut, "%s: locked %s\n", progname, acDest);
X		}
X	}
X	*pcEnd = '\000';
X
X	(void)strcpy(acNew, "/usr/tmp/mkwhatXXXXXX");
X	if ((char *)0 == (pcNew = mktemp(acNew))) {
X		fprintf(stderr, "%s: mktemp: no temp file\n", progname);
X		return 0;
X	}
X	if (NULL == (fpNew = fopen(pcNew, "w"))) {
X		fprintf(stderr, "%s: fopen: %s: %s\n", progname, pcNew, strerror(errno));
X		return 0;
X	}
X
X	if (0 == (ppWUNew = (WHATIS **)malloc((unsigned)n*sizeof(WHATIS *)))) {
X		fprintf(stderr, acNoMem, progname);
X		return 0;
X	}
X	for (i = 0; i < n; ++i) {
X		ppWUNew[i] = wufindmin(piCounts[i], ppWU[i]);
X	}
X	for (;;) {
X		f = -1;
X		for (i = 0; i < n; ++i) {
X			if ((WHATIS *)0 == ppWUNew[i]) {
X				continue;
X			}
X			if (f == -1 || wucmp(ppWUNew[f], ppWUNew[i]) > 0) {
X				f = i;
X			}
X		}
X		if (f == -1)
X			break;
X		wudump(fpNew, ppWUNew[f]);
X		wudequeue(ppWUNew[f]);
X		ppWUNew[f] = wufindmin(piCounts[f], ppWU[f]);
X	}
X	(void)fclose(fpNew);
X
X	(void) sprintf(acCmd, "cmp -s %s %s || INSTALL=\"%s\" install", acDest, pcNew, fVerbose ? "-v" : "");
X	if ((char *)0 != apcModes[WHATISOWNER]) {
X		(void)strcat(acCmd, " -o");
X		(void)strcat(acCmd, apcModes[WHATISOWNER]);
X	}
X	if ((char *)0 != apcModes[WHATISGROUP]) {
X		(void)strcat(acCmd, " -g");
X		(void)strcat(acCmd, apcModes[WHATISGROUP]);
X	}
X	if ((char *)0 != apcModes[WHATISMODE]) {
X		(void)strcat(acCmd, " -m");
X		(void)strcat(acCmd, apcModes[WHATISMODE]);
X	}
X	(void)strcat(acCmd, " ");
X	(void)strcat(acCmd, pcNew);
X	(void)strcat(acCmd, " ");
X	(void)strcat(acCmd, acDest);
X	if (fVerbose) {
X		fprintf(fpOut, "%s: %s\n", progname, acCmd);
X	}
X	(void)fflush(stdout);
X	if (fExec && 0 != system(acCmd)) {
X		return 1;
X	}
X	if (fVerbose) {
X		fprintf(fpOut, "%s: rm -f %s\n", progname, pcNew);
X	}
X	if (fExec) {
X		(void) unlink(pcNew);
X	}
X	*pcEnd = acLockExt[0];
X	if (fExec) {
X		if (-1 == unlink(acDest)) {
X			fprintf(stderr, "%s: unlink: %s: %s\n", progname, acDest, strerror(errno));
X		} else if (fVerbose) {
X			fprintf(fpOut, "%s: unlocked %s\n", progname, acDest);
X		}
X	}
X
X	return 0;
}
X
/*
X * routine for qsort(3)							(ksb)
X */
int
mysort(ppcLeft, ppcRight)
char **ppcLeft, **ppcRight;
{
X	return strcmp(*ppcLeft, *ppcRight);
}
X
/*
X * read the whatis info out of a formatted manual page			(ksb)
X * this allows us to understand what should be installed
X * or removed from the cat dirs and whatis info...
X *
X * return non-zero on error
X */
int
WUGrok(fpFmt, pPTPage, pWU)
FILE	*fpFmt;		/* file to read stuff out of		(in)	*/
PATH	*pPTPage;	/* raw page				(in)	*/
WHATIS	*pWU;		/* whatis entries to fill in		(out)	*/
{
X	extern char *strsave();
X	auto char acBase[MAXPATHLEN+1];
X	auto char acSection[MAXPATHLEN+1];
X	auto char acHeader[MAXPATHLEN+1];
X	auto char **ppcLinks, *pcAlso, *pcSummary, *pcHSection;
X	auto int iLen;
X
X	pWU->ilen = 0;
X	(void)strcpy(acBase, PTBase(pPTPage));
X	(void)strcpy(acSection, PTExt(pPTPage));
X
X	/* rdlinks leaves LN_PAD (char *)0's for us - how nice
X	 */
X	if (0 != rdlinks(fpFmt, PTFull(pPTPage), & ppcLinks, & iLen, & pcAlso, acHeader)) {
X		return 1;
X	}
X	pcSummary = ppcLinks[--iLen];	/* summary is last line	*/
X
X	if (!SplitHead(acHeader, & pcHSection)) {
X		fprintf(fpOut, "%s: `%s\' has a malformed header line\n", progname, PTLocal(pPTPage));
X		return 1;
X	}
X	if (0 == strcasecmp(acSection, "man")) {
X		if (!isdigit(*pcHSection)) {
X			fprintf(fpOut, "%s: `%s\' header extender `%s\' not numeric\n", progname, PTLocal(pPTPage), pcHSection);
X		}
X		(void)strcpy(acSection, pcHSection);
X	} else if (0 != strcasecmp(acSection, pcHSection)) {
X		fprintf(fpOut, "%s: `%s\' doesn\'t match its header extender `%s\' (not fatal)\n", progname, PTLocal(pPTPage), pcHSection);
X	}
X
X	/* here we should search the alias list for the one in the header
X	 * (acHeader) and shift our weight to that one
X	 */
X	if (0 != strcasecmp(acBase, acHeader)) {
X		register int i;
X		for (i = 0; i < iLen; ++i) {
X			if (0 != strcasecmp(ppcLinks[i], acHeader)) {
X				continue;
X			}
X			(void)strcpy(acBase, ppcLinks[i]);
X			break;
X		}
X		if (i == iLen) {
X			fprintf(fpOut, "%s: `%s.%s\' doesn\'t match its header name `%s\' (not fatal)\n", progname, acBase, acSection, acHeader);
X			ppcLinks[iLen++] = acHeader;
X		}
X	}
X
X	/* put on man name	*/
X	ppcLinks[iLen++] = pWU->pcbase = strsave(acBase);
X	ppcLinks[iLen] = (char *)0;
X	qsort((char *)ppcLinks, iLen, sizeof(char *), mysort);
X	uniq(ppcLinks, & iLen);
X
X	/* for the big whatis update at the end we save these
X	 * (note we saved the base name already)
X	 */
X	pWU->isection = atoi(acSection);
X	pWU->ilen = iLen;
X	pWU->ppclist = ppcLinks;
X	pWU->pcsummary = pcSummary;
X	pWU->pcext = strsave(acSection);
X	pWU->pcalso = pcAlso;
X
X	return 0;
}
X
/*
X * remove the links that are in both pWUCool and pWUOld from pWUOld	(ksb)
X * This keeps us from removing a link, then putting it back
X * return the number of links in common
X */
int
WUStrike(pWUOld, pWUCool)
WHATIS *pWUOld, *pWUCool;
{
X	register int cnt, i, j, c;
X
X	cnt = 0;
X	for (j = i = 0; j < pWUCool->ilen; ++j) {
X		while (i < pWUOld->ilen &&
X		   0 < (c = strcmp(pWUCool->ppclist[j], pWUOld->ppclist[i]))) {
X			++i;
X		}
X		if (i == pWUOld->ilen)
X			break;
X		if (0 != c) {
X			continue;
X		}
X		c = i;
X		do {
X			pWUOld->ppclist[c] = pWUOld->ppclist[c+1];
X		} while (++c < pWUOld->ilen);
X		--pWUOld->ilen;
X		++cnt;
X	}
X	return cnt;
}
Purdue
chmod 0444 mkcat/genwhatis.c ||
echo 'restore of mkcat/genwhatis.c failed'
Wc_c="`wc -c < 'mkcat/genwhatis.c'`"
test 15821 -eq "$Wc_c" ||
	echo 'mkcat/genwhatis.c: original size 15821, current size' "$Wc_c"
fi
# ============= mkcat/format.c ==============
if test -f 'mkcat/format.c' -a X"$1" != X"-c"; then
	echo 'x - skipping mkcat/format.c (File already exists)'
else
echo 'x - extracting mkcat/format.c (Text)'
sed 's/^X//' << 'Purdue' > 'mkcat/format.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
/*  $Id: format.c,v 3.4 90/10/23 16:43:43 ksb Exp $
X *
X * format - make a cat (formatted) page from a man page
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>
#include <fcntl.h>
#if defined(SYSV)
#include <ndir.h>
#else
#include <sys/dir.h>
#endif
#include <sys/stat.h>
X
#if !defined(MAXPATHLEN)
#define MAXPATHLEN	1024
#endif
X
#include "main.h"
#include "pt.h"
#include "genwhatis.h"
#include "readman.h"
#include "scan.h"
#include "mkcat.h"
X
extern FILE *popen();
extern int errno;
extern char *sys_errlist[];
#define strerror(Me) sys_errlist[Me]
extern char *strrchr(), *strcpy(), *strchr(), *strcat();
extern char *malloc(), *mktemp();
X
#if !defined(F_OK)
#define F_OK	0
#endif
X
X
char acRoot[] =			/* root of the formatted tree		*/
X	"/usr/man";
char acCat[] =			/* cat dir basename			*/
X	"cat";
char acWhatis[] =		/* whatis database to updtae		*/
X	"whatis";
char acTemp[] =			/* a temp file with some space		*/
X	"/usr/tmp/mkcatXXXXXX";
X
char acDotZ[] =		/* compresses extender			*/
X	".Z";
X
X
/*
X * warn of multiple manual pages, one name foo.1l, one foo.1 or		(ksb)
X * some such (only for the name provided)
X */
void
WarnMulti(iSection, ppcBase, pcSection)
int iSection;
char **ppcBase, *pcSection;
{
X	auto char acDir[MAXPATHLEN+1];
X	register DIR *pDIRCat;
X	register struct direct *pEnt;
X	register char *pcFile, **ppcTemp;
X	register char *pcHack;
X
X	if ('/' == pcCat[0] || ('.' == pcCat[0] && '/' == pcCat[1]))
X		(void)sprintf(acDir, "%s%d", pcCat, iSection);
X	else
X		(void)sprintf(acDir, "%s/%s%d", pcRoot, pcCat, iSection);
X	if ((DIR *)0 == (pDIRCat = opendir(acDir))) {
X		fprintf(stderr, "%s: opendir: %s: %s\n", progname, acDir, strerror(errno));
X		exit(1);
X	}
X
X	while ((struct direct *)0 != (pEnt = readdir(pDIRCat))) {
X		pcFile = pEnt->d_name;
X		if ((char *)0 == (pcHack = strrchr(pcFile, '.'))) {
X			/* ignore short files... or dirs */
X			continue;
X		}
X
X		/* ignore compress suffix on installed pages */
X		if (0 == strcmp(acDotZ, pcHack)) {
X			*pcHack = '\000';
X			/* d_namlen is now wrong... do we care? */
X			if ((char *)0 == (pcHack = strrchr(pcFile, '.'))) {
X				continue;
X			}
X		}
X		*pcHack++ = '\000';
X
X		/* ignore intro pages, `.', and `..'
X		 */
X		if ('.' == pcFile[0] && ('\000' == pcFile[1] ||
X		     ('.' == pcFile[1] && '\000' == pcFile[2])) ||
X		    0 == IsOKBase(pcFile)) {
X			continue;
X		}
X
X		/* go through the (sorted!) list of names
X		 * quit on match with bad extender, or no more to check
X		 * (too bad the directory is not sorted here)
X		 */
X		for (ppcTemp = ppcBase; (char *)0 != *ppcTemp; ++ppcTemp) {
X			register int cp;
X			cp = strcmp(pcFile, *ppcTemp);
X			if (0 < cp)
X				continue;
X			if (0 > cp)
X				break;
X			if (ExtConflict(pcHack, pcSection)) {
X				fprintf(fpOut, "%s: %s.%s and %s.%s both live in %s\n", progname, pcFile, pcHack, pcFile, pcSection, acDir);
X			}
X		}
X	}
X	(void)closedir(pDIRCat);
}
X
X
/*
X * sort -u, kinda							(ksb)
X */
uniq(ppcVec, piLen)
char **ppcVec;
int *piLen;
{
X	register char **ppcFrom, **ppcList;
X
X	ppcList = ppcVec;
X	if ((char *)0 == ppcList[0])
X		return;
X	for (ppcFrom = ppcList+1; (char *)0 != *ppcFrom; ++ppcFrom) {
X		if (0 != strcmp(*ppcFrom, *ppcList))
X			*++ppcList = *ppcFrom;
X		else
X			--*piLen;
X	}
X	ppcList[1] = (char *)0;
}
X
/*
X * split off the two parts of BAR(1)				(ksb)
X *                            ^^^ ^
X * (also handles missformatted bold open parens)
X */
int
SplitHead(pcHeader, ppcOpen)
char *pcHeader, **ppcOpen;
{
X	register char *pcTemp, *pcClose, *pcOpen;
X	register int retval = 1;
X
X	for (pcTemp = pcHeader; '\000' != *pcTemp; ++pcTemp) {
X		if (isupper(*pcTemp)) {
X			*pcTemp = tolower(*pcTemp);
X		}
X	}
X
X	if ((char *)0 != (pcOpen = strchr(pcHeader, '(') /*)*/)) {
X		while ('\b' == pcOpen[1] && '('/*)*/ == pcOpen[2])
X			pcOpen += 2;
X		*pcOpen++ = '\000';
X		if ((char *)0 != (pcClose = strchr(pcOpen, ')'))) {
X			*pcClose = '\000';
X		} else {
X			retval = 0;
X		}
X	} else {
X		retval = 0;
X	}
X	*ppcOpen = pcOpen;
X	return retval;
}
X
X
/*
X * format the given page with the given macro package			(ksb)
X * put it in the named file
X */
static int
MkFmt(pPTPage, pcTemp)
PATH	*pPTPage;	/* raw page				(in)	*/
char	*pcTemp;	/* temp name to build it in		(in)	*/
{
X	auto char acCmd[MAXPATHLEN*2+sizeof(acTemp)+400];
X	auto FILE *fpFake;
X
#if !defined(R_OK)
#define R_OK	4
#endif
X	if (0 != access(PTFull(pPTPage), R_OK)) {
X		fprintf(stderr, "%s: access: %s: %s\n", progname, PTFull(pPTPage), strerror(errno));
X		return 1;
X	}
X
X	(void)sprintf(acCmd, "MK=\"%s\" mk -m%s %s >%s", fVerbose ? "" : "-s", (char *)0 != pcMkToken ? pcMkToken : "Mkcat", PTFull(pPTPage), pcTemp);
X
X	if (fVerbose) {
X		fprintf(fpOut, "%s: %s\n", progname, acCmd);
X	}
X	(void)fflush(fpOut);
X	(void)fflush(stderr);
X
X	if (fExec)
X		return 0 != system(acCmd);
X
X	if (NULL == (fpFake = fopen(pcTemp, "w"))) {
X		fprintf(stderr, "%s: fopen: %s: %s\n", progname, pcTemp, strerror(errno));
X		return 1;
X	}
X	(void)fprintf(fpFake, "%s(", PTBase(pPTPage));
X	(void)fprintf(fpFake, "%s) Fake man page\n\n%s\n", PTExt(pPTPage), acMark);
X	(void)fprintf(fpFake, "\t%s - a dry run line\n\nfaked for your protection\n", PTBase(pPTPage));
X	(void)fclose(fpFake);
X	return 0;
}
X
/*
X * update an installed manual page, remove the old one and install new	(ksb)
X *
X * format it, all whatis lines to DEL or ADD list
X * install the page
X * remove or build links
X * let caller update whatis as a group
X *
X * #1 why we remove an old page, if we replace all its links
X *	in this case we have an old page called `OLD(1)' which
X *	has a NAME section `old, new, other - things'.  The new page
X *	has changed the names around (it happens) to call the page
X *	`NEW(1)' with a NAME section `new, old, other -- things' or
X *	we have added a new name `BETTER(1)' and kept all the old NAMES
X *	... in all cases we can remove the old page becasue we superceed
X *	it!
X *	If we do not cover *all* the old links we tell the user to run
X *	rmcat on the `base' manual page or remove the collisions.
X *	An example? cd(1) and sh(1).  If you run mkcat on cd(1) it will
X *	tell you to remove sh(1) or remove the name `cd' in sh(1)!
X *	*** As it should! ***
X */
int
ModFmt(pPTPage, pWU, pWUDel)
PATH *pPTPage;
WHATIS *pWU, *pWUDel;
{
X	auto FILE *fpFmt;
X	auto char acDest[MAXPATHLEN+1];
X	auto char acTName[sizeof(acTemp)+sizeof(acDotZ)+1];
X	auto char acCmd[MAXPATHLEN+sizeof(acTemp)+1000+400];
X	auto int fWasThere;
X	auto PATH PTDel;
X	auto struct stat stDest;
X
X	if (! PTHasExt(pPTPage)) {
X		fprintf(fpOut, "%s: `%s\' no manual section extender\n", progname, PTFull(pPTPage));
X		return 1;
X	}
X
X	if (! isdigit(*PTExt(pPTPage)) && 0 != strcasecmp("man", PTExt(pPTPage))) {
X		fprintf(stderr, "%s: `%s\' missing section number in extender\n", progname, PTFull(pPTPage));
X		return 1;
X	}
X
X
X	/* make a formatted page in a temp space and read the link info
X	 */
X	(void)strcpy(acTName, acTemp);
X	if ((char *)0 == mktemp(acTName)) {
X		return 1;
X	}
X	if (fFormat) {
X		/* MkFmt will fake a formatted copy if we are running under
X		 * -n, clever, eh?
X		 */
X		if (0 != MkFmt(pPTPage, acTName)) {
X			return 1;
X		}
X	} else {
X		if (PTIsComp(pPTPage)) {
X			(void)sprintf(acCmd, "zcat %s >%s", PTFull(pPTPage), acTName);
X		} else {
X			(void)sprintf(acCmd, "cp %s %s", PTFull(pPTPage), acTName);
X		}
X		if (fVerbose) {
X			(void)printf("%s: %s\n", progname, acCmd);
X		}
X		if (0 != system(acCmd)) {
X			fprintf(stderr, "%s: cp failed\n", progname);
X			return 1;
X		}
X	}
X	if (NULL == (fpFmt = fopen(acTName, "r"))) {
X		fprintf(stderr, "%s: fopen: %s: %s\n", progname, PTFull(pPTPage), strerror(errno));
X		return 1;
X	}
X	if (0 != WUGrok(fpFmt, pPTPage,  pWU)) {
X		(void)fclose(fpFmt);
X		return 1;
X	}
X	(void)fclose(fpFmt);
X
X	if (0 != strcmp(pWU->pcbase, PTBase(pPTPage))) {
X		fprintf(fpOut, "%s: `%s\' should be named `%s.%s\' (repaired)\n", progname, PTFull(pPTPage), pWU->pcbase, pWU->pcext);
X	}
X
X	if ('/' == pcCat[0] || ('.' == pcCat[0] && '/' == pcCat[1])) {
X		(void)sprintf(acDest, "%s%d/%s.%s%s", pcCat, pWU->isection, pWU->pcbase, pWU->pcext, fCompress ? acDotZ : "");
X	} else {
X		(void)sprintf(acDest, "%s/%s%d/%s.%s%s", pcRoot, pcCat, pWU->isection, pWU->pcbase, pWU->pcext, fCompress ? acDotZ : "");
X	}
X	PTInit(&PTDel, acDest);
X
X	/* if an old one exists read it for links
X	 * (no error if there is not one, might be a new file)
X	 */
X	pWUDel->ilen = 0;
X	fWasThere = (-1 != LSTAT(acDest, &stDest));
X	if (fWasThere) {
X		register int r = 0;
X		if (PTIsComp(&PTDel)) {
X			(void)sprintf(acCmd, "exec zcat %s", acDest);
X			if (NULL != (fpFmt = popen(acCmd, "r"))) {
X				r = WUGrok(fpFmt, &PTDel, pWUDel);
X			}
X			(void)pclose(fpFmt);
X		} else {
X			if (NULL != (fpFmt = fopen(acDest, "r"))) {
X				r = WUGrok(fpFmt, &PTDel, pWUDel);
X			}
X			(void)fclose(fpFmt);
X		}
X		if (0 != r) {
X			printf("%s: %s: don\'t grok old man page\n", progname, acDest);
X			fWasThere = 0;
X		}
X	}
X	if (fDelete && !fWasThere) {
X		/* no page to remove, fail */
X		return 1;
X	}
X
X	(void)sprintf(acCmd, "INSTALL=\"%s\" install%s", fVerbose ? "-v" : "", fDelete ? " -R" : "");
X	if ((char *)0 != apcModes[PAGEOWNER]) {
X		(void)strcat(acCmd, " -o");
X		(void)strcat(acCmd, apcModes[PAGEOWNER]);
X	}
X	if ((char *)0 != apcModes[PAGEGROUP]) {
X		(void)strcat(acCmd, " -g");
X		(void)strcat(acCmd, apcModes[PAGEGROUP]);
X	}
X	if ((char *)0 != apcModes[PAGEMODE]) {
X		(void)strcat(acCmd, " -m");
X		(void)strcat(acCmd, apcModes[PAGEMODE]);
X	}
X
X	if (fDelete) {
X		if (0 != strcmp(pWUDel->pcbase, pWU->pcbase) || 0 != strcmp(pWUDel->pcext, pWU->pcext)) {
X			(void)printf("%s: `%s.%s\' doesn't match `%s.%s\'\n", progname, pWU->pcbase, pWU->pcext, pWUDel->pcbase, pWUDel->pcext);
X			(void)printf("%s: use `%s -Dv -f %s\' to remove page\n", progname, progname, acDest);
X			return 1;
X		}
X		ModLinks(&PTDel, pWUDel, (struct direct **)0, 0, &stDest, 1);
X		(void)WUStrike(pWU, pWUDel);
X	} else {
X		if (-1 == access(PTDir(&PTDel), F_OK)) {
X			MakeDir(PTDir(&PTDel), DIROWNER, DIRGROUP, DIRMODE);
X		}
X		/* remove common links from pWUDel, remove non-common
X		 * from catdirs (links are not common if extender is
X		 * different!)
X		 */
X		if (fWasThere && 0 == strcmp(pWUDel->pcbase, pWU->pcbase)) {
X			if (0 == strcmp(pWUDel->pcext, pWU->pcext)) {
X				(void)WUStrike(pWUDel, pWU);
X			}
X			switch (stDest.st_mode & S_IFMT) {
X			case S_IFREG:
X				ModLinks(&PTDel, pWUDel, (struct direct **)0, 0, &stDest, 1);
X				break;
#if HAVE_SLINKS
X			case S_IFLNK:
X				fprintf(stderr, "%s: run -L before installing `%s\'\n", progname, acDest);
X				return 1;
#endif
X			default:
X				fprintf(stderr, "%s: %s: not a plain file or link\n", progname, acDest);
X				break;
X			}
X		}
X
X		WarnMulti(pWU->isection, pWU->ppclist, pWU->pcext);
X
X		/* compress the temp file if it needs it
X		 */
X		if (fCompress) {
X			auto char acDoComp[MAXPATHLEN+100];
X			(void)sprintf(acDoComp, "exec compress -f %s", acTName);
X			if (0 != system(acDoComp)) {
X				fprintf(stderr, "%s: system: `%s\' failed\n", progname, acDoComp);
X				return 1;
X			}
X			if (-1 == access(acTName, F_OK)) {
X				(void)strcat(acTName, acDotZ);
X				if (-1 == access(acTName, R_OK)) {
X					fprintf(stderr, "%s: compress lost my file, `%s\'\n", progname, acTName);
X					return 1;
X				}
X			}
X		}
X
X		(void)strcat(acCmd, " ");
X		(void)strcat(acCmd, acTName);
X	}
X
X	(void)strcat(acCmd, " ");
X	(void)strcat(acCmd, acDest);
X
X	if (fVerbose) {
X		fprintf(fpOut, "%s: %s\n", progname, acCmd);
X	}
X	(void)fflush(stdout);
X	if (fExec && 0 != system(acCmd)) {
X		return 1;
X	}
X
X	/* now we have to put in the new links if needed
X	 */
X	if (!fDelete) {
X		ModLinks(&PTDel, pWU, (struct direct **)0, 0, fWasThere ? &stDest : (struct stat *)0, 0);
X	}
X
X	/* install removed the temp file for us (unless the user set -c)
X	 * in any event we try to unlink it, just in case install bombed
X	 */
X	if (fVerbose) {
X		printf("%s: rm -f %s\n", progname, acTName);
X	}
X	(void)unlink(acTName);
X
X	return 0;
}
Purdue
chmod 0444 mkcat/format.c ||
echo 'restore of mkcat/format.c failed'
Wc_c="`wc -c < 'mkcat/format.c'`"
test 12839 -eq "$Wc_c" ||
	echo 'mkcat/format.c: original size 12839, current size' "$Wc_c"
fi
# ============= mk/setenv.c ==============
if test ! -d 'mk'; then
    echo 'x - creating directory mk'
    mkdir 'mk'
fi
if test -f 'mk/setenv.c' -a X"$1" != X"-c"; then
	echo 'x - skipping mk/setenv.c (File already exists)'
else
echo 'x - extracting mk/setenv.c (Text)'
sed 's/^X//' << 'Purdue' > 'mk/setenv.c' &&
#include <stdio.h>
#include "machine.h"
X
#define nil	((char *)0)
extern char *strcpy(), *strcat();
X
/*
X * set an environment variable						(ksb)
X * for unsetenv call with pchValue set to nil
X *
X * This routine modifies the global "environ" and will
X * build a new list if it must grow (it doesn't free the old one
X * since it may not have been malloc'd)  It will overwrite the
X * old value if the new one will fit, else is mallocs one large enough.
X *
X * returns nothing (an int)
X */
int
setenv(pchName, pchValue)
char *pchName, *pchValue;
{
X	extern char **environ, *calloc(), *malloc();
X	extern int strlen();
X	register char **ppch, *pch;
X	register int len, nv;
X
X	nv = 0;
X	len = strlen(pchName);
X	for (ppch = environ; nil != (pch = *ppch); ++ppch) {
X		if (0 == strncmp(pch, pchName, len) && '=' == pch[len]) {
X			break;
X		}
X		++nv;
X	}
X
X	if (nil == pchValue) {
X		if (nil == pch)	{
X			return ;
X		}
X		while (nil != (ppch[0] = ppch[1])) {
X			++ppch;
X		}
X		return ;
X	}
X
X	if (nil == pch) {
X		++nv;
X		ppch = (char **)calloc(nv+1, sizeof(char *));
X		while (nv) {
X			--nv;
X			ppch[nv+1] = environ[nv];
X		}
X		environ = ppch;
X	}
X
X	len += strlen(pchValue) + 1;
X	if (nil == pch || strlen(pch) < len)
X		pch = malloc(len+1);
X	strcpy(pch, pchName);
X	strcat(pch, "=");
X	strcat(pch, pchValue);
X	*ppch = pch;
}
Purdue
chmod 0444 mk/setenv.c ||
echo 'restore of mk/setenv.c failed'
Wc_c="`wc -c < 'mk/setenv.c'`"
test 1292 -eq "$Wc_c" ||
	echo 'mk/setenv.c: original size 1292, current size' "$Wc_c"
fi
# ============= mk-lib/m-clean ==============
if test ! -d 'mk-lib'; then
    echo 'x - creating directory mk-lib'
    mkdir 'mk-lib'
fi
if test -f 'mk-lib/m-clean' -a X"$1" != X"-c"; then
	echo 'x - skipping mk-lib/m-clean (File already exists)'
else
echo 'x - extracting mk-lib/m-clean (Text)'
sed 's/^X//' << 'Purdue' > 'mk-lib/m-clean' &&
# the Clean target removes the junk generated by the other targets	(ksb)
X
$Clean(*):	${rm-rm} -f %P.o
$Clean(*):	${rm-rm} -f %P
$Clean(*):	${make-make} %s clean
$Clean(*):	${make-make} clean
Purdue
chmod 0444 mk-lib/m-clean ||
echo 'restore of mk-lib/m-clean failed'
Wc_c="`wc -c < 'mk-lib/m-clean'`"
test 191 -eq "$Wc_c" ||
	echo 'mk-lib/m-clean: original size 191, current size' "$Wc_c"
fi
# ============= mkcat/readman.c ==============
if test -f 'mkcat/readman.c' -a X"$1" != X"-c"; then
	echo 'x - skipping mkcat/readman.c (File already exists)'
else
echo 'x - extracting mkcat/readman.c (Text)'
sed 's/^X//' << 'Purdue' > 'mkcat/readman.c' &&
/*
X * $Id: readman.c,v 3.4 90/11/28 09:44:05 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 * routines to read a manual page and extract useful facts		(ksb)
X * $Compile: ${cc-cc} ${cc_debug--g} -DTEST %f -o %F
X */
#include "machine.h"
#include <stdio.h>
#include <ctype.h>
X
#include "main.h"
#include "readman.h"
#include "mkcat.h"
X
extern char *malloc(), *realloc(), *calloc(), *strrchr();
extern char *strcat(), *strcpy();
extern int strlen(), errno;
extern char *sys_errlist[];
#define strerror(Me) (sys_errlist[Me])
X
static char *apcFmts[] = {
X	"normal",
X	"bold",
X	"underline",
X	"bold & underlined",
X	"overstruck",
X	"bold & overstruck",
X	"underline & overstruck",
X	"bold & underlined & overstruck",
X	(char *)0
};
X
char acMark[] =		/* the line following this mark			*/
X	"NAME";
char acAlso[] =		/* the pages we refer to follow this line	*/
X	"SEE ALSO";
char acNoMem[] =		/* message for out of mem		*/
X	"%s: out of memory\n";
X
# if 0
/* someday this will tell the user about unknown section names
X */
static char *apcHeaders[] = {
X	"AUTHOR",
X	"AUTHORS",
X	"AVAILABILITY",
X	"BUGS",
X	"CAVEATS",
X	"COMMANDS",
X	"COPYRIGHT",
X	"DESCRIPTION",
X	"DIAGNOSTICS",
X	"ENVIRONMENT",
X	"ENVIRONMENT VARIABLES",
X	"EXAMPLES",
X	"EXIT CODES",
X	"FILES",
X	"FUNCTIONS",
X	"HINTS",
X	"INITIALIZATION",
X	"LIMITATIONS",
X	"NAME",
X	"NOTES",
X	"OPTIONS",
X	"RETURN VALUES",
X	"ROUTINES",
X	"SEE ALSO",
X	"SUMMARY",
X	"SYNOPSIS",
X	"USAGE",
X	"VARIABLES",
X	(char *)0
};
#endif
X
/*
X * save a string in malloced memory					(ksb)
X */
char *
strsave(pc)
char *pc;
{
X	register char *pcRet;
X
X	if ((char *)0 == (pcRet = malloc((unsigned)strlen(pc)+1))) {
X		fprintf(stderr, acNoMem, progname);
X		exit(1);
X	}
X	return strcpy(pcRet, pc);
}
X
/*
X * strip nroff nonsense of a string					(ksb)
X */
int
StripFmt(pcBuf)
char *pcBuf;
{
X	register int fmt = FMT_NORMAL;
X	register char *pcScan;
X
X	for (pcScan = pcBuf; '\000' != *pcScan; ++pcScan) {
X		if ('\b' == pcScan[1]) {
X			if ('_' == pcScan[0]) {
X				fmt |= FMT_UNDERLINE;
X			} else if (pcScan[0] == pcScan[2]) {
X				fmt |= FMT_BOLD;
X			} else {
X				fmt |= FMT_OVERSTRIKE;
X			}
X			++pcScan;
X			continue;
X		}
X		*pcBuf++ = *pcScan;
X	}
X	*pcBuf = '\000';
X	return fmt;
}
X
/*
X * compare a mark and a word which might be bold or something		(ksb)
X */
static int
markcmp(pcLine, pcMark, mLen)
char *pcLine, *pcMark;
int mLen;
{
X	register int i;
X
X	for (i = 0; i < mLen; ++i) {
X		while ('\000' != pcLine[0] && '\b' == pcLine[1]) {
X			pcLine += 2;
X		}
X		if (*pcLine != *pcMark)
X			return *pcLine - *pcMark;
X		++pcLine, ++pcMark;
X	}
X	return 0;
}
X
/*
X * Build the names of the symbolic links the (soon to be installed)	(ksb)
X * manual page.
X * *we must* leave LN_PAD (char *)0's on the end of the vector
X * (one will be replaced be the basename before we sort -u them)
X * We will also find the header name:
X *	MKCAT(8)	....	....
X *      ^^^^^^^^
X * and save it, if pcHeader points to a buffer of about BUFSIZ.
X */
int
rdlinks(fpFmt, pcSrc, pppcVec, piLen, ppcAlso, pcHeader)
register FILE
X	*fpFmt;				/* input file to read		*/
char	*pcSrc,				/* name to call it (for user)	*/
X	***pppcVec,			/* place to store list of names	*/
X	**ppcAlso,			/* place to put see alsos	*/
X	*pcHeader;			/* place to store header name	*/
int	*piLen;				/* number of links		*/
{
X	auto char *pcBuf, *pcAlsoLine;
X	register char *pcLine, *pcNewLine, **ppcLinks, **ppcMode;
X	register int i;
X	register char ch;
#define MODE_HEADER	0
#define MODE_SCAN	1
#define MODE_LINK	2
#define MODE_LINK2	3
#define MODE_BODY	4
#define MODE_ALSO	5
#define MISSING_PAREN	1
X	auto int iLen, iMode, iMissing, fHasDash;
X	auto char acLine[BUFSIZ];
X	extern char *strchr();
X
X	pcBuf = (char *)0;
X	pcAlsoLine = (char *)0;
X	iMode = MODE_HEADER;
X	iMissing = 0;
X	while ((char *)0 != (pcLine = fgets(acLine, BUFSIZ, fpFmt))) {
X		while (isspace(*pcLine) && '\000' != *pcLine) {
X			++pcLine;
X		}
#if 0
X		printf("%s: %s", MODE_HEADER == iMode ? "header" : MODE_SCAN == iMode ? "scanner" : "links" , pcLine);
#endif
X		if (MODE_HEADER == iMode) {
X			register char *pcClose;
X
X			if ((char *)0 == (pcClose = strchr(pcLine, /*(*/')'))) {
X				if (0 == markcmp(pcLine, acMark, sizeof(acMark)-1)) {
X					(void)fprintf(fpOut, "%s: %s: bad header line or %s line before header\n", progname, pcSrc, acMark);
X					return 1;
X				}
X				if (strlen(pcLine) > 2) {
X					iMissing |= MISSING_PAREN;
X				}
X				continue;
X			}
X			*++pcClose = '\000';
X			if ((char *)0 != pcHeader) {
X				(void)strcpy(pcHeader, pcLine);
X			}
X			iMode = MODE_SCAN;
X			continue;
X		}
X		if (MODE_SCAN == iMode) {
X			if (0 == markcmp(pcLine, acMark, sizeof(acMark)-1)) {
X				iMode = MODE_LINK;
X				ppcMode = &pcBuf;
X			}
X			continue;
X		}
X		if (MODE_BODY == iMode) {
X			if (0 == markcmp(pcLine, acAlso, sizeof(acAlso)-1)) {
X				iMode = MODE_ALSO;
X				ppcMode = &pcAlsoLine;
X			}
X			continue;
X		}
X		if ((char *)0 != (pcNewLine = strrchr(pcLine, '\n'))) {
X			do {
X				*pcNewLine = '\000';
X			} while (pcNewLine != pcLine && (--pcNewLine, isspace(*pcNewLine)));
X		}
X		if ('\000' == *pcLine) {
X			if (MODE_ALSO == iMode || (char **)0 == ppcAlso)
X				break;
X			iMode = MODE_BODY;
X			continue;
X		}
X
X		/* does the input line have the magic dash?
X		 */
X		if (MODE_ALSO != iMode) {
X			register char *pcHack;
X
X			pcHack = pcLine;
X			while ((char *)0 != (pcHack = strchr(pcHack, '-'))) {
X				if ((pcHack == pcLine || isspace(pcHack[-1])) &&
X				     ('\000' == pcHack[1] || isspace(pcHack[1]))) {
X					break;
X				}
X				++pcHack;
X			}
X			fHasDash = ((char *)0 != pcHack);
X		} else {
X			fHasDash = 0;
X		}
X		if ((char *)0 == (*ppcMode)) {
X			iLen = strlen(pcLine)+1;
X			(*ppcMode) = malloc((unsigned)iLen+1);
X			if ((char *)0 == (*ppcMode)) {
X				fprintf(stderr, acNoMem, progname);
X				exit(1);
X			}
X			(void)strcpy((*ppcMode), pcLine);
X		} else {
X			register int temp;
X
X			if (MODE_LINK2 == iMode && fHasDash) {
X				fprintf(fpOut, "%s: %s: too many whatis entries at `%s\'\n", progname, pcSrc, pcLine);
X				break;
X			}
X			temp = strlen(pcLine)+1; /* put in a space */
X			(*ppcMode) = realloc((char *)(*ppcMode), (unsigned) iLen+temp+8);
X			if ((char *)0 == (*ppcMode)) {
X				fprintf(stderr, acNoMem, progname);
X				exit(1);
X			}
X			/* join lines that are hyphenated
X			 * we know iLen is >2 we assume it is >>2
X			 */
X			if ('-' == (*ppcMode)[iLen-2] && !isspace((*ppcMode)[iLen-3])) {
X				--iLen, --temp;
X				(void)strcpy(& (*ppcMode)[iLen-1], pcLine);
X			} else {
X				(*ppcMode)[iLen-1] = ' ';
X				(void)strcpy(& (*ppcMode)[iLen], pcLine);
X			}
X			iLen += temp;
X		}
X		if (fHasDash) {
X			iMode = MODE_LINK2;
X		}
X	}
X
X	if ((char *)0 == pcBuf) {
X		if (iMissing & MISSING_PAREN) {
X			(void)fprintf(fpOut, "%s: %s: header line missing parenthesis?\n", progname, pcSrc);
X		} else {
X			fprintf(fpOut, "%s: `%s\' has no %s line\n", progname, pcSrc, acMark);
X		}
X		return 1;
X	}
X
X	/* here we have something like "foo, bar, wham - meta words"
X	 * we need to build a vector "foo", "bar", "wham", "meta words"
X	 */
X	ch = '^';
X	iLen = 0;
X	for (pcLine = pcBuf; '\000' != *pcLine; ++pcLine) {
X		if (isspace(ch) && '-' == pcLine[0] && isspace(pcLine[1])) {
X			++iLen;
X			break;
X		}
X		if (',' == *pcLine || (0 == iLen && ':' == *pcLine)) {
X			++iLen;
X		}
X		ch = *pcLine;
X	}
X	if ('\000' == *pcLine || 0 == iLen) {
X		if ((char *)0 != pcBuf)
X			(void) free(pcBuf);
X		fprintf(fpOut, "%s: `%s\' has a bad %s line (too short)?\n", progname, pcSrc, acMark);
X		return 1;
X	}
X	++iLen;
X	ppcLinks = (char **)calloc((unsigned) iLen+LN_PAD, sizeof(char *));
X	if ((char **)0 == ppcLinks) {
X		fprintf(stderr, acNoMem, progname);
X		exit(1);
X	}
X	iLen = 1;
X	ppcLinks[0] = pcBuf;
X	ch = '^';
X	for (pcLine = pcBuf; '\000' != *pcLine; ++pcLine) {
X		if (! (isspace(ch) && '-' == pcLine[0] && isspace(pcLine[1])) &&
X		    ',' != *pcLine && !(iLen == 1 && ':' == *pcLine)) {
X			ch = *pcLine;
X			continue;
X		}
X		ch = *pcLine;
X		pcNewLine = pcLine;
X		while (pcNewLine > pcBuf && (--pcNewLine, isspace(*pcNewLine)))
X			*pcNewLine = '\000';
X		do {
X			*pcLine++ = '\000';
X		} while (isspace(*pcLine));
X		ppcLinks[iLen++] = pcLine;
X		if ('-' == ch) {
X			break;
X		}
X	}
X
X	/* search links for poor names, and warn.
X	 */
X	for (i = 0; i < iLen-1; ++i) {
X		register char *pcTemp;
X		register int fmt;
X
X		pcTemp = ppcLinks[i];
X		fmt = StripFmt(pcTemp);
X		if (FMT_NORMAL != fmt) {
X			fprintf(fpOut, "%s: link `%s\' should not be %s\n", progname, pcTemp, apcFmts[fmt]);
X		}
X		while ('\000' != *pcTemp && !isspace(*pcTemp)) {
X			++pcTemp;
X		}
X		if ('\000' != *pcTemp) {
X			register int j;
X			fprintf(fpOut, "%s: warning link `%s\' has a space in it (skipped)\n", progname, ppcLinks[i]);
X			--iLen;
X			for (j = i; j < iLen; ++j)
X				ppcLinks[j] = ppcLinks[j+1];
X			--i;
X			continue;
X		}
X		while ((char *)0 != (pcTemp = strrchr(ppcLinks[i], '/'))) {
X			/* eat trailing slash, paranoid */
X			if ('\000' == pcTemp[1]) {
X				*pcTemp = '\000';
X				continue;
X			}
X			++pcTemp;
X			fprintf(fpOut, "%s: link `%s\' has a `/\' in it, changed to `%s\'\n", progname, ppcLinks[i], pcTemp);
X			ppcLinks[i] = pcTemp;
X			break;
X		}
X	}
X
X	(void)StripFmt(ppcLinks[iLen-1]);
X	ch = '^';
X	for (pcLine = pcBuf = ppcLinks[iLen-1]; '\000' != *pcLine; ++pcLine) {
X		if (isspace(*pcLine)) {
X			ch = ' ';
X			continue;
X		}
X		if (' ' == ch) {
X			*pcBuf++ = ' ';
X			ch = 'W';
X		}
X		*pcBuf++ = *pcLine;
X	}
X	*pcBuf = '\000';
X
X	ppcLinks[iLen] = (char *)0;
X	ppcLinks[iLen+1] = (char *)0;
X	*pppcVec = ppcLinks;
X	*piLen = iLen;
X	if ((char **)0 != ppcAlso) {
X		*ppcAlso = pcAlsoLine;
X	}
X	return 0;
}
X
#if TEST
char *progname = "test";
X
int
main(argc, argv)
int argc;
char **argv;
{
X	register int i, j;
X	auto int iCnt, iFailed;
X	auto char **ppcLinks, acHeader[1000], *pcAlso;
X	auto FILE *fpIn;
X
X	for (iFailed = 0, i = 1; i < argc; ++i) {
X		if ('-' == argv[i][0] && '\000' == argv[i][1]) {
X			fpIn = stdin;
X		} else if (NULL == (fpIn = fopen(argv[i], "r"))) {
X			fprintf(stderr, "%s: fopen: %s: %s\n", progname, argv[i], strerror(errno));
X			++iFailed;
X			continue;
X		}
X		if (rdlinks(fpIn, argv[i], &ppcLinks, &iCnt, &pcAlso, acHeader)) {
X			printf("rdlinks fails for %s\n", argv[i]);
X			(void)fclose(fpIn);
X			++iFailed;
X			continue;
X		}
X		if (fpIn == stdin)
X			clearerr(stdin);
X		else
X			(void)fclose(fpIn);
X		(void)printf("page %s: header `%s\', links %d\n", argv[i], acHeader, iCnt);
X		(void)printf("summary: `%s\'\n", ppcLinks[--iCnt]);
X		for (j = 0; j < iCnt; ++j) {
X			(void)printf("\t`%s\'\n", ppcLinks[j]);
X		}
X		if ((char *)0 != pcAlso) {
X			printf("see also:\n\t%s\n", pcAlso);
X		}
X	}
X	exit(iFailed);
}
#endif
Purdue
chmod 0444 mkcat/readman.c ||
echo 'restore of mkcat/readman.c failed'
Wc_c="`wc -c < 'mkcat/readman.c'`"
test 11371 -eq "$Wc_c" ||
	echo 'mkcat/readman.c: original size 11371, current size' "$Wc_c"
fi
# ============= mk/mk.1l ==============
if test -f 'mk/mk.1l' -a X"$1" != X"-c"; then
	echo 'x - skipping mk/mk.1l (File already exists)'
else
echo 'x - extracting mk/mk.1l (Text)'
sed 's/^X//' << 'Purdue' > 'mk/mk.1l' &&
.TH MK 1L LOCAL
.SH NAME
mk \- detect and execute shell commands in files
.SH SYNOPSIS
.B mk
[
.B \-AVachinsv
] [
.BI \-D defn
] [
.BI \-U undef
] [
.BI \-d submarker
] [
.BI \-e template
] [
.BI \-l lines
] [
.BI \-m marker
] [
.BI \-t templates
] [
.I file
]
.SH DESCRIPTION
.I Mk
is a utility for detecting and executing shell commands within files.
It searches through the first
.I \fIlines\fP
(default 99) of named files,
looking for a \fImarker\fP (default \*(lqCompile\*(rq).
When the marker is located, the portion of the line on which it appears,
after the colon and up to a NEWLINE (or an occurrence of two sequential
unescaped dollar signs (``$$'')), is executed by issuing it to
.IR sh (1).
.PP
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
depends on the shell to do general parameter and
variable expansion.
However,
.I mk
does do some
.IR printf (3)\-like
string substitution.
These substitutions produce the file name in alternate forms.
The unlisted upper case letters below (DPQUX) each produce
the same expansion as the lower case version, but abort the
expansion if the string would be empty.
They begin with a percent sign (`%'):
.sp \n()Pu
.RS
.TS
l l.
%f	the full name of the \fIfile\fP specified on the \fImk\fP command line
%r	the RCS revision file for the specified \fIfile\fP
%d	the directory part of the specified \fIfile\fP
%p	the prefix portion of the specified \fIfile\fP
%q\fIc\fP	the prefix portion of \fIfile\fP upto \fIc\fP
%u\fIc\fP	the extension on \fIfile\fP after \fIc\fP
%x	the extension on the specified \fIfile\fP, if any
%y	the file type (f,d,b,c,l,s,p) as in \fIfind\fP(1)
%F	the base name of the \fIfile\fP, no prefix
%R	the RCS revision \fIfile\fP for the base name for the specified file
%Y\fIc\fP	the file type of \fIfile\fP must be \fIc\fP or abort
%~	the root of the default templates directory
%%	a literal percent sign
.TE
.RE
.PP
The values of \fImk\fP's command line options may be substituted, using
percent escapes.
Each of the options, \-\fBa\fP/\fBA\fP, \-\fBc\fP, \-\fBi\fP, \-\fBn\fP and
\-\fBv\fP/\fBs\fP, may be substituted via a percent escape that begins with the
option letter \- e. g., ``%c'' expands to ``\-c'' only if the \-\fBc\fP option
was specified in the call to \fImk\fP.
The upper case forms of these options suppress the leading dash.
Other command line parameters are available as listed below.
.sp \n()Pu
.RS
.TS
l l.
%b	the full path with which \fImk\fP was invoked
%e	expands to the ``\-e \fItemplate\fP'' option, if given
%l	expands to ``\-l \fIlines\fP'', if one was given
%m	the \fImarker\fP we are searching for
%o	the single letter switches supplied to \fImk\fP
%s	the \fIsubmarker\fP we are searching for
%t	expands to the ``\-t \fItemplates\fP'' option, if given
%B	the last component of the path with which \fImk\fP was invoked
%E	expands to the \fItemplate\fP option, if given
%L	expands to the \fIlines\fP option, if given
%M	the \fImarker\fP we are searching for in lower case
%O	the single letter switches supplied to \fImk\fP, no leading dash
%S	the \fIsubmarker\fP we are searching for in lower case
%T	expands to the \fItemplates\fP option, if given
.TE
.RE
.PP
If a percent escape fails to find the indicated data \- e. g., no
.I submarker
was specified \- it will silently cause the marked line being expanded to be
rejected.
.PP
All C\-like backslash (`\e') escape sequences are substituted.
These substitutions accommodate commands that require characters some
language processors might not allow in comments.
.PP
.I Mk
will also allow a resource limit to be set for each directive.
Here are the resources that can be limited:
.sp \n()Pu
.RS
.TS
l l l.
Resource	Description	Warning Signal
_
clock	wall clock seconds (done by \fImk\fP itself)	SIGALRM
core	core dump size in bytes	no warning signal
cpu	number of CPU seconds	SIGXCPU
data	data size in bytes	no warning signal
fsize	file size of any output file in bytes	SIGXFSZ
rss	resident pages in bytes	no warning signal
stack	stack size in bytes	SIGSEGV
.TE
.RE
.PP
These resources may be specified after the complete marker and submarker.
They are separated by commas from each other and the marker/submarker.
Two values may follow each resource name, separated from the
name by an equal sign (`=') \- a warning \fIlimit\fP and
an absolute \fImaximum\fP.
The
.I limit
and
.I maximum
must be separated by a slash (`/').
If only one value is specified, it is considered to be both the
.I limit
and the \fImaximum\fP.
.PP
When the directive's process reaches the \fIlimit\fP, it is
sent the indicated warning signal.
When the process reaches the \fImaximum\fP, it is sent a SIGKILL
signal.
When the
.I clock
values are the same,
.I mk
waits two seconds before sending the SIGKILL.
.PP
For example,
.sp \n()Pu
.RS
C $Run,cpu=300/360,core=0: %F 1 1000
.RE
.sp \n()Pu
would send to
.I sh
for execution (that's the meaning of the ``Run'' marker) the base name of
the file (effected with the ``%F'' substitution).
The base name executable would be supplied with the arguments ``1 1000''.
The CPU time limit would be 300 and the CPU time maximum, 360.
The maximum core dump size would be 0 bytes.
.PP
The exit status of the
.I mk
command is the count of the number of directives that exited non\-zero.
If a command is known (or intended) to fail then the expected exit code,
preceded by an equal sign (`='), may be specified after its marker
(``Fail'' in the following example).
.sp \n()Pu
.RS
# $Fail=1: %F "bad args"
.RE
.sp \n()Pu
When an exit code is specified, any other exit code will be treated
as a failure.
The special exit code `*' can be used to force
a command to always exit successfully.
Any exit code except the specified one will be considered a successful exit
if the specified code is prefixed with a tilde (`~').
In the following example, any nonzero exit code that ``%F'' issues will cause
.I mk
to issue a zero code:
.sp \n()Pu
.RS
# $NonZero=~0: %F "nonzero args"
.RE
.sp \n()Pu
.PP
The full form of an
.I mk
directive is
.sp \n()Pu
.RS
\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 ]
.RE
.sp \n()Pu
where the
.I resource
may be repeated to name and set multiple resource limits.
.PP
If no
.I marker
line can be found in the first \fIlines\fP (default 99)
of the file, a standard template file name is formed using the
\fB\-t\fItemplates\fR arguments.
The
.I templates
string is subjected to \fImk\fP's file name substitutions (see above).
.I Mk
then searches the file whose name emerges from that substitution for
\fImarkers\fP.
The special marker ``*'' matches, and is a match for, any marker
or submarker.
.SH OPTIONS
Note that options and arguments may be intermixed on the command line
to change the behavior of
.I mk
on a per file basis \- e. g., ``mk \-s foo.c \-v \-ddebug bar.c''.
.TP
.B \-A
Find all the marked lines that match the specified \fImarker\fP
and \fIsubmarker\fP, stop processing at the first command that is
successful.
.TP
.BI \-D defn
Give a definition for an environment variable.
The definition must have the form \fIident\fP\fB=\fP\fIvalue\fP.
.TP
.BI \-U undef
Remove a definition for the environment variable \fIundef\fP.
.TP
.B \-a
Find all the marked lines that match the specified \fImarker\fP
and \fIsubmarker\fP.
.TP
.B \-c 
Confirm the action before running it.
This will allow the user to see the command before it is executed.
A response of `y' or `Y' will cause the command to be run.
.TP
.BI \-d submarker
Look for the string \*(lq$\fImarker\fP(\fIsubmarker\fP):\*(rq in file.
White space is ignored.
A command line including \*(lq\-d debug\*(rq would match
\*(lq$\fImarker\fP(debug):\*(rq.
Submarkers in the file are ignored (do not take part in matching)
if no submarker is specified.
The \fIsubmarker\fP \*(lq*\*(rq matches all submarkers in the file.
.TP
.BI \-e template
This \fItemplate\fP will be searched before the file at hand.
This allows \fImk\fP to trap character special files with the %y macro.
.TP
.B \-h
Print a help message.
.TP
.B \-i
Ignore case when looking for \fImarker\fP and \fIsubmarker\fP.
.TP
.B \-l lines
Search
.I lines
lines rather than the default 99 for compilation markers.
.TP
.B \-m marker
Look for the string \*(rq\fB$\fP\fImarker\fP\fB:\fP\*(rq
as the delimiter for the compilation directive.
The default \fImarker\fP string is \*(lqCompile\*(rq.
Note that \fImarker\fP does not include either the leading dollar\-sign (`$')
or the trailing colon (`:').
The \fImarker\fP \*(lq*\*(rq matches all markers in the file.
.TP
.B \-n 
Do not execute the located commands.
This flag specifies that the resulting command(s) should not be executed,
but only printed on the standard output.
.TP
.B \-s
Be silent.
Executed commands are not output.
.TP
.BI \-t templates
The
.I templates
string produces a file name that will be searched for a
.I marker
line if no
.I marker
line is found in the file named on the
.I mk
call.
The percent escapes described above are all available for substitution
in this string, so that the template can be based on the extender of the
file being processed.
The default
\fItemplates\f string is ``/usr/local/lib/mk/def_%x''.
More than one template option my be specified.
.I Mk
searches for all of them in the order they are given.
.TP
.B \-v
Be verbose.
This is the opposite of \-\fBs\fP (above), and is implied by
the \-\fBn\fP option (above).
.TP
.B \-V
Be extra verbose.
Used only to debug \fImk\fP, or output the default templates configuration.
.SH EXAMPLES
.I Mk
is most commonly used to produce input for the shell.
The following lines might occur in a C program source file:
.sp \n()Pu
.RS
.nf
/*
X * $Compile: ${cc\-cc} ${CFLAGS\-\-O} \-o %F \-DFOO=1 %f
X * $Compile(debug): ${cc\-cc} ${CFLAGS\-\-g} \-o %F \-DDEBUG %f
X * $Run: %F /tmp/test
X * $Fail=1: %F /etc/passwd
X * $Limit,cpu=100,clock=600,fsize=10000: %F /tmp/test2
X */
.fi
.RE
.sp \n()Pu
If the file were called \*(lqfoo.c\*(rq, \fImk\fP,
invoked as
.sp \n()Pu
.RS
mk foo.c
.RE
.sp \n()Pu
would send
.I sh
the command:
.sp \n()Pu
.RS
cc \-O \-o foo \-DFOO=1 foo.c
.RE
.sp \n()Pu
With an invocation like
.sp \n()Pu
.RS
mk \-ddebug foo.c
.RE
.sp \n()Pu
the command
.sp \n()Pu
.RS
cc \-g \-o foo \-DDEBUG foo.c
.RE
.sp \n()Pu
would be sent to \fIsh\fP.
.PP
\fIMk\fP will output the default templates option if only \-\fBV\fP is
set:
.RS
.nf
mk: $Id: mk.1l,v 4.2 90/11/28 16:29:54 ksb Exp $
mk: %~ `/usr/local/lib/mk'
mk: -e `%~/type-%y:%~/pre-%x:%~/comma-%U,'
mk: -t `%~/file-%F:%~/dot-%x:%~/m-%M'
.fi
.RE
.SH SEE ALSO
sh(1),
setrlimit(2),
valid(1L).
.SH AUTHORS
S. McGeady, Intel, Inc., mcg@mipon2.intel.com
.sp 1
Kevin Braunsdorf, Purdue University Computing Center (ksb@cc.purdue.edu)
Purdue
chmod 0444 mk/mk.1l ||
echo 'restore of mk/mk.1l failed'
Wc_c="`wc -c < 'mk/mk.1l'`"
test 10944 -eq "$Wc_c" ||
	echo 'mk/mk.1l: original size 10944, current size' "$Wc_c"
fi
true || echo 'restore of mkcat/pt.c failed'
echo End of part 3, continue with part 4
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.