[comp.sources.unix] v24i063: Purdue software product installation system, Part01/07

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

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

This seven part archive contains the basic `product installation'
system that we use a PUCC.  You'll need the first part (libs + maketd)
to compile these.
	install(1l)	- update a file or directory with a guaranteed backup
	instck(1l)	- look for improperly installed files
	purge(8l)	- remove outdated backup copies of installed programs
Install is a tool for updating system files in a controlled environment.
The design philosophy of install is to automate the installation process
and put it as much out of reach of human carelessness as possible, while
providing a flexible, easy to use tool.

Among its features,Install provides the following install increases system
security by providing a controlled installation environment.  It checks
the actions of the superuser against a configuration file that you build
(either by hand or using instck), and can prevent grievous mistakes such
as installing a shell setuid to a privileged user, or installing a
world\(hywritable password file.  An appropriate configuration file can
guarantee that files are installed with correct owner, group and mode,
that strip is run on binaries and ranlib is run on libraries.

Regardless of whether you create a configuration file, install warns you
of many possible errors, unless you make it quiet with its \-q option.
For instance, if you install a new version of the ex(1) editor and forget
to update its links, install will notice the extra links to the existing
version and warn you that you might be making a mistake.


#!/bin/sh
# This is pucc-1b, a shell archive (produced by shar 3.49)
# To extract the files from this archive, save it to a file, remove
# everything above the "!/bin/sh" line above, and type "sh file_name".
#
# made 11/29/1990 17:23 UTC by ksb@cc.purdue.edu (Kevin Braunsdorf)
# Source directory /ksb/c.s.u-2
#
# existing files will NOT be overwritten unless -c is specified
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#   1813 -rw-r--r-- INSTALL.02
#  34702 -r--r--r-- instck/instck.c
#  13940 -r--r--r-- instck/gen.c
#    322 -r--r--r-- purge/README
#  46361 -r--r--r-- install.d/file.c
#   2505 -r--r--r-- instck/instck.h
#  19085 -r--r--r-- install.d/main.c
#  18260 -r--r--r-- install.d/syscalls.c
#   8078 -r--r--r-- instck/main.c
#   1858 -r--r--r-- instck/main.h
#   1669 -r--r--r-- install.cf/install.cf
#  21337 -r--r--r-- install.d/install.1l
#  14969 -r--r--r-- purge/purge.c
#   9712 -r--r--r-- install.d/special.i
#   2355 -rw-r--r-- purge/Makefile
#    379 -r--r--r-- install.d/TODO
#    232 -r--r--r-- install.d/myself.cf
#  15681 -r--r--r-- install.d/special.c
#  16773 -r--r--r-- install.d/dir.c
#   6131 -r--r--r-- install.cf/install.cf.5l
#   6205 -r--r--r-- instck/maxfreq.c
#   3595 -rw-r--r-- instck/Makefile
#    212 -r--r--r-- install.cf/README
#    157 -rw-r--r-- purge/main.h
#   6893 -r--r--r-- purge/filedup.c
#   6261 -r--r--r-- install.d/install.h
#   6676 -rw-r--r-- install.d/configure.h
#   4281 -r--r--r-- instck/instck.1l
#   3262 -r--r--r-- instck/path.c
#   2977 -rw-r--r-- install.d/Makefile
#   4174 -r--r--r-- install.d/README
#   2898 -r--r--r-- install.d/main.h
#   3429 -r--r--r-- install.d/special.h
#   2444 -r--r--r-- purge/purge.8l
#   2394 -rw-r--r-- purge/main.c
#   2197 -rw-r--r-- purge/Makefile.mkcmd
#    837 -rw-r--r-- install.cf/Makefile
#   2372 -r--r--r-- install.d/syscalls.h
#   1580 -r--r--r-- instck/path.h
#   1520 -r--r--r-- instck/gen.h
#   1307 -r--r--r-- instck/README
#   1367 -r--r--r-- purge/purge.m
#   1148 -r--r--r-- purge/purge.h
#   1262 -r--r--r-- install.d/dir.h
#   1611 -r--r--r-- install.d/file.h
#   1018 -r--r--r-- instck/maxfreq.h
#    825 -r--r--r-- purge/filedup.h
#
# ============= INSTALL.02 ==============
if test -f 'INSTALL.02' -a X"$1" != X"-c"; then
	echo 'x - skipping INSTALL.02 (File already exists)'
else
echo 'x - extracting INSTALL.02 (Text)'
sed 's/^X//' << 'Purdue' > 'INSTALL.02' &&
Contains:
X	install(1l)	- update a file or directory with a guaranteed backup
X	instck(1l)	- look for improperly installed files
X	purge(8l)	- remove outdated backup copies of installed programs
X
X
Notes on depends:
X	- libopt.a and libsrtunq.a need not be installed, but it
X	  would make life easier if you want other PUCC tools.
X
X	- install needs to be compiled with libopt, but can run on
X	  almost any UNIX system
X
X	- instck needs to know where install's source code is,
X	  and needs libopt
X
X	- purge needs to know where install's source code is,
X	  and needs libopt
X
X
To install these tools:
X
0\ read the manual pages, see if you want any of them
X
1\ Edit install.d/configure.h while looking at a hard copy of
X   install.d/install.h (which contains deafult values).
X   You'll want to set DEFGROUP at least ("binary" is a PUCC localism).
X	lpr install.d/install.h
X	vi install.d/configure.h
X
2\ decide where to install all this stuff, change the destinations in
X   the Makefiles {BIN,LIB,ETC,HEADER}
X	vi */Makefile
X	
3\ build install, re-edit configure.h as needed, and check install's defaults:
X	(cd install.d && make)
X	install.d/installp -V
X
4\ if we do not have symbolic links tell instck/Makefile and purge/Makefile
X	vi instck/Makefile purge/Makefile
X
5\ build instck & purge
X	(cd instck && make)
X	(cd purge && make)
X
6\ install the tools
X	su
X	(cd install.d && make install)			# as root
X	(cd instck && make install)			#
X	(cd purge && make install)			#
X	exit						# the root shell
X
7\ if you have mkcat, install the manual pages
X	su
X	mkcat -v install.d/install.1l install.d/install.cf.5l \
X	     instck/instck.1l purge/purge.8l
X	exit
X
8\ clean up the dirs
X	(cd install.d && make clean)
X	(cd instck && make clean)
X	(cd purge && make clean)
X
kayessbee
--
Kevin Braunsdorf, ksb@cc.purdue.edu, pur-ee!ksb, purdue!ksb
Purdue
chmod 0644 INSTALL.02 ||
echo 'restore of INSTALL.02 failed'
Wc_c="`wc -c < 'INSTALL.02'`"
test 1813 -eq "$Wc_c" ||
	echo 'INSTALL.02: original size 1813, current size' "$Wc_c"
fi
# ============= instck/instck.c ==============
if test ! -d 'instck'; then
    echo 'x - creating directory instck'
    mkdir 'instck'
fi
if test -f 'instck/instck.c' -a X"$1" != X"-c"; then
	echo 'x - skipping instck/instck.c (File already exists)'
else
echo 'x - extracting instck/instck.c (Text)'
sed 's/^X//' << 'Purdue' > 'instck/instck.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 * these routines check the installation of binary (mostly) files	(ksb)
X * we look for glob expressions to match in given directories, and
X * at what modes the matched files have.  If they look OK we say
X * nothing, else we bitch about the errors.
X */
#if !defined(lint)
static char *rcsid = "$Id: instck.c,v 7.7 90/10/22 12:58:56 ksb Exp $";
#endif	/* !lint */
X
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/time.h>
#include <fstab.h>
#include <a.out.h>
X
#include "configure.h"
#include "install.h"
#include "main.h"
#include "syscalls.h"
#include "special.h"
#include "instck.h"
#include "path.h"
X
#if defined(HPUX7)
#include <checklist.h>
#define fstab		checklist
#define fs_file		fs_dir
#endif
X
#if defined(SYSV)
#include <sgsparam.h>
#endif
#include <ar.h>
X
#if HAVE_RANLIB
#include <ranlib.h>
#endif
X
#if BSDDIR
#include <sys/dir.h>
#else
#include <ndir.h>
#endif
X
#if STRINGS
#include <strings.h>
#else
#include <string.h>
#endif
X
X
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>
X
extern int stat(), lstat();
extern char *calloc(), *malloc();
extern off_t lseek();
X
X
/*
X * remove all the dirs/files that point to the the same file/dir	(ksb)
X * in a choice between two, take the full path name, or the shorter one.
X * return the new argc
X */
int
ElimDups(argc, argv)
int argc;
char **argv;
{
X	auto int i, j, r;
X	auto struct stat *pSTScan;
X
X	pSTScan = (struct stat *)calloc((unsigned)argc, sizeof(struct stat));
X	if ((struct stat *)0 == pSTScan) {
X		(void)fprintf(stderr, "%s: calloc: out of memory\n", progname);
X		exit(1);
X	}
X
X	for (i = 0; i < argc; ++i) {
X		if (-1 == stat(argv[i], & pSTScan[i])) {
X			(void)fprintf(stderr, "%s: stat: %s: %s\n", progname, argv[i], strerror(errno));
X			argv[i] = (char *)0;
X			continue;
X		}
X		for (j = 0; j < i; ++j) {
X			if ((char *)0 == argv[j])
X				continue;
X			if (pSTScan[j].st_ino != pSTScan[i].st_ino)
X				continue;
X			if (pSTScan[j].st_dev != pSTScan[i].st_dev)
X				continue;
X			if (S_IFDIR != (pSTScan[i].st_mode & S_IFMT))
X				continue;
X			if (strlen(argv[j]) < strlen(argv[i])) {
X				r = i;
X				if ('/' != argv[j][0] && '/' == argv[i][0])
X					r = j;
X			} else {
X				r = j;
X				if ('/' != argv[i][0] && '/' == argv[j][0])
X					r =i;
X			}
X			if (fVerbose)
X				(void)fprintf(stderr, "%s: `%s\': dup of `%s\' (not scanned)\n", progname, argv[r], argv[r == i ? j : i]);
X			argv[r] = (char *)0;
X		}
X	}
X	free((char *)pSTScan);
X	for (j = 0, i = 0; i < argc; ++i) {
X		if ((char *)0 == argv[i])
X			continue;
X		argv[j++] = argv[i];
X	}
X
X	return j;
}
X
/*
X * called if the glob match code didn't find any new files		(ksb)
X */
void
NoMatches(pcGlob)
char *pcGlob;
{
X	if (fVerbose) {
X		(void)fprintf(fpOut, "%s: no matches for `%s\'\n", progname, pcGlob);
X	}
}
X
/*
X * used in the path module to init the user data field			(ksb)
X */
void
PUInit(pPD)
PATH_DATA *pPD;
{
X	pPD->fseen = 0;
}
X
X
/*
X * find matching files, look in the real file system			(ksb)
X */
int
FileMatch(pcGlob, pcDir, pfiCall)
char *pcGlob, *pcDir;
int (*pfiCall)(/* char * */);
{
X	register DIR *pDI;
X	register struct direct *pDE;
X	register char *pcTail;
X	register char *pcSlash;
X	auto int iRet;
X	auto char acDir[MAXPATHLEN+1];
X	auto struct stat stDir, stEnt;
X
X	if ((DIR *)0 == (pDI = opendir(pcDir))) {
X		return 0;
X	}
X	pcSlash = strchr(pcGlob, '/');
X	if ((char *)0 != pcSlash) {
X		*pcSlash++ = '\000';
X	}
X	(void)strcpy(acDir, pcDir);
X	pcTail = strchr(acDir, '\000');
X	if (pcTail[-1] != '/') {
X		*pcTail++ = '/';
X	}
X	iRet = 0;
X	if ('\000' == *pcGlob) {
X		if (-1 != (fLinks ? LSTAT : stat)(acDir, & stDir)) {
X			iRet = (*pfiCall)(acDir, &stDir);
X		}
X	} else if ((char *)0 == pcSlash) {
X		while (0 == iRet && (struct direct *)0 != (pDE = readdir(pDI))) {
X			if (! _SamePath(pcGlob, pDE->d_name, 1)) {
X				continue;
X			}
X			(void)strcpy(pcTail, pDE->d_name);
X			if ((char *)0 != pcSlash) {
X				pcSlash[-1] = '/';
X			}
X			if (-1 == (fLinks ? LSTAT : stat)(acDir, &stEnt)) {
X				fprintf(stderr, "%s: stat: %s: %s\n", progname, acDir, strerror(errno));
X			} else {
X				iRet = (*pfiCall)(acDir, &stEnt);
X			}
X			if ((char *)0 != pcSlash) {
X				pcSlash[-1] = '\000';
X			}
X		}
X	} else if ('\000' == pcSlash[0]) {	/* "p/" matches dirs only */
X		while (0 == iRet && (struct direct *)0 != (pDE = readdir(pDI))) {
X			if (! _SamePath(pcGlob, pDE->d_name, 1))
X				continue;
X			(void)strcpy(pcTail, pDE->d_name);
X			if (-1 == (fLinks ? LSTAT : stat)(acDir, & stDir))
X				continue;
X			if (S_IFDIR != (stDir.st_mode & S_IFMT))
X				continue;
X			(void)strcat(pcTail, "/");
X			if ((char *)0 != pcSlash) {
X				pcSlash[-1] = '/';
X			}
X			iRet = (*pfiCall)(acDir, & stDir);
X			if ((char *)0 != pcSlash) {
X				pcSlash[-1] = '\000';
X			}
X		}
X	} else {
X		while (0 == iRet && (struct direct *)0 != (pDE = readdir(pDI))) {
X			if (! _SamePath(pcGlob, pDE->d_name, 1))
X				continue;
X			(void)strcpy(pcTail, pDE->d_name);
X			(void)strcat(pcTail, "/");
X			if ((char *)0 != pcSlash) {
X				pcSlash[-1] = '/';
X			}
X			iRet = FileMatch(pcSlash, acDir, pfiCall);
X			if ((char *)0 != pcSlash) {
X				pcSlash[-1] = '\000';
X			}
X		}
X	}
X	closedir(pDI);
X	if ((char *)0 != pcSlash) {
X		*--pcSlash = '/';
X	}
X	return iRet;
}
X
X
/*
X * return either "setuid" or "setgid" if the program is loaded with	(ksb)
X * `#!...' and is currently an unsafe mode
X */
#define	BAD_LEN	2
char *
BadLoad(fdFile, mMode)
int fdFile;
int mMode;
{
X	auto char acLook[8];
X
X	if (-1 == lseek(fdFile, (off_t)0, 0)) {
X		fprintf(stderr, "%s: lseek: %d: %s\n", progname, fdFile, strerror(errno));
X		return (char *)0;
X	}
X	if (BAD_LEN != read(fdFile, &acLook[0], BAD_LEN)) {
X		return (char *)0;
X	}
X	if ('#' != acLook[0] || '!' != acLook[1]) {
X		return (char *)0;
X	}
X	if (0 != (S_ISUID&mMode)) {
X		return "setuid";
X	}
X	if (0 != (S_ISGID*mMode)) {
X		return "setgid";
X	}
X	return (char *)0;
}
#undef BAD_LEN
X
/*
X * is the file in question a binary and stripped?
X * -1 not a binary
X * 0 binary, not stripped
X * 1 binary, stripped
X */
int
IsStripped(fdFile, pstBin)
int fdFile;
struct stat *pstBin;
{
X	auto EXHDR exhd;
X
X	if (S_IFREG != (pstBin->st_mode & S_IFMT)) {
X		/* libs must be plain files */
X		return -1;
X	}
X	if (-1 == lseek(fdFile, (off_t)0, 0)) {
X		fprintf(stderr, "%s: lseek: %d: %s\n", progname, fdFile, strerror(errno));
X		return -1;
X	}
X	if (sizeof(exhd) != read(fdFile, (char *) &exhd, sizeof(exhd))) {
X		return -1;
X	}
X	switch (exhd.MNUMBER) {
#if defined(FMAGIC)
X	case FMAGIC:			/* HP format?			*/
#endif
#if defined(OMAGIC)
X	case OMAGIC:			/* old impure format		*/
#endif
#if defined(NMAGIC)
X	case NMAGIC:			/* read-only text		*/
#endif
#if defined(ZMAGIC)
X	case ZMAGIC:			/* demand load format		*/
#endif
#if defined(ETAMAGIC)
X	case ETAMAGIC:			/* eta systems load format	*/
#endif
X		if (0 == exhd.MSYMS)
X			return 1;
X		return 0;
X	default:
X		break;
X	}
X	return -1;
}
X
/*
X * is the named file a ar random library				(ksb)
X * Read the ar magic number, is it a library?
X * read the first ar member, is it __.SYMDEF
X * is __.SYMDEF OLDer that the file (only ranlib does this)
X */
int
IsRanlibbed(fdFile, pstLib)
int fdFile;
struct stat *pstLib;
{
X	extern long atol();
#if !defined(SARMAG)
X	static char ar_gross[] = ARMAG;
#define SARMAG (sizeof(ar_gross)-1)
#endif
#if defined(bsd)
X	static char acTOC[] = "__.SYMDEF";
#endif
X	auto char acBuf[SARMAG+10];
X	auto struct ar_hdr arhd;
X
X	if (S_IFREG != (pstLib->st_mode & S_IFMT)) {
X		/* libs must be plain files */
X		return -1;
X	}
X	if (-1 == lseek(fdFile, (off_t)0, 0)) {
X		fprintf(stderr, "%s: lseek: %d: %s\n", progname, fdFile, strerror(errno));
X		return -1;
X	}
X	if (SARMAG != read(fdFile, acBuf, SARMAG)) {
X		return -1;
X	}
X	if (0 != strncmp(ARMAG, acBuf, SARMAG)) {
X		return -1;
X	}
#if defined(bsd)
X	if (sizeof(arhd) != read(fdFile, (char *)& arhd, sizeof(arhd))) {
X		return 0;
X	}
X	if (0 != strncmp(acTOC, arhd.ar_name, sizeof(acTOC)-1)) {
X		return 0;
X	}
X	return pstLib->st_mtime <= atol(arhd.ar_date);
#else
X	return 1;
#endif
}
X
#if BROKEN_CHMOD
static char aaacAdd[8][8][4] =
/* add 0   01	02    03   04    05    06     07      have	*/
X  { { "", "x", "w", "wx", "r", "rx", "rw", "rwx"}, /* 00	*/
X    { "",  "", "w",  "w", "r",  "r", "rw",  "rw"}, /* 01	*/
X    { "", "x",  "",  "x", "r", "rx",  "r",  "rx"}, /* 02	*/
X    { "",  "",  "",   "", "r",  "r",  "r",   "r"}, /* 03	*/
X    { "", "x", "w", "wx",  "",  "x",  "w",  "wx"}, /* 04	*/
X    { "",  "", "w",  "w",  "",   "",  "w",   "w"}, /* 05	*/
X    { "", "x",  "",  "x",  "",  "x",   "",   "x"}, /* 06	*/
X    { "",  "",  "",   "",  "",   "",   "",    ""}};/* 07	*/
X
/*
X * sun's chmod(1) won't take litteral 0755 to change setgid bits	(ksb)
X * so we have to convert such things into symbolic +- forms (yucko)
X *
X * If we exchange the subscripts of the Add matrix we get a subtraction
X * matrix.  See assignments to pcS{Uid,Gid,All} below vs pcA{...}.
X */
static void
SunBotch(pcDo, mFrom, mTo)
char *pcDo;
int mFrom, mTo;
{
X	auto char cUid, cGid, cSticky, cSep;
X	auto char *pcAUid, *pcAGid, *pcAAll;
X	auto char *pcSUid, *pcSGid, *pcSAll;
X
X	cUid = (S_ISUID&mFrom) != (S_ISUID&mTo) ?
X		((S_ISUID&mTo) ? '+' : '-') : '\000';
X	pcAUid = aaacAdd[(mFrom&0700)>>6][(mTo&0700)>>6];
X	pcSUid = aaacAdd[(mTo&0700)>>6][(mFrom&0700)>>6];
X	cGid = (S_ISGID&mFrom) != (S_ISGID&mTo) ?
X		((S_ISGID&mTo) ? '+' : '-') : '\000';
X	pcAGid = aaacAdd[(mFrom&0070)>>3][(mTo&0070)>>3];
X	pcSGid = aaacAdd[(mTo&0070)>>3][(mFrom&0070)>>3];
X	cSticky = (S_ISVTX&mFrom) != (S_ISVTX&mTo) ?
X		((S_ISVTX&mTo) ? '+' : '-') : '\000';
X	pcAAll = aaacAdd[(mFrom&0007)>>0][(mTo&0007)>>0];
X	pcSAll = aaacAdd[(mTo&0007)>>0][(mFrom&0007)>>0];
X	if ('\000' != cUid) {
X		*pcDo++ = 'u';
X		if (cUid == cGid) {
X			cGid = '\000';
X			*pcDo++ = 'g';
X		}
X		*pcDo++ = cUid;
X		*pcDo++ = 's';
X		if (cUid == cSticky) {
X			*pcDo++ = 't';
X			cSticky = '\000';
X		}
X		cSep = ',';
X	} else {
X		cSep = '\000';
X	}
X	if ('\000' != cGid) {
X		if ('\000' != cSep)
X			*pcDo++ = cSep;
X		*pcDo++ = 'g';
X		*pcDo++ = cGid;
X		*pcDo++ = 's';
X		cSep = ',';
X	}
X	if ('\000' != cSticky) {
X		if ('\000' != cSep)
X			*pcDo++ = cSep;
X		*pcDo++ = cSticky;
X		*pcDo++ = 't';
X		cSep = ',';
X	}
X	if ('\000' != pcAUid[0]) {
X		if ('\000' != cSep)
X			*pcDo++ = cSep;
X		*pcDo++ = 'u';
X		if (0 == strcmp(pcAGid, pcAUid)) {
X			*pcDo++ = 'g';
X			pcAGid = "";
X		}
X		if (0 == strcmp(pcAAll, pcAUid)) {
X			*pcDo++ = 'o';
X			pcAAll = "";
X		}
X		*pcDo++ = '+';
X		(void)strcpy(pcDo, pcAUid);
X		pcDo += strlen(pcDo);
X		cSep = ',';
X	}
X	if ('\000' != pcSUid[0]) {
X		if ('\000' != cSep)
X			*pcDo++ = cSep;
X		*pcDo++ = 'u';
X		if (0 == strcmp(pcSGid, pcSUid)) {
X			*pcDo++ = 'g';
X			pcSGid = "";
X		}
X		if (0 == strcmp(pcSAll, pcSUid)) {
X			*pcDo++ = 'o';
X			pcSAll = "";
X		}
X		*pcDo++ = '-';
X		(void)strcpy(pcDo, pcSUid);
X		pcDo += strlen(pcDo);
X		cSep = ',';
X	}
X	if ('\000' != pcAGid[0]) {
X		if ('\000' != cSep)
X			*pcDo++ = cSep;
X		*pcDo++ = 'g';
X		if (0 == strcmp(pcAAll, pcAGid)) {
X			*pcDo++ = 'o';
X			pcAAll = "";
X		}
X		*pcDo++ = '+';
X		(void)strcpy(pcDo, pcAGid);
X		pcDo += strlen(pcDo);
X		cSep = ',';
X	}
X	if ('\000' != pcSGid[0]) {
X		if ('\000' != cSep)
X			*pcDo++ = cSep;
X		*pcDo++ = 'g';
X		if (0 == strcmp(pcSAll, pcSGid)) {
X			*pcDo++ = 'o';
X			pcSAll = "";
X		}
X		*pcDo++ = '-';
X		(void)strcpy(pcDo, pcSGid);
X		pcDo += strlen(pcDo);
X		cSep = ',';
X	}
X	if ('\000' != pcAAll[0]) {
X		if ('\000' != cSep)
X			*pcDo++ = cSep;
X		*pcDo++ = 'o';
X		*pcDo++ = '+';
X		(void)strcpy(pcDo, pcAAll);
X		pcDo += strlen(pcDo);
X		cSep = ',';
X	}
X	if ('\000' != pcSAll[0]) {
X		if ('\000' != cSep)
X			*pcDo++ = cSep;
X		*pcDo++ = 'o';
X		*pcDo++ = '-';
X		(void)strcpy(pcDo, pcSAll);
X		pcDo += strlen(pcDo);
X		cSep = ',';
X	}
X	*pcDo = '\000';
}
#endif /* brain dead chmod(2) */
X
X
/*
X * qwery the user and exec the `fix' program, if he wants to		(ksb)
X *
X * We tell RunCmd to be quiet, because we echo the command when
X * we prompt for run/not.  Return 1 for `skip the rest of this file'.
X */
static int
QExec(cmd, arg1, arg2, pcWas)
char *cmd, *arg1, *arg2, *pcWas;
{
X	auto char **ppc;
X	auto char *arg0, *pcTemp;
X	auto char acAns[MAXANS+1];
X	auto int iSave;
X	static char *apcHelp[] = {
X		"f\tskip to the next file",
X		"h\tprint this help message",
X		"n\tdo not run this command",
X		"y\trun this command",
X		"q\tquit the program",
X		(char *)0
X	};
X
X	arg0 = StrTail(cmd);
X	for (;;) {
X		(void)fprintf(fpOut, "%s: %s %s", progname, arg0, arg1);
X		if ((char *)0 != arg2)
X			(void)printf(" %s", arg2);
X		if ((char *)0 != pcWas)
X			(void)printf(" {was %s}", pcWas);
X		fputs(" [nfhqy] ", fpOut);
X
X		if ((char *)0 == fgets(acAns, MAXANS, stdin)) {
X			(void)clearerr(stdin);
X			fputs("[EOF]\n", fpOut);
X			return 0;
X		}
X		pcTemp = acAns;
X		while (isspace(*pcTemp) && '\n' != *pcTemp)
X			++pcTemp;
X		switch (*pcTemp) {
X		default:
X			(void)fprintf(stderr, "%s: unknown execute key `%c\'\n", progname, *pcTemp);
X		case 'h':
X		case 'H':
X		case '?':
X			for (ppc = apcHelp; (char *)0 != *ppc; ++ppc) {
X				(void)fprintf(fpOut, "%s\n", *ppc);
X			}
X			continue;
X		case 'y':	/* yeah, run it	*/
X		case 'Y':
X			iSave = fTrace;
X			fTrace = FALSE;
X			(void)RunCmd(cmd, arg1, arg2);
X			fTrace = iSave;
#if defined(INST_FACILITY)
X			if (bHaveRoot) {
X				if ((char *)0 == arg2) {
X					if ((char *)0 == pcWas)
X						syslog(LOG_INFO, "%s %s by %s", arg0, arg1, pcGuilty);
X					else
X						syslog(LOG_INFO, "%s %s {was %s} by %s", arg0, arg1, pcWas, pcGuilty);
X				} else if ((char *)0 == pcWas) {
X					syslog(LOG_INFO, "%s %s %s by %s", arg0, arg1, arg2, pcGuilty);
X				} else {
X					syslog(LOG_INFO, "%s %s %s {was %s} by %s", arg0, arg1, arg2, pcWas, pcGuilty);
X				}
X			}
#endif
X			break;
X
X		case 'q':	/* quit this	*/
X		case 'Q':
X			exit(0);
X
X		case '\n':
X		case 'n':	/* no thanks	*/
X		case 'N':
X			break;
X
X		case 'f':	/* next file	*/
X		case 'F':
X			return 1;
X		}
X		break;
X	}
X	return 0;
X
}
X
CHLIST CLCheck;
static COMPONENT *pCMRoot;
int iMatches;
X
/*
X * we are passed a filename that exists and matches the recorded
X * install.cf line our parrent is currently working one.  We should
X * look in the list of examined files to see if we have already
X * matched this file, if so we can ignore this match.  Else check to
X * see if it would pass the current line.
X * Report bugs to stdout as install(1l) would, or close.
X */
int
DoCk(pcFile, pstThis)
char *pcFile;
struct stat *pstThis;
{
X	register PATH_DATA *pPD;
X	auto int fdFile;
X	auto int fChGroup, fChOwner, fChMode;
X	auto char acOOwner[128], acOGroup[128], acOMode[128];
X	auto char acNMode[128];
X	auto int mMode, mNew;
X
X	pPD = AddPath(& pCMRoot, pcFile);
X	if (0 != pPD->fseen) {
X		return 0;
X	}
X	pPD->fseen = 1;
X	++iMatches;
X
X	if ('~' == CLCheck.acmode[0]) {
X		(void)fprintf(fpOut, "%s: `%s\' should not be installed\n", progname, pcFile);
X	} else if ('!' == CLCheck.acmode[0]) {
X		(void)fprintf(fpOut, "%s: `%s\' should not exist\n", progname, pcFile);
X		if (fInteract) {
X			if (QExec(BINRM, "-f", pcFile, (char *)0 != CLCheck.pcmesg && '\000' != CLCheck.pcmesg[0] ? CLCheck.pcmesg : NodeType(pstThis->st_mode, (char *)0))) {
X				return 0;
X			}
X			/* if the user didn't remove it, check it
X			 */
X			if (-1 == access(pcFile, 0)) {
X				return 0;
X			}
X		} else {
X			return 0;
X		}
X	}
X	if ('*' == CLCheck.acmode[0]) {
X		/* nothing */;
X	} else if (CLCheck.mtype != (pstThis->st_mode & S_IFMT)) {
X		(void)fprintf(fpOut, "%s: `%s\' should be a %s, not a %s\n", progname, pcFile, NodeType(CLCheck.mtype, (char *)0), NodeType(pstThis->st_mode, (char *)0));
X		return 0;
X	}
X
X	/* time to check the group, owner, mode against our check list...
X	 * We buffer all the info up for one big informative printf at the end
X	 */
X	fChOwner = fChGroup = fChMode = FALSE;
X
X	if ('*' == CLCheck.acowner[0] || (CLCheck.fbangowner ? (fChOwner = pstThis->st_uid == CLCheck.uid) : (fChOwner = pstThis->st_uid != CLCheck.uid))) {
X		register struct passwd *pwdTemp;
X
X		pwdTemp = getpwuid(pstThis->st_uid);
X		if ((struct passwd *)0 != pwdTemp) {
X			(void)strcpy(acOOwner, pwdTemp->pw_name);
X		} else {
X			(void)sprintf(acOOwner, "%d", pstThis->st_uid);
X		}
X	} else {
X		(void)strcpy(acOOwner, CLCheck.acowner);
X	}
X
X	if ('*' == CLCheck.acgroup[0] || (CLCheck.fbanggroup ? (fChGroup = pstThis->st_gid == CLCheck.gid) : (fChGroup = pstThis->st_gid != CLCheck.gid))) {
X		register struct group *grpTemp;
X
X		grpTemp = getgrgid(pstThis->st_gid);
X		if ((struct group *)0 != grpTemp) {
X			(void)strcpy(acOGroup, grpTemp->gr_name);
X		} else {
X			(void)sprintf(acOGroup, "%d", pstThis->st_gid);
X		}
X	} else {
X		(void)strcpy(acOGroup, CLCheck.acgroup);
X	}
X
X	mMode = PERM_BITS(pstThis->st_mode);
X	(void)sprintf(acOMode, "%04o", mMode);
X	if ('*' != CLCheck.acmode[0]) {
X		if (CLCheck.mmust != (mMode & CLCheck.mmust)) {
X			fChMode = 1;
X		} else if (0 != (mMode &~ (CLCheck.mmust|CLCheck.moptional))) {
X			fChMode = 1;
X		}
X		mNew = CLCheck.mmust | (mMode & CLCheck.moptional);
X		if (0 != (S_ISUID & mMode) ? 0 == (S_ISUID & (CLCheck.mmust|CLCheck.moptional)) : 0 != (S_ISUID & CLCheck.mmust)) {
X			if (fVerbose) {
X				(void)fprintf(fpOut, "%s: `%s\' setuid bit must be %s\n", progname, pcFile, apcOO[0 == (S_ISUID & mMode)]);
X			}
X			mNew |= (S_ISUID & (CLCheck.mmust|CLCheck.moptional));
X			fChMode = 1;
X		}
X		if (0 != (S_ISGID & mMode) ? 0 == (S_ISGID & (CLCheck.mmust|CLCheck.moptional)) : 0 != (S_ISGID & CLCheck.mmust)) {
X			if (fVerbose) {
X				(void)fprintf(fpOut, "%s: `%s\' setgid bit must be %s\n", progname, pcFile, apcOO[0 == (S_ISGID & mMode)]);
X			}
X			mNew |= (S_ISGID & (CLCheck.mmust|CLCheck.moptional));
X			fChMode = 1;
X		}
X		if (0 != (S_ISVTX & mMode) ? 0 == (S_ISVTX & (CLCheck.mmust|CLCheck.moptional)) : 0 != (S_ISVTX & CLCheck.mmust)) {
X			if (fVerbose) {
X				(void)fprintf(fpOut, "%s: `%s\' sticky bit must be %s\n", progname, pcFile, apcOO[0 == (S_ISVTX & mMode)]);
X			}
X			mNew |= (S_ISVTX & (CLCheck.mmust|CLCheck.moptional));
X			fChMode = 1;
X		}
#if BROKEN_CHMOD
X		if (fChMode) {
X			SunBotch(acNMode, mMode, mNew);
X		} else {
X			(void)sprintf(acNMode, "%04o", mNew);
X		}
#else
X		(void)sprintf(acNMode, "%04o", mNew);
#endif
X	} else {
X		(void)strcpy(acNMode, CLCheck.acmode);
X	}
X
X	/* tell the user of their sins, ask if they want to repent
X	 */
X	if (fChOwner || fChGroup || fChMode) {
X		fprintf(fpOut, "%s: `%s\' is %s.%s(%s) should be %s%s.%s%s(%s)\n", progname, pcFile, acOOwner, acOGroup, acOMode, CLCheck.fbangowner ? "!" : "", CLCheck.acowner, CLCheck.fbanggroup ? "!" : "", CLCheck.acgroup, acNMode);
X	}
X	if (fInteract) {
X		if (fChOwner && !CLCheck.fbangowner && QExec(BINCHOWN, CLCheck.acowner, pcFile, acOOwner)) {
X			return 0;
X		}
X		if (fChGroup && !CLCheck.fbanggroup && QExec(BINCHGRP, CLCheck.acgroup, pcFile, acOGroup)) {
X			return 0;
X		}
X		if (fChMode && QExec(BINCHMOD, acNMode, pcFile, acOMode)) {
X			return 0;
X		}
X		mMode = mNew;	/* guess that the user did change mode LLL */
X	}
X
X	/* if we don't have to check strip/ranlib, don't open the file
X	 * if opening the file might activate a device, avoid it.
X	 */
X	if ('*' == CLCheck.chstrip && 0 == ((S_ISUID|S_ISGID)&mMode)) {
X		return 0;
X	}
X	if (S_IFREG != (pstThis->st_mode & S_IFMT)) {
X		if (!CF_IS_NONE(CLCheck)) {
X			(void)fprintf(stderr, "%s: %s: will not open %s\n", progname, pcFile, NodeType(pstThis->st_mode, (char *)0));
X		}
X		return 0;
X	}
X
X	if (0 > (fdFile = open(pcFile, 0, 0000))) {
X		if (errno != EACCES) {
X			(void)fprintf(stderr, "%s: open: %s: %s\n", progname, pcFile, strerror(errno));
X		}
X		if (fVerbose) {
X			(void)fprintf(fpOut, "%s: %s: cannot check `%c' flag\n", progname, pcFile, CLCheck.chstrip);
X		}
X		return 0;
X	}
X	if (0 != ((S_ISUID|S_ISGID)&mMode)) {
X		register char *pcBad;
X		if ((char *)0 != (pcBad = BadLoad(fdFile, mMode))) {
X			fprintf(stderr, "%s: `%s\' is %s and loaded with `#!...\'!\n", progname, pcFile, pcBad);
X		}
X	}
X	switch (CLCheck.chstrip) {
X	case CF_ANY:	/* if is set%cid we can get here */
X		break;
X	default:
X		fprintf(stderr, "%s: bad strip flag %c\n", progname, CLCheck.chstrip);
X		break;
X	case CF_STRIP:
X		switch (IsStripped(fdFile, pstThis)) {
X		case -1:
X			(void)fprintf(fpOut, "%s: `%s\' is not even a binary, should be stripped?\n", progname, pcFile);
X			break;
X		case 0:
X			(void)fprintf(fpOut, "%s: `%s\' is not strip\'ed\n", progname, pcFile);
X			if (fInteract && QExec(BINSTRIP, pcFile, (char *)0, "not stripped")) {
X				(void)close(fdFile);
X				return 0;
X			}
X			break;
X		case 1:
X			break;
X		}
X		break;
X	case CF_RANLIB:
X		if (1 != IsRanlibbed(fdFile, pstThis)) {
X			register int f;
X			(void)fprintf(fpOut, "%s: `%s\' is not ranlib\'ed", progname, pcFile);
X			if (-1 != (f = IsStripped(fdFile, pstThis))) {
X				(void)fprintf(fpOut, ", it is actually a%s binary\n", f ? " strip\'ed" : "");
X			} else if (fInteract) {
X				(void)fputc('\n', fpOut);
X				if (QExec(BINRANLIB, pcFile, (char *)0, "not ranlibbed")) {
X					(void)close(fdFile);
X					return 0;
X				}
X			} else {
X				(void)fputc('\n', fpOut);
X			}
X		}
X		break;
X	case CF_NONE:
X		switch (IsStripped(fdFile, pstThis)) {
X		case -1:		/* not a binary		*/
X			switch (IsRanlibbed(fdFile, pstThis)) {
X			case -1:	/* not a lib.a		*/
X			case 0:		/* no table of contents	*/
X				break;
X			case 1:
X				(void)fprintf(fpOut, "%s: `%s\' is ranlib\'ed\n", progname, pcFile);
X				break;
X			}
X			break;
X		case 0:			/* plain binary		*/
X			break;
X		case 1:			/* stripped binary	*/
X			(void)fprintf(fpOut, "%s: `%s\' is strip\'ed \n", progname, pcFile);
X			break;
X		}
X		break;
X	}
X	(void)close(fdFile);
X
X	return 0;
}
X
/*
X * check for files that were not checked				(ksb)
X */
int
LeftCk(pcFile, pstThis)
char *pcFile;
struct stat *pstThis;
{
X	auto PATH_DATA *pPD;
X	auto char *pcType;
X	auto char acMode[100];
X
X	pPD = AddPath(& pCMRoot, pcFile);
X	if (0 != pPD->fseen) {
X		return 0;
X	}
X	pPD->fseen = 1;
X
X	pcType = NodeType(pstThis->st_mode, & acMode[0]);
X	if (0 != (pstThis->st_mode & 07022)) {
X		ModetoStr(acMode+1, (int)PERM_BITS(pstThis->st_mode), 0000);
X		fprintf(fpOut, "%s: `%s\' unchecked %s, mode %s (%04o)\n", progname, pcFile, pcType, acMode, PERM_BITS(pstThis->st_mode));
X	} else if (fLongList) {
X		fprintf(fpOut, "%s: `%s\' unchecked %s\n", progname, pcFile, pcType);
X	}
X	return 0;
}
X
/*
X * sep the dirs and the files, dirCk the dirs, fileck file files	(ksb)
X */
void
InstCk(argc, argv, pcLSpecial, pCLCheck)
int argc;
char **argv, *pcLSpecial;
CHLIST *pCLCheck;		/* the checklist buffer DoCk uses	*/
{
X	register int i, j, fSave;
X	auto struct stat statb_me;
X
X	fSave = fVerbose;
X	fVerbose = 0;
X	for (j = i = 0; i< argc; ++i) {
X		if (-1 == stat(argv[i], &statb_me)) {
X			fprintf(stderr, "%s: stat: %s: %s\n", progname, argv[i], strerror(errno));
X			continue;
X		}
X		Special(argv[i], pcLSpecial, pCLCheck);
X		if (S_IFDIR == (statb_me.st_mode & S_IFMT)) {
X			argv[j++] = argv[i];
X			if (pCLCheck->ffound)
X				DoCk(argv[i], & statb_me);
X			continue;
X		}
X		if (pCLCheck->ffound)
X			DoCk(argv[i], & statb_me);
X		else
X			printf("%s: `%s\' not in checklist %s\n", progname, argv[i], pcLSpecial);
X	}
X	fVerbose = fSave;
X
X	if (!fDirs)
X		return;
X	DirCk(j, argv, pcLSpecial, pCLCheck);
X	OldCk(j, argv, pCLCheck);
X	for (i = 0; i < j; ++i) {
X		FileMatch("*", argv[i], LeftCk);
X	}
}
X
X
/*
X * find all the links in a binary dir to a given file in an OLD dir	(ksb)
X */
void
FixLinks(pcDir, pcOld, pcBackFile, pST)
char *pcDir, *pcOld,  *pcBackFile;
struct stat *pST;
{
X	register DIR *pDI;
X	register struct direct *pDE;
X	register int i, j, fLoop, iCount;
X	register char ch;
X	register struct fstab *pFS;
X	auto struct stat statb_inst, statb_of;
X	auto char acLoop[MAXPATHLEN+1];
X	auto char acRelink[MAXPATHLEN+1];
X
X	(void)sprintf(acLoop, "../%s", pcBackFile);
X	/* strip off the mktemp suffix?
X	 */
X	i = strlen(acLoop);
X	j = i - 7;
X	if (j < 4)
X		j = 4;
X	for (ch = '\000'; i > j; acLoop[i] = ch, --i, ch = acLoop[i], acLoop[i] = '\000') {
X		if ('\000' !=ch && !isdigit(ch) &&
X		    (! isalpha(ch) && !isdigit(acLoop[i+1]))) {
X			acLoop[i] = ch;
X			i = j;
X			break;
X		}
X		if (-1 == stat(acLoop, & statb_of)) {
X			if (ENOENT == errno)
X				continue;
X			(void)fprintf(stderr, "%s: stat: %s: %s\n", progname, acLoop, strerror(errno));
X			i = j;
X			break;
X		}
X		break;
X	}
X	if (i != j && pST->st_ino == statb_of.st_ino &&
X	    pST->st_dev == statb_of.st_dev) {
X		fLoop = 1;
X		(void)fprintf(fpOut, "%s: `%s/%s\' is still linked to backup %s/%s/%s (inode %d)\n", progname, pcDir, acLoop+3, pcDir, pcOld, pcBackFile, pST->st_ino);
X	} else {
X		fLoop = 0;
X		(void)fprintf(fpOut, "%s: `%s/%s/%s\' has too many links (inode %d)\n", progname, pcDir, pcOld, pcBackFile, pST->st_ino);
X	}
X	iCount = fLoop + 1;
X
X	if ((DIR *)0 == (pDI = opendir(".."))) {
X		(void)fprintf(stderr, "%s: opendir: ..: %s\n", progname, strerror(errno));
X		return;
X	}
X	while ((struct direct *)0 != (pDE = readdir(pDI))) {
X		if ('.' == pDE->d_name[0] && ('\000' == pDE->d_name[1] ||
X		   ('.' == pDE->d_name[1] && '\000' == pDE->d_name[2])))
X			continue;
X		if (i != j && 0 == strcmp(acLoop+3, pDE->d_name)) {
X			continue;
X		}
X		(void)sprintf(acRelink, "../%s", pDE->d_name);
X		if (-1 == LSTAT(acRelink, &statb_inst)) {
X			if (ENOENT == errno)
X				continue;
X			(void)fprintf(stderr, "%s: lstat: %s: %s\n", progname, acRelink, strerror(errno));
X			continue;
X		}
X		if (pST->st_ino != statb_inst.st_ino ||
X		    pST->st_dev != statb_inst.st_dev) {
X			continue;
X		}
X		++iCount;
X		if (i == j) {
X			if (fInteract) {
X				(void)fprintf(fpOut, "%s: removing %s, a bogus link to %s (cwd=%s/%s)\n", progname, acRelink, pcBackFile, pcDir, pcOld);
X				(void)QExec(BINRM, "-f", acRelink, "a bogus link");
X			} else {
X				(void)fprintf(fpOut, "%s: `%s/%s\' is a link to %s/%s/%s\n", progname, pcDir, pDE->d_name, pcDir, pcOld, pcBackFile);
X			}
X			continue;
X		}
X		if (fLoop) {
X			(void)fprintf(fpOut, "%s: `%s/%s\' is a link to %s/%s/%s\n", progname, pcDir, pDE->d_name, pcDir, pcOld, pcBackFile);
X			continue;
X		}
X		if (fInteract) {
X			(void)fprintf(fpOut, "%s: linking %s to %s (cwd=%s/%s)\n", progname, acRelink, acLoop, pcDir, pcOld);
X			if (!QExec(BINRM, "-f", acRelink, "link into OLD"))
X				(void)QExec(BINLN, acLoop, acRelink, (char *)0);
X			continue;
X		}
X		(void)fprintf(fpOut, "%s: `%s/%s\' should be linked to `%s/%s\'\n", progname, pcDir, pDE->d_name, pcDir, acLoop+3);
X	}
X	(void)closedir(pDI);
X	/* have seen all the links
X	 */
X	if (pST->st_nlink == iCount)
X		return;
X
X	/* find the mount point for this file system
X	 */
X	(void)setfsent();
X	while ((struct fstab *)0 != (pFS = getfsent())) {
X		if ('.' == pFS->fs_file[0] && '\000' == pFS->fs_file[1])
X			continue;	/* ignore me */
X		if (-1 == stat(pFS->fs_file, &statb_of))
X			continue;
X		if (statb_of.st_dev != pST->st_dev)
X			continue;
X		/* found it */
X		(void)fprintf(fpOut, "%s: use `find %s -inum %d -print\' to locate missing links\n", progname, pFS->fs_file, pST->st_ino);
X		iCount = pST->st_nlink;
X		break;
X	}
X	(void)endfsent();
X	if (pST->st_nlink != iCount) {
X		(void)fprintf(fpOut, "%s: %s: count not find mount point\n", progname, pcDir);
X	}
}
X
X
static char
X	*pcOldOwner = ODIROWNER,
X	*pcOldGroup = ODIRGROUP,
X	*pcOldMode = ODIRMODE,
X	acOld[] = OLDDIR;
X
/*
X * check the OLD dirs for pooly installed files				(ksb)
X */
void
OldCk(iCount, ppcDirs, pCLCheck)
int iCount;
char **ppcDirs;
CHLIST *pCLCheck;		/* the checklist buffer DoCk uses	*/
{
X	register DIR *pDI;
X	register struct direct *pDE;
X	register int i, fChMode;
X	auto char acMode[64];
X	auto char acPwd[MAXPATHLEN+1];
X	auto struct stat statb_backup, statb_old, statb_dir;
X	auto struct passwd *pwdOldDef;
X	auto struct group *grpOldDef;
X	auto int mOldMust, mOldQuest;
X
X	if ((char *)0 == getwd(acPwd)) {
X		(void)fprintf(stderr, "%s: %s\n", progname, acPwd);
X		exit(99);
X	}
X
X	if (!bHaveRoot) {
X		pwdOldDef = getpwuid(geteuid());
X		if ((struct passwd *)0 == pwdOldDef) {
X			fprintf(stderr, "%s: getpwuid: %d: %s\n", progname, geteuid(), strerror(errno));
X			exit(10);
X		}
X		pwdOldDef = savepwent(pwdOldDef);
X		pcOldOwner = pwdOldDef->pw_name;
X	} else if ((char *)0 != pcOldOwner || '\000' == pcOldOwner[0]) {
X		pcOldOwner = (char *)0;
X		pwdOldDef = (struct passwd *)0;
X	} else {
X		pwdOldDef = getpwnam(pcOldOwner);
X		if ((struct passwd *)0 == pwdOldDef) {
X			fprintf(stderr, "%s: getpwname: %s: %s\n", progname, pcOldOwner, strerror(errno));
X			exit(10);
X		}
X		pwdOldDef = savepwent(pwdOldDef);
X	}
X	if (!bHaveRoot) {
X		grpOldDef = getgrgid(getegid());
X		if ((struct group *)0 == grpOldDef) {
X			fprintf(stderr, "%s: getgrgid: %d: %s\n", progname, getegid(), strerror(errno));
X			exit(10);
X		}
X		grpOldDef = savegrent(grpOldDef);
X		pcOldGroup = grpOldDef->gr_name;
X	} else if ((char *)0 == pcOldGroup || '\000' == pcOldGroup[0]) {
X		pcOldGroup = (char *)0;
X		grpOldDef = (struct group *)0;
X	} else {
X		grpOldDef = getgrnam(pcOldGroup);
X		if ((struct group *)0 == grpOldDef) {
X			fprintf(stderr, "%s: getgrname: %s: %s\n", progname, pcOldGroup, strerror(errno));
X			exit(10);
X		}
X		grpOldDef = savegrent(grpOldDef);
X	}
X	if ((char *)0 == pcOldMode || '\000' == pcOldMode[0]) {
X		pcOldMode = (char *)0;
X	} else {
X		CvtMode(pcOldMode, &mOldMust, &mOldQuest);
X	}
X
X	for (i = 0; i < iCount; ++i, chdir(acPwd)) {
X		auto char acCurOld[MAXPATHLEN+2];
X
X		if ('/' == ppcDirs[i][0]) {		/* full path to it */
X			(void)sprintf(acCurOld, "%s/%s", ppcDirs[i], acOld);
X		} else {
X			(void)sprintf(acCurOld, "%s/%s/%s", acPwd, ppcDirs[i], acOld);
X		}
X		if (-1 == stat(ppcDirs[i], & statb_dir)) {
X			(void)fprintf(fpOut, "%s: stat: %s: %s\n", progname, ppcDirs[i], strerror(errno));
X			continue;
X		}
X
X		/* check the modes on the OLD directory itself
X		 */
X		if ((char *)0 == pcOldOwner) {
X			register struct passwd *pwdTemp;
X
X			pCLCheck->uid = statb_dir.st_uid;
X			pwdTemp = getpwuid(statb_dir.st_uid);
X			if ((struct passwd *)0 != pwdTemp) {
X				(void)strcpy(pCLCheck->acowner, pwdTemp->pw_name);
X			} else {
X				(void)sprintf(pCLCheck->acowner, "%d", statb_dir.st_uid);
X			}
X		} else {	/* yeah, this is an invarient */
X			pCLCheck->uid = pwdOldDef->pw_uid;
X			(void)strcpy(pCLCheck->acowner, pwdOldDef->pw_name);
X		}
X		if ((char *)0 == pcOldGroup) {
X			register struct group *grpTemp;
X
X			pCLCheck->gid = statb_dir.st_gid;
X			grpTemp = getgrgid(statb_dir.st_gid);
X			if ((struct group *)0 != grpTemp) {
X				(void)strcpy(pCLCheck->acgroup, grpTemp->gr_name);
X			} else {
X				(void)sprintf(pCLCheck->acgroup, "%d", statb_dir.st_gid);
X			}
X		} else {
X			pCLCheck->gid = grpOldDef->gr_gid;
X			(void)strcpy(pCLCheck->acgroup, grpOldDef->gr_name);
X		}
X
X		pCLCheck->mtype = S_IFDIR;
X		if ((char *)0 == pcOldMode) {
X			pCLCheck->mmust = PERM_BITS(statb_dir.st_mode);
X			pCLCheck->moptional = S_ISGID;
X		} else {
X			pCLCheck->mmust = mOldMust;
X			pCLCheck->moptional = mOldQuest;
X		}
X		pCLCheck->chstrip = CF_NONE;
X		(void)NodeType(pCLCheck->mtype, pCLCheck->acmode);
X		ModetoStr(pCLCheck->acmode+1, pCLCheck->mmust, pCLCheck->moptional);
X
X		if (-1 == stat(acCurOld, & statb_old)) {
X			if (ENOENT != errno)
X				(void)fprintf(stderr, "%s: stat: %s: %s\n", progname, acCurOld, strerror(errno));
X			continue;
X		}
X		(void)DoCk(acCurOld, &statb_old);
X
X		/* goto the directory and seach for poor backups
X		 */
X		if (-1 == chdir(acCurOld)) {
X			(void)fprintf(stderr, "%s: chdir: %s: %s\n", progname, acCurOld, strerror(errno));
X			continue;
X		}
X		if ((DIR *)0 == (pDI = opendir("."))) {
X			(void)fprintf(stderr, "%s: opendir: %s/%s/.: %s\n", progname, ppcDirs[i], acOld, strerror(errno));
X			continue;
X		}
X		while ((struct direct *)0 != (pDE = readdir(pDI))) {
X			if ('.' == pDE->d_name[0] && ('\000' == pDE->d_name[1] ||
X			   ('.' == pDE->d_name[1] && '\000' == pDE->d_name[2])))
X				continue;
X			if (-1 == LSTAT(pDE->d_name, &statb_backup)) {
X				(void)fprintf(stderr, "%s: stat: %s/%s/%s: %s\n", progname, ppcDirs[i], acOld, pDE->d_name, strerror(errno));
X				continue;
X			}
X			if (S_IFREG != (statb_backup.st_mode & S_IFMT)) {
X				(void)fprintf(fpOut, "%s: `%s/%s/%s\' not a plain file\n", progname, ppcDirs[i], acOld, pDE->d_name);
X				continue;
X			}
X			if (1 != statb_backup.st_nlink) {
X				FixLinks(ppcDirs[i], acOld, pDE->d_name, & statb_backup);
X			}
X			fChMode = 0;
X			if (0 != (statb_backup.st_mode & S_ISUID)) {
X				(void)fprintf(fpOut, "%s: `%s/%s/%s\' has setuid bit\n", progname, ppcDirs[i], acOld, pDE->d_name);
X				fChMode = 1;
X			}
X			if (0 != (statb_backup.st_mode & S_ISGID)) {
X				(void)fprintf(fpOut, "%s: `%s/%s/%s\' has setgid bit\n", progname, ppcDirs[i], acOld, pDE->d_name);
X				fChMode = 1;
X			}
X			if (0 != (statb_backup.st_mode & S_ISVTX)) {
X				(void)fprintf(fpOut, "%s: `%s/%s/%s\' has sticky bit\n", progname, ppcDirs[i], acOld, pDE->d_name);
X				fChMode = 1;
X			}
X			if (fInteract && fChMode) {
#if BROKEN_CHMOD
X				/* We know this is a file and files allow
X				 * us to drop the set?id bit with an octal
X				 * change, but they (Sun) might break this...
X				 */
X				SunBotch(acMode, PERM_BITS(statb_backup.st_mode), PERM_RWX(statb_backup.st_mode));
#else
X				(void)sprintf(acMode, "%04o", PERM_RWX(statb_backup.st_mode));
#endif
X				if (QExec(BINCHMOD, acMode, pDE->d_name, "special OLD file"))
X					continue;
X			}
X		}
X		(void)closedir(pDI);
X	}
}
X
/*
X * if we are given an OLD dir, just run the OldCk on it			(ksb)
X */
int
FilterOld(iCount, ppcDirs, pCLCheck)
int iCount;
char **ppcDirs;
CHLIST *pCLCheck;		/* the checklist buffer DoCk uses	*/
{
X	register char *pcTail;
X	auto int i, j, fCmp, k;
X	auto char **ppcOlds;
X
X	ppcOlds = (char **)calloc(iCount, sizeof(char *));
X	if ((char **)0 == ppcOlds) {
X		Die("calloc: no memory");
X	}
X	i = j = 0;
X	for (k = 0; k < iCount; ++k) {
X		ppcDirs[i] = ppcDirs[k];
X		if ((char *)0 == (pcTail = strrchr(ppcDirs[i], '/'))) {
X			fCmp = strcmp(ppcDirs[i], acOld);
X		} else {
X			fCmp = strcmp(pcTail+1, acOld);
X		}
X		if (0 != fCmp) {
X			++i;
X			continue;
X		}
X		if ((char *)0 == pcTail) {
X			ppcOlds[j] = ".";
X		} else if (pcTail == ppcDirs[i]) {
X			ppcOlds[j] = "/";
X		} else {
X			*pcTail = '\000';
X			ppcOlds[j] = malloc(strlen(ppcDirs[i])+1);
X			if ((char *)0 == ppcOlds[j]) {
X				Die("malloc: no memory");
X			}
X			(void)strcpy(ppcOlds[j], ppcDirs[i]);
X			*pcTail = '/';
X		}
X		++j;
X	}
X	if (0 != j) {
X		OldCk(j, ppcOlds, pCLCheck);
X	}
X	return i;
}
Purdue
chmod 0444 instck/instck.c ||
echo 'restore of instck/instck.c failed'
Wc_c="`wc -c < 'instck/instck.c'`"
test 34702 -eq "$Wc_c" ||
	echo 'instck/instck.c: original size 34702, current size' "$Wc_c"
fi
# ============= instck/gen.c ==============
if test -f 'instck/gen.c' -a X"$1" != X"-c"; then
	echo 'x - skipping instck/gen.c (File already exists)'
else
echo 'x - extracting instck/gen.c (Text)'
sed 's/^X//' << 'Purdue' > 'instck/gen.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 * these routines generate a file which will tell instck(1L)		(ksb)
X * what modes and owners all the listed files should have.
X */
#if !defined(lint)
static char *rcsid =
X	"$Id: gen.c,v 7.6 90/11/28 13:08:08 ksb Exp $";
#endif	/* !lint */
X
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/dir.h>
X
#include "configure.h"
#include "install.h"
#include "main.h"
#include "syscalls.h"
#include "special.h"
#include "instck.h"
#include "gen.h"
#include "maxfreq.h"
#include "path.h"
#include "filedup.h"
X
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>
X
#if STRINGS
#include <strings.h>
#else
#include <string.h>
#endif
X
extern char *calloc();
FILEDUPS FDLinks;
X
/*
X * copy the parent directory of pcFile to pcDir, which is a buffer	(ksb)
X * given by the user. returns the `tail part'
X */
static char *
getparent(pcFile, pcDir)
char *pcFile, *pcDir;
{
X	register char *pcTail;
X
X	if ((char *)0 == (pcTail = strrchr(pcFile, '/'))) {
X		if ((char *)0 != pcDir)
X			(void)strcpy(pcDir, ".");
X		return pcFile;
X	} else if (pcFile == pcTail) {
X		if ((char *)0 != pcDir)
X			(void)strcpy(pcDir, "/");
X		return pcFile+1;
X	}
X	*pcTail = '\000';
X	if ((char *)0 != pcDir)
X		(void)strcpy(pcDir, pcFile);
X	*pcTail = '/';
X	return pcTail+1;
}
X
static COMPONENT *pCMRoot;
static int fBeenHere = 0;
static struct passwd *pwdLast;
static struct group *grpLast;
static char acModeLast[sizeof("drwxrwxrwx")+1];
static dev_t devLast;
X
extern int stat(), lstat();
X
/*
X * do the nifty output							(ksb)
X */
static int
DoGen(pcFile, pstThis)
char *pcFile;
struct stat *pstThis;
{
X	auto struct passwd *pwdThis;
X	auto struct group *grpThis;
X	auto char acModeThis[sizeof("drwxrwxrwx")+1];
X	static char acFormat[] = "%-15s %-7s %-7s ";
X	static char acLFormat[] = "%-23s %c%s\n";
X	static char acSCom[] = "\t#";
X	auto int fdFile, fBadLookUp;
X	auto int fCom;
X
X	(void)NodeType(pstThis->st_mode, acModeThis);
X	if ('l' == acModeThis[0]) {
X		auto char acLink[MAXPATHLEN+2];
X		register int i;
X		if (-1 == (i = readlink(pcFile, acLink, MAXPATHLEN))) {
X			fprintf(stderr, "%s: readlink: %s: %s\n", progname, pcFile, strerror(errno));
X			return 0;
X		}
X		acLink[i] = '\000';
X		fprintf(stdout, acLFormat, pcFile, '@', acLink);
X		return 0;
X	}
X	if (pstThis->st_nlink > 1 && fLinks) {
X		register char *pcLink;
X		pcLink = FDAdd(& FDLinks, pcFile, pstThis);
X		if (0 != strcmp(pcLink, pcFile)) {
X			fprintf(stdout, acLFormat, pcFile, ':', pcLink);
X			return 0;
X		}
X	}
X	fBadLookUp = 0;
X	pwdThis = getpwuid((int) pstThis->st_uid);
X	if ((struct passwd *)0 == pwdThis) {
X		fprintf(stderr, "%s: %s: getpwuid: %d: unknown uid\n", progname, pcFile, pstThis->st_uid);
X		fBadLookUp = 1;
X	}
X	grpThis = getgrgid((int) pstThis->st_gid);
X	if ((struct group *)0 == grpThis) {
X		fprintf(stderr, "%s: %s: getgrgid: %d: unknown gid\n", progname, pcFile, pstThis->st_gid);
X		fBadLookUp |= 2;
X	}
X	ModetoStr(acModeThis+1, (int)PERM_BITS(pstThis->st_mode), 00000);
X
X	/* make different devices show all again
X	 */
X	if (acModeLast[0] != acModeThis[0] ||
X	    (('b' == acModeLast[0] || 'c' == acModeLast[0]) &&
X	    major(devLast) != major(pstThis->st_rdev))) {
X		fBeenHere = 0;
X	}
X	devLast = pstThis->st_rdev;
X
X	/* use the \" notation for multiple lines sometimes,
X	 * looks, *lots* better
X	 */
X	if (fBadLookUp) {
X		(void)fputc('#', stdout);
X	}
X	(void)printf("%-24s ", pcFile);
X	if (fBadLookUp) {
X		static char *apcBad[3] = {
X			"uid", "gid", "uid and gid"
X		};
X		fBeenHere = 0;
X		(void)printf(acFormat, acModeThis, ((struct passwd *)0 != pwdThis) ? pwdThis->pw_name : "*", ((struct group *)0 != grpThis) ? grpThis->gr_name : "*");
X		(void)printf("-\t# unknown %s\n", apcBad[fBadLookUp-1]);
X		return 0;
X	} else if (fBeenHere && !fVerbose) {
X		/* one of the most complicated C expressions I have ever coded
X		 * keep dups in columns, keep last up to date
X		 */
X		(void)printf(acFormat,
X			0 == strcmp(acModeThis, acModeLast) && 0 == (pstThis->st_mode & (S_ISVTX|S_ISUID|S_ISGID)) ? "\"" : ((void)strcpy(acModeLast, acModeThis), acModeThis),
X			pwdThis->pw_uid == pwdLast->pw_uid && 0 == (pstThis->st_mode & S_ISUID) ? "\"" : (free((char *)pwdLast), pwdLast = savepwent(pwdThis), pwdThis->pw_name),
X			grpThis->gr_gid == grpLast->gr_gid && 0 == (pstThis->st_mode & S_ISGID) ? "\"" : (free((char *)grpLast), grpLast = savegrent(grpThis), grpThis->gr_name)
X		);
X	} else {
X		(void)printf(acFormat, acModeThis, pwdThis->pw_name, grpThis->gr_name);
X		pwdLast = savepwent(pwdThis);
X		grpLast = savegrent(grpThis);
X		(void)strcpy(acModeLast, acModeThis);
X		fBeenHere = 1;
X	}
X
X	if ('-' != acModeThis[0]) {
X		(void)fputc('-', stdout);
X	} else if (0 > (fdFile = open(pcFile, 0, 0000))) {
X		if (errno != EACCES) {
X			(void)fprintf(stderr, "%s: open: %s: %s\n", progname, pcFile, strerror(errno));
X		}
X		(void)fputc('*', stdout);
X	} else {
X		register char *pcDot;
X		if ((char *)0 != (pcDot = strrchr(pcFile, '.')) && 'a' == pcDot[1] && '\000' == pcDot[2]) {
X			switch (IsRanlibbed(fdFile, pstThis)) {
X			case -1:	/* not a library	*/
X				(void)fputc('n', stdout);
X				break;
X			case 0:		/* library, not ranlibbed */
X			case 1:		/* binary, ranlibbed */
X				(void)fputc('l', stdout);
X				break;
X			}
X		} else if (0 != (pstThis->st_mode & 0111)) {
X			switch (IsStripped(fdFile, pstThis)) {
X			case -1:	/* not a binary	*/
X				(void)fputc('n', stdout);
X				break;
X			case 0:		/* binary, not stripped */
X				(void)fputc('?', stdout);
X				break;
X			case 1:		/* binary, stripped */
X				(void)fputc('s', stdout);
X				break;
X			}
X		} else {
X			(void)fputc('n', stdout);
X		}
X		(void)close(fdFile);
X	}
X
X	/* make crass comments about things we know a little about
X	 *	mode 04100 is redundant
X	 *	mode 02010 is redundant too
X	 *	mode 00022 is dangerous
X	 *	if (perm(group) == perm(other)) group should be default
X	 *	never setgid default group
X	 */
X	fCom = 0;
X	if (0 == (pstThis->st_mode & 0011) && 0 != (pstThis->st_mode & 0100) && 0 != (pstThis->st_mode & 04000)) {
X		if (!fCom)
X			(void)printf(acSCom);
X		(void)printf(" redundant setuid?");
X		++fCom;
X	}
X	if (0 == (pstThis->st_mode & 0101) && 0 != (pstThis->st_mode & 0010) && 0 != (pstThis->st_mode & 02000)) {
X		if (!fCom)
X			(void)printf(acSCom);
X		(void)printf(" redundant setgid?");
X		++fCom;
X	}
X	if (0 != (pstThis->st_mode & 0022)) {
X		if (!fCom)
X			(void)printf(acSCom);
X		(void)printf(" write bits?");
X		++fCom;
X	}
X	if ((struct group *)0 != grpDef && pstThis->st_gid != grpDef->gr_gid && 0 == (pstThis->st_mode & S_ISGID) && ((pstThis->st_mode & 070) >> 3) == (pstThis->st_mode & 07)) {
X		if (!fCom)
X			(void)printf(acSCom);
X		printf(" group %s?", grpDef->gr_name);
X		++fCom;
X	}
X	if ((struct group *)0 != grpDef && pstThis->st_gid == grpDef->gr_gid && 0 != (pstThis->st_mode & S_ISGID)) {
X		if (!fCom)
X			(void)printf(acSCom);
X		printf(" setgid %s?", grpDef->gr_name);
X		++fCom;
X	}
X	/* someday we might do something with fCom here? -- ksb */
X	(void)fputc('\n', stdout);
X
X	return 0;
}
X
static MAXFREQ **ppMFRoot;
X
/*
X * copy an ME element
X * configure the MF routines here
X */
int
MECopy(pMEDest, pMESource)
X	ME_ELEMENT *pMEDest, *pMESource;
{
X	*pMEDest = *pMESource;
X	return 0;
}
X
/*
X * comparet 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 pME1->chtype - pME2->chtype ||
X		pME1->iuid - pME2->iuid ||
X		pME1->igid - pME2->igid ||
X		pME1->imode - pME2->imode;
}
X
/*
X * scan a directory (passwd each file within) for most common		(ksb)
X * { owner, group, mode, 'd'/'-' } tuple
X */
/*ARGSUSED*/
static int
DoStats(pcFile, pstThis)
char *pcFile;
struct stat *pstThis;
{
X	auto ME_ELEMENT METhis;
X
X	METhis.imode = PERM_BITS(pstThis->st_mode);
X	METhis.iuid = pstThis->st_uid;
X	METhis.igid = pstThis->st_gid;
X	(void)NodeType(pstThis->st_mode, & METhis.chtype);
X	if (METhis.chtype == '-') {
X		MFIncrement(ppMFRoot, & METhis);
X	}
X	return 0;
}
X
static ME_ELEMENT MESame;
X
/*
X * output files with a different `ME' for this directory		(ksb)
X * (also output if fLinks and st_nlink > 1)
X */
static int
DoDiffs(pcFile, pstThis)
char *pcFile;
struct stat *pstThis;
{
X	auto char ch;
X
X	(void)NodeType(pstThis->st_mode, &ch);
X	if (MESame.chtype == ch && MESame.iuid == pstThis->st_uid && MESame.igid == pstThis->st_gid && MESame.imode == PERM_BITS(pstThis->st_mode) && !(fLinks && 1 < pstThis->st_nlink)) {
X		return 0;
X	}
X	(void)DoGen(pcFile, pstThis);
X	return 0;
}
X
X
typedef struct PMnode {
X	struct passwd *s_pwd;
X	struct group *s_grp;
X	char s_acmode[20];
} PMODES;
X
/*
X * save the current dirs modes if we are doing an inherit on that prop	(ksb)
X * and take the new ones from the given file
X */
static void
PushModes(pST, pPM)
struct stat *pST;
PMODES *pPM;
{
X	if ((char *)0 == DEFOWNER || '\000' == DEFOWNER[0]) {
X		pPM->s_pwd = pwdDef;
X		pwdDef = getpwuid((int) pST->st_uid);
X		if ((struct passwd *)0 == pwdDef) {
X			fprintf(stderr, "%s: getpwuid: %d: unknown uid\n", progname, pST->st_uid);
X			pwdDef = pPM->s_pwd;
X			if ((struct passwd *)0 == pwdDef)
X				exit(1);
X		} else {
X			pwdDef = savepwent(pwdDef);
X		}
X	}
X	if ((char *)0 == DEFGROUP || '\000' == DEFGROUP[0]) {
X		pPM->s_grp = grpDef;
X		grpDef = getgrgid((int) pST->st_gid);
X		if ((struct group *)0 == grpDef) {
X			fprintf(stderr, "%s: getgrgid: %d: unknown gid\n", progname, pST->st_gid);
X			grpDef = pPM->s_grp;
X			if ((struct group *)0 == grpDef)
X				exit(1);
X		} else {
X			grpDef = savegrent(grpDef);
X		}
X	}
X	if ((char *)0 == pcMode) {
X		(void)strcpy(pPM->s_acmode, pcDefMode);
X		ModetoStr(pcDefMode+1, (int)PERM_BITS(pST->st_mode), 0);
X	}
}
X
/*
X * restore all the saved modes for this directory			(ksb)
X */
static void
PopModes(pPM)
PMODES *pPM;
{
X	if ((char *)0 == DEFOWNER || '\000' == DEFOWNER[0]) {
X		free((char *)pwdDef);
X		pwdDef = pPM->s_pwd;
X	}
X	if ((char *)0 == DEFGROUP || '\000' == DEFGROUP[0]) {
X		free((char *)grpDef);
X		grpDef = pPM->s_grp;
X	}
X	if ((char *)0 == pcMode) {
X		(void)strcpy(pcDefMode, pPM->s_acmode);
X	}
}
X
/*
X * generate a checklist for the given direcotires			(ksb)
X */
int
GenCk(argc, argv)
int argc;
char **argv;
{
X	register PATH_DATA *pPD;
X	register int i;
X	register char *pcFile;
X	auto struct passwd *pwdThis;
X	auto struct group *grpThis;
X	auto struct stat stThis, stAbove;
X	auto MAXFREQ **ppMFStats;
X	auto char acMode[20];
X	auto char acPat[MAXPATHLEN+3];
X	auto PMODES PMFile, PMDir;
X
X	ppMFStats = (MAXFREQ **)calloc(sizeof(MAXFREQ *), argc+1);
X	if ((MAXFREQ **)0 == ppMFStats) {
X		fprintf(stderr, "%s: calloc: %s\n", progname, strerror(errno));
X		exit(2);
X	}
X
X	FDInit(&FDLinks);
X
X	/* scan for plain files, collect stats on dirs
X	 */
X	for (i = 0; i < argc && (char *)0 != (pcFile = argv[i]); ++i) {
X		pPD = AddPath(& pCMRoot, pcFile);
X		if (0 != pPD->fseen) {
X			continue;
X		}
X		pPD->fseen = 1;
X		if (-1 == (fLinks ? lstat : stat)(pcFile, &stThis)) {
X			fprintf(stderr, "%s: stat: %s %s\n", progname, pcFile, strerror(errno));
X			continue;
X		}
X		ppMFRoot = & ppMFStats[i];
X		(void)getparent(pcFile, acPat);
X		if (-1 == stat(acPat, &stAbove)) {
X			fprintf(stderr, "%s: stat: %s %s\n", progname, acPat, strerror(errno));
X			continue;
X		}
X		PushModes(&stAbove, & PMFile);
X		if (S_IFDIR == (stThis.st_mode & S_IFMT)) {
X			(void)DoGen(pcFile, & stThis);
X			PushModes(&stThis, & PMDir);
X			if (fDirs) {
X				FileMatch("*", pcFile, DoStats);
X			}
X			PopModes(& PMDir);
X		} else {
X			(void)DoGen(pcFile, &stThis);
X		}
X		PopModes(& PMFile);
X	}
X
X	/* scan for base rules
X	 */
X	for (i = 0; i < argc && (char *)0 != (pcFile = argv[i]); ++i) {
X		if (-1 == stat(pcFile, &stThis)) {
X			fprintf(stderr, "%s: stat: %s %s\n", progname, pcFile, strerror(errno));
X			continue;
X		}
X		if (S_IFDIR != (stThis.st_mode & S_IFMT) || !fDirs || (MAXFREQ *)0 == ppMFStats[i]) {
X			continue;
X		}
X		MESame = ppMFStats[i]->ME;
X
X		PushModes(&stThis, & PMDir);
X		/* If the `*' pat for this dir would only match a few files
X		 * just put those files in literally.
X		 */
X		if (1 == ppMFStats[i]->icount || fLongList) {
X			FileMatch("*", pcFile, DoGen);
X			PopModes(& PMDir);
X			continue;
X		}
X		FileMatch("*", pcFile, DoDiffs);
X		PopModes(& PMDir);
X
X		pwdThis = getpwuid((int) ppMFStats[i]->ME.iuid);
X		if (0 == pwdThis) {
X			fprintf(stderr, "%s: %s: getpwuid: %d: unknown uid\n", progname, pcFile, ppMFStats[i]->ME.iuid);
X			continue;
X		}
X		grpThis = getgrgid((int) ppMFStats[i]->ME.igid);
X		if (0 == grpThis) {
X			fprintf(stderr, "%s: %s: getgrgid: %d: unknown gid\n", progname, pcFile, ppMFStats[i]->ME.igid);
X			continue;
X		}
X		acMode[0] = MESame.chtype;
X		ModetoStr(acMode+1, (int)PERM_BITS(ppMFStats[i]->ME.imode), 00000);
X		(void)strcpy(acPat, pcFile);
X		(void)strcat(acPat, "/*" + ('/' == pcFile[strlen(pcFile)-1]));
X		(void)printf("%-24s %-15s %-7s %-7s ?\n", acPat, acMode, pwdThis->pw_name, grpThis->gr_name);
X		/* we have to reset fBeenHere so we don't copy the
X		 * glob modes down to the next line, because we would be
X		 * comparing to the line *above* the glob line
X		 */
X		fBeenHere = 0;
X	}
}
Purdue
chmod 0444 instck/gen.c ||
echo 'restore of instck/gen.c failed'
Wc_c="`wc -c < 'instck/gen.c'`"
test 13940 -eq "$Wc_c" ||
	echo 'instck/gen.c: original size 13940, current size' "$Wc_c"
fi
# ============= purge/README ==============
if test ! -d 'purge'; then
    echo 'x - creating directory purge'
    mkdir 'purge'
fi
if test -f 'purge/README' -a X"$1" != X"-c"; then
	echo 'x - skipping purge/README (File already exists)'
else
echo 'x - extracting purge/README (Text)'
sed 's/^X//' << 'Purdue' > 'purge/README' &&
# $Id: README,v 7.0 90/09/17 11:38:41 ksb Exp $
X
Purge is run my cron at night to remove unwanted (aged) backup files
from the system OLD dirs.
X
Here is a suggested crontab line:
X	0 2 * * 1	purge -v -unews -uuucp / >>/usr/adm/purge.log 2>/tmp/prg$$; [ -s /tmp/prg$$ ] && Mail -s "purge failed" root </tmp/prg$$
X
kayessbee
Purdue
chmod 0444 purge/README ||
echo 'restore of purge/README failed'
Wc_c="`wc -c < 'purge/README'`"
test 322 -eq "$Wc_c" ||
	echo 'purge/README: original size 322, current size' "$Wc_c"
fi
true || echo 'restore of install.d/file.c failed'
echo End of part 1, continue with part 2
exit 0

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