%CONST %STRING (40) version="APM Kermit : Version 3.0.04"
![page]
!##############################################################################
!##############################################################################
!######             K E R M I T    F O R    E M A S  3                   ######
!##############################################################################
!##############################################################################

!**
!** Written by Adam Albert-Recht
!** Uh-oh.   Ported to APM by JHB.    so REALLY Uh-oh.      3/6/87
!** stream selections use OUTSTREAM more.
!** Porting philosophy - make absolute minimum changes to main code but
!** emulate EMAS-3 SVCs as far as required.
!**
!** for Edinburgh Regional Computing Centre (University of Edinburgh) July 1985
!**
!** Modified by C J Adie   June 1986
!**

{apm}%include "inc:util.imp"
{apm}%include "inc:fs.imp"
{apm}%include "inc:fsutil.imp"
{apm}@16_3724 %integer exemptmask                    ;!S, Chario.imp, S

!apm!%EXTERNAL %ROUTINE kermit(%STRING (255) s)
{apm}%begin
{apm}%string (255) s


![page]
!******************************************************************************
!******************************************************************************
!******             E X T E R N A L    S P E C S                         ******
!******************************************************************************
!******************************************************************************

!apm!%EXTERNAL %ROUTINE %SPEC prompt %ALIAS "S#PROMPT"(%STRING (255) s)
!apm!%EXTERNAL %ROUTINE %SPEC terminate %ALIAS "S#TERMINATE"
{apm}%routine terminate
{apm}%end

!apm!%EXTERNAL %ROUTINE %SPEC uinfs %ALIAS "EMAS3UINFS"(%INTEGER %NAME entry, %STRING %NAME value)
{apm}%routine uinfs(%integer entry, %string (255) %name value)
{apm}%if entry=17 %start
{apm}   value = current directory
{apm}%else
{apm}   value = "UINFS ".itos(entry, -1)
{apm}%finish
{apm}%end

!apm!%EXTERNAL %ROUTINE %SPEC uinfi %ALIAS "EMAS3UINFI"(%INTEGER %NAME entry, value)
{apm}%routine uinfi(%integer entry, %integer value)
{apm}%if entry=6 %start
{apm}   !max file-size allowed/Kb  (set infinite for now)
{apm}   value = 10000
{apm}%elseif entry=35
{apm}   !Something to do with control characters
{apm}   value = 0
{apm}%else
{apm}   value = -entry
{apm}%finish
{apm}%end

!apm!%EXTERNAL %ROUTINE %SPEC journal on %ALIAS "EMAS3JOURNALON"
{apm}%routine journal on
{apm}%end
!apm!%EXTERNAL %ROUTINE %SPEC journal off %ALIAS "EMAS3JOURNALOFF"
{apm}%routine journal off
{apm}%end
!apm!%EXTERNAL %ROUTINE %SPEC claim channel %ALIAS "EMAS3CLAIMCHANNEL"(%INTEGER %NAME channel)
{apm}%owninteger next channel = 1
{apm}%routine claim channel(%integername channel)
{apm}   channel = next channel; next channel=next channel+1
{apm}%end
!apm!%EXTERNAL %ROUTINE %SPEC existtype %ALIAS "EMAS3EXISTTYPE"(%STRING %NAME file, %INTEGER %NAME flag)
{apm}%routine existtype(%string (255) %name file, %integername type)
{apm}  type=3 ;!all APM files are data for now
{apm}%end
!apm!%EXTERNAL %STRING %FUNCTION %SPEC i to s %ALIAS "S#ITOS"(%INTEGER i)
!apm!%EXTERNAL %STRING (255) %FUNCTION %SPEC x3parms %ALIAS "S#MODESTR"
{apm}%string (255) %fn x3parms
{apm}%result=""
{apm}%end
!apm!%EXTERNAL %STRING %FUNCTION %SPEC interrupt %ALIAS "S#INTERRUPT"
{apm}%string (255) %fn interrupt
{apm} %result=""
{apm}%end
!apm!%EXTERNAL %STRING %FUNCTION %SPEC uc string %ALIAS "S#UCSTRING"(%STRING (255) str)
{apm}%string (255) %fn uc string(%string (255) %name s)
{apm}   to upper(s)
{apm}%result=s
{apm}%end
!apm!%EXTERNAL %INTEGER %FUNCTION %SPEC outstream
!apm!%EXTERNAL %INTEGER %FUNCTION %SPEC pstoi %ALIAS "S#PSTOI"(%STRING (255) s)
{apm}%integerfn pstoi(%string (255) s)
{apm}%result = stoi(s)
{apm}%end

![page]
!******************************************************************************
!******************************************************************************
!******             C O N S T A N T S   &   V A R I A B L E S            ******
!******************************************************************************
!******************************************************************************

   %CONST %BYTE lf = 10,
                cr = 13,
                yes = 1,
                no = 0

!apm!%CONST %BYTE debug = yes
{apm}%owninteger  debug = yes
!apm!%CONST %BYTE emas3 = yes
{apm}%owninteger  emas3 = yes

!apm!%EXTERNAL %INTEGER %FUNCTION %SPEC exist %ALIAS "S#EXIST"(%STRING (255) file)
{apm}%integerfn exist(%string (255) file)
{apm} %if exists(file) %then %result=yes
{apm} %result=no
{apm}%end

!apm!%EXTERNAL %INTEGER %FUNCTION %SPEC emas3checkname (%STRING %NAME name, %INTEGER %NAME type, qualifier, flag)
{apm}%integerfn emas3checkname(%string (255) %name name, %integer type, qual, %c
{apm}%integername flag)
{apm}flag=0
{apm}%result=yes ;!Always acceptable for now
{apm}%end
!apm!%IF emas3=yes %START
   %CONST %BYTE x29call = yes  { always a PAD as far as EMAS3 is concerned }
!apm!%CONST %STRING (40) helpfile = "MICROS:EMASSUPP_KERMITHELP"
{apm}%conststring (40) helpfile = "view:kermit.hlp"
!apm!%FINISH %ELSE %START
!apm!   %CONST %STRING (40) helpfile = "MICROS.KERMIT_HELP"
!apm!%FINISH

   %CONST %INTEGER console channel = 0,
                   comms port out = 0,        { channel to send packets to }
                   comms port in = 0          { channel to read packets from }

   %CONST %INTEGER rec server idle = 0,       { states for the state switcher }
                   rec init = 1,
                   rec file = 2,
                   rec data = 3,
                   send init = 4,
                   open file = 5,
                   send file = 6,
                   send data = 7,
                   send eof = 8,
                   send break = 9,
                   complete = 10,
                   abort = 11,
                   exit = 12,
                   finish = 13,
                   logout = 14

   %INTEGER command channel,                  { channel to read commands from }
            file channel 1,                   { channel to transmit from }
            file channel 2,                   { channel to receive to }
            takefile channel,                 { channel for the take file }
            null channel,                     { channel for dumping output }
            file list channel,                { wildcard file list file }
            debug channel,                    { debugging output }
            final state

!apm!%INTEGER stop, controlcharints
{apm}%INTEGER stop; %owninteger controlcharints=0

   %STRING (255) command, p1, p2, p3,  {command & parms} s1, s2 {workspace}
   %STRING (255) padparameters

   %STRING (20) command prompt, file type, int, file prot

   %BYTE receive max packet, receive retries, receive quote, receive SOH,
      send max packet, send padding, send padchar, send retries,
      send end of line, send quote, send SOH, send packet length, binary quote,
      repeat quote, packet no, retries, eight bit, repeating, taking,
      pending cr, delay, EOF, file list file

   %BYTE %ARRAY send packet(1:100), receive packet(1:100)

![page]
!******************************************************************************
!******************************************************************************
!******        G E N E R A L   U T I L I T Y   R O U T I N E S           ******
!******************************************************************************
!******************************************************************************

!===================EMAS3 VERSIONS OF SOME EMAS2900 ROUTINES====================

{apm}{apm}{apm}{apm}{apm}{apm}{apm}{apm}{apm}{apm}{apm}

%routine debug print(%string (255) s)
   %integer old channel
   %if debug=yes %start
      old channel = outstream
      selectoutput(debug channel)
      printstring(s); newline
      selectoutput(old channel)
   %finish
%end

%routine strip(%string (*) %name s)
   %integer i,l
   !Strip all spaces from supplied string.
   !JHB doesn't trust string resolutions
   %return %if s=""
   l=0
   %for i=1,1,length(s) %cycle
      l=l+1 %and charno(s,l) = charno(s,i) %if charno(s,i)#' '
   %repeat
   length(s)=l
%end
  
%owninteger tmode=no page
%externalstring(255)%fnspec FINFO(%string(255)directory,%integer number)
%routine emas3call(%string (255) command, params, %integername flag)
   %string (255) p1, p2

   %routine list files
      %integer f
      %string (255) s
      %on 3,9 %start; %return; %finish
      f=0
      %cycle
         s=finfo("", f)
         %exit %if s=""
         printline(s)
         f=f+1
      %repeat
   %end

   strip(command); strip(params); to upper(command); to upper(params)
   debug print("EMAS3CALL ".command." ".params)
   %if command = "RENAME" %start
      flag=-1 %unless params -> p1.(",").p2
      rename(p1, p2)
   %elseif command="COPY"
      flag=-1 %unless params -> p1.(",").p2
      copy(p1,p2)
   %elseif command="STOP"
      %stop
   %elseif command="DESTROY"
      delete(params)
   %elseif command = "FILES"
      list files
   %elseif command = "SETMODE"
      %if params = "NOECHO" %or params = "ECHO=OFF" %start
         tmode = tmode ! noecho
         set terminal mode(tmode)
      %elseif params = "ECHO=ON"
         tmode = tmode & (\noecho)
         set terminal mode(tmode)
      %else
         debug print("SET MODE ".params." not recognised")
      %finish
   %else
      printline("APM ".command." ".params." not implemented")
   %finish
%end
{apm}{apm}{apm}{apm}{apm}{apm}{apm}{apm}{apm}{apm}{apm}{apm}

   %ROUTINE setmode(%STRING (255) s)
!apm! %EXTERNAL %ROUTINE %SPEC emas3(%STRING %NAME command, params, %INTEGER %NAME flag)
      %INTEGER flag
{}    emas3call("SETMODE", s, flag)
   %END  { of setmode }

!-------------------------------------------------------------------------------

   %ROUTINE call(%STRING (255) command, param)
!apm! %EXTERNAL %ROUTINE %SPEC emas3(%STRING %NAME command, params, %INTEGER %NAME flag)
      %INTEGER flag
{}    emas3call(command, param, flag)
   %END  { of call }

!-------------------------------------------------------------------------------

   %ROUTINE call silently(%STRING (255) command, param)
!apm! %EXTERNAL %ROUTINE %SPEC emas3(%STRING %NAME command, params, %INTEGER %NAME flag)
      %INTEGER flag, old channel
      old channel = outstream
      select output(null channel)
{}    emas3call(command, param, flag)
      select output(old channel)
   %END  { of call silently }

!-------------------------------------------------------------------------------

   %ROUTINE view(%STRING (255) s)
!apm! %EXTERNAL %ROUTINE %SPEC emas3(%STRING %NAME command, params, %INTEGER %NAME flag)
      %INTEGER flag
{}    emas3call("VIEW", s, flag)
   %END  { of view }

!-------------------------------------------------------------------------------

   %ROUTINE define(%STRING (255) s)
!apm! %EXTERNAL %ROUTINE %SPEC emas3(%STRING %NAME command, params, %INTEGER %NAME flag)
      %INTEGER flag
{}    emas3call("DEFINE", s, flag)
   %END  { of define }

!-------------------------------------------------------------------------------

   %ROUTINE ddelay(%INTEGER secs)
!apm! %EXTERNAL %INTEGER %FUNCTION %SPEC emas3 ddelay %ALIAS "DDELAY"(%INTEGER %NAME seconds)
      %INTEGER flag
!apm! flag = emas3 ddelay(secs)
{apm} flag = cputime + secs * 1000
{apm} %while cputime <= flag %cycle; %repeat
   %END  { of ddelay }

!-------------------------------------------------------------------------------

!apm!%ROUTINE open input(%INTEGER chan, %STRING (255) filename)
!apm!   close stream(chan)
!apm!   define(itos(chan).",".filename)
!apm!%END  { of open input }

!-------------------------------------------------------------------------------

!apm!%ROUTINE open output(%INTEGER chan, %STRING (255) filename)
!apm!   %INTEGER maxfilesize
!apm!   uinfi(6, maxfilesize)
!apm!   maxfilesize = maxfilesize-1
!apm!   %IF maxfilesize>5119 %THEN maxfilesize = 5119  { 5Mbyte limit }
!apm!   close stream(chan)
!apm!   define(itos(chan).",".filename.",".itos(maxfilesize))
!apm!%END  { of open output }


!===================CHARACTER INPUT ROUTINES====================================

!apm!%ROUTINE readline(%STRING %NAME s)
!apm!   !
!apm!   ! Read a line from the console
!apm!   !
!apm!   %INTEGER i
!apm!   %BYTE q

!apm!   s = ""; i = 0
!apm!   %WHILE (nextch#nl) %AND (i<255) %CYCLE
!apm!      readch(q)
!apm!      s = s.tostring(q)
!apm!      i = i+1
!apm!   %REPEAT
!apm!   readch(q)

!apm!%END  { of readline }

!-------------------------------------------------------------------------------

{apm}%routine read ch(%bytename ch)
{apm}  %integer char
{apm}  readsymbol(char); ch<- char
{apm}%end

{apm}%integerfn next ch
{apm} %result=nextsymbol
{apm}%end

   %ROUTINE rd ch(%BYTE %NAME ch)
      !
      ! Returns a character from the current input file.  Event 9 occurs
      ! if a read beyond the end of file is attempted.  This routine hides
      ! the peculiar EMAS end-of-file mark from everyone else.
      !
      %OWN %BYTE next, ctrly=no

      %IF ctrly=yes %START
         ch = next
         ctrly = no
      %FINISH %ELSE read ch(ch)

      %IF ch=25 %START
         read ch(next)  { generates an event if at EOF }
         ctrly = yes
      %FINISH

   %END {of rd ch}

!-------------------------------------------------------------------------------

   %STRING (255) %FUNCTION next file from list
      !
      ! Return the next file name from the list in file T#KERMF
      !
      %INTEGER old channel
      %STRING (255) s

      old channel = instream
      select input(file list channel)
      %IF nextch=25 %START
         s = ""
         file list file = no
{apm}    close input
         select input(old channel)
!apm!    close stream(file list channel)
      %FINISHELSE readline(s)
      select input(old channel)
      %RESULT = s

   %END  { of next file from list }

!===================NETWORK HANDLING============================================

   %ROUTINE set up TCP or PAD
      !
      ! Do exactly that.
      !
{apm} exempt mask = exempt mask ! (1<<1) {! (1<<10) ! (1<<13)}
{apm} !Exempt SOH (ASCII 1) and don't switch CR and LF
      %IF x29call=yes %START      { we are a PAD }
         padparameters = x3parms
         setmode("transparent")
         setmode("forward=126")
         setmode("noecho")
      %ELSE
         setmode("graph=on")      { we are a TCP }
         setmode("Z=on")
         setmode("echo=off")
      %FINISH
      ddelay(2)

   %END  { of setup TCP or PAD }

!-------------------------------------------------------------------------------

   %ROUTINE reset TCP or PAD
      !
      ! You guessed it
      !
      %STRING (255) s1, s2, s3
{apm} exempt mask = exempt mask & (\( (1<<1) ! (1<<10) ! (1<<13) ))
{apm} !Un-exempt SOH etc.
      %IF x29call=yes %START
!apm!    %IF padparameters -> s1.("P11").s2.(",").s3 %THEN padparameters = s1.s3
{apm}    %if padparameters -> s1.("P11").s2 %and s2 -> s2.(",").s3 %then %c
{apm}    padparameters = s1.s3
!apm!    %IF padparameters -> s1.("P13").s2.(",").s3 %THEN padparameters = s1.s3
{apm}    %if padparameters -> s1.("P13").s2 %and s2 -> s2.(",").s3 %then %c
{apm}    padparameters = s1.s3
         setmode(padparameters)
      %ELSE
         setmode("graph=off")    { we are a TCP }
         setmode("Z=off")
         setmode("echo=on")
      %FINISH
      ddelay(2)

   %END  { of reset TCP or PAD }

!===================MISCELLANEOUS===============================================

   %STRING (255) %FUNCTION export filename(%STRING (255) filename)
      !
      ! Construct a filename in normal form from the given name
      !

      %WHILE filename -> (":").filename %CYCLE; %REPEAT
      %WHILE filename -> (".").filename %CYCLE; %REPEAT
      %RESULT = filename.".EMS"

   %END  { of export filename }

!------------------------------------------------------------------------------

   %STRING (255) %FUNCTION import filename(%STRING (255) file)
      !
      ! Convert any '/' or '\' to a '.'
      !
      %STRING (255) s1, s2

      %WHILE file -> s1.("\").s2 %CYCLE; file = s1.".".s2; %REPEAT
      %WHILE file -> s1.("/").s2 %CYCLE; file = s1.".".s2; %REPEAT
      %RESULT = file

   %END  { of import filename }

!------------------------------------------------------------------------------

   %STRING (255) %FN make filename(%STRING (255) old)
      !
      ! Construct an EMAS filename from the string given.
      !
      %INTEGER i
      %STRING (255) new, original

      new = ""
      original = ucstring(old)
      %FOR i = 1, 1, length(original) %CYCLE
         %IF charno(original, i)='\' %THEN charno(original, i) = '/' { convert \ to / }
         %IF 'A'<=charno(original, i)<='Z' %C
         %OR '0'<=charno(original, i)<='9' %C
         %OR charno(original, i)='#' %C
         %OR (emas3=yes %AND charno(original, i)=':') %C
         %OR (emas3=yes %AND charno(original, i)='/') %START
            %IF emas3=yes %AND charno(original, i)='/' %THEN charno(original, i) = '.' { convert / to . }
            new = new.tostring(charno(original, i))
         %FINISH
      %REPEAT {that got rid of all illegal characters}

      charno(new, 1) = 'X' %UNLESS 'A'<=charno(new, 1)<='Z' %OR (emas3=yes %AND charno(new, 1)=':')

      %IF file prot="ON" %START
         i = length(new)
         %WHILE exist(new)=yes %AND charno(new, i)#'.' %AND charno(new, i)#':' %CYCLE
            %IF i>1 %THEN charno(new, i) = '#' %AND i = i-1 %ELSE new = new."#"
         %REPEAT
      %FINISH

      %IF emas3=no %START
         %IF length(new)<=11 %THEN %RESULT = new %ELSE %RESULT = ""
      %FINISH

      %IF emas3=yes %START
         i = length(new)
         %WHILE i>0 %AND charno(new, i)#'.' %AND charno(new, i)#':' %CYCLE
            i = i-1
         %REPEAT
         %IF length(new)-i<=11 %THEN %RESULT = new %ELSE %RESULT = "" 
      %FINISH

{apm}%result = "" ;!Compiler dummy
   %END  { of make filename }

!------------------------------------------------------------------------------

   %BYTE %FUNCTION valid path(%STRING (255) file)
      !
      ! Return yes if path exists, no if it does not.  Strip the last
      ! element off before testing.
      !
      %STRING (255) path
      %INTEGER i, junk, flag

      i = length(file)
      %WHILE i>0 %AND charno(file, i)#'.' %AND charno(file, i)#':' %CYCLE
         i = i-1
      %REPEAT
      path = substring(file, 1, i)
      junk = emas3checkname(path, 32, 2!4, flag)
      %IF flag=0 %THEN %RESULT = yes %ELSE %RESULT = no

   %END  { of valid path }

!------------------------------------------------------------------------------

   %INTEGER %FN char file(%STRING (255) file)
      !
      ! See if the given file is a character file or not
      !
      %INTEGER type

      existtype(file,type)
      %IF type=3 %THEN %RESULT = yes %ELSE %RESULT = no

   %END  { of char file }

!-------------------------------------------------------------------------------

![page]
!******************************************************************************
!******************************************************************************
!******             K E R M I T   U T I L I T Y   R O U T I N E S        ******
!******************************************************************************
!******************************************************************************

!===================CONVERSION FUNCTIONS========================================

   %BYTE %FN char(%BYTE ch)
      %RESULT = ch+32
   %END {of char}

!-------------------------------------------------------------------------------

   %BYTE %FN unchar(%BYTE ch)
      %RESULT = ch-32
   %END {of unchar}

!-------------------------------------------------------------------------------

   %BYTE %FN ctl(%BYTE ch)
      %RESULT = ch!!64
   %END {of ctl}

!===================PACKET CONSTRUCTION AND VALIDATION==========================

   %BYTE %FN check(%BYTE %ARRAY %NAME packet)
      !
      ! Returns the checksum of the given packet
      !
      %INTEGER i, s

      s = 0
      s = s+packet(i) %FOR i = 2, 1, unchar(packet(2))+1
      %RESULT = char((s+((s&192)//64))&63)

   %END {of check}

!-------------------------------------------------------------------------------

   %ROUTINE make packet(%BYTE seq, type, %STRING (255) data)
      !
      ! This routine takes a string of already processed characters (i.e. quoted
      ! etc) and builds a complete "SEND PACKET" ready for transmission.
      !
      %INTEGER i

      send packet length = length(data)+5
      send packet(1) = send SOH {mark}
      send packet(2) = char(send packet length-2) {len}
      send packet(3) = char(seq) {seq}
      send packet(4) = type {type}
      send packet(i+4) = charno(data, i) %FOR i = 1, 1, length(data)
      send packet(send packet length) = check(send packet)

   %END {of make packet}

!-------------------------------------------------------------------------------

   %STRING (255) %FN data received
      !
      ! Returns the data part of the RECEIVE PACKET as a string
      ! (opposite of the MAKE PACKET routine)
      !
      %STRING (255) r
      %INTEGER i

      r = ""
      r = r.tostring(receive packet(i)) %FOR i = 5, 1, unchar(receive packet(2))+1
      %RESULT = r

   %END {of data received}

!===================PACKET INPUT AND OUTPUT=====================================

{apm}%routine print ch(%integer ch)
!!%if ch=13 %then printsymbol(ch) %and %return
!!%if ch<32 %or ch>=127 %start
!!   printsymbol('['); phex2(ch); printsymbol(']')
!!%else
 printsymbol(ch)
!!%finish
%end
{apm}%routine hex ch(%integer ch); phex2(ch); space; %end

   %ROUTINE transmit packet
      !
      ! Transmits SEND PACKET
      !
      %INTEGER i, old channel

{}    select output(0) ;!Nasty fudge. should not be necessary.
      print ch(send padchar) %FOR i = 1, 1, send padding
      print ch(send packet(i)) %FOR i = 1, 1, send packet length
      print ch(send end of line)
{}newline
      terminate

      %IF debug=yes %START
         old channel = outstream
         select output(debug channel)
         printstring("APM".itos(old channel, 1).": ")
         hex ch(send padchar) %FOR i = 1, 1, send padding
         hex ch(send packet(i)) %for i = 1, 1, 3
         printstring("""".tostring(send packet(4)).""" ")
         %if send packet length >=5 %start
            hex ch(send packet(i)) %FOR i = 5, 1, send packet length
         %finish
         hex ch(send end of line)
         newline
         select output(old channel)
      %FINISH

   %END {of transmit packet}

!-------------------------------------------------------------------------------

   %ROUTINE get packet
      !
      ! Gets RECEIVE PACKET
      !
      %INTEGER i,receive packet length,dmode,c, old channel
      %constinteger text=0, hex=1, unknown=-1

!for testing: Reads from console only but in single-character mode.
{apm}%routine rd ch(%bytename ch)
{apm}%integer c
{apm}%cycle
{apm}   c = testsymbol
{apm}%repeatuntil c>=0
{apm}ch<-c
{apm}%end

   debug print("Get packet")

      %CYCLE
!apm!    int = ucstring(interrupt)
!apm!    %RETURN %IF int="ABORT"
         rd ch(receive packet(1))
      %REPEAT %UNTIL receive packet(1)=receive SOH
!apm! int = ucstring(interrupt)
!apm! %RETURN %IF int="ABORT"
      rd ch(receive packet(2))
      receive packet length = unchar(receive packet(2))
      %if receive packet length>100 %then receive packet(4)='?' %elsestart
      %FOR i = 1, 1, receive packet length %CYCLE
!apm!    int = ucstring(interrupt)
!apm!    %RETURN %IF int="ABORT"
         rd ch(receive packet(i+2))
      %REPEAT
      %IF receive packet(receive packet length+2)#check(receive packet) %c
      %then receive packet(4) = '?'
      %finish

      %IF debug=yes %START
         receive packet length = receive packet length + 2 ;!Include CRC etc.
         old channel = outstream
         select output(debug channel)
         printstring("Micro: ")
         %for i = 1, 1, 3 %cycle; hex ch(receive packet(i)); %repeat
         printstring("""".tostring(receive packet(4)).""" ")
         %if receive packet length <= 25 %then space %else newline
         !Print the contents of the buffer, printable stuff in quotes, other
         !stuff in hex pair.
         dmode=unknown
         %if receive packet length>=5 %start
            %FOR i = 5, 1, receive packet length %cycle
               c = receive packet(i)
               %if 32<=c<=126 %start ;!Text
                  printsymbol('"') %if dmode # text
                  printsymbol(c); dmode=text
               %else
                  printsymbol('"') %if dmode = text
                  hex ch(c); dmode=hex
               %finish
            %repeat
            printsymbol('"') %if dmode=text
            newline
         %finish
         select output(old channel)
      %FINISH

   %END {of get packet}

!!   %ROUTINE get packet
!!      !Alternative JHB version coded for SPEED (Doesn't work needless to say)
!!      %INTEGER i,rlen,c,csum
!!
!!      %CYCLE
!!         c = testsymbol
!!      %REPEAT %UNTIL c=receive SOH
!!      %cycle
!!         csum = testsymbol
!!      %repeatuntil csum >= 0
!!      rlen = csum-30
!!      %if rlen>102 %then receive packet(4)='?' %elsestart
!!         %FOR i = 3, 1, rlen %CYCLE
!!            %cycle; c=testsymbol; %repeatuntil c>=0
!!            receive packet(i) = c
!!            csum = csum + c
!!         %repeat
!!         %cycle; c=testsymbol; %repeatuntil c>=0
!!      
!!         csum = ((csum+((csum&192)//64))&63)+32
!!         %IF csum#c %then receive packet(4) = '?'
!!      %finish
!!      receive packet(1) = receive SOH
!!      receive packet(2) = rlen+30
!!   %END {of get packet}

!-------------------------------------------------------------------------------

   %ROUTINE get good packet
      !
      ! Calls get packet, checks the checksum, if faulty, NAKs and tries again
      !
      get packet
      %IF receive packet(4)='?' %START
         make packet(packet no, 'N', "")
         transmit packet
         retries = retries+1
         %IF retries>=receive retries %START
            int = "retries"
            %RETURN
         %FINISH
         get good packet
      %FINISH

   %END {of GET GOOD PACKET}

!===================FILE I/O AND ENCODING/DECODING==============================

   %STRING (255) %FUNCTION encode(%STRING (255) s)
      !
      ! Encode the unencoded data string s and return the encoded string.
      !
      %BYTE ch
      %INTEGER i,j,repeats
      %STRING (255) r

      r = ""
      i = 1

      %WHILE i<=length(s) %AND length(r)<send max packet-9 %CYCLE

         ch = charno(s,i)

         repeats = 1
         %IF repeating=yes %START
            %WHILE i+1<=length(s) %AND ch=charno(s,i+1) %CYCLE
               repeats = repeats+1
               i = i+1
            %REPEAT
         %FINISH
         %IF repeats>=3 %START
            r = r.tostring(repeat quote).tostring(char(repeats))
            repeats = 1
         %FINISH

         %FOR j = 1, 1, repeats %CYCLE
            %IF ch&128=128 %AND eight bit=yes %THEN r = r.tostring(binary quote) %AND ch = ch&127
            %IF ch<32 %OR ch>126 %THEN r = r.tostring(send quote).tostring(ctl(ch)) %C
            %ELSE %IF ch=send quote                      %THEN r = r.tostring(send quote).tostring(ch) %C
            %ELSE %IF ch=binary quote %AND eight bit=yes %THEN r = r.tostring(send quote).tostring(ch) %C
            %ELSE %IF ch=repeat quote %AND repeating=yes %THEN r = r.tostring(send quote).tostring(ch) %C
            %ELSE r = r.tostring(ch)
         %REPEAT

      i = i+1

      %REPEAT

      %RESULT = r

   %END  { of encode }

!-------------------------------------------------------------------------------

   %STRING (255) %FUNCTION decode(%STRING (255) s)
      !
      ! Interpret the encoded data string s and return the decoded string.
      ! Note that this routine will not handle decoded strings which are
      ! longer than 255 characters, so long repeats will run into problems.
      !
      %BYTE bit set,ch
      %INTEGER i,j,repeats
      %STRING (255) data

      i = 1
      j = 1
      data = ""

      %WHILE i<=length(s) %AND length(data)<255 %CYCLE

         %IF charno(s,i)=repeat quote %AND repeating = yes %START
            repeats = unchar(charno(s,i+1))
            i = i+2
         %FINISH %ELSE repeats = 1

         %IF charno(s,i)=binary quote %AND eight bit=yes %START
            bit set = yes
            i = i+1
         %FINISH %ELSE bit set = no

         %IF charno(s,i)=receive quote %START
            i = i+1
            %IF charno(s,i)=receive quote %THEN ch = receive quote %C
            %ELSE %IF charno(s,i)=binary quote %AND eight bit=yes %THEN ch = binary quote %C
            %ELSE %IF charno(s,i)=repeat quote %AND repeating=yes %THEN ch = repeat quote %C
            %ELSE ch = ctl(charno(s,i))
         %FINISH %ELSE ch = charno(s,i)

         %IF bit set=yes %THEN ch = ch ! 128

         data = data.tostring(ch) %FOR j = 1, 1, repeats 

         i=i+1

      %REPEAT

      %RESULT = data

   %END  { of decode }

!-------------------------------------------------------------------------------

   %STRING (255) %FN read and process
      !
      ! This function reads a string of data and returns a corresponding string
      ! with all control characters etc quoted.
      !
      %STRING (255) r
      %INTEGER repeats, i
      %BYTE ch, ch8, flag

      %ON 9 %START
         !
         ! End of file on input
         !
         EOF = yes
         %IF repeats>1 %START
            repeats = repeats-1
            flag = yes;                                     ! We want to go to label "process", but
            ->while loop;                                   ! the compiler won't let us, so we must use a flag.
         %FINISH
         %RESULT = r
      %FINISH

      r = ""
      flag = no

while loop:
      %WHILE length(r)<send max packet-9 %AND (EOF#yes %OR flag=yes) %CYCLE

         %IF flag=yes %THEN ->process

         repeats = 1
         rd ch(ch)

         %IF repeating=yes %START
            %WHILE next ch=ch %AND repeats<94 %AND %NOT (ch=lf %AND file type="ASCII") %CYCLE
               repeats = repeats+1
               rd ch(ch)
            %REPEAT
         %FINISH

process:
         flag = no

         %IF repeats>=3 %START

            r = r.tostring(repeat quote).tostring(char(repeats))
            repeats = 1

         %FINISH

         ch8 = ch

         %FOR i = 1, 1, repeats %CYCLE

            %IF ch=lf %AND file type="ASCII" %THEN r = r.tostring(send quote).tostring(ctl(cr))
            { add in CR if LF }

            %IF ch8&128=128 %AND eight bit=yes %THEN r = r.tostring(binary quote) %AND ch = ch8&127
            { add in & if 8th bit set }

            %IF ch<32 %OR ch>126 %THEN r = r.tostring(send quote).tostring(ctl(ch)) %ELSE %C
               { add in # if control character } %IF ch=send quote %THEN r = r.tostring(send quote).tostring(ch) %ELSE %C
               { add in # if # } %IF ch=binary quote %AND eight bit=yes %THEN r = r.tostring(send quote).tostring(ch) %ELSE %C
               { add in # if & } %IF ch=repeat quote %AND repeating=yes %THEN r = r.tostring(send quote).tostring(ch) %ELSE %C
               { add in # if ~ } r = r.tostring(ch) { ...otherwise }

         %REPEAT  { end of the %FOR loop }

      %REPEAT  { end of the %WHILE loop }

      %RESULT = r

   %END {of READ AND PROCESS}

!-------------------------------------------------------------------------------

   %ROUTINE unprocess and write(%STRING (255) s)
      !
      ! Does the opposite of READ AND PROCESS
      !
      %STRING (4) crlf
      %INTEGER i, x, repeats, ch, bit set

      crlf = tostring(receive quote).tostring(ctl(cr)).tostring(receive quote).tostring(ctl(lf))
      i = 1

      ! This next bit is in case we've got a crlf pair split across a packet. The flag
      ! 'pending cr' is set to one if the last character of the last packet was
      ! a cr. The cr is then omitted if the first char of this packet is lf.
      ! (because kermit says eoln = crlf, emas says eoln = lf)

      %IF pending cr=yes %AND substring(s, 1, 2)#tostring(receive quote)."J" %THEN print ch(cr)

      pending cr = no

      %IF substring(s, length(s)-1, length(s))=tostring(receive quote).tostring(ctl(cr)) %AND file type="ASCII" %START
         s = substring(s, 1, length(s)-2)
         pending cr = yes { if last char = cr, set flag }
      %FINISH

      %WHILE i<=length(s) %CYCLE


         %IF charno(s, i)=repeat quote %AND repeating=yes %START

            repeats = unchar(charno(s, i+1))
            i = i+2

         %FINISH %ELSE repeats = 1


         %IF charno(s, i)=binary quote %AND eight bit=yes %START

            bit set = yes
            i = i+1

         %FINISH %ELSE bit set = no


         %IF i+3<=length(s) %AND substring(s, i, i+3)=crlf %AND file type="ASCII" %START

            ch = lf
            i = i+3

         %FINISH %ELSE %IF charno(s, i)=receive quote %START

            i = i+1

            %IF charno(s, i)=receive quote %THEN ch = receive quote %ELSE %IF %C
               charno(s, i)=binary quote %AND eight bit=yes %THEN ch = binary quote %ELSE %IF %C
               charno(s, i)=repeat quote %AND repeating=yes %THEN ch = repeat quote %ELSE ch = ctl(charno(s, i))

         %FINISH %ELSE ch = charno(s, i)


         %IF bit set=yes %THEN ch = ch!128

         print ch(ch) %FOR x = 1, 1, repeats

         i = i+1

      %REPEAT

   %END {of UNPROCESS AND WRITE}

!===================PARAMETER SETTING===========================================

   %ROUTINE setrecpar
      !
      ! Set up the "send" parameters from the received 'S' or 'Y' or 'I' packet
      !

      %BYTE %FN valid char(%INTEGER i)
         !
         ! Is character at position i in receive packet a valid, non-blank character ?
         !
         %IF unchar(receive packet(2)) < i-1 %THEN %C
            %RESULT = no  { not enough characters in data field } %C
         %ELSE %IF receive packet(i)=' ' %THEN %C
            %RESULT = no { character is a blank } %C
         %ELSE %RESULT = yes
      %END  { of valid char in setrecpar }

      %IF valid char(5)=yes %THEN send max packet = unchar(receive packet(5))
      %IF valid char(7)=yes %THEN send padding = unchar(receive packet(7))
      %IF valid char(8)=yes %THEN send padchar = ctl(receive packet(8))
      %IF valid char(9)=yes %THEN send end of line = unchar(receive packet(9))
      %IF valid char(10)=yes %THEN receive quote = receive packet(10)
      eight bit = no
      %IF valid char(11)=yes %THEN %START
         %IF receive packet(11)=binary quote %THEN eight bit = yes
         {do quoting if we are asked}
         %IF receive packet(11)='Y' %THEN eight bit = yes
         {or if the other side is happy}
         %IF 33<=receive packet(11)<=62 %OR 96<=receive packet(11)<=126 %THEN %C
            binary quote = receive packet(11)  {use what the other chap wants}
      %FINISH
      repeating = no
      %IF valid char(13)=yes %THEN %START
         %IF receive packet(4)='Y' %THEN %START
            %IF receive packet(13)=repeat quote %THEN repeating = yes
         %FINISH %ELSE %START
            %IF 33<=receive packet(13)<=62 %OR 96<=receive packet(13)<=126 %THEN %START
               repeat quote = receive packet(13)
               repeating = yes
            %FINISH
         %FINISH
      %FINISH

   %END  { of setrecpar }

!-------------------------------------------------------------------------------

!apm!%ROUTINE make up my params(%STRING %NAME data)
{apm}%routine make up my params(%string (255) %name data)
      !
      ! Form a string to send with a 'Y' or 'S' packet, containing my parameters
      !
      data = tostring(char(receive max packet)).tostring(char(15))
      data = data.tostring(char(0)).tostring(ctl(0))
      data = data.tostring(char(13)).tostring(send quote).tostring(binary quote)
      data = data."1".tostring(repeat quote)

   %END  { of make up my params }

!-------------------------------------------------------------------------------

   %ROUTINE initpar

      !
      ! Initialise parameters which are subject to negociation
      !
      receive quote = '#'
      send max packet = 80
      send padding = 0
      send padchar = 0
      send end of line = cr
      binary quote = '&'
      repeat quote = '~'

   %END  { of initpar }

!===================PREPARE AND RESET LINK======================================

   %ROUTINE prepare for transaction

      %IF debug = no %THEN journal off %else debug print("Prepare for Transaction")
!apm! prompt(tostring(0))
{apm} prompt("")
      select input(comms port in)
      select output(comms port out)
      setup TCP or PAD

   %END  { of prepare for transaction }

!-------------------------------------------------------------------------------

   %ROUTINE tidyup after transaction

      select output(console channel)
      select input(command channel)
      reset TCP or PAD
      journal on

   %END  { of tidyup after transaction }

!===================MISCELLANEOUS===============================================

   %BYTE %FN next(%BYTE packno)
      %RESULT = (packno+1)&63
   %END {of NEXT}

!-------------------------------------------------------------------------------

   %BYTE %FN prev(%BYTE packno)
      %IF packno=0 %THEN %RESULT = 63 %ELSE %RESULT = packno-1
   %END {of PREV}

!-------------------------------------------------------------------------------

   %INTEGER %FN ack OK
      !
      ! ack OK = yes iff the packet we've got is an ACK for this packet or a NAK for
      ! the next one.
      !
      %IF ((receive packet(4)='Y' %AND receive packet(3)=char(packet no)) %OR (receive packet(4)='N' %AND %C
         receive packet(3)=char(next(packet no)))) %THEN %RESULT = yes %ELSE %RESULT = no

   %END {of ACK OK}

!-------------------------------------------------------------------------------

   %ROUTINE parse G packet (%INTEGER %NAME npar, %STRING (*) %ARRAY %NAME par)
      !
      ! Return number of parameters and parameter values of current received G packet
      !
      %STRING (255) data
      %INTEGER i,j,k

      data = data received
      %IF length(data)<2 %THEN npar = 0 %AND %RETURN   { no parameters }
      data = substring(data,2,length(data))
      data = decode(data)

      i = 1
      npar = 0
      %WHILE i<=length(data) %AND npar<10 %CYCLE
         npar = npar+1
         j = 1
         par(npar) = ""
         k = unchar(charno(data,i))
         %WHILE i+j<=length(data) %AND j<=k %CYCLE
            par(npar) = par(npar).substring(data, i+j, i+j)
            j = j+1
         %REPEAT
         i = i+j
      %REPEAT

   %END  { of parse G packet }

![page]
!******************************************************************************
!******************************************************************************
!******             S T A T E    S W I T C H E R                         ******
!******************************************************************************
!******************************************************************************

! In order to follow what's going on here, it would be a good idea to get  the
! 'Advanced Kermit Protocol State Table' from the 5th edition of the Protocol
! Manual, as this routine is based entirely upon the states therein.  Any
! departures from that state table are clearly marked.

   %INTEGER %FUNCTION switch state(%INTEGER initial state, %STRING (255) filenames)

      %BYTE serving, texting
      %INTEGER state
      %STRING (255) data, this file, files

      %ROUTINE send error packet(%STRING (255) s)
         !
         ! Send error packet and abort. This is not a state routine.
         !
         %STRING (255) str
         str = encode("APM Kermit: ".s)
         make packet(packet no, 'E', str)
         transmit packet
         state = abort
      %END  { of send error packet in switch state }

      !
      ! The following are the state routines. Changing the state variable
      ! is their responsibility.
      !
      !-------------------------------------------------------------------------
      %ROUTINE rec server idle state

         %INTEGER npar, junk, type, qualifier, flag
         %STRING (255) %ARRAY par(1:10)
         %STRING (255) str, str2, str3

         serving = yes
         texting = no

         packet no = 0
         retries = 0

         get good packet
         %IF int="retries" %THEN send error packet("Retry limit exceeded") %AND %RETURN
         %IF int="ABORT" %THEN state = abort %AND %RETURN

         %IF receive packet(4)='I' %AND unchar(receive packet(3))=0 %START
            setrecpar                { set params from received packet }
            make up my params(data)  { get my parameters to send as an ACK }
            make packet(packet no, 'Y', data)
            transmit packet
            state = rec server idle
            %RETURN

         %FINISH %ELSE %IF receive packet(4)='S' %AND unchar(receive packet(3))=0 %START
            setrecpar                { set params from received packet }
            make up my params(data)  { get my parameters to send as an ACK }
            make packet(packet no, 'Y', data)
            transmit packet
            packet no = next(packet no)
            retries = 0
            files = ""
            state = rec file
            %RETURN

         %FINISH %ELSE %IF receive packet(4)='R' %AND unchar(receive packet(3))=0 %START
            files = decode(data received)
            state = send init
            %RETURN

         %FINISH %ELSE %IF receive packet(4)='C' %AND unchar(receive packet(3))=0 %START
            send error packet("REMOTE HOST command not implemented")
            %RETURN

         %FINISH %ELSE %IF receive packet(4)='K' %AND unchar(receive packet(3))=0 %START
            send error packet("REMOTE KERMIT command not implemented")
            %RETURN

         %FINISH %ELSE %IF receive packet(4)='G' %AND unchar(receive packet(3))=0 %START
            parse G packet(npar, par)
            %IF receive packet(5)='L' %START    { log out }
               make packet(packet no, 'Y', "")
               transmit packet
               state = logout
               %RETURN

            %FINISH %ELSE %IF receive packet(5)='F' %START  { return to os }
               make packet(packet no, 'Y', "")
               transmit packet
               state = finish
               %RETURN

            %FINISH %ELSE %IF receive packet(5)='C' %START  { change group }
               %IF emas3=yes %START
                  flag = 1
                  %IF npar>=1 %AND par(1)#"" %START
                     par(1) = import filename(par(1))
                     type = 4
                     qualifier = 4
                     junk = emas3checkname(par(1), type, qualifier, flag)
                     %IF flag#0 %AND par(1)=":" %THEN flag = 0
                     %IF flag=0 %THEN call silently("USEGROUP",par(1))
                  %FINISH %ELSE flag = 0
                  %IF flag#0 %THEN str = "APM Kermit: Illegal or non-existant group" %ELSE uinfs(17, str)
                  make packet(packet no, 'Y', encode(str))
                  transmit packet
                  state = rec server idle
                  %RETURN

               %FINISH %ELSE %START
                  send error packet("Command not supported")
                  %RETURN

               %FINISH

            %FINISH %ELSE %IF receive packet(5)='D' %START  { directory listing }
               %IF exist("T#KERMF")=yes %THEN call silently("DESTROY","T#KERMF")
               %IF npar=0 %THEN par(1) = ""
               %IF npar>=1 %AND par(1)->str.(",").str2 %THEN %START  { a second parameter }
                  %IF str2->str2.(",").str3 %START; %FINISH  { ignore third parameter }
                  par(1) = str.",".str2
               %FINISH %ELSE par(1) = par(1).","  { no second parameter }
               par(1) = import filename(par(1))   { for consistancy }
               call("FILES",par(1).",T#KERMF")
               files = "T#KERMF"
               texting = yes
               state = send init
               %RETURN

            %FINISH %ELSE %IF receive packet(5)='T' %START  { type a file }
               %IF npar>=1 %AND par(1)#"" %AND exist(par(1))=yes %AND char file(par(1))=yes %START
                  files = par(1) %UNLESS par(1) -> files.(",").par(1)
                  files = import filename(files)   { for consistancy }
                  texting = yes
                  state = send init
                  %RETURN

               %FINISH %ELSE %START
                  send error packet("File does not exist or wrong type")
                  %RETURN

               %FINISH

            %FINISH %ELSE %IF receive packet(5)='R' %START  { rename a file }
               par(1) = import filename(par(1))   { for consistancy }
               par(2) = import filename(par(2))   { for consistancy }
               %IF npar<2 %OR par(1)="" %OR par(2)="" %START
                  send error packet("Illegal parameters")
                  %RETURN

               %FINISH %ELSE %IF exist(par(1))=no %START
                  send error packet("File does not exist")
                  %RETURN

               %FINISH %ELSE %IF exist(par(2))=yes %START
                  send error packet("File already exists")
                  %RETURN

               %FINISH %ELSE %START
                  call silently("RENAME",par(1).",".par(2))
                  make packet(packet no, 'Y', "")
                  transmit packet
                  state = rec server idle
                  %RETURN

               %FINISH

            %FINISH %ELSE %IF receive packet(5)='K' %START  { copy a file }
               par(1) = import filename(par(1))   { for consistancy }
               par(2) = import filename(par(2))   { for consistancy }
               %IF npar<2 %OR par(1)="" %OR par(2)="" %START
                  send error packet("Illegal parameters")
                  %RETURN

               %FINISH %ELSE %IF exist(par(1))=no %START
                  send error packet("File does not exist")
                  %RETURN

               %FINISH %ELSE %IF exist(par(2))=yes %START
                  send error packet("File already exists")
                  %RETURN

               %FINISH %ELSE %START
                  call silently("COPY",par(1).",".par(2))
                  make packet(packet no, 'Y', "")
                  transmit packet
                  state = rec server idle
                  %RETURN

               %FINISH

            %FINISH %ELSE %IF receive packet(5)='E' %START  { erase a file }
               %WHILE npar>=1 %AND par(1)#"" %CYCLE
                  str = par(1) %AND par(1) = "" %UNLESS par(1) -> str.(",").par(1)
                  str = import filename(str)   { for consistancy }
                  %IF exist(str)=yes %THEN call silently("DESTROY",str) %C
                  %ELSE %START
                     send error packet(str." does not exist")
                     %RETURN
                  %FINISH
               %REPEAT
               make packet(packet no, 'Y', "")
               transmit packet
               state = rec server idle
               %RETURN

            %FINISH %ELSE %START
               send error packet("Unimplemented generic command G".tostring(receive packet(5)))
               %RETURN

            %FINISH

         %FINISH %ELSE %START
            send error packet("Unrecognised server command packet: ".tostring(receive packet(4)))
            state = rec server idle
            %RETURN

         %FINISH

      %END  { of rec server idle in switch state }
      !-------------------------------------------------------------------------
      %ROUTINE rec init state

         packet no = 0
         retries = 0

         get good packet
         %IF retries>=receive retries %THEN send error packet("Retry limit exceeded") %AND %RETURN
         %IF int="ABORT" %THEN state = abort %AND %RETURN

         %IF receive packet(4)='S' %AND unchar(receive packet(3))=0 %START
            setrecpar  { set up parameters from receive packet }
            make up my params(data)  { ACK the send init }
            make packet(packet no, 'Y', data)
            transmit packet
            packet no = next(packet no)
            retries = 0
            state = rec file
            %RETURN

         %FINISH %ELSE %START
            { ** DEVIATION **   We get here (eg) if a valid non S(0) packet }
            { arrives (perhaps the other end thinks it is in the middle of  }
            { a transfer).  We should NAK it and abort, which might result  }
            { in the other end sending packets to the command interpreter.  }
            { Insetad, we send an error packet to abort the other end, then }
            { abort ourselves.                                              }
            send error packet("Unexpected packet in rec init state")
            %RETURN

         %FINISH

      %END  { of rec init in switch state }
      !-------------------------------------------------------------------------
      %ROUTINE rec file state

         get good packet
         %IF retries>=receive retries %THEN send error packet("Retry limit exceeded") %AND %RETURN
         %IF int="ABORT" %THEN state = abort %AND %RETURN

         %IF receive packet(4)='F' %AND unchar(receive packet(3))=packet no %START
            this file = files %UNLESS files->this file.(",").files
            %IF this file="" %THEN this file = decode(data received)  { no filename given by user }
            this file = make filename(this file)
            %IF this file="" %START
               send error packet("Cannot create a unique filename")
               %RETURN

            %FINISH %ELSE %IF emas3=yes %AND valid path(this file)=no %START
               send error packet("Invalid path specification")
               %RETURN

            %FINISH %ELSE %START
               open output(file channel 2, this file)
{}selectoutput(file channel 2)
               make packet(packet no, 'Y', encode(this file))
               transmit packet
               packet no = next(packet no)
               retries = 0
               state = rec data
               %RETURN

            %FINISH

         %FINISH %ELSE %IF receive packet(4)='B' %AND unchar(receive packet(3))=packet no %START
            make packet(packet no, 'Y', "")
            transmit packet
            state = complete
            %RETURN

         %FINISH %ELSE %IF receive packet(4)='S' %AND unchar(receive packet(3))=prev(packet no) %START
            make up my params(data)
            make packet(prev(packet no), 'Y', data)
            transmit packet
            retries = retries+1
            %IF retries>=receive retries %THEN send error packet("Retry limit exceeded") %AND %RETURN
            state = rec file
            %RETURN

         %FINISH %ELSE %IF receive packet(4)='Z' %AND unchar(receive packet(3))=prev(packet no) %START
            make packet(prev(packet no), 'Y', "")
            transmit packet
            retries = retries+1
            %IF retries>=receive retries %THEN send error packet("Retry limit exceeded") %AND %RETURN
            state = rec file
            %RETURN

         %FINISH %ELSE %START
            { ** DEVIATION **     We get here if a valid, unexpected packet }
            { arrives (perhaps the other end thinks it is in the middle of  }
            { a transfer).  We should NAK it and abort, which might result  }
            { in the other end sending packets to the command interpreter.  }
            { Insetad, we send an error packet to abort the other end, then }
            { abort ourselves.                                              }
            send error packet("Unexpected packet in rec file state")
            %RETURN

         %FINISH

      %END  { of rec file state in switch state }
      !-------------------------------------------------------------------------
      %ROUTINE rec data state

         get good packet
         %IF retries>=receive retries %THEN send error packet("Retry limit exceeded") %AND %RETURN
         %IF int="ABORT" %THEN state = abort %AND %RETURN

         %IF receive packet(4)='D' %AND unchar(receive packet(3))=packet no %START
            select output(file channel 2)
            unprocess and write(data received)
            select output(comms port out)
            make packet(packet no, 'Y', "")
            transmit packet
            packet no = next(packet no)
            retries = 0
            state = rec data
            %RETURN

         %FINISH %ELSE %IF receive packet(4)='D' %AND unchar(receive packet(3))=prev(packet no) %START
            make packet(prev(packet no), 'Y', "")
            transmit packet
            retries = retries+1
            %IF retries>=receive retries %THEN send error packet("Retry limit exceeded") %AND %RETURN
            state = rec data
            %RETURN

         %FINISH %ELSE %IF receive packet(4)='Z' %AND unchar(receive packet(3))=packet no %START
            %IF pending cr=yes %START
               select output(file channel 2)
               print ch(cr)
               pending cr = no
            %FINISH
{apm}       selectoutput(file channel 2); close output
            select output(comms port out)
!apm!       close stream(file channel 2)
            %IF receive packet(5)='D' %AND unchar(receive packet(2))>=4 %START
               { Z(n)/D  means discard file }
               %IF exist(this file)=yes %THEN call silently("DESTROY",this file)
            %FINISH
            make packet(packet no, 'Y', "")
            transmit packet
            packet no = next(packet no)
            retries = 0
            state = rec file
            %RETURN

         %FINISH %ELSE %IF receive packet(4)='F' %AND unchar(receive packet(3))=prev(packet no) %START
            make packet(prev(packet no), 'Y', "")
            transmit packet
            retries = retries+1
            %IF retries>=receive retries %THEN send error packet("Retry limit exceeded") %AND %RETURN
            state = rec data
            %RETURN

         %FINISH %ELSE %IF receive packet(4)='X' %AND unchar(receive packet(3))=prev(packet no) %START
            make packet(prev(packet no), 'Y', "")
            transmit packet
            retries = retries+1
            %IF retries>=receive retries %THEN send error packet("Retry limit exceeded") %AND %RETURN
            state = rec data
            %RETURN

         %FINISH %ELSE %START
            send error packet("Unexpected packet in rec data state")
            %RETURN

         %FINISH

      %END  { of rec data state in switch state }
      !-------------------------------------------------------------------------
      %ROUTINE send init state

         %STRING (255) data

         file list file = no
         packet no = 0
         retries = 0

         make up my params(data)
         make packet(0, 'S', data)
         transmit packet

         get packet
         %IF int="ABORT" %THEN state = abort %AND %RETURN

         %IF receive packet(4)='Y' %AND unchar(receive packet(3))=0 %START
            { Y(0) received }
            setrecpar  { set up parameters from receive packet }
            packet no = next(packet no)
            retries = 0
            state =open file
            %RETURN

         %FINISH %ELSE %START
            { Other }
            retries = retries+1
            %IF retries>=send retries %THEN send error packet("Retry limit exceeded") %AND %RETURN
            state = send init
            %RETURN

         %FINISH

      %END  { of send init state in switch state }
      !-------------------------------------------------------------------------
      %ROUTINE open file state

         %INTEGER i

         EOF = no
         %IF file list file=no %START
            this file = files %AND files = "" %UNLESS files->this file.(",").files
            %FOR i = 1, 1, length(this file) %CYCLE
               %IF charno(this file, i)='\' %OR charno(this file, i)='/' %THEN charno(this file, i) = '.'
            %REPEAT  { change all \ and / to . }
            %IF this file -> ("*") %START
               %IF exist("T#KERMF")=yes %THEN call silently("DESTROY", "T#KERMF")
               call("FILES",this file.",z,T#KERMF")
               open input(file list channel, "T#KERMF")
{}select input(file list channel)
               file list file = yes

            %FINISH

         %FINISH

         %IF file list file=yes %THEN this file = next file from list
         %IF this file="" %AND files="" %THEN state = send break %AND %RETURN
         %IF exist(this file)=no %OR char file(this file)=no %START
            %IF files="" %START
               { The last file in the list does not exist }
               send error packet(this file." does not exist or wrong type")
               %RETURN

            %FINISH %ELSE %START
               { A file in the list of names does not exist }
               state = open file
               %RETURN

            %FINISH

         %FINISH %ELSE %START
            open input(file channel 1, this file)
{}select input(file channel 1)
            state = send file
            %RETURN

         %FINISH

      %END  { of open file state in switch state }
      !-------------------------------------------------------------------------
      %ROUTINE send file state

         %IF texting = yes %THEN make packet(packet no, 'X', "") %C
            %ELSE make packet(packet no, 'F', encode(export filename(this file)))
         transmit packet

         get packet
         %IF int="ABORT" %THEN state = abort %AND %RETURN

         %IF ack OK=yes %START
            select input(file channel 1)
            data = read and process
            select input(comms port in)
            packet no = next(packet no)
            retries = 0
            state = send data
            %RETURN

         %FINISH %ELSE %IF receive packet(4)='N' %AND unchar(receive packet(3))=packet no %START
            retries = retries+1
            %IF retries>=send retries %THEN send error packet("Retry limit exceeded") %AND %RETURN
            state = send file
            %RETURN

         %FINISH %ELSE %START
            state = abort
            %RETURN

         %FINISH

      %END  { of send file state in switch state }
      !-------------------------------------------------------------------------
      %ROUTINE send data state

         make packet(packet no, 'D', data)
         transmit packet

         get packet
         %IF int="ABORT" %THEN state = abort %AND %RETURN

         %IF receive packet(4)='Y' %AND unchar(receive packet(3))=packet no %C
               %AND unchar(receive packet(2))>=4 %C
               %AND (receive packet(5)='X' %OR receive packet(5)='Z') %START
            { Y(n)/X or Z }
            packet no = next(packet no)
            retries = 0
            %IF receive packet(5)='Z' %THEN files = "" %AND file list file = no  { ensuring no more files are sent }
            state = send eof
            %RETURN

         %FINISH %ELSE %IF ack OK=yes %START
            packet no = next(packet no)
            retries = 0
            %IF EOF=yes %START
               state = send eof
               %RETURN

            %FINISH %ELSE %START
               select input(file channel 1)
               data = read and process
               select input(comms port in)
               state = send data
               %RETURN

            %FINISH

         %FINISH %ELSE %IF receive packet(4)='N' %AND unchar(receive packet(3))=packet no %START
            retries = retries+1
            %IF retries>=send retries %THEN send error packet("Retry limit exceeded") %AND %RETURN
            state = send data
            %RETURN

         %FINISH %ELSE %START
            state = abort
            %RETURN

         %FINISH

      %END  { of send data state in switch state }
      !-------------------------------------------------------------------------
      %ROUTINE send eof state

         make packet(packet no, 'Z', "")
         transmit packet

         get packet
         %IF int="ABORT" %THEN state = abort %AND %RETURN

         %IF ack OK=yes %START
            packet no = next(packet no)
            retries = 0
            %IF files="" %AND file list file=no %START  { all the files have been sent }
               state = send break
               %RETURN

            %FINISH %ELSE %START
               state = open file  { ** DEVIATION **  We should open the file }
               %RETURN            { here and then go to send file.           }

            %FINISH

         %FINISH %ELSE %IF receive packet(4)='N' %AND unchar(receive packet(3))=packet no %START
            retries = retries+1
            %IF retries>=send retries %THEN send error packet("Retry limit exceeded") %AND %RETURN
            state = send eof
            %RETURN
 
         %FINISH %ELSE %START
            state = abort
            %RETURN

         %FINISH

      %END  { of send eof state in switch state }
      !-------------------------------------------------------------------------
      %ROUTINE send break state

         make packet(packet no, 'B', "")
         transmit packet

         get packet
         %IF int="ABORT" %THEN state = abort %AND %RETURN

         %IF (receive packet(4)='Y' %AND unchar(receive packet(3))=packet no) %C
               %OR (receive packet(4)='N' %AND unchar(receive packet(3))=0) %START
            state = complete
            %RETURN

         %FINISH %ELSE %IF receive packet(4)='N' %AND unchar(receive packet(3))=packet no %START
            retries = retries+1
            %IF retries>=send retries %THEN send error packet("Retry limit exceeded") %AND %RETURN
            state = send break
            %RETURN

         %FINISH %ELSE %START
            state = abort
            %RETURN

         %FINISH

      %END  { of send break state in switch state }
      !-------------------------------------------------------------------------
      %ROUTINE complete state

         packet no = 0
         retries = 0
         select input(comms port in)
{apm}    selectinput(file channel 1); close input
{apm}    selectoutput(file channel 2); close output
{apm}    selectinput(file list channel); close input
         select output(comms port out)
!apm!    close stream(file list channel)
!apm!    close stream(file channel 1)
!apm!    close stream(file channel 2)

         %IF serving=yes %START
            initpar      { reset parameters }
            state = rec server idle
            %RETURN

         %FINISH %ELSE %START
            state = exit
            %RETURN

         %FINISH

      %END  { of complete state in switch state }
      !-------------------------------------------------------------------------
      %ROUTINE abort state

         %IF int="ABORT" %THEN serving = no  { go back to command interpreter }
         int = ""
         packet no = 0
         retries = 0
{apm}    select input(file channel 1); close input
{apm}    select output(file channel 2); close output
{apm}    select input(file list channel); close input
         select input(comms port in)
         select output(comms port out)
!apm!    close stream(file list channel)
!apm!    close stream(file channel 1)
!apm!    close stream(file channel 2)

         %IF serving=yes %START
            initpar      { reset parameters }
            state = rec server idle
            %RETURN

         %FINISH %ELSE %START
            state = exit
            %RETURN

         %FINISH

      %END  { of abort state in switch state }
      !-------------------------------------------------------------------------
      files = filenames
      serving = no
      texting = no
      state = initial state

      %CYCLE
         %IF state=rec server idle %THEN rec server idle state
         %IF state=rec init %THEN rec init state
         %IF state=rec file %THEN rec file state
         %IF state=rec data %THEN rec data state
         %IF state=send init %THEN send init state
         %IF state=open file %THEN open file state
         %IF state=send file %THEN send file state
         %IF state=send data %THEN send data state
         %IF state=send eof %THEN send eof state
         %IF state=send break %THEN send break state
         %IF state=complete %THEN complete state
         %IF state=abort %THEN abort state
      %REPEAT %UNTIL state=exit %OR state=logout %OR state=finish

      %RESULT = state

   %END  { of switch state }


![page]
!******************************************************************************
!******************************************************************************
!******             H E L P    R O U T I N E                             ******
!******************************************************************************
!******************************************************************************

   %ROUTINE help

      %IF exist(helpfile)=yes %THEN view(helpfile) %ELSE %START
         newline
         printstring("Help file not available")
         newline
     %FINISH

   %END  { of help }

![page]
!******************************************************************************
!******************************************************************************
!******             S H E L L    E S C A P E    R O U T I N E            ******
!******************************************************************************
!******************************************************************************

   %ROUTINE emas(%STRING (255) command, p1, p2)

      %IF p2#"" %THEN p1 = p1." ".p2
      newline
      printstring("Command:".command." ".p1); newlines(2)
      call(command, p1)

   %END  { of emas }

![page]
!******************************************************************************
!******************************************************************************
!******             S E T    R O U T I N E                               ******
!******************************************************************************
!******************************************************************************

   %ROUTINE set(%STRING (255) p1, p2, p3)

      p1 = ucstring(p1)

      %IF p1="RECEIVE" %START
         p2 = ucstring(p2)
         %IF p2="PACKET_LENGTH" %START
            %IF pstoi(p3)>94 %START
               newline
               printstring(p3." is too long a packet length. Maximum = 94")
               newline
            %FINISH %ELSE receive max packet = pstoi(p3)
         %FINISH %ELSE %IF p2="RETRIES" %THEN %C
            receive retries = pstoi(p3) %C
         %ELSE %IF p2="START_OF_PACKET" %THEN %C
            receive SOH = pstoi(p3) %C
         %ELSE %START
            newline
            printstring(p2." is not a valid option for SET RECEIVE. Type HELP if you need it.")
            newline
         %FINISH
      %FINISH %ELSE %IF p1="SEND" %START
         p2 = ucstring(p2)
         %IF p2="PADDING" %THEN send padding = pstoi(p3) %C
         %ELSE %IF p2="PADCHAR" %THEN send padchar = pstoi(p3) %C
         %ELSE %IF p2="RETRIES" %THEN send retries = pstoi(p3) %C
         %ELSE %IF p2="END_OF_LINE" %THEN send end of line = pstoi(p3) %C
         %ELSE %IF p2="START_OF_PACKET" %THEN send SOH = pstoi(p3) %C
         %ELSE %IF p2="QUOTE" %START
            %IF (33<=charno(p2, 1)<=62 %OR 96<=charno(p2, 1)<=126) %AND charno(p2, 1)#binary quote %AND %C
               charno(p2, 1)#repeat quote %THEN send quote = charno(p2, 1) %ELSE %START
               newline
               printstring("The send quote must be between ASCII 33 and 62 or 96 and 126,")
               newline
               printstring("and it must be distinct from the binary quote and the repeat quote.")
               newline
               printstring("Type HELP if you need it.")
               newline
            %FINISH
         %FINISH %ELSE %START
            newline
            printstring(p2." is not a valid SET SEND option. Type HELP if you need it.")
            newline
         %FINISH
      %FINISH %ELSE %IF p1="PROMPT" %START
         command prompt = p2." "
         prompt(command prompt)
      %FINISH %ELSE %IF p1="FILE_TYPE" %START
         p2 = ucstring(p2)
         %IF p2#"BINARY" %AND p2#"ASCII" %START
            newline
            printstring(p2." is not a valid SET FILE_TYPE option. Type HELP if you need it.")
            newline
         %FINISH %ELSE file type = p2
      %FINISH %ELSE %IF p1="FILE_PROTECTION" %START
         p2 = ucstring(p2)
         %IF p2#"ON" %AND p2#"OFF" %START
            newline
            printstring(p2." is not a valid SET FILE_PROTECTION option. Type HELP if you need it.")
            newline
         %FINISH %ELSE file prot = p2
      %FINISH %ELSE %IF p1="BINARY_QUOTE" %START
         %IF (33<=charno(p2, 1)<=62 %OR 96<=charno(p2, 1)<=126) %AND charno(p2, 1)#send quote %AND %C
            charno(p2, 1)#receive quote %AND charno(p2, 1)#repeat quote %THEN binary quote = charno(p2, 1) %ELSE %START
            newline
            printstring("The binary quote must be between ASCII 33 and 62 or 96 and 126,")
            newline
            printstring("and it must be distinct from the control character quotes and the repeat quote.")
            newline
            printstring("Type HELP if you need it.")
            newline
         %FINISH
      %FINISH %ELSE %IF p1="DELAY" %THEN delay = pstoi(p2) %ELSE %IF p1="REPEAT_QUOTE" %START
         %IF (33<=charno(p2, 1)<=62 %OR 96<=charno(p2, 1)<=126) %AND charno(p2, 1)#send quote %AND %C
            charno(p2, 1)#receive quote %AND charno(p2, 1)#binary quote %THEN repeat quote = charno(p2, 1) %ELSE %START
            newline
            printstring("The repeat quote must be between ASCII 33 and 62 or 96 and 126,")
            newline
            printstring("and it must be distinct from the control character quotes and the binary quote.")
            newline
            printstring("Type HELP if you need it.")
            newline
         %FINISH
      %FINISH %ELSE %START
         newline
         printstring(p1." is not a valid SET option. Type HELP if you need it.")
         newline
      %FINISH

   %END
![page]
!******************************************************************************
!******************************************************************************
!******             S H O W    R O U T I N E                             ******
!******************************************************************************
!******************************************************************************

! Not a lot to say about this one really. It SHOWs the values of the parameters.

   %ROUTINE show(%STRING (255) p1, p2)

{apm} %routine pints(%string (255) text, %integer i)
{apm}    printstring(text); write(i, -1)
{apm} %end

      newline
      %IF p1="" %THEN p1="ALL"
      p1 = ucstring(p1); p2 = ucstring(p2)

      %IF p1="RECEIVE" %START
         %IF p2="PACKET_LENGTH" %THEN pints("RECEIVE PACKET LENGTH = ",receive max packet) %ELSE %C
         %IF p2="RETRIES" %THEN pints("RECEIVE RETRIES = ",receive retries) %ELSE %C
         %IF p2="START_OF_PACKET" %THEN pints("RECEIVE START OF PACKET = ",receive SOH)
      %FINISH %ELSE %IF p1="SEND" %START
         %IF p2="PADDING" %THEN pints("SEND PADDING = ",send padding) %ELSE %C
         %IF p2="PADCHAR" %THEN pints("SEND PADCHAR = ",send padchar) %ELSE %C
         %IF p2="RETRIES" %THEN pints("SEND RETRIES = ",send retries) %ELSE %C
         %IF p2="END_OF_LINE" %THEN pints("SEND END OF LINE = ",send end of line) %ELSE %C
         %IF p2="QUOTE" %THEN printstring("SEND QUOTE = '".tostring(send quote)."'") %ELSE %C
         %IF p2="START_OF_PACKET" %THEN pints("SEND START OF PACKET = ",send SOH)
      %FINISH %ELSE %IF p1="DELAY" %THEN pints("DELAY = ",delay) %C
      %ELSE %IF p1="PROMPT" %THEN printstring("PROMPT = '".command prompt."'") %C
      %ELSE %IF p1="FILE_TYPE" %THEN printstring("FILE TYPE = ".file type) %C
      %ELSE %IF p1="BINARY_QUOTE" %THEN printstring("BINARY QUOTE = '".tostring(binary quote)."'") %C
      %ELSE %IF p1="REPEAT_QUOTE" %THEN printstring("REPEAT QUOTE = '".tostring(repeat quote)."'") %C
      %ELSE %IF p1="FILE_PROTECTION" %THEN printstring("FILE PROTECTION is ".file prot) %C
      %ELSE %IF p1="ALL" %START
         pints("RECEIVE PACKET LENGTH = ",receive max packet); newline
         pints("RECEIVE RETRIES = ",receive retries); newline
         printstring("RECEIVE QUOTE = '".tostring(receive quote)."'"); newline
         pints("RECEIVE START OF PACKET = ",receive SOH); newline
         pints("SEND PACKET LENGTH = ",send max packet); newline
         pints("SEND PADDING = ",send padding); newline
         pints("SEND PADCHAR = ",send padchar); newline
         pints("SEND RETRIES = ",send retries); newline
         pints("SEND END OF LINE = ",send end of line); newline
         printstring("SEND QUOTE = '".tostring(send quote)."'"); newline
         pints("SEND START OF PACKET = ",send SOH); newline
         pints("DELAY = ",delay); newline
         printstring("PROMPT = '".command prompt."'"); newline
         printstring("FILE TYPE = ".file type); newline
         printstring("BINARY QUOTE = '".tostring(binary quote)."'"); newline
         printstring("REPEAT QUOTE = '".tostring(repeat quote)."'"); newline
         printstring("FILE PROTECTION is ".file prot)
      %FINISH %ELSE printstring(p1." is not a valid SHOW option. Type HELP if you need it.") %AND newline

      newline

   %END

![page]
!******************************************************************************
!******************************************************************************
!******             M A I N    P R O G R A M                             ******
!******************************************************************************
!******************************************************************************

   %ON 9 %START
      !
      ! We have got to the end of the TAKE file:
      !
      command channel = console channel
{apm} select input(takefile channel); close input
      select input(command channel)
!apm! close stream(takefile channel)
      taking = no
      ->command processor
   %FINISH


   ! Set up initial values

!apm!command prompt = "Kermit-EMAS> "
{apm}command prompt = "Kermit-APM> "
   receive max packet = 94
   receive retries = 10
   receive SOH = 1
   send retries = 10
   send quote = '#'
   send SOH = 1
   delay = 10
   file type = "ASCII"
   file prot = "ON"
   taking = no
   pending cr = no
   initpar
   %IF debug=yes %START
      claim channel(debug channel)
{}      open output(debug channel,"KERMDEBUG")
!! debug channel=0; selectoutput(0)
   %FINISH
   claim channel(file channel 1)
   claim channel(file channel 2)
   claim channel(takefile channel)
   claim channel(file list channel)
   claim channel(null channel)
{} define(itos(null channel,-1).",.NULL")
   command channel = console channel
{apm}selectinput(command channel)
{apm}selectoutput(console channel)
   stop = no
   uinfi(35,controlcharints)
   setmode("NOCONTROLCHARINTS")

   command = ucstring(s)

   newline; printstring(version); newline

command processor:

   %CYCLE

      prompt(command prompt)
      %WHILE length(command)=0 %OR charno(command,1)=receive SOH %CYCLE
         newline
         readline(command)
      %REPEAT

      %WHILE charno(command,1)=' ' %CYCLE
         command -> (" ").command
      %REPEAT
      %IF charno(command,1)='!' %THEN command -> ("!").command %AND command = "APM ".command
      command = s1." ".s2 %WHILE command->s1.("  ").s2      { compress multiple spaces }

      p1 = ""; p2 = ""; p3 = ""
      %IF command->command.(" ").p1 %START
         %IF p1->p1.(" ").p2 %START
            %IF p2->p2.(" ").p3 %START; %FINISH
         %FINISH
      %FINISH { split line into command & up to 3 parms }

{apm} strip(p1); strip(p2); strip(p3); strip(command)
!apm! command = s1.s2 %WHILE command->s1.(" ").s2
!apm! p1 = s1.s2 %WHILE p1->s1.(" ").s2
!apm! p2 = s1.s2 %WHILE p2->s1.(" ").s2
!apm! p3 = s1.s2 %WHILE p3->s1.(" ").s2 { remove all spaces from command & parms }

      command = ucstring(command) { translate to upper case }

      %IF taking=yes %START
         %IF charno(command, 1)=';' %THEN command = "" %ELSE %C
         printstring("TAKE> ".command." ".p1." ".p2." ".p3); newline
      %FINISH

      %IF command="SEND" %THEN %START
         prepare for transaction
         ddelay(delay-2)
         final state = switch state(send init,p1.p2.p3)
         tidyup after transaction
      %FINISH %ELSE %C
      %IF command="RECEIVE" %THEN %START
         prepare for transaction
         final state = switch state(rec init,p1.p2.p3)
         tidyup after transaction
      %FINISH %ELSE %C
      %IF command="SERVER" %THEN %START
         newline
         printstring("Type your local escape sequence to return to your local Kermit.")
         newline
         printstring("Leave server mode by typing BYE or FINISH locally.")
         newline
         prepare for transaction
         final state = switch state(rec server idle, "")
         tidyup after transaction
         %IF final state=finish %THEN stop=yes
         %IF final state=logout %THEN call("STOP","")
      %FINISH %ELSE %C
      %IF command="HELP" %THEN help %ELSE %C
      %IF command="SHOW" %THEN show(p1, p2.p3) %ELSE %C
      %IF command="SET" %THEN set(p1, p2, p3) %ELSE %C
      %IF command="APM" %THEN emas(p1, p2, p3) %ELSE %C
      %IF command="TAKE" %START
         %IF taking=yes %START
            newline
            printstring("TAKE not allowed from within a TAKE file")
            newline
         %FINISH %ELSE %IF exist(p1)=no %OR char file(p1)=no %START
            newline
            printstring("Invalid or non-existant TAKE file")
            newline
         %FINISH %ELSE %START
            open input(takefile channel, p1)
            select input(takefile channel)
            command channel = takefile channel
            taking = yes
         %FINISH
      %FINISH %ELSE %C
      %IF command="EXIT" %OR command="QUIT" %OR command="Q" %THEN stop=yes %ELSE %C
      %IF command="" %START
      %FINISH %ELSE %START
         newline
         printstring("Unknown command. Type HELP if you need it.")
         newline
      %FINISH

      command=""

   %REPEAT %UNTIL stop=yes  { command processor loop }

!apm!%IF debug=yes %THEN close stream(debug channel)
{apm}%if debug=yes %then selectoutput(debug channel) %and close output
   %IF controlcharints=1 %THEN setmode("CONTROLCHARINTS")


%END  { of kermit }

%END %OF %FILE
