home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Club Amiga de Montreal - CAM
/
CAM_CD_1.iso
/
files
/
272.lha
/
XprZmodemLibrary_v1.0
/
send.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-07-30
|
13KB
|
455 lines
/* Send.c: File transmission routines for xprzmodem.library;
Version 1.0, 29 July 1989, by Rick Huebner.
Based closely on Chuck Forsberg's sz.c example ZModem code,
but too pervasively modified to even think of detailing the changes.
Released to the Public Domain; do as you like with this code. */
#include "aztec.h"
#include "xproto.h"
#include "zmodem.h"
#include "defs.h"
#ifdef DEBUG
extern long DebugLog;
#endif
/* Main file transmission routine; called by terminal program */
long XProtocolSend(io)
struct XPR_IO *io;
{
register struct Vars *v;
register short err = FALSE;
/* Perform common setup and initializations */
if (!(v = setup(io))) return 0;
v->Rxtimeout = 300;
/* Transfer the files */
zmputs(v,"rz\r");
stohdr(v,0L);
zshhdr(v,ZRQINIT);
if (getzrxinit(v) == ERROR) upderr(v,"Upload cancelled or timed out");
else sendbatch(v);
/* Clean up and return */
if (err = v->Errcnt) upderr(v,"One or more files skipped due to errors");
else updmsg(v,"Done.");
if (v->io.xpr_setserial) calld(v->io.xpr_setserial,v->Oldstatus);
FreeMem(v->Filebuf,v->Filebufmax);
FreeMem(v,(long)sizeof(struct Vars));
#ifdef DEBUG
if (DebugLog) {
calla(v->io.xpr_fclose,DebugLog);
DebugLog = NULL;
}
#endif
return (err) ? 0 : 1;
}
/* Negotiate with receiver to start a file transfer */
short getzrxinit(v)
register struct Vars *v;
{
register short n;
for (n=10; --n>=0; ) {
switch (zgethdr(v)) {
case ZCHALLENGE: /* Echo receiver's challenge number */
stohdr(v,v->Rxpos);
zshhdr(v,ZACK);
continue;
case ZCOMMAND: /* They didn't see our ZRQINIT; try again */
stohdr(v,0L);
zshhdr(v,ZRQINIT);
continue;
case ZRINIT: /* Receiver ready; get transfer parameters */
v->Rxbuflen = ((USHORT)v->Rxhdr[ZP1]<<8) | v->Rxhdr[ZP0];
#ifdef DEBUG
sprintf(v->Msgbuf, "Rxbuflen=%d Tframlen=%d\n", v->Rxbuflen, v->Tframlen);
dlog(v,v->Msgbuf);
#endif
/* Use shortest of the two side's max frame lengths */
if (v->Tframlen && (!v->Rxbuflen || v->Tframlen < v->Rxbuflen))
v->Rxbuflen = v->Tframlen;
#ifdef DEBUG
sprintf(v->Msgbuf, "Rxbuflen=%d\n", v->Rxbuflen);
dlog(v,v->Msgbuf);
#endif
return OK;
case ZCAN:
case RCDO:
case TIMEOUT:
return ERROR;
case ZRQINIT:
if (v->Rxhdr[ZF0] == ZCOMMAND) continue;
/* fallthrough... */
default:
zshhdr(v,ZNAK);
continue;
}
}
return ERROR;
}
/* Send a batch of files */
void sendbatch(v)
register struct Vars *v;
{
register short single, done = FALSE;
register long fstate;
/* If template routines not provided, must be single filename */
if (!v->io.xpr_ffirst || !v->io.xpr_fnext) {
single = TRUE;
strcpy(v->Filename,v->io.xpr_filename);
/* Else use the template routines to get the first filename */
} else {
single = FALSE;
fstate = callaa(v->io.xpr_ffirst,v->Filename,v->io.xpr_filename);
if (!fstate) {
upderr(v,"No files match template");
return;
}
}
/* If using templates, keep getting names & sending until done */
while (!done) {
if (sendone(v) == ERROR) return;
if (single) break;
fstate = calldaa(v->io.xpr_fnext,fstate,v->Filename,v->io.xpr_filename);
done = !fstate;
}
/* End batch and return; if we never got started, just cancel receiver */
if (v->Filcnt) saybibi(v);
else canit(v);
}
/* Send the file named in v->Filename */
short sendone(v)
register struct Vars *v;
{
#ifdef DEBUG
sprintf(v->Msgbuf, "*** Sending %s\n", v->Filename);
dlog(v,v->Msgbuf);
#endif
/* Display name of file being sent for user */
v->xpru.xpru_updatemask = XPRU_FILENAME;
v->xpru.xpru_filename = (char *)v->Filename;
calla(v->io.xpr_update,&v->xpru);
/* Open the file, if possible */
if (!(v->File = bfopen(v,"r"))) {
++v->Errcnt;
upderr(v,"Can't open file; skipping");
return OK; /* pass over it, there may be others */
}
++v->Filcnt;
v->Starttime = time(NULL);
/* Kick off the file transfer */
switch (sendname(v)) {
case ERROR:
++v->Errcnt;
return ERROR;
case OK:
bfclose(v);
break;
}
return OK;
}
/* Build file info block consisting of file name, length, time, and mode */
short sendname(v)
register struct Vars *v;
{
register UBYTE *p, *q;
/* Initialize term program transfer status display */
v->Fsize = (v->io.xpr_finfo) ? callad(v->io.xpr_finfo,v->Filename,1L) : -1;
v->xpru.xpru_updatemask = XPRU_PROTOCOL | XPRU_FILESIZE | XPRU_MSG | XPRU_BLOCKS |
XPRU_ERRORS | XPRU_TIMEOUTS | XPRU_BLOCKCHECK | XPRU_BYTES |
XPRU_EXPECTTIME | XPRU_ELAPSEDTIME | XPRU_DATARATE;
v->xpru.xpru_protocol = "ZModem";
v->xpru.xpru_filesize = v->Fsize;
v->xpru.xpru_msg = "Sending...";
v->xpru.xpru_blocks = v->xpru.xpru_errors = v->xpru.xpru_timeouts = 0;
v->xpru.xpru_blockcheck = "CRC-16";
v->xpru.xpru_bytes = v->Strtpos = 0;
update_rate(v);
calla(v->io.xpr_update,&v->xpru);
/* Extract outgoing file name; no directory path, lower case */
for (p=v->Filename, q=v->Pktbuf ; *p; ++p, ++q)
if ((*q = tolower(*p)) == '/' || *q == ':') q = v->Pktbuf - 1;
*q = '\0';
p = ++q;
/* Zero out remainder of file info packet */
setmem(q,sizeof(v->Pktbuf) - (q - v->Pktbuf),'\0');
/* Store file size, timestamp, and mode in info packet */
/* XPR spec doesn't provide a way to get the file timestamp or file mode,
so we'll just fake it with the current time and a dummy 0. */
sprintf(p,"%lu %lo %o",(v->Fsize < 0) ? 0L : v->Fsize,time(NULL),0);
/* ^ ^ Yes, octal; Forsberg likes octal, don't ask me why */
/* Send filename packet */
return zsendfile(v,p - v->Pktbuf + strlen(p) + 1);
}
/* Send the filename packet and see if receiver will accept file */
short zsendfile(v,blen)
register struct Vars *v;
short blen;
{
register short c;
while (TRUE) {
v->Txhdr[ZF0] = v->Lzconv; /* Text or Binary mode; from config string */
v->Txhdr[ZF1] = LZMANAG; /* Default file management mode */
v->Txhdr[ZF2] = LZTRANS; /* Default file transport mode */
v->Txhdr[ZF3] = 0;
zsbhdr(v,ZFILE);
zsdata(v,blen,ZCRCW);
again:
switch (c = zgethdr(v)) {
case ZRINIT:
goto again;
case ZCAN:
case ZCRC:
case RCDO:
case TIMEOUT:
case ZABORT:
case ZFIN:
return ERROR;
case ZSKIP: /* Receiver doesn't want this one */
upderr(v,"SKIP command received");
bfclose(v);
return c;
case ZRPOS: /* Receiver wants it; this is starting position */
bfseek(v,v->Rxpos);
v->Strtpos = v->Txpos = v->Rxpos;
/* WARNING: comment out following line if using VLT 4.058 */
if (v->io.xpr_sflush) (*v->io.xpr_sflush)();
return zsendfdata(v);
}
}
}
/* Send the file data */
short zsendfdata(v)
register struct Vars *v;
{
register short c, e, blklen, goodbytes;
USHORT framelen, maxblklen, goodneeded = 512;
/* Figure out max data packet size to send */
maxblklen = KSIZE;
if (v->Rxbuflen && maxblklen > v->Rxbuflen) maxblklen = v->Rxbuflen;
blklen = (v->Baud < 1200) ? 256 : KSIZE;
if (blklen > maxblklen) blklen = maxblklen;
#ifdef DEBUG
sprintf(v->Msgbuf, "Rxbuflen=%d blklen=%d\n", v->Rxbuflen, blklen);
dlog(v,v->Msgbuf);
#endif
/* If an interruption happened, handle it; else keep sending data */
somemore:
if (char_avail(v)) {
waitack:
#ifdef DEBUG
dlog(v,"--- At waitack\n");
#endif
switch (c = getinsync(v)) {
default:
upderr(v,"Transfer cancelled");
bfclose(v);
return ERROR;
case ZSKIP: /* Receiver changed its mind and wants to skip the file */
return c;
case ZACK: /* ACK at end of frame; resume sending data */
break;
case ZRPOS: /* An error; resend data from last good point */
blklen >>= 2;
if (blklen < MINBLOCK) blklen = MINBLOCK;
if (goodneeded < MAXGOODNEEDED) goodneeded <<= 1;
v->xpru.xpru_updatemask = XPRU_ERRORS;
++v->xpru.xpru_errors;
calla(v->io.xpr_update,&v->xpru);
break;
case ZRINIT:
updmsg(v,"Done.");
return OK;
}
/* Check for another incoming packet while discarding line noise */
while (char_avail(v)) {
switch (readock(v,1)) {
case CAN:
case RCDO:
case ZPAD:
goto waitack;
}
}
}
/* Transmit ZDATA frame header */
framelen = v->Rxbuflen;
stohdr(v,v->Txpos);
zsbhdr(v,ZDATA);
/* Keep sending data packets until finished or interrupted */
do {
/* Read next chunk of file data */
c = bfread(v,v->Pktbuf,(long)blklen);
/* Figure out how to handle this data packet */
if (c < blklen) e = ZCRCE; /* If end of file, this is last data packet */
else if (v->Rxbuflen && (framelen -= c) <= 0) e = ZCRCW; /* If end of frame, ask for ACK */
else e = ZCRCG; /* Else tell receiver to expect more data packets */
zsdata(v,c,e); /* Send the packet */
/* Update terminal program status display */
v->xpru.xpru_updatemask = XPRU_BLOCKS | XPRU_BLOCKSIZE | XPRU_BYTES |
XPRU_EXPECTTIME | XPRU_ELAPSEDTIME | XPRU_DATARATE;
++v->xpru.xpru_blocks;
v->xpru.xpru_blocksize = c;
v->xpru.xpru_bytes = v->Txpos += c;
update_rate(v);
calla(v->io.xpr_update,&v->xpru);
/* If we've been sending smaller than normal packets, see if it's
time to bump the packet size up a notch yet */
if (blklen < maxblklen && (goodbytes += c) >= goodneeded) {
blklen <<= 1;
if (blklen > maxblklen) blklen = maxblklen;
goodbytes = 0;
#ifdef DEBUG
sprintf(v->Msgbuf,"Bumping packet size to %d at %ld\n",blklen,v->Txpos);
dlog(v,v->Msgbuf);
#endif
}
/* Give terminal program its timeslice if it needs one */
if (v->io.xpr_chkmisc) (*v->io.xpr_chkmisc)();
/* Check for abort from terminal program */
if (v->io.xpr_chkabort && (*v->io.xpr_chkabort)()) goto aborted;
/* If this was last packet in frame, go wait for ACK from receiver */
if (e == ZCRCW) goto waitack;
/* Check if receiver trying to interrupt us; look for incoming packet
while discarding line noise */
while (char_avail(v)) {
switch (readock(v,1)) {
case CAN:
case RCDO:
case ZPAD:
/* Interruption detected; stop sending and process complaint */
#ifdef DEBUG
dlog(v,"--- Interrupted send\n");
#endif
zsdata(v,0,ZCRCE);
goto waitack;
}
}
} while (e == ZCRCG); /* If no interruption, keep sending data packets */
/* Done sending file data; send EOF and wait for receiver to acknowledge */
while (TRUE) {
updmsg(v,"Sending EOF");
stohdr(v,v->Txpos);
zsbhdr(v,ZEOF);
switch (c = getinsync(v)) {
case ZACK:
continue;
case ZRPOS:
goto somemore;
case ZRINIT:
updmsg(v,"EOF acknowledged");
return OK;
case ZSKIP:
return c;
default:
aborted: upderr(v,"Transfer cancelled");
bfclose(v);
return ERROR;
}
}
}
/* Respond to receiver's complaint, get back in sync with receiver */
short getinsync(v)
register struct Vars *v;
{
register short c;
while (TRUE) {
#ifdef DEBUG
dlog(v,"--- At getinsync\n");
#endif
c = zgethdr(v);
/* WARNING: comment out following line if using VLT 4.058 */
if (v->io.xpr_sflush) (*v->io.xpr_sflush)();
switch (c) {
case ZCAN:
case ZABORT:
case ZFIN:
case RCDO:
case TIMEOUT:
return ERROR;
case ZRPOS:
bfseek(v,v->Rxpos);
v->Txpos = v->Rxpos;
sprintf(v->Msgbuf,"Error reported; resending from byte %ld",v->Txpos);
upderr(v,v->Msgbuf);
return c;
case ZSKIP:
upderr(v,"SKIP command received");
/* fallthrough... */
case ZRINIT:
bfclose(v);
/* fallthrough... */
case ZACK:
return c;
default:
zsbhdr(v,ZNAK);
continue;
}
}
}
/* End of batch transmission; disengage cleanly from receiver */
void saybibi(v)
register struct Vars *v;
{
while (TRUE) {
stohdr(v,0L);
zsbhdr(v,ZFIN);
switch (zgethdr(v)) {
case ZFIN:
sendline(v,'O');
sendline(v,'O');
/* fallthrough... */
case ZCAN:
case RCDO:
case TIMEOUT:
return;
}
}
}