home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Frozen Fish 1: Amiga
/
FrozenFish-Apr94.iso
/
bbs
/
alib
/
d2xx
/
d236
/
xprzmodem.lha
/
XprZmodem
/
utils.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-08-09
|
18KB
|
613 lines
/* Utils.c: Miscellaneous support routines for xprzmodem.library;
Version 1.0, 29 July 1989, by Rick Huebner.
Released to the Public Domain; do as you like with this code. */
#include <exec/memory.h>
#include "aztec.h"
#include "xproto.h"
#include "zmodem.h"
#include "defs.h"
/* Label and version info for .library file */
char XPRname[] = "xprzmodem.library";
char XPRid[] = "xprzmodem 1.0, 29 July 89\r\n";
short XPRrevision = 0; /* Version number is XPRVERSION in rtag.asm */
/* Transfer options to use if XProtocolSetup not called */
char Default_Config[] = "T?,ON,B16,F0";
#ifdef DEBUG
UBYTE DebugName[] = "Log:ZDebug.log";
long DebugLog = NULL;
#endif
/* Called by terminal program to set transfer options */
long XProtocolSetup(io)
register struct XPR_IO *io;
{
UBYTE buf[256], t, o;
register UBYTE *p;
register long len, b, f;
/* Allocate memory for transfer options string */
if (!io->xpr_data) {
io->xpr_data = AllocMem((long)CONFIGLEN,0L);
if (!io->xpr_data) {
ioerr(io,"Not enough memory for ZModem config string");
return 0;
}
/* Start out with default options; merge user changes into defaults */
strcpy(io->xpr_data,Default_Config);
}
/* Extract current settings from options string */
t = *(strchr(io->xpr_data,'T')+1);
o = *(strchr(io->xpr_data,'O')+1);
b = atol(strchr(io->xpr_data,'B')+1);
f = atol(strchr(io->xpr_data,'F')+1);
/* If config string passed by term prog, use it; else prompt user */
if (io->xpr_filename) strcpy(buf,io->xpr_filename);
else {
/* Start buffer with current settings so user can see/edit them in place */
strcpy(buf,io->xpr_data);
if (io->xpr_gets) callaa(io->xpr_gets,"ZModem options:",buf);
}
/* Upshift config string for easier parsing */
for (p=buf; *p; ++p)
*p = toupper(*p);
/* Merge new T(ext) option into current settings if given */
/* "TY" = Force Text mode on,
"TN" = Force Text mode off,
"T?" = Use other end's text mode suggestion (default to binary) */
if (p = strchr(buf,'T')) {
++p;
if (*p == 'Y' || *p == 'N' || *p == '?') t = *p;
else ioerr(io,"Invalid T flag ignored; should be Y, N, or ?");
}
/* Merge new O(verwrite) option into current settings if given */
/* "OY" = Yes, delete old file and replace with new one,
"ON" = No, prevent overwrite by appending ".dup" to avoid name collision,
"OR" = Resume transfer at end of existing file,
"OS" = Skip file if it already exists; go on to next */
if (p = strchr(buf,'O')) {
++p;
if (*p == 'R' && !io->xpr_finfo) ioerr(io,"Can't Resume; xpr_finfo() not supported");
else if (*p == 'Y' || *p == 'N' || *p == 'R' || *p == 'S') o = *p;
else ioerr(io,"Invalid O flag ignored; should be Y, N, R, or S");
}
/* Merge new B(uffer) setting into current settings if given */
/* Size of file I/O buffer in kilobytes */
if (p = strchr(buf,'B')) {
len = atol(++p);
if (len < 1) len = 1;
b = len;
}
/* Merge new F(ramelength) setting into other settings if given */
/* Number of bytes we're willing to send or receive between ACKs.
0 = unlimited; nonstop streaming data */
if (p = strchr(buf,'F')) {
len = atol(++p);
if (len < 0) len = 0;
if (len > 0 && len < MINBLOCK) len = MINBLOCK;
f = len;
}
/* Update config string with new settings */
sprintf(io->xpr_data,"T%c,O%c,B%ld,F%ld",t,o,b,f);
return 1;
}
/* Called by terminal program to give us a chance to clean up before program ends */
long XProtocolCleanup(io)
register struct XPR_IO *io;
{
/* Release config option memory, if any */
if (io->xpr_data) {
FreeMem(io->xpr_data,(long)CONFIGLEN);
io->xpr_data = NULL;
}
return 1;
}
/* Perform setup and initializations common to both Send and Receive routines */
struct Vars *setup(io)
register struct XPR_IO *io;
{
static long bauds[] = { 110,300,1200,2400,4800,9600,19200,38400,38400,57600,76800,115200 };
register struct Vars *v;
register long newstatus;
/* Make sure terminal program supports the required call-back functions */
if (!io->xpr_update) return NULL;
if (!io->xpr_fopen || !io->xpr_fclose || !io->xpr_fread || !io->xpr_fwrite ||
!io->xpr_fseek || !io->xpr_sread || !io->xpr_swrite) {
ioerr(io,"Term prog missing required function(s); see docs");
return NULL;
}
/* Hook in default transfer options if XProtocolSetup wasn't called */
if (!io->xpr_data) {
io->xpr_data = AllocMem((long)CONFIGLEN,0L);
if (!io->xpr_data) {
ioerr(io,"Not enough memory for ZModem config string");
return NULL;
}
strcpy(io->xpr_data,Default_Config);
}
/* Allocate memory for our unshared variables, to provide reentrancy */
if (!(v = AllocMem((long)sizeof(struct Vars),MEMF_CLEAR))) {
nomem:
ioerr(io,"Not enough memory for xprzmodem");
return NULL;
}
/* Allocate memory for our file I/O buffer; if we can't get as much as
requested, keep asking for less until we hit minimum before giving up */
v->Filebufmax = atol(strchr(io->xpr_data,'B')+1) * 1024;
while (!(v->Filebuf = AllocMem(v->Filebufmax,0L))) {
if (v->Filebufmax > 1024) v->Filebufmax -= 1024;
else {
FreeMem(v,(long)sizeof(struct Vars));
goto nomem;
}
}
/* Copy caller's io struct into our Vars for easier passing */
v->io = *io;
#ifdef DEBUG
if (!DebugLog) DebugLog = callaa(v->io.xpr_fopen,DebugName,"w");
#endif
/* Initialize Vars as required */
switch(*(strchr(io->xpr_data,'T')+1)) {
case 'Y':
v->Rxascii = TRUE;
v->Rxbinary = FALSE;
v->Lzconv = ZCNL;
break;
case 'N':
v->Rxascii = FALSE;
v->Rxbinary = TRUE;
v->Lzconv = ZCBIN;
break;
case '?':
v->Rxascii = v->Rxbinary = FALSE;
v->Lzconv = 0;
break;
}
v->Tframlen = atol(strchr(io->xpr_data,'F')+1);
/* Get baud rate; set serial port mode if necessary (and possible) */
if (v->io.xpr_setserial) {
v->Oldstatus = calld(v->io.xpr_setserial,-1L);
/* ZModem requires 8 data bits, no parity (full transparency),
leave other settings alone */
newstatus = v->Oldstatus & 0xFFFFE0BC;
/* newstatus |= on_flags; */
if (newstatus != v->Oldstatus) calld(v->io.xpr_setserial,newstatus);
v->Baud = bauds[(newstatus>>16) & 0xFF];
#ifdef DEBUG
sprintf(v->Msgbuf,"Old serial status = %lx, new = %lx, baud = %ld\n",v->Oldstatus,newstatus,v->Baud);
dlog(v,v->Msgbuf);
#endif
/* If no xpr_setserial(), muddle along with most likely guess */
} else v->Baud = 2400;
return v;
}
/* send cancel string to get the other end to shut up */
void canit(v)
register struct Vars *v;
{
static char canistr[] = { 24,24,24,24,24,24,24,24,24,24,8,8,8,8,8,8,8,8,8,8,0 };
zmputs(v,canistr);
}
/* Send a string to the modem, with processing for \336 (sleep 1 sec)
and \335 (break signal, ignored since XPR spec doesn't support it) */
void zmputs(v,s)
register struct Vars *v;
register UBYTE *s;
{
register short c;
while (*s) {
switch (c = *s++) {
case '\336':
TimeOut(50L);
case '\335':
break;
default:
sendline(v,c);
}
}
}
/* Write one character to the modem */
void xsendline(v,c)
register struct Vars *v;
UWORD c;
{
UBYTE buf = c;
callad(v->io.xpr_swrite,&buf,1L);
}
/* Get a byte from the modem;
return TIMEOUT if no read within timeout tenths of a second,
return RCDO if carrier lost (supposedly; XPR spec doesn't support carrier detect, though).
Added in some buffering so we wouldn't hammer the system with single-byte
serial port reads. Also, the buffering makes char_avail() a lot easier to implement. */
short readock(v,tenths)
register struct Vars *v;
short tenths;
{
/* If there's data waiting in the buffer, return next byte */
if (v->Modemcount) {
gotdata:
--v->Modemcount;
return *v->Modemchar++;
}
/* Buffer is empty; try to read more data into it */
v->Modemcount = calladd(v->io.xpr_sread,v->Modembuf,(long)sizeof(v->Modembuf),tenths*100000L);
if (v->Modemcount < 1) { /* Didn't get anything within time limit; timeout */
v->Modemcount = 0;
return TIMEOUT;
} else { /* Got something; return first byte of it */
v->Modemchar = v->Modembuf; /* Reset buffer pointer to start of data */
goto gotdata;
}
}
/* Check if there's anything available to read from the modem */
char char_avail(v)
register struct Vars *v;
{
if (v->Modemcount) return TRUE;
/* No data in our buffer; check system's input buffer */
v->Modemcount = calladd(v->io.xpr_sread,v->Modembuf,(long)sizeof(v->Modembuf),0L);
if (v->Modemcount < 1) { /* Nothing in system buffer either */
v->Modemcount = 0;
return FALSE;
} else { /* System buffer had something waiting for us */
v->Modemchar = v->Modembuf;
return TRUE;
}
}
/* Update the elapsed time, expected total time, and effective data
transfer rate values for status display */
void update_rate(v)
register struct Vars *v;
{
static char *timefmt = "%2d:%02d:%02d";
register long sent, elapsed, expect;
register short hr, min;
/* Compute effective data rate so far in characters per second */
sent = v->xpru.xpru_bytes - v->Strtpos; /* Actual number of chars transferred */
elapsed = time(NULL) - v->Starttime; /* Time it took to send them */
if (elapsed < 1) elapsed = 1;
/* If we haven't transferred anything yet (just starting), make reasonable
guess (95% throughput); otherwise, compute actual effective transfer rate */
v->xpru.xpru_datarate = (sent) ? sent / elapsed : v->Baud * 95L / 1000;
/* Compute expected total transfer time based on data rate so far */
if (v->xpru.xpru_filesize < 0) expect = 0; /* Don't know filesize; display time=0 */
else expect = (v->xpru.xpru_filesize - v->Strtpos) / v->xpru.xpru_datarate;
hr = expect / (60*60); /* How many whole hours */
expect -= hr * (60*60); /* Remainder not counting hours */
min = expect / 60; /* How many whole minutes */
expect -= min * 60; /* Remaining seconds */
sprintf(v->Msgbuf,timefmt,hr,min,(short)expect);
v->xpru.xpru_expecttime = (char *)v->Msgbuf;
/* Compute elapsed time for this transfer so far */
hr = elapsed / (60*60);
elapsed -= hr * (60*60);
min = elapsed / 60;
elapsed -= min * 60;
sprintf(v->Msgbuf+20,timefmt,hr,min,(short)elapsed);
v->xpru.xpru_elapsedtime = (char *)v->Msgbuf+20;
}
/* Buffered file I/O fopen() interface routine */
long bfopen(v,mode)
register struct Vars *v;
UBYTE *mode;
{
/* Initialize file-handling variables */
v->Filebufpos = v->Filebuflen = v->Filebufcnt = 0;
v->Fileflush = FALSE;
v->Filebufptr = v->Filebuf;
/* Open the file */
return callaa(v->io.xpr_fopen,v->Filename,mode);
}
/* Buffered file I/O fclose() interface routine */
void bfclose(v)
register struct Vars *v;
{
/* If bfwrite() left data lingering in buffer, flush it out before closing */
if (v->Fileflush) calladda(v->io.xpr_fwrite,v->Filebuf,1L,v->Filebufcnt,v->File);
/* Close the file */
calla(v->io.xpr_fclose,v->File);
v->File = NULL;
}
/* Buffered file I/O fseek() interface routine */
void bfseek(v,pos)
register struct Vars *v;
register long pos;
{
register long offset;
/* If new file position is within currently buffered section, reset pointers */
if (pos >= v->Filebufpos && pos <= v->Filebufpos + v->Filebuflen - 1) {
offset = pos - v->Filebufpos;
v->Filebufptr = v->Filebuf + offset;
v->Filebufcnt = v->Filebuflen - offset;
/* Otherwise, fseek() file and discard buffer contents to force new read */
} else {
calladd(v->io.xpr_fseek,v->File,pos,0L);
v->Filebuflen = v->Filebufcnt = 0;
v->Filebufpos = pos;
}
}
/* Buffered file I/O fread() interface routine */
long bfread(v,buf,length)
register struct Vars *v;
UBYTE *buf;
register long length;
{
register long count, total = 0;
/* If there's already data buffered up, try to get what we need from there */
if (v->Filebufcnt) {
readmore:
count = (length <= v->Filebufcnt) ? length : v->Filebufcnt;
CopyMem(v->Filebufptr,buf,count);
#ifdef DEBUG
sprintf(v->Msgbuf,"bfread got %ld bytes from buffer\n",count);
dlog(v,v->Msgbuf);
#endif
total += count;
v->Filebufptr += count;
v->Filebufcnt -= count;
}
/* If there wasn't enough in the buffer, read next buffer's worth and try again */
if (total < length) {
v->Filebufpos += v->Filebuflen;
v->Filebufptr = v->Filebuf;
v->Filebufcnt = v->Filebuflen = calladda(v->io.xpr_fread,v->Filebuf,1L,v->Filebufmax,v->File);
#ifdef DEBUG
sprintf(v->Msgbuf,"bfread read %ld bytes\n",v->Filebuflen);
dlog(v,v->Msgbuf);
#endif
if (v->Filebufcnt) goto readmore;
/* else we couldn't read as much as requested; return partial count */
}
return total;
}
/* Buffered file I/O fwrite() interface routine */
long bfwrite(v,buf,length)
register struct Vars *v;
register UBYTE *buf;
register long length;
{
register long count, total = 0;
/* Keep going until entire request completed */
while (length > 0) {
/* Copy as much as will fit into the buffer */
count = v->Filebufmax - v->Filebufcnt;
if (length < count) count = length;
CopyMem(buf,v->Filebufptr,count);
#ifdef DEBUG
sprintf(v->Msgbuf,"bfwrite buffered %ld bytes\n",count);
dlog(v,v->Msgbuf);
#endif
buf += count;
total += count;
length -= count;
v->Filebufptr += count;
v->Filebufcnt += count;
v->Fileflush = TRUE;
/* If we've filled the buffer, write it out */
if (v->Filebufcnt == v->Filebufmax) {
count = calladda(v->io.xpr_fwrite,v->Filebuf,1L,v->Filebufcnt,v->File);
#ifdef DEBUG
sprintf(v->Msgbuf,"bfwrite wrote %ld bytes\n",count);
dlog(v,v->Msgbuf);
#endif
if (count < v->Filebufcnt) return -1;
v->Filebufptr = v->Filebuf;
v->Filebufcnt = 0;
v->Fileflush = FALSE;
}
}
return total;
}
/* Have the terminal program display an error message for us, using a
temporary XPR_UPDATE structure; used to display errors before Vars
gets allocated */
void ioerr(io,msg)
register struct XPR_IO *io;
char *msg;
{
struct XPR_UPDATE xpru;
if (io->xpr_update) {
xpru.xpru_updatemask = XPRU_ERRORMSG;
xpru.xpru_errormsg = msg;
calla(io->xpr_update,&xpru);
}
}
/* Have the terminal program display an error message for us, using the
normal XPR_IO structure allocated in Vars */
void upderr(v,msg)
register struct Vars *v;
char *msg;
{
v->xpru.xpru_updatemask = XPRU_ERRORMSG;
v->xpru.xpru_errormsg = msg;
calla(v->io.xpr_update,&v->xpru);
#ifdef DEBUG
dlog(v,msg);
dlog(v,"\n");
#endif
}
/* Have the terminal program display a normal message for us */
void updmsg(v,msg)
register struct Vars *v;
char *msg;
{
v->xpru.xpru_updatemask = XPRU_MSG;
v->xpru.xpru_msg = msg;
calla(v->io.xpr_update,&v->xpru);
#ifdef DEBUG
dlog(v,msg);
dlog(v,"\n");
#endif
}
/* Figure out how many bytes are free on the drive we're uploading to.
Stubbed out for now; not supported by XPR spec. */
long getfree() {
return 0x7FFFFFFF;
}
/* Check whether file already exists; used to detect potential overwrites */
char exist(v)
register struct Vars *v;
{
register long file;
file = callaa(v->io.xpr_fopen,v->Filename,"r");
if (file) {
calla(v->io.xpr_fclose,file);
return TRUE;
} else return FALSE;
}
#ifdef DEBUG
/* Write a message to the debug log */
dlog(v,s)
register struct Vars *v;
register UBYTE *s;
{
/* Open the debug log if it isn't already open */
if (!DebugLog) DebugLog = callaa(v->io.xpr_fopen,DebugName,"a");
calladda(v->io.xpr_fwrite,s,1L,(long)strlen(s),DebugLog);
/* Close file to flush output buffer; comment these two lines out if
you aren't crashing your system and don't mind waiting until the
transfer finishes to look at your log file. */
calla(v->io.xpr_fclose,DebugLog);
DebugLog = NULL;
}
#endif
/**
*
* The following functions setup the proper registers for the call-back
* functions.
*
**/
#asm
public _calla
_calla:
movea.l 8(sp),a0 ; Second argument goes in a0
; Clever trick to allow indirect JSR without using register
move.l 4(sp),-(sp) ; Push address of function to call
rts ; "Return" to new function; its rts will...
; ...return to function who called us
public _callaa
_callaa:
movea.l 8(sp),a0 ; Second argument goes in a0
movea.l 12(sp),a1 ; Third argument goes in a1
move.l 4(sp),-(sp) ; First argument is function
rts
public _callad
_callad:
movea.l 8(sp),a0 ; Second argument goes in a0
move.l 12(sp),d0 ; Third argument goes in d0
move.l 4(sp),-(sp) ; First argument is function
rts
public _calladd
_calladd:
movea.l 8(sp),a0 ; Second argument goes in a0
move.l 12(sp),d0 ; Third argument goes in d0
move.l 16(sp),d1 ; Fourth argument goes in d1
move.l 4(sp),-(sp) ; First argument is function
rts
public _calladda
_calladda:
movea.l 8(sp),a0 ; Second argument goes in a0
move.l 12(sp),d0 ; Third argument goes in d0
move.l 16(sp),d1 ; Fourth argument goes in d1
movea.l 20(sp),a1 ; Fifth argument goes in a1
move.l 4(sp),-(sp) ; First argument is function
rts
public _calld
_calld:
move.l 8(sp),d0 ; Second argument goes in d0
move.l 4(sp),-(sp) ; First argument is function
rts
public _calldaa
_calldaa:
move.l 8(sp),d0 ; Second argument goes in d0
movea.l 12(sp),a0 ; Third argument goes in a0
movea.l 16(sp),a1 ; Fourth argument goes in a1
move.l 4(sp),-(sp) ; First argument is function
rts
#endasm