! File MOUSE:CLI %begin %option "-nons-nocheck-nodiag" %externalroutinespec define logical name(%string(255)name,equiv) %externalpredicatespec translated logical name(%string(*)%name s) %externalroutinespec define command symbol(%string(255)sym,equiv) %externalpredicatespec translated command symbol(%string(*)%name s) %externalroutinespec readline(%string(*)%name s) %externalroutinespec runprogram(%string(255)s) %externalroutinespec phex(%integer x) %externalroutinespec phex2(%integer x) %externalroutinespec set terminalmode(%integer mode) %constinteger noecho=1,notermecho=2,single=4 %ownstring(255)%namearrayname p('1':'8') %string(255)%fn percent(%integer n) %result = p(n) %unless p(n)==nil %result = "%".tostring(n) %end %routine definepercent(%integer n,%string(255)s) dispose(p(n)) %unless p(n)==nil p(n) == nil; %returnif s="" p(n) == new(p(n)); p(n) = s %end %string(255)%fn read interactive line - (%string(255)promptstring, initialstring) ! Read a line interactively from the terminal. ! Advanced line editing features include: ! Command history recall (including line-starting-with "..." matching) ! Cursor movement left/right ! Character deletion backwards/forwards ! Word and line repeat operations for last movement or deletion ! Character insertion or replacement ! To exploit the advanced editing features, the terminal must be ! of an "approved" type. Two distinct types are supported, namely ! ANSI-compatible (WY75, VT100/200, Facit) and Visual200-compatible. ! Which of these types of terminal is being used is determined dynamically, ! but instead of using a "who-are-you" enquiry sequence, the type is ! deduced from the first incoming escape sequence. If the first character ! immediately following the ESC is '[', ANSI mode is assumed. ! Line editing functions are assigned to the following keys: ! Return: Accept (the whole of) the current line. ! North/South: Recall a line from the history buffer. The recalled line ! must match that part of the current line to the left of ! the cursor (this condition is trivially met if the cursor ! is already at the beginning of the line). ! East/West: Move the cursor around within the current line. ! DEL: Erase the character to the left of the cursor. ! BS: Erase the character under the cursor, but if the cursor ! is at the end of the line, treat it as DEL. ! HOME: Repeat the last East/West/BS/DEL until the end of the ! line (in the current direction of travel) is reached. ! LF: Repeat the last East/West/BS/DEL as far as the next word ! boundary (a word is a sequence of alphanumerics). ! CAN(^X): Erase the current line completely. ! ^R: Re-display the current line. ! ^A: Switch between insert and replace mode. ! EOT(^D): Return a blank-line-result, which may be used by the ! calling code to take special action; normally blank ! lines are ignored. %constinteger maxwidth=131,maxhist=40 %recordformat hf(%record(hf)%name next,last,%string(maxwidth)s) %ownrecord(hf)%name hput==nil,hget==nil %constinteger- eot=4, bs=8, tab=9, lf=10, cr=13, esc=27, del=127, toggle='A'-64, refresh='R'-64, can='X'-64, north='A'+128, south='B'+128, east='C'+128, west='D'+128, home='H'+128, clearline='K'+128, delete='O'+128, ansidelete='P'+128, ansiinsert='@'+128, inserton='i'+128, insertoff='j'+128 %owninteger last=0 %constinteger unknown=-1,visual=0,ansi=1 %owninteger terminal=unknown %constinteger insertmode=1,overwritemode=0 %owninteger mode=insertmode %bytearray b(0:maxwidth) %string(maxwidth)%name line %bytename maxcol %integer col,sym %routine setup ! initialise (circular) history buffer. ! this uses a doubly linked list with two pointers: ! HPUT is where the current command is stored, ! HGET is reset to HPUT between commands, and moves backwards ! and forwards through the buffer during editing as the ! North/South keys are used. %integer i %record(hf)%namearray h(0:maxhist-1) h(i-1) == new(hput) %for i = 1,1,maxhist %for i = 0,1,maxhist-1 %cycle h(i)_s = "" h(i)_last == h(rem(i-1+maxhist,maxhist)) h(i)_next == h(rem(i+1,maxhist)) %repeat hput == h(0); hget == hput %end %routine putsym(%integer sym) ! output ordinary (control or printing) character or escape sequence %if sym&128#0 %start %returnif terminal=unknown %if terminal=ansi %start sym = ansidelete %if sym=delete sym = ansiinsert %if sym=inserton %returnif sym=insertoff %finish printsymbol(esc) printsymbol('[') %if terminal=ansi %finish printsymbol(sym&127) %end %routine newline printsymbol(cr);printsymbol(lf) %End %integerfn getsym ! input ordinary character or escape sequence and deduce terminal type %integer sym sym = readsymbol&127 sym = sym!!(cr!!lf) %if sym=cr %or sym=lf %result=sym %unless sym=esc terminal = visual { %if terminal=unknown sym = readsymbol&127 terminal = ansi %and sym = readsymbol&127 %if sym='[' %result = sym!128 %end %routinespec right %routine left %if col<=0 %start col = 0 right %while col=maxcol %start col = maxcol left %while col>0 %return %finish col = col+1 putsym(east) %end %routinespec deleteforward %routine deletebackward %integer i %if col<=0 %start col = 0; %returnif col>=maxcol deleteforward; %return %finish last = del putsym(bs) %if col=maxcol %start putsym(' '); putsym(bs) %else putsym(delete) %finish b(i) = b(i+1) %for i = col,1,maxcol-1 col = col-1; maxcol = maxcol-1 %end %routine deleteforward %integer i %if col>=maxcol %start col = maxcol; %returnif col<=0 deletebackward; %return %finish last = bs putsym(delete) b(i) = b(i+1) %for i = col+1,1,maxcol maxcol = maxcol-1 %end %routine again %if last=west %then left %else- %if last=east %then right %else- %if last=del %then deletebackward %else- %if last=bs %then deleteforward %end %routine insert(%integer sym) ! deal with ordinary printing character %integer i %if col=maxcol %or mode=overwritemode %start %returnif col=maxwidth; col = col+1 b(col) = sym; putsym(sym) maxcol = col %if col>maxcol %else %returnif maxcol=maxwidth b(i+1) = b(i) %for i = maxcol,-1,col+1 putsym(inserton); putsym(sym); putsym(insertoff) col = col+1; maxcol = maxcol+1 b(col) = sym %finish %end %predicate alpha(%integer pos) %integer sym %falseif pos<=0 %falseif pos>maxcol sym = b(pos) %trueif 'a'<=sym!32<='z' %trueif '0'<=sym<='9' %false %end %routine locate(%integer ns) ! Scan the history buffer in the direction specified by NS (North/South). ! Replace the current line with the first suitable history line we find, ! if any. "Suitable" means it must start with that part of the current ! line to the left of the cursor (COL). %integer i %record(hf)%name pos %string(maxwidth)match match = line; length(match) = col; pos == hget %cycle %if ns=north %then hget == hget_last %else hget == hget_next %returnif pos==hget %if length(hget_s)>col %and substring(hget_s,1,col)=match %start line = hget_s putsym(cr); putsym(clearline); printstring(promptstring) printstring(line) i = maxcol putsym(bs) %and i = i-1 %while i>col %return %finish %repeat %end ! Main body of READ INTERACTIVE LINE maxcol == b(0) line == string(addr(b(0))) setup %if hput==nil set terminalmode(single+noecho+notermecho); prompt("") line = initialstring; col = maxcol printstring(promptstring); printstring(line) %cycle sym = getsym %if sym=eot %start {Return null Line} newline; line = ""; %exit %finish %if sym=cr %start {Return current line if non-empty} newline %if maxcol#0 %start hput_s = line; hput == hput_next; hget == hput; %exit %finish putsym(cr); printstring(promptstring) %elseif sym=north %or sym=south {History scan up/down} locate(sym); last = 0 %elseif sym=west %or sym=east %or sym=del %or sym=bs {L/R/DB/DF} last = sym; again %elseif sym=home {Repeat to end of line} %if last=west %or last=del %start again %while col>0 %elseif last=east %or last=bs again %while col=maxcol %or col<=0 again %repeatuntil alpha(col+1) %andnot alpha(col) ! %elseif last=west ! %cycle ! %exitif col<=0 ! left ! %repeatuntil alpha(col+1) %andnot alpha(col) %elseif last=bs %cycle %exitif col>=maxcol %ornot alpha(col+1) again %repeat %cycle %exitif col>=maxcol %or alpha(col+1) again %repeat %else {last=del} %cycle %exitif col<=0 %ornot alpha(col) again %repeat %cycle %exitif col<=0 %or alpha(col-1) again %repeat %finish %elseif sym=can %if terminal=unknown %start delete backward %while col>0 %else putsym(cr); putsym(clearline); printstring(promptstring) col = 0; maxcol = 0 %finish %elseif sym=refresh %if terminal=unknown %then newline %elsestart putsym(cr); putsym(clearline) %finish printstring(promptstring); printstring(line) col = maxcol %elseif sym=toggle mode = mode!!(insertmode!!overwritemode) %elseif ' '<=sym0 %and l[1]=' ' {strip leading spaces} v = 0 %while v2 %and v[2]='!' %start printstring(substring(verb,2,v);line); newline %else verify(verb.line) %finish %elseif k='=' {symbol or logical name (un-)definition} verify(verb.line) %if l=1 %start {single = without RHS} define command symbol(verb,"") %elseif l[2]='=' {double =} %if l=2 %start {without RHS} define logical name(verb,"") %else {with RHS} line = substring(line,3,l) %if line="?" %start line = verb %if translated logical name(line) %start printstring(verb;"==";line); newline %finish %else define logical name(verb,line) %finish %finish %else {single = with RHS} line = substring(line,2,l) %if line="?" %start line = verb %if translated command symbol(line) %start printstring(verb;"=";line); newline %finish %else define command symbol(verb,line) %finish %finish %elseif v[1]='@' {obey file} verify(verb.line) verb = substring(verb,2,v).".com" line = substring(line,2,l) %if l>0 %and l[1]=' ' obeyfile(verb,line) %else %if v[v]='_' %start {suppress symbol translation} v = v-1 %elseif lives>0 %and translated command symbol(verb) lives = lives-1 line = verb.line; ->again %finish verify(verb.line) line = substring(line,2,l) %if l>0 %and l[1]=' ' cliparam = line prompt(":") run program(verb) %finish %end %routine obeyfile(%string(255)file,param) %string(255)%namearray percent('1':'8') %string(255)s %integer i %on 9 %start closeinput %return %finish percent(i) == nil %for i = '1',1,'8' p == percent %unless file="" %start openinput(0,file) %finish %if param -> param.("/").s %start closeoutput openoutput(0,s) %finish param = param."," %unless param="" i = '1' %while i<='8' %and param -> s.(",").param %cycle define percent(i,s) i = i+1 %repeat %cycle p == percent %if file="" %then s = readcommand %elsestart prompt(promptstring) %cycle i = nextsymbol %exitif ' '