[comp.sources.unix] v24i067: Purdue software product installation system, Part05/07

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

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

#!/bin/sh
# This is part 05 of pucc-1b
# ============= install.d/special.c ==============
if test ! -d 'install.d'; then
    echo 'x - creating directory install.d'
    mkdir 'install.d'
fi
if test -f 'install.d/special.c' -a X"$1" != X"-c"; then
	echo 'x - skipping install.d/special.c (File already exists)'
else
echo 'x - extracting install.d/special.c (Text)'
sed 's/^X//' << 'Purdue' > 'install.d/special.c' &&
/*
X * $Id: special.c,v 7.1 90/09/17 10:33:49 ksb Exp $
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 * handle the configuration file code					(ksb)
X */
X
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/time.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>
X
#include "configure.h"
#include "install.h"
#include "syscalls.h"
#include "special.h"
#include "main.h"
#if defined(INSTCK)
#include "instck.h"
#endif
X
#if STRINGS
#include <strings.h>
#else
#include <string.h>
#endif
X
char *apcOO[] = {	/* name of 0/1 for user			*/
X	"off",
X	"on"
};
X
struct group *grpDef;		/* default group on all files (this dir)*/
struct passwd *pwdDef;		/* default owner on all files (this dir)*/
X
/* set up the users values for '=' in the config file			(ksb)
X */
void
InitCfg(pcOwner, pcGroup)
char *pcOwner, *pcGroup;
{
X	extern char *getenv();
X	pwdDef = (struct passwd *)0;
X	if ((char *)0 == pcOwner) {
X		if (bHaveRoot) {
X			pcOwner = DEFOWNER;
X		} else {
X			auto char *pcUser;
X
X			pcUser = getenv("USER");
X			if ((char *)0 == pcUser) {
X				pcUser = getenv("LOGNAME");
X			}
X			if ((char *)0 != pcUser && '\000' != pcUser[0] && (struct passwd *)0 != (pwdDef = getpwnam(pcUser)) && getuid() == pwdDef->pw_uid) {
X				/* got him */;
X			} else if ((struct passwd *)0 == (pwdDef = getpwuid(getuid()))) {
X				fprintf(stderr, "%s: getpwuid: %d: %s\n", progname, getuid(), strerror(errno));
X				exit(10);
X			}
X			pwdDef = savepwent(pwdDef);
X			pcOwner = pwdDef->pw_name;
X		}
X	}
X	if ((char *)0 == pcOwner || '\000' == *pcOwner) {
X		pcOwner = (char *)0;
X	} else if ((struct passwd *)0 == pwdDef) {
X		pwdDef = getpwnam(pcOwner);
X		if ((struct passwd *)0 == pwdDef) {
X			fprintf(stderr, "%s: getpwname: %s: %s\n", progname, pcOwner, strerror(errno));
X			exit(10);
X		}
X		pwdDef = savepwent(pwdDef);
X	}
X
X	grpDef = (struct group *)0;
X	if ((char *)0 == pcGroup) {
X		if (bHaveRoot) {
X			pcGroup = DEFGROUP;
X		} else {
X			if ((struct group *)0 == (grpDef = getgrgid(getgid()))) {
X				fprintf(stderr, "%s: getgrgid: %d: %s\n", progname, getgid(), strerror(errno));
X				exit(11);
X			}
X			grpDef = savegrent(grpDef);
X			pcGroup = grpDef->gr_name;
X		}
X	}
X	if ((char *)0 == pcGroup || '\000' == *pcGroup) {
X		pcGroup = (char *)0;
X	} else if ((struct group *)0 == grpDef) {
X		grpDef = getgrnam(pcGroup);
X		if ((struct group *)0 == grpDef) {
X			fprintf(stderr, "%s: getgrname: %s: %s\n", progname, pcGroup, strerror(errno));
X			exit(11);
X		}
X		grpDef = savegrent(grpDef);
X	}
}
X
X
/*
X * get the user-level name for this node `plain file' or `socket'	(ksb)
X *	return string, ref-out single char
X */
char *
NodeType(mType, pcChar)
unsigned int mType;
char *pcChar;
{
X	auto char acTemp[2];
X	if ((char *)0 == pcChar)
X		pcChar = acTemp;
X
X	switch (mType & S_IFMT) {
X	case S_IFDIR:
X		*pcChar = 'd';
X		return "directory";
#if defined(S_IFSOCK)
X	case S_IFSOCK:
X		*pcChar = 's';
X		return "socket";
#endif	/* no sockets */
#if defined(S_IFIFO)
X	case S_IFIFO:
X		*pcChar = 'p';
X		return "fifo";
#endif
#if defined(S_IFLNK)
X	case S_IFLNK:
X		*pcChar = 'l';
X		return "symbolic link";
#endif
X	case S_IFBLK:
X		*pcChar = 'b';
X		return "block device";
X	case S_IFCHR:
X		*pcChar = 'c';
X		return "character device";
X	case S_IFREG:
X	case 0:
X		*pcChar = '-';
X		return "plain file";
X	default:
X		break;
X	}
X	*pcChar = '?';
X	return "type unknown";
}
X
/*
X * convert an octal mode to an integer					(jms)
X */
static void
OctMode(pcLMode, pmMode)
char *pcLMode;		/* the string to convert (e.g., "755")		*/
int *pmMode;		/* the place to put the converted mode		*/
{
X	register int mode;	/* mode of installed file		*/
X	register int c;		/* next character			*/
X
X	for (mode = 0; (c = *pcLMode) != '\000'; ++pcLMode) {
X		if (c >= '0' && c <= '7') {
X			mode = (mode << 3) + (c - '0');
X			continue;
X		}
X		(void)fprintf(stderr, "%s: bad digit int mode `%c\'\n", progname, *pcLMode);
X		exit(EXIT_OPT);
X	}
X	if ((int *)0 != pmMode) {
X		*pmMode |= mode;
X	}
}
X
X
/*
X * extra bits in the bit listing are handled strangely			(ksb)
X */
static void
EBit(pcBit, bSet, cSet, cNotSet)
char *pcBit;		/* pointer the the alpha bit			*/
int bSet;		/* should it set over struck			*/
int cSet;		/* overstrike with this				*/
int cNotSet;		/* it should have been this, else upper case it	*/
{
X	if (0 != bSet) {
X		*pcBit = (cNotSet == *pcBit) ? cSet : toupper(cSet);
X	}
}
X
X
static char acOnes[] = "rwxrwxrwx";		/* mode 0777		*/
/*
X * string to octal mode mask "rwxr-s--x" -> 02751.			(ksb)
X * Note that we modify the string to do this, but we put it back.
X */
static void
StrMode(pcLMode, pmBits, pmQuests)
char *pcLMode;		/* alpha bits buffer (about 10 chars)		*/
register int *pmBits;	/* manditory mode				*/
int *pmQuests;		/* optional mode bits				*/
{
X	register int iBit, i;
X	register char *pcBit;
X
X	*pmBits = 0;
X	if ((int *)0 != pmQuests) {
X		*pmQuests = 0;
X	}
X
X	/* if the mode is long enough to have a file type field check that
X	 * out and set the file type bits too
X	 */
X	switch (i = strlen(pcLMode)) {
X	default:
X		(void)fprintf(stderr, "%s: alphabetical mode must be 9 or 10 characters `%s\' is %d\n", progname, pcLMode, i);
X		exit(EXIT_OPT);
X	case 9:
X		*pmBits |= S_IFREG;
X		break;
X	case 10:
X		switch (*pcLMode) {
#if HAVE_SLINKS
X		case 'l':	/* symbolic link */
X			*pmBits |= S_IFLNK;
X			break;
#endif	/* no links to think about */
#if defined(S_IFIFO)
X		case 'p':	/* fifo */
X			*pmBits |= S_IFIFO;
X			break;
#endif	/* no fifos */
#if defined(S_IFSOCK)
X		case 's':	/* socket */
X			*pmBits |= S_IFSOCK;
X			break;
#endif	/* no sockets */
X		case 'b':
X			*pmBits |= S_IFBLK;
X			break;
X		case 'c':
X			*pmBits |= S_IFCHR;
X			break;
X		case '-':
X		case 'f':
X			*pmBits |= S_IFREG;
X			break;
X		case 'd':
X			*pmBits |= S_IFDIR;
X			break;
X		default:
X			(void)fprintf(stderr, "%s: unknown file type `%c\'\n", progname, *pcLMode);
X			exit(EXIT_OPT);
X		}
X		++pcLMode;
X		break;
X	}
X
X	/* copy out and turn off set{g,u}id and stick bits
X	 * (The upper case bit means no execute underneath.)
X	 */
X	if ((char *)0 != (pcBit = strchr("sS", pcLMode[2]))) {
X		pcLMode[2] = isupper(*pcBit) ? '-' : 'x';
X		*pmBits |= S_ISUID;
X	}
X	if ((char *)0 != (pcBit = strchr("sS", pcLMode[5]))) {
X		pcLMode[5] = isupper(*pcBit) ? '-' : 'x';
X		*pmBits |= S_ISGID;
X	}
X	if ((char *)0 != (pcBit = strchr("tT", pcLMode[8]))) {
X		pcLMode[8] = isupper(*pcBit) ? '-' : 'x';
X		*pmBits |= S_ISVTX;
X	}
X
X	/* read normal mode bits
X	 */
X	for (i = 0, iBit = 0400; i < 9; ++i, iBit >>= 1) {
X		if (pcLMode[i] == acOnes[i]) {
X			*pmBits |= iBit;
X		} else if ('-' == pcLMode[i]) {
X			continue;
X		} else if ('\?' == pcLMode[i]) {
X			if ((int *)0 != pmQuests)
X				*pmQuests |= iBit;
X		} else if ('\000' == pcLMode[i]) {
X			(void)fprintf(stderr, "%s: not enough bits in file mode\n", progname);
X		} else if ((char *)0 != strchr("rwxtTsS", pcLMode[i])) {
X			(void)fprintf(stderr, "%s: bit `%c\' in file mode is in the wrong column\n", progname, pcLMode[i]);
X			exit(EXIT_OPT);
X		} else {
X			(void)fprintf(stderr, "%s: unknown bit in file mode `%c\'\n", progname, pcLMode[i]);
X			exit(EXIT_OPT);
X		}
X	}
X
X	/* here we put the set{u,g}id and sticky bits back
X	 */
X	EBit(pcLMode+2, S_ISUID & *pmBits, 's', 'x');
X	EBit(pcLMode+5, S_ISGID & *pmBits, 's', 'x');
X	EBit(pcLMode+8, S_ISVTX & *pmBits, 't', 'x');
}
X
X
/*
X * Convert a string that ls(1) understands into something that	     (jms&ksb)
X * chmod(2) understands (extended), or an octal mode.
X ! The string we are given may be in the text (const) segment,
X ! we have to copy it to a write segment to allow StrMode to work.
X *
X * Optional bits may be specified after a `/' as in
X *	0711/044
X * which allows any of {0711, 0751, 0715, 0755}, or write symolicly
X *	-rwx--x--x/----r--r--
X * or a symbolic mode may contain a `?' in place of a bit for optional:
X *	-rwx?-x?-x
X * (which is quite readable).
X *
X * The slash notaion is needed for optional setgid directories, at least.
X */
void
CvtMode(pcLMode, pmMode, pmQuest)
char *pcLMode;		/* the string to convert (e.g., "755")		*/
int *pmMode;		/* the place to put the converted mode		*/
int *pmQuest;		/* the place to put the converted optional mode	*/
{
X	register char *pcScan;	/* copy mode so we can write on it	*/
X	auto char acDown[MAXPATHLEN+1];	/* where we write		*/
X
X	if ((int *)0 != pmMode)
X		*pmMode = 0;
X	if ((int *)0 != pmQuest)
X		*pmQuest = 0;
X
X	pcScan = acDown;
X	while ('/' != *pcLMode && '\000' != *pcLMode) {
X		*pcScan++ = *pcLMode++;
X	}
X	*pcScan = '\000';
X
X	if (isdigit(*acDown)) {
X		OctMode(acDown, pmMode);
X	} else {
X		StrMode(acDown, pmMode, pmQuest);
X	}
X
X	if ('/' == *pcLMode && (int *)0 != pmQuest) {
X		++pcLMode;
X		(void)strcpy(acDown, pcLMode);	/* need a writable copy */
X		if (isdigit(*acDown)) {
X			OctMode(acDown, pmQuest);
X		} else {
X			StrMode(acDown, pmQuest, pmQuest);
X		}
X	}
}
X
X
#if defined(CONFIG)
/*
X * convert an integer mode into a symbolic format			(ksb)
X */
void
ModetoStr(pcLMode, mMode, mOptional)
char *pcLMode;		/* alpha bits buffer (10 chars or more)		*/
int mMode;		/* maditory mode bits				*/
int mOptional;		/* optional mode bits				*/
{
X	register int i, iBit;
X
X	(void)strcpy(pcLMode, acOnes);
X	for (i = 0, iBit = 0400; i < 9; ++i, iBit >>= 1) {
X		if (0 == (mMode & iBit))
X			pcLMode[i] = '-';
X	}
X	EBit(pcLMode+2, S_ISUID & mMode, 's', 'x');
X	EBit(pcLMode+5, S_ISGID & mMode, 's', 'x');
X	EBit(pcLMode+8, S_ISVTX & mMode, 't', 'x');
X	for (i = 0, iBit = 0400; i < 9; ++i, iBit >>= 1) {
X		if (0 == (mOptional & iBit))
X			continue;
X		if ('-' == pcLMode[i])
X			pcLMode[i] = '\?';
X	}
}
X
X
/*
X * CompPath()
X *	Compress a path by assuming that . and .. really point to	(ksb)
X *	what they should (good under BSD at least :-|), we also
X *	assume the all things used as a directory all directories
X *	(and exist)
X *
X * Remove extra `/', `./', `foo/..'  (in place)
X * also replace a null path with `.'
X */
static char *
CompPath(pcFile)
char *pcFile;		/* path to compress				*/
{
X	register char *pcScan, *pcPut;
X	register int bFirst;
X
X	pcScan = pcPut = pcFile;
X	bFirst = 1;
X	for (;;) { switch (*pcPut = *pcScan) {
X	case '/':
X		++pcPut;
X		do
X			++pcScan;
X		while ('/' == *pcScan);
X		bFirst = 1;
X		continue;
X	case '.':
X		if (!bFirst) {
X			++pcPut, ++pcScan;
X			continue;
X		}
X		if ('/' == pcScan[1] || '\000' == pcScan[1]) {
X			do
X				++pcScan;
X			while ('/' == *pcScan);
X			continue;
X		}
X		/* do foo/.., but not /.. or just ..
X		 */
X		if ('.' == pcScan[1] && pcPut != pcFile &&
X		    ('/' == pcScan[2] || '\000' == pcScan[2]) &&
X		    !('/' == pcFile[0] && pcPut == pcFile+1)) {
X			--pcPut;
X			do
X				--pcPut;
X			while ('/' != *pcPut && pcPut != pcFile);
X			if ('/' == *pcPut)
X				++pcPut;
X			pcScan += 2;
X			while ('/' == *pcScan)
X				++pcScan;
X			continue;
X		}
X	default:
X		++pcPut, ++pcScan;
X		bFirst = 0;
X		continue;
X	case '\000':
X		break;
X	} break; }
X
X	/* clean up trailing / and empty path name
X	 */
X	if (pcPut != pcFile) {
X		while (--pcPut != pcFile && '/' == *pcPut)
X			*pcPut = '\000';
X	}
X	if ('\000' == *pcFile) {
X		pcFile[0] = '.';
X		pcFile[1] = '\000';
X	}
X	return pcFile;
}
X
X
X
/*
X * _SamePath()
X *	We want /bin/* to match every file in /bin OK.			(ksb)
X *	return 1 for ==, 0 for !=
X *
X * N.B. Must call CompPath on pcFile first, or know it is minimal
X */
int
_SamePath(pcGlob, pcFile, fDot)
char *pcGlob;		/* the pattern to match				*/
char *pcFile;		/* the file to match with			*/
int fDot;		/* are we at the start of pcFile, or post '/'	*/
{
X	register char *pc;
X	register int iLenGlob, iLenFile;
X	auto int bFound, cStop;
X
X	for (;;) { switch (*pcGlob) {
X	case '*':		/* match any string			*/
X		pc = ++pcGlob;
X		iLenGlob = 0;
X		while ('\\' != *pc && '?' != *pc && '[' != *pc && '*' != *pc && '\000' != *pc && '/' != *pc) {
X			++pc, ++iLenGlob;
X		}
X
X		iLenFile = 0;
X		while ('/' != pcFile[iLenFile] && '\000' != pcFile[iLenFile] &&
X		       (!fDot || '.' != pcFile[iLenFile])) {
X			++iLenFile;
X			fDot = 0;
X		}
X
X		bFound = 0;
X		do {
X			if (iLenGlob == 0 || 0 == strncmp(pcGlob, pcFile, iLenGlob)) {
X				if (_SamePath(pc, pcFile+iLenGlob, fDot)) {
X					bFound = 1;
X					break;
X				}
X			}
X			--iLenFile, ++pcFile;
X		} while (iLenFile >= iLenGlob);
X		return bFound;
X	case '[':		/* any of				*/
X		++pcGlob;
X		cStop = *pcFile++;
X		if (cStop == '/')	/* range never match '/'	*/
X			break;
X		bFound = 0;
X		if ('-' == *pcGlob) {
X			bFound = '-' == cStop;
X			++pcGlob;
X		}
X		while (']' != *pcGlob) {
X			if ('-' == pcGlob[1]) {
X				if (pcGlob[0] <= cStop && cStop <= pcGlob[2])
X					bFound = 1;
X				pcGlob += 2;
X			} else {
X				if (pcGlob[0] == cStop)
X					bFound = 1;
X			}
X			++pcGlob;
X		}
X		++pcGlob;
X		if (!bFound)
X			break;
X		continue;
X	case '?':		/* and single char but '/'		*/
X		if ('/' == *pcFile || (fDot && '.' == *pcFile) || '\000' == *pcFile)
X			break;
X		++pcGlob, ++pcFile;
X		fDot = 0;
X		continue;
X	case '\\':		/* next char not special		*/
X		++pcGlob;
X		/*fall through*/
X	case '/':		/* delimiter				*/
X		fDot = 1;
X		if (*pcGlob != *pcFile)
X			break;
X		++pcGlob;
X		do {
X			++pcFile;
X		} while ('/' == *pcFile);
X		continue;
X	default:		/* or any other character		*/
X		fDot = 0;
X		if (*pcGlob != *pcFile)
X			break;
X		++pcGlob, ++pcFile;
X		continue;
X	case '\000':		/* end of pattern, end of file name	*/
X		return '\000' == *pcFile;
X	} break; }
X	return 0;
}
X
/*
X * called if we have a config line that makes little sense		(ksb)
X *  (in that is says to set{u,g}id to a `*' {user,group})
X */
void
BadSet(iLine, cWhich, pcNoun, pcBadMode)
int iLine;
char cWhich, *pcNoun, *pcBadMode;
{
#if INSTALL
X	if (FALSE == fVerbose)
X		return;
X	(void)fprintf(stderr, "%s: %s(%d): checklist specifies set%cid to a random %s with mode %s\n", progname, pcSpecial, iLine, cWhich, pcNoun, pcBadMode);
#else
X	(void)fprintf(fpOut, "%s: %s(%d): checklist specifies set%cid to a random %s with mode %s\n", progname, pcSpecial, iLine, cWhich, pcNoun, pcBadMode);
#endif
}
X
X
/*
X * find an apropriate place to compare a file to a pattern		(ksb)
X *  pat == `RCS/*,v'
X *  file == /tmp/junk/RCS/main.h,v
X * return a pointer to
X *  `RCS/main.h,v'
X */
static char *
RJust(pcPat, pcFile, pcLast)
char *pcPat, *pcFile, *pcLast;
{
X	register char *pcRev;
X
X	if ('/' == pcPat[0])
X		return pcFile;
X	pcRev = pcPat + strlen(pcPat);
X	do {
X		do {
X			--pcRev;
X		} while (pcPat != pcRev && '/' != *pcRev);
X		if ('/' == *pcRev) {
X			do {
X				--pcRev;
X			} while (pcPat != pcRev && '/' == *pcRev);
X			do {
X				--pcLast;
X			} while (pcFile != pcLast && '/' == *pcLast);
X			while (pcFile != pcLast && '/' != *pcLast)
X				--pcLast;
X			if (pcLast == pcFile)
X				return pcFile;
X			while ('/' == *pcLast)
X				++pcLast;
X		}
X	} while (pcPat != pcRev);
X	return pcLast;
}
X
/* build "DirCk"  or "Special"
X */
#include "special.i"
X
#if defined(INSTCK)
/* if we are instck build "Specail"
X */
#undef INSTCK
#define INSTALL	1
#include "special.i"
#undef INSTALL
#endif
X
#endif	/* check list file */
Purdue
chmod 0444 install.d/special.c ||
echo 'restore of install.d/special.c failed'
Wc_c="`wc -c < 'install.d/special.c'`"
test 15681 -eq "$Wc_c" ||
	echo 'install.d/special.c: original size 15681, current size' "$Wc_c"
fi
# ============= install.d/dir.c ==============
if test -f 'install.d/dir.c' -a X"$1" != X"-c"; then
	echo 'x - skipping install.d/dir.c (File already exists)'
else
echo 'x - extracting install.d/dir.c (Text)'
sed 's/^X//' << 'Purdue' > 'install.d/dir.c' &&
/*
X * Copyright 1990 Purdue Research Foundation, West Lafayette, Indiana
X * 47907.  All rights reserved.
X *
X * Written by Kevin S Braunsdorf, ksb@cc.purdue.edu, purdue!ksb
X *
X * This software is not subject to any license of the American Telephone
X * and Telegraph Company or the Regents of the University of California.
X *
X * Permission is granted to anyone to use this software for any purpose on
X * any computer system, and to alter it and redistribute it freely, subject
X * to the following restrictions:
X *
X * 1. Neither the authors nor Purdue University are responsible for any
X *    consequences of the use of this software.
X *
X * 2. The origin of this software must not be misrepresented, either by
X *    explicit claim or by omission.  Credit to the authors and Purdue
X *    University must appear in documentation and sources.
X *
X * 3. Altered versions must be plainly marked as such, and must not be
X *    misrepresented as being the original software.
X *
X * 4. This notice may not be removed or altered.
X */
X
/*
X * install a directory						(ksb)
X */
#if !defined(lint)
static char *rcsid = "$Id: dir.c,v 7.2 90/10/22 11:47:04 ksb Exp $";
#endif	/* !lint */
X
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>
X
#include "configure.h"
#include "install.h"
#include "main.h"
#include "dir.h"
#include "file.h"
#include "syscalls.h"
#include "special.h"
X
#if STRINGS
#include <strings.h>
#else
#include <string.h>
#endif
X
#if !defined(F_OK)
#define F_OK	0
#endif
X
#if !defined(BINLS)
#define BINLS		"/bin/ls"
#endif
X
char acLs[] =		BINLS;
X
X
#if !defined(LSDIRARGS)
#if defined(SYSV) || defined(HPUX7)
#define LSDIRARGS	"-ld"
#else	/* bsd needs a -g option to show group	*/
#define LSDIRARGS	"-lgd"
#endif	/* how does ls(1) show both owner&group	*/
#endif
X
char acLsDirArgs[] =	LSDIRARGS;
X
X
#if !HAVE_MKDIR
#if !defined(BINMKDIR)
#define BINMKDIR	"/bin/mkdir"
#endif
char acMkdir[] =	BINMKDIR;
#endif
X
X
static char acDCreated[] = "directory `%s\' created %s.%s(%04o) by %s\n";
static char acDUpdated[] = "directory `%s\' updated %s.%s(%04o) by %s\n";
static char acDRemoved[] = "directory `%s\' removed %s.%s(%04o) by %s\n";
X
/*
X * DirInstall()
X *	Create a directory with given modes and owners
X *
X * Side effects
X *	Creates a directory somewhere
X */
int
DirInstall(pcDir, pcOwner, pcGroup, pcMode, pcDefOwner, pcDefGroup, pcDefMode, pcSLinks, fRemove)
char *pcDir;		/* the directory to create			*/
char *pcOwner;		/* owner to give it				*/
char *pcGroup;		/* group ownership				*/
char *pcMode;		/* explicit -m option				*/
char *pcDefOwner;	/* default owner to give it			*/
char *pcDefGroup;	/* default group ownership			*/
char *pcDefMode;	/* default if no -m option and doesn't exist	*/
char *pcSLinks;		/* symbolic links to the directory		*/
int fRemove;		/* remove the directory, do not install		*/
{
X	register char *pcSlash;
X	auto int bSame;			/* exist dir is the same as req.*/
X	auto struct passwd *pwd;	/* owner of this dir.		*/
X	auto struct group *grp;		/* group of this directory	*/
X	auto int mMode, mOptMode;	/* mode of this directory	*/
X	auto struct stat statb_dir;	/* for this dir or parrent	*/
X	auto int bDestExists;		/* destination file exists?	*/
X	auto char acDir[MAXPATHLEN+1];	/* copy of our name to munge	*/
X	auto char *pcMsg;		/* error message		*/
#if defined(CONFIG)
X	auto CHLIST Check;		/* must match this record	*/
#endif	/* set check flags from config file	*/
#if defined(INST_FACILITY)
X	auto char *pcLogStat;
X	auto char acLogBuf[MAXLOGLINE];
#endif	/* we should syslog changes		*/
X
X	if (strlen(pcDir) > MAXPATHLEN) {
#if defined(ENAMETOOLONG)
X		fprintf(stderr, "%s: directory name: %s\n", progname, strerror(ENAMETOOLONG));
#else
X		fprintf(stderr, "%s: directory name: name too long\n", progname);
#endif
X		return FAIL;
X	}
X
X	/*	install -d /bin/
X	 * used to choke us because we tried to make `/bin' and `/bin/'
X	 *	install -d /usr/local//bin
X	 * was a funny case too
X	 */
X	/* the New Castle Conection would hate us here ... */
X	for (bSame = '\000', pcSlash = acDir; '\000' != *pcDir; ++pcDir) {
X		if ('/' == bSame && '/' == *pcDir)
X			continue;
X		*pcSlash++ = bSame = *pcDir;
X	}
X	if (pcSlash == acDir) {
X		(void)strcpy(acDir, ".");
X	} else if (pcSlash != acDir+1 && '/' == pcSlash[-1]) {
X		pcSlash[-1] = '\000';
X	} else {
X		pcSlash[0] = '\000';
X	}
X
#if defined(CONFIG)
X	Special(acDir, pcSpecial, & Check);
#endif	/* set check flags from config file	*/
X
X	if (-1 != stat(acDir, & statb_dir)) {
X		bDestExists = TRUE;
X	} else {
X
X		/* we need the parrent dirs modes to copy here...
X		 */
X		if ((char *)0 != (pcSlash = strrchr(acDir, '/'))) {
X			while (pcSlash != acDir && '/' == pcSlash[-1])
X				pcSlash--;
X			*pcSlash = '\000';
X			if (pcSlash == acDir) {
X				if (-1 == stat("/", & statb_dir)) {
X					fprintf(stderr, "%s: stat: /: %s\n", progname, strerror(errno));
X					return FAIL;
X				}
X			} else if (-1 == stat(acDir, & statb_dir)) {
X				/* we could use code like this to get
X				 * Sun-like recursive dir install behavior
X				 */
X				if (errno != ENOENT || FALSE == fRecurse) {
X					fprintf(stderr, "%s: stat: %s: %s\n", progname, acDir, strerror(errno));
X					return FAIL;
X				} else if (FAIL == DirInstall(acDir, pcOwner, pcGroup, pcMode, pcDefOwner, pcDefGroup, pcDefMode, (char *)0, 0)) {
X					return FAIL;
X				}
X				if (FALSE == fTrace && -1 == stat(acDir, & statb_dir)) {
X					fprintf(stderr, "%s: stat: %s: %s\n", progname, acDir, strerror(errno));
X					return FAIL;
X				}
X			}
X			*pcSlash = '/';
X		} else  if (-1 == stat(".", & statb_dir)) {
X			fprintf(stderr, "%s: stat: .: %s\n", progname, strerror(errno));
X			return FAIL;
X		}
X		bDestExists = FALSE;
X	}
X
X	(void)setpwent();
X	if ((char *)0 != pcOwner) {
X		if ((struct passwd *)0 == (pwd = getpwnam(pcOwner))) {
X			(void)fprintf(stderr, "%s: getpwname: %s not found\n", progname, pcOwner);
X			exit(EXIT_OPT);
X		}
X		if (FALSE == bHaveRoot && pwd->pw_uid != geteuid()) {
X			(void)fprintf(stderr, "%s: effective uid cannot make directory owned by %s (%d != %d)\n", progname, pcOwner, geteuid(), pwd->pw_uid);
X			exit(EXIT_OPT);
X		}
X	} else if (bDestExists != FALSE) {
X		if ((struct passwd *)0 == (pwd = getpwuid((int) statb_dir.st_uid))) {
X			(void)fprintf(stderr, "%s: destination owner %d doesn\'t exist\n", progname, statb_dir.st_uid);
X			exit(EXIT_OPT);
X		}
X	} else if (bHaveRoot) {
X		if ((char *)0 != pcDefOwner) {
X			pcOwner = pcDefOwner;
X			if ((struct passwd *)0 == (pwd = getpwnam(pcOwner))) {
X				(void)fprintf(stderr, "%s: getpwname: %s not found\n", progname, pcOwner);
X				exit(EXIT_OPT);
X			}
X		} else {
X			if ((struct passwd *)0 == (pwd = getpwuid((int) statb_dir.st_uid))) {
X				(void)fprintf(stderr, "%s: destination directory owner %d doesn\'t exist\n", progname, statb_dir.st_uid);
X				exit(EXIT_OPT);
X			}
X		}
X	} else if ((struct passwd *)0 == (pwd = getpwuid((int) geteuid()))) {
X		(void)fprintf(stderr, "%s: getpwuid: %d (effective uid) not found\n", progname, geteuid());
X		exit(EXIT_OPT);
X	}
X	pwd = savepwent(pwd);
X	(void)endpwent();
X
X	(void)setgrent();
X	if ((char *)0 != pcGroup) {
X		if ((struct group *)0 == (grp = getgrnam(pcGroup))) {
X			(void)fprintf(stderr, "%s: getgrname: %s not found\n", progname, pcGroup);
X			exit(EXIT_OPT);
X		}
X	} else if (bDestExists != FALSE) {
X		grp = getgrgid((int) statb_dir.st_gid);
X		if ((struct group *)0 == grp) {
X			(void)fprintf(stderr, "%s: no group entry for destination group %d\n", progname, statb_dir.st_gid);
X			exit(EXIT_OPT);
X		}
X	} else if (bHaveRoot) {
X		if ((char *)0 != pcDefGroup) {
X			pcGroup = pcDefGroup;
X			if ((struct group *)0 == (grp = getgrnam(pcGroup))) {
X				(void)fprintf(stderr, "%s: getgrname: %s not found\n", progname, pcGroup);
X				exit(EXIT_OPT);
X			}
X		} else {
X			grp = getgrgid((int) statb_dir.st_gid);
X			if ((struct group *)0 == grp) {
X				(void)fprintf(stderr, "%s: no group entry for destination directory group %d\n", progname, statb_dir.st_gid);
X				exit(EXIT_OPT);
X			}
X		}
X	} else if ((struct group *)0 == (grp = getgrgid((int) getegid()))) {
X		(void)fprintf(stderr, "%s: getgrgid: %d (effective gid) not found\n", progname, getegid());
X		exit(EXIT_OPT);
X	}
X	grp = savegrent(grp);
X	(void)endgrent();
X
X	/* take specified mode, use destination mode, or the default
X	 * or inherit it from the parrent directory
X	 */
X	if ((char *)0 != pcMode) {
X		CvtMode(pcMode, & mMode, & mOptMode);
X		if (0 != mOptMode) {
X			mMode |= statb_dir.st_mode & mOptMode;
X		}
X	} else if (bDestExists != FALSE) {
X		mMode = statb_dir.st_mode &~ S_IFMT;
X	} else if ((char *)0 != pcDefMode) {
X		CvtMode(pcDefMode, & mMode, & mOptMode);
X		if (0 != mOptMode) {
X			mMode |= statb_dir.st_mode & mOptMode;
X		}
X	} else {
X		mMode = statb_dir.st_mode &~ S_IFMT;
X	}
X
#if defined(CONFIG)
X	if (Check.ffound) {
X		register int mChk;
X		register int fFail;
X
X		fFail = FALSE;
X		if ((char *)0 != Check.pclink) {
X			(void)fprintf(stderr, "%s: `%s\' be a %s link to `%s\'\n", progname, acDir, ':' == Check.pclink[0] ? "hard" : "symbolic", Check.pclink+1);
X			fFail = TRUE;
X			goto quit;
X		}
X		if ('*' == Check.acowner[0]) {
X			/* OK no check */;
X		} else if (Check.fbangowner) {
X			if (Check.uid == pwd->pw_uid) {
X				(void)fprintf(stderr, "%s: `%s\' should not have owner %s\n", progname, acDir, Check.acowner);
X				fFail = TRUE;
X			}
X		} else if (Check.uid != pwd->pw_uid) {
X			(void)fprintf(stderr, "%s: `%s\' owner %s should be %s\n", progname, acDir, pwd->pw_name, Check.acowner);
X			fFail = TRUE;
X		}
X
X		if ('*' == Check.acgroup[0]) {
X			/*OK */;
X		} else if (Check.fbangowner) {
X			if (Check.uid == pwd->pw_uid) {
X				(void)fprintf(stderr, "%s: `%s\' should not have group %s\n", progname, acDir, Check.acgroup);
X				fFail = TRUE;
X			}
X		} else if (Check.gid != grp->gr_gid) {
X			(void)fprintf(stderr, "%s: `%s\' group %s should be %s\n", progname, acDir, grp->gr_name, Check.acgroup);
X			fFail = TRUE;
X		}
X
X		switch (Check.acmode[0]) {
X		case '?':
X		case '*':
X			/*OK*/;
X			break;
X		case 'd':
X			mChk = PERM_RWX(Check.mmust|Check.moptional);
X			if (PERM_RWX(Check.mmust) != PERM_RWX(mMode&Check.mmust)) {
X				(void)fprintf(stderr, "%s: `%s\' mode %04o doesn\'t have bits to match %s (%04o)\n", progname, acDir, PERM_RWX(mMode), Check.acmode, mChk);
X				fFail = TRUE;
X			} else if (0 != (PERM_RWX(mMode) &~ mChk)) {
X				(void)fprintf(stderr, "%s: `%s\' mode %04o has too many bits to match %s (%04o)\n", progname, acDir, PERM_RWX(mMode), Check.acmode, mChk);
X				fFail = TRUE;
X			}
X			if (0 != (S_ISUID & mMode) ? 0 == (S_ISUID & (Check.mmust|Check.moptional)) : 0 != (S_ISUID & Check.mmust)) {
X				(void)fprintf(stderr, "%s: `%s\' setuid bit must be %s\n", progname, acDir, apcOO[0 == (S_ISUID & mMode)]);
X				fFail = TRUE;
X			}
X			if (0 != (S_ISGID & mMode) ? 0 == (S_ISGID & (Check.mmust|Check.moptional)) : 0 != (S_ISGID & Check.mmust)) {
X				(void)fprintf(stderr, "%s: `%s\' setgid bit must be %s\n", progname, acDir, apcOO[0 == (S_ISGID & mMode)]);
X				fFail = TRUE;
X			}
X			if (0 != (S_ISVTX & mMode) ? 0 == (S_ISVTX & (Check.mmust|Check.moptional)) : 0 != (S_ISVTX & Check.mmust)) {
X				(void)fprintf(stderr, "%s: `%s\' sticky bit must be %s\n", progname, acDir, apcOO[0 == (S_ISVTX & mMode)]);
X				fFail = TRUE;
X			}
X			break;
X		default:
X			(void)fprintf(stderr, "%s: `%s\' must be a %s\n", progname, acDir, NodeType(Check.mtype, (char *)0));
X			fFail = TRUE;
X			break;
X		case '!':
X			(void)fprintf(stderr, "%s: `%s\' should not be installed", progname, acDir);
X			if ((char *)0 != Check.pcmesg && '\000' != Check.pcmesg[0])
X				(void)fprintf(stderr, ", %s", Check.pcmesg);
X			(void)fputc('\n', stderr);
X			fFail = TRUE;
X			break;
X		}
X	quit:
X		if (FALSE != fFail) {
X			return FAIL;
X		}
X	}
#endif	/* have check list to compare with	*/
X
X	if (fRemove) {
X		register char *pcEnd;
X
X		if (!bDestExists) {
X			(void)fprintf(stderr, "%s: `%s\' doesn't exist to remove\n", progname, acDir);
X			return FAIL;
X		}
X
X		/* strangely this will remove /bin/OLD/OLD too,
X		 * but I think this is what we want in that case...
X		 */
X		if ((char *)0 == (pcEnd = strrchr(acDir, '\000')))
X			Die("nil eos pointer");
X		pcEnd[0] = '/';
X		(void)strcpy(pcEnd+1, OLDDIR);
X		if (-1 != access(acDir, F_OK) &&
X		    FAIL == DirInstall(acDir, FALSE == bHaveRoot ? (char *)0 : ODIROWNER, FALSE == bHaveRoot ? (char *)0 : ODIRGROUP, ODIRMODE, (char *)0, (char *)0, (char *)0, (char *)0, 1)) {
X			return FAIL;
X		}
X		*pcEnd = '\000';
X
#if defined(CONFIG)
X		if (Check.ffound) {
X			(void)printf("%s: %s(%d) %s %s, remove it?\n", progname, Check.pcspecial, Check.iline, 0 == strcmp(Check.pcpat, acDir) ? "is" : "matches", acDir);
X		}
#endif	/* config list to modify		*/
X
X		if (FALSE != fTrace) {
X			(void)printf("%s: rmdir %s\n", progname, acDir);
X		} else if (-1 == rmdir(acDir)) {
X			(void)fprintf(stderr, "%s: rmdir: %s: %s\n", progname, acDir, strerror(errno));
X			return FAIL;
X		}
X
#if defined(INST_FACILITY)
X		/*
X		 * do not syslog any user built directories
X		 */
X		if (bHaveRoot && FALSE == fTrace) {
X			(void)sprintf(acLogBuf, acDRemoved, acDir, pwd->pw_name, grp->gr_name, mMode, pcGuilty);
X			syslog(LOG_INFO, acLogBuf);
X		}
#endif	/* we should syslog changes		*/
X
X		return SUCCEED;
X	} else if (bDestExists) {
X		pcMsg = NodeType(statb_dir.st_mode, (char *)0);
X		switch (statb_dir.st_mode & S_IFMT) {
#if HAVE_SLINKS
X		case S_IFLNK:	/* symbolic link */
#endif	/* no links to think about */
#if defined(S_IFIFO)
X		case S_IFIFO:	/* fifo */
#endif	/* no fifos */
#if defined(S_IFSOCK)
X		case S_IFSOCK:	/* socket */
#endif	/* no sockets */
X		case S_IFCHR:	/* character special */
X		case S_IFBLK:	/* block special */
X		case 0:
X		case S_IFREG:
X			(void)fprintf(stderr, "%s: directory `%s\' is already a %s\n", progname, acDir, pcMsg);
X			return FAIL;
X
X		case S_IFDIR:	/* directory */
X			break;
X
X		default:
X			(void)fprintf(stderr, "%s: unrecognized file type on `%s\'\n", progname, acDir);
X			return FAIL;
X		}
X		bSame = TRUE;
X		if (statb_dir.st_uid != pwd->pw_uid) {
X			if (FALSE == fQuiet)
X				(void)fprintf(stderr, "%s: `%s\' owner mismatch (%d != %d)\n", progname, acDir, pwd->pw_uid, statb_dir.st_uid);
X			bSame = FALSE;
X		}
X		if (statb_dir.st_gid != grp->gr_gid) {
X			if (FALSE == fQuiet)
X				(void)fprintf(stderr, "%s: `%s\' group mismatch (%d != %d)\n", progname, acDir, grp->gr_gid, statb_dir.st_gid);
X			bSame = FALSE;
X		}
X		if (PERM_BITS(statb_dir.st_mode) != PERM_BITS(mMode)) {
X			if (FALSE == fQuiet)
X				(void)fprintf(stderr, "%s: `%s\' mode mismatch (%04o != %04o)\n", progname, acDir, mMode, statb_dir.st_mode &~ S_IFMT);
X			bSame = FALSE;
X		}
X		if ((S_ISUID & mMode) != (S_ISUID & statb_dir.st_mode)) {
X			(void)fprintf(stderr, "%s: `%s\' setuid bit changed, was %s\n", progname, acDir, apcOO[0 != (S_ISUID & mMode)]);
X			bSame = FALSE;
X		}
X		if ((S_ISGID & mMode) != (S_ISGID & statb_dir.st_mode)) {
X			(void)fprintf(stderr, "%s: `%s\' setgid bit changed, was %s\n", progname, acDir, apcOO[0 != (S_ISGID & mMode)]);
X			bSame = FALSE;
X		}
X		if ((S_ISVTX & mMode) != (S_ISVTX & statb_dir.st_mode)) {
X			(void)fprintf(stderr, "%s: `%s\' sticky bit changed, was %s\n", progname, acDir, apcOO[0 != (S_ISVTX & mMode)]);
X			bSame = FALSE;
X		}
X		if (FALSE != bSame) {
X			goto show_dir;
X		}
#if defined(INST_FACILITY)
X		pcLogStat = acDUpdated;
#endif	/* we should syslog changes		*/
X	} else {
#if HAVE_MKDIR
X		if (FALSE != fTrace) {
X			(void)printf("%s: mkdir %s\n", progname, acDir);
X		} else if (-1 == mkdir(acDir, mMode)) {
X			(void)fprintf(stderr, "%s: mkdir: %s: %s\n", progname, acDir, strerror(errno));
X			return FAIL;
X		}
#else	/* BSD */
X		if (RunCmd(acMkdir, acDir, (char *)0) != 0) {
X			/* mkdir reported fail for us */
X			return FAIL;
X		}
#endif	/* make a directory */
#if defined(INST_FACILITY)
X		pcLogStat = acDCreated;
#endif	/* we should syslog changes		*/
X	}
X
X	if (FALSE != bHaveRoot) {
X		ChOwnGrp(acDir, pwd, grp);
X	} else {
X		ChGroup(acDir, grp);
X	}
X	ChMode(acDir, mMode);
X
#if defined(INST_FACILITY)
X	/*
X	 * do not syslog any user built directories
X	 */
X	if (bHaveRoot && FALSE == fTrace) {
X		(void)sprintf(acLogBuf, pcLogStat, acDir, pwd->pw_name, grp->gr_name, mMode, pcGuilty);
X		syslog(LOG_INFO, acLogBuf);
X	}
#endif	/* we should syslog changes		*/
X
show_dir:
X	/* here we could make symbolic links	LLL
X	 */
X	if ((char *)0 != pcSLinks) {
X		if (LaunchLinks(&statb_dir, acDir, (char *)0, pcSLinks, mMode, pwd, grp)) {
X			(void)fprintf(stderr, "%s: %s: symbolic links failed\n", progname, acDir);
X		}
X	}
X
#if defined(CONFIG)
X	/* if the file is in a check list report installation message
X	 */
X	if (Check.ffound && '\000' != Check.pcmesg[0]) {
X		(void)printf("%s: %s: %s\n", progname, acDir, Check.pcmesg);
X	}
#endif	/* have check list to output a comment	*/
X
X	/*
X	 * show what we built with ls(1)
X	 */
X	if (FALSE != fVerbose) {
X		(void)RunCmd(acLs, acLsDirArgs, acDir);
X	}
X
X	return SUCCEED;
}
Purdue
chmod 0444 install.d/dir.c ||
echo 'restore of install.d/dir.c failed'
Wc_c="`wc -c < 'install.d/dir.c'`"
test 16773 -eq "$Wc_c" ||
	echo 'install.d/dir.c: original size 16773, current size' "$Wc_c"
fi
# ============= install.cf/install.cf.5l ==============
if test ! -d 'install.cf'; then
    echo 'x - creating directory install.cf'
    mkdir 'install.cf'
fi
if test -f 'install.cf/install.cf.5l' -a X"$1" != X"-c"; then
	echo 'x - skipping install.cf/install.cf.5l (File already exists)'
else
echo 'x - extracting install.cf/install.cf.5l (Text)'
sed 's/^X//' << 'Purdue' > 'install.cf/install.cf.5l' &&
.\" $Id: install.cf.5l,v 7.0 90/09/17 09:41:53 ksb Exp $
.\"
.\" 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.
.\"
.\" $Laser: ${tbl-tbl} %f |${ltroff-ltroff} -man
.\" $Compile: ${tbl-tbl} %f |${nroff-nroff} -man |${PAGER-${more-more}}
.TH INSTALL.CF 5L PUCC
.SH NAME
install.cf \- a check list to confirm correct installations
.SH DESCRIPTION
.PP
.IR Install (1L)
allows the system administrator to update important files
while maintaining a backup copy of any old versions.
.I Install
checks the actions of the superuser against a
list of important files, to prevent careless security
breaches.
The \fB\-C\fP option controls which
checklist \fIinstall\fP examines.
.PP
.I Install
searches \fIinstall.cf\fP for an
.IR sh (1)
\fIglob\fP expression that matches
the file to be installed.
When it finds a matching expression it checks
the proposed owner, group, and mode against those listed in
the checklist; any variation causes
.I install
to abort.
This mechanism is provided to protect the superuser from installing,
for instance, the shell setuid:
.sp 1
X	install -m7555 -o root -g wheel sh /bin
.sp 1
(note the extra \`5\').
.PP
Each data line in \fIinstall.cf\fP consists of 6 fields;
.br
X	a glob expression
.br
X	a mode, in either octal or symbolic format
.br
X	a symbolic owner
.br
X	a symbolic group
.br
X	a \fIstrip\fR(1) or \fIranlib\fR(1) indicator
.br
X	an optional comment
.br
Blank lines and lines which begin with a pound sign (#) are ignored.
.PP
If the glob expression begins with a slash (\*(lq/\*(rq)
it is matched against the full \fItarget\fP,
otherwise it is matched against only the last component of the \fItarget\fP.
(For the definition of \fItarget\fP see \fIinstall\fP(1L).)
For instance, the expression
.br
X	*.a
.br
will match every library (file ending in \`.a\'), while
.br
X	/lib/*.a
.br
will only match libraries in \*(lq/lib\*(rq.
.PP
The mode field may be filled with either an octal number (discouraged)
or a symbolic mode (see
.IR ls (1)).
Any bit specified as on \fBmust\fP be on,
any bit specified as off (\-) \fBmust\fP be off.
The symbolic mode may also include \`?\' to indicate an optional bit.
For instance the mode
.br
X	\-r?\-r\-\-r\-\-
.br
will match either \-rw\-r\-\-r\-\- (0644) or \-r\-\-r\-\-r\-\- (0444).
If a setuid, setgid, or sticky bit is set without the underlying
execute bit being set the \`s\' or \`t\' should be given in
uppercase.
.PP
Some combinations of modes are not expressible in the above format,
e.g. 0755 with an optional setgid bit.  For such modes a special
format is allowed:
.br
X	\fImode\fP/\fIoptional-mode\fP
.br
in which case all the bits in \fIoptional-mode\fP are taken as optional.
To express our example mode one might use:
.br
X	drwxr-xr-x/02000
.br
(this is a good mode for a directory under SunOS).
.PP
The type bit in the symbolic mode may be any of the \fIfind\fP(1) type
bits, or an exclamation point (\*(lq!\*(rq).  The ! is used to indicate files
that should \fBnever\fP exist, and may prefix an auxilary file type.
.PP
The owner (group) column may either contain an asterisk (\*(lq*\*(rq) or
an alphanumeric identifier.
If an asterisk is given any valid owner (group) may be used.
No numeric user (group) identifiers are allowed.
A owner (group) name may be prefixed by an exclamation point (\*(lq!\*(rq)
which indicates that only this owner (group) is not allowed.
.PP
The strip/ranlib indicator follows this table
.RS
.TS
l l.
n	do not run either \fIstrip\fP(1) or \fIranlib\fP(1)
l	run \fIranlib\fP(1)
s	run \fIstrip\fP(1)
*	allows either to be run, requires neither
.TE
.RE
It makes no sense (to the authors) to run
both \fIstrip\fP(1) and \fIranlib\fP(1).
.PP
.I Install
displays the comment section when the file is installed.
.PP
A double quote (\*(rq) substitutes the entry from the previous line.
An equals sign (=) substitutes \fIinstall\fP's default value for a field.
.PP
The tool \fIinstck\fP(1L) may be used to generate configuration
files for named files (assuming the current ones are correct).
.SH EXAMPLE
.RS
# this file tells install(1L) what files to check as special
.br
.br
.TS
l l l l l l.
# file	modes	owner	group	strip	comment
# name				ranlib	for user
/unix	\-r??r\--r\--	binary	*	n	new kernel
/vmunix	\*(rq	\*(rq	\*(rq	\*(rq	\*(rq
/dynix	\*(rq	\*(rq	\*(rq	\*(rq	\*(rq
X
/bin/sh	\-rwx?\-x?\-x	*	*	*	do not setuid
/bin/su	\-rws?\-x?\-x	root	*	*	must setuid, not group
/bin/init	\-rwx\-\-\-\-\-\-	root	*	*	no world execute
/etc/init	\*(rq	\*(rq	*	*	no world execute
/etc/passwd	\-r?\-r\-\-r\-\-	root	*	n	forbid world write
/etc/group	\*(rq	\*(rq	*	n	\*(rq
/tmp	drwxrwxrwt	binary	*	n	must be sticky at PUCC
/usr/tmp	\*(rq	\*(rq	*	n	sticky
/usr/ucb/eyacc	\-rwxr\-x?\-x	*	*	n	no strip
/usr/ucb/lisp	\*(rq	\*(rq	\*(rq	\*(rq	\*(rq
X
core	!-??-?--?--	*	*	n	a bogus core file
.TE
.RE
.SH AUTHORS
Jeff Smith, Purdue University Computing Center (jsmith@cc.purdue.edu)
.br
Kevin Braunsdorf, Purdue University Computing Center (ksb@cc.purdue.edu)
.br
Copyright \*(co 1990 Purdue Research Foundation.  All rights reserved.
.SH SEE ALSO
install(1L), instck(1L), ls(1), sh(1), strip(1), ranlib(1)
Purdue
chmod 0444 install.cf/install.cf.5l ||
echo 'restore of install.cf/install.cf.5l failed'
Wc_c="`wc -c < 'install.cf/install.cf.5l'`"
test 6131 -eq "$Wc_c" ||
	echo 'install.cf/install.cf.5l: original size 6131, current size' "$Wc_c"
fi
# ============= instck/maxfreq.c ==============
if test ! -d 'instck'; then
    echo 'x - creating directory instck'
    mkdir 'instck'
fi
if test -f 'instck/maxfreq.c' -a X"$1" != X"-c"; then
	echo 'x - skipping instck/maxfreq.c (File already exists)'
else
echo 'x - extracting instck/maxfreq.c (Text)'
sed 's/^X//' << 'Purdue' > 'instck/maxfreq.c' &&
/*
X * Copyright 1990 Purdue Research Foundation, West Lafayette, Indiana
X * 47907.  All rights reserved.
X *
X * Written by D. Scott Guthridge, aho@cc.purdue.edu			(dsg)
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 * $Compile(test): ${cc-cc} ${cc_debug--g} -DTEST -o %F %f
X * $Id: maxfreq.c,v 7.1 90/11/28 09:09:55 ksb Exp $
X * @(#)
X */
X
/*@Header		@*/
#include <stdio.h>		/* for stderr		*/
#include "configure.h"
#include "main.h"
#include "gen.h"
#include "maxfreq.h"
X
#if STRINGS
#include <strings.h>
#else
#include <string.h>
#endif
X
/*@Explode config	@*/
#ifdef TEST
X
/*
X * copy an ME element
X *
X * This happens only when a new element is added to the
X * cache. Return non-zero on failure.
X */
int
MECopy(pMEDest, pMESource)
X	ME_ELEMENT *pMEDest, *pMESource;
{
X	(void)strcpy(pMEDest->ac, pMESource->ac);
X	return 0;
}
X
/*
X * compare two ME elements return 0 if they are equal; non-zero if they
X * are not
X */
int
MECompare(pME1, pME2)
X	ME_ELEMENT *pME1, *pME2;
{
X	return strcmp(pME1->ac, pME2->ac);
}
#endif /* TEST	*/
X
/*@Explode inc		@*/
extern char *malloc();
X
/*
X * insert/search for a node then increment the count
X * (this often means moving it to preserve the lower/equal rules)
X *
X * returns the user-defined part of the node it finds or
X * (ME_ELEMENT *)0 if the copy or malloc fails
X */
ME_ELEMENT *
MFIncrement(ppMFRoot, pME)
X	MAXFREQ **ppMFRoot;
X	ME_ELEMENT *pME;
{
X	register MAXFREQ *pMF;
X	register MAXFREQ **ppMFAnchor;
X	register MAXFREQ *pMFColumn;
X	register MAXFREQ **ppMFOneBack;
X	register MAXFREQ **ppMFTwoBack;
X
X	ppMFTwoBack = (MAXFREQ **)0;
X	ppMFOneBack = ppMFRoot;
X	ppMFAnchor = ppMFRoot;
X	for (pMFColumn = *ppMFRoot; nilMF != pMFColumn; pMFColumn = pMFColumn->pMFlower) {
X		for (pMF = pMFColumn; nilMF != pMF; pMF = pMF->pMFequal) {
X			if (0 == MECompare(pME, & pMF->ME)) {
X				/*
X				 * remove this one even though it may
X				 * be in the right place -- it's too
X				 * expensive to find out
X				 */
X				++pMF->icount;
X				if (nilMF != pMF->pMFequal) {
X					*ppMFAnchor = pMF->pMFequal;
X					pMF->pMFequal->pMFlower = pMF->pMFlower;
X				} else {
X					*ppMFAnchor = pMF->pMFlower;
X				}
X				if ((MAXFREQ **)0 == ppMFTwoBack) {
X					pMF->pMFlower = *ppMFRoot;
X					pMF->pMFequal = nilMF;
X					*ppMFRoot = pMF;
X					return & pMF->ME;
X				}
X				if ((*ppMFTwoBack)->icount == pMF->icount) {
X					pMF->pMFlower = (*ppMFTwoBack)->pMFlower;
X					pMF->pMFequal = *ppMFTwoBack;
X					(*ppMFTwoBack)->pMFlower = nilMF;
X					*ppMFTwoBack = pMF;
X					return & pMF->ME;
X				}
X				pMF->pMFlower = *ppMFOneBack;
X				pMF->pMFequal = nilMF;
X				*ppMFOneBack = pMF;
X				return & pMF->ME;
X			}
X			ppMFAnchor = & pMF->pMFequal;
X		}
X		ppMFAnchor = & pMFColumn->pMFlower;
X		ppMFTwoBack = ppMFOneBack;
X		ppMFOneBack = & pMFColumn->pMFlower;
X	}
X	/*
X	 * not found -- push a new element on the top of the last column
X	 *
X	 * assert(nilMF == pMFColumn);
X	 * assert(nilMF == pMF);
X	 */
X	if (nilMF == (pMF = (MAXFREQ *)malloc(sizeof(MAXFREQ)))) {
X		return (ME_ELEMENT *)0;
X	}
X	pMF->pMFlower = nilMF;
X	if (0 != MECopy(& pMF->ME, pME)) {
X		return (ME_ELEMENT *)0;
X	}
X	pMF->icount = 1;
X	if ((MAXFREQ **)0 != ppMFTwoBack && 1 == (*ppMFTwoBack)->icount) {
X		pMF->pMFequal = *ppMFTwoBack;
X		*ppMFTwoBack = pMF;
X		return & pMF->ME;
X	}
X	pMF->pMFequal = nilMF;
X	*ppMFOneBack = pMF;
X	return & pMF->ME;
}
X
/*@Explode free	@*/
/* 
X * free a maxfreq cache
X */
static void
MFFree(pMFRoot)
X	MAXFREQ *pMFRoot;
{
X	register MAXFREQ *pMF;
X	register MAXFREQ *pMFFree;
X	register MAXFREQ *pMFColumn;
X
X	for (pMFColumn = pMFRoot; nilMF != pMFColumn; pMFColumn = pMFColumn->pMFlower) {
X		for (pMF = pMFColumn; nilMF != pMF; /*EMPTY*/) {
X			pMFFree = pMF;
X			pMF = pMF->pMFequal;
X			free((char *)pMFFree);
X		}
X	}
}
X
/*@Explode check	@*/
/*
X * determine whether a value is among those with the highest
X * frequency
X *
X * return 0 for no; non-zero for yes.
X */
int
MFCheckMax(pMFRoot, pME)
X	register MAXFREQ *pMFRoot;
X	ME_ELEMENT *pME; 
{
X	while (nilMF != pMFRoot) {
X		if (0 == MECompare(pME, & pMFRoot->ME)) {
X			return 1;
X		}
X		pMFRoot = pMFRoot->pMFequal;
X	}
X	return 0;
}
X
/*@Explode scan	@*/
/*
X * scan over the nodes of a maxfreq cache
X *
X * Stop the scan and return the non-zero value if the given function
X * returns non-zero.
X */
int
MFScan(pMFRoot, pfi)
X	MAXFREQ *pMFRoot;
X	int (*pfi)();
{
X	register MAXFREQ *pMF;
X	register int iRet;
X	register MAXFREQ *pMFColumn;
X
X	for (pMFColumn = pMFRoot; nilMF != pMFColumn; pMFColumn = pMFColumn->pMFlower) {
X		for (pMF = pMFColumn; nilMF != pMF; pMF = pMF->pMFequal) {
X			if (0 != (iRet = (*pfi)(pMF))) {
X				return iRet;
X			}
X		}
X	}
X	return 0;
}
X
/*@Explode test		@*/
#ifdef TEST
char *progname;
X
/*
X * print routine prototyped for MFScan					(dsg)
X */
static int
PrintNode(pMF)
X	MAXFREQ *pMF;
{
X	(void)printf("%7d %s", pMF->icount, pMF->ME.ac);
X	return 0;
}
X
/* a driver								(dsg)
X * do maxfreq computation on small strings (group names, for example)
X */
int
main(argc, argv)
X	int argc;
X	char **argv;
{
X	auto ME_ELEMENT ME;
X	auto MAXFREQ *pMFRoot;
X
X	progname = argv[0];
X	pMFRoot = nilMF;
X	while ((char *)0 != fgets(& ME.ac[0], MAXBUF, stdin)) {
#ifdef TESTCHECK
X		(void)printf("before insertion element is ");
X		if (0 == MFCheckMax(pMFRoot, & ME)) {
X			(void)printf("not ");
X		}
X		(void)printf("among those with highest freq\n");
#endif /* TESTCHECK	*/
X		(void)MFIncrement(& pMFRoot, & ME);
X	}
X	(void)MFScan(pMFRoot, PrintNode);
X	MFFree(pMFRoot);
X
X	exit(0);
}
#endif /* TEST	*/
Purdue
chmod 0444 instck/maxfreq.c ||
echo 'restore of instck/maxfreq.c failed'
Wc_c="`wc -c < 'instck/maxfreq.c'`"
test 6205 -eq "$Wc_c" ||
	echo 'instck/maxfreq.c: original size 6205, current size' "$Wc_c"
fi
# ============= instck/Makefile ==============
if test -f 'instck/Makefile' -a X"$1" != X"-c"; then
	echo 'x - skipping instck/Makefile (File already exists)'
else
echo 'x - extracting instck/Makefile (Text)'
sed 's/^X//' << 'Purdue' > 'instck/Makefile' &&
# 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.
#
# Makefile for instck							(ksb)
#
# $Id: Makefile,v 7.1 90/09/17 11:34:00 ksb Exp $
#
X
BIN=	${DESTDIR}/usr/local/etc
PROG=	instck
X
# where is the source code for install(1L)?
INSTALLD= ../install.d
#INSTALLD= /usr/src/usr.bin/install.d
#INSTALLD= /usr/src/local/cmd/install.d
X
PURGED= ../purge
#PURGED= /usr/src/local/etc/purge
X
# do we have symbolic links?
#  N.B. without symbolic links the source for install must be on
#	this file system.
#LN=ln
LN=ln -s
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=	instck.h maxfreq.h main.h path.h gen.h
SRC=	instck.c maxfreq.c main.c path.c gen.c
LINKH=	configure.h install.h special.h special.i syscalls.h
LINKC=	syscalls.c special.c
LINK=	${LINKH} ${LINKC}
LINKPH=	filedup.h
LINKPC=	filedup.c
LINKP=	${LINKPH} ${LINKPC}
GENC=	${LINKC} ${LINKPC}
GENH=	${LINKH} ${LINKPH}
DEP=	${SRC} ${GENC}
GEN=	${GENC} ${GENH}
OBJ=	main.o instck.o maxfreq.o syscalls.o special.o path.o gen.o filedup.o
MAN=	instck.1l
OTHER=	README
SOURCE=	Makefile ${HDR} ${SRC} ${MAN} ${OTHER}
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
links: ${LINK} ${LINKP}
X
${LINK}:
X	${LN} ${INSTALLD}/$@ ./$@
X
${LINKP}:
X	${LN} ${PURGED}/$@ ./$@
X
clean: FRC
X	rm -f Makefile.bak ${PROG} ${GEN} *.o a.out core errs tags
X
depend: ${HDR} ${SRC} ${GEN} FRC
X	maketd ${CDEFS} ${INCLUDE} ${DEP}
X
dirs: ${BIN}
X
install: all dirs 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
/ ${BIN}:
X	install -d -r $@
X
${SOURCE}:
X	co -q $@
X
FRC:
X
# DO NOT DELETE THIS LINE - maketd DEPENDS ON IT
X
instck.o: configure.h install.h instck.c instck.h main.h path.h special.h \
X	syscalls.h
X
maxfreq.o: configure.h gen.h main.h maxfreq.c maxfreq.h
X
main.o: configure.h install.h instck.h main.c main.h special.h syscalls.h
X
path.o: configure.h install.h instck.h main.h path.c path.h special.h
X
syscalls.o: configure.h install.h main.h syscalls.c
X
special.o: configure.h install.h instck.h main.h special.c special.h special.i \
X	syscalls.h
X
gen.o: configure.h filedup.h gen.c gen.h install.h instck.h main.h maxfreq.h \
X	path.h special.h syscalls.h
X
filedup.o: filedup.c filedup.h
X
# *** Do not add anything here - It will go away. ***
Purdue
chmod 0644 instck/Makefile ||
echo 'restore of instck/Makefile failed'
Wc_c="`wc -c < 'instck/Makefile'`"
test 3595 -eq "$Wc_c" ||
	echo 'instck/Makefile: original size 3595, current size' "$Wc_c"
fi
# ============= install.cf/README ==============
if test -f 'install.cf/README' -a X"$1" != X"-c"; then
	echo 'x - skipping install.cf/README (File already exists)'
else
echo 'x - extracting install.cf/README (Text)'
sed 's/^X//' << 'Purdue' > 'install.cf/README' &&
You might want to use instck(1L) to generate more lines for this
file, for example all the setuid/setgid files on your system:
X
X	find / \( -perm -4000 -o -perm -2000 \) -type f -print |
X		xargs instck -GSLv
X
ksb
Purdue
chmod 0444 install.cf/README ||
echo 'restore of install.cf/README failed'
Wc_c="`wc -c < 'install.cf/README'`"
test 212 -eq "$Wc_c" ||
	echo 'install.cf/README: original size 212, current size' "$Wc_c"
fi
# ============= purge/main.h ==============
if test ! -d 'purge'; then
    echo 'x - creating directory purge'
    mkdir 'purge'
fi
if test -f 'purge/main.h' -a X"$1" != X"-c"; then
	echo 'x - skipping purge/main.h (File already exists)'
else
echo 'x - extracting purge/main.h (Text)'
sed 's/^X//' << 'Purdue' > 'purge/main.h' &&
/*
X * parse options
X */
X
extern char *progname, u_terse[], *u_help[];
extern int main();
extern int fAnyOwner, fSuperUser, fVersion, iDays, fExec, fVerbose;
Purdue
chmod 0644 purge/main.h ||
echo 'restore of purge/main.h failed'
Wc_c="`wc -c < 'purge/main.h'`"
test 157 -eq "$Wc_c" ||
	echo 'purge/main.h: original size 157, current size' "$Wc_c"
fi
true || echo 'restore of purge/filedup.c failed'
echo End of part 5, continue with part 6
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.