home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / archives / sinclairqlb.zip / ql2pro.bcp < prev    next >
Text File  |  1988-08-16  |  20KB  |  886 lines

  1. // This is file QL2PRO.BCP
  2. //
  3. // To be renamed FLP2_KERPROTO_BCPL for QDOS
  4. SECTION "Protocol"
  5.  
  6. GET "LIBHDR"
  7. GET "FLP2_KERHDR"
  8.  
  9. /*  These routines embody the Kermit protocol as described in the manual.
  10.  
  11.     The main routines were written by C.G. Selwyn using the  C program in
  12.     the fifth edition of the protocol manual as a guide.
  13.  
  14.     Any alterations by David Harper are made only to enable the routines
  15.     to work under QDOS, and are minimal.
  16. */
  17.  
  18. /*
  19.     s e n d s w
  20.  
  21.    Sendsw is the state table switcher for sending
  22.    files. It loops until either it finishes, or
  23.    an error is encountered. The routines called by
  24.    sendsw are responsible for changing the state.
  25. */
  26.  
  27. LET sendsw() = VALOF
  28. $(
  29.    n := 0
  30.    astate := 'S'
  31.    numtry := 0
  32.    readchar := (ser.interface=interface.qconnect -> qcon.rdch,raw.rdch)
  33.  
  34.    $( SWITCHON astate INTO
  35.       $(
  36.       CASE 'D' : astate := sdata() ; ENDCASE  /* Data-send state */
  37.       CASE 'F' : astate := sfile() ; ENDCASE  /* File-send */
  38.       CASE 'Z' : astate := seof()  ; ENDCASE  /* End-Of-File */
  39.       CASE 'S' : astate := sinit() ; ENDCASE  /* Send Init */
  40.       CASE 'B' : astate := sbreak(); ENDCASE  /* Break-Send */
  41.       CASE 'C' : RESULTIS TRUE                /* Complete */
  42.       DEFAULT  :                              /* Unknown, fail */
  43.       CASE 'A' : erroring := TRUE
  44.                  RESULTIS FALSE               /* Unknown, fail */
  45.       $)
  46.    $) REPEAT
  47. $)
  48.  
  49. /*
  50.     s i n i t
  51.  
  52.    Send initiate: Send my parameters, get other side's back.
  53.  
  54. */
  55.  
  56. AND sinit() = VALOF
  57. $( LET num,len = ?,?
  58.  
  59.    IF numtry > maxtry THEN
  60.    $( numtry := numtry + 1
  61.       RESULTIS 'A'
  62.    $)
  63.    numtry := numtry + 1
  64.  
  65.    len := spar(packet)
  66.    IF remote & (\serving) THEN delay(remote.delay)
  67.    spack('S',n,len,packet)
  68.    SWITCHON rpack(@len,@num,recpkt) INTO
  69.    $( CASE 'N' :
  70.          report(FALSE)
  71.          RESULTIS astate              /* Nak */
  72.       CASE 'Y' :                     /* Ack */
  73.       $( report(n=num)
  74.          IF n \= num RESULTIS astate
  75.          rpar(recpkt,len)
  76.          numtry := 0
  77.          n := (n+1) REM 64
  78.          fd := find.old.file(local.fname)
  79.          IF fd<=0 THEN RESULTIS 'A'
  80.          cons(writef,"Sending file %S*N",local.fname)
  81.          selectinput(fd)
  82.          RESULTIS 'F'
  83.       $)
  84.       CASE FALSE :
  85.          report(FALSE)
  86.          RESULTIS astate
  87.       DEFAULT :
  88.          RESULTIS 'A'
  89.    $)
  90. $)
  91.  
  92. /*
  93.     s f i l e
  94.  
  95.    Send File Header
  96.  
  97. */
  98. AND sfile() = VALOF
  99. $( LET num,len = ?,?
  100.    LET name = VEC 20
  101.    wptr := 4
  102.    IF numtry > maxtry THEN
  103.    $( numtry := numtry + 1
  104.       RESULTIS 'A'
  105.    $)
  106.    numtry := numtry + 1
  107.  
  108.    len := filnam%0
  109.    FOR i = 1 TO len DO name%(i-1) := filnam%i
  110.  
  111.    spack('F',n,len,name)
  112.  
  113.    SWITCHON rpack(@len,@num,recpkt) INTO
  114.    $(
  115.    CASE 'N' :                             /* NAK */
  116.       $( num := num = 0 -> 63,num-1
  117.          IF n \= num THEN
  118.          $( report(FALSE)
  119.             RESULTIS astate
  120.          $)
  121.       $)
  122.    CASE 'Y' :
  123.       $( report(n=num)
  124.          IF n \= num THEN RESULTIS astate
  125.          numtry := 0
  126.          n := (n+1) REM 64
  127.          size := bufill(packet)
  128.          RESULTIS 'D'
  129.       $)
  130.    CASE FALSE :
  131.       report(FALSE)
  132.       RESULTIS astate
  133.    DEFAULT :
  134.       RESULTIS 'A'
  135.    $)
  136. $)
  137.  
  138. /*
  139.     s d a t a
  140.  
  141.    Send File Data
  142.  
  143. */
  144. AND sdata() = VALOF
  145. $( LET num,len = ?,?
  146.  
  147.    IF numtry > maxtry THEN
  148.    $( numtry := numtry + 1
  149.       RESULTIS 'A'
  150.    $)
  151.    numtry := numtry + 1
  152.  
  153.    spack('D',n,size,packet)
  154.  
  155.    SWITCHON rpack(@len,@num,recpkt) INTO
  156.    $(
  157.    CASE 'N' :                             /* NAK */
  158.       $( num := num = 0 -> 63,num-1
  159.          IF n \= num THEN
  160.          $( report(FALSE)
  161.             RESULTIS astate
  162.          $)
  163.       $)
  164.    CASE 'Y' :
  165.       $( report(n=num)
  166.          IF n \= num THEN RESULTIS astate
  167.          numtry := 0
  168.          n := (n+1) REM 64
  169.          size := bufill(packet)
  170.          RESULTIS size = 0 ->'Z','D'
  171.       $)
  172.    CASE FALSE :
  173.       report(FALSE)
  174.       RESULTIS astate
  175.    DEFAULT :
  176.       RESULTIS 'A'
  177.    $)
  178. $)
  179.  
  180. /*
  181.     s e o f
  182.  
  183.    Send End-Of-File
  184.  
  185. */
  186. AND seof() = VALOF
  187. $( LET num,len = ?,?
  188.    AND closed.file = 0
  189.  
  190.    IF numtry > maxtry THEN
  191.    $( numtry := numtry + 1
  192.       RESULTIS 'A'
  193.    $)
  194.    numtry := numtry + 1
  195.  
  196.    spack('Z',n,0,packet)
  197.  
  198.    SWITCHON rpack(@len,@num,recpkt) INTO
  199.    $(
  200.    CASE 'N' :                             /* NAK */
  201.       $( num := num = 0 -> 63,num-1
  202.          IF n \= num THEN
  203.          $( report(FALSE)
  204.             RESULTIS astate
  205.          $)
  206.       $)
  207.    CASE 'Y' :
  208.       $( report(n=num)
  209.          IF n \= num THEN RESULTIS astate
  210.          numtry := 0
  211.          n := (n+1) REM 64
  212.          closed.file := close(fd)
  213.          UNLESS closed.file=0 DO
  214.          $(CF  selectoutput(console)
  215.                writef("Return code %N from close*N",closed.file)
  216.                catastrophe("Failed to close file in SEOF")
  217.          $)CF
  218.          fd := 0
  219.          RESULTIS 'B'
  220.       $)
  221.    CASE FALSE :
  222.       report(FALSE)
  223.       RESULTIS astate
  224.    DEFAULT :
  225.       RESULTIS 'A'
  226.    $)
  227. $)
  228.  
  229. /*
  230.     s b r e a k
  231.  
  232.    Send Break (EOT)
  233.  
  234. */
  235. AND sbreak() = VALOF
  236. $( LET num,len = ?,?
  237.  
  238.    IF numtry > maxtry THEN
  239.    $( numtry := numtry + 1
  240.       RESULTIS 'A'
  241.    $)
  242.    numtry := numtry + 1
  243.  
  244.    spack('B',n,0,packet)
  245.  
  246.    SWITCHON rpack(@len,@num,recpkt) INTO
  247.    $(
  248.    CASE 'N' :                             /* NAK */
  249.       $( num := num = 0 -> 63,num-1
  250.          IF n \= num THEN
  251.          $( report(FALSE)
  252.             RESULTIS astate
  253.          $)
  254.       $)
  255.    CASE 'Y' :
  256.       $( report(n=num)
  257.          IF n \= num THEN RESULTIS astate
  258.          numtry := 0
  259.          n := (n+1) REM 64
  260.          RESULTIS 'C'
  261.       $)
  262.    CASE FALSE :
  263.       report(FALSE)
  264.       RESULTIS astate
  265.    DEFAULT :
  266.       RESULTIS 'A'
  267.    $)
  268. $)
  269.  
  270. /*
  271.     r e c s w
  272.  
  273.    This is the state table switcher for receiving files.
  274.  
  275. */
  276.  
  277. AND recsw() = VALOF
  278. $( TEST serving THEN
  279.    $( astate := 'F'
  280.       n := 1
  281.    $)
  282.    ELSE
  283.    $( n := 0
  284.       astate := 'R'
  285.    $)
  286.    numtry := 0
  287.    readchar := (ser.interface=interface.qconnect -> qcon.rdch,raw.rdch)
  288.  
  289.    $( SWITCHON astate INTO
  290.       $(
  291.       CASE 'D' : astate := rdata() ; ENDCASE     // Data receive state
  292.       CASE 'F' : astate := rfile() ; ENDCASE     // File receive state
  293.       CASE 'R' : astate := rinit() ; ENDCASE     // Send initiate state
  294.       CASE 'C' : RESULTIS TRUE                   // Complete state
  295.       CASE 'A' : erroring := TRUE
  296.                  RESULTIS FALSE                  // Abort state
  297.       $)
  298.    $) REPEAT
  299. $)
  300.  
  301. /*
  302.     r i n i t
  303.  
  304.    Receive Initialisation
  305.  
  306. */
  307. AND rinit() = VALOF
  308. $( LET len,num = ?,?
  309.  
  310.    IF numtry > maxtry THEN
  311.    $( numtry := numtry + 1
  312.       RESULTIS 'A'
  313.    $)
  314.    numtry := numtry + 1
  315.  
  316.    SWITCHON rpack(@len,@num,packet) INTO
  317.    $(
  318.    CASE 'S' :
  319.       $( rpar(packet,len)
  320.          len := spar(packet)
  321.          report(TRUE)
  322.          spack('Y',n,len,packet)
  323.          oldtry := numtry
  324.          numtry := 0
  325.          n := (n+1) REM 64
  326.          RESULTIS 'F'
  327.       $)
  328.    CASE FALSE :
  329.       report(FALSE)
  330.       RESULTIS astate
  331.    DEFAULT : RESULTIS 'A'
  332.    $)
  333. $)
  334.  
  335. /*
  336.     r f i l e
  337.  
  338.    Receive File Header
  339.  
  340. */
  341.  
  342. AND rfile() = VALOF
  343. $( LET len,num = ?,?
  344.    wptr := 0
  345.    IF numtry > maxtry THEN
  346.    $( numtry := numtry + 1
  347.       RESULTIS 'A'
  348.    $)
  349.    numtry := numtry + 1
  350.  
  351.    SWITCHON rpack(@len,@num,packet) INTO
  352.    $(
  353.    CASE 'S' :
  354.       $( IF oldtry > maxtry THEN
  355.          $( oldtry := oldtry + 1
  356.             RESULTIS 'A'
  357.          $)
  358.          oldtry := oldtry + 1
  359.  
  360.          TEST (num = (n=0 -> 63,n-1)) THEN
  361.          $( len := spar(packet)
  362.             report(FALSE)
  363.             spack('Y',num,len,packet)
  364.             numtry := 0
  365.             RESULTIS astate
  366.          $)
  367.          ELSE RESULTIS 'A'
  368.       $)
  369.    CASE 'Z' :
  370.       $( IF oldtry > maxtry THEN
  371.          $( oldtry := oldtry + 1
  372.             RESULTIS 'A'
  373.          $)
  374.          oldtry := oldtry + 1
  375.  
  376.          TEST (num = (n=0 -> 63,n-1)) THEN
  377.          $( spack('Y',num,0,0)
  378.             report(FALSE)
  379.             numtry := 0
  380.             RESULTIS astate
  381.          $)
  382.          ELSE RESULTIS 'A'
  383.       $)
  384.    CASE 'F' :                          /* File Header */
  385.       $( IF (num \= n) RESULTIS 'A'
  386.          IF serving THEN
  387.          $(S // get QDOS file name from other Kermit's F packet
  388.            FOR k=0 TO len-1 DO local.fname%(k+1) := packet%k
  389.            local.fname%0 := len
  390.          $)S
  391.          fd := getfil()
  392.          IF fd<=0 THEN RESULTIS 'A'
  393.          spack('Y',num,0,0)
  394.          report(TRUE)
  395.          oldtry := numtry
  396.          numtry := 0
  397.          n := (n+1) REM 64
  398.          RESULTIS 'D'
  399.       $)
  400.    CASE 'B' :                          /* Break transmission */
  401.       $( IF num \= n THEN RESULTIS 'A'
  402.          spack('Y',n,0,0)
  403.          RESULTIS 'C'
  404.       $)
  405.    CASE FALSE :
  406.       report(FALSE)
  407.       RESULTIS astate
  408.    DEFAULT : RESULTIS 'A'
  409.    $)
  410. $)
  411.  
  412. /*
  413.     r d a t a
  414.  
  415.    Receive data
  416.  
  417. */
  418. AND rdata() = VALOF
  419. $( LET num,len = ?,?
  420.    AND closed.file = 0
  421.    IF numtry > maxtry THEN
  422.    $( numtry := numtry + 1
  423.       RESULTIS 'A'
  424.    $)
  425.    numtry := numtry + 1
  426.  
  427.    SWITCHON rpack(@len,@num,packet) INTO
  428.    $(
  429.    CASE 'D' :
  430.       $( TEST num \= n THEN
  431.          $( IF oldtry > maxtry THEN
  432.             $( oldtry := oldtry + 1
  433.                RESULTIS 'A'
  434.             $)
  435.             oldtry := oldtry + 1
  436.  
  437.             IF num = (n=0 -> 63,n-1) THEN
  438.             $( spack('Y',num,6,packet)
  439.                report(FALSE)
  440.                numtry := 0
  441.                RESULTIS astate
  442.             $)
  443.             RESULTIS 'A'
  444.          $)
  445.          ELSE
  446.          $( bufemp(packet,len)
  447.             spack('Y',n,0,0)
  448.             report(TRUE)
  449.             oldtry := numtry
  450.             numtry := 0
  451.             n := (n+1) REM 64
  452.             RESULTIS 'D'
  453.          $)
  454.       $)
  455.    CASE 'F' :                    // Got a file header
  456.       $( IF oldtry > maxtry THEN
  457.          $( oldtry := oldtry + 1
  458.             RESULTIS 'A'
  459.          $)
  460.          oldtry := oldtry + 1
  461.  
  462.          IF num = (n=0 -> 63,n-1) THEN
  463.          $( spack('Y',num,0,0)
  464.             report(FALSE)
  465.             numtry := 0
  466.             RESULTIS astate
  467.          $)
  468.          RESULTIS 'A'
  469.       $)
  470.    CASE 'Z' :
  471.       $( IF num \= n THEN RESULTIS 'A'
  472.          spack('Y',n,0,0)
  473.          report(TRUE)
  474.          IF image & (wptr \= 0) THEN writewords(@word,1)
  475.          closed.file := close(fd)
  476.          UNLESS closed.file=0 DO
  477.          $(CF  selectoutput(console)
  478.                writef("Return code %N from close*N",closed.file)
  479.                catastrophe("Could not close the file in RDATA")
  480.          $)CF
  481.          fd := 0
  482.          n := (n+1) REM 64
  483.          RESULTIS 'F'
  484.       $)
  485.    CASE FALSE :
  486.       report(FALSE)
  487.       RESULTIS astate
  488.    DEFAULT    : RESULTIS 'A'
  489.    $)
  490. $)
  491.  
  492. /*
  493.       KERMIT utilities
  494. */
  495.  
  496. /* tochar converts a control character to a printable one by adding a space */
  497.  
  498. AND tochar(ch) = ch + '*S'
  499.  
  500. /* unchar undoes tochar */
  501.  
  502. AND unchar(ch) = ch - '*S'
  503.  
  504. /*
  505.    ctl turns a control character into a printable character by toggling the
  506.    control bit (ie. ~A -> A and A -> ~A
  507. */
  508.  
  509. AND ctl(ch) = ch NEQV 64
  510.  
  511. /*
  512.     s p a c k
  513.  
  514.    Send a packet
  515. */
  516. AND spack(type,num,len,data) BE
  517. $( LET i = ?
  518.    LET chksum = ?
  519.    LET buffer = VEC 100/bytesperword
  520.  
  521.    selectoutput(remfd)
  522.    IF s.pad>0 THEN
  523.    $(1
  524.      FOR i = 0 TO s.pad-1 DO buffer%i := s.padchar
  525.      sendchars(buffer,s.pad)
  526.    $)1
  527.  
  528.    buffer%0 := s.sop
  529.    chksum := tochar(len+3)
  530.    buffer%1 := tochar(len+3)
  531.    chksum := chksum+tochar(num)
  532.    buffer%2 := tochar(num)
  533.    chksum := chksum+type
  534.    buffer%3 := type
  535.  
  536.    FOR i = 4 TO 4+len-1 DO
  537.    $( LET d = data%(i-4)
  538.       buffer%i := d
  539.       chksum := chksum+d
  540.    $)
  541.  
  542.    chksum := (chksum + ((chksum & #XC0) >> 6)) & #X3F
  543.    buffer%(4+len) := tochar(chksum)
  544.    buffer%(5+len) := s.eol
  545.    sendchars(buffer,6+len)
  546.    IF debug THEN
  547.    $(D
  548.      debug.report(writef,
  549.        "*N*NSent packet number %N, type %C*NData field : ",num,type)
  550.      debug.report(writebytes,data,len)
  551.      debug.report(writes,"*N*N")
  552.    $)D
  553. $)
  554.  
  555. /*
  556.     r p a c k
  557.  
  558.    Receive a packet
  559.  
  560. */
  561. AND rpack(len,num,data) = VALOF
  562. $( LET i,done = ?,?
  563.    LET chksum,t,type = ?,\SOH,?
  564.  
  565.    selectinput(remfd)
  566.    IF (r.timeout < mintim) THEN r.timeout := mytime
  567.    endtime := time() + r.timeout
  568.  
  569.    WHILE t \= r.sop DO $(1 t := readchar()
  570.                            IF t=rpack.timeout THEN
  571.                            $(D1 debug.report(writes,
  572.                                   "*NTimed out waiting for SOH*N")
  573.                                 RESULTIS FALSE
  574.                            $)D1
  575.                        $)1
  576.  
  577.    done := FALSE
  578.    WHILE (\done)  DO
  579.    $( t := readchar()
  580.       IF t=rpack.timeout THEN
  581.       $(D2 debug.report(writes,"*NTimed out waiting for length byte*N")
  582.            RESULTIS FALSE
  583.       $)D2
  584.       IF \image THEN t := t & #X7F
  585.       IF t = r.sop LOOP
  586.  
  587.       chksum := t
  588.       !len := unchar(t)-3
  589.  
  590.       t := readchar()
  591.       IF t=rpack.timeout THEN
  592.       $(D3 debug.report(writes,"*NTimed out waiting for packet count byte*N")
  593.            RESULTIS FALSE
  594.       $)D3
  595.       IF \image THEN t := t & #X7F
  596.       IF t = r.sop LOOP
  597.       chksum := chksum+t
  598.       !num := unchar(t)
  599.  
  600.       t := readchar()
  601.       IF t=rpack.timeout THEN
  602.       $(D4 debug.report(writes,"*NTimed out waiting for packet type byte*N")
  603.            RESULTIS FALSE
  604.       $)D4
  605.       IF \image THEN t := t & #X7F
  606.       IF t = r.sop LOOP
  607.       chksum := chksum+t
  608.       type := t
  609.  
  610.       FOR i = 0 TO (!len)-1 DO
  611.       $( t := readchar()
  612.          IF t=rpack.timeout THEN
  613.          $(D5 debug.report(writef,
  614.               "*NTimed out after receiving %N data bytes*N",i+1)
  615.               RESULTIS FALSE
  616.          $)D5
  617.          IF \image THEN t := t & #X7F
  618.          IF t = r.sop LOOP
  619.          chksum := chksum+t
  620.          data%i := t
  621.       $)
  622.       data%(!len) := 0
  623.  
  624.       t := readchar()
  625.       IF t=rpack.timeout THEN
  626.       $(D6 debug.report(writes,"*NTimed out waiting for checksum byte*N")
  627.            RESULTIS FALSE
  628.       $)D6
  629.       IF \image THEN t := t & #X7F
  630.       IF t = r.sop LOOP
  631.       done := TRUE
  632.  
  633.    $)
  634.    IF debug THEN
  635.    $(D
  636.      debug.report(writef,
  637.      "*N*NReceived packet number %N, type %C*NData field : ",!num,type)
  638.      debug.report(writebytes,data,!len)
  639.      debug.report(writes,"*N*N")
  640.    $)D
  641.    chksum := (chksum + ((chksum & #XC0)>>6)) & #X3F
  642.    IF chksum \= unchar(t) THEN
  643.    $(F
  644.      debug.report(writes,"*NChecksum incorrect. Packet rejected*N")
  645.      RESULTIS FALSE
  646.    $)F
  647.    RESULTIS type
  648. $)
  649.  
  650. /*
  651.    p u t b u f f
  652.  
  653.    Put a character in the buffer
  654.  
  655.    Control and 8-bit quoting are performed if required/elected
  656. */
  657.  
  658. AND putbuff(buffer,i,ch) = VALOF
  659. $( LET j = 0
  660.    LET ch7 = ch & #X7F
  661.  
  662.    IF quote8ing THEN              // Do 8-bit quote
  663.    $( IF (ch & #X80) \= 0 THEN
  664.       $( buffer%(i+j) := quote8
  665.          j := j+1
  666.       $)
  667.       ch := ch7
  668.    $)
  669.  
  670.    IF (ch7 < sp) | (ch7 = del) |             // Quote control characters
  671.       (ch7 = s.quote) |                      // And the funnies
  672.       ((ch7 = quote8) & quote8ing) THEN
  673.    $( IF \image & (ch7 = '*N') THEN
  674.       $( buffer%(i+j) := s.quote
  675.          buffer%(i+j+1) := ctl(cr)
  676.          j := j+2
  677.       $)
  678.       buffer%(i+j) := s.quote
  679.       j := j+1
  680.       IF (ch7 < sp) | (ch7 = del) THEN ch := ctl(ch)
  681.    $)
  682.  
  683.    buffer%(i+j) := ch
  684.    j := j+1
  685.    RESULTIS j
  686. $)
  687.  
  688. /*
  689.     b u f i l l
  690.  
  691.    Get a bufferful of data from the file that's being sent.
  692.  
  693. */
  694.  
  695. AND image.rdch() = VALOF
  696. $( LET r = ?
  697.    IF wptr = 4 THEN
  698.    $( r := readwords(@word,1)
  699.       IF r = 0 THEN RESULTIS endstreamch
  700.       wptr := 0
  701.    $)
  702.    r := (@word)%wptr
  703.    wptr := wptr+1
  704.    RESULTIS r
  705. $)
  706.  
  707. AND image.unrdch() BE wptr := wptr-1
  708.  
  709. AND bufill(buffer) = VALOF
  710. $( LET i,j = ?,?
  711.    LET rch = image -> image.rdch,rdch
  712.    LET unrch = image -> image.unrdch,unrdch
  713.    LET t = 0
  714.  
  715.    selectinput(fd)
  716.    t := rch()
  717.    i := 0
  718.  
  719.    WHILE t \= endstreamch DO
  720.    $( bytes := bytes+1
  721.       j := putbuff(buffer,i,t)
  722.       IF i+j > s.packet.length-8 THEN $( unrch() ; RESULTIS i $)
  723.       i := i+j
  724.       t := rch()
  725.    $)
  726.    RESULTIS i
  727. $)
  728.  
  729. /*
  730.     b u f e m p
  731.  
  732.    Get data from an incoming packet into a file
  733.  
  734. */
  735. AND image.wrch(ch) BE
  736. $( (@word)%wptr := ch
  737.    wptr := (wptr + 1) REM 4
  738.    IF wptr = 0 THEN
  739.       writewords(@word,1)
  740. $)
  741.  
  742. AND bufemp(buffer,len) BE
  743. $( LET t = ?
  744.    LET wch = image-> image.wrch,wrch
  745.    selectoutput(fd)
  746.    FOR i = 0 TO len-1 DO
  747.    $( LET m = 0
  748.       t := buffer%i
  749.       IF (t = quote8) & quote8ing THEN
  750.       $( m := #X80
  751.          i := i+1
  752.          t := buffer%i
  753.       $)
  754.       IF t = r.quote THEN
  755.       $( LET t7 = ?
  756.          i := i+1
  757.          t := buffer%i
  758.          t7 := t & #X7F
  759.          IF (t7 \= r.quote) &
  760.             (t7 \= quote8) THEN
  761.             t := ctl(t)
  762.       $)
  763.       IF image | (t \= '*C') THEN $( bytes := bytes+1 ; wch(t|m) $)
  764.    $)
  765. $)
  766.  
  767. /*
  768.     g e t f i l
  769.  
  770.    Open a new file
  771.  
  772. */
  773.  
  774. AND alphanumeric(ch) = ('A' <= capitalch(ch) <= 'Z') | ('0' <= ch <= '9')
  775.  
  776. AND getfil() = find.new.file(local.fname)
  777.  
  778. AND cons(f,a1,a2,a3,a4,a5) BE IF \remote THEN
  779. $( LET co = COS
  780.    selectoutput(console)
  781.    f(a1,a2,a3,a4,a5)
  782.    selectoutput(co)
  783. $)
  784.  
  785. AND report(f) BE IF reporting THEN
  786. $( TEST f THEN
  787.    $( pakcnt := (pakcnt+1) REM 5
  788.       IF pakcnt = 0 THEN
  789.          cons(writes,".")
  790.    $)
  791.    ELSE
  792.       cons(writes,"%")
  793. $)
  794.  
  795. /*
  796.     s p a r
  797.  
  798.    Fill the data area with the send-init parameters
  799.  
  800. */
  801. AND spar(data) = VALOF
  802. $( data%0 := tochar(r.packet.length)
  803.    data%1 := tochar(s.timeout)
  804.    data%2 := tochar(r.pad)
  805.    data%3 := ctl(r.padchar)
  806.    data%4 := tochar(r.eol)
  807.    data%5 := s.quote
  808.    data%6 := command = w.s -> 'Y', quote8ing -> quote8,'*S'
  809.    RESULTIS 7
  810. $)
  811.  
  812. /*
  813.     r p a r
  814.  
  815.    Get the remote's send-init parameters
  816.  
  817. */
  818.  
  819. AND rpar(data,len) BE
  820. $( LET v = ?
  821.    s.packet.length := maxpack
  822.    s.eol := myeol
  823.    s.quote := myquote
  824.    s.pad := mypad
  825.    s.padchar := mypchar
  826.    quote8ing := FALSE
  827.  
  828.    SWITCHON len INTO
  829.    $(
  830.    DEFAULT :
  831.    CASE 8:
  832.    CASE 7 :                      // 8-bit
  833.       SWITCHON data%6 INTO
  834.       $(
  835.       CASE 'N' : quote8ing := FALSE
  836.                  ENDCASE
  837.       DEFAULT  : quote8 := data%6
  838.       CASE 'Y' : quote8ing := TRUE
  839.                  ENDCASE
  840.       $)
  841.    CASE 6 :                      // quote character
  842.       UNLESS data%5 = '*S' THEN
  843.          r.quote := data%5
  844.    CASE 5 :                      // eol character
  845.       UNLESS data%4 = '*S' THEN
  846.          s.eol := unchar(data%4)
  847.    CASE 4 :                      // pad character
  848.       UNLESS data%3 = '*S' THEN
  849.         s.padchar := ctl(data%3)
  850.    CASE 3 :                      // no. of pad characters
  851.       UNLESS data%2 = '*S' THEN
  852.          s.pad := unchar(data%2)
  853.    CASE 2 :                      // timeout
  854.       UNLESS data%1 = '*S' THEN
  855.          r.timeout := unchar(data%1)
  856.    CASE 1 :                      // packet length
  857.       UNLESS data%0 = '*S' THEN
  858.          s.packet.length := unchar(data%0)
  859.    CASE 0 :
  860.    $)
  861. $)
  862. //
  863. AND delay(interval) BE $(0
  864.   LET time.to.end = time()
  865.   AND time.now = 0
  866.   time.to.end := time.to.end + interval
  867.   UNTIL time.now>=time.to.end DO $( time.now := time() $) REPEAT
  868. $)0
  869. //
  870. AND writewords(aword,k) BE $(0
  871.   selectoutput(fd)
  872.   FOR i=0 TO 3 DO wrch(aword%i)
  873. $)0
  874. //
  875. AND readwords(aword,k) = VALOF $(0
  876.   LET i,ch = 0,0
  877.   selectinput(fd)
  878.   $(1
  879.     ch := rdch()
  880.     IF ch=ENDSTREAMCH THEN BREAK
  881.     aword%i := ch
  882.     i := i + 1
  883.   $)1 REPEATUNTIL i=4
  884.   RESULTIS i
  885. $)0
  886.