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