home *** CD-ROM | disk | FTP | other *** search
- /* 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
-