{----}
{    }
{ IE }
{    }
{----}

%external %string (255) Copyright %alias "IE_C_RMM" = "Copyright (C) Richard M. Marshall 1984"

{A Terminal Independent Keypad Editor}

{RMM October 1982}

{Compilation options:}
{APM}
!\APM!         {not APM "Imp"}
!VMS!
{\VMS}         {not VMS name conventions}
!UNIX!
{\UNIX}        {not UNIX case conventions}

%include "SYSDEP.INC"

%include "TERMINAL.INC"

%include "IE.INC"

%constant %integer yes = -1
%constant %integer no  = 0

%constant %integer delta minus = -1
%constant %integer absolute    = 0
%constant %integer delta plus  = 1
%constant %integer query = 2
%constant %integer Infinite  = 3

%constant %integer Forwards = 1,
                   Backwards = 2

%constant %integer insert character    = 0
%constant %integer overwrite character = 1

%constant %integer max text length = 100
%record %format text fm (%byte length %or %c
                         %byte %array text (0 : max text length) %or %c
                         %string (max text length) string)

%own %record (Text fm) Size Of Text Fm = 0

%constant %integer Max Command = 71

%record %format Key Definition FM (%byte Type,
                                   %byte Value,
                                   %record (Text fm) %name Text,
                                   %record (Key Definition FM) %name Stack)
%constant %byte Undefined  = 0,
                Letter     = 1,
                Command    = 2,
                Macro      = 3,
                Busy Macro = 4

%own %record (Key Definition FM) %array Keys (0:255) = 0 (*)

%own %record (Key Definition FM) %name Current Macro == nil,
                                       Current Definition == nil
%own %integer Macro Pos = 0,
              Definition Pos = 0

%constant %integer IO error = 9
%constant %integer shift = 32

!VMS!%external %string (255) %function %spec expand file name %c
!VMS!                                        %alias "IE_EXPAND" %c
!VMS!                                        (%string (255) fn)
!VMS!%external %routine %spec normalise file name %c
!VMS!                         %alias "IE_NORMALISE" %c
!VMS!                         (%string (*) %name file)
!VMS!%external %string (255) %function %spec out file name
!VMS!%own %string (255) Grand Default File Name = ""

%external %integer Changed Main Buffer %alias "IE_CHANGED_FLAG"

{APM}%external %record (Preload fm) %name Preload List -
{APM}                                     %alias "IE_PRELOADS" == Nil
!\APM!%external %record (Preload fm) %name Preload List -
!\APM!                                     %alias "IE_PRELOADS"==0

!UNIX!   %constinteger interrupted = 0


%external %predicate %spec Exists (%string (255) File Name)

{APM} %external %string (255) %function %spec I To S (%integer I, F)
{APM} %external %integer %function %spec Free Store

%own %string (255) broadcast = ""
%own %integer broadcast window open = no
%own %integer TT wait = no
%routine %spec display broadcast
%routine %spec clear broadcast window

%own %string (255) in file = "",
                   out file = ""

%own %integer From Journal = 0,
              Journal Frequency = 0,
              Debug = 0,
              Keep Journal = 0,
              Always Keep Journal = No

%own %string (255) profile = "",
                   keyboard = ""

%own %integer window top = 0,
              window bottom = 0,
              start line = 0,
              start pos = 0

%own %integer at rest = yes,
              Top X = -1

%own %integer write back = Yes,
              Beep Mute = No,
              Forbid Exit = Yes,
              Wiggle = No,
              Put Buffer Verify = No,
              Join At End = No

%own %integer Last Search Direction = 0

%external %string (80) %spec default EOF marker %alias "IE_EOF_MARKER"

%own %string (80) EOF marker = ""

%own %integer options = 0

%external %record (buffer fm) %name buffers %alias "IE_BUFFERS"
%own %record (Buffer fm) %name current buffer == nil,
                               main == nil

%own %record (line fm) %name current == nil,
                             TOS == nil,
                             BOS == nil,
                             deleted == nil,
                             Found Line == nil
   
%own %record (line fm) %name copy position == nil
%own %record (buffer fm) %name copy buffer == nil


%own %integer CX = 0, CY = 0,              {current screen coordinates}
              pos = 0,                     {current character on the line}
              desired X           = -1,    {for Wiggle}
              TAB Wiggle          = No,
              finished            = no,
              show TAB            = no,            {display TAB space with lines}
              auto indent         = yes,
              auto line break     = no,
              Left Margin         = 1,
              Right Margin        = 80,
              Justify             = No,
              case significance   = no,
              TAB width           = 3,
              search before replace = yes,
              free cursor         = no,
              max lines = 0,
              screen lines        = 0,             {no of lines actually displyed}
              typing mode         = insert character,
              Repeat Count        = 0,             {not repeating}
              Repeat Place = 0,
              found = 0,
              failed = 0,
              Found Pos = 0,
              Found Length = 0,
              Wrap Search = No,
              Back Search = No

%own %integer beep column = 75

%own %record (text fm) search string = 0
%own %record (text fm) replace search = 0
%own %record (text fm) replace string = 0
%own %record (text fm) Repeat Text = 0

{SET CURSOR}

%routine set cursor
   %if Top X < 0 %start
      cursor (CX, CY)
   %else %if At Rest = Yes
      Cursor (Top X, Window Top)
   %finish
%end {of set cursor}

%own %integer reported = No

{CLEAR WINDOW}

%routine clear window
   %integer Y

   cursor (0, window top)
   clear line
   %for Y = window top + 1, 1, window bottom %cycle
      Cursor (0, Y)
      Clear Line   
   %repeat
   Reported = No
%end {of clear window}

{UPPER CASE}

%integer %function upper case (%integer ch)
   %result = ch - shift %if 'a' <= ch <= 'z'
   %result = ch
%end {of upper case}

{LOWER CASE}

%integer %function lower case (%integer ch)
   %result = ch + shift %if 'A' <= ch <= 'Z'
   %result = ch
%end {of lower case}

{FLIP CASE}

%integer %function flip case (%integer ch)
   %result = ch + shift %if 'A' <= ch <= 'Z'
   %result = ch - shift %if 'a' <= ch <= 'z'
   %result = ch
%end {of flip case}

{PUNCTUATION}

%predicate punctuation (%integer ch)
   %false %if 'A' <= ch <= 'Z' %or %c
              'a' <= ch <= 'z' %or %c
              '0' <= ch <= '9' %or %c
              ch <= ' '
   %true
%end {of punctuation}

{ALPHANUMERIC}

%predicate alphanumeric (%integer ch)
   %true %if 'A' <= ch <= 'Z' %or %c
             'a' <= ch <= 'z' %or %c
             '0' <= ch <= '9'
   %false
%end {of alphanumeric}

{FORMATTING}

%predicate formatting (%integer ch)
   %true %if ch <= ' '
   %false
%end {of formatting}

{CHECK FOR CCs}

%routine check for CCs (%record (line fm) %name L)
   %integer p

   p = 1
   %while p <= L_length %cycle
      %if L_text (p) < ' ' %or -
          L_Text (P) >= DEL %start
         L_bits = L_bits!contains CCs
         %return
      %finish
      p = p + 1
   %repeat
   L_bits = L_bits & (\contains CCs)
%end {of check for CCs}

{FIND X}

%integer %function find X (%record (line fm) %name L,
                           %integer P)
   {This function calculates what screen coordinate is correct for}
   {the given column on the given line}
   %integer res, pos, extra

   %if L_bits = untouched %start
      L_bits = 0
      check for CCs (L)
   %finish
{**   extra = P - current_length - 1
   extra = P - L_length - 1
   extra = 0 %if extra < 0
   P = L_length + 1 %if P > L_length
   %if L_bits &contains CCs = 0 %start
      {easy case - no TABs}
      res = P - 1          {coords start at 0, columns at 1}
   %else
      res = 0
      pos = 1
      %while pos < P %cycle
         %if L_text (pos) = TAB %start
{*            res = res + 1 %while res & 7 # 0
            res = res&(\7) + 8
         %else %if L_Text (pos) < ' ' %or -
                   L_Text (pos) = DEL %or -
                   L_Text (pos) >= ' '!128
            res = res + 2
         %else %if DEL < L_Text (Pos) < ' '!128
            Res = Res + 3
         %else
            res = res + 1
         %finish
         pos = pos + 1
      %repeat
   %finish
   res = res + extra
   res = Terminal Width - 1 %if res >= Terminal Width
   %result = res
%end {of find X}

{FIND POS}

%routine Find Pos (%record (Line fm) %name L,
                   %integer %name X,
                                  P)
   %integer NP, NX

   {This routine takes the X position on the screen, the old column position}
   {and returns the new column position and X appropriately for the new line L}
   %if L_Bits = Untouched %start
      L_Bits = 0
      Check for CCs (L)
   %finish
   %return %if X = Terminal Width - 1 %or {off edge of screen - can get away with it} %c
               X = 0           {easy case - nothing to do!}
   %if L_Bits&Contains CCs = 0 %start
      {no TABs - easy}
      P = X + 1
      {X is unchanged}
   %else
      {contains TABs}
      NX = 0
      NP = 1
      %while NP <= Current_Length %cycle
         %if Current_Text (NP) = TAB %start
            NX = NX&(\7) + 8
         %else %if Current_Text (NP) < ' ' %or -
                   Current_Text (NP) = DEL %or -
                   Current_Text (NP) > ' '!128
            NX = NX + 2
         %else %if DEL < Current_Text (NP) < ' '!128
            NX = NX + 3
         %else
            NX = NX + 1
         %finish
         NP = NP + 1
         %if NX >= X %start
            P = NP
            Desired X = X
            TAB Wiggle = Yes
            X = NX
            %return
         %finish
      %repeat
      {nasty - off the end of the line}
      P = NP + (X - NX) + 1
      {X is unchanged}
   %finish
%end {Find Pos}

{RESET CURSOR}

%routine reset cursor
   CX = find X (current, pos)
   set cursor
%end {of reset cursor}

{PUT CC}

%routine put CC (%integer ch)
   Feature
   %if Ch = DEL %start
      Put String ("^?")
   %else %if Ch > DEL
      Graph On
      Degree Sign
      Graph Off
      Ch = Ch&127
      -> CC %if Ch < ' '
      Put Symbol (Ch)
   %else
   CC:
      Put Symbol ('^')
      Put Symbol (Ch + '@')
   %finish
   Normal
%end {of put CC}

{REPORT}

%routine report (%string (255) message)
   %if options & silent # 0 %start
      cursor (0, window bottom)
   %else
      cursor (0, window bottom)
      clear line
      put symbol (' ')                          {a place for the cursor to sit}
      put string (message)
      put symbol (CR)
   %finish
   flush symbols
   Reported = Yes
%end {of report}

{PUT CH}

%routine put ch (%integer ch)
   %if ch < ' ' %or ch >= DEL %start
      put CC (ch)
   %else
      put symbol (ch)
   %finish
%end {of put ch}

{PRINT LINE}

%routine print line (%record (line fm) %name L, 
                     %integer from, 
                              Y)
   %integer I, col, pos, ch = ' ', spaces = 0

   Insert Off                       {Under No Circumstances can this stay On!!!}
   %if L_bits & pseudo line # 0 %start
      %if From < Length (EOF Marker) %start
         Cursor (0, Y)
         Clear Line
         Graph On
         put string (EOF marker)
         Graph Off
      %finish
   %else
      %if L_bits = untouched %start
         check for CCs (L)
      %finish
      col = find X (L, from)
      %if Col = 0 %start
         Clear Line
      %else
         Erase Line
      %finish
      pos = from
      %while pos <= L_length %cycle
         ch = L_text (pos)
         col = col + 1
         %if col >= Terminal Width %and %c
             pos < L_Length %start
            cursor (Terminal Width - 1, Y) %if spaces > 0
            Graph On
            Left T
            Graph Off
            %exit
         %finish
         %if ch = ' ' %start
            spaces = spaces + 1
         %else
            %if spaces > 0 %start
               %if spaces < 5 %start
                  %cycle
                     put symbol (' ')
                     spaces = spaces - 1
                  %repeat %until spaces = 0
               %else
                  cursor (col - 1, Y)
                  spaces = 0
               %finish
            %finish
            %if ' ' < ch < DEL %start
               put symbol (ch)
            %else
               %if ch = TAB %start
                  Graph On %if Show TAB = Yes
                  %cycle
                     %if show TAB = yes %then TAB Representation %c
                                        %else spaces = spaces + 1
                     %exit %if col & 7 = 0 
                     col = col + 1
                  %repeat
                  Graph Off %if Show TAB = Yes
               %else
                  put CC (ch)
                  %if DEL < Ch < ' '!128 %start
                     Col = Col + 2
                  %else
                     Col = Col + 1     {Extra one}
                  %finish
               %finish
            %finish
         %finish
         pos = pos + 1
      %repeat
      Put Symbol (' ') %if Ch < ' ' %and %c
                           Col < Terminal Width {hack to fix background problem}
   %finish
   L_bits = L_bits ! displayed
%end {of print line}

{FAIL}

%routine fail (%string (80) message, %integer beep)
   %record (Key Definition FM) %name Key

   cursor (0, window top)
   clear line
   Feature
   put string (" " . message . " ")
   Normal
   put symbol (BEL) %if beep = yes %and Beep Mute = No
   %if CY = window top %start
      put symbol (CR)
   %else
      set cursor
   %finish
   failed = yes
   {Unwind macro stack}
   %while Current Macro ## Nil %cycle
      Key == Current Macro
      Current Macro == Current Macro_Stack
      Key_Stack == Nil
      Key_Type = Macro
   %repeat
   Repeat Count = 0                           {and any repeats}
%end {fail}

%routine %spec Redraw Screen

%routine %spec Replace Top Line
%own %integer Jump = 0

%routine %spec Abort Edit

!{APM}@16_3724 %integer Exempt Mask

{IE DEBUGGER}

%routine IE Debugger (%string (31) Name)
   %integer Action,
            On,
            Save Rest = At Rest,
            Save
   %own %integer Journal Suspended = No

   {PUT LINE}

   %routine Put Line (%record (Line fm) %name L)
      %constant %byte %array Bits (0 : 7) = %c
         'U', 'D', 'C', 'M', 'P', 'm', '?', '?'
        { 0    1    2    3    4    5    6    7}
      %integer I

      %if L == Nil %start
         Put String ("*Nil*")
      %else
         Put String ("L=");         Put Integer (L_Length)
         Put String ("  M=");       Put Integer (L_Max Length)
         Put String ("  B=")
         %for I = 0, 1, 7 %cycle
            Put Symbol (Bits (I)) %if L_Bits&(1<<I) # 0
         %repeat
         %if TOS == L %start
            Put String ("  TOS")
         %else %if BOS == L 
            Put String ("  BOS")
         %finish
         Put String ("  A==");      Put Hex (Addr (L))
         Put String ("  L==");      Put Hex (Addr (L_Last))
         Put String ("  N==");      Put Hex (Addr (L_Next))
      %finish                 
   %end {Put Line}

   {GET NUMBER}

   %routine Get Number (%integer %name R)
      %integer Ch,
               Length

      R = 0
      Length = 0
      %cycle
         Ch = Get Key
         %if Ch = CR %or %c
             Ch = Keypad Enter %start
            %exit
         %else %if '0' <= Ch <= '9'
            Put Symbol (Ch)
            R = R*10 + Ch - '0'
            Length = Length + 1
         %else %if Ch = DEL
            %if Length > 0 %start
               Put Symbol (BS)
               Put Symbol (' ')
               Put Symbol (BS)
               Length = Length - 1
               R = R//10
            %finish
         %else
            Put Symbol (BEL) %unless Beep Mute = Yes
         %finish
      %repeat
   %end {Get Number}

   {SHOW STRING}

   %routine Show String (%record (Text fm) %name S)
      %if S_Length = 0 %start
         Feature
         Put String ("Nil")
         Normal
      %else
         Put String (S_String)
      %finish
   %end {Show String}

   {MAIN CODE OF IE DEBUGGER}

   At Rest = No
   Insert Off
   %if CY = Window Bottom %start
      On = Window Top
      On = On + 1 %if Save Rest = No
   %else
      On = Window Bottom
   %finish
   Cursor (0, On)
   Clear Line
   Feature
   Cursor (15, On)
   %if Name = "" %start
      Put Line (Current)
   %else
      Put String ("About to do ")
      Feature
      Put String (Name)
      Normal
   %finish
   %cycle
      Cursor (0, On)
      Put String ("IE FP> ")
      Cursor (7, On)
      Set Cursor                          {park cursor in correct place}
      Action = Upper Case (Get Key)
      Cursor (0, On)
      Clear Line
      Cursor (15, On)
      %if Action = 'O' %start
         Debug = No
         %exit
      %else %if Action = CR
         %exit
!{APM} %else %if Action = '!'
!{APM}    Exempt Mask = Exempt Mask !! (1<<('T' - '@'))
!{APM}    Put String ("Software front panel ")
!{APM}    %if Exempt Mask&(1<<('T' - '@')) = 0 %start
!{APM}       Put String ("Enabled")
!{APM}    %else
!{APM}       Put String ("Disabled")
!{APM}    %finish
      %else %if Action = 'P'
         Put String ("Pos = ");              Put Integer (Pos)
         Put String ("  CX = ");             Put Integer (CX)
         Put String ("  CY = ");             Put Integer (CY)
         Put String ("  Desired X = ");      Put Integer (Desired X)
      %else %if Action = 'S'
         Put String ("By: ")
         Get Number (Jump)
         %exit
      %else %if Action = '*'
!\APM!    %signal 0, 0
{APM}    %signal 0, 0, 0, "IE Debug Test"
      %else %if Action = 'C'
         Put Line (Current)
      %else %if Action = 'L'
         Put Line (Current_Last)
      %else %if Action = 'N'
         Put Line (Current_Next)
      %else %if Action = 'T'
         %if From Journal = Yes %start
            From Journal = No
            Journal Suspended = Yes
            Put String ("Journal reading suspended")
         %finish
      %else %if Action = 'J'
         %if Journal Suspended = Yes %start
            From Journal = Yes
            Journal Suspended = No
            Put String ("Journal reading resumed")
         %finish
      %else %if Action = 'H'
         Put String ( %c
                 "Pos, Cur, Last, Next, Help, Off, Skip, Term, Journ, ^Y, Keep")
      %else %if Action = 'K'
         Always Keep Journal = Yes
      %else %if Action = EM
         Normal
         Save = Forbid Exit
         Forbid Exit = No
         Abort Edit
         Forbid Exit = Save
         Feature
      %else %if Action = 'F'
         %if Search String_Length > 0 %start
            Feature 
            %if Last Search Direction = Forwards %start
               Put Symbol ('>')
            %else
               Put Symbol ('<')
            %finish
         %finish
         Normal
         Show String (Search String)
      %else %if Action = 'R'
         Normal
         Show String (Replace String)
      %else
         Put String ("Unknown command - RETURN to go back to edit")
      %finish
   %repeat
   Normal
   %if On = Window Top %start
      Replace Top Line
   %else %if On = Window Top + 1 %and %c
             BOS ## TOS
      Cursor (0, On)
      Print Line (TOS_Next, 1, On)
   %else %if Screen Lines = Max Lines 
      Cursor (0, Window Bottom)
      Print Line (BOS, 1, Window Bottom)
   %finish
   Set Cursor
   Flush Symbols
   At Rest = Save Rest
%end {IE Debugger}

{GET NEXT CH}

%integer %function get next ch
   %integer ch
   %record (Key Definition FM) %name Old
   
   %on 9, Abort %start
      %if From Journal = Yes %start
         Close Input 
         From Journal = No
         Fail ("Journal File rerun complete", No)
      %else
         fail (event_message, yes)
      %finish
   %finish

   %if Current Macro ## Nil %start
      flush symbols         {give the same effect as get symbol}
      %if Interrupted # 0 %start
!VMS!    Interrupted = 0
!VMS!    Redraw Screen
         %if From Journal = Yes %start
            Fail ("Macro and Rerun interrupted", No)
            Close Input
            From Journal = No
         %else
            Fail ("Macro interrupted", No)
         %finish
         Current Macro == Nil
         Repeat Count = 0
         -> Normal Case
      %finish
      {Unwind the stack if currently exhausted}
      %while Macro Pos > Current Macro_Text_Length %cycle
         Old == Current Macro
         Current Macro == Current Macro_Stack
         Macro Pos = Old_Value
         Old_Stack == Nil
         Old_Type = Macro
         %if Current Macro == Nil %start
            -> Repeat Case %if Repeat Count # 0
            -> Journal Case %if From Journal = Yes
            -> Normal Case 
         %finish
      %repeat 
      Ch = Current Macro_Text_Text (Macro Pos)
      Macro Pos = Macro Pos + 1
   %else %if Repeat Count # 0 
      %if Interrupted # 0 %start
!VMS!    Interrupted = 0
!VMS!    Redraw Screen
         %if From Journal = Yes %start
            Fail ("Repeat and Rerun interrupted", No)
            Close Input
            From Journal = No
         %else
            Fail ("Repeat interrupted", No)
         %finish
         Repeat Count = 0
         -> Normal Case
      %else
      Repeat Case:
         Ch = Repeat Text_Text (Repeat Place)
         Repeat Place = Repeat Place + 1
         %if Repeat Place > Repeat Text_Length %start
            Repeat Place = 1
            Repeat Count = Repeat Count - 1
         %finish
      %finish
   %else %if From Journal = Yes
      %if Interrupted # 0 %start
!VMS!    Interrupted = 0
!VMS!    Redraw Screen
         Fail ("Rerun interrupted", No)
         Close Input
         From Journal = No
         -> Normal Case
      %finish
   Journal Case:
      Read Symbol (Ch)
      %if Debug = Yes %and %c
          Jump = 0 %start
         IE Debugger (Key Name (Ch))
      %finish
      %if Ch = 255 %start
         Close Input
         -> Normal Case
      %else
         Jump = Jump - 1 %if Jump > 0
      %finish
   %else
   Normal Case:
      Ch = Get Key
      Put Journal Symbol (Ch)
      %if Current Definition ## Nil %start
         %if Definition Pos > Max Text Length %start
            Fail ("Keyboard definition is too long - terminated", Yes)
            Current Definition_Text_Length = Definition Pos - 1
            Current Definition == Nil
            %result = Ch
         %else
            Current Definition_Text_Text (Definition Pos) = Ch
            Definition Pos = Definition Pos + 1
         %finish
      %finish
   %finish
   %if failed = yes %start
      cursor (0, window top)
      clear line       {background}
      print line (TOS, 1, window top)
      set cursor
      flush symbols
   %finish
   failed = no
   %result = ch
%end {of get next ch}

{READ INTEGER}

%routine read integer (%integer %name sign, number, 
                       %string (31) P,
                       %integer Plus Minus, Question, Star)
   %integer ch, L,
            save rest = at rest
   %label restart

   at rest = no
   Keypad Off
restart:
   cursor (0, window top)
   Clear Line
   put string (P)
   sign   = absolute
   number = 0
   L = 0
   Top X = Length (P)
   %cycle
      ch = get next ch
      %if ch = CAN %start
         -> restart 
      %else %if ch = DEL %or ch = BS
         %if L > 0 %start
            L = L - 1
            number = number // 10                  {reduce number}
            sign = absolute %if L = 0         {take away sign}
            put symbol (BS)
            put symbol (' ')
            put symbol (BS)
            Top X = Top X - 1
         %finish
      %else %if ch = CR
         %exit
      %else %if '0' <= ch <= '9'
         number = number * 10 + ch - '0'
         put symbol (ch)
         L = L + 1
         Top X = Top X + 1
      %else %if ch = '-' %and L = 0 %and Plus Minus = Yes
         sign = delta minus
         L = L + 1
         put symbol ('-') 
         Top X = Top X + 1
      %else %if ch = '+' %and L = 0 %and Plus Minus = Yes
         sign = delta plus
         L = L + 1
         put symbol ('+')
         Top X = Top X + 1
      %else %if ch = '?' %and L = 0 %and Question = Yes
         sign = query
         %exit
      %else %if Ch = '*' %and L = 0 %and Star = Yes
         Sign = Infinite
         %exit
      %else
         put symbol (BEL) %if Beep Mute = No
      %finish
   %repeat
   put symbol (CR)
   Keypad On
   Erase Line
   Flush Symbols
   at rest = save rest
   Top X = -1
%end {of read integer}
      
{READ STRING}

%routine read string (%string (*) %name S, 
                      %string (63) P)
   %integer ch,
            save rest = at rest
   %label restart
   
   at rest = no
restart:
   S = ""
   cursor (0, window top)
   clear line
   keypad off
   put string (P)
   Top X = Length (P)
   %cycle
      ch = get next ch
      %if ch = CAN %start
         -> restart
      %else %if ch = CR
         %exit
      %else %if ch = DEL %or ch = BS
         %if length (S) > 0 %start
            length (S) = length (S) - 1
            put symbol (BS)
            put symbol (' ')
            put symbol (BS)
            Top X = Top X - 1
         %finish
      %else %if ' ' <= ch <= '~'
!\UNIX!  S <- S . to string (upper case (ch))
{UNIX}   S = S . To String (Ch)   
         put symbol (ch)
         Top X = Top X + 1
      %else
         put symbol (BEL) %if Beep Mute = No
      %finish
   %repeat
   keypad on
   put symbol (CR)
   Clear Line
   Flush Symbols
   at rest = save rest
   Top X = -1
%end {of read string}

{- REPLACE TOP LINE-}

%routine Replace Top Line
   Cursor (0, Window Top)
   Clear Line
   print line (TOS, 1, window top)
   set cursor
%end {Replace Top Line}

{REDRAW SCREEN}

%routine redraw screen
   %integer Y
   %record (line fm) %name L

   Cursor Off %if Terminal Characteristics&Supress Cursor Ability # 0
   keypad on                        {just in case...}
   L == TOS
   %for Y = window top, 1, window bottom %cycle
      cursor (0, Y)
      %if L == Nil %start
         Clear Line %unless Y = Window Bottom %and Reported = Yes
      %else
         print line (L, 1, Y)
         L == L_next
      %finish
   %repeat
   Cursor On %if Terminal Characteristics&Supress Cursor Ability # 0
   set cursor
   Reported = No
%end {of redraw screen}

{FIND LINE}

%record (line fm) %map find line (%integer sign, which)
   {This locates line which in the file}
   %integer count
   %record (line fm) %name L

   %if sign = absolute %start
      L == current buffer_first
      count = 1
      %while count < which %cycle
         %exit %if L_next == nil
         L == L_next
         count = count + 1
      %repeat
   %else %if sign = delta plus
      L == current
      count = 0
      %while count < which %cycle
         %exit %if L_next == nil
         L == L_next
         count = count + 1
      %repeat
   %else %if sign = delta minus
      L == current
      count = 0
      %while count < which %cycle
         %exit %if L_last == nil
         L == L_last
         count = count + 1
      %repeat
   %finish
   %result == L
%end {of find line}

{CLEAR DISPLAY BITS}

%routine clear display bits
   %record (line fm) %name L
   
   %return %if TOS == nil         {start up condition}
   L == TOS
   %cycle
      L_bits = L_bits & (\displayed)
      %exit %if L == BOS
      L == L_next
   %repeat
%end {of clear display bits}

{SET SCREEN}

%routine set screen (%integer DY)
   {DY indicates what the new position on the screen of the cursor}
   {is expected to be.  If this is 0 then it is not known, and}
   {should be calculated.}
   %integer n, i, extra
   %record (line fm) %name L

   %if current_bits & displayed # 0 %start
      {The line is already on the screen - always a help!}
      %if current == TOS %start
         {on the top line of the screen}
         %if current_last == nil %start
            %if DY = 0 %then CY = window top %c
                       %else CY = CY + DY
         %else
            {must move another line onto the top of the screen}
            {first the buffer}
            TOS == TOS_last
            %if screen lines = max lines %start
               {a line goes off the bottom, so change pointers}
               BOS_bits = BOS_bits & (\displayed)
               BOS == BOS_last
            %else
               screen lines = screen lines + 1
            %finish
            Reverse Scroll (Window Top, Window Bottom)
            print line (TOS, 1, window top)
            CY = window top + 1 %if DY = 0
         %finish
         set cursor
      %else %if current == BOS
         {on the bottom of the screen}
         {move another line up}
         %if BOS_next == nil %start
            %if DY = 0 %then CY = screen lines + window top - 1 %c
                       %else CY = CY + DY
         %else
            BOS == BOS_next
            TOS_bits = TOS_bits & (\displayed)
            TOS == TOS_next
            Forward Scroll (Window Top, Window Bottom)
            print line (BOS, 1, window bottom)
            CY = screen lines + window top - 2 %if DY = 0
         %finish
         set cursor
      %else   {we are not going to change the screen, only the cursor}
         %if DY # 0 %start
            {we have a hint about how to do this}
            CY = CY + DY
         %else
            {we need to calculate the line}
            L == TOS
            CY = window top
            %while L ## current %cycle
               L == L_next                {a line down}
               CY = CY + 1                {so move cursor}
            %repeat
         %finish
      %finish
      set cursor
   %else {the line is NOT displayed}   
      {see if we can find a line nearby that is displayed}
      %unless TOS == nil %start
         L == current
         {first above the current line}
         %for n = 1, 1, max lines - 2 %cycle
            L == L_last
            %exit %if L == nil
            %if L == BOS %start
               {we have found the bottom of the screen n lines above}
               {N.B. n + 1 so that the line we want becomes the second bottom}
               %if current_next == nil %then extra = 0 %c
                                       %else extra = 1
               %for i = 1, 1, n + extra %cycle
                  TOS_bits = TOS_bits & (\displayed)
                  TOS == TOS_next

                  Forward Scroll (Window Top, Window Bottom)

                  BOS == BOS_next
                  print line (BOS, 1, window bottom)
               %repeat
               CY = window bottom - extra
               set cursor
               %return
            %finish
         %repeat
         {if not, try the other direction}
         L == current
         %for n = 1, 1, max lines - 2 %cycle
            L == L_next
            %exit %if L == nil
            %if L == TOS %start
               {we have found the top of the screen N lines below}
               %if current_last == nil %then extra = 0 %c
                                       %else extra = 1
               %for i = 1, 1, n + extra %cycle

                  Reverse Scroll (Window Top, Window Bottom)

                  BOS_bits = BOS_bits & (\displayed)
                  BOS == BOS_last
                  TOS == TOS_last
                  print line (TOS, 1, window top)
               %repeat
               CY = window top + extra
               set cursor
               %return
            %finish
         %repeat
      %finish
      clear display bits        {zonk the current display}
      screen lines = 1
      TOS == current
      {work back the way}
      %while screen lines < max lines // 2 %cycle
         %exit %if TOS_last == nil
         TOS == TOS_last
         screen lines = screen lines + 1
      %repeat
      {work forwards}
      CY = screen lines + window top - 1
      BOS == current
      %while screen lines < max lines %cycle
         %exit %if BOS_next == nil
         BOS == BOS_next
         screen lines = screen lines + 1
      %repeat
      {now move back up if necesary and possible}
      %while screen lines < max lines %cycle
         %exit %if TOS_last == nil
         TOS == TOS_last
         screen lines = screen lines + 1
         CY = CY + 1
      %repeat
      redraw screen
   %finish
%end {of set screen}

{NEW BUFFER}

%record (Buffer fm) %map New Buffer (%string (31) Name)
   %record (Buffer fm) %name B

   B == New (Size of Buffer fm)
   B_Name = Name 
   B_Markers == Nil
   B_Magic Markers == Nil
   B_Pos = 1
   B_Next == Buffers                                  {knit it in}
   Buffers == B
   %result == B
%end {New Buffer}

{MAKE EMPTY BUFFER}

%routine Make Empty Buffer (%record (buffer fm) %name B)
   %record (line fm) %name L
   
   L        == new line fm (0)
   L_length =  0
   L_last   == nil
   L_next   == nil
   L_bits   =  pseudo line
   B_first  == L
   B_last   == L
!VMS!   B_access =  default access
   B_source file = ""
%end {Make Empty Buffer}

{FIND BUFFER}

%record (buffer fm) %map find buffer (%string (31) name)
   %record (buffer fm) %name B == buffers
   
   %cycle
      %result == B %if B_name = name
      B == B_next
   %repeat %until B == nil
   %result == nil
%end {of find buffer}

{SET KEYBOARD}

%routine Set Keyboard (%string (255) File)
   %record (Key Definition FM) %name Key
   %integer Ch, K, Type

   {EVENT TRAP}

   %on IO error %start
      close input
      Select Input (3) %if From Journal = Yes
      %return
   %finish

   {MAIN CODE OF SET KEYBOARD}

   %begin

      %on 3, IO error %start
!\APM!   %signal abort
{APM}    %signal abort, 0, 0, Event_Message
      %finish
       
!VMS! Default File Name = ".KIE"
!VMS! File = Expand File Name (File)
      %if From Journal = Yes %start
{\UNIX}  open input (2, file)
!UNIX!   Open Binary Input (2, File)
         select input (2)
      %else
{\UNIX}  open input (3, file)
!UNIX!   Open Binary Input (3, File)
         select input (3)
      %finish
!VMS! Default File Name = Grand Default File Name
   %end

   %cycle
      Read Symbol (K)
      Key == Keys (K)
      Read Symbol (Type)
      %if Type = 'L' %start
         Key_Type = Letter
         Read Symbol (Key_Value)
         Skip Symbol
      %else %if Type = 'C'
         Key_Type = Command
         Read Symbol (Key_Value)
         Skip Symbol
      %else %if Type = 'M'
         Key_Type = Macro
         Key_Text == New (Size Of Text FM) %if Key_Text == Nil
         Key_Stack == Nil
         Key_Text_Length = 0
         %cycle
            Read Symbol (Ch)
            %exit %if Ch = NL
            Key_Text_Length = Key_Text_Length + 1
            Ch = NL %if Ch = 255
            Key_Text_Text (Key_Text_Length) = Ch
         %repeat
      %finish
   %repeat
%end {Set Keyboard}

   {PRELOAD BUFFER}

   %routine Preload Buffer (%string (255) File Name,
                            %string (31) Buffer Name)
      %integer Created = No
      %record (buffer fm) %name B
      
      %on 3, IO error, 10 %start
         %if Event_Event = IO Error %or %c
             Event_Event = 3 %start
            Event_Message = "Preloading buffer " . Buffer Name . ": " . event_message
         %else
            Event_Message = "Insufficient Memory preloading " . Buffer Name
         %finish
!\APM!    %signal Abort
{APM}    %signal Abort, 0, 0, Event_Message
      %finish
      
      {MAIN CODE OF PRELOAD BUFFER}

      %unless find buffer (buffer name) == Nil %start
         Event_Message = "Preloaded buffer " . buffer name . " already exists"
!\APM!    %signal Abort
{APM}    %signal Abort, 0, 0, Event_Message
      %finish
      %if file name = Null Device %start
         created = yes
!VMS! %else
!VMS!    file name = expand file name (file name)
      %finish
      B == New Buffer (Buffer Name)
      %if created = yes %start
         Make Empty Buffer (B)
         report ("Creating preloaded buffer " . buffer name)
      %else
         report ("Preloading from " . file name . " into buffer " . buffer name)
         load buffer (B, file name)
         Make Empty Buffer (B) %if B_first == nil
      %finish
      B_Current == B_First
   %end {Preload Buffer}

{SET PROFILE}

%routine set profile (%string (255) file)
   %integer ch
   %integer n, 
            which,           
            Backwards Compatible
   %string (20) option
   %string (255) File Given

   %constant %integer max options = 30
   
   %constant %string (20) %array options (1 : max options) =
      "AUTOINDENT",
      "AUTOLINEBREAK",
      "BEEPCOLUMN",
      "BOTTOM",
      "CASESIGNIFICANCE",
      "FREECURSOR",
      "RIGHTMARGIN",
      "SEARCHBEFOREREPLACE",
      "SHOWTAB",
      "TABWIDTH",
      "TOP",
      "WINDOWBOTTOM",
      "WINDOWTOP",
      "END",
      "EOFMARKER",
      "TICK",
      "WRITEBACK",
      "BEEPMUTE",
      "FORBIDEXIT",
      "WIGGLE",
      "PUTBUFFERVERIFY",
      "JOURNALFREQUENCY",
      "DEBUG",
      "KEEPJOURNAL",
      "KEYBOARD",
      "LEFTMARGIN",
      "JUSTIFY",
      "WRAPSEARCH",
      "BACKSEARCH",
      "JOINATEND"

   %switch action (1 : max options)

   {FAULTY FORM}

   %routine faulty form
!\APM!      event_message = "Faulty form in profile file"
!\APM!      %signal abort

{APM} %signal Abort, 0, 0, "Faulty form in profile file"
   %end {of faulty form}

   {SKIP PAST NL}

   %routine skip past NL
      skip symbol %while next symbol # NL
      skip symbol
   %end {of skip past NL

   {GET Y OR N}

   %routine get Y or N (%integer %name what)
      %integer c

      %cycle
         read symbol (c)
         c = upper case (c)
         %if c = 'Y' %start
            what = yes
            %exit
         %else %if c = 'N'
            what = no
            %exit
         %else %if c # ' '
            faulty form
         %finish
      %repeat
      skip past NL
   %end {of get Y or N}

   {GET NUMBER}

   %routine get number (%integer %name N)
      %on 3, 4 %start
         faulty form
      %finish

      read (N)
      skip past NL
   %end {of get number}

   {GET STRING}
   
   %routine get string (%string (*) %name S)
      %integer ch
      
      read symbol (ch) %until ch # ' '
      faulty form %if ch = NL
      S = to string (ch)
      %cycle
         read symbol (ch)
         %exit %if ch = NL
         S <- S . to string (ch)
      %repeat
      skip past NL %unless ch = NL
   %end {get string}

   {GET NAME}

   %routine Get Name (%string (*) %name Name)
      %integer Ch

      Name = ""
      %cycle
         read symbol (ch)
         %exit %if ch = '='
         faulty form %if ch = NL
         Name <- Name . to string (upper case (ch)) %unless ch = ' '
      %repeat
   %end {Get Name}

   {PROFILE PRELOAD}

   %routine Profile Preload
      %string (255) File Name
      %string (31) Buffer Name

      Get Name (Buffer Name)
      Get String (File Name)

      Preload Buffer (File Name, Buffer Name)
   %end {Profile Preload}
      
   {EVENT TRAP}

   %on IO error %start
      close input
      Select Input (3) %if From Journal = Yes
      %return
   %finish

   {MAIN CODE OF SET PROFILE}

   !! printstring("Into set profile");  print symbol(13);  newline
   %begin
      %on 3, IO error %start
         !! printstring("Open failed");  print symbol(13);  newline
!\APM!   %signal Abort
{APM}    %signal abort, 0, 0, Event_Message
      %finish
       
!VMS! Default File Name = ".IE"
!VMS! File = Expand File Name (File)
      !! printstring("Open profile file ");  printstring(file)
      !! print symbol(13);  newline
      %if From Journal = Yes %start
         open input (2, file)
         select input (2)
      %else
         open input (3, file)
         select input (3)
      %finish
!VMS! Default File Name = Grand Default File Name
   %end
   %cycle
      read symbol (ch) %until ch > ' '
      ch = upper case (ch)
      %if ch = '!' %or ch = '{' %start
         skip past NL
      %else %if Ch = '%'
         Profile Preload 
      %else %if 'A' <= ch <= 'Z'
         Get Name (Option)
         option = to string (ch) . Option
         which = 0
         !! printstring("Option: ");  printstring(option)
         !! print symbol(13);  newline
         %for n = 1, 1, max options %cycle
            %if options (n) = option %start
               which = n
               %exit
            %finish
         %repeat
         faulty form %if which = 0
         -> action (which)

      action (1):
         get Y or N (auto indent)
         %continue

      action (2):
         get Y or N (auto line break)
         %continue

      action (3):
         get number (beep column)
         %continue
      
      action (4):
         get number (window bottom)
         window bottom = 23 %unless 10 <= window bottom <= 23
         %continue
         
      action (5):
         get Y or N (case significance)
         %continue

      action (6):
         get Y or N (free cursor)
         %continue

      action (7):
         get number (Right Margin)
         Right Margin = Terminal Width %unless 1 <= Right Margin <= Max Line Length
         %continue

      action (8):
         get Y or N (search before replace)
         %continue

      action (9):
         get Y or N (show TAB)
         %continue

      action (10):
         get number (TAB width)
         TAB width = 3 %unless 1 <= TAB width <= max line length
         %continue

      action (11):
         get number (window top)
         window top = 23 %unless 0 <= window top <= 12
         %continue

      action (12):
         get number (window bottom)
         window bottom = 23 %unless 10 <= window bottom <= 23
         %continue

      action (13):
         get number (window top)
         window top = 23 %unless 0 <= window top <= 12
         %continue

      action (14):
         %exit

      action (15):
         get string (EOF marker)
         %continue

      action (16):
         get Y or N (Backwards Compatible)
         %continue

      action (17):   
         get Y or N (write back)
         %continue

      action (18):
         get Y or N (Beep Mute)
         %continue

      action (19):
         get Y or N (Forbid Exit)
         %continue

      action (20):
         get Y or N (Wiggle)
         %continue

      action (21):
         get Y or N (Put Buffer Verify)
         %continue

      action (22):
         get number (Journal Frequency)
         Journal Frequency = 0 %unless 0 <= Journal Frequency <= 512
         %if Out File = "" %start
            Journal Frequency = 0 
         %else
            Set Journal Frequency (Journal Frequency)
         %finish
         %continue

      action (23):
         Get Y Or N (Debug)
         %continue

      action (24):
         Get Y Or N (Keep Journal)
         %continue

      action (25):
         Get String (File Given)
         Set Keyboard (File Given)
         %continue

      action (26):
         Get Number (Left Margin)
         Left Margin = 1 %unless 1 <= Left Margin <= Max Line Length
         %continue

      action (27):
         Get Y or N (Justify)
         %continue

      action (28):
         Get Y or N (Wrap Search)
         %continue

      action (29):
         Get Y or N (Back Search)
         %continue

      action (30):
         Get Y Or N (Join At End)
      %else
         faulty form
      %finish
   %repeat
   close input
   Select Input (3) %if From Journal = Yes
%end {of set profile}

{INITIALISE KEYS}

%routine Initialise Keys
   %integer I

   %routine Command Key (%integer K, C)
      Keys (K)_Type = Command
      Keys (K)_Value = C
   %end

   Keys (I)_Type = Undefined %for I = 0, 1, 255
   %for I = ' ', 1, '~' %cycle
      Keys (I)_Type = Letter
      Keys (I)_Value = I
   %repeat
   Command Key (Keypad Enter, 13)
   Set Keyboard (Keyboard)
%end {Initialise Keys}


%own %integer Journal Heading Done = 0

{-PUT JOURNAL HEADING-}

%routine Put Journal Heading
   %if Journal Heading Done = No %start
      Put Journal String ("IE Journal File Format 2.1")
      Put Journal Symbol (NL)
      Put Journal String (In File);             Put Journal Symbol (NL)
      Put Journal String (Keyboard);            Put Journal Symbol (NL)
      Put Journal String (Profile);             Put Journal Symbol (NL)
      Put Journal Integer (Start Line);         Put Journal Symbol (' ')
      Put Journal Integer (Start Pos);          Put Journal Symbol (NL)
      Journal Heading Done = Yes
   %finish
%end {Put Journal Heading}

{START UP}

%routine start up
   %integer I
   %string (127) def,
                 Rerun Message = "",
                 S

   %on 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 %start
!\APM! %signal Abort
{APM} %signal abort, 0, 0, Event_Message    {Avoid zapping message}
   %finish

   {RERUN JOURNAL FILE}
   
   %routine Rerun Journal File
   
      %on 3, 9 %start
         Rerun Message = "Journal File - " . Event_Message
         %return
      %finish

      %if Journal Frequency > 0 %start
         Open Input (3, Old Journal)
      %else
         Open Input (3, Journal File)
      %finish
      Select Input (3)
      From Journal = Yes
   %end {Rerun Journal File}

   {READ LINE}

   %routine Read Line (%string (*) %name L)
      %integer Ch

      Length (L) = 0
      %cycle
         Read Symbol (Ch)
         %exit %if Ch = NL
         L <- L . To String (Ch)
      %repeat
   %end {Read Line}

   {MAIN CODE OF STARTUP}

   Set Terminal Characteristics
   EOF Marker = Default EOF Marker
   keypad on
   flush symbols

   main == New Buffer ("MAIN")

   !! printstring("Keypad on, initialising keys");  print symbol(13)
   !! newline
   Initialise Keys
   !! printstring("Setting profile");  print symbol(13);  newline
   Set Profile (profile) %if profile # ""

   !! printstring("Preloading");  print symbol(13);  newline
   %while Preload List ## Nil %cycle
      Preload Buffer (Preload List_File Name,
                      Preload List_Buffer Name)
      Preload List == Preload List_Next
   %repeat                  

   max lines = window bottom - window top + 1
   current buffer == main
   %if In File # "" %and %c
       Exists (In File) %start
      Clear Window
      report ("Reading from " . in file)
      !! newline
      !! printstring("About to call Load Buffer")
      !! print symbol(13);  newline
      load buffer (main, in file)
      !! printstring("Back from Load Buffer")
      !! print symbol(13);  newline
      Make Empty Buffer (main) %if main_first == nil
   %else
      {no input file, so create a new buffer, with one line in it}
      %if out file = "" %start
         {if there is no output file}
         event_message = "File does not exist - " . in file
!\APM!    %signal abort
{APM}    %signal Abort, 0, 0, Event_Message
      %finish
      Clear Window
      report ("Creating " . out file)
      Make Empty Buffer (main)
   %finish

   %if Journal Frequency > 0 %start
      {record starting coords go out into journal}
      Put Journal Heading
   %finish

   %if Options&Recover Edit # 0 %start
      Rerun Journal File 
      %if From Journal = Yes %start
         {check for consistancy}
         Read Line (S)
         %if S = "IE Journal File Format 2.1" %start
            %if Options&Ignore Differences # 0 %start
               Read Line (S) %for I = 1, 1, 3
            %else
               Read Line (S)
               %if S = In File %start
                  Read Line (S)
                  %if S = Keyboard %start
                     Read Line (S)
                     %unless S = Profile %start
                        Rerun Message = "Not the same Profile File"
                     %finish
                  %else
                     Rerun Message = "Not the same Keyboard File"
                  %finish
               %else
                  Rerun Message = "Not the same Input File"
               %finish
            %finish
            %if Rerun Message # "" %start
               Close Input
               From Journal = No
               Rerun Message = "Recovery fails: " . Rerun Message
            %finish
         %else
            Rerun Message = "Not a correct format Journal File"
         %finish
      %finish
   %finish

   %if From Journal = Yes %start
      {Fish Start position for this journal file from the file}
      Read (Start Line)
      Read (Start Pos)
      Skip Symbol
   %finish

   current == find line (absolute, start line)
   current == current buffer_first %if current == nil
   CX = find X (current, start pos)
   TOS == nil                          {to prevent unassigned}
   BOS == nil
   set screen (0)
   search string_length = 0
   replace string_length = 0
   replace search_length = 0
   Display Broadcast %if Broadcast # ""

   Fail (Rerun Message, Yes) %if Rerun Message # ""
%end {of start up}

{CLOSE EDIT}

%routine close edit
   %integer state = 0,
            n     = 0,
            changed = 0,
            cleared = no,
            ch
   %string (255) file, file2
!VMS! %string (255) Munged
   %string (255) FF
   %record (line fm) %name L
   %record (buffer fm) %name B

   %on 3, IO error %start
      {an error on output has happened}
      options = options & (\silent)      {kill off any silence}
      %if state = 0 %start
         report ("Write out fails - " . event_message)
      %else
         report (event_message)
      %finish
      -> extra junk %if state = 2
      read string (FF, "New file name (RETURN to go back to edit): ")
      %if FF = "" %start
         Redraw Screen
         Finished = No
         At Rest = Yes
         Main_Changed = Yes               {just in case}
         %return 
      %finish
{\VMS} Out File = FF
!VMS! out file = expand file name (FF) {catch the rest of the file name}
      -> try again
   %finish
   

   %if Forbid Exit = Yes %and %c
       current buffer ## main %start
      fail ("Not in main buffer", yes)
      %return
   %finish
   at rest = no
   %if out file # "" %start
      %if write back = yes %start
         B == buffers
         %cycle
            %if B ## main %and B_changed = yes %start
               clear window %and cleared = yes %if cleared = no
               cursor (0, window top)
               put string ("Buffer " . B_name . " changed. Write back (Y/N) ? ")
               %cycle
                  ch = upper case (Get Key)
               %repeat %until ch = 'Y' %or ch = 'N' %or ch = CR %or ch = EM
               %exit %if ch = EM
               %if ch = 'Y' %start
               extra junk:
                  %if B_source file = "" %start
                     %cycle
                        read string (file, "File for output: ")
                     %repeat %until file # ""
                  %else
!VMS!                %if B_source file -> file2 .(";") %start
!VMS!                %finish
{\VMS}                file2 = B_Source File
                     read string (file, "File for output (" . File2 . "): ")
                     %if file = "" %start
                        file = file2 
!VMS!                %else
!VMS!                   file = expand file name (file)
                     %finish
                  %finish
                  state = 2
                  report ("Writing buffer " . B_name . " to " . file)
                  unload buffer (B, file)
                  B_Changed = No
               %finish
            %finish
            B == B_next
         %repeat %until B == nil
         state = 0
      %finish
   %finish

   finished = yes
   %if out file = "" %start
      {read only}
      report (in file . " examined")
      Changed Main Buffer = No
   %else

{\VMS} %if main_changed = yes %or in file # out file %start

!VMS! %if In File = "" %start
!VMS!    Munged = ""
!VMS! %else
!VMS!    In File -> Munged .(";")
!VMS!    Munged = Munged . ";"
!VMS! %finish

!VMS! %if main_changed = yes %or Munged # out file %start

      try again:
         report ("Writing to " . out file)
         unload buffer (main, out file)
         Changed Main Buffer = Yes
      %else
         report ("No changes to " . in file)
         Changed Main Buffer = No
      %finish
   %finish
   keypad off
   flush symbols
   {calculate current position}
   %if current buffer == main %start
      L == current               {find current line number}
   %else
      L == main_current
   %finish
   %cycle
      n = n + 1
      L == L_last
   %repeat %until L == nil
   start line = n             {pass back current position}
   start pos = pos
%end {of close edit}

{ABORT EDIT}

%routine abort edit
   %integer ch
   %integer Changed
   %record (Buffer fm) %name B
   
   %if Forbid Exit = Yes %and %c
       Current Buffer ## Main %start
      Fail ("Not in main buffer", Yes)
      %return
   %finish
   %if options & confirm # 0 %start
      Changed = No
      B == Buffers
      %while B ## Nil %cycle
         Changed = Yes %if B_Changed = Yes
         B == B_Next
      %repeat  
      %if Changed = Yes %and %c
          Out File # "" %start
         At Rest = No 
         cursor (0, window top)
         Clear Line
         put string ("Are you sure you want to abort (Y/N) ?")
         put symbol (BEL) %if Beep Mute = No
         %cycle
            ch = upper case (Get Key)
            %if ch = 'Y' %or %c
                ch = EM %start   {allow ^Y as well}
               %exit
            %else %if ch = 'N' %or ch = CR
               cursor (0, window top)
               print line (TOS, 1, window top)
               set cursor
               At Rest = Yes
               %return
            %else
               put symbol (BEL) %if Beep Mute = No
            %finish
         %repeat
      %finish
   %finish
   keypad off
   cursor (0, window bottom)
   flush symbols
   Changed Main Buffer = No
   %if Keep Journal = Yes %or %c
       Always Keep Journal = Yes %start
      Keep Journal File             
   %else
      Scrap Journal File
   %finish
!\APM! event_message = "Edit Aborted"
!\APM! %signal abort
{APM} %signal Abort, 0, 0, "Edit Aborted"
%end {of abort edit}

   {RESTRICT CURSOR}
   
   %routine restrict cursor
      Find Pos (Current, CX, Pos)             {handle TABs in vertical movement}
      %if (free cursor = no %or %c
           Wiggle = Yes) %and %c
          pos > current_length %start
         pos = current_length + 1 
         TAB Wiggle = No
         Desired X = CX
         CX = find X (current, pos)
      %finish
   %end {restrict cursor}

   {ADD LINE}

   %routine add line (%record (line fm) %name line)
      %record (line fm) %name L, N

      current buffer_changed = yes
      L == current_last
      N == current_next   
      current_last == line          {link back from current}
      line_next == current          {link forward to current}
      line_last == L                {link backwards}
      current == line               {move onto the new line}
      %if L == nil %start
         {this is the top line}
         current buffer_first == line
         TOS   == line
      %else
         L_next == line             {put in forward link to new}
      %finish
      %if N == nil %and screen lines = max lines %start
         {at the bottom of the file and the window is full}
         {must scroll off the top}
         TOS_bits = TOS_bits & (\displayed) {remove top line}
         TOS == TOS_next                    {in pointers}

         CY = CY - 1                        {move up a line on the screen}

         Forward Scroll (Window Top, CY)

      %else
         %if screen lines = max lines %start
            {normal case}
            BOS_bits = BOS_bits & (\displayed)
            BOS == BOS_last
         %else
            screen lines = screen lines + 1
         %finish

         Reverse Scroll (CY, Window Bottom)

         cursor (0, CY)

      %finish
      print line (current, 1, CY)
      reset cursor
   %end {of add line}

   {ADD NEW LINE}

   %routine add new line
      %integer indent
      %record (line fm) %name line

      %on 2, 10 %start
         Fail ("Insufficient Memory", Yes)
         %return
      %finish

      line == new Line fm (80)
      line_bits = 0
      line_length = 0
      %if auto indent = yes %start
         Indent = 1
         %while indent <= current_length %and %c
                (current_text (indent) = ' ' %or %c
                 Current_Text (Indent) = TAB) %cycle
            indent = indent + 1
         %repeat
         Pos = Find X (Current, Indent) + 1
      %else
         pos = Left Margin
      %finish
      add line (line)
   %end {of add new line}

   {BREAK LINE}

   %routine break line
      %integer indent = 1
      %integer length = 0,
               New Length,
               P
      %integer Keep = No
      %record (line fm) %name line, 
                              next

      %on 2, 10 %start
         Fail ("Insufficient Memory", Yes)
         %return
      %finish

      %if current_bits & pseudo line # 0 %start
         add new line
      %else
         length = current_length - pos + 1 %if pos <= current_length
         current buffer_changed = yes
         %if auto indent = yes %start
            %if current_length = 0 %start
               {This line has already been autoindented}
               {Sort out TABs}
               Indent = Find X (Current, Pos) + 1
            %else
               %while indent <= current_length %and %c
                      (current_text (indent) = ' ' %or %c
                       current_text (indent) = TAB) %cycle
                  Keep = Yes %if Current_Text (Indent) = TAB
                  indent = indent + 1
               %repeat
               indent = pos %if pos < indent
            %finish
         %else %if Left Margin > 1
            Indent = Left Margin
         %finish
         New Length = Length + Indent
         %if New Length < 80 %start
            New Length = 80 
         %else
            New Length = New Length + 16
         %finish
         line == new Line fm (New Length)
         line_last == current
         line_bits = 0
         next == current_next
         current_next == line
         line_next == next
         line_length = 0
         %if Auto Indent = Yes %and %c
             Current_Length > 0 %start
            P = 1
            line_text (P) = current_text (P) %and P = P + 1 %while P < Indent
            Line_Length = Indent - 1 %if Keep = Yes
         %else %if Left Margin > 1 %and %c
                   Current_Length > 0
            Line_Text (P) = ' ' %for P = 1, 1, Left Margin - 1
         %finish
         %if length > 0 %start
            bulk move (length,
                       current_text (pos),
                       line_text (indent))
            line_length = indent - 1             {record the size for tacking on}
         %finish
         line_length = line_length + length
         current_length = pos - 1 %if pos <= current_length
         check for CCs (current)         {see if changed}
         current == line                 {now on the new one}
         check for CCs (current)         {there may now be TABs}
         {now update the screen}
         %unless pos > Terminal Width %start        {clear off the current line}
            %if Current_Last_Bits&Contains CCs # 0 %start
               Clear Line
               Print Line (Current_Last, 1, CY)
            %else
               erase line 
            %finish
         %finish
         pos = indent                            {move to start of line}
         CX = find X (current, pos)
         next_last == current                {link backwards}
         %if screen lines < max lines %start
            {just add the new line}
            screen lines = screen lines + 1 
            CY = CY + 1                          {move down a line}
            Reverse Scroll (CY, Window Bottom)
         %else
            {a full screen, so something will have to be scrolled off}
            %if current_next == BOS %start
               {if we are on the scond bottom line scroll of the top}
               TOS_bits = TOS_bits & (\displayed)
               TOS == TOS_next
               Forward Scroll (Window Top, CY)
               {CY stays the same}
            %else
               {just scroll off the bottom as usual}
               BOS_bits = BOS_bits & (\displayed)
               BOS == BOS_last
               CY = CY + 1                       {move down a line}
               Reverse Scroll (CY, Window Bottom)
            %finish
         %finish

         Cursor (0, CY)
         print line (current, 1, CY)                    {the new line}
         set cursor
      %finish
   %end {of break line}

   {FIND WORD LEFT}
   
   %routine find word left (%integer %name P)
      P = Current_Length + 1 %if P > Current_Length
      %return %if P = 1
      %cycle
         P = P - 1
         %if alphanumeric (current_text (P)) %start
            %return %if P = 1
            %cycle
               P = P - 1
               %if %not alphanumeric (current_text (P)) %start
                  P = P + 1   {back up onto the word}
                  %return
               %finish
            %repeat %until P = 1
         %else %if punctuation (current_text (P))
            {a puntuation mark is a word (?)}
            %exit
         %finish
      %repeat %until P = 1
   %end {of find word left}
   
   {FIND WORD RIGHT}
   
   %routine find word right (%integer %name P)
      %return %if P > current_length
      %if alphanumeric (current_text (P)) %start
         {skip off the end of the word}
         P = P + 1 %until P > current_length %or %c
                          (%not alphanumeric (current_text (P)))
      %else
         P = P + 1
      %finish
      {now find the next word}
      %while P <= current_length %and %c
             formatting (current_text (P)) %cycle
         P = P + 1
      %repeat
   %end {of find word right}
   
   {-CHARACTER DELETION BUFFER-}

   {Deleted Characters are

{APM} %constant %integer Bin Max = 255
!VMS! %constant %integer Bin Max = 1023
!UNIX! %constant %integer Bin Max = 1023

   %constant %integer Divider = 255
   %own %byte %array Bin (0:Bin Max) = 0(*)
   %own %integer Bin Free = 0,
                 Bin Start = 0,
                 Parcel Open = No,
                 Divided = No

   {DUMP CHARACTER}

   %routine Dump Character (%integer Ch)
      %if Ch = Divider %start
         %return %if Divided = Yes
         Divided = Yes 
      %else
         Divided = No
      %finish
      Bin (Bin Free) = Ch
      Bin Free  = (Bin Free  + 1)&Bin Max
      Bin Start = (Bin Start + 1)&Bin Max %if Bin Free = Bin Start
   %end {Dump Character}

   {OPEN PARCEL}

   %routine Open Parcel
      Parcel Open = Yes

      Dump Character (Divider)
   %end {Open Parcel}

   {CLOSE PARCEL}

   %routine Close Parcel
      %return %if Parcel Open = No
      Parcel Open = No
      Dump Character (Divider)
   %end {Close Parcel}

   {DUMP PARCEL}

   %routine Dump Parcel (%integer Start, Length)
      %integer I

      Open Parcel
      %for I = Start, 1, Start + Length - 1 %cycle
         Dump Character (Current_Text (I))
      %repeat
      Close Parcel
   %end {Dump Parcel}

   {PUT OVERFLOW MARKER}
   
   %routine put overflow marker
      cursor (Terminal Width - 1, CY)
      Graph On
      Left T
      Graph Off
   %end {of put overflow marker}
   
   {DUPLICATE LINE}

   %routine Duplicate Line (%record (Line fm) %name Old, New)
      %integer Size = Size of Line Fm + Old_Length,
               Max = New_Max Length

      Bulk Move (Size, 
                 Byte Integer (Addr (Old)),
                 Byte Integer (Addr (New)))
      New_Max Length = Max
   %end {Duplicate Line}

   {EXTEND CURRENT LINE}

   %routine Extend Current Line (%integer By)
      %integer New Size,
               Max
      %record (Line fm) %name L
      %record (Marker fm) %name  M
      %record (Magic Marker fm) %name  MM
      
      %return %if Current_Length + By <= Current_Max Length
      Max = Current_Max Length + By
      %if Max < 80 %start
         New Size = 80
      %else
         New Size = Max + 16
      %finish
      L == New Line fm (New Size)
      Duplicate Line (Current, L)
      %if L_Last == Nil %start
         Current Buffer_First == L 
      %else
         L_Last_Next == L 
      %finish
      Found Line == L %if Current == Found Line
      Copy Position == L %if Copy Position == Current
      L_Next_Last == L
      %if L_Bits&Marked # 0 %start
         M == Current Buffer_Markers
         %while M ## Nil %cycle
            M_Line == L %if M_Line == Current
            M == M_Next
         %repeat
      %finish
      %if L_Bits&Magic Markered # 0 %start
         MM == Current Buffer_Magic Markers
         %while MM ## Nil %cycle
            MM_Line == L %if MM_Line == Current
            MM == MM_Next
         %repeat
      %finish
      TOS == L %if TOS == Current
      Dispose Line FM (Current) 
      Current == L
   %end {Extend Current Line}

   {JUSTIFY LINE}

   %routine Justify Line (%record (Line FM) %name L,
                          %integer Y)
      %own %integer Direction = Forwards
      %integer Needed,
               Gaps = 0,
               First Gap = 0,
               Last Gap,
               P
      %constant %byte Added Space = 255
      %record (Line fm) %name Curr

      %return %if L_Length = 0
      L_Length = L_Length - 1 %while L_Text (L_Length) = ' '
      %return %if L_Length >= Right Margin
      Needed = Right Margin - L_Length
      P = 1
      P = P + 1 %while P < L_Length %and %c
                       L_Text (P) = ' '
      %return %if P = L_Length      {nothing but spaces on the line}
      {now at first printing character - now find how many gaps there are}
      %while P < L_Length %cycle
         %if L_Text (P) = ' ' %start
            First Gap = P %if First Gap = 0
            Gaps = Gaps + 1 
         %finish
         P = P + 1
      %repeat
      %return %if Gaps = 0          {nothing can be done}
      {now fill in the line}
      %if L_Max Length < Right Margin %start
         %if L == Current %start
            Extend Current Line (Right Margin - L_Length)
            L == Current               {point back at it}
         %else
            Curr == Current
            Current == L
            Extend Current Line (Right Margin - L_Length)
            Current == Curr
         %finish
      %finish
      {now pad it out}
      %if Direction = Forwards %start
         P = First Gap
      %else
         P = L_Length
      %finish
      %cycle
         %if L_Text (P) = ' ' %start
            {now insert a space}
            Bulk Move (L_Length - P,
                       L_Text (P + 1),
                       L_Text (P + 2))
            L_Text (P + 1) = Added Space
            L_Length = L_Length + 1
         %finish
         %if Direction = Forwards %start
            P = P + 1
            P = First Gap %if P = L_Length
         %else
            P = P - 1
            P = L_Length %if P < First Gap
         %finish
      %repeat %until L_Length = Right Margin
      {now replace all the Added Spaces with real spaces}
      %for P = 1, 1, L_Length %cycle
         L_Text (P) = ' ' %if L_Text (P) = Added Space
      %repeat
      Cursor (0, Y)
      Print Line (L, 1, Y)
      %if Direction = Forwards %start
         Direction = Backwards
      %else
         Direction = Forwards
      %finish
   %end {Justify Line}

   {INSERT}

   %routine insert (%integer what)
      {This routine adds a character to the current line}
      {First, actually add it}
      %integer New Space

      desired X = -1
      add new line %if current_bits & pseudo line # 0
      %if typing mode = insert character %start
         New Space = 1
         New Space = Pos - Current_Length + 1 %if Pos > Current_Length
         Extend Current Line (New Space)
         %unless pos > current_length %start
            bulk move (current_length - pos + 1,      {the rest of the line}
                       current_text (pos),            {from the current position}
                       current_text (pos + 1))        {to one place along}
         %else
            %while pos > (current_length + 1) %cycle
               current_length = current_length + 1
               current_text (current_length) = ' '
            %repeat
         %finish
         current_length = current_length + 1          {bump up line length}
      %else {in overwrite character mode}
         %if pos > current_length %start
            %if Current_Length = Max Line Length %start
               fail ("Line too long", yes)
               %return
            %finish
            New Space = 1
            New Space = Pos - Current_Length + 1 %if Pos > Current_Length
            Extend Current Line (New Space)
            %while pos > (current_length + 1) %cycle
               current_length = current_length + 1
               current_text (current_length) = ' '
            %repeat
            current_length = current_length + 1
         %finish
      %finish
      {at this point we have a normalised line}
      current_text (pos) = what                 {fill in}
      current buffer_changed = yes
      current_bits = current_bits ! contains CCs %if what < ' ' %or -
                                                     What >= DEL
      pos = pos + 1
      %if pos > current_length %and -
          Auto Line Break = Yes %and -
          Pos = Right Margin + 2 %start
         pos = pos - 1 %until current_text (pos) = ' ' %or %c
                              pos = 1
         %if pos = 1 %start
            pos = current_length + 1 
         %else
            pos = pos + 1          {leave the space at the end of the line}
         %finish
         reset cursor
         break line
         current_last_length = current_last_length - 1 {take away the space}
         pos = current_length + 1 %unless Current_Length < Pos
         reset cursor
         %return
      %finish
      {now update the screen}
      %if current_bits & contains CCs = 0 %and %c
          Terminal Characteristics&Insert Mask # 0 %start
         %if pos > current_length %start
            {we have added a character at the end of the line}
            %if pos <= Terminal Width  + 1 %start
               {normal case}
               put ch (what)
               CX = CX + 1 %unless pos = Terminal Width + 1
                                                           {move along the line}
            %else %if pos = Terminal Width + 2
               {moved off the end of the line}
               Graph On
               Left T
               Graph Off
            %finish
         %else
            %if pos < Terminal Width %start
               {on the screen}
               %if Typing Mode = Insert Character %start
                  %if Terminal Characteristics&Static Insert Ability # 0 %start
                     Static Insert (1)
                     Put Ch (What)
                  %else %if Terminal Characteristics&Noisy Insert Ability # 0
                     Insert On
                     Put Ch (What)
                     Insert Off
                  %else
                     Insert On
                     Put Ch (What)
                  %finish
               %else
                  Insert Off
                  put ch (what)
               %finish
               CX = CX + 1
               %if current_length > Terminal Width %and %c
                   typing mode = insert character %start
                  put overflow marker
                  set cursor
               %finish
            %finish
         %finish
      %else
         print line (current, pos - 1, CY)
         reset cursor
      %finish               
      put symbol (BEL) %if pos = beep column + 1
   %end {of insert}

   {UNDO PARCEL}

   %routine Undo Parcel
      %integer I, End

      %if Bin Free = Bin Start %start
      Empty:
         Fail ("Character Bin Empty", Yes)
      %else
         %cycle
            Bin Free = Bin Free - 1
            Bin Free = Bin Max %if Bin Free < 0
            -> Empty %if Bin Free = Bin Start
         %repeat %until Bin (Bin Free) # Divider
         Divided = No
         End = Bin Free
         %cycle
            Bin Free = Bin Free - 1
            Bin Free = Bin Max %if Bin Free < 0
            %exit %if Bin (Bin Free) = Divider %or %c
                      Bin Free = Bin Start
         %repeat
         %if Bin (Bin Free) = Divider %start
            Divided = Yes
            Bin Free = (Bin Free + 1)&Bin Max 
         %finish
         I = Bin Free
         %cycle
            Insert (Bin (I))
            I = (I + 1)&Bin Max
         %repeat %until I > End
      %finish
      Parcel Open = No
   %end {Undo Parcel}

   {INSERT TAB}

   %routine insert TAB
      %integer old pos = pos
      %integer size    
      %integer I
      
      desired X = -1
      Close Parcel
      add new line %if current_bits & pseudo line # 0
      size = TAB width - rem (pos - 1, TAB width)
      size = TAB width %if size = 0
      pos  = pos + size
      %if old pos > current_length %or %c
          (typing mode = overwrite character %and %c
          pos > current_length) %start
         %if Current_Length + Size > Max Line Length %start
            Pos = Old Pos
            Fail ("Line too long", Yes)
            %return
         %finish
         {simply extend the line}
         Extend Current Line (Pos - Current_Length)
         %while current_length + 1 < pos %cycle
            current_length = current_length + 1
            current_text (current_length) = ' '
            put symbol (' ') %if current_next == nil %and %c
                                 current_length < Terminal Width
         %repeat  
         put overflow marker %if old pos <= Terminal Width < pos
         reset cursor
      %else             
         %if typing mode = insert character %start
            %if Current_Length + Size > Max Line Length %start
               Pos = Old Pos
               Fail ("Line too long", Yes)
               %return
            %finish
            {first the buffer}
            Extend Current Line (Size)
            bulk move (current_length - old pos + 1,
                       current_text (old pos),
                       current_text (pos))
            current_length = current_length + size
            current_text (I) = ' ' %for I = old pos, 1, pos - 1
            {now update the display}
            %if current_bits & contains CCs = 0 %and %c
                Terminal Characteristics&Insert Mask # 0 %start
               %if pos < Terminal Width %start
                  %if Terminal Characteristics&Static Insert Ability # 0 %start
                     Static Insert (Size)
                  %else %if Terminal Characteristics&Noisy Insert Ability # 0
                     Insert On
                     Put Symbol (' ') %for I = 1, 1, Size
                     Insert Off
                  %else
                     Insert On
                     Put Symbol (' ') %for I = 1, 1, Size
                  %finish
               %finish
               put overflow marker %if current_length > Terminal Width
            %else
               print line (current, old pos, CY)
            %finish
         %finish
         reset cursor
      %finish
      Current Buffer_Changed = Yes
   %end {of insert TAB}

   {BACK TAB}

   %routine back TAB
      %integer cut, 
               old pos = pos,
               n,
               Old Length = Current_Length

      %return %if current_bits & pseudo line # 0 %or %c
                  pos = 1

{*      pos = pos - TAB width
      pos = ((pos - 2)//TAB Width)*TAB Width + 1
      %if typing mode = overwrite character %or %c
          Pos > Current_Length %or %c
          Pos < 0 %start
         pos = 1 %if pos < 1
         reset cursor
      %else
         %if pos > 0 %start
            Cut = Old Pos - Pos
         %else
            cut = old pos - 1
            pos = 1
         %finish
         Dump Parcel (Pos, Cut)
         bulk move (current_length - old pos + 1,      {remainder of line}
                    current_text (old pos),
                    current_text (pos))
         current_length = current_length - cut
         reset cursor
         %if current_bits & contains CCs = 0 %and %c
             Terminal Characteristics&Delete Character Ability # 0 %start
            Delete Characters (Cut)
            %if Old Length > Terminal Width %start
               {Fix end of line}
               Cursor (Terminal Width - Cut - 2, CY)
               Print Line (Current, Terminal Width - Cut - 1, CY) 
               Reset Cursor
            %finish
         %else
            print line (current, pos, CY)
            set cursor
         %finish
      %finish
      Current Buffer_Changed = Yes
   %end {back TAB}

   %routine %spec Join Line
   %routine %spec Remove Line

   {ERASE}

   %routine erase
      {the routine deletes the character under the cursor}
      {first deal with the buffer}
      %return %if current_bits & pseudo line # 0
      %if current_length = 0 %or pos > current_length %start
         %if Join At End = Yes %start
            %if Current_Length = 0 %and -
                Current Buffer_Last == Current_Next %start
               Remove Line
               %return
            %finish
            Current == Current_Next       {move onto the next line}
            CX = 0
            Pos = 1
            Set Screen (1)
            Join Line
         %finish
         %return 
      %finish
      desired X = -1
      Open Parcel %unless Parcel Open = Yes    {Parcel up all single characters}
      dump character (current_text (pos))
      current buffer_changed = yes
      bulk move (current_length - pos,    {the rest of the line}
                 current_text (pos + 1),
                 current_text (pos))
      current_length = current_length - 1
      {now update the screen}

      %if current_bits & contains CCs = 0 %and %c
          Terminal Characteristics&Delete Character Ability # 0 %start
         %if current_length < Terminal Width %start
            Delete Characters (1)
         %else %if current_length = Terminal Width
            Delete Characters (1)
            cursor (Terminal Width - 2, CY)
            put symbol (current_text (Terminal Width - 1))       {new character}
                                                 {and scrap the off edge marker}
            put symbol (current_text (Terminal Width))
            set cursor
         %else
            %if pos < Terminal Width %start
               Delete Characters (1)
               cursor (Terminal Width - 2, CY)

               put symbol (current_text (Terminal Width - 1))
               Graph On
               Left T
               Graph Off
               set cursor
            %finish
         %finish
      %else
         print line (current, pos, CY)
         reset cursor
         check for CCs (current) %if current_bits & contains CCs # 0
      %finish
   %end {of erase}

   {REMOVE MARKER}
   
   %routine remove marker
      {this routine removes the marker from the current line}
      %record (marker fm) %name L == nil,
                                J,
                                M == Current Buffer_Markers
      
      %while M ## Nil %cycle
         %if M_line == current %start
            %if L == nil %start
               current buffer_markers == M_next
            %else
               L_next == M_next
            %finish
            J == M
            M == M_Next
            dispose (J)
         %else
            L == M
            M == M_next
         %finish
      %repeat
      Current_Bits = Current_Bits & (\Marked)
   %end {of remove marker}
   
   {REMOVE MAGIC MARKER}
   
   %routine Remove Magic Marker
      {this routine removes the marker from the current line}
      %record (Magic Marker fm) %name L == nil,
                                      J,
                                      M == Current Buffer_Magic Markers
      
      %while M ## Nil %cycle
         %if M_line == current %start
            %if L == nil %start
               current buffer_Magic Markers == M_next
            %else
               L_next == M_next
            %finish
            J == M
            M == M_Next
            dispose (J)
         %else
            L == M
            M == M_next
         %finish
      %repeat
      Current_Bits = Current_Bits & (\Magic Markered)
   %end {of Remove Magic Marker}
   
   {REMOVE LINE}

   %routine remove line
      %integer where
      %record (line fm) %name L, N, added

      {SHRINK}

      %routine shrink
         restrict cursor
         Forward Scroll (CY, Window Bottom)
         screen lines = screen lines - 1
         set cursor
      %end {of shrink}

      {MAIN CODE OF REMOVE LINE}

      %return %if current_bits & pseudo line # 0
      L == current_last
      N == current_next
      current buffer_changed = yes
      remove marker %if current_bits & marked # 0
      Remove Magic Marker %if Current_Bits & Magic Markered # 0
      copy position == nil %if current == copy position
      Found Line    == Nil %if Current == Found Line
      current_last == deleted      {save the deleted line}
      deleted      == current
      Deleted_Bits = Deleted_Bits&(\Displayed)

      %if L == nil %start
         {at the top of the file}
         N_last == nil
         current buffer_first == N {new top line of screen}
         TOS     == N              {also new top of screen}
         current == N              {move down since we can}
         {now see about the screen}
         %if BOS_next == nil %start
            {no more text at the bottom}
            shrink
            %return
         %else
            BOS == BOS_next        {bring on next line as expected}
            added == BOS
            where = window bottom
            Forward Scroll (CY, Window Bottom)
         %finish
      %else
         L_next  == N              {rejoin up the linked list}
         N_last  == L
         current == N
         %if BOS_next == nil %start
            %if TOS_last == nil %start
               shrink
               %return
            %else
               TOS   == TOS_last
               added == TOS
               where =  window top
               Reverse Scroll (Window Top, CY)
               CY = CY + 1
            %finish
         %else
            {simply bring on another line}
            BOS   == BOS_next
            added == BOS
            where =  window bottom
            Forward Scroll (CY, Window Bottom)
         %finish
      %finish

      print line (added, 1, where)
      restrict cursor
      %if current_next == nil %start
         {bring back end marker}
         cursor (find X (current, current_length + 1), CY)
         print line (current, current_length + 1, CY)
      %finish
      reset cursor
   %end {of remove line}

      {SET Magic Marker}

      %routine Set Magic Marker
         %record (Magic Marker fm) Magic Marker Pattern
         %record (Magic Marker fm) %name Magic Marker

         Magic Marker == New (Size Of Magic Marker fm)
         Current_Bits = Current_Bits ! Magic Markered
         Magic Marker_Pos = Pos
         Magic Marker_Line == Current
         Magic Marker_Next == Current Buffer_Magic Markers
         Current Buffer_Magic Markers == Magic Marker
      %end {Set Magic Marker}

      {SEARCH}

      %routine search (%record (text fm) %name what,
                       %integer replace,
                                Direction)
         %integer                P      = pos
         %record (line fm) %name L      == current
         %integer                line   = 0,
                                 heading = no,
                                 N,
                                 Y,
                                 First = Yes,
                                 Local Direction
         %own %integer Underline X = 0

         %return %if what_length = 0
         {deal with current line first}
         %if Direction = Forwards %start
            P = find string (case significance,
                             what_string,
                             pos + 1,
                             current)
         %else
            P = Reverse find string (case significance,
                                     what_string,
                                     pos - 1,
                                     current)
         %finish
         Local Direction = Direction
         %if P = 0 %start
!VMS!       Interrupted = 0
            %cycle
               %if First = No %and -
                   L == Current %start
                  L == Nil
               %else
                %if Local Direction = Forwards %start
                     L == L_Next
                  %else
                     L == L_Last
                  %finish
               %finish
               %if L == nil %start
                  %if Wrap Search = Yes %and -
                      First = Yes %start
                     cursor (0, window top)
                     Clear Line
                     %if Direction = Forwards %start
                        L == Current Buffer_First
                        put string ("Wrapping...")
                     %else
                        L == Current Buffer_Last
                        Cursor (68, Window Top)
                        Put String ("...gnipparW")
                        Cursor (67, Window Top)
                     %finish
                     Heading = Yes
                     Replace = Yes
                     Flush Symbols
                     First = No
                     %continue
                  %else %if Back Search  = Yes %and -
                            First = Yes
                     cursor (0, window top)
                     Clear Line
                     %if Direction = Forwards %start
                        Local Direction = Backwards
                        L == Current_Last
                        Cursor (69, Window Top)
                        Put String ("...gnikcaB")
                        Cursor (68, Window Top)
                     %else
                        Local Direction = Forwards
                        L == Current_Next
                        Put String ("Backing...")
                     %finish
                     Heading = Yes
                     Replace = Yes
                     %if L == Nil %start
                        Local Direction = Direction
                     %else
                        Heading = Yes
                        Flush Symbols
                        First = No
                        %continue
                     %finish
                  %finish
                  %if Local Direction = Forwards %start
                     fail (what_string . " not found", yes)
                  %else
                     fail (what_string . " not found backwards", yes)
                  %finish
                  found = no
                  Last Search Direction = Local Direction
                  %return
               %finish
               line = line + 1
               %if line & 127 = 0 %start
                  %if Interrupted # 0 %start
!VMS!                Redraw Screen
!VMS!                Interrupted = 0
                     Fail ("Search interrupted", No)
                     Found = No
                     %return
                  %finish
                  %if heading = no %start
                     cursor (0, window top)
                     Clear Line
                     %if Local Direction = Forwards %start
                        put string ("Searching...")
                     %else
                        Cursor (67, Window Top)
                        Put String ("...gnihcraeS")
                        Cursor (66, Window Top)
                     %finish
                     heading = yes
                     replace = yes
                  %else
                     put symbol ('.')
                     %if Local Direction = Backwards %start
                        Put Symbol (BS)
                        Put Symbol (BS)
                     %finish
                  %finish
                  flush symbols
               %finish
               %if Local Direction = Forwards %start
                  P = find string (case significance,
                                   what_string,
                                   1,
                                   L)
               %else
                  P = Reverse find string (case significance,
                                           what_string,
                                           L_Length,
                                           L)
               %finish
            %repeat %until P # 0
         %finish
         {found it}
         Set Magic Marker
         current == L
         pos = P
         CX = find X (current, pos)
         desired X = -1
         Close Parcel
         %if replace = yes %start
            cursor (0, window top)
            print line (TOS, 1, window top)
         %finish
         set screen (0)
         %if Terminal Characteristics&Underline Text Ability # 0 %start
            %if Found = Yes %and %c
                Found Line ## Nil %and -
                Found Line_Bits&Displayed # 0 %and %c
                Underline X < Terminal Width %start
               {some other underline to remove}
               L == TOS
               Y = Window Top
               %while L ## Found Line %cycle
                  Y = Y + 1 
                  L == L_Next 
                  -> Security %if L == Nil         {just in case}
               %repeat
               Cursor (Find X (Found Line, Found Pos), Y)
               %for N = Found Pos, 1, Found Pos + Found Length - 1 %cycle
                  Put Ch (Found Line_Text (N))
               %repeat
               Set Cursor
            %finish
        Security:
            Underline X = CX + 1
            %if Underline X < Terminal Width %start
               Underline On
               %for N = Pos, 1, Pos + What_Length - 1 %cycle
                  Put Ch (Current_Text (N))
               %repeat
               Normal
               Set Cursor
            %finish
         %finish
         found = yes
         Found Line == Current
         Found Pos  =  Pos
         Found Length = What_Length
         Last Search Direction = Local Direction
      %end {of search}

      {REPLACE}
      
      %routine replace (%record (text fm) %name what)
         %integer I,
                  Old Length = Current_Length,
                  Cut
         %label Refresh It

         {this removes the "what" and puts in the "replace string"}
         %return %if found = no %or %c
                     Found Line ## Current %or %c
                     Found Pos # Pos
         desired X = -1
         Close Parcel
         %if what_length # replace string_Length %start
            %if What_Length < Replace String_Length %start
               Extend Current Line (Replace String_Length - What_Length)
            %finish
            bulk move (Current_Length - pos - what_length + 1,
                       current_text (pos + what_Length),
                       current_text (pos + replace string_length))
            current_length = current_length - what_length + %c
                             replace string_length
         %finish
         bulk move (replace string_length,
                    replace string_text (1),
                    current_text (pos))
         %if pos < Terminal Width + 1 %start
            %if current_bits & contains CCs = 0 %start
               {First sort out the display}
               %if what_length < replace string_length %start
                  %if Terminal Characteristics&(Noisy Insert Ability! %c
                                                Dynamic Insert Ability) # 0 %start
                     Insert On
                     put symbol (' ') %for I = 1, 1, 
                                           replace string_length - what_length
                     Insert Off
                  %else %if Terminal Characteristics&Static Insert Ability # 0
                     Static Insert (Replace String_Length - What_Length)
                  %else
                     -> Refresh It
                  %finish
                  put overflow marker %if current_length > Terminal Width
                  set cursor
               %else %if replace string_length < what_length
                  -> Refresh It %if %c
                           Terminal Characteristics&Delete Character Ability = 0
                  Cut = What_Length - Replace String_Length
                  Delete Characters (Cut)
                  %if Old Length > Terminal Width %start
                     Cursor (Terminal Width - Cut - 2, CY)
                     Print Line (Current, Terminal Width - Cut - 1, CY) 
                     Set Cursor
                  %finish
               %finish
               {Now put out the new text}
               put string (replace string_string)
            %else
            Refresh It:
               print line (current, pos, CY)
            %finish
         %finish
         pos = pos + replace string_length
         reset cursor
         current buffer_changed = yes
         found = no                    {only do one change per find!}
      %end {of replace}

      {MOVE TO END OF LINE}

      %routine move to end of line
         Set Magic Marker
         desired X = -1
         Close Parcel
         pos = current_length + 1     {off end of line}
         reset cursor
      %end {move to end of line}

      {MOVE TO START OF LINE}

      %routine move to start of line
         Set Magic Marker
         desired X = -1
         Close Parcel
         pos = 1
         CX = 0
         set cursor
      %end {of move to start of line}

      {MOVE TO END OF FILE}

      %routine move to end of file
         Set Magic Marker
         current == current buffer_last
         pos = 1
         CX = 0
         set screen (0)
      %end {of move to end of file}

      {SCROLL PAGE DOWN}
      
      %routine scroll page down
         %while CY > window top + 1 %cycle
            %exit %if BOS_next == nil
            TOS_bits = TOS_bits & (\displayed)
            TOS == TOS_next
            BOS == BOS_next


            Forward Scroll (Window Top, Window Bottom)

            print line (BOS, 1, window bottom)
            CY = CY - 1
         %repeat
         set cursor
      %end {of scroll page down}
      
      {SCROLL PAGE UP}
      
      %routine scroll page up
         %while CY < window bottom - 1 %cycle
            %exit %if TOS_last == nil
            BOS_bits = BOS_bits & (\displayed)
            BOS == BOS_last

            Reverse Scroll (Window Top, Window Bottom)
 
            TOS == TOS_last
            print line (TOS, 1, window top)
            CY = CY + 1
         %repeat
         set cursor
      %end {of scroll page up
      
      {GET TEXT}

      %routine get text (%record (text fm) %name T,
                         %string (31) P,
                         %integer flip)
         %label restart
         %integer ch, col
         %byte %name L == T_length
         %integer Save Rest = At Rest

         at rest = no
         {first get the text}
         keypad off
      restart:
         col = length (P)
         cursor (0, window top)
         clear line
         put string (P)
         Top X = Length (P)
         L = 0
         %cycle
            ch = get next ch
            %if ch = CAN %start
               -> restart
            %else %if ch = DEL %or ch = BS
               %if L > 0 %start
                  %if T_Text (L) < ' ' %start
                     put symbol (BS)
                     put Symbol (BS)
                     put string ("  ")
                     put symbol (BS)
                     put symbol (BS)
                  %else
                     put symbol (BS)
                     put symbol (' ')
                     put symbol (BS)
                  %finish
                  L = L - 1
                  col = col - 1
                  %if col = 0 %start
                     put symbol (T_text (L))
                     col = 1
                  %finish
                  Top X = Top X - 1
               %finish
            %else %if ch = CR
               %exit
            %else %if (ch = DLE %or ' ' <= ch <= '~') %and %c
                      L < max text length
               %if ch = DLE %start
                  %if From Journal = yes %start
                     Read Symbol (Ch)
                  %else
                     Ch = Get Symbol
                     Put Journal Symbol (Ch)
                  %finish
                  -> Nasty %if Ch > 127
               %finish
               %if col = Terminal Width - 1 %start
                  cursor (0, Window Top)
                  Delete Characters (1)
                  cursor (Terminal Width - 2, Window Top)
               %else
                  col = col + 1
               %finish
               Put ch (ch)
               Top X = Top X + 1
               ch = upper case (ch) %if flip = yes
               L = L + 1
               T_text (L) = ch
            %else
            Nasty:
               put symbol (BEL) %if Beep Mute = No
            %finish
         %repeat   
         keypad on
         Top X = -1
         %if L = 0 %start
            cursor (0, window top)
            print line (TOS, 1, window top)
            set cursor
         %finish
         At Rest = Save Rest
      %end {of get text}

      {MOVE TO END OF PAGE}

      %routine move to end of page
         Set Magic Marker
         current == BOS
         pos = current_length + 1
         CX = find X (current, pos)
         set screen (0)
      %end {of move to end of page}

      {MOVE TO START OF PAGE}
      
      %routine move to start of page
         Set Magic Marker
         current == TOS
         pos = 1
         CX = 0
         set screen (0)
      %end {of move to start of page}

      %routine %spec Put Key Definition (%integer K)

      {INTERACT}
      
      %routine interact
         %integer ch,
                  interaction failed = no
         
         %constant %integer max option = 18,
                            max Y or N = 13,
                            TAB width option         = Max Y or N + 1,
                            Left Margin Option       = Max Y or N + 3,
                            Right Margin Option      = Max Y or N + 4,
                            Journal Frequency Option = Max Y Or N + 5

         %own %string (21) %array names (0 : max option) = %c
            "Show TAB",
            "Auto Indent",
            "Auto Line Break",
            "Justify",
            "Case Significance",
            "Search Before Replace",
            "Free Cursor",
            "Write Back",
            "Beep Mute",
            "Forbid Exit",
            "Wiggle",
            "Wrap Search",
            "Back Search",
            "Join At End",
            "TAB Width",
            "Beep Column",
            "Left Margin",
            "Right Margin",
            "Journal Frequency"

         %constant %integer %array defaults (0 : max option) =
            no,                     {Show TAB}
            yes,                    {Auto Indent}
            no,                     {Auto Line Break}
            No,                     {Justify}
            no,                     {Case Significance}
            yes,                    {Search Before Replace}
            no,                     {Free Cursor}
            yes,                    {Write Back}
            no,                     {Beep Mute}
            Yes,                    {Forbid Exit}
            No,                     {Wiggle}
            No,                     {Wrap Search}
            No,                     {Back Search}
            No,                     {Join At End}
            3,                      {TAB Width}
            75,                     {Beep Column}
            1,                      {Left Margin}
            80,                     {Right Margin}
            0                       {Journal Frequency}

         {WHICH}
         
         %integer %map which (%integer sel)
            {this simulates an %integer %name %array}
            %switch O (0 : max option)
         
            -> O (sel)
            
            {Y or N}
            O (0):  %result == show TAB
            O (1):  %result == auto indent
            O (2):  %result == auto line break
            O (3):  %result == Justify
            O (4):  %result == case significance
            O (5):  %result == search before replace
            O (6):  %result == free cursor
            O (7):  %result == write back
            O (8):  %result == Beep Mute
            O (9):  %result == Forbid Exit
            O (10): %result == Wiggle
            O (11): %result == Wrap Search
            O (12): %result == Back Search
            O (13): %result == Join At End

            {Numbers}
            O (Max Y Or N + 1): %result == TAB width
            O (Max Y Or N + 2): %result == beep column
            O (Max Y Or N + 3): %result == Left Margin
            O (Max Y Or N + 4): %result == Right Margin
            O (Max Y Or N + 5): %result == Journal Frequency
         %end {of which}

         {SET MODES}
   
         %routine set modes
            %integer line
            %integer count
            %integer n
            
               
            {PUT VALUE}
      
            %routine put value
               %if line > max Y or N %start
                  erase line
                  put integer (which (line))
               %else
                  %if which (line) = yes %then put string ("Yes") %c
                                         %else put string ("No ")
               %finish
            %end {of put value}
      
            {MAIN CODE OF SET MODES}
      
            clear window
            %for line = 0, 1, max option %cycle
               cursor (35 - length (names (line)), window top + line)
               put string (names (line))
               cursor (40, window top + line)
               %if Terminal Characteristics&Reverse Text Ability # 0 %start
                  Reverse On %if Line = 0
               %else %if Terminal Characteristics&Dim Text Ability # 0
                  Dim On %unless Line = 0
               %finish
               put value
               Normal
            %repeat
            cursor (10, window top + max option + 2)
            put string ("Use: cursor keys to select desired option")
            cursor (15, window top + max option + 3)
            put string ("Y or N, or number to change option, D for default")
            cursor (15, window top + max option + 4)
            put string ("RETURN to return to edit")
            line = 0
            Cursor Off %if Terminal Characteristics&Supress Cursor Ability # 0
            %cycle
               cursor (40, line + window top)
               ch = upper case (get next ch)
               %if line > max Y or N %start
                  Reverse On %if Terminal Characteristics&Reverse Text Ability # 0
                  %if ch = 'D' %start
                     which (line) = defaults (line)
                     put value
                  %else %if '1' <= ch <= '9'
                     which (line) = ch - '0'
                     cursor (40, line + window top)
                     erase line
                     put symbol (ch)
                     count = 1
                     %cycle
                        ch = upper case (Get Next Ch)
                        %if ch = DEL %or ch = BS %start
                           %if count > 0 %start
                              count = count - 1
                                 put symbol (BS)
                                 put symbol (' ')
                                 put symbol (BS)
                              which (line) = which (line) // 10
                           %finish
                        %else %if ch = CAN
                           which (line) = 0
                           count = 0
                           cursor (40, line + window top)
                           erase line
                        %else %if '0' <= ch <= '9'
                           which (line) = which (line) * 10 + ch - '0'
                           put symbol (ch)
                           count = count + 1
                        %else
                           %exit
                        %finish
                     %repeat
                     %if line = Right Margin Option %start
                        %unless 1 <= Right Margin <= max line length %start
                           Right Margin = Terminal Width
                        %finish
                     %else %if line = TAB width option
                        %unless 1 <= TAB width <= Terminal Width %start
                           TAB width = 3
                        %finish
                     %else %if Line = Journal Frequency Option
                        %if Out File = "" %start
                           Journal Frequency = 0
                        %else
                           %unless 0 <= Journal Frequency <= 512 %start
                              Journal Frequency = 0 
                           %finish
                           Set Journal Frequency (Journal Frequency)
                           Put Journal Heading %if Journal Frequency > 0
                              {Only does it once}
                        %finish
                     %finish
                     which (line) = defaults (line) %if which (line) = 0
                  %finish
                  Normal %if Terminal Characteristics&Reverse Text Ability # 0
               %else
                 %if ch = 'Y' %start
                     which (line) = yes
                  %else %if ch = 'N'
                     which (line) = no
                  %else %if ch = 'D'
                     which (line) = defaults (line)
                  %finish
                  Reverse On %if Terminal Characteristics& %c
                                 Reverse Text Ability # 0
                  put value
                  Normal %if Terminal Characteristics& %c
                             Reverse Text Ability # 0
               %finish
               %if ch = CR %or ch = 'R' %or ch = EM %start
                  %exit
               %else %if ch = Up Key %or %c
                         Ch = Down Key 
                  cursor (40, line + window top)

                  Dim On %if Terminal Characteristics&Dim Text Ability # 0

                  put value

                  Normal %if Terminal Characteristics&Dim Text Ability # 0

                  %if ch = Down Key %start
                     %if line = max option %then line = 0 %c
                                           %else line = line + 1 
                  %else
                     %if line = 0 %then line = max option %c
                                  %else line = line - 1 
                  %finish
                  cursor (40, line + window top)

                  Reverse On %if Terminal Characteristics& %c
                                 Reverse Text Ability # 0

                  put value

                  Normal %if Terminal Characteristics& %c
                             Reverse Text Ability # 0
               %finish
            %repeat
            Cursor On %if Terminal Characteristics&Supress Cursor Ability # 0
         %end {of set modes}
   
         {PROFILE OPERATIONS}

         %routine Profile Operations
            %integer Ch

            {DUMP PROFILE}
   
            %routine dump profile
               %string (255) file
               %integer n
   
               %on 3, IO error %start
                  cursor (1, window bottom)
                  erase line
                  put string (event_message)
                  interaction failed = yes
                  %return
               %finish
   
               read string (file, "Filename for Profile: ")
               cursor (window top, 0)
               erase line
               flush symbols
               %unless file = "" %start
!VMS!             Default File Name = ".IE"
!VMS!             File = Expand File Name (File)
!VMS!             File -> File .(";")
!VMS!             Default File Name = Grand Default File Name
                  open output (3, file)
                  select output (3)
                  cursor (1, window bottom)
!VMS!             put string ("Dumping Profile to " . out file name)
{APM}             Put String ("Dumping Profile to " . File)
!UNIX!            Put String ("Dumping Profile to " . Output Name)
                  put symbol (CR)
                  flush symbols
   
                  %for n = 0, 1, max option %cycle
                     %if which (n) # defaults (n) %start
                        print string (names (n))
                        print string (" = ")
                        %if n > max Y or N %start
                           write (which (n), 0)
                        %else
                           %if which (n) = yes %start
                              print string ("Yes")
                           %else
                              print string ("No")
                           %finish
                        %finish
                        new line
                     %finish
                  %repeat
   
                  %if window top > 0 %start
                     print string ("Window Top = ")
                     write (window top, 0)
                     new line
                  %finish
   
                  %if window bottom < 23 %start
                     print string ("Window Bottom = ")
                     write (window bottom, 0)
                     new line
                  %finish
   
                  %if EOF marker # default EOF marker %start
                     print string ("EOF Marker = ")
                     print string (EOF marker)
                     new line
                  %finish
   
                  %if Put Buffer Verify = Yes %start
                     Print String ("Put Buffer Verify = Yes")
                     New Line
                  %finish
   
                  %if Keep Journal = Yes %start
                     Print String ("Keep Journal = Yes")
                     New LIne
                  %finish
   
                  close output
               %finish
            %end {dump profile}
   
            {NEW PROFILE}
            
            %routine new profile
               %string (255) file, JunKK
   
               %on 3, IO error, abort %start
                  cursor (1, window bottom)
                  %if Event_Message # "" %start
                     erase line
                     put string (event_message)
                  %finish
                  interaction failed = yes
                  %return
               %finish
               
               read string (file, "Profile file: ")
               cursor (0, window top)
               erase line
               flush symbols
               %unless file = "" %start
                  cursor (1, window bottom)
!VMS!             JunKK = Default File Name
!VMS!             Default File Name = ".IE"
!VMS!             file = expand file name (file)
!VMS!             Default File Name = JunKK
                  put string ("Setting Profile from " . file)
                  put symbol (CR)
                  flush symbols
                  set profile (file) 
               %finish
            %end {new profile}
   
            {MAIN CODE OF PROFILE OPERATIONS}

            Cursor (0, Window Top)
            Put String ("S)et or D)ump profile: ")
            Ch = Upper Case (Get Next Ch)
            Clear Line
            %if Ch = 'S' %start
               New Profile
            %else %if Ch = 'D'
               Dump Profile
            %finish
         %end {Profile Operations}

         {KEYBOARD OPERATIONS}

         %routine Keyboard Operations
            %integer Ch

            {DUMP KEYBOARD}
   
            %routine dump Keyboard
               %string (255) file
               %integer Ch
                        
   
               {PUT KEY}

               %routine Put Key (%integer K)
                  %record (Key Definition fm) %name Key == Keys (K)
                  %integer N

                  Print Symbol (K)
                  %if Key_Type = Letter %start
                     Print Symbol ('L')
                     Print Symbol (Key_Value)
                  %else %if Key_Type = Command
                     Print Symbol ('C')
                     Print Symbol (Key_Value)
                  %else {%if Key_Type = Macro}
                     Print Symbol ('M')
                     %for N = 1, 1, Key_Text_Length %cycle
                        %if Key_Text_Text (N) = NL %start
                           Print Symbol (255)
                        %else
                           Print Symbol (Key_Text_Text (N))
                        %finish
                     %repeat
                  %finish
                  New Line
               %end {Put Key}

               %on 3, IO error %start
                  cursor (1, window bottom)
                  erase line
                  put string (event_message)
                  interaction failed = yes
                  %return
               %finish
   
               read string (file, "Filename for Keyboard: ")
               cursor (window top, 0)
               erase line
               flush symbols
               %unless file = "" %start
!VMS!             Default File Name = ".KIE"
!VMS!             File = Expand File Name (File)
!VMS!             File -> File .(";")
!VMS!             Default File Name = Grand Default File Name
{\UNIX}           Open Output (3, File)
!UNIX!            Open Binary Output (3, file)
                  select output (3)
                  cursor (1, window bottom)
!VMS!             put string ("Dumping Keyboard to " . out file name)
{APM}             Put String ("Dumping Keyboard to " . File)
!UNIX!            Put String ("Dumping Keyboard to " . Output Name)
                  put symbol (CR)
                  flush symbols
   
                  %for Ch = 0, 1, 31 %cycle
                     Put Key (Ch) %if Keys (Ch)_Type # Undefined  {%and %c}
                  %repeat

                  %for Ch = ' ', 1, '~' %cycle
                     Put Key (Ch) %if Keys (Ch)_Type # Letter %or %c
                                      Keys (Ch)_Value # Ch 
                  %repeat

                  %for Ch = 127, 1, 255 %cycle
                     Put Key (Ch) %if Keys (Ch)_Type # Undefined
                  %repeat

                  close output
               %finish
            %end {dump Keyboard}

            {NEW KEYBOARD}
            
            %routine New Keyboard
               %string (255) file, JunKK
   
               %on 3, IO error, abort %start
                  cursor (1, window bottom)
                  erase line
                  put string (event_message)
                  interaction failed = yes
                  %return
               %finish
               
               read string (file, "Keyboard file: ")
               cursor (0, window top)
               erase line
               flush symbols
               %unless file = "" %start
!VMS!             JunKK = Default File Name
!VMS!             Default File Name = ".KIE"
!VMS!             file = expand file name (file)
!VMS!             Default File Name = JunKK
                  cursor (1, window bottom)
                  put string ("Setting Keyboard from " . file)
                  put symbol (CR)
                  flush symbols
                  set Keyboard (file) 
               %finish
            %end {New Keyboard}

            {SET ALL KEYS}

            %routine Set All Keys
               %integer Ch,
                        I,
                        L
               %own %integer K = Extra Keys ! 1
               %string (20) Name
               %constant %string (10) Not Used = "Unused Key"

            And Prompt:
               Cursor (0, Window Top)
               Clear Line
               Put String ("Code, Name, Cursor Key, U)ndefine: ")
               %cycle
                  Cursor (40, Window Top)
                  Erase Line
                  Put Key Definition (K)
                  Cursor (35, Window Top)
                  Ch = Upper Case (Get Next Ch)
                  %if Ch = CR %or %c
                      Ch = EM %start
                     Clear Line
                     %return
                  %else %if Ch = Up Key
                     K = K + 1 %if K < 255
                  %else %if Ch = Down Key
                     K = K - 1 %if K > 0
                  %else %if Ch = 'U'
                     Keys (K)_Type = Undefined
                  %else %if '0' <= Ch <= '9'
                     Clear Line
                     Put String ("Code: ")
                     I = Ch - '0'
                     L = 1
                     %cycle
                        Put Symbol (Ch)
                        Ch = Get Next Ch
                        %if Ch = DEL %or %c
                            Ch = BS %start
                           %if L > 0 %start
                              Left;    Put Symbol (' ');    Left
                              I = I//10
                              L = L - 1
                           %finish
                        %else %if Ch = CR
                           %exit
                        %else %if '0' <= Ch <= '9'
                           I = I*10 + (Ch - '0')
                        %finish
                     %repeat
                     K = I %if 0 <= I <= 255
                     -> And Prompt
                  %else %if ' ' < Ch < '~'
                     Clear Line
                     Put String ("Name: ")
                     Name = To String (Ch)
                     %cycle
                        Put Symbol (Ch)
                        Ch = Get Next Ch
                        %if Ch = BS %or %c
                            Ch = DEL %start
                           %if Name # "" %start
                              Length (Name) = Length (Name) - 1
                              Left;    Put Symbol (' ');    Left
                           %finish
                        %else %if ' ' <= Ch <= '~'
                           Name = Name . To String (Ch)
                        %else %if Ch = CR
                           %exit
                        %finish
                     %repeat
                     K = Extra Keys   {unknown key!}
                     %for I = 0, 1, 255 %cycle
                        %if Key Name (I) = Name %start
                           K = I
                           %exit
                        %finish
                     %repeat
                     -> And Prompt
                  %else
                     K = Ch
                  %finish
                  %if Key Name (K) = Not Used %start
                     {Skip over unused key codes}
                     %if Ch = Up Key %start
                        K = K + 1 %until  Key Name (K) # Not Used
                     %else {%if Ch = Down Key}
                        K = K - 1 %until  Key Name (K) # Not Used
                     %finish
                  %finish
               %repeat
            %end {Set All Keys}

            {MAIN CODE OF KEYBOARD OPERATIONS}

            Cursor (0, Window Top)
            Put String ("S)et or D)ump keyboard: ")
            Ch = Upper Case (Get Next Ch)
            Clear Line
            %if Ch = 'S' %start
               New Keyboard
            %else %if Ch = 'D'
               Dump Keyboard
            %else %if Ch = 'A'
               Set All Keys
            %finish
         %end {Keyboard Operations}
   
         {FILES}
         
         %routine files

!VMS!       %string (255) current = "",
!VMS!                     file spec,
!VMS!                     dir, file, extension, last, fancy

!VMS!       %integer Y, X, n,
!VMS!                first = yes,
!VMS!                new spec = no,
!VMS!                page, number
!VMS!                     
!VMS!       {PAUSE}
!VMS!       
!VMS!       %routine pause (%integer End)
!VMS!          %integer any
   
!VMS!          cursor (20, window bottom)
!VMS!          Feature
!VMS!          Keypad Off 
!VMS!          %if End = Yes %start
!VMS!             Put String ("RETURN to go back to edit, F for new file spec: ")
!VMS!          %else
!VMS!             put string ("RETURN to continue, ^Y to abort, F for new file spec: ")
!VMS!          %finish
!VMS!          Normal
!VMS!          any = get next Ch
!VMS!          Keypad Off
!VMS!          %signal 10 %if (any = EM %or %c
!VMS!                          Any = ETX) %and %c
!VMS!                         End = No
!VMS!          new spec = yes %and %signal 11 %if any = 'F' %or any = 'f'
!VMS!          cursor (20, window bottom)
!VMS!          clear line
!VMS!       %end {pause}
   
!VMS!       {SCROLL}
!VMS!       
!VMS!       %routine scroll
!VMS!          page = page + 1
!VMS!          %if page = max lines - 1 %start
!VMS!             pause (No)
!VMS!             page = 1
!VMS!          %finish
!VMS!          %if Y = window bottom - 1 %start
!VMS!             Forward Scroll (Window Top + 1, Window Bottom - 1)
!VMS!          %else
!VMS!             Y = Y + 1
!VMS!          %finish
!VMS!       %end {scroll}

!VMS!       %on 9, 10, 11 %start
!VMS!       zot:
!VMS!          %return %if event_event = 10
!VMS!          %if event_event = 11 %start
!VMS!             clear window
!VMS!             first = yes
!VMS!          %else
!VMS!             cursor (0, window top)
!VMS!             erase line
!VMS!             put string (event_message)
!VMS!             %if first = yes %start
!VMS!                interaction failed = yes
!VMS!                %return 
!VMS!             %finish
!VMS!             %begin
!VMS!                %on 10, 11 %start
!VMS!                   %return
!VMS!                %finish

!VMS!                pause (No)
!VMS!             %end
!VMS!             -> zot
!VMS!          %finish
!VMS!       %finish

!VMS!       {MAIN CODE OF FILES}

!VMS!       read string (file spec, "File spec:")
!VMS!       normalise file name (file spec)
!VMS!       set wild file spec (file spec)
!VMS!       number = 0
!VMS!       %cycle
!VMS!          file = next wild file
!VMS!          %exit %if file = ""
!VMS!          number = number + 1
!VMS!          file -> dir .("]"). file
!VMS!          dir = dir . "]"
!VMS!          file -> file .("."). extension
!VMS!          %if dir # current %start
!VMS!             pause (No) %unless first = yes
!VMS!             first = no
!VMS!             clear window
!VMS!             cursor (0, window top)
!VMS!             Feature
!VMS!             clear line
!VMS!             put string (dir)
!VMS!             Normal 
!VMS!             Y = window top + 1
!VMS!             cursor (5, Y)
!VMS!             current = dir
!VMS!             last = "~~~"                       {no way is that valid!}
!VMS!             page = 0
!VMS!          %finish
!VMS!          %for n = 1, 1, length (extension) %cycle
!VMS!             char no (extension, n) = lower case (char no (extension, n))
!VMS!          %repeat
!VMS!          %if last = file %start
!VMS!             %if X + length (extension) > Terminal Width %start
!VMS!                scroll
!VMS!                X = 9
!VMS!             %finish
!VMS!             cursor (X, Y)
!VMS!             put string ("." . extension)
!VMS!             X = X + 9
!VMS!          %else
!VMS!             fancy = file
!VMS!             %if length (file) > 1 %start
!VMS!                length (fancy) = length (file)
!VMS!                %for n = 2, 1, length (file) %cycle
!VMS!                   char no (fancy, n) = lower case (char no (file, n))
!VMS!                %repeat
!VMS!             %finish
!VMS!             scroll
!VMS!             cursor (0, Y)
!VMS!             put string (fancy)
!VMS!             cursor (9, Y)
!VMS!             put string ("." . extension)
!VMS!             X = 18
!VMS!             last = file
!VMS!          %finish
!VMS!       %repeat
!VMS!       Scroll
!VMS!       Scroll
!VMS!       Cursor (0, Y)
!VMS!       %if number = 0 %then put string ("No") %c
!VMS!                      %else put integer (number)
!VMS!       put string ( " file")
!VMS!       put symbol ('s') %unless number = 1
!VMS!       put symbol (' ')
!VMS!       put string (file spec)
!VMS!       pause (Yes)

{APM}       %string (255) Directory
{APM}       %integer X = 0, 
{APM}                Y = Window Top + 2, 
{APM}                Ch,
{APM}                N,
{APM}                State = 0

!{APM}@16_01110 %integer %function  FCOMMR %c
!{APM}                              (%integer c,
!{APM}                               %string (255) p,
!{APM}                               %byte %name b,
!{APM}                               %integer max)
!
!{APM}       %string (255) %fn Current Directory
!{APM}          %string (255) Me
!{APM}          %integer I = 1
!{APM}
!{APM}          Length (Me) = Fcommr ('F'<<8, "", Charno (Me, 1), 255)
!{APM}          I = I + 1 %while Charno (Me, I) # ' '
!{APM}          Length (Me) = I - 1
!{APM}          %result = Me . ":"
!{APM}       %end

{APM}       %on 3, 9 %start
{APM}          %if State > 0 %start
{APM}             Cursor (0, Window Top)
{APM}             Erase Line
{APM}             Put String (Directory)
{APM}             Feature
{APM}             Cursor (50, Window Bottom)
{APM}             Put String ("Press any key to continue: ")
{APM}             Normal
{APM}             Close Input
{APM}             Select Input (3) %if From Journal = Yes
{APM}             Ch = Get Next Ch
{APM}          %else
{APM}             Cursor (0, Window Top)
{APM}             Erase Line
{APM}             Put String (Event_Message)
{APM}             Interaction Failed = Yes
{APM}          %finish
{APM}          %return
{APM}       %finish

{APM}       Read String (Directory, "Directory: ")
{APM}       %if Directory = "" %start
!{APM}          Directory = Current Directory 
{APM}       %else
{APM}          %if Char No (Directory, Length (Directory)) # ':' %start
{APM}             Directory = Directory . ":" 
{APM}          %finish
{APM}       %finish
{APM}       %if From Journal = Yes %start
{APM}          Open Input (2, Directory)
{APM}          Select Input (2)
{APM}       %else
{APM}          Open Input (3, Directory)
{APM}          Select Input (3)
{APM}       %finish
{APM}       Clear Window
{APM}       State = 1
{APM}       %cycle
{APM}          Cursor (X, Y)
{APM}          Read Symbol (Ch)
{APM}          Put Symbol (Ch)
{APM}          %cycle
{APM}             Read Symbol (Ch)
{APM}             %exit %if Ch = NL
{APM}             Put Symbol ({Lower Case (} Ch {)})
{APM}          %repeat
{APM}          X = X + 14
{APM}          Y = Y + 1 %and X = 0 %if X > Terminal Width
{APM}       %repeat

         %end {files}
   
!VMS!    {DO NOTIFY}
!VMS!
!VMS!    %routine Do Notify
!VMS!       %string (80) Who, Aberistwith, Beatock
!VMS!       %record (Text fm) What
!VMS!       %integer N
!VMS!
!VMS!       %on 9 %start
!VMS!          Clear Line
!VMS!          Cursor (0, Window Bottom)
!VMS!          Put String (Event_Message)
!VMS!          Interaction Failed = Yes
!VMS!          %return
!VMS!       %finish
!VMS!
!VMS!       Read String (Who, "Who: ")
!VMS!       Who = Aberistwith . Beatock %while Who -> Aberistwith .(" "). Beatock
!VMS!       %if Who # "" %start
!VMS!          Get Text (What, 
!VMS!                    Who . ": ",
!VMS!                    No)
!VMS!          %if What_Length > 0 %start
!VMS!             %for N = 1, 1, What_Length %cycle
!VMS!                What_Text (N) = '.' %unless ' ' <= What_Text (N) <= '~'
!VMS!             %repeat
!VMS!          %finish
!VMS!       %finish
!VMS!       %if Who = "" %or
!VMS!           What_Length = 0 %start
!VMS!          Interaction Failed = Yes
!VMS!          Clear Line
!VMS!          %return 
!VMS!       %finish
!VMS!       Notify (Who, What_String . To String (BEL))
!VMS!    %end {Do Notify}

{APM}    {DO QUOTE PASSWORD}
{APM}    
{APM}    %routine Do Quote Password
{APM}       %string (255) Quoted
{APM}       %integer Ch
{APM}      
{APM}       %on 3, 9 %start
{APM}          Fail (Event_Message, Yes)
{APM}          %return
{APM}       %finish
{APM}      
{APM}       Cursor (0, Window Top)
{APM}       Put String ("Pass: ")
{APM}       Quoted = ""
{APM}       %cycle
{APM}          Ch = Get Symbol
{APM}          %if Ch = CR %start
{APM}             %exit 
{APM}          %else %if Ch = DEL
{APM}             %if Quoted # "" %start
{APM}                Length (Quoted) = Length (Quoted) - 1
{APM}             %finish
{APM}          %else %if Ch = CAN
{APM}             Quoted = ""
{APM}          %else %if Ch > ' '
{APM}             Quoted = Quoted . To String (Ch)
{APM}          %else
{APM}             Put Symbol (BEL) %if Beep Mute = No
{APM}          %finish
{APM}       %repeat
{APM}       %if Quoted # "" %start
{APM}          Quote Password (Quoted)
{APM}       %finish
{APM}       Clear Line
{APM}    %end {Do Quote Password}

         {DISPLAY KEYS}

         %routine Display Keys
            %integer Ch

            %routine Display Function
               %integer K,
                        Y = Window Top + 2,
                        X = 0

               Clear Window 
               %for K = Key F0, 1, Key F32 %cycle                        
                  %unless Keys (K)_Type = Undefined %start
                     Cursor (X, Y)
                     Put Key Definition (K) 
                  %finish
                  Y = Y + 1
                  %if Y > Window Bottom %start
                     Y = Window Top + 2
                     X = 39
                  %finish
               %repeat
            %end {Display Function}

            %routine Display Keypad
               %integer K,
                        Y = Window Top + 2

               Clear Window 
               %for K = Keypad 0, 1, Keypad ENTER %cycle
                  Cursor (0, Y)
                  Put Key Definition (K) %unless Keys (K)_Type = Undefined
                  Y = Y + 1
               %repeat
               Y = Window Top + 2
               %for K = Function Keypad 0, 1, Function Keypad ENTER %cycle
                  %unless Keys (K)_Type = Undefined %start
                     Cursor (39, Y)
                     Put Key Definition (K) 
                  %finish
                  Y = Y + 1
               %repeat
            %end {Display Keypad}

            %routine Display Control
               %integer K,
                        Y = Window Top + 2,
                        X = 0

               Clear Window 
               %for K = 0, 1, 31 %cycle
                  %unless Keys (K)_Type = Undefined %start
                     Cursor (X, Y)
                     Put Key Definition (K) 
                  %finish
                  Y = Y + 1
                  %if Y > Window Bottom %start
                     Y = Window Top + 2
                     X = 39
                  %finish
               %repeat
            %end {Display Control}

            %routine Display Oddities
               %integer K,
                        Y = Window Top + 2,
                        X = 0

               Clear Window
               %for K = Panic Key, 1, 255 %cycle
                  %unless Keys (K)_Type = Undefined %start
                     Cursor (X, Y)
                     Put Key Definition (K) 
                  %finish
                  Y = Y + 1
                  %if Y > Window Bottom %start
                     Y = Window Top + 2
                     X = 39
                  %finish
               %repeat
            %end {Display Oddities}

            {MAIN CODE OF DISPLAY KEYS}

            Cursor (0, Window Top)
            Clear Line
            Put String ( %c
          "F)unction keys, K)eypad, C)ontrol keys, O)ddities, RETURN to edit: ")
            Window Top = Window Top + 1
            %cycle
               Cursor (67, Window Top - 1)
               Ch = Upper Case (Get Next Ch)
               %if Ch = 'F' %start
                  Display Function
               %else %if Ch = 'K'
                  Display Keypad
               %else %if Ch = 'C'
                  Display Control
               %else %if Ch = 'O'
                  Display Oddities
               %else %if Ch = CR %or %c
                         Ch = Keypad ENTER
                  %exit
               %else
                  Put Symbol (BEL) %unless Beep Mute = Yes
               %finish
            %repeat
            Window Top = Window Top - 1
         %end {Display Keys}

         {CLEAR HEIGHT}

         %routine Clear Height
            Cursor (0, Window Top + 1)
            Clear Line
            Normal Size
            Cursor (0, Window Top + 2)
            Clear Line
            Normal Size
         %end {Clear Height}

         {MAIN CODE OF INTERACT}
   
         at rest = no
         keypad off
         clear window

         %if Terminal Characteristics&Double Height Ability # 0 %start
            Blink On %if Terminal Characteristics&Blink Text Ability # 0
            Cursor (30, Window Top + 1)
            Put String ("IE")
            Upper Half Double Height
            Cursor (30, Window Top + 2)
            Put String ("IE")
            Lower Half Double Height
            Normal %if Terminal Characteristics&Blink Text Ability # 0
            Cursor (60, Window Top + 3)
            Put String (IE Version)
{APM}       Cursor (60, Window Top + 5)
         %else
            cursor (60, window top + 1)
            put string ("IE Version " . IE Version)
{APM}       Cursor (60, Window Top + 2)
         %finish
{APM}    Put Integer (Free Store>>10)
{APM}    Put String ("K Free Store")

         cursor (0, window top + 3)
         put string ("Select function from: ")
         cursor (10, window top + 5)
         put string ("P)rofile operations")
         Cursor (10, Window Top + 6)
         Put String ("K)eyboard operations")
         cursor (10, window top + 7)
         put string ("S)et Modes")
{\UNIX}  cursor (10, window top + 8)
{\UNIX}  put string ("F)iles")

{APM}    Cursor (10, Window Top + 9)
{APM}    Put String ("Q)uote Password")

!VMS!    Cursor (10, Window Top + 9)
!VMS!    Put String ("N)otify")

         Cursor (10, Window Top + 10)
         Put String ("D)isplay Keys")
         cursor (10, window top + 11)
         put string ("RETURN to edit")
         cursor (15, window top + 13)
         put string ("Which: ")
         %cycle
            %cycle
               interaction failed = no
               cursor (22, window top + 13)
               ch = upper case (Get Next Ch)
               %if ch = 'S' %start
                  %if Terminal Characteristics&Double Height Ability # 0 %start
                     Clear Height 
                  %finish
                  set modes
                  %exit
               %else %if ch = CR %or ch = EM
                  %exit
               %else %if ch = 'P'
                  Profile Operations
               %else %if Ch = 'K'
                  Keyboard Operations
               %else %if Ch = 'D'
                  %if Terminal Characteristics&Double Height Ability # 0 %start
                     Clear Height
                  %finish
                  Display Keys 
                  %exit
{\UNIX}        %else %if ch = 'F'
{\UNIX}           %if Terminal Characteristics&Double Height Ability # 0 %start
{\UNIX}              Clear Height
{\UNIX}           %finish
{\UNIX}           files
{\UNIX}           %exit
{APM}          %else %if Ch = 'Q'
{APM}             Do Quote Password
{APM}             Cursor (22, Window Top + 12)
{APM}             %exit
!VMS!          %else %if Ch = 'N'
!VMS!             Do Notify
!VMS!             %exit
               %else
                  put symbol (BEL) %if Beep Mute = No
               %finish
            %repeat
         %repeat %until interaction failed = no
         %if Terminal Characteristics&Double Height Ability # 0 %start
            Clear Height
         %finish
         Clear Window
         redraw screen
         at rest = yes
      %end {of interact}

      {READ TEXT}

      %routine Read Text (%record (Text fm) %name T,
                          %string (63) Prompt)
         %integer ch, State, I, Loop
         %string (12) K
         %label restart

         %on 9 %start
            Fail ("Rerun complete", No)
            Close Input
            From Journal = No
            -> Read On 1 %if State = 0
            -> Read On 2
         %finish  

      restart:
         T_length = 0
         Cursor (0, Window Top)
         Clear Line
         put string (Prompt)
         Top X = Length (Prompt)
         %cycle
            %if From Journal = Yes %start
               State = 0
               Read Symbol (Ch)
            %else
            Read On 1:
               ch = Get Key
               Put Journal Symbol (Ch)
            %finish
            %if ch = CAN %start
               -> restart
            %else %if ch = CR
               %exit
            %else %if ch = DEL %or ch = BS   
               %if T_length > 0 %start
                  K = Key Name (T_Text (T_Length))
                  Loop = Length (K)
                  %for I = 1, 1, Loop %cycle
                     put symbol (BS)
                     put symbol (' ')
                     put symbol (BS)
                  %repeat
                  Top X = Top X - 1
                  T_length = T_length - 1
               %finish
            %else %if (ch = DLE %or %c
                       ch >= ' ') %and %c
                      T_length < max text length
               %if ch = DLE %start
                  %if From Journal = yes %start
                     State = 1
                     Read Symbol (Ch)
                  %else
                  Read On 2:
                     ch = Get Key
                     Put Journal Symbol (Ch)
                  %finish
               %finish
               Top X = Top X + 1
               T_length = T_length + 1
               T_text (T_length) = ch
    
               %if ' ' <= Ch <= '~' %start
                  Put Symbol (Ch)
               %else
                  Feature
                  Put String (Key Name (Ch))
                  Normal
               %finish
    
            %else
               put symbol (BEL) %if Beep Mute = No
            %finish
         %repeat
         put symbol (CR)
         Top X = -1
      %end {Read Text}

      {COMMAND NAME}

      %string (17) %function Command Name (%integer Magic)
         %constant %string (17) %array Names (0 : Max Command) = %c
            "Insert Line",
            "Insert Mode",
            "Search",
            "Delete Line",
            "Erase Char",
            "Change Text",
            "End Of Line",
            "Start Of Line",
            "End Of File",
            "End Of Page",
            "Close Edit",
            "Scroll Page Down",
            "Interact",
            "Define Key",
            "Panic!",
            "Left",
            "Right",
            "Up",
            "Down",
            "Start Of File",
            "Move Line",
            "Restore All",
            "Overwrite Mode",
            "Search Again",
            "Restore Line",
            "Change Again",
            "Abort Edit",
            "Erase To End",
            "Delete to End",
            "Scroll Page Up",
            "Start Of Page",
            "Word Left",
            "Word Right",
            "Up To Start",
            "Down To Start",
            "Lower Case",
            "Upper Case",
            "Flip Case",
            "Swap About",
            "Set Marker",
            "Jump To Marker",
            "Select Buffer",
            "Get Buffer",
            "Put Buffer",
            "Include Buffer",
            "Set Copy Point",
            "Clone Line",
            "Back TAB",
            "Erase Left",
            "Move To Column",
            "Match Bracket",
            "Clear Broadcast",
            "Toggle Mode",
            "Insert TAB",
            "Move To Line",
            "Last Position",
            "Break Line",
            "Repeat Command",
            "Insert Literal",
            "Redraw",
            "Erase Word",
            "Erase To Start",
            "Undo Erasure",
            "Backwards Search",
            "Debugger",
            "Duplicate Line",
            "Justify Line",
            "Delete To Start",
            "No Op",
            "Shell Escape",
            "Shell Buffer",
            "Insert ASCII"

         %result = Names (Magic)
      %end {Command Name}

      {GET COMMAND NUMBER}

      %integer %function Get Command Number
         %constant %integer Sections = 12

         %constant %integer Slots = Max Command + Sections

         %own %string (17) %array Section Names (1 : Sections) = %c
            "Insertion",
            "Movement", 
            "Searching",
            "Deletion", 
            "Scrolling",
            "Admin",    
            "Typing Mode",
            "Recovery",   
            "Duplication",
            "Character Ops",
            "Markers",
            "Buffers"

         %constant %byte Section = 128

         %constant %byte %array Magic (0:slots) = %c
            Section ! 1,
            0,
            58,
            71,
            53,
            56,
            Section ! 2,
            15,
            16,
            17,
            18,
            6,
            9,
            8,
            7,
            30,
            19,
            31,
            32,
            33,
            34,
            54,
            49,
            Section ! 3,
            2,
            63,
            23,
            5,
            25,
            50,
            Section ! 4,
            3,
            28,
            67,
            4,
            47,
            48,
            60,
            61,
            27,
            Section ! 5,
            29,
            11,
            Section ! 6,
            10,
            26,
            12,
            14,
            59,
            51,
            13,
            57,
            64,
            68,
            69,
            70,
            Section ! 7,
            1,
            22,
            52,
            Section ! 8,
            24,
            21,
            62,
            55,
            Section ! 9,
            45,
            20,
            65,
            46,
            Section ! 10,
            35,
            36,
            37,
            38,
            66,
            Section ! 11,
            39,
            40,
            Section ! 12,
            41,
            42,
            43,
            44

         %integer I,
                  L,
                  N,
                  X, Y,
                  Ch
         %byte %array Display (0:3, 3:23)
         %constant %byte Avoid = 255

         %if Max Lines < 24 %start
            Fail ("Screen Window Too Small", Yes)
            %result = 255
         %finish
         Clear Window
         Cursor (0, Window Top)
         Put String ("Use cursor keys to point at desired command")
         Cursor (0, Window Top + 1)
         Put String ("Press RETURN to select")
         %for X = 0, 1, 3 %cycle
            %for Y = 3, 1, 23 %cycle
               Display (X, Y) = Avoid
            %repeat
         %repeat
         X = 0
         Y = 3
         Dim On %if Terminal Characteristics&Dim Text Ability # 0
         Cursor Off %if Terminal Characteristics&Supress Cursor Ability # 0
         %for N = 0, 1, Slots %cycle
            Cursor (X*20 + 1, Y)
            %if N = 1 %start
               %if Terminal Characteristics&Dim Text Ability # 0 %start
                  Normal
               %else
                  Feature
               %finish
            %finish
            %if Magic (N)&Section # 0 %start
               %if Y = 23 %start
                  {Don't put a header at the foot of the display}
                  X = X + 1
                  Y = 3
                  Cursor (X*20 + 1, Y)
               %finish
               Normal %if Terminal Characteristics&Dim Text Ability # 0
               Left
               Graph On
               Top Left
               Graph Off
               Put String (Section Names (Magic (N)&127))
               Graph On
               L = 20 - Length (Section Names (Magic (N)&127)) - 2
               %if L > 0 %start
                  Horizontal %for I = 1, 1, L
               %finish
               Top Right
               Graph Off
               Feature %if Terminal Characteristics&Dim Text Ability # 0
            %else
               Put String (Command Name (Magic (N)))
               %if Magic (N) = 64 %and %c
                   Debug = No %start
                  Display (X, Y) = Avoid
               %else
                  Display (X, Y) = N
               %finish
            %finish
            Y = Y + 1
            %if Y > 23 %start
               X = X + 1
               Y = 3
            %finish
            %if N = 1 %start
               %if Terminal Characteristics&Dim Text Ability # 0 %start
                  Dim On
               %else
                  Normal
               %finish
            %finish
         %repeat
         Normal %if Terminal Characteristics&Dim Text Ability # 0

         X = 0
         Y = 4
         Cursor (X*20 + 1, Y)
         %cycle
            Ch = Get Next Ch
            N = Display (X, Y)
            %if Ch = CR %or %c
                Ch = Keypad Enter %start
               %exit
            %else %if Ch = EM
               N = -1
               %exit
            %else %if Ch = Up Key
               %cycle
                  Y = Y - 1
                  Y = 23 %if Y < 3
               %repeat %until Display (X, Y) # Avoid
            %else %if Ch = Down Key
               %cycle
                  Y = Y + 1
                  Y = 3 %if Y > 23
               %repeat %until Display (X, Y) # Avoid
            %else %if Ch = Left Key
               X = X - 1
               X = 3 %if X < 0
               %if Display (X, Y) = Avoid %start
                  {Move to a non avoid}
               Juggle:
                  %if Y = 23 %start
                     %cycle
                        Y = Y - 1 
                        Y = 23 %if Y < 3
                     %repeat %until Display (X, Y) # Avoid
                  %else
                     %cycle
                        Y = Y + 1 
                        Y = 3 %if Y > 23
                     %repeat %until Display (X, Y) # Avoid
                  %finish
               %finish
            %else %if Ch = Right Key
               X = X + 1
               X = 0 %if X > 3
               -> Juggle %if Display (X, Y) = Avoid
            %else
               Put Symbol (Bel) %unless Beep Mute = Yes
               %continue
            %finish
            {Now move to a new place}
            Dim On %if Terminal Characteristics&Dim Text Ability # 0
            Put String (Command Name (Magic (N)))
            Cursor (X*20 + 1,Y)
            Normal %if Terminal Characteristics&Dim Text Ability # 0
            Reverse On %if Terminal Characteristics&Reverse Text Ability # 0
            Put String (Command Name (Magic (Display (X, Y))))
            Normal %if Terminal Characteristics&Reverse Text Ability # 0
            Cursor (X*20 + 1,Y)
         %repeat
         Cursor On %if Terminal Characteristics&Supress Cursor Ability # 0
         Redraw Screen
         Set Cursor
         %result = 255 %if N < 0
         %result = Magic (Display (X, Y))
      %end {Get Command Number}
      
      {PUT KEY DEFINITION}

      %routine Put Key Definition (%integer Key)
         %integer N
         %record (Key Definition FM) %name Key Def == Keys (Key)
               
         Feature
         Put String (Key Name (Key))
         Normal 
         Put String (" is ")
         %if Key Def_Type = Letter %start
            Put String ("letter = '")
            Put Ch (Key Def_Value)
            Put Symbol ('''')
         %else %if Key Def_Type = Command
            Put String (Command Name (Key Def_Value))
         %else %if Key Def_Type = Macro
            Put Symbol ('"')
            %for N = 1, 1, Key Def_Text_Length %cycle
               %if ' ' <= Key Def_Text_Text (N) <= '~' %start
                  Put Symbol (Key Def_Text_Text (N))
               %else
                  Feature
                  Put String (Key Name (Key Def_Text_Text (N)))
                  Normal
               %finish
            %repeat
            Put Symbol ('"')
         %else
            Put String ("undefined")
         %finish
      %end {Put Key Definition}

      {DEFINE KEY}
      
      %routine Define Key        
         %integer key,
                  Bell = Yes,
                  Ch,
                  Number,
                  Junk,
                  N
         %record (Key Definition FM) %name Key Def

         %on 9 %start
            Fail ("Rerun Complete", No)
            Close Input
            From Journal = No
            %return
         %finish

         %if Current Definition ## Nil %start
            Current Definition_Text_Length = Definition Pos - 2     {ignore the}
            Current Definition == Nil                             {Keypad Enter}
            Fail ("Keyboard definition terminated", No)
            %return
         %finish
         at rest = no
         cursor (0, window top)
         erase line
         put string ("Press key to be defined: ")
         %if From Journal = No %start
            Key = Get Key
         %else
            Read Symbol (Key)
         %finish
         Put Journal Symbol (Key)

         Cursor (0, Window Top)
         Clear Line
         Put String (" Define ") 
         Feature
         Put String (Key Name (Key)) 
         Normal
         Put String (" as a L)etter, C)ommand, K) & M)acro, Q)uery: ")
         Ch = Upper Case (Get Next Ch)
         
         Key Def == Keys (Key)
         %if Ch = 'L' %start
            Clear Line
            Put String ("Press letter: ")
            Ch = Get Next Ch
            %if Ch >= Extra Keys %start
               Fail ("Invalid letter - must be normal ASCII", Yes)
               %return
            %finish
            Key Def_Type = Letter
            Key Def_Value = Ch
         %else %if Ch = 'C'
            Key Def_Type = Command
            Key Def_Value = Get Command Number
            Key Def_Type = Undefined %if Key Def_Value = 255
            %return
         %else %if Ch = 'M' %or -
                   Ch = 'K'
            %if Key Def_Text == Nil %start
               Key Def_Text == New (Size of Text fm)
            %finish
            Key Def_Type = Macro
            Key Def_Stack == Nil
            %if Ch = 'M' %start
               Read Text (Key Def_Text,
                          Key Name (Key) . ":")
               Key Def_Type = Undefined %if Key Def_Text_Length = 0
            %else
               Current Definition == Key Def
               Definition Pos = 1
               Fail ("Keyboard Macro Entry - Terminate with Keypad ENTER", No)
            %finish
         %else %if Ch = 'U'
            Key Def_Type = Undefined
         %else %if Ch = 'Q'
            Clear Line
            Put Key Definition (Key)
            %if CY = Window Top %start
               Put Symbol (CR)
            %else
               Set Cursor
            %finish
            Failed = Yes               {needs top line replaced}
         %finish
         Replace Top Line %unless Ch = 'Q' %or Ch = 'K'
         at rest = yes
      %end {of Define Key}

      {REPEAT COMMAND}

      %routine Repeat Command
         %integer Sign,        {ignored}
                  Count

         At Rest = No
         Read Text (Repeat Text,
                    "Repeat: ")
         %if Repeat Text_Length > 0 %start
            Read Integer (Sign,
                          Count,
                          "Times: ",
                          No, No, Yes)
            %if Sign = Infinite %start
               Repeat Count = -1 
            %else
               Repeat Count = Count
            %finish
            Repeat Place = 1
         %finish
         Replace Top Line
         At Rest = Yes
      %end {Repeat Command}

      {CHANGE TEXT}

      %routine change text
         %if search before replace = yes %start
            get text (replace search, "Change: ", \case significance)
            %return %if replace search_length = 0
            get text (replace string, "To: ", no)
            search (replace search, yes, Forwards)
            replace (replace search)
         %else
            get text (replace string, "Change To: ", no)
            cursor (0, window top)                      {redraw top line}
            print line (TOS, 1, window top)
            set cursor
            replace (search string)
         %finish
      %end {of change text}

      {MOVE LEFT}

      %routine move left
         %integer old X

         desired X = -1
         Close Parcel
         %return %if pos = 1
         pos = pos - 1
         old X = CX
         CX = find X (current, pos)
         CX = Terminal Width - 1 %if CX >= Terminal Width
         %if old X - 1 = CX %start
            left
         %else %if old X # CX
            set cursor
         %finish
      %end {of move left}

      {MOVE WORD LEFT}
      
      %routine move word left
         desired X = -1
         Close Parcel
         find word left (pos)
         reset cursor
      %end {of move word left}

      {MOVE RIGHT}

      %routine move right
         %integer old X

         desired X = -1
         Close Parcel
         %return %if pos > current_length %and %c
                     Free Cursor = No
         pos = pos + 1
         old X = CX
         CX = find X (current, pos)
         CX = Terminal Width - 1 %if CX >= Terminal Width
         %if old X + 1 = CX %start
            right
         %else %if old X # CX
            set cursor
         %finish
      %end {of move right}

      {MOVE WORD RIGHT}
      
      %routine move word right
         desired X = -1
         Close  Parcel
         find word right (pos)
         reset cursor
      %end {of move word right}
      
      {SORT WIGGLE}

      %routine Sort Wiggle
         %if (Wiggle = Yes %or %c
              TAB Wiggle = Yes) %and %c
             Desired X >= 0 %start
            CX = Desired X 
            TAB Wiggle = No
         %finish
      %end {Sort Wiggle}

      {MOVE UP}

      %routine move up
         %unless current_last == nil %start
            Close Parcel
            current == current_last
            Sort Wiggle
            restrict cursor
            set screen (-1)
         %finish
      %end {of move up}

      {MOVE UP TO START OF LINE}

      %routine move up to start of line
         %unless current_last == nil %start
            desired X = -1
            Close  Parcel
            Set Magic Marker
            current == current_last
            pos = 1
            CX = 0
            set screen (-1)
         %finish
      %end {of move up to start of line}

      {MOVE DOWN}

      %routine move down
         %unless current_next == nil %start
            current == current_next
            Sort Wiggle
            restrict cursor
            set screen (1)
         %finish
      %end {of move down}

      {MOVE DOWN TO START OF LINE}

      %routine move down to start of line
         %unless current_next == nil %start
            desired X = -1
            Close Parcel
            Set Magic Marker
            current == current_next
            pos = 1
            CX = 0
            set screen (1)
         %finish
      %end {of move down to start of line}

      {MOVE TO START OF FILE}

      %routine move to start of file
         Set Magic Marker
         current == current buffer_first
         pos = 1
         Desired X = -1
         CX = find X (current, pos)
         set screen (0)
      %end {of move to start of file}

      {ERASE TO END OF LINE}

      %routine erase to end of line
         %return %if current_bits & pseudo line # 0 %or %c
                     pos > current_length
         current buffer_changed = yes
         dump parcel (pos, current_length - pos + 1)
         current_length = pos - 1
         %if pos < Terminal Width %start
            %if Current_Bits & Contains CCs = 0 %start
               erase line
            %else
               Clear Line
               Print Line (Current, 1, CY)
            %finish
         %finish
         check for CCs (current)
      %end {of erase to end of line}

      %constant %integer To Upper Case = 1,
                         To Lower Case = 2,
                         To Flip  Case = 3

      {CHANGE CASE}
      
      %routine change case (%integer Type)
         %switch Do Op (To Upper Case:To Flip Case)

         %return %if pos > current_length
         current buffer_changed = yes
         -> Do Op (Type)

      Do Op (To Upper Case):
         current_text (pos) = Upper Case (current_text (pos))
         -> Cont

      Do Op (To Lower Case):
         current_text (pos) = Lower Case (current_text (pos))
         -> Cont

      Do Op (To Flip Case):
         current_text (pos) = Flip Case (current_text (pos))
      Cont:
         %if current_bits & contains CCs = 0 %start
            put symbol (current_text (pos)) {also moves right}
            pos = pos + 1
            CX = CX + 1                 {so record the fact}
         %else
            print line (current, pos, CY)
            pos = pos + 1
            reset cursor
         %finish
         desired X = -1
         Close  Parcel
      %end {of change case}
      
      {CHANGE TO UPPER CASE}
      
      %routine change to upper case
         %integer Rubbish

         change case (to upper case)
      %end {of change to upper case}
   
      {CHANGE TO LOWER CASE}
   
      %routine change to lower case
         change case (to lower case)
      %end {of change to lower case}
      
      {CHANGE TO FLIPPED CASE}
      
      %routine change to flipped case
         change case (to flip case) 
      %end {of change to flipped case}

      {SWAP ABOUT}
      
      %routine swap about
         %integer T
         
         %return %if pos < 3 %or pos > current_length + 1
         desired X = -1
         Close Parcel
         current buffer_changed = yes
         T = current_text (pos - 1)
         current_text (pos - 1) = current_text (pos - 2)
         current_text (pos - 2) = T
         %if current_bits & contains CCs = 0 %start
            %if pos < Terminal Width + 1 %start
               cursor (CX - 2, CY)
               put ch (current_text (pos - 2))
            %else %if pos < Terminal Width + 2
               cursor (CX - 1, CY)
               put ch (current_text (pos - 2))
            %finish
            %if pos < Terminal Width + 1 %start
               cursor (CX - 1, CY)
               put ch (current_text (pos - 1))
            %finish
         %else
            cursor (find X (current, pos - 2), CY)
            print line (current, pos - 2, CY)
            reset cursor
         %finish
      %end {of swap about}
      
      {ADD MARKER}
      
      %routine add marker (%string (31) name)
         %record (marker fm) %name M

         M == current buffer_markers
         %while M ## nil %cycle         {check if there already is one}
            %exit %if M_name = name
            M == M_next
         %repeat
         %if M == nil %start
            M == new (Size Of Marker fm)
            M_name =  name
            M_next == current buffer_markers
            current buffer_markers == M
         %finish
         M_line == current
         M_pos  =  pos
         current_bits = current_bits ! marked
      %end {add marker}

      {SET MARKER}
      
      %routine set marker
         %string (31) name
         %record (marker fm) %name M
         
         read string (name, "Set Marker: ")
         Replace Top Line
         %if name = "CP" %start
            put symbol (BEL) %if Beep Mute = No
            Replace Top Line
         %else
            add marker (name) %if name # ""
         %finish
      %end {of set marker}
      
      {JUMP TO MARKER}
      
      %routine jump to marker
         %record (marker fm) %name M
         %string (31) Name
         %integer L = window top + 2
         %record (line fm) %name Copy,
                                 Line
         %integer P,
                  Amount,
                  N,
                  Max = 0,
                  Ch

         {MARKER}

         %record (Marker fm) %map Marker (%integer N)
            %record (Marker fm) %name M

            %result == Nil %if N = 0
            M == Current Buffer_Markers
            %cycle   
               N = N - 1
               %exit %if N = 0
               M == M_Next
            %repeat
            %result == M
         %end {Marker}

         {DISPLAY LINE}

         %routine Display Line (%record (Line fm) %name Line)
            %integer P

            %if Line_Bits&Pseudo Line # 0 %start
               Cursor (29, L)
               Graph On
               Put String (EOF Marker)
               Graph Off
            %else
               P = 1
               P = P + 1 %while P <= Line_length %and %c
                                Line_text (P) = ' '
               Amount = Line_Length - P + 1
               Amount = 50 %if Amount > 50
               {Copy the text into a new record}
               Bulk Move (Amount,
                          Line_Text (P),
                          Copy_Text (30))
               Copy_Text (P) = ' ' %for P = 1, 1, 29
               Copy_Length = 29 + Amount
               Copy_Bits = Untouched
               Cursor (0, L)
               print line (copy, 1, L)
            %finish
            Max = Max + 1
         %end {Display Line}

         {MAIN CODE OF JUMP TO MARKER}

         M == current buffer_markers
         %if M == nil %start
            fail ("No markers set", yes)
            %return
         %finish
         Copy == New Line fm (80)
         clear window
         Display Line (Current)
         Put Symbol (CR)
         Reverse On %if Terminal Characteristics&Reverse Text Ability # 0
         Put String ("Current Line")
         Normal %if Terminal Characteristics&Reverse Text Ability # 0
         L = L + 1
         %cycle
            Line == M_Line
            Display Line (Line)
            Put Symbol (CR)
            Dim On %if Terminal Characteristics&Dim Text Ability # 0
            Put String (M_Name)
            Normal %if Terminal Characteristics&Dim Text Ability # 0
            L = L + 1
            %if L = window bottom %start
               cursor (0, L)
               put string ("And some more that there isn't room to show")
               %exit
            %finish
            M == M_next
         %repeat %until M == nil

         Max = Max - 1
         N = 0
         L = Window Top + 2
         At Rest = No

      Complete Restart:
         Cursor Off %if Terminal Characteristics&Supress Cursor Ability # 0
         Cursor (0, Window Top)
         Put String ( %c
            "Use cursor keys to select marker, then press RETURN, or type name")

         %cycle
            Cursor (0, L)
            Ch = Get Next Ch
            %if Ch = Up Key %or %c
                Ch = Down Key %start
               {Move up or down}
               Dim On %if Terminal Characteristics&Dim Text Ability # 0
               %if N = 0 %start
                  Put String ("Current Line")
               %else
                  Put String (Marker (N)_Name)
               %finish
               Normal %if Terminal Characteristics&Dim Text Ability # 0

               %if Ch = Up Key %start
                  %if N = 0 %start
                     N = Max
                  %else
                     N = N - 1
                  %finish
               %else
                  %if N = Max %start
                     N = 0
                  %else
                     N = N + 1
                  %finish
               %finish

               L = Window Top + 2 + N
               Cursor (0, L)
               Reverse On %if Terminal Characteristics&Reverse Text Ability # 0
               %if N = 0 %start
                  Put String ("Current Line")
               %else
                  Put String (Marker (N)_Name)
               %finish
               Normal %if Terminal Characteristics&Reverse Text Ability # 0
            %else %if Ch = CR
               {Select this one}
               %exit %if N = 0
               M == Marker (N)
               -> Gotcha
            %else %if ' ' < Ch <= '~'
               Name = to string (upper case (ch))
               keypad off
               cursor (0, window top)
               erase line
               put string ("Marker: " . to string (ch))
               Cursor On %if Terminal Characteristics&Supress Cursor Ability#0
               %cycle
                  ch = get next ch
                  %if ch = CAN %start
                     -> complete restart
                  %else %if ch = CR
                     %exit
                  %else %if ch = DEL %or ch = BS
                     %if length (Name) > 0 %start
                        length (Name) = length (Name) - 1
                        put symbol (BS)
                        put symbol (' ')
                        put symbol (BS)
                     %else
                        -> complete restart
                     %finish
                  %else %if ch >= ' '
                     Name <- Name . to string (upper case (ch))
                     put symbol (ch)
                  %else
                     put symbol (BEL) %if Beep Mute = No
                  %finish
               %repeat
               -> complete restart %if Name = ""
               {Now find marker by name}
               M == current buffer_markers
               %while M ## nil %cycle
                  %if M_name = name %start
                  Gotcha:
                     Set Magic Marker
                     clear display bits 
                     desired X = -1
                     Close Parcel
                     current == M_line
                     pos     =  M_pos
                     pos     =  current_length + 1 %if pos > current_length
                     CX      =  find X (current, pos)
                     TOS == nil                       {tell set screen not to optimise}
                     set screen (0)
                     Cursor On %if Terminal Characteristics&Supress Cursor Ability # 0
                     At Rest = Yes
                     %return
                  %finish
                  M == M_next
               %repeat
               cursor (0, window top + 1)
               put string ("No marker " . name)
               -> Complete Restart
            %finish
         %repeat

         Cursor On %if Terminal Characteristics&Supress Cursor Ability # 0
         redraw screen                    {if not wanted or not found}
         At Rest = Yes
      %end {of jump to marker}

      {RESTORE LINE}

      %routine restore line
         %record (line fm) %name L

         L == deleted
         %if L == nil %start
            fail ("Deletion buffer empty", yes)
         %else
            deleted == deleted_last
            pos = L_length + 1 %if pos > L_length                  
            add line (L)
         %finish
      %end {of restore line}

      {CLONE LINE}

      %routine clone line
         %record (line fm) %name line

         %on 2, 10 %start
            Fail ("Insufficient Memory", Yes)
            %return
         %finish

         %if deleted == nil %start
            fail ("Deletion buffer empty", yes)
         %else
            line == New Line FM (Deleted_Length)
            Duplicate Line (Deleted, Line)
            pos = line_length + 1 %if pos > line_length               
            add line (line)
         %finish
      %end {of clone line}

      {NOMINATE BUFFER}

      %routine Nominate Buffer (%record (Buffer fm) %name B)
         {save current buffer position}
         current buffer_current == current
         current buffer_pos     =  pos
         clear display bits
         {set up new one}
         current buffer == B
         pos = current buffer_pos
         Desired X = -1
         Close Parcel
         current == B_current
         CX = find X (current, pos)
         TOS == nil                       {don't know}
         BOS == nil
         set screen (0)
      %end {Nominate Buffer}

      {CHOOSE BUFFER}

      %record (Buffer fm) %map Choose Buffer

         %record (buffer fm) %name B == buffers
         %integer n, max, ch, cur, Save
         %string (255) S

         {BUFFER N}

         %record (buffer fm) %map buffer N (%integer n)
            %record (buffer fm) %name B == buffers

            %while n > 1 %cycle
               n = n - 1
               B == B_next
            %repeat
            %result == B
         %end {buffer N}

         {MAIN CODE OF CHOOSE BUFFER}

         at rest = no
         clear window
         cur = 0
         n = 0
         %cycle
            n = n + 1
            cur = n %if current buffer == B
            cursor (10, window top + 2 + n)
            %if Terminal Characteristics&Reverse Text Ability # 0 %start
               Reverse On %if N = Cur
            %else %if Terminal Characteristics&Dim Text Ability # 0
               Dim On %unless N = Cur
            %finish
            put string (B_name)
            Normal
            cursor (35, window top + 2 + n)
            %if B_source file = "" %start
               Feature
               put string ("Created")
               Normal 
            %else
               put string (B_source file)
            %finish
            B == B_next
         %repeat %until B == nil
         max = n
         n = cur
      complete restart:
         B == buffer N (n)
         Cursor Off %if Terminal Characteristics&Supress Cursor Ability # 0
         cursor (0, window top)
         put string ( %c
     "Use Cursor keys to select buffer, then press RETURN, or type buffer name")
         cursor (10, window top + 2 + n)
         %cycle
            ch = get next ch
            %if ch = Up Key %or %c
                Ch = Down Key %start
               %if max > 1 %start
                  %if ch = Up Key %start
                     %if n = 1 %start
                        n = max
                     %else
                        n = n - 1
                     %finish
                  %else %if ch = Down Key
                     %if n = max %start
                        n = 1
                     %else
                        n = n + 1
                     %finish
                  %finish
                  {now change display}
                  Dim On %if Terminal Characteristics&Dim Text Ability # 0
                  put string (B_name)
                  Normal %if Terminal Characteristics&Dim Text Ability # 0
                  cursor (10, window top + 2 + n)
                  B == buffer N (n)
                  Reverse On %if Terminal Characteristics& %c
                                 Reverse Text Ability # 0
                  put string (B_name)
                  Normal %if Terminal Characteristics& %c
                             Reverse Text Ability # 0
                  cursor (10, window top + 2 + n)
               %finish
            %else %if ch = CR
               %exit
            %else %if ' ' < ch < DEL
               S = to string (upper case (ch))
               keypad off
               cursor (0, window top)
               erase line
               put string ("Buffer name: " . to string (ch))
               Cursor On %if Terminal Characteristics&Supress Cursor Ability#0
               %cycle
                  ch = get next ch
                  %if ch = CAN %start
                     -> complete restart
                  %else %if ch = CR
                     %exit
                  %else %if ch = DEL %or ch = BS
                     %if length (S) > 0 %start
                        length (S) = length (S) - 1
                        put symbol (BS)
                        put symbol (' ')
                        put symbol (BS)
                     %else
                        -> complete restart
                     %finish
                  %else %if ch >= ' ' %and -
                            Length (S) < 255
                     S = S . to string (upper case (ch))
                     put symbol (ch)
                  %else
                     put symbol (BEL) %if Beep Mute = No
                  %finish
               %repeat
               -> complete restart %if S = ""
               {here if we have had one typed}
               keypad on
               B == find buffer (S)
               %exit %unless B == nil
               cursor (0, window top + 1)
               put string ("No such buffer as " . S)
               -> complete restart
            %else %if Ch = EM
               Save = Forbid Exit
               Forbid Exit = No
               Cursor On %if Terminal Characteristics&Supress Cursor Ability#0
               Abort Edit
               Forbid Exit = Save
               -> Complete Restart
            %else
               put symbol (BEL) %if Beep Mute = No
            %finish
         %repeat
         Cursor On %if Terminal Characteristics&Supress Cursor Ability # 0
         clear window
         %result == B
      %end {Choose Buffer}

      {SELECT BUFFER}

      %routine select buffer
         %if Buffers_next == nil %start
            fail ("Only one buffer - " . Current Buffer_Source File, yes)
            %return
         %finish
         Nominate Buffer (Choose Buffer)
         at rest = yes
      %end {select buffer}

      {GET BUFFER}

      %routine get buffer
         %integer Created = No,
                  Cleared = No
         %record (buffer fm) %name B == Nil,
                                   NB 
         %string (255) file name
         %string (255) buffer name
         %string (255) Compiler Bug
         %string (255) What You Get
         
         %on 3, IO error, 10 %start
            %if Event_Event = IO Error %or %c
                Event_Event = 3 %start
               NB == Buffers
               Buffers == Buffers_Next
               Dispose (NB)
               B == Nil
               options = options & (\silent)
               Compiler Bug = file name . " " . event_message
               report (Compiler Bug)
               -> get name
            %finish
            Dispose (B)
            Redraw Screen
            Fail ("Insufficient Memory", Yes)
            %return
         %finish
         
         read string (buffer name, "Buffer name: ")
         Replace Top Line %and %return %if buffer name = ""
         B == find buffer (buffer name)  {see if one already exists}
         %unless B == nil %start
            fail ("Buffer " . buffer name . " already exists", yes)
            %return
         %finish
!VMS!    What You Get = Expand File Name (Buffer Name)
{\VMS}   What You Get = Buffer Name
         %if Exists (What You Get) %start
            Read String (File Name, "File Name (" . What You Get . "): ")
            File Name = What You Get %if File Name = ""
         %else
         get name:
            read string (file name, "File name: ")
            %if file name = "" %start
               %if Cleared = Yes %start
                  Redraw Screen
               %else
                  Replace Top Line
               %finish
               %return 
            %finish
         %finish
         Clear Window %unless Cleared = Yes
         Cleared = Yes
         %if file name = Null Device %start
            created = yes
!VMS!    %else
!VMS!       file name = expand file name (file name)
         %finish
         B == New Buffer (Buffer Name) %if B == nil
         %if created = yes %start
            Make Empty Buffer (B)
            report ("Creating buffer " . buffer name)
         %else
            report ("Reading from " . file name . " into buffer " . buffer name)
            load buffer (B, file name)
            Make Empty Buffer (B) %if B_first == nil
         %finish
         B_Current == B_First
         Nominate Buffer (B)
      %end {of get buffer}

      {PUT BUFFER}
      
      %routine put buffer
         %own %string (255) file name = ""
         %string (255) From
         %integer Ch,
                  Zapped Bottom = No
         
         %on 3, IO error %start
            options = options & (\silent)  {kill off any silence}
            Zapped Bottom = Yes %if Screen Lines = Max Lines
            report (event_message)
            file name = ""
            -> get name
         %finish
         
      get name:
         From = Current Buffer_Source File
         %if From = "" %start
            From = "Created"
         %else
            From = "from " . From
         %finish
         read string (file name, 
                      "File name for output (" . From . "):")
         %if file name = "" %start
            %if Zapped Bottom = Yes %start
               Cursor (0, Window Bottom)
               Print Line (BOS, 1, Window Bottom)
            %finish
            Replace Top Line
            %return 
         %finish
         %if Put Buffer Verify = Yes %start
            Cursor (0, Window Top)
            Clear Line
            Put String ("Are you SURE you didn't mean F11? (Y/N)")
            Ch = Upper Case (Get Next Ch)
            %if Ch # 'Y' %start
               Replace Top Line
               %return
            %finish
         %finish
!VMS!    file name = expand file name (file name)
!VMS!    File Name -> File Name .(";")
         report ("Writing buffer " . current buffer_name . " into " . file name)
         unload buffer (current buffer, file name)
         redraw screen
      %end {of put buffer}

      {INCLUDE BUFFER}
      
      %routine include buffer
         %record (buffer fm) %name B
         %record (line fm)   %name copy
         %record (line fm)   %name line
         %record (line fm)   %name L
         
         %on 2, 10 %start
            Fail ("Insuffient Memory", Yes)
            -> Patch Up
         %finish

         %if Buffers_next == nil %start
            fail ("Only one buffer - " . Current Buffer_Source File, yes)
            %return
         %finish
         B == Choose Buffer
         %if B == Current Buffer %start
            Redraw Screen
            %return
         %finish
         desired X = -1
         Close Parcel
         clear display bits
         line == B_first
         L == current_last
         %cycle
            copy == new Line FM (Line_Length)
            Duplicate Line (Line, Copy)
            copy_bits = copy_bits & (\marked)
            %if L == nil %start
               {must be at the top of the file}
               current buffer_first == copy
            %else
               L_next == copy       {link last one to this one}
            %finish
            copy_last == L          {link this one to the last one}
            L    == copy            {move last pointer onto this one}
            line == line_next       {next one to be copied}
         %repeat %until line_bits & pseudo line # 0
      Patch Up:
         L_next == current          {last link}
         current_last == copy
         pos = 1
         CX  = 0
         TOS == nil
         BOS == nil
         set screen (0)
         current buffer_changed = yes
      %end {of include buffer}

      {SET COPY POSITION}
      
      %routine set copy position
         copy position == current
         copy buffer == current buffer
         add marker ("CP")
      %end {set copy position}

      {MOVE LINE}
      
      %routine move line (%integer Remove)
         %integer copy Y
         %record (line fm) %name copy
         %record (line fm) %name t, New

         %on 2, 10 %start
            Fail ("Insufficient Memory", Yes)
            %return
         %finish

         %return %if current_bits & pseudo line # 0
         %if current == copy position %start
            fail ("Current line is Copy Position", yes)
            %return
         %finish
         %if copy position == nil %start
            fail ("Copy position not set", yes)
            %return
         %finish
         copy buffer_changed = yes
         %if current_last == nil %and current_next == nil %start
            {only line in the file - special frig}
            copy == new Line FM (Current_Length)
            Duplicate Line (Current, Copy)
            current_length = 0    {zap current line}
            pos = 1
            cursor (0, CY)
            print line (current, 1, CY)
            CX = 0
            set cursor
         %else
            %if Remove = Yes %start
               remove line
               copy == deleted          {rescue deleted line from the deleted list}
               deleted == deleted_last
            %else
               Copy == New Line FM (Current_Max Length)
               Duplicate Line (Current, Copy)
               {move down a line}
               Current == Current_Next
               Sort Wiggle
               Restrict Cursor
               Set Screen (1)
            %finish
         %finish
         copy_next == copy position
         copy_last == copy position_last
         %if copy position_last == nil %start
            copy buffer_first == copy
         %else
            copy position_last_next == copy 
         %finish
         copy position_last == copy
         %if copy position_bits & displayed # 0 %and %c
             copy position ## TOS %start
            {find Y coordinate of copy position}
            copy Y = window top
            t == TOS
            %while t_next ## copy position %cycle
               copy Y = copy Y + 1
               t == t_next
            %repeat
            %if copy Y > CY %start
               {below current line, so scroll a line of the bottom}
               %if Screen Lines < Max Lines %start
                  Screen Lines = Screen Lines + 1
               %else
                  BOS_bits = BOS_bits & (\displayed)
                  BOS == BOS_last
               %finish

               Reverse Scroll (Copy Y, Window Bottom)

            %else
               TOS_bits = TOS_bits & (\displayed)
               TOS == TOS_next
               copy Y = copy Y - 1

               Forward Scroll (Window Top, Copy Y)

            %finish
                 cursor (0, copy Y)

            print line (copy, 1, copy Y)
            set cursor
         %else
            copy_bits = copy_bits & (\displayed)   {clear display bit}
         %finish
      %end {move line}

      {DELETE TO END OF FILE}

      %routine delete to end of file
         %integer ch
         %record (line fm) %name l == current_last,
                                 EOF == current buffer_last,
                                 junk start == current,
                                 junk finish == EOF_last

         %return %if Current_Bits & Pseudo Line # 0
         at rest = no
         cursor (0, window top)
         Clear line
         put string ( %c
            "K)eep on deletion list, T)hrow away, otherwise no deletion: ")

         ch = upper case (get next ch)
         %if ch = 'T' %or ch = 'K' %start
            Clear Display Bits
            EOF_last == l
            %if L == Nil %start
               Current Buffer_First == EOF
            %else
               l_next == EOF
            %finish
            CX = 0
            pos = 1
            Current == EOF
            TOS == nil
            BOS == Nil
            set screen (0)
            %if ch = 'K' %start
               junk start_last == deleted
               deleted == junk finish
            %finish
            Current Buffer_Changed = Yes
         %else
            Replace Top Line
         %finish
         at rest = yes
      %end {delete to end of file}

      {DELETE TO START OF FILE}

      %routine Delete To Start Of File
         %integer ch
         %record (line fm) %name junk finish == current_Last,
                                 junk start == Current Buffer_First

         %return %if Current_Last == Nil
         at rest = no
         cursor (0, window top)
         Clear line
         put string ( %c
            "K)eep on deletion list, T)hrow away, otherwise no deletion: ")

         ch = upper case (get next ch)
         %if ch = 'T' %or ch = 'K' %start
            Clear Display Bits

            Current Buffer_First == Current
            Current_Last == Nil
            TOS == nil
            BOS == Nil
            set screen (0)
            %if ch = 'K' %start
               junk start_last == deleted
               deleted == junk finish
            %finish
            Current Buffer_Changed = Yes
         %else
            Replace Top Line
         %finish
         at rest = yes
      %end {delete to start of file}

      {RESTORE ALL DELETIONS}

      %routine restore all deletions
         %record (line fm) %name Above == Current_Last,
                                 L == Current,
                                 Added
         %integer Ch

         %if deleted == nil %start
            fail ("Deletion Buffer Empty", yes)
         %else
            %if Options&Confirm # 0 %start
               Cursor (0, Window Top)
Clear Line
               Put String ("Do you REALLY want to restore ALL deletions? (Y/N)")
               Ch = Upper Case (Get Next Ch)
               %unless Ch = 'Y' %start
                  Cursor (0, Window Top)
                  Replace Top Line
                  %return
               %finish
            %finish

            Clear Display Bits
            %cycle
               Added == Deleted
               Deleted == Deleted_Last
               
               Added_Next == L
               Added_Last == Above
               L_Last == Added
               L == Added
            %repeat %until deleted == nil

            %if Above == Nil %start
               Current Buffer_First == L
            %else
               Above_Next == L
            %finish

            TOS == Nil
            BOS == Nil
            Set Screen (0)
            Current Buffer_Changed = Yes
         %finish
      %end {restore all deletions}

      {JOIN LINE}

      %routine join line
         %integer New Size,
                  Med = 0,
                  MMed = 0
         %record (Magic Marker fm) %name MM
         %record (Marker fm) %name M
         %record (line fm) %name L,
                                 New One

         %on 2, 10 %start
            Fail ("Insuffient Memory", Yes)
            %return
         %finish
         
         L == current_last
         %return %if L == nil %or current_bits & pseudo line # 0

         New Size = Current_Length + L_Length + 16
         %if New Size > Max Line Length %start
            Fail ("New line would be too long", Yes)
         %else
            New Size = 80 %if New Size < 80
            New One == New Line FM (New Size)

            %if (Current_Bits&Magic Markered) ! (L_Bits&Magic Markered) # 0 %start
               MM == Current Buffer_Magic Markers
               %while MM ## Nil %cycle
                  %if MM_Line == Current %or %c
                      MM_Line == L %start
                     MM_Line == New One
                     MMed = Magic Markered
                  %finish
                  MM == MM_Next
               %repeat
               Current_Bits = Current_Bits&(\Magic Markered)
               L_Bits       = L_Bits&(\Magic Markered)
            %finish
            %if (Current_Bits&Marked) ! (L_Bits&Marked) # 0 %start
               M == Current Buffer_Markers
               %while M ## Nil %cycle
                  %if M_Line == Current %or %c
                      M_Line == L %start
                     Med = Marked
                     M_Line == New One
                  %finish
                  M == M_Next
               %repeat
               Current_Bits = Current_Bits&(\Marked)
               L_Bits       = L_Bits&(\Marked)
            %finish
            Copy Position == New One %if Copy Position == Current %or %c
                                         Copy Position == L
            Found = No %if Found Line == Current %or %c
                           Found Line == L
            TOS == New One %if TOS == L

            {Form the new line}
            Duplicate Line (L, New One)
               {New One_Last is filled in here}
            New One_Bits = New One_Bits!Med!MMed
            New One_Bits = New One_Bits!Contains CCs %if Current_Bits&Contains CCs # 0
            Pos = New One_Length + 1
            %if current_length > 0 %start
               bulk move (current_length,
                          current_text (1),
                          New One_text (New One_length + 1))
               New One_length = New One_length + current_length
            %finish
            New One_Next == Current_Next

            {New One is now correct, so set up the links}
            {Link to New One from Above}
            %if L_Last == Nil %start
               Current Buffer_First == New One
            %else
               L_Last_Next == New One 
            %finish
            {Now the link from below}
            Current_Next_Last == New One
            {Now get rid of L and Current}
            Dispose Line fm (L)
            Dispose Line fm (Current)
            {New Current Line}
            Current == New One

            {Now Remunge the screen to match the Data Structure}
            %if Current == TOS %and %c
                TOS_Last ## Nil %start
               {put out the new line}
               Cursor (0, CY)
               Print Line (Current, 1, CY)
               {Bring new line on at the top of the screen}
               TOS == TOS_Last
               Cursor (0, Window Top)
               Print Line (TOS, 1, Window Top)
            %else
               %if BOS_Next == Nil %start
                  {no more to come on at the bottom, add from the top if pos}
                  %if TOS_Last == Nil %start
                     {nothing to add at the top}
                     Forward Scroll (CY - 1, Window Bottom)
                     CY = CY - 1
                     Cursor (0, CY)
                     Print Line (Current, 1, CY)
                     Screen Lines = Screen Lines - 1
                  %else
                     Cursor (0, CY)
                     Print Line (Current, 1, CY)
                     Reverse Scroll (Window Top, CY - 1)
                     TOS == TOS_Last
                     Cursor (0, Window Top)
                     Print Line (TOS, 1, Window Top)
                  %finish
               %else
                  {bring a line on at the bottom}
                  CY = CY - 1
                  Cursor (0, CY)
                  Print Line (Current, 1, CY)
                  Forward Scroll (CY + 1, Window Bottom)
                  BOS == BOS_Next
                  Cursor (0, Window Bottom)
                  Print Line (BOS, 1, Window Bottom)
               %finish
            %finish
            Reset Cursor
         %finish
         current buffer_changed = yes
      %end {of join line}

   {ERASE LEFT}

   %routine erase left
      desired X = -1
      %if pos = 1 %start
         join line
      %else
         pos = pos - 1                          {move left}
         reset cursor
         %return %if pos > current_length
         %if typing mode = insert character %start
            erase                               {delete}
         %else
            current_text (pos) = ' '            {overwrite it with a space}
            Current Buffer_Changed = Yes
            %if current_bits & contains CCs = 0 %start
               put symbol (' ')
               left
            %else
               print line (current, pos, CY)
               set cursor
            %finish
         %finish
      %finish
   %end {of erase left}

   {ERASE TO START OF LINE}
  
   %routine erase to start of line
      CX = 0
      desired X = -1
      %if current_length = 0 %start
         %if pos > 1 %start
            pos = 1
            reset cursor
         %finish
      %else
         current buffer_changed = yes
         %if pos > current_length %start
            dump parcel (1, current_length)
            current_length = 0
            pos = 1
            put symbol (CR)
            clear line
         %else
            dump parcel (1, pos - 1)
            current_length = current_length - pos + 1
            bulk move (current_length,
                       current_text (pos),
                       current_text (1))
            pos = 1
            set cursor
            clear line %if Current_Bits & Contains CCs # 0
            print line (current, 1, CY)
            set cursor
            check for CCs (current)
         %finish
      %finish
   %end {of erase to start of line}

   {ERASE WORD}
   
   %routine erase word
      %integer word end
      %integer length
      %integer cut
      %integer I,
               Old Length = Current_Length
      %label after thought

      desired X = -1
      -> after thought %if pos > current_length 
      %if punctuation(current_text (pos)) %start
         erase                               {delete punctuation mark}
      %else
         %if formatting (current_text (pos)) %or %c
             (pos > 1 %and alphanumeric (current_text (pos - 1))) %start
         after thought:
            find word left (pos)
         %finish
         {we are at the start of the word, now find the end}
         word end = pos
         find word right (word end)
         cut = word end - pos
         %return %if Cut = 0
         %if Cut < 0 %start
            Fail ("Internal Error in Erase Word", Yes)
            %return
         %finish
         dump parcel (pos, cut)
         length = current_length - word end + 1
         bulk move (length,
                    current_text (word end),
                    current_text (pos))
         current_length = current_length - cut
         reset cursor

         %if CX < Terminal Width %start
            %if current_bits & contains CCs = 0 %and %c
                Terminal Characteristics&Delete Character Ability # 0 %start
               Delete Characters (Cut)
               %if Old Length > Terminal Width %start
                  {The following - 2 is to do with the X range being 0.. and}
                  {the characters being 1.. and that Terminal Width is Max Y + 1}
                  Cursor (Terminal Width - Cut - 2, CY)
                  Print Line (Current, Terminal Width - Cut - 1, CY) 
                  Reset Cursor
               %finish
            %else
               print line (current, pos, CY)
               set cursor
            %finish
         %finish
         current buffer_changed = yes
      %finish
   %end {of erase word}
     
   {MOVE TO LINE}

   %routine move to line
      %integer sign, line,
               cur, tot
      %record (line fm) %name L

      read integer (sign, line, 
                    "Line: ", 
                    Yes, Yes, No)        {+- ? *}
      %if sign = query %start
         tot = 0
         L == current buffer_first
         %while L ## nil %cycle
            tot = tot + 1
            cur = tot %if L == current
            L == L_next
         %repeat
         %if Current_Bits&Pseudo Line # 0 %start
            Fail ("This buffer is " . I To S (Tot - 1, 0) . " lines long.", No)
         %else
            fail ("Current line number is " . I to S (cur, 0) . %c
                  ". This buffer is " . %c
                  I to S (tot - 1, 0) . " lines long.", no)
         %finish
      %else
         Replace Top Line
         %if line > 0 %start
            Set Magic Marker
            current == find line (sign, line)
            restrict cursor
            set screen (0)
         %else
            set cursor
         %finish
         desired X = -1
      %finish
   %end {of move to line}

   {MOVE TO COLUMN}
   
   %routine move to column
      %integer sign, col
      %integer spaces, I
      
      read integer (sign, col, "Column: ",
                    Yes, Yes, No)
      %if sign = query %start
         fail ("Current column is " . I to S (pos, 0) . %c
               ".  Line length is " . I to S (current_length, 0) . ".", no)
      %else
         %if col > 0 %start
            %if sign = absolute %start
               pos = col
            %else %if sign = delta plus
               pos = pos + col
            %else
               pos = pos - col
            %finish
            CX = find X (current, pos)
            Desired X = -1
            Close Parcel
         %finish
         Replace Top Line
      %finish
   %end {of move to column}

   {INSERT ASCII}

   %routine Insert ASCII
      %integer sign, Ch

      read integer (sign, Ch, "ASCII: ",
                    No, No, No)
      Replace Top Line
      Insert (Ch) %unless Ch = 0
   %end {Insert ASCII}

   {FIND BRACKET}
   
   %routine find bracket
      %record (line fm) %name NC == Current
      %integer NP = pos
      
      %constant %integer forwards = 0,
                         backwards = 1,
                         max bracket = 12
      
      %constant %byte %array ons (1 : max bracket) = 
         '(',
         ')',
         '[',
         ']',
         '{',
         '}',
         '<',
         '>',
         '"',
         '''',
         '`',
         '%'
   
      %constant %byte %array matches (1 : max bracket) =
         ')',
         '(',
         ']',
         '[',
         '}',
         '{',
         '>',
         '<',
         '"',
         '''',
         '`',
         '%'
      
      %constant %byte %array directions (1 : max bracket) =
         forwards,
         backwards,
         forwards,
         backwards,
         forwards,
         backwards,
         forwards,
         backwards,
         forwards,
         forwards,
         forwards,
         forwards
         
      %integer n, 
               which = 0,
               match,
               on,
               direction,
               stack = 0
      
      %label error
      
      %return %if pos > current_length %or %c
                  current_bits&pseudo line # 0
      %for n = 1, 1, max bracket %cycle
         %if current_text (pos) = ons (n) %start
            which = n
            %exit
         %finish
      %repeat
      %if which = 0 %start
         put symbol (BEL) %if Beep Mute = No
         %return
      %finish
      match = matches (which)
      on = ons (which)
      direction = directions (which)
      %cycle
         {move cursor}
         %if direction = forwards %start
            NP = NP + 1
            %while  NP > NC_length %cycle
               %if NC_next == nil %start
               error:
                  fail ("No matching bracket", yes)
                  %return
               %else
                  NC == NC_next
                  NP = 1
               %finish
            %repeat
         %else
            NP = NP - 1
            %while NP = 0 %cycle
               %if NC_last == nil %start
                  -> error
               %else
                  NC == NC_last
                  NP = NC_length
               %finish
            %repeat
         %finish
         %if NC_text (NP) = match %start
            stack = stack - 1 
         %else %if NC_text (NP) = on
            stack = stack + 1 
         %finish
      %repeat %until stack < 0
      Set Magic Marker
      Current == NC
      Pos     =  NP
      CX = find X (current, pos)
      set screen (0)
   %end {find bracket}

   {LAST POSITION}

   %routine Last Position
      %record (Magic Marker fm) %name Magic Marker == Current Buffer_Magic Markers

      %if Magic Marker == Nil %start
         Put Symbol (BEL) %if Beep Mute = No
      %else
         Desired X = -1
         Close Parcel
         Current Buffer_Magic Markers == Magic Marker_Next
         Current == Magic Marker_Line
         Pos = Magic Marker_Pos
         CX = Find X (Current, Pos)
         Set Screen (0)
         Dispose (Magic Marker)
      %finish
   %end {Last Position}

{CALM PANIC}

%routine Calm Panic
   Set Up Terminal
   Set Terminal Characteristics
   Keypad On
   Normal
   Graph Off
   Redraw Screen
   Clear Broadcast Window
%end {Calm Panic}

{APM} %external %routine %spec Mark
{APM} %external %routine %spec Release

!!!%external %routine %spec Scenario

{DO SHELL ESCAPE}

%routine Do Shell Escape
!VMS! Put Symbol (BEL) %unless Beep Mute = Yes

!{APM}   %on 0 %start
!{APM}      Calm Panic
!{APM}      %return
!{APM}   %finish
!{APM}
!!!!!Scenario
!{APM}   Cursor (0, Window Bottom - 1)
!{APM}   Clear Line
!{APM}   Put String ("Tread warily...")
!{APM}   Cursor (0, Window Bottom)
!{APM}   CLear Line
!{APM}   %cycle
!{APM}      Open Input (0, ":")
!{APM}      Mark
!{APM}      Obey Stream ("")
!{APM}      Release
!{APM}   %repeat

!!!Scenario
   Fail ("Pending systems software upgrade", Yes)
%end {Do Shell Escape}

{DO SHELL BUFFER}

%routine Do Shell Buffer
!VMS! Put Symbol (BEL) %unless Beep Mute = Yes

!   %record (Text fm) Params
!   %record (Buffer fm) %name B
!   %string (31) Output
!   %constant %string (31) Input  File = "$IE_Shell_I"
!   %constant %string (31) Output File = "$IE_Shell_O"
!
!   %on 0 %start
!      Calm Panic
!      %return
!   %finish
!
!   Get Text (Params, "Parameters: ", No)
!   Read String (Output, "Output buffer: ")
!!!Scenario
!   %if Output # "" %start
!      %unless Find Buffer (Output) == Nil %start
!         Fail ("Buffer " . Output . " already exists", Yes)
!         %return
!      %finish
!      Report ("Obeying buffer " . Current Buffer_Name . -
!              " with output to buffer " . Output)
!      Open Output (0, Output File)
!   %else
!      Cursor (0, Window Bottom)
!      Clear Line
!      Open Output (0, ":")
!   %finish
!   Unload Buffer (Current Buffer, Input File)
!   Open Input (0, Input File)
!   Select Input (0)
!   Select Output (0)
!   Mark
!   Obey Stream (Params_String)
!   Release
!   Open Input (0, ":")
!   Select Input (0)
!   %if Output # "" %start
!      Close Output
!      Open Output (0, ":")
!      B == New Buffer (Output)
!      Load Buffer (B, Output File)
!      Make Empty Buffer (B) %if B_First ## Nil
!      B_Current == B_First
!      Nominate Buffer (B)
!   %else
!      Calm Panic
!   %finish
!!Scenario

   Fail ("Pending systems software upgrade", Yes)
%end {Do Shell Buffer}

%own %integer Line Altered = 0

{-EXECUTE COMMAND-}

%routine Execute Command (%integer Command)
   %record (Text fm) New String
   %integer Ch
   %switch Commands (0 : Max Command)

   Line Altered = No
   -> Commands (Command)

commands (0):                           {--INSERT LINE--}
   add new line
   %return

commands (1):                           {--INSERT CHARACTER--}
   typing mode = insert character
   %return

commands (22):                          {--OVERWRITE MODE--}
   typing mode = overwrite character
   %return

commands (52):                          {--TOGGLE MODE--}
   %if Typing Mode = Insert Character %start
      Typing Mode = Overwrite Character
   %else
      Typing Mode = Insert Character
   %finish
   %return

commands (2):                           {--SEARCH--}
   get text (new string, "Search: ", \case significance)
   %if new string_length > 0 %start
      search string = new string
      search (search string, yes, Forwards)
   %finish
   %return

commands (3):                           {--DELETE LINE--}
   remove line
   %return

commands (4):                           {--DELETE CHARACTER--}
   erase
   Line Altered = Yes
   %return

commands (5):                           {--CHANGE TEXT--}
   change text
   %return

commands (6):                           {--END OF LINE--}
   move to end of line
   %return

commands (7):                           {--START OF LINE--}
   move to start of line
   %return

commands (8):                           {--MOVE TO END OF FILE--}
   move to end of file
   %return

commands (9):                           {--END OF PAGE--}
   move to end of page
   %return

commands (10):                          {--CLOSE--}
   close edit %unless From Journal = Yes
   %return

commands (11):                          {--SCROLL PAGE DOWN--}
   scroll page down
   %return

commands (12):                          {--INTERACT--}
   interact
   %return

commands (13):                          {--DEFINE KEY--}
   Define Key
   %return

commands (14):                          {--PANIC--}
   Calm Panic
   %return

commands (15):                          {--LEFT--}
   move left
   %return

commands (16):                          {--RIGHT--}
   move right
   %return

commands (17):                          {--UP--}
   move up
   %return

commands (18):                          {--DOWN--}
   move down
   %return

commands (19):                          {--START OF FILE--}
   move to start of file
   %return

commands (20):                          {--MOVE LINE--}
   move line (Yes)
   %return

commands (65):                          {--DUPLICATE LINE--}
   Move Line (No)
   %return

commands (21):                          {--RESTORE ALL DELETIONS--}
   restore all deletions
   %return

commands (23):                          {--SEARCH AGAIN--}
   search (search string, no, Last Search Direction)
   %return

commands (24):                          {--RESTORE LINE--}
   restore line
   %return

commands (25):                          {--REPLACE AGAIN--}
   %if search before replace = yes %start
      search  (replace search, no, Forwards)
      replace (replace search)
   %else
      replace (search string)
   %finish
   %return

commands (26):                          {--ABORT--}
   abort edit %unless From Journal = Yes
   %return

commands (27):                          {--ERASE TO END OF LINE--}
   erase to end of line
   Line Altered = Yes
   %return

commands (28):                          {--DELETE TO END OF FILE--}
   delete to end of file
   %return

commands (67):
   Delete To Start Of File              {--DELETE TO START OF FILE--}
   %return

commands (29):                          {--SCROLL PAGE UP--}
   scroll page up
   %return

commands (30):                          {--MOVE TO START OF PAGE--}
   move to start of page
   %return

commands (31):                          {--MOVE WORD LEFT--}
   move word left
   %return

commands (32):                          {--MOVE WORD RIGHT--}
   move word right
   %return

commands (33):                          {--MOVE UP TO START OF LINE--}
   move up to start of line
   %return

commands (34):                          {--MOVE DOWN TO START OF LINE--}
   move down to start of line
   %return

commands (35):                          {--CHANGE TO LOWER CASE--}
   change to lower case
   Line Altered = Yes
   %return

commands (36):                          {--CHANGE TO UPPER CASE--}
   change to upper case
   Line Altered = Yes
   %return

commands (37):                          {--CHANGE TO FLIPPED CASE--}
   change to flipped case
   Line Altered = Yes
   %return

commands (38):                          {--SWAP ABOUT--}
   swap about
   Line Altered = Yes
   %return

commands (39):                          {--SET MARKER--}
   set marker
   %return
      
commands (40):                          {--JUMP TO MARKER--}
   jump to marker
   %return

commands (41):                          {--SELECT BUFFER--}
   select buffer
   %return
     
commands (42):                          {--GET BUFFER--}
   get buffer
   %return

commands (43):                          {--PUT BUFFER--}
   put buffer
   %return

commands (44):                          {--INCLUDE BUFFER--}
   include buffer
   %return

commands (45):                          {--SET COPY POSITION--}
   set copy position
   %return

commands (46):                          {--CLONE LINE--}
   clone line
   %return

commands (47):                          {--BACK TAB--}
   back TAB
   Line Altered = Yes
   %return

commands (48):                          {--ERASE LEFT--}
   erase left
   Line Altered = Yes
   %return

commands (49):                          {--MOVE TO COLUMN--}
   move to column
   %return

commands (50):                          {--MATCH BRACKET--}
   find bracket
   %return

commands (51):                          {--CLEAR BROADCAST WINDOW--}
   clear broadcast window
   %return

commands (53):                          {--INSERT TAB--}
   insert TAB
   Line Altered = Yes
   %return

commands (54):                          {--MOVE TO LINE--}
   move to line
   %return

commands (55):                          {--LAST POSITION--}
   Last Position
   %return

commands (56):                          {--BREAK LINE--}
   break line
   %return

commands (57):                          {--REPEAT COMMAND--}
   Repeat Command
   %return

commands (58):                          {--INSERT LITERAL--}
   %if From Journal = yes %start
      Read Symbol (Ch)
   %else
      Ch = Get Symbol
      Put Journal Symbol (Ch)
   %finish
   Insert (Ch)
   Line Altered = Yes
   %return

commands (59):                          {--REDRAW SCREEN--}
   redraw screen
   %return

commands (60):                          {--ERASE WORD--}
   erase word
   Line Altered = Yes
   %return

commands (61):                          {--ERASE TO START OF LINE--}
   erase to start of line
   Line Altered = Yes
   %return

commands (62):                          {--UNDO DELETION--}
   Undo Parcel
   Line Altered = Yes
   %return

commands (63):                          {--BACKWARDS SEARCH--}
   get text (new string, "hcraeS: ", \case significance)
   %if new string_length > 0 %start
      search string = new string
      search (search string, yes, Backwards)
   %finish
   %return

commands (64):                          {--DEBUGGER--}
   %if Debug = Yes %start
      IE Debugger ("")
   %else
      Put Symbol (BEL)
   %finish
   %return

commands (66):                          {--JUSTIFY LINE--}
   Justify Line (Current, CY)
   POS = 1
   CX = 0
   Reset Cursor
   Line Altered = Yes
   %return

Commands (69):
   Do Shell Escape
   %return

Commands (70):
   Do Shell Buffer
   %return

Commands (71):
   Insert ASCII
   Line Altered = Yes
   %return

Commands (68):                          {--NO OP--}
%end {Execute Command}

{FINANGLE}

%routine Finangle
   %integer Ch

   %on 0 %start
      Abort Edit %if Event_Sub = 1 %and -
                     Event_Extra = 0
   %finish

   Clear Window
   Always Keep Journal = Yes
   Cursor (0, Window Top + 1)
   Feature
   Put String ("********** ARGH: Event ")
   Put Integer (Event_Event)
   Put String (", ")
   Put Integer (Event_Sub)
   Put String (", ")
   Put Hex (Event_Extra)
   Put String (": " . Event_Message)
   Normal
   Cursor (0, Window Top + 4)
   Put String ("This shouldn't have happened, but it has!")
   Cursor (0, Window Top + 6)
   Put String ( %c
"It is possible that your edit is corrupt, I would recommend closing the edit.")
   Cursor (0, Window Top + 7)
   Put String ( %c
     "In any event your journal file will be kept even on a normal Close Edit.")
   Cursor (0, Window Top + 11)

{APM}   Put String ( -
{APM} "Please inform Richard Marshall and Rainer Thonnes if this has happened,")

!VMS!   Put String ("Please inform Richard Marshall if this has happened, ")

!UNIX!  Put String ("Please inform George Ross if this has happened")

   Cursor (0, Window Top + 12)
   Put String ("including all the event information given above.")

   Cursor (20, Window Bottom)
   Feature
   Put String ("Press any key to resume edit: ")
   Normal
   Ch = Get Key %until Ch # XON %and %c
                       Ch # XOFF
   Normal
   Redraw Screen
   Fail ("Tread warily...", No)
%end {Finangle}

{-EDIT-}

%routine Edit
   %integer Ch
   %record (Key Definition fm) %name Key

   %on 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 %start
      Finangle
   %finish

   TT wait = yes
   ch = Get Next Ch
   TT wait = no
   Key == Keys (Ch)
   %if Key_Type = Letter %start
      Insert (Key_Value)
      Line Altered = Yes
   %else %if Key_Type = Command
      Insert Off
      Execute Command (Key_Value)
   %else %if Key_Type = Macro
      Key_Value = Macro Pos
      Key_Stack == Current Macro
      Key_Type = Busy Macro
      Current Macro == Key
      Macro Pos = 1
      Line Altered = No
   %else %if Key_Type = Busy Macro
      Fail ("Recursive Macro", Yes)
   %else {undefined}
      Put Symbol (BEL) %unless Beep Mute = Yes
   %finish
   Found = No %if Line Altered = Yes %and %c
                  found = yes %and %c
                  Current == Found Line 
   display broadcast %if broadcast # ""
%end {of edit}

{CLEAR BROADCAST WINDOW}

%routine clear broadcast window
   %if Terminal Characteristics&Line 25 Ability # 0 %start
      Close Line 25
      %return
   %finish
   %return %unless broadcast window open = yes
   window top = window top - 1
   %if TOS_last == nil %start
      Forward Scroll (Window Top, Window Bottom)

      CY = CY - 1
      %if screen lines = max lines %and %c
          BOS_next ## nil %start
         cursor (0, window bottom)
         BOS == BOS_next
         print line (BOS, 1, window bottom)
         screen lines = screen lines + 1
      %finish
   %else
      cursor (0, window top)
      clear line
      TOS == TOS_last
      print line (TOS, 1, window top)
      screen lines = screen lines + 1
   %finish
   max lines = max lines + 1
   set cursor
   broadcast window open = no
%end {clear broadcast window}

{DISPLAY BROADCAST}

%routine display broadcast

   {OPEN WINDOW}
   
   %routine open window

      {SCROLL DOWN}
      
      %routine scroll down

         Reverse Scroll (Window Top, Window Bottom)

         CY = CY + 1
      %end {scroll down}

      %if max lines > screen lines %start
         scroll down
      %else
         %if CY < window top + 2 %start
            BOS_bits = BOS_bits & (\displayed)
            BOS == BOS_last
            scroll down
         %else
            TOS_bits = TOS_bits & (\displayed)
            TOS == TOS_next
         %finish
         screen lines = screen lines  - 1
      %finish
      window top = window top + 1
      max lines = max lines - 1
      broadcast window open = yes
   %end {open window}
   
   {MAIN CODE OF DISPLAY BROADCAST}

   %if Terminal Characteristics&Line 25 Ability # 0 %start
      Put Line 25 (Broadcast)
   %else
      open window %if broadcast window open = no
      Insert Off
      cursor (0, window top - 1)
      clear line
      Feature
      put string (broadcast)
      Normal
   %finish
   put symbol (BEL)
   set cursor
   flush symbols
   broadcast = ""
%end {display broadcast}

{RECEIVE BROADCAST}

{This is called asynchronously}

%external %routine receive broadcast %alias "IE_BROADCAST" %c
                                     (%string (255) message)
   %return %if options&deaf # 0
   broadcast = message
   display broadcast %if TT wait = yes 
%end {receive broadcast}

{IE EDITOR}

%external %routine IE editor %alias "IE_EDITOR" %c
                             (%string (255) p in file, p out file,
                              %integer p window top, p window bottom,
                              %integer %name p start line, p start pos,
                              %string (255) p profile,
                                            p keyboard,
                              %integer p options)

{\UNIX}   %integer Saved Input = In Stream,
{\UNIX}            Saved Output = Out Stream
!UNIX!    %integer Saved Input = Input Stream,
!UNIX!             Saved Output = Output Stream

   in file = p in file
   out file = p out file
   window top = p window top 
   window bottom = p window bottom
   start line = p start line
   start pos = p start pos
   pos = start pos
   options = p options

!VMS! %if In File = "" %start
!VMS!    Grand Default File Name = Default File Name
!VMS! %else
!VMS!    Grand Default File Name = In File
!VMS! %finish
!VMS! %if P Profile # "" %start
!VMS!    Default File Name = ".IE"
!VMS!    profile = Expand File Name (p profile)
!VMS! %finish
!VMS! %if P Keyboard # "" %start
!VMS!    Default File Name = ".KIE"
!VMS!    keyboard = Expand File Name (p keyboard)
!VMS! %finish
!VMS! Default File Name = Grand Default File Name

{\VMS} Profile = P Profile
{\VMS} Keyboard = P Keyboard

   broadcast window open = no
   TT wait               = no
   deleted               == nil
   copy position         == nil
   Found Line            == Nil
   Buffers               == Nil
   Current Macro         == Nil
   Macro Pos             = 0
   Current Definition    == Nil
   finished              = no
   screen lines          = 0             {no of lines actually displayed}
   typing mode           = insert character
   found                 = no
   failed                = no
   Last Search Direction = Forwards
   Wrap Search           = No
   Back Search           = No
   From Journal          = No
   Debug                 = No
   Journal Frequency     = 0
   Keep Journal          = No
   Journal Heading Done  = No
   Beep Column           = Terminal Width - 5
   Left Margin           = 1
   Right Margin          = Terminal Width


   !! printstring("Initialise heap");  print symbol(13);  newline
   Initialise Heap Space
   !! printstring("Start up");  print symbol(13);  newline
   Start Up
   !! printstring("Edit cycle");  print symbol(13);  newline
   %cycle
      edit
   %repeat %until finished = yes
   pstart pos = start pos
   p start line = start line
   Broadcast = ""          {no flow over from previous calls!}
   Reset Heap Space %if P Options&Reset Heap # 0
   %if Always Keep Journal = Yes %start
      Keep Journal File
   %else
      Scrap Journal File
   %finish

   Select Input (Saved Input)
   Select Output (Saved Output)
%end {of IE editor}

%end %of %file
