home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Club Amiga de Montreal - CAM
/
CAM_CD_1.iso
/
files
/
485.lha
/
xprzmodem.library_v2.10
/
send.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-03-09
|
14KB
|
508 lines
/* Send.c: File transmission routines for xprzmodem.library;
Version 2.10, 12 February 1991, 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 <proto/all.h>
#include <exec/types.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include "xproto.h"
#include "zmodem.h"
#include "xprzmodem.h"
#ifdef DEBUGLOG
extern void *DebugLog;
#endif
/* Main file transmission routine; called by comm program */
long __saveds XProtocolSend(struct XPR_IO *io) {
struct Vars *v;
short err;
/* Perform common setup and initializations */
if (!(v = setup(io)))
return XPRS_FAILURE;
v->Rxtimeout = 600;
/* Transfer the files */
zmputs(v,"rz\r");
stohdr(v,0L);
zshhdr(v,ZRQINIT);
sendbuf(v);
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 && v->Oldstatus != -1)
xpr_setserial(&v->io,v->Oldstatus);
FreeMem(v->Filebuf,v->Filebufmax);
FreeMem(v,(long)sizeof(struct Vars));
#ifdef DEBUGLOG
if (DebugLog) {
xpr_fclose(&v->io,DebugLog);
DebugLog = NULL;
}
#endif
return (err) ? XPRS_FAILURE : XPRS_SUCCESS;
}
/* Negotiate with receiver to start a file transfer */
short getzrxinit(struct Vars *v) {
short n;
for (n=v->ErrorLimit; --n>=0; ) {
/* Check for abort from comm program */
if (v->io.xpr_chkabort && xpr_chkabort(&v->io))
return ERROR;
switch (zgethdr(v)) {
case ZCHALLENGE: /* Echo receiver's challenge number */
stohdr(v,v->Rxpos);
zshhdr(v,ZACK);
sendbuf(v);
continue;
case ZCOMMAND: /* They didn't see our ZRQINIT; try again */
stohdr(v,0L);
zshhdr(v,ZRQINIT);
sendbuf(v);
continue;
case ZRINIT: /* Receiver ready; get transfer parameters */
v->Rxbuflen = ((USHORT)v->Rxhdr[ZP1]<<8) | v->Rxhdr[ZP0];
#ifdef DEBUGLOG
sprintf(v->Msgbuf,"Rxbuflen=%ld Tframlen=%ld\n",(long)v->Rxbuflen,(long)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 DEBUGLOG
sprintf(v->Msgbuf,"Rxbuflen=%ld\n",(long)v->Rxbuflen);
dlog(v,v->Msgbuf);
#endif
return OK;
case ZCAN:
case RCDO:
case TIMEOUT:
upderr(v,v->Msgbuf);
return ERROR;
case ZRQINIT:
if (v->Rxhdr[ZF0] == ZCOMMAND) continue;
/* fallthrough... */
default:
zshhdr(v,ZNAK);
sendbuf(v);
continue;
}
}
return ERROR;
}
/* Send a batch of files */
void sendbatch(struct Vars *v) {
UBYTE single, done = FALSE;
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 = xpr_ffirst(&v->io,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 = xpr_fnext(&v->io,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(struct Vars *v) {
struct SetupVars *sv;
#ifdef DEBUGLOG
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 = v->Filename;
xpr_update(&v->io,&v->xpru);
/* Set text/binary mode according to options before opening file */
set_textmode(v);
/* 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;
GetSysTime(&v->Starttime);
/* Kick off the file transfer */
sv = (void *)v->io.xpr_data;
switch (sendname(v)) {
case ERROR:
++v->Errcnt;
return ERROR;
case OK:
bfclose(v);
/* File sent; if option DY, delete file after sending */
if (*sv->option_d == 'Y' && v->io.xpr_extension >= 2 && v->io.xpr_unlink) {
updmsg(v,"Deleting file after send");
xpr_unlink(&v->io,v->Filename);
}
break;
}
return OK;
}
/* Build file info block consisting of file name, length, time, and mode */
short sendname(struct Vars *v) {
struct SetupVars *sv;
UBYTE *p, *q, buff[32];
/* Initialize comm program transfer status display */
v->Fsize = (v->io.xpr_finfo) ? xpr_finfo(&v->io,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 = (v->Lzconv == ZCNL) ? "Sending text file..." :
( (v->Lzconv == ZCBIN) ? "Sending binary file..." : "Sending file...");
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);
xpr_update(&v->io,&v->xpru);
sv = (void *)v->io.xpr_data;
if (*sv->option_s == 'Y') {
/* If "SY" option selected, send full path */
strcpy(v->Pktbuf,v->Filename);
p = v->Pktbuf + strlen(v->Pktbuf) + 1;
} else {
/* else extract outgoing file name without directory path */
for (p=v->Filename, q=v->Pktbuf ; *p; ++p, ++q)
if ((*q = *p) == '/' || *q == ':') q = v->Pktbuf - 1;
*q = '\0';
p = ++q;
}
/* Zero out remainder of file info packet */
memset(p,0,sizeof(v->Pktbuf) - (p - v->Pktbuf));
/* 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. */
stcl_o(buff,GetSysTime(NULL)+UnixTimeOffset);
/* amiga.lib sprintf() can't do %lo format, so we do it the hard way */
/* Yes, octal; ZModem was originally done on Unix, and they like octal there */
sprintf(p,"%ld %s 0",(v->Fsize < 0) ? 0L : v->Fsize,buff);
/* Send filename packet */
return zsendfile(v,(short)(p - v->Pktbuf + strlen(p) + 1));
}
/* Send the filename packet and see if receiver will accept file */
short zsendfile(struct Vars *v,short blen) {
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);
sendbuf(v);
again:
/* Check for abort from comm program */
if (v->io.xpr_chkabort && xpr_chkabort(&v->io)) {
bfclose(v);
return ERROR;
}
switch (c = zgethdr(v)) {
case ZRINIT:
goto again;
case ZCAN:
case ZCRC:
case RCDO:
case TIMEOUT:
case ZABORT:
case ZFIN:
upderr(v,v->Msgbuf);
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;
if (v->io.xpr_sflush) xpr_sflush(&v->io);
v->Modemcount = 0;
return zsendfdata(v);
}
}
}
/* Send the file data */
short zsendfdata(struct Vars *v) {
short c, e, blklen, goodbytes = 0;
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 DEBUGLOG
sprintf(v->Msgbuf,"Rxbuflen=%ld blklen=%ld\n",(long)v->Rxbuflen,(long)blklen);
dlog(v,v->Msgbuf);
#endif
/* If an interruption happened, handle it; else keep sending data */
somemore:
while (char_avail(v)) {
/* Check for another incoming packet while discarding line noise */
switch (readock(v,1)) {
case CAN:
case RCDO:
case ZPAD:
break;
default:
continue;
}
waitack:
#ifdef DEBUGLOG
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;
xpr_update(&v->io,&v->xpru);
break;
case ZRINIT:
updmsg(v,"Done.");
return OK;
}
}
/* 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 */
sendbuf(v);
/* Update comm 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);
xpr_update(&v->io,&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 DEBUGLOG
sprintf(v->Msgbuf,"Bumping packet size to %ld at %ld\n",(long)blklen,v->Txpos);
dlog(v,v->Msgbuf);
#endif
}
/* Give comm program its timeslice if it needs one */
if (v->io.xpr_chkmisc) xpr_chkmisc(&v->io);
/* Check for abort from comm program */
if (v->io.xpr_chkabort && xpr_chkabort(&v->io)) 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 DEBUGLOG
dlog(v,"--- Interrupted send\n");
#endif
zsdata(v,0,ZCRCE);
sendbuf(v);
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);
sendbuf(v);
switch (c = getinsync(v)) {
case ZACK:
continue;
case ZRPOS:
goto somemore;
case ZRINIT:
updmsg(v,"EOF acknowledged");
++v->Starttime.tv_secs;
update_rate(v);
v->xpru.xpru_updatemask = XPRU_EXPECTTIME | XPRU_ELAPSEDTIME | XPRU_DATARATE;
xpr_update(&v->io,&v->xpru);
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(struct Vars *v) {
short c;
while (TRUE) {
#ifdef DEBUGLOG
dlog(v,"--- At getinsync\n");
#endif
c = zgethdr(v);
if (v->io.xpr_sflush)
xpr_sflush(&v->io);
v->Modemcount = 0;
switch (c) {
case ZCAN:
case ZABORT:
case ZFIN:
case RCDO:
case TIMEOUT:
upderr(v,v->Msgbuf);
return ERROR;
case ZRPOS:
bfseek(v,v->Rxpos);
v->Txpos = v->Rxpos;
sprintf(v->Msgbuf,"Resending from %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);
sendbuf(v);
continue;
}
}
}
/* End of batch transmission; disengage cleanly from receiver */
void saybibi(struct Vars *v) {
while (TRUE) {
stohdr(v,0L);
zsbhdr(v,ZFIN);
sendbuf(v);
switch (zgethdr(v)) {
case ZFIN:
sendline(v,'O');
sendline(v,'O');
sendbuf(v);
/* fallthrough... */
case ZCAN:
case RCDO:
case TIMEOUT:
return;
}
}
}