home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS 1992 June / SIMTEL_0692.cdr / msdos / turbopas / tpzsfz.arc / TPZ.PAS next >
Pascal/Delphi Source File  |  1988-09-18  |  50KB  |  1,883 lines

  1. UNIT TPZ;
  2. INTERFACE
  3. USES Crt, Dos, TPZasync, TPZVideo, TPZFiles, TPZunix, TPZcrc;
  4.  
  5. FUNCTION Zmodem_Receive(path: STRING; comport: WORD; baudrate: LONGINT): BOOLEAN;
  6. FUNCTION Zmodem_Send(pathname: STRING; lastfile: BOOLEAN; comport: WORD; baudrate: LONGINT): BOOLEAN;
  7.  
  8. IMPLEMENTATION
  9.  
  10. CONST
  11.    TPZVER = 'TPZ [Zmodem] 2.1ß';
  12.    ZBUFSIZE = 1024;
  13.    zport: WORD = 1;
  14.    zbaud: LONGINT = 0;
  15.  
  16. TYPE
  17.    hdrtype = ARRAY[0..3] OF BYTE;
  18.    buftype = ARRAY[0..1023] OF BYTE;
  19.  
  20. CONST
  21.    ZPAD = 42;  { '*' }
  22.    ZDLE = 24;  { ^X  }
  23.    ZDLEE = 88;
  24.    ZBIN = 65;  { 'A' }
  25.    ZHEX = 66;  { 'B' }
  26.    ZBIN32 = 67;{ 'C' }
  27.    ZRQINIT = 0;
  28.    ZRINIT = 1;
  29.    ZSINIT = 2;
  30.    ZACK = 3;
  31.    ZFILE = 4;
  32.    ZSKIP = 5;
  33.    ZNAK = 6;
  34.    ZABORT = 7;
  35.    ZFIN = 8;
  36.    ZRPOS = 9;
  37.    ZDATA = 10;
  38.    ZEOF = 11;
  39.    ZFERR = 12;
  40.    ZCRC = 13;
  41.    ZCHALLENGE = 14;
  42.    ZCOMPL = 15;
  43.    ZCAN = 16;
  44.    ZFREECNT = 17;
  45.    ZCOMMAND = 18;
  46.    ZSTDERR = 19;
  47.    ZCRCE = 104; { 'h' }
  48.    ZCRCG = 105; { 'i' }
  49.    ZCRCQ = 106; { 'j' }
  50.    ZCRCW = 107; { 'k' }
  51.    ZRUB0 = 108; { 'l' }
  52.    ZRUB1 = 109; { 'm' }
  53.    ZOK = 0;
  54.    ZERROR = -1;
  55.    ZTIMEOUT = -2;
  56.    RCDO = -3;
  57.    FUBAR = -4;
  58.    GOTOR = 256;
  59.    GOTCRCE = 360; { 'h' OR 256 }
  60.    GOTCRCG = 361; { 'i' "   "  }
  61.    GOTCRCQ = 362; { 'j' "   "  }
  62.    GOTCRCW = 363; { 'k' "   "  }
  63.    GOTCAN = 272;  { CAN OR  "  }
  64.  
  65. { xmodem paramaters }
  66. CONST
  67.    ENQ = 5;
  68.    CAN = 24;
  69.    XOFF = 19;
  70.    XON = 17;
  71.    SOH = 1;
  72.    STX = 2;
  73.    EOT = 4;
  74.    ACK = 6;
  75.    NAK = 21;
  76.    CPMEOF = 26;
  77.  
  78. { byte positions }
  79. CONST
  80.    ZF0 = 3;
  81.    ZF1 = 2;
  82.    ZF2 = 1;
  83.    ZF3 = 0;
  84.    ZP0 = 0;
  85.    ZP1 = 1;
  86.    ZP2 = 2;
  87.    ZP3 = 3;
  88.  
  89. { bit masks for ZRINIT }
  90. CONST
  91.    CANFDX = 1;    { can handle full-duplex          (yes for PC's)}
  92.    CANOVIO = 2;   { can overlay disk and serial I/O (ditto)       }
  93.    CANBRK = 4;    { can send a break - True but superfluous       }
  94.    CANCRY = 8;    { can encrypt/decrypt - not defined yet         }
  95.    CANLZW = 16;   { can LZ compress - not defined yet             }
  96.    CANFC32 = 32;  { can use 32 bit crc frame checks - true        }
  97.    ESCALL = 64;   { escapes all control chars. NOT implemented    }
  98.    ESC8 = 128;    { escapes the 8th bit. NOT implemented          }
  99.  
  100. { bit masks for ZSINIT }
  101. CONST
  102.    TESCCTL = 64;
  103.    TESC8 = 128;
  104.  
  105. { paramaters for ZFILE }
  106. CONST
  107. { ZF0 }
  108.    ZCBIN = 1;
  109.    ZCNL = 2;
  110.    ZCRESUM = 3;
  111. { ZF1 }
  112.    ZMNEW = 1;   {I haven't implemented these as of yet - most are}
  113.    ZMCRC = 2;   {superfluous on a BBS - Would be nice from a comm}
  114.    ZMAPND = 3;  {programs' point of view however                 }
  115.    ZMCLOB = 4;
  116.    ZMSPARS = 5;
  117.    ZMDIFF = 6;
  118.    ZMPROT = 7;
  119. { ZF2 }
  120.    ZTLZW = 1;   {encryption, compression and funny file handling }
  121.    ZTCRYPT = 2; {flags - My docs (03/88) from OMEN say these have}
  122.    ZTRLE = 3;   {not been defined yet                            }
  123. { ZF3 }
  124.    ZCACK1 = 1;  {God only knows...                               }
  125.  
  126. VAR
  127.    rxpos: LONGINT; {file position received from Z_GetHeader}
  128.    rxhdr: hdrtype;    {receive header var}
  129.    rxtimeout,
  130.    rxtype,
  131.    rxframeind: INTEGER;
  132.    attn: buftype;
  133.    secbuf: buftype;
  134.    fname: STRING;
  135.    fmode: INTEGER;
  136.    ftime,
  137.    fsize: LONGINT;
  138.    usecrc32: BOOLEAN;
  139.    zcps, zerrors: WORD;
  140.    txpos: LONGINT;
  141.    txhdr: hdrtype;
  142.    ztime: LONGINT;
  143.  
  144. CONST
  145.    lastsent: BYTE = 0;
  146.  
  147. FUNCTION Z_SetTimer: LONGINT;
  148. VAR
  149.    l: LONGINT;
  150.    h,m,s,x: WORD;
  151. BEGIN
  152.    GetTime(h,m,s,x);
  153.    l := LONGINT(h) * 3600;
  154.    l := l + LONGINT(m) * 60;
  155.    l := l + LONGINT(s);
  156.    Z_SetTimer := l
  157. END;
  158.  
  159. FUNCTION Z_FileCRC32(VAR f: FILE): LONGINT;
  160. VAR
  161.    fbuf: buftype;
  162.    crc: LONGINT;
  163.    bread, n: INTEGER;
  164. BEGIN {$I-}
  165.    crc := $FFFFFFFF;
  166.    Seek(f,0);
  167.    IF (IOresult <> 0) THEN
  168.       {null};
  169.    REPEAT
  170.       BlockRead(f,fbuf,ZBUFSIZE,bread);
  171.       FOR n := 0 TO (bread - 1) DO
  172.          crc := UpdC32(fbuf[n],crc)
  173.    UNTIL (bread < ZBUFSIZE) OR (IOresult <> 0);
  174.    Seek(f,0);
  175.    IF (IOresult <> 0) THEN
  176.       {null};
  177.    Z_FileCRC32 := crc
  178. END; {$I+}
  179.  
  180. FUNCTION Z_GetByte(tenths: INTEGER): INTEGER;
  181. (* Reads a byte from the modem - Returns RCDO if *)
  182. (* no carrier, or ZTIMEOUT if nothing received   *)
  183. (* within 'tenths' of a second.                  *)
  184. VAR
  185.    n: INTEGER;
  186. BEGIN
  187.    REPEAT
  188.       IF (NOT Z_Carrier) THEN
  189.       BEGIN
  190.          Z_GetByte := RCDO; { nobody to talk to }
  191.          Exit
  192.       END;
  193.       IF (Z_CharAvail) THEN
  194.       BEGIN
  195.          Z_GetByte := Z_ReceiveByte; { got character }
  196.          Exit
  197.       END;
  198.       Dec(tenths);              { dec. the count    }
  199.       Delay(100)                { pause 1/10th sec. }
  200.    UNTIL (tenths <= 0);
  201.    Z_GetByte := ZTIMEOUT        { timed out }
  202. END;
  203.  
  204. FUNCTION Z_qk_read: INTEGER;
  205. (* Just like Z_GetByte, but timeout value is in *)
  206. (* global var rxtimeout.                        *)
  207. BEGIN
  208.    Z_qk_read := Z_GetByte(rxtimeout)
  209. END;
  210.  
  211.  
  212. FUNCTION Z_TimedRead: INTEGER;
  213. (* A Z_qk_read, that strips parity and *)
  214. (* ignores XON/XOFF characters.        *)
  215. VAR
  216.    done: BOOLEAN;
  217.    c: INTEGER;
  218. BEGIN
  219.    done := FALSE;
  220.    REPEAT
  221.       c := Z_qk_read AND $FF7F                { strip parity }
  222.    UNTIL (c < 0) OR (NOT (Lo(c) IN [17,19])); { wait for other than XON/XOFF }
  223.    Z_TimedRead := c
  224. END;
  225.  
  226. PROCEDURE Z_SendCan;
  227. (* Send a zmodem CANcel sequence to the other guy *)
  228. (* 8 CANs and 8 backspaces                        *)
  229. VAR
  230.    n: BYTE;
  231. BEGIN
  232.    Z_ClearOutbound; { spare them the junk }
  233.    FOR n := 1 To 8 DO
  234.    BEGIN
  235.       Z_SendByte(CAN);
  236.       Delay(100)     { the pause seems to make reception of the sequence }
  237.    END;              { more reliable                                     }
  238.    FOR n := 1 TO 10 DO
  239.       Z_SendByte(8)
  240. END;
  241.  
  242. PROCEDURE Z_PutString(VAR p: buftype);
  243. (* Outputs an ASCII-Z type string (null terminated) *)
  244. (* Processes meta characters 221 (send break) and   *)
  245. (* 222 (2 second delay).                            *)
  246. VAR
  247.    n: INTEGER;
  248. BEGIN
  249.    n := 0;
  250.    WHILE (n < ZBUFSIZE) AND (p[n] <> 0) DO
  251.    BEGIN
  252.       CASE p[n] OF
  253.          221 : Z_SendBreak;
  254.          222 : Delay(2000)
  255.          ELSE
  256.             Z_SendByte(p[n])
  257.       END;
  258.       Inc(n)
  259.    END
  260. END;
  261.  
  262. PROCEDURE Z_PutHex(b: BYTE);
  263. (* Output a byte as two hex digits (in ASCII) *)
  264. (* Uses lower case to avoid confusion with    *)
  265. (* escaped control characters.                *)
  266. CONST
  267.    hex: ARRAY[0..15] OF CHAR = '0123456789abcdef';
  268. BEGIN
  269.    Z_SendByte(Ord(hex[b SHR 4]));  { high nybble }
  270.    Z_SendByte(Ord(hex[b AND $0F])) { low nybble  }
  271. END;
  272.  
  273. PROCEDURE Z_SendHexHeader(htype: BYTE; VAR hdr: hdrtype);
  274. (* Sends a zmodem hex type header *)
  275. VAR
  276.    crc: WORD;
  277.    n, i: INTEGER;
  278. BEGIN
  279.    Z_SendByte(ZPAD);                  { '*' }
  280.    Z_SendByte(ZPAD);                  { '*' }
  281.    Z_SendByte(ZDLE);                  { 24  }
  282.    Z_SendByte(ZHEX);                  { 'B' }
  283.    Z_PutHex(htype);
  284.    crc := UpdCrc(htype,0);
  285.    FOR n := 0 TO 3 DO
  286.    BEGIN
  287.       Z_PutHex(hdr[n]);
  288.       crc := UpdCrc(hdr[n],crc)
  289.    END;
  290.    crc := UpdCrc(0,crc);
  291.    crc := UpdCrc(0,crc);
  292.    Z_PutHex(Lo(crc SHR 8));
  293.    Z_PutHex(Lo(crc));
  294.    Z_SendByte(13);                    { make it readable to the other end }
  295.    Z_SendByte(10);                    { just in case                      }
  296.    IF (htype <> ZFIN) AND (htype <> ZACK) THEN
  297.       Z_SendByte(17);                 { Prophylactic XON to assure flow   }
  298.    IF (NOT Z_Carrier) THEN
  299.       Z_ClearOutbound
  300. END;
  301.  
  302. FUNCTION Z_PullLongFromHeader(VAR hdr: hdrtype): LONGINT;
  303. (* Stuffs a longint into a header variable - N.B. - bytes are REVERSED! *)
  304. VAR
  305.    l: LONGINT;
  306. BEGIN
  307.    l := hdr[ZP3];               { hard coded for efficiency }
  308.    l := (l SHL 8) OR hdr[ZP2];
  309.    l := (l SHL 8) OR hdr[ZP1];
  310.    l := (l SHL 8) OR hdr[ZP0];
  311.    Z_PullLongFromHeader := l
  312. END;
  313.  
  314. PROCEDURE Z_PutLongIntoHeader(l: LONGINT);
  315. (* Reverse of above *)
  316. BEGIN
  317.    txhdr[ZP0] := BYTE(l);
  318.    txhdr[ZP1] := BYTE(l SHR 8);
  319.    txhdr[ZP2] := BYTE(l SHR 16);
  320.    txhdr[ZP3] := BYTE(l SHR 24)
  321. END;
  322.  
  323. FUNCTION Z_GetZDL: INTEGER;
  324. (* Gets a byte and processes for ZMODEM escaping or CANcel sequence *)
  325. VAR
  326.    c, d: INTEGER;
  327. BEGIN
  328.    IF (NOT Z_Carrier) THEN
  329.    BEGIN
  330.       Z_GetZDL := RCDO;
  331.       Exit
  332.    END;
  333.    c := Z_qk_read;
  334.    IF (c <> ZDLE) THEN
  335.    BEGIN
  336.       Z_GetZDL := c;
  337.       Exit
  338.    END;   {got ZDLE or 1st CAN}
  339.    c := Z_qk_read;
  340.    IF (c = CAN) THEN  {got 2nd CAN}
  341.    BEGIN
  342.       c := Z_qk_read;
  343.       IF (c = CAN) THEN {got 3rd CAN}
  344.       BEGIN
  345.          c := Z_qk_read;
  346.          IF (c = CAN) THEN {got 4th CAN}
  347.             c := Z_qk_read
  348.       END
  349.    END;
  350.    { Flags set in high byte }
  351.    CASE c OF
  352.       CAN: Z_GetZDL := GOTCAN; {got 5th CAN}
  353.       ZCRCE,                   {got a frame end marker}
  354.       ZCRCG,
  355.       ZCRCQ,
  356.       ZCRCW: Z_GetZDL := (c OR GOTOR);
  357.       ZRUB0: Z_GetZDL := $007F; {got an ASCII DELete}
  358.       ZRUB1: Z_GetZDL := $00FF  {any parity         }
  359.       ELSE
  360.       BEGIN
  361.          IF (c < 0) THEN
  362.             Z_GetZDL := c
  363.          ELSE IF ((c AND $60) = $40) THEN  {make sure it was a valid escape}
  364.             Z_GetZDL := c XOR $40
  365.          ELSE
  366.             Z_GetZDL := ZERROR
  367.       END
  368.    END
  369. END;
  370.  
  371. FUNCTION Z_GetHex: INTEGER;
  372. (* Get a byte that has been received as two ASCII hex digits *)
  373. VAR
  374.    c, n: INTEGER;
  375. BEGIN
  376.    n := Z_TimedRead;
  377.    IF (n < 0) THEN
  378.    BEGIN
  379.       Z_GetHex := n;
  380.       Exit
  381.    END;
  382.    n := n - $30;                     {build the high nybble}
  383.    IF (n > 9) THEN
  384.       n := n - 39;
  385.    IF (n AND $FFF0 <> 0) THEN
  386.    BEGIN
  387.       Z_GetHex := ZERROR;
  388.       Exit
  389.    END;
  390.    c := Z_TimedRead;
  391.    IF (c < 0) THEN
  392.    BEGIN
  393.       Z_GetHex := c;
  394.       Exit
  395.    END;
  396.    c := c - $30;                     {now the low nybble}
  397.    IF (c > 9) THEN
  398.       c := c - 39;
  399.    IF (c AND $FFF0 <> 0) THEN
  400.    BEGIN
  401.       Z_GetHex := ZERROR;
  402.       Exit
  403.    END;
  404.    Z_GetHex := (n SHL 4) OR c        {Insert tab 'A' in slot 'B'...}
  405. END;
  406.  
  407. FUNCTION Z_GetHexHeader(VAR hdr: hdrtype): INTEGER;
  408. (* Receives a zmodem hex type header *)
  409. VAR
  410.    crc: WORD;
  411.    c, n: INTEGER;
  412. BEGIN
  413.    c := Z_GetHex;
  414.    IF (c < 0) THEN
  415.    BEGIN
  416.       Z_GetHexHeader := c;
  417.       Exit
  418.    END;
  419.    rxtype := c;                        {get the type of header}
  420.    crc := UpdCrc(rxtype,0);
  421.    FOR n := 0 To 3 DO                  {get the 4 bytes}
  422.    BEGIN
  423.       c := Z_GetHex;
  424.       IF (c < 0) THEN
  425.       BEGIN
  426.          Z_GetHexHeader := c;
  427.          Exit
  428.       END;
  429.       hdr[n] := Lo(c);
  430.       crc := UpdCrc(Lo(c),crc)
  431.    END;
  432.    c := Z_GetHex;
  433.    IF (c < 0) THEN
  434.    BEGIN
  435.       Z_GetHexHeader := c;
  436.       Exit
  437.    END;
  438.    crc := UpdCrc(Lo(c),crc);
  439.    c := Z_GetHex;
  440.    IF (c < 0) THEN
  441.    BEGIN
  442.       Z_GetHexHeader := c;
  443.       Exit
  444.    END;
  445.    crc := UpdCrc(Lo(c),crc);             {check the CRC}
  446.    IF (crc <> 0) THEN
  447.    BEGIN
  448.       Inc(zerrors);
  449.       Z_Errors(zerrors);
  450.       Z_GetHexHeader := ZERROR;
  451.       Exit
  452.    END;
  453.    IF (Z_GetByte(1) = 13) THEN           {throw away CR/LF}
  454.       c := Z_GetByte(1);
  455.    Z_GetHexHeader := rxtype
  456. END;
  457.  
  458.  
  459. FUNCTION Z_GetBinaryHeader(VAR hdr: hdrtype): INTEGER;
  460. (* Same as above, but binary with 16 bit CRC *)
  461. VAR
  462.    crc: WORD;
  463.    c, n: INTEGER;
  464. BEGIN
  465.    c := Z_GetZDL;
  466.    IF (c < 0) THEN
  467.    BEGIN
  468.       Z_GetBinaryHeader := c;
  469.       Exit
  470.    END;
  471.    rxtype := c;
  472.    crc := UpdCrc(rxtype,0);
  473.    FOR n := 0 To 3 DO
  474.    BEGIN
  475.       c := Z_GetZDL;
  476.       IF (Hi(c) <> 0) THEN
  477.       BEGIN
  478.          Z_GetBinaryHeader := c;
  479.          Exit
  480.       END;
  481.       hdr[n] := Lo(c);
  482.       crc := UpdCrc(Lo(c),crc)
  483.    END;
  484.    c := Z_GetZDL;
  485.    IF (Hi(c) <> 0) THEN
  486.    BEGIN
  487.       Z_GetBinaryHeader := c;
  488.       Exit
  489.    END;
  490.    crc := UpdCrc(Lo(c),crc);
  491.    c := Z_GetZDL;
  492.    IF (Hi(c) <> 0) THEN
  493.    BEGIN
  494.       Z_GetBinaryHeader := c;
  495.       Exit
  496.    END;
  497.    crc := UpdCrc(Lo(c),crc);
  498.    IF (crc <> 0) THEN
  499.    BEGIN
  500.       Inc(zerrors);
  501.       Z_Errors(zerrors);
  502.       Exit
  503.    END;
  504.    Z_GetBinaryHeader := rxtype
  505. END;
  506.  
  507.  
  508. FUNCTION Z_GetBinaryHead32(VAR hdr: hdrtype): INTEGER;
  509. (* Same as above but with 32 bit CRC *)
  510. VAR
  511.    crc: LONGINT;
  512.    c, n: INTEGER;
  513. BEGIN
  514.    c := Z_GetZDL;
  515.    IF (c < 0) THEN
  516.    BEGIN
  517.       Z_GetBinaryHead32 := c;
  518.       Exit
  519.    END;
  520.    rxtype := c;
  521.    crc := UpdC32(rxtype,$FFFFFFFF);
  522.    FOR n := 0 To 3 DO
  523.    BEGIN
  524.       c := Z_GetZDL;
  525.       IF (Hi(c) <> 0) THEN
  526.       BEGIN
  527.          Z_GetBinaryHead32 := c;
  528.          Exit
  529.       END;
  530.       hdr[n] := Lo(c);
  531.       crc := UpdC32(Lo(c),crc)
  532.    END;
  533.    FOR n := 0 To 3 DO
  534.    BEGIN
  535.       c := Z_GetZDL;
  536.       IF (Hi(c) <> 0) THEN
  537.       BEGIN
  538.          Z_GetBinaryHead32 := c;
  539.          Exit
  540.       END;
  541.       crc := UpdC32(Lo(c),crc)
  542.    END;
  543.    IF (crc <> $DEBB20E3) THEN   {this is the polynomial value}
  544.    BEGIN
  545.       Inc(zerrors);
  546.       Z_Errors(zerrors);
  547.       Z_GetBinaryHead32 := ZERROR;
  548.       Exit
  549.    END;
  550.    Z_GetBinaryHead32 := rxtype
  551. END;
  552.  
  553. FUNCTION Z_GetHeader(VAR hdr: hdrtype): INTEGER;
  554. (* Use this routine to get a header - it will figure out  *)
  555. (* what type it is getting (hex, bin16 or bin32) and call *)
  556. (* the appropriate routine.                               *)
  557. LABEL
  558.    gotcan, again, agn2, splat, done;  {sorry, but it's actually eisier to}
  559. VAR                                   {follow, and lots more efficient   }
  560.    c, n, cancount: INTEGER;           {this way...                       }
  561. BEGIN
  562.    n := zbaud * 2;                    {A guess at the # of garbage characters}
  563.    cancount := 5;                     {to expect.                            }
  564.    usecrc32 := FALSE;                 {assume 16 bit until proven otherwise  }
  565. again:
  566.    IF (KeyPressed) THEN               {check for operator panic}
  567.       IF (ReadKey = #27) THEN         {in the form of ESCape   }
  568.       BEGIN
  569.          Z_SendCan;                              {tell the other end,   }
  570.          Z_message('Cancelled from keyboard');  {the operator,         }
  571.          Z_GetHeader := ZCAN;                   {and the rest of the   }
  572.          Exit                                   {routines to forget it.}
  573.       END;
  574.    rxframeind := 0;
  575.    rxtype := 0;
  576.    c := Z_TimedRead;
  577.    CASE c OF
  578.       ZPAD: {we want this! - all headers begin with '*'.} ;
  579.       RCDO,
  580.       ZTIMEOUT: GOTO done;
  581.       CAN: BEGIN
  582. gotcan:
  583.               Dec(cancount);
  584.               IF (cancount < 0) THEN
  585.               BEGIN
  586.                  c := ZCAN;
  587.                  GOTO done
  588.               END;
  589.               c := Z_GetByte(1);
  590.               CASE c OF
  591.                  ZTIMEOUT: GOTO again;
  592.                  ZCRCW: BEGIN
  593.                            c := ZERROR;
  594.                            GOTO done
  595.                         END;
  596.                  RCDO: GOTO done;
  597.                  CAN: BEGIN
  598.                          Dec(cancount);
  599.                          IF (cancount < 0) THEN
  600.                          BEGIN
  601.                             c := ZCAN;
  602.                             GOTO done
  603.                          END;
  604.                          GOTO again
  605.                       END
  606.                  ELSE
  607.                     {fallthru}
  608.               END {case}
  609.            END {can}
  610.       ELSE
  611. agn2: BEGIN
  612.          Dec(n);
  613.          IF (n < 0) THEN
  614.          BEGIN
  615.             Inc(zerrors);
  616.             Z_Errors(zerrors);
  617.             Z_message('Header is FUBAR');
  618.             Z_GetHeader := ZERROR;
  619.             Exit
  620.          END;
  621.          IF (c <> CAN) THEN
  622.             cancount := 5;
  623.          GOTO again
  624.       END
  625.    END;           {only falls thru if ZPAD - anything else is trash}
  626.    cancount := 5;
  627. splat:
  628.    c := Z_TimedRead;
  629.    CASE c OF
  630.       ZDLE: {this is what we want!} ;
  631.       ZPAD: GOTO splat;   {junk or second '*' of a hex header}
  632.       RCDO,
  633.       ZTIMEOUT: GOTO done
  634.       ELSE
  635.          GOTO agn2
  636.    END; {only falls thru if ZDLE}
  637.    c := Z_TimedRead;
  638.    CASE c OF
  639.       ZBIN32: BEGIN
  640.                  rxframeind := ZBIN32;        {using 32 bit CRC}
  641.                  c := Z_GetBinaryHead32(hdr)
  642.               END;
  643.       ZBIN: BEGIN
  644.                rxframeind := ZBIN;            {bin with 16 bit CRC}
  645.                c := Z_GetBinaryHeader(hdr)
  646.             END;
  647.       ZHEX: BEGIN
  648.                rxframeind := ZHEX;            {hex}
  649.                c := Z_GetHexHeader(hdr)
  650.             END;
  651.       CAN: GOTO gotcan;
  652.       RCDO,
  653.       ZTIMEOUT: GOTO done
  654.       ELSE
  655.          GOTO agn2
  656.    END; {only falls thru if we got ZBIN, ZBIN32 or ZHEX}
  657.    rxpos := Z_PullLongFromHeader(hdr);        {set rxpos just in case this}
  658. done:                                         {header has file position   }
  659.    Z_GetHeader := c                           {info (i.e.: ZRPOS, etc.   )}
  660. END;
  661.  
  662. (***************************************************)
  663. (* RECEIVE FILE ROUTINES                           *)
  664. (***************************************************)
  665.  
  666. CONST
  667.    ZATTNLEN = 32;  {max length of attention string}
  668.    lastwritten: BYTE = 0;
  669. VAR
  670.    t: LONGINT;
  671.    rzbatch: BOOLEAN;
  672.    outfile: FILE;     {this is the file}
  673.    tryzhdrtype: BYTE;
  674.    rxcount: INTEGER;
  675.    filestart: LONGINT;
  676.    isbinary, eofseen: BOOLEAN;
  677.    zconv: BYTE;
  678.    zrxpath: STRING;
  679.  
  680. FUNCTION RZ_ReceiveDa32(VAR buf: buftype; blength: INTEGER): INTEGER;
  681. (* Get a 32 bit CRC data block *)
  682. LABEL
  683.    crcfoo;
  684. VAR
  685.    c, d, n: INTEGER;
  686.    crc: LONGINT;
  687.    done: boolean;
  688. BEGIN
  689.    usecrc32 := TRUE;
  690.    crc := $FFFFFFFF;
  691.    rxcount := 0;
  692.    done := FALSE;
  693.    REPEAT
  694.       c := Z_GetZDL;
  695.       IF (Hi(c) <> 0) THEN
  696.       BEGIN
  697. crcfoo:  CASE c OF
  698.             GOTCRCE,
  699.             GOTCRCG,
  700.             GOTCRCQ,
  701.             GOTCRCW: BEGIN
  702.                         d := c;
  703.                         crc := UpdC32(Lo(c),crc);
  704.                         FOR n := 0 TO 3 DO
  705.                         BEGIN
  706.                            c := Z_GetZDL;
  707.                            IF (Hi(c) <> 0) THEN
  708.                               GOTO crcfoo;
  709.                            crc := UpdC32(Lo(c),crc)
  710.                         END;
  711.                         IF (crc <> $DEBB20E3) THEN
  712.                         BEGIN
  713.                            Inc(zerrors);
  714.                            Z_Errors(zerrors);
  715.                            RZ_ReceiveDa32 := ZERROR
  716.                         END
  717.                         ELSE
  718.                            RZ_ReceiveDa32 := d;
  719.                         DONE := TRUE
  720.                      END;
  721.             GOTCAN: BEGIN
  722.                        RZ_ReceiveDa32 := ZCAN;
  723.                        DONE := TRUE
  724.                     END;
  725.             ZTIMEOUT: BEGIN
  726.                          RZ_ReceiveDa32 := c;
  727.                          DONE := TRUE
  728.                       END;
  729.             RCDO: BEGIN
  730.                      RZ_ReceiveDa32 := c;
  731.                      done := TRUE
  732.                   END
  733.             ELSE
  734.             BEGIN
  735.                Z_message('Debris');
  736.                Z_ClearInbound;
  737.                RZ_ReceiveDa32 := c;
  738.                DONE := TRUE
  739.             END
  740.          END
  741.       END;
  742.       IF (NOT done) THEN
  743.       BEGIN
  744.          Dec(blength);
  745.          IF (blength < 0) THEN
  746.          BEGIN
  747.             Z_message('Long packet');
  748.             RZ_ReceiveDa32 := ZERROR;
  749.             done := TRUE
  750.          END;
  751.          buf[INTEGER(rxcount)] := Lo(c);
  752.          Inc(rxcount);
  753.          crc := UpdC32(Lo(c),crc)
  754.       END
  755.    UNTIL done
  756. END;
  757.  
  758. FUNCTION RZ_ReceiveData(VAR buf: buftype; blength: INTEGER): INTEGER;
  759. (* get a 16 bit CRC data block *)
  760. LABEL
  761.    crcfoo;
  762. VAR
  763.    c, d: INTEGER;
  764.    crc: WORD;
  765.    done: boolean;
  766. BEGIN
  767.    IF (rxframeind = ZBIN32) THEN
  768.    BEGIN
  769.       Z_ShowCheck(TRUE);
  770.       RZ_ReceiveData := RZ_ReceiveDa32(buf,blength);
  771.       Exit
  772.    END;
  773.    Z_ShowCheck(FALSE);
  774.    crc := 0;
  775.    rxcount := 0;
  776.    done := FALSE;
  777.    REPEAT
  778.       c := Z_GetZDL;
  779.       IF (Hi(c) <> 0) THEN
  780.       BEGIN
  781. crcfoo:  CASE c OF
  782.             GOTCRCE,
  783.             GOTCRCG,
  784.             GOTCRCQ,
  785.             GOTCRCW: BEGIN
  786.                         d := c;
  787.                         crc := UpdCrc(Lo(c),crc);
  788.                         c := Z_GetZDL;
  789.                         IF (Hi(c) <> 0) THEN
  790.                            GOTO crcfoo;
  791.                         crc := UpdCrc(Lo(c),crc);
  792.                         c := Z_GetZDL;
  793.                         IF (Hi(c) <> 0) THEN
  794.                            GOTO crcfoo;
  795.                         crc := UpdCrc(Lo(c),crc);
  796.                         IF (crc <> 0) THEN
  797.                         BEGIN
  798.                            Inc(zerrors);
  799.                            Z_Errors(zerrors);
  800.                            RZ_ReceiveData := ZERROR;
  801.                            done := TRUE
  802.                         END;
  803.                         RZ_ReceiveData := d;
  804.                         DONE := TRUE
  805.                      END;
  806.             GOTCAN: BEGIN
  807.                        Z_Message('Got CANned');
  808.                        RZ_ReceiveData := ZCAN;
  809.                        DONE := TRUE
  810.                     END;
  811.             ZTIMEOUT: BEGIN
  812.                          RZ_ReceiveData := c;
  813.                          DONE := TRUE
  814.                       END;
  815.             RCDO: BEGIN
  816.                      Z_Message('Lost carrier');
  817.                      RZ_ReceiveData := c;
  818.                      done := TRUE
  819.                   END
  820.             ELSE
  821.             BEGIN
  822.                Z_message('Debris');
  823.                Z_ClearInbound;
  824.                RZ_ReceiveData := c;
  825.                DONE := TRUE
  826.             END
  827.          END
  828.       END;
  829.       IF (NOT done) THEN
  830.       BEGIN
  831.          Dec(blength);
  832.          IF (blength < 0) THEN
  833.          BEGIN
  834.             Z_message('Long packet');
  835.             RZ_ReceiveData := ZERROR;
  836.             done := TRUE
  837.          END;
  838.          buf[INTEGER(rxcount)] := Lo(c);
  839.          Inc(rxcount);
  840.          crc := UpdCrc(Lo(c),crc)
  841.       END
  842.    UNTIL done
  843. END;
  844.  
  845. PROCEDURE RZ_AckBibi;
  846. (* ACKnowledge the other ends request to terminate cleanly *)
  847. VAR
  848.    n: INTEGER;
  849. BEGIN
  850.    Z_PutLongIntoHeader(rxpos);
  851.    n := 4;
  852.    Z_ClearInbound;
  853.    REPEAT
  854.       Z_SendHexHeader(ZFIN,txhdr);
  855.       CASE Z_GetByte(20) OF
  856.          ZTIMEOUT,
  857.          RCDO: Exit;
  858.          79: BEGIN
  859.                 IF (Z_GetByte(10) = 79) THEN
  860.                    {null};
  861.                 Z_ClearInbound;
  862.                 Exit
  863.              END
  864.          ELSE
  865.             Z_ClearInbound;
  866.             Dec(n)
  867.       END
  868.    UNTIL (n <= 0)
  869. END;
  870.  
  871. FUNCTION RZ_InitReceiver: INTEGER;
  872. LABEL
  873.    again;
  874. VAR
  875.    c, n, errors: INTEGER;
  876. BEGIN
  877.    FillChar(attn,SizeOf(attn),0);
  878.    zerrors := 0;
  879.    FOR n := 10 DOWNTO 0 DO
  880.    BEGIN
  881.       IF (NOT Z_Carrier) THEN
  882.       BEGIN
  883.          Z_Message('Lost carrier');
  884.          RZ_InitReceiver := ZERROR;
  885.          Exit
  886.       END;
  887.       Z_PutLongIntoHeader(LONGINT(0));
  888.       txhdr[ZF0] := CANFDX OR CANOVIO OR CANFC32 OR CANBRK; {Full dplx, overlay I/O and CRC32}
  889.       Z_SendHexHeader(tryzhdrtype,txhdr);
  890.       IF (tryzhdrtype = ZSKIP) THEN
  891.          tryzhdrtype := ZRINIT;
  892. again:
  893.          c := Z_GetHeader(rxhdr);
  894.          Z_Frame(c);
  895.          CASE c OF
  896.          ZFILE: BEGIN
  897.                    zconv := rxhdr[ZF0];
  898.                    tryzhdrtype := ZRINIT;
  899.                    c := RZ_ReceiveData(secbuf,ZBUFSIZE);
  900.                    Z_Frame(c);
  901.                    IF (c = GOTCRCW) THEN
  902.                    BEGIN
  903.                       RZ_InitReceiver := ZFILE;
  904.                       Exit
  905.                    END;
  906.                    Z_SendHexHeader(ZNAK,txhdr);
  907.                    GOTO again
  908.                 END;
  909.          ZSINIT: BEGIN
  910.                    c := RZ_ReceiveData(attn,ZBUFSIZE);
  911.                    Z_Frame(c);
  912.                    IF (c = GOTCRCW) THEN
  913.                        Z_SendHexHeader(ZACK,txhdr)
  914.                     ELSE
  915.                        Z_SendHexHeader(ZNAK,txhdr);
  916.                     GOTO again
  917.                  END;
  918.          ZFREECNT: BEGIN
  919.                       Z_PutLongIntoHeader(DiskFree(0));
  920.                       Z_SendHexHeader(ZACK,txhdr);
  921.                       GOTO again
  922.                    END;
  923.          ZCOMMAND: BEGIN
  924.                       c := RZ_ReceiveData(secbuf,ZBUFSIZE);
  925.                       Z_Frame(c);
  926.                       IF (c = GOTCRCW) THEN
  927.                       BEGIN
  928.                          Z_PutLongIntoHeader(LONGINT(0));
  929.                          REPEAT
  930.                             Z_SendHexHeader(ZCOMPL,txhdr);
  931.                             Inc(errors)
  932.                          UNTIL (errors > 10) OR (Z_GetHeader(rxhdr) = ZFIN);
  933.                          RZ_AckBibi;
  934.                          RZ_InitReceiver := ZCOMPL;
  935.                          Exit
  936.                       END;
  937.                       Z_SendHexHeader(ZNAK,txhdr);
  938.                       GOTO again
  939.                    END;
  940.          ZCOMPL,
  941.          ZFIN: BEGIN
  942.                   RZ_InitReceiver := ZCOMPL;
  943.                   Exit
  944.                END;
  945.          ZCAN,
  946.          RCDO: BEGIN
  947.                   RZ_InitReceiver := c;
  948.                   Exit
  949.                END
  950.       END
  951.    END;
  952.    Z_message('Timeout');
  953.    RZ_InitReceiver := ZERROR
  954. END;
  955.  
  956. FUNCTION RZ_GetHeader: INTEGER;
  957. VAR
  958.    e, p, n, i: INTEGER;
  959.    multiplier: LONGINT;
  960.    s: STRING;
  961.    ttime, tsize: LONGINT;
  962.    tname: STRING;
  963. BEGIN
  964.    isbinary := TRUE;    {Force the issue!}
  965.    fsize := LONGINT(0);
  966.    p := 0;
  967.    s := '';
  968.    WHILE (p < 255) AND (secbuf[p] <> 0) DO
  969.    BEGIN
  970.       s := s + UpCase(Chr(secbuf[p]));
  971.       Inc(p)
  972.    END;
  973.    Inc(p);
  974.    (* get rid of drive & path specifiers *)
  975.    WHILE (Pos(':',s) > 0) DO
  976.       Delete(s,1,Pos(':',s));
  977.    WHILE (Pos('\',s) > 0) DO
  978.       Delete(s,1,Pos('\',s));
  979.    fname := s;
  980.  
  981. (**** done with name ****)
  982.  
  983.    fsize := LONGINT(0);
  984.    WHILE (p < ZBUFSIZE) AND (secbuf[p] <> $20) AND (secbuf[p] <> 0) DO
  985.    BEGIN
  986.       fsize := (fsize *10) + Ord(secbuf[p]) - $30;
  987.       Inc(p)
  988.    END;
  989.    Inc(p);
  990.  
  991. (**** done with size ****)
  992.  
  993.    s := '';
  994.    WHILE (p < ZBUFSIZE) AND (secbuf[p] IN [$30..$37]) DO
  995.    BEGIN
  996.       s := s + Chr(secbuf[p]);
  997.       Inc(p)
  998.    END;
  999.    Inc(p);
  1000.    ftime := Z_FromUnixDate(s);
  1001.  
  1002. (**** done with time ****)
  1003.  
  1004.    IF (Z_FindFile(zrxpath+fname,tname,tsize,ttime)) THEN
  1005.    BEGIN
  1006.       IF (zconv = ZCRESUM) AND (fsize > tsize) THEN
  1007.       BEGIN
  1008.          filestart := tsize;
  1009.          IF (NOT Z_OpenFile(outfile,zrxpath + fname)) THEN
  1010.          BEGIN
  1011.             Z_message('Error opening '+fname);
  1012.             RZ_GetHeader := ZERROR;
  1013.             Exit
  1014.          END;
  1015.          IF (NOT Z_SeekFile(outfile,tsize)) THEN
  1016.          BEGIN
  1017.             Z_Message('Error positioning file');
  1018.             RZ_GetHeader := ZERROR;
  1019.             Exit
  1020.          END;
  1021.          Z_Message('Recovering')
  1022.       END
  1023.       ELSE
  1024.       BEGIN
  1025.          Z_ShowName(fname);
  1026.          Z_Message('File is already complete');
  1027.          RZ_GetHeader := ZSKIP;
  1028.          Exit
  1029.       END
  1030.    END
  1031.    ELSE
  1032.    BEGIN
  1033.       filestart := 0;
  1034.       IF (NOT Z_MakeFile(outfile,zrxpath + fname)) THEN
  1035.       BEGIN
  1036.          Z_message('Unable to create '+fname);
  1037.          RZ_GetHeader := ZERROR;
  1038.          Exit
  1039.       END
  1040.    END;
  1041.    Z_ShowName(fname);
  1042.    Z_ShowSize(fsize);
  1043.    Z_ShowTransferTime(fsize,zbaud);
  1044.    RZ_GetHeader := ZOK
  1045. END;
  1046.  
  1047. FUNCTION RZ_SaveToDisk(VAR rxbytes: LONGINT): INTEGER;
  1048. BEGIN
  1049.    IF (KeyPressed) THEN
  1050.       IF (ReadKey = #27) THEN
  1051.       BEGIN
  1052.          Z_message('Aborted from keyboard');
  1053.          Z_SendCan;
  1054.          RZ_SaveToDisk := ZERROR;
  1055.          Exit
  1056.       END;
  1057.    IF (NOT Z_WriteFile(outfile,secbuf,rxcount)) THEN
  1058.    BEGIN
  1059.       Z_Message('Disk write error');
  1060.       RZ_SaveToDisk := ZERROR
  1061.    END
  1062.    ELSE
  1063.       RZ_SaveToDisk := ZOK;
  1064.    rxbytes := rxbytes + rxcount
  1065. END;
  1066.  
  1067. FUNCTION RZ_ReceiveFile: INTEGER;
  1068. LABEL
  1069.    err, nxthdr, moredata;
  1070. VAR
  1071.    c, n: INTEGER;
  1072.    rxbytes: LONGINT;
  1073.    sptr: STRING;
  1074.    done: BOOLEAN;
  1075. BEGIN
  1076.    zerrors := 0;
  1077.    done := FALSE;
  1078.    eofseen := FALSE;
  1079.    c := RZ_GetHeader;
  1080.    IF (c <> ZOK) THEN
  1081.    BEGIN
  1082.       IF (c = ZSKIP) THEN
  1083.          tryzhdrtype := ZSKIP;
  1084.       RZ_ReceiveFile := c;
  1085.       Exit
  1086.    END;
  1087.    c := ZOK;
  1088.    n := 10;
  1089.    rxbytes := filestart;
  1090.    rxpos := filestart;
  1091.    ztime := Z_SetTimer;
  1092.    zcps := 0;
  1093.    REPEAT
  1094.       Z_PutLongIntoHeader(rxbytes);
  1095.       Z_SendHexHeader(ZRPOS,txhdr);
  1096. nxthdr:
  1097.       c := Z_GetHeader(rxhdr);
  1098.       Z_Frame(c);
  1099.       CASE c OF
  1100.          ZDATA: BEGIN
  1101.                    IF (rxpos <> rxbytes) THEN
  1102.                    BEGIN
  1103.                       Dec(n);
  1104.                       Inc(zerrors);
  1105.                       Z_Errors(zerrors);
  1106.                       IF (n < 0) THEN
  1107.                          GOTO err;
  1108.                       Z_message('Bad position');
  1109.                       Z_PutString(attn)
  1110.                    END
  1111.                    ELSE
  1112.                    BEGIN
  1113. moredata:
  1114.                       c := RZ_ReceiveData(secbuf,ZBUFSIZE);
  1115.                       Z_Frame(c);
  1116.                       CASE c OF
  1117.                          ZCAN,
  1118.                          RCDO: GOTO err;
  1119.                          ZERROR: BEGIN
  1120.                                     Dec(n);
  1121.                                     Inc(zerrors);
  1122.                                     Z_Errors(zerrors);
  1123.                                     IF (n < 0) THEN
  1124.                                         GOTO err;
  1125.                                     Z_PutString(attn)
  1126.                                  END;
  1127.                          ZTIMEOUT: BEGIN
  1128.                                       Dec(n);
  1129.                                       IF (n < 0) THEN
  1130.                                          GOTO err
  1131.                                    END;
  1132.                          GOTCRCW: BEGIN
  1133.                                      n := 10;
  1134.                                      c := RZ_SaveToDisk(rxbytes);
  1135.                                      IF (c <> ZOK) THEN
  1136.                                      BEGIN
  1137.                                         RZ_ReceiveFile := c;
  1138.                                         Exit
  1139.                                      END;
  1140.                                      Z_ShowLoc(rxbytes);
  1141.                                      Z_PutLongIntoHeader(rxbytes);
  1142.                                      Z_SendHexHeader(ZACK,txhdr);
  1143.                                      GOTO nxthdr
  1144.                                   END;
  1145.                          GOTCRCQ: BEGIN
  1146.                                      n := 10;
  1147.                                      c := RZ_SaveToDisk(rxbytes);
  1148.                                      IF (c <> ZOK) THEN
  1149.                                      BEGIN
  1150.                                         RZ_ReceiveFile := c;
  1151.                                         Exit
  1152.                                      END;
  1153.                                      Z_ShowLoc(rxbytes);
  1154.                                      Z_PutLongIntoHeader(rxbytes);
  1155.                                      Z_SendHexHeader(ZACK,txhdr);
  1156.                                      GOTO moredata
  1157.                                   END;
  1158.                          GOTCRCG: BEGIN
  1159.                                      n := 10;
  1160.                                      c := RZ_SaveToDisk(rxbytes);
  1161.                                      IF (c <> ZOK) THEN
  1162.                                      BEGIN
  1163.                                         RZ_ReceiveFile := c;
  1164.                                         Exit
  1165.                                      END;
  1166.                                      Z_ShowLoc(rxbytes);
  1167.                                      GOTO moredata
  1168.                                   END;
  1169.                          GOTCRCE: BEGIN
  1170.                                      n := 10;
  1171.                                      c := RZ_SaveToDisk(rxbytes);
  1172.                                      IF (c <> ZOK) THEN
  1173.                                      BEGIN
  1174.                                         RZ_ReceiveFile := c;
  1175.                                         Exit
  1176.                                      END;
  1177.                                      Z_ShowLoc(rxbytes);
  1178.                                      GOTO nxthdr
  1179.                                   END
  1180.                       END {case}
  1181.                    END
  1182.                 END; {case of ZDATA}
  1183.          ZNAK,
  1184.          ZTIMEOUT: BEGIN
  1185.                       Dec(n);
  1186.                       IF (n < 0) THEN
  1187.                          GOTO err;
  1188.                       Z_ShowLoc(rxbytes)
  1189.                    END;
  1190.          ZFILE: BEGIN
  1191.                    c := RZ_ReceiveData(secbuf,ZBUFSIZE);
  1192.                    Z_Frame(c)
  1193.                 END;
  1194.          ZEOF: IF (rxpos = rxbytes) THEN
  1195.                BEGIN
  1196.                   RZ_ReceiveFile := c;
  1197.                   Exit
  1198.                END
  1199.                ELSE
  1200.                   GOTO nxthdr;
  1201.          ZERROR: BEGIN
  1202.                     Dec(n);
  1203.                     IF (n < 0) THEN
  1204.                        GOTO err;
  1205.                     Z_ShowLoc(rxbytes);
  1206.                     Z_PutSTring(attn)
  1207.                  END
  1208.          ELSE
  1209.          BEGIN
  1210.             c := ZERROR;
  1211.             GOTO err
  1212.          END
  1213.       END {case}
  1214.    UNTIL (NOT done);
  1215. err:
  1216.    RZ_ReceiveFile := ZERROR
  1217. END;
  1218.  
  1219. FUNCTION RZ_ReceiveBatch: INTEGER;
  1220. VAR
  1221.    s: STRING;
  1222.    c: INTEGER;
  1223.    done: BOOLEAN;
  1224. BEGIN
  1225.    Z_Message('Receiving...');
  1226.    done := FALSE;
  1227.    WHILE (NOT done) DO
  1228.    BEGIN
  1229.       IF NOT (Z_Carrier) THEN
  1230.       BEGIN
  1231.          RZ_ReceiveBatch := ZERROR;
  1232.          Exit
  1233.       END;
  1234.       c := RZ_ReceiveFile;
  1235.       zcps := fsize DIV (Z_SetTimer - ztime);
  1236.       Z_Frame(c);
  1237.       Z_SetFTime(outfile,ftime);
  1238.       Z_CloseFile(outfile);
  1239.       Str(zcps:4,s);
  1240.       Z_Message(s+' cps');
  1241.       CASE c OF
  1242.          ZEOF,
  1243.          ZSKIP: BEGIN
  1244.                    c := RZ_InitReceiver;
  1245.                    Z_Frame(c);
  1246.                    CASE c OF
  1247.                       ZFILE: {null};
  1248.                       ZCOMPL: BEGIN
  1249.                                  RZ_AckBibi;
  1250.                                  RZ_ReceiveBatch := ZOK;
  1251.                                  Exit
  1252.                               END;
  1253.                       ELSE
  1254.                       BEGIN
  1255.                          RZ_ReceiveBatch := ZERROR;
  1256.                          Exit
  1257.                       END
  1258.                    END
  1259.                 END
  1260.          ELSE
  1261.          BEGIN
  1262.             RZ_ReceiveBatch := c;
  1263.             Exit
  1264.          END
  1265.       END {case}
  1266.    END {while}
  1267. END;
  1268.  
  1269.  
  1270. FUNCTION Zmodem_Receive(path: STRING; comport: WORD; baudrate: LONGINT): BOOLEAN;
  1271. VAR
  1272.    i: INTEGER;
  1273. BEGIN
  1274.    zbaud := baudrate;
  1275.    zport := comport;
  1276.    Z_OpenWindow(TPZVER);
  1277.    Z_Message('Initializing...');
  1278.    IF (NOT Z_AsyncOn(comport,baudrate)) THEN
  1279.    BEGIN
  1280.       ClrScr;
  1281.       WRITELN('Unable to open:');
  1282.       WRITELN('Port: ',comport);
  1283.       WRITELN('Baud: ',baudrate);
  1284.       Delay(2000);
  1285.       Z_CloseWindow;
  1286.       Zmodem_Receive := FALSE;
  1287.       Exit
  1288.    END;
  1289.    zrxpath := path;
  1290.    IF (zrxpath[Length(zrxpath)] <> '\') AND (zrxpath <> '') THEN
  1291.       zrxpath := zrxpath + '\';
  1292.    rxtimeout := 100;
  1293.    tryzhdrtype := ZRINIT;
  1294.    i := RZ_InitReceiver;
  1295.    IF (i = ZCOMPL) OR ((i = ZFILE) AND ((RZ_ReceiveBatch) = ZOK)) THEN
  1296.    BEGIN
  1297.       Z_Message('Restoring async params');
  1298.       Z_AsyncOff;
  1299.       Z_CloseWindow;
  1300.       Zmodem_Receive := TRUE
  1301.    END
  1302.    ELSE
  1303.    BEGIN
  1304.       Z_ClearOutbound;
  1305.       Z_Message('Sending CAN');
  1306.       Z_SendCan;
  1307.       Z_Message('Restoring async params');
  1308.       Z_AsyncOff;
  1309.       Z_CloseWindow;
  1310.       Zmodem_Receive := FALSE;
  1311.    END
  1312. END;
  1313.  
  1314.  
  1315. (*######### SEND ROUTINES #####################################*)
  1316.  
  1317.  
  1318.  
  1319. VAR
  1320.    infile: FILE;
  1321.    strtpos: LONGINT;
  1322.    rxbuflen: INTEGER;
  1323.    txbuf: buftype;
  1324.    blkred: INTEGER;
  1325.  
  1326.  
  1327. PROCEDURE SZ_Z_SendByte(b: BYTE);
  1328. BEGIN
  1329.    IF ((b AND $7F) IN [16,17,19,24]) OR (((b AND $7F) = 13) AND ((lastsent AND $7F) = 64)) THEN
  1330.    BEGIN
  1331.       Z_SendByte(ZDLE);
  1332.       lastsent := (b XOR 64)
  1333.    END
  1334.    ELSE
  1335.       lastsent := b;
  1336.    Z_SendByte(lastsent)
  1337. END;
  1338.  
  1339. PROCEDURE SZ_SendBinaryHead32(htype: BYTE; VAR hdr: hdrtype);
  1340. VAR
  1341.    crc: LONGINT;
  1342.    n: INTEGER;
  1343. BEGIN
  1344.    Z_SendByte(ZPAD);
  1345.    Z_SendByte(ZDLE);
  1346.    Z_SendByte(ZBIN32);
  1347.    SZ_Z_SendByte(htype);
  1348.    crc := UpdC32(htype,$FFFFFFFF);
  1349.    FOR n := 0 TO 3 DO
  1350.    BEGIN
  1351.       SZ_Z_SendByte(hdr[n]);
  1352.       crc := UpdC32(hdr[n],crc)
  1353.    END;
  1354.    crc := (NOT crc);
  1355.    FOR n := 0 TO 3 DO
  1356.    BEGIN
  1357.       SZ_Z_SendByte(BYTE(crc));
  1358.       crc := (crc SHR 8)
  1359.    END;
  1360.    IF (htype <> ZDATA) THEN
  1361.       Delay(500)
  1362. END;
  1363.  
  1364. PROCEDURE SZ_SendBinaryHeader(htype: BYTE; VAR hdr: hdrtype);
  1365. VAR
  1366.    crc: WORD;
  1367.    n: INTEGER;
  1368. BEGIN
  1369.    IF (usecrc32) THEN
  1370.    BEGIN
  1371.       SZ_SendBinaryHead32(htype,hdr);
  1372.       Exit
  1373.    END;
  1374.    Z_SendByte(ZPAD);
  1375.    Z_SendByte(ZDLE);
  1376.    Z_SendByte(ZBIN);
  1377.    SZ_Z_SendByte(htype);
  1378.    crc := UpdCrc(htype,0);
  1379.    FOR n := 0 TO 3 DO
  1380.    BEGIN
  1381.       SZ_Z_SendByte(hdr[n]);
  1382.       crc := UpdCrc(hdr[n],crc)
  1383.    END;
  1384.    crc := UpdCrc(0,crc);
  1385.    crc := UpdCrc(0,crc);
  1386.    SZ_Z_SendByte(Lo(crc SHR 8));
  1387.    SZ_Z_SendByte(Lo(crc));
  1388.    IF (htype <> ZDATA) THEN
  1389.       Delay(500)
  1390. END;
  1391.  
  1392. PROCEDURE SZ_SendDa32(VAR buf: buftype; blength: INTEGER; frameend: BYTE);
  1393. VAR
  1394.    crc: LONGINT;
  1395.    t: INTEGER;
  1396. BEGIN
  1397.    crc := $FFFFFFFF;
  1398.    FOR t := 0 TO (blength - 1) DO
  1399.    BEGIN
  1400.       SZ_Z_SendByte(buf[t]);
  1401.       crc := UpdC32(buf[t],crc)
  1402.    END;
  1403.    crc := UpdC32(frameend,crc);
  1404.    crc := (NOT crc);
  1405.    Z_SendByte(ZDLE);
  1406.    Z_SendByte(frameend);
  1407.    FOR t := 0 TO 3 DO
  1408.    BEGIN
  1409.       SZ_Z_SendByte(BYTE(crc));
  1410.       crc := (crc SHR 8)
  1411.    END;
  1412.    BEGIN
  1413.       Z_SendByte(17);
  1414.       Delay(500)
  1415.    END
  1416. END;
  1417.  
  1418. PROCEDURE SZ_SendData(VAR buf: buftype; blength: INTEGER; frameend: BYTE);
  1419. VAR
  1420.    crc: WORD;
  1421.    t: INTEGER;
  1422. BEGIN
  1423.    IF (usecrc32) THEN
  1424.    BEGIN
  1425.       SZ_SendDa32(buf,blength,frameend);
  1426.       Exit
  1427.    END;
  1428.    crc := 0;
  1429.    FOR t := 0 TO (blength - 1) DO
  1430.    BEGIN
  1431.       SZ_Z_SendByte(buf[t]);
  1432.       crc := UpdCrc(buf[t],crc)
  1433.    END;
  1434.    crc := UpdCrc(frameend,crc);
  1435.    Z_SendByte(ZDLE);
  1436.    Z_SendByte(frameend);
  1437.    crc := UpdCrc(0,crc);
  1438.    crc := UpdCrc(0,crc);
  1439.    SZ_Z_SendByte(Lo(crc SHR 8));
  1440.    SZ_Z_SendByte(Lo(crc));
  1441.    IF (frameend = ZCRCW) THEN
  1442.    BEGIN
  1443.       Z_SendByte(17);
  1444.       Delay(500)
  1445.    END
  1446. END;
  1447.  
  1448.  
  1449. PROCEDURE SZ_EndSend;
  1450. VAR
  1451.    done: BOOLEAN;
  1452. BEGIN
  1453.    done := FALSE;
  1454.    REPEAT
  1455.       Z_PutLongIntoHeader(txpos);
  1456.       SZ_SendBinaryHeader(ZFIN,txhdr);
  1457.       CASE Z_GetHeader(rxhdr) OF
  1458.          ZFIN: BEGIN
  1459.                   Z_SendByte(Ord('O'));
  1460.                   Z_SendByte(Ord('O'));
  1461.                   Delay(500);
  1462.                   Z_ClearOutbound;
  1463.                   Exit
  1464.                END;
  1465.          ZCAN,
  1466.          RCDO,
  1467.          ZFERR,
  1468.          ZTIMEOUT: Exit
  1469.       END {case}
  1470.    UNTIL (done)
  1471. END;
  1472.  
  1473. FUNCTION SZ_GetReceiverInfo: INTEGER;
  1474. VAR
  1475.    rxflags, n, c: INTEGER;
  1476. BEGIN
  1477.    Z_Message('Getting info.');
  1478.    FOR n := 1 TO 10 DO
  1479.    BEGIN
  1480.       c := Z_GetHeader(rxhdr);
  1481.       Z_Frame(c);
  1482.       CASE c OF
  1483.          ZCHALLENGE: BEGIN
  1484.                         Z_PutLongIntoHeader(rxpos);
  1485.                         Z_SendHexHeader(ZACK,txhdr)
  1486.                      END;
  1487.          ZCOMMAND: BEGIN
  1488.                       Z_PutLongIntoHeader(LONGINT(0));
  1489.                       Z_SendHexHeader(ZRQINIT,txhdr)
  1490.                    END;
  1491.          ZRINIT: BEGIN
  1492.                     rxbuflen := (WORD(rxhdr[ZP1]) SHL 8) OR rxhdr[ZP0];
  1493.                     usecrc32 := ((rxhdr[ZF0] AND CANFC32) <> 0);
  1494.                     Z_ShowCheck(usecrc32);
  1495.                     SZ_GetReceiverInfo := ZOK;
  1496.                     Exit
  1497.                  END;
  1498.          ZCAN,
  1499.          RCDO,
  1500.          ZTIMEOUT: BEGIN
  1501.                       SZ_GetReceiverInfo := ZERROR;
  1502.                       Exit
  1503.                    END
  1504.          ELSE
  1505.             IF (c <> ZRQINIT) OR (rxhdr[ZF0] <> ZCOMMAND) THEN
  1506.                Z_SendHexHeader(ZNAK,txhdr)
  1507.       END {case}
  1508.    END; {for}
  1509.    SZ_GetReceiverInfo := ZERROR
  1510. END;
  1511.  
  1512. FUNCTION SZ_SyncWithReceiver: INTEGER;
  1513. VAR
  1514.    c, num_errs: INTEGER;
  1515.    done: BOOLEAN;
  1516. BEGIN
  1517.    num_errs := 7;
  1518.    done := FALSE;
  1519.    REPEAT
  1520.       c := Z_GetHeader(rxhdr);
  1521.       Z_Frame(c);
  1522.       Z_ClearInbound;
  1523.       CASE c OF
  1524.          ZTIMEOUT: BEGIN
  1525.                       Dec(num_errs);
  1526.                       IF (num_errs < 0) THEN
  1527.                       BEGIN
  1528.                          SZ_SyncWithReceiver := ZERROR;
  1529.                          Exit
  1530.                       END
  1531.                    END;
  1532.          ZCAN,
  1533.          ZABORT,
  1534.          ZFIN,
  1535.          RCDO: BEGIN
  1536.                   SZ_SyncWithReceiver := ZERROR;
  1537.                   Exit
  1538.                END;
  1539.          ZRPOS: BEGIN
  1540.                    IF (NOT Z_SeekFile(infile,rxpos)) THEN
  1541.                    BEGIN
  1542.                       Z_Message('File seek error');
  1543.                       SZ_SyncWithReceiver := ZERROR;
  1544.                       Exit
  1545.                    END;
  1546.                    Z_Message('Repositioning...');
  1547.                    Z_ShowLoc(rxpos);
  1548.                    txpos := rxpos;
  1549.                    SZ_SyncWithReceiver := c;
  1550.                    Exit
  1551.                 END;
  1552.          ZSKIP,
  1553.          ZRINIT,
  1554.          ZACK: BEGIN
  1555.                   SZ_SyncWithReceiver := c;
  1556.                   Exit
  1557.                END
  1558.          ELSE
  1559.          BEGIN
  1560.             Z_Message('I dunno what happened!');
  1561.             SZ_SendBinaryHeader(ZNAK,txhdr)
  1562.          END
  1563.       END {case}
  1564.    UNTIL (done)
  1565. END;
  1566.  
  1567.  
  1568. FUNCTION SZ_SendFileData: INTEGER;
  1569. LABEL
  1570.    waitack, somemore, oops;
  1571. VAR
  1572.    c, e: INTEGER;
  1573.    newcnt, blklen, blkred, maxblklen, goodblks, goodneeded: WORD;
  1574. BEGIN
  1575.    Z_Message('Sending file...');
  1576.    goodneeded := 1;
  1577.    IF (zbaud < 300) THEN
  1578.       maxblklen := 128
  1579.    ELSE
  1580.       maxblklen := (WORD(zbaud) DIV 300) * 256;
  1581.    IF (maxblklen > ZBUFSIZE) THEN
  1582.       maxblklen := ZBUFSIZE;
  1583.    IF (rxbuflen > 0) AND (rxbuflen < maxblklen) THEN
  1584.       maxblklen := rxbuflen;
  1585.    blklen := maxblklen;
  1586.    ztime := Z_SetTimer;
  1587. somemore:
  1588.    IF (Z_CharAvail) THEN
  1589.    BEGIN
  1590. WaitAck:
  1591.       c := SZ_SyncWithReceiver;
  1592.       Z_Frame(c);
  1593.       CASE c OF
  1594.          ZSKIP: BEGIN
  1595.                    SZ_SendFileData := ZSKIP;
  1596.                    Exit
  1597.                 END;
  1598.          ZACK: {null};
  1599.          ZRPOS: BEGIN
  1600.                    Inc(zerrors);
  1601.                    Z_Errors(zerrors);
  1602.                    IF ((blklen SHR 2) > 32) THEN
  1603.                       blklen := (blklen SHR 2)
  1604.                    ELSE
  1605.                       blklen := 32;
  1606.                    goodblks := 0;
  1607.                    goodneeded := (goodneeded SHL 1) OR 1
  1608.                 END;
  1609.          ZRINIT: BEGIN
  1610.                     SZ_SendFileData := ZOK;
  1611.                     Exit
  1612.                  END
  1613.          ELSE
  1614.          BEGIN
  1615.             SZ_SendFileData := ZERROR;
  1616.             Exit
  1617.          END
  1618.       END {case};
  1619.       WHILE (Z_CharAvail) DO
  1620.       BEGIN
  1621.          CASE (Z_GetByte(1)) OF
  1622.             CAN,
  1623.             ZPAD: GOTO waitack;
  1624.             RCDO: BEGIN
  1625.                      SZ_SendFileData := ZERROR;
  1626.                      Exit
  1627.                   END
  1628.          END {case}
  1629.       END
  1630.    END; {if char avail}
  1631.    newcnt := rxbuflen;
  1632.    Z_PutLongIntoHeader(txpos);
  1633.    SZ_SendBinaryHeader(ZDATA,txhdr);
  1634.    Z_Message('Sending data header');
  1635.    REPEAT
  1636.       IF (KeyPressed) THEN
  1637.          IF (ReadKey = #27) THEN
  1638.          BEGIN
  1639.             Z_Message('Aborted from keyboard');
  1640.             Z_SendCan;
  1641.             GOTO oops
  1642.          END;
  1643.       IF (NOT Z_Carrier) THEN
  1644.          GOTO oops;
  1645.       IF (NOT Z_ReadFile(infile,txbuf,blklen,blkred)) THEN
  1646.       BEGIN
  1647.          Z_Message('Error reading disk');
  1648.          Z_SendCan;
  1649.          GOTO oops
  1650.       END;
  1651.       IF (blkred < blklen) THEN
  1652.          e := ZCRCE
  1653.       ELSE IF (rxbuflen <> 0) AND ((newcnt - blkred) <= 0) THEN
  1654.       BEGIN
  1655.          newcnt := (newcnt - blkred);
  1656.          e := ZCRCW
  1657.       END
  1658.       ELSE
  1659.          e := ZCRCG;
  1660.       SZ_SendData(txbuf,blkred,e);
  1661.       txpos := txpos + blkred;
  1662.       Z_ShowLoc(txpos);
  1663.       Inc(goodblks);
  1664.       IF (blklen < maxblklen) AND (goodblks > goodneeded) THEN
  1665.       BEGIN
  1666.          IF ((blklen SHL 1) < maxblklen) THEN
  1667.             blklen := (blklen SHL 1)
  1668.          ELSE
  1669.             blklen := maxblklen;
  1670.          goodblks := 0
  1671.       END;
  1672.       IF (e = ZCRCW) THEN
  1673.          GOTO waitack;
  1674.       WHILE (Z_CharAvail) DO
  1675.       BEGIN
  1676.          CASE Z_GetByte(1) OF
  1677.             CAN,
  1678.             ZPAD: BEGIN
  1679.                      Z_Message('Trouble?');
  1680.                      Z_ClearOutbound;
  1681.                      SZ_SendData(txbuf,0,ZCRCE);
  1682.                      GOTO waitack
  1683.                   END;
  1684.             RCDO: BEGIN
  1685.                      SZ_SendFileData := ZERROR;
  1686.                      Exit
  1687.                   END
  1688.          END {case}
  1689.       END {while}
  1690.    UNTIL (e <> ZCRCG);
  1691.    REPEAT
  1692.       Z_PutLongIntoHeader(txpos);
  1693.       Z_Message('Sending EOF');
  1694.       SZ_SendBinaryHeader(ZEOF,txhdr);
  1695.       c := SZ_SyncWithReceiver;
  1696.       CASE c OF
  1697.          ZACK: {null};
  1698.          ZRPOS: GOTO somemore;
  1699.          ZRINIT: BEGIN
  1700.                     SZ_SendFileData := ZOK;
  1701.                     Exit
  1702.                  END;
  1703.          ZSKIP: BEGIN
  1704.                    SZ_SendFileData := c;
  1705.                    Exit
  1706.                 END
  1707.          ELSE
  1708. oops:    BEGIN
  1709.             SZ_SendFileData := ZERROR;
  1710.             Exit
  1711.          END
  1712.       END {case}
  1713.    UNTIL (c <> ZACK)
  1714. END;
  1715.  
  1716. FUNCTION SZ_SendFile: INTEGER;
  1717. VAR
  1718.    c: INTEGER;
  1719.    done: BOOLEAN;
  1720. BEGIN
  1721.    zerrors := WORD(0);
  1722.    done := FALSE;
  1723.    REPEAT
  1724.       IF (KeyPressed) THEN
  1725.          IF (ReadKey = #27) THEN
  1726.          BEGIN
  1727.             Z_SendCan;
  1728.             Z_Message('Aborted from keyboard');
  1729.             SZ_SendFile := ZERROR;
  1730.             Exit
  1731.          END;
  1732.       IF (NOT Z_Carrier) THEN
  1733.       BEGIN
  1734.          Z_Message('Lost carrier');
  1735.          SZ_SendFile := ZERROR;
  1736.          Exit
  1737.       END;
  1738.       FillChar(txhdr,4,0);
  1739.       txhdr[ZF0] := ZCRESUM; {recover}
  1740.       SZ_SendBinaryHeader(ZFILE,txhdr);
  1741.       SZ_SendData(txbuf,ZBUFSIZE,ZCRCW);
  1742.       REPEAT
  1743.          c := Z_GetHeader(rxhdr);
  1744.          Z_Frame(c);
  1745.          CASE c OF
  1746.             ZCAN,
  1747.             RCDO,
  1748.             ZTIMEOUT,
  1749.             ZFIN,
  1750.             ZABORT: BEGIN
  1751.                        SZ_SendFile := ZERROR;
  1752.                        Exit
  1753.                     END;
  1754.             ZRINIT: {null - this will cause a loopback};
  1755.             ZCRC: BEGIN
  1756.                      Z_PutLongIntoHeader(Z_FileCRC32(infile));
  1757.                      Z_SendHexHeader(ZCRC,txhdr)
  1758.                   END;
  1759.             ZSKIP: BEGIN
  1760.                        SZ_SendFile := c;
  1761.                        Exit
  1762.                     END;
  1763.             ZRPOS: BEGIN
  1764.                       IF (NOT Z_SeekFile(infile,rxpos)) THEN
  1765.                       BEGIN
  1766.                          Z_Message('File positioning error');
  1767.                          Z_SendHexHeader(ZFERR,txhdr);
  1768.                          SZ_SendFile := ZERROR;
  1769.                          Exit
  1770.                       END;
  1771.                       Z_Message('Setting start position');
  1772.                       Z_ShowLoc(rxpos);
  1773.                       strtpos := rxpos;
  1774.                       txpos := rxpos;
  1775.                       SZ_SendFile := SZ_SendFileData;
  1776.                       Exit
  1777.                    END
  1778.          END {case}
  1779.       UNTIL (c <> ZRINIT)
  1780.    UNTIL (done)
  1781. END;
  1782.  
  1783. FUNCTION Zmodem_Send(pathname: STRING; lastfile: BOOLEAN; comport: WORD; baudrate: LONGINT): BOOLEAN;
  1784.  
  1785. VAR
  1786.    s: STRING;
  1787.    n: INTEGER;
  1788. BEGIN
  1789.    zerrors := 0;
  1790.    zbaud := baudrate;
  1791.    zport := comport;
  1792.    Z_OpenWindow(TPZVER);
  1793.    IF (NOT Z_AsyncOn(comport,baudrate)) THEN
  1794.    BEGIN
  1795.       Z_Message('Unable to open port');
  1796.       Delay(2000);
  1797.       Z_CloseWindow;
  1798.       Zmodem_Send := FALSE;
  1799.       Exit
  1800.    END;
  1801.    IF (NOT Z_Carrier) THEN
  1802.    BEGIN
  1803.       Z_Message('Lost carrier');
  1804.       Delay(2000);
  1805.       Z_CloseWindow;
  1806.       Z_AsyncOff;
  1807.       Zmodem_Send := FALSE;
  1808.       Exit
  1809.    END;
  1810.    IF (NOT Z_FindFile(pathname,fname,fsize,ftime)) THEN
  1811.    BEGIN
  1812.       Z_Message('Unable to find/open file');
  1813.       SZ_EndSend;
  1814.       Z_CloseWindow;
  1815.       Z_AsyncOff;
  1816.       Zmodem_Send := FALSE;
  1817.       Exit
  1818.    END;
  1819.    Z_ShowName(fname);
  1820.    Z_ShowSize(fsize);
  1821.    Z_ShowTransferTime(fsize,zbaud);
  1822.    Str(fsize,s);
  1823.    s := (fname + #0 + s + ' ');
  1824.    s := s + Z_ToUnixDate(ftime);
  1825.    n := Length(s);
  1826.    FOR n := 1 TO Length(s) DO
  1827.    BEGIN
  1828.       IF (s[n] IN ['A'..'Z']) THEN
  1829.          s[n] := Chr(Ord(s[n]) + $20)
  1830.    END;
  1831.    FillChar(txbuf,ZBUFSIZE,0);
  1832.    Move(s[1],txbuf[0],Length(s));
  1833.    IF (zbaud > 0) THEN
  1834.       rxtimeout := INTEGER(614400 DIV zbaud)
  1835.    ELSE
  1836.       rxtimeout := 100;
  1837.    IF (rxtimeout < 100) THEN
  1838.       rxtimeout := 100;
  1839.    attn[0] := Ord('r');
  1840.    attn[1] := Ord('z');
  1841.    attn[3] := 13;
  1842.    attn[4] := 0;
  1843.    Z_PutString(attn);
  1844.    FillChar(attn,SizeOf(attn),0);
  1845.    Z_PutLongIntoHeader(LONGINT(0));
  1846.    Z_Message('Sending ZRQINIT');
  1847.    Z_SendHexHeader(ZRQINIT,txhdr);
  1848.    IF (SZ_GetReceiverInfo = ZERROR) THEN
  1849.    BEGIN
  1850.       Z_CloseWindow;
  1851.       Z_AsyncOff;
  1852.       Zmodem_Send := FALSE;
  1853.       Exit
  1854.    END;
  1855.    IF (NOT Z_OpenFile(infile,pathname)) THEN
  1856.    IF (IOresult <> 0) THEN
  1857.    BEGIN
  1858.       Z_Message('Failure to open file');
  1859.       Z_SendCan;
  1860.       Z_CloseWindow;
  1861.       Z_AsyncOff;
  1862.       Zmodem_Send := FALSE;
  1863.       Exit
  1864.    END;
  1865.    n := SZ_SendFile;
  1866.    zcps := (fsize DIV (Z_SetTimer - ztime));
  1867.    Z_CloseFile(infile);
  1868.    Z_Frame(n);
  1869.    Str(zcps:4,s);
  1870.    Z_Message(s+' cps');
  1871.    IF (n = ZOK) AND (lastfile) THEN
  1872.       SZ_EndSend
  1873.    ELSE
  1874.       Z_SendCan;
  1875.    Z_CloseWindow;
  1876.    Z_AsyncOff;
  1877.    Zmodem_Send := TRUE
  1878. END;
  1879. END.
  1880.  
  1881.  
  1882.  
  1883.