[net.sources] Access Program - Part 1 of 3

mahler@ecn-ee.UUCP (11/08/84)

This is the distribution for the access, info, and lock programs.
It is contained in three separate mailings; each mailing is in
so-called "shell archive" format.

This is file 1 of 3.

To extract the sources and compile the programs, do the following:

	1. Save this file and the others into three separate
	   files.  Edit the files and delete the mail headers
	   and this text (delete from line 1 up to and including 
	   the line marked "--- CUT HERE ---").

	2. Create an empty directory and copy the files you
	   made in step 1 into it.  Now give each file to
	   the shell by typing

			sh filename1
			sh filename2
			sh filename3

	   This will extract the various files in the
	   distribution.

	3. Fill in the answers to the questions by editing the file
	   QUESTIONAIRE and mail it to

		{decvax, ihnp4, ucbvax}!pur-ee!mahler

	   This is so we'll know just who is running the
	   program, and so we can distribute any corrections
	   or enhancements.

	4. Follow the directions contained in the README file.

------------------------- CUT HERE -----------------------------
# This is a shell archive.  Save this into a file, edit it
# and delete all lines above this comment.  Then give this
# file to sh by executing the command "sh file".  The files
# will be extracted into the current directory owned by
# you with default permissions.
#
# The files contained herein are:
#        Makefile         README       access.c     commands.c
#          defs.h         glob.c         main.c
#
echo 'x - Makefile'
sed 's/^X//' <<'________This_Is_The_END________' >>Makefile
X#
X# Makefile for access, info, lock.  Before you "make install", be sure
X# to define BINDIR and LIBDIR, and create LIBDIR.
X#
X# David A. Curry
X# Purdue University Engineering Computer Network
X# October 1984
X#
X# These programs are hereby placed in the public domain, and may
X# be used by anyone for any purpose provided that they are not
X# used or sold for profit and that this notice and the name
X# of the original author appears with all copies of the programs.
X#
XCC = /bin/cc
XCFLAGS = -O
XBINDIR = /usr/bin
XLIBDIR = /usr/lib/access
XSRCS = defs.h main.c access.c commands.c glob.c mkaccessdb.c info.c lock.sh
XOBJS = main.o access.o commands.o glob.o
X
Xall: access mkaccessdb info lock
X
Xaccess: $(OBJS)
X	$(CC) $(CFLAGS) -o access $(OBJS)
X
Xmkaccessdb: defs.h mkaccessdb.c
X	$(CC) $(CFLAGS) -o mkaccessdb mkaccessdb.c
X
Xinfo: info.c
X	$(CC) $(CFLAGS) -o info info.c
X
Xlock: lock.sh
X
Xinstall: 
X	install -s info $(BINDIR)/info
X	install -c -m 755 lock.sh $(BINDIR)/lock
X	ln $(BINDIR)/lock $(BINDIR)/unlock
X	install -s access $(BINDIR)/access
X	install -s mkaccessdb $(LIBDIR)/mkaccessdb
X	install -c -m 755 mkaccesspw.sh $(LIBDIR)/mkaccesspw
X	install -c -m 644 access.help $(LIBDIR)/access.help
X	chmod go+x $(LIBDIR)
X
Xprint:
X	@ctags -x $(SRCS)
X	@pr $(SRCS)
X
Xclean:
X	rm -f $(OBJS) mkaccessdb.o core
X
Xmain.o: defs.h main.c
Xglob.o: defs.h glob.c
Xaccess.o: defs.h access.c
Xcommands.o: defs.h commands.c
________This_Is_The_END________
echo 'x - README'
sed 's/^X//' <<'________This_Is_The_END________' >>README
XThis directory contains the sources for access, info, and lock, three
Xprograms used for checking and modifying file access permissions.
XThe file access.ms describes how these programs work, you should read
Xthis before proceeding.
X
XFILES:
X	BINDIR/access
X		The access program itself.
X	BINDIR/info
X		The info program, used by access (it's not required,
X		but it's kinda nice).
X	BINDIR/lock
X	BINDIR/unlock (linked)
X		The lock program, used by access (it's not required
X		either, but it's sort of nice).
X	LIBDIR/access.help
X		The help file used by the access program.
X	LIBDIR/mkaccessdb
X                The program to make the user and group files used by 
X                access. This gets run by cron every night, and creates 
X                the files "userfile" and "groupfile" in LIBDIR. 
X	LIBDIR/mkaccesspw
X                If you wish, you can have mkaccessdb use a copy of the 
X                password file sorted by user id. This will make it run 
X                much faster, at the cost of a little disk space. If you 
X                wish to do this, then cron should run this program 
X                instead of mkaccessdb (mkaccesspw runs mkaccessdb by 
X                itself). The mkaccesspw command will create the file 
X                "passwd.by.uids" in LIBDIR (see step 3, below). 
X
XBefore you type make, do the following:
X
X        1. Edit "Makefile" and define the directories BINDIR and LIBDIR. 
X           Create the directory LIBDIR if it does not exist. 
X
X        2. Edit "defs.h" and change any of the constants there which 
X           need to be changed for your site. The ones you'll most 
X           likely change are: 
X
X                INFO - the path to the info command. This should be 
X                       defined to BINDIR/info where BINDIR is what you 
X                       defined in step 1. Change the paths to any other 
X                       programs which are incorrect here. 
X
X                HELPFILE - the path to the help file. This should be 
X                       defined to LIBDIR/access.help where LIBDIR is 
X                       what you defined in step 1. 
X
X		USERFILE
X                GROUPFILE - the paths to the user and group files. These 
X                       should be defined to LIBDIR/userfile and 
X                       LIBDIR/groupfile where LIBDIR is what you defined 
X                       in step 1. 
X
X                PASSWDBYUID - this should be defined to 1 if you plan to 
X                       use a password file sorted by user id. If you 
X                       define it to 0, the regular password file will be 
X                       used. 
X
X        3. If you defined PASSWDBYUID to be 1 in "defs.h", edit 
X           "mkaccessdb.c" and change the character string PASSWDUID (down 
X           near the bottom of the file) to reflect the path to the file 
X           which mkaccesspw will create. You should also, of course, 
X           edit mkaccesspw and make sure the name is the same there. 
X
X        4. If you are not running 4.2BSD, edit "info.c" and delete the 
X           cases for S_IFLNK and S_IFSOCK. You may wish to add cases for 
X           non-4.2BSD file types such as S_IFMPB (multiplexed file under 
X           4.1BSD). Also, define STAT to be "stat" (no quotes) instead 
X           of "lstat". 
X
X	5. If you are not running 4.2BSD, delete the line
X
X		   #include <sys/resource.h>
X
X           from "defs.h", and delete the calls to setrlimit in "main.c" 
X           and "mkaccessdb.c". These calls jack up the resource limits 
X           for the programs. If you have something similar (4.1BSD uses 
X           the vlimit call, V7 and System V use ulimit), you should 
X           probably put in calls to unlimit the filesize, datasize, and 
X           stacksize. If you don't, you may have trouble if you have a 
X           huge password file. 
X
XNow you should compile the programs by typing
X
X		make
X
XWhen this finishes, you will have copies of info, access, and
Xmkaccessdb.  In addition, the shell files lock.sh and mkaccesspw.sh
Xand the help file access.help are going to be installed. To install the
Xprograms, make sure BINDIR and LIBDIR are properly defined in Makefile
Xand be sure to create LIBDIR.  Then type
X
X		make install
X
XThis will put all the programs in their proper homes.  Next you should
Xinstall the manual pages by typing
X
X		cp info.1 /usr/man/man1/info.1
X		cp lock.1 /usr/man/man1/lock.1
X		cp access.1 /usr/man/man1/access.1
X
XThe file "access.ms" is a paper describing the access program in some
Xdetail.  You may wish to run off some copies (use "nroff -ms access.ms"
Xor "troff -ms access.ms") to distribute to your users.  This file
Xbelongs in /usr/doc if you have one, or just leave it in the source
Xdirectory.
X
XNext, edit /usr/lib/crontab and install a line to run mkaccesspw or
Xmkaccessdb every night.  This line should look something like
X
X	0 2 * * * sh /usr/lib/access/mkaccesspw
X
X(This runs at 2:00 a.m. every day.)  Finally, you should cd into LIBDIR
Xand execute mkaccesspw (or mkaccessdb, if appropriate) by hand by typing
X
X		sh mkaccesspw
X
Xor
X
X		mkaccessdb
X
XThis will take a few minutes on loaded systems or systems with large
Xpassword files.
X
XYou're finished!  You can now execute access, and see that everything
Xwas done properly.  If so, you can type
X
X		make clean
X
Xto dispose of all the object files.
X
XIf you have any problems, questions, or comments please send mail to:
X
X		Stephen J. Mahler
X		Purdue University
X		Electrical Engineering Building
X		West Lafayette, Indiana 47907
X		(317) 494-3373
X
X		{decvax, ihnp4, ucbvax}!pur-ee!mahler
X			- or -
X		ecn.mahler@purdue
X
________This_Is_The_END________
echo 'x - access.c'
sed 's/^X//' <<'________This_Is_The_END________' >>access.c
X/*
X * $Header: access.c,v 1.2 84/07/19 09:07:29 root Exp $
X *
X * access.c - file access routines for access program.
X *
X * David A. Curry
X * Purdue University Engineering Computer Network
X * April 1984
X *
X * This program is hereby placed in the public domain, and may
X * be used by anyone for any purpose provided that it is not
X * used or sold for profit and that this notice and the name
X * of the original author appears with all copies of the program.
X *
X * Modified: 7/18/84 (also defs.h, main.c)
X *	The method of reading the user/group data was taken out of this
X *	program and placed in a separate program, mkaccessdb.  This
X *	program now reads in the files created by mkaccessdb, a huge
X *	time improvement.
X * 	--Dave Curry
X *
X * $Log:	access.c,v $
X * Revision 1.2  84/07/19  09:07:29  root
X * fast version .. pre-structured
X * 
X * Revision 1.1  84/06/25  10:58:42  root
X * Initial revision
X * 
X */
X#include "defs.h"
X
Xextern struct grp *ghead, *gtail;
Xextern struct user *uhead, *utail;
X
Xchar *malloc();
Xextern int nusers;
Xextern char *pname;
Xextern char cur_dir[];
X
Xloaduids()
X{
X	struct stat sbuf;
X	register FILE *fp;
X	register struct user *u;
X
X	if ((fp = fopen(USERFILE, "r")) == NULL) {
X		fprintf(stderr, "%s: cannot open %s.\n", pname, USERFILE);
X		exit(1);
X	}
X	
X	fstat(fileno(fp), &sbuf);
X	nusers = sbuf.st_size / sizeof(struct user);
X	
X	if ((uhead = (struct user *) malloc(nusers * sizeof(struct user))) == USNULL) {
X		fprintf(stderr, "%s: not enough memory.\n", pname);
X		exit(1);
X	}
X	
X	u = uhead;
X	
X	while (fread((char *) u, USIZE, 1, fp) != NULL)
X		u++;
X	
X	utail = u;
X	fclose(fp);
X}
X
Xloadgids()
X{
X	int ngids;
X	struct stat sbuf;
X	register FILE *fp;
X	register struct grp *g;
X
X	if ((fp = fopen(GROUPFILE, "r")) == NULL) {
X		fprintf(stderr, "%s: cannot open %s.\n", pname, GROUPFILE);
X		exit(1);
X	}
X	
X	fstat(fileno(fp), &sbuf);
X	ngids = sbuf.st_size / sizeof(struct grp);
X	
X	if ((ghead = (struct grp *) malloc(ngids * sizeof(struct grp))) == GRNULL) {
X		fprintf(stderr, "%s: not enough memory.\n", pname);
X		exit(1);
X	}
X	
X	g = ghead;
X	
X	while (fread((char *) g, GSIZE, 1, fp) != NULL)
X		g++;
X	
X	gtail = g;
X	fclose(fp);
X}
X
X/*
X * process - gets full path to file, processes it and prints theresults.
X */
Xprocess(file)
Xchar *file;
X{
X	short endpath;
X	register char *s;
X	char path[MAXPATHLEN];
X	struct stat sbuf, sbuf2;
X	
X#ifdef DEBUG /*------------------------------------------------------------*/
X	printf("process(%s)\n", file);
X#endif /*------------------------------------------------------------------*/
X	
X	/*
X	 * If beginning of pathname is '/', we don't need the
X	 * current directory.  Otherwise, we have to make a path
X	 * like <current directory>/file.
X	 */
X	if (*file == '/') {
X		strcpy(path, file);
X	}
X	else {
X		/*
X		 * Put the pieces together, resolve "../"
X		 * and things.
X		 */
X		copy(path, cur_dir, file);
X	}
X
X	/*
X	 * Start with the root directory.
X	 */
X	if (stat(ROOTDIR, &sbuf) < 0) {
X		fprintf(stderr, "%s: cannot stat %s\n", pname, ROOTDIR);
X		return;
X	}
X
X	/*
X	 * Since this is the first time through, we
X	 * want to set the permissions to what they
X	 * are here.
X	 */
X	doaccess(sbuf.st_uid, sbuf.st_gid, sbuf.st_mode, SET);
X	
X	s = path + 1;
X	
X	/*
X	 * Now follow the path component by component.
X	 */
X	do {
X		while ((*s != '/') && (*s != NULL))
X			s++;
X		
X		endpath = (*s == '/' ? 0 : 1);
X		*s = NULL;
X		
X		/*
X		 * Save old stat structure.
X		 */
X		sbuf2 = sbuf;
X		
X		if (stat(path, &sbuf) < 0) {
X			fprintf(stderr, "%s: cannot stat %s\n", pname, path);
X			return;
X		}
X		
X		/*
X		 * Now we want to add the present permissions
X		 * to those already there.
X		 */
X		doaccess(sbuf.st_uid, sbuf.st_gid, sbuf.st_mode, ADD);
X		
X		*s++ = '/';
X	} while (!endpath);
X
X	*--s = NULL;
X	
X	/*
X	 * Print out the information.
X	 */
X	printout(path, sbuf, sbuf2);
X}
X
X/*
X * copy - copies s1 and s2 into dest, resolving things like "./" and "../".
X */
Xcopy(dest, s1, s2)
Xchar *dest, *s1, *s2;
X{
X	char *rindex();
X	char buf[MAXPATHLEN];
X	register char *s, *t;
X	
X	s = s2;
X	strcpy(buf, s1);
X	s1 = buf;
X	
X	/*
X	 * Until we run out of things like "./" and "../"...
X	 */
X	while (!strncmp(s, "./", 2) || !strncmp(s, "../", 3)) {
X		/*
X		 * If it was "./", just skip over it.
X		 */
X		if (*(s+1) == '/') {
X			s += 2;
X			continue;
X		}
X		
X		/* 
X		 * If it was "../", we back up to the previous
X		 * '/' in the s1 string.
X		 */
X		t = rindex(s1, '/');
X		
X		/*
X		 * Should never be NULL, since s1[0] == '/'.
X		 * If it's the beginning slash, then leave the
X		 * slash there.
X		 * If it's a different slash, just set it
X		 * to NULL.
X		 */
X		if (t == NULL)
X			break;
X		else if (t == s1)
X			*(t+1) = NULL;
X		else
X			*t = NULL;
X		
X		s += 3;
X	}
X	
X	/*
X	 * Now put together the pieces.
X	 */
X	strcpy(dest, s1);
X
X	/*
X	 * If all that remains of s is "..", skip
X	 * backwards in dest.
X	 */
X	if (!strcmp(s, "..")) {
X		t = rindex(dest, '/');
X		
X		if (t == dest)
X			*(t+1) = NULL;
X		else
X			*t = NULL;
X		
X		*s = NULL;
X	}
X
X	/*
X	 * Only add s if it is non-NULL and not equal to "."
X	 */
X	if ((*s != NULL) && (strcmp(s, ".") != 0)) {
X		strcat(dest, "/");
X		strcat(dest, s);
X	}
X
X#ifdef DEBUG /*------------------------------------------------------------*/
X	printf("copy - final path is '%s'\n", dest);
X#endif /*------------------------------------------------------------------*/
X
X}
X
X/*
X * doaccess - for each user, determines the permissions he gets given
X *	      that the file is owned by user u, group g, with mode m.
X */
Xdoaccess(u, g, m, how)
Xint u, g, m, how;
X{
X	register struct user *p;
X	register int uid, gid, mode, shift;
X	
X	uid = u;
X	gid = g;
X	mode = m;
X	
X	/*
X	 * If how is SET, then we simply set the 
X	 * bits in each user's structure to be "his"
X	 * bits in mode.
X	 */
X	if (how == SET) {
X		p = uhead;
X		while (p != utail) {
X			if (p->uid == uid)
X				p->perm = (mode >> 6) & 07;
X			else if (member(gid, p->groups))
X				p->perm = (mode >> 3) & 07;
X			else
X				p->perm = mode & 07;
X
X			/*
X			 * Old permission bits get same as current.
X			 */
X			p->operm = p->perm;
X
X			p++;
X		}
X		
X		return;
X	}
X	
X	/*
X	 * Otherwise, we have to selectively turn on bits.
X	 */
X	p = uhead;
X	while (p != utail) {
X		if (p->uid == uid)
X			shift = 6;
X		else if (member(gid, p->groups))
X			shift = 3;
X		else
X			shift = 0;
X		
X		/*
X		 * Save the old permission bits.
X		 */
X		p->operm = p->perm;
X
X		/*
X		 * If a given permission is on for this user, he is
X		 * only allowed to gain that permission if he was
X		 * able to search the previous directory.  We don't
X		 * ever turn on execute permission, because once you
X		 * lose it, you can never regain it.
X		 */
X		if (((mode >> shift) & READP) == READP) {
X			if ((p->perm & EXECP) == EXECP)
X				p->perm |= READP;
X		}
X		else {
X			p->perm &= ~READP;
X		}
X		
X		if (((mode >> shift) & WRITEP) == WRITEP) {
X			if ((p->perm & EXECP) == EXECP)
X				p->perm |= WRITEP;
X		}
X		else {
X			p->perm &= ~WRITEP;
X		}
X		
X		if (((mode >> shift) & EXECP) != EXECP)
X			p->perm &= ~EXECP;
X		
X		p++;
X	}
X}
X
X/*
X * member - determine if group g is in the group list gl.
X */
Xmember(g, gl)
Xint g, *gl;
X{
X	register int i, gid;
X	
X	gid = g;
X	
X	for (i=0; (i < NGROUPS) && (gl[i] != NOGROUP); i++) {
X		if (gl[i] == gid)
X			return(1);
X	}
X	
X	return(0);
X}
X
X/*
X * printout - print out the information
X */
Xprintout(file, sbuf, sbuf2)
Xchar *file;
Xstruct stat sbuf, sbuf2;
X{
X	char *username(), *groupname();
X	int ingroup, ningrp, nread, nexec, nwrite;
X
X	printf("%s (%s):\n", file, (isdirectory(sbuf.st_mode) ? "directory" : "file"));
X
X	/*
X	 * Count number of users with each permission,
X	 * count members of group, see if uid is in
X	 * group.
X	 */
X	count(&nread, &nwrite, &nexec);
X	ningrp = numingroup(sbuf.st_gid);
X	ingroup = ingrp(sbuf.st_uid, sbuf.st_gid);
X	
X	/*
X	 * Print the modes out.
X	 */
X	printf("\tReadable by: ");
X	printmode(nread, READP, sbuf.st_uid, sbuf.st_gid, ningrp, ingroup);
X
X	printf("\tWritable by: ");
X	printmode(nwrite, WRITEP, sbuf.st_uid, sbuf.st_gid, ningrp, ingroup);
X
X	printf("\t%sable by: ", (isdirectory(sbuf.st_mode) ? "Search" : "Execut"));
X	printmode(nexec, EXECP, sbuf.st_uid, sbuf.st_gid, ningrp, ingroup);
X	
X	/*
X	 * If the file is not a directory, we simply swap the old
X	 * permissions (parent directory permissions) for each user.
X	 * The same goes for an empty directory, since you needn't 
X	 * be able to write the directory itself to remove it.  But,
X	 * if the directory has files in it, you must be able to
X	 * remove them too, so we AND the old permissions and the
X	 * current permissions.
X	 */
X	if (!isdirectory(sbuf.st_mode))
X		swapperm(SET);
X	else if (emptydir(file))
X		swapperm(SET);
X	else
X		swapperm(ADD);
X		
X	/*
X	 * Now count everything again using the new permissions
X	 * and print it out.
X	 */
X	printf("\tRemovable by: ");
X	ningrp = numingroup(sbuf2.st_gid);
X	count(&nread, &nwrite, &nexec);
X
X	if (sbuf.st_gid != sbuf2.st_gid)
X		ingroup = -1;
X	else
X		ingroup = ingrp(sbuf2.st_uid, sbuf2.st_gid);
X
X	printmode(nwrite, WRITEP, sbuf2.st_uid, sbuf2.st_gid, ningrp, ingroup);
X
X	printf("\n");
X	fflush(stdout);
X}
X
X/*
X * printmode - prints out the number of people (nperm) who have permission
X *	       perm.  The file is owned by uid and gid, there are ningrp
X *	       people in group gid, and ingroup is 1 if uid is in group
X *	       gid.
X */
Xprintmode(nperm, perm, uid, gid, ningrp, ingroup)
Xint nperm, perm, uid, gid, ningrp, ingroup;
X{
X	int n;
X	
X#ifdef DEBUG
X	printf("PRINTMODE: nperm %d perm %d uid %d gid %d ningrp %d ingroup %d\n", nperm, perm, uid, gid, ningrp, ingroup);
X#endif
X
X	/*
X	 * Each of these conditions handles one or two
X	 * modes.  It's pretty bizarre.
X	 */
X	if (nperm == 0)
X		printf("nobody\n");
X	else if (nperm == nusers)
X		printf("everybody\n");
X	else if (nperm == 1)
X		prsingle(perm, 1);
X	else if (nperm == (nusers - 1)) {
X		printf("everybody except ");
X		prsingle(perm, 0);
X	}
X	else if (nperm == (ningrp + 1)) {
X		printf("%s and ", username(uid));
X		printf("members of group %s\n", groupname(gid));
X	}
X	else if ((n = commongroup(gid, perm)) != 0) {
X		if (n < (ningrp / 2)) {
X			printall(perm);
X		}
X		else {
X			printf("members of group %s", groupname(gid));
X
X			if (n != ningrp) {
X				printf(" execpt ");
X				prgroup(gid, perm);
X			}
X		
X			printf("\n");
X		}
X	}
X	else if (ingroup == 1) {
X		printf("everybody except members of group %s", groupname(gid));
X
X		if (nperm == (nusers - ningrp + 1))
X			printf(" excluding %s", username(uid));
X			
X		printf("\n");
X	}
X	else if (ingroup == 0) {
X		printf("everybody execept members of group %s", groupname(gid));
X		
X		if (nperm == (nusers - ningrp - 1))
X			printf(" and %s", username(uid));
X		
X		printf("\n");
X	}
X	else if (ingroup == -1) {
X		printall(perm);
X	}
X	else {
X		/*
X		 * Should never get here.  If we do, something
X		 * is trashed somewhere.  This will give you
X		 * a list of everyone in the password file.
X		 */
X		printall(perm);
X	}
X}
X
X/*
X * swapperm - if how is SET, sticks operm into perm.  Otherwise, ands them
X *	      together into perm.
X */
Xswapperm(how)
Xint how;
X{
X	register struct user *p;
X	
X	if (how == SET) {
X		p = uhead;
X		while (p != utail) {
X			p->perm = p->operm;
X			p++;
X		}
X	}
X	else {
X		p = uhead;
X		while (p != utail) {
X			p->perm &= p->operm;
X			p++;
X		}
X	}
X}
X
X/*
X * count - count the number of users who have read, write, execute
X *	   permissions.
X */
Xcount(r, w, x)
Xint *r, *w, *x;
X{
X	register struct user *p;
X	register int nr, nw, nx;
X	
X	nr = 0;
X	nw = 0;
X	nx = 0;
X	
X	p = uhead;
X	while (p != utail) {
X		if ((p->perm & READP) == READP)
X			nr++;
X		if ((p->perm & WRITEP) == WRITEP)
X			nw++;
X		if ((p->perm & EXECP) == EXECP)
X			nx++;
X
X		p++;
X	}
X	
X	*r = nr;
X	*w = nw;
X	*x = nx;
X}
X
X/*
X * numingroup - returns the number of RECORDED users in group g.  There
X *		may be other members in the group for whom we don't have
X *		records (because they don't have an account on this
X *		machine).
X */
Xnumingroup(g)
Xint g;
X{
X	register int gid;
X	register struct grp *p;
X	
X	gid = g;
X	
X	p = ghead;
X	while (p != gtail) {
X#ifdef DEBUG
X	printf("numingroup: p->gid = %d\n", p->gid);
X#endif
X		if (p->gid == gid)
X			return(p->nmembers);
X					
X		p++;
X	}
X
X	/*
X	 * Should never hit this.
X	 */
X	return(0);
X}
X
X/*
X * commongroup - returns number of users who have permission m and are 
X *		 members of group g.  If a user with permission m who
X *		 is NOT a member of group g is found, 0 is returned.
X */
Xcommongroup(g, m)
Xint g, m;
X{
X	register int n;
X	register int gid, mode;
X	register struct user *p;
X
X	n = 0;
X	gid = g;
X	mode = m;
X	
X	p = uhead;
X	while (p != utail) {
X		if ((p->perm & mode) != mode) {
X			p++;
X			continue;
X		}
X		
X		if (!member(gid, p->groups))
X			return(0);
X		n++;
X		p++;
X	}
X	
X	return(n);
X}
X
X/*
X * prsingle - print the name of the single user with permission m.  The o
X *	      argument reverses the sense of the check (0 = off, 1 = on).
X */
Xprsingle(m, o)
Xint m, o;
X{
X	register int off, mode;
X	register struct user *p;
X	
X	off = o;
X	mode = m;
X	
X	p = uhead;
X	while (p != utail) {
X		if (off == 0) {
X			if ((p->perm & mode) == 0) {
X				printf("%s\n", p->uname);
X				return;
X			}
X		}
X		else {
X			if ((p->perm & mode) == mode) {
X				printf("%s\n", p->uname);
X				return;
X			}
X		}
X		
X		p++;
X	}
X}
X
X/*
X * prgroup - print the names of any members of group g who do NOT have
X *	     permission m.  
X */
Xprgroup(g, m)
Xint g, m;
X{
X	short didone;
X	register int gid, mode;
X	register struct user *p;
X	
X	gid = g;
X	mode = m;
X	didone = 0;
X	
X	p = uhead;
X	while (p != utail) {
X		if ((p->perm & mode) == mode) {
X			p++;
X			continue;
X		}
X			
X		if (!member(gid, p->groups)) {
X			p++;
X			continue;
X		}
X
X		printf("%s%s", (didone++ ? ", " : ""), p->uname);
X
X		p++;
X	}
X	
X	if (!didone)
X		printf("???");
X}
X
X/*
X * printall - print the name of every user who has permission m.
X */
Xprintall(m)
Xint m;
X{
X	char ans[8];
X	register struct user *p;
X	register int mode, didone;
X	int nread, nwrite, nexec, nperm;
X	
X	count(&nread, &nwrite, &nexec);
X	
X	if (m == READP)
X		nperm = nread;
X	else if (m == WRITEP)
X		nperm = nwrite;
X	else
X		nperm = nexec;
X
X	/*
X	 * If there's greater than THRESHOLD names,
X	 * they may not want to see them all, so we
X	 * ask them.
X	 */
X	if (nperm > THRESHOLD) {
X		printf("There are %d names in this list.\n", nperm);
X		printf("\t\t      Do you really want to see them (y/[n])? ");
X		fflush(stdout);
X
X		nread = read(0, ans, sizeof(ans));
X		ans[nread-1] = NULL;
X		
X		if ((*ans != 'y') && (*ans != 'Y'))
X			return;
X	}
X	
X	mode = m;
X	didone = 0;
X
X	p = uhead;
X	while (p != utail) {
X		if ((p->perm & mode) == mode)
X			printf("%s%s", (didone++ ? ", " : ""), p->uname);
X		
X		p++;
X	}
X	
X	printf("\n");
X}
X
X/*
X * ingrp - different interface to member().
X */
Xingrp(u, g)
Xint u, g;
X{
X	register int uid;
X	register struct user *p;
X	
X	uid = u;
X	
X	/*
X	 * Find the uid, do a member().
X	 */
X	p = uhead;
X	while (p != utail) {
X		if (p->uid == uid)
X			return(member(g, p->groups));
X		
X		p++;
X	}
X	
X	/*
X	 * Should never get here.
X	 */
X	return(0);
X}
X
X/*
X * emptydir - returns 1 if directory is empty.
X */
Xemptydir(dir)
Xchar *dir;
X{
X	register int n;
X	DIR *dirp, *opendir();
X	struct direct *d, *readdir();
X	
X	/*
X	 * If we can't read the directory, assume
X	 * it's non-empty.
X	 */
X	if ((dirp = opendir(dir)) == NULL)
X		return(0);
X	
X	n = 0;
X	while ((d = readdir(dirp)) != NULL) {
X		if (d->d_ino == 0)
X			continue;
X		
X		/*
X		 * If we find 3 entries, it cannot
X		 * be empty ("." + ".." + any file).
X		 */
X		if (n++ >= 3) {
X			closedir(dirp);
X			return(0);
X		}
X	}
X	
X	closedir(dirp);
X	return(1);
X}
X
X/*
X * username - returns the name of the user associated with u.
X */
Xchar *username(u)
Xint u;
X{
X	register int uid;
X	register struct user *p;
X	
X	uid = u;
X	
X	p = uhead;
X	while (p != utail) {
X		if (p->uid == uid)
X			return(p->uname);
X		
X		p++;
X	}
X	
X	/*
X	 * Should never get here.
X	 */
X	return("???");
X}
X
X/*
X * groupname - returns the name of the group associated with g.
X */
Xchar *groupname(g)
Xint g;
X{
X	register int gid;
X	register struct grp *gr;
X	
X	gid = g;
X
X	gr = ghead;
X	while (gr != gtail) {
X		if (gr->gid == gid)
X			return(gr->gname);
X		
X		gr++;
X	}
X	
X	/*
X	 * Should never get here.
X	 */
X	return("???");
X}
X
X#ifdef LISTDEBUG /*--------------------------------------------------------*/
X/*
X * printlist - prints out the linked list.
X */
Xprintlist()
X{
X	register int i, didone;
X	register struct user *p;
X	
X	p = uhead;
X	while (p != utail) {
X		didone = 0;
X		printf("%6d%9s: ", p->uid, p->uname);
X
X		printf("perm: %02o ", p->perm & 07);
X		printf("groups: ");
X		for (i=0; (i < NGROUPS) && (p->groups[i] != NOGROUP); i++)
X			printf("%s%d", (didone++ ? ", " : ""), p->groups[i]);
X		
X		printf("\n");
X		p++;
X	}
X	
X	fflush(stdout);
X}
X#endif /*------------------------------------------------------------------*/
________This_Is_The_END________
echo 'x - commands.c'
sed 's/^X//' <<'________This_Is_The_END________' >>commands.c
X/*
X * $Header: commands.c,v 1.2 84/07/19 09:07:52 root Exp $
X *
X * commands.c - command routines for access program
X *
X * David A. Curry
X * Purdue University Engineering Computer Network
X * April 1984
X *
X * This program is hereby placed in the public domain, and may
X * be used by anyone for any purpose provided that it is not
X * used or sold for profit and that this notice and the name
X * of the original author appears with all copies of the program.
X *
X * $Log:	commands.c,v $
X * Revision 1.2  84/07/19  09:07:52  root
X * fast version .. pre-structured
X * 
X * Revision 1.1  84/06/25  10:58:44  root
X * Initial revision
X * 
X */
X#include "defs.h"
X
Xextern int reset;
Xextern int env[];
Xextern int kidpid;
Xextern char cur_dir[];
X
Xint ac;					/* arg count			*/
Xchar *argptr;				/* pointer to end of argbuf	*/
Xchar *av[MAXARGS];			/* argument pointers		*/
Xchar argbuf[MAXARGLEN];			/* arguments			*/
X
X/*
X * command - top level interface to the user.
X */
Xcommand()
X{
X	char *index();
X	char buf[1024];
X	register int i;
X	char *homedir();
X	register char *s, *t;
X	
X	printf("\n\nWelcome to the file access program.\n");
X	printf("If you need instructions, type \"help\".\n");
X	printf("When you're ready to leave, type \"quit\".\n\n");
X	
X	printf("You are in '%s'\n\n", cur_dir);
X	
X	/*
X	 * We want to come back here on interrupt.
X	 */
X	reset = 1;
X	
X	for (;;) {
X		/*
X		 * Come back to here on longjmp.
X		 */
X		setjmp(env);
X
X		/*
X		 * Zero the string.
X		 */
X		for (i=0; i < 1024; i++)
X			buf[i] = NULL;
X
X		/*
X		 * Prompt and read in a line.
X		 */
X		printf("Command: ");
X		fflush(stdout);
X		
X		if (gets(buf) == NULL)
X			quit();
X		
X		/*
X		 * Don't do anything if they typed a
X		 * blank line.
X		 */
X		if (*buf == NULL)
X			continue;
X
X		/*
X		 * Skip whitespace at beginning of line.
X		 */
X		ac = 0;
X		s = buf;
X		while ((*s != NULL) && ((*s == ' ') || (*s == '\t')))
X			s++;
X		
X		/*
X		 * Save first argument.
X		 */
X		av[ac++] = s;
X
X		while ((*s != NULL) && (*s != ' ') && (*s != '\t'))
X			s++;
X
X		*s++ = NULL;
X		
X		/*
X		 * Now process all the other arguments.
X		 */
X		argptr = argbuf;
X		
X		while ((*s != NULL) && (ac < MAXARGS)) {
X			/*
X			 * Skip leading whitespace.
X			 */
X			while ((*s == ' ') || (*s == '\t'))
X				s++;
X			
X			t = s;
X			
X			/*
X			 * Skip to end of word.
X			 */
X			while ((*s != ' ') && (*s != '\t') && (*s != NULL))
X				s++;
X			
X			*s++ = NULL;
X
X			/*
X			 * Glob it.
X			 */
X			if (*t == '~')
X				t = homedir(t);
X
X			glob(t);
X		}
X		
X		/*
X		 * Null terminate the arg list.
X		 */
X		av[ac] = 0;
X		
X#ifdef DEBUG /*------------------------------------------------------------*/
X		for (i=0; i < ac; i++)
X			printf("argv[%d] = '%s'\n", i, av[i]);
X#endif /*------------------------------------------------------------------*/
X
X		/*
X		 * Match the command name.
X		 */
X		if (!strcmp(av[0], "access"))
X			access(ac, av);
X		else if (!strcmp(av[0], "cat"))
X			cat(ac, av);
X		else if (!strcmp(av[0], "cd"))
X			cd(ac, av);
X		else if (!strcmp(av[0], "chmod"))
X			changemode(ac, av);
X		else if (!strcmp(av[0], "help") || !strcmp(av[0], "?"))
X			help(ac, av);
X		else if (!strcmp(av[0], "info"))
X			info(ac, av);
X		else if (!strcmp(av[0], "lock"))
X			lock(ac, av);
X		else if (!strcmp(av[0], "ls"))
X			ls(ac, av);
X		else if (!strcmp(av[0], "more"))
X			more(ac, av);
X		else if (!strcmp(av[0], "pwd"))
X			pwd(ac, av);
X		else if (!strcmp(av[0], "quit") || !strcmp(av[0], "exit"))
X			quit(ac, av);
X		else if (!strcmp(av[0], "sh") || !strcmp(av[0], "csh"))
X			shell(ac, av);
X		else if (!strcmp(av[0], "unlock"))
X			unlock(ac, av);
X		else
X			printf("Unknown command: '%s'\n", av[0]);
X	}
X}
X
X/*
X * access - print out who can access a file.
X */
Xaccess(argc, argv)
Xint argc;
Xchar **argv;
X{
X	if (argc < 2) {
X		printf("Usage: access file(s) .....\n");
X		return;
X	}
X	
X	while (--argc)
X		process(*++argv);
X}
X
X/*
X * cat - run cat on file
X */
Xcat(argc, argv)
Xint argc;
Xchar **argv;
X{
X	argv[0] = CAT;
X	execute(argc, argv);
X}
X
X/*
X * cd - change directory
X */
Xcd(argc, argv)
Xint argc;
Xchar **argv;
X{
X	if (argc != 2) {
X		printf("Usage: cd directory-name .....\n");
X		return;
X	}
X
X	if (chdir(argv[1]) < 0) {
X		printf("cd: cannot change into '%s'\n", argv[1]);
X		return;
X	}
X	
X	/*
X	 * Get new directory path.
X	 */
X	if (getwd(cur_dir) == NULL) {
X		printf("cd: getwd failed -- '%s'\n", cur_dir);
X		return;
X	}
X	
X	printf("Now in directory '%s'\n", cur_dir);
X}
X
X/*
X * changemode - chmod files
X */
Xchangemode(argc, argv)
Xint argc; 
Xchar **argv;
X{
X	if (argc < 3) {
X		printf("Usage: chmod mode filename(s) .....\n");
X		return;
X	}
X	
X	argv[0] = CHMOD;
X	execute(argc, argv);
X}
X
X/*
X * help - print help file
X */
Xhelp(argc, argv)
Xint argc;
Xchar **argv;
X{
X	argv[0] = MORE;
X	argv[1] = HELPFILE;
X	argv[2] = NULL;
X	argc = 2;
X	
X	execute(argc, argv);
X}
X
X/*
X * info - run the info command
X */
Xinfo(argc, argv)
Xint argc;
Xchar **argv;
X{
X	argv[0] = INFO;
X	execute(argc, argv);
X}
X
X/*
X * lock - lock files by chmod'ing them all to 0700.
X */
Xlock(argc, argv)
Xint argc;
Xchar **argv;
X{
X	int i;
X	
X	if (argc == 1) {
X		argv[argc++] = "0700";
X		argv[argc++] = ".";
X	}
X	else {
X		for (i=argc++; i > 1; i--)
X			argv[i] = argv[i-1];
X		argv[1] = "0700";
X	}
X	
X	argv[0] = CHMOD;
X	execute(argc, argv);
X}
X
X/*
X * ls - run ls.
X */
Xls(argc, argv)
Xint argc;
Xchar **argv;
X{
X	argv[0] = LS;
X	execute(argc, argv);
X}
X
X/*
X * more - run more on file
X */
Xmore(argc, argv)
Xint argc;
Xchar **argv;
X{
X	argv[0] = MORE;
X	execute(argc, argv);
X}
X
X/*
X * pwd - print working directory
X */
Xpwd(argc, argv)
Xint argc;
Xchar **argv;
X{
X	printf("You are in '%s'\n", cur_dir);
X}
X
X/*
X * quit - go bye-bye.
X */
Xquit(argc, argv)
Xint argc;
Xchar **argv;
X{
X	printf("\n");
X	exit(0);
X}
X
X/*
X * unlock - unlock files by chmod'ing them to 0755.
X */
Xunlock(argc, argv)
Xint argc;
Xchar **argv;
X{
X	int i;
X	
X	if (argc == 1) {
X		argv[argc++] = "0755";
X		argv[argc++] = ".";
X	}
X	else {
X		for (i=argc++; i > 1; i--)
X			argv[i] = argv[i-1];
X		argv[1] = "0755";
X	}
X	
X	argv[0] = CHMOD;
X	execute(argc, argv);
X}
X
X/*
X * shell - fork a shell.
X */
Xshell(argc, argv)
Xint argc;
Xchar **argv;
X{
X	char *getenv();
X	
X	if ((argv[0] = getenv("SHELL")) == NULL)
X		argv[0] = SH;
X	
X	execute(argc, argv);
X	printf("\n");
X}
X
X/*
X * execute - fork and execute a command
X */
Xexecute(argc, argv)
Xint argc;
Xchar **argv;
X{
X	while ((kidpid = fork()) < 0) {
X		printf("execute: can't fork -- retrying .....\n");
X		sleep(5);
X	}
X	
X	if (kidpid == 0) {
X		/*
X		 * Restore signals to default actions.
X		 */
X		signal(SIGINT, SIG_DFL);
X		signal(SIGQUIT, SIG_DFL);
X		
X		execv(argv[0], argv);
X		printf("execute: cannot execute '%s'\n", argv[0]);
X		exit(0);
X	}
X	while (wait((int *) 0) != kidpid);
X	
X	kidpid = 0;
X}
X
X/*
X * homedir - does "~" expansion to home directories.
X */
Xchar *homedir(str)
Xchar *str;
X{
X	int count;
X	char name[9];
X	char *malloc();
X	register char *s, *t;
X	register struct passwd *p;
X	struct passwd *getpwuid(), *getpwnam();
X
X	s = str;
X	t = name;
X	count = 0;
X	
X	/*
X	 * Copy the login name.
X	 */
X	s++;
X	while (isalpha(*s) || isdigit(*s) || (*s == '-') || (*s == '_')) {
X		*t++ = *s++;
X		
X		if (++count > 8)
X			return(str);
X	}
X
X	*t = NULL;
X	
X	/*
X	 * Get the directory.  If *name is NULL,
X	 * they gave us just "~".
X	 */
X	if (*name == NULL) {
X		if ((p = getpwuid(getuid())) == PNULL)
X			return(str);
X	}
X	else {
X		if ((p = getpwnam(name)) == PNULL)
X			return(str);
X	}
X	
X	if ((t = malloc(strlen(s) + strlen(p->pw_dir) + 1)) == NULL)
X		return(str);
X
X	strcpy(t, p->pw_dir);
X	
X	if (*s)
X		strcat(t, s);
X	
X	return(t);
X}
________This_Is_The_END________
echo 'x - defs.h'
sed 's/^X//' <<'________This_Is_The_END________' >>defs.h
X/*
X * $Header: defs.h,v 1.2 84/07/19 09:07:58 root Exp $
X *
X * defs.h - constants and structure declarations for the access program
X *
X * David A. Curry
X * Purdue University Engineering Computer Network
X * April 1984
X *
X * This program is hereby placed in the public domain, and may
X * be used by anyone for any purpose provided that it is not
X * used or sold for profit and that this notice and the name
X * of the original author appears with all copies of the program.
X *
X * $Log:	defs.h,v $
X * Revision 1.2  84/07/19  09:07:58  root
X * fast version .. pre-structured
X * 
X * Revision 1.1  84/06/25  10:58:45  root
X * Initial revision
X * 
X */
X#include <sys/param.h>
X#include <sys/time.h>
X#include <sys/resource.h>
X#include <sys/stat.h>
X#include <sys/dir.h>
X#include <signal.h>
X#include <setjmp.h>
X#include <stdio.h>
X#include <ctype.h>
X#include <pwd.h>
X#include <grp.h>
X
X/*
X * These constants are site dependent.  
X *
X * LS, INFO, MORE, and CHMOD are the pathnames of the programs of the
X * same name.  SH should be the path to the shell your site normally uses.  
X * The "info" program is a program which basically prints out the stat(2)
X * structure of a given file.  HELPFILE is the path to the access.help
X * file, displayed by the "help" command.
X *
X * MAXARGS is the maximum number of arguments to be passed to a command.
X * It should be big enough to permit wildcard globbing.  MAXARGLEN is
X * the maximum combined length of all the arguments to a command.  If
X * you plan to use a password file sorted by user id (it increases the
X * speed of initialization greatly, for large password files), define
X * the constant PASSWDBYUID and change the PASSWDUID character string
X * in mkaccessdb.c.
X *
X * THRESHOLD is the maximum number of names printed by the printall()
X * routine without asking the user if he really wants to see the list.
X * This is for when permissions come out really bizzare and access has
X * no alternative but to print practically the entire password file.
X */
X#define SH		"/bin/sh"
X#define LS		"/bin/ls"
X#define CAT		"/bin/cat"
X#define INFO		"/usr/ecn/info"
X#define MORE		"/usr/ucb/more"
X#define CHMOD		"/bin/chmod"
X#define MAXARGS		1024
X#define HELPFILE	"/usr/lib/access/access.help"
X#define USERFILE	"/usr/lib/access/userfile"
X#define GROUPFILE	"/usr/lib/access/groupfile"
X#define THRESHOLD	25
X#define MAXARGLEN	10240
X#define PASSWDBYUID	1		/* use passwd file sorted by uid*/
X
X/*
X * These constants shouldn't need to be changed.
X */
X#define ADD		0
X#define SET		1
X#define GNULL		(struct group *) NULL
X#define PNULL		(struct passwd *) NULL
X#define USIZE		sizeof(struct user)
X#define GSIZE		sizeof(struct grp)
X#define NSIZE		9
X#define EXECP		01		/* execute permission		*/
X#define READP		04		/* read permission		*/
X#define GRNULL		(struct grp *) NULL
X#define USNULL		(struct user *) NULL
X#define WRITEP		02		/* write permission		*/
X#define ROOTDIR		"/"		/* root directory		*/
X#define isdirectory(x)	((x & S_IFMT) == S_IFDIR)
X
Xstruct user {
X	int uid;			/* user id			*/
X	char perm;			/* current permissions enabled	*/
X	char operm;			/* pervious permissions	enabled	*/
X	char uname[NSIZE];		/* login name			*/
X	int groups[NGROUPS];		/* groups he's a member of	*/
X};
X
Xstruct grp {
X	int gid;			/* group id			*/
X	int nmembers;			/* number of people in group	*/
X	char gname[NSIZE];		/* group name			*/
X};
X
Xstruct Grp {
X	struct grp g;
X	struct Grp *next;
X};
X
Xstruct User {
X	struct user u;
X	struct User *next;
X};
X
________This_Is_The_END________
echo 'x - glob.c'
sed 's/^X//' <<'________This_Is_The_END________' >>glob.c
X/*
X * $Header: glob.c,v 1.2 84/07/19 09:08:02 root Exp $
X *
X * glob - filename expansion for access program
X *
X * This code is lifted almost directly from the Version 6 "glob"
X * program.  It is responsible for expanding the *, ?, and [...]
X * thingies in filenames.
X *
X * David A. Curry
X * Purdue University Engineering Computer Network
X * April 1984
X *
X * This program is hereby placed in the public domain, and may
X * be used by anyone for any purpose provided that it is not
X * used or sold for profit and that this notice and the name
X * of the original author appears with all copies of the program.
X *
X * $Log:	glob.c,v $
X * Revision 1.2  84/07/19  09:08:02  root
X * fast version .. pre-structured
X * 
X * Revision 1.1  84/06/25  10:58:46  root
X * Initial revision
X * 
X */
X#include "defs.h"
X
Xextern int ac;
Xextern int env[];
Xextern char *av[];
Xextern char *argptr;
Xextern char argbuf[];
X
Xglob(as)
Xchar *as;
X{
X	char **oav;
X	char *concat();
X	DIR *dirp, *opendir();
X	register char *s, *cs;
X	char *index(), *homedir();
X	struct direct *d, *readdir();
X
X	/* 
X	 * Save current place so we can sort
X	 * things.
X	 */
X	oav = &av[ac];
X	
X	s = cs = as;
X
X	while ((*cs != '*') && (*cs != '?') && (*cs != '[')) {
X		if (*cs++ == NULL) {
X			av[ac++] = concat(s, "");
X			return;
X		}
X	}
X	
X	for (;;) {
X		if (cs == s) {
X			dirp = opendir(".");
X			s = "";
X			break;
X		}
X		
X		if (*--cs == '/') {
X			*cs = NULL;
X			dirp = opendir(s == cs ? "/" : s);
X			*cs++ = 0200;
X			break;
X		}
X	}
X	
X	if (dirp == NULL) {
X		printf("glob: no directory\n");
X		longjmp(env);
X	}
X	
X	while ((d = readdir(dirp)) != NULL) {
X		if (d->d_ino == 0)
X			continue;
X		
X		if (match(d->d_name, cs))
X			av[ac++] = concat(s, d->d_name);
X
X		if (ac > MAXARGS)
X			toomany();
X	}
X	
X	closedir(dirp);
X	sort(oav);
X}
X
Xmatch(s, p)
Xchar *s, *p;
X{
X	if ((*s == '.') && (*p != '.'))
X		return(0);
X	
X	return(amatch(s, p));
X}
X
Xamatch(as, ap)
Xchar *as, *ap;
X{
X	register short scc;
X	short c, cc, ok, lc;
X	register char *s, *p;
X	
X	s = as;
X	p = ap;
X	
X	if (scc = *s++)
X		if ((scc &= 0177) == 0)
X			scc = 0200;
X	
X	switch (c = *p++) {
X	case '[':
X		ok = 0;
X		lc = 077777;
X		
X		while (cc = *p++) {
X			if (cc == ']') {
X				if (ok)
X					return(amatch(s, p));
X				else
X					return(0);
X			}
X			else if (cc == '-') {
X				if ((lc <= scc) && (scc <= (c = *p++)))
X					ok++;
X			}
X			else {
X				if (scc == (lc = cc))
X					ok++;
X			}
X		}
X		return(0);
X	default:
X		if (c != scc)
X			return(0);
X	case '?':
X		if (scc)
X			return(amatch(s, p));
X		return(0);
X	case '*':
X		return(umatch(--s, p));
X	case '\0':
X		return(!scc);
X	}
X}
X
Xumatch(s, p)
Xchar *s, *p;
X{
X	if (*p == 0)
X		return(1);
X	
X	while (*s) {
X		if (amatch(s++, p))
X			return(1);
X	}
X	
X	return(0);
X}
X
Xsort(oav)
Xchar **oav;
X{
X	register char **p1, **p2, *c;
X
X	p1 = oav;
X
X	while (p1 < &av[ac-1]) {
X		p2 = p1;
X
X		while (++p2 < &av[ac]) {
X			if (compar(*p1, *p2) > 0) {
X				c = *p1;
X				*p1 = *p2;
X				*p2 = c;
X			}
X		}
X
X		p1++;
X	}
X}
X
Xcompar(as1, as2)
Xchar *as1, *as2;
X{
X	register char *s1, *s2;
X	
X	s1 = as1;
X	s2 = as2;
X	
X	while (*s1++ == *s2) {
X		if (*s2++ == NULL)
X			return(0);
X	}
X
X	return(*--s1 - *s2);
X}
X
Xchar *concat(as1, as2)
Xchar *as1, *as2;
X{
X	register int c;
X	register char *s1, *s2;
X	
X	s1 = as1;
X	s2 = argptr;
X
X	while (c = *s1++) {
X		if (s2 > &argbuf[MAXARGLEN])
X			toolong();
X		
X		c &= 0177;
X		
X		if (c == NULL) {
X			*s2++ = '/';
X			break;
X		}
X		
X		*s2++ = c;
X	}
X	
X	s1 = as2;
X	
X	do {
X		if (s2 > &argbuf[MAXARGLEN])
X			toolong();
X
X		*s2++ = c = *s1++;
X	} while (c);
X	
X	s1 = argptr;
X	argptr = s2;
X	
X	return(s1);
X}
X
Xtoolong()
X{
X	printf("glob: argument expansion list too long\n");
X	longjmp(env);
X}
X
Xtoomany()
X{
X	printf("glob: too many arguments\n");
X	longjmp(env);
X}
________This_Is_The_END________
echo 'x - main.c'
sed 's/^X//' <<'________This_Is_The_END________' >>main.c
Xstatic char *RCSid = "$Header: main.c,v 1.3 84/08/07 13:30:45 root Exp $";
X
X/*
X * main.c - main() and handler() routines for the access program.
X *	    All global variables are declared here.
X *
X * David A. Curry
X * Purdue University Engineering Computer Network
X * April 1984
X *
X * This program is hereby placed in the public domain, and may
X * be used by anyone for any purpose provided that it is not
X * used or sold for profit and that this notice and the name
X * of the original author appears with all copies of the program.
X *
X * $Log:	main.c,v $
X * Revision 1.3  84/08/07  13:30:45  root
X * allow file names on the command line
X * 
X * Revision 1.2  84/07/19  09:08:08  root
X * fast version .. pre-structured
X * 
X * Revision 1.1  84/06/25  10:58:47  root
X * Initial revision
X * 
X */
X#include "defs.h"
X
Xstruct grp *ghead, *gtail;		/* ptrs to list's head and tail	*/
Xstruct user *uhead, *utail;		/* ptrs to list's head and tail */
X
Xint nusers;				/* number of entries in list	*/
Xint kidpid = 0;				/* pid of child process		*/
X
Xchar reset;				/* 1 if should reset on int.	*/
Xjmp_buf env;				/* where to reset to		*/
Xchar *pname;				/* global argv[0]		*/
Xchar cur_dir[MAXPATHLEN];		/* current working directory	*/
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X	char *getwd();
X	char *rindex();
X	extern handler();
X	struct rlimit rlim;
X	extern char _sobuf[];
X	
X	setbuf(stdout, _sobuf);
X
X	reset = 0;
X	signal(SIGINT, handler);
X	signal(SIGQUIT, handler);
X
X	/*
X	 * Only print this stuff if we
X	 * are interactive, i.e., no
X	 * arguments given.
X	 */
X	if (argc < 2) {
X		printf("Initializing.....please wait.....");
X		fflush(stdout);
X	}
X	
X	if ((pname = rindex(*argv, '/')) == NULL)
X		pname = *argv;
X	else
X		pname++;
X	
X	/*
X	 * Jack up our limits.
X	 */
X	rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
X
X	setrlimit(RLIMIT_CPU, &rlim);
X	setrlimit(RLIMIT_RSS, &rlim);
X	setrlimit(RLIMIT_DATA, &rlim);
X	setrlimit(RLIMIT_STACK, &rlim);
X	
X	/*
X	 * Get the current directory.
X	 */
X	if (getwd(cur_dir) == NULL) {
X		fprintf(stderr, "%s: getwd failed - %s\n", pname, cur_dir);
X		exit(1);
X	}
X		
X	/*
X	 * Load in the password and group files.
X	 */
X	loaduids();
X	loadgids();
X
X#ifdef DEBUG /*------------------------------------------------------------*/
X	printf("Done processing uids and gids\n");
X#endif /*------------------------------------------------------------------*/
X
X#ifdef LISTDEBUG /*--------------------------------------------------------*/
X	printlist();
X#endif /*------------------------------------------------------------------*/
X
X	/*
X	 * No return.  If we are interactive, we
X	 * go to our command interpreter, otherwise,
X	 * we just call access with our arguments.
X	 */
X	if (argc < 2)
X		command();
X	else
X		access(argc, argv);
X	
X	exit(0);
X}
X
X/*
X * handler - handles interrupts.
X */
Xhandler(sig)
Xint sig;
X{
X	signal(sig, SIG_IGN);
X	signal(sig, handler);
X	
X	/*
X	 * If there's a child process, blow
X	 * it away.
X	 */
X	if (kidpid)
X		kill(kidpid, sig);
X
X	printf("\nINTERRUPT!\n");
X	
X	/*
X	 * Reset or exit.
X	 */
X	if (reset)
X		longjmp(env);
X	else
X		exit(0);
X}
________This_Is_The_END________
exit