/* Utils.c: Miscellaneous support routines for xprzmodem.library; Version 2.0, 28 October 1989, by Rick Huebner. Released to the Public Domain; do as you like with this code. */ #include #include #include #include #include #include #include #include "xproto.h" #include "zmodem.h" #include "xprzmodem.h" /* Transfer options to use if XProtocolSetup not called */ struct SetupVars Default_Config = { NULL, NULL, 0, { "C" }, { "N" }, { "16" }, { "0" }, { "N" }, { "N" }, { "Y" }, { "N" }, { "Y" }, { "" } }; #ifdef DEBUGLOG UBYTE DebugName[] = "Log:ZDebug.log"; void *DebugLog = NULL; #endif /* Called by comm program to set transfer options */ long __saveds XProtocolSetup(struct XPR_IO *io) { struct SetupVars *sv, tempsv; struct xpr_option *option_ptrs[11], *optr, xo_hdr, xo_t, xo_o, xo_b, xo_f, xo_s, xo_r, xo_a, xo_d, xo_k, xo_p; UBYTE buf[256], *p; long i, len; /* Allocate memory for transfer options string */ if (!(sv = io->xpr_data)) { io->xpr_data = AllocMem((long)sizeof(struct SetupVars),MEMF_CLEAR); if (!(sv = io->xpr_data)) { ioerr(io,"Not enough memory"); return XPRS_FAILURE; } /* Start out with default options; merge user changes into defaults */ *sv = Default_Config; } /* If options string passed by comm prog, use it; else prompt user */ if (io->xpr_filename) strcpy(buf,io->xpr_filename); else { /* If xpr_options() implemented by comm program, use it */ if (io->xpr_extension >= 1 && io->xpr_options) { /* Let user edit temp copy of options so we can ignore invalid entries */ /* Have to init all this crud the hard way 'cause it's got to be on the stack in order to maintain reentrancy */ tempsv = *sv; xo_hdr.xpro_description = "ZModem options:"; xo_hdr.xpro_type = XPRO_HEADER; xo_hdr.xpro_value = NULL; xo_hdr.xpro_length = 0; option_ptrs[0] = &xo_hdr; xo_t.xpro_description = "Text mode (Y,N,?,C):"; xo_t.xpro_type = XPRO_STRING; xo_t.xpro_value = tempsv.option_t; xo_t.xpro_length = sizeof(tempsv.option_t); option_ptrs[1] = &xo_t; xo_o.xpro_description = "Overwrite mode (Y,N,R,S):"; xo_o.xpro_type = XPRO_STRING; xo_o.xpro_value = tempsv.option_o; xo_o.xpro_length = sizeof(tempsv.option_o); option_ptrs[2] = &xo_o; xo_b.xpro_description = "I/O buffer size (KB):"; xo_b.xpro_type = XPRO_LONG; xo_b.xpro_value = tempsv.option_b; xo_b.xpro_length = sizeof(tempsv.option_b); option_ptrs[3] = &xo_b; xo_f.xpro_description = "Frame size (bytes):"; xo_f.xpro_type = XPRO_LONG; xo_f.xpro_value = tempsv.option_f; xo_f.xpro_length = sizeof(tempsv.option_f); option_ptrs[4] = &xo_f; xo_a.xpro_description = "Auto-activate receiver:"; xo_a.xpro_type = XPRO_BOOLEAN; xo_a.xpro_value = tempsv.option_a; xo_a.xpro_length = sizeof(tempsv.option_a); option_ptrs[5] = &xo_a; xo_d.xpro_description = "Delete after sending:"; xo_d.xpro_type = XPRO_BOOLEAN; xo_d.xpro_value = tempsv.option_d; xo_d.xpro_length = sizeof(tempsv.option_d); option_ptrs[6] = &xo_d; xo_k.xpro_description = "Keep partial files:"; xo_k.xpro_type = XPRO_BOOLEAN; xo_k.xpro_value = tempsv.option_k; xo_k.xpro_length = sizeof(tempsv.option_k); option_ptrs[7] = &xo_k; xo_s.xpro_description = "Send full path:"; xo_s.xpro_type = XPRO_BOOLEAN; xo_s.xpro_value = tempsv.option_s; xo_s.xpro_length = sizeof(tempsv.option_s); option_ptrs[8] = &xo_s; xo_r.xpro_description = "Use received path:"; xo_r.xpro_type = XPRO_BOOLEAN; xo_r.xpro_value = tempsv.option_r; xo_r.xpro_length = sizeof(tempsv.option_r); option_ptrs[9] = &xo_r; xo_p.xpro_description = "Default receive path:"; xo_p.xpro_type = XPRO_STRING; xo_p.xpro_value = tempsv.option_p; xo_p.xpro_length = sizeof(tempsv.option_p); option_ptrs[10] = &xo_p; /* Convert Y/N used elsewhere into "yes"/"no" required by spec */ for (i=5; i<=9; ++i) { optr = option_ptrs[i]; strcpy(optr->xpro_value,(*optr->xpro_value == 'Y') ? "yes" : "no"); } (*io->xpr_options)(11L,option_ptrs); /* Convert "yes"/"no" or "on"/"off" into Y/N */ for (i=5; i<=9; ++i) { optr = option_ptrs[i]; strcpy(optr->xpro_value,(!stricmp(optr->xpro_value,"yes") || !stricmp(optr->xpro_value,"on")) ? "Y" : "N"); } /* Convert xpr_options() results into parseable options string */ sprintf(buf,"T%s,O%s,B%s,F%s,A%s,D%s,K%s,S%s,R%s,P%s",tempsv.option_t,tempsv.option_o,tempsv.option_b,tempsv.option_f, tempsv.option_a,tempsv.option_d,tempsv.option_k,tempsv.option_s,tempsv.option_r,tempsv.option_p); /* If xpr_options() not provided, try xpr_gets() instead */ } else { /* Start buffer with current settings so user can see/edit them in place */ sprintf(buf,"T%s,O%s,B%s,F%s,A%s,D%s,K%s,S%s,R%s,P%s",sv->option_t,sv->option_o,sv->option_b,sv->option_f, sv->option_a,sv->option_d,sv->option_k,sv->option_s,sv->option_r,sv->option_p); if (io->xpr_gets) (*io->xpr_gets)("ZModem options:",buf); } } /* Upshift options string for easier parsing */ strupr(buf); /* 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) "TC" = Ask Comm program for file type */ if (p = find_option(buf,'T')) { if (*p == 'Y' || *p == 'N' || *p == '?' || *p == 'C') *sv->option_t = *p; else ioerr(io,"Invalid T flag ignored; should be Y, N, ?, or C"); } /* 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 = find_option(buf,'O')) { 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') *sv->option_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 = find_option(buf,'B')) { len = atol(p); if (len < 1) len = 1; sprintf(sv->option_b,"%ld",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 = find_option(buf,'F')) { len = atol(p); if (len < 0) len = 0; if (len > 0 && len < MINBLOCK) len = MINBLOCK; sprintf(sv->option_f,"%ld",len); } /* Merge new A(uto-activate) setting into other settings if given */ /* "AY" = Automatically call XProtocolReceive() if ZRQINIT string received "AN" = Don't look for ZRQINIT; user will explicitly activate receive */ if (p = find_option(buf,'A')) { if (*p == 'Y' || *p == 'N') *sv->option_a = *p; else ioerr(io,"Invalid A flag ignored; should be Y or N"); } /* Merge new D(elete after sending) setting into other options */ /* "DY" = Delete files after successfully sending them "DN" = Don't delete files after sending */ if (p = find_option(buf,'D')) { if (*p == 'Y' && (io->xpr_extension < 2 || !io->xpr_unlink)) ioerr(io,"Can't use DY; xpr_unlink() not supported"); else if (*p == 'Y' || *p == 'N') *sv->option_d = *p; else ioerr(io,"Invalid D flag ignored; should be Y or N"); } /* Merge new K(eep partial files) setting into other options */ /* "KY" = Keep partially-received file fragments to allow later resumption "KN" = Delete partially-received file fragments */ if (p = find_option(buf,'K')) { if (*p == 'N' && (io->xpr_extension < 2 || !io->xpr_unlink)) ioerr(io,"Can't use KN; xpr_unlink() not supported"); else if (*p == 'Y' || *p == 'N') *sv->option_k = *p; else ioerr(io,"Invalid K flag ignored; should be Y or N"); } /* Merge new S(end full path) setting into other options */ /* "SY" = Send full filename including directory path to receiver "SN" = Send only simple filename portion, not including directory path */ if (p = find_option(buf,'S')) { if (*p == 'Y' || *p == 'N') *sv->option_s = *p; else ioerr(io,"Invalid S flag ignored; should be Y or N"); } /* Merge new R(eceive path) setting into other options */ /* "RY" = Use full filename exactly as received; don't use P option path "RN" = Ignore received directory path if any; use path from P option */ if (p = find_option(buf,'R')) { if (*p == 'Y' || *p == 'N') *sv->option_r = *p; else ioerr(io,"Invalid R flag ignored; should be Y or N"); } /* Merge new P(ath) setting into other options */ /* "Pdir" = Receive files into directory "dir" if RN selected "dir" can by any valid existing directory, with or without trailing "/" */ if (p = find_option(buf,'P')) { strcpy(sv->option_p,p); p = sv->option_p + strcspn(sv->option_p," ,\t\r\n"); *p = '\0'; } return (*sv->option_a == 'Y') ? XPRS_SUCCESS|XPRS_NORECREQ|XPRS_HOSTMON : XPRS_SUCCESS|XPRS_NORECREQ; } /* Called by comm program to give us a chance to clean up before program ends */ long __saveds XProtocolCleanup(struct XPR_IO *io) { /* Release option memory, if any */ if (io->xpr_data) { FreeMem(io->xpr_data,(long)sizeof(struct SetupVars)); io->xpr_data = NULL; } return XPRS_SUCCESS; } /* Called by comm program upon our request (XPRS_HOSTMON) to let us monitor the incoming data stream for our receiver auto-activation string (ZRQINIT packet). We only ask for this to be called if option AY is set. */ long __saveds XProtocolHostMon(struct XPR_IO *io,UBYTE *serbuff,long actual,long maxsize) { static UBYTE startrcv[] = { ZPAD, ZDLE, ZHEX, "00" }; struct SetupVars *sv; if (!(sv = io->xpr_data)) return actual; /* XProtocolSetup() never called?! */ if (!sv->matchptr) sv->matchptr = startrcv; /* Scan through serbuff to see if we can match all bytes in the start string in sequence */ for (sv->bufpos=serbuff; sv->bufpos < serbuff+actual; ++sv->bufpos) { if (*sv->bufpos == *sv->matchptr) { /* if data matches current position in match string */ ++sv->matchptr; /* increment match position */ if (!*sv->matchptr) { /* if at end of match string, it all matched */ sv->matchptr = startrcv; sv->buflen = actual - (sv->bufpos - serbuff); XProtocolReceive(io); actual = 0; /* serbuff contents probably trashed by Receive() */ break; } } else if (sv->matchptr > startrcv) { /* if mismatch, reset to start of match string */ sv->matchptr = startrcv; if (*sv->bufpos == *sv->matchptr) ++sv->matchptr; } } sv->bufpos = NULL; return actual; } /* Called by comm program to let us monitor user's inputs; we never ask for this to be called, but it's better to recover gracefully than guru the machine */ long __saveds XProtocolUserMon(struct XPR_IO *io,UBYTE *serbuff,long actual,long maxsize) { return actual; } /* Perform setup and initializations common to both Send and Receive routines */ struct Vars *setup(struct XPR_IO *io) { static long bauds[] = { 110,300,1200,2400,4800,9600,19200,31250,38400,57600,76800,115200 }; struct SetupVars *sv; struct Vars *v; long origbuf, newstatus; #ifdef DEBUGLOG long i, *lng; #endif /* Make sure comm 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,"Comm prog missing required function(s); see docs"); return NULL; } /* Hook in default transfer options if XProtocolSetup wasn't called */ if (!(sv = io->xpr_data)) { io->xpr_data = AllocMem((long)sizeof(struct SetupVars),MEMF_CLEAR); if (!(sv = io->xpr_data)) { ioerr(io,"Not enough memory"); return NULL; } *sv = 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"); 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 = origbuf = atol(sv->option_b) * 1024; while (!(v->Filebuf = AllocMem(v->Filebufmax,0L))) { if (v->Filebufmax > 1024) v->Filebufmax -= 1024; else { FreeMem(v,(long)sizeof(struct Vars)); goto nomem; } } /* If framelength was intended to match buffer size, stay in sync */ v->Tframlen = atol(sv->option_f); if (v->Tframlen && v->Tframlen == origbuf) v->Tframlen = v->Filebufmax; /* Copy caller's io struct into our Vars for easier passing */ v->io = *io; #ifdef DEBUGLOG if (!DebugLog) DebugLog = (*v->io.xpr_fopen)(DebugName,"w"); dlog(v,"XPR_IO struct:\n"); for (i=0,lng=(long *)io; i < sizeof(struct XPR_IO)/4; ++i) { sprintf(v->Msgbuf," %08lx\n",*lng++); dlog(v,v->Msgbuf); } #endif /* Get baud rate; set serial port mode if necessary (and possible) */ if (v->io.xpr_setserial) { v->Oldstatus = (*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; Here's where we'd turn bits on if we needed to */ if (newstatus != v->Oldstatus) (*v->io.xpr_setserial)(newstatus); v->Baud = bauds[(newstatus>>16) & 0xFF]; #ifdef DEBUGLOG 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; } /* Set text/binary mode flags in accordance with T option setting */ void set_textmode(struct Vars *v) { struct SetupVars *sv; long i; sv = v->io.xpr_data; switch(*sv->option_t) { case 'Y': /* Force text mode on receive; suggest text mode on send */ TY: v->Rxascii = TRUE; v->Rxbinary = FALSE; v->Lzconv = ZCNL; break; case 'N': /* Force binary mode on receive; suggest binary mode on send */ TN: v->Rxascii = FALSE; v->Rxbinary = TRUE; v->Lzconv = ZCBIN; break; case 'C': /* Ask comm program for proper mode for this file */ if (v->io.xpr_finfo) { i = (*v->io.xpr_finfo)(v->Filename,2L); if (i == 1) goto TN; /* Comm program says use binary mode */ if (i == 2) goto TY; /* Comm program says use text mode */ } /* xpr_finfo() not provided (or failed); default to T? */ case '?': v->Rxascii = v->Rxbinary = FALSE; v->Lzconv = 0; break; } } /* Search for specified option setting in string */ UBYTE *find_option(UBYTE *buf,UBYTE option) { while (*buf) { buf += strspn(buf," ,\t\r\n"); if (*buf == option) return ++buf; buf += strcspn(buf," ,\t\r\n"); } return NULL; } /* send cancel string to get the other end to shut up */ void canit(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(struct Vars *v,UBYTE *s) { UBYTE 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(struct Vars *v,UBYTE c) { (*v->io.xpr_swrite)(&c,1L); } /* Get a byte from the modem; return TIMEOUT if no read within timeout tenths of a second, return RCDO if carrier lost or other fatal error (sread returns -1). 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(struct Vars *v,short tenths) { /* If there's data waiting in our buffer, return next byte */ if (v->Modemcount) { gotdata: --v->Modemcount; return (short)(*v->Modemchar++); } /* Our buffer is empty; see if there's anything waiting in system buffer */ v->Modemcount = (*v->io.xpr_sread)(v->Modembuf,(long)sizeof(v->Modembuf),0L); if (v->Modemcount < 0) { /* Carrier dropped or other fatal error; abort */ v->Modemcount = 0; return RCDO; } else if (!v->Modemcount) { /* Nothing in system buffer; try waiting */ v->Modemcount = (*v->io.xpr_sread)(v->Modembuf,1L,tenths*100000L); if (v->Modemcount < 0) { v->Modemcount = 0; return RCDO; } else if (!v->Modemcount) return TIMEOUT; /* Nothing received in time */ } 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(struct Vars *v) { if (v->Modemcount) return TRUE; /* No data in our buffer; check system's input buffer */ v->Modemcount = (*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(struct Vars *v) { long sent, elapsed, expect; 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 = GetSysTime(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,"%02ld:%02ld:%02ld",(long)hr,(long)min,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,"%02ld:%02ld:%02ld",(long)hr,(long)min,elapsed); v->xpru.xpru_elapsedtime = (char *)v->Msgbuf+20; } /* Buffered file I/O fopen() interface routine */ void *bfopen(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 */ #ifdef DEBUGLOG sprintf(v->Msgbuf,"bfopen: %s %s\n",v->Filename,mode); dlog(v,v->Msgbuf); #endif return (*v->io.xpr_fopen)(v->Filename,mode); } /* Buffered file I/O fclose() interface routine */ void bfclose(struct Vars *v) { /* If bfwrite() left data lingering in buffer, flush it out before closing */ if (v->Fileflush) (*v->io.xpr_fwrite)(v->Filebuf,1L,v->Filebufcnt,v->File); /* Close the file */ (*v->io.xpr_fclose)(v->File); v->File = NULL; } /* Buffered file I/O fseek() interface routine */ void bfseek(struct Vars *v,long pos) { long offset; /* If new file position is within currently buffered section, reset pointers */ if (pos >= v->Filebufpos && pos < v->Filebufpos + v->Filebuflen) { 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 { (*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(struct Vars *v,UBYTE *buf,long length) { long count, total = 0; /* Keep going until entire request completed */ while (length > 0) { /* Copy as much of the request as possible from the buffer */ count = (length <= v->Filebufcnt) ? length : v->Filebufcnt; CopyMem(v->Filebufptr,buf,count); #ifdef DEBUGLOG sprintf(v->Msgbuf,"bfread got %ld bytes from buffer\n",count); dlog(v,v->Msgbuf); #endif buf += count; total += count; length -= count; v->Filebufptr += count; v->Filebufcnt -= count; /* If we've emptied the buffer, read next buffer's worth */ if (!v->Filebufcnt) { v->Filebufpos += v->Filebuflen; v->Filebufptr = v->Filebuf; v->Filebufcnt = v->Filebuflen = (*v->io.xpr_fread)(v->Filebuf,1L,v->Filebufmax,v->File); #ifdef DEBUGLOG sprintf(v->Msgbuf,"bfread read %ld bytes\n",v->Filebufcnt); dlog(v,v->Msgbuf); #endif /* If we hit the EOF, return with however much we read so far */ if (!v->Filebufcnt) break; } } return total; } /* Buffered file I/O fwrite() interface routine */ long bfwrite(struct Vars *v,UBYTE *buf,long length) { 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 DEBUGLOG 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 = (*v->io.xpr_fwrite)(v->Filebuf,1L,v->Filebufcnt,v->File); #ifdef DEBUGLOG 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 comm program display an error message for us, using a temporary XPR_UPDATE structure; used to display errors before Vars gets allocated */ void ioerr(struct XPR_IO *io,char *msg) { struct XPR_UPDATE xpru; if (io->xpr_update) { xpru.xpru_updatemask = XPRU_ERRORMSG; xpru.xpru_errormsg = msg; (*io->xpr_update)(&xpru); } } /* Have the comm program display an error message for us, using the normal XPR_IO structure allocated in Vars */ void upderr(struct Vars *v,char *msg) { v->xpru.xpru_updatemask = XPRU_ERRORMSG; v->xpru.xpru_errormsg = msg; (*v->io.xpr_update)(&v->xpru); #ifdef DEBUGLOG dlog(v,msg); dlog(v,"\n"); #endif } /* Have the comm program display a normal message for us */ void updmsg(struct Vars *v,char *msg) { v->xpru.xpru_updatemask = XPRU_MSG; v->xpru.xpru_msg = msg; (*v->io.xpr_update)(&v->xpru); #ifdef DEBUGLOG 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(void) { return 0x7FFFFFFF; } /* Check whether file already exists; used to detect potential overwrites */ char exist(struct Vars *v) { void *file; file = (*v->io.xpr_fopen)(v->Filename,"r"); if (file) { (*v->io.xpr_fclose)(file); return TRUE; } else return FALSE; } #ifdef DEBUGLOG /* Write a message to the debug log */ void dlog(struct Vars *v,UBYTE *s) { /* Open the debug log if it isn't already open */ if (!DebugLog) DebugLog = (*v->io.xpr_fopen)(DebugName,"a"); (*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. (*v->io.xpr_fclose)(DebugLog); DebugLog = NULL; */ } #endif