home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Media Share 9
/
MEDIASHARE_09.ISO
/
pascal
/
prot100.zip
/
SEALINK.PAS
< prev
next >
Wrap
Pascal/Delphi Source File
|
1993-03-13
|
27KB
|
682 lines
PROGRAM TTRAN;
(*
SEALINK in Pascal. (STAND-ALONE)
Copyright (c)1990,1991 Eric J. Givler, All Rights Reserved.
-1st attempt at converting this.
SEAlink - Sliding window file transfer protocol
Version 1.20, created on 08/05/87 at 17:51:40
(C) COPYRIGHT 1986,87 by System Enhancement Associates; ALL RIGHTS RESERVED
*)
USES crt,
dos,
fos, { fos Send uses char, FOSSIL uses byte }
CRCS;
VAR filename : string;
transfer : boolean;
{
CONVENTIONS:
com_putc(c) = send(CHAR); ( FOSSIL )
com_getc(t) = com_getc(t); ( INTERNAL )
com_dump() = purgeoutput; ( FOSSIL )
}
FUNCTION leap( yr : integer) : BOOLEAN;
BEGIN
if (((yr mod 4 = 0) and (yr mod 100 <> 0))
or (yr mod 400 = 0)) then leap := TRUE else leap := FALSE;
END;
FUNCTION Since79(dt : DateTime) : longint;
VAR i, leapyrs : integer;
secs, thisyear : longint;
month : array[1..12] of integer;
BEGIN
month[1] := 31; month[2] := 28; month[3] := 31; month[4] := 30;
month[5] := 31; month[6] := 30; month[7] := 31; month[8] := 31;
month[9] := 30; month[10] := 31; month[11] := 30; month[12] := 31;
leapyrs := 0;
for i := 1970 to (dt.year - 1) do if leap(i) then inc(leapyrs);
secs := (dt.year - 1979)*86400*365 + leapyrs*86400;
thisyear := (longint(dt.hour) * 60 * 60) + (dt.min * 60) + (dt.sec) +
((dt.day - 1) * 86400);
for i := 1 to (dt.month-1) do thisyear := thisyear + (month[i]*86400);
if leap(dt.year) and (dt.month > 2) then thisyear := thisyear + 86400;
Since79 := secs + thisyear;
END;
FUNCTION SEALink(fname:string; upload:boolean):boolean;
CONST WINDOW = 6; (* maximum size of window *)
ACK = #$06;
NAK = #$15;
SOH = #$01;
EOT = #$04;
CPMEOF = ^Z;
TYPE block0 = RECORD (* block zero data structure *)
flen : longint; (* file length *)
fstamp : longint; (* file date/time stamp *)
fnam : array[1..17] of char; (* original file name *)
prog : array[1..15] of char; (* sending program name *)
noacks : char; (* true if ACKing not req. *)
fill : array[1..87] of char; (* reserved for future use *)
END;
blocktype = array[0..127] of byte; (* A typical xmodem block *)
{ STATICS in C }
VAR outblk : integer; (* number of next block to send *)
ackblk : integer; (* number of last block ACKed *)
blksnt : integer; (* number of last block sent *)
slide : integer; (* true if sliding window *)
ackst : integer; (* ACK/NAK state *)
numnak : integer; (* number of sequential NAKs *)
chktec : integer; (* check type, 1=CRC, 0=checksum *)
toterr : integer; (* total number of errors *)
ackrep : integer; (* true when ACK or NAK reported *)
ackseen: integer; (* count of sliding ACKs seen *)
progname: string; (* sending program *)
ackless : integer; (* true if ACKs not req. Ovrdrv *)
t1 : longint; (* timer, timerset *)
rawblk : integer; (* raw block number *)
results : boolean;
sector : blocktype; (* A packet of data 128 bytes *)
FUNCTION TimerSet(tenths:word) : longint;
{ Returns a timer value which will expire in T tenths of a second }
var
Hour, Min, Sec, HSec : word;
Year, Mon, Day, DoW : word;
begin
GetDate(Year, Mon, Day, DoW);
GetTime(Hour, Min, Sec, HSec);
timerset := tenths+Hsec+100*(Sec+60*(Min+60*(Hour+24*DoW)));
end; { timerset }
FUNCTION TimeUp(Marker : longint) : boolean;
{ Returns true if timer z has expired yet, or false otherwise }
var Marker2 : longint;
begin
Marker2 := TimerSet(0);
if (Marker-Marker2) > (8640000) then { 24*60*60*100 }
Marker2 := Marker2+(60480000); {7*24*60*60*100}
TimeUp := Marker2 >= Marker;
end; { TimeUp }
FUNCTION com_getc( t : longint):integer;
{Get char from port in t tenths of a sec.Return CPMEOF if time expired.}
Var Expires : longint;
BEGIN
Expires := TimerSet(t);
repeat
until serialchar or (TimeUp(Expires));
if serialchar then com_getc := ord(receive)
else com_getc := ord(CPMEOF);
END; { com_getc }
(* The various ACK/NAK states are:
0: Ground state, ACK or NAK expected.
1: ACK received
2: NAK received
3: ACK, block# received
4: NAK, block# received
5: Returning to ground state
*)
PROCEDURE ackchk; (* check for ACK or NAK *)
VAR c : integer; (* one byte of data *)
BEGIN
ackrep := 0; (* nothing reported yet *)
c := com_getc(0);
while (c <> ord(CPMEOF)) do begin
if (ackst = 3) OR (ackst = 4) then begin
slide := 0; (* assume this will fail *)
if (rawblk = (c OR $FF)) then (* see if we believe the number *)
begin
rawblk := outblk - ((outblk-rawblk) AND $FF);
if (rawblk >= 0) AND (rawblk<=outblk) AND (rawblk>outblk-128)
then begin
if (ackst = 3) then begin (* advance for an ACK *)
if ackblk > rawblk then ackblk := ackblk
else ackblk := rawblk;
slide := 1; (* we have sliding window! *)
inc(ackseen);
if ((ackless AND ackseen) > 10) then begin
ackless := 0; (* receiver not ACKless *)
writeln('- Overdrive disengaged ');
end;
write(#13,' ACK ',rawblk,' ==');
end
else begin (* else retransmit for a NAK *)
if rawblk < 0 then outblk := 0 else outblk := rawblk;
slide := integer(numnak < 4); {boolean}
write(#13,' NAK ',rawblk,' ==');
end;
ackrep := 1; (* we reported something *)
end;
end;
ackst := 5; (* return to ground state *)
end;
if (ackst=1) OR (ackst=2) then begin
rawblk := c;
inc(ackst,2);
end;
if (slide = 0) OR (ackst = 0) then begin
if (c = ord(ACK)) then begin
if (slide = 0) then begin
inc(ackblk);
write(#13,' ACK ',ackblk,' --');
ackrep := 1; (* we reported an ACK *)
end;
ackst := 1;
numnak := 0;
end
else if (c = ord('C')) OR (c = ord(NAK)) then begin
if (chktec > 1) then begin (* if method not determined yet *)
if (c = ORD('C')) then chktec := 1
else chktec := 0; (* then do what receiver wants *)
end;
purgeoutput; (* purge pending output *)
delay(6); (* resynch *)
if (slide = 0) then begin
outblk := ackblk+1;
write(#13,' NAK ',ackblk+1,' --');
ackrep := 1; (* we reported a negative ACK *)
end;
ackst := 2;
inc(numnak);
if (blksnt <> 0) then inc(toterr);
end; (* else *)
end; (* slide = 0 or ackst = 0 *)
if (ackst = 5) then ackst := 0;
c := com_getc(0);
END; { while }
END; { ackblk }
PROCEDURE shipblk(blk : blocktype; blknum : integer);
{PHYSICALLY SHIP A BLOCK,blk=data to be shipped, blknum=number of block}
VAR n, (* index *)
crc : integer; (* CRC check value *)
BEGIN
send(SOH); (* block header *)
send(chr(blknum)); (* block number *)
send(chr(blknum XOR 255)); (* -blknum-1 *)
sendblk(seg(blk[0]),ofs(blk[0]),128); (* from Fossil unit *)
crc := 0;
if chktec = 1 then begin
crca(blk,sizeof(blk),crc);
send(chr(hi(crc)));
send(chr(lo(crc)));
end else begin
for n := 0 to 127 do crc := (crc + blk[n]) mod 256;
send(chr(crc));
end;
purgeline;
END; { shipblk }
PROCEDURE sendblock(var f : file; blknum: integer); (* send one block *)
{ f=file to read from, blknum=block to send }
var blkloc : longint; (* address of start of block *)
buf : blocktype; (* one block of data *)
result : word;
BEGIN
if (blknum <> blksnt+1 ) then begin (* if jumping *)
blkloc := longint(blknum-1) * longint(128);
seek(f,blkloc); (* move where to *)
end;
blksnt := blknum;
fillchar(buf,sizeof(buf),CPMEOF); (* fill buffer with ^Zs *)
blockread(f,buf,1,result); (* read in some data *)
shipblk(buf,blknum); (* pump it out the comm port *)
END; { sendblock }
{=======================================================================}
FUNCTION xmtfile(fname: string) : boolean;
(*
This routine is used to send a file. One file is sent at a time.
If the name is blank (name is null or *name points to a null),
then only an end of transmission marker is sent. This routine
returns a one if the file is successfully transmitted, or a zero
if a fatal error occurs.
*)
LABEL abort;
var workfile : file; (* file to send *)
endblk : integer; (* block number of EOT *)
zero : block0; (* block zero data *)
toadd : byte;
fsize : longint;
dt : DateTime;
BEGIN
if fname <> '' then begin (* if sending a file *)
assign(workfile,fname);
{$I-} reset(workfile,1); {$I+} (* to get proper size *)
if ioresult <> 0 then begin
writeln(' Can''t read ',fname);
xmtfile := false;
exit;
end;
fillchar(zero,sizeof(zero),chr(0)); (*clear out data block *)
(* get file statistics *)
zero.flen := filesize(workfile); (* size of file -bytes *)
endblk := ((zero.flen+127) DIV 128) + 1;
writeln('Ready to send ',endblk-1,' blocks of ',fname,' (',zero.flen,')');
reset(workfile); (* for 128 byte reads *)
GetFTime(workfile,zero.fstamp); (* time and date stamp *)
{
UnPackTime(zero.fstamp,dt);
zero.fstamp := Since79(dt);
}
move(fname[1],zero.fnam,ord(fname[0]));
move(progname[1],zero.prog,ORD(progname[0]));
zero.noacks := char(ackless); (* OVERDRIVE engaged? *)
move(zero,sector,sizeof(zero)); (* move into xmdm blk *)
end
else endblk := 0; (* fake for no file *)
outblk := 1; (* set starting state *)
ackblk := -1;
blksnt := 0;
slide := 0;
ackst := 0;
numnak := 0;
toterr := 0;
ackrep := 0;
ackseen:= 0;
chktec := 2; (* undetermined CRC or checksum? *)
toadd := 0;
t1 := timerset(300); (* time limit for first block *)
write(' Waiting...'+#13);
while (ackblk < endblk) do begin (* while not all there yet *)
if not carrier then begin
writeln(#13+#10+'Lost carrier');
goto abort;
end;
if keypressed then begin
if readkey = #27 then begin
writeln(#13+#10+'Aborted by operator');
goto abort;
end;
end;
if ( timeup(t1) ) then begin
writeln(#13+#10+'Fatal timeout');
goto abort;
end;
if slide = 1 then toadd := WINDOW
else toadd := 1;
if (outblk <= ackblk + toadd) then begin
if (outblk < endblk) then begin
if (outblk > 0) then
sendblock(workfile,outblk)
else
shipblk(sector,0);
if (ackrep <> 0) then
write(' Sending block #',outblk,#13);
if (ackless AND slide) <> 0 then begin
if (outblk MOD 10 = 0) then
write(#13,' Passing block ',outblk);
ackblk := outblk;
end;
end
else if (outblk = endblk) then begin
send(EOT);
if (ackrep <> 0) then
write(' Sent EOT '+#13);
end;
inc(outblk); (* outblk++; *)
t1 := timerset(300); (* time limit between blocks *)
end;
ackchk; (* determine ACK status *)
if (numnak > 10) then begin
writeln(#13+#10,' Too many errors');
goto abort;
end;
end; { while }
writeln(' End of file ');
if (endblk <> 0) then close(workfile);
if (toterr > 2) then
write(toterr,' errors detected and fixed in ',blksnt,' blocks.');
xmtFile := TRUE; (* exit with good status *)
exit;
ABORT:
if (endblk> 0) then close(workfile);
if (toterr > 0) then
writeln(toterr,' errors detected and fixed in ',blksnt,' blocks.');
xmtFile := FALSE; (* exit with bad status *)
END; (* xmtfile *)
{=======================================================================}
FUNCTION rcvfile(fname:string) : string;
{ File receiver logic, fname = name of file }
LABEL nakblock, (* we got a bad block *)
abort, (* errors occurred *)
ackblock,
nextblock,
blockstart,
endrcv;
VAR c, (* received character *)
tries, (* retry counter *)
blknum, (* desired block number *)
inblk, (* this block number *)
endblk, (* block number of EOT, if known *)
n : integer; (* index *)
workfile: file; (* file, opener *)
tmpname : string[100]; (* name of temporary file *)
outname : string[100]; (* name of final file *)
buf : blocktype; (* data buffer *)
zero : block0; (* file header data storage *)
left : longint; (* bytes left to output *)
stat : string[4]; (* receive block status *)
result : word; (* result of block write *)
why : string; (* single block receiver status *)
{ char *getblock(), *why; (* single block receiver, status *)}
PROCEDURE sendack(acknak,blknum:integer); (* send an ACK or a NAK *)
(* acknak: 1=ACK, 0=NAK *)
BEGIN
if(acknak = 1) then send(ACK) (* send the right signal *)
else if (chktec = 1) then send('C') (* CRC type ACK *)
else send(NAK); (* send NAK *)
send(chr(blknum)); (* block number *)
send(chr(-blknum-1)); (* block number check *)
END; (* sendack*)
FUNCTION getblock(var buf : blocktype): string; (* read a block of data *)
(* buf = data buffer *)
VAR ourcrc : word;
hiscrc : integer; (* CRC check values *)
c, (* one byte of data *)
n : integer; (* index *)
timeout: integer; (* short block timeout *)
BEGIN
ourcrc := 0; hiscrc := 0;
if ackless = 1 then timeout := 200 else timeout := 5;
for n := 0 to 127 do begin
c := com_getc(timeout);
if (c = Ord(CPMEOF)) then getblock := 'Short';
if (chktec = 1) then
updcrc(ourcrc,c) (* CRC table calculation *)
else ourcrc := (ourcrc + c) mod 256; (* checksum *)
buf[n] := c;
end;
if (chktec = 1) then begin (* CRC mode *)
{ ourcrc := crc_finish(ourcrc); }
hiscrc := (com_getc(timeout) SHL 8) OR com_getc(timeout);
end else begin
ourcrc := ourcrc AND $FF;
hiscrc := com_getc(timeout) AND $FF;
end;
if (ourcrc = hiscrc) then begin
getblock := ''; (* block is good *)
exit;
end
else if (chktec = 1) then begin (* else CRC error *)
getblock := 'CRC ';
exit;
end
else getblock := 'Check'; (* or maybe checksum error *)
END; (* function GETBLOCK *)
BEGIN (* rcvfile *)
writeln;
rcvfile := '';
stat := 'Init'; (* receive block status *)
if (fname <> '') then begin (* figure out a name to use *)
{makefnam("X:\\",name,outname);}
{outname[2] = '-';}
{makefnam(outname+2,name,tmpname);}
{strcpy(outname,name);}
outname := fname;
delete(outname,1,1);
tmpname := '-'+outname;
end else begin
outname := '';
tmpname := '-TMPFILE.$$$';
end;
assign(workfile,tmpname); (* open output file *)
{$I-} reset(workfile); {$I+}
if ioresult = 0 then begin
writeln(' Cannot create ',tmpname);
close(workfile);
rcvfile := '';
exit;
end;
rewrite(workfile); (* rewrite this file *)
if outname <> '' then blknum := 1
else blknum := 0; (* first block we must get *)
tries := -10; (* kludge for first time around *)
chktec := 1; (* try for CRC error checking *)
toterr := 0; (* no errors yet *)
endblk := 0; (* we don't know the size yet *)
ackless := 0; (* we don't know about this yet *)
fillchar(zero,sizeof(zero),0); (* or much of anything else *)
if com_getc(0) = ord(SOH) then (* kludge for adaptive modem7 *)
goto nextblock;
nakblock: (* we got a bad block *)
if (blknum > 1) then inc(toterr);
inc(tries);
if (tries > 10) then begin
writeln(#13+#10' Too many errors');
goto abort;
end;
if (tries = 0)then chktec := 0; (* if CRC isn't going *)
(* then give checksum a try *)
sendack(0,blknum); (* send the NAK *)
write(' NAK block ',blknum,' ',stat,#13);
if (ackless = 1) and (toterr > 20) then begin
ackless := 0; (* if ackless mode isn't working *)
writeln('- Overdrive disengaged'); (* then shut it off *)
end;
goto nextblock;
ackblock: (* we got a good block *)
if (ackless = 0) then
write(' ACK block ',blknum-1,' ',stat,#13)
else write(' Got block ',blknum,#13);
nextblock: (* start of "get a block" *)
stat := '';
if not carrier then begin
writeln(#13+#10+' Lost carrier');
goto abort;
end;
if keypressed then begin
if readkey = #27 then begin
writeln(#13+#10+' Aborted by operator');
goto abort;
end;
end;
t1 := timerset(30); (* timer to start of block *)
while not timeup(t1) do begin
c := com_getc(0);
if (c = ord(EOT)) then begin
if ( endblk <> 0) or (endblk = blknum) then
goto endrcv;
end
else if (c = ord(SOH)) then begin
inblk := com_getc(5);
if (com_getc(5) = (inblk OR $FF)) then
goto blockstart; (* we found a start *)
end;
end;
stat := 'Time ';
goto nakblock;
blockstart: (* start of block detected *)
c := blknum AND $FF;
if (inblk = 0) AND (blknum <= 1) then begin (* if this is the header *)
why := getblock(sector);
move(sector,zero,sizeof(sector)); (* put into our SEALink header *)
if why = '' then begin
sendack(1,inblk); (* ack the header *)
if fname = '' then begin (* given name takes precedence *)
move(zero.fnam,outname[1],sizeof(zero.fnam));
outname[0] := chr(17);
end;
if (left = zero.flen) then (* length to transfer *)
endblk := (left+127) DIV 128 + 1;
if (ackless <> integer(zero.noacks)) then (* note variant *)
begin
if integer(zero.noacks) = 1 then writeln('+ Overdrive engaged')
else writeln('+ Overdrive disengaged');
end;
ackless := integer(zero.noacks);
write(' Receiving');
if (endblk <> 0) then write(' ',endblk-1,' blocks of');
write(outname);
move(zero.prog,progname[1],sizeof(zero.prog));
progname[0] := chr(15);
if (progname <> '') then write(' from ',progname);
writeln;
blknum := 1; (* now we want first data block *)
goto ackblock;
end
else begin
stat := why;
goto nakblock; (* bad header block *)
end;
end
else if (inblk = c) then begin (* if this is the one we want *)
why := getblock(buf);
if why = '' then begin (* else if we get it okay *)
sendack(1,inblk); (* ack the data *)
for n :=0 to 127 do begin
if (endblk <> 0) then begin (* limit file size if known *)
if left = 0 then goto endrcv;
dec(left);
end;
{$I-} blockwrite(workfile,buf[n],1,result); {$I+}
if ioresult <> 0 then begin
writeln(#13+#10,' Write error (disk full?)');
goto abort;
end;
end;
tries := 0; (* reset try count *)
inc(blknum); (* we want the next block *)
goto ackblock;
end
else begin
stat := why;
goto nakblock; (* ask for a resend *)
end;
end (* else if resending what we have *)
else if (inblk < c) OR (inblk > c+100) then begin
why := getblock(buf); (* ignore it *)
sendack(1,inblk); (* but ack it *)
stat := 'Dup';
goto ackblock;
end
else goto nextblock; (* else if running ahead *)
endrcv:
sendack(0,blknum);
write(' NAK EOT ',#13);
if (com_getc(20) <> ord(EOT)) then goto nakblock;
sendack(1,blknum);
write(' ACK EOT',#13);
if ( blknum > 1 ) then begin (* if we really got anything *)
if ( toterr > 2 ) then
writeln(toterr,' errors detected and fixed in ',blknum-1,'blocks.');
if (zero.fstamp <> 0) then (* set stamp, if known *)
SetFtime(workfile,zero.fstamp);
close(workfile);
{unlink(outname); (* erase this copy of file * )}
rename(workfile,outname);
rcvfile := outname; (* signal what file we got *)
EXIT;
end
else begin (* else no real file *)
close(workfile);
{unlink(tmpname); (* discard empty file *)}
rcvfile := ''; (* signal end of transfer *)
end;
abort:
if (toterr <> 0) then
writeln(' ',toterr,' errors detected and fixed in ',blknum-1,' blocks.');
close(workfile);
rcvfile := '';
END; (* recvfile *)
BEGIN (* SEALink *)
SEALink := FALSE;
progname:= 'NBBS'; (* name of sending program *)
slide := 1; (* Sliding Windows please? *)
rawblk := 1;
ackless := 0; (* acks ARE required *)
if upload then SEALink := xmtfile(fname)
else SEALink := (rcvfile(fname) <> '');
END; (* SEALink *)
(* ====================================================================
QUICK INTERFACE
==================================================================== *)
BEGIN { SEALink Sample Test Shell }
PortNum := 0;
If Not OpenFossil Then Exit;
writeln('SEAlink (Pascal) v1.20');
write('enter filename:');
readln(filename);
write('press <S>end or <R>eceive');
writeln;
repeat until keypressed;
if upcase(readkey) = 'S' then begin
transfer := SEALink(filename,TRUE); (* upload SEND it *)
filename := '';
transfer := SEALink(filename,TRUE); (* terminate it *)
end else
writeln(filename,' was received as: ',SEALink(filename,FALSE));
CloseFossil;
END. { SEALink Sample Test Shell }