home *** CD-ROM | disk | FTP | other *** search
- /*
- LEGAL STUFF
- ------------------------------------------------------------------------------
-
- The protocol, documentation, and Zmax.c routines are by Mike Bryeans.
-
- Zmax is NOT public domain. Micro TECH Systems Retains ALL RIGHTS TO
- THIS PROTOCOL.
-
- No PART of Zmax may be used in development of a NON-ZMAX protocol.
-
- Developers are granted a license to use Zmax in NON-COMMERCIAL
- applications and environments ONLY.
-
- A 10 dollar donation is requested to help
- offset development costs and to help with
- the salaries of the technical support
- person(s).
-
- If you application is intended for use in a program that is to be sold
- commercially, including shareware, you must apply for a Zmax COMMERCIAL
- License.
-
- If you intend to develop a Zmax driver for USE in a commercial
- environment, you must apply for a Zmax COMMERCIAL license.
-
- All goverment agents Federal, State, and Local must apply for a Zmax
- COMMERICAL license.
-
- You may NOT modify Zmax specifications. Zmax MUST remain uniform and
- only Micro TECH Systems may issue NEW or IMPROVED Zmax specifications.
-
- If you have a particular item in mind, submit it along with a modified
- Zmax.c (Using our Zmax.c driver code) to Micro TECH Systems.
-
- Micro TECH Systems
- 555 North Spring, #39
- Cape Girardeau Mo. 63701
-
- Data Number 1-314-334-6359
-
- Zmax specifications, ver. 1.00
-
- This is a full functional, tested, driver. I've attempted to keep
- the code as simple as possible and use terms that anyone thats
- written any type of protocol knows since its to be used as a guide
- to other implementations.
-
- Zmax was designed with reliablity in mind as the FIRST and foremost
- concern, speed came in a close second. You'll no doubt notice a couple
- of areas where it could be speeded up and I was tempted. Zmax isn't
- the FASTEST protocol around, but then it wasn't suppose to be.
-
- I wanted the best all round protocol which would always get the file
- there intact and as quickly as possible. I beleive you'll be very hard
- pressed to find any protocol that will best Zmax in RELIABLITY, FLEXABLITY,
- and SPEED.
-
-
- */
-
- /* Include files */
-
- #include<stdio.h>
- #include "zmax.h"
- #include<malloc.h>
- #include<filedata.h>
- #include<comm.h>
- #include<ctype.h>
-
- /* Globle Declares */
- FILE *out;
- unsigned long UpdCrc32(char byte, unsigned long crc);
- long file_position,seof;
- int cancel,percent_size,total_errors = 0,endsend = 0,delete_aborted_transfers = 1;
- struct filedata filstruc;
- long locked_port, atol(), timerset(),amt_sent;
- char *Screen_ptr;
- char acks_required=0;
- char tbuf[2048],junk_string[81];
- int block_len,return_code = 1,port_ptr;
- unsigned cur_baud,connect_rate,sending_file;
- ASYNC *port;
- int IOadrs = 0x3f8; /* comm port base I/O address */
- char irqm = IRQ4; /* mask for selected IRQ line */
- char vctrn = VCTR4; /* comm interrupt vector nbr */
- char log_file[81];
-
- /* Zmax Structures */
-
- struct Zhead /* File info data structure */
- { long flen; /* file length */
- unsigned fdate; /* Files DATE stamp, DOS format */
- unsigned ftime; /* Files TIME stamp, DOS format */
- char fnam[14]; /* original file name */
- } ;
-
- struct _block_header
- {
- long position;
- unsigned bytes;
- char ack; /* If on (set to 1) the receiver must
- ACK data block if good.
-
- */
- };
-
- struct _info_header
- {
- unsigned flags;
- /* 00000000 00000001 = Can handle Continues Stream Transmissions.
-
- If this flag is off, ACK is required following
- each block of data.
-
- ACKING each block of data turns Zmax into a
- X/Ymodem Batch type protocol.
-
- It is suggested that once you reach 128
- byte blocks, bad lines, Zmax should
- require each block of data to be ACKED.
-
- This will switch it to a batch
- Xmodem style protocol which is
- better for bad phone lines.
-
- You'll also notice that although there are
- provisions for decreasing the block size
- when noisy lines are encountered, you don't
- see any place where I raise the block size back
- up.
-
- There is nothing saying you CAN'T do that, I just
- don't beleive you should. If you encounter
- enough noise to cause the block size to drop, then
- the likely hood of you encountering MORE is very high.
-
- If you do raise the block size back up, I would recommend
- that you do it based on TIME and not number of good block
- sent. Given that 13 blocks (256 byte blocks) can be
- stuffed into a 3K TX buffer, you may THINK you've sent
- 13 good blocks when in fact the very next one you sent
- could be bad. I think 10 to 20 seconds without an error
- would be a good time frame.
-
- You'll also notice that I do not drop the block size
- on the first error. One error does not make for bad
- phone lines, 2 or more does.
-
- If you decide to use large blocks than I do here,
- then you may want to go ahead and drop them on the
- first error.
-
-
- */
-
-
- int buf_size; /* Used to pass transmitter the receivers max
- buffer size. Its is suggested that the receiver
- support 2K buffer.
-
- The transmitter doesn't have to give you those
- sizes and can replace them with his own as long
- as it is less than or equal to your max buffer
- size.
-
- Nor do the block sizes have to be powers of 2.
-
- You can use odd block sizes...231, 888, etc.
-
- I used powers of 2 because of the way DOS formats
- harddrives, ie. cluster size.
-
- You'll also notice that Zmax places little restrictions
- on the maximum SIZE of the block although going over
- 2048 shows little gain and lots of loses when a resend
- is requested.
-
- To give you some guidelines for setting block sizes
- see chart below:
-
- Chart based on sending a 60,416 byte file WITH the
- protocol overhead figured in and driving the serial
- port to its maximum Cps at that baud rate which this
- driver does.
-
- Blk Size Tx Time
- Bytes 2400 9600 LOCKED Port.
-
- 64 318.60 79.65
- 128 285.17 71.29
- 256 268.45 67.11
- 512 260.09 65.02
- 1024 255.91 63.98
- 2048 253.82 63.46
- 4096 252.78 63.20
- 8192 252.26 63.07
-
- As you can see, there's quite a bit of savings
- across the baud rate scale with each increament
- of the block size up to 2048 bytes. Once you exceed
- 2048 BPB, the savings is almost not measureable.
-
- Taking into account the time required to resend 2K of
- data at 2400 bps, I wouldn't risk going over 1K at 2400
- unless the call is local or under MNP.
-
- You may REQUEST any size data block up to
- the size of a signed integer.
-
-
- */
- unsigned char version ; /*
-
- This allows for future expansion to the protocol
- and still yet allow for backwards compatibly.
-
- This is the version of the protocol specs that the
- receiver supports. Currently this should be set to one.
-
- */
- char empty[123]; /* Reserves an extra 123 bytes for future expansion */
- };
-
- /* DEFINES */
-
- #define STX 2
- #define TSYNCH 0xae
- #define ACK 0x06
- #define NAK 0x15
- #define SOH 0x01
- #define EOT 0x04
- #define CAN 0x18
- #define RESYNC 9
- #define PER_WEEK 60480000L
- #define PER_DAY 8640000L
- #define PER_HOUR 360000L
- #define PER_MINUTE 6000L
- #define PER_SECOND 100L
- #define ENQ 0x05
-
- main(int argc, char *argv[])
- {
- /*
-
- This is the main section of this driver. It's makeup is entirely
- up to you. With of course, several REQUIRED sets.
-
- */
- int i,x,count,can_display_user_name=0;
- int y=0,block_size=1024;
- int send=0,do_acks=0;
- char s[81],s1[81];
- FILE *in;
- qvideo();
- clrscrn();
- Screen_ptr = malloc(1 * 4000);
- if(Screen_ptr == NULL) exit(1);
- uncomprs(&Screen_ptr[0*4000], tmod); /* uncompress screen 1 */
- memtoscr(2000, 0, Screen_ptr);
- curoff();
- memset(s,'\0',sizeof(s));
- if(!envget2("OSIRIS",s))
- {
- sprintf(log_file,"%s\\Zmax.log",s);
- can_display_user_name=1;
- }
- else
- sprintf(log_file,"Zmax.log");
- for(i=1;i<argc;i++)
- {
- if(argv[i][0] == '/' || argv[i][0] == '-')
- {
- switch(toupper(argv[i][1]))
- {
- case 'N' :
- if(can_display_user_name)
- {
- sprintf(s1,"%s\\node%d",s,atoi(argv[i+1]));
- in = fopen(s1,"r");
- fgets(s,80,in);
- fclose(in);
- s[strlen(s)] = '\0';
- curlocat(23,2);
- colrprts(s,15,1);
- }
- break;
- case 'S' : block_size = atoi(argv[i+1]);
- if(block_size > 2048)
- block_size = 2048;
- break;
- case 'A' : do_acks = 1;break;
- case 'K' : delete_aborted_transfers = 0;
- break;
- case 'P' : port_ptr = atoi(argv[i+1]);
- port_ptr--;
- break;
- case 'B' : connect_rate = atoi(argv[i+1]);
- cur_baud = connect_rate;
- block_size = (connect_rate > 2400) ? 2048 : 1024;
- break;
- case 'L' : locked_port = atol(argv[i+1]);
- break;
- case 'R' : set_com();
-
- /*
- You should continue to cycle receiving files until
- Zrecfile returns a ZERO, indicating that it did not
- receive any files or the session was aborted.
- */
- while ( Zrecfile(do_acks,block_size) && (async_carrier(port))) clear_screen() ;
-
- /* Following the last file, the sender will require an ACK
- to signal the receivers acknowledges end of session.
-
- The Sender doesn't respond back after receiving the ACK so
- continue with you processing after sending a couple of
- ACKS. Two is suggested, an extra incase line noise blew the
- first one.
-
- */
-
- com_putc(ACK);
- com_putc(ACK);
- end_prg();
- break;
-
- case 'F' : set_com();
- i++;
- for(;i<argc;i++)
- {
- memset(s,'\0',sizeof(s));
- parser(argv[i],&s[0]);
-
- /* Cycle until all files are sent or Zsendfile returns
- a zero indicating Session was aborted. */
- acks_required = 0;
- if(!Zsendfile(argv[i],s))
- end_prg();
- if(!async_carrier(port)) end_prg();
- clear_screen();
- }
- i = count = 0;
-
- /*
- Signal end of Batch Transfer.
-
- Cycle sending EOT until receiver sees it and ACKs
- the ending EOT. I suggest cycling only two periods
- , period being defined as trying ACKEOT function twice.
-
- If your using Zmax in a mailer environment and as the
- sender you find that you have NOTHING to send, then bypass
- the Zsendfile routine and go directly to this section.
-
- */
-
- while(!count && i <= 1)
- {
- com_putc(EOT);
- i++;
- if(ackeot()) count = 1;
- if(!async_carrier(port)) count = 1;
- }
- end_prg();
- break;
- }
- }
- }
-
- }
- Zsendfile(name,s_name) /* transmit a file */
- char *name; /* FULL Drive, path, and name of file to send */
- char s_name[]; /* Only the filename, fit to be used in file info
- header.
- */
-
-
- {
- long location, blkloc,size,st,ft,t1,t2;
- int error=0,endblk,x,y,i,cps;
- struct Zhead zero;
- struct _info_header info_header;
- unsigned file_date,file_time,actual;
- char s[80];
- int ok;
- struct _block_header block_header;
- if(!async_carrier(port)) return(0);
- acks_required = 1;
- async_txflush(port);
- file_position = 0L;
-
- if(!filexist(name)) return(1);
-
- x = openfil(name,2,&sending_file);
- getstamp(sending_file,&file_date,&file_time);
- memset(&zero,'\0',sizeof(zero)); /* clear out data block */
- filsize(name,&size);
- percent_size = size / 40;
- seof = size;
- strcpy(zero.fnam,s_name);
- zero.fdate = (unsigned) file_date;
- zero.ftime = (unsigned) file_time;
- zero.flen = (long) size;
- curlocat(5,28);
- colrprtf(0,0,7,"%-14s",zero.fnam);
- curlocat(6,28);
- colrprtf(0,0,7,"%-7ld",size);
- amt_sent = 0L;
-
- total_errors = cancel = 0;
- t1 = timerset(6000L); /* time limit for first block */
- com_putc(TSYNCH);
- com_putc(TSYNCH);
- endsend = i = 0;
- while(x != 'C' && !timeup(t1)) /* Wait for primer start */
- {
- x = com_getc(2);
- if(x != 'C')
- com_putc(TSYNCH);
- if(!async_carrier(port)) return(0);
- if(x == 0x18) cancel++;
- if(cancel > 3)
- {
- curlocat(7,28);
- colrprtf(0,0,7,"%-40s","Rcv Canceled Transfer");
- closefil(sending_file);
- end_prg();
- }
- }
- cancel = 0;
- if(x != 'C')
- goto abort; /* If we did not get it then abort */
-
- ok = 0;
- while(!get_system_info(&info_header) && async_carrier(port))
- {
- if(++ok > 3)
- async_dtr(port,0); /* After 3 attempts, drop carrier
- and bail out.
- */
- }
- if(!async_carrier(port)) goto abort;
-
- block_len = info_header.buf_size;
- if(block_len > 2048)
- block_len = 2048;
- if( (info_header.flags&0x0001))
- acks_required = 0;
- cancel = ok = 0;
-
- curlocat(7,28);
- colrprtf(0,0,7,"%-40s","Sending File Header");
- while(!ok)
- {
- async_rxflush(port);
- SendHeader(&zero);
- switch(wait_ack())
- {
- case 1: break; /* I treat both an Abort and
- a duplicate the same to save
- a little space. You don't have
- to.
-
- case 2:
- closefil(sending_file);
- curlocat(7,28);
- colrprtf(0,0,7,"%-40s","Rcv Already has File");
- return(1);
-
- case 3:/* REPOS ie. Restart */
- default: curlocat(7,28);
- sprintf(junk_string,"BufSize %d (%sStream Mode)",info_header.buf_size,(acks_required) ? "Non-" : "");
- colrprtf(0,0,7,"%-40s",junk_string);
- ok = 1;
- break;
-
- }/* End switch */
- }
-
- st = timerset(0L);
- async_rxflush(port);
- async_txflush(port);
- do
- {
- if( !async_carrier(port) )
- goto abort;
- if(timeup(t1))
- goto abort;
- if(checkkey())
- {
- i = getkey(&x);
- if(x == 27)
- {
- while(!async_txempty(port)){
- if(!async_carrier(port)) break;
- }
- cancel_transfer();
- goto abort;
- }
- }
- ChkForNak();
- while(file_position < zero.flen)/* loop until all sent */
- {
- if(!async_carrier(port))
- goto abort;
- ChkForNak();
- if(checkkey())
- {
- i = getkey(&x);
- if(x == 27)
- {
- while(!async_txempty(port)){
- if(!async_carrier(port)) break;
- }
- cancel_transfer();
- goto abort;
- }
- }
- /*
- NOTE: My file functions; seekfil,
- readfil, creatfil, openfil, writefil,
- etc. all have built in error handling
- routines to reduce redundent coding on
- MY part. You should add your own error
- trapping according to what your using.
-
- */
- i = seekfil(sending_file,0,file_position,&location);
- i = readfil(sending_file,block_len,tbuf,&actual);
-
-
- /*
- Sending the current file position with
- each data block removes all
- possiblities of Desqview shifting the
- file position, a network or satellite
- link mis-routing or losing a block of
- data. I've seen both happen before and
- they weren't detected until the end of
- file.
-
- This totally insures that the receiver
- and sender are never out of sync.*/
-
- block_header.position = file_position;
- block_header.bytes = actual;
- block_header.ack = (acks_required) ? 1 : 0 ;
- file_position = (long) file_position + (long) actual;
- amt_sent = (long) file_position;
- com_putc(SOH);
- com_putc(ENQ);
- send_block_header(&block_header);
- SendDataBlock(tbuf,actual);
- if(block_header.ack)
- {
- while(!async_txempty(port))
- if(!async_carrier(port)) break; /* Wait till
- its all out
- before beginning
- timer*/
- t2 = timerset(3000L);/* Give receiver 30
- seconds to ACK or
- NAK the block.
- 30 seconds seems
- like a lot of time,
- but you have to allow
- for satellite/network delays,
- HST and Telebit ARQ resends.
- etc.
- */
- ok = i = 0;
- while(!timeup(t2) && !ok)
- {
- i = com_getc(0);
- if(i == ACK) ok = 1;
- else
- if(i == 'C')
- {
- i = com_getc(2);
- if(i == '*')
- {
- if(GetNewPosition())
- ok = 1;
-
- }/* End Get REPOS */
-
- }/* End possible NAK */
-
- } /* End 10 second timer loop */
- if(!ok)
- async_dtr(port,0); /* No responds in
- 30 seconds, drop carrier
- and get out. */
- }/* End wait for ACK */
- else
- ChkForNak();
- if(file_position == zero.flen)
- {
- com_putc(EOT);
- endsend = 0;
- t1 = timerset(9000L); /* time limit for last block if ackless(90 secs) */
- sprintf(junk_string,"%-7ld",zero.flen);
- curlocat(6,58);
- colrprtf(0,0,7,"%s",junk_string);
- for(i=1;i<=40;i++)
- {
- curlocat(19,(21+i));
- colrprtf(0,15,7,"█");
- }
- }
- ChkForNak();
- if(cancel >= 5) goto abort;
- }/* Still to go */
-
- ChkForNak();
- if(cancel >= 5) goto abort;
- }while(!endsend);
- /* end main send */
-
- ft = timerset(0L);
- closefil(sending_file);
- ft = (long) (ft-st) / 100L;
- if(ft == 0) ft = 1L;
- cps = (int) (zero.flen / ft);
- x = (cps*10) / (connect_rate/100);
- curlocat(14,28);
- colrprtf(0,0,7,"%3d%%",x);
- out = fopen(log_file,"a");
- fprintf(out,"%d bps %-14s %-7ld %3d%%\n",connect_rate,zero.fnam,zero.flen,x);
- fclose(out);
- return_code = 0;
- return(1); /* exit with good status */
-
- abort:
- async_txflush(port);
- closefil(sending_file);
- return(0); /* exit with bad status */
- }
-
-
- ChkForNak() /* check for bad block */
- {
- unsigned char c=0;
- int q;
- top:;
- c = com_getc(0);
- if(c == 'C' || c == ACK)
- {
- if(c == ACK)
- {
- if(file_position == seof)
- endsend = 1;
- return;
- }
- q = com_getc(1);
- if(q != '*')
- return;
- async_txflush(port);
- q = GetNewPosition();
- }
- else
- if(c == CAN)
- cancel++;
-
- if( (async_rxcnt(port)))
- goto top;
- }
-
- SendDataBlock(blk,size) /* physically ship a block */
- char *blk; /* data to be shipped */
- int size;/* size of block */
- {
- register i,x,y;
- unsigned long oldcrc;
- long location;
- static unsigned char ch;
- char *b = blk;
- char *j = blk;
- static int n,q,flow,aa,kk;
-
-
- while( (async_txblk(port, b, size)) == R_TXERR)
- {
- curlocat(6,58);
- location = (long) file_position - size;
- colrprtf(0,0,7,"%-7ld",location);
- kk = location / percent_size;
- if(kk > 40) kk = 40;
- for(aa=1;aa<=kk;aa++)
- {
- curlocat(19,(21+aa));
- colrprtf(0,15,7,"█");
- }
- if(async_rxcnt(port))
- {
- flow = (async_peek(port,0)&0xff);
- if(flow == 'C')
- {
- async_txflush(port);
- return;
- }
- else
- if(flow != CAN)
- flow = async_rx(port); /* Eat non C/CAN char, more
- than likely was line noise
- */
- }
- else
- if(!async_carrier(port)) return;
-
- }/* end while*/
- oldcrc = 0xFFFFFFFF;
- for(n=0;n<size;n++){
- oldcrc = UpdCrc32(((unsigned short) (*j++)), oldcrc);
- }
- oldcrc = ~oldcrc;
- for(n=4; --n >=0;)
- {
- com_putc((unsigned char) oldcrc);
- oldcrc >>= 8;
- }
- if(acks_required)
- {
- curlocat(6,58);
- location = (long) file_position - size;
- colrprtf(0,0,7,"%-7ld",location);
- kk = location / percent_size;
- if(kk > 40) kk = 40;
- for(aa=1;aa<=kk;aa++)
- {
- curlocat(19,(21+aa));
- colrprtf(0,15,7,"█");
- }
- }
- }
-
- ackeot()
- {
- long t;
- int c;
- t = timerset(300L);
- while(!timeup(t))
- {
- c = com_getc(0);
- if(c == ACK)
- return(1);
- }
-
- return(0);
- }
- long timerset (t)
- long t;
- {
- long l;
- int l2;
- int hours,mins,secs,ths,year,month,day;
-
- sysitime (&hours,&mins,&secs,&ths);
- sysidate(&year,&month,&day);
- l2 = dayofwee(year,month,day);
-
- l = l2 * PER_DAY +
- hours * PER_HOUR +
- mins * PER_MINUTE +
- secs * PER_SECOND +
- ths ;
-
- l += t;
-
- return (l);
- }
-
- timeup (t)
- long t;
- {
- long l;
-
- l = timerset (0L);
- if(l < (t-PER_DAY)) l = l + PER_DAY;
- return ((l - t) >= 0L);
- }
-
- com_getc(t)
- int t;
- {
- static long t1,t2;
- int c;
- if(!((c = async_rx(port)) & B_RXEMPTY))
- return((c&0xff));
- t2 = (long) t * 100L;
- t1 = timerset (t2);
- while(((c = async_rx(port)) & B_RXEMPTY))
- {
- if(!async_carrier(port))
- {
- return(EOF);
- }
- if (timeup(t1))
- {
- return(EOF);
- }
- }
- return((c&0xff));
-
-
- }
- wait_ack()
- {
- int ch;
- long t;
- struct _block_header block_header;
-
- t = timerset(3000L); /* If no rsponds in 30 seconds, skip file */
- for(;;)
- {
- ch = com_getc(5);
- if(ch == 'D')
- {
- ch = com_getc(5);
- if(ch == 'P')
- {
- com_putc(ACK);
- return(2);
- }
- }/* End if Duplicate File */
-
- if(ch == ACK) return(4);
-
- if(ch == CAN || timeup(t))
- {
- async_rxflush(port);
- return(2);
- }
-
- if(ch == RESYNC)
- {
- if(get_block_header(&block_header))
- {
- file_position = block_header.position;
- return(3);
- }
-
- }/* Resync */
-
- if(ch == 'C')
- return(1);
- }
-
- }
-
- SendHeader(blk) /* physically ship Fileinfo block */
- char *blk; /* data to be shipped */
- {
- register ch,n;
- unsigned long oldcrc; /* CRC check value */
-
- com_putc(SOH);
- com_putc(NAK);
-
- while( (async_txblk(port, blk, 22)) == R_TXERR);
-
- oldcrc = 0xFFFFFFFF;
- for(n=0;n<22;n++){
- oldcrc = UpdCrc32(((unsigned short) (*blk++)), oldcrc);
- }
- oldcrc = ~oldcrc;
- for(n=4; --n >=0;)
- {
- com_putc((unsigned char) oldcrc);
- oldcrc >>= 8;
- }
-
- }
-
- set_com()
- {
- char parms[80],s2[30],s3[30];
- int ch;
- long atol();
- if(port_ptr)
- {
- IOadrs = 0x2f8;
- irqm = 0x08;
- vctrn = 11;
- }
- port = malloc(sizeof(ASYNC));
- sprintf(s3,"COM%d",port_ptr+1);
- memset(s2,'\0',sizeof(s2));
- if(!envget2(s3,s2))
- locked_port = (long) atol(s2);
- if(locked_port)
- sprintf(parms,"%ldN81",locked_port);
- else
- sprintf(parms,"%dN81",cur_baud);
- if(!(AllocRingBuffer(port, 2048, 3072, 1)))/* Note that I allocate
- a TX buffer 1K larger
- than the largest block
- size that I support.
-
- This gives me a little
- breathing space in STREAM
- mode at 9600+ for file management. */
- exit(0);
- if ((ch = async_open(port, IOadrs, irqm, vctrn, parms)) != R_OK)
- exit(0);
- async_rxflush(port);
- async_txflush(port);
- async_msrflow(port, B_CTS);
- async_FIFOtxlvl(port,4);/*
- I strongly suggest that you keep the
- FIFO Tx bytes per interrupt to no more
- than 4.
-
- Reason:
-
- This driver will drive the modem so
- fast and hard that an HST 450
- Backchannel will loose its sync with
- an HST Dual Standard or HST 14.4 if
- its (450) doing the sending.
-
-
- */
- async_FIFOrxlvl(port,1);/* Never go over 1 here */
- }
-
- parser(s,name) char s[],name[];
- {
- char stack[80];
- int i,x;
- if(strindex("\\",s) == -1)
- {
- name[0] = 0;
- strcpy(name,s);
- return;
-
- }
-
- memset(stack,'\0',sizeof(stack));
- x = strlen(s);
- i = 0;
- while(s[x] != '\\' && x > 0)
- {
- if(s[x] != 0 && s[x] != 13 && s[x] != 10) stack[i++] = s[x];
- x--;
- }
- x = 0;
- i--;
- while(i >= 0)
- name[x++] = stack[i--];
- name[x] = '\0';
- }
-
- end_prg()
- {
- curon();
- if(async_carrier(port))
- port->OldMCR |= B_DTR;
- async_close(port);
- if(!delete_aborted_transfers)
- sleep(20);
- exit(return_code);
-
- }
-
- Zrecfile(int do_acks,int block_size) /* receive file */
- {
- int n,w,x,y,tries,i,c,cps;
- char outname[81];
- int kk,aa;
- unsigned send,actual,file_date,file_time;
- struct Zhead zero; /* file header data storage */
- struct _block_header block_header;
- struct _info_header SystemInfo;
- long t1,st,ft,lsize,testl;
-
- cancel=tries = total_errors = c = 0;
- percent_size = 32000;
- memset(outname,'\0',sizeof(outname));
- memset(&zero,'\0',sizeof(zero));
- amt_sent = 0L;
- file_position = 0L;
- st = timerset(6000L);
- com_putc(' ');
- while(!timeup(st))
- {
- if(!async_carrier(port))
- return(0);
- c = com_getc(1);
- if(c == TSYNCH)
- st =timerset(0L);
- if(c == EOT)
- return(0);
-
- if(c == ENQ)
- {
- com_putc(ACK);
- st = timerset(30000L); /*
- This is relevant only if
- zmax is being used in a mailer
- application.
-
- ENQ at this point means to
- extend the time out period to 5 minutes.
-
- This is used primaryly for allowing
- service requests time to be processed
- by exteranl programs. See Mailer.Doc
- for information on service requests.
-
-
-
- */
- }
- }
- if(c != TSYNCH)
- {
- curlocat(7,28);
- colrprtf(0,0,7,"%-40s","Sender flobbed it");
- st = timerset(500L);
- while(!timeup(st)) ;
- return(0);
- }
-
- cancel = c = 0;
- st = 0L;
- com_putc('C');
- c = '\0';
- memset(&SystemInfo,'\0',sizeof(SystemInfo));
- if(!do_acks)
- SystemInfo.flags ^= 0x0001;
- SystemInfo.buf_size = block_size;
- SystemInfo.version = 1;
-
- send_system_info(&SystemInfo);
-
- curlocat(7,28);
- colrprtf(0,0,7,"%-40s","Waiting For File");
-
- goto nextblock;
-
- badblock: /* we got a bad block */
-
- if(++tries>10)
- {
- async_dtr(port,0);
- sleep(3);
- async_dtr(port,1);
- goto abort;
- }
- async_rxflush(port);
- send_resync();
- i = seekfil(send,2,0L,&testl);
- curlocat(5,58);
- colrprtf(0,0,7,"%d",++total_errors);
- goto nextblock;
-
- goodblock: /* we got a good block */
- if(block_header.ack)
- com_putc(ACK);
- amt_sent = (long) file_position;
-
- nextblock: /* start of "get a block" */
- t1 = timerset(2000L);
- while(!timeup(t1))
- {
- if(checkkey())
- {
- aa = getkey(&kk);
- if(kk == 27)
- {
- cancel_transfer();
- goto abort;
- }
- }
- c = (com_getc(3)&0xff);
- switch (c)
- {
- case CAN : cancel++;
- if(cancel >= 5)
- goto abort;
- break;
- case SOH :
- x = com_getc(5);
- switch(x)
- {
- case NAK :
- if(readblock(&zero,22))
- {
- st = timerset(0L);
- sprintf(outname,"%s",zero.fnam);
- if(!filexist(outname))
- com_putc(ACK);
- if(filexist(outname))
- {
- openfil(outname,2,&send);
- getstamp(send,&file_date,&file_time);
- closefil(send);
- filsize(outname,&lsize);
- if(lsize < zero.flen && zero.fdate == file_date && zero.ftime == file_time)
- {
- openfil(outname,2,&send);
- seekfil(send,2,0L,&file_position);
- curlocat(7,28);
- sprintf(junk_string,"Re-Start: %ld",file_position);
- colrprtf(0,0,7,"%-40s",junk_string);
- com_putc(RESYNC);
- block_header.position = file_position;
- send_block_header(&block_header);
- }
- else
- {
- if(lsize == zero.flen && zero.fdate == file_date && zero.ftime == file_time)
- {
- x = i = 0;
- curlocat(7,28);
- colrprtf(0,0,7,"%-40s","Duplicate: Asking for Skip");
- async_rxflush(port);
- com_putc('D');
- com_putc('P');
- c = com_getc(5);
- if(c == ACK)
- return(1);
-
- return(0);
- }
-
- }
- } /* end if exits */
- else
- i = creatfil(outname,0,&send);
- curlocat(5,28);
- colrprtf(0,0,7,"%-14s",outname);
- curlocat(6,28);
- colrprtf(0,0,7,"%-7ld",zero.flen);
- percent_size = zero.flen / 40;
- curlocat(7,28);
- colrprtf(0,0,7,"%-40s","Getting File");
-
- goto goodblock;
- }
- curlocat(7,28);
- colrprtf(0,0,7,"%-40s","Bad File Header");
- com_putc('C');
- goto nextblock; /* bad header block */
- break;
- case ENQ :memset(&block_header,'\0',sizeof(block_header));
- if(get_block_header(&block_header))
- {
- if(block_header.position != file_position)
- {
- curlocat(7,28);
- sprintf(junk_string,"Got %ld, Needed %ld",block_header.position,file_position);
- colrprtf(0,0,7,"%-40s",junk_string);
- goto badblock;
- }
- if(readblock(&tbuf[0],block_header.bytes)) /* else if we get it okay */
- {
- n = writefil(send,block_header.bytes,tbuf,&actual);
- file_position = (long) file_position + (long) actual;
- curlocat(6,58);
- colrprtf(0,0,7,"%-7ld",file_position);
- cancel = tries = 0;
- kk = file_position / percent_size;
- if(kk > 40) kk = 40;
- for(aa=1;aa<=kk;aa++)
- {
- curlocat(19,(21+aa));
- colrprtf(0,15,7,"█");
- }
- goto goodblock;
- }
- curlocat(7,28);
- sprintf(junk_string,"Bad Block, REPOS %ld",file_position);
- colrprtf(0,0,7,"%-40s",junk_string);
- goto badblock;
- }
- goto badblock;
- break;
- default :curlocat(7,28);
- sprintf(junk_string,"Huh?? (%02x)",x);
- colrprtf(0,0,7,"%-40s",junk_string);
- goto badblock;
-
- }/* end switch x*/
- break;
- case EOT :
- if(file_position == zero.flen || file_position == 0)
- {
- com_putc(ACK);
- if(file_position == 0) return(0);
- ft = timerset(0L);
- sprintf(junk_string,"%-7ld",zero.flen);
- curlocat(6,58);
- colrprtf(0,0,7,"%s",junk_string);
- closefil(send);
- openfil(outname,2,&send);
- setstamp(send,zero.fdate,zero.ftime);
- closefil(send);
- for(i=1;i<=40;i++)
- {
- curlocat(19,(21+i));
- colrprtf(0,15,7,"█");
- }
- ft = (long) (ft - st) /100L;
- if(ft == 0) ft = 1L;
- cps = (int) (zero.flen / ft);
- i = (connect_rate/100);
- if(i<=0)i=1;
- x = (cps*10) / i;
- curlocat(14,28);
- return_code = 0;
- colrprtf(0,0,7,"%3d%%",x);
- out = fopen(log_file,"a");
- fprintf(out,"%d bps %-14s %-7ld %3d%%\n",connect_rate,zero.fnam,zero.flen,x);
- fclose(out);
- return(1);
-
- }
- break;
- }
- if(!async_carrier(port)){
- goto abort;
- }
- }
- curlocat(7,28);
- sprintf(junk_string,"Timeout, Position %ld",file_position);
- colrprtf(0,0,7,"%-40s",junk_string);
- goto badblock;
- abort:
- async_rxflush(port);
- if(file_position > 0 )
- {
- closefil(send);
- openfil(outname,2,&send);
- setstamp(send,zero.fdate,zero.ftime);
- closefil(send);
- if(delete_aborted_transfers)
- unlink(outname);
- }
- else
- return(0);
- }
-
-
- readblock(buf,size) /* read a block of data */
- char *buf; /* data buffer */
- int size;
- {
- register n;
- unsigned long crc; /* CRC check values */
- long t1; /* short block timeout */
- int x,c;
- char *p = buf;
- char *s = buf;
-
- n = 0;
- c = size;
-
- while(n<size)
- {
- x = async_rxblk(port,s,c);
- n+=x,c-=x,s+=x;
- if(!(async_rxcnt(port)) && n<size)
- {
- t1 = timerset(1000L);
- while(!timeup(t1) && !async_rxcnt(port))
- {
- if(!async_carrier(port)) return(0);
- }
- if(timeup(t1)) return(0);
- }/* end if */
-
- } /* end while n<size */
- crc = 0xFFFFFFFF;
- for(n=0;n<size;n++){
- crc = UpdCrc32(((unsigned short) (*p++)), crc);
- }
-
- for (n = 4; --n >= 0;)
- {
- c = com_getc(2);
- crc = UpdCrc32(c, crc);
- }
- if (crc != 0xDEBB20E3)
- {
- return(0);
- }
- return(1);
- }
-
-
- com_putc(int t)
- {
-
- if(!async_carrier(port)) return;
-
- while((async_tx(port,t)) == R_TXERR)
- {
- if(!async_carrier(port)) return;
- }
-
-
- }
-
- sleep(delay)
- int delay;
- {
- long a;
- a = timerset( (long) (delay * 10L) );
- while(!timeup(a)) ;
- }
-
- clear_screen()
- {
- int x;
- curlocat(4,28); colrprtf(0,0,7,"%-19s","");
- curlocat(5,28); colrprtf(0,0,7,"%-14s","");
- curlocat(6,28); colrprtf(0,0,7,"%-8s","");
- curlocat(7,28); colrprtf(0,0,7,"%-40s","");
- curlocat(5,58); colrprtf(0,0,7,"%-3s","");
- curlocat(6,58); colrprtf(0,0,7,"%-7s","");
- for(x=1;x<=40;x++)
- {
- curlocat(19,(21+x));
- colrprtf(0,0,7,"▓");
- }
- }
-
- get_block_header(char *buf)
- {
- register n;
- unsigned long crc;
- int c,x;
- long t1;
- char *s = buf;
- char *p = buf;
-
- n = 0;
- c = 7;
-
- while(n<7)
- {
- x = async_rxblk(port,s,c);
- n+=x,c-=x,s+=x;
- if(!(async_rxcnt(port)) && n<7)
- {
- t1 = timerset(1000L);
- while(!timeup(t1) && !async_rxcnt(port))
- {
- if(!async_carrier(port)) return(0);
- }
- if(timeup(t1)) return(0);
- }/* end if */
-
- } /* end while n<7 */
- crc = 0xFFFFFFFF;
- for(n=0;n<7;n++){
- crc = UpdCrc32(((unsigned short) (*p++)), crc);
- }
-
- for (n = 4; --n >= 0;)
- {
- c = com_getc(2);
- crc = UpdCrc32(c, crc);
- }
-
- if (crc != 0xDEBB20E3)
- return(0);
-
- return(1);
-
- }
-
- send_block_header(char *b)
- {
- register n;
- unsigned long oldcrc;
- /*
-
- Considerable thought went into making the decision to split the
- header block apart from the actual data block.
-
- Including it as part of the block would mean that only one CRC
- would have to be sent, but considering that this information is
- critical to the proper reception of the block (size and file
- position), it could then be used for error repositioning and
- restarts without additional code (some computers will be running
- under memory restrictions that IBM Compatibles may not have), and
- finally... I couldn't tell any difference either way, speed wise.
-
- I elected to go for the additional reliablity and reduced code
- size and keep them apart.
-
- */
- while( (async_txblk(port, b, 7)) == R_TXERR) ;
-
- oldcrc = 0xFFFFFFFF;
- for(n=0;n<7;n++){
- oldcrc = UpdCrc32(((unsigned short) (*b++)), oldcrc);
- }
- oldcrc = ~oldcrc;
- for(n=4; --n >=0;)
- {
- com_putc((unsigned char) oldcrc);
- oldcrc >>= 8;
- }
- }
-
- get_system_info(char *buf)
- {
- register n;
- unsigned long crc;
- int c,x;
- long t1;
- char *s = buf;
- char *p = buf;
- curlocat(7,28);
- colrprtf(0,0,7,"%-40s","Waiting For System Information");
-
- n = 0;
- c = 128;
-
- while(n<128)
- {
- x = async_rxblk(port,s,c);
- n+=x,c-=x,s+=x;
- if(!(async_rxcnt(port)) && n<128)
- {
- t1 = timerset(1000L);
- while(!timeup(t1) && !async_rxcnt(port))
- {
- if(!async_carrier(port)) return(0);
- }
- if(timeup(t1)) return(0);
- }/* end if */
-
- } /* end while n<128 */
- crc = 0xFFFFFFFF;
- for(n=0;n<128;n++){
- crc = UpdCrc32(((unsigned short) (*p++)), crc);
- }
-
- for (n = 4; --n >= 0;)
- {
- c = com_getc(2);
- crc = UpdCrc32(c, crc);
- }
-
- if (crc != 0xDEBB20E3){
- com_putc('C');
- return(0);
- }
-
- com_putc(ACK);
- return(1);
- }
-
- send_system_info(char *b)
- {
- register n;
- int c,attempts=0;
- unsigned long oldcrc;
- char *p;
- curlocat(7,28);
- colrprtf(0,0,7,"%-40s","Sending System Information");
- async_rxflush(port);
- while(async_rxcnt(port)){
- async_rxflush(port);
- }
- for(;;)
- {
-
- while( (async_txblk(port, b, 128)) == R_TXERR) ;
-
- oldcrc = 0xFFFFFFFF;
- p = b;
- for(n=0;n<128;n++){
- oldcrc = UpdCrc32(((unsigned short) (*p++)), oldcrc);
- }
- oldcrc = ~oldcrc;
- for(n=4; --n >=0;)
- {
- com_putc((unsigned char) oldcrc);
- oldcrc >>= 8;
- }
- c = com_getc(5);
- if(c == ACK) return;
- if(++attempts > 3)
- {
- async_dtr(port,0);
- sleep(3);
- async_dtr(port,1);
- return;
- }/* Screw it, drop connection and try later */
-
- }/* End send system info */
-
- }
- cancel_transfer()
- {
-
- /* only 5 CAN's needed to abort a session, but I like
- to send more thans actual needed. */
-
- com_putc(CAN);
- com_putc(CAN);
- com_putc(CAN);
- com_putc(CAN);
- com_putc(CAN);
- com_putc(CAN);
- com_putc(CAN);
- com_putc(CAN);
- com_putc(CAN);
- com_putc(CAN);
- com_putc(CAN);
- com_putc(CAN);
- com_putc(CAN);
- com_putc(CAN);
- com_putc(CAN);
- com_putc(CAN);
- com_putc(CAN);
- com_putc(CAN);
- com_putc(CAN);
- com_putc(CAN);
- while(!async_txempty(port))
- if(!async_carrier(port)) break;
- }
- send_resync()
- {
- long t1,t2;
- int ch;
- struct _block_header block_header;
- /* Here is where the receiver does all of the
- actual resend requests.
-
- The receiver will send the resend request every 1.5
- seconds until the sender acknowledges that he has
- received it.
-
- At that point, the receiver will go into a wait loop
- eating everything that comes in until NO data is present
- before giving the sender the OK to resume sending.
-
- This means that when we go back to the receive file loop,
- the buffers will be clear and the next characters should
- be the start of the next block.
-
- This also gives us a chance to clear out any garbage caused
- by a burst of static on the phone line.
-
- */
- com_putc('C');com_putc('*');
- block_header.position = file_position;
- send_block_header(&block_header);
-
- t1 = timerset(150L);
- t2 = timerset(6000L);
-
- for(;;)
- {
- if(!async_carrier(port))
- return;
- if(timeup(t2))
- {/* if you can't get the sender to acknowledge your request
- within a reasonable time frame, 60 seconds in this
- case, then something is really wrong. Best to drop
- carrier and try later.
-
- 60 Seconds can be shorter if you like, but bare in mind
- that HSTs and TELEBITS are half duplex modems and can
- have some trouble turning around and accepting incoming
- data when there sending full bore.
-
- */
- async_dtr(port,0);
- return;
- }
- if(com_getc(2) == ACK) /* I *THINK* that this method is
- slightly faster than using a compound
- conditional statment, as least with
- my compiler. */
- {
- if(com_getc(1) == '*')
- {
- if(com_getc(1) == ACK)
- {
- if(com_getc(1) == '*')
- {
- t1 = timerset(1000L);
- while (readbyte(1) != 0)
- {
- ch = com_getc(0);
- if(timeup(t1))
- return;
- }
- com_putc(SOH);
- return;
- }
- }
- }
- }
- if(timeup(t1)){
- com_putc('C');com_putc('*');
- block_header.position = file_position;
- send_block_header(&block_header);
- t1 = timerset(150L);
- }
- }
- }
- readbyte(t)
- int t;
- {
- unsigned char c;
- long t1,t2;
- t2 = (long) t * 100;
- if (async_rxcnt(port))
- return(1);
- t1 = timerset (t2);
- while (!(async_rxcnt(port)))
- {
- if (timeup (t1))
- return (0);
- if(!async_carrier(port))
- {
- return(0);
- }
- }
-
- return(1);
-
-
- }
- GetNewPosition()
- {
- unsigned char c=0;
- struct _block_header block_header;
- long t1,t2;
- /* This routine retreives the new file position from
- the receiver when a REPOS is requested. Its returns 1 if
- it got the block header and 0 if it didn't.
-
- In this driver, the return code is only used if in ACK required
- mode.*/
-
- if(get_block_header(&block_header))
- {
-
- /* There really isn't any need to validate
- the file position since the block function is
- protected with its own 32 bit CRC. The likely
- hood of it being incorrect is so remote....*/
-
- /*
- One bad aspect to a streamed protocol is the
- possiblity of a HUGE amount of data being in
- the stream. In the tx buffer, the modem's
- tx buffer, on the actual phone line, and if a
- network or satellite link is involved that
- can add even more.
-
- I've seen the sender as far as 8K ahead of
- the receiver and at 2400 baud, that would
- take awhile to clear out before the resent
- block would ever make it there.
-
- This can make resyncing difficult to do as
- well as time consuming. Especially if the
- sender miss the first resend request. HST's
- and Telebits being half duplex sometimes don't
- work well when your pushing them near the
- maximum.
-
- In order to get the resyncs to work 100 percent
- of the time and as quickly as possible, complex
- resend request and verfication routines have to be
- used, complex compared to most protocols.
-
- Here you'll find that the sender once he's received
- the resend request is required to send the receiver
- an acknowledgement that he HAS processed it.
-
- The acknowledgement will consist of 4 bytes arranged
- so that the likely hood of running across the same
- pattern in the actual file data is small.
-
- The sender MUST wait until the receiver gives the
- all clear to resume sending. This helps keep both
- sides in sync with each other.
-
- If the sender doesn't get the OK to resume sending
- within 60 seconds, we must assume that the receiver
- has hung or there is so much line static that there
- isn't much hope of resuming. Our only recourse at
- that point is to drop carrier.
-
- */
- file_position = block_header.position;
- com_putc(ACK);/* 4 byte seq. to
- signal recevier
- that repos was
- received ok */
- com_putc('*');
- com_putc(ACK);
- com_putc('*');
- curlocat(7,28);
- sprintf(junk_string,"REPOS %ld",file_position);
- colrprtf(0,0,7,"%-40s",junk_string);
- curlocat(5,58);
- colrprtf(0,0,7,"%d",++total_errors);
- if(total_errors > 1 && block_len > 128)
- block_len >>= 1;
- if(block_len == 128 && !acks_required)
- acks_required = 1;
- t1 = timerset(6000L);
- t2 = timerset(200L);/* given the possiblity that
- the receiver sent more than
- one resend request and we
- missed the first, resend
- the resync acknowledgement no
- closer than 2 seconds apart.
- Anything received before
- that time we'll ignore.
- */
-
- while(!timeup(t1))
- {
- c = com_getc(1);
- if(c == 'C' && timeup(t2))
- {
- /* Must have missed it, send the
- resync acknowledgement again */
- com_putc(ACK);
- com_putc('*');
- com_putc(ACK);
- com_putc('*');
- t2 = timerset(200L);
- }
- if(c == SOH) return(1);
-
- } /* end t1 time loop */
- /* Receiver possible hung or lines are just flat to bad*/
- async_dtr(port,0);
- }
- sprintf(junk_string,"Bad Blk Header");
- colrprtf(0,0,7,"%-40s",junk_string);
- curlocat(5,58);
- return(0);