!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
!      V6.1 (01/05/85): use VMS command line parsing (ADC at Lattice)
!
!  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.
!
!
!
!
!
!
!
!
!
!
!
!!!!!!!!!!!!!!  Standard Video Terminal Interface  !!!!!!!!!!!!!
!$IF EMAS OR VAX
! ASCII control characters:
constinteger BS=8, TAB=9, LF=10, FF=12, RT=13, ESC=27
constinteger DEL=127
! Terminal mode:
constinteger SINGLE=1<<0, NOECHO=1<<2, PASSDEL=1<<3,
              NOTYPEAHEAD=1<<4, NOTERMECHO=1<<5,
              CONTROLTERM=1<<6, NOEVENT9=1<<7, LEAVECONTROLS=1<<8,
              SPECIALPAD=1<<13, NEWTCP=1<<29, INSERTING=0
constinteger SCREENMODE=controlterm+notermecho+leavecontrols+passdel
! Video FUNction/MODE flag values:
constinteger INTENSE=1, REVERSE=2, UNDERLINE=4, BLINK=8,
              GRAPHICAL=16, SHADE=31
constinteger FULLSCROLL=64, ANYSCROLL=128;  !FUN only
constinteger NOSCROLL=64, FREEZE=128;  !MODE only
recordformat WININFO(byteinteger top,rows,left,cols,
                                   row,col,fun,mode)
externalrecord(wininfo)spec VDU
externalrecord(wininfo)spec WIN
externalintegerspec VTTYPE
!
externalroutinespec DEFINE VIDEO   alias "VTDEFVIDEO"(integer emastype)
externalroutinespec SET VIDEO MODE alias "VTSETVIDEO"(integer mode)
externalroutinespec PUSH WINDOW    alias "VTPUSH"
externalroutinespec POP WINDOW     alias "VTPOP"
externalroutinespec SWOP WINDOW    alias "VTSWOP"
externalroutinespec SET FRAME      alias "VTSETFRAME"(integer t,r,l,c)
externalroutinespec SET MODE       alias "VTSETMODE"(integer m)
externalroutinespec SET SHADE      alias "VTSETSHADE"(integer s)
externalroutinespec CLEAR LINE     alias "VTCROL"
externalroutinespec CLEAR FRAME   alias "VTCFRAME"
externalroutinespec SCROLL         alias "VTSCROLL"(integer t,b,n)
externalroutinespec VT AT          alias "VTSETCURSOR"(integer row,col)
externalroutinespec GOTOXY        alias "VTGOTOXY"(integer x,y)
!
!$IF EMAS
{%recordformat EVENTFM(%integer event,sub,extra, %string(255) message)
{%externalrecord(eventfm)%spec EVENT %alias "VTEVENT"
{%externalroutinespec OPEN INPUT    %alias "VTOPIN"(%integer s,
{                                          %string(255) file)
{%externalroutinespec OPEN OUTPUT   %alias "VTOPOUT"(%integer s,
{                                          %string(255) file)
{%externalroutinespec CLOSE INPUT   %alias "VTCLIN"
{%externalroutinespec CLOSE OUTPUT  %alias "VTCLOUT"
{%externalintegerfnspec OUTSTREAM   %alias "VTOUTS"
!$IF EMAS OR VAX
externalroutinespec SELECT INPUT   alias "VTSELIN"(integer i)
externalroutinespec SELECT OUTPUT  alias "VTSELOUT"(integer i)
externalroutinespec PRINT SYMBOL   alias "VTPSYM"(integer sym)
externalroutinespec SPACE          alias "VTSP"
externalroutinespec SPACES         alias "VTSPS"(integer n)
externalroutinespec NEWLINE        alias "VTNL"
externalroutinespec NEWLINES       alias "VTNLS"(integer n)
externalroutinespec PRINT STRING   alias "VTPSTRING"(string(255) s)
externalroutinespec WRITE          alias "VTWRITE"(integer v,p)
externalroutinespec VTPROMPT       alias "VTPROMPT"(string(255) s)
externalroutinespec READ SYMBOL    alias "VTRSYM"(integername k)
externalintegerfnspec NEXT SYMBOL  alias "VTNSYM"
externalroutinespec SKIP SYMBOL    alias "VTSSYM"
externalroutinespec READ           alias "VTREAD"(integername v)
!$FINISH
constinteger BANTAM=6, ESPRIT=13
!
!!!!!!!!!!!!!!!!!  Other external refs and globals  !!!!!!!!!!!!!!!!!!!!!!!!!
constinteger RET=10
constinteger CASEBIT=32;           !upper<->lower
!
constinteger MAXNAME=255
recordformat EDFILE(integer start1,lim1, {part 1}
                              start2,lim2, {part2}
                              lim, {VMLIM}
                              lbeg,fp,change,flag,
                              line  {line number of current pos},
                              diff  {diff between LINE and ROW},
                byteinteger  top  {top row of sub_window},
                              win  {floating top},
                              bot  {bottom row +1 of sub_window},
                              min  {minimum window size},
                              row  {last row position},
                              col  {last col position},
             string(maxname) name)
!
!** Note that LBEG is such that FP-LBEG = #chars to left of FP
!   even if this means that LBEG lies within the 'gap'
!
!$IF VAX OR APM
constinteger CORDON=0
constinteger BSDEF='g'
!$IF VAX
!$$$%include "IMP_INCLUDE:CONNECT.INC";  !dictionary connection
!$$$%include "IMP_INCLUDE:PAM.INC";  !parameter processing
from Imp include Connect, CLIParse
!ADC!%constinteger MINWIN0=7, MAXWIN0=99
{ADC}constinteger MINWIN0=24,MAXWIN0=99  {More sensible default}
conststring(13) HELPFILE="ECCE_HELP"
conststring(13) DICTFILE="ECCE_DICT"
externalroutinespec VIEW(string(255) S)
externalroutinespec MOVE(integer length,from,to)
!%externalintegerfnspec UINFI(%integer i)
!%externalintegerfnspec CHECKQUOTA(%string(127) filename)
{¬V10IMP %externalstring(72)%fnspec SYSMESS  (%integer i)
{V10IMP} from imp include sysmisc
!
! Special routines from PMM to handle file referencing and i/o
externalintegerfnspec READIN(string(maxname)name file,
   integer extra, integername base,start,fend,limit)
externalintegerfnspec WRITEOUT(string(maxname)name file,
   integer base,start,fend,limit)
externalroutinespec DELETEVM(integer base,limit)
!
externalroutine CONNECT EDFILE(record(edfile)name f)
!  Reference file specified by F_NAME
!   allocate store to hold it + extra bytes specified by F_FLAG
!   place the file in store
!   Return store addresses in F_START1/F_LIM
!          file addresses in F_START2/F_LIM2
!              ( START1 <= START2 <= LIM2 <= LIM )
!   Update F_NAME to full file name
!
! Discard any previous input file
  deletevm(f_start1,f_lim) if f_start1 # 0
! Read the file in
  f_flag = readin(f_name,f_flag>>9,f_start1,f_start2,f_lim2,f_lim)
  if f_flag # 0 start
{¬V10IMP  print string(" *".sysmess(f_flag).": ".f_name)
{V10IMP}    print string(" *".get message(f_flag).": ".f_name)
    newline
    f_start1 = 0;  f_start2 = 0;  f_lim2 = 0
  finish
  f_lim1 = f_start1
!  Ensure that file does not end with partial line
  f_lim2 = f_lim2-1 while f_lim2 # f_start2 and byteinteger(f_lim2-1)#nl
end;   !connect edfile

routine CONNECT DIRECT(string(255) file, integername base)
integer f,s,l
!%externalintegerfnspec connect(%string(127) file,
!                               %integername start,length, %integer mode)
  on event 3,4,9 start
    return
  finish

!  f = connect(file,s,l,0)
  connect file(file,0,s,l)
  base = s {%if f&1 # 0
end
!
!$IF APM
{!UTIL should be in PAM but no nested includes
{%include "I:UTIL.INC" {for STOI, etc -- also PAM flags}
{%include "UTILS:PAM";  !parameter processing
{%constinteger MINWIN0=10, MAXWIN0=10
{%routine MOVE(%integer length,from,to)
{!  %while length > 0 %cycle
{!    byteinteger(to) = byteinteger(from)
{!    to = to+1;  from = from+1;  length = length-1
{!  %repeat
{!  %return
{  *MOVE FROM,A0; *MOVE TO,A1; *MOVE LENGTH,D0
{  *BLE #6
{  *MOVE.B (A0)+,(A1)+; *SUBQ #1,D0; *BNE #-6
{%end
{!
{!!!!!!!!!!!!!!!!!!!!!!   'Connect'  file    !!!!!!!!!!!!!!!!!!!!!!!!!
{%recordformat CONNINFO(%integer memstart,fstart,flim,memlim)
{@16_11B8 %routine CONNECT(%string(255) s, %record(conninfo)%name r)
{%external%routine CONNECT EDFILE(%record(edfile)%name f)
{%record(conninfo) r
{%integer i
{%on %event 3,4,9 %start
{    select output(0)
{    printstring(event_message);  newline
{    f_flag = 1
{    %return
{%finish
{  i = f_start1
{  %if i # 0 %start;  !VM previously allocated
{    i = i+256;  *MOVE i,D6;  !restore heap pointer
{  %finish
{  r_fstart = f_flag>>1;  r_memlim = r_fstart;  !extra space fore and aft
{  f_start1 = 0;  f_lim1 = 0;  f_start2 = 0;  f_lim2 = 0
{  f_change = 0;  f_line = 0
{  connect(f_name,r)
{  r_flim = r_flim-1 %while r_flim > r_fstart %and byteinteger(r_flim-1) # nl
{  f_start1 = r_memstart;  f_lim1 = f_start1;  !VM start
{  f_start2 = r_fstart;  f_lim2 = r_flim;      !file start/limit
{  f_lim = r_memlim;                           !VM limit
{  f_flag = 0
{%END
{!
!$IF EMAS
{%include "ECSC10.PAMINC"
{%constinteger CORDON=2;  !to alleviate effects of echoed typeahead
{%constinteger BSDEF='<'
{%constinteger MINWIN0=7, MAXWIN0=99
{%conststring(24) HELPFILE = "ECSLIB.GENERALY_ECCEVIEW"
{%conststring(24) DICTFILE = "ECSLIB.GENERALY_ECCEDICT"
{%externalroutinespec PROMPT(%string(15) S)
{%externalroutinespec VIEW(%string(255) S)
{!
{%routine MOVE(%integer length, from, to)
{   *LB_LENGTH
{   *JAT_14,<L99>
{   *LDTB_X'18000000'
{   *LDB_%B
{   *LDA_FROM
{   *CYD_0
{   *LDA_TO
{   *MV_%L=%DR
{L99:
{%END
{%INCLUDE "ECSC10.ECCE_FCP"
{!
!$FINISH
!
!!!!!!!!!!!!!!!!!!!  Editor parameters and options  !!!!!!!!!!!!!!!!!
!** NB ORDER -- see VALUE
constinteger ENUMCASES=5, INTCASES=3
!
ownbyte    MAPCASE=1         {1/0 ignore/heed case},
            MARK=0            {1/0 show FP by mark/hilight},
            EARLY=0           {1/0 update early/late},
            DMODE=0           {1/0 insert/replace},
            EMODE=0           {1/0 command/data}
owninteger WIDTH=80          {line width},
            MARGIN=0          {left margin},
            MINWIN=minwin0    {minimum window size}
!Settable at outset only:-
!$IF EMAS OR VAX
owninteger TTYPE=-1
!$IF APM
{%owninteger TTYPE=11;              !terminal type (ERCC coding)
!$FINISH
owninteger WTOP=0, WROWS=255;      !window area top,rows
owninteger WLEFT=0, WCOLS=255;     !window area left,cols
owninteger CTOP=99;                !command row (1st of 2)
owninteger CLEFT=0, CCOLS=255;     !command area left,cols
owninteger MAXWIN=maxwin0
!$IF VAX OR APM
owninteger VMODE=0
!$IF EMAS
{%owninteger VMODE=newtcp
!$FINISH
externalstring(maxname) PRE=""
!** end of OPTIONS
!
bytemap   BVALUE(integer i)
!$IF APM or EMAS
{  %result == byteinteger(addr(mapcase)+i)
!$IF VAX
  result == byteinteger(addr(mapcase)+i<<2)
!$FINISH
end
integermap VALUE(integer i)
  result == integer(addr(width)+(i-enumcases)<<2)
end

!!!!!!!!!!!!!!!!!  Command parameter processing  !!!!!!!!!!!!!!!!!
!
! SET PARAMETERS rewritten by ADC (1-MAY-1985) to do
! standard VMS command line parsing, with an external .CLD file.
! Old code commented out: !ADC!
!
externalroutine SET PARAMETERS(string(maxname)name in,sec,out,
                                string(255) parm)
   !The value of PARM is ignored.  QUALIFIERx routines access the DCL
   !command line directly.

   !NB QualifierI returns zero if the qualifier is not present.  Similarly,
   !   QualifierS returns the null string

   Map Case = 0
   Map Case = 1 if Qualifier Present("MATCH")
   Width    = Qualifier I("WIDTH")
   Margin   = Qualifier I("MARGIN")
   Min Win  = Min Win 0
   Min Win  = Qualifier I("MINWIN") if Qualifier Present("MINWIN")
   Mark     = 0 if Qualifier Present("HILIGHT")
   Mark     = 1 if Qualifier Present("MARK")
   Early    = 0 if Qualifier Present("EARLY")
   Early    = 1 if Qualifier Present("LATE")
   T Type   = Qualifier I("TTYPE")  if Qualifier Present("TTYPE")
   W Top    = Qualifier I("WTOP")
   W Rows   = 255
   W Rows   = Qualifier I("WROWS")  if Qualifier Present("WROWS")
   W Left   = Qualifier I("WLEFT")
   W Cols   = 255
   W Cols   = Qualifier I("WCOLS")  if Qualifier Present("WCOLS")
   C Top    = 99
   C Top    = Qualifier I("CTOP")   if Qualifier Present("CTOP")
   C Left   = Qualifier I("CLEFT")
   C Cols   = 255
   C Cols   = Qualifier I("CCOLS")  if Qualifier Present("CCOLS")
   Max Win  = Qualifier I("MAXWIN") if Qualifier Present("MAXWIN")
   Vmode    = Qualifier I("VMODE")
   Pre      = Qualifier S("PRE")
   In       = Qualifier S("FILE")
   In       = "" if In = "NL:"   {Ugh. Indicates "Creating" a new file}
   Sec      = Qualifier S("SECNAME")
   Out      = Qualifier S("OUTPUT")
   Out      = In if Out =""  { V INFILE  ==  V INFILE INFILE }
end

!ADC/JGH!%externalroutine SET PARAMETERS(%string(maxname)%name in,sec,out,
!ADC/JGH!                                %string(255) parm)
!ADC/JGH!%on %event 5 %start
!ADC/JGH!  printstring(event_message);  newline
!ADC/JGH!  %stop
!ADC/JGH!%finish
!ADC/JGH!  define param("FILE to be edited",in,pam major+pam nodefault)
!ADC/JGH!  define param("SECondary input",sec,0)
!ADC/JGH!  define param("PREdefinition file",pre,0)
!ADC/JGH!  define param("OUTput file (if not same as input)",out,pam newgroup)
!ADC/JGH!  define enum param("NOMATCH,MATCH cases",mapcase,0)
!ADC/JGH!  define enum param("COMmand,DATA edit mode",emode,0)
!ADC/JGH!  define enum param("REPlace,INSert data mode",dmode,0)
!ADC/JGH!  define enum param("HIlight,MARK",mark,0)
!ADC/JGH!  define enum param("LATE,EARLY scrolling",early,0)
!ADC/JGH!  define int param("WIDTH of line",width,0)
!ADC/JGH!  define int param("MARGIN",margin,0)
!ADC/JGH!  define int param("MINWIN",minwin,0)
!ADC/JGH!  define int param("TTYPE",ttype,0)
!ADC/JGH!  define int param("WTOP",wtop,0)
!ADC/JGH!  define int param("WROWS",wrows,0)
!ADC/JGH!  define int param("WLEFT",wleft,0)
!ADC/JGH!  define int param("WCOLS",wcols,0)
!ADC/JGH!  define int param("CTOP",ctop,0)
!ADC/JGH!  define int param("CLEFT",cleft,0)
!ADC/JGH!  define int param("CCOLS",ccols,0)
!ADC/JGH!  define int param("MAXWIN",maxwin,0)
!ADC/JGH!  define int param("VMODE",vmode,0)
!ADC/JGH!  parm = ".N".parm %if parm # "" %and charno(parm,1) = pam_groupsep # ' '
!ADC/JGH!  process parameters(parm)
!ADC/JGH!%end

!!!!!!!!!!!!!!!!!!!  Start of Editor proper !!!!!!!!!!!!!!!!!!!
!
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 STOPPER=-10000;       !loop stop
!$IF EMAS OR VAX
constinteger MINGAP=4096;          !room for manoeuvre
!$IF APM
{%constinteger MINGAP=1024
!$FINISH
!Own variables (plus MACROS):-
owninteger TOGGLE=0
owninteger CASEMASK=¬casebit;      !¬casebit/¬0 to ignore/heed case
owninteger DICT=0
owninteger TERM=ret;               !last symbol read
owninteger SYM=ret;                !last symbol got
!
integer CODE;                      !command code
owninteger LAST='}'
integer REF;                       !text or bracket pointer
integer SCOPE;                     !search limit
owninteger NUM=0;                  !repetition number
integer CONTROL,PEND;              !characters
integer HOLD,HOLDSYM,QSYM;         !work variables
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
!$IF EMAS
{%integer GDIFF
!$IF VAX or APM
constinteger GDIFF=0
!$FINISH
integer FOUNDPOS,FOUNDSIZE;  !matched text info
owninteger MARKPOS=0,MARKLINE=0;   !marker positions
record(edfile) CUR
!
! Video control
integer VIDEO
integer SMODE
integer FSCROLL, CSCROLL
integer CHALF
integer VGAP
owninteger PAN=0
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
owninteger PRINTLINE=0,PRINTED=0;  !for hard-copy
!
ownstring(15) NEWPROM="??", CURPROM=""
!
integer DICTPOS
integer MAC0,MACM4,MACBASE
constinteger MSTBOUND=7
integerarray MSTACK(0:mstbound)
integer MSP;                  !macro stack pointer
!
!Cell format for storage of commands
!$IF EMAS
{%recordformat COMMANDCELL(%byteinteger code,ref,
{                          %halfinteger scope, %integer count)
!$IF VAX OR APM
recordformat COMMANDCELL(byteinteger code,ref,
                          shortinteger scope, integer count)
!$FINISH
constinteger CBOUND=60
record(commandcell) array R(1:cbound)
owninteger CI=0,CMAX=0,CMAX1=0;    !indexing R
!
switch C(4:15), PC('A':95), S(' ':127)
integer TYPE,CHAIN
ownrecord(commandcell) LASTCELL = 0
!
!!!!!!!!!!!!!  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]
ownintegerarray DEF(0:255) =
  ' ', 'G', 'K', ' ',
  ' ', ' ', ' ', ' ',
  bsdef{BS}, 'N'{TAB}, 'M'{LF}, '{'{VT},
  '>'{FF}, '1'{RT}, 'E', 'I',
  '>', ' ', ' ', ' ',
  ' ', ' ', '}', ' ',
  '>'{CAN}, 'E'+'0'<<8, ' ', ' '{ESC},
  ' ', ' ', '}', ' ',
  ' ', '!', '"', '#', '$', '%', '&', '''',
  '(', ')', '*', '+', ',', '-', '.', '/',
  '0', '1', '2', '3', '4', '5', '6', '7',
  '8', '9', ':', ';', '<', '=', '>', '?',
  '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
  'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
  'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
  ' ', 526<<limshift+525{Y}, 527<<limshift+526{Z}, '[',
  '¬', ']', '^', '_',
  '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
  '%'+'H'<<8, 'I', 'J', 'K', 'L', 'M', 'N', 'O',
  'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
  ' ', ' ', ' ', '{', '|', '}', '~', null,
  ' ', ' ', 'F'+'"'<<8, ' '{?c},
  ' ', ' ', ' ', ' '{?g},
  ' ', ' '{?i}, ' ', '}'{ESC:VT,?k},
  '%'+'C'<<8{?l}, 'm'+'0'<<8{?m}, '%'+'D'<<8, ' '{?o},
  'F'+'!'<<8{?p}, 'E'+'0'<<8{?q}, 'S'+'!'<<8{?r}, '^'{?s},
  'K'{?t}, 'E'{?u}, 520<<limshift+516{?v}, 'G'+'0'<<8{?w},
  'I'{?x}, 516<<limshift+512{?y}, 'G'+'0'<<8{?z}, ' ',
  ' ', ' ', ' ', ' ',
  ' ', ' ', ' ', ' '{?C},
  ' ', ' ', ' ', ' '{?G},
  ' ', ' ', ' ', '}'{?K},
  '{'{?L}, '¬'{?M}, ' ', ' '{?O},
  ' ', 'o'+'0'<<8{?Q}, 525<<limshift+520{?R}, ' '{?S},
  ' ', ' ',  ' ' ,' '{?W},
  ' ', ' ',  ' ', ' '{?[},
  ' '{¬}, ' '{]},  ' '{^}, ' '{_},
  '}'{@}, '{'{A}, '}'{B}, '>'{C},
  '<'{D}, 'G'{E}, ' ', ' '{G},
  'H'{H}, ' ', '$'{J}, 'e'+'0'<<8{K},
  'g'{L}, 'k'{M}, ' ', ' '{O},
  ' ', 'I'{Q}, 'K'{R}, ' ',
  'E'+'0'<<8{T}, ' ', ' ', 'E'{W},
  ' ', ' ', ' ', ' '{[},
  ' ', '|'{]}, ' '{^},
  ' '('f'-'^'-1), 'S'+'"'<<8{f}, ' ', ' ',
  'i'+'0'<<8{i}, ' ', '}'{k}, '{'{l},
  ' '('p'-'l'-1), 'F'+'"'<<8,
  ' '('z'-'p'-1), 'n'{z}, ' '(127-'z')

!Indexing MAC:
constinteger MACBOUND=8191
! 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
byteintegerarray MAC(0:macbound)
owninteger INPOS=0,INLIM=0
owninteger NEWDEF=null,CDEF=null,IDEF=null,MDEF=null
owninteger DELS=0,INITDELS=0
owninteger MPOS=0,MLIM=0
owninteger TREFLIM=trefbase,TREFLIM1=trefbase
!
  on event 9,10,14 start;                !End-of-input, Too big
    curprom = ""
    -> ignore
  finish

  -> edistart

!!!!!!!!!  Simple (command) stream opening and closing  !!!!!!!!!!!
!
routine OPEN IN(string(maxname) file)
on event 3,4,9 start
!$IF APM
{  select input(0)
!$FINISH
  printstring(event_message);  newline
  return
finish
!$IF VAX
  set video mode(smode);  !without NOEVENT9
!$FINISH
  open input(1,file);  select input(1)
  commandstream = 1
end
routine OPEN OUT(string(maxname) file)
on event 3,4,9 start
!$IF APM
{  select output(0)
!$FINISH
  printstring(event_message);  newline
  signal 10
finish
  open output(1,file);  select output(1)
end
routine CLOSE IN
  close input;  select input(0);  commandstream = 0
end
routine CLOSE OUT
  close output;  select output(0)
end
!
!!!!!!!!!!!!!!  General-purpose output routines  !!!!!!!!!!!!!!!!!!!
!
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
  if win_top # wtop start
    swop window
  finish
!$IF EMAS OR VAX
  vt at(row,col)
!$IF APM
{  gotoxy(col,row)
!$FINISH
end
routine CAT(integer row,col);    !command window
  if win_top # ctop start
    swop window
  finish
!$IF EMAS OR VAX
  vt at(row,col)
!$IF APM
{  gotoxy(col,row)
!$FINISH
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
integer vrows
  vrows = vdu_rows-cordon;  !effective screen size [temp for Emas]
  wrows = vrows-2 if wrows > vrows-2;   !must have 2 lines for commands
  ctop = vrows-2 if ctop > vrows-2
  wtop = vrows-1 if wtop >= vrows
  wrows = vrows-wtop if wrows > vrows-wtop
  wtop = 0 if wtop = 1 and wtop+wrows > vrows-2
  wcols = vdu_cols if wcols > vdu_cols
  if wtop-2 < ctop < wtop+wrows start
    ctop = wtop+wrows;  !try after file window
    ctop = wtop-2 if ctop+2 > vrows;   !before file window
  finish
  ccols = 40 if ccols < 40
  ccols = vdu_cols if ccols > vdu_cols
  chalf = ccols>>1
  video = vdu_fun
  fscroll = 0;  cscroll = 0
  if vdu_fun&anyscroll # 0 start;    !video can scroll
    if wcols = vdu_cols start;           !full-length rows
      fscroll = 1
      video = video-256 and wrows = wrows+1 if ctop = wtop+wrows
    finish
    cscroll = 1 if ccols = vdu_cols
  finish
  set frame(wtop,wrows,wleft,wcols)
  wrows = wrows-1 if video < 0;   !restore
  win_mode = noscroll
  push window;                        !save
  set frame(ctop,2,cleft,ccols)
  win_mode = noscroll
  mark = 1 if vdu_fun&intense = 0;   !cannot highlight
  if maxwin >= wrows then maxwin = wrows c
  else sec_min = wrows-maxwin-1 and cur_top = sec_min+1
end
!
routine COERCE PARAMETERS
!Make (dynamically alterable) parameters consistent
  cur_min = wrows if cur_min > wrows
  cur_min = 1 if cur_min = 0;   !** allow as disable? **
  mark = 0 if video = 0
  width = 80 unless 5 <= width <= 256
  margin = 0 unless margin < width
  casemask = ¬0;  casemask = ¬casebit if mapcase # 0
end
!
routine HEADER(integer r)
  if video # 0 start
    at(r,0)
!$IF VAX OR EMAS
    print string("<<");  newline
!$IF APM
{    set shade(intense+graphical)
{    print symbol('`') %for r = 1,1,80
{    set shade(0)
!$FINISH
  finish
end
!
routine SAVE COMMAND
!scroll down to preserve command
  swop window if win_top # ctop
  scroll(0,1,-1);  curprom = ""
end
!
!!!!!!!!!!!!!!!!!!!!!!   Misc  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!$IF AnotnowPM
{%routine READ FILE
{!Read in more of the file (at least one line)
{%integer p
{%on %event 9 %start
{  select input(0)
{  %return
{%finish
{  p = cur_lim2
{  %if p = sec_lim2 %start
{    %return %if p >= sec_lim-80
{    select input(3)
{  %else
{    %return %if p >= newlim-80
{    select input(2)
{  %finish
{  %cycle
{    read ch(byteinteger(p))
{    p = p+1
{  %repeat %until byteinteger(p-1) = nl
{  %if cur_lim2 = sec_lim2 %then sec_lim2 = p %else main_lim2 = p
{  cur_lim2 = p
{  select input(0)
{%end
!$FINISH

routine SET LEND
  lend = fp
!$IF AnotnowPM
{  %if fp = cur_lim2 %start
{    read file
!$FINISH
    return if fp = cur_lim2
!$IF AnotnowPM
{ %finish
!$FINISH
!$IF APM
{  *MOVE LEND,A0; *MOVEQ #10,D0
{  *CMP.B (A0)+,D0; *BNE #-4; *SUBQ #1,A0
{  *MOVE A0,LEND
!$IF VAX OR EMAS
  if lend # cur_lim2 start
    lend = lend+1 while byteinteger(lend) # nl
  finish
!$FINISH
end
!
routine SET LBEG
!Establish line start position
  cur_lbeg = fp
  cycle
    if cur_lbeg = cur_start2 start
      cur_lbeg = cur_lim1
      while cur_lbeg # cur_start1 and byteinteger(cur_lbeg-1) # nl cycle
        cur_lbeg = cur_lbeg-1
      repeat
      cur_lbeg = cur_lbeg+(cur_start2-cur_lim1)
      return
    finish
    return if cur_lbeg = cur_start1 or byteinteger(cur_lbeg-1) = nl
    cur_lbeg = cur_lbeg-1
  repeat
end
!
!!!!!!!!!!!!!!  S c r e e n   u p d a t i n g   !!!!!!!!!!!!!!!!!
!
routine DISPLAY LINE
integer k,p
  p = fp;  p = lend if fp > lend
  cycle
    vp = cur_start2 if vp = cur_lim1
    exit if vp = endon
    if vp = p start
      cur_diff = cur_line-win_row;  !NB external ref
!$IF EMAS or VAX
      while vgap > 0 cycle
        vgap = vgap-1;  print symbol(' ')
      repeat
!$FINISH
    finish
    if vp = vplim start
      vplim = -1
      return if joins = 0 and vp-altlimlbeg = win_col-mark
    finish
!$IF AnotnowPM
{    read file %if vp = cur_lim2
!$FINISH
    if vp = cur_lim2 start
      endon = vp
      print string(" **END**")
      exit
    finish
    k = byteinteger(vp);  vp = vp+1
    if k < ' ' or k >= 127 start
      exit if k = nl
      k = '_'
    finish
    print symbol(k)
  repeat
  newline
end
!
routine REMOVE POINTER
  if cur_flag >= ' ' start
    at(cur_row,cur_col)
!$IF VAX or EMAS
    print symbol(cur_flag)
!$IF APM
{   lolight(cur_flag)
!$FINISH
    cur_flag = 0
  finish
end

routine REPAIR LINE
  at(cur_line-cur_diff,fp-cur_lbeg+mark)
  vp = fp
  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
    altlimlbeg = 0;  cur_start2 = fp;  altlim = fp
    set lbeg
  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 # 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,mark);  display line;  r = r+1
            repeat
            exit
          finish
        finish
      finish
      at(r,c+mark);  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,fullpre,pre,count
!
routine SCANBACK
  count = 1
  while pre > 0 cycle
    vp = cur_lim1 if vp = cur_start2
    exit 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
end
!
routine DISPLAY LINES(integer n)
  cycle
    at(r,0)
    print symbol(' ') if 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 # wtop
  remove pointer if cur_flag > 0
  endon = -1
  fullpre = cur_min-1
  fullpre = fullpre>>1 if lend # cur_lim2
  r = cur_line-cur_diff;  pre = r-cur_win
  if pre < 0 start;                     !before start of window
    if pre > -cur_min start;            !not far before
      if fscroll # 0 or r >= cur_top start
        while r < cur_top cycle
          scroll(cur_top,cur_bot-1,-1);        !scroll down
          r = r+1
        repeat
        if cur_win # r start
          cur_win = r
          header(cur_win-1) if cur_win > cur_top
        finish
        display lines(-pre)
        return
      finish
!$IF VAX or EMAS or APM
    finish
!$IF APG
{    %finish %else fullpre = 0
!$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
    if pre < cur_min start;             !not far ahead
      if fscroll # 0 start
        scanback
        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,mark)
          display line
          count = count-1
        repeat until count = 0
        return
      finish
!$IF VAX or EMAS or APM
    finish
!$IF APG
{    %finish %else fullpre = cur_min-1-pre
!$FINISH
  finish
!Complete refresh (including window init)
  pre = fullpre
  scanback
  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
          main_top = cur_bot+1
          main_win = main_top if main_win < main_top
        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
    header(cur_win-1) if cur_win > cur_top
  finish
  display lines(0)
end
!
!!!!!!!!!!!!!!!!!   Command input routines  !!!!!!!!!!!!!!!!!!!!!!!!
!
routine SHOW POINTER
  cur_row = cur_line-cur_diff;  cur_col = fp-cur_lbeg
  at(cur_row,cur_col)
  cur_flag = ' '
  if mark = 0 start
    cur_flag = byteinteger(fp) if fp < lend
!$IF VAX or EMAS
    set shade(intense)
    if cur_flag > ' ' then print symbol(cur_flag) c
    else print symbol('|')
    set shade(0)
 else
    if vttype # bantam then print symbol('~') c
    else print symbol(esc) and print symbol(127);   !splodge
!$IF APM
{    hilight(cur_flag)
{  %else
{    print symbol('~')
!$FINISH
    if fp # cur_lbeg and fp <= lend start
      if fp # cur_start2 then cur_flag = byteinteger(fp-1) c
      else cur_flag = byteinteger(cur_lim1-1)
    finish
  finish
  cur_flag = '_' if cur_flag < ' '
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(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 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,pos,lim
on event 9 start
  if commandstream # 0 start
    close in
  else;                           !input 0 EOF
!$IF VAX
    set video mode(smode!noevent9);  !to force use of TT
!$IF APM
{    open input(0,":T");  select input(0)
{    read symbol(q);  !!***TEMP ignore spurious NL***
!$FINISH
  finish
  signal 10
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
  if mode >= 0 start;    !data entry
    length(newprom) = 2
    if sin = 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
again:
!$IF APM
{  %if mode = inserting %start
{    insertpos = fp
{    insertpos = lend %if insertpos > lend
{  %finish
!$FINISH
  at(cur_line-cur_diff,fp-cur_lbeg+mark) if mode >= 0
  cycle
    read symbol(term)
    unless ' ' <= term <= del 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)
          if term < ' ' then printsymbol('_') else print symbol(term)
          mac(q) = term;  q = q+1;  q = q-1 if q&127 = 0
          pos = pos+1
        repeat
      finish
    finish else if term = del start
      dels = dels+1
!$IF EMAS
{      initdels = initdels+1 %if q = p
{      curprom = "";  !in case corrupt
!$IF APM (DEL passed through without action)
{      %if q > p %start
{        q = q-1
{        %if mode = replacing %and 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
{                                          ! as BS SP BS or BS DC
{      %else %if mode >= 0 %and fp # cur_lbeg
{        %if fp > lend %or mode = replacing %start
{          %if fp = cur_start2 %then consolidate(1,sin) %else fp = fp-1
{        %else;  !inserting: erase back
{          printsymbol(del)
{          split(0)
{          consolidate(1,-1)
{          cur_change = altmin %if altmin < cur_change
{          altlim = floor;  altmin = ceiling
{        %finish
{        -> again
{      %finish
!$FINISH
    else
      mac(q) = term;  q = q+1;  q = q-1 if q&127 = 0
    finish
  repeat
!$IF APM
{  insertpos = 0
!$FINISH
  newdef = q<<16+p and return if q > p
  newdef = null
!$IF EMAS OR VAX
  return if mode < 0;    !not data entry
  dels = 0 and initdels = 0 if fp >= lend
!$IF EMAS
{  %while initdels # 0 %and fp # cur_lbeg %cycle
{    %if fp = cur_start2 %then consolidate(1,sin) %else fp = fp-1
{    initdels = initdels-1
{  %repeat
!$FINISH
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_12{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_02{~}, 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
  prepare for input
  cat(0,0);  print code(code);  print symbol('>')
  curprom = ""
  clear line
  read text(standard)
  mdef = newdef
  remove pointer if emode # 0;   !in data entry mode
end
!
routine READ NUMBER
integer pos,lim,m
  prepare for input
  cat(0,0);  print code(code);  print symbol('>')
  curprom = ""
  pos = inpos;  lim = inlim;  m = msp
  msp = 0
  clear line;  read command line
  remove pointer if 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

!$IF EMAS
{%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
!$FINISH

routine MAKE ROOM(integer mingap)
!The gap has become too small: shuffle to enlarge it
integer amount,gap
!$IF EMAS
{  copy across %if gdiff # 0
!$FINISH
  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
!$IF EMAS
{        copy across %if gdiff # 0
!$FINISH
        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
!$IF EMAS
{      copy across %if gdiff # 0
!$FINISH
      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
      cur_lim1 = cur_lim1-1
      if cur_lim1 < altmin start
        altmin = cur_lim1
        if cur_lim1 < oldlim1 start
!$IF EMAS
{          copy across %if gdiff # 0
!$FINISH
          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
    fp = cur_start2;  cur_lbeg = fp;  set lend
    consolidate(0,0)
    fp = newfp
  else
    fp = newfp
    return if cur_lbeg <= fp <= lend
  finish
  set lbeg;  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 AnotnowPM
{  read file %if fp = cur_lim2
!$FINISH
  if lend # cur_lim2 start
!$IF APM
{    *MOVE LEND,A0; *MOVEQ #10,D0
{    *CMP.B (A0)+,D0; *BNE #-4; *SUBQ #1,A0
{    *MOVE A0,LEND
!$IF VAX OR EMAS
    lend = lend+1 while byteinteger(lend) # nl
!$FINISH
  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
  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
  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 start
      make room(mingap) if oldstart2+gdiff-cur_lim1 <= mingap
      while fp < lend cycle
        byteinteger(cur_lim1) = mac(fp)
        cur_lim1 = cur_lim1+1;  fp = fp+1
      repeat
      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
    finish else if fp < lend then fp = fp+1 c
    else cur_lbeg = cur_lbeg-1
    byteinteger(cur_lim1) = mac(pos)
    cur_lim1 = cur_lim1+1;  pos = pos+1
  repeat until pos = lim
  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
    main = cur;  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
    sec = cur;  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,cur_fp-cur_lbeg);  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 = 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
    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+margin if lend # cur_lim2
  -> ok
!
s('}'):                                 !Cursor down
  hold = fp-cur_lbeg
  -> no if line after = 0
  fp = fp+hold if fp # cur_lim2
  -> oklast

s('{'):                                 !Cursor up
  hold = fp-cur_lbeg
  fp = cur_lbeg+hold and -> no if line before = 0
  hold = hold+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 fp-cur_lbeg >= 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 and sin >= 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+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
  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)
!$IF EMAS
{  copy across %if gdiff # 0
!$FINISH
  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 = wcols>>1;  pan = pan-num
    finish
  finish else if last = '>' start
    num = lend-fp
    -> next if num <= 0
    if fp = cur_lbeg+pan+wcols start
      num = wcols>>1;  pan = pan+num
    finish
  finish else if last = '{' start
    update
    num = cur_line-cur_diff-cur_win
    num = cur_min-2 if num <= 0
    num = 1 if num <= 0
  else
    update
    num = cur_bot-1-(cur_line-cur_diff)
    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
  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
  -> 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
  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
  -> 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
!
constinteger termbit=1<<16, lastbit=1<<15, dummy='a'-1
s('Q'):                                 !Query spelling
!$IF APM
{  complain("Dictionary not available")
!$IF EMAS OR VAX
  if dict = 0 start
    connect direct(dictfile,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:
  -> no if qsym >= 'a'
  qsym = qsym+casebit
  -> qagain
!$FINISH

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)
  cur_lbeg = cur_lbeg+foundsize;  fp = fp+foundsize;  cur_start2 = fp
  altlim = cur_start2 if altlim < cur_start2
!
s('I'):                                 !+Insert
  -> no if fp-cur_lbeg > width and code # 'S'
  if ref = 0 start
    -> over if fp >= lend
    split(mingap)
!$IF EMAS OR VAX
    vgap = wcols - (lend-cur_lbeg+mark)
    vgap = 10 if vgap < 10
    display(0)
    read text(inserting)
    idef = newdef
    if idef >= macro start
      insert(idef)
      altlim = lend+1;  altlimlbeg = altlim;  !to remove spaces
    finish else repair line;    !to remove spaces
!$IF APM
{    display(0)
{    read text(inserting)
{    idef = newdef
{    %if idef >= macro %start
{      insert(idef)
{      altlim = floor;  altmin = ceiling; !up-to-date
{    %finish
!$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;  set lend
  finish
  split(0);  !(to update?)
  if cur_lim1 > oldlim1 start
    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;  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
   -> ok
!
s('i'):                                 !Insert back
  fp = lend if fp > lend
  store deletions if oldstart2 < cur_start2
  -> no if delmax <= lastdelmax
  split(mingap>>1)
!$IF EMAS
{    copy across %if gdiff # 0
!$FINISH
  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
  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
!$IF EMAS
{    copy across %if gdiff # 0
!$FINISH
  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
  -> ok
!
s('O'):                                 !Overwrite
  -> no if fp-cur_lbeg > width
over:
  if ref = 0 start
    display(0)
    read text(replacing)
    idef = newdef
    if idef >= macro start
      overwrite(idef)
      altlim = floor;  altmin = ceiling; !up-to-date
    finish
    repair line if dels # 0
    -> 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(0)
!$IF APM
{    read text(dmode)
!$IF VAX OR EMAS
    read text(0)
!$FINISH
    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 # 0 or lend = cur_lim2 start
        repair line
        -> read
      finish
      if dmode = replacing then overwrite(newdef) else insert(newdef)
      altlim = floor;  altmin = ceiling; !up-to-date
    finish
    repair line if dels # 0
    exit if term # ret or dmode = inserting
    hold = line after
    fp = fp+margin if lend # cur_lim2
  repeat
controlterm:
  control = term;  cur_flag = 0
  -> again
!
!!!!!!!!!!!!!!!!!!!!!!!  end of data entry  !!!!!!!!!!!!!!!!!!!!!

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(0)
        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,fp-cur_lbeg+mark)
    finish else printsymbol(':')
    read text(standard)
    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(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 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
  sin = -1;  hold = line before;  sin = 0
  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 fp-cur_lbeg > 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+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) = ' '
    fp = fp+1 while byteinteger(fp) > ' '
    size = fp-cur_lbeg
    if size > width start
      result = false if byteinteger(fp1) # ' '
      fp = fp1
      result = true
    finish
    if fp = lend start
      fp1 = fp+1
      fp1 = cur_start2 if fp1 = cur_lim1
!$IF AnotnowPM
{      read file %if fp1 = cur_lim2
!$FINISH
      result = false if fp1 = cur_lim2
      foundpos = fp1
      fp1 = fp1+1 while byteinteger(fp1) = ' '
      result = false if byteinteger(fp1) = nl or fp1-foundpos < margin
      foundpos = fp1
      fp1 = fp1+1 until byteinteger(fp1) <= ' '
      foundsize = fp1-foundpos;  size = size+1+foundsize
      result = true if size > 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
      cur_lbeg = fp-size
    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 < margin cycle
      byteinteger(cur_lim1) = ' ';  cur_lim1 = cur_lim1+1
      hold = hold+1
    repeat
    oldlim1 = cur_lim1
    cur_lbeg = fp-margin
  finish
  -> ok if type # 0
  -> no
!
s('@'):                                 !'at' Column NUM
  -> fail if lend = cur_lim2
  hold = width-(lend-fp)
  num = hold if hold < num
  if fp >= lend start
    fp = cur_lbeg+num and -> next if cur_lbeg+num >= lend
    fp = lend
  finish
  hold = fp-cur_lbeg-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) # ' '
      cur_lim1 = cur_lim1-1;  cur_lbeg = cur_lbeg+1
      altmin = cur_lim1 if altmin > cur_lim1
      hold = hold-1
    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
  cur_start2 = fp;  altlim = cur_start2 if altlim < cur_start2
  -> next
s('|'):                                !Toggle Destructive Mode
  -> disallowed if sin > 0
  if sin = 0 start
    fp = lend if fp > lend
    -> fail if fp-cur_lbeg > width
    split(0);  altlim = floor+1;  sin = -1
    markpos = 0
  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('$'):                                 !switch inputs
  fp1 = markpos;  fp = lend if fp > lend
  switch
  if sin = 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;  -> err if sym < '@'
      code = sym&31
      if code = esc start
        get sym
        if sym = '?' start;    !canonical 2nd leadin
          get sym;  sym = sym!!96
        finish
        code = sym+128
      finish
    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
!
routine SET OPTIONS
integer i,k
constinteger showpointer=1
conststring(15)array text(0:enumcases+intcases-1) =
  "Case-matching [",
  "Show position [",
  "Update [",
  "Data mode [",
  "Edit mode [",
  "Line width [",
  "Left margin [",
  "Min. window ["
conststring(7)array OPTNAME(0:enumcases*2-1) =
  "NOMATCH", "MATCH",
  "HILIGHT","MARK",
  "LATE", "EARLY",
  "REPLACE", "INSERT",
  "COMMAND", "DATA"

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

cat(1,0)
printstring( "RETURN to step through   value or 'x' to alter   ':' to exit")
newline
cycle
  for i = 0,1,enumcases+intcases-1 cycle
    cat(0,0)
    printstring(text(i))
    minwin = cur_min;  !relevant current setting
    show(i)
    printstring("] :")
    clear line
    read command line
    get sym
    if sym # ret start
      if sym = ':' start
        save command;  !ie last shown
        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 # minwin start
          cur_min = minwin
          cur_win = offscreen;  cur_diff = unknown
        finish
      else
        bvalue(i) = bvalue(i)!!1
        cur_diff = unknown if i = showpointer
      finish
      coerce parameters
      i = i-1
    finish
  repeat
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 = '"' 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",
  "reserved",
  "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:
  lastcell_code = ')';  lastcell_count = 1
!Stored text pointers
  cdef = null;  idef = null;  mdef = 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
!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
!$IF EMAS
{  gdiff = 0
{  %unless cur_lim1 <= cur_lim2 <= cur_lim %start
{    newlim = cur_lim-1024
{    gdiff = newlim-cur_lim2
{  %finish
!$IF APM
{  newlim = cur_lim-1024
!$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
  vgap = 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;  set lend
!
!Initialise video info
  ![XOR so that VMODE can, awkwardly, suppress]
  smode = vmode!!screenmode!!specialpad
!$IF VAX or EMAS
  define video(ttype) and ttype = -2 if 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
!$FINISH
  prompt("")
  set video mode(smode)
  set windows
  cur_bot = wrows;  cur_min = minwin
  cur_win = offscreen;  cur_diff = unknown
  coerce parameters
  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 pre # "" start
    open in(pre)
    emode = 0
  finish
resetread:
  pre = "";  curprom = ""
  inpos = inlim;  msp = 0
read:
  if markpos = 0 then newprom = prom(sin) c
  else newprom = prom(sin+4)
  -> data entry if emode # 0
  pend = 0;  control = -1
  if inpos >= inlim start;    !no input available
    if commandstream = 0 start;    !on-line
      prepare for input
      if newprom # curprom or video = 0 start
        curprom = newprom
        cat(0,0);  printstring(curprom)
      finish
      cat(0,2);  clear line
    finish
    read command line until inlim > inpos or commandstream+msp = 0
    control = term if inpos >= inlim
  finish
!Reset command variables
again:
  chain = 0;  cmax = cmax1
  get code(first)
  if control >= 0 start;               !control key
    if code = '¬' start;    !toggle editing mode
      emode = emode!!1;  toggle = ¬toggle
!$IF APM
{      dmode = dmode!!1 %if toggle = 0;  !insert<->replace
!$FINISH
      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 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 until sym # ' '
    -> pc(code&95)
  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 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 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
pc(*):  c(*):
er5:
   report("   ");  print code(code)
  -> erq
er6:
  report("   Size")
erq:
  print symbol('?')
  cmax1 = 0 if ci > 1
  newline
  save command if 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 if sym = '='
  if sym >= ' ' start
    get name(sec_name)
    sec_flag = 0
    connect edfile(sec)
    sec_flag = 0
  finish
  sec_line = 0;  !indicator for reset
  switch
  -> read
pc('G'):                                !Get command file
  get name(pre)
  close in if commandstream # 0
  -> comread
pc('P'):                                !Put key definitions
  get name(pre)
  open out(pre) if pre # ""
  pre = ""
  output keydefs
  close out
  -> read
pc('U'):                                !ignore/heed case
  mapcase = 1
  mapcase = 0 and get sym if sym = '-'
  coerce parameters
  -> read
pc('L'):                              !Line width
  get sym if sym = '='
  number;  -> erq if type # 0
  width = num
  coerce parameters
  -> read
pc('M'):                                !Margin
  get sym if sym = '='
  number;  -> erq if type # 0
  margin = num
  coerce parameters
  -> read
pc('D'):                                !Display
  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 = ""
  -> read
pc('H'):                           !Help
  remove pointer
!$IF EMAS or VAX
  push window
  win = vdu
  vt at(ctop+1,0);  !in case of error report
!$IF EMAS
{  set video mode(0)
!$FINISH
!$IF EMAS OR VAX
  if sym < ' ' then view(helpfile) c
  else get name(pre) and view(pre) and pre = ""
!$IF EMAS
{  set video mode(smode)
!$FINISH
!$IF EMAS OR VAX
  pop window
  -> qread
!$IF APM
{  complain("Help not available")
!$FINISH
pc('E'):                              !Environment
  remove pointer
  set options
  curprom = ""
  -> read
pc('W'):
  -> erq if sin # 0
  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'):
  if sym >= ' ' start;                   !definition
    pend = sym
    define(code)
  else;                  !enquiry
    explain(code)
  finish
  -> read
pc('Q'):
  if sym # ret or term # ret start
    pend = sym;  get code(nomac)
    get sym if sym >= ' '
    explain(code)
  else
    cycle
      cat(0,0);  printstring("Key (or :): ");  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
  curprom = ""
  -> read
pc('K'):  !define key(s)
  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
      cat(0,0);  printstring("Key = defn: ");  clear line
      read text(nomac)
      inpos = newdef&posmask;  inlim = newdef>>16
      get code(nomac)
      exit if code = ':'
      if inpos >= inlim start
        printsymbol('*') unless ' ' <= code < del
        read command line
      finish
      define(code)
    repeat
  finish
  curprom = ""
  -> read
pc('A'):                                !Abandon
  update
  switch if sin&(¬1) # 0
  if cur_change # ceiling start
   !Change made
    printstring(" Abandon complete edit? (y/n) ")
    read command line
    get sym;  -> ignore if sym!casebit # 'y'
    get sym;  -> ignore if sym >= ' '
  finish
  sym = -1;  cur_change = ceiling
pc('C'):                                !Close
  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
  main = cur
  pop window;  pop window
!$IF EMAS OR VAX
  vt at(vdu_rows-1,0)
!$IF APM
{  gotoxy(0,vdu_rows-1)
!$FINISH
  clear line; !  print symbol(rt);  print symbol(0);  !to flush
  set video mode(0)
end;                                    !END OF EDI
!
!$IF VAX
externalroutine DISCONNECT EDFILE(record(edfile)name out)
integer i,k
  if out_flag < 0 or out_change < 0 start
    deletevm(out_start1,out_lim)
    return
  finish
  i = out_lim2-out_start2;  !lower half
  move(i,out_start2,out_lim1);  ! concatenated to upper
  out_lim1 = out_lim1+i
  cycle
    i = writeout(out_name,out_start1,out_start1,out_lim1,out_lim)
    exit if i = 0
{¬V10IMP    print string(" *".sysmess(i).": ".out_name)
{V10IMP}    print string(" *".get message(i).": ".out_name)
    newline
    print string(" Please supply alternative file-name: ")
    select input(0);  prompt("")
    out_name = ""
    read symbol(k) until k # ' '
    cycle
      out_name = out_name.tostring(k);  read symbol(k)
    repeat until k < ' '
    newline
  repeat
end
!$IF APM
{%external%routine DISCONNECT EDFILE(%record(edfile)%name out)
{%label nogo
{%integer i,k
{%on %event 9,4 %start
{  select output(0)
{  printstring("*Unable to write to ".out_name." [".event_message."]")
{  newline
{  printstring("Please supply alternative filename [eg PUB:...] ")
{  select input(0);  prompt("")
{  out_name = ""
{  read symbol(k) %until k # ' '
{  %cycle
{    k = k-32 %if k > 96
{    out_name = out_name.tostring(k);  read symbol(k)
{  %repeat %until k < ' '
{  newline
{%finish
{  %if out_flag >= 0  %and out_change >= 0 %start
{    open output(2,out_name)
{    select output(2)
{    i = out_start1
{    %while i # out_lim1 %cycle
{      print ch(byteinteger(i));  i = i+1
{    %repeat
{    i = out_start2
{    %while i # out_lim2 %cycle
{      print ch(byteinteger(i));  i = i+1
{    %repeat
{    close output
{    select output(0)
{  %finish
{  i = out_lim+256
{  *cmp i,d6
{  *bne nogo
{  i = out_start1+256
{  *move i,d6
{nogo:
{%end
!$FINISH
endoffile