home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / archives / tpdoskermit.zip / kermit.pas < prev    next >
Pascal/Delphi Source File  |  1991-04-18  |  61KB  |  2,208 lines

  1. $R-,S-,I-,D+,T+,F-,V-,B-,N-
  2.  $R+,S+,I-,D+,T+,F-,V-,B-,N-
  3. $M $2000,$9000,$18000}     {8k STACK, 36k-96k HEAP
  4.  
  5. PROGRAM Kermits;
  6.  
  7. Uses MyDos, Crt, Timers, {Keyboard, }Async, Crcs, FeltEdit, FixAttr;
  8.  
  9. CONST
  10.   CpRt : String[40] = 'KERMIT file transfer. V1.1a TMa, NH 1988';
  11.  
  12.   DiskStopInt : BOOLEAN = FALSE;
  13.  
  14. (**********************************************************************)
  15. (*                                                                    *)
  16. (*                   Start for Kermits egne procedures                *)
  17. (*                                                                    *)
  18. (**********************************************************************)
  19.  
  20. VAR TotalTime, TotalBytes, SendBytes, ReceiveBytes : LongInt;
  21.     FileNr : WORD;
  22.  
  23. $I KERMIT.INC}            {Kermit const, type, var and some proc's.
  24.  
  25. PROCEDURE InitWindow;
  26. VAR i : WORD;
  27.     p : Pointer;
  28. BEGIN
  29.   FillChar(pw,SizeOf(pw),#0);
  30.   ninn := PakkeNr; nut := PakkeNr;
  31.   p := Next_Pac;
  32.  
  33.   FOR i := 0 TO 31 DO BEGIN
  34.     pw[i].dptr    := p;
  35.     pw[i+32].dptr := p;
  36.     Inc(Word(p),108);                  {Room for 95 char + fudge factor}
  37.   END;
  38.   GotoXY(33,10); WriteStr('Window:');
  39.   LongPakke := FALSE;
  40. END;                                   { InitWindow }
  41.  
  42. PROCEDURE Warning(msg : String);
  43. BEGIN
  44.   ScrollWin(41,14,80,24,-1,KermitAttr);
  45.   GotoXY(27,14); WriteStr('Last warning: '+msg);
  46. END;
  47.  
  48. TYPE Retry_Code = (r_ok, r_keyboard, r_timeout, r_exit);
  49. VAR r_code : Retry_Code;
  50.  
  51. FUNCTION Retry : Retry_Code;
  52. VAR ch : CHAR;
  53.     code : INTEGER;
  54.     enable : BOOLEAN;
  55. BEGIN
  56.   r_code := r_ok;
  57.   enable := FALSE;
  58.   IF KeyPress THEN BEGIN
  59.     BIOSKbd(-1,FALSE,ch,code);
  60.     IF (ch = #0) THEN
  61.       CASE code OF
  62.         45 : enable := TRUE;
  63.         59 : StopFile := TRUE;
  64.         67 : BEGIN
  65.                r_code := r_keyboard;
  66.                   enable := TRUE;
  67.              END;
  68.         68 : r_code := r_exit;
  69.       END;
  70.   END
  71.   ELSE IF NOT RunningTimer(t2) THEN BEGIN
  72.     r_code := r_timeout;
  73.     enable := TRUE;
  74.   END;
  75.   IF enable THEN BEGIN
  76.     RS_Enable(CurComPort);
  77.     StartLink;
  78.   END;
  79.   Retry := r_code;
  80. END;                                   {Retry}
  81.  
  82. PROCEDURE SendLink(VAR buf; n : WORD);
  83. LABEL Ferdig;
  84. VAR d : CharArray ABSOLUTE buf;
  85.     i, len : WORD;
  86.     ok : BOOLEAN;
  87.     ch : CHAR;
  88.     dptr : ^CHAR;
  89. BEGIN
  90.   Inc(SendBytes,n+2);
  91.   i := 10;
  92.   IF SendTimeOut > 0 THEN
  93.     i := SendTimeOut;
  94.   StartTimerSek(t2,i);
  95.   IF NOT WindowData THEN BEGIN
  96.     WHILE (RS_Buffer[CurComPort].HostXoff OR
  97.         NOT RS_Empty(CurComPort)) DO BEGIN
  98.       RS_ClrBuffer(CurComPort);
  99.       IF Retry <> r_ok THEN GOTO Ferdig;
  100.     END;
  101.     Delay(PacketDelay);             { Wait if neccessary! }
  102.   END;
  103.   REPEAT
  104.     IF Retry <> r_ok THEN GOTO Ferdig;
  105.     RS_Write(YourSOH,ok,CurComPort);
  106.   UNTIL ok;
  107.   IF CurBaud > 30000 THEN Delay(1);
  108.  
  109.   IF IBM_Mode = 1 THEN BEGIN
  110.     REPEAT
  111.       RS_BusyRead(ch,ok,CurComPort);
  112.       IF NOT ok THEN
  113.         IF Retry <> r_ok THEN GOTO Ferdig;
  114.     UNTIL ok AND (ch = YourSOH);
  115.     len := 1;
  116.     i := 1;
  117.     REPEAT
  118.       IF len <= n THEN BEGIN
  119.         RS_Write(d[len],ok,CurComPort);
  120.         IF ok THEN BEGIN
  121.           Inc(len);
  122.           Delay(SendDelay);
  123.         END;
  124.       END;
  125.       REPEAT
  126.         RS_BusyRead(ch,ok,CurComPort);
  127.         IF ok THEN BEGIN
  128.           IF (d[i] = ch) OR (d[i] = ' ') THEN
  129.             Inc(i);
  130.         END
  131.         ELSE
  132.           IF Retry <> r_ok THEN GOTO Ferdig;
  133.       UNTIL (len - i < 40) AND NOT ok;
  134.     UNTIL (len > n) AND (i > n);
  135.   END
  136.   ELSE BEGIN
  137.     dptr := Addr(d[1]);
  138.  
  139.     IF CurBaud > 30000 THEN BEGIN
  140.       len := MaxPrTick;
  141.       REPEAT
  142.         IF len > n THEN len := n;
  143.         RS_WriteBlock(dptr^,len,i,CurComPort);
  144.         Dec(n,len);
  145.         Inc(Word(dptr),len);
  146.         Delay(1);
  147.       UNTIL n = 0;
  148.     END
  149.     ELSE BEGIN
  150.       REPEAT
  151.         RS_WriteBlock(dptr^,n,i,CurComPort);
  152.         IF Retry <> r_ok THEN GOTO Ferdig;
  153.         Dec(n,i);
  154.         Inc(Word(dptr),len);
  155.       UNTIL n = 0;
  156.     END;
  157.   END;
  158.  
  159.   REPEAT
  160.     RS_Write(YourCR,ok,CurComPort);
  161.   UNTIL ok OR (Retry <> r_ok);
  162.  
  163. Ferdig:
  164.  
  165. END;                         { SendLink }
  166.  
  167. PROCEDURE GetLink(VAR buf; VAR n : WORD; max : WORD);
  168. LABEL Ferdig, Restart_Packet;
  169. VAR d : ARRAY [0..4000] OF CHAR ABSOLUTE buf;
  170.     bytes, i, x : WORD;
  171.     ch : CHAR;
  172.     done : BOOLEAN;
  173.     escape : STRING[10];
  174. BEGIN
  175.   StartTimerSek(t2,YourTimeOut);
  176.   ch := ' ';
  177.  
  178.   REPEAT
  179.     RS_BusyRead(ch,done,CurComPort);
  180.     IF NOT done THEN
  181.       IF Retry <> r_ok THEN GOTO Ferdig;
  182.     Inc(ReceiveBytes,Ord(done));
  183.   UNTIL (ch=MySOH);
  184.  
  185.   x := 3;
  186.  
  187. Restart_Packet:
  188.   n := 0;
  189.  
  190.   d[0] := '~';                   { len   = 94 }
  191.   d[3] := Chr(LenModulo+31);     { plen1 = 94/63 }
  192.   d[4] := Chr(LenModulo+31);     { plen2 = 94/63 }
  193.  
  194.   REPEAT
  195.     RS_ReadBlock(d[n],max - n,bytes,CurComPort);
  196.     Inc(ReceiveBytes,bytes);
  197.     IF bytes=0 THEN BEGIN
  198.       IF d[0] > ' ' THEN BEGIN
  199.         IF n > Ord(d[0]) - 32 THEN GOTO Ferdig;
  200.       END
  201.       ELSE
  202.         IF n > (Ord(d[3]) - 32) * LenModulo + Ord(d[4]) - 32 THEN GOTO Ferdig;
  203.       IF Retry <> r_ok THEN GOTO Ferdig;
  204.       Write_String(d[0],1,1,Byte_Stay,n,KermitAttr);    
  205.     END
  206.     ELSE IF NOT BinaryData AND (d[n] < ' ') THEN BEGIN
  207.       IF d[n] = MyCR THEN GOTO Ferdig;
  208.       IF d[n] = MySOH THEN BEGIN
  209.         GOTO Restart_Packet;
  210.       END;
  211.       IF (d[n] = ^[) AND (IBM_Mode > 0) THEN BEGIN
  212.         escape[0] := #0;
  213.         REPEAT                         { Read an Escape Seq's }
  214.           RS_BusyRead(ch,done,CurComPort);
  215.           IF NOT done THEN BEGIN
  216.             IF Retry <> r_ok THEN GOTO Ferdig;
  217.           END
  218.           ELSE
  219.             escape := escape + ch;
  220.         UNTIL done AND (ch IN ['@'..'Z','a'..'z']);
  221.  
  222.         Dec(escape[0]);
  223.         IF ch = 'H' THEN BEGIN
  224.           WHILE x < 81 DO BEGIN
  225.             Inc(x);
  226.             d[n] := ' ';
  227.             Inc(n);
  228.           END;
  229.           x := 1;
  230.           ch := escape[Length(escape)];
  231.           WHILE ch > '1' DO BEGIN
  232.             Inc(x);
  233.             d[n] := ' ';
  234.             Inc(n);
  235.             Dec(ch);
  236.           END;
  237.         END;
  238.       END;
  239.       { Ignore other control characters ! }
  240.     END
  241.     ELSE BEGIN
  242.       Inc(n,bytes);
  243.       IF IBM_Mode > 0 THEN BEGIN
  244.         Inc(x,bytes);
  245.         IF x > 81 THEN x := 81;
  246.       END;
  247.       IF (n >= max) THEN GOTO Ferdig;
  248.     END;
  249.   UNTIL FALSE;
  250.   Ferdig:
  251. END;                         { GetLink }
  252.  
  253. FUNCTION CheckSum(VAR buf; n, CheckType : WORD): WORD;
  254. BEGIN
  255.   IF CheckType <= 2 THEN BEGIN
  256.     n := ChkSum(buf,n);
  257.     IF CheckType = 1 THEN
  258.       CheckSum := (n + Lo(n) Shr 6) AND 63
  259.     ELSE
  260.       CheckSum := n AND $FFF;
  261.   END
  262.   ELSE { CRC }
  263.     CheckSum := CRC(buf,n);
  264. END;                         { CheckSum }
  265.  
  266. PROCEDURE SendPakkeT(VAR T : PakkeType);
  267. VAR s : WORD;
  268. BEGIN
  269.   IF T.long THEN BEGIN
  270.     T.plen := ' ';
  271.     T.plen1 := Chr(32 + (T.TotLen - 1) DIV LenModulo);
  272.     T.plen2 := Chr(32 + ((T.TotLen - 1) MOD LenModulo));
  273.     s := CheckSum(T.plen,5,1);
  274.     T.hchk := Chr(32 + s);
  275.   END
  276.   ELSE BEGIN
  277.     IF (T.TotLen > 95) OR (T.TotLen < 4) THEN BEGIN
  278.       WriteLn('Gal lengde: ',T.TotLen);
  279.       Exit;
  280.     END;
  281.     T.plen := Chr(31 + T.TotLen);
  282.   END;
  283.   s := CheckSum(T.plen,T.TotLen-CheckType,CheckType);
  284.   IF CheckType >= 2 THEN BEGIN
  285.     IF CheckType = 3 THEN
  286.       T.pdata[T.TotLen-5] := Chr(32 + (s Shr 12));
  287.     T.pdata[T.TotLen-4] := Chr(32 + ((s Shr 6) AND 63));
  288.   END;
  289.   T.pdata[T.TotLen-3] := Chr(32 + (s AND 63));
  290.   SendLink(T.plen,T.TotLen);
  291. END;                         { SendPakkeT }
  292.  
  293. PROCEDURE SendPakke;
  294. BEGIN
  295.   SendPakkeT(TX_Pac^);
  296. END;
  297.  
  298. PROCEDURE MakePakke(VAR p : PakkeType; nr : CarNum;
  299.                     typ : PakkeCh; data : String);
  300. BEGIN
  301.   p.pnr := Chr(32 + nr);
  302.   p.ptype := typ;
  303.   p.TotLen := Length(data) + 3 + CheckType;
  304.   p.plen := Chr(31 + p.TotLen);
  305.   p.long := FALSE;
  306.   Move(data[1],p.pdata,Length(data));
  307. END;                         { MakePakke }
  308.  
  309. FUNCTION TestPakke(VAR p : PakkeType): BOOLEAN;
  310. VAR chk, c : WORD;
  311. BEGIN
  312.   TestPakke := FALSE;
  313.   IF p.TotLen <= 2 + CheckType THEN BEGIN
  314.     IF p.TotLen > 0 THEN
  315.       Warning('Too short packet!')
  316.     ELSE IF (p.TotLen = 0) AND ShowTimeOut THEN
  317.       Warning('TimeOut!');
  318.     Exit;
  319.   END;
  320.   IF (p.ptype < 'A') OR (p.ptype > 'Z') THEN BEGIN
  321.     Warning('Error in packet type!');
  322.     Exit;
  323.   END;
  324.   IF p.plen > ' ' THEN BEGIN
  325.     chk := Ord(p.plen) - 32;
  326.     p.long := FALSE;
  327.   END
  328.   ELSE BEGIN
  329.     chk := CheckSum(p.plen,5,1);
  330.     IF chk <> Ord(p.hchk)-32 THEN BEGIN
  331.       Warning('Error in header checksum!');
  332.       Exit;
  333.     END;
  334.     chk := (Ord(p.plen1) - 32) * LenModulo + Ord(p.plen2) - 32;
  335.     p.long := TRUE;
  336.   END;
  337.   IF chk >= p.TotLen THEN BEGIN
  338.     Warning('Len error: '+Tstr(chk-p.TotLen-1,1));
  339.     Exit;
  340.   END;
  341.   p.TotLen := Succ(chk);
  342.   IF Ord(p.pnr) - 32 > 63 THEN Exit;
  343.   chk := CheckSum(p.plen,p.TotLen - CheckType,CheckType);
  344.   c := Ord(p.pdata[p.TotLen-3]) - 32;
  345.   IF CheckType >= 2 THEN BEGIN
  346.     Inc(c,(Ord(p.pdata[p.TotLen-4]) - 32) Shl 6);
  347.     IF CheckType = 3 THEN
  348.       Inc(c,(Ord(p.pdata[p.TotLen-5]) - 32) Shl 12);
  349.   END;
  350.   IF c = chk THEN
  351.     TestPakke := TRUE
  352.   ELSE
  353.     Warning('CHK err: Calc='+Tstr(chk,1)+', Rec='+Tstr(c,1));
  354. END;                                   {TestPakke}
  355.  
  356. PROCEDURE GetFast(VAR p; VAR len : WORD; max : WORD);
  357. LABEL Avbryt;
  358. VAR by : BYTE;
  359.     ch : CHAR;
  360.     ok : BOOLEAN;
  361.     dptr : ^BYTE;
  362.     md, dend, bytes, receive, status : WORD;
  363.     count : WORD;
  364. BEGIN
  365.   StartTimerSek(t2,YourTimeOut);
  366.   dptr := Addr(p);
  367.   dend := Word(dptr) + max;
  368.  
  369.   receive := RS_Buffer[CurComPort].ICadr;
  370.   status := receive + 5;
  371.  
  372.   count := MaxPrTick;
  373.   ch := #255;
  374.   REPEAT
  375.     IF (Retry <> r_ok) OR NOT RunningTimer(t2) THEN GOTO Avbryt;
  376.     RS_BusyRead(ch,ok,CurComPort);
  377.     Inc(ReceiveBytes,Ord(ok));
  378.   UNTIL ch = MySOH;
  379.  
  380.   RS_Set_TX_Int(0,CurComPort);
  381.   InLine($FA);                         {CLI}
  382.  
  383.   Port[receive+1] := 0;                {Turn off all Serial int's}
  384.  
  385.   md := 2000;                          {Wait up to 8 ms for first char.}
  386.   REPEAT
  387.     repeat
  388.       Dec(md);
  389.       if md = 0 then goto avbryt;
  390.     until Odd(Port[status]);           {Received data available}
  391.  
  392.     dptr^ := Port[receive];
  393.     Inc(Word(dptr));
  394.     md := 200;                         { >1 ms delay between two chars}
  395.     Dec(count);
  396.     IF count = 0 THEN BEGIN
  397.       InLine($FB);
  398.         md := 2000;
  399.         count := MaxPrTick;
  400.       InLine($FA);
  401.     END;
  402.   UNTIL Word(dptr) >= dend;
  403.  
  404. Avbryt:
  405.   InLine($FB);
  406.   Port[receive+1] := RX_int+RLS_int;   {Turn off all Serial int's}
  407.  
  408.   len := Word(dptr) - Ofs(p);
  409.   Inc(ReceiveBytes,len);
  410. END;
  411.  
  412. PROCEDURE GetPakke;
  413. VAR max : WORD;
  414. BEGIN
  415.   IF LongPakke THEN max := 9030 ELSE max := 95;
  416.   IF (CurBaud > 30000) THEN
  417.     GetFast(RX_Pac^.plen,RX_Pac^.TotLen,max)
  418.   ELSE
  419.     GetLink(RX_Pac^.plen,RX_Pac^.TotLen,max);
  420.   IF r_code = r_ok THEN BEGIN
  421.     IF NOT TestPakke(RX_Pac^) THEN BEGIN
  422.       MakePakke(RX_Pac^,PakkeNr,'T','P');
  423.     END;
  424.   END
  425.   ELSE IF r_code = r_keyboard THEN
  426.     MakePakke(RX_Pac^,PakkeNr,'T','K')
  427.   ELSE IF r_code = r_timeout THEN
  428.     MakePakke(RX_Pac^,PakkeNr,'T','T')
  429.   ELSE IF r_code = r_exit THEN
  430.     MakePakke(RX_Pac^,PakkeNr,'E','F10')
  431.   ELSE BEGIN
  432.     Warning('r_code error!');
  433.     MakePakke(RX_Pac^,PakkeNr,'T','R');
  434.   END;
  435. END;                         { GetPakke }
  436.  
  437. PROCEDURE Extract(VAR st : String);
  438. VAR i, l : WORD;
  439. BEGIN
  440.   i := 1;
  441.   IF RX_Pac^.long THEN i := 4;
  442.  
  443.   l := RX_Pac^.TotLen - i - 2 - CheckType;
  444.   IF l >= SizeOf(st) THEN l := SizeOf(st) - 1;
  445.   st[0] := Chr(l);
  446.   Move(RX_Pac^.pdata[i],st[1],l);
  447. END;                         { Extract }
  448.  
  449. PROCEDURE DumpPointers;
  450. CONST NackCh : ARRAY [0..10] OF CHAR = '-123456789A';
  451. VAR n, i : WORD;
  452. BEGIN
  453.   st[0] := #31;
  454.   FillChar(st[1],31,' ');
  455.   n := nut;
  456.   FOR i := 1 TO (ninn-nut) AND 63 DO BEGIN
  457.     st[i] := NackCh[pw[n].retry];
  458.     n := Succ(n) AND 63;
  459.   END;
  460.   GotoXY(41,10); WriteStr(st);
  461. END;
  462.  
  463. PROCEDURE MakeInfoScreen(s : String);
  464. BEGIN
  465.   ClrAll;
  466.   ClrLast;
  467.   GotoXY(30,6); WriteStr('File name:');
  468.   GotoXY(22,7); WriteStr('Bytes transferred:');
  469.   GotoXY(30,9); WriteStr(s);
  470.   GotoXY(22,11); WriteStr('Number of packets:');
  471.   GotoXY(22,12); WriteStr('Number of retries:');
  472.   GotoXY(29,13); WriteStr('Last error:');
  473.   GotoXY(1,25); WriteStr('Kermit:  F1=Cancel File');
  474.  
  475.   GotoXY(61,MaxY);  WriteStr('F9=Retry  F10=Abort');
  476. END;  { MakeInfoScreen }
  477.  
  478. PROCEDURE WriteFileName;
  479. BEGIN
  480.   GotoXY(41,6);
  481.   IF OriginalName <> FileName THEN
  482.     WriteStr(Pad(OriginalName + ' as '+FileName,40))
  483.   ELSE
  484.     WriteStr(Pad(FileName,40));
  485. END;
  486.  
  487. PROCEDURE WriteBytes;
  488. BEGIN
  489.   GotoXY(41,7); Write(Bytes);
  490. END;
  491.  
  492. PROCEDURE WriteFileSize;
  493. BEGIN
  494.   GotoXY(30,8); Write('File size: ',FileMax); ClrEol;
  495. END;  { WriteSize }
  496.  
  497. PROCEDURE WriteStatus;
  498. BEGIN
  499.   GotoXY(41,9); WriteStr(StatusString); ClrEol;
  500. END;
  501.  
  502. PROCEDURE WriteTotalNr;
  503. BEGIN
  504.   Inc(TotalNr);
  505.   GotoXY(41,11); Write(TotalNr);
  506. END;  { WriteTotalNr }
  507.  
  508. PROCEDURE WriteFeilNr;
  509. BEGIN
  510.   Inc(FeilNr);              {Auto-Increment FeilNr}
  511.   GotoXY(41,12); Write(FeilNr);
  512. END;
  513.  
  514. PROCEDURE WriteError;
  515. BEGIN
  516.   GotoXY(41,13); WriteStr(Pad(ErrorString,57));
  517.   RS_ClrBuffer(CurComPort);
  518. END;
  519.  
  520. PROCEDURE ZeroBytes;
  521. BEGIN
  522.   Bytes := 0;
  523.   GotoXY(41,7); ClrEol;
  524. END;
  525.  
  526. PROCEDURE AddBytes(n : WORD);
  527. BEGIN
  528.   Bytes := Bytes + n;
  529.   WriteBytes;
  530. END;                                   {AddBytes}
  531.  
  532. PROCEDURE SendPacket(PakkeNr : CarNum; typ : PakkeCh; st : String);
  533. BEGIN
  534.   MakePakke(TX_Pac^, pakkenr, typ, st);
  535.   SendPakke;
  536. END;                         { SendPacket }
  537.  
  538. PROCEDURE SendAbort(s : String);
  539. BEGIN
  540.   ErrorString := s;
  541.   WriteError;
  542.   SendPacket(PakkeNr,'E',s);
  543. END;                         { SendAbort }
  544.  
  545. PROCEDURE MakeNextData; FORWARD;
  546.  
  547. TYPE KermitState = (Abort, Complete, SendInit, SendName,
  548.                     SendAttr, SendData, SendEOF,
  549.                     SendEnd, WaitInit, WaitName, WaitData, TimeOut);
  550.  
  551. PROCEDURE SendAndGet(VAR s : KermitState; OkState : KermitState;
  552.                          data : BOOLEAN);
  553. VAR Ferdig : BOOLEAN;
  554.     nr : WORD;
  555. BEGIN
  556.   RetryNr := 0; Ferdig := FALSE;
  557.   REPEAT
  558.     SendPakke;
  559.     IF data THEN
  560.       MakeNextData;
  561.     GetPakke;
  562.     WITH RX_Pac^ DO BEGIN
  563.       nr := Ord(pnr) - 32;
  564.       IF ((ptype = 'Y') AND (nr = PakkeNr)) OR
  565.          ((ptype = 'N')) AND (nr = Succ(PakkeNr) AND 63) THEN BEGIN
  566.         Ferdig := TRUE;
  567.         s := OkState;
  568.         PakkeNr := Succ(PakkeNr) AND 63;
  569.         WriteTotalNr;
  570.       END
  571.       ELSE IF (ptype IN ['N','T']) OR (ptype = TX_Pac^.ptype) THEN BEGIN
  572.         Inc(RetryNr);
  573.         WriteFeilNr;
  574.         Warning(ptype+'-packet received!');
  575.         IF RetryNr >= RetryLimit THEN BEGIN
  576.           Ferdig := TRUE;
  577.           s := Abort;
  578.           SendAbort('Too many retries!');
  579.         END;
  580.       END
  581.       ELSE IF ptype = 'E' THEN BEGIN
  582.         Ferdig := TRUE;
  583.         s := Abort;
  584.         Extract(ErrorString);
  585.         WriteError;
  586.       END
  587.       ELSE IF (nr = PakkeNr) OR (nr = Succ(PakkeNr) AND 63) THEN BEGIN
  588.         SendAbort('Wrong packet type: '+ptype);
  589.         ptype := 'E';
  590.         Ferdig := TRUE;
  591.         s := Abort;
  592.       END;
  593.     END;
  594.   UNTIL Ferdig;
  595.   IF s = Abort THEN ErrorLevel := 2;
  596. END;                         { SendAndGet }
  597.  
  598. CONST
  599.       Reserved1Bit = 32;
  600.       Reserved2Bit = 16;
  601.       A_PacketBit  =  8;
  602.       WindowBit    =  4;
  603.       LongPakkeBit =  2;
  604.  
  605.       BinaryDataBit= 32;
  606.  
  607. PROCEDURE MakeInitPacket(Ptyp : PakkeCh);
  608. VAR s : String;
  609.     b : BYTE;
  610. BEGIN
  611.   s := Pad('',14);
  612.   IF LongMaxLength < 95 THEN BEGIN
  613.     s[1] := Chr(32 + (LongMaxLength));
  614.     LongPakke := FALSE;
  615.   END
  616.   ELSE
  617.     s[1] := '~';
  618.   IF Ptyp = 'Y' THEN
  619.     IF Abs(YourTimeOut-MyTimeOut) < 2 THEN
  620.       MyTimeOut := YourTimeOut - 2
  621.   ELSE
  622.     AttrPakke := TRUE;
  623.   s[2] := Chr(32 + (MyTimeOut));
  624.   s[3] := Chr(32 + (MyPad));
  625.   s[4] := Chr(64 XOR Ord(MyPadChar));
  626.   s[5] := Chr(32 + (Ord(MyCR)));
  627.   s[6] := MyQCtrlChar;
  628.   s[7] := Q8BitChar;
  629.   IF (Ptyp = 'S') AND (CurBits=8) THEN
  630.     s[7] := 'Y'
  631.   ELSE IF (Ptyp = 'Y') AND NOT Q8Bit THEN
  632.     s[7] := 'N';
  633.   s[8] := Chr(FileCheck+48);
  634.   s[9] := QrepChar;
  635.  
  636.   b := A_PacketBit + 1;
  637.   IF LongPakke THEN BEGIN
  638.     b := b OR LongPakkeBit;
  639.     s[13] := Chr(32 + (LongMaxLength DIV LenModulo));
  640.     s[14] := Chr(32 + (LongMaxLength MOD LenModulo));
  641.   END;
  642.   IF WindowData THEN BEGIN
  643.     b := b OR WindowBit;
  644.     s[12] := Chr(32 + WinSize);
  645.   END;
  646.   s[10] := Chr(b+32);
  647.   b := 0;
  648.   IF BinaryData THEN b := BinaryDataBit;
  649.   s[11] := Chr(b+32);
  650.   MakePakke(TX_Pac^, 0, ptyp, s);
  651. END;                         { MakeInitPacket }
  652.  
  653. PROCEDURE TolkInitPacket;
  654. VAR c, l, w, a2 : INTEGER;
  655.     s    : String;
  656. BEGIN
  657.   Extract(s);
  658.   s := Pad(s,30);
  659.   YourMaxLength := Ord(s[1]) - 32;
  660.   IF s[2] > ' ' THEN YourTimeOut := -32 + Ord(s[2]);
  661.   IF RX_Pac^.ptype <> 'Y' THEN
  662.     IF Abs(YourTimeOut-MyTimeOut) < 2 THEN
  663.       MyTimeOut := YourTimeOut - 2;
  664.   YourPad := -32 + Ord(s[3]);
  665.   YourPadChar := Chr(64 XOR Ord(s[4]));
  666.   IF s[5] > ' ' THEN YourCR := Chr(Ord(s[5]) - 32);
  667.   IF s[6] > ' ' THEN YourQCtrlChar := s[6];
  668.   IF s[7] IN ['!'..'>',#96..'~'] THEN BEGIN
  669.     Q8bitChar := s[7];
  670.     Q8bit := TRUE;
  671.   END
  672.   ELSE Q8bit := (s[7] = 'Y') AND (CurBits=7);
  673.   CASE s[8] OF
  674.     '2' : FileCheck := 2;
  675.     '3' : FileCheck := 3;
  676.     ELSE
  677.       FileCheck := 1;
  678.   END;
  679.   Qrep := s[9] = QrepChar;
  680.   IF Qrep THEN maxrep := 94 ELSE maxrep := 1;
  681.   c := Ord(s[10]) - 32;
  682.   a2 := 0;
  683.   IF Odd(c) THEN a2 := Ord(s[11]) - 32;
  684.   l := 10;
  685.   WHILE Odd(Ord(s[l])) DO Inc(l);         {skip all other attribute bits}
  686.   WindowData := WindowData AND (c AND WindowBit <> 0);
  687.   IF WindowData THEN BEGIN
  688.     WinSize := Ord(s[l+1]) - 32;      {We can accept any size up to 31}
  689.     WindowData := WinSize > 1;
  690.   END;
  691.   LongPakke := LongPakke AND (c AND LongPakkeBit <> 0) AND NOT WindowData;
  692.   AttrPakke := AttrPakke AND (c AND A_PacketBit <> 0);
  693.   IF LongPakke THEN BEGIN
  694.     l := (Ord(s[l+2]) - 32) * LenModulo + Ord(s[l+3]) - 32;
  695.     IF l = 0 THEN
  696.       LongMaxLength := 500
  697.     ELSE IF l < LongMaxLength THEN
  698.       LongMaxLength := l;
  699.   END;
  700.   BinaryData := BinaryData AND (a2 AND BinaryDataBit <> 0);
  701. END;                                   {TolkInitPacket}
  702.  
  703. PROCEDURE XmitAttr(VAR state : KermitState);
  704. VAR siz : String[12];
  705. BEGIN
  706.   UnPackTime(DTA.Time,FTime);
  707.  
  708.   Str((FileMax + 1023) DIV 1024:1,st);
  709.   Str(FileMax:1,siz);
  710.   st := '#/861124 14:56:30!'+Chr(32+Length(st))+
  711.         st+'1'+Chr(32+Length(siz))+siz;
  712.  
  713.   ByteToDigits(FTime.year MOD 100,st[3]);
  714.   ByteToDigits(FTime.month,st[5]);
  715.   ByteToDigits(FTime.day,st[7]);
  716.   ByteToDigits(FTime.hour,st[10]);
  717.   ByteToDigits(FTime.min,st[13]);
  718.   ByteToDigits(FTime.sec,st[16]);
  719.  
  720.   MakePakke(TX_Pac^, PakkeNr,'A',st);
  721.   SendAndGet(state,SendData,FALSE);
  722.   IF (state = SendData) THEN BEGIN
  723.     Extract(st);
  724.     IF (Length(st) > 0) AND (st[1] = 'N') THEN BEGIN
  725.       StopFile := TRUE;
  726.       state := SendEOF;
  727.     END;
  728.   END;
  729. END;
  730.  
  731. PROCEDURE XmitEOF(VAR s : KermitState);
  732. BEGIN
  733.   Inc(TotalBytes,FilePos(fil));
  734.   Close(fil);
  735.   Debug('Enter XmitEOF'); 
  736.   IF StopFile THEN BEGIN
  737.     MakePakke(TX_Pac^, PakkeNr,'Z','D');
  738.     Warning(FileName+' discarded!');
  739.   END
  740.   ELSE
  741.     MakePakke(TX_Pac^, PakkeNr,'Z','');
  742.   SendAndGet(s,SendName,FALSE);
  743. END;                         { XmitEOF }
  744.  
  745. PROCEDURE XmitEnd(VAR s : KermitState);
  746. BEGIN
  747.   MakePakke(TX_Pac^, PakkeNr,'B','');
  748.   SendAndGet(s,Complete,FALSE);
  749. END;                         { XmitEnd }
  750.  
  751. TYPE STRING3 = RECORD
  752.                  CASE BOOLEAN OF
  753.                    FALSE: (st : STRING[3]);
  754.                    TRUE:  (p  : Pointer);
  755.                END;
  756.  
  757. VAR CodeTab : ARRAY [CHAR] OF STRING3;
  758.  
  759. PROCEDURE MakeCodeTab;
  760. TYPE Str3Ptr = ^String3;
  761. VAR lch, ch : CHAR;
  762.     b       : WORD;
  763.     CodePtr : Str3Ptr;
  764.     st      : ARRAY [0..3] OF CHAR;
  765.     len     : BYTE ABSOLUTE st;
  766. BEGIN
  767.   CodePtr := @CodeTab;
  768.   FOR b := 0 TO 255 DO BEGIN
  769.     ch := Chr(b);
  770.     lch := Chr(b AND 127);
  771.     len := 0;
  772.     IF (ch > #127) AND Q8Bit THEN BEGIN
  773.       len := 1;
  774.       st[1] := Q8BitChar;
  775.       ch := lch;
  776.     END;
  777.     IF (Succ(b) AND 127) <= 32 THEN BEGIN
  778.       Inc(len);
  779.       st[len] := YourQCtrlChar;
  780.       ch := Chr(64 XOR Ord(ch));
  781.     END
  782.     ELSE IF ((lch = Q8BitChar) AND Q8Bit) OR ((lch = QrepChar) AND Qrep) OR
  783.        (lch = YourQCtrlChar) THEN BEGIN
  784.       Inc(len);
  785.       st[len] := YourQCtrlChar;
  786.     END;
  787.     Inc(len);
  788.     st[len] := ch;
  789.  
  790.     CodePtr^ := String3(st);
  791.     Inc(Word(CodePtr),SizeOf(String3));
  792.   END;
  793. END;                                   {MakeCodeTab}
  794.  
  795. PROCEDURE MakeDataPac(VAR p : PakkeType);
  796. LABEL Avbryt;
  797. VAR ch : CHAR;
  798.     st : STRING[3];
  799.     pst : Pointer ABSOLUTE st;
  800.     n, max, databytes : WORD;
  801.     dptr : ^CHAR;
  802. BEGIN
  803.   p.ptype := 'D';
  804.   p.pnr := Chr(32 + PakkeNr);
  805.   dptr := @p.pdata[1];
  806.   IF LongPakke THEN BEGIN
  807.     Inc(Word(dptr),3);                 {Skip over long header}
  808.     max := LongMaxLength - 7 - CheckType;
  809.     p.long := TRUE;
  810.   END
  811.   ELSE BEGIN
  812.     max := YourMaxLength - 7 - CheckType;
  813.     p.long := FALSE;
  814.   END;
  815.  
  816.   databytes := 0;
  817.   IF EndOfFile THEN GOTO Avbryt;
  818.  
  819.   IF BinaryData THEN BEGIN
  820.     Inc(max,4);
  821.     IF BufCount < max THEN BEGIN
  822.       IF BufCount > 0 THEN BEGIN
  823.         Move(BufPtr^,dptr^,BufCount);
  824.         Inc(Word(dptr),BufCount);
  825.         Inc(databytes,BufCount);
  826.         Dec(max,BufCount);
  827.       END;
  828.  
  829.       BlockRead(fil,buffer^,BufSize,BufCount);
  830.  
  831.       IF (IOresult <> 0) OR (BufCount = 0) THEN BEGIN
  832.         EndOfFile := TRUE;
  833.         GOTO Avbryt;
  834.       END;
  835.       BufferPtr(BufPtr) := Buffer;
  836.       IF max > BufCount THEN max := BufCount;
  837.     END;
  838.     Move(BufPtr^,dptr^,max);
  839.     Inc(Word(BufPtr),max);
  840.     Dec(BufCount,max);
  841.     Inc(Word(dptr),max);
  842.     Inc(databytes,max);
  843.     GOTO Avbryt;
  844.   END;
  845.  
  846.   max := Ofs(p.pdata[max]);
  847.  
  848.   REPEAT
  849.     IF BufCount = 0 THEN BEGIN
  850.       StopLink;
  851.       BlockRead(fil,buffer^,BufSize,BufCount);
  852.       StartLink;
  853.       IF (IOresult <> 0) OR (BufCount = 0 ) THEN BEGIN
  854.         EndOfFile := TRUE;
  855.         GOTO AvBryt;
  856.       END;
  857.       BufferPtr(BufPtr) := Buffer;
  858.       buffer^[BufCount] := Chr(NOT Ord(buffer^[BufCount - 1]));  {guard!}
  859.     END;
  860.     ch := BufPtr^;
  861.     n := 1;
  862.     Inc(Word(BufPtr));
  863.     Dec(BufCount);
  864.     WHILE (ch = BufPtr^) AND (n < MaxRep) DO BEGIN
  865.       Inc(n);
  866.       Inc(Word(BufPtr));
  867.       Dec(BufCount);
  868.     END;
  869.     IF TextFile THEN BEGIN
  870.       ch := UtConvert[ch];
  871.       IF ch = ^Z THEN BEGIN
  872.         EndOfFile := TRUE;
  873.         Goto Avbryt;
  874.       END;
  875.     END;
  876.     Inc(databytes,n);
  877.     pst := CodeTab[ch].p;   {st := CodeTab[ch].st;}
  878.     IF (n = 2) AND (st[0] = #1) THEN BEGIN
  879.       dptr^ := st[1];
  880.       Inc(Word(dptr));
  881.       dptr^ := st[1];  {repeat 2 times!}
  882.       Inc(Word(dptr));
  883.     END
  884.     ELSE BEGIN
  885.       IF n >= 2 THEN BEGIN
  886.         dptr^ := QrepChar;
  887.         Inc(Word(dptr));
  888.         dptr^ := Chr(n+32);
  889.         Inc(WORD(dptr));
  890.       END;
  891.  
  892.       dptr^ := st[1];
  893.       Inc(WORD(dptr));
  894.       IF st[0] > #1 THEN BEGIN
  895.         dptr^ := st[2];
  896.         Inc(WORD(dptr));
  897.         IF st[0] > #2 THEN BEGIN
  898.           dptr^ := st[3];
  899.           Inc(WORD(dptr));
  900.         END;
  901.       END;
  902.     END;
  903.   UNTIL Word(dptr) >= max;
  904. Avbryt:
  905.   IF databytes = 0 THEN
  906.     p.TotLen := 0
  907.   ELSE BEGIN
  908.     AddBytes(databytes);
  909.     p.TotLen := Word(dptr) - Ofs(p.plen) + CheckType;
  910.   END;
  911. END;                                   {MakeDataPac}
  912.  
  913. PROCEDURE MakeNextData;
  914. BEGIN
  915.   IF NOT Next_Data_OK AND (CurBaud < 30000) THEN BEGIN
  916.     MakeDataPac(Next_Pac^);
  917.     Next_Data_OK := TRUE;
  918.   END;
  919. END;
  920.  
  921. PROCEDURE MakeData;
  922. VAR temp : PakkeTypePtr;
  923. BEGIN
  924.   IF Next_Data_OK THEN BEGIN
  925.     temp := TX_Pac;
  926.     TX_Pac := Next_Pac;
  927.     Next_Pac := temp;
  928.  
  929.     TX_Pac^.pnr := Chr(32 + PakkeNr);
  930.     Next_Data_OK := FALSE;
  931.   END
  932.   ELSE
  933.     MakeDataPac(TX_Pac^);
  934. END;                         { MakeData }
  935.  
  936. PROCEDURE Ack(PakkeNr : WORD);
  937. BEGIN
  938.   SendPacket(PakkeNr,'Y','');
  939. END;
  940.  
  941. PROCEDURE Nack(PakkeNr : WORD);
  942. BEGIN
  943.   SendPacket(PakkeNr,'N','');
  944. END;
  945.  
  946. VAR state : KermitState;
  947.     NackedNr : WORD;
  948.     RX_Start : BOOLEAN;
  949.  
  950. PROCEDURE InitLesPakke;
  951. BEGIN
  952.   StartTimerSek(t2,YourTimeOut);
  953.   RX_Start := TRUE;
  954. END;
  955.  
  956. PROCEDURE LesPakke(VAR RX: PakkeType; VAR ok : BOOLEAN);
  957. LABEL Ferdig, Init;
  958. VAR bytes, n : WORD;
  959.     buf : ARRAY [-3..100] OF CHAR ABSOLUTE RX;
  960. BEGIN
  961.   ok := FALSE;
  962.   WITH RX DO BEGIN
  963.     IF Retry <> r_ok THEN BEGIN
  964.       IF r_code = r_timeout THEN
  965.         MakePakke(RX,nut,'T','T')
  966.       ELSE IF r_code = r_keyboard THEN
  967.         MakePakke(RX,nut,'T','K')
  968.       ELSE
  969.         MakePakke(RX,nut,'E','F10');
  970.       ok := TRUE;
  971.       GOTO Init;
  972.     END;
  973.     IF RX_Start THEN BEGIN
  974.       n := 100;
  975.       REPEAT
  976.         Dec(n);
  977.         IF n = 0 THEN Exit;
  978.         RS_ReadBlock(plen,96,bytes,CurComPort);
  979.         IF bytes = 0 THEN Exit;
  980.         Inc(ReceiveBytes,bytes);
  981.       UNTIL plen = MySOH;
  982.       RX_Start := FALSE;
  983.       TotLen := 0;
  984.       plen := '~';
  985.     END;
  986.     REPEAT
  987.       RS_ReadBlock(buf[TotLen],96-TotLen,bytes,CurComPort);
  988.       IF bytes = 0 THEN BEGIN
  989.         IF TotLen > Ord(plen) - 32 THEN GOTO Ferdig;
  990.         Exit;
  991.       END;
  992.       Inc(ReceiveBytes,bytes);
  993.       IF NOT BinaryData AND (buf[TotLen] < ' ') THEN BEGIN
  994.         IF buf[TotLen] = MyCR THEN GOTO Ferdig;
  995.         IF buf[TotLen] = MySOH THEN BEGIN
  996.           TotLen := 0;
  997.           plen := '~';
  998.         END;
  999.         Exit;
  1000.       END;
  1001.       Inc(TotLen,bytes);
  1002.     UNTIL TotLen > 100;
  1003.  
  1004.   Ferdig:
  1005.     ok := TestPakke(RX) AND (TotLen < 96) AND NOT RX.long;
  1006. $IFDEF DEBUG
  1007.     IF LogFileMode = LogAll THEN BEGIN
  1008.       LogChar('<');
  1009.       FOR n := 0 TO Pred(TotLen) DO
  1010.         LogChar(buf[n]);
  1011.       LogChar('>');
  1012.     END;
  1013. $ENDIF
  1014.   Init:
  1015.     InitLesPakke;
  1016.   END;
  1017. END;                                   {LesPakke}
  1018.  
  1019. PROCEDURE TrySend;
  1020. BEGIN
  1021.   IF RS_Room(CurComPort) < 4000 THEN Exit; { >1 packet already in pipeline}
  1022.   IF NackedNr = 0 THEN BEGIN
  1023.     IF (ninn-nut) AND 63 < WinSize THEN BEGIN
  1024.       IF EndOfFile THEN BEGIN
  1025. {        IF nut = ninn THEN
  1026.         Debug('File completed'); }
  1027.         Exit;            {No more Data packets}
  1028.       END;
  1029.       PakkeNr := ninn;
  1030.       WITH pw[ninn] DO BEGIN
  1031.         MakeDataPac(dptr^);
  1032.         IF dptr^.TotLen > 0 THEN BEGIN
  1033.           SendPakkeT(dptr^);
  1034.           acknack := 0; {acked := FALSE; nacked := FALSE;}
  1035.           retry := 0;
  1036.           ninn := Succ(ninn) AND 63;
  1037.         END;
  1038.       END;
  1039.       Exit;
  1040.     END;
  1041.                                        {Window is full, see if any acked}
  1042.     IF pw[nut].retry > 0 THEN Exit;
  1043.     n := nut;
  1044.     REPEAT
  1045.       n := Succ(n) AND 63;
  1046.       IF n = ninn THEN Exit;
  1047.     UNTIL pw[n].acknack <> 0;
  1048.     SendPakkeT(pw[nut].dptr^);
  1049.     pw[nut].retry := 1;
  1050.     Exit;
  1051.   END
  1052.   ELSE BEGIN  {NackedNr > 0}
  1053.     n := nut;
  1054.     Dec(NackedNr);
  1055.     WHILE NOT pw[n].nacked DO BEGIN
  1056.       n := Succ(n) AND 63;
  1057.       IF n = ninn THEN BEGIN
  1058.         Warning('No NACK');
  1059.         Exit;
  1060.       END;
  1061.     END;
  1062.     SendPakkeT(pw[n].dptr^);
  1063.     pw[n].nacked := FALSE;
  1064.   END;
  1065. END;                                   {TrySend}
  1066.  
  1067. PROCEDURE DoPakke;
  1068. VAR msg : String;
  1069. BEGIN
  1070.   WITH RX_Pac^ DO BEGIN
  1071.     IF EndOfFile THEN Debug('EOF - '+Tstr((ninn-nut) AND 63,1));  
  1072.     WriteTotalNr;
  1073.     nr := -32 +Ord(pnr);                  {Position in circular buffer}
  1074.     n := (nr - nut) AND 63;            {Offset from first packet}
  1075.  
  1076.     Extract(msg);
  1077.  
  1078.     IF ptype = 'T' THEN BEGIN
  1079.       RS_Enable(CurComPort);
  1080.       WriteFeilNr;
  1081.       WITH pw[nut] DO BEGIN
  1082.         IF NOT nacked THEN BEGIN
  1083.           Inc(NackedNr);
  1084.           nacked := TRUE;
  1085.         END;
  1086.       END;
  1087.       Inc(RetryNr);
  1088.       IF RetryNr > 10 THEN BEGIN
  1089.         SendAbort('Too many retries!');
  1090.         state := Abort;
  1091.       END;
  1092.       Exit;
  1093.     END;
  1094.  
  1095.     RetryNr := 0;
  1096.  
  1097.     IF ptype = 'Y' THEN BEGIN
  1098.       IF msg = 'X' THEN BEGIN
  1099.         StopFile := TRUE;
  1100.         state := SendEOF;
  1101.       END;
  1102.       IF n >= (ninn-nut) AND 63 THEN BEGIN
  1103.         Debug('ACK outside');  
  1104.         Exit;    {ACK outside of window}
  1105.       END;
  1106.       WITH pw[nr] DO BEGIN
  1107.         acked := TRUE;
  1108.         IF nacked THEN BEGIN
  1109.           Dec(NackedNr);
  1110.           nacked := FALSE;
  1111.         END;
  1112.       END;
  1113.       WHILE pw[nut].acked DO BEGIN
  1114.         nut := Succ(nut) AND 63;
  1115.         IF ninn = nut THEN BEGIN
  1116.           IF EndOfFile THEN BEGIN
  1117.             state := SendEOF;
  1118.             Debug('Exit TrySend'); 
  1119.           END;
  1120.           Exit;
  1121.         END;
  1122.       END;
  1123.       Exit;
  1124.     END;
  1125.     IF ptype = 'N' THEN BEGIN
  1126.       RS_Enable(CurComPort);
  1127.       IF n >= (ninn-nut) AND 63 THEN BEGIN {NACK outside window}
  1128.         Debug('NACK outside');  
  1129.         IF nut = ninn THEN BEGIN
  1130.           Debug('Window empty'); 
  1131.           Exit;
  1132.         END;
  1133.         nr := nut
  1134.       END;
  1135.       WriteFeilNr;
  1136.       WITH pw[nr] DO BEGIN
  1137.         Inc(retry);
  1138.         IF retry > 10 THEN BEGIN
  1139.           SendAbort('Too many retries!');
  1140.           state := Abort;
  1141.           Exit;
  1142.         END;
  1143.         NackedNr := Succ(NackedNr) - Ord(nacked);
  1144.         nacked := TRUE;
  1145.       END;
  1146.       Exit;
  1147.     END;
  1148.     IF ptype = 'E' THEN BEGIN
  1149.       Extract(ErrorString);
  1150.       IF ErrorString <> 'F10' THEN
  1151.         WriteError;
  1152.       state := Abort;
  1153.       Exit;
  1154.     END;
  1155.     SendAbort('Unexpected packet type: '+ptype);
  1156.     state := Abort;
  1157.   END;
  1158. END;
  1159.  
  1160. PROCEDURE SendWindow;
  1161. VAR done : BOOLEAN;
  1162.     i : WORD;
  1163. BEGIN
  1164.   NackedNr := 0;
  1165.   InitLesPakke;
  1166.   InitWindow;
  1167.   REPEAT
  1168.     TrySend;
  1169.     FOR i := 1 TO 4 DO BEGIN
  1170.       LesPakke(RX_Pac^,done);          {Bad packet will be ignored}
  1171.       IF done THEN DoPakke;
  1172.     END;
  1173.     DumpPointers;
  1174.     IF StopFile AND (state<>Abort) THEN state := SendEOF;
  1175.   UNTIL state IN [SendEOF,Abort];
  1176. {
  1177.   IF state = SendEOF THEN
  1178.     Debug('Exit SendEOF')
  1179.   ELSE
  1180.     Debug('Exit Abort');
  1181. }
  1182.   PakkeNr := ninn;
  1183. END;
  1184.  
  1185. PROCEDURE SendManyFiles(FilePattern : String);
  1186. VAR ok, server : BOOLEAN;
  1187.     po : INTEGER;
  1188.     fn : String;
  1189. BEGIN
  1190.   server := FilePattern <> '';
  1191.   IF NOT server THEN BEGIN
  1192.     ReadFileName('File(s) to send: ',FilePattern);
  1193.     IF FilePattern = '' THEN Exit;
  1194.   END;
  1195.  
  1196.   IF Pos('.',FilePattern) = 0 THEN
  1197.     FilePattern := FilePattern + '.';
  1198.   FindFirst(FilePattern,0,DTA);
  1199.   ok := DosError = 0;
  1200.   IF NOT ok THEN BEGIN
  1201.     Error('No files found!');
  1202.     Exit;
  1203.   END;
  1204.  
  1205.   FileName := DTA.Name;
  1206.  
  1207.     po := Ord(FilePattern[0]);
  1208.     WHILE po > 0 DO BEGIN
  1209.       IF FilePattern[po] IN ['\',':'] THEN BEGIN
  1210.         Delete(FilePattern,po+1,30);
  1211.         po := 0;
  1212.       END;
  1213.       Dec(po);
  1214.     END;
  1215.     IF po = 0 THEN FilePattern[0] := #0;
  1216.     state := SendInit;
  1217.     ShowTimeOut := TRUE;
  1218.     PakkeNr := 0;
  1219.     FeilNr := 0;
  1220.     TotalNr := 0;
  1221.     LastNr := 63;
  1222.     MakeInfoScreen('  Sending:');
  1223.     StatusString := 'Init';
  1224.     WriteStatus;
  1225.     InitStat;
  1226.     RS_ClrBuffer(CurComPort);
  1227.     REPEAT
  1228.       CASE state OF
  1229.         SendData : BEGIN
  1230.                      IF WindowData THEN SendWindow
  1231.                      ELSE BEGIN
  1232.                        MakeData;
  1233.                        IF StopFile OR (TX_Pac^.TotLen = 0) THEN
  1234.                          state := SendEOF
  1235.                        ELSE BEGIN
  1236.                          SendAndGet(state,SendData,TRUE);
  1237.                          IF state=Abort THEN BEGIN
  1238.                            Close(fil);
  1239.                          END
  1240.                          ELSE IF (RX_Pac^.TotLen > 4) AND
  1241.                                  (RX_Pac^.pdata[1] = 'X') THEN BEGIN
  1242.                            StopFile := TRUE;
  1243.                            state := SendEOF;
  1244.                          END;
  1245.                        END;
  1246.                      END;
  1247.                    END;
  1248.         SendInit : BEGIN
  1249.                      MakeInitPacket('S');
  1250.                      SendAndGet(state,SendName,FALSE);
  1251.                      IF state=SendName THEN BEGIN
  1252.                        TolkInitPacket;
  1253.                        MakeCodeTab;
  1254.                        CheckType := FileCheck;
  1255.                      END;
  1256.                    END;
  1257.         SendName : BEGIN
  1258.                      fn := FilePattern + FileName + #0;
  1259.                      OriginalName := FileName;
  1260.                      Assign(fil,fn);
  1261.                      Reset(fil,1);
  1262.                      Next_Data_OK := FALSE;
  1263.                      IF IOresult = 0 THEN BEGIN
  1264.                        WriteFileName;
  1265.                        FileMax := FileSize(fil);
  1266.                        WriteFileSize;
  1267.                        Inc(FileNr);
  1268.                        MakePakke(TX_Pac^, PakkeNr,'F',FileName);
  1269.                        SendAndGet(state,SendData,FALSE);
  1270.                        IF state=SendData THEN BEGIN
  1271.                          BufCount := 0;
  1272.                          BufferPtr(BufPtr) := Buffer;
  1273.                          EndOfFile := FALSE;
  1274.                          ZeroBytes;
  1275.                          StatusString := 'In Progress';
  1276.                          WriteStatus;
  1277.                          StopFile := FALSE;
  1278.                          IF AttrPakke THEN state := SendAttr;
  1279.                        END;
  1280.                      END
  1281.                      ELSE BEGIN
  1282.                        Error('File not found: '+fn);
  1283.                        state := Abort;
  1284.                      END;
  1285.                    END;
  1286.         SendAttr : BEGIN
  1287.                      XmitAttr(state);
  1288.                      IF state = Abort THEN
  1289.                        Close(fil)
  1290.                    END;
  1291.         SendEOF  : BEGIN
  1292.                      XmitEOF (state);
  1293.                      IF state <> Abort THEN BEGIN
  1294.                        FindNext(DTA);
  1295.                        ok := DosError = 0;
  1296.                        IF ok THEN BEGIN
  1297.                          state := SendName;
  1298.                          FileName := DTA.Name;
  1299.                        END
  1300.                        ELSE
  1301.                          state := SendEnd;
  1302.                      END;
  1303.                    END;
  1304.         SendEnd  : BEGIN
  1305.                      XmitEnd(state);
  1306.                      StatusString := 'Completed!';
  1307.                      WriteStatus;
  1308.                    END;
  1309.         Abort    : BEGIN
  1310.                      StatusString := 'Aborted';
  1311.                      WriteStatus;
  1312.                      SendAbort('Too many retries!');
  1313.                      Close(fil);
  1314.                      ErrorLevel := 3;
  1315.                    END;
  1316.       END;
  1317.     UNTIL state IN [Complete,Abort];
  1318.   Bell;
  1319.   ShowStat;
  1320. END;  { SendManyFiles }
  1321.  
  1322. TYPE PakkeChar = 'A'..'Z';
  1323.      PakkeSet = SET OF PakkeChar;
  1324.      ReceiveType = (RecF, GetF, ServF, TextF);
  1325.  
  1326. VAR Ferdig, CheckSkip, ValidDate : BOOLEAN;
  1327.     Expect : PakkeSet;
  1328.  
  1329. PROCEDURE TestDate;
  1330. VAR old : FILE;
  1331.     newTime, oldTime : LongInt;
  1332. BEGIN
  1333.   IF OriginalName <> FileName THEN BEGIN
  1334.     Assign(old,OriginalName); Reset(old,1);
  1335.     GetFTime(old,oldTime);
  1336.     Close(old);
  1337.  
  1338.     PackTime(FTime,newTime);
  1339.     IF ((newTime > oldTime) AND (NewDupHandle = SkipFile)) OR
  1340.        ((newTime <= oldTime) AND (OldDupHandle = SkipFile)) THEN
  1341.       StopFile := TRUE;
  1342.   END;
  1343.   CheckSkip := TRUE;
  1344.   IF IOresult <> 0 THEN WriteStr('Test Error'^G);
  1345. END;
  1346.  
  1347. PROCEDURE GetFileAttr;
  1348. VAR l, st : String;
  1349.     p, feil, len : INTEGER;
  1350. BEGIN
  1351.   Extract(st);
  1352.     WHILE st[0] >= #3 DO BEGIN
  1353.       len := Ord(st[2]) - 32;
  1354.       l := Copy(st,3,len);
  1355.       CASE st[1] OF
  1356.         '!' : BEGIN GotoXY(30,8); WriteStr('File size: '+Pad(l+'k',10)); END;
  1357.         '1' : BEGIN GotoXY(30,8); WriteStr('File size: '+Pad(l,10)); END;
  1358.         '#' : BEGIN
  1359.                 p := Pos(' ',l);
  1360.                 Val(Copy(l,p-6,2),FTime.year,feil);
  1361.                 Inc(FTime.year,1900);
  1362.                 IF feil = 0 THEN Val(Copy(l,p-4,2),FTime.month,feil);
  1363.                 IF feil = 0 THEN Val(Copy(l,p-2,2),FTime.day,feil);
  1364.                 IF feil = 0 THEN Val(Copy(l,p+1,2),FTime.hour,feil);
  1365.                 IF feil = 0 THEN Val(Copy(l,p+4,2),FTime.min,feil);
  1366.                 IF (feil = 0) AND (Ord(l[0]) >= p + 8) THEN
  1367.                   Val(Copy(l,p+7,2),FTime.sec,feil);
  1368.                 IF feil = 0 THEN BEGIN
  1369.                   ValidDate := TRUE;
  1370.                   TestDate;
  1371.                 END;
  1372.               END;
  1373.       END;
  1374.       Delete(st,1,len+2);
  1375.     END;
  1376. END;
  1377.  
  1378. PROCEDURE SetFileDate;
  1379. VAR t : LongInt;
  1380. BEGIN
  1381.   IF NOT ValidDate THEN Exit;
  1382.   PackTime(FTime,t);
  1383.   SetFTime(fil,t);
  1384. END;
  1385.  
  1386. VAR CtrlTab : ARRAY [CHAR] OF CHAR;
  1387.  
  1388. PROCEDURE MakeCtrlTab;
  1389. VAR ch : CHAR;
  1390. BEGIN
  1391.   FOR ch := #0 TO #255 DO CtrlTab[ch] := ch;
  1392.   FOR ch := #$3F TO #$5F DO CtrlTab[ch] := Chr(Ord(ch) XOR 64);
  1393.   FOR ch := #$BF TO #$DF DO CtrlTab[ch] := Chr(Ord(ch) XOR 64);
  1394. END;
  1395.  
  1396. PROCEDURE DecodeData(VAR p : PakkeType);
  1397. VAR n, mask : BYTE;
  1398.     ch : CHAR;
  1399.     dptr : ^CHAR;
  1400.     dlen, max, databytes : WORD;
  1401. BEGIN
  1402.   IF DiskError THEN Exit;
  1403.   max := 1;
  1404.   IF p.long THEN max := 4;
  1405.   dptr := Addr(p.pdata[max]);
  1406.   max := Ofs(p.pdata[p.TotLen - 2 - CheckType]);
  1407.   databytes := 0;
  1408.   IF BinaryData THEN BEGIN
  1409.     dlen := max - Word(dptr);
  1410.     IF BufCount < dlen THEN BEGIN
  1411.       Move(dptr^,BufPtr^,BufCount);
  1412.       BlockWrite(fil,buffer^,BufSize);
  1413.       IF IOresult <> 0 THEN BEGIN
  1414.         DiskError := TRUE;
  1415.         Exit;
  1416.       END;
  1417.       Inc(Word(dptr),BufCount);
  1418.       AddBytes(BufCount);
  1419.       Dec(dlen,BufCount);
  1420.       BufferPtr(BufPtr) := Buffer;
  1421.       BufCount := BufSize;
  1422.     END;
  1423.     Move(dptr^,BufPtr^,dlen);
  1424.     Inc(Word(BufPtr),dlen);
  1425.     Dec(BufCount,dlen);
  1426.     AddBytes(dlen);
  1427.  
  1428.     Exit;
  1429.   END;
  1430.  
  1431.   REPEAT
  1432.     ch := dptr^; Inc(WORD(dptr));
  1433.     n := 1;
  1434.     IF ch = RepQ THEN BEGIN
  1435.       n := BYTE(dptr^) - 32; Inc(WORD(dptr));
  1436.       ch := dptr^; Inc(WORD(dptr));
  1437.     END;
  1438.     mask := 0;
  1439.     IF ch = Bit8Q THEN BEGIN
  1440.       mask := $80;
  1441.       ch := dptr^; Inc(WORD(dptr));
  1442.     END;
  1443.     IF ch = YourQCtrlChar THEN BEGIN
  1444.       ch := CtrlTab[dptr^]; Inc(WORD(dptr));
  1445.     END;
  1446.  
  1447.     ch := CHAR(BYTE(ch) OR mask);
  1448.  
  1449.     IF TextFile THEN ch := InnConvert[ch];
  1450.     Inc(databytes,n);
  1451.  
  1452.     REPEAT
  1453.       BufPtr^ := ch;
  1454.       Inc(Word(BufPtr));
  1455.       Dec(BufCount);
  1456.       IF BufCount = 0 THEN BEGIN
  1457.         StopLink;
  1458.         BlockWrite(fil,buffer^,BufSize);
  1459.         StartLink;
  1460.         BufferPtr(BufPtr) := Buffer;
  1461.         BufCount := BufSize;
  1462.         IF IOresult <> 0 THEN BEGIN
  1463.           DiskError := TRUE;
  1464.           Exit;
  1465.         END;
  1466.       END;
  1467.       Dec(n);
  1468.     UNTIL n = 0;
  1469.   UNTIL WORD(dptr) >= max;
  1470.   AddBytes(databytes);
  1471. END;                                   {DecodeData}
  1472.  
  1473. PROCEDURE EOF_Packet;
  1474. VAR EraseFile : BOOLEAN;
  1475.     old, bak : FILE;
  1476.     Bak_file : String[64];
  1477.     punkt : INTEGER;
  1478.     oldTime, newTime : LongInt;
  1479. BEGIN
  1480.   Extract(st);
  1481.   IF BufCount < BufSize THEN BlockWrite(fil,Buffer^,BufSize-BufCount);
  1482.   SetFileDate;
  1483.   Inc(TotalBytes,FilePos(fil));
  1484.   Close(fil);
  1485.   IF (st = 'D') OR StopFile THEN BEGIN
  1486.     Erase(fil);
  1487.     Warning(Filename+' skipped!');
  1488.   END
  1489.   ELSE BEGIN
  1490.     IF OriginalName <> FileName THEN BEGIN
  1491.       Assign(old,OriginalName); Reset(old,1);
  1492.       IF ValidDate THEN BEGIN
  1493.         GetFTime(old,oldTime);
  1494.         PackTime(FTime,newTime);
  1495.         EraseFile := ((newTime>oldTime) AND (NewDupHandle=OverWriteFile)) OR
  1496.                     ((newTime<=oldTime) AND (OldDupHandle=OverWriteFile));
  1497.       END
  1498.       ELSE BEGIN
  1499.         EraseFile := DupHandle = OverWriteFile;
  1500.       END;
  1501.       Close(old);
  1502.       IF EraseFile THEN BEGIN
  1503.         punkt := Pos('.',OriginalName);
  1504.         IF punkt = 0 THEN punkt := Length(OriginalName)+1;
  1505.         BAK_file := Copy(OriginalName,1,punkt-1) + '.BAK';
  1506.         IF (OriginalName <> BAK_File) THEN BEGIN
  1507.           IF Exist(BAK_File) THEN BEGIN
  1508.             Assign(bak,BAK_File);
  1509.             Erase(bak);
  1510.           END;
  1511.           Rename(old,BAK_File);
  1512.           Rename(fil,OriginalName);
  1513.           Warning(FileName+' renamed to '+OriginalName);
  1514.         END;
  1515.       END;
  1516.     END;
  1517.   END;
  1518.   IF IOresult=0 THEN
  1519.     Ack(PakkeNr)
  1520.   ELSE BEGIN
  1521.     SendAbort('File close error!');
  1522.     Ferdig := TRUE;
  1523.   END;
  1524.   Expect := ['B','F'];
  1525.   StatusString := 'File Closed';
  1526.   WriteStatus;
  1527. END;
  1528.  
  1529. PROCEDURE TestPacketNr(VAR ok : BOOLEAN);
  1530. VAR i, j : WORD;
  1531. BEGIN
  1532.   ok := FALSE;
  1533.   n := (nr - nut) AND 63;
  1534.   IF n < (ninn-nut) AND 63 THEN BEGIN
  1535.     ok := n < WinSize;                 {Retransmitted packet}
  1536.     Exit;
  1537.   END;
  1538.   i := (nr - ninn) AND 63;             {Packets past last}
  1539.   IF i >= WinSize THEN Exit;           {Outside of max send window}
  1540.   FOR j := 0 TO i DO BEGIN
  1541.     IF (ninn-nut) AND 63 = WinSize THEN BEGIN
  1542.       IF NOT pw[nut].acked THEN BEGIN
  1543.         SendAbort('Window overflow!');
  1544.         ferdig := TRUE;
  1545.         Exit;
  1546.       END;
  1547.       DecodeData(pw[nut].dptr^);
  1548.       nut := Succ(nut) AND 63;
  1549.     END;
  1550.     WITH pw[ninn] DO BEGIN
  1551.       retry := 0;
  1552.       acked := FALSE;
  1553.       IF j < i THEN BEGIN
  1554.         Nack(ninn);
  1555.         retry := 1;
  1556.       END;
  1557.     END;
  1558.     ninn := Succ(ninn) AND 63;
  1559.   END;
  1560.   ok := TRUE;
  1561. END;                                   { TestPacketNr }
  1562.  
  1563. PROCEDURE WindowReceive;
  1564. VAR ok : BOOLEAN;
  1565. BEGIN                                  { RX_Pac has the first data packet }
  1566.   InitWindow;
  1567.   REPEAT
  1568.    DumpPointers;
  1569.    WITH RX_Pac^ DO BEGIN
  1570.     nr := -32 +Ord(pnr);
  1571.     CASE ptype OF
  1572.       'T' : BEGIN
  1573.         Inc(RetryNr);
  1574.         WriteFeilNr;
  1575.         IF RetryNr > 10 THEN BEGIN
  1576.           SendAbort('Too many timeouts!');
  1577.           Ferdig := TRUE;
  1578.           Exit;
  1579.         END;
  1580.         n := nut;
  1581.         WHILE pw[n].acked AND (n <> ninn) DO n := Succ(n) AND 63;
  1582.         IF (n <> ninn) OR (pdata[1] <> 'P') THEN
  1583.           Nack(n);                         { Most wanted packet nr! }
  1584.         RS_Enable(CurComPort);
  1585.       END;
  1586.       'E' : BEGIN
  1587.         Extract(ErrorString);
  1588.         IF ErrorString <> 'F10' THEN WriteError;
  1589.         IF ErrorLevel < 2 THEN ErrorLevel := 2;
  1590.         Ferdig := TRUE;
  1591.         Exit;
  1592.       END
  1593.       ELSE BEGIN
  1594.         RetryNr := 0;
  1595.         IF ptype = 'Z' THEN BEGIN
  1596.           Extract(st);
  1597.           IF st <> 'D' THEN BEGIN
  1598.             WHILE nut <> ninn DO BEGIN
  1599.               IF NOT pw[nut].acked THEN BEGIN
  1600.                 SendAbort('No ACK at EOF:'+pnr);
  1601.                 Ferdig := TRUE;
  1602.                 Exit;
  1603.               END;
  1604.               DecodeData(pw[nut].dptr^);
  1605.               nut := Succ(nut) AND 63;
  1606.               DumpPointers;
  1607.             END;
  1608.           END;
  1609.           PakkeNr := nr;
  1610.           EOF_Packet;
  1611.           Exit;
  1612.         END;
  1613.         IF StopFile THEN
  1614.           SendPacket(nr,'Y','X')
  1615.         ELSE IF DiskError THEN BEGIN
  1616.           SendAbort('File write error!');
  1617.           ferdig := TRUE;
  1618.           Exit;
  1619.         END
  1620.         ELSE BEGIN
  1621.           TestPacketNr(ok);              {Sjekk om nr i vindu, sett n}
  1622.           IF ferdig THEN Exit;
  1623.           IF ok THEN WITH pw[nr] DO BEGIN
  1624.             IF ptype = 'D' THEN BEGIN
  1625.               IF NOT acked THEN BEGIN
  1626.                 Move(RX_Pac^,dptr^,100);{Room for overhead}
  1627.                 acked := TRUE;
  1628.               END
  1629.               ELSE BEGIN
  1630.                 Inc(retry);
  1631.                 IF retry > 10 THEN BEGIN
  1632.                   SendAbort('Too many retries!');
  1633.                   ferdig := TRUE;
  1634.                   Exit;
  1635.                 END;
  1636.               END;
  1637.               Ack(nr);
  1638.             END
  1639.             ELSE BEGIN
  1640.               SendAbort('Unexpected packet type: '+ptype);
  1641.               Ferdig := TRUE;
  1642.               Exit;
  1643.             END;
  1644.           END
  1645.           ELSE BEGIN
  1646.             WriteFeilNr;
  1647.           END
  1648.         END;
  1649.       END;  {ELSE BEGIN}
  1650.     END;    {CASE ptype OF}
  1651.     GetPakke;
  1652.     WriteTotalNr;
  1653.    END;          {WITH RX_Pac^ DO}
  1654.   UNTIL FALSE;
  1655. END;                                   { WindowReceive }
  1656.  
  1657. PROCEDURE ReceiveFiles(GetFile : ReceiveType; GetName : String);
  1658. VAR LastPk : PakkeCh;
  1659.     state : KermitState;
  1660.     l, n : INTEGER;
  1661.     ch : CHAR;
  1662.     MainName, Ext, Path, st : String;
  1663.     ok, done : BOOLEAN;
  1664. BEGIN
  1665.   IF (GetFile=GetF) AND (GetName = '') THEN BEGIN
  1666.     ReadFileName('File(s) to Get: ',GetName);
  1667.     IF GetName[0]=#0 THEN Exit;
  1668.   END;
  1669.   RS_ClrBuffer(CurComPort);
  1670.   Expect := ['S'];
  1671.   LastPk := '@';
  1672.   PakkeNr := 0;
  1673.   TotalNr := 0;
  1674.   FeilNr  := 0;
  1675.   LastNr := 63;
  1676.   RetryNr := 0;
  1677.   Ferdig := FALSE;
  1678.   ShowTimeOut := TRUE;
  1679.   MakeInfoScreen('Receiving:');
  1680.   FileName[0] := #0;
  1681.   ErrorString[0] := #0;
  1682.   StatusString := 'Init';
  1683.   WriteStatus;
  1684.   RS_ClrBuffer(CurComPort);
  1685.   DiskError := FALSE;
  1686.   IF GetFile=GetF THEN BEGIN
  1687.     MakeInitPacket('I');
  1688.     SendAndGet(state,Complete,FALSE);
  1689.     IF state=Complete THEN
  1690.       TolkInitPacket;
  1691.     SendPacket(0,'R',GetName);
  1692.   END;
  1693.   PakkeNr := 0;
  1694.  
  1695.   IF GetFile<>ServF THEN
  1696.     GetPakke;
  1697.  
  1698.   InitStat;
  1699.   REPEAT
  1700.     WITH RX_Pac^ DO BEGIN
  1701.       IF ptype = 'T' THEN BEGIN
  1702.         Inc(RetryNr);
  1703.         IF RetryNr <= RetryLimit THEN BEGIN
  1704.           WriteFeilNr;
  1705.           Nack(PakkeNr);
  1706.         END
  1707.         ELSE BEGIN
  1708.           SendAbort('Too many retries!');
  1709.           Ferdig := TRUE;
  1710.           ErrorLevel := 1;
  1711.         END;
  1712.       END
  1713.       ELSE BEGIN
  1714.         RetryNr := 0;
  1715.         IF (pnr = Chr(32 + PakkeNr)) AND (ptype IN Expect) THEN BEGIN
  1716.           CASE ptype OF
  1717.             'D' :
  1718.             BEGIN
  1719.               IF NOT CheckSkip THEN BEGIN
  1720.                 IF OriginalName <> FileName THEN
  1721.                   StopFile := DupHandle = SkipFile;
  1722.                 CheckSkip := TRUE;
  1723.               END;
  1724.               IF WindowData THEN
  1725.                 WindowReceive
  1726.               ELSE IF StopFile THEN
  1727.                 SendPacket(PakkeNr,'Y','X')
  1728.               ELSE IF DiskError THEN
  1729.                 SendAbort('File write error!')
  1730.               ELSE BEGIN
  1731.                 IF NOT DiskStopInt THEN Ack(PakkeNr);
  1732.                 Expect := ['D','Z'];
  1733.                 DecodeData(RX_Pac^);
  1734.                 IF DiskStopInt THEN Ack(PakkeNr);
  1735.               END;
  1736.             END;
  1737.             'S' : BEGIN
  1738.                     TolkInitPacket;
  1739.                     RepQ := #0;
  1740.                     IF Qrep THEN RepQ := QrepChar;
  1741.                     Bit8Q := #0;
  1742.                     IF Q8bit THEN Bit8Q := Q8bitChar;
  1743.                     MakeInitPacket('Y');
  1744.                     SendPakke;
  1745.                     CheckType := FileCheck;
  1746.                     IF GetFile = TextF THEN
  1747.                       Expect := ['X']
  1748.                     ELSE
  1749.                       Expect := ['F'];
  1750.                     StatusString := 'GetFileName';
  1751.                     WriteStatus;
  1752.                     MakeCtrlTab;
  1753.                   END;
  1754.             'X' :
  1755.             BEGIN
  1756.               FileName := 'CON'; OriginalName := FileName;
  1757.               Assign(fil,'KERMIT.$$$');
  1758.               ReWrite(fil,1);
  1759.               IF IOresult<>0 THEN BEGIN
  1760.                 SendAbort('Cannot Create File!');
  1761.                 Ferdig := TRUE;
  1762.               END
  1763.               ELSE BEGIN
  1764.                 CheckSkip := FALSE;
  1765.                 ValidDate := FALSE;
  1766.                 BufferPtr(BufPtr) := Buffer;
  1767.                 BufCount := BufSize;
  1768.                 Expect := ['A','D','Z'];
  1769.                 StatusString := 'In progress';
  1770.                 WriteStatus;
  1771.                 WriteFileName;
  1772.                 ZeroBytes;
  1773.                 StopFile := FALSE;
  1774.                 Ack(PakkeNr);
  1775.                 LongReply := TRUE;
  1776.               END;
  1777.             END;
  1778.             'F' :
  1779.             BEGIN
  1780.               Inc(FileNr);
  1781.               Extract(FileName);
  1782.               FOR l := 1 TO Ord(FileName[0]) DO
  1783.                 IF NOT (FileName[l] IN FileNameSet) THEN
  1784.                   FileName[l] := 'X';
  1785.               Ext := '.';
  1786.               MainName[0] := #0;
  1787.               Path[0] := #0;
  1788.               IF Pos(':',FileName) = 2 THEN BEGIN
  1789.                 Path := Copy(FileName,1,2);
  1790.                 IF NOT (Path[1] IN ['A'..'Z']) THEN Path[0] := #0;
  1791.                 Delete(FileName,1,2);
  1792.               END;
  1793.               l := Ord(FileName[0]);
  1794.               WHILE l > 0 DO BEGIN
  1795.                 IF FileName[l] = '.' THEN BEGIN
  1796.                   IF Ext = '.' THEN BEGIN
  1797.                     Ext := Copy(FileName,l,4);
  1798.                     FileName := Copy(FileName,1,Pred(l));
  1799.                   END
  1800.                   ELSE
  1801.                     FileName[l] := 'X';
  1802.                 END
  1803.                 ELSE IF FileName[l] = '\' THEN BEGIN
  1804.                   Path := Path + Copy(FileName,1,l);
  1805.                   Delete(FileName,1,l);
  1806.                   l := 0;
  1807.                 END
  1808.                 ELSE IF FileName[l] = ':' THEN
  1809.                   FileName[l] := 'X';
  1810.                 Dec(l);
  1811.               END;
  1812.               IF FileName[0] > #8 THEN FileName[0] := #8;
  1813. (*
  1814.               IF Path = '' THEN BEGIN
  1815.                 Path := DownLoadPath;
  1816.                 IF Path[Length(Path)] <> '\' THEN
  1817.                   Path := Path + '\';
  1818.               END;
  1819. *)
  1820.               OriginalName := Path+FileName+Ext;
  1821.  
  1822.               MainName := Copy(FileName+'________',1,8);
  1823.               l := 1;
  1824.               FileName := OriginalName;
  1825.  
  1826.               WHILE Exist(FileName) AND (l<100) DO BEGIN
  1827.                 MainName[8] := Chr(l MOD 10 + 48);
  1828.                 IF l>9 THEN MainName[7] := Chr(l DIV 10 + 48);
  1829.                 FileName := MainName+Ext;
  1830.                 Inc(l);
  1831.               END;
  1832.               IF Exist(FileName) THEN BEGIN
  1833.                 SendAbort('Existing File!');
  1834.                 Ferdig := TRUE;
  1835.               END
  1836.               ELSE BEGIN
  1837.                 Assign(fil,FileName);
  1838.                 ReWrite(fil,1);
  1839.                 IF IOresult<>0 THEN BEGIN
  1840.                   SendAbort('Cannot Create File!');
  1841.                   Ferdig := TRUE;
  1842.                 END
  1843.                 ELSE BEGIN
  1844.                   CheckSkip := FALSE;
  1845.                   ValidDate := FALSE;
  1846.                   BufferPtr(BufPtr) := Buffer;
  1847.                   BufCount := BufSize;
  1848.                   Expect := ['A','D','Z'];
  1849.                   StatusString := 'In progress';
  1850.                   WriteStatus;
  1851.                   WriteFileName;
  1852.                   ZeroBytes;
  1853.                   StopFile := FALSE;
  1854.                   Ack(PakkeNr);
  1855.                 END;
  1856.               END;
  1857.               LongReply := FALSE;
  1858.             END;
  1859.             'A' : BEGIN
  1860.                     GetFileAttr;
  1861.                     IF StopFile THEN
  1862.                       SendPacket(PakkeNr,'Y','N')
  1863.                     ELSE
  1864.                       Ack(PakkeNr);
  1865.                   END;
  1866.             'Z' : EOF_Packet;
  1867.             'B' : BEGIN
  1868.                     Ack(PakkeNr);
  1869.                     Ferdig := TRUE;
  1870.                     StatusString := 'Completed';
  1871.                     WriteStatus;
  1872.                   END;
  1873.           END;  { CASE }
  1874.           LastPk := ptype;
  1875.           LastNr := PakkeNr;
  1876.           PakkeNr := Succ(PakkeNr) AND 63;
  1877.           RetryNr := 0;
  1878.           WriteTotalNr;
  1879.         END
  1880.         ELSE IF (pnr = Chr(32 + LastNr)) AND (ptype = LastPk) THEN BEGIN
  1881.           Inc(RetryNr);
  1882.           WriteFeilNr;
  1883.           IF RetryNr > RetryLimit THEN BEGIN
  1884.             SendAbort('Too many retries!');
  1885.             Ferdig := TRUE;
  1886.           END
  1887.           ELSE BEGIN
  1888.             IF ptype = 'S' THEN BEGIN
  1889.               MakeInitPacket('Y');
  1890.               SendPakke;
  1891.             END
  1892.             ELSE
  1893.               Ack(LastNr);
  1894.           END;
  1895.         END
  1896.         ELSE IF ptype = 'E' THEN BEGIN
  1897.           Extract(ErrorString);
  1898.           IF ErrorString <> 'F10' THEN WriteError;
  1899.           IF ErrorLevel < 2 THEN ErrorLevel := 2;
  1900.           Ferdig := TRUE;
  1901.         END
  1902.         ELSE IF (ptype = 'D') AND WindowData THEN
  1903.           WindowReceive
  1904.         ELSE IF (ptype <> 'Y') AND (ptype <> 'N') AND
  1905.                 (pnr <> Chr(32 + LastNr)) THEN BEGIN
  1906.           SendAbort('Wrong packet type: '+ptype);
  1907.           Ferdig := TRUE;
  1908.         END;
  1909.       END;
  1910.     END;
  1911.     IF NOT ferdig THEN
  1912.       GetPakke;
  1913.   UNTIL Ferdig;
  1914.   IF 'D' IN Expect THEN BEGIN
  1915.     Close(fil);
  1916.     IF IOresult = 0 THEN
  1917.       Erase(fil);
  1918.   END;
  1919.   Bell;
  1920.   ShowStat;
  1921.   IF LongReply THEN {ShowReply};
  1922. END;                         { ReceiveFiles }
  1923.  
  1924. PROCEDURE HostCommand;
  1925. BEGIN
  1926.   ClrLast;
  1927.   WriteStr('Remote Directory: ');
  1928.   SendPacket(0,'G','D');
  1929.   GetPakke;
  1930.   IF RX_Pac^.ptype = 'Y' THEN BEGIN
  1931.     Extract(st);
  1932.     IF st = '' THEN BEGIN
  1933.       ReceiveFiles(TextF,'');
  1934.     END
  1935.     ELSE BEGIN
  1936.       GotoXY(1,25);
  1937.       WriteLn(st);
  1938.     END;
  1939.     GetF10;
  1940.   END;
  1941. END;                                   {HostCommand}
  1942.  
  1943. PROCEDURE FinishServer;
  1944. BEGIN
  1945.   ClrLast;
  1946.   WriteStr('Logging out remote server: ');
  1947.   SendPacket(0,'G','F');
  1948.   GetPakke;
  1949.   IF RX_Pac^.ptype = 'Y' THEN BEGIN
  1950.     WriteStr('Done!');
  1951.     Delay(1000);
  1952.   END;
  1953. END;                         { FinishServer }
  1954.  
  1955. VAR
  1956.   StartPath : String[80];
  1957.  
  1958. PROCEDURE Server;
  1959. VAR FilP, FilN, st : String;
  1960.     ok, ResetTimer : BOOLEAN;
  1961. BEGIN
  1962.   ResetTimer := TRUE;
  1963.   ClrScr;
  1964.   REPEAT
  1965.     IF (ServerTime > 0) AND ResetTimer THEN BEGIN
  1966.       MaxServer.count := ServerTime * 1092;
  1967.       MaxServer.UserInt := FALSE;
  1968.       StartTimer(MaxServer);
  1969.     END;
  1970.     CheckType := 1;                          { First packet is always type 1 }
  1971.     ClrLast;
  1972.     WriteStr('Kermit  SERVER');
  1973.     GotoXY(72,MaxY); WriteStr('F10=Exit');
  1974.     PakkeNr := 0;
  1975.     GetPakke;
  1976.     ResetTimer := TRUE;
  1977.     ShowTimeOut := FALSE;
  1978.     IF RX_Pac^.pnr = ' ' THEN BEGIN
  1979.       CASE RX_Pac^.ptype OF
  1980.         'S' : ReceiveFiles(ServF,'');
  1981.         'I' : BEGIN
  1982.                 TolkInitPacket;
  1983.                 MakeInitPacket('Y');
  1984.                 SendPakke;
  1985.               END;
  1986.         'R' : BEGIN
  1987.                 Extract(FilP);
  1988.                 IF FilP[0] = #0 THEN
  1989.                   ok := FALSE
  1990.                 ELSE BEGIN
  1991.                   IF Pos('.',FilP) = 0 THEN FilP := FilP + '.';
  1992.                   FindFirst(FilP,0,DTA);
  1993.                   ok := DosError = 0;
  1994.                 END;
  1995.                 IF ok THEN
  1996.                   SendManyFiles(FilP)
  1997.                 ELSE
  1998.                   SendAbort('No Files Found!');
  1999.               END;
  2000.         'T' : BEGIN
  2001.                 IF ServerTimeOut THEN Nack(PakkeNr);
  2002.                 ResetTimer := FALSE;
  2003.               END;
  2004.         'E' : BEGIN
  2005.                 Extract(ErrorString);
  2006.                 IF ErrorString = 'F10' THEN BEGIN
  2007.                   IF ErrorLevel = 0 THEN ErrorLevel := 1;
  2008.                   Exit;
  2009.                 END;
  2010.                 WriteError;
  2011.               END;
  2012.         'G' : BEGIN
  2013.                 Extract(st);
  2014.                 IF st[1] IN ['F','L'] THEN BEGIN
  2015.                   Ack(0);
  2016.                   Exit;
  2017.                 END
  2018.                 ELSE
  2019.                   SendAbort('Unknown Generic Command!');
  2020.               END;
  2021.         'C' : BEGIN
  2022.                 Extract(st);
  2023.                 IF st = '' THEN st := StartPath;
  2024.                 ChDir(st);
  2025.                 GetDir(0,DownLoadPath);
  2026.                 IF IOresult = 0 THEN ;
  2027.                 SendPacket(PakkeNr,'Y','New dir: '+DownLoadPath);
  2028.               END;
  2029.         ELSE SendAbort('Unknown Server Command!');
  2030.       END;
  2031.     END
  2032.     ELSE
  2033.       Nack(PakkeNr);
  2034.   UNTIL (ServerTime > 0) AND NOT RunningTimer(MaxServer);
  2035. END;                                   {Server}
  2036.  
  2037. $I Terminal
  2038.  
  2039. PROCEDURE Kermit;
  2040. VAR
  2041.   key : KeyType;
  2042.   heap : Pointer;
  2043.   st : String;
  2044.   i : INTEGER;
  2045. BEGIN                        { Kermit }
  2046.   Mark(heap);
  2047.  
  2048.   New(RX_Pac); New(TX_Pac); New(Next_Pac);
  2049.  
  2050.   IF MemAvail < KermitBufSize + 2048 THEN
  2051.     KermitBufSize := (MemAvail - 2048) AND $F800;
  2052.  
  2053.   GetMem(buffer,KermitBufSize+1);
  2054.   BufSize := KermitBufSize;
  2055.  
  2056.   AttrPakke := TRUE;
  2057.  
  2058.   YourMaxLength := 80;
  2059.   PakkeNr := 0;
  2060.   ServerTime := 0;
  2061.   PacketDelay := 0;
  2062.   r_code := r_ok;
  2063.  
  2064.   IF ArgC >= 1 THEN BEGIN
  2065.     ShowTimeOut := TRUE;
  2066.     CheckType := 1;
  2067.  
  2068.     Init_Params;
  2069.  
  2070.     st := ArgV[1];
  2071.  
  2072.     IF Pos(st,'SERVER') = 1 THEN Server
  2073.     ELSE IF (Pos(st,'SEND') = 1) AND (ArgC >= 2) THEN SendManyFiles(ArgV[2])
  2074.     ELSE IF Pos(st,'RECEIVE') = 1 THEN ReceiveFiles(RecF,'')
  2075.     ELSE IF (Pos(st,'GET') = 1) AND (ArgC >= 2) THEN ReceiveFiles(GetF,ArgV[2])
  2076.     ELSE BEGIN
  2077.       GotoXY(1,25);
  2078.       WriteLn('Usage: Kermit [SERVER] | [SEND <file>] | [RECEIVE] | [GET <file>');
  2079.       Exit;
  2080.     END;
  2081.   END
  2082.   ELSE BEGIN
  2083.     REPEAT
  2084.       ShowTimeOut := TRUE;
  2085.       CheckType := 1;
  2086.  
  2087.       Meny(key);
  2088.  
  2089.       CASE key OF
  2090.       1 : BEGIN
  2091.             SendManyFiles('');
  2092.             GetF10;
  2093.           END;
  2094.       2 : BEGIN
  2095.             ReceiveFiles(RecF,'');
  2096.             GetF10;
  2097.           END;
  2098.       3 : BEGIN
  2099.             ReceiveFiles(GetF,'');
  2100.             GetF10;
  2101.           END;
  2102.       4 : Server;
  2103.       5 : SaveParam;
  2104.       6 : HostCommand;
  2105.       7 : BEGIN
  2106.             GotoXY(1,25); WriteLn; CursorOn; Exec(FindEnv('COMSPEC='),'');
  2107.             IF DosError <> 0 THEN BEGIN
  2108.               WriteLn('EXEC error # ',DosError);
  2109.               Delay(2000);
  2110.             END;
  2111.           END;
  2112.       8 : BEGIN
  2113.             GotoXY(1,25);
  2114.             ClrEol;
  2115.             GotoXY(72,25); Write('F10-Exit');
  2116.             Window(1,18,80,24);
  2117.             ClrScr;
  2118.             CursorOn;
  2119.             Terminal;
  2120.             Window(1,1,80,25);
  2121.           END;
  2122.       9 : FinishServer;
  2123.       END;
  2124.     UNTIL key = 10;
  2125.   END;
  2126.   Release(heap);
  2127. END;                         { Kermit }
  2128.  
  2129. VAR
  2130.   ok : BOOLEAN;
  2131.   ch : CHAR;
  2132.   key : WORD;
  2133.  
  2134. CONST
  2135.   US_Tab : ARRAY [1..6] OF CHAR = '[\]{|}';
  2136.   NO_Tab : ARRAY [1..6] OF CHAR = '';
  2137.  
  2138. BEGIN                                  {Kermits}
  2139.   CheckBreak := FALSE; 
  2140.   FileMode := 0;
  2141.  
  2142.   OrigText := TextAttr;
  2143.   OrigMenu := OrigText XOR 8;
  2144.   OrigField := FeltAttr;
  2145.   OrigEdit := EditAttr;
  2146.  
  2147.   GetDir(0,StartPath); DownLoadPath := StartPath;
  2148.  
  2149.   FOR ch := #0 TO #255 DO InnConvert[ch] := ch;
  2150.   UtConvert := InnConvert;
  2151.   FOR key := 1 TO 6 DO BEGIN
  2152.     InnConvert[US_Tab[key]] := NO_Tab[key];
  2153.     UtConvert[NO_Tab[key]] := US_Tab[key];
  2154.   END;
  2155.  
  2156.   RS_MakeBuffer($1000,0,0,0,0);        {Use same buffers for all ports!}
  2157.  
  2158.   MakeStr(4,5,64,LeftJ,'Current Dir: ',DownLoadPath,Addr(FileNameSet),ToUpper);
  2159.  
  2160.   MakeLong(10,7,6,LeftJ,'Baud: ',CurBaud,2,115200);
  2161.   MakeWord(10,8,1,LeftJ,'Bits: ',CurBits,7,8);
  2162.   MakeEnum(8,9,5,CenterJ,'Parity: ',CurParity,5,ParityStr);
  2163.   MakeWord(5,10,1,LeftJ,'Stop Bits: ',CurStop,1,2);
  2164.   MakeWord(6,11,1,LeftJ,'Com Port: ',CurComPort,1,4);
  2165.  
  2166.   MakeWord(32,7,4,LeftJ, 'Max Packet: ',LongMaxLength,20,9020);
  2167.   MakeWord(32,8,2,LeftJ, 'Max Window: ',WinSize,0,31);
  2168.   MakeWord(28,9,3,LeftJ, 'Packet Timeout: ',MyTimeOut,0,120);
  2169.   MakeWord(28,10,3,LeftJ,'Server Timeout: ',ServerTime,0,500);
  2170.   MakeByte(32,11,1,LeftJ,'Check Type: ',FileCheck,1,3);
  2171.  
  2172.   MakeBool(58,7,5,LeftJ, 'Long Packets: ',LongPakke);
  2173.   MakeBool(56,8,5,LeftJ, 'Sliding Window: ',WindowData);
  2174.   MakeEnum(61,9,4,LeftJ,  'File Type: ',TextFile,2,BinText);
  2175.   MakeEnum(62,10,3,LeftJ,  'IBM Mode: ',IBM_Mode,3,Std_IBM);
  2176.   MakeBool(60,11,5,LeftJ,'High Speed: ',BinaryData);
  2177.  
  2178.   MakeByte(2,13,2,LeftJ, 'Packet Start: ',BYTE(MySOH),1,31);
  2179.   MakeByte(4,14,2,LeftJ, 'Packet End: ',BYTE(MyCR),1,31);
  2180.   MakeChar(4,15,1,LeftJ, 'Ctl Prefix: ',MyQCtrlChar,NIL,0);
  2181.   MakeChar(3,16,1,LeftJ, '8bit Prefix: ',Q8bitChar,NIL,0);
  2182.   MakeChar(4,17,1,LeftJ, 'Rep Prefix: ',QrepChar,NIL,0);
  2183.  
  2184.   MakeEnum(34,15,10,CenterJ,' No Date: ',DupHandle,3,DupString);
  2185.   MakeEnum(34,16,10,CenterJ,'Old File: ',OldDupHandle,3,DupString);
  2186.   MakeEnum(34,17,10,CenterJ,'New File: ',NewDupHandle,3,DupString);
  2187.  
  2188.   MakeByte(60,13,3,LeftJ, 'Text Color: ',KermitAttr,0,255);
  2189.   MakeByte(60,14,3,LeftJ, 'Menu Color: ',MenuAttr,0,255);
  2190.   MakeByte(59,15,3,LeftJ,'Field Color: ',FieldAttr,0,255);
  2191.   MakeByte(60,16,3,LeftJ, 'Edit Color: ',EditAttr,0,255);
  2192.  
  2193.   MakeBool(58,17,5,LeftJ,'Direct Video: ',DirVideo);
  2194.  
  2195.   IF NOT GetParam THEN Halt(1);
  2196.  
  2197.   DirectVideo := DirVideo;
  2198.   ClrScr;        {Keep current screen colors!}
  2199.  
  2200.   CursorOff;
  2201.   Kermit;
  2202.   CursorOn;
  2203.  
  2204.   RS_Stop(CurComPort);
  2205.   ChDir(StartPath);
  2206.   GotoXY(1,25);
  2207. END.
  2208.