!VECCE: Screen editing implementation of ECCE for VAX, EMAS and
!       Perkin-Elmer 32-bit machines running MOUSES.
! Files addressed in virtual memory
!      Hamish Dewar  --  February 1981
!      V00: 09/02/81
!      V01: Bantam/hardcopy support added
!
%begin;                                   !MOUSES version,  A.Culloch
%constinteger rt=13, esc=27, tab=9;       !ASCII
%constinteger EOT = 4, ctrl X = 24, DEL = 127, BS = 8, CR = 13, LF = NL

!*System-dependent

%externalroutinespec   Temp library (%string(27) lib)
   Temp library ("ECCE:VM");              !dynamic linking with BYTEINTEGER etc
%externalpredicatespec Exists (%string(31) file)
%externalroutinespec CPU limit (%integer seconds)
   CPU limit (65535)
%externalroutinespec Terminal mode (%integer bits)
   Terminal mode (3)
   !(Binary input/output - no echoing)

%routine newline
   print symbol (CR);  print symbol (LF);  print symbol (-1)
%end
%routine newlines (%integer n)
   newline %and n = n - 1 %while n > 0
%end

%constinteger maxname=127;         !max file name length
%recordformat file(%string(maxname) name, %integer chan,
                    %integer top,lim,vmtop,vmlim,flag)
   %record %format blockfm (%byte %array B(0:511))
   %record %format buffm(%integer from,to,  {block maps these V.addrs}
                         %integer chan,     {paged in from here}
                         %integer block,    {block in paging file}
                         %record(buffm)%name link,   {circular list}
                         %record(blockfm) blk)
   %include "NewSrc:Command.inc"

   !*These procedures in ECCE:VM.EXE

   %external %routine %spec Close Off
   %external %byte %map %spec byte integer (%integer index)
   %external %routine %spec move (%integer length, from, to)
   %string(72) sys mess = "*unknown error*"
   %external %routine %spec connect input (%record(file)%name f)
   %external %integer buffers
      buffers = (Free Space - 3000) // (512 + 5*4)
   %record(buffm) %array buffr(1:buffers)
   %external %record(buf fm) %array %name buffer;  buffer == buffr
   %external %record(buf fm) %name buf
   buf == buffr(buffers)

!!!!!!!!!!!!!!!!!!!  Start of Editor proper !!!!!!!!!!!!!!!!!!!

!   Low                                                            High
!   addresses                                                      addresses
!   _______________________________________________________________________
!  | |      | |            |  |      |/|//////////| |                   |  |
!  | |      |C|urrent line |NL|      |/|//////////| |                 NL|NL|
!  |_|______|_|____________|__|______|_|__________|_|___________________|__|
!   ^        ^              ^         ^            ^                     ^
!   NEWTOP   LBEG           LEND      GAPTOP       GAPLIM                FLIM
!  
!   FP (The file pointer) may point above, below or at the gap (shaded area).
!   When the gap has been moved onto the file pointer (necessary for I/.../,
!   E,G etc), FP equals GAPLIM & characters may be inserted at GAPTOP.
!   (The "move gap onto FP" operation is performed by SPLIT).  Consolidate
!   Moves the gap to a line boundary.

%integer i,j,f,l,sym,default
%external %record(file) in,sec,out
%string(63) parm
parm = command_parameter

%routine edi(%record(file)%name old,sec,new, %integer video,rows,cols)
! Only the TOP and LIM components of the file info records are
!  relevant for editing (except when referencing secondary input)
!  In the EMAS version the original file is not copied into the
!  working space (NEW) prior to entry, so that OLD_LIM is not
!  equal to NEW_LIM: on VAX, OLD_LIM is equal to NEW_LIM (except
!  when just 'showing' in which case NEW_TOP = NEW_LIM = 0).
! The parameter VIDEO defines terminal characteristics:
!    signbit: Can DELETE-LINE & INSERT-LINE
!    4000000: Can reverse/reduce/underline arbitrary character
!          2: VT52-compatible
!          1: Bantam compatible
!   all zero: hardcopy
%constinteger cbound=50;        !command cell array bound
%constinteger tbound=120;       !quoted text array bound
%constinteger stopper=-10000;   !loop stop
%constinteger casebit=32
%integer cmax;                  !command cell max
%integer ci;                    !command index
%integer ti;                    !text index
%integer code;                  !command code
%integer ref;                   !text or bracket pointer
%integer scope;                 !search limit
%integer num;                   !repetition number
%integer sym,sym1,pend
%integer casemask;              !\0(\CASEBIT) to respect(ignore) case
%integer width;                 !line width (default 80)
%integer dir;                   !current direction of movement
%integer sin;                   !0(1) for main(secondary) input
%integer macpos;                !macro index values (3-byte nest)
!File pointers (byte addresses)
%integer lbeg;                  !line start position
%integer fp;                    !current file position
%integer lend;                  !line end position
%integer flim;                  !limit of current infile
%integer gaptop;                !start of gap
%integer gaplim;                !limit of gap
%integer mstart,msize;          !match start/size
%integer sfp,slim;              !saved FP/LIM for switching
%integer newtop,newlim;         !copies of NEW_TOP,NEW_LIM
! Video control variables
%integer line;                  !line number of current line
%integer winline;               !line number of top line on screen
%integer limline;               !limit line number (if known)
%integer breaks,joins;          !count of lines added/removed
%integer vrow,vcol;             !video cursor position
%own %integer zrow = 0
%own %integer mon = 1
%integer fprow
%own %integer print line = 0, printed = 0
%integer wintop, winbot;        !first/last window row
                                !the variable size window extends
                                !from floating WINTOP to fixed WINBOT
%integer winlim;                != WINBOT+1
%integer minwin;                !minimum window size
%integer changemin,changelbeg;  !alteration positions
%integer vp
%integer endon
!
!
%ownstring(9)%array prom(0:3) = ">", "^X  ^I >", "^Y  ^x>", "^Y  ^x>"
%ownstring(3)%array gprom(0:3)= ":", "^A ", "", "";  !*MOUSES*
%byteintegerarray macstore(1:192)
%recordformat commandcell(%byteinteger code,ref, %c
                     %shortinteger scope, %integer count)
%record(commandcell) %array r(1:cbound)
%byteintegerarray text(1:tbound); !text strings
!
! V i d e o   c o n t r o l   r o u t i n e s
!
%routine at(%integer row,col)
! Set cursor to row ROW (0:ROWS-1) and column COL (0:COLS-1)
  %if video # 0 %start
    vrow = row;  vcol = col
    col = cols - 1 %if col >= cols
    print symbol (ESC)
    %if video&2 # 0 %start;  !VT52
      print symbol ('Y');  print symbol (' '+row)
    %finish %else %start;    !Bantam
      print symbol ('X');  print symbol (' '+row)
      print symbol (ESC);  print symbol ('Y')
    %finish
    print symbol (' '+col)
  %finish
%end
!
%routine clear line
! Clear rest of current line
  %if video # 0 %and vcol < cols %start
    print symbol (ESC)
    %if video&2 # 0 %then print symbol ('x') %c
                    %else print symbol ('I') %and print symbol (0)
  %finish
%end
!
%routine vnewline
  vrow = vrow+1;  vcol = 0
  newline
%end
!
%routine insert line(%integer row)
  %if video < 0 %start
    at (row,0) %if vrow # row
    vcol = 0
    print symbol (ESC);  print symbol ('L')
  %finish
%end
!
%routine delete line(%integer row)
  %if video < 0 %start
    at (row,0) %if vrow # row
    vcol = 0
    print symbol (ESC);  print symbol ('M')
  %finish
%end
!
%const %integer clear = 1, inc = 2
!
%routine display line (%integer mode)
%integer k
  %if video<<1 > 0 %and vcol = 0 %start
    print symbol (' ');  vcol = 1
  %finish
  %cycle
    vp = gaplim %if vp = gaptop
    %if vp = flim %start
      %if endon = 0 %start
        print string(" **END**")
        limline = winline + vrow - wintop
      %finish
      endon = 1
      %exit
    %finish
    k = byteinteger(vp);  vp = vp+1
    %exit %if k = nl
    print symbol(k) %if vcol < cols
    vcol = vcol+1
  %repeat
  clear line %if mode&clear # 0
  vnewline %if mode&inc # 0
%end
!
%routine update
! If a change has been made to the file
!  update screen image, but only if current line is on screen
!  CHANGEMIN identifies the earliest point in the file affected
!  by alterations
%integer l,d,j,nop
  %return %if changemin = newlim;          !no change =>
  %return %if video = 0
  joins = joins-breaks;  l = line - winline + wintop - breaks
  %if l >= wintop %and l < winlim %start;  !within window
    vp = changemin;  endon = 0
    changemin = changemin-1 %while changemin # newtop %c
            %and byteinteger(changemin-1) # nl
    %if video < 0 %then at(l,vp-changemin) %c
                  %else at(l,vp-changemin+1)
    nop = 1
    %if changelbeg # lbeg %or vp # changemin %start
      nop = 0
      display line (clear+inc)
      l = l+1
    %finish
    -> done %if l = winlim
    %if joins < 0 %and video < 0 %start;  !net expansion
      j = joins
      %cycle
        %if wintop > zrow %start
          delete line(zrow)
          wintop = wintop-1;  l = l-1
        %finish %else %start
          delete line(winbot)
        %finish
        j = j+1
      %repeat %until j = 0
      %cycle
        insert line(l)
        joins = joins+1
      %repeat %until joins = 0
    %finish
    %while vrow # winlim %c
      %and (newtop<=vp<gaptop %or (vp=gaptop %and nop=0)) %cycle
      display line (clear+inc)
      l = l+1
    %repeat
    %if joins # 0 %start
      d = winlim-l-joins
      %if d <= 0 %or video > 0 %start
        display line (clear+inc) %while vrow # winlim
      %finish %else %start
        j = joins
        %cycle
          delete line(vrow)
          j = j-1
        %repeat %until j = 0
        %cycle
          %cycle
            vp = gaplim %if vp = gaptop
            endon = 1 %and %exit %if vp = flim
            vp = vp + 1
          %repeat %until byteinteger(vp-1) = NL
          d = d-1
        %repeat %until d = 0
        at(winlim-joins,0)
        %cycle
          insert line(vrow)
          display line(clear+inc)
        %repeat %until vrow = winlim
      %finish
    %finish
  %finish
done:
  changemin = newlim;  joins = 0;  breaks = 0
%end
!
%routine display
! Update screen & ensure that current line is on screen
%integer pre,k
!
%routine scanback
  %while pre > 0 %cycle
    vp = gaptop %if vp = gaplim
    %exit %if vp = newtop
    %cycle
      vp = vp-1
      vp = gaptop %if vp = gaplim
    %repeat %until vp = newtop %or byteinteger(vp-1) = nl
    pre = pre-1
  %repeat
%end
!
  update
  vp = lbeg;  endon = 0
  vp = vp-gaplim+gaptop %if vp < gaplim <= fp
  %if video = 0 %start;                    !hardcopy
    vcol = 0;  printline = line;  printed = gaptop+fp
    %if vp # fp %and num = 1 %start
      %cycle
        print symbol (byteinteger(vp))
        vp = vp + 1
        vp = gaplim %if vp = gaptop
      %repeat %until vp = fp
      print symbol ('^')
    %finish
    display line (inc)
    %return
  %finish
  pre = line - winline
  %if pre < 0 %start;                      !before start of window
    %if pre > -minwin %and (video<0 %or wintop+pre>=zrow) %start
                                           !worth prefixing
      wintop = wintop+pre;                 !decrease wintop (PRE neg)
      %while wintop < zrow %cycle
        delete line(winbot)
        insert line(zrow)
        wintop = wintop+1
      %repeat
      %if wintop > zrow %start
        at(wintop-1,0)
        print symbol('=');  clear line
      %finish
      at(wintop,0)
      %cycle
        display line (clear)
        pre = pre+1
        %exit %if pre >= 0
        vnewline
      %repeat
      winline = line
      %return
    %finish
  %finish %else %start
    pre = pre+wintop-winlim
    %return %if pre < 0;                   !within window =>
    %if pre < minwin %start;               !worth appending
      k = pre
      scanback
      at (winlim,0) %if video > 0
      %cycle
        delete line(zrow)
        %if wintop > zrow %then wintop = wintop-1 %c
        %else winline = winline+1
        insert line(winbot)
        display line (clear+inc)
        k = k-1
      %repeat %until k < 0
      NEWLINE %IF VIDEO > 0;               !*MOUSES*/*VAX*
      %return
    %finish
  %finish
  pre = minwin-1
  pre = pre//2 %if fp # flim
  winline = line-pre
  scanback
  winline = winline+pre
  wintop = winlim-minwin
  %if wintop > zrow %start
    at(wintop-1,0)
    print symbol('=');  clear line
    vnewline
  %finish %else %start
    at(zrow,0)
  %finish
  %cycle
    display line (clear)
    %exit %if vrow >= winbot
    vnewline
  %repeat
%end
!
! F i l e   m o d i f y i n g   r o u t i n e s
!
%routine split
! Create gap in file if gap is not at FP
%integer j,diff,unit
  %if newtop <= fp < gaptop %start;        !FP in upper half
!??????
!??????    %if flim # newlim %start;              !Lower half still in old
!??????      j = flim - gaplim;                   !  so must now copy across
!??????      ->abdn %if newlim - j < gaptop
!??????      move (j,gaplim,newlim-j)
!??????      gaplim = newlim - j
!??????      flim = newlim
!??????    %finish
!??????
    j = gaptop-fp;                         !amount to shift down
    diff = gaplim-gaptop
    %cycle;                                !shift in units <= gapsize
      unit = j;  unit = diff %if diff < unit
      gaptop = gaptop-unit;  gaplim = gaplim-unit
      move(unit,gaptop,gaplim)
      j = j-unit
    %repeat %until j = 0
    mstart = gaplim %if mstart = fp
    lbeg = lbeg+diff;  lend = lend+diff
    fp = gaplim
  %finish %else %start
    j = fp-gaplim
    %if j > 0 %start
      ->abdn %if gaptop+j >= newlim
      move(j,gaplim,gaptop)
    %finish
    gaptop = gaptop+j;  gaplim = fp
  %finish
  %if gaptop < changemin %start
    changemin = gaptop;  changelbeg = lbeg
  %finish
  j = gaplim;  j = newlim %if flim # newlim
  %return %if gaptop+256 < j
abdn:
  print string(" ** File too big ")
  %stop
%end
!
%routine consolidate
! Ensure that the gap lies on a line boundary
!  by copying up the remainder of a split line
%integer diff
  %if gaptop # newtop %and byteinteger(gaptop-1) # nl %start
    %cycle
      byteinteger(gaptop) = byteinteger(gaplim)
      %if gaplim = fp %start
        diff = gaplim-gaptop
        lbeg = lbeg-diff;  fp = fp-diff
        lend = lend-diff;  mstart = fp %if mstart = gaplim
      %finish
      gaptop = gaptop+1;  gaplim = gaplim+1
    %repeat %until byteinteger(gaptop-1) = nl
  %finish
%end
!
%routine join
! Erase from FP to end of line AND the line terminator
!  (covers Kill, Join, Uncover)
!  NB LEND not updated
  split
  lbeg = lbeg+(lend-fp+1);  fp = lend+1
  changelbeg = lbeg
  gaplim = fp
  %if line >= winline %then joins = joins+1 %c
  %else winline = winline-1
  limline = limline-1
%end
!
%routine switch
! Switch between main and secondary input
!  Create false bottom if switching from main and lower half is in NEW
%integer i
  split
  newlim = gaplim %if flim = newlim;       !create false bottom
  i = sfp;  sfp = fp;  fp = i
  i = slim;  slim = flim;  flim = i
  sin = sin!!1
  lbeg = lbeg-sfp+fp;  gaplim = fp
  newlim = flim %if newlim = gaplim;       !remove false bottom
  lend = fp
  lend = lend+1 %while lend # flim %and byteinteger(lend) # nl
  joins = 999
  limline = 99999
%end
!
! C o m m a n d   i n p u t   routines
!
%integer type,chain,hold,echo
!
%routine print code(%integer k)
! Print command letter (mapping 'minus' values)
  print symbol(k-casebit) %and k='-' %if k >= 'a'
  print symbol(k)
%end
!
%own %string(80) com line = ""
%own %integer com pos = 1
%own %string(15) prompt
!
%routine report bells(%integer N)
   %cycle
      printsymbol(7)
      printsymbol(-1)
      %exit %if n <= 0
      n = n-1
      skipsymbol
   %repeat
%end

%routine read line
! Binary input burdens us with line editing
  %integer sym
  print string (prompt)
  com line = ""
  %cycle
    print symbol (-1);                     !force output
    read symbol (sym);  sym = sym&127
    %if sym = ctrl X %start;               !line cancel
      com line = ""
      print symbol (CR);  clear line;  newline
      print string (prompt);  print symbol (-1)
    %else %if sym = DEL %or sym = BS
      %if com line # "" %start
        length(com line) = length(com line) - 1
        print symbol (BS)
        print symbol (' ')
        print symbol (BS);                 !erase char on screen
      %finish
    %else %if sym = TAB
      print symbol (' ');  print symbol (' ');  print symbol (' ')
      com line = com line . "   "
    %else
      %if sym = CR %start
        print symbol (CR);  sym = NL
        print symbol (LF)
      %else %if sym = NL
        report bells(0) %and %continue %if Com Line # ""
        sym = CR
      %else %if sym = ESC
        !no echo
        report bells(1) %and %continue %if Com Line # ""
      %else %if sym < ' '
        %continue
      %finish %else print symbol (sym)
      com line = com line . to string (sym)
      %exit %if sym < ' '
    %finish
  %repeat
  print symbol (-1)
  com pos = 1
%end
!
%routine read sym
! Read next command symbol to SYM (maybe from macro def)
  %if pend # 0 %start
    sym = pend;  pend = 0
  %finish %else %start
    %while macpos # 0 %cycle;              !expanding macro
      sym = macstore(macpos&255)
      macpos = macpos+1
      %return %unless sym = nl;            !(NL is definition terminator)
      macpos = macpos>>8
    %repeat
    read line %if com pos > length(com line)
    sym = char no (com line,com pos)
    com pos = com pos + 1
    print symbol (sym) %if echo # 0 %and sym >= ' '
  %finish
%end
!
%routine ignore
  echo = 0
  read sym %while sym # nl
%end
!
!Symbol types:-
!   0:numeric  1:terminator 2:illegal  3:quote
!   4:DU       5:IS         6:E        7:KJBGC@$
!   8:F        9:TV        10:M       11:LR
!  12:( [     13:,         14:) ]     15:? \
!High order bits relevant following percent
%constbyteintegerarray symtype(32:95) = %c
   16_03,16_03,16_03,16_03,16_07,16_02,16_03,16_03,
   16_0C,16_0E,16_00,16_03,16_0D,16_02,16_03,16_03,
   16_00,16_00,16_00,16_00,16_00,16_00,16_00,16_00,
   16_00,16_00,16_03,16_01,16_02,16_02,16_02,16_0F,
   16_07,16_87,16_07,16_17,16_54,16_06,16_08,16_07,
   16_02,16_05,16_07,16_07,16_6B,16_4A,16_02,16_02,
   16_0B,16_02,16_0B,16_25,16_09,16_34,16_49,16_70,
   16_70,16_70,16_70,16_0C,16_0F,16_0E,16_03,16_03
!     sp    !     "     #     $     %     &     '
!     (     )     *     +     ,     -     .     /
!     0     1     2     3     4     5     6     7
!     8     9     :     sc    <     =     >     ?
!     @     A     B     C     D     E     F     G
!     H     I     J     K     L     M     N     O
!     P     Q     R     S     T     U     V     W
!     X     Y     Z     (     \     )     ^     _
!
%routine read item
! Read symbol to SYM and codify in TYPE
! intercept macro letters ('W', 'X', 'Y', 'Z')
! assemble numeric items in NUM
  %cycle
    type = 1;                              !for terminator
    read sym %until sym # ' ';             !ignore spaces
    %return %if sym < ' ';                 !terminator (any control) =>
    sym = sym&95 %if sym >= 96;            !ensure upper case (and more)
    type = symtype(sym)&15
    %return %if type # 0;                  !not numeric or macro =>
    %exit %if sym < 'W'
    %if macpos>>24 # 0 %then macpos = 0 %c
    %else macpos = (macpos<<2+(sym-'W'))<<6+1;   !push down any existing calls
  %repeat
  num = 0
  %return %if sym = '*'
  %cycle
    num = num*10+sym-'0'
    read sym
  %repeat %until %not '0' <= sym <= '9'
  pend = sym
%end
!
%routine minus
! Test for minus: map CODE if so
  %if sym = '-' %start
    code = code+casebit;                   !lower-case
    read item
  %finish
%end
!
%routine unchain
! Insert forward references in left bracket and comma cells
  %cycle
    ref = chain
    %return %if ref = 0
    chain = r(ref)_ref
    r(ref)_ref = ci
  %repeat %until r(ref)_code = '['
%end
!
!
%const %byte %array immed('A':'D') =
   'm',  'M',  'R',  'L'
   !up   down  right left
   !escA escB  escC  escD  (fn keys on input)
%switch c(4:15), pc(0:15), s('$':'p')
%integer i,j,p,fp1
%shortinteger forced
!
! I n i t i a l i s a t i o n
!
  mstart = 0;  cmax = 0
  r(cbound-1) = 0;                         !'immediate-action' cell
  r(cbound-1)_count = 1
  r(cbound) = 0
  r(cbound)_code = ']';                    !terminating cell for i-a
  r(cbound)_count = stopper+1
  macpos = 0
  macstore(1) = nl;  macstore(65) = nl
  macstore(129) = nl;  macstore(192) = nl
  casemask = \casebit;                     !ignore case distinctions (default)
  width = 80;  dir = 0
  newtop = new_top;  gaptop = newtop
  newlim = new_lim
  fp = old_top
  gaplim = fp;  lbeg = fp;  lend = fp
  flim = old_lim
  lend = lend+1 %while lend # flim %and byteinteger(lend) # nl
  sin = 0;  sfp = sec_top;  slim = sec_lim
  changemin = newlim;  changelbeg = 0
  joins = 0;  breaks = 0
  line = 1;  winline = 0;  limline = 999999
  WINLIM = ROWS-2;  !VAX/MOUSES
  winbot = winlim-1;  wintop = winbot
  minwin = 3
  vrow = 99
  newlines (rows-winlim-1) %if video # 0
  %for i = 0,1,3 %cycle
    %for j = 1,1,length(prom(i)) %cycle
      charno(prom(i),j) = esc %if charno(prom(i),j) = '^'
    %repeat
    charno(prom(i),3) = ' '+winlim %if i > 0
    CHARNO(GPROM(I),1) = ESC %IF I > 0;  !VAX/MOUSES
  %repeat
  charno(prom(1),4) = rt;  charno(prom(1),7) = rt
  %IF NEW_LIM > OLD_LIM %START;          !MOUSES
    GAPTOP = OLD_LIM;  GAPLIM = NEW_LIM
    FLIM = GAPLIM
    BYTEINTEGER(GAPLIM) = NL
    FP = GAPLIM %IF FP = GAPTOP
    LBEG = FP;  LEND = FP
  %FINISH
  LEND = LEND+1 %WHILE LEND # FLIM %AND BYTEINTEGER(LEND) # NL
  sym = nl
!
! R e a d   n e w   c o m m a n d   l i n e
!
read:
  %if sin = 0 %then prompt = prom(video&3) %c
  %else prompt = prom(video&3).">"
  pend = 0;  sym1 = sym
  r(cbound-1)_count = 1
  %if sym = nl %start
    %if video = 0 %start
      num = 1
      display %if (mon=0 %and printline#line) %c
          %or (mon>0 %and printed#gaptop+fp)
    %finish %else %start
      display
      ! show position of pointer
      fprow = line-winline+wintop
      at(fprow,fp-lbeg)
      %if video > 0 %start
        print symbol ('^')
        sym1 = ' '
        %if fp # lbeg %start
          %if fp # gaplim %then sym1 = byteinteger(fp-1) %c
          %else sym1 = byteinteger(gaptop-1)
        %finish
      %finish %else %start
        print symbol(esc);  print symbol('4');  !reduced intensity
        sym1 = ' ';  sym = '|'
        %if fp # lend %and byteinteger(fp) # ' ' %start
          sym1 = byteinteger(fp);  sym = sym1
        %finish
        print symbol(sym)
        print symbol(esc);  print symbol('3');  !normal intensity
      %finish
      NEWLINE;    !VAX to flush
    %finish
  %finish %else %start
    read sym
    pend = sym %if sym # nl;               !NL not significant
  %finish
! Deal with special cases
  ci = 0;  ti = 0;  chain = 0
  echo = 0
  %cycle
    read item
    %exit %if sym # '-'
    dir = dir!!casebit;                    !toggle direction
  %repeat
  %if type = 1 %and sym = ESC %start;      !fn key input
    read symbol (sym)
    r(cbound-1)_code = immed(sym) %if 'A' <= sym <= 'D'
    %if sym = 'P' %start
      r(cbound-1)_code = 'W';  r(cbound-1)_count = 0
    %finish
    ci = cbound - 2
    sym = NL;  !force display later
    ->restore
  %finish
  %if type = 1 %start
    ->read %if cmax = 0 %or sym # CR
    type = 0;  num = 1
    r(cmax)_count = num
    sym = NL;  ->restore
  %finish;                                 !LF->CR (in READLINE) -> "1" (again)
  %if type = 0 %and cmax # 0 %start;       !repetition
    r(cmax)_count = num
    read item;  -> er1 %if type # 1
    -> restore
  %finish
  %if sym = '%' %start
    read sym;  -> erq %if sym < 'A'
    code = sym&95;                         !upper-case
    read sym
    -> pc(symtype(code)>>4&15)
  %finish
  dir = 0
  %if video > 0 %start
    clear line;  print symbol('=')
    print symbol(sym)
    echo = 1
  %finish
! C o m m a n d   i n p u t:  m a i n   l o o p
more:                                      !(command code has been read)
  hold = type;  -> er2 %if hold < 4
  -> er0 %if hold < 8 %and newtop = 0;     !'change' command when Showing
  ci = ci+1;  -> er6 %if ci >= cbound-3
  code = sym;                              !command letter
  ref = 0;  num = 1;  scope = 1;           !default values
  read item
  -> c(hold)
c(8):                                      !Find
  minus
  scope = 0
c(4):                                      !+ Delete, Uncover
c(9):                                      !+ Traverse, Verify
  %if type = 0 %start;                     !followed by number
    scope = num
    read item
  %finish
  num = 0;                                 !as indicator (not I,S)
c(5):                                      !+ Insert, Substitute
  hold = sym;  -> er4 %if type # 3;        !not valid quote mark ->
  ti = ti+1;  ref = ti;                    !REF indexes first quoted symbol
  %cycle
    read sym
    %if sym = nl %start;                   !closing quote omitted
      -> er4 %if num = 0;                  !allowed only for I,S
      pend = sym;  sym = hold
    %finish
    %exit %if sym = hold
    -> er6 %if ti > tbound-2
    sym = sym&casemask %if 'a' <= sym <= 'z' %and num = 0
    text(ti) = sym;  ti = ti+1
  %repeat
  -> er4 %if ti = ref %and num = 0;        !null allowed only for I,S
  text(ti) = 0;                            !end-marker
  num = 1;                                 !restore default
  read item
  -> nput
c(6):                                      !Erase
c(10):                                     !+ Move, Print
  minus
c(7):                                      !+ Get, Kill, etc
c(11):                                     !+ Left, Right, etc
  -> er1 %if type = 3;                     !(redundant, but better report)
  -> nput
c(12):                                     !open bracket
  code = '['
  -> comma
c(13):                                     !comma
  code = '^'
! read item %if type = 1;                  !permit line break (better not?)
comma:
  ref = chain;  chain = ci
  -> put
c(14):                                     !close bracket
  unchain;  -> er3 %if ref = 0
  code = ']';  r(ref)_count = num
nput:
  read item %if type = 0;                  !get next item if number
c(15):                                     !invert, query
put:
  r(ci)_code = code;  r(ci)_ref = ref
  r(ci)_scope = scope;  r(ci)_count = num
  -> more %unless type = 1
  ci = ci+1;  cmax = ci
  unchain;  -> er3 %if ref # 0
  r(ci)_code = ']'; r(ci)_ref = 0
  r(ci)_count = 1
  ci = 0
  insert line(winlim);                     !preserve command line
restore:
  %if sym1 # nl %start
    at(fprow,fp-lbeg)
    print symbol(sym1)
  %finish
  -> next
!
er0:print symbol(' ');  print code(sym)
    print string(" when showing")
    -> erq
er1:print symbol(' ');  print symbol(code)
er2:code = sym
    -> er5
er3:printstring(" ()")
    -> erq
er4:printstring(" Text for")
pc(0):
er5:print symbol(' ');  print code(code)
    -> erq
er6:printstring(" Size")
erq:print symbol('?')
    %if video # 0 %then clear line %else newline
    cmax = 0 %if ci > 1
    ignore
    -> read
!
! Percent commands
pc(1):                                     !%C: close
  switch %if sin # 0
  new_flag = 1 %if changelbeg # 0
  new_lim = gaptop
  old_top = gaplim;  old_lim = flim
  clear line
  %return
pc(2):                                     !%S: switch inputs
  switch %if sin # 0
  %if sym = '=' %start
    i = 0
    %cycle
      read sym
      %exit %if sym = ';' %or sym = nl
      -> er6 %if i = maxname
      i = i+1;  charno(sec_name,i) = sym
    %repeat
    %if i # 0 %start
      length(sec_name) = i
      ->erq %unless exists (sec_name)
      connect input(sec)
    %finish
  %finish
  sfp = sec_top;  slim = sec_lim
  -> erq %if sec_top = sec_lim
  switch
  -> read
pc(3):                                     !%U: ignore/respect case
  casemask = \casebit
  casemask = \0 %and read sym %if sym = '-'
  -> read
pc(4):                                     !%M (%V)
  %if code = 'M' %start
    mon = 0
    mon = 1 %and read sym %if sym = '+'
    mon = -1 %and read sym %if sym = '-'
  %finish %else %start
    read item
    -> erq %if type # 0
    video = (num&x'C')<<28+num&3
    winline = -99
  %finish
  -> read
pc(5):                                     !%D: define window size
  %if sym = '=' %start
    read item
    -> erq %if type # 0
    -> er6 %unless num <= winlim
    minwin = num
    video = 0 %if num <= 0
  %finish
  winline = -99
  -> read
pc(6):                                     !%L: define line length
  -> erq %if sym # '='
  read item
  -> erq %if type # 0
  -> er6 %unless 5 <= num <= 256
  width = num
  -> read
pc(7):                                     !%W, %X, %Y, %Z
  i = (code-'W')<<6+1;                     !64-byte increments
  %if sym = '=' %start;                    !definition
    %cycle
      read sym
      macstore(i) = sym
      %exit %if sym = nl
      i = i+1 %if i # 255
    %repeat
  %finish %else %start;                    !enquiry
    -> er1 %if sym # nl
    print symbol(code);  print symbol('=')
    %cycle
      sym = macstore(i)
      %exit %if sym = nl
      print symbol(sym)
      i = i+1
    %repeat
    %if video # 0 %then clear line %else newline
  %finish
  -> read
!
pc(8):                                     !%A: abandon edit
  out_name = "";                           !force SHOW
  %return;                                 !back from EDI
!
pc(*): ->er5
!
! C o m m a n d   e x e c u t i o n
!
%integerfn matched
%integer i,j,k
  i = fp-1; j = ref-1
  %cycle
    i = i+1;  j = j+1
    k = byteinteger(i)
    k = k&casemask %if 'a' <= k <= 'z'
  %repeat %until k # text(j)
  %result = 0 %if text(j) # 0
  mstart = fp;  msize = i-fp
  %result = 1
%end
!
! Extract next command
!
next: s('?'):
  ci = ci+1
  code = r(ci)_code;  ref = r(ci)_ref
  num = r(ci)_count;  forced = 0
  -> s(code)
!
! Successful return after Move etc
!   FP has been advanced into next line, new LEND to be determined
lok:
  lend = fp
  %if lend # flim %start
    lend = lend+1 %while byteinteger(lend) # nl
  %finish
! Successful return from execution
ok:
  num = num-1
  -> next %if num = 0
  -> s(code)
fail:
  num = 1
! Failure return
no: s('\'):
  %cycle
    -> next %if num <= 0;                  !indefinite repetition ->
    ci = ci+1
    -> next %if r(ci)_code = '\';          !invert immediately after ->
    -> next %if r(ci)_code = '?';          !query immediately after ->
    %while r(ci)_code # ']' %cycle
      -> next %if r(ci)_code = '^';        !comma ->
      ci = r(ci)_ref %if r(ci)_code = '['
      ci = ci+1
    %repeat
    num = r(ci)_count
  %repeat %until ci >= cmax
  -> read %if num <= 0
!
!E x e c u t i o n   e r r o r
!
  at(winlim+1,40)
  printstring(" Failure: ")
  print code(code)
  %if ref # 0 %start
    print symbol('''')
    %while text(ref) # 0 %cycle
      print symbol(text(ref))
      ref = ref+1
    %repeat
    print symbol('''')
  %finish
  %if video # 0 %then clear line %else newline
  ignore
  -> read

!
!I n d i v i d u a l   c o m m a n d s
!
s('['):                                    !open bracket
  r(ref)_count = num;                      !restore count on ']'
  -> next
!
s(']'):                                    !close bracket
  num = num-1
  %if num # 0 %and num # stopper %start
    r(ci)_count = num;                     !update
    ci = ref;                              !position of '['
  %finish %else %start
    -> read %if ci >= cmax
  %finish
  -> next
!
s('^'):                                    !comma
  ci = ref-1;                              !position of ']' - 1
  -> next
!
s('P'):
  display
  ->ok %if num = 1
s('M'):                                    !Move
  -> no %if fp = flim
  update
  fp = lend+1
  fp = gaplim %if fp = gaptop
  line = line+1
  fp = flim %and line = limline %if num = 0 %and code = 'M'
  lbeg = fp
  -> lok
!
s('m'):                                    !Move back
  -> no %if lbeg = newtop %or sin # 0
  update
  consolidate
  mstart = 0
  %if num = 0 %start;                      !M-*
    lbeg = newtop
    lbeg = gaplim %if lbeg = gaptop
    fp = lbeg
    line = 1;  num = 1
    -> lok
  %finish
  %if lbeg = gaplim %start
    -> no %if gaptop = newtop
    lbeg = gaptop
  %finish
  line = line-1;                           !there is a line there
  lend = lbeg-1
  lbeg = lbeg-1 %until lbeg=gaplim %or lbeg=newtop %or byteinteger(lbeg-1)=nl
  fp = lbeg
  -> ok
!
s('c'):                                    !Left-shift then case flip
s('L'):                                    !Left-shift
left:
  -> no %if fp = lbeg %or sin # 0
  consolidate %if fp = gaplim
  mstart = 0
  fp = fp-1
  forced = 0 %and ->ok %if forced # 0 %or code = 'L'
!
s('C'):                                    !Case-change with right-shift
  -> no %if fp = lend
  %if 'A' <= byteinteger(fp)&95 <= 'Z' %start
    split
    byteinteger(gaptop) = byteinteger(fp)!!casebit
    gaptop = gaptop+1;  gaplim = gaplim+1
  %finish
!
s('R'):                                    !Right-shift
  -> no %if fp = lend
  fp = fp+1
  forced = 1 %and ->left %if code = 'c'
  -> ok
!
s('E'):                                    !Erase
  -> no %if fp = lend
  split
  lbeg = lbeg+1;  fp = fp+1
  gaplim = fp
  -> ok
!
s('e'):                                    !Erase back
  -> no %if fp = lbeg
  split
  lbeg = lbeg+1;  gaptop = gaptop-1
  changemin = gaptop %if gaptop < changemin
  -> ok
!
s('V'):                                    !Verify
  -> no %if fp = lend
  -> no %if (byteinteger(fp)!!text(ref))&casemask # 0; !quick check
  -> no %if matched = 0
  -> next
!
s('D'):                                    !Delete
s('T'):                                    !+ Traverse
  fp1 = fp
  -> find
!
s('F'):                                    !Find
s('U'):                                    !+ Uncover
  fp1 = fp
  fp = fp+1 %if fp = mstart
find:
  scope = r(ci)_scope;                     !number of lines to search
  sym1 = text(ref);                        !first symbol of quoted text
  %while fp # flim %cycle
    %while byteinteger(fp) # nl %cycle
      %if (byteinteger(fp)!!sym1)&casemask = 0 %start
        -> found %if matched # 0
      %finish
      fp = fp+1
    %repeat
    lend = fp
    scope = scope-1
    %exit %if scope = 0
    %if code # 'U' %start
      update
      fp = fp+1
      fp = gaplim %if fp = gaptop
      lbeg = fp
      line = line+1
    %finish %else %start
      fp = fp1;  join
    %finish
    fp1 = fp
  %repeat
  lend = fp;  fp = fp1
  -> no
found:
  %if code = 'D' %start
    split
    lbeg = lbeg+msize;  fp = fp+msize
    gaplim = fp
  %finish
  fp = fp+msize %if code = 'T'
  %if code = 'U' %start
    i = fp-fp1;  fp = fp1
    split
    lbeg = lbeg+i;  fp = fp+i
    gaplim = fp
  %finish
  -> lok
!
s('f'):                                    !Find back
  scope = r(ci)_scope
  update
  consolidate
  %cycle
    %while fp # lbeg %cycle
      fp = fp-1
      -> ok %if matched # 0
    %repeat
    scope = scope-1
    -> no %if scope = 0 %or fp = newtop
    %if fp = gaplim %start
      -> no %if gaptop = newtop
      fp = gaptop
    %finish
    lbeg = fp
    fp = fp-1;  lend = fp
    line = line-1
    lbeg = lbeg-1 %until lbeg=gaplim %or lbeg=newtop %or byteinteger(lbeg-1)=nl
  %repeat
!
s('S'):                                    !Substitute
  -> no %if fp # mstart
  split
  lbeg = lbeg+msize;  fp = fp+msize
  gaplim = fp
!
s('I'):                                    !+Insert
  -> ok %if text(ref) = 0
  -> no %if fp-lbeg > width %or fp = flim
  split
  j = ref
  %cycle
    byteinteger(gaptop) = text(j)
    gaptop = gaptop+1;  j = j+1
  %repeat %until text(j) = 0
  lbeg = lbeg-(j-ref)
  -> ok
!
s('W'):                                    !immediate insert
  split
  display
  at (line-winline+wintop,fp-lbeg)
  print symbol (-1)
  %cycle
    read symbol (sym);  sym = sym&127
    %if sym = ESC %start
      read symbol (Sym);  %continue %unless Sym&127 = 'H'
      print symbol (ESC);  print symbol ('j');  !out of insert mode
      consolidate
      sym = NL
      ->no
    %finish
    %if sym = DEL %or sym = ctrl X %start
      %while gaptop # newtop %and byteinteger(gaptop-1) # NL %cycle
         gaptop = gaptop - 1
         print symbol (BS);  print symbol (ESC);  print symbol ('O')
         print symbol (-1)
         lbeg = lbeg + 1
      %repeat %until sym = DEL
      %continue
    %finish
    %if sym = CR %start
      consolidate
      display
      ->s('B')
    %finish
    %if sym = TAB %start
      %for j = 0,1,2 %cycle
        byte integer (gaptop+j) = ' '
        print symbol (' ')
      %repeat
      print symbol (-1)
      gaptop = gaptop + 3
      %continue
    %finish
    print symbol (ESC);  print symbol ('i');      !into insert mode
    print symbol (sym)
    sym = NL %and print symbol (sym) %if sym = CR
    print symbol (ESC);  print symbol ('j');      !& out again
    print symbol (-1)
    byte integer (gaptop) = sym
    gaptop = gaptop + 1
    lbeg = lbeg - 1 {?}
  %repeat
!
s('G'):                                    !Get (line from terminal)
  -> no %if sin # 0 %and fp # lbeg
  consolidate
  fp = lbeg
  split
  %if video # 0 %start
    %if video < 0 %start
      display
      fprow = line-winline+wintop
      delete line(winlim)
      insert line(fprow)
    %finish %else %start
      byteinteger(gaptop) = nl;  gaptop = gaptop+1
      breaks = breaks+1
      line = line+1;  limline = limline+1
      update
      fp = gaptop-1;  lbeg = fp
      line = line-1
      display
      fprow = line-winline+wintop
      gaptop = gaptop-1
      fp = gaplim;  lbeg = fp
      limline = limline-1
      at(fprow,0)
    %finish
    NEWLINE;   !VAX
  %finish
  prompt = gprom(video&3)
  read sym
  vrow = 999
  %if sym = ':' %start
    read sym %until sym = nl
    %if video # 0 %start
      %if video < 0 %start
        delete line(fprow)
        insert line(winlim)
      %finish %else %start
        changemin = gaptop;  changelbeg = lbeg
        joins = joins+1
      %finish
    %finish
    -> no
  %finish
  line = line+1;  limline = limline+1
  %cycle
    byteinteger(gaptop) = sym;  gaptop = gaptop+1
    %exit %if sym = nl
    read sym
  %repeat
  %if video < 0 %and fprow = winbot %start;  !bring back
    delete line(zrow)
    %if wintop > zrow %then wintop = wintop-1 %c
    %else winline = winline+1
    insert line(winlim)
  %finish
  -> ok
!
s('B'):                                    !Break
  num = 66 %if num = 0 %or num > 66
  split
break:
  byteinteger(gaptop) = nl;  gaptop = gaptop+1
  breaks = breaks+1
  line = line+1;  limline = limline+1
  lbeg = fp
  -> ok
!
s('K'):                                    !Kill
  -> no %if fp = flim
  fp = lbeg
  join
  -> lok
!
s('J'):                                    !Join
  fp = lend
  -> no %if fp = flim %or fp-lbeg > width
  join
  -> lok
!
s('A'):                                    !Adjust
  -> s('M') %if lend = lbeg
  consolidate
  %while lend-lbeg <= width %cycle
    fp = lend+1
    %if fp = gaptop %start
      fp = gaplim;  lbeg = lbeg+(gaplim-gaptop)
    %finish
    lend = fp
    lend = lend+1 %while lend # flim %and byteinteger(lend) # nl
    %if fp = lend %or byteinteger(fp) = ' ' %start
      update
      lbeg = fp;  line = line+1
      ->no
    %finish
    split
    byteinteger(gaptop-1) = ' '
    changemin = gaptop-1 %if changemin >= gaptop
    joins = joins+1;  limline = limline-1
  %repeat
  consolidate
  fp = lbeg+width+1
  %cycle
    fp = fp-1
    -> no %if fp = lbeg
  %repeat %until byteinteger(fp) = ' '
  split
  fp = fp+1
  gaplim = fp
  -> break
!
!
s('@'):                                    !'at' Column NUM
  -> fail %if lend = flim
  i = width-(lend-fp)
  num = i %if i < num
  i = fp-lbeg-num
  -> next %if i = 0
  split
  %cycle
    %if i < 0 %start;                      !left of it
      byteinteger(gaptop) = ' ';  gaptop = gaptop+1
      lbeg = lbeg-1;  i = i+1
    %finish %else %start
      -> fail %if fp = lbeg %or byteinteger(gaptop-1) # ' '
      gaptop = gaptop-1;  lbeg = lbeg+1
      changemin = gaptop %if gaptop < changemin
      i = i-1
    %finish
  %repeat %until i = 0
  -> next
s('$'):
  switch
  -> next
%end;                                      !END OF EDI
!
!
  in = 0;  sec = 0;  out = 0
  l = 0
  IN_NAME  = COMMAND_IN1
  SEC_NAME = COMMAND_IN2
  out_name = in_name;  default = 1
  OUT_NAME = COMMAND_OUT1 %AND DEFAULT = 0 %IF COMMAND_OUT1 # ""
  IN_NAME = "" %IF IN_NAME = "n:"
  OUT_NAME = "" %IF OUT_NAME = "n:"
  %IF OUT_NAME = "" %START
    CONNECT INPUT(IN);                     !WITHOUT EXTRA
    %STOP %IF IN_FLAG # 0
  %FINISH %ELSE %START
    IN_FLAG = 20;                          !EXTRA BLOCKS
    CONNECT INPUT(IN)
    %STOP %IF IN_FLAG # 0
    %IF SEC_NAME # "" %START
      CONNECT INPUT(SEC)
      %STOP %IF SEC_FLAG # 0
    %FINISH
    OUT_TOP = IN_VMTOP;  OUT_LIM = IN_ LIM; !VAX
    in_top = in_lim %if in_name = ""
  %finish
  newline
  %if command_modifier # 0 %then EDI(in,sec,out,0,24,80) %c
  %else edi (in,sec,out,x'C0000002'{vt52},24,80)
  %if (default # 0 %and out_flag = 0) %or out_name = "" %start
    print string("No changes")
    out_top = 0
  %finish
  Terminal mode (0);   !back to normal
  %if out_top # 0 %start
    i = in_lim-in_top;  move(i,in_top,out_lim)
    out_lim = out_lim+i
    CLOSE OFF
  %finish
  %if out_flag # 0 %and out_name # "" %start
    print string (out_name . " complete")
    newline
  %finish
%endofprogram
