[comp.sys.m6809] UUCP on OS9

pete@wlbr.EATON.COM (Pete Lyall) (04/19/88)

For those that are interested in the OS9/UUCP porting project (which
is as yet, loosely defined), here is the code we will likely be
starting with. This particular group of files was ported to UNIFlex
(a 680x0 unix clone) by Steve Sampson. Bob Santy has expressed an
interest to commence work on the port, but is currently changing jobs.
I'll be happy to gateway any requests or information.

There are also two doc files that will be included in another posting.
They are somewhat general in that they pertain more to protocol
innards that the actual system operation.

============================== Cut Here ============================


#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	Makefile
#	README
#	uucp.h
#	mail.c
#	rmail.c
#	uucico.c
#	uuxqt.c
# This archive created: Mon Apr 18 11:13:13 1988
export PATH; PATH=/bin:$PATH
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
#
#	Makefile for Unix(tm) <-> UniFLEX(tm) Mail
#
#	+O is for Optimize, +Q is for don't align on quad word boundry.
#
#	You should be super-user when running this!
#
#	This is for GIMIX Micro-20 68020 UniFLEX
#

CFLAGS	= +OQ

install:
	move /usr/bin/mail /usr/bin/lmail
	perms o-w u+rwx s+ /usr/bin/lmail
	owner system /usr/bin/lmail
	/etc/addusr uucp
	mkdir /gen/spooler/uucp
	mkdir /gen/spooler/uucp/.Log
	owner uucp /gen/spooler/uucp /gen/spooler/uucp/.Log
	move .Systems /gen/spooler/uucp
	move .Devices /gen/spooler/uucp
	move .Config /gen/spooler/uucp
	owner uucp /gen/spooler/uucp/.Systems
	owner uucp /gen/spooler/uucp/.Devices
	owner uucp /gen/spooler/uucp/.Config
	make uucico
	make uuxqt
	make mail
	make rmail
	make strip
#	make clean
	echo "++\n++ Now edit the password file. Change the 'uucp' default\n"
	echo "++ shell to 'uucico slave'\n++\n"

uucico:	uucico.r
	cc $(CFLAGS) uucico.r +o=/etc/uucico
	owner uucp /etc/uucico
	perms o-rwx u+rwx s+ /etc/uucico

uuxqt:	uuxqt.r
	cc $(CFLAGS) uuxqt.r +o=/etc/uuxqt
	owner uucp /etc/uuxqt
	perms o-rwx u+rwx s+ /etc/uuxqt

mail:	mail.r
	cc $(CFLAGS) mail.r +o=/usr/bin/mail
	owner system /usr/bin/mail
	perms o-w o+rx u+rwx /usr/bin/mail

rmail:	rmail.r
	cc $(CFLAGS) rmail.r +o=/usr/bin/rmail
	owner uucp /usr/bin/rmail
	perms o-w o+rx u+rwx s+ /usr/bin/rmail

#
#	Get rid of symbol table from binaries when debugging complete
#
strip:
	strip /etc/uucico /etc/uuxqt /usr/bin/mail /usr/bin/rmail

#
#	Remove the object files from the work directory
#
clean:
	kill *.r

#
#	Dependencies
#
uucico.r: uucico.c uucp.h
uuxqt.r:  uuxqt.c uucp.h
mail.r:   mail.c uucp.h
rmail.r:  rmail.c uucp.h

# EOF

SHAR_EOF
fi # end of overwriting check
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
cat << \SHAR_EOF > 'README'

		    Unix to UniFLEX Mail Machine
		      Version 2.1, March 1988
			   S. R. Sampson


This archive of files is the basis for sending mail from a UniFLEX(tm)
operating system to the Unix(tm) operating system, using the uucp 'G'
Protocol.  It is based on the DCP program by Richard Lamb as posted on
Usenet.

The files are:

README		This file
Makefile	All encompassing build instructions
uucp.h		Header file for Operating System specifics
mail.c		Remote/Local mail front-end
rmail.c		Remote mail work file generator (simple bang mode)
uucico.c	G Protocol dialup machine
uuxqt.c		Work file execution machine
.Systems	A sample dialing directory
.Devices	A sample dialing port setup
.Config		A sample configuration setup


I've been doing some minor testing on this code and would like others to
play around and improve it.  It may be an useful start for other machines.

Put the files in a source code directory and simply execute 'make'.
This will almost completely build the mail machine.  You will have to
manualy edit the /etc/log/password file to change the default shell to
'/etc/uucico'.  Then run 'password uucp' and assign the account a password.
Following that, find a Unix machine to talk to and setup the .Config,
.Systems, and .Devices files for your serial ports.

The format for the .Devices file is:

/dev/ttyxx Baudrate Type Status

Where:	xx is the TTY number
	Baudrate is the setting of the port
	Type is MODEM or DIRECT
	Status is FREE or USED

You initially create the file with everything FREE.  I'm using a GMX Micro-20
and have it set up like so:

/dev/tty03 1200 MODEM FREE
/dev/tty02 2400 DIRECT FREE
/dev/tty01 2400 DIRECT FREE

The format for the .Systems file is:

Machine-Name Call-Time Baudrate Type Dialup-Sequence

Wher:	Machine-name is 6 character significance
	Call-Time is Any (Not implemented)
	Baudrate is the machines (or your modems) maximum speed
	Type is MODEM or DIRECT which is used when looking for a port
	Dialup-Sequence is a send/receive sequence for logging on

The format for the .Config file is:

User-Name	(The owner of the files (eg. 'uucp'))
Machine-Name	(6 character significance (eg. 'test'))
Error-Name	(Who gets mail when problems are found (eg. 'root', 'system'))


The uucp directory will contain all the work files to be sent and received.
The log file will contain information written by the programs as they run.
This information can be useful when debugging.  Remember to purge this stuff
when disk space starts filling up.

This UniFLEX version will automatically run in the slave mode upon login to
the uucp account.  An example cron entry to run uucico in the master mode with
a debug level 9 would look like so:

0 * * * * /etc/uucico master 9

If you have problems check on all permissions and ownership:

/usr/bin/mail			system	 rwxr-x
/usr/bin/lmail			system	 rwxr-x	SUID
/usr/bin/rmail			uucp	 rwxr-x	SUID
/etc/uucico			uucp	 rwx---	SUID
/etc/uuxqt			uucp	 rwx--- SUID
/gen/spooler/uucp		uucp	drwxr-x
/gen/spooler/uucp/.Log		uucp	drwx---
/gen/spooler/uucp/.Systems	uucp	 rw----
/gen/spooler/uucp/.Devices	uucp	 rw----
/gen/spooler/uucp/.Config	uucp	 rw----

Sometimes the .Devices file may become corrupt.  Make sure you view it for
problems such as no FREE device, or missplaced writes.

To send remote mail:

mail myconnect!ihnp4!killer!sampson
This is a test
[] <- Type a Control-D for EOF and mail will be spooled.

sampson@killer.UUCP	ihnp4!killer!sampson
Steve Sampson, Box 45668, Tinker AFB, OK 73145

/* EOF */

SHAR_EOF
fi # end of overwriting check
if test -f 'uucp.h'
then
	echo shar: will not over-write existing file "'uucp.h'"
else
cat << \SHAR_EOF > 'uucp.h'
/*
 *	uucp.h
 *
 *	Header file for Unix(tm) to UniFLEX(tm) mail machine
 */

/*
 *	Spool Directory
 */

#define SPOOLDIR	"/gen/spooler/uucp"

/*
 *	Configuration file (in spooldir directory) format
 *
 *	1.	user			uucp
 *	2.	myname			test
 *	3.	errorname		system
 */

#define	CONFIG		"/gen/spooler/uucp/.Config"

/*
 *	Systems file (in spooldir directory) format
 *
 *	1.	rmtname		ihnp4
 *	2.	cctime		2300-2359
 *	3.	ttype		MODEM
 *	4.	tspeed		2400
 *	5.	loginseq	/n/n:gin:uucp\n:sword:secret\n
 */

#define	SYSTEMS		".Systems"	/* file with calling info	     */

/*
 *	Devices file (in spooldir directory) format
 *
 *	1.	Name		/dev/ttyxx
 *	2.	Max-Speed	75 - 19200 Baud
 *	3.	Type		MODEM or DIRECT
 *	4.	Status		USED or FREE
 */

#define	DEVICES		".Devices"	/* file with terminal info	     */


/*
 *	Error log files (relative to spooldir)
 */

#define	SYSLOG		".Log/uucico"	/* error log file for uucico	     */
#define	XQTLOG		".Log/uuxqt"	/* error log file for uuxqt	     */

/* EOF */

SHAR_EOF
fi # end of overwriting check
if test -f 'mail.c'
then
	echo shar: will not over-write existing file "'mail.c'"
else
cat << \SHAR_EOF > 'mail.c'
/*
 *	mail.c
 *
 *	UniFLEX(tm) Version 2.1
 *	Public Domain, March 1988 by S. R. Sampson
 *
 *	This program checks for local or remote indication
 *	and sends mail using rmail or lmail.
 *
 */

/* Defines */

#define	FALSE	0
#define	TRUE	~FALSE

/* Includes */

#include "uucp.h"

#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>
#include <pwd.h>

/* Globals */

long	stamp;
char	dfile[140], from[16], tmp[140];
int	remote;

/* The program */

main(argc, argv)
int	argc;
char	*argv[];
{
	register FILE	*fdfile;
	register char	*ptr;
	struct passwd	*name;

	if (argc == 1)  {
		execl("/usr/bin/lmail", "mail", (char *)NULL);
		exit(-1);
	}

	strcpy(dfile, "/tmp/msXXXXXX");
	mktemp(dfile);

	if ((fdfile = fopen(dfile, "w")) == (FILE *)NULL)  {
		perror("mail 1");
		exit(-1);
	}

	/*
	 *	If address has a '!' character flag address as remote
	 *	else local.
	 */

        if ((ptr = strchr(argv[1], '!')) == (char *)NULL)
		remote = FALSE;
	else
		remote = TRUE;

	/*
	 *	Find out who we are and generate the required
	 *	"From name date" line
	 */

	if (remote)  {
		name = getpwuid(getuid());
		strcpy(from, name->pw_name);
		endpwent();

		time(&stamp);
		sprintf(tmp, "From %s %s", from, ctime(&stamp));

		fwrite(tmp, sizeof(char), strlen(tmp), fdfile);
	}

	/*
	 *	Copy stdin to a /tmp file
	 */

	for (;;)  {
                if (fgets(tmp, sizeof tmp, stdin) == (char *)NULL)
                        break;

		fwrite(tmp, sizeof(char), strlen(tmp), fdfile);
        }

	fflush(fdfile);
	fclose(fdfile);

	if (remote)  {
		uufix(dfile);	 /* Change UniFLEX CR to Unix LF */
		sprintf(tmp, "rmail %s <%s >/dev/null", argv[1], dfile);
	} else
		sprintf(tmp, "lmail %s <%s >/dev/null", argv[1], dfile);

	if (system(tmp) != 0)  {
		fprintf(stderr, "mail: Fatal - Could not exec() mailer\n");
		exit(-1);
	}

#ifndef DEBUG
	unlink(dfile);
#endif
}


uufix(file)
register char	*file;
{
	register FILE	*fd, *temp;
	register int	scoop;
	char		name[32];

	strcpy(name, "/tmp/uufixXXXXXX");
	mktemp(name);

	if ((fd = fopen(file, "r")) == (FILE *)NULL)  {
		fprintf(stderr, "mail: File '%s' not found\n", file);
		exit(-1);
	}

	if ((temp = fopen(name, "w")) == (FILE *)NULL)  {
		fprintf(stderr, "mail: Unable to open temp file '%s'\n", name);
		exit(-1);
	}

	while ((scoop = getc(fd)) != EOF)  {
		if (scoop == 0x0D)
			putc(0x0A, temp);
		else
			putc(scoop, temp);
	}

	fflush(temp);
	fclose(temp);
	fclose(fd);

	unlink(file);
	link(name, file);
	unlink(name);
}

/* EOF */

SHAR_EOF
fi # end of overwriting check
if test -f 'rmail.c'
then
	echo shar: will not over-write existing file "'rmail.c'"
else
cat << \SHAR_EOF > 'rmail.c'
/*
 *      rmail.c
 *
 *      Mini Remote Mail Handler
 *
 *	Version 2.1, Public Domain (p) March 1988
 *
 *	This program is executed by either 'mail' or 'uuxqt'.
 *
 *	The standard UniFLEX(tm) mail doesn't handle remote addresses
 *	so I made a new program called mail and renamed the old
 *	mail to lmail.  Now if the new mail finds a remote address
 *	it calls rmail, else lmail.
 *
 *	UniFLEX has a different End of Line convention than Unix
 *	does.  Therefore rmail will need to fix the transfered
 *	files before sending to mail.
 *
 *	Usage:
 *        rmail remotesystem!remoteuser myname (test myname)
 *        rmail remotesystem!remoteuser (.Config myname)
 *	  rmail localuser
 *
 *	Limitation:
 *	  Will only do bang '!' addresses.
 */

/* Defines */

#define	FALSE	0
#define	TRUE	~FALSE

/* Includes */

#include "uucp.h"

#include <sys/modes.h>
#include <sys/dir.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>
#include <pwd.h>

/* Globals */

extern	char	*tzname[2];		/* contains two strings CDT or CST   */
extern	int	daylight;		/* array index for above	     */

struct	passwd	*names;

long	stamp;
int	remote;
char	bfile[32], cfile[32], dfile[32], xfile[32];
char	rmtname[16], rmtuser[128], myname[16], user[16];
char	datetime[48], ofrom[256], from[256], tmp[256], sys[32];
char	errorname[8], *p, *ptr, *mfgets(), *fgets();

FILE	*fbfile, *fcfile, *fdfile;

/* The program */

main(argc, argv)
int     argc;
char    *argv[];
{
	if (argc < 2 || argc > 3)  {
		fprintf(stderr, "Usage: rmail [remote!]user [testname]\n");
		exit(-1);
	}

	/*
	 *	Read Configuration file
	 *
	 *	The UniFLEX 2.3 C Compiler would not compile this
	 *	correctly from an include file.
	 */

#undef CONFIG
#define CONFIG "/gen/spooler/uucp/.Config"

	if ((fcfile = fopen(CONFIG, "r")) != (FILE *)NULL)  {
		mfgets(user, sizeof user, fcfile);
		mfgets(myname, sizeof myname, fcfile);
		mfgets(errorname, sizeof errorname, fcfile);
		fclose(fcfile);
	}
	else
		exit(-1);

	if (argc == 3)			/* useful for debuging		     */
                strcpy(myname, argv[2]);

	/*
	 *	If address has a '!' character flag address as remote
	 *	else local.
	 */

        if ((ptr = strchr(argv[1], '!')) == (char *)NULL)
                remote = FALSE;
        else  {
                *ptr = '\0';
                remote = TRUE;
        }

	/*
	 *	Change file creation mask to rwx------
	 */

	umask(S_IOREAD | S_IOWRITE | S_IOEXEC);

	/*
	 *	Change to working directory
	 */

        chdir(SPOOLDIR);

	/*
	 *	Calculate the time
	 */

        time(&stamp);
        strcpy(datetime, ctime(&stamp));
        datetime[strlen(datetime) - 1] = '\0';
	strcat(datetime, " ");
	strcat(datetime, tzname[daylight]);	/* add in CDT or CST	     */

	/*
	 *	See if this mail was generated locally
	 *	or came from somewhere else.  Local mail
	 *	has a "From x y" header, remote has a 
	 *	"From x y remote from z" header.
	 */

	strcpy(from, "");
	strcpy(ofrom, "");

	fgets(tmp, sizeof tmp, stdin);	/* get a line from stdin	     */

	/*
	 *  First line should be a From line.  Our goal is to change from this:
	 *
	 *  	From a!b (date) remote from c
	 *
	 *  To this:
	 *
	 *	From c!a!b (date) remote from d
	 *
	 *  Alternatively we may get a line from mail:
	 *
	 *	From a (date)
	 *
	 *  Now we must add on the machine name:
	 *
	 *	From a (date) remote from b
	 *
	 *  No 'from line' causes an error
	 */

	if (strncmp(tmp, "From ", 5) != 0 && strncmp(tmp, ">From ", 6) != 0)  {
		strcpy(dfile, "/tmp/rmXXXXXX");
                mktemp(dfile);

		if ((fdfile = fopen(dfile, "w")) == (FILE *)NULL)  {
			perror("rmail 1");
			exit(-1);
		}

		/* write out old 'from' line */

		fwrite(tmp, sizeof(char), strlen(tmp), fdfile);

		/*
		 *	Copy file from stdin to the temporary file
		 */

		for (;;)  {
	                if (fgets(tmp, sizeof tmp, stdin) == (char *)NULL)
	                        break;

			fwrite(tmp, sizeof(char), strlen(tmp), fdfile);
	        }

		fflush(fdfile);
		fclose(fdfile);
		uufix(dfile);		/* change LF to required EOL */

		/*
		 *	Send a letter to manager about this
		 */

		SendMail("No From Line\012\012", TRUE);
		exit(0);
	}

	/*
	 *  Get the (now) garbage "From" into sys
	 *  and the remote address into ofrom
	 */

	sscanf(tmp, "%s %s ", sys, ofrom);
	p = tmp;

	/*
	 *  Parse line to seek out "remote from"
	 */

	for (;;)  {
		p = strchr(p + 1, 'r');
		if (p == NULL)  {

			/*
			 * You get here after parsing to the end of the string
			 * and didn't find "remote from" text.  The only other
			 * option is the basic "From" from mail.
			 */

			break;
		}

		if (strncmp(p, "remote from ", 12) == 0)
			break;
	}

	if (p == NULL)
		strcat(from, ofrom);
	else  {
		sscanf(p, "remote from %s", from);
		strcat(from, "!");
		strcat(from, ofrom);
	}

        if (remote)  {

		/* spool a remote file */

                strcpy(rmtname, argv[1]);
                strcpy(rmtuser, ptr + 1);

                sprintf(dfile, "D.%.6sXXXXXX", myname);
                mktemp(dfile);

		if ((fdfile = fopen(dfile, "w")) == (FILE *)NULL)  {
			perror("rmail 2");
			exit(-1);
		}

		sprintf(tmp, "From %s %s remote from %s\012",
		                                     from, datetime, myname);

		/* put out new from */

		fwrite(tmp, sizeof(char), strlen(tmp), fdfile);

	        sprintf(tmp, "Received: by %s on %s\012", myname, datetime);
		fwrite(tmp, sizeof(char), strlen(tmp), fdfile);

        } else  {
        
                /* set up a local file */

                strcpy(dfile, "/tmp/rmXXXXXX");
                mktemp(dfile);

		if ((fdfile = fopen(dfile, "w")) == (FILE *)NULL)  {
			perror("rmail 3");
			exit(-1);
		}

		fwrite(">", sizeof(char), 1, fdfile);
		fwrite(tmp, sizeof(char), strlen(tmp), fdfile);
	}

	/*
	 *	uuxqt and mail will call with:
	 *	"rmail [remote!]user <D.xxx >/dev/null"
	 *
	 *	Copy the stdin to the spool or /tmp file
	 */

	for (;;)  {
                if (fgets(tmp, sizeof tmp, stdin) == (char *)NULL)
                        break;

		fwrite(tmp, sizeof(char), strlen(tmp), fdfile);
        }

	if (!remote)  {
		fflush(fdfile);
		fclose(fdfile);
		uufix(dfile);

		/*
		 *	See if destination user name exists on this machine
		 */

		names = getpwnam(argv[1]);
		endpwent();

		if (names == (struct passwd *)NULL)  {
			SendMail("Unknown User\012\012", FALSE);
			exit(0);
		}
		else
			sprintf(tmp, "mail %s <%s", argv[1], dfile);

		if (system(tmp) != 0)
			execerr();

#ifndef DEBUG
		unlink(dfile);
#endif
	}  else  {
		fclose(fdfile);

	        /*
		 *	Now forward the mail, if user does not exist
		 *	or the remote machine is not known
		 *	then send mail to system manager and sender
		 *
		 *	The senders name is now in the global from[] string
		 */


		/*
		 *	See if we talk to requested remote machine
		 */

		if (CheckLegalName(rmtname) == FALSE)  {
			SendMail("Unknown Machine\012\012", FALSE);
			exit(0);
		}

                /* make the spool files for uucico */

                sprintf(bfile, "B.%.6sXXXXXX", rmtname);
                mktemp(bfile);

		if ((fbfile = fopen(bfile, "w")) == (FILE *)NULL)  {
			perror("rmail 4");
			exit(-1);
		}

                sprintf(tmp, "U %s %s\012F %s\012I %s\012C rmail %s\012",
                  user, myname, dfile, dfile, rmtuser);
		fwrite(tmp, sizeof(char), strlen(tmp), fbfile);

		fclose(fbfile);

                sprintf(xfile, "X.%.6sXXXXXX", rmtname);
                sprintf(cfile, "C.%.6sXXXXXX", myname);
                mktemp(xfile);
                mktemp(cfile);

		if ((fcfile = fopen(cfile, "w")) == (FILE *)NULL)  {
			perror("rmail 5");
			exit(-1);
		}

                sprintf(tmp,"S %s %s %s - %s 0666\012S %s %s %s - %s 0666\012",
                  dfile, dfile, user, dfile, bfile, xfile, user, bfile);
		fwrite(tmp, sizeof(char), strlen(tmp), fcfile);

		fclose(fcfile);
	}
}


execerr()
{
	fprintf(stderr, "rmail: Fatal - Could not exec() mailer\n");
	exit(-1);
}


/*
 *	Send mail to system manager upon errors
 *
 *	Mail is contained in a file referenced
 *	by Global variable 'dfile'
 */

SendMail(str, mgronly)
char	*str;
int	mgronly;
{
	strcpy(cfile, "/tmp/rmtXXXXXX");
	mktemp(cfile);

	fcfile = fopen(cfile, "w");
	fdfile = fopen(dfile, "r");

	strcpy(tmp, "Subject: ");
	strcat(tmp, str);

	fwrite(tmp, sizeof(char), strlen(tmp), fcfile);

	while (feets(tmp, sizeof tmp, fdfile) != (char *)NULL)  {
		fwrite("> ", sizeof(char), 2, fcfile);
		fwrite(tmp, sizeof(char), strlen(tmp), fcfile);
	}

	fclose(fcfile);
	fclose(fdfile);

#ifndef DEBUG
	unlink(dfile);
#endif

	/*
	 *	Return mail to system manager
	 *	(and sender if mgronly == FALSE)
	 */

	if (!mgronly)  {
		sprintf(tmp, "mail %s <%s", from, cfile);
		if (system(tmp) != 0)
			execerr();
	}

	sprintf(tmp, "mail %s <%s", errorname, cfile);
	if (system(tmp) != 0)
		execerr();

#ifndef DEBUG
	unlink(cfile);
#endif
}


/*
 *	Check the machine name by reading the SYSTEMS file
 *
 *	returns FALSE if not found, else TRUE
 *
 */

CheckLegalName(name)
register char	*name;
{
	register FILE	*fd;
	char		line[256], tmp[16];

	if ((fd = fopen(SYSTEMS, "r")) == NULL)
		return(FALSE);

	while (fgets(line, sizeof line, fd) != NULL)  {
	        sscanf(line, "%s ", tmp);

	        if (strncmp(name, tmp, 6) == 0)  {
			fclose(fd);
			return(TRUE);
		}
	}

	fclose(fd);

	return(FALSE);
}


/*
 *	mfgets (modified fgets)
 *
 *	Same as fgets() only this version deletes '\n'
 */

char *mfgets(s, n, iop)
register char	*s;
register int	n;
register FILE	*iop;
{
	register int	c;
	register char	*cs;

	cs = s;
	while (--n > 0 && (c = getc(iop)) != EOF)  {
		if (c == 0x0D)  {
			*cs = '\0';
			break;
		} else
			*cs++ = c;
	}

	return((c == EOF && cs == s) ? (char *)NULL : s);
}


/*
 *	UniFLEX uses CR instead of LF (Just to be different I guess)
 */

char *fgets(s, n, iop)
register char	*s;
register int	n;
register FILE	*iop;
{
	register int	c;
	register char	*cs;

	cs = s;
	while (--n > 0 && (c = getc(iop)) != EOF)  {
		*cs++ = c;
		if (c == 0x0A)
			break;
	}

	*cs = '\0';
	return((c == EOF && cs == s) ? (char *)NULL : s);
}


/*
 *	Change Unix LF to UniFLEX CR
 */

uufix(file)
register char	*file;
{
	register FILE	*fd, *temp;
	register int	scoop;
	char		name[32];

	strcpy(name, "/tmp/uufixXXXXXX");
	mktemp(name);

	if ((fd = fopen(file, "r")) == (FILE *)NULL)  {
		fprintf(stderr, "rmail: File '%s' not found\n", file);
		exit(-1);
	}

	if ((temp = fopen(name, "w")) == (FILE *)NULL)  {
		fprintf(stderr,"rmail: Unable to open temp file '%s'\n", name);
		exit(-1);
	}

	while ((scoop = getc(fd)) != EOF)  {
		if (scoop == 0x0A)
			putc(0x0D, temp);
		else
			putc(scoop, temp);
	}

	fclose(temp);
	fclose(fd);

	unlink(file);
	link(name, file);
	unlink(name);
}

/* EOF */

SHAR_EOF
fi # end of overwriting check
if test -f 'uucico.c'
then
	echo shar: will not over-write existing file "'uucico.c'"
else
cat << \SHAR_EOF > 'uucico.c'
/*
 *	uucico.c
 *
 *	Copy In - Copy Out 'G' Protocol
 *
 *	68020 UniFLEX(tm) Version 2.1
 *
 *	Based on dcp.c Copyright (c) 1985, 1986, 1987 by Richard H. Lamb
 *	Changes are Public Domain (p) March 1988 by S. R. Sampson
 *
 *	Called by 'cron', 'at', or 'uucp login'.
 *
 *	Define DEBUG to prevent unlinking of C. files
 */

/* Includes */

#include "uucp.h"

#include <sys/signal.h>
#include <sys/sgtty.h>
#include <sys/modes.h>
#include <sys/dir.h>
#include <string.h>
#include <setjmp.h>
#include <errno.h>
#include <ctype.h>
#include <stdio.h>
#include <time.h>
#include <pwd.h>

/* General purpose defines */

#define	FALSE		0
#define	TRUE		~FALSE

#define	ERROR		FALSE
#define	OK		TRUE

#define	MAXLINE		132
#define	MAXLOGTRY	  4
#define MSGTIME		 25
#define MAXTRY		  5
#define	PKTTIME		 10
#define PKTSIZE		 64
#define PKTSIZ2		  2
#define HDRSIZE 	  6
#define WNDSIZE		  3
#define	DLE		 16

/* Main Switching states */

#define	INITIAL		000
#define	DEVICE		001
#define	LOGIN		002
#define	HANDSHAKE	003
#define	MASTER		004
#define	SLAVE		005
#define	HANGUP		006
#define	END		007
#define	ABORT		010

/* Master/Slave states */

#define	MS_INIT		020
#define	MS_SCANDIR	021
#define	MS_SEND		022
#define	MS_HANGUP	023
#define	MS_RECEIVE	024
#define	MS_ABORT	025
#define	MS_CHKWORK	026
#define	MS_END		027

/* Switching States */

#define	SS_HEADER	030
#define	SS_DATA		031
#define	SS_EOF		032
#define	SS_END		033
#define	SS_ABORT	034

/*
 *	Control Packet Defines
 *
 *	xxx	name	yyy
 *
 *	001	CLOSE	n/a
 *	002	RJ	last correctly received sequence number
 *	004	RR	last correctly received sequence number
 *	005	INITC	window size
 *	006	INITB	data seqment size
 *	007	INITA	window size
 */

#define DATA		000	/* The packet is data */
#define CLOSE		001	/* CLOSE, Comunications complete */
#define RJ		002	/* RJ Reject, detected an error */
#define RR		004	/* RR Receiver ready, detected no errors */
#define	INITC		005
#define	INITB		006
#define	INITA		007

#define	MODEM		000	/* TTY device types */
#define	DIRECT		001

/* Global Variables */

struct passwd	*pw;

struct	{
	int	baudrate;
	char	*code;
} rates[] = {			/* what I use the most */
    B300,	"300",
    B1200,	"1200",
    B2400,	"2400",
    B9600,	"9600",
    B19200,	"19200",
    0,		""
};

#define	DEFAULT_BAUD	B1200

struct	sgttyb	NewSetting,
		OldSetting;

long	Dpos;

FILE	*fd,
	*fdC,
	*log,
	*input,
	*output;

int	type,
	size,
	pkrec,
	pksent,
	pknerr,
	master,
	debug_level;

char	InPacket[PKTSIZE],
	OutPacket[PKTSIZE],
	Filename[MAXLINE],
	Cfilename[MAXNAMLEN+1],		/* defined in dir.h */
	Loginseq[64],
	Rmtname[8],
	Device[16],
	myname[8],
	user[8],
	speed[8];

char	*rcverr[] = {
	"",
	"Input Buffer Empty",
	"Bad Header",
	"Packet Timeout",
	"Checksum Error",
	"Wrong Packet Size"
};

/* Forward Declarations */

unsigned checksum();
char	 *mfgets(), *gtime();
jmp_buf  env;

/*
 *	Usage:
 *	  uucico mode debug
 *
 *	  Where mode is 'master' or 'slave'
 *	  And debug is an integer from 0 to 9
 *
 *	  Defaults are slave mode and level 0 debug
 *
 */

main(argc, argv)
int	argc;
char	**argv;
{
	register int	state;

	/*
	 *	Read Configuration file
	 *
	 *	The UniFLEX 2.3 C Compiler would not compile this
	 *	correctly from an include file.
	 */

#undef CONFIG
#define CONFIG "/gen/spooler/uucp/.Config"

	if ((log = fopen(CONFIG, "r")) != (FILE *)NULL)  {
		mfgets(user, sizeof user, log);
		mfgets(myname, sizeof myname, log);
		fclose(log);
	}
	else
		exit(1);

	/*
	 *	Open the error log file
	 */

	log = fopen(SYSLOG, "a");
	setbuf(log, (char *)NULL);	/* log file should be unbuffered */

	/*
	 *	Setup defaults
	 */

	master = FALSE;
	debug_level = 0;
	fd = fdC = (FILE *)NULL;

	/*
	 *	Change to the spool directory
	 */

	chdir(SPOOLDIR);

	/*
	 *	Process the two command line arguments
	 *	The first is "master" or "slave", the second
	 *	is an integer debug level between 0 and 9.
	 */

	if (argc > 1 && strcmp(argv[1], "master") == 0)
		master = TRUE;

	if (argc > 2)  {
		debug_level = abs(atoi(argv[2]));
		if (debug_level > 9)
			debug_level = 9;
	}

	state = INITIAL;
	while (TRUE)  {
		switch(state)  {
			case INITIAL:
				if (master)
					state = GetSystem();
				else
					state = HandShake();
				break;
			case DEVICE:
				state = GetTTY();
				if (state == ABORT)
				  fprintf(log,"Main(): No Device Available %s",
				   			gtime());
				break;
			case LOGIN:
				state = Login();
				break;
			case HANDSHAKE:
				state = HandShake();
				break;
			case MASTER:
				state = Master();
				break;
			case SLAVE:
				state = Slave();
				break;
			case HANGUP:
				state = MasterHangup();
				break;
			case END:
				state = SendOO();
		}

		if (state == ABORT)
			break;
	}

	CloseTTY();
	fclose(log);

	/* go execute any transfered work files */

	execl("/etc/uuxqt", "uuxqt", (char *)NULL);
}


/*-------------------------
 *	uucico Subroutines
 *-------------------------
 */

GetString(string)
register char	*string;
{
	register int	i, len;
	char		c[MAXLINE];

	len = strlen(string);
	c[len--] = 0;
	while (strcmp(string, c) != 0)  {
	        for (i = 0; i < len; i++)
			c[i] = c[i+1];

		if (ReadTTY(&c[i], 1, PKTTIME) == 0)  {
		       fprintf(log,"GetString(): Input timed out %s", gtime());
		       fprintf(log," Wanted %s got %s\n", string, c);
		       return ERROR;
		}

		toascii(c[i]);
        }

	return OK;
}


/*
 *	mfgets (modified fgets)
 *
 *	Same as fgets() only this version deletes '\n'
 */

char *mfgets(s, n, iop)
register char	*s;
register int	n;
register FILE	*iop;
{
	register int	c;
	register char	*cs;

	cs = s;
	while (--n > 0 && (c = getc(iop)) != EOF)  {
		if (c == 0x0D)  {
			*cs = '\0';
			break;
		}
		else
			*cs++ = c;
	}

	return((c == EOF && cs == s) ? (char *)NULL : s);
}


char *gtime()
{
	long	value;

	time(&value);
	return(ctime(&value));
}


CheckName()
{
	register FILE	*fdsys;
	char		line[MAXLINE], tmp[16];

	if ((fdsys = fopen(SYSTEMS, "r")) == NULL)  {
		fprintf(log,"CheckName(): Can't open %s %s", SYSTEMS, gtime());
		return ERROR;
	}

	while (mfgets(line, sizeof line, fdsys) != (char *)NULL)  {
	        sscanf(line, "%s ", tmp);

	        if (strncmp(Rmtname, tmp, 6) == 0)  {
			fclose(fdsys);
			return OK;
		}
	}

	fclose(fdsys);
	fprintf(log, "CheckName(): Unknown system %s attempted login %s",
				Rmtname, gtime());
	return ERROR;
}


CheckTime(time)
register char	*time;
{
	return OK;
}


/*-------------------------------------------------
 *	uucico High Level State Switching routines
 *-------------------------------------------------
 */

GetSystem()
{
	register FILE	*fdsys;
	char		line[MAXLINE], ttype[8], cctime[16];

	if ((fdsys = fopen(SYSTEMS, "r")) == NULL)  {
		fprintf(log,"GetSystem(): Can't open %s %s", SYSTEMS, gtime());
		return ABORT;
	}

	do {
		if (mfgets(line, sizeof line, fdsys) == (char *)NULL)  {
			fclose(fdsys);
			return ABORT;		/* no more systems */
		}

		sscanf(line, "%s %s %s %s %s",
		        Rmtname, cctime, ttype, speed, Loginseq);
		type = strcmp(ttype, "MODEM") ? DIRECT : MODEM;
	} while ( !strcmp(cctime, "Slave") || !CheckTime(cctime) );

	fclose(fdsys);

	return DEVICE;			/* go get a device and login */
}


/*
 *	Find the next device of the type and speed requested
 *	in the DEVICES file
 */

GetTTY()
{
	register FILE	*fdtty;
	int		Type, Used;
	char		line[MAXLINE], Types[8], Useds[8], Bauds[8];

	if ((fdtty = fopen(DEVICES, "r+")) == (FILE *)NULL)  {
		fprintf(log,"GetTTY(): Can't open %s %s", DEVICES, gtime());
		return ABORT;
	}

	do  {
		if (mfgets(line, sizeof line, fdtty) == (char *)NULL)  {
			fprintf(log,"GetTTY(): No devices available %s",
							gtime());
			fclose(fdtty);
			return ABORT;
		}

		sscanf(line, "%s %s %s %s", Device, Bauds, Types, Useds);
		Type = strcmp(Types, "MODEM") ? DIRECT : MODEM;
		Used = strcmp(Useds, "FREE");
	} while (Type != type || strcmp(Bauds, speed) || Used);

	fseek(fdtty, -5L, 1);
	Dpos = ftell(fdtty);		/* Dpos is used in CloseTTY() */
	fputs("USED", fdtty);
	fclose(fdtty);

	if (OpenTTY() == ERROR)
		return ABORT;

	return LOGIN;
}


Login()
{
	register int	k, j, trys;
	register char	*last;
	char		buffer[MAXLINE];
	int		flag;

	j = k = 0;

	/*
	 *	First move everything to 'buffer', changing text
	 *	newlines to binary
	 */

	while(Loginseq[j] != '\0')  {
	        if (Loginseq[j] == '\\' && Loginseq[j+1] == 'n')  {
	                buffer[k++] = '\n';
	                j += 2;
	                continue;
	        }

	        buffer[k++] = Loginseq[j++];
	}

	buffer[k] = j = 0;
	flag = TRUE;	/* start by sending */

	while (buffer[j])  {
	        k = j;

	        while (buffer[k] != '-' && buffer[k] != '\0')
	                k++;

	        if (buffer[k] == '\0')
			buffer[k+1] = '\0';

	        buffer[k] = '\0';

	        if (flag)  {
	                last = &buffer[j];
	                WritePacket(&buffer[j], FALSE);
	                flag = FALSE;
	        } else  {
			trys = 1;
	                while (GetString(&buffer[j]) == ERROR)  {
			   if (trys >= MAXLOGTRY)  {
			      fprintf(log,"Login(): Failed login on remote %s",
								gtime());
			      return ABORT;
			   }

			   /* try resending the last sequence */

			   trys++;
			   WritePacket(last, FALSE);
	                }

	                flag = TRUE;
	        }

	        j = k + 1;
	}

	return HANDSHAKE;
}


HandShake()
{
	char	t1[16], t2[16];

	if (master)  {
		if (ReadPacket(InPacket, MSGTIME) == 0)
			return ABORT;

		if (strncmp(InPacket + 6, Rmtname, 6))  {
			fprintf(log,"HandShake(): System name match error %s",
						gtime());
			return ABORT;
		}

		sprintf(OutPacket, "S%.6s", myname);
		WritePacket(OutPacket, TRUE);

		if (ReadPacket(InPacket, MSGTIME) == 0)
			return ABORT;

		if (strncmp(InPacket + 1, "OK", 2))  {
			fprintf(log,"HandShake(): Remote doesn't like name %s",
						gtime());
			return ABORT;
		}

		if (ReadPacket(InPacket, MSGTIME) == 0)
			return ABORT;

		if (InPacket[0] != 'P' && strchr(InPacket[1], 'g') == 0)  {
			WritePacket("UN", TRUE);
hserr:			fprintf(log,"HandShake(): G Protocol not available %s",
							gtime());
			return ABORT;
		}

		WritePacket("Ug", TRUE);
		return MASTER;
	} else {
		sprintf(OutPacket, "Shere=%.6s", myname);
		WritePacket(OutPacket, TRUE);

		if (ReadPacket(InPacket, MSGTIME) == 0)
			return ABORT;

		sscanf(InPacket, "S%s %s %s", Rmtname, t1, t2);
		sscanf(t2, "-x%d", &debug_level);

		if (CheckName())
			return ABORT;

		WritePacket("ROK", TRUE);
		WritePacket("Pg", TRUE);

		if (ReadPacket(InPacket, MSGTIME) == 0)
			return ABORT;

		if (strcmp(InPacket, "Ug") != 0)
			goto hserr;

		return SLAVE;
	}
}


MasterHangup()
{
	int	len;

	if (SendPacket("H", 0, TRUE) == ERROR)
		return ABORT;

	if (ReceivePacket(InPacket, &len) == ERROR)
		return ABORT;

	if (strncmp(InPacket, "HN", 2) != 0)
		return SLAVE;

        return END;
}


/*---------------------------------------------------
 *	uucico Medium Level State Switching routines
 *---------------------------------------------------
 */

Master()
{
	register int	state;

	state = MS_INIT;

	while (TRUE)  {
		switch (state)  {
		case MS_INIT:
			state = SendInit();
			break;
		case MS_SCANDIR:
			state = ScanDirectory();
			break;
		case MS_SEND:
			state = Send();
			break;
		case MS_HANGUP:
			state = MasterHangup();
			break;
		case MS_RECEIVE:
			state = Receive();
			break;
		case MS_ABORT:
			return ABORT;
		}
	}
}


Slave()
{
	register int	state;

	state = MS_INIT;

	while (TRUE)  {
		switch (state)  {
		case MS_INIT:
			state = ReceiveInit();
			break;
		case MS_RECEIVE:
			state = Receive();
			break;
		case MS_CHKWORK:
			state = CheckForWork();
			break;
		case MS_SCANDIR:
			state = ScanDirectory();
			break;
		case MS_SEND:
			state = Send();
			break;
		case MS_HANGUP:
			state = SlaveHangup();
			break;
		case MS_ABORT:
			return ABORT;
		}
	}
}


/*------------------------------------------------
 *	uucico Low Level State Switching routines
 *------------------------------------------------
 */

Send()
{
	register int	state;

	state = SS_HEADER;

	while (TRUE)  {		/* Do this as long as necessary */
		switch (state)  {
		case SS_HEADER:
		     if (fd != (FILE *)NULL)  {
		       fprintf(log,"Send(): File already open in SS_HEADER %s",
			    				gtime());
		       state = SS_ABORT;
		     }
		     else
		         state = SendFileHeader();
		     break;
		case SS_DATA:
			state = SendData();
			break;
		case SS_EOF:
			state = SendEof();
			break;
		case SS_END:
			fclose(fdC);
#ifndef DEBUG
			unlink(Cfilename);
#endif
			fdC = (FILE *)NULL;
			return MS_SCANDIR;
		case SS_ABORT:
			return MS_ABORT;
		}
	}
}


Receive()
{
	register int	state;

	state = SS_HEADER;

	while (TRUE)  {
		switch (state)  {
		case SS_HEADER:
			state = ReceiveFileHeader();
			break;
		case SS_DATA:
			state = ReceiveData();
			break;
		case SS_ABORT:
			return MS_ABORT;
		case SS_END:
			return MS_CHKWORK;
		}
	}
}


SendOO()
{
	register int	i;
	char		msg[MAXLINE];

	msg[1] = 0;
	for (i = 0; ((msg[1] != 'O') && (i < MAXTRY)); i++)  {
		WritePacket("OOOOOO", TRUE);

		if (ReadPacket(msg, MSGTIME) == 0)
			break;
	}

	if (i == MAXTRY)
		fprintf(log, "SendOO(): Can't \"Over and Out\" with %s %s",
		  Rmtname, gtime());

        WritePacket("OOOOOO", TRUE);	/* one last time for mama */

	if (master)
		return INITIAL;
	else
		return ABORT;
}

SlaveHangup()
{
	SendPacket("HY", 0, 2); /* don't wait for Acknowledge */
	ClosePacket();

	return END;
}


GetFile(header)
register char	*header;
{
	register int	i;
	char		line[MAXLINE];

	if (mfgets(line, sizeof line, fdC) == (char *)NULL)  {
		fprintf(log,"GetFile(): Unexpected End Of File %s", gtime());
		return ERROR;
	}

	sscanf(&line[2], "%s ", Filename);

	for (i = 0; line[i]; i++)
		if (strncmp(&line[i], "0666", 4) == 0)
			break;

	line[i+4] = '\0';
	strcpy(header, line);	/* now contains the whole line up past 0666 */

	return OK;
}


CheckForWork()
{
	register int	c;

	c = ScanDirectory();
	if (c == MS_ABORT)
		return MS_ABORT;

	if (c == MS_SEND)
		if (SendPacket("HN", 0, TRUE) == ERROR)
			return MS_ABORT;

	return MS_SEND;
}


ScanDirectory()
{
	register DIR		*dir;
	register struct	direct	*pdir;

	strcpy(Cfilename, "C.");
	strncat(Cfilename, Rmtname, 6);

	if ((dir = opendir(SPOOLDIR)) == (DIR *)NULL)  {
		fprintf(log, "ScanDirectory(): Could not open %s %s",
					SPOOLDIR, gtime());
		return MS_ABORT;
	}

	while ((pdir = readdir(dir)) != (struct direct *)NULL)  {
		if (strncmp(pdir->d_name, Cfilename, MAXNAMLEN) == 0) {
			strncpy(Cfilename, pdir->d_name, (int)(pdir->d_namlen));
	                closedir(dir);

			if (fdC == (FILE *)NULL)  {
			   if ((fdC = fopen(Cfilename, "r")) == (FILE *)NULL) {
			     fprintf(log,"ScanDirectory(): Could not open %s %s",
							Cfilename, gtime());
				  return MS_ABORT;
			   }
			}

			return MS_SEND;
		}
	}

	closedir(dir);
	return MS_END;
}


/*---------------------------------------------------
 *	uucico G protocol High Level packet routines
 *---------------------------------------------------
 */

SendInit()
{
	if (OpenPacket() == ERROR)
		return MS_ABORT;

	return MS_SEND;
}


ReceiveInit()
{
	if (OpenPacket() == ERROR)
		return MS_ABORT;

	return MS_RECEIVE;
}


SendFileHeader()
{
	int	len;
	char	header[MAXLINE];

	/* get next file from current work */

	if (GetFile(header) == ERROR)
		return SS_END;			/* end sending session */

	if ((fd = fopen(Filename, "r")) == NULL)  {
		fprintf(log, "SendFileHeader(): Can't open %s %s", Filename,
						gtime());
		return SS_ABORT;
	}

	/*
	 *	Send the header
	 */

	if (SendPacket(header, 0, TRUE) == ERROR)
		return SS_ABORT;

	/*
	 *	Get the reply from remote
	 */

	if (ReceivePacket(InPacket, &len) == ERROR)
		return SS_ABORT;

	/*
	 *	Abort if remote doesn't say yes
	 */

	if (strncmp(InPacket, "SY", 2) != 0)  {
		fprintf(log,"SendFileHeader(): Remote refused request %s",
						gtime());
		return SS_ABORT;
	}

	/*
	 *	Scoop some poop
	 */

	size = fread(OutPacket, sizeof(char), PKTSIZE, fd);

	/*
	 *	Change to DATA state
	 */

	return SS_DATA;
}


ReceiveFileHeader()
{
	int	len;
	char	fromfile[MAXLINE];

	if (ReceivePacket(InPacket, &len) == ERROR)
		return SS_ABORT;

	if (strncmp(InPacket, "H", 1) == 0)
		return SS_END;

	sscanf(&InPacket[2], "%s %s ", fromfile, Filename);

	if ((fd = fopen(Filename, "w")) == NULL)  {
		fprintf(log, "ReceiveFileHeader(): Can't create %s %s",
					Filename, gtime());
		return SS_ABORT;
	}


	if (SendPacket("SY", 0, TRUE) == ERROR)
		return SS_ABORT;

	/*
	 *	Change to DATA state
	 */

	return SS_DATA;
}


SendData()
{
	if (SendPacket(OutPacket, size, FALSE) == ERROR)
		return SS_ABORT;

	/* Get data from file */

	if ((size = fread(OutPacket, sizeof(char), PKTSIZE, fd)) == 0)
		return SS_EOF;

	return SS_DATA;
}


SendEof()
{
	int	len;

	if (SendPacket("", 0, FALSE) == ERROR)
		return SS_ABORT;

	if (ReceivePacket(InPacket, &len) == ERROR)
		return SS_ABORT;	/* rec CY or CN */

	if (strncmp(InPacket, "CY", 2))  {
		fprintf(log, "SendEof(): Received %s expected CY %s",
					InPacket, gtime());
		return SS_ABORT;
	}

	fclose(fd);
	fd = (FILE *)NULL;

	return SS_HEADER;		/* go get the next file to send */
}


ReceiveData()
{
	int	len;

	if (ReceivePacket(InPacket, &len) == ERROR)
		return SS_ABORT;

	if (len == 0)  {
	        fclose(fd);
		if (SendPacket("CY", 0, TRUE) == ERROR)
			return SS_ABORT;

		return SS_HEADER;
	}

	fwrite(InPacket, sizeof(char), len, fd);

	return SS_DATA;
}


/*-----------------------------------------------------
 *	uucico G protocol Medium Level packet routines
 *-----------------------------------------------------
 */

ClosePacket()
{
	char	tmp[PKTSIZE];

	spack(CLOSE, 0, 0, 0, tmp);
	spack(CLOSE, 0, 0, 0, tmp);
}


/*
 *	INITA - INITC Handshake
 */

OpenPacket()
{
	register int	i, j;
	int		npkrec, npksent, len;
	char		tmp[PKTSIZE];

	pkrec   = 0;
	pksent  = 1;
	pknerr  = 0;

	for (j = INITA; j >= INITC; j--)  {
		for (i = 0; i < MAXTRY; i++)  {
			spack(j, 0, 0, 0, tmp);
			if (rpack(&npkrec, &npksent, &len, tmp) == j)
				goto nextinit;
		}

		fprintf(log,"OpenPacket(): Could not INIT Handshake %s",
						gtime());
		return ERROR;
nextinit:;
	}

	return OK;
}


ReceivePacket(data, len)
register char	*data;
register int	*len;
{
	register int	i, val;
	int		npkrec, npksent, nlen, nakflg;
	char		tmp[PKTSIZE];

	nakflg = FALSE;

	for (i = 0; i < MAXTRY; i++)  {
		switch (val = rpack(&npkrec, &npksent, &nlen, data))  {
		case DATA:
			if (npksent != ((pkrec + 1) % 8))
                		break;

		        pkrec = (pkrec + 1) % 8;
		        *len = nlen;

		        if (nakflg)
		        	spack(RJ, pkrec, 0, 0, tmp);
		        else
		      		spack(RR, pkrec, 0, 0, tmp);

		        return OK;
		case CLOSE:
			fprintf(log,"ReceivePacket(): Received CLOSE from remote %s",
						gtime());
			return ERROR;
		default:
			fprintf(log,"ReceivePacket(): rpack() returned %s %s",
						rcverr[abs(val)], gtime());
			nakflg = TRUE;
		        break;  
		}
	}

	if (++pknerr >= MAXTRY)  {
		fprintf(log,"ReceivePacket(): Too many packet errors %s",
						gtime());
		return ERROR;
	}

	return OK;
}


/*
 *	flg = 2      Just send the packet with no wait for ACK.
 *	flg = TRUE   Zero out the unused part of the buffer (for "msg" pkts).
 *	flg = FALSE  Normal data
 */

SendPacket(data, len, flg)
register char	*data;
register int	len, flg;
{
	register int	i, val;
	int		nlen, npkrec, npksent;
	char		tmp[PKTSIZE];

	if (flg)  {
		len = PKTSIZE;
		for (i = strlen(data); i < PKTSIZE; i++)
			data[i] = '\0';
	}

	for (i = 0; i < MAXTRY; i++)  {
		spack(DATA, pkrec, pksent, len, data);

		if (flg == 2)
			return OK;

		switch (val = rpack(&npkrec, &npksent, &nlen, tmp))  {
		case RR:
		case RJ:
 			if (npkrec != pksent)
				break;	/* Retry on wrong Packet Numbers */

	        	pksent = (pksent + 1) % 8;
	        	return OK;

		case CLOSE:
			fprintf(log,"SendPacket(): Received CLOSE from remote %s",
							gtime());
			return ERROR;
		default:
			fprintf(log,"SendPacket(): rpack() returned %s %s",
						rcverr[abs(val)], gtime());
		}
	}

	if (++pknerr >= MAXTRY)  {
		fprintf(log,"SendPacket(): Too many packet errors %s",
						gtime());
		return ERROR;
	}

	return OK;
}


/*
 *	Send a Packet
 */

spack(type, npkrec, npksent, len, packet)
int	type, npkrec, npksent, len;
char	*packet;
{
	unsigned int	check, i;
	char		c, pkt[HDRSIZE];

	if (len == 0)
		*packet = 0;

	pkt[0] = DLE;
	pkt[4] = type << 3;

	switch (type)  {
		case CLOSE:
			break;
		case RR:
		case RJ:
			pkt[4] |= npkrec;
			break;
		case INITA:
		case INITC:
			pkt[4] |= WNDSIZE;	/* window size */
			break;
		case INITB:
			pkt[4] |= 1;		/* segment size (1 = 64) */
			break;
		case DATA:
			pkt[4] = 0x80 | (npksent << 3) | npkrec;

			/*
			 *	If packet length is less than 64
			 *	then first byte needs to indicate the
			 *	difference.  So shift everything right
			 *	and put the difference in first byte
			 */

			if (c = PKTSIZE - len)  {
				pkt[4] |= 0x40;   /* "short" data packet */
				for (i = PKTSIZE - 1; i > 0; i--)
					packet[i] = packet[i - 1];

				packet[0] = c;
			}
	}

	if (type != DATA)  {
	        pkt[1] = 9;		/* control packet, size = 0 */
		check = (unsigned)pkt[4];
	} else {
	        pkt[1] = PKTSIZ2;	/* data packet, size = 64 */
	        check = checksum(packet, PKTSIZE);
		check = (check ^ (unsigned)pkt[4]);
	}

	check = (unsigned)0xAAAA - check;
	pkt[2] = check & 0xFF;
	pkt[3] = check >> 8;
	pkt[5] = (pkt[1] ^ pkt[2] ^ pkt[3] ^ pkt[4]);

	WriteTTY(pkt, HDRSIZE);       /* header is 6-bytes long */

	if (pkt[1] != 9)
		WriteTTY(packet, PKTSIZE);   /* data is always 64 bytes long */
}


/*
 *	Read Packet
 *
 *	Returns:
 *	+n	Okey-Dokey;
 *	-1	Input buffer empty;
 *	-2	Bad header;
 *	-3	Lost packet timeout;
 *	-4	Checksum error;
 *	-5	Wrong packet size
 */

rpack(npkrec, npksent, len, packet)
register int	*npkrec, *npksent, *len;
register char	*packet;
{
	register unsigned type, check, checkchk;
	register int	  i;
	char		  c, pkt[HDRSIZE];

	c = 0;
	while ((c & 0x7F) != DLE)
		if (ReadTTY(&c, 1, PKTTIME) < 1)
			return(-1);	/* input buffer empty */

	if (ReadTTY(&pkt[1], HDRSIZE - 1, PKTTIME) < (HDRSIZE - 1))
		return(-1);		/* input buffer empty */

	/* header is 6-bytes long */

	if (pkt[1] ^ pkt[2] ^ pkt[3] ^ pkt[4] ^ pkt[5])
	        return(-2);		/* bad header */

	if ((pkt[1] & 0x7F) == 9)  {		/* control packet */
	        *len     = 0;
	        type     = (unsigned)pkt[4] >> 3;
	        *npkrec  = (int)(pkt[4] & 0x07);
	        *npksent = *packet = check = checkchk = 0;
	}
	else {					/* data packet */
	        if ((pkt[1] & 0x7F) != PKTSIZ2)
			return(-5);   /* can't handle other than size 64 */

	        type     = DATA;
	        c        = pkt[4] & 0x3F;
	        *npksent = (unsigned)c >> 3;
	        *npkrec  = (int)(c & 0x07);

	        if (ReadTTY(packet, PKTSIZE, PKTTIME) < PKTSIZE)
			return(-3);	/* packet timeout */

	        /* 64 byte packets even if partial */

		check    = ((unsigned)pkt[3] << 8) | (unsigned)pkt[2];
	        checkchk = checksum(packet, PKTSIZE);
	        checkchk = (unsigned)0xAAAA-(checkchk^((unsigned)pkt[4]|0x80));

	        if (checkchk != check)
	                return(-4);

		/*
		 *	See if a "short" packet was received
		 *	If so, delete the size difference in first byte
		 */

	        *len = PKTSIZE;
	        if (pkt[4] & 0x40)  {
	                *len -= *packet;
	                for (i = 0; i < *len; i++)
				packet[i] = packet[i + 1];
	        }

	        packet[*len] = 0;
	}

	return type;
}


unsigned checksum(data, n)
register char	*data;
register int	n;
{
	register int	sum, x;
	register unsigned t;

	sum = -1;
	x = 0;

	do  {
	        if (sum < 0)  {
			sum <<= 1;
			sum++;
		}
	        else
			sum <<= 1;

		t = sum;
		sum += (unsigned)*data++;
		x += sum ^ n;

		if ((unsigned)sum < t)
			sum ^= x;

	} while (--n > 0);

	return sum;
}


/*--------------------------------------------------
 *	uucico G protocol Low Level packet routines
 *--------------------------------------------------
 */

WritePacket(msg, syn)
register char	*msg;
register int	syn;
{
	if (syn)
		WriteTTY("\020", 1);

	WriteTTY(msg, strlen(msg));

	if (syn)
		WriteTTY("", 1);
}


ReadPacket(msg, seconds)
register char	*msg;
register int	seconds;
{
	register int	i;
	char		c;

	do {
		if (ReadTTY(&c, 1, seconds) == 0)  {
rperr:			fprintf(log, "ReadPacket(): Input timed out %s",
						gtime());
			return 0;
		}
	} while ((c = toascii(c)) != '\020');

	dor (i = 0; i < MAXLINE && c ; i++)  {
		if (ReadTTY(&c, 1, seconds) == 0)
			goto rperr;

		if ((c = toascii(c)) == '\n')
			msg[i] = c = '\0';
		else
			msg[i] = c;
	}

	return(strlen(msg));
}


/*------------------------------
 *	uucico TTY I/O routines
 *------------------------------
 */

ReadTTY(data, len, seconds)
register char	*data;
register int	len, seconds;
{
	register int	count;
	int		ClkInt();

	if (setjmp(env))
		return 0;

	signal(SIGALRM, ClkInt);	/* execute ClkInt() if alarm         */
	alarm(seconds);			/*      goes off		     */

	if ((count = fread(data, sizeof(char), len, input)) < 1)  {
		alarm(0);
		return 0;
	}

	alarm(0);
	return count;
}


WriteTTY(data, len)
register char	*data;
register int	len;
{
	fwrite(data, sizeof(char), len, output);
}


ClkInt()
{
	longjmp(env, 1);
}


OpenTTY()
{
	register int	i, baud;

	i = 0;
	baud = DEFAULT_BAUD;

	/*
	 *	Check baud rate entry against table
	 *	change 'baud' to new code
	 */

	do  {
		if (strcmp(speed, rates[i].code) == 0)  {
			baud = rates[i].baudrate;
			break;
		}
	} while (rates[++i].baudrate != 0);

	input  = fopen(Device, "r");
	output = fopen(Device, "w");

	if (input == NULL || output == NULL)  {
		fprintf(log, "OpenTTY(): Failure opening device %s %s",
					Device, gtime());
		return ERROR;
	}

	SetTTY(fileno(input), baud);
	SetTTY(fileno(output), baud);

	return OK;
}


CloseTTY()
{
	register FILE	*fdtty;

	stty(fileno(input), &OldSetting);
	stty(fileno(output), &OldSetting);

	fclose(input);
	fclose(output);

	if ((fdtty = fopen(DEVICES, "r+")) == (FILE *)NULL)  {
		fprintf(log, "CloseTTY(): Can't re-open %s %s", DEVICES, gtime());
		return;
	}

	fseek(fdtty, Dpos, 0);	/* Dpos is computed in GetTTY() */
	fputs("FREE", fdtty);
	fclose(fdtty);
}


SetTTY(tty, baud)
register int	tty, baud;
{
	gtty(tty, &OldSetting);			/* get the TTY settings      */
	gtty(tty, &NewSetting);			/* copy old settings to new  */

	NewSetting.sg_speed = D8S1NONE;		/* 8 Bits No Parity One Stop */
	NewSetting.sg_flag  = RAW & ~ECHO;	/* no echo and no processing */
	NewSetting.sg_prot  = baud;		/* slap in the given baud    */

	stty(tty, &NewSetting);			/* set the TTY new settings  */
}

/* EOF */

SHAR_EOF
fi # end of overwriting check
if test -f 'uuxqt.c'
then
	echo shar: will not over-write existing file "'uuxqt.c'"
else
cat << \SHAR_EOF > 'uuxqt.c'
/*
 *	uuxqt.c
 *
 *	Command File Execute
 *
 *	UniFLEX(tm) Version 2.1
 *
 *	Based on dcp.c Copyright (c) 1985, 1986, 1987 by Richard H. Lamb
 *	Changes are Public Domain (p) March 1988 by S. R. Sampson
 *
 *	This program searches for work files in the spool directory and
 *	executes them.  Work files have a X. prefix.
 *
 *	It is executed by 'uucico', 'cron', or 'at'
 */

/* Includes */

#include "uucp.h"

#include <sys/modes.h>
#include <sys/fcntl.h>
#include <sys/dir.h>		/* defines MAXNAMLEN and directory stuff */
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <pwd.h>

/* Defines */

#define	MAXLINE		256
#define	ERROR		-1

/* Globals */

long	stamp;
char	command[MAXLINE], input[MAXLINE], output[MAXLINE], line[MAXLINE];
char	Cfilename[MAXNAMLEN+1], filename[MAXNAMLEN+1], ltime[32];
char	systemname[16], username[16], user[16];

/* Forward Declarations */

char	*mfgets();


main(argc, argv)
int	argc;
char	**argv;
{
	register char		*p;
	register FILE		*fdC, *log;
	register DIR		*dd;
	register struct	direct  *dir;
	struct	 passwd		*pw;
	int			inull, onull;

	/*
	 *	Read Configuration file
	 *
	 *	The UniFLEX 2.3 C Compiler would not compile this
	 *	correctly from an include file.
	 */

#undef CONFIG
#define CONFIG "/gen/spooler/uucp/.Config"

	if ((log = fopen(CONFIG, "r")) != (FILE *)NULL)  {
		mfgets(user, sizeof user, log);
		fclose(log);
	}
	else
		exit(1);

	/*
	 *	Change to spool directory
	 */

	chdir(SPOOLDIR);

	id ((dd = opendir(SPOOLDIR)) == (DIR *)NULL)
		exit(1);

	while ((dir = readdir(dd)) != (struct direct *)NULL)  {
	        if (strncmp(dir->d_name, "X.", 2) != 0)
			continue;

		strncpy(Cfilename, dir->d_name, dir->d_namlen);

		if ((fdC = fopen(Cfilename, "r")) == ERROR)
			continue;

		inull = onull = TRUE;

		while (mfgets(line, sizeof line, fdC) != NULL)  {
			switch (line[0])  {
		        case 'C':
				strcpy(command, &line[2]);
				break;

			/*
			 *	See if all required files are present
			 */

			case 'F':
				strcpy(filename, &line[2]);
				p = filename;

				while ((*p != ' ') && *p)
					p++;

				*p = '\0';

				if (access(filename, 1) == ERROR)  {

					/*
					 * All files not present, go check
					 * other work files and give up
					 * on this one till next time
					 */

					goto not_ready;
				}

				break;

		        case 'I':
				strcpy(input, &line[2]);
				inull = FALSE;
				break;

		        case 'O':
				strcpy(output, &line[2]);
				onull = FALSE;
				break;

		        case 'U':
				strcpy(username, &line[2]);
				p = username;

				while ((*p != ' ') && *p)
					p++;

				*p++ = '\0';
				strcpy(systemname, p);
				break;

		        default:
				break;
			}
		}

		if (inull)
			strcpy(input, "/dev/null");

		if (onull)
			strcpy(output, "/dev/null");

		sprintf(line,"%s <%s >%s", command, input, output);

		if ((log = fopen(XQTLOG, "a")) != ERROR)  {
			time(&stamp);
			strcpy(ltime, ctime(&stamp));
			ltime[strlen(ltime) - 1] = '\0';

			fprintf(log, "%s %s %s %s\n",
				ltime, systemname, username, line);

			fclose(log);
		}

		if (system(line) != ERROR)  {
#ifndef DEBUG
			unlink(Cfilename);

			if (!inull)
				unlink(input);

			if (!onull)
				unlink(output);
#endif
		}

not_ready:	fclose(fdC);

	}

	closedir(dd);
}


/*
 *	mfgets (modified fgets)
 *
 *	Same as fgets() only this version deletes '\n'
 */

char *mfgets(s, n, iop)
register char	*s;
register int	n;
register FILE	*iop;
{
	register int	c;
	register char	*cs;

	cs = s;
	while (--n > 0 && (c = getc(iop)) != EOF)  {
		if (c == '\n')  {
			*cs = '\0';
			break;
		} else
			*cs++ = c;
	}

	return((c == EOF && cs == s) ? (char *)NULL : s);
}

/* EOF */

SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0
-- 
Pete Lyall (OS9 Users Group VP)|  DELPHI: OS9UGVP  |  Eaton Corp.(818)-706-5693
Compuserve: 76703,4230 (OS9 Sysop) OS9 (home): (805)-985-0632 (24hr./1200 baud)
Internet: pete@wlbr.eaton.com      UUCP: {ihnp4,scgvax,jplgodo,voder}!wlbr!pete