/*
 * Copyright 1984,1985 Omen Technology Inc All Rights Reserved
 * Ward Christensen Protocol handler for sending and receiving
 * ascii and binary files.  Modified for choice of checksum or crc.
 * This code may be used for developing YMODEM support ONLY IF
 * the program documentation acknowledges the origin of this code.
 */

#ifdef CPM
EXT char defdisk;		/* Default disk */
EXT char origdisk;		/* Disk originally logged into */
#define PSTRLEN 33	/* For misc string params */
#ifndef PATHLEN
#define PATHLEN 65	/* Plenty long */
#endif
#endif

#define CTRL(v) ('v' & 037)
#define ERROR (-1)
#define BACKUP (-3)	/* Returned to expand to access prev file */
#define NOTFOUND (-4)	/* Returned to expand to indicate can't open */
#define FOUNDIT (-5)	/* Function found what it was looking for */
#define OK 0
#define TRUE 1
#define FALSE 0
#define SAYTERM 2	/* Doykbd returns this if sayterm needed */
#define SECSIZ 128
#define CPMEOF 0x1A

/*
 *  Some important usage() error numbers
 */
#define SJ_ABORT 20	/* Keyboard abort via abort key */

EXT FILE *cfin,		/* Surrogate input getcty, kgets */
	*fin,		/* For file uploads */
	*fout,		/* For file downloads */
	*cout,		/* For text capture and utility command redirection */
	*fcall;		/* script reader */

EXT char cfast;		/* BDS C fastest access is to extern's */
EXT char checksum;	/* Delcared here for speed */
EXT unsigned oldcrc;	/* Accumulates CRC checksum */
EXT int wcj, firstch, errors;
EXT int firstsec;	/* First sector, C instead of NAK for crc */

#define CMDLEN 130

#define SOH 1
#define STX 2
#define ETX 3
#define EOT 4
#define ENQ 5
#define ACK 6
#define SO 016
#define SI 017
#define DLE 020
#define XON 021
#define XOFF 023
#define NAK 025
#define CAN 030
#define ESC 033
#define WANTCRC 0103	/* Send C not NAK to get crc not checksum */
#define WANTG 0107	/* Send G not NAK to get nonstop batch xmsn */
#define TIMEOUT (-2)
#define WAICHR '\336'	/* Causes a wait in put[w] and answerback */
#define RETRYMAX 10
#define RETRYNOCRC 4	/* Drop CRC request after 4 retries */
#define KSIZE	1024	/* Block length with k option */

/* Declare all globally used functions not returning int */
char *index(), *cisubstr(), *stem();
char *ascgettime(), *ascgetd(), *ascexpftime();
char **gettoken();
long fseek(), toutime(), time();
unsigned updetime();
FILE *xfopen();

This code has been modified from the Professional-YAM code with
EXT unsigned Etime;		/* Elapsed time in seconds */
EXT struct tsruct calltime;	/* Set when connection is made */
EXT int Zone;			/* Zone in minutes from GMT */
EXT int Thiszone;		/* Timezone to use for this command */

/*
 * Structure passed by e1xpand to called function.
 *  most just use the first string part (sorry, lint!)
 */
#define UFNSIZE 68			/* 64 byte pathlen plus drive */
#define FNLENGTH 15			/* Directory filename */
#define DONOMATCH 0x8000		/* Always expand pathspecs */
struct expf {
	char expfname[UFNSIZE];		/* ASCIZ file name */
	long expflen;			/* File length in bytes */
	unsigned exptime;
	unsigned expdate;
	char expfn[FNLENGTH];		/* Filename only no path */
	char expfattr;			/* File attribute */
};


EXT char Cname[PATHLEN], Rname[PATHLEN], Tname[PATHLEN]; /* Saved filenames */

#define MAXPACK	94		/* Maximum Kermit packet size */
#define RBUFL	200		/* Kermit Receive buffer length */

			/* Some declarations for USQ feature */
#define SQMAGIC 0xFF76		/* SQueezed file prefix */
#define KSQMAGIC 0xFF75		/* Cipher key magic word */
#define KEYSIZE 4096		/* Max size of key file */
#define NUMVALS 257		/* 256 data values plus SPEOF*/

extern union {		/* Decoding tree for usq feature */
	struct {
		char ffxpkt[MAXPACK+8];	/* Far fetch MUST BE FIRST */
		char rxpkt[RBUFL+2];	/* Receive packet buffer */
		char txpkt[MAXPACK+8];	/* Packet buffer */
		char sxpkt[MAXPACK+8];	/* Server command buffer */
		char dxpkt[MAXPACK+8];	/* Packet buffer */
		char axbuf[CMDLEN+2];	/* For CB command, etc. */
		char kfilnam[PATHLEN+2];
	} k;
	char ubuf[KSIZE+2];
	struct {
		int children[2];	/* Left, Right */
	} dnode[NUMVALS - 1];
} U;

EXT FLAG Key;			/* True iff unsqueezing encrypted file */



expanations added for support routines not included.

#include "yamsys.h"		/* Installation specific stuff */
#include "yam.h"
#define WCEOT (-10)

long Modtime;		/* Unix style mod time for incoming file */
int Usemtime;		/* <>0: Accept file mod time */
int Filemode;		/* Unix style mode for incoming file */
char rxcmdchar;		/* NAK, C, or G to specify rx mode */
extern char *Rcmdlog;	/* Remote commands logged to this file */
static FLAG eotseen;	/* <>0 if an EOT has been seen for this rx packet */

wcsend(argc, argp)
char **argp;
{
	int wcs();

	Crcflg = FALSE;
	firstsec = TRUE;
	if (Batch) {
		lhmargin();
		printf("Sending in Batch Mode\n");
/* Expand calls the specified function once for each file, with that
pathname as argument */
		if (expand(wcs, argc, argp, 0) == ERROR)
			goto fubar;
		if (wctxpn("") == ERROR)
			goto fubar;
	}
	else {
		for (; --argc>=0;) {
/* Opentx opens a file for sending - `ala fopen(name, "rb" .. */
			if (opentx(*argp++) == ERROR)
				goto fubar;
			if (wctx() == ERROR)
				goto fubar;
		}
	}
	return OK;
fubar:
	closetx('E'); ++Errcnt;
	canit();
	return ERROR;
}

wcs(ufn)
register struct expf *ufn;
{
	if (opentx(ufn->expfname) == ERROR)
		return OK;		/* skip over inaccessible files */
	if (wctxpn(ufn) ==  ERROR)
		return ERROR;
	if (wctx() == ERROR)
		return ERROR;
	return OK;
}


wcreceive(argc, argp)
char **argp;
{
	rxcmdchar = NAK;
	if (Batch || argc == 0)
		Crcflg = Batch = TRUE;
	if (Crcflg)
		rxcmdchar = WANTCRC;
	if (Optiong > 0)
		Crcflg = rxcmdchar = WANTG;

	if (Batch) {
		lhmargin(); Batch = TRUE;
		printf("Receiving in Batch Mode\n");
		if (argc)
			usage(11);
		for (;;) {
			if (wcrxpn(U.ubuf)== ERROR)
				goto fubar;
			if (U.ubuf[0] == 0) {
				pstat("");  return OK;
			}
			procheader(U.ubuf);

			if (wcrx(U.ubuf) == ERROR)
				goto fubar;
		}
	}
	else {
		procheader(NULL);
		if (Xmodem)
			printf("Receive:'%s' FILE OPEN\n", *argp);
		if (wcrx(*argp) == ERROR)
			goto fubar;
	}
	Bytesleft = DEFBYTL; return OK;
fubar:
	Bytesleft = DEFBYTL; canit(); closerx(eotseen?'Q':ERROR);
	++Errcnt; return ERROR;
}

/*
 * Fetch a pathname from the other end as a C ctyle ASCIZ string.
 * Length is indeterminate as long as less than blklen
 * a null string represents no more files
 */
wcrxpn(rpn)
char *rpn;	/* receive a pathname */
{
	purgeline();
	Crcflg = firstsec = TRUE;
	lpstat("Fetching pathname");
	totsecs = -1;
	if (wcgetsec(rpn, rxcmdchar) != 0)
		return ERROR;
	/* Send the ACK unless g option */
	if (Optiong <= 0)
		sendline(ACK);
	FLUSHMO;
	return OK;
}

wctxpn(ufn)
struct expf *ufn;
{
	register char *p, *q;
	int i;
#ifdef MTIME
	long mdt; struct dstruct d; struct tsruct t;
#endif
	totsecs = -1;
	pstat("Awaiting pathname NAK");
	if (getnak())
		return ERROR;
	for (i = KSIZE, q = U.ubuf;  --i >= 0;  )
		*q++ = 0;
	p = ufn->expfname;
	if (*p) {
#ifdef MTIME
		tdexpftime(&d, &t, ufn);
		mdt = toutime(&d, &t) + Thiszone*60L;
#endif
		if (!Fullpath) {
			p = stem(p);
			uncaps(p);
		}
		for(q = U.ubuf; *p; )	/* don't send drive: */
			if((*q++ = *p++) == ':')
				q = U.ubuf;
		/* transmit file length */
#ifdef MTIME
		sprintf(++q, "%ld %lo 0 %ld", ufn->expflen, mdt, Serialn);
#else
		sprintf(++q, "%ld 0 0 %ld", ufn->expflen, Serialn);
#endif
	}

	if (wcputsec(U.ubuf, 0, SECSIZ) == ERROR) {
		wcperr("Can't send pathname %s", p);
		return ERROR;
	}
	return OK;
}

getnak()
{
	register c;

	Lastrx = 0;
	for (;;) {
		FLUSHMO;
		switch (firstch = readline(400)) {
		case TIMEOUT:
			return TRUE;
		case WANTG:
			Optiong = 1; blklen = 1024;
			/* **** FALL THRU TO **** */
		case WANTCRC:
			Crcflg = TRUE;
			/* **** FALL THRU TO **** */
		case NAK:
			if ((c=readl0()) == TIMEOUT)
				return FALSE;
			if (c == 'K') {		/* IMP hack */
				blklen = 1024; return FALSE;
			}
			/* **** FALL THRU TO **** */
		case CAN:
			if (Lastrx == CAN)
				return TRUE;
			/* **** FALL THRU TO **** */
		default:
			showctl(firstch);
		}
		Lastrx = firstch;
	}
}

/*
 * Adapted from CMODEM13.C, written by
 * Jack M. Wierda and Roderick W. Hart
 */

wcrx(name)
char *name;
{
	register char *p;
	register cblklen;			/* bytes to dump this block */
	int sendchar, sectnum, sectcurr;
	int syntry;			/* number of tries to get right sec */

	if (openrx(name) == ERROR)
		return ERROR;
	firstsec = TRUE; eotseen = FALSE;  totsecs = sectnum = 0;
	sendchar = rxcmdchar;

	for (;;) {
		syntry = 0;		/* # of tries for right sector */
synagain:
		sectcurr = wcgetsec(U.ubuf, sendchar);
		if (sectcurr == (sectnum+1 & 0377)) {
			sectnum++;
/*
 * if the compiler supports longs && the o/s records the
 *  exact length of files then and only then use the file length
 *  info (if transmitted).
 */
			wcj = cblklen = Bytesleft>blklen ? blklen:Bytesleft;

			Charsrx += wcj;
			if (Overlapio) {
				if (Optiong <= 0)
					sendline(ACK);
				sendchar = -1;
			} else
				sendchar=ACK;
			FLUSHMO;
			for (p = U.ubuf; --wcj>=0; )
				if (putc(*p++, fout) == ERROR)
					usage(30);
			if (View) {
				wcj = cblklen;
				for (p = U.ubuf;--wcj>=0;)
					bttyout(*p++);
			}
			if ((Bytesleft -= cblklen) < 0)
				Bytesleft = 0;
			totsecs += blklen/128;
		}
		else if (sectcurr == (sectnum & 0377)) {
			wcperr("Received dup Sector");
			sendchar = ACK;
		}
		else if (sectcurr == WCEOT) {
			sendline(ACK);
			FLUSHMO;
			/* Don't pad the file any more than it already is */
			closerx('R');
#ifdef MTIME
			if (Modtime) {
				sutime(Rname, Modtime - Thiszone*60L);
				Modtime = 0L;
			}
#endif
			return OK;
		}
		else if (sectcurr == ERROR)
			return ERROR;
		else {
			wcperr("Sync Error: got %d", sectcurr);
			if (++syntry < 12) {
				sendchar = Batch ? WANTCRC : NAK;
				goto synagain;
			}
			return ERROR;
		}
	}
}

/*
 * wcgetsec fetches a Ward Christensen type sector.
 * Returns:
 *  sector number encountered
 *  or ERROR if valid sector not received, or CAN CAN received
 *  or WCEOT if eot sector
 ***************** NO ACK IS SENT IF SECTOR IS RECEIVED OK **************
 *    (Caller must do that when he is good and ready to get next sector)
 */

wcgetsec(rxbuf, sendchar)
char *rxbuf;
{
	register char *p;
	register c;
	register sectcurr;
	register maxtim = 100;

	eotseen = FALSE;
	for (Lastrx = errors = 0; errors < RETRYMAX;  ++errors, ++toterrs) {
		if(Ctrlbrk)
			return ERROR;

		if (firstsec && !Batch && errors >= RETRYNOCRC ) {
			sendchar = NAK;  Crcflg = FALSE;
		}
		showsec();
		if (sendchar > 0) {
			sendline(sendchar);	/* Send it now, we're ready! */
		}
		FLUSHMO;
		blklen = SECSIZ;
noisereject:
		switch (c = readline(maxtim)) {
		case CAN:
			if (Lastrx == CAN) {
				wcperr("Sender CANcelled");
				return ERROR;
			} else {
				Lastrx = CAN;
				continue;
			}
		case EOT:
			if (Optiong)
				return WCEOT;
			++eotseen;
			/* make sure eot really is eot and not just mixmash */
			if (readl0() == TIMEOUT) {
				if (!Crcflg || Lastrx == EOT)
					return WCEOT;
				Lastrx = EOT;  goto bilge3;
			}
			goto bilge;
		case STX:
			blklen = KSIZE;
		case SOH:
			sectcurr = readline(50);
			if ((sectcurr+readline(50)) == 0377) {
				checksum = oldcrc = 0;
				for (p = rxbuf,wcj = blklen; --wcj>=0; ) {
					if ((c = readline(50)) < 0)
						goto bilge;
					oldcrc = updcrc(c, oldcrc);
					checksum += (*p++ = c);
				}
				if ((c = readline(50)) < 0)
					goto bilge;
				if (Crcflg) {
					oldcrc = updcrc(c, oldcrc);
					if ((c = readline(50)) < 0)
						goto bilge;
					oldcrc = updcrc(c, oldcrc);
					if (oldcrc) {
						wcperr("Bad CRC=%04x",
						  oldcrc);
						goto bilge2;
					}
					else {
						firstsec = FALSE;
						return sectcurr;
					}
				}
				else if ((checksum-c) & 0377) {
					wcperr("Checksum Bad rx=%02x cx=%02x",
					 c, checksum);
					goto bilge2;
				} else {
					firstsec = FALSE;
					return sectcurr;
				}
			}
			wcperr("Sector number garbled"); goto bilge;
		default:
			if (Crcflg)
				goto noisereject;
		case TIMEOUT:
			break;
		}
bilge:
		switch (c) {
		case TIMEOUT:
			wcperr("Timeout"); goto bilge3;
		case ERROR:  case (ERROR & ~0200):
			Mcstat = 0;
			wcperr("Modem SR=%02x", Mcstat);
			break;
		default:
			wcperr("Got %02x sector header", c);
			break;
		}
bilge2:
		junkpacket();
bilge3:
		if (firstsec)
			sendchar = rxcmdchar;
		else {
			maxtim = 50;
			sendchar = NAK;
		}
	}
	/* try to stop the bubble machine. */
	canit(); return ERROR;
}

/*VARARGS1*/
wcperr(s,p,q)
char *s, *p, *q;
{
	char bufx[70];

	llhmargin();
	sprintf(bufx, s, p, q);
	lprintf("Sector %3d error %d: %s\n", totsecs, errors, bufx);
#ifdef DEBUG2
	if ( !Serialn) {
		logfile(Rcmdlog, bufx, firstch, (long)totsecs);
		if (Xmodem && !carrier())
			logfile(Rcmdlog, "CARRIER LOST", 'C', (long)updetime());
	}
#endif
}

showsec()
{
	if (!Quiet)
		pstat("Sector %3d %2dk %s",
		  totsecs, totsecs/8, Crcflg?"CRC-16":"" );
}

wctx()
{
	register char *p;
	register unsigned sectnum;

	firstsec = TRUE; totsecs = 0;
	pstat("Awaiting initial NAK");

	if (getnak())
		return ERROR;
	sectnum = 1;
	while (filbuf(U.ubuf, blklen)) {
		totsecs += (blklen/128);
		showsec();
		if (wcputsec(U.ubuf, sectnum, blklen) == ERROR) {
#ifdef DEBUG2
			if ( !Serialn)
				logfile(Rcmdlog, "SE1", firstch, Charstx);
#endif
			if (firstch == WANTCRC && sectnum > 1) {
				long lseek();

				if (lseek(fileno(fin), -blklen*2L, 1) == -1L)
					return ERROR;
				--sectnum; Charstx -= blklen;
				totsecs -= (blklen/64);
				wcperr("Resynchronizing");
				continue;
			}
			return ERROR;
		} else {
			if (View)
				for (p = U.ubuf,wcj = blklen;--wcj>=0;)
					bttyout(*p++);
			++sectnum; Charstx += blklen;
		}
	}
	closetx('S');
	for (errors = 0; ++errors<RETRYMAX; ) {
		purgeline();
		sendline(EOT);
		FLUSHMO;
		if (Ctrlbrk)
			break;
		if((firstch=readline(100)) == ACK || firstch == (ACK|0200))
			return OK;
		wcperr("Got %02x for ACK to EOT", firstch);
	}
	wcperr("No ACK on EOT");
	return ERROR;
}

wcputsec(txbuf, sectnum, cseclen)
char *txbuf;
register unsigned sectnum;
int cseclen;	/* data length of this sector to send */
{
	register char *p;
	FLAG nogood;

	firstch = 0;	/* part of logic to detect CAN CAN */

	for (errors = 0; errors < RETRYMAX; ++errors, ++toterrs) {
		nogood = FALSE;
		if (Ctrlbrk)
			goto cancan;
		Lastrx =  firstch;
		sendline(cseclen == KSIZE?STX:SOH);
		sendline(sectnum);
		sendline(-sectnum-1);
		oldcrc = checksum = 0;
		for (wcj = cseclen,p = txbuf; --wcj>=0; ) {
			sendline(*p);
			oldcrc = updcrc(*p, oldcrc);
			checksum += *p++;
			if (miinqueue()) {
				switch (michar()) {
				case CAN:
					goto chkcan;
				case XOFF:
					readline(500);  break;
				default:
					break;
				}
			}
		}
		if (Crcflg) {
			oldcrc = updcrc(0,updcrc(0,oldcrc));
			sendline(oldcrc>>8);sendline(oldcrc);
		}
		else
			sendline(checksum);
		if (Ctrlbrk)
			goto cancan;

		if (miready()) {
			wcperr("Noise Burst Detected");
			if (Optiong > 0)
				goto cancan;
			nogood = TRUE; purgeline();
		}
		if (Optiong) {
			firstsec = FALSE; return OK;
		}
		FLUSHMO;
		firstch = readline(400);
gotnak:
		switch (firstch) {
		case CAN:
chkcan:
			if(Lastrx == CAN) {
cancan:
				wcperr("Receiver Cancelled");  return ERROR;
			}
			break;
		case TIMEOUT:
			wcperr("Timeout on sector ACK"); continue;
		case WANTCRC:
			if (firstsec)
				Crcflg = TRUE;
		case NAK:
			wcperr("NAK on sector");
#ifdef NOTDEF
			/* ******* TESTING ********** */
			{
				extern Quitafter;
				if (Quitafter && Verbose) {
					wcperr("Faking ACK: ALT-Q");
					Quitafter = FALSE; return OK;
				}
			}
#endif
			continue;
		case ACK: 
gotack:
			if (nogood)
				break;
			if (!Tfile || (sectnum==0) || (readl0()==TIMEOUT)) {
				firstsec = FALSE; return OK;
			}
			wcperr("Got burst for sector ACK");
			break;
		case ACK|0200: 
			if ( !Tfile)
				goto gotack;
		default:
			wcperr("Got %02x for sector ACK", firstch); break;
		}
		for (;;) {
			Mcstat = 0;
			Lastrx = firstch;
			if ((firstch = readline(400)) == TIMEOUT)
				break;
			if ((firstch == NAK || firstch == WANTCRC)
			  && readl0() == TIMEOUT)
				goto gotnak;
			if (firstch == CAN && Lastrx == CAN)
				goto cancan;
			/* Let user see it if strange char */
			showctl(firstch);
		}
	}
	wcperr("No ACK on sector");
	return ERROR;
}

/* Throw away incoming packet */
junkpacket()
{
	register n = 3600;
	register c;
	register b;

	b = Ctrlbrk; Ctrlbrk = 0;
	pstat("%sError Recovery", Outarev);
	while (--n) {
		if (cdo()) {
			prcdo(); goto done;
		}
		switch (c = readline(20)) {
		case TIMEOUT:
			goto done;
		case CAN:
		case ETX:
			if (c == readline(10)) {
				b=c; goto done;
			}
		}
	}
done:	Ctrlbrk |= b;  Mcstat = 0;
	pstat("");
}


/*
 *  Send 5 CAN's to try to get the other end to shut up
 *  then 5 backspaces to edit out the CAN's
 */
canit()
{
	register c;

	junkpacket();
	for (c = 5; --c>=0; )
		sendline(CAN);
	for (c = 5; --c>=0; )
		sendline('\b');
	FLUSHMO;
}

/* Fill buf with count chars padding with ^Z for CPM */
filbuf(buf, count)
char *buf;
{
	register m;

#ifdef NOTDEF
	register c;
	m = count;
	while((c=getc(fin))!=EOF) {
		*buf++ =c;
		if(--m == 0)
			break;
	}
	if(m == count)
		return 0;
	else
		while(--m >= 0)
			*buf++ = 032;
	return count;
#else
	m = read(fileno(fin), buf, count);
	if (m <= 0)
		return 0;
	while (m < count)
		buf[m++] = 032;
	return count;
#endif
}


/*
 * Process incoming header
 */
procheader(name)
register char *name;
{
	register char *p;
	long theirsn;
	extern char Instmsg[];

	/* set default parameters */
	Bytesleft = DEFBYTL;  Filemode = 0666; Modtime = 0L;

	if (name) {
		p = name + 1 + strlen(name);
		if (*p) { 	/* file coming from Unix type system */
			sscanf(p, "%ld%lo%o%lo", &Bytesleft,
			 &Modtime, &Filemode, &theirsn);
#ifndef DEMO
			if (Serialn && theirsn == Serialn) {
				Instmsg[0] = 0; usage(26);
			}
#endif
		}
	}
}

/*
 *  Routines to convert to/from Unix(TM) 32 bit time code (epoch:1970).
 *  Good for times 1970 to 2035 approximately, by which time we should
 *  be timestamping files in microseconds since the Big Bang ...
 *
 *  Original factor routine from  Lynn Long
 */

#define EPOCH 719542L

long int factor(mm, dd, yy)
long int mm, dd, yy;
{
	long int raw;	

	raw = 365 * yy + dd -1 + 31 * (mm - 1);
	if(mm < 3)
		raw += ((yy -1)/4);
	else
		raw -= (23 + 4*mm)/10L - yy/4;
	return(raw);
}

#ifdef MTIME


struct dstruct {		/* Structure filled in by msdate() */
	int year;
	char month;
	char day;
};
struct tsruct {			/* Structure filled in by mstod() */
	char hrs;
	char mins;
	char secs;
	char csecs;
};

static dayinmo[]= { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

#define DAYSEC 86400L
#define DAYPYR(y) (((y&03)) ? 365:366)
/*
 * Unpacks Unix format time into dstruct and tsruct structures
 */

unutime(ds, ts, t)
register struct dstruct *ds;
register struct tsruct *ts;
long t;
{
	register int c;
	register long days, rem, y, mo;

	rem = t % DAYSEC;  days = t / DAYSEC;
	ts->csecs = 0;
	ts->secs = rem % 60L; rem /= 60L;
	ts->mins = rem % 60L; ts->hrs = rem/60L;

	for (y=1970; days>=(c=DAYPYR(y)); ++y)
		days -= c;

	dayinmo[1] = c==366 ? 29:28;

	for (mo=0; days>=(c=dayinmo[mo]); ++mo)
		days -= c;
	
	ds->day = days+1; ds->month = mo+1; ds->year = y - 1980;
#ifdef DEBUG
	lprintf("Unutime: da %d mo %d yr %d %d:%d:%d\n",
	 ds->day, ds->month, ds->year, ts->hrs, ts->mins, ts->secs);
#endif
}


/*
 * Packs dstruct and tsruct structures into Unix format time
 */

long
toutime(ds, ts)
register struct dstruct *ds;
register struct tsruct *ts;
{
	register long t;

	t = factor((long)ds->month, (long)ds->day, ds->year + 1980L);
	t -= EPOCH; t *= DAYSEC;
	t += ts->secs; t += ts->mins*60; t += ts->hrs*3600L;
#ifdef DEBUG
	lprintf("Toutime: da %d mo %d yr %d %d:%d:%d %lo\n",
	 ds->day, ds->month, ds->year, ts->hrs, ts->mins, ts->secs, t);
#endif
	return t;
}

/*
 * Sets file s to Unix time t
 */
sutime(s, ti)
char *s;
long ti;
{
	struct dstruct ds;
	struct tsruct ts;
	register unsigned d, t;

	unutime(&ds, &ts, ti);

	d = ds.day + (ds.month << 5) + (ds.year << 9);
	t = (ts.secs >> 1) + (ts.mins << 5) + (ts.hrs << 11);
#ifdef DEBUG
	lprintf("Sutime: d=%x t=%x", d, t);
	lprintf(" da %d mo %d yr %d %d:%d:%d\n",
	 ds.day, ds.month, ds.year, ts.hrs, ts.mins, ts.secs);
#endif
	if (Usemtime)
		utime(s, d, t);
}

/*
 * Unpack time from struct expf to tsruct and dstruct
 */
tdexpftime(ds, ts, e)
register struct expf *e;
register struct dstruct *ds;
register struct tsruct *ts;
{
	ts->hrs = (e->exptime>>11);
	ts->mins = ((e->exptime>>5)&077);
	ts->secs = ((e->exptime&037)<<1);
	ds->month = ((e->expdate>>5)&017);
	ds->day = (e->expdate&037);
	ds->year = ((e->expdate>>9)%100);
#ifdef DEBUG
	lprintf("TDEX.. da %d mo %d yr %d %d:%d:%d\n",
	 ds->day, ds->month, ds->year, ts->hrs, ts->mins, ts->secs);
#endif
}

#endif	/* M T I M E */
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    