A version of Unidata's AE that was written to cope with a move to Universe when we were relying on its security capabilities. This has a few extra things like an embedded full-page editor and search and change histories. The original source code (version 1.03) was released to public domain by Public Trust of New Zealand as a way of thanking the Pick community for assistance given over the years.

This version adds command line editing and fixes a slight bug in DELR processing in full screen editing.

LED_6.TXT is the previous version

     program led
* ECL - KRJ - Line Editor (Replicating Unidata's AE)
* VERSION: 01.07
* This editor reproduces the UniData Alternate Editor under UniVerse.
* The code is supplied on an as-is basis as a source-code resource.
* No representation whatever is made as to the operation of the program.
* I have made use of $IFDEF to switch in and out particular features.
* This method means that it should be obvious where to edit/comment out
* lines that are not available for any particular environment (like R83).
* NEWDIMWORKS Can change the size of a dimensioned array, but I can't see
*             a performance difference.  This does NOT work in R83.
* REMOVEWORKS Allows the use of REMOVE to build cells.  This is a real
*             speed up.  This does NOT work in R83.
* CHANGEWORKS Allows CHANGE and CONVERT.  This does NOT work in R83.
* TRIMFBWORKS Allows TRIMF & TRIMB.  This does NOT work in R83
* WERRORWORKS Has ON ERROR for WRITE & DELETE.  This does NOT work in R83
* LABCOMWORKS Allows labelled COMMON.  This does NOT work in R83
*             It also assumes that ASSIGNED() or UNASSIGNED() will work
* SECUREWORKS is the flag for your AE-style security (SEC... variables)
*             which is a PREPROG program - haven't done POSTPROG
* FILINFWORKS Means that we can use FILEINFO(). This does NOT work in R83
* USEATVALUES Means we use @ variables.  This does NOT work in R83
$define newdimworks
$define removeworks
$define changeworks
$define trimfbworks
$define werrorworks
$define labcomworks
$define secureworks
$define filinfworks
$define useatvalues
* Note that a QM system has QM automatically defined
* On QM you must comment out the next two lines ($UNDEFINE won't work)
$define universe
$undefine unidata
*===
* Version information
* 01.07 - Added command line editing (if the terminal supports screen
*         addressing) and so all input can use editing keys.
* 01.06 - Changed delete-to-end-of-line to delete line feed when at
*         or after the end-of-line.
*         Made to work in terminal independant mode (like FINP program)
*         Single dot shows last command only.
*         Fixed subtle bug when inserting by using .C then .X commands
* 01.05 - Changed RA to work like S and made both move command to top
*         Changed non-print to display differently
*         Changed page editing commands slightly,  abandons changes
* 01.04A- Added command SPACE for whitespace insensitive search
*         Extend CASE to cover FL and FLA, and save it in EDKEEP
*         Add the VIEW capability (IE a command to browse files)
* 01.04 - Added commands
*         CASE DISPLAY EIT FLA M .A .I .S .U
*         Changed TRIMBIT to use CHIT, not TEMP
*         Amended 'I' for multiline insert to fix bug
*         Fixed KMUL input and clarified its operation
*         Changed EC to work with more awkwardly written lines
*         Changed TTID to use WHOM, not TERM, at the start
*         Changed STAMP to show @WHO, not @PATH
*         Fixed bug when deleting to end of line in page edit mode
* 01.03 - Added commands
*         CUT MACRO
*         Added patterm matching to FL command
*         Reset OOPS after write of record
*         Added GOSUB GET.LINE; TEMP = LINE a couple of times in PED.COMD
*         Rewritten as a monolithic program for portability.  If you
*         want to break out the help data and/or the indenter logic
*         it's reasonably clear where you should do so as old code is
*         just commented out.
* 01.03UD UniData doesn't have screen addressing on NT "Console"
*         AND doesn't support "R%n" formatting in BASICTYPE "U"
* 01.03QM Note that FOR NEXT loop numbers increment differently so
*         a lot of HERE = HERE - 1 change to HERE = MSUP even though
*         these are not needed for QM - means it still works for U2.
*         Changed ASSIGNED back to UNASSIGNED for QM.
*         Put in TEST = @(0,0) to stop pagination prompting for QM.
*         Amended DTX and XTD - only UNIVERSE has the functions, and
*         DTX conversion is wrong if input is too large (crashes QM!)
*         Added logic to cater for QM use of SOURCE.CONTROL program
*         Amended page editor keys for "qmterm" terminal
* 01.03UV Added test for "console" as UV on NT does not have screen
*         addressing and @(-4) will insert a line feed.
* 01.02 - Added commands
*         < > <> COPY DROP FM FMA LA LNA MOVE SORT
*         Changed "SHOW = FALSE" to "SHOW = SHEW" for Change Match
*         Can see new features in old versions like "HELP VER01_01 NEW"
* 01.01 - Added Commands
*        BK CM COUNT DTX EPR FL HEX IC LL LN MV NULL OC PE SEQ SHOW V XTD
*         Added Synonyms CAT and FILE
*         Tidy up, add CHANGE.MADE, add change commands to '?'
*         Help logic put in line
*         Used ASSIGNED rather than UNASSIGNED (Possible UniVerse Bug)
*         Added speed-up code when deleting to end of file
* 01.00 - First production version - Doesn't line-wrap!
*         Many and varied changes, notably use of $IFDEF
* 00.01 - Pre-production version (released to MaVerick)
*===
* Easier to see than MCP display conversion
     badc = char(255)
     for xx = 0 to 31    ; badc := char(xx) ; next xx
     for xx = 128 to 250 ; badc := char(xx) ; next xx
     gudc = str('~',len(badc))

$ifdef universe
$options information
$endif

* Special subtlety for Unidata not supporting 'R%3' form
$ifdef unidata
$basictype "U"
     prfx = ''; sufx = '\0R'
$else
     prfx = 'R%'; sufx = ''
$endif
*===
* INITIALISE
     prompt ''
$ifdef useatvalues
     am = @am; vm = @vm; sm = @sm
$else
     am = char(254); vm = char(253); sm = char(252)
$endif
     true = 1 = 1; false = not(true); qt = '"\':"'"
$ifdef labcomworks
     common /led$data/ edkeep,safe
$endif
$ifdef newdimworks
     equ cellsize to 50
     dim memr(1)
$else
     equ numcells to 100
     dim memr(numcells)
$endif
* List of verbs for viewing data only
     viewverb = 'VIEW':am:'BROWSE':am:'LOOK'
* XCOM data
     dim junk(100)
     equ this to junk(1)
     equ item to junk(2)
     equ here to junk(3)
     equ x$cc to junk(11)
     equ comi to junk(13)
     equ comd to junk(14)
     equ last to junk(15)
     equ comdmark to junk(19)
     equ wordmark to junk(20)
     equ fnam to junk(24)
     equ xsep to junk(25)
     mat junk = ''
     xsep = ' '
     wordmark = ' '
     comdmark = '`'
* Local data
     begn = @(0) ; ceop = @(-3) ; ceol = @(-4) ; goup = @(-10)
     editpage = true ; pageedit = true
     caseflag = true
     spaceflag = true
     heap = false ; salt = ''
* This next bit is to cope with U2 on NT at console
$ifndef qm
     if oconv(system(7),'MCU') eq 'CONSOLE' then
        editpage = false ; pageedit = false
        begn = char(13); ceop = ''; ceol = ''; goup = ''
     end
$endif
     plen = 23 ; pwin = 17
     dim fr(10) ; mat fr = '' ; fr(3) = 'MCU'
     oops = '' ; oopc = '' ; oopl = '' ; oopf = ''
     oopb = ''
     join = '' ; nill = '' ; fold = ''
     macn = 0; macc = ''
     scrset = false
$ifdef qm
     scrset = true
$endif
     if scrset then test = @(0,0)
* Find Match words
     fm.words = '' ; fm.findf = '' ; fm.finda = ''
     fm.words<1> = 'END' ; fm.findf<1> = 'END'
     fm.finda<1> = 'IF':vm:'END':vm:'OPEN':vm:'OPENSEQ':vm:'BEGIN'
     fm.words<2> = 'LOOP' ; fm.findf<2> = 'REPEAT':vm:'UNTIL':vm:'WHILE'
     fm.words<3> = 'UNTIL'; fm.findf<3> = fm.findf<2>
     fm.finda<3> = 'LOOP':vm:'UNTIL':vm:'WHILE'
     fm.words<4> = 'WHILE'
     fm.findf<4> = fm.findf<2>; fm.finda<4> = fm.finda<3>
     fm.words<5> = 'FOR' ; fm.findf<5> = 'NEXT'
     fm.words<6> = 'NEXT' ; fm.finda<6> = 'FOR'
     fm.words<7> = 'BEGIN' ; fm.findf<7> = 'END CASE':vm:'CASE'
     fm.words<8> = 'CASE' ; fm.findf<8> = 'CASE':vm:'END CASE'
     fm.finda<8> = 'BEGIN CASE':vm:'CASE'
     fm.words<9> = 'LOCKED' ; fm.findf<9> = 'END'
     fm.finda<9> = 'READU':vm:'READVU':vm:'MATREADU'
     fm.words<10> = 'REPEAT' ; fm.finda<10> = fm.finda<3>
     endwords = 'IF\OPEN\OPENSEQ\READNEXT\READ\READU\READV\READVU\'
     endwords = endwords:'MATREAD\MATREADU\LOCATE'
$ifdef changeworks
     convert '\' to am in endwords
$else
     chit = endwords ; old1 = '\' ; new1 = am
     gosub changeit ; endwords = chit
$endif

* page editor stuff
* Define page limits
     wide = system(2)
     botl = system(3) - 2; clpg = @(-1)
     bell = str(char(7),3)
     botp = @(0,system(3)-1):ceol

* Define key activity numbers

     equ uarr to 1; equ darr to 2; equ larr to 3; equ rarr to 4
     equ upag to 5; equ dpag to 6; equ lpag to 7; equ rpag to 8
     equ tpag to 9; equ bpag to 10
     equ escp to 11; equ phlp to 12; equ zoom to 13
     equ delc to 14; equ dell to 15; equ delr to 16
     equ back to 17; equ carr to 18; equ togg to 19
     equ writ to 20; equ doit to 21

* Define key caps for help
* Note Ctrl-B is left arrow in EMACS-like, but Ctrl-U works for ADDS and Wyse
* and I wanted to have Top and Bottom, so...
     dim kcap(21)
     mat kcap = ''
     kcap(uarr) = ' or '
     kcap(darr) = ' or '
     kcap(larr) = ' or '  
     kcap(rarr) = ' or '
     kcap(upag) = ' or '
     kcap(dpag) = ' or '
     kcap(lpag) = ' or '
     kcap(rpag) = ' or '
     kcap(tpag) = ''
     kcap(bpag) = ''
     kcap(escp) = ''
     kcap(phlp) = ''
     kcap(zoom) = ''
     kcap(delc) = ' or '
     kcap(dell) = ''
     kcap(delr) = ''
     kcap(back) = ''
     kcap(carr) = ''
     kcap(togg) = ' or '
     kcap(writ) = ''

* Define activity/key combinations
* Standard keys
     acts = '' ; keys = ''

     acts<-1> = carr ; keys<-1> = char(13)
     acts<-1> = back ; keys<-1> = char(8)
     acts<-1> = escp ; keys<-1> = char(27)
     acts<-1> = upag ; keys<-1> = char(16)
     acts<-1> = dpag ; keys<-1> = char(14)
     acts<-1> = rpag ; keys<-1> = char(05)
     acts<-1> = tpag ; keys<-1> = char(20)
     acts<-1> = bpag ; keys<-1> = char(02)
     acts<-1> = zoom ; keys<-1> = char(07)
     acts<-1> = delc ; keys<-1> = char(04)
     acts<-1> = dell ; keys<-1> = char(24)
     acts<-1> = delr ; keys<-1> = char(11)
     acts<-1> = togg ; keys<-1> = char(09)

* VT Keys (Note two arrow possibilities)
     acts<-1> = uarr; keys<-1> = char(27):'[A'
     acts<-1> = darr; keys<-1> = char(27):'[B'
     acts<-1> = larr; keys<-1> = char(27):'[D'
     acts<-1> = rarr; keys<-1> = char(27):'[C'

     acts<-1> = uarr; keys<-1> = char(27):'OA'
     acts<-1> = darr; keys<-1> = char(27):'OB'
     acts<-1> = larr; keys<-1> = char(27):'OD'
     acts<-1> = rarr; keys<-1> = char(27):'OC'
     acts<-1> = upag; keys<-1> = char(27):'[5~'
     acts<-1> = dpag; keys<-1> = char(27):'[6~'
* Following 4 are per docs but don't work in HOSTACCESS (?)
!    ACTS<-1> = LPAG; KEYS<-1> = CHAR(27):'[H'
!    ACTS<-1> = RPAG; KEYS<-1> = CHAR(27):'[1~'
!    ACTS<-1> = DELC; KEYS<-1> = CHAR(27):'[3~'
!    ACTS<-1> = TOGG; KEYS<-1> = CHAR(27):'[2~'

     acts<-1> = lpag; keys<-1> = char(27):'[2~'
     acts<-1> = rpag; keys<-1> = char(27):'[3~'
     acts<-1> = delc; keys<-1> = char(27):'[4~'
     acts<-1> = togg; keys<-1> = char(27):'[1~'

     acts<-1> = phlp; keys<-1> = char(27):'OP'
     acts<-1> = writ; keys<-1> = char(27):'OQ'

* ADDS keys - Arrow keys are simple defaults
     acts<-1> = uarr; keys<-1> = char(26)
     acts<-1> = darr; keys<-1> = char(10)
     acts<-1> = larr; keys<-1> = char(21)
     acts<-1> = rarr; keys<-1> = char(06)
     acts<-1> = upag; keys<-1> = char(27):'J'
     acts<-1> = dpag; keys<-1> = char(27):'|'
     acts<-1> = lpag; keys<-1> = char(01)
     acts<-1> = rpag; keys<-1> = char(27):'T'
     acts<-1> = delc; keys<-1> = char(27):'W'
     acts<-1> = togg; keys<-1> = char(27):'Q'
     acts<-1> = phlp; keys<-1> = char(02):'B1':char(13)
     acts<-1> = writ; keys<-1> = char(02):'B2':char(13)

* Wyse Keys
   ! ACTS<-1> = UARR; KEYS<-1> = CHAR(11)  ;* want this for DELR
   ! ACTS<-1> = DARR; KEYS<-1> = CHAR(10)  ;* same as ADDS
   ! ACTS<-1> = LARR; KEYS<-1> = CHAR(21)  ;* same as ADDS
     acts<-1> = rarr; keys<-1> = char(12)
     acts<-1> = dpag; keys<-1> = char(27):'K'
     acts<-1> = lpag; keys<-1> = char(30)
     acts<-1> = rpag; keys<-1> = char(27):'T'
     acts<-1> = delc; keys<-1> = char(27):'W'
     acts<-1> = togg; keys<-1> = char(27):'Q'
     acts<-1> = togg; keys<-1> = char(27):'q'
     acts<-1> = phlp; keys<-1> = char(01):'@':char(13)
     acts<-1> = writ; keys<-1> = char(01):'A':char(13)

* Set up the multi-part keys
     kmul = ''; kini = ''
     xxno = dcount(keys,am)
     for xx = 1 to xxno
        temp = keys
        if len(temp) le 1 then continue
        locate(temp[1,1],kini;kpos) else
           kpos = dcount(kini,am)+1
        end
        kini = temp[1,1]
        kmul = temp
     next xx

     modesave = 'LINE'
     mode = modesave
* HELP location
     help.def = 'VER01_06'
*     OPEN 'LED.HELP' TO LED.HELP ELSE HELP.DEF = ''
     gosub setup.help
* These things aren't used yet
     bort = false ; blok = false ; atta = false
     brigchar = '>'
* The saved stuff
     pres = '' ; look = '' ; stak = ''
     wild = false ; shew = false
     chan = '' ; olda = '' ; cmat = ''
$ifdef labcomworks
$ifdef universe
     test = not(assigned(edkeep))
$else
     test = unassigned(edkeep)
$endif
     if test then
        edkeep = pres:am:look:am:stak:am:wild:am:chan:am:olda:am:shew
        edkeep = edkeep:am:cmat:am:not(caseflag)
     end else
        pres = edkeep<1>
        look = edkeep<2>
        stak = edkeep<3>
        wild = edkeep<4> ; wild = not(not(wild))
        chan = edkeep<5>
        olda = edkeep<6>
        shew = edkeep<7> ; shew = not(not(shew))
        cmat = edkeep<8>
        caseflag = not(edkeep<9>)
     end
$ifdef universe
     test = not(assigned(safe))
$else
     test = unassigned(safe)
$endif
     if test then safe = ''
$else
     safe = ''
$endif
     prepprog = '' ; prepflag = false
     postprog = '' ; postflag = false
*********** UniData AE-style security start
$ifdef secureworks
$ifdef unidata
     prepprog = getenv('PREPROG_AE_UDT')
$endif
$ifdef universe
     execute 'ENV' capturing temp
     xxno = dcount(temp,am)
     for xx = 1 to xxno
        line = temp
        if field(line,'=',1) = 'PREPROG_AE_UDT' then
           prepprog = field(line,'=',2)
           xxno = xx
        end
     next xx
$endif
* QM doesn't allow underscores in environmental variables, so
* this is the closest I can get to AE environmental variable name
* and there is a bug when it hasn't been defined so the name
* requested is returned.
$ifdef qm
     call !atvar(prepprog,'@PREPROG.AE.UDT')
     if prepprog[1,1] = '@' then prepprog = ''
$endif
* These next two tests are from the AE security documentation
* They may not be required, but you can set them up if you want
*    IF PREPPROG[1,3] = 'AE_' THEN PREPPROG = ''
*    IF PREPPROG[LEN(PREPPROG)-2,3] NE '_AE' THEN PREPPROG = ''
     if prepprog ne '' then prepflag = true
$endif
*** end of SECUREWORKS section
* The following security definitions mirror those of I_AE_SECURITY
* in UniData.  I have only copied the functionality for SEC.SET
* being "NONE" (that is, this user cannot edit) and the general
* disabling of LOAD via the SEC.LOAD.FLG at first call to @PREPPROG;
* and inhibiting of file updates via subsequent @PREPPROG calls.
     dim security(40)
     equ sec.set to security(1) ;* set by preprog on very first call
* These fields are set in preprog
     equ sec.read.flg to security(2) ;* read ok or not
     equ sec.write.flg to security(3) ;* write ok or not
     equ sec.delete.flg to security(4) ;* delete ok or not
     equ sec.unload.flg to security(5) ;* unload ok or not
     equ sec.load.flg to security(6) ;* load ok or not
     equ sec.xeq.flg to security(7) ;* xeq ok or not
     equ sec.xcom.flg to security(8) ;* xcoms ok or not
* the following 5 fields pass information to preprog & postprog,
     equ sec.fn to security(9) ;* file name
     equ sec.id to security(10) ;* record id
     equ sec.dir.flg to security(11) ;* 1 if file is a directory
     equ sec.newfile.flg to security(12) ;* 1 if new file name
     equ sec.active.sel.flg to security(13) ;* 1 if select list is active
* this is how to make AE stop and return to calling program or ecl
     equ sec.stop.flg to security(14) ;* set to 1 to force ae to stop
* for secondary calls to preprog; the first 3 cannot be changed
     equ sec.call2.type to security(15) ;* 1 load, 2 unload
     equ sec.fn2 to security(16) ;* second file - for load or unload
     equ sec.id2 to security(17) ;* second id - for load or unload
     equ sec.ok2.flg to security(18) ;* if 1, ok to load/unload
* 19-22 are used by postprog, which I have not implemented
     equ sec.dict.flg to security(23) ;* 4 preprog, 1 if fn is dict ...
     equ sec.dict2.flg to security(24) ;* 4 preprog, 1 if fn2 is dict ...
* field 25 is specific to AE, this and all other fields unused
* WARNING: preprog & postprog should not use the STOP/ABORT UniBasic
* statements because files will be left open. Use the SEC.STOP.FLG
* to stop AE.
*********** UniData AE-style security end
* QM has it's own source control system depending on a callable program
* named SOURCE.CONTROL existing.  It has the following fields
*
* DICT.FLAG     - 'DICT' if a dictionary, otherwise ''
* FILE.NAME     - name of file to be written
* RECORD.NAME   - name of record to be written
* RECORD.DATA   - the record to write
* CALLER        - calling program identifier, I have used '3'
* WRITE.ALLOWED - 1 on call, returns 1 if write allowed and 0 otherwise
* UPDATED       - 0 on call, returns 1 if RECORD.DATA is changed
     source.control = false
$ifdef qm
     if catalogued('SOURCE.CONTROL') then source.control = true
$else
* Could UniVerse use !EXIST to implement the same thing?
* No matter, we can always use PREPPROG to flag it
     if prepprog = 'SOURCE.CONTROL' then source.control = true
$endif
     if source.control then prepflag = false ; prepprog = ''
$ifdef useatvalues
     name = @logname
     levl = @level
     path = @path
     term = @tty
     whom = @userno
     acct = @who
* the following line is strictly PTO
*     CALL GETWHO(WHOM)
$else
     name = field(oconv(0,'U50BB'),' ',2)
     levl = system(16)
     acct = field(oconv(0,'U50BB'),' ',3)
     if acct = '' then acct = name
     path = ''
     term = field(oconv(0,'U50BB'),' ',1)
     whom = term + 1
$endif
* IF NAME EQ 'your name here' THEN DBUG = TRUE ELSE DBUG = FALSE
     equ presnumb to 20; equ looknumb to 20; equ staknumb to 99
     equ channumb to 20
* Parse the command line - long way in before work starts, eh?
* VERB is how this was called so it should work to call again
     verb = ''
     rest = @sentence
     keepquot = false
     gosub parse.rest
     temp = dcount(bite,am)
     options = bite
     if options[1,1] = '(' then
        options = field(field(options,'(',2),')',1)
        bite = delete(bite,temp,0,0)
     end else options = ''
     if oconv(bite<1>,'MCU') = 'RUN' then
        verb = bite<1>:' ':bite<2>:' '
        bite = delete(bite,1,0,0)
        bite = delete(bite,1,0,0)
     end
     verb = verb:bite<1>
     bite = delete(bite,1,0,0)
* Check if a viewing verb has been used
* If so, we can turn off both security systems (I hope I'm right!)
* The security flags are set safe, and each command is tested
* individually, so I think it's pretty safe.
* FORMAT is still allowed, but no other change command.
     viewflag = false
     locate(oconv(verb,'MCU'),viewverb;posn) then
        viewflag = true
        source.control = false
        prepflag = false
     end
*
     fnam = bite<1>
     bite = delete(bite,1,0,0)
     if oconv(fnam,'MCU') = 'DICT' then
        fnam = 'DICT ':bite<1>
        bite = delete(bite,1,0,0)
     end
     idlist = bite
     bite = '' ; rest = '' ; ncel = ''
     if system(11) and idlist ne '' then
        crt 'A select list was active, but specific ids were entered.'
        crt 'Select list will be ignored.'
        crt '----------------------------'
        clearselect
     end
     open 'AE_COMS' to acom else
$ifdef qm
        execute 'CREATE.FILE AE_COMS'
$else
        execute 'CREATE-FILE AE_COMS 1 7'
$endif
        open 'AE_COMS' to acom else stop 'Cannot open file AE_COMS'
        if scrset then test = @(0,0)
     end
* Get file
     loop
        got.file = false
        if fnam = '' then
            stub = 'File >'; gosub get.rope; fnam = rope; crt
        end
        if fnam = '' then stop
        dprt = field(fnam,' ',1)
        fprt = field(fnam,' ',2)
        if fprt = '' then fprt = dprt ; dprt = ''
        open dprt, fprt to file then
           got.file = true
        end else
           open dprt,oconv(fprt,'MCU') to file then
              got.file = true
           end else
              crt 'Unable to find file "':fnam:'".'
              fnam = ''
           end
        end
     until got.file do
     repeat
* Get the record
     if idlist = '*' then
        idlist = ''
        execute 'SELECT ':dprt:' ':fprt
        if scrset then test = @(0,0)
     end
     if system(11) then
        eof = false
        loop
           readnext id else eof = true
        until eof do
           idlist<-1> = id
        repeat
     end
get.idlist:
     killsign = false
     if idlist = '' then
            stub = 'Record ids >'; gosub get.rope; rest = rope; crt
        keepquot = false
        gosub parse.rest
        idlist = bite
        bite = '' ; rest = ''
     end
     idcnt = dcount(idlist,am)
     for id = 1 to idcnt
        item = idlist
        gosub edit.item
        if killsign then idlist = '' ; go get.idlist
     next id
$ifdef labcomworks
     edkeep = pres:am:look:am:stak:am:wild:am:chan:am:olda:am:shew
     edkeep = edkeep:am:cmat:am:not(caseflag)
$endif
     stop
*===
* SUBROUTINES
edit.item:
     stopsign = false
     here = 0
     beg = 0 ; fin = 0
     nb = 'Command requests a block operation,'
     nb = nb:' but no block is defined.'
     crt
     if idcnt gt 1 then crt '< ':id:' > ':

     if prepflag then
        if safe = '' then
           mat security = ''
           sec.set = ''
           call @prepprog(mat security)
           if sec.set = 'NONE' then stop
           if sec.stop.flg then stop
           safe = sec.set
        end
        mat security = ''
        sec.set = safe
        if sec.set then
           sec.fn = fprt
           sec.id = item
$ifdef filinfworks
           sec.dir.flg = fileinfo(file,3) = '4'
$endif
           sec.newfile.flg = false
           sec.active.sel.flg = false
           sec.dict.flg = (dprt = 'DICT')
           call @prepprog(mat security)
           if sec.stop.flg then stop
           if not(sec.read.flg) then return
        end
     end else
        sec.stop.flg = false
        sec.read.flg = true
        sec.write.flg = true
        sec.delete.flg = true
        sec.xcom.flg = true
        sec.unload.flg = true
        sec.load.flg = true
        sec.xeq.flg = true
        sec.ok2.flg = true
* Apply the viewing flag
        if viewflag then
           crt 'VIEW ONLY - NO UPDATES ALLOWED'
           sec.write.flg = false
           sec.delete.flg = false
           sec.xcom.flg = false
           sec.unload.flg = false
           sec.load.flg = false
           sec.xeq.flg = false
           sec.ok2.flg = false
        end
     end

     readu this from file, item locked go locked.record then
        lock = true
carry.on:
        gosub parse.record
        crt 'Top of "':item:'" in "':fnam:'", ':last:
        crt ' lines, ':len(this):' characters.'
     end else
        lock = true
        this = ''
        gosub parse.record
        crt 'Top of new "':item:'" in "':fnam:'".'
     end
!!   LOCK = TRUE
     orig = this
     gosub get.lfmt
* Edit the record
     if mode<1> = 'PAGE' then
        ptop = 1
        pchr = 1
page.editor:
        gosub get.line ; temp = line
        loop
           pcol = rem(pchr-1,wide)
           prow = here+1-ptop
$ifdef universe
           input bite,-1;* universe specific
           if not(bite) then
$endif
              crt @(60,0):ceol:mode<2>:' ':fmt(here,'R#4'):
              crt ',':fmt(pchr,'L#4'):
              bite = seq(temp[pchr,1])
              if bite ne '255' then crt ' (':bite:')':
$ifdef universe
           end
$endif
           crt @(pcol,prow):
           gosub get.page.comd
           if mode = 'LINE' then
              crt botp:'Line editor mode':
              if that ne this then
                 crt ' - CHANGES HAVE BEEN MADE':
                 oops = that ; oopc = 'PE'
                 oopl = savl<1> ; oopf = savl<2>
                 oopb = beg:am:fin
              end
              crt
              that = ''
              gosub display.line
              go line.editor
           end
        repeat
        return
     end
line.editor:
     loop
* Get the command
        if x$cc ne '' then
           comi = x$cc<1>
           x$cc = delete(x$cc,1,0,0)
        end else
           if mode<1> = 'PAGE' then go page.editor
           if salt ne '' then
              comi = salt<1,1,1>; del salt<1,1,1>
           end else
              stub = prmt:': '; heap = true
              gosub get.rope; comi = rope; heap = false
           end
           if macn then macc<1,1,-1> = comi
        end
        gosub parse.command
        if not(numb = '' or numb matches '1N0N') then
           crt ; gosub bad.command
           continue
        end
        if comd = '' then
           gosub null.command
           if comd = '' then continue
        end
* Save command to list
        if comi ne '' then
           stak = insert(stak,1,1,0,comi)
           loop
           while dcount(stak,vm) gt staknumb do
              stak = delete(stak,1,staknumb,0)
           repeat
        end
* Apply the command
        if comd ne 'R' then crt
        if comd matches '1N0N' then
           here = comd
           if here gt last then here = last
           gosub display.line
           continue
        end
        first = comd[1,1]
        posn = index('ABCDEFGHIJKLMNOPQRSTUVWXYZ',first,1)
        if not(posn) then gosub bad.command ; continue
        on posn gosub a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z
        if stopsign then release file, item ; lock = false ; return
     repeat
     return

parse.command:
$ifdef trimfbworks
     comi = trimf(comi)
$else
     chit = comi ; gosub trimfit ; comi = chit
$endif
     dlim = oconv(oconv(comi,'MC/A'),'MC/N')[1,1]
     if dlim = '' then
        rest = ''
        comd = oconv(comi,'MCU')
     end else
        posn = index(comi,dlim,1)
        rest = comi[posn+1,999999]
        comd = oconv(comi[1,posn-1],'MCU')
     end
     temp = oconv(comd,'MCN')[1,1]
     if temp matches '1N' then
        temp = index(comd,temp,1)
        numb = comd[temp,999]
        comd = comd[1,temp-1]
     end else numb = ''
     return

parse.line:
     if line = comdmark then line = ''
     xx = 1
     loop
        temp = index(line,'^',xx)
     while temp do
        bite = line[temp,5]
        if bite matches '"^^"3N' then
           line = line[1,temp-1]:line[temp+1,len(line)]
           xx = xx + 1
        end else
           bite = bite[1,4]
           if bite matches '"^"3N' then
              line= line[1,temp-1]:char(bite[2,3]):line[temp+4,len(line)]
           end else xx = xx + 1
        end
     repeat
     return

a:   begin case
        case comd = 'A'                 ; * append
           if viewflag then gosub viewonly ; return
           if rest = '' then rest = olda<1,1>
           if rest = '' then
              crt 'No previous append command to repeat.'
              gosub bad.comd
              return
           end
           olda = rest:vm:dlim
           line = rest ; gosub parse.line ; rest = line
           chng = 0 ; save = here ; savl = last
           gosub set.bounds
           for here = bot to msup
              gosub get.line
              line = line:rest
              memr(cell) = line
              chng = chng + 1
              gosub display.line
           next here
           here = msup
           if chng then gosub change.made
        case 1 ; gosub bad.command
     end case
     return

b:   begin case
        case comd = 'B' and dlim = ''   ; * bottom
           here = last ; gosub display.line
        case comd = 'B' or comd = 'BD' or comd = 'BK'   ; * break line
           if viewflag then gosub viewonly ; return
           if rest = '' then
              crt 'The second field is empty.'
              gosub bad.comd
              return
           end
           chng = 0 ; save = here ; savl = last ; show = ''
           gosub set.bounds
           for here = msup to bot step -1
              gosub get.line
              posn = index(line,rest,1)
              if posn then
                 posn = posn + len(rest)
                 temp = line[posn,len(line)]
                 if temp ne '' then
                    line = line[1,posn-1]
                    if comd = 'BK' then line = temp
                    memr(cell) = line
                    show = insert(show,1,0,0,here)
                    numb = numb + 1
                    chng = chng + 1
                    if comd = 'B' then
                       msup = msup + 1
                       last = last + 1
                       lnum = lnum + 1
                       line = temp
                       gosub insert.line
                    end
                 end
              end
           next here
           if chng then
              gosub change.made
              xxno = dcount(show,am)
              savl = 0
              for xx = 1 to xxno
                 here = show + savl
                 gosub display.line
                 if comd = 'B' and xxno gt 1 then
                    here = here + 1
                    savl = savl + 1
                    gosub display.line
                 end
              next xx
           end
           here = msup + numb - 2
           if comd = 'BD' or comd = 'BK'then here = msup
           if here gt last then here = last
           if xxno gt 1 then
              crt 'Split ':numb:' records.  Now at line ':here
           end
           gosub get.line
        case comd = 'BC'         ; * bridge character display (change)
           dlim = trim(dlim):trim(rest)
           dlim = dlim[1,1]
           if dlim ne '' then brigchar = dlim
           crt 'BridgeChar is ':brigchar
           comi = ''
        case 1 ; gosub bad.command
     end case
     return

c:   begin case
        case comd = 'C'                 ; * change
           if viewflag then gosub viewonly ; return
           if numb = '' and dlim = '' then
              comd = 'RA'
              comi = 'RA1'
              numb = 1
           end
           gosub change.command
        case comd = 'CAT' ; comd = 'J' ; go j
        case comd = 'CASE'               ; * change casing flag for 'L'
           rest = oconv(rest,'MCU')
           begin case
              case rest = 'ON' ; caseflag = true
              case rest = 'OFF' ; caseflag = false
              case 1 ; caseflag = not(caseflag)
           end case
           crt 'CASE flag is ':
           if caseflag then crt 'ON' else crt 'OFF'
        case comd = 'CD'        ; * command delimiter display (change)
           if dlim = '' then
              crt 'Command delimiter is ':
           end else
              temp = '`,;#$%&~|[]{}/"':"'"
              if index(temp,dlim,1) then
                 comdmark = dlim
                 crt 'Command delimiter is now ':
              end else
                 crt dlim:' is not a valid command delimiter.'
                 crt 'Characters available for delimiters: ':temp
                 crt 'Characters reserved for other uses: \.*!?-+=^@<>_:'
                 crt 'Command delimiter remains ':
              end
           end
           if comdmark = '"' then
              crt "'":comdmark:"'"
           end else crt '"':comdmark:'"'
        case comd = 'CM'                ; * changematch command
           if viewflag then gosub viewonly ; return
           if rest = '' then
              if cmat = '' then
                 crt 'No previous changematch command to repeat.'
                 comi = ''
                 return
              end else
                 dlim = cmat<1,1>
                 rest = cmat<1,2>
                 numb = cmat<1,3>
              end
           end
           gosub changematch.command
        case comd = 'COL'               ; * column display
           temp = ''
           for xx = 1 to 9
              temp = temp:space(9):xx
           next xx
           if lfmt then
              crt begn:space(llen+2):temp[1,wide-llen-2]
           end else crt begn:temp[1,wide]
           temp = str('1234567890',10)
           if lfmt then
              crt begn:space(llen+2):temp[1,wide-llen-2]
           end else crt begn:temp[1,wide]
           temp = ''
        case comd = 'COPY'              ; * copy (merge) the block
           if viewflag then gosub viewonly ; return
           if not(beg) and not(fin) then
              crt nb ; gosub bad.comd ; return
           end
           numb = fin - beg + 1
           crt 'Lines ':beg:' to ':fin:' (':numb:') will be Copied.'
           stub = 'You are now at line ':here:'. Do it? n/y > '
           gosub get.rope; answ = rope; crt
           answ = oconv(trim(answ),'MCU')[1,1]
           if answ ne 'Y' then
              crt 'Block command canceled.'
              return
           end
           dlim = ' '
           rest = beg:' ':fin
           numb = ''
           gosub merge
           if numb then
              if beg gt here then beg = beg + numb
              if fin gt here then fin = fin + numb
           end
        case comd = 'COUNT'             ; * show the count of a string
           line = rest ; gosub parse.line ; rest = line
           if rest = '' then
              crt 'No string given to count.'
              gosub bad.comd ; return
           end
           gosub set.bounds
           temp = 0
           for here = bot to msup
              gosub get.line
              temp = temp + count(line,rest)
           next here
           here = msup
           crt temp:' occurrances of string.'
        case comd = 'CRT'           ; * insert crt line for programmer
           if viewflag then gosub viewonly ; return
           if rest = '' then
              crt 'You have not said what to put on CRT line'
              comi = ''
              return
           end
           oops = this ; oopc = comi ; oopl = here ; oopf = last
           oopb = beg:am:fin
           here = here + 1 ; last = last + 1 ; lnum = lnum + 1
           if dlim ne '"' and dlim ne '\' then dlim = "'"
           line = 'CRT ':dlim:rest:' = ':dlim:':':rest
           gosub insert.line
           gosub reset.record
           gosub display.line
        case comd = 'CUT'               ;* cut and save lines
           if viewflag then gosub viewonly ; return
           if not(sec.unload.flg) then
              crt 'Unload disabled'
              comi = ''
              return
           end
           if comi = comd then
              if not(beg) and not(fin) then
                 crt nb; gosub bad.comd; return
              end
              rest = beg
              numb = fin-beg+1
           end
           rest = trim(rest)
           if numb = '' then
              if dlim = '"' then temp = "'" else temp = '"'
              temp = '1N0N':temp:dlim:temp:'1N0N'
              if rest matches temp then
                 numb = field(rest,dlim,2) - field(rest,dlim,1) + 1
                 rest = field(rest,dlim,1)
              end
           end
           if not(rest matches '1N0N') or numb = '' then
              crt 'Format of CUT command is: "CUTn/n"; eg: "CUT10/15"':
              crt ' or "CUT/n/m"; eg: "CUT/5/24".'
              gosub bad.comd; return
           end
           if numb gt last then
              crt 'Nothing done - record does not have that many lines.'
              comi = '' ; return
           end
           bite = field(this,am,rest,numb)
           if numb ne 1 or bite ne '' then numb = dcount(bite,am)
           if numb then
              save = rest
              rest = ''
              gosub get.load
              rest = save
              if temp = '' then return
              if prepflag then
                 sec.call2.type = 2
                 sec.fn2 = ofpt
                 sec.id2 = oipt
                 sec.dict2.flg = (odpt = 'DICT')
                 call @prepprog(mat security)
                 if sec.stop.flg then stop
                 if not(sec.ok2.flg) then
                    gosub bad.comd ; return
                 end
              end
              bite = field(this,am,rest,numb)
              if source.control then
                 dict.flag = odpt
                 file.name = ofpt
                 record.name = onid
                 record.data = bite
                 caller = '3'
                 write.allowed = '1'
                 updated = '0'
                 call source.control(dict.flag,file.name,
                 record.name,record.data,caller,write.allowed,updated)
                 if write.allowed ne '1' then
                    crt 'WRITE NOT ALLOWED'
                    return
                 end
              end
$ifdef werrorworks
              write bite on ofil, onid on error crt 'FAILURE'; return
$else
              write bite on ofil, onid
$endif
              oops = this ; oopc = comi ; oopl = here ; oopf = last
              oopb = beg:am:fin
              if comi = comd then beg = 0; fin = 0
              begin case
                 case rest = 1
                    this = field(this,am,numb+1,last)
                 case rest+numb ge last
                    this = field(this,am,1,rest-1)
                 case 1
                    bite = field(this,am,rest+numb,last)
                    this = field(this,am,1,rest-1):am:bite
              end case
              begin case
                 case here ge rest+numb; here = here - numb
                 case here ge rest; here = rest
              end case
              if here lt 1 then here = 1
              gosub set.record
              if beg gt last then beg = last
              if fin gt last then fin = last
              crt 'Cut ':numb:' lines starting at line ':rest
              gosub display.line
           end else
              crt 'Nothing done - no lines selected.'
              comi = '' ; return
           end
           bite = ''; temp = ''
        case 1 ; gosub bad.command
     end case
     return

d:   begin case
        case comd = 'D'                 ; * display current line
           if here gt last then here = last
           gosub display.line
        case comd = 'DE'                ; * delete lines
           if viewflag then gosub viewonly ; return
           chng = 0 ; save = here ; savl = last
           if rest ne '' then
              gosub parse.cols
              if not(good) then return
           end
           gosub set.bounds
* Quicker and also copes with CHAR(255)
           gosub delete.lines
           here = bot
           if here gt last then
              here = last
              crt 'Bottom. Line ':here:' was above the last delete.'
           end else
              crt 'At line ':here:'. Deleted ':chng:' lines.'
              gosub display.line
           end
$ifdef qm
        case comd = 'DISPLAY'     ; * insert display line for programmer
           if viewflag then gosub viewonly ; return
           if rest = '' then
              crt 'You have not said what to put on DISPLAY line'
              comi = ''
              return
           end
           oops = this ; oopc = comi ; oopl = here ; oopf = last
           oopb = beg:am:fin
           here = here + 1 ; last = last + 1 ; lnum = lnum + 1
           if dlim ne '"' and dlim ne '\' then dlim = "'"
           line = 'DISPLAY ':dlim:rest:' = ':dlim:':':rest
           gosub insert.line
           gosub reset.record
           gosub display.line
$endif
        case comd = 'DROP'              ; * remove the block
           if viewflag then gosub viewonly ; return
           if not(beg) and not(fin) then
              crt nb ; gosub bad.comd ; return
           end
           if beg le 1 then
              temp = 0
           end else
              temp = index(this,am,beg-1)
              if not(temp) then
                 crt 'Cannot find beginning of block'
                 gosub bad.comd ; return
              end
           end
           if fin = last then
              temp = temp - 1
              temp<2> = len(this)
           end else
              temp<2> = index(this,am,fin)
           end
           if not(temp<2>) then
              crt 'Cannot find end of block'
              gosub bad.comd ; return
           end
           oops = this ; oopc = comi ; oopl = here ; oopf = last
           oopb = beg:am:fin
           last = last - fin + beg - 1
           begin case
              case here le beg
              case here le fin
                 here = beg
              case 1
                 here = here - fin + beg - 1
           end case
           beg = 0 ; fin = 0
           this = this[1,temp<1>]:this[temp<2>+1,len(this)]
           gosub set.record
           gosub display.line
        case comd = 'DTX'               ; * decimal to hex
           if not(rest matches '1N0N') then
              crt 'Must be numeric'
              gosub bad.comd ; return
           end
           if len(rest) gt 9 then
              crt 'Must be less than 1,000,000,000'
              gosub bad.comd ; return
           end
$ifdef universe
           crt dtx(rest)
$else
           crt oconv(rest,'MX')
$endif
        case comd = 'DUP'               ; * duplicate previous line
           if viewflag then gosub viewonly ; return
           if here lt 1 then
              crt 'Nothing done - I abhor duplicating vacuums.'
              gosub bad.comd ; return
           end
           if numb = '' and rest matches '1N0N' then numb = rest
           if numb lt 1 then numb = 1
           oops = this ; oopc = comi ; oopl = here ; oopf = last
           oopb = beg:am:fin
           gosub get.line
           for xx = 1 to numb
              gosub insert.line
              last = last + 1
           next xx
           gosub reset.record
           crt 'Inserted ':numb:' copies of line ':here:
           crt ' after line ':here:'. Still at ':here:'.'
        case 1 ; gosub bad.command
     end case
     return

e:   begin case

case comd = 'EDITPAGE'
if not(pageedit) then gosub bad.command; return
if editpage then
   editpage = false
   begn = char(13); ceop = ''; ceol = ''; goup = ''
end else
   editpage = true
   begn = @(0) ; ceop = @(-3) ; ceol = @(-4) ; goup = @(-10)
end
crt 'EDITPAGE = ':editpage

        case comd = 'EC'        ; * edit called program (in this file)
           if here lt 1 then here = 1
           gosub get.line
           line = trim(line)
           temp = oconv(line,'MCU')
           good = index(line,'CALL ',1)
           if good then line = trim(line[good+5,99999])
           line = trim(field(line,'(',1))
           if index(line,' ',1) then good = false
           if line[1,1] = '@' then
              crt 'Leading "@" is logical pointer'
              good = false
           end
           if not(good) then
              crt 'The EC command requires lines in format ':
              crt '"CALL ID" or "CALL ID(..."'
              gosub bad.comd ; return
           end
           readv temp from file, line, 1 else
              crt '"':line:'" is not in this file'
              gosub bad.comd ; return
           end
           execute verb:' ':fnam:' ':line
           if scrset then test = @(0,0)
           crt 'Back editing record "':item:'" in file "':fnam:'"'
        case comd = 'EF'                ; * edit fields
           numb = numb + 0
           if numb lt 0 or numb gt 255 then
              crt numb:' is outside range 0-255'
              comi = ''
              return
           end
           vmrk = char(numb)
           vals = 'CHAR_':numb
           gosub edit.fields
           vmrk = char(numb); gosub reset.fields
        case comd = 'EI'                ; * edit included code
           if here lt 1 then here = 1
           gosub get.line
           line = field(line,';',1)
           line = trim(line)
           good = true
           temp = field(line,' ',1)
           temp = oconv(temp,'MCU')
           if temp ne 'INCLUDE' and temp ne '$INCLUDE' then good = false
           line = trim(line[len(temp)+1,len(line)])
           begin case
              case dcount(line,' ') gt 3 ; good = false
              case dcount(line,' ') eq 3
                 if field(line,' ',1) ne 'DICT' then good = false
              case dcount(line,' ') eq 1
                 line = fnam:' ':line
           end case
           if not(good) then
              crt 'The EI command requires lines in format ':
              crt '"$IN... {DICT} {FN} ID"'
              gosub bad.comd ; return
           end
           execute verb:' ':line
           if scrset then test = @(0,0)
           crt 'Back editing record "':item:'" in file "':fnam:'"'
        case comd = 'EIT'                ; * edit i-types (@)
           gosub get.line ; temp = line
           gosub split.itype
           ttid = whom:'_':levl:'_IType.in.line#':here
           write bite on acom, ttid on error crt 'FAIL' ; return
           crt 'editing IType as fields...':
           execute verb:' AE_COMS ':ttid
           if scrset then test = @(0,0)
           crt 'Back editing record "':item:'" in file "':fnam:'"'
           read line from acom, ttid else line = ''
           delete acom, ttid
           vmrk = ';'; gosub reset.fields
        case comd = 'EPR'               ; * edit prestores
           ttid = whom:'_':levl:'_prestores'
           temp = raise(pres)
$ifdef werrorworks
           write temp on acom, ttid on error crt 'FAIL' ; return
$else
           write temp on acom, ttid
$endif
           crt 'editing prestores':
           execute verb:' AE_COMS ':ttid
           if scrset then test = @(0,0)
           crt 'Back editing record "':item:'" in file "':fnam:'"'
           read temp from acom, ttid else temp = ''
           pres = lower(temp)
           delete acom, ttid
        case comd = 'ESV'               ; * edit subvalues
           vmrk = sm ; vals = 'subvalues'
           gosub edit.fields
           vmrk = sm; gosub reset.fields
        case comd = 'EV'                ; * edit values
           vmrk = vm ; vals = 'values'
           gosub edit.fields
           vmrk = vm; gosub reset.fields
        case comd = 'EW'     ; * edit words (as defined by wordmark)
           vmrk = wordmark ; vals = 'words'
           gosub edit.fields
           vmrk = wordmark; gosub reset.fields
* Various forms for quitting
        case comd = 'EX' or comd = 'EXIT' ; comd = 'Q' ; go q
        case comd = 'EXK' or comd = 'EXITK' ; comd = 'QK' ; go q
        case 1 ; gosub bad.command
     end case
     return

f:   begin case
        case comd = 'FD'                ; * delete item
           if viewflag then gosub viewonly ; return
           if not(sec.delete.flg) then
              crt 'Delete disabled'
              comi = ''
              return
           end
           gosub write.record
        case comd = 'FILE' ; comd = 'SV' ; go s
        case comd[1,2] = 'FI'           ; * file item
           if viewflag then gosub viewonly ; return
           if not(sec.write.flg) then
              crt 'File disabled'
              comi = ''
              return
           end
           temp = comd[3,len(comd)]
$ifdef changeworks
           convert 'BCRDL' to '' in temp
$else
           chit = temp ; new1 = ''
           old1 = 'B' ; gosub changeit
           old1 = 'C' ; gosub changeit
           old1 = 'R' ; gosub changeit
           old1 = 'D' ; gosub changeit
           old1 = 'L' ; gosub changeit
           temp = chit
$endif
           if temp = '' then gosub write.record else gosub bad.command
        case comd = 'FL' or comd = 'FLA'  ; * find labels
           if not(caseflag) then rest = oconv(rest,'MCU')
           if index(comd,'A',1) then
              bump = -1
              bot = here - 1
              if bot lt 1 then return
              if numb then msup = here - numb else msup = 1
              if msup lt 1 then msup = 1
           end else
              bump = 1
              bot = here + 1
              if bot gt last then bot = 1
              if numb then msup = here + numb else msup = last
              if msup gt last then msup = last
           end
           for here = bot to msup step bump
              gosub get.line
$ifdef trimfbworks
              temp = field(trimf(line),' ',1)
$else
              chit = line ; gosub trimfit ;  temp = field(chit,' ',1)
$endif
              if temp matches '1N0N"*"0X' then
                 temp = field(temp,'*',1)
              end
              if temp matches '1N0N"!"0X' then
                 temp = field(temp,'!',1)
              end
              bite = index(temp,':',1)
              if bite then
                 bit = temp[bite+1,1]
                 if bit = ';' or bit = '*' or bit = '!' then
                    temp = temp[1,bite]
                 end
              end
              begin case
                 case temp = ''
                 case num(temp)
                 case temp[len(temp),1] = ':'
                    temp = temp[1,len(temp)-1]
                 case 1
                    temp = ''
              end case
              if not(caseflag) then temp = oconv(temp,'MCU')
              if temp ne '' then
                 if rest = '' or temp matches rest then
                    gosub display.line
                    if not(numb) then return
                 end
              end
           next here
           crt
           gosub display.line
        case comd = 'FM' or comd = 'FMA'       ; * find match command
           gosub get.line
           word = field(trim(oconv(line,'MCU')),' ',1)
           begin case
              case rest ne ''
                 seek = rest
              case word[1,1] = '*' or word[1,1] = '!'
                 seek = word[1,1]
              case 1
                 locate(word,fm.words;posn) then
                    if index(comd,'A' ,1) then
                       seek = fm.finda
                    end else
                       seek = fm.findf
                    end
                 end else
                    locate(word,endwords;posn) then
                       seek = 'END'
                    end else
                       crt 'Starting word unknown'
                       gosub bad.comd ; return
                    end
                 end
           end case
           if seek = '' then
              crt word:' has no matching word for ':comd
              gosub bad.comd ; return
           end
           posn = index(oconv(line,'MCU'),word,1)
           xxno = dcount(seek,vm)
           for xx = 1 to xxno
              seek<1,xx> = space(posn-1):seek<1,xx>
           next xx
           if index(comd,'A',1) then
              bump = -1
              bot = here - 1
              if bot lt 1 then return
              msup = 1
           end else
              bump = 1
              bot = here + 1
              if bot gt last then return
              msup = last
           end
           save = here
           for here = bot to msup step bump
              gosub get.line
              line = oconv(line,'MCU')
              if line[1,1] ne '' then
                 temp = field(line,' ',1)
                 if num(temp) or temp[len(temp),1] = ':' then
                    temp = len(temp)
                    line = space(temp):line[temp+1,len(line)]
                 end
              end
              for xx = 1 to xxno
                 slen = len(seek<1,xx>)
                 if line[1,slen] = seek<1,xx> then
                    if trim(line[slen+1,1]) = '' then
                       gosub display.line
                       return
                    end
                 end
              next xx
           next here
           here = save
           gosub get.line
        case comd = 'FOLD'              ; * fold the line
           if viewflag then gosub viewonly ; return
           chng = 0 ; save = here ; savl = last ; show = true
           if dlim ne '' then fold = ''
           if rest = '' then rest = fold
           if rest = '' then rest = wide-llen-2
           if not(rest matches '1N0N') then
              crt 'Non-numeric length - try HELP FOLD.'
              comi = ''
              return
           end
           fold = rest
           gosub get.line
           crt 'FOLD line to length ':fold
* In UniVerse, we COULD replace the next two lines with
*           TEMP = FOLD(LINE,FOLD)
* but then I'd need another flag, a nested one at that to cope with TRIMF
* and since I already wrote the code...
           bite = line
           gosub parse.bite
           gosub check.line
           if chng then
              gosub change.made
           end
        case comd = 'FOR' or comd = 'FORMAT'      ; * format this item
           rest = oconv(rest,'MCU')
           temp = index(rest,'-M',1)
           if temp then fr(1) = field(rest[temp+2,99],' ',1) ; fr(2) = ''
           if not(fr(1) matches '1N0N') then fr(1) = ''
           if fr(1) = '' then
              temp = this<1>
$ifdef trimfbworks
              fr(1) = len(temp) - len(trimf(temp))
$else
              chit = temp ; gosub trimfit
              fr(1) = len(temp) - len(chit)
$endif
           end
           temp = index(rest,'-I',1)
           if temp then fr(2) = field(rest[temp+2,99],' ',1)
           if not(fr(2) matches '1N0N') then fr(2) = ''
           if fr(2) = '' then
              fr(2) = int((fr(1)+1)/2)
              if fr(2) lt 2 then fr(2) = 2
           end
           if index(rest,'-A',1) then fr(9) = true else fr(9) = ''
           if index(rest,'-N',1) then fr(10) = true else fr(10) = ''
           if index(rest,'-C',1) then
              fr(1) = 0
              fr(2) = 1
              fr(9) = true
              fr(10) = true
           end
           if last lt 1 then return
           oops = this ; oopc = comi ; oopl = here ; oopf = last
           oopb = beg:am:fin
           crt 'Margin=':fr(1):', Indentation=':fr(2)
*           FORMAT.BASIC = 'INDENTER'
*           CALL @FORMAT.BASIC(THIS,LAST,MAT FR)
           gosub indenter
           gosub set.record
        case 1 ; gosub bad.command
     end case
     return

g:   begin case
        case comd = 'G'                 ; * go to line
           if numb ne '' then here = numb
           if here gt last then here = last
           gosub display.line
        case 1 ; gosub bad.command
     end case
     return

h:   begin case
        case comd = 'H' or comd = 'HELP'
           gosub show.help
        case comd = 'HEX'            ; * show this line in hexadecimal
           if not(here) then return
           gosub get.line
           temp = ''
           xxno = len(line)
           for xx = 1 to xxno
              bit = line[xx,1]
$ifdef universe
              bit = dtx(seq(bit))
$else
              bit = oconv(seq(bit),'MX')
$endif
              bit = fmt(bit,prfx:'2':sufx)
              temp<1> = temp<1>:bit[1,1]
              temp<2> = temp<2>:bit[2,1]
           next xx
           if lfmt then crt fmt(here,lfmt):': ':
           crt temp<1>
           if lfmt then crt space(llen+2):
           crt temp<2>
           temp = ''
        case 1 ; gosub bad.command
     end case
     return

i:   begin case
        case comd = 'I'                 ; * insert lines
           if viewflag then gosub viewonly ; return
           chng = 0 ; save = here ; savl = last
           if rest ne '' then
              if numb lt 1 then numb = 1
              inum = numb
              if here gt 0 then
                 memr(cell) := str(am:rest,inum)
              end else memr(1)<1> = str(rest:am,inum):memr(1)<1>
              here = here + inum
              gosub change.made
              gosub get.line
              gosub display.line
              crt 'At line ':here:'. ':inum:' lines inserted,':
              crt ' bottom now at line ':last:'.'
           end else
              if nill ne '' then
                 crt 'Terminate input with ':nill
              end
              loop
                 new1 = here + 1
                 stub = new1:'= '
                 if lfmt then stub = fmt(new1,lfmt):'='
                 gosub get.rope; line = rope
              until line = nill do
                 gosub parse.line
                 last = last + 1
                 here = here + 1
                 lnum = lnum + 1
                 chng = chng + 1
                 gosub insert.line
                 temp = len(last)
                 if lfmt then
                    if temp gt 3 and temp ne llen then gosub get.lfmt
                 end
                 if line = nill then
                    crt begn:
                    if lfmt then crt fmt(new1,lfmt):'= ':
                 end
                 crt
                 numb = numb - 1
                 if numb = 0 then exit
              repeat
              crt begn:ceol:
           end
           if chng then gosub change.made
        case comd = 'IC'                ; * iconv
           if viewflag then gosub viewonly ; return
           if rest = '' then
              crt 'No conversion given'
              gosub bad.comd ; return
           end
           ccom = '*':rest ; gosub conv.command
        case comd = 'IN'                ; * insert from execute
           if viewflag then gosub viewonly ; return
           if trim(rest) = '' then
              crt 'No external command given'
              comi = '' ; return
           end
           execute rest capturing bite
           if scrset then test = @(0,0)
           numb = dcount(bite,am)
           if numb then
              oops = this ; oopc = comi ; oopl = here ; oopf = last
              oopb = beg:am:fin
              this = insert(this,here+1,0,0,bite)
              gosub set.record
              crt 'Inserted ':numb:' lines':
              crt '; still at line ':here:'.'
           end else
              crt 'Nothing done - no output from command.'
              comi = '' ; return
           end
        case 1 ; gosub bad.command
     end case
     return

j:   begin case
        case comd = 'J'                 ; * join lines
           if viewflag then gosub viewonly ; return
           if dlim ne '' then
              line = rest ; gosub parse.line ; join = line
           end
           if here and here lt last then
              chng = 0 ; save = here ; savl = last
              gosub get.line
              test = line
              here = here + 1
              gosub set.bounds
              for here = bot to msup
                 gosub get.line
                 test = test:join:line
              next here
              gosub delete.lines
              if chng = 0 then return
              here = save
              oopl = here
              gosub get.line
              memr(cell) = test
              gosub reset.record
           end
           gosub display.line
        case 1 ; gosub bad.command
     end case
     return

k:   begin case
        case 1 ; gosub bad.command
     end case
     return

l:   begin case
* List lines or Locate string
        case comd = 'L' or comd = 'LN' or comd = 'LA' or comd = 'LNA'
           if oconv(comi,'MCU') = 'L' then
              if look<1,1> = '' then
                 crt 'No previous locate command to repeat.'
                 comi = ''
                 return
              end
              comi = look<1,1>
              gosub parse.command
              if comd = '' then comd = 'L' ; numb = 99999999
              go l
           end
           if rest ne '' then
              gosub parse.cols
              if not(good) then return
           end else
              if dlim ne '' then
                 crt 'The second field is empty.'
                 gosub bad.comd ; return
              end
              cols = ''
           end
           if index(comd,'A',1) then
              bump = -1
              bot = here - 1
              if bot lt 1 then bot = 1
              if numb then msup = here - numb + 1 else msup = 1
              if msup lt 1 then msup = 1
           end else
              bump = 1
              bot = here + 1
              if bot gt last then bot = 1
              if numb then msup = bot + numb - 1 else msup = last
              if msup gt last then msup = last
           end
           line = rest ; gosub parse.line ; rest = line
           if not(caseflag) then rest = oconv(rest,'MCU')
           if not(spaceflag) then
$ifdef changeworks
              convert ' ':char(9) to '' in rest
$else
              chit = rest; old1 = ' '; new1 = ''
              gosub changeit ; old1 = char(9)
              gosub changeit ; rest = chit
$endif
           end
           for here = bot to msup step bump
              gosub get.line
              if cols then line = line[cols,colf]
              if caseflag then temp = line else temp = oconv(line,'MCU')
              if not(spaceflag) then
$ifdef changeworks
                 convert ' ':char(9) to '' in temp
$else
                 chit = temp; old1 = ' '; new1 = ''
                 gosub changeit ; old1 = char(9)
                 gosub changeit ; temp = chit
$endif
              end
              if index(temp,rest,1) then
                 if not(index(comd,'N',1)) then
                    gosub display.line
                    if not(numb) then exit
                 end
              end else
                 if index(comd,'N',1) then
                    gosub display.line
                    if not(numb) then exit
                 end
              end
           next here
           if numb then here = msup
           crt 'Now at line ':here:
           if here = last then crt ' (bottom)':
           crt '.'
           if rest ne '' and comi ne '' and comi ne look<1,1> then
              look = insert(look,1,1,0,comi)
              loop
              while dcount(look,vm) gt looknumb do
                 look = delete(look,1,looknumb,0)
              repeat
           end
        case comd = 'LC'                ; * lower case (make line in)
           if viewflag then gosub viewonly ; return
           if rest = '' then ccom = 'MCL' else ccom = 'QMCL'
           gosub conv.command
* Various forms for loading stuff
        case comd = 'LD' or comd= 'LOAD' or comd= 'LDA' or comd= 'LOADA'
           if viewflag then gosub viewonly ; return
           if not(sec.load.flg) then
              crt 'LOAD disabled'
              comi = ''
              return
           end
           gosub get.load
           if temp = '' then return
           if prepflag then
              sec.call2.type = 1
              sec.fn2 = ofpt
              sec.id2 = oipt
              sec.dict2.flg = (odpt = 'DICT')
              call @prepprog(mat security)
              if sec.stop.flg then stop
              if not(sec.ok2.flg) then
                 gosub bad.comd ; return
              end
           end
           if comd[len(comd),1] ne 'A' then
              gosub get.lines
              if not(temp) then return
           end
           oops = this ; oopc = comi ; oopl = here ; oopf = last
           oopb = beg:am:fin
           this = insert(this,here+1,0,0,temp)
           here = here + dcount(temp,am)
           crt 'At line ':here:', ':dcount(temp,am):' lines loaded.'
           temp = ''
           gosub set.record
        case comd = 'LL'                ; * long lines
           if not(rest matches '1N0N') then
              rest = wide-llen-2
              crt 'LL':numb:'/':rest
           end
           bot = here + 1
           if bot gt last then bot = 1
           if numb then msup = here + numb else msup = last
           if msup gt last then msup = last
           for here = bot to msup
              gosub get.line
              temp = trim(line[rest,999])
              if temp ne '' then
                 gosub display.line
                 if not(numb) then return
              end
           next here
           crt
           gosub display.line
        case 1 ; gosub bad.command
     end case
     return

m:   begin case
        case comd = 'M'         ; * pattern matching
           rest = field(rest,dlim,1)
           if rest = '' then crt 'No pattern given to match'; return
           gosub changematch.command
        case comd = 'MACRO'
           if macn then
              temp = dcount(macc<1,1>,sm)
              macc = delete(macc,1,1,temp)
              macc = delete(macc,1,1,temp)
              if macc ne '' then
                 pres<1,macn> = macc<1,1>
                 crt 'Macro saved to PRestore ':macn
              end else
                 crt 'Macro empty - not saved'
              end
              macc = ''
              macn = 0
           end else
              if numb = '' then numb = 1
              if numb gt presnumb or numb lt 1 then
                 crt 'PRestore must be in range 1-':presnumb:'.'
                 comi = ''
                 return
              end
              crt 'Macro being recorded for PRestore ':numb
              macn = numb
           end
        case comd = 'ME'                ; * merge stuff from this file
           if viewflag then gosub viewonly ; return
merge:     rest = trim(rest)
           if numb = '' then
              if dlim = '"' then temp = "'" else temp = '"'
              temp = '1N0N':temp:dlim:temp:'1N0N'
              if rest matches temp then
                 numb = field(rest,dlim,2) - field(rest,dlim,1) + 1
                 rest = field(rest,dlim,1)
              end
           end
           if not(rest matches '1N0N') or numb = '' then
              crt 'Format of MErge command is: "MEn/n"; eg: "ME10/15"':
              crt ' or "ME/n/m"; eg: "ME/15/24".'
              gosub bad.comd ; return
           end
           if numb gt last then
              crt 'Nothing done - record does not have that many lines.'
              comi = '' ; return
           end
           bite = field(this,am,rest,numb)
           if numb ne 1 or bite ne '' then numb = dcount(bite,am)
           if numb then
              oops = this ; oopc = comi ; oopl = here ; oopf = last
              oopb = beg:am:fin
              this = insert(this,here+1,0,0,bite)
              gosub set.record
              crt 'Merged ':numb:' lines starting at line ':rest:
              crt '; still at line ':here:'.'
           end else
              crt 'Nothing done - no lines selected.'
              comi = '' ; return
           end
        case comd = 'MOVE'              ; * move the block
           if viewflag then gosub viewonly ; return
           if not(beg) and not(fin) then
              crt nb ; gosub bad.comd ; return
           end
           if here le fin and here ge beg then
              crt 'A block may not be moved into itself. Copy will work.'
              comi = ''
              return
           end
           numb = fin - beg + 1
           crt 'Lines ':beg:' to ':fin:' (':numb:
           crt ') will be Copied (and deleted).'
           stub = 'You are now at line ':here:'. Do it? n/y > '
           gosub get.rope; answ = rope; crt
           answ = oconv(trim(answ),'MCU')[1,1]
           if answ ne 'Y' then
              crt 'Block command canceled.'
              return
           end
           dlim = ' '
           rest = beg:' ':fin
           numb = ''
           gosub move
           if numb then
              beg = here + 1
              fin = here + numb
           end
        case comd = 'MV'                ; * move stuff from this file
           if viewflag then gosub viewonly ; return
move:      rest = trim(rest)
           if numb = '' then
              if dlim = '"' then temp = "'" else temp = '"'
              temp = '1N0N':temp:dlim:temp:'1N0N'
              if rest matches temp then
                 numb = field(rest,dlim,2) - field(rest,dlim,1) + 1
                 rest = field(rest,dlim,1)
              end
           end
           if not(rest matches '1N0N') or numb = '' then
              crt 'Format of MoVe command is: "MVn/n"; eg: "MV10/15"':
              crt ' or "MV/n/m"; eg: "MV/15/24".'
              gosub bad.comd ; return
           end
           msup = rest + numb - 1
           if msup gt last then msup = last
           if here ge rest and here le msup then
              crt 'Nothing done - This line is within the range.'
              comi = '' ; return
           end
           bite = field(this,am,rest,numb)
           if numb ne 1 or bite ne '' then numb = dcount(bite,am)
           if numb then
              oops = this ; oopc = comi ; oopl = here ; oopf = last
              oopb = beg:am:fin
              if here gt msup then
                 this = insert(this,here+1,0,0,bite)
                 this = this[1,col1()-1]:this[col2(),len(this)]
              end else
                 this = this[1,col1()-1]:this[col2(),len(this)]
                 this = insert(this,here+1,0,0,bite)
              end
              gosub set.record
              crt 'Moved ':numb:' lines starting at line ':rest:
              if here gt msup then
                 here = here - numb
                 crt '; now at line ':here:'.'
              end else
                 crt '; still at line ':here:'.'
              end
           end else
              crt 'Nothing done - no lines selected.'
              comi = '' ; return
           end
        case 1 ; gosub bad.command
     end case
     return

n:   begin case
        case comd = 'N'                 ; * same as "+"
           if numb = '' then numb = 1
           here = here + numb
           if here gt last then here = last
           gosub display.line
        case comd = 'NULL'              ; * null line input definition
           dlim = trim(dlim):trim(rest)
           dlim = dlim[1,1]
           nill = dlim
           crt 'NULL to terminate Insert is ':
           if nill = '"' then
              crt "'":nill:"'"
           end else crt '"':nill:'"'
           comi = ''
        case comd = 'NUM'               ; * toggle the line numbering
           crt 'Line numbering ':
           if lfmt = '' then
              crt 'ON'
              gosub get.lfmt
           end else crt 'OFF' ; lfmt = ''
        case 1 ; gosub bad.command
     end case
     return

o:   begin case
        case comd = 'OC'                ; * oconv
           if viewflag then gosub viewonly ; return
           if rest = '' then
              crt 'No conversion given'
              gosub bad.comd ; return
           end
           ccom = rest ; gosub conv.command
        case comd[1,2] = 'OO'           ; * undo last change
           if oopc ne '' then
              this = oops
              here = oopl
              last = oopf
              beg = oopb<1>
              fin = oopb<2>
              gosub set.record
              crt '"':oopc:'" undone - now at line ':here:'.'
              oops = '' ; oopc = '' ; oopl = '' ; oopf = ''
              oopb = ''
              gosub display.line
           end else
              crt 'last change already "undone" or nothing to undo'
           end
           comi = ''
        case 1 ; gosub bad.command
     end case
     return

p:   begin case
        case comd = 'P'                 ; * page on
           if numb then plen = numb
           numb = plen
           if here ge last then here = 0
           here = here + 1
           gosub set.bounds
           for here = bot to msup
              gosub display.line
           next here
           here = msup
        case comd = 'PA'                ; * print window up to here
           if numb then pwin = numb
           numb = int(pwin)
           save = here
           here = here - numb
           gosub set.bounds
           for here = bot to msup
              gosub display.line
           next here
           here = save
           crt 'Still at ':here:'.'
        case comd = 'PE'                ; * page editor mode
           if not(editpage) then
              crt 'Page editing not possible at this terminal'
              comi = ''
              return
           end
           that = this ; savl = here:am:last
           if this = '' then this = am
           if here lt 1 then here = 1
           ptop = here
           mode = 'PAGE':am:'View'
           if sec.write.flg then mode<2> = 'Ins'
           pchr = 1
           chng = '' ; show = ''
           gosub display.page
        case comd = 'PL'                ; * print window from here
           if numb then pwin = numb
           numb = int(pwin)
           save = here
           gosub set.bounds
           for here = bot to msup
              gosub display.line
           next here
           here = save
           crt 'Still at ':here:'.'
        case comd = 'PP'                ; * print window bracketing here
           if numb then pwin = numb
           numb = pwin
           save = here
           here = here - int(pwin/2)
           gosub set.bounds
           for here = bot to msup
              gosub display.line
           next here
           here = save
           crt 'Still at ':here:'.'
        case comd = 'PR'                ; * prestore processing
           if numb = '' then
              crt 'Defined prestores'
              for xx = 1 to presnumb
                 crt fmt(xx,prfx:'2':sufx):' ':pres<1,xx>
              next xx
              return
           end
           if numb gt presnumb or numb lt 1 then
              crt 'PRestore must be in range 1-':presnumb:'.'
              comi = ''
              return
           end
           if dlim ne '' then
              if not(rest = rest<1,1,1>) then
                 crt 'Invalid - delimiter in prestore'
                 comi = ''
                 return
              end
$ifdef changeworks
              pres<1,numb> = change(rest,dlim,sm)
$else
              old1 = dlim ; new1 = sm ; chit = rest
              gosub changeit ; pres<1,numb> = chit
$endif
           end else
              salt = pres<1,numb>
           end
        case 1 ; gosub bad.command
     end case
     return

q:   begin case
* Various forms for quitting
        case comd = 'Q' or comd = 'QK' or comd = 'QUIT' or comd = 'QUITK'
           if orig ne this then
              stub = '***** Record changed --- OK to Quit? n/y > '
              gosub get.rope; answ = rope; crt
              answ = oconv(trim(answ),'MCU')[1,1]
              if answ = 'Y' then stopsign = true
           end else stopsign = true
           if stopsign then
              crt 'Quit "':item:'" in file "':fnam:'" ':
              if orig = '' then crt 'not created.' else crt 'unchanged.'
              if index(comd,'K',1) then
                 killsign = true
                 if idcnt gt 1 then crt 'Select list canceled.'
              end
           end
        case 1 ; gosub bad.command
     end case
     return

r:   begin case
        case comd = 'RA'
           if viewflag then gosub viewonly ; return
           gosub change.command
        case comd = 'R' and dlim ne '' and index(rest,dlim,1) ; * change
           if viewflag then gosub viewonly ; return
           crt ; comd = 'C'
           gosub change.command
        case comd = 'R'                 ; * replace lines
           if viewflag then crt ; gosub viewonly ; return
           if not(last) then
              crt 'Empty record, use Insert (I).'
              comi = ''
              return
           end
           if here lt 1 then here = 1 ; gosub display.line
           chng = 0 ; save = here ; savl = last
           if numb lt 1 then numb = 1
           if dlim ne '' and rest = '' then rest = ' '
           loop
              crt begn:
              if lfmt then crt fmt(here,lfmt):'= ':
              crt ceop:
              if rest = '' then
                 stub = here:'= '
                 if lfmt then stub = fmt(here,lfmt):'='
                 gosub get.rope; line = rope
              end else line = rest
              gosub parse.line
           until line = '' do
              crt goup:begn:ceol:
              if lfmt then crt fmt(here,lfmt):': ':
              crt line
              if line = comdmark then
                 line = ''
                 crt begn:
                 if lfmt then crt fmt(here,lfmt):': ':
              end
              if numb gt 1 then crt
              if line = ' ' then line = ''
              chng = chng + 1
              memr(cell) = line
              here = here + 1 ; numb = numb - 1
              gosub get.line
              if numb = 0 then exit
           repeat
           if here ne save then here = here - 1
           if chng then
              gosub change.made
              gosub get.line
           end
           crt begn:ceol:
        case comd = 'RELEASE'           ; * release the item lock
           release file,item
           lock = false
        case 1 ; gosub bad.command
     end case
     return

s:   begin case
        case comd = 'S'                 ; * search processing
           if numb = '' then
              crt 'Last ':looknumb:' searches (latest first)'
              for xx = 1 to looknumb
                 crt fmt(xx,prfx:'2':sufx):' ':look<1,xx>
              next xx
              return
           end
           if numb gt looknumb or numb lt 1 then
              crt 'Search must be in range 1-':looknumb:'.'
              comi = ''
              return
           end
           comi = look<1,numb>
           if comi = '' then
              crt 'There is no search ':numb
              return
           end
           look = delete(look,1,numb,0)
           look = insert(look,1,1,0,comi)
           gosub parse.command
           if comd = '' then comd = 'L' ; numb = 99999999
           comi = ''
           go l
        case comd = 'SAVE' or comd = 'SV'         ; * save the item
           if viewflag then gosub viewonly ; return
           comd = 'SV'
           if rest = '' then
              if not(sec.write.flg) then
                 crt 'File disabled'
                 comi = ''
                 return
              end
              gosub write.record
           end else
              if not(sec.unload.flg) then
                 crt 'Unload disabled'
                 comi = ''
                 return
              end
              keepquot = false
              gosub parse.rest
              odpt = '' ; ofpt = bite<1> ; oipt = bite<2>
              onam = ofpt
              if ofpt = 'DICT' then
                 odpt = ofpt ; ofpt = oipt ; oipt = bite<3>
                 onam = onam:' ':ofpt
              end
              if oipt = '' then
                 if odpt ne '' then
                    crt 'Cannot save to null item.'
                    gosub bad.comd ; return
                 end
                 oipt = ofpt ; odpt = dprt ; ofpt = fprt ; onam = fnam
              end
              if dprt = odpt and fprt = ofpt then
                 ofil = file
              end else
                 open odpt, ofpt to ofil else
                    crt 'File "':onam:'" was not found.'
                    gosub bad.comd ; return
                 end
              end
              if prepflag then
                 sec.call2.type = 2
                 sec.fn2 = ofpt
                 sec.id2 = oipt
                 sec.dict2.flg = (odpt = 'DICT')
                 call @prepprog(mat security)
                 if sec.stop.flg then stop
                 if not(sec.ok2.flg) then
                    gosub bad.comd ; return
                 end
              end
              if source.control then
                 dict.flag = odpt
                 file.name = ofpt
                 record.name = oipt
                 record.data = this
                 caller = '3'
                 write.allowed = '1'
                 updated = '0'
                 call source.control(dict.flag,file.name,
                 record.name,record.data,caller,write.allowed,updated)
                 if write.allowed ne '1' then
                    crt 'WRITE NOT ALLOWED'
                    return
                 end
              end
$ifdef werrorworks
              write this on ofil, oipt on error crt 'FAIL' ; return
$else
              write this on ofil, oipt
$endif
              crt 'Record "':oipt:'" saved in "':onam:'".'
           end
        case comd = 'SEQ'               ; * build a sequence
           if viewflag then gosub viewonly ; return
           if dlim = '' then
              crt 'Too few fields in this command.'
              gosub bad.comd ; return
           end
           good = true
           cfrom = field(rest,dlim,1)
           cto = field(rest,dlim,2)
           if cto = '' then cto = 1
           if not(num(cto)) then
              crt 'Base for sequence command must be a number.'
              good = false
           end
           bit = field(rest,dlim,3)
           if bit = '' then bit = 1
           if not(num(bit)) then
              crt 'Increment for sequence command must be a number.'
              good = false
           end else
              if not(bit) then
                 crt 'Increment for sequence command must not be zero.'
                 good = false
              end
           end
           if not(good) then gosub bad.comd ; return
           rest = dlim:field(rest,dlim,4)
           if rest ne dlim then
              gosub parse.cols
              if not(good) then return
           end else cols = ''
           chng = 0 ; save = here ; savl = last
           gosub set.bounds
           for here = bot to msup
              gosub get.line ; temp = line
              if cols then
                 bite = index(line[cols,colf],cfrom,1)
                 if bite then bite = bite + cols - 1
              end else
                 bite = index(line,cfrom,1)
              end
              if bite then
                 temp = line[1,bite-1]:cto
                 temp = temp:line[bite+len(cfrom),len(line)]
              end
              if '*':temp ne '*':line then
                 cto = cto + bit
                 chng = chng + 1
                 memr(cell) = temp
                 gosub display.line
              end
           next here
           here = msup
           if chng then gosub change.made
        case comd = 'SHOW'              ; * show changes flag
           shew = not(shew)
           crt 'SHOW changes flag is ':
           if shew then crt 'ON' else crt 'OFF'
        case comd = 'SORT'              ; * sort the block
           if viewflag then gosub viewonly ; return
           if not(beg) and not(fin) then
              crt nb ; gosub bad.comd ; return
           end
           if beg le 1 then
              temp = 0
           end else
              temp = index(this,am,beg-1)
              if not(temp) then
                 crt 'Cannot find beginning of block'
                 gosub bad.comd ; return
              end
           end
           rest = oconv(rest,'MCU')
           if rest = '' then rest = 'AL'
           if not(index('*AR*AL*DR*DL*','*':rest:'*',1)) then
              crt 'Invalid sort sequence - use "AL" "AR" "DL" or "DR"'
              gosub bad.comd ; return
           end
           temp<2> = index(this,am,fin)
           if fin = last then temp<2> = len(this)+1
           if not(temp<2>) then
              crt 'Cannot find end of block'
              gosub bad.comd ; return
           end
           oops = this ; oopc = comi ; oopl = here ; oopf = last
           oopb = beg:am:fin
           bits = ''
           for here = beg to fin
              gosub get.line
              locate(line,bits;posn;rest) else null
              bits = insert(bits,posn;line)
           next here
           here = oopl
           if fin ne last then
              this = this[1,temp<1>]:bits:am:this[temp<2>+1,len(this)]
           end else this = this[1,temp<1>]:bits
           bits = ''
           gosub set.record
           gosub display.line
        case comd = 'SPACE'               ; * change spacing flag for 'L'
           rest = oconv(rest,'MCU')
           begin case
              case rest = 'ON' ; spaceflag = true
              case rest = 'OFF' ; spaceflag = false
              case 1 ; spaceflag = not(spaceflag)
           end case
           crt 'SPACE flag is ':
           if spaceflag then crt 'ON' else crt 'OFF'
        case comd = 'SPOOL'             ; * print
           save = here
           if numb = '' and rest matches '1N0N' then numb = rest
           if numb = '' then here = 1 ; numb = last
           gosub set.bounds
           head = 'Record - ':item:' File - ':fnam
           head = head:' Account - ':acct:' ':timedate():"'LL'"
           temp = wide
           printer on
           temp = temp - llen - 2
           heading head
           for here = bot to msup
              gosub get.line
$ifdef changeworks
              convert badc to gudc in line
$else
              chit = line; gosub change.non.print; line = chit
$endif
              print fmt(here,lfmt):': ':line[1,temp]
              loop
                 line = line[temp+1,len(line)]
              until line = '' do
                 print space(llen+2):line[1,temp]
              repeat
           next here
           printer close
           if bot ne 1 or msup ne last then
              crt 'Lines ':bot:' to ':msup:' of ':
           end
           crt '"':item:'" spooled to the printer.'
           here = save
        case comd = 'SPOOLHELP'         ; * print the help
           rest = am
           gosub show.help
        case comd = 'STAMP'             ; * stamp it
           if viewflag then gosub viewonly ; return
           oops = this ; oopc = comi ; oopl = here ; oopf = last
           oopb = beg:am:fin
           last = last + 1 ; here = here + 1 ; lnum = lnum + 1
           line = '* Last updated by ':acct
           if acct ne name then line = line:' (':name:')'
           line = line:' at ':oconv(time(),'MTS')
           line = line:' on ':oconv(date(),'D'):'.'
           gosub insert.line
           gosub reset.record
           gosub display.line
        case 1 ; gosub bad.command
     end case
     return

t:   begin case
        case comd = 'T'                 ; * top
           here = 0
           gosub display.line
        case comd = 'TC'                ; * text case (make line in)
           if viewflag then gosub viewonly ; return
           if rest = '' then ccom = 'MCT' else ccom = 'QMCT'
           gosub conv.command
* Various ways to TRIM the line
        case comd = 'TRIM' or comd = 'TRIMF' or comd = 'TRIMB'
           if viewflag then gosub viewonly ; return
           chng = 0 ; save = here ; savl = last
           show = shew or index(rest,'S',1) or index(rest,'s',1)
           if numb lt plen then show = true
           if numb = '' and rest matches '1N0N' then numb = rest
           gosub set.bounds
           for here = bot to msup
              gosub get.line
              begin case
                 case comd = 'TRIM' ; temp = trim(line)
$ifdef trimfbworks
                 case comd = 'TRIMF' ; temp = trimf(line)
                 case comd = 'TRIMB' ; temp = trimb(line)
$else
                 case comd = 'TRIMF'
                    chit = line ; gosub trimfit ; temp = chit
                 case comd = 'TRIMB'
                    chit = line ; gosub trimbit ; temp = chit
$endif
              end case
              gosub check.line
           next here
           here = msup
           if chng then
              gosub change.made
              if not(show) then
                 crt chng:' lines changed - now at ':here
              end
           end
           return
        case 1 ; gosub bad.command
     end case
     return

u:   begin case
        case comd = 'U'                 ; * same as "-"
           if numb = '' then numb = 1
           here = here - numb
           if here lt 0 then here = 0
           if here gt last then here = last
           gosub display.line
        case comd = 'UC'                ; * upper case (make line in)
           if viewflag then gosub viewonly ; return
           if rest = '' then ccom = 'MCU' else ccom = 'QMCU'
           gosub conv.command
        case comd = 'UNLOAD' ; comd = 'SV' ; go s
           if viewflag then gosub viewonly ; return
        case 1 ; gosub bad.command
     end case
     return
v:   begin case
        case comd = 'V'                 ; * version information
           crt 'Line EDitor version ':help.def
$ifdef qm
           temp = trans('NEWVOC','$RELEASE',2,'X')
           if temp = '' then
              temp = trans('VOC','$RELEASE',2,'X')
           end
           if temp = '' then temp = 'not known'
           crt '         QM version ':temp
$endif
$ifdef unidata
           crt '    UniData version ':oconv('version','TVOC;X;;1')
$endif
$ifdef universe
           temp =  oconv('RELLEVEL','TNEWACC;X;;2')
           if temp = '' then
              temp = oconv('RELLEVEL','TVOC;X;;2')
           end
           if temp = '' then temp = 'not known'
           crt '   UniVerse version ':temp
        case comd = 'VLIST'             ; * view vlist for programmer
           crt 'Capturing VLIST data'
           execute comi capturing temp
           if temp<2> = '' then crt temp<1> ; return
           ttid = whom:'_':levl:'_VLIST'
$ifdef werrorworks
           write temp on acom, ttid on error temp = ''
           if temp = '' then crt 'FAIL' ; return
$else
           write temp on acom, ttid
$endif
           temp = ''
           crt 'editing VLIST data':
           execute verb:' AE_COMS ':ttid
           crt 'Back editing record "':item:'" in file "':fnam:'"'
           delete acom, ttid
$endif
        case 1 ; gosub bad.command
     end case
     return

w:   begin case
        case comd = 'W' or comd = 'WHERE'      ; * what we are editing
           crt
           if not(viewflag) then crt 'Editing ': else crt 'Viewing ':
           crt 'record "':item:'" in file "':fnam:'"  ':
           if idcnt gt 1 then crt '[':id:'/':idcnt:']':
           crt
           if here gt last then here = last
           gosub display.line
        case comd = 'WM'             ; * word marker display (change)
           if dlim = '' then
              crt 'WordMark is ':
           end else
              wordmark = dlim
              crt 'WordMark changed to ':
           end
           if wordmark = '"' then
              crt "'":wordmark:"'"
           end else crt '"':wordmark:'"'
        case 1 ; gosub bad.command
     end case
     return

x:   begin case
* Another way of quitting
        case comd = 'X' ; comd = 'QK' ; go q
        case comd = 'XEQ'               ; * execute a command
           if viewflag then gosub viewonly ; return
           if not(sec.xeq.flg) then
              crt 'XEQ disabled'
              comi = ''
              return
           end
           loop
              if rest ne '' then execute rest
              if scrset then test = @(0,0)
              stub = 'return or command :'
              gosub get.rope; rest = rope
              if rest = '' then
                 crt begn:'returned: editing "':item:'" in "':fnam:'"'
              end
           until rest = '' do
           repeat
           gosub display.line
        case comd = 'XTD'               ; * hex to decimal
$ifdef universe
           crt xtd(rest)
$else
           crt iconv(rest,'MX')
$endif
        case 1 ; gosub bad.command
     end case
     return

y:   begin case
        case 1 ; gosub bad.command
     end case
     return

z:   begin case
        case 1 ; gosub bad.command
     end case
     return

set.bounds:
     if numb = '' then numb = 1
     bot = here
     if bot lt 1 then bot = 1
     msup = bot + numb - 1
     if msup gt last then msup = last
     numb = 0
     return

null.command:
     if dlim = '/' then
        comd = 'L'
        if numb = '' then numb = 99999999
        return
     end
     if dlim = '-' or dlim = '+' then
        if rest = '' then rest = 1
     end
     if numb ne '' then comd = numb ; return
     if dlim = '' and rest = '' then
        here = here + 1
        if here gt last then here = 1
        gosub display.line
        return
     end
     crt
     begin case
        case dlim = '+' and rest matches '1N0N'
           here = here + rest
           if here gt last then here = last
           gosub display.line
        case dlim = '-' and rest matches '1N0N'
           here = here - rest
           if here lt 0 then here = 0
           if here gt last then here = last
           gosub display.line
        case dlim = '^'
           wild = not(wild)
           crt 'Unprintable characters ':
           if wild then crt 'shown.' else crt 'not shown.'
        case dlim = '='
           crt 'UNIDATA prestore is not implemented - Use "PR"'
        case dlim = '.'
           gosub dot.command
        case dlim = '$'
           if not(sec.xcom.flg) then
              crt '$ external commands disabled'
              comi = ''
              return
           end
           save = comi ; comi = rest
           gosub parse.command
           comi = save
           comd = '$':comd
           xcom = oconv(comd,'TAE_XCOMS;X;2;2')
           begin case
              case xcom = ''
                 crt 'Record "':comd:'" does not exist in "AE_XCOMS".'
              case xcom[len(xcom)-2,3] ne '_AE'
                 gosub bad.xcom
              case 1
                 oops = this ; oopc = comi ; oopl = here ; oopf = last
                 oopb = beg:am:fin
                 comd = comd:' ':rest
                 call @xcom(mat junk)
                 comd = ''
           end case
        case comi = '?'
           temp = 'R#22'
           crt fmt('Login name = ',temp):name:
           crt' (':term:', userno ':whom:')'
           crt fmt('Account = ',temp):acct
           if path ne '' then crt fmt('VOC path = ',temp):path
           crt fmt('Level = ',temp):levl
           crt fmt('File name = ',temp):fnam
           crt fmt('Record id = ',temp):item
           crt fmt('Current line = ',temp):here
           crt fmt('Lines = ',temp):last
           crt fmt('Characters = ',temp):len(this)
           if chan ne '' then
              crt fmt('Last Change command = ',temp):chan<1,1>
           end
           if cmat ne '' then
              crt fmt('Last CMatch command = ',temp):
              crt 'CM':cmat<1,3>:cmat<1,1>:cmat<1,2>
           end
           if olda then
              crt fmt('Last Append command = ',temp):'A':
              crt olda<1,2>:olda<1,1>
           end
           if beg or fin then
              crt fmt('Block = ',temp):beg:'-':fin
           end
           crt 'Command Delimiter is ':
           if comdmark = '"' then
              crt "'":comdmark:"'":
           end else crt '"':comdmark:'"':
           crt ', NULL to terminate insert is ':
           if nill = '"' then
              crt "'":nill:"'":
           end else crt '"':nill:'"':
           crt ', WordMark is ':
           if wordmark = '"' then
              crt "'":wordmark:"'"
           end else crt '"':wordmark:'"'
           crt 'Page: window for PA/PL/PP is ':pwin:
           crt ', length for P is ':plen
           crt 'Up arrow ^ ':
           if wild then crt 'on': else crt 'off':
           crt ', CASE flag ':
           if caseflag then crt 'on': else crt 'off':
           crt ', SPACE flag ':
           if spaceflag then crt 'on': else crt 'off':
           crt ', SHOW flag ':
           if shew then crt 'on' else crt 'off'
           if oopc ne '' then
              crt 'OOPS will restore record prior to command: ':oopc
           end else
              crt 'OOPS already executed, or no changes in effect.'
           end
        case comi[1,2] = '<>' ; gosub botharr
        case comi[1,1] = '<' ; gosub leftarr
        case comi[1,1] = '>' ; gosub rightarr
        case 1
           gosub bad.command
     end case
     return

parse.cols:
     good = true
     cols = field(rest,dlim,2)
     rest = field(rest,dlim,1)
     colf = field(cols,'-',2)
     cols = field(cols,'-',1)
     if colf = '' then colf = cols
     if cols ne '' then
        if not(cols matches '1N0N') or not(colf matches '1N0N') then
           crt 'Column specifications must be positive whole numbers.'
           gosub bad.comd
           good = false
           return
        end
     end
     if colf lt cols then
        crt 'Ending column # must exceed or equal starting column #.'
        gosub bad.comd
        good = false
        return
     end
     colf = colf - cols + 1
     return

change.command:
     if comd = 'RA' then
        if numb = '' then
           crt 'Last ':channumb:' changes (latest first)'
           for xx = 1 to channumb
              crt fmt(xx,prfx:'2':sufx):' ':chan<1,xx>
           next xx
           return
        end
        if numb gt channumb or numb lt 1 then
           crt 'Change must be in range 1-':channumb:'.'
           comi = ''
           return
        end
        comi = chan<1,numb>
        if comi = '' then
           crt 'There is no change ':numb
           return
        end
        chan = delete(chan,1,numb,0)
        chan = insert(chan,1,1,0,comi)
        gosub parse.command
        comi = 'RA'
     end
     gosub get.fromto
     if comi = '' then return
     chng = 0 ; save = here ; savl = last
     glob = index(field(rest,dlim,3),'G',1)
     glob = glob + index(field(rest,dlim,3),'g',1)
     show = shew or index(field(rest,dlim,3),'S',1)
     show = show or index(field(rest,dlim,3),'s',1)
     if numb lt plen then show = true
     gosub set.bounds
     for here = bot to msup
        gosub get.line
        gosub change.line
        gosub check.line
     next here
     here = msup
     if comi ne '' and oconv(comi,'MCU') ne 'RA' then
        chan = insert(chan,1,1,0,comi)
        loop
        while dcount(chan,vm) gt channumb do
           chan = delete(chan,1,channumb,0)
        repeat
     end
     if chng then
        gosub change.made
        if not(show) then crt chng:' lines changed - now at ':here
     end
     return

get.fromto:
     if count(rest,dlim) gt 2 then
        crt 'Too many delimiters (3 max.).'
        comi = ''
        return
     end
     line = field(rest,dlim,1)
     gosub parse.line
     cfrom = line
     line = field(rest,dlim,2)
     gosub parse.line
     cto = line
     if cto = '' and count(rest,dlim) lt 2 then
        crt 'Missing required TO field (for "CHANGE/FROM/TO").'
        comi = ''
        return
     end
     return

change.line:
     if cfrom = '' then
        temp = cto:line
     end else
        if glob then
$ifdef changeworks
           temp = change(line,cfrom,cto)
$else
           chit = line ; old1 = cfrom ; new1 = cto
           gosub changeit ; line = chit
$endif
        end else
           temp = index(line,cfrom,1)
           if temp then
              temp = line[1,temp-1]:cto:line[temp+len(cfrom),len(line)]
           end else temp = line
        end
     end
     return

conv.command:
     chng = 0 ; save = here ; savl = last
     show = shew or index(rest,'S',1) or index(rest,'s',1)
     if numb lt plen then show = true
     if numb = '' and rest matches '1N0N' then numb = rest
     gosub set.bounds
     ctyp = ccom[1,1]
     begin case
* ICONV
        case ctyp = '*' ; ccom = ccom[2,999]
* Text conversion LC, TC, or UC command
        case ctyp = 'Q' ; ccom = ccom[2,999]
     end case
     for here = bot to msup
        gosub get.line
        begin case
           case ctyp = '*'
              temp = iconv(line,ccom)
           case ctyp = 'Q'
              temp = field(trim(line),' ',1)
              flag = false
              if temp ne 'REMOVE' then
                 if temp[1,3] = 'REM' then flag = true
                 if temp[1,1] = '*' then flag = true
                 if temp[1,1] = '!' then flag = true
              end
              if flag then
                 temp = line
              end else
                 xxno = len(line)
                 temp = ''
                 flag = ''
                 for xx = 1 to xxno
                    bit = line[xx,1]
                    begin case
                       case bit = flag ; flag = ''
                       case flag ne ''
                       case index(qt,bit,1) ; flag = bit
                       case 1 ; bit = oconv(bit,ccom)
                    end case
                    temp = temp:bit
                 next xx
              end
           case 1
              temp = oconv(line,ccom)
        end case
        if temp = '' then temp = line
        gosub check.line
     next here
     here = msup
     if chng then
        gosub change.made
        if not(show) then
           crt chng:' lines changed - now at ':here
        end
     end
     return

dot.command:
     save = comi
     comi = field(comi,dlim,2,999)
     gosub parse.command
     begin case
        case comd = 'A'                 ; * append to line
           if numb = '' then numb = 1
           if numb gt dcount(stak,vm) then
              crt 'History command ':numb:' does not exist.'
           end else
              stak<1,numb> := rest
              crt fmt(numb,prfx:'3':sufx):'. ':stak<1,numb>
           end
        case comd = 'C'                 ; * change lines
           if numb = '' then numb = 1
           if numb gt dcount(stak,vm) then
              crt 'History command ':numb:' does not exist.'
           end else
              gosub get.fromto
              if comi = '' then comd = '' ; return
              glob = index(field(rest,dlim,3),'G',1)
              glob = glob + index(field(rest,dlim,3),'g',1)
              line = stak<1,numb>
              gosub change.line
              stak<1,numb> = temp
              crt fmt(numb,prfx:'3':sufx):'. ':temp
           end
        case comd = 'D'                 ; * delete lines
           if numb = '' then numb = 1
           if numb gt dcount(stak,vm) then
              crt 'History command ':numb:' does not exist.'
           end else
              stak = delete(stak,1,numb,0)
              crt 'History #':numb:' DELETEd.'
           end
        case comd = 'I'                 ; * insert a new line
           if numb = '' then numb = 1
           if numb gt dcount(stak,vm) then
              crt 'History command ':numb:' does not exist.'
           end else
              if rest ne '' then
                 stak = insert(stak,1,numb,0,rest)
                 stak = delete(stak,1,staknumb,0)
                 crt fmt(numb,prfx:'3':sufx):'. ':stak<1,numb>
              end
           end
        case comd = 'L' or comd = ''    ; * list lines
           if numb = '' then
              if comd = 'L' then numb = plen else numb = 1
           end
           if numb gt dcount(stak,vm) then numb = dcount(stak,vm)
           temp = rem(numb+1,plen)
           for xx = numb to 1 step -1
              crt fmt(xx,prfx:'3':sufx):'. ':stak<1,xx>
              if xx gt 1 and rem(xx,plen) = temp then
                 stub = 'Press Enter'
                 gosub get.rope; wait = rope; crt
                 if wait = 'X' then exit
              end
           next xx
        case comd = 'R'                 ; * restore a line to latest
           if numb = '' then numb = 1
           if numb le dcount(stak,vm) then
              temp = stak<1,numb>
              stak = insert(stak,1,1,0,temp)
              stak = delete(stak,1,staknumb,0)
           end
        case comd = 'S'
           if numb = '' then numb = 1
           if numb gt presnumb then
              crt numb:' exceeds the pre-store limit of ':presnumb
              return
           end
           rest = trim(rest)
           bot = field(rest,dlim,1) ; if bot = '' then bot = 1
           msup = field(rest,dlim,2) ; if msup = '' then msup = 1
           if not(bot matches '1N0N' and msup matches '1N0N') then
              crt 'One of the values was not a number'
              return
           end
           if bot gt msup then temp = bot ; bot = msup ; msup = temp
           temp = ''
           for xx = msup to bot step -1
              temp<1,1,-1> = stak<1,xx>
           next xx
           crt 'TEMP = ':temp
           pres<1,numb> = temp
        case comd = 'U'                 ; * upcase line
           if numb = '' then numb = 1
           if numb gt dcount(stak,vm) then
              crt 'History command ':numb:' does not exist.'
           end else
              stak<1,numb> = oconv(stak<1,numb>,'MCU')
           end
        case comd = 'X'              ; * re-execute an editor command
           if numb = '' then numb = 1
           if numb gt dcount(stak,vm) then
              crt 'History command ':numb:' does not exist.'
           end else
              salt = stak<1,numb>
              stak = delete(stak,1,numb,0)
           end
        case 1
           comi = save
           gosub bad.command
     end case
     comi = '' ; comd = ''
     return

viewonly:
     crt 'That command is not allowed in VIEW mode':bell
     comi = ''
     return

bad.command:
     crt 'Command not understood - try "H" for help.'
bad.comd:
     xxno = len(comi)
     temp = ''
     for xx = 1 to xxno
        bite = comi[xx,1]
        bite = seq(bite)
        if bite ge 127 or bite lt 32 then
           bite = '^':fmt(bite,prfx:'3':sufx)
        end else bite = char(bite)
        temp = temp:bite
     next xx
     crt 'Command was: "':temp:'"'
     comi = ''
temp = ''
     return

bad.xcom:
     crt 'Line 2 of record "':rest:'" in file "AE_XCOMS"'
     crt 'contains "':xcom:'".'
     crt
     crt 'This line should contain the name of a Basic subroutine that'
     crt 'has been written to implement the external command "':rest:'".'
     crt 'The program name must end in "_AE". Try "help xcoms".'
     return

write.record:
     if rest ne '' then
        if comd eq 'FD' then
           crt '"FD" operates only on the current record & file.'
        end else
           crt '"FI" only for current record & file. Use SAVE.'
        end
        gosub bad.comd ; return
     end
     if source.control then
        dict.flag = dprt
        file.name = fprt
        record.name = item
        if comd = 'FD' then record.data = '' else record.data = this
        caller = '3'
        write.allowed = '1'
        updated = '0'
        call source.control(dict.flag,file.name,
        record.name,record.data,caller,write.allowed,updated)
        if write.allowed ne '1' then
           crt 'WRITE NOT ALLOWED'
           return
        end
     end
     if not(lock) then
        crt 'Record lock has been released!   Write not allowed.'
        comi = ''
        return
     end
     if comd = 'FD' then
        stub = '***** You are about to DELETE the record! OK? n/y >'
        gosub get.rope; temp = rope; crt
        if trim(oconv(temp,'MCU'))[1,1] ne 'Y' then return
$ifdef werrorworks
        delete file, item on error crt 'FAIL' ; return
$else
        delete file, item
$endif
        crt 'Deleted "':item:'" from file "':fnam:'".'
     end else
        if comd = 'SV' then
$ifdef werrorworks
           writeu this on file, item on error crt 'FAIL' ; return
$else
           writeu this on file, item
$endif
           orig = this ; oops = '' ; oopc = '' ; oopl = '' ; oopf = ''
           oopb = ''
           crt 'Saved "':item:'" in "':fnam:'" - now at line ':here:'.'
           return
        end else
$ifdef werrorworks
           write this on file, item on error crt 'FAIL' ; return
$else
           write this on file, item
$endif
           crt 'Filed "':item:'" in file "':fnam:'"':
           if orig = this then crt 'unchanged.' else crt '.'
           oops = '' ; oopc = '' ; oopl = '' ; oopf = ''    ; * ewd
        end
     end
     stopsign = true
     if index(comd,'B',1) then
        temp = 'BASIC'
$ifdef qm
        if index(comd,'D',1) then temp<2> = ' DEBUGGING'
$endif
        gosub exec.that
     end
     if index(comd,'C',1) then
        temp = 'CATALOG'
        begin case
           case index(comd,'L',1) ; temp<2> = ' LOCAL'
*          CASE INDEX(COMD,'G',1) ; TEMP<2> = ' GLOBAL'
        end case
        gosub exec.that
     end
     if index(comd,'R',1) then temp = 'RUN' ; gosub exec.that
     return

edit.fields:
     if here lt 1 then here = 1
     gosub get.line ; temp = line
$ifdef changeworks
     convert vmrk to am in line
$else
     chit = line ; old1 = vmrk ; new1 = am
     gosub changeit ; line = chit
$endif
     ttid = whom:'_':levl:'_':vals:'.in.line#':here
$ifdef werrorworks
     write line on acom, ttid on error crt 'FAIL' ; return
$else
     write line on acom, ttid
$endif
     crt 'editing ':vals:' as fields...':
     execute verb:' AE_COMS ':ttid
     if scrset then test = @(0,0)
     crt 'Back editing record "':item:'" in file "':fnam:'"'
     read line from acom, ttid else line = ''
     delete acom, ttid
     return

reset.fields:
$ifdef changeworks
     convert am to vmrk in line
$else
     chit = line ; old1 = am ; new1 = vmrk
     gosub changeit ; line = chit
$endif
     if temp ne line then
        oops = this ; oopc = comi ; oopl = here ; oopf = last
        oopb = beg:am:fin
        memr(cell) = line
        gosub reset.record
     end
     return

get.load:
     temp = ''
     if trim(rest) = '' then
        stub = 'Record name, or file name and record name >'
        gosub get.rope; rest = rope; crt
        if trim(rest) = '' then temp = ''; return
     end
     keepquot = false
     gosub parse.rest
     onam = bite<1>
     onid = bite<2>
     if onam = 'DICT' then
        onam = onam:' ':onid
        onid = bite<3>
     end
     if onid = '' then onid = onam ; onam = ''
     if onid = '' then return
     if onam = '' then
        onam = fnam
        odpt = dprt
        ofpt = fprt
        ofil = file
     end else
        odpt = field(onam,' ',1)
        ofpt = field(onam,' ',2)
        if ofpt = '' then ofpt = odpt ; odpt = ''
        if odpt ne '' and odpt ne 'DICT' then
           crt 'File "':onam:'" was not found.'
           gosub bad.comd ; return
        end
        open odpt, ofpt to ofil else
           crt 'File "':onam:'" was not found.'
           gosub bad.comd ; return
        end
     end
     read temp from ofil, onid then
        if comd = 'CUT' then
           stub = 'Record "':onid:'" already exists - overwrite? (Y/N) '
           gosub get.rope; answ = rope; crt
           answ = oconv(trim(answ),'MCU')[1,1]
           if answ = 'Y' then temp = 'TRUE' else temp = ''
        end
     end else
        if comd = 'CUT' then temp = 'TRUE'; return
        crt 'Record "':onid:'" was not found on file "':onam:'".'
        gosub bad.comd ; temp = '' ; return
     end
     return

changematch.command:
     patt = field(rest,dlim,1)
     gosub parse.pattern
     if not(good) then
        crt 'Pattern: character "':bit:'" is not allowed unless quoted.'
        comi = ''
        return
     end
     if comd = 'CM' then cmat = dlim:vm:rest:vm:numb
     cto = field(rest,dlim,3)
     line = cto ; gosub parse.line ; cto = line
     cfrom = oconv(field(rest,dlim,2),'MCU')
     if cfrom = '' then cfrom = 'L'
     if numb = '' and cto = '' and (cfrom = 'L' or cfrom = 'N') then
        numb = last
        flag = true
     end else flag = false
     if len(cfrom) = 1 and index('ADLNPR',cfrom,1) then
     end else
        gosub parse.cols
        if not(good) then return
        cfrom = ''
        colf = cols + colf - 1
     end
     gosub set.bounds
     show = shew
     chng = 0 ; save = here ; savl = last
     test = ''
     for here = bot to msup
        gosub get.line
        if not(line matches patt) then
           if cfrom = 'N' then
              numb = numb + 1
              gosub display.line
              if flag then msup = here
           end
           continue
        end
        numb = numb + 1
        temp = line
        begin case
           case cfrom = 'A'
              temp = cto:line
           case cfrom = 'D'
              crt fmt((here + chng),lfmt):'+ ':line
              test<-1> = here
           case cfrom = 'L'
              gosub display.line
              if flag then msup = here
           case cfrom = 'P'
              temp = line:cto
           case cfrom = 'R'
              temp = cto
           case cfrom = 'N'
              numb = numb - 1
           case 1
              gosub parse.temp
        end case
        if index('APR',cfrom,1) or cfrom = '' then
           gosub check.line
        end
     next here
     if cfrom = 'D' and numb then
        oops = this ; oopc = comi ; oopl = save ; oopf = savl
        oopb = beg:am:fin
        for here = numb to 1 step -1
           temp = test
           del this
           if beg = temp then beg = 0
           if beg gt temp then beg = beg - 1
           if fin = temp then fin = 0
           if fin gt temp then fin = fin - 1
        next here
        test = ''
        gosub set.record
        here = msup - numb
     end else
        here = msup
     end
     crt 'At line ':here:
     if here ge last then crt '(bottom)':
     crt '. ':
     if not(numb) then
        crt 'No lines (in ':bot:'-':msup:') ':
        if cfrom = 'N' then crt 'NOT ':
        crt 'matching pattern "':patt:'"':
     end else
        begin case
           case cfrom = 'D'
              crt 'Deleted ':numb:' lines matching "':patt:'"':
           case cfrom = 'L'
              crt 'Found ':numb:' lines matching "':patt:'"':
           case cfrom = 'N'
              crt 'Found ':numb:' lines NOT matching "':patt:'"':
        end case
     end
     crt
     if chng then
        gosub change.made
        begin case
           case cfrom = 'A'
              crt '"':cfrom:'" appended to ':
           case cfrom = 'D'
              crt 'Deleted ':
           case cfrom = 'P'
              crt '"':cfrom:'" prefixed to ':
           case cfrom = 'R'
              crt 'Replaced with "':cto:'", ':
           case cfrom = 'L' or cfrom = 'N'
           case cols = colf
              crt 'Element ':cols:' changed to "':cto:'" in ':
           case cols
              crt 'Element ':cols:'-':colf:' changed to "':cto:'" in ':
        end case
        crt chng:' line':
        if chng gt 1 then crt 's':
        crt ' matching "':patt:'"'
     end
     return

parse.pattern:
* BITS<1> is the pattern pieces, <2> 'S'tring or 'P'attern flag
     cntr = 1
     bits = ''
     flag = ''
     good = true
     first = true
     xxno = len(patt)
     for xx = 1 to xxno
        bit = patt[xx,1]
        begin case
           case bit = flag
              flag = ''
              first = true
              cntr = cntr + 1
           case flag ne ''
              bits<1,cntr> = bits<1,cntr>:bit
           case index(qt,bit,1)
              bits<2,cntr> = 'S'
              flag = bit
           case first
              if not(bit matches '1N') then
                 good = false
                 return
              end
              first = false
              bits<2,cntr> = 'P'
              bits<1,cntr> = bits<1,cntr>:bit
           case 1
              if not(index('AaNnXx',bit,1)) then
                 good = false
                 return
              end
              bits<1,cntr> = bits<1,cntr>:oconv(bit,'MCU')
              first = true
              cntr = cntr + 1
        end case
     next xx
     cntr = cntr - 1
* Get rid of some hard ones (0N0N --> 1N0N, like)
     loop
        chng = false
        for xx = 1 to cntr
           type = bits<2,xx>
           if type = 'P' and bits<2,xx+1> = 'P' then
              bit = bits<1,xx>
              if bit[1,1] = '0' then
                 if bits<1,xx+1> = '0X' or bits<1,xx+1> = bit then
                    bits<1,xx> = '1':bit[2,1]
                    chng = true
                 end else
                    if bits<1,xx+1>[1,1] = '0' and bit = '0X' then
                       bits<1,xx+1> = '1':bits<1,xx+1>[2,1]
                    end
                 end
              end
           end
        next xx
     while chng do
     repeat
     return

parse.temp:
     temp = ''
     posn = 1
     for xx = 1 to cntr
        what = bits<1,xx>
        type = bits<2,xx>
        nmbr = what[1,1]
        begin case
           case xx gt colf
              temp = line[posn,len(line)]
              xx = cntr
           case type = 'S'
              bit = bits<1,xx>
              posn = posn + len(bit)
              temp = bit
           case nmbr = '0' and xx = cntr
              temp = line[posn,len(line)]
           case nmbr = '0' and what ne '0X'
              type = what[2,1]
              bit = ''
              yyno = len(line)
              for yy = posn to yyno
                 chit = line[yy,1]
                 if chit matches '1':type then
                    bit = bit:chit
                 end else yy = yyno
              next yy
              if bits<2,xx+1> = 'P' then
                 what = bits<1,xx+1>
                 if what[2,1] = type then
                    crt 'BIT = ':bit
                    bit = bit[1,len(bit)-what[1,1]]
                    crt 'BIT = ':bit
                 end
              end
              temp = bit
              posn = posn + len(bit)
           case nmbr = '0' and bits<2,xx+1> = 'S'
              temp = line[posn,len(line)]
              bit = index(temp,bits<1,xx+1>,1)
              bit = temp[1,bit-1]
              temp = bit
              posn = posn + len(bit)
           case nmbr = '0'
* OK if we can get the rest of the length
              bit = 0
              for yy = xx + 1 to cntr
                 if bits<2,yy> = 'S' then
                    bit = bit + len(bits<1,yy>)
                 end else
                    nmbr = bits<1,yy>[1,1]
* I can't figure out what to do - SODDIT
                    if nmbr = '0' then
                       crt 'Line matches, but pattern ambiguous.'
                       crt fmt(here,lfmt):'# ':line
                       if here lt msup then msup = here ; crt 'Stopped ':
                       temp = line ; return
                    end
                    bit = bit + nmbr
                 end
              next yy
              bit = line[posn,len(line)-posn-bit+1]
              temp = bit
              posn = posn + len(bit)
           case 1
              bit = line[posn,nmbr]
              posn = posn + len(bit)
              temp = bit
        end case
     next xx
     temp = cto
     for xx = cols + 1 to colf
        temp = delete(temp,cols+1)
     next xx
$ifdef changeworks
     convert am to '' in temp
$else
     chit = temp ; old1 = am ; new1 = ''
     gosub changeit ; temp = chit
$endif
     return

get.lines:
     stub = "'Q'uit, or starting line > "
     gosub get.rope; bot = rope
     bot = oconv(trim(bot),'MCU')
     if bot = '' then bot = 1 ; crt bot:
     if bot[1,1] = 'Q' then temp = false ; return
     if not(bot matches '1N0N') then
        crt
        crt 'Nothing done - starting and ending lines must be numeric.'
        gosub bad.comd ; temp = false ; return
     end
     if bot gt dcount(temp,am) then
        crt
        crt 'Nothing done - the record does not have that many lines.'
        gosub bad.comd ; temp = false ; return
     end
     stub = "'Q'uit, or starting line > ":bot:', ending line > '
     gosub get.rope; msup = rope
     msup = oconv(trim(msup),'MCU')
     if msup = '' then msup = dcount(temp,am) ; crt msup:
     if msup[1,1] = 'Q' then temp = false ; return
     if not(msup matches '1N0N') then
        crt
        crt 'Nothing done - starting and ending lines must be numeric.'
        gosub bad.comd ; temp = false ; return
     end
     if msup gt dcount(temp,am) then
        msup = dcount(temp,am)
        crt begn:"File is ":onam:": 'Q'uit, or starting line > ":
        crt bot:', ':msup:
     end
     temp = field(temp,am,bot,msup-bot+1)
     crt
     return

parse.bite:
     temp = ''
     loop
     while bite ne '' do
$ifdef trimfbworks
        bite = trimf(bite)
$else
        chit = bite ; gosub trimfit ; bite = chit
$endif
        xx = fold
        if count(bite[1,xx],' ') and trim(bite[xx+1,1]) ne '' then
           loop
           until trim(bite[xx,1]) = '' do
              xx -=1
           repeat
           temp<-1> = bite[1,xx-1]
        end else
           temp<-1> = bite[1,xx]
        end
        bite = bite[xx+1,len(bite)]
     repeat
     return

show.help:
     if help.def = '' then
        crt '**** the file LED.HELP could not be opened'
        return
     end
     rest = trim(oconv(rest,'MCU'))
     temp = field(rest,' ',1)
*     IF TEMP NE '' AND TEMP NE 'NEW' AND TEMP NE AM THEN
*        READ HELP FROM LED.HELP,TEMP THEN
*           REST = FIELD(REST,' ',2)
*        END ELSE HELP = ''
*     END ELSE HELP = ''
*     IF HELP = '' THEN
*        READ HELP FROM LED.HELP,HELP.DEF ELSE
*           CRT 'There is no default help for ':HELP.DEF
*           RETURN
*        END
*     END
     if rest = am then
        printer on
        heading "LED editor help file    ":timedate():"'LL'"
        xxno = dcount(help,am)
        for xx = 1 to xxno
           temp = help
           print temp
        next xx
        printer close
        return
     end
     if rest = 'NEW' then crt ; crt 'New features' ; crt
     good = false
     xxno = dcount(help,am)
     pg = 0
     flag = false
     for xx = 1 to xxno
        temp = help
        bit = temp[1,len(rest)]
        if temp[14,1] = '=' then
           temp = temp[1,13]:'-':temp[15,99]
           if rest = 'NEW' then bit = 'NEW'
        end
        if trim(bit) ne '' then flag = false
        if bit = rest or flag then
           flag = true
           pg = pg + 1
           if pg ge system(3) then
              stub = "'S'kip the rest of this explanation, "
              stub := 'or press return >'
              gosub get.rope; answ = rope
              crt begn:ceol:
              answ = trim(oconv(answ,'MCU'))[1,1]
              if answ = 'S' or answ = 'Q' then
                 crt
                 return
              end
              pg = 1
           end
           crt temp
           good = true
        end
     next xx
     if not(good) then
        crt
        crt "No explanation of '":rest:"' is available."
        crt "For a list of words that have explanations, type 'HELP'."
        crt
     end
     return

get.page.comd:
     gosub get.comd
do.page.comd:
     locate(comd,keys;cpos) then cpos = acts else cpos = 0
     begin case
        case cpos = uarr ;* up key
           if here le 1 then crt bell:; return
           gosub check.page
           here = here - 1
           if prow le 1 then
              ptop = ptop - botl
              if ptop lt 1 then ptop = 1
              gosub disp.page
           end
           gosub get.line; temp = line
        case cpos = darr ;* down key
           if here ge last then crt bell:; return
           gosub check.page
           here = here + 1
           if prow ge botl then
              ptop = ptop + botl
              if ptop ge last then ptop = last - botl + 1
              if ptop le 1 then ptop = 1
              gosub display.page
           end
           gosub get.line; temp = line
        case cpos = larr ;* left key
           if pchr le 1 then crt bell:; return
           pchr = pchr - 1
           if pchr lt ppos then
              gosub check.page
              gosub disp.page
           end
        case cpos = rarr ;* right key
           pchr = pchr + 1
           if pchr-ppos ge wide then
              gosub check.page
              gosub disp.page
           end
        case cpos = upag ;* page up key
           gosub check.page
           ptop = ptop - botl
           if ptop lt 1 then ptop = 1
           here = here - botl
           if here lt 1 then here = 1
           pchr = 1
           gosub get.line; temp = line
           gosub disp.page
        case cpos = dpag ;* page down key
           gosub check.page
           ptop = ptop + botl
           if ptop ge last then ptop = last - botl + 1
           here = here + botl
           if here gt last then here = last
           pchr = 1
           gosub get.line; temp = line
           gosub disp.page
        case cpos = lpag ;* start of line key
           pchr = 1
           if pchr lt ppos then gosub check.page; gosub disp.page
        case cpos = rpag ;* end of line key
           pchr = len(temp)+1
           if pchr lt ppos then gosub check.page; gosub disp.page
           if pchr-ppos ge wide then
              gosub check.page
              gosub disp.page
           end
        case cpos = tpag ;* top page key
           gosub check.page
           here = 1
           ptop = 1
           pchr = 1
           gosub disp.page
           gosub get.line; temp = line
        case cpos = bpag ;* bottom page key
           gosub check.page
           here = last
           ptop = last - botl + 1
           gosub get.line
           pchr = len(line)
           gosub disp.page
           gosub get.line; temp = line
        case cpos = escp ;* escape key
           if this ne that then
              crt botp:@(-13):'Abandoning changes':@(-14)
           end
           this = that
           here = savl<1>
           gosub set.record
           mode = 'LINE'
        case cpos = phlp ;* help key
           gosub page.help
        case cpos = zoom ;* go to line key
           stub = 'Go to line :'
           crt botp:
           gosub get.rope; numb = trim(rope)
           crt botp:'Press ':kcap(phlp):' for help':
           if not(numb matches '1N0N') then numb = here
           if numb gt last then numb = last
           if numb = here then return
           gosub check.page
           here = numb
           ptop = here
           pchr = 1
           gosub disp.page
           gosub get.line; temp = line
        case not(sec.write.flg)
           crt bell:
        case cpos = delc ;* delete character key
           if temp = '' then return
           if pchr = 1 then
              temp = temp[2,len(temp)]
           end else
              temp = temp[1,pchr-1]:temp[pchr+1,len(temp)]
           end
           crap = temp[pchr,wide-pcol]
$ifdef changeworks
           convert badc to gudc in crap
$else
           chit = crap; gosub change.non.print; crap = chit
$endif
           crt @(pcol,prow):ceol:crap:
        case cpos = dell ;* delete line key
           del this
           gosub set.record
           gosub disp.page
           gosub get.line; temp = line
        case cpos = delr ;* delete to end of line key
           if pchr gt len(temp) then
              if here ge last then crt bell:; return
              line = fmt(temp,'L#':pchr-1):this
              del this
              this = line
              gosub set.record
              gosub disp.page
              gosub get.line; temp = line
           end else
              temp = temp[1,pchr-1]
              this = temp
              memr(cell) = temp
              line = temp
              crt @(pcol,prow):ceol:
           end
        case cpos = back ;* keys(back)space key
           if pchr = 1 then crt bell:; return
           pchr = pchr - 1
           temp = temp[1,pchr-1]:temp[pchr+1,len(temp)]
           if pchr lt ppos then
              gosub check.page
              gosub disp.page
           end else
              pcol = rem(pchr-1,wide)
              crt @(pcol,prow):ceol:
              crap = temp[pchr,wide-pcol]
$ifdef changeworks
              convert badc to gudc in crap
$else
              chit = crap; gosub change.non.print; crap = chit
$endif
              crt crap:
           end
        case cpos = carr ;* keys(carr)iage return key
           if pchr = 1 then
              line = ''
           end else
              line = temp[1,pchr-1]
              temp = temp[pchr,len(temp)]
           end
           memr(cell) = line
           last = last + 1
           lnum = lnum + 1
           line = temp
           gosub insert.line
           gosub reset.record
           here = here + 1
           pchr = 1
           if prow ge botl then
              ptop = ptop + botl
              if ptop ge last then ptop = last - botl + 1
              if ptop le 1 then ptop = 1
              gosub display.page
           end else gosub disp.page
           gosub get.line; temp = line
        case cpos = togg ;* keys(togg)le mode key
           if mode<2> = 'Ins' then
              mode<2> = 'Over'
           end else mode<2> = 'Ins'
        case cpos = writ ;* write away data key
           gosub check.page
           mode = 'LINE'
           return
        case cpos = doit
           gosub doit.comd
        case seq(comd) lt 28 or seq(comd) gt 127 or len(comd) gt 1
           crt bell:
        case seq(comd) eq 30 or seq(comd) eq 31
           crt bell:
        case 1
           if seq(comd) = 28 then comd = char(252)
           if seq(comd) = 29 then comd = char(253)
           if pchr and len(temp) lt (pchr-1) then
              temp = temp:str(' ',pchr)
              temp = temp[1,pchr-1]
           end
           if mode<2> = 'Ins' then
              offset = pchr
           end else offset = pchr+1
           if pchr = 1 then
              temp = comd:temp[1,len(temp)]
           end else
              temp = temp[1,pchr-1]:comd:temp[offset,len(temp)]
           end
           if mode<2> = 'Ins' then
              crt @(pcol,prow):ceol:
              crap = temp[pchr,wide-pcol]
$ifdef changeworks
              convert badc to gudc in crap
$else
              chit = crap; gosub change.non.print; crap = chit
$endif
              crt crap:
           end else
              crt @(pcol,prow):comd:
           end
           pchr = pchr + 1
           if pchr-ppos ge wide then
              gosub check.page
              gosub disp.page
           end
     end case
     return

check.page:
     chng = ''
     gosub check.line
     if chng then gosub reset.record
     return

get.comd:
$ifdef qm
     comd = keyinc()
     locate(comd,kini;kpos) then
        loop
           nap 5
           input full,-1
        while full do
           comd := keyinc()
           locate(comd,kmul,kpos;full) then return
        repeat
     end
$endif
$ifdef universe
     comd = keyin()
     locate(comd,kini;kpos) then
        loop
           nap 5
           input full,-1
        while full do
           comd := keyin()
           locate(comd,kmul,kpos;full) then return
        repeat
     end
$endif
$ifdef unidata
     comd = in()
     locate(comd,kini;kpos) then
        loop
           a = system(12)
           loop
           until system(12) ge (a+5) do
           repeat
           input full,-1
        while full do
           comd := in()
           locate(comd,kmul,kpos;full) then return
        repeat
     end
$endif
     return

page.help:
     gosub clear.page
     crt @(30,0):'Page editing help':
     crt @(0,2):' Cursor movement keys ':
     crt @(0,4):'       UP = ':kcap(uarr):
     crt @(0,5):'     DOWN = ':kcap(darr):
     crt @(0,6):'     LEFT = ':kcap(larr):
     crt @(0,7):'    RIGHT = ':kcap(rarr):

     crt @(0,9):' Page movement keys':
     crt @(0,11):' PREVIOUS = ':kcap(upag):
     crt @(0,12):'     NEXT = ':kcap(dpag):
     crt @(0,13):'      TOP = ':kcap(tpag):
     crt @(0,14):'   BOTTOM = ':kcap(bpag):

     crt @(40,2):' Line movement keys':
     crt @(40,4):'   LEFT END = ':kcap(lpag):
     crt @(40,5):'  RIGHT END = ':kcap(rpag):
     crt @(40,6):' GO TO LINE = ':kcap(zoom):
     crt @(50,7):'(prompts for desired line)':

     if sec.write.flg then
        crt @(40,9):' Deleting keys':
        crt @(40,11):'   DELETE CHAR = ':kcap(delc):
        crt @(40,12):'   DELETE LINE = ':kcap(dell):
        crt @(40,13):' DELETE TO EOL = ':kcap(delr):
        crt @(50,14):'(at EOL joins next line)':

        crt @(40,15):'  = Value Mark':
        crt @(40,16):'  = Sub-value Mark':

        crt @(0,16):space(15-len(kcap(back))):
        crt ' ':kcap(back):' is destructive':
        crt @(0,17):space(15-len(kcap(carr))):
        crt ' ':kcap(carr):' splits the line':
        crt @(0,18):space(15-len(kcap(togg))):
        crt ' ':kcap(togg):
        crt ' toggles between the insert and overwrite modes':
        if keys(writ) ne '' then
           crt @(0,20):space(9-len(kcap(writ))):
           crt ' ':kcap(writ):' Returns to line editor mode':
           if sec.write.flg then crt ' WITH changes':
        end
     end
     crt @(0,21):space(9-len(kcap(escp))):
     crt ' ':kcap(escp):' Returns without changes'
     crt botp:
     stub = 'Press  to continue:'
     gosub get.rope
     gosub display.page
     return

get.line:
     line = ''
     if here = 0 or here gt last then return
     cell = int((here-1)/cellsize) + 1
$ifdef removeworks
     coff = rem(here,cellsize)
     if ooff and ocel = cell and ooff = coff - 1 then
        lnum = ooff ; ooff = coff
     end else
        tlin = memr(cell)
        lnum = 0 ; ocel = cell ; ooff = coff
     end
     loop
        remove bite from tlin setting dlim
        line = line:bite
     while dlim do
        if dlim = 2 then
           lnum = lnum + 1
           if lnum = coff then exit
           line = ''
        end else
           line = line:char(256-dlim)
        end
     repeat
     if not(dlim) then lnum = lnum + 1
$else
     lnum = rem(here,cellsize)
     if not(lnum) then lnum = cellsize
     line = memr(cell)
$endif
     return

delete.lines:
     chng = 0
     if bot gt msup then
        crt 'No deletion possible - ':bot:' > ':msup
        return
     end
     oops = this ; oopc = comi ; oopl = here ; oopf = last
     oopb = beg:am:fin
     chng = msup - bot + 1
     begin case
        case bot = 1 and msup = last
           this = ''
           beg = 0
           fin = 0
        case msup = last
           temp = index(this,am,bot-1)
           this = this[1,temp-1]
           if beg gt bot then beg = 0
           if fin gt bot then fin = 0
        case bot = 1
           temp = index(this,am,msup)
           this = this[temp+1,len(this)]
           if beg le msup then beg = 0 else beg = beg - chng
           if fin le msup then fin = 0 else fin = fin - chng
        case 1
           temp = index(this,am,bot-1)
           temp<2> = index(this,am,msup)
           this = this[1,temp<1>]:this[temp<2>+1,len(this)]
           if beg ge bot and beg le msup then beg = 0 else
              if beg gt msup then beg = beg - chng
           end
           if fin ge bot and fin le msup then fin = 0 else
              if fin gt msup then fin = fin - chng
           end
     end case
     gosub set.record
     return

check.line:
     if '*':temp ne '*':line then
        chng = chng + 1
        memr(cell) = temp
        if show then gosub display.line
     end
     return

insert.line:
     if here le beg then beg = beg + 1
     if here le fin then fin = fin + 1
     memr(cell) = insert(memr(cell),lnum,0,0,line)
     if lfmt and len(last) gt 3 and len(last) ne llen then gosub get.lfmt
     return

display.line:
     begin case
        case last = 0 ; here = 0 ; crt begn:'Top of empty record.'
        case here = 0 ; cell = 1 ; lnum = 0 ; crt 'Top.'
        case here gt last ; crt 'Bottom.'
        case 1
           gosub get.line
           if wild then
              xxno = len(line)
              temp = ''
              for xx = 1 to xxno
                 bite = line[xx,1]
                 bite = seq(bite)
                 if bite ge 127 or bite lt 32 then
                    bite = '^':fmt(bite,prfx:'3':sufx)
                 end else bite = char(bite)
                 temp = temp:bite
              next xx
              line = temp
           end else
$ifdef changeworks
              convert badc to gudc in line
$else
              chit = line; gosub change.non.print; line = chit
$endif
           end
           crt begn:ceol:
           if lfmt then
              blk = ': '
              if here = beg then blk = '< '
              if here = fin then blk = '> '
              if here = beg and here = fin then blk = '<>'
              if lfmt then crt (here lfmt):blk:
           end
           crt line
           if here = last then crt 'Bottom at line ':last:'.'
     end case
     return

clear.page:
     crt @(0,1):ceop:@(0,0):ceol:
     return

display.page:
     crt clpg
disp.page:
     gosub clear.page
     gosub get.lfmt
     crt botp:'Press ':kcap(phlp):' for help':
     crt @(0,0):
     if not(viewflag) then crt 'Editing ': else crt 'Viewing ':
     crt '"':item:'" in file "':fnam:'" ':ceol:
     if idcnt gt 1 then crt '<':id:'/':idcnt:'> ':
     crt
     ppos = int((pchr-1)/wide)
     ppos = wide*ppos+1
     save = here:am:lnum:am:cell:am:line
     for xx = 1 to botl
        here = ptop + xx - 1
        gosub get.line
        disp = line[ppos,wide]
$ifdef changeworks
        convert badc to gudc in disp
$else
        chit = disp; gosub change.non.print; disp = chit
$endif
        crt @(0,xx):disp
     next xx
     here = save<1>; lnum = save<2>; cell = save<3>; line = save<4>
     return

change.made:
     oops = this ; oopc = comi ; oopl = save ; oopf = savl
     oopb = beg:am:fin
reset.record:
     matbuild this from memr using am
set.record:
     gosub parse.record
     if here and here le last then
        gosub get.line
     end else cell = 1 ; lnum = 0
     if len(last) gt 3 and len(last) ne llen then gosub get.lfmt
     return

parse.record:
     this = this
     ocel = '' ; ooff = ''
     last = dcount(this,am)
     if last = 0 then
$ifdef newdimworks
        dim memr(1)
$else
        cellsize = 1
$endif
        mat memr = ''
        cell = 1 ; lnum = 0
        return
     end
$ifdef newdimworks
     numcells = int((last-1)/cellsize)+1
     dim memr(numcells)
$else
     cellsize = int(last/numcells)+1
$endif
     mat memr = ''
$ifdef removeworks
     cell = 1
     lnum = 0
     line = ''
     loop
        remove bite from this setting mark
        line = line:bite
        begin case
           case mark = 0
              if line ne '' then
                 line = line[1,len(line)]
              end
              memr(cell) = line
           case mark = 2
              lnum = lnum + 1
              if lnum ge cellsize then
                 memr(cell) = line
                 line = ''
                 cell = cell + 1
                 lnum = 0
              end else
                 line = line:char(256-mark)
              end
           case 1
              line = line:char(256-mark)
        end case
     while mark do
     repeat
$else
     lnum = 1
     cell = 0
     xx = 0
     loop
        cell = cell + 1
        xx = xx + cellsize
        temp = index(this,am,xx)
        if temp then
           memr(cell) = this[lnum,temp-lnum]
           lnum = temp + 1
        end else
           memr(cell) = this[lnum,9999999]
        end
     while xx lt last do
     repeat
$endif
     cell = 1
     lnum = 0
     return

locked.record:
     stub = 'Record currently locked by another user. Try again (Y/N) '
     gosub get.rope; answ = rope; crt
     answ = oconv(trim(answ),'MCU')
     if answ = 'PASSWORD' then
        lock = false
        read this from file, item then go carry.on
     end
     if answ[1,1] = 'Y' then go edit.item
     return

exec.that:
     temp = temp<1>:' ':fnam:' ':item:temp<2>
$ifdef filinfworks
     if fileinfo(file,3) ne '4' then
        crt 'Cannot ':temp:' - must be type 1 or 19'
        return
     end
$endif
     execute temp
     if scrset then test = @(0,0)
     return
parse.rest:
     bite = ''
     flag = ''
     posn = 1
     xxno = len(rest)
     for xx = 1 to xxno
        bit = rest[xx,1]
        if flag eq '' then
           if bit = ' ' then
              if bite ne '' then posn = posn + 1
           end else
              if index(qt,bit,1) then
                 flag = bit
                 if keepquot then bite = bite:bit
              end else
                 if bit = '(' then
                    flag = ')'
                    if bite ne '' then posn = posn + 1
                    bite = '('
                 end else bite = bite:bit
              end
           end
        end else
           if bit ne flag then
              bite = bite:bit
           end else
              if keepquot or bit = ')' then bite = bite:bit
              posn = posn + 1
              flag = ''
           end
        end
     next xx
     return

split.itype:
     bite = ''
     flag = ''
     posn = 1
     xxno = len(line)
     for xx = 1 to xxno
        bit = line[xx,1]
        if flag eq '' then
           if bit = ';' then
              posn = posn + 1
           end else
              if index(qt,bit,1) then flag = bit
              if bit = '(' then flag = ')'
              bite = bite:bit
           end
        end else
           if bit = flag then flag = ''
           bite = bite:bit
        end
     next xx
     return

get.lfmt:
* set up the line format
     llen = len(last)
     if llen lt 3 then llen = 3
     lfmt = prfx:llen:sufx
     prmt = '*':str('-',llen-1)
     return

doit.comd:
* This is where the page editor could be expanded - does nothing now
     return

leftarr: *
     numb = oconv(trim(comi[2,len(comi)]),'MCN')
     if numb = '' then numb = here
     if numb gt last then numb = ''
     if numb ge 0 then
        crt 'Block starts at line ':numb:
        beg = numb
        if fin and beg gt fin then
           crt '; End moved from ':fin:' to ':beg
           fin = beg
           mov = 1
        end else
           if fin then mov = fin - beg + 1 else mov = last - beg
           crt ' (':mov:' lines)'
        end
        if numb = here then gosub display.line
     end else crt 'Cannot mark line ':numb
     return

rightarr: *
     numb = oconv(trim(comi[2,len(comi)]),'MCN')
     if numb = '' then numb = here
     if numb gt last then numb = ''
     if numb ge 0 then
        crt 'Block ends at line ':numb:
        fin = numb
        if beg gt fin then
           crt '; Start moved from ':beg:' to ':fin
           beg = fin
           mov = 1
        end else
           if beg then mov = fin - beg + 1 else mov = fin
           crt ' (':mov:' lines)'
        end
        if numb = here then gosub display.line
     end else crt 'Cannot mark line ':numb
     return

botharr: *
     numb = trim(comi[3,len(comi)])
     begin case
        case numb matches '1N0N'
           numb = numb:am:numb
        case numb matches '1N0N"-"1N0N'
           numb = field(numb,'-',1):am:field(numb,'-',2)
        case numb matches '1N0N" "1N0N'
           numb = field(numb,' ',1):am:field(numb,' ',2)
        case numb = ''
           numb = here:am:here
        case 1
           numb = ''
     end case
     if numb<2> gt last then numb<2> = last
     if numb<1> gt last then numb = ''
     if numb ne '' then
        beg = numb<1>
        fin = numb<2>
        if beg = fin then
           crt 'Block starts and ends at line ':beg
        end else
           crt 'Block starts at ':beg:' and ends at ':fin
        end
        if here = beg or here = fin then
           gosub display.line
        end
     end else crt 'Cannot mark Block'
     return

$ifndef changeworks
* This is only required if CHANGE and CONVERT don't work
changeit:
     loop
        bits = index(chit,old1,1)
     while bits do
        chit = chit[1,bits-1]:new1:chit[bits+len(old1),len(chit)]
     repeat
     return

change.non.print:
     if chit ne oconv(chit,'MCP') then
        yyno = len(chit)
        bite = ''
        for yy = 1 to yyno
           bit = chit[yy,1]
           if bit ne oconv(bit,'MCP') then
              sit = seq(bit)
              if sit = char(253) or sit = char(252) else bit = '~'
           end
           bite := bit
        next yy
        chit = bite
     end
     return
$endif

$ifndef trimfbworks
* This is only required if TRIMF doesn't work
trimfit:
     bit = trim(chit)
     if bit = '' then chit = bit ; return
     bit = bit[1,1]
     bit = index(chit,bit,1)
     chit = chit[bit,len(chit)]
     return
* This is only required if TRIMB doesn't work
trimbit:
     bit = trim(chit)
     if bit = '' then chit = bit ; return
     bit = bit[len(bit),1]
     bits = count(chit,bit)
     bits = index(chit,bit,bits)
     chit = chit[1,bits]
     return
$endif

setup.help:
     help = ''
     help<-1> = " LED version 01.05"
     help<-1> = " The Line EDitor can be called with the following formats:"
     help<-1> = " LED               file and record id's are prompted for"
     help<-1> = " LED file          record id's are prompted for"
     help<-1> = " LED file id       edit the record 'id' in 'file'"
     help<-1> = " LED file id id... edit multiple records in 'file'"
     help<-1> = " LED file *        edit all records in 'file'"
     help<-1> = " SELECT            may precede 'LED file' command"
     help<-1> = " Special ASCII characters may be entered as:"
     help<-1> = "    ^nnn  where nnn is the decimal character code (like ^027)"
     help<-1> = "    ^     will enter a single UP ARROW character."
     help<-1> = " The following commands may be used in the Editor:"
     help<-1> = " (NOTE that '/' is used as delimiter, but any non-alphanumeric works)"
     help<-1> = "A            - Do the last 'A any' (APPEND) command again."
     help<-1> = "A any        - APPEND 'any' to this line."
     help<-1> = "B            - Set the current line pointer to the BOTTOM line."
     help<-1> = "B any        - BREAK the current line after 'any' into two lines."
     help<-1> = "BD any       - BREAK and discard the second part."
     help<-1> = "BK any       - BREAK and Keep only the second part."
     help<-1> = "C            - Do the last 'CHANGE' command again."
     help<-1> = "C///         - CHANGE one or more lines. Formats are:"
     help<-1> = "               C/from/to C#/from/to/"
     help<-1> = "               C/from/to/G C#/from/to/G"
     help<-1> = "               C/from/to/S C#/from/to/S"
     help<-1> = "               where    / - is any delimiter character."
     help<-1> = "                        # - number of lines to CHANGE (default 1)."
     help<-1> = "                     from - is the character string to be replaced."
     help<-1> = "                       to - is the character string to substitute."
     help<-1> = "                        G - 'G'lobal flag - CHANGE all instances in line."
     help<-1> = "                        S - 'S'how flag - display all changes made."
     help<-1> = "CAT          - Synonym for 'J'oin."
     help<-1> = "CD           - Show or change the command delimiter."
     help<-1> = "               (this is the input for a blank line)"
     help<-1> = "CM///        - ChangeMatch one or more lines. Formats are:"
     help<-1> = "               CM/pattern             CM#/pattern"
     help<-1> = "               CM/pattern/range/to    CM#/pattern/range/to"
     help<-1> = "               where      / - is any delimiter character."
     help<-1> = "                          # - number of lines to CHANGE (default 1)."
     help<-1> = "                    pattern - is the pattern match for the line."
     help<-1> = "                         to - is the character string to substitute/add."
     help<-1> = "                      range - Can be numeric, saying which field(s) to change,"
     help<-1> = "                           or 'A'ppend or 'P'refix to the line,"
     help<-1> = "                           or 'D'elete, 'R'eplace, 'L'ocate (default) the line."
     help<-1> = "               EG 'CM/6X' will scan to the line matching '6X'."
     help<-1> = "               Also; 'N'ot - locate the next non-matching line"
     help<-1> = "CASE ON/OFF  - Switch CASE flag for FL, FLA, L, LA, LN, LNA commands"
     help<-1> = "               If neither ON nor OFF is used, then toggle CASE flag."
     help<-1> = "               OFF means that the commands are not case sensitive."
     help<-1> = "COL          - Display relative COLUMN POSITIONS on the CRT."
     help<-1> = "COPY         - Copy the predefined block to after the current line"
     help<-1> = "COUNT#/any   - Count of 'any' in next # lines (default 1)."
     help<-1> = "CRT xxxx     - Inserts a line CRT 'xxxx = ':xxxx"
     help<-1> = '               Use " or \ as delimiter to change quotes>'
     help<-1> = "CUT          - Remove and save the predefined block."
     help<-1> = "               You will be prompted for a file and item to save it to."
     help<-1> = "CUTx/y       - Remove and save x lines starting at line y."
     help<-1> = "CUT/x/y      - Remove and save the lines starting at x to line y inclusive."
     help<-1> = "D            - Display the current line."
     help<-1> = "DE           - DELETE the current line."
     help<-1> = "DE#          - DELETE '#' lines (default 1)."
     help<-1> = "DE#/any      - DELETE as above, but only if the line contains 'any'."
$ifdef qm
     help<-1> = "DISPLAY xxxx - Inserts a line DISPLAY 'xxxx = ':xxxx"
     help<-1> = "               Just like CRT, handy to distinguish debug code."
$endif
     help<-1> = "DROP         - Remove the predefined block."
     help<-1> = "DTX any      - Convert decimal string 'any' to hexadecimal and display it."
     help<-1> = "DUP          - DUPLICATE the current line."
     help<-1> = "DUP#         - DUPLICATE the current line '#' times."
     help<-1> = "EC           - Edit a called subroutine in this file."
     help<-1> = "EF#          - Edit fields delimited by CHAR(#) as lines"
     help<-1> = "EI           - Edit the included code."
     help<-1> = "EIT          - Edit I-type (not just a split on semi-colon)"
     help<-1> = "EPR          - Edit the prestored commands."
     help<-1> = "ESV          - Edit subvalues as lines."
     help<-1> = "EV           - Edit multivalues as lines."
     help<-1> = "EW           - Edit words as lines."
     help<-1> = "EXIT (EX)    - QUIT - EXIT the editor."
     help<-1> = "EXITK (EXK)  - QUITKill - EXIT the editor, abandon any active SELECT list."
     help<-1> = "FD           - DELETE the entire record from the file."
     help<-1> = "FI           - FILE the record. You can also process it."
     help<-1> = "               FIB = BASIC, FIC = CATALOG, FIR = RUN"
     help<-1> = "               You can have up to three processes (EG. FIBCR)."
$ifdef qm
     help<-1> = "               You can modify BASIC with D for DEBUGGING (EG. FIBD)."
$endif
     help<-1> = "               You can modify CATALOG with L for LOCAL (EG. FICL)."
     help<-1> = "FILE         - Synonym for SAVE"
     help<-1> = "FM           - Find Matching logic by position"
     help<-1> = "FM/any       - Find next line with string 'any' matching start of this line"
     help<-1> = "FMA          - Find Matching logic by position above this line"
     help<-1> = "FMA/any      - Find Matching string 'any' above this line."
     help<-1> = "FOLD/length  - Split current line (on blanks if possible) to fit the width."
     help<-1> = "FL           - Find the next Label"
     help<-1> = "FL any       - Find the label 'any' or matching pattern 'any'"
     help<-1> = "FL#          - Find (display) the labels in next # lines."
     help<-1> = "FLA          - Find label above this line."
     help<-1> = "FORMAT (FOR) - FORMAT a BASIC program to show logical structure by"
     help<-1> = "               indenting. This has the following keywords;"
     help<-1> = "               '-Mx' = Set margin to x"
     help<-1> = "               '-Iy' = Set Indentation to y"
     help<-1> = "               '-A' = Align comments with code"
     help<-1> = "               '-N' = No CASE indentation"
     help<-1> = "               '-C' = Compress - same as '-M0 -I1 -A -N'"
     help<-1> = "G#           - GO TO line '#' ('G' is optional)"
     help<-1> = "HELP (H)     - Prompt user to display HELP information on the CRT."
     help<-1> = "HELP any     - Display HELP information on CRT for 'any'."
     help<-1> = "HELP NEW     - Display HELP information on new features."
     help<-1> = "HEX          - Displays the current line in hexadecimal."
     help<-1> = "I            - INSERT new lines AFTER the current line. Prompt for"
     help<-1> = "               successive lines. INPUT until NULL input. An INPUT line"
     help<-1> = "               of a single space will store an empty line."
     help<-1> = "I any        - INSERT (INPUT) the line 'any' AFTER the current line."
     help<-1> = "I#/any       - INSERT # lines of 'any' AFTER the current line."
     help<-1> = "IC any       - IConv the line using the conversion 'any'"
     help<-1> = "IN command   - Insert the results of the command AFTER the current line."
     help<-1> = "               It is not a good idea to use a command requiring input."
     help<-1> = "J#/any       - Join next '#' lines (default 1), separated by 'any'."
     help<-1> = "L            - Repeat the last 'LOCATE' command (L, LA, LN, or LNA)"
     help<-1> = "L any        - LOCATE the next line that contains the string 'any'."
     help<-1> = "L#/any/10-20 - LOCATE in next # lines those with 'any' in columns 10 to 20."
     help<-1> = "               So 'L#' effectively lists # lines."
     help<-1> = "LA#/any/1-20 - Locate lines above this one (reverse order)"
     help<-1> = "LC#          - Change '#' lines to lower case (default 1)."
     help<-1> = "LC# any        Comments and quoted strings are unchanged."
     help<-1> = "LN#/any/1-20 - LOCATE NOT - line without 'any' in columns 10 to 20"
     help<-1> = "LNA#/any/1-2 - LOCATE line above this one without 'any' in columns 10 to 20"
     help<-1> = "LL#/length   - Show lines 'length' or longer (null '#' is a search)"
     help<-1> = "LOAD name    - LOAD the record 'name' from the current FILE,"
     help<-1> = "               line #'s will be prompted."
     help<-1> = "LOAD f name  - LOAD the record 'name' from file 'f',"
     help<-1> = "               line #'s will be prompted."
     help<-1> = "LOADA        - LOADAll - LOAD without line # prompting."
     help<-1> = "LD           - Synonym for LOAD"
     help<-1> = "LDA          - Synonym for LOADA"
     help<-1> = "M pattern    - Search for a line matching the pattern"
     help<-1> = "MACRO#       - Toggle macro recording into #th PRESTORE command."
     help<-1> = "MEx/y        - Merge x lines starting at line y"
     help<-1> = "ME/x/y       - Merge lines starting at x to line y inclusive"
     help<-1> = "MOVE         - Move the predefined block to after the current line"
     help<-1> = "MVx/y        - Move the x lines starting at line y"
     help<-1> = "MV/x/y       - Move the lines starting at x to line y inclusive"
     help<-1> = "NUM          - Toggle the line numbering"
     help<-1> = "NULL/        - Change the 'end of inserting' character to '/'"
     help<-1> = "OC# any      - OConv '#' lines using the conversion 'any'"
     help<-1> = "OOPS         - RESTORE the record to the condition prior to last change."
     help<-1> = "P            - PRINT on CRT the number of lines specified by last 'P#'."
     help<-1> = "P#           - PRINT on CRT '#' lines starting with the current line."
     help<-1> = "PA#          - PRINT the current line and the prior '#' lines,"
     help<-1> = "               do not change the current line pointer."
     help<-1> = "PE           - Page Edit mode"
     help<-1> = "PL#          - PRINT the current line and the next '#' lines,"
     help<-1> = "               do not change the current line pointer."
     help<-1> = "PP#          - PAGE.PRINT a window of '#' lines around the current line,"
     help<-1> = "               do not change the current line pointer."
     help<-1> = "PR           - Show the PRESTORE commands."
     help<-1> = "PR#          - Run the #th PRESTORE command."
     help<-1> = "PR#/any      - Change the #th PRESTORE command."
     help<-1> = "               where / - is any delimiter character which will also be"
     help<-1> = "                         used as the command separator."
     help<-1> = "QUIT (Q)     - QUIT - EXIT the editor."
     help<-1> = "QUITK (QK)   - QuitKill - EXIT the editor, abandon any active SELECT list."
     help<-1> = "R            - Replace the line with prompted for text."
     help<-1> = "R any        - REPLACE this line with 'any'."
     help<-1> = "R#/any       - REPLACE # lines with 'any'."
     help<-1> = "R///         - CHANGE one or more lines (same as C/// command.)"
     help<-1> = "RA           - Show last 20 'CHANGE' commands."
     help<-1> = "RA#          - Repeat #th 'CHANGE' command and move it to #1."
     help<-1> = "RELEASE      - RELEASE the update record LOCK for this file."
     help<-1> = "S            - Show last 20 'LOCATE' commands."
     help<-1> = "S#           - Repeat #th 'LOCATE' command and move it to #1."
     help<-1> = "SAVE         - SAVE a copy of this record under the original name."
     help<-1> = "SAVE name    - SAVE a copy of this record under the specified 'name'."
     help<-1> = "SAVE f name  - SAVE a copy of this record as record 'name' in file 'f'."
     help<-1> = "SEQ#////     - Build a sequence. Format is:"
     help<-1> = "               SEQ#/from/base/inc/cols"
     help<-1> = "               where    / - is any delimiter character."
     help<-1> = "                        # - number of lines to CHANGE (default 1)."
     help<-1> = "                     from - is the character string to be replaced."
     help<-1> = "                     base - is the start number (defaults to 1)."
     help<-1> = "                      inc - is the increment (defaults to 1)."
     help<-1> = "                     cols - restricts the change to a column range."
     help<-1> = "SHOW         - toggle overriding 'S'how flag for 'C' command."
     help<-1> = "SORT seq     - Sort the predefined block (seq defaults to 'AL')"
     help<-1> = "SPACE ON/OFF - Switch SPACE flag for L, LA, LN, LNA commands"
     help<-1> = "               If neither ON nor OFF is used, then toggle SPACE flag."
     help<-1> = "               OFF means that the commands will ignore spaces and tabs."
     help<-1> = "SPOOL        - SPOOL entire record to PRINTER."
     help<-1> = "SPOOL#       - SPOOL '#' lines to the PRINTER."
     help<-1> = "SPOOLHELP    - SPOOL the HELP listing to the default PRINTER."
     help<-1> = "STAMP        - INSERT a 'last modified' stamp into the record, which"
     help<-1> = "               begins with a '*' (for BASIC 'comment'), and contains the"
     help<-1> = "               account name, LOGIN name (if different from account name),"
     help<-1> = "               date and time. Used to mark when record was last changed."
     help<-1> = "SV           - Synonym for SAVE"
     help<-1> = "T            - Set current line to the TOP (before first line)."
     help<-1> = "TC#          - Change '#' lines to text or mixed case (default 1)."
     help<-1> = "TC# any        Comments and quoted strings are unchanged."
     help<-1> = "TRIM#        - TRIM '#' lines (default 1)."
     help<-1> = "TRIMB#       - TRIMB '#' lines (default 1)."
     help<-1> = "TRIMF#       - TRIMF '#' lines (default 1)."
     help<-1> = "UC#          - Change '#' lines to upper case (default 1)."
     help<-1> = "UC# any        Comments and quoted strings are unchanged."
     help<-1> = "V            - Version information"
$ifdef universe
     help<-1> = "VLIST        - Allows viewing a VLIST output (UniVerse only)"
$endif
     help<-1> = "WHERE (W)    - Show the item and file being edited."
     help<-1> = "WM           - Show or change the word marker."
     help<-1> = "X            - QuitKill - EXIT the editor, abandon any active SELECT list."
     help<-1> = "XEQ          - The XEQ command allows a user to execute any legal PERFORM"
     help<-1> = "                command from within the editor. Upon completion of the"
     help<-1> = "                command, control will be returned back to the editor."
     help<-1> = "XTD any      - Convert hexadecimal string 'any' to decimal and display it."
     help<-1> = "/any         - Same as L99999999/any - NOTE you are left at the bottom"
     help<-1> = ".A# any      = APPEND 'any' to command '#' (default 1)."
     help<-1> = ".C#///       - CHANGE stack command '#' (default 1). Syntax is like 'C'."
     help<-1> = ".D#          - DELETE stack command '#' (default 1)."
     help<-1> = ".I# any      - INSERT 'any' at stack position '#' (default 1)."
     help<-1> = ".L#          - LIST on the CRT the last '#' stack commands."
     help<-1> = ".R#          - RECALL (copy) command '#' to stack position 1."
     help<-1> = ".S# n m      - SAVE stack n to m as prestore '#' (all default to 1)."
     help<-1> = ".U#          - UPCASE stack command '#' (default 1)."
     help<-1> = ".X#          - EXECUTE stack command '#' (default 1)."
     help<-1> = "               The command will be put in stack position 1."
     help<-1> = "+#           - Advance current line POINTER by '#' lines."
     help<-1> = "-#           - Back up current line POINTER by '#' lines."
     help<-1> = "#            - Set the current line POINTER to the '#' line."
     help<-1> = "<#           - Sets the starting block pointer to # (current line default)"
     help<-1> = ">#           - Sets the ending block pointer to # (current line default)"
     help<-1> = "<># #        - Set both block pointers at the same time"
     help<-1> = "^            - Switch UP ARROW on/off to display non-printing characters as"
     help<-1> = "               ^nnn where nnn is the decimal equivalent of ASCII code."
     help<-1> = "?            - Show various parameters."
     help<-1> = ""
     return

indenter:
*   SUBROUTINE INDENTER(THIS,XXNO,MAT FR)
* S/R - Indents program logic
*   DIM FR(10)
     marg = fr(1) ;* the margin
     dent = fr(2) ;* the indentation
     supp = fr(6) ;* flag - suppress '!' output
     astx = not(fr(9)) ;* flag - keep '*' comments on page edge
     suit = not(fr(10)) ;* flag - indent 'CASE' statements
     equ dead to 'ACDGHIJKMOPQSVXYZ'
     push = 'LOOP\WHILE\UNTIL\FOR\THEN\ELSE\BEGIN\LOCKED\ERROR'
$ifdef changeworks
     push = convert('\',am,push)
     pull = convert('\',am,'UNTIL\WHILE\REPEAT\NEXT\END')
$else
     chit = push ; old1 = '\' ; new1 = am
     gosub changeit ; push = chit
     chit = pull ; old1 = '\' ; new1 = am
     gosub changeit ; pull = chit
$endif
     skip = ';:" (' : "'"
     marx = '\"' : "'"
     bang = false
     xxno = dcount(this,am)
     dim part(100)
     matparse part from this, am
     this = ''
     bite = ''
     first = true
     for xx = 1 to xxno
        there = rem(xx,100)
        if not(there) then
           if first then this = bite else this = this:am:bite
           first = false
           bite = ''
           thisline = part(100)
           temp = part(0)
           matparse part from temp, am
           if not(supp) then bang = true; crt '!':
        end else thisline = part(there)
        if trim(thisline) = '' then
           if first then bite = '' else bite = ''
           continue
        end
        note = false
        wcnt = 0; more = 0; less = 0
        mark = ''; tags = ''; lastword = ''
        zz = 1
$ifdef trimfbworks
        thisline = trimf(thisline)
$else
        chit = thisline ; gosub trimfit ; thisline = chit
$endif
        if thisline matches '1N0N"*"0X' then
           temp = field(thisline,'*',1)
           temp = thisline[col2(),len(thisline)]
           thisline = field(thisline,'*',1):' ':temp
        end
        left = field(thisline,' ',1)
        if num(left) or left[len(left),1] = ':' then
           if not(index(left,'=',1)) then tags = left
        end
        if tags gt '' then
$ifdef trimfbworks
           thisline = trimf(thisline[col2()+1,len(thisline)])
$else
           chit = thisline[col2()+1,len(thisline)]
           gosub trimfit
           thisline = chit
$endif
        end
        zzno = len(thisline)
        loop
        while zz lt zzno and not(note) do
           loop
              thisun = thisline[zz,1]
              begin case
                 case mark = '' and index(marx,thisun,1)
                    mark = thisun
                 case mark ne ''
                    if thisun = mark then mark = ''
                 case wcnt and thisun = ';'
                    that = field(trim(thisline[zz+1,zzno]),' ',1)
                    that = oconv(that,'MCU')
                    if that ne 'REMOVE' then
                       if that[1,3] = 'REM' then that = ''
                       if that[1,1] = '*' then that = ''
                       if that[1,1] = '!' then that = ''
                    end
                    if that = '' then zz = zzno
                 case wcnt
                 case thisun = '!' or thisun = '$'
                    note = true; zz = zzno
                 case astx and thisun = '*'
                    note = true; zz = zzno
                 case oconv(field(thisline,' ',1),'MCU') = 'REMOVE'
                 case oconv(thisline[zz,3],'MCU') = 'REM'
                    note = true; zz = zzno
              end case
           while (index(skip,thisun,1) or mark) and zz lt zzno do
              zz += 1
           repeat
           left = zz
           loop
              thisun = thisline[zz,1]
           until index(skip,thisun,1) or zz gt zzno do
              zz += 1
           repeat
           word = oconv(thisline[left,zz-left],'MCU')
           wcnt += 1
           if wcnt ne 1 then
              if word = 'WHILE' or word = 'UNTIL' then word = ' '
              if word = 'NEXT' or word = 'REPEAT' then
                 word = ' '
                 more -= dent
              end
              if lastword = 'LOCKED' then more -= dent
           end
           if word = 'CASE' then
              if lastword ne 'BEGIN' and lastword ne 'END' then
                 more += dent
                 less += dent
              end
              if suit and lastword = 'BEGIN' then more += dent
              if suit and lastword = 'END' then less += dent
           end
           if not(index(dead,word[1,1],1)) then
              locate(word,pull;rubbish) then less += dent
              test = word ne 'THEN' and word ne 'ELSE' and word ne 'ERROR'
              that = trim(thisline[zz,zzno])
              if that[1,1] = ';' then
                 that = trim(that[2,zzno])[1,3]
                 if that ne 'REMOVE' then
                    if that[1,3] = 'REM' then that = ''
                    if that[1,1] = '*' then that = ''
                    if that[1,1] = '!' then that = ''
                 end
              end
              if test or that = '' then
                 locate(word,push;rubbish) then
                    more += dent
                 end
              end
              if that ne '' and lastword = '' then
                 if word = 'THEN' or word = 'ELSE' then
                    more -= dent
                    less -= dent
                 end
              end
           end
           lastword = word
        repeat
        marg -= less
        if tags = '' then pict = '' else pict = 'L#':(len(tags)+1)
        if marg gt len(tags) then pict = 'L#':marg
        if thisline = '!' or thisline = '$' then note = true
        if oconv(thisline,'MCU') = 'REM' then note = true
        if astx and thisline = '*' then note = true
        if note then
           if tags = '' then pict = '' else pict = 'L#':(len(tags)+1)
        end
$ifdef trimfbworks
        thisline = trimb(fmt(tags,pict):thisline)
$else
        chit = fmt(tags,pict):thisline
        gosub trimbit
        thisline = chit
$endif
        if first then
           bite = thisline
        end else bite = thisline
        marg += more
     next xx
     if bang then crt
     if bite ne '' then
        if first then this = bite else this = this:am:bite
     end
     that = ''
     return

get.rope:
* If the terminal doesn't support screen addressing, do simple input
     if not(editpage) then
        crt begn:stub:
        input rope:
        return
     end

* The following variables are used
*
*    BARE - what you are going to reveal (the displayed part)
*    CRAM - insert mode on (vs overwrite mode)
*    DPOT - display position
*    STEM - the prefix part of the display line
*    ICON - a picture of what you last displayed
*    PANS - the PAN increment
*    PPOT - the PAN origin position
*    SPAN - the PAN width
*    PULP - SEQ(COMI) - what you get from a key press
*    PURE - untouched, a virgin
*    POST - the stack position
*    TPOT - text position
*    XX   - loop variable
*    yy   - loop variable

     post = 0
     rope = ''

     loop
        if heap then
           stem = prmt:': '
           if post then stem = '>':fmt(post, "2'0'R"):stem[4,99]
        end else
           stem = '>':stub
        end
        span = wide - len(stem)
        pans = int(span/2)
        ppot = 1
        tpot = 1
        cram = true
        icon = space(wide)
        crt begn : ceol :
        pure = true

        loop

           begin case
              case tpot lt ppot ; ppot -= pans
              case tpot ge (ppot+span) ; ppot += pans
           end case

           bare = stem : rope[ppot, span]
           dpot = 0
           if icon ne bare then
              left = 0
              for xx = 1 to wide until left
                 if bare[xx,1] ne icon[xx,1] then left = xx
              next xx
             crt @(left-1):bare[left,wide]:ceol:@(dpot):
             icon = bare[1,wide]
           end

           dpot = len(stem) + tpot - ppot
           crt @(dpot) :

           gosub get.comd
           locate(comd,keys;cpos) then cpos = acts else cpos = 0

           pulp = seq(comd)
           if pulp lt 32 or pulp ge 128 then comd = ''

           if pure and cpos eq 0 and comd ne '' then
              rope = ''
              tpot = 1
           end
           pure = false

           begin case

              case heap and (cpos eq uarr or cpos eq upag)
                 if post lt dcount(stak, vm) then
                    post += 1
                    rope = stak<1,post>
                 end
                 exit

              case heap and (cpos eq darr or cpos eq dpag)
                 if post gt 1 then
                    post -= 1
                    rope = stak<1,post>
                 end else
                    post = 0
                    rope = ''
                 end
                 exit

              case cpos eq larr
                 if tpot gt 1 then tpot -= 1

              case cpos eq rarr
                 if tpot le len(rope) then tpot += 1

              case cpos eq lpag
                 tpot = 1

              case cpos eq rpag
                 tpot = len(rope) + 1

              case cpos eq escp
                 post = 0
                 rope = ''
                 exit

              case cpos eq delc
                 rope = rope[1, tpot - 1] : rope[tpot + 1, 999]

              case cpos eq delr
                 rope = rope[1, tpot - 1]

              case cpos eq back
                 if tpot gt 1 then
                    tpot -= 1
                    rope = rope[1, tpot - 1] : rope[tpot + 1, 999]
                 end

              case cpos eq carr
!IF NOT(HEAP) THEN RETURN
!$IFDEF TRIMFBWORKS
!                 ROPE = TRIMF(TRIMB(ROPE))
!$ELSE
!                 CHIT = ROPE
!                 GOSUB TRIMFIT; GOSUB TRIMBIT
!                 ROPE = CHIT
!$ENDIF
                 if heap then crt begn : ceol : ':' : rope:
                 return

              case cpos eq togg
                 cram = not(cram)
                 crt @(0):
                 if cram then crt '>': else crt '#':
                 crt @(dpot):

              case pulp eq 23         ;* ctrl-w
                 begin case
                    case rope eq oconv(rope,'MCL')
                       rope = oconv(rope,'MCU')
                    case rope eq oconv(rope,'MCT')
                       rope = oconv(rope,'MCL')
                    case rope eq oconv(rope,'MCU')
                       rope = oconv(rope,'MCT')
                    case 1
                       rope = oconv(rope,'MCU')
                 end case

              case pulp ge 32 and pulp lt 128
                 if cram then
                    rope = rope[1, tpot - 1] : comd : rope[tpot, 999]
                 end else
                    if tpot le len(rope)
                       then rope[tpot, 1] = comd
                       else rope := comd
                 end
                 tpot += 1

              case 1
                 crt bell:
           end case
        repeat
     repeat

     return