[comp.sources.unix] v24i072: Purdue tool for students to turn in work, Part02/02

rsalz@uune.tuu.net (Rich Salz) (03/21/91)

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

[ This has some very long lines in it; if your feed chopped them off
  (shame on them!) don't worry about it -- it is only a sample.  --r$ ]

#!/bin/sh
# This is part 02 of pucc-1e
# ============= turnin.cf/turnin.cf ==============
if test ! -d 'turnin.cf'; then
    echo 'x - creating directory turnin.cf'
    mkdir 'turnin.cf'
fi
if test -f 'turnin.cf/turnin.cf' -a X"$1" != X"-c"; then
	echo 'x - skipping turnin.cf/turnin.cf (File already exists)'
else
echo 'x - extracting turnin.cf/turnin.cf (Text)'
sed 's/^X//' << 'Purdue' > 'turnin.cf/turnin.cf' &&
# $Id: turnin.cf,v 5.19 90/09/18 15:12:38 ksb Exp $
# ksb's test line
test:ksb:submit:system:ALL
agen231:o1v:submit:agen231:ALL
agen321:agen321:submit:agen321:ALL
engr115:siphon:submit:engr115:130tom,330joe
engr195e:195e:submit:engr195e:wed830,wed930,wed1030,wed1130,wed1230,wed130,wed230,wed330,wed430,thu830,thu930,thu1030,thu1130,thu1230,fri830,fri930,fri1030,fri1130,fri1230,fri130,fri230
cs110:cvo:submit:cs110:grades
cs145:w1h:submit:cs145:830mark,1030dan,1130dan,130mark,230dan,330mark
cs150:xt2:submit:cs150:tagraders,d1s1,d2s1,d2s2,d3s1,d4s1,d4s2,d5s1,d5s2,d6s1,d7s1,d7s2,d8s1,d9s1,d9s2,d10s1,d10s2,d11s1,d12s1,d12s2,d13s1,d14s1,d14s2,d15s1,d15s2,d16s1,d17s1,d17s2,d18s1,d19s1,d19s2,d20s1,d20s2,d21s1,d22s1,d22s2,d23s1,d24s1,d24s2,d25s1
cs151:cs151:submit:cs151:d0s0
cs180:wp4:turnin:cs180:930Buster,1030Steve,130Hiralal
cs181:cs181:submit:cs181:1030rec,1130rec,130rec
cs250:cs250:fall90:cs250:130,330
cs251:3ax:project1:cs251:ds1,ds2
cs352:cs352:submit:cs352:d1s1,d3s1
cs400:cs440:submit:csusers:ALL
cs404:1kz:submit:cs404:ALL
404hw:cs404:submit:cs404:ALL
cs413:cs413:submit:csusers:ALL
cs440:cs440:submit:csusers:930
cs442:cs442:submit:csusers:830,1130
cs490a:cs490a:submit:csusers:d1s1,d2s1
cs502:cs502:proj:csusers:ALL
cs520:cs520:submit:csusers:ALL
cs536:cs536ta:submit:cs536t:ALL
cs543:cs543:submit:csusers:ALL
cs565:cs565:submit:csusers:ALL
cs590d:wne:submit:cs590d:ALL
Purdue
chmod 0644 turnin.cf/turnin.cf ||
echo 'restore of turnin.cf/turnin.cf failed'
Wc_c="`wc -c < 'turnin.cf/turnin.cf'`"
test 1386 -eq "$Wc_c" ||
	echo 'turnin.cf/turnin.cf: original size 1386, current size' "$Wc_c"
fi
# ============= turnin.cf/turnin.cf.5l ==============
if test -f 'turnin.cf/turnin.cf.5l' -a X"$1" != X"-c"; then
	echo 'x - skipping turnin.cf/turnin.cf.5l (File already exists)'
else
echo 'x - extracting turnin.cf/turnin.cf.5l (Text)'
sed 's/^X//' << 'Purdue' > 'turnin.cf/turnin.cf.5l' &&
.\" 
.TH TURNIN.CF 5L "PUCC"
.SH NAME
turnin.cf \- turnin configuration file format
.SH DESCRIPTION
.IR Turnin,
the electronic submissions program, reads which courses are using turnin,
which accounts should be sent submissions, what directory to put submitted
files in for each course, as well as what group the submissions should be
saved under from a database called `turnin.cf'.
.PP
The file turnin.cf consists of a line for each course with the following
information separated by colons.  The course name, the id for the account the
data should be put into, the subdirectory name in which the data should
be kept, and the group which should own the files after they have been
submitted, and a comma separated list of the sections for this course.
.SH EXAMPLE
.PP
A sample turnin configuration file follows:
.RS
.nf
agen231:j1c:submit:agen231:only
cs180:tt2:turnin:cs180adm:830Bob,930Allan,1030Beth,1230Raghu,330Matt
cs181:lmt:submit:cs181:1030,1130,130
cs190k:hpv:submit:cs190k:div1,div2,div3,div4
cs250:nru:submit:cs250:d1s1,d1s2
cs251:nnm:submit:cs251:Beaven,Mathur
cs404:olk:submit:cs404:lab
cs414:gk3:submit:csusers:mav,lian,jxy
cs536:cs536ta:submit:cs536t:d1s1,d1s2
eng115:m2n:submit:eng115:130,230
test:ksb:submit:system:one,two
.fi
.RE
.SH BUGS
The superuser must update this file by hand.
.SH "SEE ALSO"
turnin(1L), project(1L), turnin(5L), tar(1)
Purdue
chmod 0444 turnin.cf/turnin.cf.5l ||
echo 'restore of turnin.cf/turnin.cf.5l failed'
Wc_c="`wc -c < 'turnin.cf/turnin.cf.5l'`"
test 1361 -eq "$Wc_c" ||
	echo 'turnin.cf/turnin.cf.5l: original size 1361, current size' "$Wc_c"
fi
# ============= project/install ==============
if test ! -d 'project'; then
    echo 'x - creating directory project'
    mkdir 'project'
fi
if test -f 'project/install' -a X"$1" != X"-c"; then
	echo 'x - skipping project/install (File already exists)'
else
echo 'x - extracting project/install (Text)'
sed 's/^X//' << 'Purdue' > 'project/install' &&
$ project -i
project: initialize user tst for a course
X
Project allows other users to put files in your account.
If you do not know what you are doing, stop now.
Send mail to enroll-info if you have questions or need help.
X
Are you sure you want to do this? [ny] y
X
Have you ever used project to administer electronic submissions before? [ny] y
X
A long name for a course might be something like 
X        Programming in Mud
X
What is the long name of this course? This is a test
X
A short name for a course might be something like 
X        cs200
X
What is the short name of this course? test
X
All submitted files will be under a special subdirectory in your account.
This directory will be world readable.
What name would you give the submission directory? [submit] 
X
The submitted files will not be group readable, but should have an
appropriate group none the less.  For most people the default is fine.
X
Which group should the submitted files be in ? [staff] system
X
Division/section identifiers separate large classes into
many smaller parts for grading.
Examples might be
X        morning
X        afternoon
Or:
X        d1s1
X        d2s1
X
Enter the divisions/sections of your course one per line.
Use a dot (`.') on a line by itself to stop.
div/section [d1s1] > one
div/section [d1s2] > two
div/section [d1s3] > .
X
Lastly, if you would like to be notified by phone when this is done
Please enter a phone number now: [No, Thanks] 
X
Mail has been sent to a superuser to finish the configuration.
You will be contacted via email or telephone when your account
is ready for use.
Purdue
chmod 0444 project/install ||
echo 'restore of project/install failed'
Wc_c="`wc -c < 'project/install'`"
test 1575 -eq "$Wc_c" ||
	echo 'project/install: original size 1575, current size' "$Wc_c"
fi
# ============= project/Makefile ==============
if test -f 'project/Makefile' -a X"$1" != X"-c"; then
	echo 'x - skipping project/Makefile (File already exists)'
else
echo 'x - extracting project/Makefile (Text)'
sed 's/^X//' << 'Purdue' > 'project/Makefile' &&
#	$Id: Makefile,v 4.5 90/10/07 12:55:22 ksb Exp $
#
#	Makefile for project
#
# if you do nat have strcasecmp in libc get it and add it to SRC and OBJ
# (Berkeley has a free one, mkcat uses that one)
X
PROG=	project
BIN=	${DESTDIR}/usr/local/bin
X
L=../libopt
#L=/usr/include/local
I=/usr/include
S=/usr/include/sys
P=
X
INCLUDE= -I$L
DEBUG=	-O
CDEFS=  
CFLAGS= ${DEBUG} ${CDEFS} ${INCLUDE}
X
HDR=	machine.h
SRC=	project.c
OBJ=	project.o
#SRC=	project.c strcasecmp.c
#OBJ=	project.o strcasecmp.o
OTHER=	
MAN=	project.1l
SOURCE=	Makefile ${HDR} ${SRC} ${MAN} ${OTHER}
X
all: ${PROG}
X
${PROG}:$P ${OBJ}
#	${CC} -o $@ ${CFLAGS} ${OBJ} -lopt
#	${CC} -o $@ ${CFLAGS} ${OBJ} -L /usr/local/lib -lopt
X	${CC} -o $@ ${CFLAGS} ${OBJ} ../libopt/libopt.a
X
clean: FRC
X	rm -f Makefile.bak ${PROG} *.o a.out core errs tags
X
depend: ${HDR} ${SRC} FRC
X	maketd ${CDEFS} ${INCLUDE} ${SRC}
X
install: all FRC
X	install -cs ${PROG} ${BIN}/${PROG}
X
lint: ${HDR} ${SRC} FRC
X	lint -h ${CDEFS} ${INCLUDE} ${SRC}
X
mkcat: ${MAN}
X	mkcat ${MAN}
X
print: source FRC
X	lpr -J'${PROG} source' ${SOURCE}
X
source: ${SOURCE}
X
spotless: clean
X	rcsclean ${SOURCE}
X
tags: ${HDR} ${SRC}
X	ctags -t ${HDR} ${SRC}
X
${SOURCE}:
X	co -q $@
X
FRC:
X
# DO NOT DELETE THIS LINE - maketd DEPENDS ON IT
X
project.o: machine.h project.c
X
# *** Do not add anything here - It will go away. ***
Purdue
chmod 0644 project/Makefile ||
echo 'restore of project/Makefile failed'
Wc_c="`wc -c < 'project/Makefile'`"
test 1326 -eq "$Wc_c" ||
	echo 'project/Makefile: original size 1326, current size' "$Wc_c"
fi
# ============= turnin.cf/Makefile ==============
if test -f 'turnin.cf/Makefile' -a X"$1" != X"-c"; then
	echo 'x - skipping turnin.cf/Makefile (File already exists)'
else
echo 'x - extracting turnin.cf/Makefile (Text)'
sed 's/^X//' << 'Purdue' > 'turnin.cf/Makefile' &&
#	$Id: Makefile,v 4.1 90/11/28 12:42:14 ksb Exp $
#
#	Makefile for turnin.cf
#
X
PROG=	turnin.cf
LIB=	${DESTDIR}/usr/local/lib
X
SRCl=	turnin.cf
SRCs=	
MAN=	turnin.5l turnin.cf.5l
OTHER=	Distfile
SOURCE=	Makefile ${SRCl} ${SRCs} ${MAN} ${OTHER}
X
all: ${SRCl} ${PROG}
X
clean: FRC
X	rm -f Makefile.bak *.o a.out core errs tags
X
deinstall: ${MAN}
X	install -R ${LIB}/${PROG}
X	mkcat -D ${MAN}
X
depend: FRC
X
install: all FRC
X	install -cm 644 ${SRCl} ${LIB}
X	distrib -a Distfile
X
lint: FRC
X
mkcat: ${MAN}
X	mkcat ${MAN}
X
print: source FRC
X	lpr -J'${PROG} source' ${SOURCE}
X
source: ${SOURCE}
X
spotless: clean
X	rcsclean ${SOURCE}
X
tags: FRC
X
${SOURCE}:
X	co -q $@
X
FRC:
X
# DO NOT DELETE THIS LINE - make depend DEPENDS ON IT
X
# *** Do not add anything here - It will go away. ***
Purdue
chmod 0644 turnin.cf/Makefile ||
echo 'restore of turnin.cf/Makefile failed'
Wc_c="`wc -c < 'turnin.cf/Makefile'`"
test 767 -eq "$Wc_c" ||
	echo 'turnin.cf/Makefile: original size 767, current size' "$Wc_c"
fi
# ============= turnin.cf/ckcf.sh ==============
if test -f 'turnin.cf/ckcf.sh' -a X"$1" != X"-c"; then
	echo 'x - skipping turnin.cf/ckcf.sh (File already exists)'
else
echo 'x - extracting turnin.cf/ckcf.sh (Text)'
sed 's/^X//' << 'Purdue' > 'turnin.cf/ckcf.sh' &&
#!/bin/sh
X
BASE=${1-turnin.cf}
X
grep -v '#' < $BASE | awk -F: '{ print $4 }' |
while read group
do
X	grep "^$group:" /etc/group >/dev/null && continue
X	echo "$0: $BASE: $group: no such group"
done
X
grep -v '#' < $BASE | awk -F: '{ print $2 }' |
while read user
do
X	grep "^$user:" /etc/passwd >/dev/null && continue
X	echo "$0: $BASE: $user: no such user"
done
X
Xexit 0
Purdue
chmod 0644 turnin.cf/ckcf.sh ||
echo 'restore of turnin.cf/ckcf.sh failed'
Wc_c="`wc -c < 'turnin.cf/ckcf.sh'`"
test 366 -eq "$Wc_c" ||
	echo 'turnin.cf/ckcf.sh: original size 366, current size' "$Wc_c"
fi
# ============= turnin.cf/Distfile ==============
if test -f 'turnin.cf/Distfile' -a X"$1" != X"-c"; then
	echo 'x - skipping turnin.cf/Distfile (File already exists)'
else
echo 'x - extracting turnin.cf/Distfile (Text)'
sed 's/^X//' << 'Purdue' > 'turnin.cf/Distfile' &&
#
# $Header: /usr/msrc/vax/local/lib/turnin.cf/RCS/Distfile,v 4.0 90/01/13 12:02:14 ksb Exp $
#
X
( /usr/local/lib/turnin.cf ) -> ( HOST )
X	install ;
Purdue
chmod 0444 turnin.cf/Distfile ||
echo 'restore of turnin.cf/Distfile failed'
Wc_c="`wc -c < 'turnin.cf/Distfile'`"
test 149 -eq "$Wc_c" ||
	echo 'turnin.cf/Distfile: original size 149, current size' "$Wc_c"
fi
# ============= project/project.c ==============
if test -f 'project/project.c' -a X"$1" != X"-c"; then
	echo 'x - skipping project/project.c (File already exists)'
else
echo 'x - extracting project/project.c (Text)'
sed 's/^X//' << 'Purdue' > 'project/project.c' &&
/*
X * project -- reads the turnin database to determin what directory	(ksb)
X * it should build a turnin(1l) directory tree for the given course
X * in.  Then reads a local database for a list of dXsY directories
X * to build.
X *
X *	project [-dehiorvV] [-c course] [-G cmd] [-g cmd] [number|name]
X *
X * $Compile: ${cc-cc} ${cc_debug--O} -I/usr/include/local %f -o %F -lopt
X */
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/param.h>
#ifdef pdp11
#include <ndir.h>
#else /* VAX */
#include <sys/dir.h>
#include <sys/time.h>
#include <sys/resource.h>
#endif
#include <pwd.h>
#include <grp.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
extern int errno;
extern char *sys_errlist[];
#define strerror(Me)	(sys_errlist[Me])
extern int pclose(), fclose();
X
#include "getopt.h"
#include "machine.h"
X
#if HAVE_STRINGS
#include <strings.h>
#else
#include <string.h>
#endif
X
#if USE_LOCKF
#include <unistd.h>
#endif
X
extern FILE *popen();
extern char *malloc(), *strchr(), *strrchr();
#define strsave(Mpch) strcpy(malloc(strlen(Mpch)+1), Mpch)
X
static char RCSId[] =
X	"$Id: project.c,v 4.9 90/09/04 08:33:35 ksb Exp $";
X
typedef enum {
X	ON,
X	OFF,
X	DELETE
} STATUS;
X
static STATUS Projstatus[MAXPROJECTS];
X
typedef enum {
X	DISABLE,		/* submissions off			*/
X	ENABLE,			/* submissions on for project		*/
X	HELP,			/* need help				*/
X	INITIALIZE,		/* set up the intructors account	*/
X	OUTPUT,			/* display status			*/
X	QUIT,			/* stop using project, request del	*/
X	REMOVE,			/* delete submissions for proj		*/
X	GRADE,			/* scan directories for tar file	*/
X	LATE,			/* enable late submissions		*/
X	VERSION,		/* show version of project running	*/
X	UNKNOWN			/* we were not told			*/
} WHAT;
X
static WHAT What;		/* what am I doing			*/
X
static int
X	fExec = 1,		/* do commands				*/
X	fVerbose = 0,		/* print shell cmds			*/
X	iProjnum = -1,		/* default project			*/
X	iProjcount = 0;		/* number of projects			*/
X
static struct passwd
X	*ppwMe;			/* more deatails about the owner	*/
X
static char
X	acMd[MAXDIVSEC+1],	/*	%d	div/sec as in (dXsX)	*/
X	acMp[MAXPROJNAME+1],	/*	%p	the project number	*/
X	acMu[MAXLOGINNAME+1],	/*	%u	uid of student		*/
X	aacProjnames[MAXPROJNAME+1][MAXPROJECTS],
X				/* names of projects			*/
X	*progname =		/* name we run under			*/
X		RCSId,
X	*pcSum,			/* command per division			*/
X	*pcCmd;			/* command to unpack			*/
X
static char
X	acLine[MAXCHARS],	/* all these point in here		*/
X	*pcCourse,		/* who					*/
X	*pcUid,			/* who -> uid				*/
X	*pcDir,			/* who -> where				*/
X	*pcGroup,		/* who -> group				*/
X	*pcSections,		/* who -> sections			*/
X	acSubmit[] = "submit",	/* default turnin directory		*/
X	acLock[] = LOCKFILE,	/* keep lock a file, many projects loose*/
X	acMailAddr[] = MAILTO,	/* sys admin's address			*/
X	acSectIgnore[] =	/* section that means ``none given''	*/
X		SECTIGNORE,
X	acTurnbase[] =		/* data base of project co-ordinators	*/
X		TURNBASE,
X	acDeadLetter[] =	/* maildrop for dead mail		*/
X		"dead.letter";
X
X
/*
X * reads a configuration file of the form:			(doc/ksb)
X * course:alpha login:subdir for this course:alpha gid:sections\n
X */
int
getcourse(pcBase)
char *pcBase;
{
X	register FILE *fpDB;
X	register char *pc;
X	register int line;
X	register char *pcMaybe;
X	static char acError[] = "%s: %s(%d): error in data base\n";
X
X	if (0 == (fpDB = fopen(pcBase, "r"))) {
X		fprintf(stderr, "%s: fopen: %s: %s\n", progname, pcBase, strerror(errno));
X		exit(errno);
X	}
X
X	line = 0;
X	while (NULL != fgets(acLine, MAXCHARS, fpDB)) {
X		++line;
X		pcMaybe = acLine;
X		while (isspace(*pcMaybe) && '\n' != *pcMaybe)
X			++pcMaybe;
X		if ('#' == *pcMaybe || '\n' == *pcMaybe)
X			continue;
X		if (NULL == (pc = strchr(acLine, SEP))) {
X			fprintf(stdout, acError, progname, pcBase, line);
X			exit(16);
X		}
X		*pc++ = '\000';
X		pcUid = pc;
X		if (NULL == (pc = strchr(pc, SEP))) {
X			fprintf(stdout, acError, progname, pcBase, line);
X			exit(17);
X		}
X		*pc++ = '\000';
X		pcDir = pc;
X		if (NULL == (pc = strchr(pc, SEP))) {
X			fprintf(stdout, acError, progname, pcBase, line);
X			exit(18);
X		}
X		*pc++ = '\000';
X		pcGroup = pc;
X		if (NULL == (pc = strchr(pc, SEP))) {
X			fprintf(stdout, acError, progname, pcBase, line);
X			exit(19);
X		}
X		*pc++ = '\000';
X		pcSections = pc;
X		if (NULL != (pc = strchr(pc, '\n'))) {
X			*pc = '\000';
X		}
X		if ((char *)0 != pcCourse && 0 == strcmp(pcCourse, pcMaybe)) {
X			return 1;
X		}
X		if ((char *)0 == pcCourse && 0 == strcmp(pcUid, ppwMe->pw_name)) {
X			pcCourse = pcMaybe;
X			return 1;
X		}
X	}
X	return 0;
}
X
/*
X * get the cmdline options
X */
static void
options(argc, argv)
register int argc;
register char **argv;
{
X	static char acOpts[] = "c:dehinorvVlqg:G:";
X	static char acTwo[] = "%s: %s: more than one task given\n";
X	register int n;
X
X	What = UNKNOWN;
X	while (EOF != (n = getopt(argc, argv, acOpts))) {
X		switch (n) {
X		case 'c':
X			pcCourse = optarg;
X			break;
X		case 'd':
X			if (UNKNOWN != What) {
X				fprintf(stderr, acTwo, progname, "disable");
X				exit(1);
X			}
X			What = DISABLE;
X			break;
X		case 'e':
X			if (UNKNOWN != What) {
X				fprintf(stderr, acTwo, progname, "enable");
X				exit(1);
X			}
X			What = ENABLE;
X			break;
X		case 'G':
X			if (UNKNOWN != What && GRADE != What) {
X				fprintf(stderr, acTwo, progname, "grade");
X				exit(1);
X			}
X			if ((char *)0 != pcSum) {
X				fprintf(stderr, "%s: grade: two commands given for `G\'\n", progname);
X				exit(1);
X			}
X			What = GRADE;
X			pcSum = optarg;
X			break;
X		case 'g':
X			if (UNKNOWN != What && GRADE != What) {
X				fprintf(stderr, acTwo, progname, "grade");
X				exit(1);
X			}
X			if ((char *)0 != pcCmd) {
X				fprintf(stderr, "%s: grade: two commands given for `g\'\n", progname);
X				exit(1);
X			}
X			What = GRADE;
X			pcCmd = optarg;
X			break;
X		case 'l':
X			if (UNKNOWN != What) {
X				fprintf(stderr, acTwo, progname, "late");
X				exit(1);
X			}
X			What = LATE;
X			break;
X		case 'h':
X			if (UNKNOWN != What) {
X				fprintf(stderr, acTwo, progname, "help");
X				exit(1);
X			}
X			What = HELP;
X			break;
X		case 'i':
X			if (UNKNOWN != What) {
X				fprintf(stderr, acTwo, progname, "initialize");
X				exit(1);
X			}
X			What = INITIALIZE;
X			break;
X		case 'o':
X			if (UNKNOWN != What) {
X				fprintf(stderr, acTwo, progname, "output");
X				exit(1);
X			}
X			What = OUTPUT;
X			break;
X		case 'n':
X			fExec = 0;
X			break;
X		case 'V':
X			if (UNKNOWN != What) {
X				fprintf(stderr, acTwo, progname, "version");
X				exit(1);
X			}
X			What = VERSION;
X			break;
X		case 'v':
X			fVerbose = 1;
X			break;
X		case 'q':
X			if (UNKNOWN != What) {
X				fprintf(stderr, acTwo, progname, "quit");
X				exit(1);
X			}
X			What = QUIT;
X			break;
X		case 'r':
X			if (UNKNOWN != What) {
X				fprintf(stderr, acTwo, progname, "remove");
X				exit(1);
X			}
X			What = REMOVE;
X			break;
X		default:
X			break;
X		}
X	}
}
X
/*
X * remove the trailing newline from a fgets'd input line		(ray)
X */
static void
fixup(string)
register char *string;
{
X	char *newline;
X
X	if (NULL != (newline = strchr(string, '\n'))) {
X		*newline = '\000';
X	}
}
X
static char acProjlist[] =		/* path to project list		*/
X	PROJLIST;
X
/*
X * check for illegal chars in project name				(ksb)
X */
void
CheckChars(pcFrom, pcProj)
char *pcFrom, *pcProj;
{
X	if ('\000' == *pcProj) {
X		fprintf(stderr, "%s: empty project name given %s\n", progname, pcFrom);
X		exit(8);
X	}
X	do {
X		switch (*pcProj) {
X		case '/':
X		case '.':
X			fprintf(stderr, "%s: project name given %s cannot contain `%c\'.\n", progname, pcFrom, *pcProj);
X			exit(8);
X		default:
X			break;
X		}
X	} while ('\000' != *++pcProj);
}
X
X
/*
X * open projlist & read it						(ksb)
X */
int
getprojects()
{
X	register FILE *fp;
X	auto int status;
X	auto int proj;
X
X	if (NULL == (fp = fopen(acProjlist, "r"))) {
X		if (fExec) {
X			if (NULL == (fp = fopen(acProjlist, "w"))) {
X				fprintf(stderr, "%s: fopen: %s: %s\n", progname, acProjlist, strerror(errno));
X				exit(errno);
X			}
X			fclose(fp);
X		}
X		iProjcount = 0;
X		return;
X	}
X	proj = 0;
X	while (EOF != (status = getc(fp))) {
X		switch(status) {
X		case '-':
X			fgets(aacProjnames[proj], MAXCHARS, fp);
X			Projstatus[proj] = OFF;
X			break;
X		case '+':
X			fgets(aacProjnames[proj], MAXCHARS, fp);
X			Projstatus[proj] = ON;
X			break;
X		case '=':
X			fgets(aacProjnames[proj], MAXCHARS, fp);
X			Projstatus[proj] = ON;
X			iProjnum = proj;
X			break;
X		default:
X			fprintf(stderr, "%s: `%s\' is hosed\n", progname, acProjlist);
X			exit(20);
X			break;
X		}
X		fixup(aacProjnames[proj]);
X		CheckChars("in `Projlist\'", aacProjnames[proj]);
X		proj++;
X	}
X	iProjcount = proj;
X	fclose(fp);
}
X
/*
X * find a project in the in-core database				(ksb)
X */
int
search(name)
register char *name;
{
X	int i;
X
X	for (i = 0; i < iProjcount; ++i) {
X		if (0 == strcasecmp(name, aacProjnames[i])) {
X			return i;
X		}
X	}
X
X	++iProjcount;
X	Projstatus[i] = ON;
X	strcpy(aacProjnames[i], name);
X	return i;
}
X
/*
X * update the PROJLIST file					    (doc/ksb)
X */
static void
newprojects(fWarn)
int fWarn;	/* warn of no default project. */
{
X	register FILE *fp;
X	register int projout;
X	register int flag1 = 0;
X	register int flag2 = 0;
X
X	if (fExec) {
X		if (NULL == (fp = fopen(acProjlist, "w"))) {
X			fprintf(stderr, "%s: fopen: %s: %s\n", progname, acProjlist, strerror(errno));
X			exit(1);
X		}
X	}
X
X	for (projout = 0; projout < iProjcount; projout++) {
X		if (Projstatus[projout] != DELETE) {
X			if (fExec) {
X				fprintf(fp, "%c%s\n",
X					Projstatus[projout] == OFF ? '-' :
X					iProjnum == projout ? '=' : '+',
X					aacProjnames[projout]);
X			}
X			if (fVerbose) {
X				fprintf(stdout, "echo %c%s %s %s\n",
X					Projstatus[projout] == OFF ? '-' :
X					iProjnum == projout ? '=' : '+',
X					aacProjnames[projout],
X					flag2 == 0 ? ">" : ">>",
X					acProjlist);
X				flag2 = 1;
X			}
X			if (projout == iProjnum && Projstatus[projout] == ON) {
X				flag1 = 1;
X			}
X		}
X	}
X	if (fExec) {
X		fclose(fp);
X	}
X	if (flag1 == 0 && fWarn) {
X		fprintf(stderr, "%s: no default project\n", progname);
X	}
}
X
/*
X * chdir to the argument, create it if you can't find it		(ksb)
X */
static void
ceedee(pcMove)
char *pcMove;
{
X	if (0 != chdir(pcMove)) {
X		if (fExec) {
X			if (0 != mkdir(pcMove, 0755) || 0 != chdir(pcMove)) {
X				fprintf(stderr, "%s: chdir: %s: %s\n", progname, pcMove, strerror(errno));
X				exit(1);
X			}
X		}
X		if (fVerbose) {
X			fprintf(stdout, "mkdir %s\n", pcMove);
X		}
X	}
X	if (fVerbose) {
X		fprintf(stdout, "cd %s\n", pcMove);
X	}
}
X
/*
X * you don't want to look at thew next four blocks			(ksb)
X */
static char acComma[] = ",.";
static char *_pc = acComma+1;
X
static void
trav_init()
{
X	_pc = pcSections;
}
X
static int
traverse(pcBuf)
char *pcBuf;
{
X	extern char *strchr();
X	register char *p;
X
X	if (_pc == acComma+1)
X		return 0;
X
X	if ((char *)0 == (p = strchr(_pc, ','))) {
X		p = acComma;
X	}
X	*p = '\000';
X	strcpy(pcBuf, _pc);
X	*p = ',';
X	_pc = p+1;
X
X	return 1;
}
X
/*
X * close off traverse
X */
static void
travisty()
{
X	;
}
X
/*
X * get the file lock for us						(ksb)
X */
int
OpenLock()
{
X	auto int fdLock;
X
X	if (-1 == (fdLock = open(acLock, O_RDWR|O_CREAT, 0600))) {
X		fprintf(stderr, "%s: open: %s: %s\n", progname, acLock, strerror(errno));
X		exit(1);
X	}
X
#if USE_LOCKF
X	if (lockf(fdLock, F_TLOCK, 0) == -1) {
X		if (errno == EAGAIN || errno == EACCES) {
X			fprintf(stderr, "%s: waiting for lock\n", progname);
X			if (lockf(fdLock, F_LOCK, 0) == -1) {
X				goto bad_lock;
X			}
X			fprintf(stderr, "%s: got lock\n", progname);
X		} else {
X	bad_lock:
X			fprintf(stderr, "%s: lockf: %s\n", progname, strerror(errno));
X			exit(2);
X		}
X	}
#else
X	if (-1 == flock(fdLock, LOCK_NB|LOCK_EX)) {
X		if (EWOULDBLOCK == errno) {
X			fprintf(stderr, "%s: waiting for lock\n", progname);
X			if (-1 == flock(fdLock, LOCK_EX)) {
X				goto bad_lock;
X			}
X			fprintf(stderr, "%s: got lock\n", progname);
X		} else {
X	bad_lock:
X			fprintf(stderr, "%s: flock: %s\n", progname, strerror(errno));
X			exit(1);
X		}
X	}
#endif
X
#if defined(F_SETFD)
X	if (-1 == fcntl(fdLock, F_SETFD, 1)) {
X		fprintf(stderr, "%s: fcntl: %s (ignored)\n", progname, strerror(errno));
X	}
#endif
X	return fdLock;
}
X
/*
X * make the project tree (if it doesn't exist)				(ksb)
X */
static void
Make(pcProj)
char *pcProj;
{
X	auto char acDivSec[MAXDIVSEC];
X
X	ceedee(pcProj);
X	if (0 != strcmp(acSectIgnore, pcSections)) {
X		trav_init();
X		while (traverse(acDivSec)) {
X			if (fVerbose) {
X				fprintf(stdout, "mkdir %s\n", acDivSec);
X			}
X			if (!fExec) {
X				continue;
X			}
X			if (0 != mkdir(acDivSec, 0755) && EEXIST != errno) {
X				fprintf(stderr, "%s: mkdir: %s\n", progname, strerror(errno));
X				exit(2);
X			}
X		}
X		travisty();
X	}
X	ceedee("..");
}
X
/*
X * fork off a UNIX rm to recursively remove unloved files		(ksb)
X */
static void
rm(pcFile)
char *pcFile;
{
X	if (fVerbose) {
X		fprintf(stdout, "rm -rf %s\n", pcFile);
X	}
X	if (fExec) {
X		if (0 == fork()) {
X			execl("/bin/rm", "rm", "-rf", pcFile, 0);
X			fprintf(stderr, "%s: execl: /bin/rm: %s\n", progname, strerror(errno));
X			exit(1);
X		}
X		wait(0);
X	}
}
X
/*
X * remove the dir struct for a project with some rm's			(ksb)
X */
static void
Remove(pcProj)
char *pcProj;
{
X	auto char acDivSec[MAXDIVSEC];
X
X	if (0 != chdir(pcProj)) {		/* no exist, don't make */
X		return;
X	}
X	chdir("..");
X	ceedee(pcProj);		/* for verbose mode	*/
X	if (0 != strcmp(acSectIgnore, pcSections)) {
X		trav_init();
X		while (traverse(acDivSec)) {
X			rm(acDivSec);
X		}
X		travisty();
X	}
X	ceedee("..");
X	rm(pcProj);
}
X
/*
X * like strcat, but return value is end of string			(doc)
X */
static char *
stradd(pcTo, pcFrom)
register char *pcFrom, *pcTo;
{
X	while (*pcTo = *pcFrom++)
X		++pcTo;
X	return pcTo;
}
X
/*
X * using the global state, produce a shell cammand an system it off	(ksb)
X *
X * escapse in command:
X *	%u	uid of student
X *	%d	division/section is (dXsX)
X *	%p	the project number
X *	%t	submissions directory
X *	%h	home of grader
X *	%s	full path %h/%t/%p/%d/%u
X *	%%	a %
X */
static void
grade(pcFrom)
register char *pcFrom;
{
X	static char *pcExec = (char *)0;
X	register char *pcTo;
X
X	if ((char *)0 == pcExec) {
X		pcExec = malloc(NCARGS);
X		if ((char *)0 == pcExec) {
X			fprintf(stderr, "%s: out of memory\n", progname);
X			exit(errno);
X		}
X	}
X	pcTo = pcExec;
X	while ('\000' != pcFrom[0]) {
X		if ('%' == pcFrom[0]) {
X			++pcFrom;
X			switch (*pcFrom++) {
X			case '%':
X				*pcTo++ = '%';
X				continue;
X			case 'u':
X				pcTo = stradd(pcTo, acMu);
X				break;
X			case 'd':
X				pcTo = stradd(pcTo, acMd);
X				break;
X			case 'p':
X				pcTo = stradd(pcTo, acMp);
X				break;
X			case 't':
X				pcTo = stradd(pcTo, pcDir);
X				break;
X			case 'h':
X				pcTo = stradd(pcTo, ppwMe->pw_dir);
X				break;
X			case 's':
X				pcTo = stradd(pcTo, ppwMe->pw_dir);
X				*pcTo++ = '/';
X				pcTo = stradd(pcTo, pcDir);
X				*pcTo++ = '/';
X				pcTo = stradd(pcTo, acMp);
X				*pcTo++ = '/';
X				pcTo = stradd(pcTo, acMd);
X				*pcTo++ = '/';
X				pcTo = stradd(pcTo, acMu);
X				break;
X			case '\000':
X				fprintf(stderr, "%s: grade: command ends in %%?", progname);
X				exit(1);
X			default:
X				fprintf(stderr, "%s: grade: `%c\' unknown %% escape\n", progname, pcFrom[-1]);
X				exit(1);
X			}
X		} else {
X			*pcTo++ = *pcFrom++;
X		}
X	}
X	*pcTo = '\000';
X
X	if (fVerbose) {
X		printf("%s\n", pcExec);
X	}
X
X	if (fExec) {
X		system(pcExec);
X	}
}
X
/* scan a directory for tar files to grade				(ksb)
X */
scandiv(student)
DIR *student;
{
X	register struct direct *dpstudent;
X	register char *pcDot;
X
X	while (NULL != (dpstudent = readdir(student))) {
X		if ('.' == dpstudent->d_name[0])	/* skip . */
X			continue;
X		strcpy(acMu, dpstudent->d_name);
X		if ((char *)0 == pcCmd) {
X			/* do nothing */;
X		} else if ((char *)0 != (pcDot = strrchr(acMu, '.')) && 'Z' == pcDot[1] && '\000' == pcDot[2]) {
X			*pcDot = '\000';
X			grade("zcat %u.Z >%u");
X			grade(pcCmd);
X			grade("rm -f %u");
X			*pcDot = '.';
X		} else {
X			grade(pcCmd);
X		}
X	}
X	closedir(student);
X	if ((char *)0 != pcSum) {
X		strcpy(acMu, "*");
X		grade(pcSum);
X	}
}
X
/*
X * scan the project directory and system off the commands		(ksb)
X * (with grade())
X */
void
scan()
{
X	register struct direct *dp;
X	register DIR *proj, *student;
X
X	ceedee(acMp);
X	proj = opendir(".");
X	if (0 == strcmp(pcSections, acSectIgnore)) {
X		(void)strcpy(acMd, ".");
X		scandiv(proj);
X	} else {
X		while (NULL != (dp = readdir(proj))) {
X			if ('.' == dp->d_name[0]) /* dot entries skipped */
X				continue;
X			ceedee(dp->d_name);
X			(void)strcpy(acMd, dp->d_name);
X			student = opendir(".");
X			scandiv(student);
X			ceedee("..");
X		}
X		closedir(proj);
X	}
X	ceedee("..");
}
X
/*
X * get the words of wisdom from the user				(ksb)
X * strip leading white space, etc
X */
char *
GetWord(pcBuf, iLen, pcDef)
char *pcBuf, *pcDef;
int iLen;
{
X	register char *pcWhite, *pcCopy;
X	register int l;
X
X	fflush(stdout);
X	pcBuf[0] = '\n';
X	if ((char *)0 == fgets(pcBuf, iLen, stdin))
X		return (char *)0;
X	pcWhite = pcBuf;
X	while (isspace(*pcWhite) && '\n' != *pcWhite)
X		++pcWhite;
X
X	pcCopy = pcBuf;
X	l = iLen;
X	while (l-- > 0 && '\n' != (*pcCopy = *pcWhite))
X		++pcCopy, ++pcWhite;
X
X	if (pcCopy == pcBuf)
X		(void)strncpy(pcBuf, pcDef, iLen);
X	else
X		*pcCopy = '\000';
X	return pcBuf;
}
X
/*
X * is a name a poor name for a course or subdirectory			(ksb)
X */
int
BadName(pc)
char *pc;
{
X	if ('\000' == *pc)
X		return 1;
X	for (; '\000' != *pc; ++pc) {
X		if (!isalnum(*pc) && *pc != '-' && *pc != '_')
X			return 1;
X	}
X	return 0;
}
X
/*
X * build the project managers data structures in the file system	(ksb)
X * also mail off a request to be added to the database
X * we must be in the users home directory here!
X * (Return the locked fd that keeps other projects blocked)
X */
int
DoInit()
{
X	auto char acBuf[MAXCHARS+1], acCmd[MAXPATHLEN+300],
X		acShort[MAXCOURSENAME+1], acDir[MAXSUBDIRNAME+1],
X		acGroup[MAXGROUPNAME+1], acDefGroup[MAXGROUPNAME+1],
X		acLong[MAXLONGNAME+1],
X		acTemp[MAXDIVSEC+1], acDiv[MAXDIVSEC+1],
X		acPrevExp[MAXYESNO+1], *pcComma;
X	auto struct group *pgr;
X	auto FILE *fpMail;
X	auto int fdLock, cnt, div, sec, fProc, fFound;
X	auto struct stat stDir;
X	extern struct group *getgrgid(), *getgrnam();
X
X	setgrent();
X	if ((struct group *)0 == (pgr = getgrgid(getegid()))) {
X		fprintf(stderr, "%s: getgrgid: %d: %s\n", progname, getegid(), strerror(errno));
X		exit(1);
X	}
X	(void)strcpy(acDefGroup, pgr->gr_name);
X
X	printf("\nEnabling %s to accept electronic submissions\n", ppwMe->pw_name);
X	printf("\n\
Project allows other users to put files in your account.  Please supply\n\
the information requested below so that your account can be enabled to\n\
accept submissions from turnin(1L).  If you have questions or need help\n\
ask a general consultant or send mail to \"%s\".\n", acMailAddr);
X	printf("\n\
If you make a mistake you do not see a way to correct simply\n\
finish this run and run the program again.\n");
X
X	for (;;) {
X		printf("\nAre you sure you want to do this? [ny] ");
X		if ((char *)0 == GetWord(acBuf, MAXCHARS, "no"))
X			exit(0);
X		switch (acBuf[0]) {
X		case 'q':
X		case 'Q':
X		case 'N':
X		case 'n':
X			exit(0);
X		case 'y':
X		case 'Y':
X			break;
X		default:
X			continue;
X		}
X		break;
X	}
X
X	if (0 != stat(".", & stDir)) {
X		fprintf(stderr, "%s: stat: .: %s\n", progname, strerror(errno));
X		exit(2);
X	}
X	if ((stDir.st_mode & 0755) != 0755) {
X		printf("\nYour home directory *must* be kept world readable so that\n");
X		printf("students and consultants can use turnin -v to see if they have\n");
X		printf("submitted anything.\n");
X		if (0 != chmod(".", stDir.st_mode | 0755)) {
X			fprintf(stderr, "%s: chmod: .: %s\n", progname, strerror(errno));
X			exit(2);
X		}
X	} else {
X		printf("\nYour home directory is already world readable.  Please keep it that way\n");
X		printf("so that students and consultants can use turnin -v.\n");
X	}
X
X	printf("\nHave you ever used %s to administer electronic submissions before? [ny] ", progname);
X	if ((char *)0 == GetWord(acPrevExp, MAXYESNO, "none"))
X		exit(0);
X
X	do {
X		printf("\nA long name for a course might be something like \n\tProgramming in Mud\n");
X		printf("\nWhat is the long name of this course? ");
X		if ((char *)0 == GetWord(acLong, MAXLONGNAME, ""))
X			exit(0);
X	} while ('\000' == acLong[0]);
X
X	for (;;) {
X		do {
X			printf("\nA short name for a course might be something like \n\tcs200\n");
X			printf("\nWhat is the short name of this course [%s]? ", acDefGroup);
X
X			if ((char *)0 == GetWord(acShort, MAXCOURSENAME, acDefGroup))
X				exit(0);
X		} while ('\000' == acShort);
X		if (strlen(acShort) > MAXCOURSENAME) {
X			printf("Pick a shorter name, please.\n");
X			continue;
X		}
X		if (BadName(acShort)) {
X			printf("Course names cannot have special characters in them (except '-').\n");
X			continue;
X		}
X		break;
X	}
X
X	pcCourse = acShort;
X	pcUid = (char *)0;
X	fFound = getcourse(acTurnbase);
X	if (fFound) {
X		printf("Course `%s' already exists owner %s.%s in %s", acShort, pcUid, pcGroup, pcDir);
X		if (0 != strcmp(acSectIgnore, pcSections))
X			printf("/{%s}", pcSections);
X		printf(".\n");
X	} else {
X		pcDir = acSubmit;
X		pcGroup = acDefGroup;
X		pcSections = (char *)0;
X	}
X
X	for (;;) {
X		printf("\nAll submitted files will be under a special subdirectory in your account.\n");
X		printf("This directory will be world readable.\n");
X		printf("\nWhat do you want your submission directory named? [%s] ", pcDir);
X		if ((char *)0 == GetWord(acDir, MAXSUBDIRNAME, pcDir))
X			exit(0);
X		if (!BadName(acDir)) {
X			break;
X		}
X		printf("The directory name must be alpha-numeric.\n");
X	}
X
X	printf("\nThe submitted files will not be group readable, but should have an\n");
X	printf("appropriate group none the less.  For most people the default is fine.\n");
X	for (;;) {
X		printf("\nWhich group should the submitted files be in ? [%s] ", pcGroup);
X		if ((char *)0 == GetWord(acGroup, MAXGROUPNAME, pcGroup)) {
X			exit(0);
X		}
X		if ((struct group *)0 != (pgr = getgrnam(acGroup))) {
X			break;
X		}
X		printf("getgrname: %s: not found\n", acGroup);
X	}
X
X	ceedee(acDir);
X	fdLock = OpenLock();	/* closed by exit or caller */
X
X	sprintf(acCmd, "%s -s \"%s: request for \\`%s'\" %s", MAILX_PATH, progname, acShort, acMailAddr);
X	if (!fExec) {
X		if ((FILE *)0 == (fpMail = fopen("/dev/null", "w"))) {
X			fprintf(stderr, "%s: fopen: /dev/null: %s\n", progname, strerror(errno));
X			exit(1);
X		}
X		fProc = 0;
X	} else if ((FILE *)0 == (fpMail = popen(acCmd, "w"))) {
X		fprintf(stderr, "%s: cannot mail to (%s).\n", progname, strerror(errno));
X		if ((FILE *)0 == (fpMail = fopen(acDeadLetter, "a"))) {
X			exit(1);
X		}
X		fprintf(stderr, "%s: holding letter in %s\n", progname, acDeadLetter);
X		fProc = 0;
X	} else {
X		fProc = 1;
X	}
X	fprintf(fpMail, "Entry for `%s\' requested as follows:\n", acLong);
X	fprintf(fpMail, "\t%s%c%s%c%s%c%s", acShort, SEP, ppwMe->pw_name, SEP, acDir, SEP, acGroup);
X
X	printf("\n\
You can separate a large course into a few smaller parts for grading by\n\
assigning division/section identifiers.\n\n");
X	printf("Three common naming conventions are listed below:\n");
X	printf("\tmorning\t\tafternoon\n");
X	printf("\t930scott\t230kevin\n");
X	printf("\td1s1\t\td2s1\n\n");
X	printf("Choose the type your students can best remember.\n");
X	printf("\nEnter the sections of your course one per line.\n");
X	printf("Use a dot (`.\') on a line by itself to stop.\n");
X	div = 1;
X	sec = 1;
X	cnt = 0;
X	pcComma = pcSections;
X	for (;;) {
X		if ((char *)0 == pcComma || '\000' == pcComma) {
X			sprintf(acTemp, "d%ds%d", div, sec);
X		} else {
X			register int i;
X			for (i = 0; i < MAXDIVSEC; ++i) {
X				if ('\000' == *pcComma) {
X					pcComma = (char *)0;
X					break;
X				}
X				if (',' == *pcComma) {
X					++pcComma;
X					break;
X				}
X				acTemp[i] = *pcComma++;
X			}
X			acTemp[i] = '\000';
X		}
X		printf("section [%s] > ", acTemp);
X		if ((char *)0 == GetWord(acDiv, MAXDIVSEC, acTemp)) {
X			fprintf(fpMail, "\n\n!! Aborted !!\n\n");
X			(fProc ? pclose : fclose)(fpMail);
X			exit(0);
X		}
X		if ('.' == acDiv[0] && '\000' == acDiv[1]) {
X			if (0 == cnt) {
X				fprintf(fpMail, "%c%s", SEP,  acSectIgnore);
X				++cnt;
X			}
X			break;
X		}
X		if (BadName(acDiv)) {
X			printf("Divisions must have alpha-numeric names.\n");
X			continue;
X		}
X		fprintf(fpMail, "%c%s", (0 == cnt ? SEP : ','), acDiv);
X		++cnt;
X
X		/* make d<div>s<sec> easy to do for Purdue
X		 */
X		if ('d' == acDiv[0] && isdigit(acDiv[1])) {
X			register char *pc;
X			div = atoi(acDiv+1);
X			if ((char *)0 != (pc = strchr(acDiv+2, 's')) && isdigit(pc[1])) {
X				sec = atoi(pc+1);
X			}
X		}
X		if (10 == ++sec) {
X			++div;
X			sec = 1;
X		}
X	}
X
X	printf("\nLastly, if you would like to be notified by phone when this is done\n");
X	printf("Please enter a phone number now: [No, Thanks] ");
X	(void) GetWord(acLong, MAXLONGNAME, "[No Phone Call Requested]");
X
X	if (0 != strcmp(acDefGroup, acGroup)) {
X		fprintf(fpMail, "\n(default group was %s)", acDefGroup);
X	}
X	fprintf(fpMail, "\n\nPhone: %s\n", acLong);
X	fprintf(fpMail, "\nPrevious experience: %s", acPrevExp);
X	fprintf(fpMail, "\nDatabase file: `%s\'", acTurnbase);
X	if (fFound) {
X		fprintf(fpMail, "\nPrevious database line:\n\t%s:%s:%s:%s:%s", pcCourse, pcUid, pcDir, pcGroup, pcSections);
X	}
X
X	(fProc ? pclose : fclose)(fpMail); /* works for fopen too, now */
X
X	endgrent();
X
X	printf("\nMail %s been sent to a superuser to finish the configuration.\n", fExec ? "has" : "would have" );
X	if (fExec) {
X		printf("You will be contacted via email or telephone when your account\n");
X		printf("is ready for use.\n");
X	}
X	return fdLock;
}
X
X
/*
X * send the sysadmin mail telling her to remove us from the db		(ksb)
X */
static void
DoQuit()
{
X	auto char acCmd[MAXPATHLEN+300];
X	auto int fProc;
X	auto FILE *fpMail;
X
X	sprintf(acCmd, "%s -s \"%s: please delete \\`%s'\" %s", MAILX_PATH, progname, pcCourse, acMailAddr);
X
X	if ((FILE *)0 == (fpMail = popen(acCmd, "w"))) {
X		fprintf(stderr, "%s: cannot open mail cmd (%s).\n", progname, strerror(errno));
X		if ((FILE *)0 == (fpMail = fopen(acDeadLetter, "a"))) {
X			exit(1);
X		}
X		fprintf(stderr, "%s: holding letter in %s\n", progname, acDeadLetter);
X		fProc = 0;
X	} else {
X		fProc = 1;
X	}
X	fprintf(fpMail, "\nPlease remove %s from the project database, `%s\'.\n", pcCourse, acTurnbase);
X	fprintf(fpMail, "\nThanks.\n");
X	(fProc ? pclose : fclose)(fpMail);
X	printf("\nMail %s been sent to a superuser to finish the job.\n", fExec ? "has" : "would have" );
}
X
X
static char acUsage[] = "[-cdehinqrvV] [-c course] [-G cmd] [-g cmd] [project]";
static char *apcHelp[] = {	/* a task help message			*/
X	"c course select which course to administer",
X	"d	 allow no submissions for a while",
X	"e	 allow submissions and change default project name",
X	"l	 allow submissions without changing default project",
X	"G cmd	 execute command for each division",
X	"g cmd	 execute command for all submitted files",
X	"h	 print this message",
X	"i	 initialize an account for submissions",
X	"n	 just print what would be done",
X	"o	 display a status line",
X	"q	 quit using this account for submissions",
X	"r	 delete a project",
X	"v	 output shell commands",
X	"V	 output version information",
X	(char *)0
};
static char *apcEsc[] =	 {	/* an escape help message		*/
X	"cmd: may include any of %[htpdus%] which will be expanded before execution",
X	"%h	home directory of instructor",
X	"%t	turnin directory name",
X	"%p	current project number",
X	"%d	division/section identifier",
X	"%u	current user identifier, or `*\'",
X	"%s	full path (%h/%t/%p/%d/%u) to tar file",
X	"%%	a %",
X	(char *)0
};
X
/*
X * read turnin data base						(ksb)
X * make a guess at the course
X * which project, on?  default?
X * make tar file
X * maybe display it
X */
int
main(argc, argv)
int argc;
char **argv;
{
X	static int fGiven = 0;
X	auto char acTop[MAXPATHLEN+1];
X	auto char acYes[MAXYESNO+1];
X	auto int i;
X	auto int iTemp, fdLock;
X
X	umask(022);
X
X	if ((char *)0 == (progname = strrchr(argv[0], '/')))
X		progname = argv[0];
X	else
X		++progname;
X
X	(void) setpwent();
X	if (NULL == (ppwMe = getpwuid(getuid()))) {
X		fprintf(stderr, "%s: getpwuid: %d: %s\n", progname, getuid(), strerror(errno));
X		exit(99);
X	}
X
X	options(argc, argv);
X	if (What == INITIALIZE) {
X		printf("%s: initialize user %s for a course\n", progname, ppwMe->pw_name);
X		if (-1 == chdir(ppwMe->pw_dir)) {
X			fprintf(stderr, "%s: chdir: %s: %s\n", progname, ppwMe->pw_dir, strerror(errno));
X			exit(2);
X		}
X		fdLock = DoInit();
X		iProjnum = 0;
X		Projstatus[0] = OFF;
X		newprojects(0);
X		/* reply mail should include instructions for enabling projects
X		 */
X		(void) close(fdLock);
X		exit(0);
X	}
X
X	/* we change ppwMe below to reflect root becoming the user
X	 */
X	if (NULL == (ppwMe = getpwuid(geteuid()))) {
X		fprintf(stderr, "%s: getpwuid: %d: %s\n", progname, geteuid(), strerror(errno));
X		exit(99);
X	}
X	if (0 == getcourse(acTurnbase)) {
X		if ((char *)0 == pcCourse)
X			fprintf(stderr, "%s: user %s not in database\n", progname, ppwMe->pw_name);
X		else
X			fprintf(stderr, "%s: course %s not in database\n", progname, pcCourse);
X		exit(1);
X	}
X	/* if we are root, become Prof here (which might be `lroot')
X	 */
X	if ((struct passwd *)0 == (ppwMe = getpwnam(pcUid))) {
X		fprintf(stderr, "%s: getpwname: %s: %s\n", progname, pcUid, strerror(errno));
X		fprintf(stderr, "%s: no such course here\n", progname);
X		exit(1);
X	}
X	if (-1 == setuid(ppwMe->pw_uid)) {
X		fprintf(stderr, "%s: setuid: %s\n", progname, strerror(errno));
X		exit(20);
X	}
X
X	if (0 == geteuid()) {
X		fprintf(stderr, "%s: run as root [ny]? ", progname);
X		fflush(stderr);
X		(void) GetWord(acYes, MAXYESNO, "no");
X		if ('y' != acYes[0] && 'Y' != acYes[0]) {
X			fprintf(stderr, "Aborted.\n");
X			exit(0);
X		}
X	}
X	sprintf(acTop, "%s/%s", ppwMe->pw_dir, pcDir);
X	ceedee(acTop);
X
X	fdLock = OpenLock();
X
X	getprojects();
X
X	if (EOF != getarg(argc, argv)) {
X		fGiven = 1;
X		CheckChars("on the command line", optarg);
X		iTemp = search(optarg);
X	} else {
X		iTemp = iProjnum;
X	}
X
X	if (-1 != iTemp) {
X		strcpy(acMp, aacProjnames[iTemp]);
X	}
X
X	switch (What) {
X	case ENABLE:
X		if (-1 == iTemp) {
X			fprintf(stderr, "%s: no default project\n", progname);
X			exit(1);
X		}
X		iProjnum = iTemp;
X		Projstatus[iTemp] = ON;
X		Make(acMp);
X		newprojects(1);
X		break;
X
X	case QUIT:
X		fprintf(stdout, "Are you sure you want to stop using %s [ny]? ", progname);
X		(void) GetWord(acYes, MAXYESNO, "no");
X		if ('y' != acYes[0] && 'Y' != acYes[0]) {
X			fprintf(stdout, "Aborted.\n");
X			exit(0);
X		}
X		for (i = 0; i < iProjcount; ++i) {
X			if (DELETE == Projstatus[i]) {
X				continue;
X			}
X			strcpy(acMp, aacProjnames[i]);
X			fprintf(stdout, "%s: remove project %s [ynq] ", progname, acMp);
X			(void) GetWord(acYes, MAXYESNO, "yes");
X			if ('q' == acYes[0] || 'Q' == acYes[0]) {
X				fprintf(stdout, "Aborted.\n");
X				exit(0);
X			}
X			if ('y' != acYes[0] && 'Y' != acYes[0]) {
X				continue;
X			}
X			Remove(aacProjnames[i]);
X			Projstatus[i] = DELETE;
X		}
X		for (i = 0; i < iProjcount; ++i) {
X			if (DELETE != Projstatus[i]) {
X				break;
X			}
X		}
X		if (i != iProjcount) {
X			printf("%s: not all projects removed, quit.\n", progname);
X			newprojects(1);
X			break;
X		}
X		ceedee("..");
X		if (fExec) {
X			DoQuit();
X		}
X		rm(pcDir);
X		break;
X
X	case REMOVE:
X		if (-1 == iTemp) {
X			fprintf(stderr, "%s: no default project\n", progname);
X			exit(1);
X		}
X		fprintf(stdout, "Are you sure you want to delete all submissions for project %s [ny]? ", acMp);
X		(void) GetWord(acYes, MAXYESNO, "no");
X		if ('y' != acYes[0] && 'Y' != acYes[0]) {
X			fprintf(stdout, "Aborted.\n");
X			exit(0);
X		}
X		Remove(acMp);
X		Projstatus[iTemp] = DELETE;
X		newprojects(1);
X		break;
X
X	case DISABLE:
X		if (-1 == iTemp) {
X			fprintf(stderr, "%s: no default project\n", progname);
X			exit(1);
X		}
X		Projstatus[iTemp] = OFF;
X		newprojects(1);
X		break;
X
X	case GRADE:
X		if (-1 == iTemp) {
X			fprintf(stderr, "%s: no default project\n", progname);
X			exit(1);
X		}
#if HAVE_NICE
X		nice(2);
#else /* vax */
X		setpriority(PRIO_PROCESS, getpid(), 2);
#endif /* machine */
X		scan();
X		break;
X
X	case OUTPUT:
X		fprintf(stdout, "%s: user: %s.%s, course: %s, subdirectory: %s\n", progname, ppwMe->pw_name, pcGroup, pcCourse, pcDir);
X		for (i = 0; i < iProjcount; ++i) {
X			fprintf(stdout, "%s: project %-8s (%s)\n", progname, aacProjnames[i], (OFF == Projstatus[i] ? "off" : iProjnum == i ? "default" : "on"));
X		}
X		break;
X
X	case VERSION:
X		printf("%s: %s\n", progname, RCSId);
X		printf("%s: default directory name: %s\n", progname, acSubmit);
X		break;
X
X	case LATE:
X		if (-1 == iTemp) {
X			fprintf(stderr, "%s: no default project\n", progname);
X			exit(1);
X		}
X		if (iTemp == iProjnum) {
X			iProjnum = -1;
X		}
X		Projstatus[iTemp] = ON;
X		Make(acMp);
X		newprojects(1);
X		break;
X
X	case HELP:
X		fprintf(stdout, "%s: usage %s\n", progname, acUsage);
X		for (i = 0; apcHelp[i]; ++i) {
X			fprintf(stdout, "%s\n", apcHelp[i]);
X		}
X		if (fVerbose) {
X			for (i = 0; apcEsc[i]; ++i) {
X				fprintf(stdout, "%s\n", apcEsc[i]);
X			}
X		}
X		break;
X
X	case UNKNOWN:
X		if (fGiven) {
X			Make(acMp);
X			newprojects(1);
X			break;
X		}
X		/*fallthrough*/
X	default:
X		fprintf(stderr, "%s: usage %s\n", progname, acUsage);
X		break;
X	}
X
X	(void) endpwent();
X	(void) close(fdLock);
X	exit(0);
}
Purdue
chmod 0444 project/project.c ||
echo 'restore of project/project.c failed'
Wc_c="`wc -c < 'project/project.c'`"
test 32572 -eq "$Wc_c" ||
	echo 'project/project.c: original size 32572, current size' "$Wc_c"
fi
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.