!XECCE: Version of ECCE as external procedures
!ECCExx:    Implementation of ECCE for 2900/EMAS, VAX/VMS and APM
!  Revised specification (1981/82) including video support.
!  Hamish Dewar   Edinburgh University Computer Science Department
!
!      V0   (09/02/81): initial test release
!      V1   (04/06/81): VT52/Bantam/hard-copy support
!      V2   (16/11/81): Esprit supported / Overwrite + C-
!      V3   (03/03/82): Overwrite modded + K-
!      V4   (15/12/82): revised macros & block move
!      V5.0 (29/01/83): standard VTI / revised overwrite
!      V6.0 (12/04/83): integration with syntax checking
!      V7.0 (08/04/87): %B,%R,Insert mode, ~, ! added, KR
!                       bug in OVERWRITE corrected
!
!  This single source file covers the three versions.
!  Simulated conditional compilation statements are used for parts
!  which are special to specific versions.  All these versions
!  assume the availability of sufficient memory (virtual or real)
!  to avoid the necessity for manipulating explicitly created
!  temporary files.  In the Emas version the source file (and any
!  secondary files) are mapped directly into virtual memory and
!  a separate area is used for the new file being created; in the
!  VMS version (because of the idiosyncratic record format of files),
!  and the APM version (because of lack of virtual memory at present),
!  the source file is 'read in' to the new file area (and secondary
!  file to its own area).
!  All versions use the EUCSD standard Video Terminal Interface and
!  VM management routines, together with the IMP run-time support
!  library.
!
!  The ASCII character set is assumed, with NL (pre-defined = LF)
!  as the line-break character WITHIN THE TEXT FILE.
!  The Editor expects to receive RETURN (= ASCII RT) and LF distinctively
!  FROM THE KEYBOARD, and at present expects THESE CHARACTERS TO BE
!  INTERCHANGED.
!  The present treatment of the DEL character is interim; the Editor
!  assumes the ad hoc treatment of the VTI package thus:
!  (a) DELs which can validly delete printing characters which have
!      just been typed do remove those characters from the input stream
!  (b) Initial and trailing DELs which may have erased surrounding
!      text are passed through.
!
!  One of the objectives in the design of the video facilities was
!  to avoid having to pre-suppose single-character interaction on
!  sequences of printing characters.  There are a few cases where
!  there would be a small ergonomic gain from exploiting this mode
!  of operation on a system where it is unproblematic, but it
!  would be a pity to lose compatibility on that score.
!  The Editor does pre-suppose termination of input on any control
!  character or control sequence without echoing; it might be possible
!  to make a special case of some or all of the cursor controls
!  where the performance implications of interaction even on every
!  control key is problematic.
!
!
!
!
!
!
!
!
!
!
!
include  "VTINC"
include  "RECORDS"
include  "MACINC"
include  "SYSROUTS"

externalroutine  EDI(record (edfile)name  main,sec,
                     string (255) message)
!  In the Vax version the original file is copied into the
!  working space prior to entry; in the EMAS version
!  it is accessed (initially) in its original mapped site.
!
constinteger  RET=10
constinteger  CASEBIT=32;          !upper<->lower
record (opt fm) O;                 !Local copy of editor options
constinteger  STOPPER=-10000;      !loop stop
constinteger  MINGAP=4096;         !room for manoeuvre
!Own variables (plus MACROS):-
integer  TOGGLE  
integer  CASEMASK;                 !\casebit/\0 to ignore/heed case
integer  DICT  
integer  TERM;                     !last symbol read
integer  SYM;                      !last symbol got
!
integer  CODE;                     !command code
integer  PCFLAG;                   !set while % command in execution. for logfile
integer  LAST    
integer  REF;                      !text or bracket pointer
integer  SCOPE;                    !search limit
integer  NUM;                      !repetition number
integer  CONTROL,PEND;             !characters
integer  HOLD,HOLDSYM,QSYM;        !work variables
integer  HOLD1
integer  ERROR
integer  COMMANDSTREAM;            !0[1] for terminal[file]
integer  SIN;                      !-1: destroying
                                   ! 0: main file (editing)
                                   ! 1:     "     (showing)
                                   ! 2: sec file  (from 0)
                                   ! 3:     "     (from 1)
!
integer  FP;                       !current file position
integer  FP1;                      !temporary FP
integer  LEND;                     !line end position
integer  OLDLIM1,OLDSTART2
integer  GAPLINE
integer  NEWLIM;                   !effective limit of new file
                                   !also = start of deletion store
integer  DELMAX,LASTDELMAX;        !current end of deletions
integer  CONSOLIDATED
integer  NEG
integer  KEEPLOG;                  ! -1 for log = journal, +1 for file
owninteger      LOGSTREAM=0
integer  T;                        ! Current tab
integer  GDIFF
integer  FOUNDPOS,FOUNDSIZE; !matched text info
integer  MARKPOS,MARKLINE;      !marker positions
record (edfile)name  CUR
!
! Video control
integer  VIDEO
integer  SMODE
integer  FSCROLL, CSCROLL
integer  CHALF
const  integer  VGAP = 0
integer  PAN
constinteger  UNKNOWN=-99999;      !impossible value for _DIFF
constinteger  OFFSCREEN=255;       !impossible value for _WIN
integer  JOINS;               !count of lines added/removed
integer  ENDON;              !**END** displayed indic
!The following assumes that (relevant) addresses are positive
constinteger  FLOOR=0;             !** LESS THAN ANY VALID ADDRESS **
constinteger  CEILING=16_7FFFFFFF
integer  ALTMIN,ALTLIM; !pos of earliest/latest alteration
integer  ALTLINE;                  !for ALTMIN
integer  ALTLIMLBEG;               !for ALTLIM
integer  VP;                       !file pointer for displaying
integer  VPLIM;                    !pointer to end of alterations for displaying
integer  INSERTLEN;                !Length of buffer text - insert mode only
integer  INSERTDIF;                !Change in insertlen from previous call
integer  PRINTLINE,PRINTED;        !for hard-copy
!
string (15) NEWPROM,CURPROM
!
string (maxname) COMMAND ;        ! Passed to Operating system for execution
integer  DICTPOS
integer  MAC0,MACM4,MACBASE
constinteger  MSTBOUND=7
integerarray  MSTACK(0:mstbound)
integer  MSP;                 !macro stack pointer
!
!Cell format for storage of commands
recordformat  COMMANDCELL(byteinteger  code,ref,
                          shortinteger  scope, integer  count)
constinteger  CBOUND=200
record (commandcell) array  R(1:cbound)
integer  CI,CMAX,CMAX1;           !indexing R
!
switch  C(4:15), PC('A':95), S(' ':127)
integer  TYPE,CHAIN
record (commandcell) LASTCELL
!
!!!!!!!!!!!!!  Key definition map and macros  !!!!!!!!!!!!!!!!!
! The Video Terminal Interface converts multi-character
! control sequences to character values in the range 128:255.
! For 2-char sequences, the value is 2nd char + 128.
! For 3-char sequences, the value is 3rd char!!96 + 128
! The array DEF records the significance of each symbol,
! as either a basic symbol (<32768) or macro definition.
! Initial entries are a melange of values relevant to specific
! known terminals.
constinteger  POSMASK=16_3FFF, LIMSHIFT=16
constinteger  NULL=' ', NULLREF=' ', TREFBASE='"'+1,
              MACRO=1<<limshift,
              PREDEFLIM=528, PREMACRO=(predeflim+1)<<limshift
!128:159   second 0-31    third 96-127
!160:191   second 32-63   third 64-95
!192:223   second 64-95   third 32-63
!224:255   second 96-127  third 0-31
![entries for ' ' to 'X'-1 by-passed]
external  integer  array  INITDEF(0:255) =
  { Predefinitions for ASCII characters }
  { first the control characters }
  {NUL ^@}' '       , {SOH ^A}'%'+'A'<<8, {STX ^B}'K'       , {EXT ^C}'%'+'A'<<8,
  {EOT ^D}'%'+'A'<<8, {ENQ ^E}' '       , {ACK ^F}' '       , {BEL ^G}' ',
  {BS  ^H}'g'       , {TAB ^I}'N'       , {LF  ^J}'M'       , {VT  ^K}'{',
  {FF  ^L}'>'       , {CR  ^M}'1'       , {SO  ^N}'E'       , {SI  ^O}'I',
  {DLE ^P}'>'       , {DC1 ^Q}' '       , {DC2 ^R}' '       , {DC3 ^S}' ',
  {DC4 ^T}' '       , {NAK ^U}' '       , {SYN ^V}'}'       , {ETB ^W}' ',
  {CAN ^X}'>'       , {EM  ^Y}'%'+'A'<<8, {SUB ^Z}' '       , {ESC ^[}' ',
  {FS  ^\}' '       , {GS  ^]}' '       , {RS  ^^}'}'       , {US  ^_}' ',
  { now the printing characters }
  ' '               , '!'               , '"'               , '#'        ,
  '$'               , '%'               , '&'               , ''''       ,
  '('               , ')'               , '*'               , '+'        ,
  ','               , '-'               , '.'               , '/'        ,
  { numbers }
  '0'               , '1'               , '2'               , '3'        ,
  '4'               , '5'               , '6'               , '7'        ,
  '8'               , '9'               , ':'               ,
  { things }
  ';'               , '<'               , '='               , '>'        ,
  '?'               , '@'               ,
  { upper case letters }
  'A'               , 'B'               , 'C'               , 'D'        ,
  'E'               , 'F'               , 'G'               , 'H'        ,
  'I'               , 'J'               , 'K'               , 'L'        ,
  'M'               , 'N'               , 'O'               , 'P'        ,
  'Q'               , 'R'               , 'S'               , 'T'        ,
  'U'               , 'V'               , 'W'               , ' '        ,
  {Y} 526<<limshift+525                 , {Z} 527<<limshift+526          ,
  { more things }
  '['               , '\'               , ']'               , '^'        ,
  '_'               , '`'               ,
  { lower case letters - mostly the same as upper case }
  'A'               , 'B'               , 'C'               , 'D'        ,
  'E'               , 'F'               , 'G'               , '%'+'H'<<8 ,
  'I'               , 'J'               , 'K'               , 'L'        ,
  'M'               , 'N'               , 'O'               , 'P'        ,
  'Q'               , 'R'               , 'S'               , 'T'        ,
  'U'               , 'V'               , 'W'               , ' '        ,
  ' '               , ' '               ,
  { even more things }
  '{'               , '|'               , '}'               , '~'        ,
  {DEL}  'e'        ,
  { escape followed by a control character }
  { or escape, question mark, lower case letter (on Hazeltine anyway!)}
  {?SP} ' '         , {?a} '%'+'A'<<8   , {?b} '%'+'B'<<8   , {?c} '%'+'C'<<8 ,
  {?d} '<'          , {?e} ' '          , {?f} ' '          , {?g} ' '        ,
  {?h} '%'+'H'<<8   , {?i} ' '          , {?j} ' '          , {?k} '}'        ,
  {?l} '{'          , {?m} 'm'+'0'<<8   , {?n} '%'+'D'<<8   , {?o} ' '        ,
  {?p} 'F'+'!'<<8   , {?q} 'E'+'0'<<8   , {?r} 'S'+'!'<<8   , {?s} '^'        ,
  {?t} 'K'          , {?u} 'E'          , {?v} 520<<limshift+516              ,
  {?w} 'G'+'0'<<8   , {?x} 'I'          , {?y} 516<<limshift+512              ,
  {?z} 'G'+'0'<<8   ,
  {?[} ' '          , {?\} ' '          , {?]} ' '          , {?^} ' '        ,
  {?_} ' '          ,
  { escape, question mark, Upper-case letter (on the Hazeltine anyway!!) }
  {?`} ' '          ,
  {?A} '%'+'E'<<8   , {?B} '%'+'B'<<8   , {?C} ' '          , {?D} ' '        ,
  {?E} ' '          , {?F} ' '          , {?G} ' '          , {?H} ' '        ,
  {?I} ' '          , {?J} ' '          , {?K} '}'          , {?L} '{'        ,
  {?M} '\'          , {?N} ' '          , {?O} ' '          , {?P} ' '        ,
  {?Q} 'o'+'0'<<8   , {?R} 525<<limshift+520                , {?S} ' '        ,
  {?T} ' '          , {?U} ' '          , {?V} ' '          , {?W} ' '        ,
  {?X} ' '          , {?Y} ' '          , {?Z} ' '          ,
  {?[} ' '          , {?\} ' '          , {?]} ' '          , {?^} ' '        ,
  {?_} ' '          ,
  { escape followed by an upper case command character } 
  { ( lower case means add a minus to the command string backwards) }
  {?@} '}'          ,
  {?A} '{'          , {?B} '}'          , {?C} '>'          , {?D} '<'        ,
  {?E} 'G'          , {?F} ' '          , {?G} ' '          , {?H} 'H'        ,
  {?I} ' '          , {?J} '$'          , {?K} 'e'+'0'<<8   , {?L} 'g'        ,
  {?M} 'k'          , {?N} ' '          , {?O} ' '          , {?P} ' '        ,
  {?Q} 'I'          , {?R} 'K'          , {?S} ' '          , {?T} 'E'+'0'<<8 ,
  {?U} ' '          , {?V} ' '          , {?W} 'E'          , {?X} ' '        ,
  {?Y} ' '          , {?Z} ' '          ,
  {?[} ' '          , {?\} ' '          , {?]} '|'          , {?^} ' '        ,
  {?_} ' '          , {?`} ' '          ,
  { escape followed by a lower case command character }
  {?a} '%'+'A'<<8   , {?b} '%'+'B'<<8   , {?c} '%'+'C'<<8   , {?d} '%'+'D'<<8 ,
  {?e} '%'+'E'<<8   , {?f} 'S'+'"'<<8   , {?g} 'G'+'0'<<8   , {?h} '%'+'H'<<8 ,
  {?i} 'i'+'0'<<8   , {?j} 'J'          , {?k} '}'          , {?l} '{'        ,
  {?m} 'M'          , {?n} 'N'          , {?o} 'O'          , {?p} 'F'+'"'<<8 ,
  {?q} 'Q'+'0'<<8   , {?r} ' '          , {?s} ' '          , {?t} 'T'+'!'<<8 ,
  {?u} 'U'+'!'<<8   , {?v} ' '          , {?w} ' '          , {?x} ' '        ,
  {?y} ' '          , {?z} 'n'          , {? l-curly} ' '   , {?|} ' '        ,
  {? r-curly} ' '   , {?~} ' '          , {?DEL} ' '
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! The storage of the macros is in 2 'arrays'
! %own %byte %integer %array MAC(0:macbound)
! %own %integer%array DEF(0:255)
! To allow EMAS to map these into the editor file, they are accessed by
! the %map's which must be in the externs file.  The map DEF must initially
! have the values in INITDEF.
!Indexing MAC:
! The initial part of the array MAC is reserved for
!  a pool of 4 128-byte buffers used to hold
!  new input, command text, match text, insert text
integer  INPOS,INLIM
integer  NEWDEF,CDEF,IDEF,MDEF
integer  DELS,INITDELS,REPAIRCH
integer  MPOS,MLIM
integer  TREFLIM,TREFLIM1
integer  EFLAG
string (255) MESS

routine  spec  CAT(integer  row,col)
 
  switch  eventno(0:15)
  on  event  9,10,13,14 start ;             !End-of-input, Too big
    ! traps events signalled in program - 
    ! print out system message of EVENT_EXTRA on event 9
    ! print out text in EVENT_MESSAGE on event 10
    ! Jump to %C on event 13 (signalled by READ TEXT for end of input from main in)
    ! No output on event 14
    curprom = ""
    -> eventno(event_event)
eventno(9):
      cat(1,0); print string(sysmess(event_extra)); new line
      -> ignore
eventno(10):
      cat(1,0); print string(event_message); new line
      -> ignore
eventno(13):                                         ! End of file from batch or file
      -> pc('C')
eventno(14):
eventno(*):
     -> ignore
  finish 

  -> edistart
!!!!!!!!!  Simple (command) stream opening and closing  !!!!!!!!!!!
!
const  integer  maxstream = 15
routine  OPEN IN(string (maxname) file)
  integer  newstream
  on  event  3,4,9 start 
    select input(commandstream)
    signal  9,event_sub
  finish 
  newstream = commandstream + 1
  event_message = "Command files nested too deeply" and  signal  10,2 if  newstream > maxstream
  open input(newstream,file);  select input(newstream)
  commandstream = newstream
end 
routine  OPEN OUT(string (maxname) file)
  on  event  3,4,9 start 
    select output(0)
    signal  9,event_sub
  finish 
  open output(logstream+1,file);  select output(logstream+1)
end 
routine  CLOSE IN
  commandstream = commandstream - 1 if  commandstream > 0
  close input;  select input(commandstream)
end 
routine  CLOSE OUT
  close output;  select output(0)
end 
!
!!!!!!!!!!!!!!  General-purpose output routines  !!!!!!!!!!!!!!!!!!!
!
string (31)fn  ITOS(integer  i)
  string (31) s
  integer  sign,j
  s = ""; sign = i; i = -i if  i < 0
  while  i # 0 cycle 
     j = i//10
     s = tostring(i-10*j+'0').s
     i = j
  repeat 
  s = "0" if  s = ""
  s = "-".s if  sign < 0
  result  = s
end 

routine  PRINT CODE(integer  k)
! Print command letter (mapping 'minus' values)
  print symbol(k-casebit) and  k='-' if  'a' <= k <= 'w'
  print symbol(k)
end 
!
routine  AT(integer  row,col);  !file window
  col = 0 if  col < 0
  if  win_top # o_wtop start 
    swop window
  finish 
  vt at(row,col)
end 
routine  CAT(integer  row,col);  !command window
  if  win_top # o_ctop start 
    swop window
  finish 
  vt at(row,col)
end 
!
routine  COMPLAIN(string (255) text)
  cat(1,chalf);  print string(text);  newline
  error = 1
  signal  14
end 
!
routine  GASP
  complain("* Insertions too big")
end 

integerfn  DEF1(integer  k)
  k = def(k)
  result  = k if  k < macro
  result  = mac(k&posmask)-128
end 

!!!!!!!!!!!!!!!!!!!!  Macro management  !!!!!!!!!!!!!!!!!!!!!!!!!!
!
routine  MACPUSH(integer  newdef)
  if  newdef >= macro start 
    complain("* Too many macro levels") if  msp > mstbound
    mstack(msp) = inlim<<limshift+inpos
    msp = msp+1
    inpos = newdef&posmask;  inlim = newdef>>limshift
  finish 
end 
!
routine  RELEASE(integer  k)
integer  i
  i = def(k)
  if  i >= premacro start 
    i = i&posmask+macm4
    if  integer(i) >= 0 then  monitor  else  integer(i) = -integer(i)
  finish 
  def(k) = ' '
end 
!
integerfn  MACSPACE(integer  needed)
integer  p,q
  needed = (needed+7)&(\3);  !add 4 & align
  p = macbase
  cycle 
    q = integer(p)
    complain("* Macros too long *") if  q = 0
    if  q < 0 start ;  !chunk in use
      p = p-q;         !skip over
    else 
      q = q+integer(p+q) while  integer(p+q) > 0;  !consolidate
      exit  if  q >= needed
      integer(p) = q
      p = p+q
    finish 
  repeat 
  integer(p) = q-needed
  p = p+q-needed
  integer(p) = -needed
  result  = p-macm4
end 

! E d i t o r - s p e c i f i c   v i d e o   r o u t i n e s
!
routine  SET WINDOWS
! Make window parameters consistent and set up sub-windows
! -- called at outset only
const  integer  cordon=0
integer  vrows
  vrows = vdu_rows-cordon;  !effective screen size [temp for Emas]
  o_wrows = vrows-2 if  o_wrows > vrows-2;  !must have 2 lines for commands
  o_ctop = vrows-2 if  o_ctop > vrows-2
  o_wtop = vrows-1 if  o_wtop >= vrows
  o_wrows = vrows-o_wtop if  o_wrows > vrows-o_wtop
  o_wtop = 0 if  o_wtop = 1 and  o_wtop+o_wrows > vrows-2
  o_wcols = vdu_cols if  o_wcols > vdu_cols
  if  o_wtop-2 < o_ctop < o_wtop+o_wrows start 
    o_ctop = o_wtop+o_wrows;  !try after file window
    o_ctop = o_wtop-2 if  o_ctop+2 > vrows;  !before file window
  finish 
  o_ccols = 40 if  o_ccols < 40
  o_ccols = vdu_cols if  o_ccols > vdu_cols
  chalf = o_ccols>>1
  video = vdu_fun
  fscroll = 0;  cscroll = 0
  if  vdu_fun&anyscroll # 0 start ;  !video can scroll
    if  o_wcols = vdu_cols start ;         !full-length rows
      fscroll = 1
      video = video-256 and  o_wrows = o_wrows+1 if  o_ctop = o_wtop+o_wrows
    finish 
    cscroll = 1 if  o_ccols = vdu_cols
  finish 
  set frame(o_wtop,o_wrows,o_wleft,o_wcols)
  o_wrows = o_wrows-1 if  video < 0;  !restore
  win_mode = noscroll
  push window;                        !save
  set frame(o_ctop,2,o_cleft,o_ccols)
  win_mode = noscroll
  o_mark = 1 if  vdu_fun&intense = 0;  !cannot highlight
  if  o_maxwin >= o_wrows then  o_maxwin = o_wrows c 
  else  sec_min = o_wrows-o_maxwin-1 and  cur_top = sec_min+1
end 
!
routine  COERCE PARAMETERS
!Make (dynamically alterable) parameters consistent
  cur_min = o_wrows if  cur_min > o_wrows
  cur_min = 1 if  cur_min = 0;  !** allow as disable? **
  o_mark = 0 if  video = 0
  o_width = 80 unless  5 <= o_width <= 512
  o_margin = 0 unless  o_margin < o_width
  casemask = \0;  casemask = \casebit if  o_mapcase # 0
  o_dmode = 0 if  video = 0;       ! Disallow INSERT mode in line mode
end 
!
routine  HEADER(integer  r)
  if  video # 0 start 
    at(r,0)
    print string("<<");  newline
  finish 
end 
!
routine  SAVE COMMAND
!scroll down to preserve command
  swop window if  win_top # o_ctop
  scroll(0,1,-1);  curprom = ""
end 
!
!!!!!!!!!!!!!!!!!!!!!!   Misc  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

routine  SET LEND
  lend = fp
  return  if  fp = cur_lim2
  if  lend # cur_lim2 start 
    lend = lend+1 while  byteinteger(lend) # nl
  finish 
end 
!
routine  SET LBEG(integer  name  lbeg, integer  p)
!Establish line start position
  lbeg = p
  cycle 
    if  lbeg = cur_start2 start 
      lbeg = cur_lim1
      while  lbeg # cur_start1 and  byteinteger(lbeg-1) # nl cycle 
        lbeg = lbeg-1
      repeat 
      lbeg = lbeg+(cur_start2-cur_lim1)
      return 
    finish 
    return  if  lbeg = cur_start1 or  byteinteger(lbeg-1) = nl
    lbeg = lbeg-1
  repeat 
end 

integer  fn  COLTAB(integer  col,lbeg)
  ! Returns number of file characters corresponding to screen column COL
  integer  p,t,c,lend
  result  = col if  o_exptabs = 0
  lbeg = lbeg - cur_start2 + cur_lim1 unless  cur_start1 <= lbeg <  cur_lim1 or  c 
                                              cur_start2 <= lbeg <= cur_lim2
  p = lbeg; t = 0; c = 0; lend = 0
  while  c < col cycle 
    lend = p if  lend = 0 and  byteinteger(p) = nl
    if  lend = 0 and  byteinteger(p) = tab and  t < maxtab start 
      t = t + 1 while  t < maxtab and  c >= o_tabs(t)
      if  c < o_tabs(t) then  c = o_tabs(t) else  c = c + 1
    finish  else  c = c + 1
    p = p + 1
    if  p = cur_lim1 start 
      p = cur_start2
      lbeg = lbeg-cur_lim1+cur_start2
    finish 
  repeat 
  result  = p - lbeg
end 
  
integer  fn  TABCOL(integer  vp, lbeg, next)
  ! returns screen column of VP if next=0, next tab after VP if next > 0
  integer  col, p, t, lend
  result  = vp - lbeg if  o_exptabs = 0
  lbeg = lbeg - cur_start2 + cur_lim1 unless  cur_start1 <= lbeg <  cur_lim1 or  c 
                                              cur_start2 <= lbeg <= cur_lim2
  vp = vp + cur_start2 - cur_lim1 if  cur_lim1 <= vp < cur_start2 or  c 
                                      cur_lim2 < cur_lim1 <= vp
  col = 0 ; t = 0; p = lbeg; lend = 0
  while  p # vp cycle 
    lend = p if  lend = 0 and  byteinteger(p) = nl
    if  lend = 0 and  byteinteger(p) = tab and  t < maxtab start 
      t = t + 1 while  col >= o_tabs(t) and  t < maxtab; ! Find next tab
      if  col < o_tabs(t) then  col = o_tabs(t) else  col = col + 1
    finish  else  col = col + 1
    monitor  and  stop  if  col > 10000
    p = p + 1
    p = cur_start2 if  p = cur_lim1
  repeat 
  result  = col if  next = 0
  t = t + 1 while  col >= o_tabs(t) and  t < maxtab; ! Find next tab
  result  = o_tabs(t) if  t < maxtab;    ! Next tab position
  result  = col+1;                            ! No more tabs set
end  
!
!!!!!!!!!!!!!!  S c r e e n   u p d a t i n g   !!!!!!!!!!!!!!!!!
!
routine  DISPLAY LINE
integer  k,p,lbeg, t, t0, col
  if  cur_lbeg <= vp <= fp then  lbeg = cur_lbeg else  start 
    if  vp = cur_start1 or  byteinteger(vp-1) = nl then  lbeg = vp c 
                                                   else  set lbeg(lbeg,vp)
  finish ; ! All this to avoid procedure call overhead to SET LBEG. worth it?
  t = 0; t0 = 0; col = insertlen; col = col + tabcol(vp,lbeg,0) if  vp # lbeg
  p = fp;  p = lend if  fp > lend
  cycle 
    vp = cur_start2 and  lbeg = lbeg+(cur_start2-cur_lim1) if  vp = cur_lim1
    exit  if  vp = endon
    if  vp = p start 
      cur_diff = cur_line-win_row;  !NB external ref
    finish 
    if  vp = vplim start 
      vplim = -1
      return  if  joins = 0 and  lbeg = altlimlbeg
    finish 
    if  vp = cur_lim2 start 
      endon = vp
      print string(" **END**")
      exit 
    finish 
    k = byteinteger(vp)
    if  k = tab and  o_exptabs # 0 start 
      t = t + 1 while  col >= o_tabs(t) and  t < maxtab
      vp = vp + 1 if  col >= o_tabs(t) - 1
      k = ' '
      if  insertdif # 0 start ;  ! Test for ,partial line update in insert mode
        t0 = t0 + 1 while  col-insertdif >= o_tabs(t0) and  t0 < maxtab
        if  t0 = t and  t < maxtab start 
          spaces(-insertdif) if  insertdif < 0
          insertdif = 0;                    ! To prevent return on next cycle
          return 
        finish  else  insertdif = 0
      finish 
    finish  else  vp = vp + 1
    if  k < ' ' or  k >= 127 start 
      exit  if  k = nl
      k = '_'
    finish 
    print symbol(k) if  col >= cur_shift; col = col + 1
  repeat 
  newline
end 
!
routine  REMOVE POINTER
  if  cur_flag >= ' ' start 
    at(cur_row,cur_col)
    print symbol(cur_flag)
    cur_flag = 0
  finish 
end 

routine  REPAIR LINE
  at(cur_line-cur_diff,tabcol(fp,cur_lbeg,0)+o_mark-cur_shift)
  vp = fp; vp = lend if  fp > lend
  display line
end 

routine  REPAIR CHARS(integer  n)
  return  if  n <= 0
  vp = fp; vplim = fp + n
  display line
end 

routine  UPDATE
! If a change has been made to the file, update screen,
!  but only if change has affected screen line(s).
!  ALTMIN and ALTLIM delimit the area which has been affected
!  by alterations
integer  r,c,d
  return  if  altlim = floor;      !no change =>
  if  sin < 0 start 
    fp = lend if  fp > lend
    return  if  cur_start2 = fp and  altmin = ceiling
    if  cur_line > gapline start 
      joins = joins+(cur_line-gapline);  cur_line = gapline
    finish  else  if  cur_line < gapline start 
      joins = joins-(cur_line-gapline);  gapline = cur_line; altline = cur_line
    finish 
    markpos = 0 if  cur_start2 <= markpos < fp
    altlimlbeg = 0;  cur_start2 = fp;  altlim = fp
    set lbeg(cur_lbeg,fp)
  finish 
  cur_change = altmin if  altmin < cur_change
  return  if  video = 0
  cur_diff = unknown if  joins+cur_min <= 0;  !many breaks
  r = altline-cur_diff
  if  r < cur_win start 
    cur_diff = cur_diff-joins
    cur_diff = unknown if  cur_line-cur_diff >= cur_win
  finish  else  if  r < cur_bot start ; !within current window
    swop window if  win_top # o_wtop
    remove pointer if  cur_flag > 0
    altmin = cur_lim1 if  altmin > cur_lim1;  !?[or only SIN<0]
    altlim = cur_start2 if  altlim < cur_start2;  !?
    vp = altmin
    altmin = altmin-1 while  altmin # cur_start1 and  byteinteger(altmin-1) # nl
    c = vp-altmin
    d = 0;  endon = -1
    vplim = altlim
    cycle 
      vp = cur_start2 if  vp = cur_lim1
      if  c+vgap = 0 and  fscroll # 0 and  joins # 0 start 
        if  joins < 0 start ;  !net expansion
          if  cur_win > cur_top start 
            cur_win = cur_win-1;  r = r-1
            cur_diff = cur_diff+1
            scroll(cur_top,r,1)
          else 
            scroll(r,cur_bot-1,-1)
          finish 
          joins = joins+1
        finish  else  if  vplim < 0 c 
                      or  (vp = vplim and  vp = altlimlbeg) start 
          d = cur_bot-r-joins
          if  d > 0 start 
            cycle 
              scroll(r,cur_bot-1,1)
              joins = joins-1
            repeat  until  joins = 0
            cycle ;                         !Scan forward
              cycle 
                vp = cur_start2 if  vp = cur_lim1
                endon = vp and  exit  if  vp = cur_lim2
                vp = vp+1
              repeat  until  byteinteger(vp-1) = nl
              r = r+1;  d = d-1
            repeat  until  d = 0
            while  r < cur_bot cycle 
              at(r,o_mark);  display line;  r = r+1
            repeat 
            exit 
          finish 
        finish 
      finish 
      c = tabcol(c+altmin,altmin,0) if  c > 0
      at(r,c+o_mark-cur_shift);  display line;  c = 0;  r = r+1
    repeat  until  r >= cur_bot or  (vplim < 0 and  joins=0)
  finish 
  joins = 0;  altmin = ceiling
  altlim = floor;  altlim = floor+1 if  sin < 0
end 
!
routine  DISPLAY(integer  indic)
! Update screen & ensure that current line is on screen
integer  r,r1,fullpre,pre,count,standoff
!
routine  SCAN(integer  pre, integer  name  count)
  ! Move vp forward or back PRE lines or to beginning/end of file
  ! and set COUNT to actual number
  count = 0
  while  pre > 0 cycle 
    vp = cur_lim1 if  vp = cur_start2
    return  if  vp = cur_start1
    cycle 
      vp = vp-1
      vp = cur_lim1 if  vp = cur_start2
    repeat  until  vp = cur_start1 or  byteinteger(vp-1) = nl
    count = count + 1;  pre = pre-1
  repeat 
  while  pre < 0 cycle 
    return  if  vp = cur_lim2
    cycle 
      vp = cur_start2 if  vp = cur_lim1
      return  if  vp = cur_lim2
      vp = vp+1
    repeat  until  byteinteger(vp-1) = nl
    pre = pre + 1; count = count - 1
  repeat 
end 

routine  DISPLAY LINES(integer  n)
  cycle 
    at(r,0)
    print symbol(' ') if  o_mark # 0
    display line
    r = r+1;  n = n-1
  repeat  until  n = 0 or  r >= cur_bot
end 

  update;  vplim = -1
  vp = cur_lbeg
  vp = vp-cur_start2+cur_lim1 if  vp < cur_start2 <= fp
  if  video = 0 start 
    printline = cur_line;  printed = cur_lim1+fp
    cycle 
      printstring("**END**") and  exit  if  vp = cur_lim2
      exit  if  byteinteger(vp) = nl
      print symbol(byteinteger(vp))
      vp = vp+1
      vp = cur_start2 if  vp = cur_lim1
      print symbol('^') if  vp = fp and  num = 1
    repeat 
    newline
    return 
  finish 
  swop window if  win_top # o_wtop
  remove pointer if  cur_flag > 0
  endon = -1
  fullpre = cur_min-1
  fullpre = fullpre>>1 if  lend # cur_lim2
  standoff = (cur_bot-cur_top)>>2
  r = cur_line-cur_diff;  pre = r-cur_win
  if  pre-indic*standoff < 0 start ;                   !before start of window
    if  pre-indic*standoff > -cur_min start ;          !not far before
      if  fscroll # 0 or  r >= cur_top start 
! ****** Better Scrolling Algorithm KR 1987 ****
        scan(pre+1,count)
        if  pre < 0 or  count = pre + 1 start 
          count = count - 1 - indic*standoff
          while  count < 0 cycle 
            if  cur_win <= cur_top start 
              scroll(cur_top,cur_bot-1,-1);         ! Scroll down
              r = r  + 1
              cur_diff = cur_diff-1
              at(cur_top,o_mark)
            else                 ;                  ! expand window
              cur_win = cur_win-1
              at(cur_win,0)
              print symbol(' ') if  o_mark # 0;     ! Clear header in MARK mode
            finish 
            display line
            scan(2,hold);                           ! Back vp 2 lines to prev
            exit  if  hold#2;                     ! reached beginning of file
            count = count + 1
          repeat 
          header(cur_win-1) if  cur_win > cur_top
        finish 
! ****
        return 
      finish 
    finish 
  else 
    pre = r-cur_bot
!    %if pre < 0 %start;                 !within window
!      %return %if indic = 0 %or pre # -1 %or lend = cur_lim2
!      vp = lend+1
!    %finish
    return  if  pre+indic*standoff < 0
    if  pre+indic*standoff < cur_min start ;           !not far ahead
      if  fscroll # 0 start 
        scan(pre,count);  
        if  pre >= 0 or  count = pre start 
          count = count + 1 + indic*standoff
          while  count > 0 cycle 
            cur_win = cur_win-1 if  cur_win > cur_top
            scroll(cur_top,cur_bot-1,1)
            cur_diff = cur_diff+1
            at(cur_bot-1,o_mark)
            display line
            exit  if  vp=endon;                        ! Reached end of file
            count = count-1
          repeat 
        finish 
        return 
      finish 
    finish 
  finish 
!Complete refresh (including window init)
  scan(fullpre,count)
  r = cur_bot-cur_min;  !floating window top
  if  r # cur_win start ;  !changed
    if  r < cur_top start ;  !sub-window changed
      if  sin < 2 start ;              !on main sub-window
        cur_top = r
        if  cur_top < sec_bot+1 start 
          sec_bot = 0;  sec_bot = r-1 if  r > 0
          sec_win = offscreen if  sec_bot = 0
        finish 
      else ;             !on sec sub-window
        cur_bot = cur_min
        if  cur_bot+1 > main_top start 
          if  cur_bot < main_bot then  main_top = cur_bot+1 else  main_top = main_bot
          if  main_bot - main_top < main_min start ;  ! Main window < minimum size
            main_min = main_bot - main_top;           ! new minimum is remaining window
            main_min = o_wrows>>1 if  main_min < o_wrows>>1;  ! Reset to reasonable value
          finish 
          main_win = main_top if  main_win < main_top
          main_win = offscreen if  main_bot - main_top < main_min
        finish 
        r = 0
      finish 
      cur_win = offscreen
    finish 
    if  cur_win = offscreen start 
      if  sin < 2 start 
        header(cur_top-1) if  cur_top > 0
      else 
        header(cur_bot) if  cur_bot < main_bot
      finish 
    else 
      cur_win = cur_top if  cur_win < cur_top
      cur_win = cur_win-1 if  cur_win > cur_top
      while  cur_win < r-1 cycle 
        at(cur_win,0);  clear line;  cur_win = cur_win+1
      repeat 
    finish 
    cur_win = r
    r1 = cur_top
    while  r1 < cur_win cycle ;   ! Clear unused screen area
      at(r1,0); clear line
      r1 = r1 + 1
    repeat 
    header(cur_win-1) if  cur_win > cur_top
  finish 
  display lines(0)
end 
!
!!!!!!!!!!!!!!!!!   Command input routines  !!!!!!!!!!!!!!!!!!!!!!!!
!
routine  SHOW POINTER
  integer  col,p
  cur_row = cur_line-cur_diff; p = fp
  col = tabcol(fp,cur_lbeg,0)-cur_shift
! %return %if col < 0 %or col >= win_cols-1
  if  col < 0 start 
    if  cur_start1 <= fp < cur_lim1 and  fp-col > cur_lim1 then  p = p-cur_lim1+cur_start2
    p = p - col
    col = 0
  finish  else  if  col > win_cols-1 start 
    if  fp-col < cur_start2 and  cur_start2 <= fp <= cur_lim2 then  p = p+cur_lim1-cur_start2
    p = p - col + win_cols-1
    col = win_cols - 1
  finish 
  cur_flag = ' ' 
  cur_col <- col
  at(cur_row,cur_col)
  if  o_mark = 0 start 
    cur_flag = byteinteger(fp) if  fp < lend
    set shade(intense)
    if  del > cur_flag > ' ' then  print symbol(cur_flag) c 
    else  print symbol('|')
    set shade(0)
    if  p < lend then  cur_flag = byteinteger (p) else  cur_flag = ' '
 else 
   if  vttype # bantam then  print symbol('~') c 
    else  print symbol(esc) and  print symbol(127); !splodge
    if  col > 0 and  p <= lend start 
      if  p # cur_start2 then  cur_flag = byteinteger(p-1) c 
      else  cur_flag = byteinteger(cur_lim1-1)
    finish 
  finish 
  cur_flag = ' ' if  cur_flag = tab and  o_exptabs#0
  cur_flag = '_' unless  ' ' <= cur_flag < del
end 

routine  PREPARE FOR INPUT
  if  video = 0 start 
    num = 1 and  display(0) if  printed # cur_lim1+fp and  cur_min # 0
  else 
    display(o_early)
    show pointer
  finish 
end ;  !PREPARE FOR INPUT
!
routinespec  SPLIT(integer  gap)
routinespec  CONSOLIDATE(integer  amount,mode)
constinteger  nomac=-2, standard=-1, replacing=0, inserting=1

routine  output logfile(integer  p,q,mode)
  integer  i,sym,data entry,k,flag
  string (255) text
  on  event  9 start 
    keeplog = 0
    event_message = "Error writing log file."
    signal  10,4
  finish 
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!      Sends output either to slected stream or to journal (EMAS only)
!      For other machines a dummy called TOJOURNAL must be provided
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  routine  out symbol(integer  k)
    if  keeplog > 0 then  print symbol(k) else  tojournal(addr(k)+3 ,1); !+3 gives LSB
  end 
  routine  out string(string (255) s)
    if  keeplog > 0 then  print string(s) else  tojournal(addr(s)+1,length(s))
  end 

  routine  out control(integer  sym)
    out symbol('&');  sym = sym+64
    if  sym >= 128 start 
      sym = (sym-64)&127
      out symbol('[');          !ESC
      out symbol('?') and  sym = sym!!96 if  sym < 64
    finish 
    out symbol(sym)
  end 

  integerfn  define free key(string (15) defn)
    integer  k
    k=255
    k = k - 1 until  def(k) = null or  k = 128; ! Find free control seq
    out string("%K"); out control(k);out symbol(':');      ! Define to be
    out string(defn); out symbol(nl)
    result  = k
  end 

  return  if  keeplog = 0
  select output(logstream) if  keeplog > 0

  flag = 0
  if  q-p >= 2 and  mac(p) = '%' start ; ! %K or %Q are special cases
    flag = mac(p+1)&95
    if  flag = 'K' or  flag = 'Q' then  p = p + 2 else  flag = 0
  finish 
  text = "" ; text = text.tostring(mac(i)) for  i = p,1,q-1

  data entry = 0 
  data entry = 1 if  mode >= 0 and  ci = cmax1 {ie command buffer empty}
  if  mode = inserting and  term = del and  q = p start 
    out symbol(nl) if  data entry = 0;              !Insert nothing before erasing back
    out string("& "); out symbol(nl);             ! Special sequence translated by GET CODE as DEL
  finish  else  if  data entry > 0 start ;    ! Data Entry
    ! Handle DATA ENTRY by simulating O! and I! commands issued by control key
   if  def1(term) = 'H' start ;              ! Interpret text as command
     out string(text); out symbol(nl);       ! (see DATA ENTRY)
   else 
     if  q > p start ;                         ! Text to be inserted or replace
       -> exit if  sin&(\1) # 0 or  lend = cur_lim2;! No alterations allowed
       if  mode = inserting then  k = define free key("I") c 
                            else  k = define free key("O")
       out control(k); out symbol(nl)
       out string(text); out symbol(nl)
       out string("%K"); out control(k); out symbol(':'); ! Remove macro definition
       out symbol(nl)
     finish 
     out control(term); out symbol(nl)
   finish 
  finish  else  if  pcflag = 'Q' or  pcflag = 'K' c 
                  or  flag = 'K' or  flag = 'Q' start 
    out string("%K") if  flag = 'K'; out string("%Q") if  flag = 'Q'
    out string(text)
    text = substring(text,2,length(text)) while  length(text) > 0 and  char no(text,1) = ' '
    if  text = "" start 
      if  pcflag = 0 and  term = ret start 
        out symbol(nl)
      else 
        out control(term)
        out symbol(nl) unless  flag = 'K' or  (mode=nomac and  pcflag='K')
      finish 
    else 
      out symbol(term)
      out symbol(nl) unless  term = ret
    finish 
  else 
    out string(text)
      ! VECCE handling of controls as terminators is rather ad hoc. 
    if  ci = cmax1 {Command entry} start 
      ! Allow for null line composed of spaces
      length(text) = length(text) - 1 while  c 
                     length(text) > 0 and  char no(text,length(text)) = ' '
      out control(term) if  text = "" and  pcflag = 0
    finish  else  if  (mode>=0 and  term#ret) {control term of I!,O! or S!} start 
      out symbol(nl)
      out control(term)
    finish  else  if  (mode = standard and  term # ret and  code = 'G') start 
      out symbol(nl) if  text # ""
      if  num # 0 start ;                    ! Control terminator of G
        out symbol(':'); out symbol(nl);         ! Simulate with normal termination
      finish 
      out control(term)
    finish 
    out symbol(nl)
  finish 
exit:
  select output(0) if  keeplog > 0
end 
      
routine  READ TEXT(integer  mode)
!MODE = nomac,standard,replacing,inserting
![most of the business of interfacing to lower-level screen
! input facilities is concentrated here]
integer  p,q,q0,pos,lim,dumbinsert,insertpos,col,j,c
on  event  9 start 
  if  commandstream # 0 start 
    close in
    o_emode = 1 and  eflag = 0 if  eflag # 0
  else ;                          !input 0 EOF
!$IF VAX OR AMDAHL
    signal  13;                   ! Exit and Close edit
!$IF APM
{    open input(0,":T");  select input(0)
{    read symbol(q);  !!***TEMP ignore spurious NL***
!$FINISH
  finish 
  signal  14
finish 
  q = 0
  cycle ;  !find free buffer (there are 4)
    p = q;  q = q+128
  repeat  until  not  (p <= cdef&posmask < q c 
                   or  p <= mdef&posmask < q c 
                   or  p <= idef&posmask < q)
  q = p;  initdels = 0;  dels = 0;  repairch = 0
  mode = standard if  mode >= 0 and  video = 0;     ! Not for hardcopy
  if  mode >= 0 start ;  !data entry
    length(newprom) = 2
    if  sin&(\1) = 0 and  lend # cur_lim2 start 
      if  mode # 0 then  newprom = newprom."INSERTING" c 
      else  newprom = newprom."REPLACING"
    finish 
    if  newprom # curprom start 
      curprom = newprom
      cat(0,0);  printstring(curprom);  clear line
    finish 
  finish 
  dumbinsert = 0;  insertlen = 0
  mode = replacing if  mode = inserting and  sin&(\1) # 0;    !Don't allow delete while showing
  if  mode = inserting start ;            ! Choose and set dumb/clever insert mode
    if  vdu_fun&caninsert#0 start ;       ! Terminal has insert capability
       insertpos = fp
       if  o_exptabs # 0 start ;          ! Use dumb mode if there is a tab
         while  insertpos < lend cycle ;  ! on the rest of the line
           dumbinsert = 1 and  exit  if  byteinteger(insertpos) = tab
           insertpos = insertpos + 1
         repeat 
       finish 
    finish  else  dumbinsert = 1
    ! Appropriate mode is INSERTING for clever terminals,
    !                     SINGLE    for less clever ones
    !                     NODELECHO tells VTLIB to pass all dels without any echoing
    if  dumbinsert = 0 then  set video mode(smode ! insertmode ! nodelecho) c 
                       else  if  fp < lend then  set video mode(smode ! single) c 
                       else  set video mode(smode ! nodelecho)
    insertpos = fp; insertpos = lend if  fp > lend
  finish  else  if  mode = replacing start 
    ! NODELECHO mode so that we can redraw characters overwritten and then deleted.
    ! This is not essential if it is problemetic over the network, variable
    ! DELS will handle refresh in that case.
    set video mode(smode ! nodelecho)
  finish 
  t = 0
  col = tabcol(fp,cur_lbeg,0)+o_mark-cur_shift
again:
  at(cur_line-cur_diff,col) if  mode >= 0
  cycle 
    read symbol(term)
    unless  ' ' <= term <= del+1 or  (commandstream > 0 and  term # ret) start 
      exit  if  mode = nomac
      pos = def(term)
      if  pos < macro start ;  !test for text macro
        exit 
!        %exit %unless pos&128 = 0
!        %cycle
!          term = pos&127;  print symbol(term)
!          mac(q) = term;  q = q+1;  q = q-1 %if q&127 = 0
!          pos = pos>>8
!        %repeat %until pos = 0
      else 
        exit  unless  mac(pos&posmask)&128 = 0;  !not text macro
        lim = pos>>limshift;  pos = pos&posmask
        while  pos < lim cycle 
          term = mac(pos)
          mac(q) = term;  q = q+1;  q = q-1 if  q&127 = 0
          pos = pos+1
          if  o_exptabs # 0 and  term = tab and  mode >= 0 start 
            c = col - o_mark + cur_shift
            t = t + 1 while  t < maxtab and  o_tabs(t) <= c
            insertdif = 0
            cycle 
              c = c + 1; col = col + 1
              print symbol(' ') if  col >= 0
              insertdif = insertdif + 1 and  insertlen = insertlen + 1 if  mode = inserting
            repeat  until  c >= o_tabs(t) 
          else 
            if  term < ' ' then  printsymbol('_') else  print symbol(term)
            col = col + 1
            insertdif = 1 and  insertlen = insertlen + 1 if  mode = inserting
          finish 
        repeat 
        if  dumbinsert # 0 and  insertpos < lend start ;            ! Redraw rest of line after itext macro
          at(cur_line-cur_diff,col)
          vp = insertpos; display line 
          at(cur_line-cur_diff,col)
        finish 
      finish 
    finish  else  if  term = del start 
      if  mode = inserting start  
        if  q > p or  fp # cur_lbeg start 
          col = col - 1; q0 = q; 
          if  insertlen > 0 then  insertlen = insertlen - 1
          insertdif = -1
          if  q = p start ;    ! Delete file text - simulate E-
            fp = lend if  fp > lend
            if  fp # cur_lbeg start ;      ! Don't delete newline
              split(0)
              consolidate(1,-1)
              cur_change = altmin if  altmin < cur_change
              altlim = floor;  altmin = ceiling
            finish 
            insertpos = fp
            output logfile(p,q,mode) if  keeplog # 0;   ! Put E- into LOG
          finish  else  q = q - 1
          if  o_exptabs # 0 and  c 
              ((q0 > p and  mac(q) = tab) or  c 
               (q0 = p and  byteinteger(cur_lim1) = tab)) start 
            c = tabcol(fp,cur_lbeg,0)
            t = 0;  t = t + 1 while  t < maxtab and  o_tabs(t) <= c
            for  j = p,1,q-1 cycle 
              if  mac(j) = tab and  tab < maxtab start 
                c = o_tabs(t); t = t + 1
              finish  else  c = c + 1
            repeat 
            c = c+o_mark-cur_shift
            if  insertlen > 0 then  insertlen = insertlen + c - col
            insertdif = c - col - 1
            col = c
            if  fp < lend start 
              vp = insertpos
              set video mode(smode) if  dumbinsert = 0
              at(cur_line-cur_diff,col); display line
              set video mode(smode!insertmode!nodelecho) if  dumbinsert = 0
            finish 
            at(cur_line-cur_diff,col)
          finish  else  if  dumbinsert # 0 and  insertpos < lend start 
            at(cur_line-cur_diff,col)
            vp = insertpos; display line 
            at(cur_line-cur_diff,col)
          finish  else  if  q0 = p and  fp = lend start 
            col = tabcol(fp+1,cur_lbeg,0)+o_mark-cur_shift
            at(cur_line-cur_diff,col)
            print symbol(del)
          finish  else  print symbol(del)
        finish 
      finish  else  if  mode = replacing start 
        if  q > p start 
          q = q-1
          if  fp+(q-p) < lend start 
            printsymbol(bs)
            printsymbol(byteinteger(fp+(q-p)));  !restore original
            printsymbol(bs)
          finish  else  print symbol(del);  !specially treated by VTI
        finish 
      finish 
    else 
      mac(q) = term;  q = q+1;  q = q-1 if  q&127 = 0
      dels = dels - 1 if  dels > 0
      col = col + 1
      if  dumbinsert # 0 start 
        insertlen = insertlen + 1 if  mode = inserting
        insertdif = 1
        if  insertpos < lend start 
!         at(cur_line-cur_diff,col)
          vp = insertpos; display line 
          at(cur_line-cur_diff,col)
        finish 
      finish 
    finish 
  repeat 

  if  mode >= 0 then  set video mode(smode)

  output logfile(p,q,mode) if  keeplog # 0

  insertlen = 0; insertdif = 0

  newdef = q<<16+p and  return  if  q > p
  newdef = null
  return  if  mode < 0;  !not data entry
  dels = 0 and  initdels = 0 if  fp >= lend
end 
!
routine  READ COMMAND LINE
  read text(standard)
  inpos = newdef&posmask;  inlim = newdef>>16
end 
!
routine  GET SYM
!Extract next command input symbol
!Deal with macro termination
  if  pend # 0 start 
    sym = pend;  pend = 0
  else 
    while  inpos >= inlim cycle 
      sym = ret and  return  if  msp = 0
      msp = msp-1
!     inpos = mstack(msp)&posmask;  inlim = mstack(msp)>>limshift
      inlim = mstack(msp);  inpos = inlim&posmask;  inlim = inlim>>limshift
    repeat 
    sym = mac(inpos)&127;  inpos = inpos+1
  finish 
end 
!
!!!!!!!!!!!!!!!!!!!  Symbol types  !!!!!!!!!!!!!!!!!!!!!!!!!!
! 0-3:non-commands  4-7:alteration group  7-9:location group
!   0:numeric 1:terminator   2:illegal    3:quote
!   4:        5:ABCEJKLR@$   6:ISOG       7:DU
!   8:F       9:TV          10:MNP<>{}   11:( ,
!  12:^      13::           14:)         15:? \ $ =
!High-order bits used to classify chars in file:
constinteger  lowercase=16_10,digit=16_20,uppercase=16_30,
              letter=16_10,upperordigit=16_20,alphanum=16_30,
              opener=16_40,closer=16_80
constbyteintegerarray  SYMTYPE(0:255) = c 
  16_01 (32),
  16_02{ }, 16_03{!}, 16_03{"}, 16_0A{#},
  16_0F{$}, 16_02{%}, 16_03{&}, 16_03{'},
  16_4B{(}, 16_8E{)}, 16_00{*}, 16_0A{+},
  16_0B{,}, 16_02{-}, 16_03{.}, 16_03{/},
  16_20{0}, 16_20{1}, 16_20{2}, 16_20{3},
  16_20{4}, 16_20{5}, 16_20{6}, 16_20{7},
  16_20{8}, 16_20{9}, 16_0D{:}, 16_01{;},
  16_0A{<}, 16_0F{=}, 16_0A{>}, 16_0F{?},
  16_05{@}, 16_35{A}, 16_35{B}, 16_35{C},
  16_37{D}, 16_35{E}, 16_38{F}, 16_36{G},
  16_3A{H}, 16_36{I}, 16_35{J}, 16_35{K},
  16_3A{L}, 16_3A{M}, 16_3A{N}, 16_36{O},
  16_3A{P}, 16_3A{Q}, 16_3A{R}, 16_36{S}, 
  16_39{T}, 16_37{U}, 16_39{V}, 16_32{W},
  16_32{X}, 16_32{Y}, 16_32{Z}, 16_42{[},
  16_0F{\}, 16_82{]}, 16_0C{^}, 16_02{_},
  16_02{`}, 16_12{a}, 16_12{b}, 16_15{c},
  16_17{d}, 16_15{e}, 16_18{f}, 16_15{g},
  16_12{h}, 16_15{i}, 16_12{j}, 16_15{k},
  16_1A{l}, 16_1A{m}, 16_1A{n}, 16_15{o},
  16_12{p}, 16_1A{q}, 16_1A{r}, 16_12{s}, 
  16_17{t}, 16_12{u}, 16_12{v}, 16_12{w},
  16_12{x}, 16_12{y}, 16_12{z}, 16_4A{{},
  16_0F{|}, 16_8A{},  16_0F{~}, 16_02{127},
  16_02 (128)
!
routine  NUMBER
!Test for numeric item
  if  symtype(sym)&15 = 0 start 
    type = 0;  num = 0
    if  sym = '*' then  get sym else  start 
      cycle 
        num = num*10+sym-'0' if  num < 100000
        get sym
      repeat  until  not  '0' <= sym <= '9'
    finish 
  finish 
end 
!
routine  READ MATCH TEXT
  string (7) prom
  prom = tostring(code&(\casebit)); prom = prom."-" if  code&casebit # 0
  prom = prom.">" 
  vt prompt(prom)
  prepare for input
  cat(0,0);  clear line
  curprom = ""
  read text(standard)
  vt prompt("")
  mdef = newdef
  remove pointer if  o_emode # 0;  !in data entry mode
end 
!
routine  READ NUMBER
integer  pos,lim,m
  prepare for input
  cat(0,0);  clear line
  vt prompt(tostring(code).">")
  curprom = ""
  pos = inpos;  lim = inlim;  m = msp
  msp = 0
  read command line
  vt prompt("")
  remove pointer if  o_emode # 0;  !in data entry mode
  pend = 0;  num = 0
  get sym;  number
  inpos = pos;  inlim = lim;  msp = m
end 
!
! F i l e   m a n i p u l a t i o n   r o u t i n e s
!
integerfn  distance(integer  from,to)
  if  cur_start2 <= to <= cur_lim2 start 
    from = from+(cur_start2-cur_lim1) unless  cur_start2 <= from <= cur_lim2
  else 
    to = to+(cur_start2-cur_lim1) if  cur_start2 <= from <= cur_lim2
  finish 
  result  = to-from
end 
!
routine  MOVE BLOCK(integer  length,from,to)
!Move block of file, dealing with overlap & relocation
!The following are relocated: FP, LBEG, LEND, FOUNDPOS, MARKPOS
! NB FP <= LEND
integer  reloc,limit
  reloc = to-from;  limit = from+length
  if  from <= fp < limit start 
    fp = fp+reloc;  cur_lbeg = cur_lbeg+reloc;  !LBEG always relative to FP
  finish 
  lend = lend+reloc if  from <= lend < limit
  foundpos = foundpos+reloc if  from <= foundpos < limit
  markpos = markpos+reloc if  from <= markpos < limit
  while  reloc > 0 and  length > reloc cycle ;  !down and bigger than gap
    length = length-reloc
    move(reloc,from+length,to+length)
  repeat 
  move(length,from,to)
end 

routine  COPY ACROSS
  move block(cur_lim2-oldstart2,oldstart2,oldstart2+gdiff)
  cur_start2 = cur_start2+gdiff;  oldstart2 = oldstart2+gdiff
  if  fp = cur_lim2 start ;  !hence not relocated
    fp = newlim;  cur_lbeg = fp;  lend = fp
  finish 
  cur_lim2 = newlim;  gdiff = 0
end 

routine  MAKE ROOM(integer  mingap)
!The gap has become too small: shuffle to enlarge it
integer  amount,gap
  copy across if  gdiff # 0
  amount = cur_lim-delmax-1;  gap = oldstart2-cur_lim1
  gasp if  amount+gap < mingap
  amount = amount>>1 if  amount>>1+gap >= mingap
  move block(delmax+1-oldstart2,oldstart2,oldstart2+amount)
  oldstart2 = oldstart2+amount;  cur_start2 = cur_start2+amount
  cur_lim2 = cur_lim2+amount;  newlim = newlim+amount
  delmax = delmax+amount;  lastdelmax = lastdelmax+amount
end 
!
routine  STORE DELETIONS
integer  l,k
!Discard part line
  if  cur_start2-consolidated > oldstart2 start 
    delmax = delmax-1 while  byteinteger(delmax) # nl
    lastdelmax = delmax
    cycle 
      l = cur_start2-consolidated-oldstart2
      exit  if  l <= 0
      if  l+delmax >= cur_lim start 
        copy across if  gdiff # 0
        k = oldstart2-cur_lim1;  gasp if  k <= 0
        if  k > 1024 start ;  !a bit much
          if  k > l > 1024 then  k = l else  k = 1024
        finish 
        move block(delmax+1-oldstart2,oldstart2,oldstart2-k)
        cur_start2 = cur_start2-k;  oldstart2 = oldstart2-k
        cur_lim2 = cur_lim2-k;  newlim = newlim-k
        delmax = delmax-k;  lastdelmax = lastdelmax-k
        l = k if  k < l
      finish 
      move(l,oldstart2,delmax+1)
      oldstart2 = oldstart2+l;  delmax = delmax+l
    repeat 
  finish 
  oldstart2 = cur_start2;  consolidated = 0
end 

routine  SPLIT(integer  mingap)
!Create gap ahead of FP
integer  j
  if  fp # cur_start2 start 
    update if  altlim # floor
    store deletions if  oldstart2 < cur_start2
    foundpos = 0 if  foundpos < fp < foundpos+foundsize
    if  cur_start1 <= fp < cur_lim1 start ; !fp in upper half
      copy across if  gdiff # 0
      j = cur_lim1-fp;                     !amount to shift down
      cur_lim1 = cur_lim1-j;  cur_start2 = cur_start2-j
      move block(j,cur_lim1,cur_start2)
    else ;                 !fp in lower half (old or new)
      j = fp-cur_start2
      move block(j,cur_start2,cur_lim1)
      cur_lim1 = cur_lim1+j;  cur_start2 = cur_start2+j
    finish 
    oldstart2 = cur_start2;  oldlim1 = cur_lim1
  finish 
  if  cur_lim1 < altmin start 
    altmin = cur_lim1
    altline = cur_line;  gapline = altline
  finish 
  if  cur_start2 > altlim start 
    altlim = cur_start2;  altlimlbeg = cur_lbeg
  finish 
  if  mingap # 0 start 
    make room(mingap) if  oldstart2+gdiff-cur_lim1 < mingap
  finish 
end 
!
routine  BREAK
!Break line in two (SPLIT already called)
  byteinteger(cur_lim1) = nl;  cur_lim1 = cur_lim1+1
  joins = joins-1
  markline = markline+1 if  markline >= cur_line
  cur_line = cur_line+1;  gapline = gapline+1
  cur_lbeg = fp
  make room(mingap) if  oldstart2+gdiff-cur_lim1 < mingap
end 
!
routine  CONSOLIDATE(integer  amount,mode)
! Make it possible to move or erase FP back over the gap
! (in the former case, ensure that the gap lies on a
! line boundary by copying up the remainder of a split line
! or inserting a newline at end of file)
  return  if  cur_lim1 = cur_start1 or  mode > 0;  !sec in (??)
  if  mode < 0 start ;  !erasing
    cycle 
      markpos = 0 if  cur_lim1-1 = markpos;       ! Erased over marker
      cur_lim1 = cur_lim1-1
      if  cur_lim1 < altmin start 
        altmin = cur_lim1
        if  cur_lim1 < oldlim1 start 
          copy across if  gdiff # 0
          oldlim1 = cur_lim1;  oldstart2 = oldstart2-1
          byteinteger(oldstart2) = byteinteger(oldlim1)
        finish 
      finish 
      cur_lbeg = cur_lbeg+1
      amount = amount-1
    repeat  until  amount <= 0
    return 
  finish 
  if  byteinteger(cur_lim1-1) # nl start ;  !gap in mid-line
    if  cur_start2 # cur_lim2 start ;          !not at end of file
      consolidated = lend+1-cur_start2
      move block(consolidated,cur_start2,cur_lim1)
      cur_lim1 = cur_lim1+consolidated;  cur_start2 = cur_start2+consolidated
      gapline = gapline+1
    else 
      split(mingap)
      break
      amount = 0
    finish 
  finish 
  fp = fp-amount
end 
!
routine  JUMP TO(integer  newfp)
  if  cur_start1 <= newfp < cur_lim1 and  not  cur_start1 <= fp < cur_lim1 start 
    if  sin < 0 start 
      consolidate(cur_lim1-newfp,sin)
    else 
      fp = cur_start2;  cur_lbeg = fp;  set lend
      consolidate(0,0)
      fp = newfp
    finish 
  else 
    fp = newfp
    return  if  cur_lbeg <= fp <= lend
  finish 
  set lbeg(cur_lbeg,fp);  set lend
end 
!
integerfn  LINE AFTER
!Test Move possible and if so perform it
!  update %if altlim # floor
  result  = 0 if  lend = cur_lim2
  lend = lend+1
  lend = cur_start2 if  lend = cur_lim1
  fp = lend;  cur_lbeg = fp
  cur_line = cur_line+1
  if  lend # cur_lim2 start 
    lend = lend+1 while  byteinteger(lend) # nl
  finish 
  result  = 1
end 
!
integerfn  LINE BEFORE
!Set FP to end of previous line if there is one
  update if  altlim # floor
  fp = lend if  fp > lend
  if  cur_lbeg < cur_start2 <= fp then  consolidate(fp-cur_lbeg,sin) c 
  else  fp = cur_lbeg
  result  = 0 if  fp = cur_start1
  if  fp = cur_start2 start 
    result  = 0 if  cur_lim1 = cur_start1
    fp = cur_lim1
  finish 
  cur_line = cur_line-1
  if  sin < 0 start 
    fp = cur_start2;  !restore
    consolidate(1,-1)
    altline = cur_line;  gapline = altline
    joins = joins+1
  else 
    fp = fp-1;  lend = fp
  finish 
  set lbeg(cur_lbeg,fp)
  result  = 1
end 
!
routine  EXTEND LINE
!Append spaces when FP beyond end of line
integer  hold
  hold = fp-lend;  fp = lend
  split(mingap)
  while  hold > 0 cycle 
    byteinteger(cur_lim1) = ' ';  cur_lim1 = cur_lim1+1
    cur_lbeg = cur_lbeg-1;  hold = hold-1
  repeat 
end 
!
routine  INSERT(integer  DEF)
!Insert text specified by DEF
integer  pos,lim
  pos = def&posmask;  lim = def>>limshift
  return  if  pos >= lim
  if  fp > lend start 
    fp = lend if  mac(pos) = nl
    extend line
  finish  else  split(mingap)
  cycle 
    if  mac(pos) = nl then  break else  start  
      byteinteger(cur_lim1) = mac(pos)
      cur_lim1 = cur_lim1+1;  cur_lbeg = cur_lbeg-1
    finish 
    pos = pos+1
  repeat  until  pos = lim
  cur_change = altmin if  altmin < cur_change
end 

routine  OVERWRITE(integer  DEF)
!Overwrite existing text with text specified by DEF
integer  pos,lim, c, s2
  pos = def&posmask;  lim = def>>limshift
  return  if  pos >= lim
  if  fp > lend start 
    fp = lend if  mac(pos) = nl
    extend line
  finish  else  split(mingap)
  s2 = cur_start2;            ! Store it
  cycle 
    if  mac(pos) = nl start 
      make room(mingap) if  oldstart2+gdiff-cur_lim1 <= mingap
!     %while fp < lend %cycle              ;  ! This code is wrong
!       byteinteger(cur_lim1) = mac(fp)    ;  ! mac(fp) doesn't make sense
!       cur_lim1 = cur_lim1+1;  fp = fp+1  ;
!     %repeat                              ;
      fp = fp + 1 while  fp < lend;        ;  ! Correction KR Apr 87
      if  fp # cur_lim2 start 
        fp = fp+1;  cur_lbeg = fp;  set lend
        cur_line = cur_line+1;  gapline = gapline+1
        altlimlbeg = cur_lbeg if  altlim < fp
      finish 
    else  
      if  fp < lend start 
        if  o_exptabs # 0 start  
          cur_start2 = fp
          if  byteinteger(fp) = tab and  mac(pos) # tab start 
            if  tabcol(fp+1,cur_lbeg,0) - tabcol(fp,cur_lbeg,0) = 1 c 
                then  fp = fp + 1 else  cur_lbeg = cur_lbeg - 1
          finish  else  if  mac(pos) = tab and  byteinteger(fp) # tab start   
            c = tabcol(fp,cur_lbeg,1) - tabcol(fp,cur_lbeg,0)
            cur_lbeg = cur_lbeg - 1
            while  c > 0 cycle 
              c = c - 1
              fp = fp + 1
              cur_lbeg = cur_lbeg + 1
              exit  if  byteinteger(fp) = nl or  byteinteger(fp-1) = tab
            repeat 
          finish  else  fp = fp + 1
        finish  else  fp = fp + 1
      finish  else  cur_lbeg = cur_lbeg - 1
    finish 
    byteinteger(cur_lim1) = mac(pos)
    cur_lim1 = cur_lim1+1;  pos = pos+1
  repeat  until  pos = lim
  markpos = 0 if  s2 <= markpos < fp;        ! Overwritten marker
  cur_start2 = fp;  altlim = cur_start2 if  altlim < cur_start2
  cur_change = altmin if  altmin < cur_change
end 

routine  JOIN
! Erase from FP to end of line AND the line terminator
!  (covers Kill, Join, Uncover)
! SPLIT already called
integer  j
  markpos = 0 if  cur_start2 <= markpos <= lend
  j = lend-fp+1
  cur_lbeg = cur_lbeg+j;  fp = fp+j;  cur_start2 = cur_start2+j
  joins = joins+1
  if  altlim < cur_start2 start 
    altlim = cur_start2;  altlimlbeg = altlim
  finish 
  set lend
  markline = markline-1 if  markline > cur_line
end 
!
routine  SWITCH
! Switch between main and secondary input
  update if  altlim # floor
  if  sin < 0 start ;  !what are you doing here?
    altlim = floor;  sin = 0
    return 
  finish 
  cur_fp = fp;  !store
  markpos = 0;                          !clear marker
  sin = sin!!2
  if  sin >= 2 start ;                  !main -> sec
    cur == sec
    if  cur_min = 0 start 
      cur_min = 10;  cur_win = offscreen
      coerce parameters
    finish 
    if  cur_line = 0 start ;  !indicator for reset
      cur_line = 1
      cur_fp = sec_start2;  cur_lbeg = cur_fp
      cur_win = offscreen;  cur_diff = unknown
    finish 
  else ;                !sec -> main
    cur == main
    if  cur_flag >= ' ' start 
      if  cur_win <= cur_line-cur_diff < cur_bot start 
        cur_row = cur_line-cur_diff
        at(cur_row,tabcol(cur_fp,cur_lbeg,0));  print symbol(cur_flag)
      finish 
      cur_flag = 0
    finish 
  finish 
  fp = cur_fp
  set lend
end 
!
integerfn  MATCHED
! Compare text @FP with text @MPOS:MLIM (full pointers)
integer  p,pos,k,l
  p = fp;  pos = mpos
  cycle 
    k = byteinteger(pos)
    result  = 0 if  k = nl
    l = k!!byteinteger(p)
    if  l # 0 start 
      result  = 0 if  l&casemask # 0 or  symtype(k)&alphanum = 0
    finish 
    p = p+1;  pos = pos+1
  repeat  until  pos = mlim
  foundpos = fp;  foundsize = p-fp
  result  = 1
end 

!
! extract next command
!
execute:
  ci = 0
  ci = cmax1 if  cmax > cmax1

next: s('?'):
  ci = ci+1
  code = r(ci)_code;  ref = r(ci)_ref
  num = r(ci)_count
  -> s(code) if  sin&(\1) = 0 or  symtype(code)&15 >= 8
disallowed:
  complain("* Moving commands only")
!
! Successful return from execution
oklast:
  last = code
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;                          !check following cell:-
    -> next if  r(ci)_code = '\';       !invert  -> 
    -> next if  r(ci)_code = '?';       !query  -> 
    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 
!
s(*):  ![safety]
!suppress report for simple moves as control key macros
  -> read if  control >= 0 and  def(control) < 127 c 
          and  symtype(def(control))&15 = 10
  cat(1,chalf)
  printstring(" Failure: ")
  print code(code)
  if  7 <= symtype(code)&15 <= 9 start ;  !text matching group
    print symbol('''')
    hold = mpos
    mlim = 0 if  hold = null;   !No stored text
    cycle 
      print symbol('''') and  exit  if  hold >= mlim
      print symbol('_') and  exit  if  byteinteger(hold) < ' '
      print symbol(byteinteger(hold))
      hold = hold+1
    repeat  until  hold-mpos >= chalf
  finish 
  newline
  error = 1
  -> ignore
!
!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 '('
  else 
    -> read if  ci >= cmax
  finish 
  -> next
!
s(','):                                 !comma
  ci = ref-1;                           !position of ')' - 1
  -> next
!
s('P'):
  display(0)
  -> ok if  num = 1
s('M'):                                 !Move
  -> no if  line after = 0
  fp = fp+o_margin if  lend # cur_lim2
  -> ok
!
s('}'):                                 !Cursor down
  hold = tabcol(fp,cur_lbeg,0)
  -> no if  line after = 0
  fp = coltab(hold,cur_lbeg) + cur_lbeg if  fp # cur_lim2
  -> oklast

s('{'):                                 !Cursor up
  fp1 = fp
  hold = tabcol(fp,cur_lbeg,0)
  fp = fp1 and  -> no if  line before = 0
  hold = coltab(hold,cur_lbeg) + cur_lbeg
  if  hold < cur_start2 <= fp then  consolidate(fp-hold,sin) c 
  else  fp = hold
  -> oklast
 
s('<'):                                 !Cursor Left
  -> no if  fp = cur_lbeg
  last = code
  -> left

s('>'):                                 !Cursor right
  -> no if  tabcol(fp,cur_lbeg,0) >= o_width or  lend = cur_lim2
  fp = fp+1
  ->oklast
!
s('#'):                                 !absolute line n
  if  num = 0 start 
    read number
    -> fail if  num = 0
  finish 
  code = 'M'
  num = num-cur_line
  -> next if  num = 0
  -> s('M') if  num > 0
  num = -num;  code = 'm'
s('m'):                                 !Move back
  -> no if  line before = 0
  if  num = 0 start ;  !M-*
    if  cur_start1 # cur_lim1 then  jump to(cur_start1) c 
    else  jump to(cur_start2)
    cur_line = 1
  finish 
  hold = cur_lbeg+o_margin;  hold = lend if  hold > lend
  if  hold < cur_start2 <= fp then  consolidate(fp-hold,sin) c 
  else  fp = hold
  -> ok
!
s('C'):                                 !Case-change with right-shift
  -> no if  fp >= lend
  split(mingap)
  holdsym = byteinteger(fp)
  holdsym = holdsym!!casebit if  symtype(holdsym)&letter # 0
  byteinteger(cur_lim1) = holdsym
  markpos = 0 if  fp = markpos;         ! Destroyed marker
  cur_lim1 = cur_lim1+1;  fp = fp+1
  cur_start2 = fp;  altlim = cur_start2 if  altlim < cur_start2
  -> ok
!
s('R'): s('l'):                         !Right-shift
  -> no if  fp >= lend
  fp = fp+1
  -> ok
!
s('c'):                                 !Case-change with left-shift
![unsatisfactory]
  fp = lend if  fp > lend
  -> no if  fp = cur_lbeg
  split(mingap)
  copy across if  gdiff # 0
  markpos = 0 if  cur_lim1-1 = markpos;       ! Erased over marker
  cur_lim1 = cur_lim1-1;  oldlim1 = cur_lim1
  altmin = cur_lim1 if  altmin > cur_lim1
  holdsym = byteinteger(cur_lim1)
  holdsym = holdsym!!casebit if  symtype(holdsym)&letter # 0
  fp = fp-1;  cur_start2 = cur_start2-1
  oldstart2 = cur_start2;  consolidated = 0
  byteinteger(fp) = holdsym
  -> ok

s('L'): s('r'):                         !Left-shift
  fp = lend if  fp > lend
  -> no if  fp = cur_lbeg
left:
  if  fp = cur_start2 then  consolidate(1,sin) else  fp = fp-1
  -> ok
!
s('H'):                                 !Home (multi-function)
  if  last = '<' start 
    num = 0
    if  fp = cur_lbeg+pan and  pan # 0 start 
      num = o_wcols>>1;  pan = pan-num
    finish 
  finish  else  if  last = '>' start 
    num = lend-fp
    -> next if  num <= 0
    if  fp = cur_lbeg+pan+o_wcols start 
      num = o_wcols>>1;  pan = pan+num
    finish 
  finish  else  if  last = '{' start 
    update
    num = cur_line-cur_diff-cur_win
    num = num - (cur_bot-cur_top)>>2 if  o_early # 0
    num = cur_min-2 if  num <= 0
    num = 1 if  num <= 0
  else 
    update
    num = cur_bot-1-(cur_line-cur_diff)
    num = num - (cur_bot-cur_top)>>2 if  o_early # 0
    num = cur_min-2 if  num <= 0
    num = 1 if  num <= 0
  finish 
  code = last
  -> s(code)
!
s('E'):                                 !Erase
  -> no if  fp >= lend
  split(0)
  cur_lbeg = cur_lbeg+1
  markpos = 0 if  fp = markpos;         ! Destroyed marker
  fp = fp+1;  cur_start2 = fp
  altlim = cur_start2 if  altlim < cur_start2
  -> ok
!
s('e'):                                 !Erase back
  fp = lend if  fp > lend
  -> no if  fp = cur_lbeg
  split(0)
  consolidate(1,-1)
  -> ok
!
s('V'):                                 !Verify
  mpos = null
  -> no if  fp >= lend
  if  ref = 0 then  read match text c 
  else  if  ref # '"' then  mdef = def(ref)
  mpos = mdef&posmask+mac0;  mlim = mdef>>limshift+mac0
  holdsym = byteinteger(mpos);             !first symbol of quoted text
  -> no if  mpos # mlim and  matched = 0
  -> next
!
s('D'):                                 !Delete
s('T'):                                 !+ Traverse
  if  ref = 0 then  read match text c 
  else  if  ref # '"' then  mdef = def(ref)
  fp1 = fp
  -> find
!
s('U'):                                 !Uncover
s('F'):                                 !+Find
  if  ref = 0 then  read match text c 
  else  if  ref # '"' then  mdef = def(ref)
  fp1 = fp
  fp = fp+1 if  fp = foundpos
find:
  scope = r(ci)_scope;                  !number of lines to search
  -> next if  mdef < macro;  !null
  mpos = mdef&posmask+mac0;  mlim = mdef>>limshift+mac0
  holdsym = byteinteger(mpos);             !first symbol of quoted text
  cycle 
    while  fp < lend cycle 
      if  (byteinteger(fp)!!holdsym)&casemask = 0 start 
        -> found if  matched # 0
      finish 
      fp = fp+1
    repeat 
    exit  if  fp = cur_lim2
    scope = scope-1
    exit  if  scope = 0
    if  code # 'U' start 
      exit  if  line after = 0
    else 
      fp = fp1;  fp = lend if  fp > lend
      split(0);  join
    finish 
    fp1 = fp
  repeat 
  fp = fp1
  -> no
found:
  -> ok if  code = 'F'
  fp = fp+foundsize and  -> ok if  code = 'T'
found1:
  if  code # 'U' start ;  !'D','d'
    split(0)
    hold = foundsize
  else 
    hold = fp-fp1;  fp = fp1
    split(0);  foundpos = fp+hold
  finish 
  markpos = 0 if  cur_start2 <= markpos < cur_start2+hold;    ! Destroyed marker
  cur_lbeg = cur_lbeg+hold;  fp = fp+hold;  cur_start2 = cur_start2+hold
  altlim = cur_start2 if  altlim < cur_start2
  -> ok
!
s('t'): s('d'):
s('f'):                                 !Find back
  mpos = null
  -> no if  sin < 0;  !**for now [too difficult]
  fp = lend if  fp > lend
  scope = r(ci)_scope
  if  ref = 0 then  read match text c 
  else  if  ref # '"' then  mdef = def(ref)
  -> next if  mdef < macro
  mpos = mdef&posmask+mac0;  mlim = mdef>>limshift+mac0
  holdsym = byteinteger(mpos);             !first symbol of quoted text
  update
  cycle 
    while  fp = cur_lbeg cycle 
      scope = scope-1
      -> no if  scope = 0 or  line before = 0
    repeat 
    if  fp = cur_start2 then  consolidate(1,sin) c 
    else  fp = fp-1
  repeat  until  (byteinteger(fp)!!holdsym)&casemask = 0 c 
        and  matched # 0
  -> ok if  code = 'f'
  fp = fp+foundsize and  -> ok if  code = 't'
  -> found1
!
s('q'):
  complain("Private dictionary not available")
constinteger  termbit=1<<16, lastbit=1<<15, dummy='a'-1
s('Q'):                                 !Query spelling
  if  dict = 0 start 
    connect dictionary(dict)
    complain("Dictionary not available") if  dict = 0
  finish 
  if  fp = foundpos and  foundsize < 0 start ;  !already Queried
     fp = fp+1 until  symtype(byteinteger(fp))&letter = 0
  finish 
qnext:
  cycle 
    while  fp >= lend cycle 
      -> no if  fp = cur_lim or  line after = 0
    repeat 
    qsym = byteinteger(fp)
    exit  if  symtype(qsym)&letter # 0
    fp = fp+1
  repeat 
  foundpos = fp;  foundsize = -1
qagain:
  fp1 = fp
  hold = termbit>>10
  dictpos = integer(dict+qsym<<2)
  cycle 
    fp1 = fp1+1;  holdsym = byteinteger(fp1)-dummy
    if  holdsym <= 0 or  holdsym > 26 start ;  !end of word
      if  hold&termbit>>10 # 0 start ;  !successful match
        -> ok if  num > 0;  !not Q*
        fp = fp1
        -> qnext
      finish 
      exit 
    finish 
    -> qno if  dictpos = 0
    dictpos = dictpos+dict
    cycle 
      hold = integer(dictpos)
      exit  if  hold&31 = holdsym
      -> qno if  hold&lastbit # 0
      dictpos = dictpos+4
    repeat 
    hold = hold>>5
    if  hold&31 # 0 start 
      fp1 = fp1+1
      exit  if  hold&31+dummy # byteinteger(fp1)
    finish 
    hold = hold>>5
    if  hold&31 # 0 start 
      fp1 = fp1+1
      exit  if  hold&31+dummy # byteinteger(fp1)
    finish 
    dictpos = hold>>5&(\3)
  repeat 
  holdsym = byteinteger(fp1)
  -> ok if  holdsym = '-' or  symtype(holdsym)&upperordigit # 0
qno:
  ! Failed to find a match in dictionary - carry on to end of word so
  ! that we can build the whole word into word
  cycle 
    exit  if  symtype(byteinteger(fp1))&letter = 0
    fp1 = fp1+1
  repeat 

  ! We can call a private dictionary lookup here if we want
    integerfn  in private dictionary
      ! uses fp and fp1 as limits of a word to be checked 
      ! return 0 if not found in private dictionary else non-zero
      string (30) word
      integer  i
      ! %owninteger x = 50 ;! debugging purposes only !!!
      ! x = x - 1
      i = fp1 - fp
      i = 30 if  i > 30
      move(i, fp, addr(word)+1)
      length(word) = i
      ! %if x <= 0 %then complain("looping on word:")
      { look up word in private dictionary                              }
      { %if found in dictionary %then %start                            }
      {    %result = 1                                                  } 
      { %finish %else %start                                            }
          cat(1,chalf)                                                  
          printstring("'".word."' not in dictionary")                   
          newline                                                       
          result  = 0
      { %finish                                                         }
    end 
 
  if  in private dictionary # 0 then  start 
    ! word found ok in private dictionary so treat it as found
    -> ok if  num > 0;  !not Q*
    fp = fp1
    -> qnext
  finish 
  -> no if  qsym >= 'a'
  qsym = qsym+casebit
  -> qagain

integerfn  found closer
integer  k
  k = byteinteger(fp)+2;  k = ')' if  k = '('+2
  cycle 
    fp = fp+1
    result  = 0 if  fp >= lend
    result  = 1 if  byteinteger(fp) = k
    if  symtype(byteinteger(fp))&opener # 0 start 
      result  = 0 if  found closer = 0
    finish 
  repeat 
end 
s('N'):                                 !Next word/element
  -> no if  lend = cur_lim2
  fp = lend if  fp > lend
  holdsym = byteinteger(fp)
  hold = symtype(holdsym)
  if  hold&alphanum # 0 or  holdsym <= ' ' start 
    fp = fp+1 while  symtype(byteinteger(fp))&alphanum # 0
    cycle 
      while  fp >= lend cycle 
        -> no if  line after = 0
      repeat 
      exit  if  symtype(byteinteger(fp))&alphanum # 0
      fp = fp+1
    repeat 
    foundsize = 0
  finish  else  if  hold&opener # 0 start 
    -> no if  found closer = 0
    foundsize = 1
  else 
    cycle 
      fp = fp+1
      -> no if  fp >= lend
    repeat  until  byteinteger(fp) = holdsym
    foundsize = 1
  finish 
  foundpos = fp
  -> ok
!
routine  backup
  if  fp = cur_start2 start 
    holdsym = byteinteger(cur_lim1-1)
    consolidate(1,sin)
  else 
    fp = fp-1;  holdsym = byteinteger(fp)
  finish 
end 
integerfn  found opener
integer  k
  k = holdsym-2;  k = '(' if  k = ')'-2
  cycle 
    result  = 0 if  fp = cur_lbeg
    backup
    result  = 1 if  holdsym = k
    if  symtype(holdsym)&closer # 0 start 
      result  = 0 if  found opener = 0
    finish 
  repeat 
end 
s('n'):                                 !Locate previous word/element
  if  fp >= lend start 
    fp = lend;  holdsym = ' '
  finish  else  holdsym = byteinteger(fp)
  hold = symtype(holdsym)
  if  hold&alphanum # 0 or  holdsym = ' ' start 
    cycle 
      while  fp = cur_lbeg cycle 
        -> no if  line before = 0
      repeat 
      backup
    repeat  until  symtype(holdsym)&alphanum # 0
    cycle 
      exit  if  fp = cur_lbeg
      if  fp = cur_start2 start 
        exit  if  symtype(byteinteger(cur_lim1-1))&alphanum = 0
        consolidate(1,sin)
      else 
        exit  if  symtype(byteinteger(fp-1))&alphanum = 0
        fp = fp-1
      finish 
    repeat 
    foundsize = 0
  finish  else  if  hold&closer # 0 start 
    -> no if  found opener = 0
    foundsize = 1
  else 
    hold = holdsym
    cycle 
      -> no if  fp = cur_lbeg
      backup
    repeat  until  hold = holdsym
    foundsize = 1
  finish 
  foundpos = fp
  -> ok
!
s('S'):                                 !Substitute
  -> no if  fp # foundpos
  if  foundsize <= 0 start ;                 !following 'N' etc
    fp1 = fp
    fp1 = fp1+1 until  symtype(byteinteger(fp1))&alphanum = 0
    foundsize = fp1-fp
  finish 
  split(0)
  markpos = 0 if  fp <= markpos < fp+foundsize;       ! Destroyed marker
  cur_lbeg = cur_lbeg+foundsize;  fp = fp+foundsize;  cur_start2 = fp
  altlim = cur_start2 if  altlim < cur_start2
!
s('I'):                                 !+Insert
  -> no if  tabcol(fp,cur_lbeg,0) > o_width and  code # 'S'
  if  ref = 0 start 
    -> over if  fp >= lend
    if  video # 0 start 
      display(o_early)
      read text(inserting)
    else 
      vt prompt("I>")
      read text(standard)
      vt prompt("")
    finish 
    idef = newdef
    if  idef >= macro start 
      hold = tabcol(fp,cur_lbeg,0)
      insert(idef)
      altlim = floor and   altmin = ceiling if  hold >= cur_shift
    finish 
    ->controlterm if  term # ret
  else 
    idef = def(ref) if  ref # '"'
    -> next if  idef < macro
    insert(idef)
  finish 
  -> ok
!
!Recovery commands
s('o'):                                 !Overwrite back
  -> no if  cur_lim1 <= oldlim1 and  cur_start2 <= oldstart2
  if  fp # cur_start2 start 
    update
    fp = cur_start2
    cur_line = gapline;  set lbeg(cur_lbeg,fp);  set lend
  finish 
  split(0);  !(to update?)
  if  cur_lim1 > oldlim1 start 
    markpos = 0 if  cur_lim1-1 = markpos;       ! Erased marker
    cur_lim1 = cur_lim1-1
    if  byteinteger(cur_lim1) = nl start 
      joins = joins+1
      cur_line = cur_line-1;   altline = cur_line
    finish 
    set lbeg(cur_lbeg,fp);  altmin = cur_lim1
   finish 
   -> ok if  cur_start2 <= oldstart2
   fp = fp-1;  cur_start2 = fp
   cur_lbeg = cur_lbeg-1
   -> ok if  byteinteger(fp) # nl
   joins = joins-1;  lend = fp
   set lbeg(cur_lbeg,fp)
   -> ok
!
s('i'):                                 !Insert back
  fp = lend if  fp > lend
  store deletions if  oldstart2 < cur_start2
  -> no if  delmax <= lastdelmax
  split(mingap>>1)
  copy across if  gdiff # 0
  fp = fp-1
  byteinteger(fp) = byteinteger(delmax)
  delmax = delmax-1
  cur_start2 = fp;  oldstart2 = cur_start2
  cur_lbeg = cur_lbeg-1
  if  byteinteger(fp) = nl start 
    joins = joins-1;  lend = fp;  set lbeg(cur_lbeg,fp)
  finish 
  -> ok
!
s('g'):                                 !Get back
  fp = lend if  fp > lend
  store deletions if  oldstart2 < cur_start2
  split(mingap>>1)
  delmax = delmax-1 while  byteinteger(delmax) # nl
  -> no if  delmax = newlim
  copy across if  gdiff # 0
  lend = fp-1
  cycle 
    fp = fp-1;  byteinteger(fp) = byteinteger(delmax)
    delmax = delmax-1
  repeat  until  byteinteger(delmax) = nl
  cur_start2 = fp;  oldstart2 = cur_start2
  joins = joins-1;  set lbeg(cur_lbeg,fp)
  -> ok
!
s('O'):                                 !Overwrite
  -> no if  tabcol(fp,cur_lbeg,0) > o_width
over:
  if  ref = 0 start 
    if  video # 0 start 
      display(o_early)
      read text(replacing)
    else 
      vt prompt("O>")
      read text(standard)
      vt prompt("")
    finish 
    idef = newdef
    if  idef >= macro start 
      hold = tabcol(fp,cur_lbeg,0)
      overwrite(idef)
      altlim = floor and   altmin = ceiling if  hold >= cur_shift
    finish 
    if  dels#0 then  repair line else  repair chars(repairch)
    -> controlterm if  term # ret
  else 
    idef = def(ref) if  ref # '"'
    -> next if  idef < macro
    overwrite(idef)
  finish 
  -> ok
!
!!!!!!!!!!!!!!!!!!!!!!  Data entry mode  !!!!!!!!!!!!!!!!!!!!!!
data entry:
  cycle 
    display(o_early)
    read text(o_dmode)
    if  newdef >= macro start ;  !non-null
      if  def1(term) = 'H' start ;  !treat as command
        inlim = newdef>>16;  inpos = newdef&posmask
        control = -1
        repair line
        -> again
      finish 
      if  sin&(\1) # 0 or  lend = cur_lim2 start 
        repair line
        -> read
      finish 
      hold = tabcol(fp,cur_lbeg,0)
      if  o_dmode = replacing then  overwrite(newdef) else  insert(newdef)
      altlim = floor and   altmin = ceiling if  hold >= cur_shift; ! up to date unless before screen
    finish 
    repair line if  dels # 0 
    exit  if  term # ret or  def(ret) # 'M'
    hold = line after
    fp = fp+o_margin if  lend # cur_lim2
  repeat 
controlterm:
  update;                      ! If input to left of screen, redraw now.
  control = term;  cur_flag = 0
  -> again
!
!!!!!!!!!!!!!!!!!!!!!!!  end of data entry  !!!!!!!!!!!!!!!!!!!!!
  routine  insert spaces(integer  hold)
    ! Inserts 'hold' spaces before fp. Assumes 'SPLIT' called
    while  hold > 0 cycle 
      byteinteger(cur_lim1) = ' '; cur_lim1 = cur_lim1 + 1
      cur_lbeg = cur_lbeg - 1;     hold = hold - 1
    repeat 
  end 

s('G'):                                 !Get (line from terminal)
  if  cur_lbeg < cur_start2 <= fp start 
    update;  consolidate(fp-cur_lbeg,0);  ![update needed?]
  finish  else  fp = cur_lbeg
  if  ref = 0 start 
    split(mingap)
    if  video # 0 start 
      if  video < 0 start 
        display(o_early)
        cur_row = cur_line-cur_diff
        scroll(cur_row,cur_bot,-1)
        curprom = "";  !lost it
      else ;               !Simulate Break & Move back
       !SPLIT already done
        break
        update
        fp = cur_lim1-1;  cur_lbeg = fp
        cur_line = cur_line-1
        display(0)
        cur_row = cur_line-cur_diff
        cur_lim1 = cur_lim1-1
        fp = cur_start2;  cur_lbeg = fp
      finish 
      at(cur_row,tabcol(fp,cur_lbeg,0)+o_mark-cur_shift)
    finish  else  vt prompt(":")
    read text(standard); vt prompt("")
    newdef = null and  term = ':' if  newdef # null c 
                                  and  mac(newdef&posmask) = ':'
    if  newdef = null and  term # ret start 
      if  video # 0 start 
        if  video < 0 start 
          scroll(cur_row,cur_bot,1)
        else 
          split(0);  !to set ALT...
          joins = joins+1
        finish 
      finish 
      term = ret and  -> no if  term = ':'
      -> controlterm
    finish 
    idef = newdef
    insert spaces(cur_shift);       ! Put'shift' spaces at beginning of line
    insert(idef)
    break
    cur_change = altmin if  altmin < cur_change
    altlim = floor;  altmin = ceiling;  !screen up-to-date
    joins = 0
    if  video < 0 start ; !bring back
      if  cur_row = cur_bot-1 start 
        cur_win = cur_win-1 if  cur_win > cur_top
        cur_diff = cur_diff+1
        scroll(cur_top,cur_bot,1)
      finish  else  if  o_emode # 0 start 
        cat(0,0);  clear line
      finish 
    finish 
    -> controlterm if  term # ret
  else 
    idef = def(ref) if  ref # '"'
    insert(idef)
    break
  finish 
  -> ok
!
s('B'):                                 !Break
  fp = lend if  fp > lend
  num = 66 if  num = 0 or  num > 66
  split(mingap)
  break
  -> ok
!
s('k'):                                 !Kill back
  update if  altlim # floor
  if  cur_lbeg < cur_start2 <= fp start 
    fp = lend if  fp > lend;  consolidate(fp-cur_lbeg,0)
  finish  else  fp = cur_lbeg
  split(0)
  -> no if  cur_lim1 = cur_start1
  hold = sin; sin = -1;  fp1 = line before;  sin = hold
  consolidate(fp-cur_lbeg,-1) if  fp # cur_lbeg
  -> ok

s('K'):                                 !Kill
  -> no if  lend = cur_lim2
  fp = lend if  fp > lend
  split(0)
  consolidate(fp-cur_lbeg,-1) and  cur_lbeg = fp if  fp # cur_lbeg
  join
  -> ok
!
s('J'):                                 !Join
  fp = lend if  fp < lend
  -> no if  lend = cur_lim2 or  tabcol(fp,cur_lbeg,0) > o_width
  if  fp > lend then  extend line else  split(0)
  join
  -> ok
!
![unsatisfactory]
constinteger  true=1,false=0
integerfn  ADJUSTED
integer  size
  fp1 = cur_lbeg+o_margin
  fp = lend and  result  = true if  fp1 >= lend;  !blank line ->
  fp = fp1 if  fp < fp1
  cycle 
    fp1 = fp;  !last boundary
    fp = fp+1 while  byteinteger(fp) = ' ' or  byteinteger(fp) = tab
    fp = fp+1 while  byteinteger(fp) > ' '
    size = tabcol(fp,cur_lbeg,0)
    if  size > o_width start 
      result  = false if  byteinteger(fp1) # ' ' and  byteinteger(fp) # tab
      fp = fp1
      result  = true
    finish 
    if  fp = lend start 
      fp1 = fp+1
      fp1 = cur_start2 if  fp1 = cur_lim1
      result  = false if  fp1 = cur_lim2
      foundpos = fp1
      fp1 = fp1+1 while  byteinteger(fp1) = ' ' or  byteinteger(fp1) = tab
      result  = false if  byteinteger(fp1) = nl or  fp1-foundpos < o_margin
      foundpos = fp1
      fp1 = fp1+1 until  byteinteger(fp1) <= ' '
      foundsize = fp1-foundpos;  size = size+1+foundsize
      result  = true if  size > o_width
      split(mingap)
      join
      byteinteger(cur_lim1) = ' ';  cur_lim1 = cur_lim1+1
      move(foundsize,foundpos,cur_lim1)
      cur_lim1 = cur_lim1+foundsize;  oldlim1 = cur_lim1
      fp = foundpos+foundsize
      cur_start2 = fp;  oldstart2 = cur_start2
      altlim = cur_start2 if  altlim < cur_start2
      set lbeg(cur_lbeg,fp)
    finish 
  repeat 
end ;  !ADJUSTED

s('A'):                                 !Adjust
  type = adjusted
  if  fp = lend start ;  !break position is at end of line
    -> no if  line after = 0
  else 
    split(0)
    fp = fp+1;  cur_start2 = fp;  !erase space
    oldstart2 = cur_start2;  altlim = cur_start2 if  altlim < cur_start2
    break
    hold = 0
    while  hold < o_margin cycle 
      byteinteger(cur_lim1) = ' ';  cur_lim1 = cur_lim1+1
      hold = hold+1
    repeat 
    oldlim1 = cur_lim1
    cur_lbeg = fp-o_margin
  finish 
  -> ok if  type # 0
  -> no
!
s('@'):                                 !'at' Column NUM
  -> fail if  lend = cur_lim2
  hold = o_width-(tabcol(lend,cur_lbeg,0)-tabcol(fp,cur_lbeg,0))
  num = hold if  hold < num
  if  fp >= lend start 
    fp = cur_lbeg+num and  -> next if  cur_lbeg+num >= lend
    fp = lend
  finish 
  hold = tabcol(fp,cur_lbeg,0) - num
  -> next if  hold = 0
!old?  fp = fp-hold %and -> next %if fp >= lend %and fp-hold >= lend
  split(mingap)
  cycle 
    if  hold < 0 start ;                !left of it
      byteinteger(cur_lim1) = ' ';  cur_lim1 = cur_lim1+1
      cur_lbeg = cur_lbeg-1;  hold = hold+1
    else 
      -> fail if  fp = cur_lbeg or  (byteinteger(cur_lim1-1) # ' ' and  byteinteger(cur_lim1-1) # tab)
      markpos = 0 if  cur_lim1-1 = markpos;       ! Deleted marker
      cur_lim1 = cur_lim1-1;  cur_lbeg = cur_lbeg+1
      altmin = cur_lim1 if  altmin > cur_lim1
      hold = tabcol(fp,cur_lbeg,0) - num
    finish 
  repeat  until  hold = 0
  -> next
!
routine  put number(integer  v)
  put number(v//10) and  v = v-v//10*10 if  v >= 10
  byteinteger(cur_lim1) = v+'0'
  cur_lim1 = cur_lim1+1;  cur_lbeg = cur_lbeg-1
end 
s('-'):
s('+'):                                 !Increment Number
  cycle 
    -> no if  fp >= lend
    hold = symtype(byteinteger(fp))
    exit  if  hold&alphanum # 0
    fp = fp+1
  repeat 
  split(mingap)
  if  hold = digit start 
    hold = 0;  fp1 = fp
    cycle 
      hold = hold*10+byteinteger(fp)-'0';  fp = fp+1
    repeat  until  symtype(byteinteger(fp)) # digit
    if  code = '-' start 
      hold = hold-num;  -> fail if  hold < 0
    finish  else  hold = hold+num
    cur_lbeg = cur_lbeg+(fp-fp1)
    put number(hold)
  else 
    hold = byteinteger(fp)
    if  code = '-' then  hold = hold-num else  hold = hold+num
    -> fail unless  'A' <= hold <= 'z' and  symtype(hold)&letter # 0
    byteinteger(cur_lim1) = hold
    cur_lim1 = cur_lim1+1;  fp = fp+1
  finish 
  markpos = 0 if  cur_start2 <= markpos < fp
  cur_start2 = fp;  altlim = cur_start2 if  altlim < cur_start2
  -> next
s('|'):                                !Toggle Destructive Mode
  -> disallowed if  sin > 0
  if  sin&(\1) = 0 start 
    fp = lend if  fp > lend
    -> fail if  tabcol(fp,cur_lbeg,0) > o_width
    split(0);  altlim = floor+1;  sin = -1
  else 
    update;  altlim = floor;  sin = 0
  finish 
  -> next
!
s('^'):                                 !Set Marker / Delimit Text
  -> disallowed if  sin < 0
  fp = lend if  fp > lend
  if  num = 0 and  markpos = 0 start 
    markpos = fp;  markline = cur_line
    if  sin = 0 start 
      store deletions if  oldstart2 < cur_start2
      oldlim1 = cur_lim1
    finish 
  else 
    fp1 = markpos
    if  fp1 # 0 start 
      hold = distance(fp1,fp)
      if  hold < 0 start 
        hold = -hold
        fp1 = fp
      finish 
      markpos = 0
    else 
      -> fail if  fp # foundpos
      if  foundsize <= 0 start ;                 !following 'N' etc
        fp1 = fp
        fp1 = fp1+1 until  symtype(byteinteger(fp1))&alphanum = 0
        foundsize = fp1-fp
      finish 
      fp1 = fp;  hold = foundsize
    finish 
    num = 'X' if  num < 'X'
    release(num)
    if  hold = 0 then  def(num) = null else  start 
      mpos = macspace(hold)
      def(num) = (mpos+hold)<<limshift+mpos
      while  hold > 0 cycle 
        mac(mpos) = byteinteger(fp1)
        mpos = mpos+1;  fp1 = fp1+1
        fp1 = cur_start2 if  fp1 = cur_lim1
        hold = hold-1
      repeat 
    finish 
  finish 
  -> next
!
s('='):
  -> no if  markpos = 0
  jump to(markpos)
  cur_line = markline
  markpos = 0
  -> ok

s('~'):                                 ! Toggle replace/insert data mode
  -> no if  video = 0
  o_dmode = o_dmode!!1
  -> ok

s('$'):                                 !switch inputs
  fp1 = markpos;  fp = lend if  fp > lend
  switch
  if  sin&(\1) = 0 and  fp1 # 0 and  fp1 # sec_fp start 
    hold = sec_fp
    hold = fp1 and  fp1 = sec_fp if  fp1 > hold
    if  fp > lend start 
      fp = lend if  byteinteger(fp1) = nl
      extend line
    finish  else  split(mingap)
    cycle 
      if  byteinteger(fp1) = nl then  break else  start 
        byteinteger(cur_lim1) = byteinteger(fp1)
        cur_lim1 = cur_lim1+1;  cur_lbeg = cur_lbeg-1
      finish 
      fp1 = fp1+1
    repeat  until  fp1 = hold
  finish 
  -> next
!
! C o m m a n d   i n p u t
!
routine  GET NAME(string (maxname)name  s)
!First symbol in SYM
  s = ""
  while  ' ' <= sym < 127 cycle 
    s = s.tostring(sym) if  length(s) < maxname
    get sym
  repeat 
  cat(1,0);  !in case of error-report
end 

constinteger  first=0, normal=1;  !(nomac=-1)
routine  GET CODE(integer  mode)
! Read command unit to CODE, classifying in TYPE
! Expand macros if MODE >= 0 / Leading element if MODE = 0
integer  k
  cycle 
    get sym until  sym # ' '
    code = sym
    if  sym < ' ' start ;  !control
      type = 1
      return  if  mode > 0;  !non-initial
      code = term
    finish 
   !Test for printing char version of control sequence
    if  code = '&' start ;              !control shift
      get sym;  
      if  sym = ' ' start ; ! Impossible sequence signals DEL
        code = del 
      else  
        -> err if  sym < '@'
        code = sym&31
      finish  
      if  code = esc start 
        get sym
        if  sym = '?' start ;  !canonical 2nd leadin
          get sym;  sym = sym!!96
        finish 
        code = sym+128
      finish 
      control = code {%unless def(code) = '\';   ! Don't allow Data entry mode
    finish 
    k = code;  k = def(code) unless  ' ' <= k < 'X'
    return  if  mode = nomac
    exit  if  k < macro;  !not macro
    macpush(k)
    mode = normal
  repeat 
  pend = k>>8;  code = k&255
  type = symtype(code)&15
  return 
err:
  type = 1;  code = ' '
end 
!
routine  GET TEXT
integer  pos,lim
  if  sym = '!'  start 
    if  msp # 0 start ; !dummy parameter
      pos = inpos;  lim = inlim
      msp = msp-1
      inpos = mstack(msp)&posmask;  inlim = mstack(msp)>>limshift
      get sym if  inpos < inlim
      get text
!      %return %if ref = 0;  !trailing
      if  inpos < inlim start 
        mstack(msp) = inlim<<limshift+inpos
        msp = msp+1
      finish 
      inpos = pos;  inlim = lim
      return 
    finish 
    ref = 0
  finish  else  if  sym = '"' or  'X' <= sym&95 <= 'Z' start ; !text macro
    ref = sym
  else 
    ref = nullref;  ref = 0 if  num # 0;  !Insert,etc
    pend = sym and  return  if  symtype(sym) # 3; !not valid quote ->
    ref = nullref
    hold = sym
    get sym
    pos = inpos-1;  lim = pos
    cycle 
      if  sym < ' ' start ;             !closing quote omitted
        return  if  num = 0;            !allowed only for I,S
        pend = sym;  sym = hold
      finish 
      exit  if  sym = hold
      lim = inpos
      if  inpos >= inlim start 
        return  if  num = 0
        exit 
      finish 
      get sym
    repeat 
    if  lim > pos start ; !not null
      def(treflim) = lim<<limshift+pos
      ref = treflim;  treflim = treflim+1
    finish 
  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 
!

bytemap    BVALUE(integer  i)
  switch  b(0:enumcases-1)
  -> b(i)
b(0): result  == O_MAPCASE
b(1): result  == O_MARK
b(2): result  == O_EARLY
b(3): result  == O_DMODE
b(4): result  == O_EMODE
b(5): result  == O_EXPTABS
b(*): event_message = "Unknown Option"; signal  10,4
end 
integermap  VALUE(integer  i)
  switch  v(0:intcases-1)
  -> v(i-enumcases)
v(0): result  == O_WIDTH
v(1): result  == O_MARGIN
v(2): result  == O_MINWIN
v(*): event_message = "Unknown Option"; signal  10,4
end 

routine  SET OPTIONS
integer  i
constinteger  showpointer=1,expandtabs=5
conststring (15)array  text(0:enumcases+intcases-1) =
  "Case-matching [",
  "Show position [",
  "Update [",
  "Data mode [",
  "Edit mode [",
  "Expand Tabs [",
  "Line width [",
  "Left margin [",
  "Min. window ["
conststring (7)array  OPTNAME(0:enumcases*2-1) =
  "NOMATCH", "MATCH",
  "HILIGHT","MARK",
  "LATE", "EARLY",
  "REPLACE", "INSERT",
  "COMMAND", "DATA",
  "NO","YES"

!%routine SHOW(%integer i)
!  %if i >= enumcases %then write(value(i),1) %c
!  %else print string(optname(i+i+bvalue(i)))
!%end

string (15)fn  SHOW(integer  i)
  if  i >= enumcases then  result  = itos(value(i))
  result  = optname(i+i+bvalue(i))
end 

cat(1,0)
printstring( "RETURN to step through   value or 'x' to alter   ':' to exit") c 
            if  commandstream = 0;    !Not for PRE file
newline
i = 0
cycle 
  i = 0 if  i = enumcases+intcases
  cat(0,0)
  o_minwin = cur_min;  !relevant current setting
  vt prompt(text(i).show(i)."] :")
  clear line
  read command line
  get sym
  if  sym # ret start 
    if  sym = ':' start 
      save command;  !ie last shown
      vt prompt("")
      return 
    finish 
    num = 0
    while  sym >= ' ' cycle 
      num = num*10+sym-'0' if  '0' <= sym <= '9'
      get sym
    repeat 
    if  i >= enumcases start 
      value(i) = num
      if  cur_min # o_minwin start 
        cur_min = o_minwin
        cur_win = offscreen;  cur_diff = unknown
      finish 
    else 
      bvalue(i) = bvalue(i)!!1
      cur_diff = unknown if  i = showpointer or  i = expandtabs
    finish 
    coerce parameters
    i = i-1
  finish 
  i = i + 1
repeat 
end ;  !set options

routine  DEFINE(integer  k)
integer  m,n,pos,macpos,control
  control = 1;  control = 0 if  ' ' <= k < del
  if  ' ' <= k < del start 
    control = 0
    complain(tostring(k)." cannot be re-defined") c 
          unless  'X' <= k <= 'Z' or  'a' <= k <= 'z'
  finish 
  release(k)
  get sym
  n = 0
  if  sym = '"' and  cdef # null start 
    n = cdef>>16-cdef&posmask
  else 
    if  sym # '=' start 
      complain("*Missing equals-sign/colon") if  sym # ':'
      mac(inpos) = mac(inpos)!128 if  control # 0
    finish 
    if  inpos >= inlim start 
      return  unless  term < ' ' and  term # ret
      mac(inlim) = term;  inlim = inlim+1
    finish 
  finish 
  pos = inpos
  inpos = inpos+1 while  inpos < inlim and  mac(inpos) # nl
  m = inpos-pos
  macpos = macspace(n+m)
  move(n,mac0+cdef&posmask,mac0+macpos);  macpos = macpos+n
  move(m,mac0+pos,mac0+macpos);  macpos = macpos+m
  def(k) = macpos<<limshift+(macpos-n-m)
end 

routine  EXPLAIN(integer  k)
!K is initial symbol (NOMAC)
integer  m,control,back,flag
conststring (35)array  text(' ':127) =
  "undefined",
  "prefix for system command",
  "'ditto' text parameter",
  "Move to absolute line n",
  "Switch between input files",
  "prefix for Special command",
  "prefix for control character",
  "a possible text delimiter",
  "left parenthesis",
  "right parenthesis",
  "repeat indefinitely",
  "Increment Number",
  "separator for alternatives",
  "back",
  "a possible text delimiter",
  "a possible text delimiter",
  "repeat indefinitely",
  "repeat once",
  "repeat twice",
  "repeat three times",
  "repeat four times",
  "repeat five times",
  "repeat six times",
  "repeat seven times",
  "repeat eight times",
  "repeat nine times",
  "Define Macro letter",
  "reserved",
  "Cursor Left",
  "Revert to Marker",
  "Cursor Right",
  "ignore failure condition",
  "Align to column position",
  "Adjust line length",
  "Break line in two",
  "Case-change character",
  "Delete text",
  "Erase character",
  "Find text",
  "Get text as complete line",
  "Home (north,south,east,west)",
  "Insert text",
  "Join next line to this",
  "Kill (delete current line)",
  "move Left one character",
  "Move to next line",
  "locate Next word/unit",
  "Overwrite with text",
  "Print line(s)",
  "Query form",
  "move Right one character",
  "Substitute text",
  "Traverse text",
  "Uncover (delete up to) text",
  "Verify text",
  "reserved",
  "undefined macro",
  "undefined macro",
  "undefined macro",
  "reserved",
  "invert failure condition",
  "reserved",
  "Set Marker to delimit text",
  "reserved",
  "reserved",
  "reserved",
  "reserved",
  "Case-change character backwards",
  "reserved",
  "Erase character backwards",
  "Find text backwards",
  "Get back - recover deleted line",
  "reserved",
  "Insert back - recover character",
  "reserved",
  "Kill previous line",
  "move Right one character",
  "Move to previous line",
  "Next word/unit backwards",
  "Overwrite back (recover)",
  "Print previous line",
  "reserved",
  "move Left one character",
  "reserved",
  "reserved",
  "reserved",
  "reserved",
  "reserved",
  "reserved",
  "reserved",
  "reserved",
  "Cursor Up",
  "Toggle Destructive mode",
  "Cursor Down",
  "Toggle INSERT/REPLACE data mode",
  "illegal"

  cat(1,0)
  m = k;  m = def(k) unless  ' ' <= m < 'X'
  control = 0;  control = 1 unless  ' ' <= k < del
  if  control # 0 or  (m >= macro and  sym < ' ') start ;  !macro (alone)
    print symbol(k) if  control = 0
    flag = '='
    if  m >= macro start ;  !defined macro
      macpush(m)
      flag = ':' if  mac(inpos)&128 # 0
      get sym;  k = sym
      m = k;  m = def(k) unless  ' ' <= m < 'X'
      get sym
    finish  else  if  control # 0 start 
      flag = ':'
    finish 
    print symbol(flag);  print symbol(' ')
  finish 
  back = 0
  if  'A' <= m <= 'W' and  sym = '-' start 
    m = m+casebit;  get sym;  back = 1
  finish 
  if  sym >= ' ' start ;  !not single command letter
    print symbol(k)
    print symbol('-') if  back # 0
    cycle 
      print symbol(sym)
      get sym
    repeat  until  sym < ' '
    print symbol('/') and  msp = 0 if  msp # 0
  finish  else  if  control # 0 and  m = '\' start 
    print string("\ : Swop between command/data modes")
  finish  else  if  control # 0 and  m = '1' start 
    printstring("1 : repeat last command line")
  else 
    print code(m&255)
    k = m>>8
    if  k # 0 start 
      if  k # '0' start 
        printsymbol(k)
      else 
        printstring("* (ie ")
        print code(m&255)
        printstring(" indefinitely)")
      finish 
    else 
      printstring(" : ");  printstring(text(m))
    finish 
  finish 
  newline
end ;  !explain

routine  OUTPUT KEYDEFS
integer  i,j,kk,sym
  for  kk = 0,1,255 cycle 
    i = def(kk)
    if  i >= premacro and  not  ' ' <= kk < 'X' start 
      print symbol('%');  print symbol('K')
      sym = kk
      if  sym < ' ' or  sym >= 128 start 
        print symbol('&');  sym = sym+64
        if  sym >= 128 start 
          sym = kk&127
          print symbol('[');          !ESC
          print symbol('?') and  sym = sym!!96 if  sym < 64
        finish 
      finish 
      print symbol(sym)
      j = i>>limshift;  i = i&posmask
      if  mac(i)&128 = 0 then  printsymbol('=') else  printsymbol(':')
      while  i # j cycle 
        print symbol(mac(i)&127);  i = i+1
      repeat 
      newline
    finish 
  repeat 
end 

routine  ECHO COMMAND
integer  pos
  cat(1,0)
  if  control < 0 start 
    printsymbol(charno(curprom,1));  printsymbol(charno(curprom,2))
    pos = cdef&posmask
    while  pos < cdef>>16 cycle 
      print symbol(mac(pos));  pos = pos+1
    repeat 
    clear line
  finish 
end 

!
! I n i t i a l i s a t i o n
!
routine  macinit(string (255) s)
integer  i,k
  for  i = 1,1,length(s) cycle 
    k = charno(s,i);  k = k+128 if  'A' <= k <= 'Z'
    mac(i+511) = k
  repeat 
end 

conststring (2)array  PROM(-1:6) = "|>", ">>", "$>", "$$",
                                   "^?", "^>", "$^", "^$"
edistart:
  o = options;           ! Assign local copy of editor options
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!    Initialisation of former %OWNs. Some of these may be unneccessary
  TOGGLE=0
  CASEMASK=\casebit;     !\casebit/\0 to ignore/heed case
  DICT=0
  TERM=ret;              !last symbol read
  SYM=ret;               !last symbol got
  LAST='}'
  NUM=0;                 !repetition number
  PAN=0
  MARKLINE=0; !marker positions
  PRINTLINE=0;PRINTED=0; !for hard-copy
  NEWPROM="??";CURPROM=""
  CI=0; CMAX=0; CMAX1=0;   !indexing R
  INPOS=0;INLIM=0
  DELS=0;INITDELS=0;REPAIRCH=0
  TREFLIM=trefbase;TREFLIM1=trefbase
  INSERTLEN = 0; INSERTDIF = 0
  ENDON = -1; ALTLIMLBEG = 0
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  lastcell_code = ')';  lastcell_count = 1; lastcell_ref = 0
!Stored text pointers
  newdef = null; cdef = null;  idef = null;  mdef = null
  code = null
  mac0 = addr(mac(0));
  macm4 = mac0-4
  macbase = mac0+528
  integer(macbase) = macbound+1-532
  integer(macbase+(macbound+1-532)) = 0
  macinit("I. .D. .D-. .")
  mac(525) = ff;  mac(526) = tab
  !  Check for newlines missing
  if  main_start2 # main_lim2 and  byteinteger(main_lim2-1) # nl start 
  ! no newline at end of file
    message = "No Newline!!!"
    if  main_lim2 < main_lim start 
      ! there is room to add the newline 
      byteinteger(main_lim2) = nl
      main_lim2 = main_lim2+1
    finishelsestart 
      ! Cant add it so we just ignore the last line
      main_lim2 = main_lim2-1 while  main_lim2 > main_start2 and  c 
        byteinteger(main_lim2-1) # nl
    finish 
  finish 
  if  sec_start2 # sec_lim2 and  byteinteger(sec_lim2-1) # nl start 
    ! no newline at end of file
    ! Cant add it so we just ignore the last line
    sec_lim2 = sec_lim2-1 while  sec_lim2 > sec_start2 and  c 
      byteinteger(sec_lim2-1) # nl
  finish 
!File pointers
  cur == main
  oldlim1 = cur_lim1;  oldstart2 = cur_start2
  fp = cur_fp
  if  cur_line = 0 start 
    fp = cur_start1
    cur_line = 1
    cycle 
      fp = cur_start2 if  fp = cur_lim1
      exit  if  fp = cur_fp
      return  if  fp = cur_lim2
      cur_line = cur_line+1 if  byteinteger(fp) = nl
      fp = fp+1
    repeat 
  finish 
  newlim = cur_lim2
  gdiff = 0
  unless  cur_lim1 <= cur_lim2 <= cur_lim start 
    newlim = cur_lim-1024
    gdiff = newlim-cur_lim2
  finish 
  delmax = newlim;  byteinteger(delmax) = nl if  delmax > 0
  lastdelmax = delmax
  foundpos = 0;  foundsize = 0;  markpos = 0
  cmax1 = 0;  consolidated = 0
  error = 0;  commandstream = 0;  pend = 0
  joins = 0
  sin = 0
  if  cur_change < 0 start ;  !showing only
    sin = 1
  else 
    cur_change = ceiling if  cur_change = 0
    cur_change = ceiling-1 if  cur_change # ceiling
  finish 
  altmin = ceiling;  altlim = floor
  set lbeg(cur_lbeg,fp);  set lend
!Initialise video info
  ![XOR so that o_VMODE can, awkwardly, suppress]
  smode = o_vmode!!screenmode!!specialpad
  define video(o_ttype) and  o_ttype = -2 if  o_ttype > -2
  smode = 0 if  vdu_fun = 0
  if  vttype = esprit start 
    def(128+'L'&31) = '{';  !cursor up
    def(128+'S'&31) = 'K';  !del line
  finish 
! SET UP CURSOR KEYS
  def(key u)='{' if  key u#0
  def(key d)='}' if  key d#0
  def(key l)='<' if  key l#0
  def(key r)='>' if  key r#0
  prompt("")
  set video mode(smode)
  set windows
  cur_bot = o_wrows;  cur_min = o_minwin
  cur_win = offscreen;  cur_diff = unknown
  coerce parameters


  begin 
  ! Initialise log file
    on  event  9 start 
      o_logfile = ""
      logstream = logstream - 1
      selectoutput(0)
      -> logfile failed
    finish 
    keeplog = 0
    if  o_logfile # "" start 
      if  o_logfile # ".JOURNAL" start 
        logstream = logstream + 1
        open output(logstream,o_logfile) 
        select output(logstream); select output(0)
        keeplog = 1
      else 
        keeplog = -1
      finish 
    finish 
logfile failed:
  end ; ! of block to set up logfile 
!
  cat(1,0);  printstring(message);  newline
!
! R e a d   n e w   c o m m a n d   l i n e
!
comread:  !Read command file if present
  if  o_pre # "" start 
    open in(o_pre)
    eflag = o_emode
    o_emode = 0
  finish 
resetread:
  o_pre = "";  curprom = ""
  inpos = inlim;  msp = 0
read:
  ci = cmax1;                     ! ci gets reset later, but set here for logfile routine
  pcflag = 0
  if  markpos = 0 then  newprom = prom(sin) c 
  else  newprom = prom(sin+4)
  -> data entry if  o_emode # 0
  pend = 0;  control = -1
  if  inpos >= inlim start ;  !no input available
    if  commandstream = 0 start ;  !on-line
      prepare for input
      if  video = 0 start 
        vt prompt(newprom)
      finish  else  if  newprom # curprom start 
        cat(0,0);  printstring(newprom) ; clear line
      else 
        cat(0,2);  clear line
      finish 
      curprom = newprom
    finish  else  if  video # 0 then  display(o_early);       !So that a log file is correctly executed
    read command line until  inlim > inpos or  commandstream+msp = 0
    vt prompt("") if  video = 0
    control = term if  inpos >= inlim
  finish 
!Reset command variables
again:
  chain = 0;  cmax = cmax1
  get code(first)
  if  control >= 0 and  commandstream = 0 start ;             !control key
    if  code = '\' start ;  !toggle editing mode
      o_emode = o_emode!!1;  toggle = \toggle
      remove pointer
      -> resetread
    finish 
  finish  else  if  code = '-' and  def(ret)&casemask = 'M' start 
    def(ret) = def(ret)!!casebit;       !toggle direction
    control = term if  inpos >= inlim
    get code(first)
  finish 
  toggle = 0
  -> read if  type = 1
  if  code = '?' start 
    cat(1,40);  write(cur_line,0);  clear line
    -> resetread
  finish 
  if  code = '!' start 
    get sym until  sym # ' '
    get name(command)
    -> ignore if  command = ""
    remove pointer
    push window
    win = vdu
    clear frame
    set video mode(0)
    hold = vttype
    hold1 = keeplog;  keeplog = 0;     ! Inhibit logfile
newcommand:

    call out(command)

    if  commandstream = 0 start ;    !No delay if from command file
      new line
      vt prompt("Enter ""!<command>"" or RETURN "); 
      read command line 
      vt prompt ("")
      if  mac(inpos)&127 = '!' start 
        get sym; get sym; get name(command)
        -> newcommand if  command # ""
      finish 
      get sym;  get sym while  sym >= ' '
    finish 
    define video(hold) unless  vttype = hold; ! In case recursive call changed TTYPE
    set video mode(smode)
    pop window
    keeplog = hold1;                 ! Restore logfile
    ! Flag for screen redraw
    ! In case called from secondary file
    if  sin = 2 or  sin = 3 then  main_win = offscreen and  main_diff = unknown
    if  sec_min # 0 then  sec_win = offscreen and  sec_diff = unknown
    -> qread
  finish 
  if  type = 0 start ;                  !repetition number
    sym = code;  number
    -> er2 if  sym >= ' '
    def(ret) = 'M' if  def(ret) = 'm'
    -> read if  cmax = 0;              !no command to repeat
    r(cmax)_count = num
    -> restore
  finish 
  if  code = '%' start 
    get sym;  code = sym
    sym = sym&95
    ->er2 if  code < 'A'
    get sym
    pcflag = code&95
    -> pc(pcflag)
  finish 
  if  control < 0 start ;            !not control key
    def(ret) = 'M' if  def(ret) = 'm'; !restore
    cdef = newdef
    cmax = 0;  treflim1 = trefbase
  finish 
!
! C o m m a n d   i n p u t:  m a i n   l o o p
  ci = cmax;  treflim = treflim1
more:                                   !(command code has been read)
  -> er5 if  type < 4
  -> er0 if  type < 8 and  newlim <= 0; !no changes when Showing
  ci = ci+1;  -> er6 if  ci >= cbound
  num = 1;  scope = 0;  ref = 0;        !defaults
  get sym;                              !next symbol without mapping
  if  sym = '-' start 
    code = code!casebit;  type = symtype(code)&15
    -> er5 if  type < 4
    code = '-' if  code = '+'
    get sym
  finish 
  -> c(type)
c(8):                                   !Find
  num = 0
c(7):                                   !+ Delete, Uncover
c(9):                                   !+ Traverse, Verify
  number
  scope = num
  num = 0;                              !as indicator (not I,O,S,G)
c(6):                                   !+ Insert, Overwrite,
                                        !  Substitute, Get
  get text
  -> er4 if  ref = nullref and  num = 0
  get sym
  num = 1;                              !restore default
c(5):                                   !Erase, Get, etc
c(10):                                  !+ Move, Next, Print
  num = 0 if  code = '#';  number
  -> put
c(11):                                  !open bracket, comma
  ref = chain;  chain = ci
  -> put
c(12):                                  !^
  num = 0;  number
  if  num # 0 start 
    -> erq if  num > 6
    num = num+('X'-1);  num = num+('x'-'Z'-1) if  num > 'Z'
  finish 
  -> put
c(13):                                  !: [temp]
  -> erq unless  'X' <= sym&95 <= 'Z'
  num = sym;  code = '^'
  get sym
  -> put
c(14):                                  !close bracket
  unchain;  -> er3 if  ref = 0
  number
  r(ref)_count = num
c(15):                                  !invert, query
put:
  r(ci)_code = code;  r(ci)_ref = ref
  r(ci)_scope = scope;  r(ci)_count = num
  pend = sym;  get code(normal)
  -> more unless  type = 1
  ci = ci+1;  cmax = ci
  r(ci) = lastcell
  unchain;  -> er3 if  ref # 0
  if  control < 0 start ;            !not control key
    cmax1 = cmax;  treflim1 = treflim
    if  o_emode # 0 or  cscroll = 0 start ; !'home' used
                                     ! or can't scroll command window
      echo command if  video # 0
    else 
      save command
    finish 
    error = 0
  finish 
restore:
  if  error # 0 start 
    cat(1,chalf);  clear line
    error = 0
  finish 
  sym = ret if  sym < ' '
!  %if cur_flag >= ' ' %start
!    at(cur_row,cur_col)
!    print symbol(fpsym)
!    at(cur_row,cur_col)
!    print symbol(0);                   !to flush & position video cursor
!  %finish
  -> execute
!
routine  REPORT(string (255) message)
!Make command error report (to right of command text)
  if  o_emode = 0 start 
    echo command if  cscroll = 0
  finish  else  cat(1,0)
  printstring(message)
end 
!
er0:
  report("   ");  print code(code)
  print string(" when Showing")
  -> erq
er3:
  report("   Brackets")
  -> erq
er4:
  report("   Text for ")
  print code(code)
  -> erq
er2:
  code = sym
c(*):
er5:
   report("   ");  print code(code)
  -> erq
pc(*):
   get sym while  sym >= ' '
   report("   ");  print symbol('%'); print code(code)
  -> erq
er6:
  report("   Size")
erq:
  print symbol('?')
  cmax1 = 0 if  ci > 1
  newline
  save command if  o_emode = 0 and  cscroll # 0;  !(else REPORT echoed)
ignore:
! close in %if commandstream # 0
  -> resetread
!
! Percent commands
pc('S'):                                !Secondary input
  switch if  sin&(\1) # 0
  get sym while  sym = ' '
  get sym if  sym = '='
  if  sym >= ' ' start 
    get name(sec_name)
    sec_flag = 0
    connect edfile(sec)
    if  sec_flag # 0 start 
       event_extra = sec_flag
       sec = 0
       signal  9
    finish 
    if  sec_start2 # sec_lim2 and  byteinteger(sec_lim2-1) # nl start 
      ! no newline at end of file
      ! Cant add it so we just ignore the last line
      sec_lim2 = sec_lim2-1 while  sec_lim2 > sec_start2 and  c 
        byteinteger(sec_lim2-1) # nl
    finish 
  finish 
  sec_line = 0;  !indicator for reset
  switch
  -> read
pc('G'):                                !Get command file
  get sym while  sym = ' '
  get name(o_pre)
!  close in %if commandstream # 0
  -> comread
pc('P'):                                !Put key definitions
  get sym while  sym = ' '
  get name(o_pre)
  open out(o_pre) if  o_pre # ""
  o_pre = ""
  output keydefs
  close out
  -> read
pc('U'):                                !ignore/heed case
  o_mapcase = 1
  o_mapcase = 0 and  get sym if  sym = '-'
  coerce parameters
  -> read
pc('L'):                              !Line width
  get sym while  sym = ' '
  get sym if  sym = '='
  number;  -> erq if  type # 0
  o_width = num
  coerce parameters
  -> read
pc('M'):                                !Margin
  get sym while  sym = ' '
  get sym if  sym = '='
  number;  -> erq if  type # 0
  o_margin = num
  coerce parameters
  -> read
pc('R'):
  get sym while  sym = ' '
  get sym if  sym = '='
  if  sym >= ' ' start 
    neg = 1
    get sym and  neg = -1 if  sym = '-'
    number
    num = num*neg
    -> erq if  type # 0
  finish  else  num = 0
  if  num = 0 then  cur_shift = 0 else  start 
    cur_shift = cur_shift + num
    cur_shift = 0 if  cur_shift < 0
    cur_shift = 1000 if  cur_shift > 1000
  finish 
  cur_win = offscreen
  -> read
pc('D'):                                !Display
  get sym while  sym = ' '
  get sym if  sym = '='
  if  sym >= ' ' start 
    number
    -> erq if  type # 0
    cur_min = num
  finish 
  remove pointer
  coerce parameters
qread:
  cur_win = offscreen;  cur_diff = unknown
  curprom = "";         vdu_row = 255
  -> read
pc('H'):                           !Help
  get sym while  sym = ' '
  remove pointer
  push window
  win = vdu
  hold = vttype
  set video mode(0)
  vt at(o_ctop+1,0);  !in case of error report
  if  sym < ' ' then  view("") c 
    else  get name(o_pre) and  view(o_pre) and  o_pre = ""
  define video(hold) unless  vttype = hold; ! In case recursive call changed TTYPE
  set video mode(smode)
  pop window
  ! Flag for screen redraw
  ! In case called from secondary file
  if  sin = 2 or  sin = 3 then  main_win = offscreen and  main_diff = unknown
  if  sec_min # 0 then  sec_win = offscreen and  sec_diff = unknown
  -> qread
pc('E'):                              !Environment
  remove pointer
  set options
  curprom = ""
  -> read
pc('W'):
  -> erq if  sin&(\1) # 0
  get sym while  sym = ' '
  get sym if  sym = '='
  num = 1;  number
  store deletions if  oldstart2 < cur_start2
  cycle 
    exit  if  delmax <= newlim
    delmax = delmax-1
    num = num-1 if  byteinteger(delmax) = nl
  repeat  until  num = 0
  oldlim1 = cur_lim1;  oldstart2 = cur_start2
  -> read
 pc('X'): pc('Y'): pc('Z'):
  get sym while  sym = ' '
  if  sym >= ' ' start ;                 !definition
    pend = sym
    define(code)
  else ;                 !enquiry
    explain(code)
  finish 
  -> read
pc('Q'):
  get sym while  sym = ' '
  if  sym # ret or  term # ret start 
    pend = sym;  get code(nomac)
    get sym if  sym >= ' '
    explain(code)
  else 
    vt prompt("Key (or :): ")
    cycle 
      cat(0,0);  clear line
      read text(nomac)
      inpos = newdef&posmask;  inlim = newdef>>16
      get code(nomac)
      get sym if  sym >= ' '
      exit  if  code = ':'
      explain(code)
    repeat 
  finish 
  vt prompt("")
  curprom = ""
  -> read
pc('K'):  !define key(s)
  get sym while  sym = ' '
  if  sym # ret or  term # ret start 
    pend = sym;  get code(nomac)
    if  inpos >= inlim start 
      printsymbol('*') unless  ' ' <= code < del
      read command line
    finish 
    define(code)
  else 
    cycle 
      vt prompt("Key = defn: ")
      cat(0,0);  clear line
      read text(nomac)
      inpos = newdef&posmask;  inlim = newdef>>16
      get code(nomac)
      exit  if  code = ':'
      if  inpos >= inlim start 
        cat(0,0) and  vt prompt("Key = defn: *") unless  ' ' <= code < del
        read command line
      finish 
      vt prompt("");       ! 'cause DEFINE can exit back to command
      define(code)
    repeat 
    vt prompt("")
  finish 
  curprom = ""
  -> read
pc('T'):    ; ! Set TAB positions
  get sym while  sym = ' '
  if  sym = '?' start ;                      !Enquiry
    cat(1,0); print string("Tab positions are ")
    for  t = 1,1,maxtab-1 cycle 
      write(o_tabs(t),0); print symbol(',')
    repeat 
    write(o_tabs(maxtab),0); newline
    -> read
  finish 
  get sym if  sym = '='
  t = 0
  cycle 
    number; -> ertab if  type # 0
    get sym if  sym = ',' or  sym = ';'
    -> ertab unless  num&(\255) = 0 and  num > o_tabs(t)
    t = t + 1
    o_tabs(t) = num
  repeat  until  sym < ' ' or  t = maxtab
  o_tabs(t) = num for  t = t+1,1,maxtab;       ! Remaining tabs are set to last one
  -> read
ertab:
  report("   Tabs ") 
  o_tabs(t) = 0 for  t = 1,1,maxtab
  -> erq
pc('B'):    ; ! Backup
  options = o;          ! Save editor options record for re-entry
  copy across if  gdiff # 0
  remove pointer
  update
  switch if  sin&(\1)#0
  consolidate(0,0)
  pop window; win = vdu
  get sym while  sym = ' '
  if  sym >= ' ' then  get name(cur_name)
  cur_flag='B'
  cur_fp = fp
  vt at(vdu_rows-1,0)
  clear line
  set video mode(0)
  return 
pc('A'):                                !Abandon
  update
  switch if  sin&(\1) # 0
  if  cur_change # ceiling and  sin#1 and  sin#3 start 
   !Change made and NOT showing
    cat(1,0)
    vt prompt(" Abandon complete edit? (y/n) "); clear line
    read command line
    vt prompt("")
    get sym;  -> ignore if  sym!casebit # 'y'
    get sym;  -> ignore if  sym >= ' '
    cur_change = ceiling
  finish 
  sym = -1;  
pc('C'):                                !Close
  options = o;          ! Save editor options record for re-entry
  if  keeplog > 0 start 
    select output(logstream); close output
    select output(0); logstream = logstream - 1 if  logstream > 0
  finish 
  remove pointer
  update
  switch if  sin&(\1) # 0
  fp = cur_start2;  cur_lbeg = fp;  set lend
  consolidate(0,0);  !ensure no split line
  cur_flag = sym
  if  sym = ' ' or  sym = '=' start 
    get sym;   get sym while  sym = ' '
    get name(cur_name) if  sym >= ' ';   ! New name specified
  finish 
  pop window;  win = vdu
  vt at(vdu_rows-1,0)
  clear line; 
  set video mode(0)
end ;                                   !END OF EDI
endoffile