-- LaurelTTYEditor.mesa
-- Edited by Brotz, June 25, 1982 5:26 PM
-- Edited by Taft, May 8, 1983 2:11 PM

DIRECTORY
Answer,
Ascii,
Editor,
exD: FROM "ExceptionDefs",
inD: FROM "InteractorDefs",
Inline,
intCommon,
LaurelTTYDefs,
opD: FROM "OperationsDefs",
Storage,
String,
vmD: FROM "VirtualMgrDefs";

LaurelTTYEditor: PROGRAM
IMPORTS Answer, Editor, exD, inD, Inline, intC: intCommon, opD, Storage, String, vmD
EXPORTS LaurelTTYDefs =

BEGIN


commandBuffer: vmD.ComposedMessagePtr ← NIL;
buffer: PUBLIC vmD.ComposedMessagePtr ← NIL;
pointer: vmD.CharIndex ← 0;
qBuffer: ARRAY [0 .. 9] OF vmD.ComposedMessagePtr
← [NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL];
qRegister: ARRAY [0 .. 9] OF LONG INTEGER ← ALL[0];
edited: BOOLEAN ← FALSE;
filename: STRING ← [64];


TTYEditor: PUBLIC PROCEDURE [h: LaurelTTYDefs.Handle] =
BEGIN
OPEN h;
char: CHARACTER;
IF buffer = NIL THEN ZeroBuffer[@buffer];
DO
WriteLine[""L]; WriteChar[’*]; SendNow[];
ZeroBuffer[@commandBuffer];
DO
length: vmD.CharIndex ← vmD.GetMessageSize[commandBuffer];
char ← ReadChar[];
SELECT char FROM
Ascii.ControlA, Ascii.BS =>
BEGIN
IF length > 0 THEN
BEGIN
WriteChar[’/];
WriteChar[vmD.GetMessageChar[commandBuffer, length - 1]];
SendNow[];
vmD.UnAppendMessageChar[commandBuffer];
END
ELSE BEGIN
vmD.AppendMessageChar[commandBuffer, Ascii.BS];
WriteLine[""L];
[] ← ExecuteCommandString[h, commandBuffer];
EXIT;
END;
END;
Ascii.ControlQ => {WriteString[" XXX"L]; EXIT};
Ascii.ControlR =>
BEGIN
WriteLine[""L];
FOR i: vmD.TOCIndex IN [0 .. vmD.GetMessageSize[commandBuffer]) DO
WriteChar[vmD.GetMessageChar[commandBuffer, i]];
ENDLOOP;
SendNow[];
END;
Ascii.ESC =>
BEGIN
WriteLine["$"L];
IF ExecuteCommandString[h, commandBuffer] THEN RETURN ELSE EXIT;
END;
ENDCASE =>
BEGIN
vmD.AppendMessageChar[commandBuffer, char];
IF length = 0 AND char = Ascii.LF THEN
{WriteLine[""L]; [] ← ExecuteCommandString[h, commandBuffer]; EXIT};
WriteChar[char];
SendNow[];
END;
ENDLOOP;
ENDLOOP;
END; -- of TTYEditor --


ExecuteCommandString: PROCEDURE
[h: LaurelTTYDefs.Handle, cm: vmD.ComposedMessagePtr]
RETURNS [return: BOOLEAN] =
BEGIN
OPEN h;
arg1, arg2: LONG INTEGER ← 0;
haveArg1, haveArg2: BOOLEAN ← FALSE;

Term: PROCEDURE RETURNS [n: LONG INTEGER, found: BOOLEAN] =
BEGIN
unaryMinus: BOOLEAN ← FALSE;
found ← FALSE;
n ← 0;
UNTIL index >= vmD.GetMessageSize[cm] DO
char: CHARACTER ← vmD.GetMessageChar[cm, index];
SELECT char FROM
IN [’0 .. ’9] =>
BEGIN
found ← TRUE;
n ← n * 10 + (char - ’0);
index ← index + 1;
END;
’- =>
BEGIN
IF found THEN EXIT;
IF unaryMinus THEN ERROR SyntaxError;
index ← index + 1;
unaryMinus ← TRUE;
END;
’B, ’b =>
BEGIN
IF found OR unaryMinus THEN ERROR SyntaxError;
found ← TRUE;
index ← index + 1;
n ← 0;
END;
’Q, ’q =>
BEGIN
IF found THEN ERROR SyntaxError;
index ← index + 1;
IF index >= vmD.GetMessageSize[cm] THEN ERROR SyntaxError;
char ← vmD.GetMessageChar[cm, index];
IF char ~IN [’0 .. ’9] THEN {WriteLine["Q-registers 0 - 9 only"L]; ERROR ErrorNoted};
index ← index + 1;
n ← qRegister[char - ’0];
found ← TRUE;
END;
’% =>
BEGIN
IF found THEN ERROR SyntaxError;
index ← index + 1;
IF index >= vmD.GetMessageSize[cm] THEN ERROR SyntaxError;
char ← vmD.GetMessageChar[cm, index];
IF char ~IN [’0 .. ’9] THEN {WriteLine["Q-registers 0 - 9 only"L]; ERROR ErrorNoted};
index ← index + 1;
n ← qRegister[char - ’0] ← qRegister[char - ’0] + 1;
found ← TRUE;
END;
’Z, ’z =>
BEGIN
IF found OR unaryMinus THEN ERROR SyntaxError;
found ← TRUE;
index ← index + 1;
n ← vmD.GetMessageSize[buffer];
END;
’. =>
BEGIN
IF found THEN ERROR SyntaxError;
found ← TRUE;
index ← index + 1;
n ← pointer;
END;
’\ =>
BEGIN
IF found THEN EXIT;
found ← TRUE;
index ← index + 1;
UNTIL pointer >= vmD.GetMessageSize[buffer] DO
char ← vmD.GetMessageChar[buffer, pointer];
IF char ~IN [’0 .. ’9] THEN EXIT;
n ← n * 10 + (char - ’0);
pointer ← pointer + 1;
ENDLOOP;
EXIT;
END;
’: =>
BEGIN
IF found OR unaryMinus THEN ERROR SyntaxError;
index ← index + 1;
IF index >= vmD.GetMessageSize[cm] THEN ERROR SyntaxError;
char ← vmD.GetMessageChar[cm, index];
index ← index + 1;
arg1 ← 1;
haveArg1 ← TRUE;
SELECT char FROM
’S, ’s => {found ← TRUE; n ← IF SearchOperation[FALSE] THEN -1 ELSE 0};
’R, ’r => {found ← TRUE; n ← IF SearchOperation[TRUE] THEN -1 ELSE 0};
ENDCASE => ERROR SyntaxError;
END;
’S, ’s =>
IF iterationLevel > 0 THEN
BEGIN
IF found OR unaryMinus THEN ERROR SyntaxError;
index ← index + 1;
arg1 ← 1;
haveArg1 ← TRUE;
found ← TRUE;
n ← IF SearchOperation[FALSE] THEN -1 ELSE 0;
END
ELSE EXIT;
’R, ’r =>
IF iterationLevel > 0 THEN
BEGIN
IF found OR unaryMinus THEN ERROR SyntaxError;
index ← index + 1;
arg1 ← 1;
haveArg1 ← TRUE;
found ← TRUE;
n ← IF SearchOperation[TRUE] THEN -1 ELSE 0;
END
ELSE EXIT;
’( =>
BEGIN
index ← index + 1;
[n, found] ← Expression[];
IF index >= vmD.GetMessageSize[cm] OR vmD.GetMessageChar[cm, index] # ’) THEN
ERROR SyntaxError;
index ← index + 1;
END;
ENDCASE => EXIT;
ENDLOOP;
IF unaryMinus THEN
BEGIN
n ← (IF found THEN -n ELSE -1);
found ← TRUE;
END;
END; -- of Term --

Expression: PROCEDURE RETURNS [n: LONG INTEGER, found: BOOLEAN] =
BEGIN
termFound: BOOLEAN;
termN: LONG INTEGER;
[n, found] ← Term[];
UNTIL index >= vmD.GetMessageSize[buffer] DO
char: CHARACTER ← vmD.GetMessageChar[cm, index];
SELECT char FROM
’+ =>
BEGIN
index ← index + 1;
[termN, termFound] ← Term[];
IF ~termFound THEN ERROR SyntaxError ELSE n ← n + termN;
END;
’- =>
BEGIN
index ← index + 1;
[termN, termFound] ← Term[];
IF ~termFound THEN ERROR SyntaxError ELSE n ← n - termN;
END;
’* =>
BEGIN
index ← index + 1;
[termN, termFound] ← Term[];
IF ~termFound THEN ERROR SyntaxError ELSE n ← n * termN;
END;
’/ =>
BEGIN
index ← index + 1;
[termN, termFound] ← Term[];
IF ~termFound THEN ERROR SyntaxError
ELSE IF termN # 0 THEN n ← n / termN
ELSE {WriteLine["Divide by 0!"L]; ERROR ErrorNoted};
END;
ENDCASE => EXIT;
ENDLOOP;
END; -- of Expression --

ACommand: PROCEDURE =
BEGIN -- Answer message n
answerError: BOOLEAN ← FALSE;
toc: vmD.TOCHandle = intC.tocTextNbr.toc;
dm: vmD.DisplayMessagePtr;
key: CARDINAL;
index: vmD.TOCIndex;

GetChar: PROCEDURE [index: CARDINAL] RETURNS [CHARACTER] =
{RETURN[vmD.GetMessageChar[dm, index]]};

PutBlock: PROCEDURE [block: Answer.Block] =
BEGIN
[] ← vmD.InsertSubstringInMessage
[buffer, LOOPHOLE[Inline.LowHalf[block.buffer] - 2, STRING], 0, block.length];
END; -- of PutBlock --

GetPages: PROCEDURE [nPages: CARDINAL] RETURNS [LONG POINTER] =
{RETURN[Storage.Pages[nPages]]};

FreePages: PROCEDURE [p: LONG POINTER] =
{Storage.FreePages[Inline.LowHalf[p]]};

answerBody: STRING ="Message

"L;

-- main body of ACommand

IF ~haveArg1 THEN ERROR SyntaxError;
IF edited THEN
BEGIN
WriteString["Message has been edited but not saved! "L];
IF ~inD.Confirm[1] THEN RETURN
END;
index ← Inline.LowHalf[arg1];
key ← vmD.WaitForLock[toc];
IF index ~IN [1 .. toc.indexFF) THEN
{vmD.UnlockTOC[toc, key]; WriteString["Illegal message number."L]; ERROR ErrorNoted};
dm ← vmD.AllocateDisplayMessageObject[];
vmD.LoadDisplayMessage[toc, key, index, dm];
vmD.DeleteRangeInMessage[[0, vmD.GetMessageSize[buffer], buffer]];
vmD.StartMessageInsertion[buffer, 0];
answerError ← Answer.MakeHeader
[getChar: GetChar,
getLength: vmD.GetMessageSize[dm],
putBlock: PutBlock,
getPages: GetPages,
freePages: FreePages,
userName: intC.user.name,
userRegistry: intC.user.registry,
cForCopies: intC.cForCopies];
vmD.FlushDisplayMessage[dm, key];
vmD.UnlockTOC[toc, key];
vmD.FreeVirtualMessageObject[dm];
[] ← vmD.InsertStringInMessage[buffer, answerBody];
vmD.StopMessageInsertion[buffer];
pointer ← 0;
edited ← FALSE;
END; -- of ACommand --

CCommand: PROCEDURE =
BEGIN
IF ~haveArg1 THEN arg1 ← 1;
IF arg1 > 0 AND arg1 + pointer > vmD.GetMessageSize[buffer]
OR arg1 < 0 AND arg1 + pointer < 0 THEN
{WriteString["Off the edge!"L]; ERROR ErrorNoted};
pointer ← Inline.LowHalf[arg1 + pointer];
END; -- of CCommand --

DCommand: PROCEDURE =
BEGIN
IF ~haveArg1 THEN arg1 ← 1;
IF arg1 > 0 THEN
BEGIN
IF arg1 + pointer > vmD.GetMessageSize[buffer] THEN
{WriteString["Off the edge!"L]; ERROR ErrorNoted};
vmD.DeleteRangeInMessage[[pointer, Inline.LowHalf[arg1 + pointer], buffer]];
END
ELSE BEGIN
IF arg1 + pointer < 0 THEN {WriteString["Off the edge!"L]; ERROR ErrorNoted};
vmD.DeleteRangeInMessage[[Inline.LowHalf[arg1 + pointer], pointer, buffer]];
pointer ← Inline.LowHalf[arg1 + pointer];
END;
edited ← TRUE;
END; -- of DCommand --

EXCommand: PROCEDURE =
BEGIN
index ← vmD.GetMessageSize[commandBuffer];
IF edited THEN
BEGIN
WriteString["The text has been edited, but hasn’t been saved. Still want to exit? "L];
IF NextCharConfirms[] THEN WriteString["Yes. "L] ELSE {WriteLine["XXX"L]; RETURN};
END;
WriteLine["Exiting editor."L];
ERROR Exit;
END; -- of EXCommand --

GCommand: PROCEDURE =
BEGIN
char: CHARACTER;
IF index >= vmD.GetMessageSize[cm] THEN ERROR SyntaxError;
char ← vmD.GetMessageChar[cm, index];
IF char ~IN [’0 .. ’9] THEN {WriteLine["Q-registers 0 - 9 only"L]; ERROR ErrorNoted};
index ← index + 1;
vmD.InsertRangeInMessage
[pointer, buffer, [0, vmD.GetMessageSize[qBuffer[char - ’0]], qBuffer[char - ’0]]];
pointer ← pointer + vmD.GetMessageSize[qBuffer[char - ’0]];
END; -- of GCommand --

ICommand: PROCEDURE =
BEGIN
char: CHARACTER;
vmD.StartMessageInsertion[buffer, pointer];
IF haveArg1 THEN
BEGIN
char ← Inline.LowHalf[arg1];
vmD.InsertMessageChar[buffer, char];
pointer ← pointer + 1;
END
ELSE
UNTIL index >= vmD.GetMessageSize[cm] DO
char ← vmD.GetMessageChar[cm, index];
index ← index + 1;
IF char = Ascii.ControlD THEN EXIT;
vmD.InsertMessageChar[buffer, char];
pointer ← pointer + 1;
ENDLOOP;
vmD.StopMessageInsertion[buffer];
edited ← TRUE;
END; -- of ICommand --

JCommand: PROCEDURE =
BEGIN
SELECT TRUE FROM
~haveArg1 => pointer ← 0;
haveArg2 => ERROR SyntaxError;
arg1 IN [0 .. vmD.GetMessageSize[buffer]] => pointer ← Inline.LowHalf[arg1];
ENDCASE => {WriteLine["Off the edge!"L]; ERROR ErrorNoted};
END; -- of JCommand --

KCommand: PROCEDURE =
BEGIN
IF ~haveArg1 THEN arg1 ← 1;
IF haveArg2 THEN
BEGIN
arg1 ← MIN[MAX[arg1, 0], vmD.GetMessageSize[buffer]];
arg2 ← MIN[ABS[arg2], vmD.GetMessageSize[buffer]];
IF arg2 >= arg1 THEN
BEGIN
pointer ← Inline.LowHalf[arg1];
vmD.DeleteRangeInMessage[[pointer, Inline.LowHalf[arg2], buffer]];
END;
SendNow[];
END
ELSE BEGIN
lines: CARDINAL ← 0;
arg1 ← ABS[arg1];
FOR i: vmD.CharIndex IN [pointer .. vmD.GetMessageSize[buffer]) DO
IF vmD.GetMessageChar[buffer, i] = Ascii.CR THEN lines ← lines + 1;
IF lines = arg1 THEN {vmD.DeleteRangeInMessage[[pointer, i + 1, buffer]]; EXIT};
REPEAT
FINISHED =>
vmD.DeleteRangeInMessage[[pointer, vmD.GetMessageSize[buffer], buffer]];
ENDLOOP
END;
edited ← TRUE;
END; -- of KCommand --

LCommand: PROCEDURE =
BEGIN
start, end: vmD.CharIndex;
IF ~haveArg1 THEN arg1 ← 1;
[start, end] ← MapLinesToRange[Inline.LowHalf[arg1]];
pointer ← IF arg1 > 0 THEN end ELSE start;
END; -- of LCommand --

MCommand: PROCEDURE =
BEGIN
char: CHARACTER;
IF index >= vmD.GetMessageSize[cm] THEN ERROR SyntaxError;
char ← vmD.GetMessageChar[cm, index];
IF char ~IN [’0 .. ’9] THEN {WriteLine["Q-buffers 0 - 9 only"L]; ERROR ErrorNoted};
index ← index + 1;
IF qBuffer[char - ’0] = NIL THEN {WriteLine["Q-buffer is empty!"L]; ERROR ErrorNoted};
[] ← ExecuteCommandString[h, qBuffer[char - ’0]];
END; -- of MCommand --

NCommand: PROCEDURE =
BEGIN -- New form command --
string: STRING ← "Subject: xxx
To: xxx

xxx

"L;
IF edited THEN
BEGIN
WriteString["Message has been edited but not saved! "L];
IF ~inD.Confirm[1] THEN RETURN
END;
vmD.DeleteRangeInMessage[[0, vmD.GetMessageSize[buffer], buffer]];
vmD.StartMessageInsertion[buffer, 0];
[] ← vmD.InsertStringInMessage[buffer, string];
vmD.StopMessageInsertion[buffer];
pointer ← 0;
edited ← FALSE;
END; -- of NCommand --

RCommand: PROCEDURE =
BEGIN
IF ~SearchOperation[TRUE] THEN WriteLine["Search failed!"L];
END; -- of RCommand --

SearchOperation: PROCEDURE [replace: BOOLEAN] RETURNS [succeed: BOOLEAN] =
BEGIN
searchPattern: STRING ← [inD.maxBracketStringLength];
char: CHARACTER;
start: vmD.CharIndex ← pointer;
at, atEnd, end, insertStartIndex: vmD.CharIndex;
found: BOOLEAN;
IF ~haveArg1 THEN arg1 ← 1;
UNTIL index >= vmD.GetMessageSize[cm] DO
char ← vmD.GetMessageChar[cm, index];
index ← index + 1;
IF char = Ascii.ControlD THEN EXIT;
IF searchPattern.length >= searchPattern.maxlength THEN
{WriteLine["Search string too big!"L]; ERROR ErrorNoted};
searchPattern[searchPattern.length] ← char;
searchPattern.length ← searchPattern.length + 1;
ENDLOOP;
insertStartIndex ← index;
IF arg1 = 0 THEN {succeed ← TRUE; GO TO Return};
IF arg1 > 0 THEN
BEGIN
THROUGH [1 .. Inline.LowHalf[arg1]] DO
[found, at, atEnd]
← Editor.FindOperation[searchPattern, start, vmD.GetMessageSize[buffer], buffer];
IF ~found THEN {succeed ← FALSE; GO TO Return};
IF replace THEN
BEGIN
vmD.DeleteRangeInMessage[[at, atEnd, buffer]];
vmD.StartMessageInsertion[buffer, at];
UNTIL index >= vmD.GetMessageSize[cm] DO
char ← vmD.GetMessageChar[cm, index];
index ← index + 1;
IF char = Ascii.ControlD THEN EXIT;
vmD.InsertMessageChar[buffer, char];
at ← at + 1;
ENDLOOP;
vmD.StopMessageInsertion[buffer];
index ← insertStartIndex;
start ← pointer ← at;
END
ELSE start ← at + 1;
REPEAT
FINISHED => {IF ~replace THEN pointer ← atEnd; succeed ← TRUE; GO TO Return};
ENDLOOP;
END
ELSE BEGIN
arg1 ← ABS[arg1];
end ← pointer;
THROUGH [1 .. Inline.LowHalf[arg1]] DO
start ← end;
DO
[found, at, atEnd] ← Editor.FindOperation[searchPattern, start, end, buffer];
IF found THEN
BEGIN
IF atEnd > 0 THEN end ← atEnd - 1 ELSE end ← 0;
EXIT;
END;
IF start = 0 THEN {succeed ← FALSE; GO TO Return};
start ← start - 1;
ENDLOOP;
IF replace THEN
BEGIN
vmD.DeleteRangeInMessage[[at, atEnd, buffer]];
atEnd ← at;
vmD.StartMessageInsertion[buffer, at];
UNTIL index >= vmD.GetMessageSize[cm] DO
char ← vmD.GetMessageChar[cm, index];
index ← index + 1;
IF char = Ascii.ControlD THEN EXIT;
vmD.InsertMessageChar[buffer, char];
atEnd ← atEnd + 1;
ENDLOOP;
vmD.StopMessageInsertion[buffer];
pointer ← end ← atEnd;
index ← insertStartIndex;
END;
REPEAT
FINISHED => {pointer ← atEnd; succeed ← TRUE; GO TO Return};
ENDLOOP;
END;
EXITS
Return =>
IF replace THEN
UNTIL index >= vmD.GetMessageSize[cm] DO
char: CHARACTER ← vmD.GetMessageChar[cm, index];
index ← index + 1;
IF char = Ascii.ControlD THEN EXIT;
ENDLOOP;
END; -- of SearchOperation --

SCommand: PROCEDURE =
BEGIN
IF ~SearchOperation[FALSE] THEN WriteLine["Search failed!"L];
END; -- of SCommand --

TCommand: PROCEDURE =
BEGIN
start, end: vmD.CharIndex;
IF ~haveArg1 THEN arg1 ← 1;
IF haveArg2 THEN
BEGIN
start ← Inline.LowHalf[MAX[arg1, 0]];
end ← MIN[Inline.LowHalf[ABS[arg2]], vmD.GetMessageSize[buffer]];
END
ELSE [start, end] ← MapLinesToRange[Inline.LowHalf[arg1]];
FOR i: vmD.CharIndex IN [start .. end) DO
WriteChar[vmD.GetMessageChar[buffer, i]];
ENDLOOP;
SendNow[];
END; -- of TCommand --

UCommand: PROCEDURE =
BEGIN
char: CHARACTER;
IF ~haveArg1 THEN {WriteLine["Missing argument for U."L]; ERROR ErrorNoted};
IF index >= vmD.GetMessageSize[cm] THEN ERROR SyntaxError;
char ← vmD.GetMessageChar[cm, index];
index ← index + 1;
IF char ~IN [’0 .. ’9] THEN {WriteLine["Q-registers 0 - 9 only"L]; ERROR ErrorNoted};
qRegister[char - ’0] ← arg1;
END; -- of UCommand --

VCommand: PROCEDURE =
BEGIN
start, end: vmD.CharIndex;
IF ~haveArg1 THEN arg1 ← 1;
arg1 ← MAX[ABS[arg1], 1];
IF ~haveArg2 THEN arg2 ← arg1;
arg2 ← ABS[arg2];
[start, ] ← MapLinesToRange[Inline.LowHalf[1 - arg1]];
[ , end] ← MapLinesToRange[Inline.LowHalf[arg2]];
FOR i: vmD.CharIndex IN [start .. end) DO
WriteChar[vmD.GetMessageChar[buffer, i]];
ENDLOOP;
END; -- of VCommand --

XCommand: PROCEDURE =
BEGIN
char: CHARACTER;
start, end: vmD.CharIndex;
IF index >= vmD.GetMessageSize[cm] THEN ERROR SyntaxError;
char ← vmD.GetMessageChar[cm, index];
index ← index + 1;
IF char ~IN [’0 .. ’9] THEN {WriteLine["Q-registers 0 - 9 only"L]; ERROR ErrorNoted};
IF haveArg2 THEN
BEGIN -- m,nX command --
start ← Inline.LowHalf[arg1];
end ← Inline.LowHalf[arg2];
IF start > end THEN {WriteLine["Backward substring!"L]; ERROR ErrorNoted};
IF start > vmD.GetMessageSize[buffer] OR end > vmD.GetMessageSize[buffer]
THEN {WriteLine["Off the edge!"L]; ERROR ErrorNoted};
END
ELSE BEGIN
IF ~haveArg1 THEN arg1 ← 1;
[start, end] ← MapLinesToRange[Inline.LowHalf[arg1]];
END;
ZeroBuffer[@qBuffer[char - ’0]];
vmD.InsertRangeInMessage[0, qBuffer[char - ’0], [start, end, buffer]];
vmD.DeleteRangeInMessage[[start, end, buffer]];
pointer ← start;
END; -- of XCommand --

MapLinesToRange: PROCEDURE [n: INTEGER] RETURNS [start, end: vmD.CharIndex] =
BEGIN
lines: INTEGER ← 0;
IF n <= 0 THEN
BEGIN
end ← pointer;
n ← ABS[n];
FOR i: vmD.CharIndex DECREASING IN [0 .. pointer) DO
IF vmD.GetMessageChar[buffer, i] = Ascii.CR THEN
BEGIN
IF lines >= n THEN {start ← i + 1; EXIT};
lines ← lines + 1;
END;
REPEAT
FINISHED => start ← 0;
ENDLOOP;
END
ELSE BEGIN
start ← pointer;
FOR i: vmD.CharIndex IN [pointer .. vmD.GetMessageSize[buffer]) DO
IF vmD.GetMessageChar[buffer, i] = Ascii.CR THEN
BEGIN
lines ← lines + 1;
IF lines >= n THEN {end ← i + 1; EXIT};
END;
REPEAT
FINISHED => end ← vmD.GetMessageSize[buffer];
ENDLOOP
END;
END; -- of MapLinesToRange --

LineFeedCommand: PROCEDURE =
BEGIN
haveArg1 ← TRUE;
arg1 ← 1;
LCommand[];
TCommand[];
END; -- of LineFeedCommand --

BackSpaceCommand: PROCEDURE =
BEGIN
haveArg1 ← TRUE;
arg1 ← -1;
LCommand[];
TCommand[];
END; -- of BackSpaceCommand --

BackSlashCommand: PROCEDURE =
BEGIN
string: STRING ← [11]; -- -6553665536 --
IF ~haveArg1 THEN ERROR SyntaxError;
String.AppendLongDecimal[string, arg1];
vmD.StartMessageInsertion[buffer, pointer];
vmD.InsertStringInMessage[buffer, string];
vmD.StopMessageInsertion[buffer];
pointer ← pointer + string.length;
END; -- of BackSlashCommand --

SemiGCommand: PROCEDURE =
BEGIN -- Get message n into buffer --
index: vmD.TOCIndex;
dm: vmD.DisplayMessagePtr;
toc: vmD.TOCHandle ← intC.tocTextNbr.toc;
key: CARDINAL;
IF ~haveArg1 THEN ERROR SyntaxError;
index ← Inline.LowHalf[arg1];
key ← vmD.WaitForLock[toc];
IF index ~IN [1 .. toc.indexFF) THEN
{vmD.UnlockTOC[toc, key]; WriteString["Illegal message number."L]; ERROR ErrorNoted};
dm ← vmD.AllocateDisplayMessageObject[];
vmD.LoadDisplayMessage[toc, key, index, dm];
vmD.InsertRangeInMessage[pointer, buffer, [0, vmD.GetMessageSize[dm], dm]];
pointer ← pointer + vmD.GetMessageSize[dm];
vmD.FlushDisplayMessage[dm, key];
vmD.UnlockTOC[toc, key];
vmD.FreeVirtualMessageObject[dm];
END; -- of SemiGCommand --

SemiTCommand: PROCEDURE =
BEGIN
UNTIL index >= vmD.GetMessageSize[cm] DO
char: CHARACTER ← vmD.GetMessageChar[cm, index];
index ← index + 1;
IF char = Ascii.ControlD THEN EXIT;
WriteChar[char];
ENDLOOP;
END; -- of SemiTCommand --

SemiYCommand: PROCEDURE =
BEGIN
WriteString["Input file: "L];
[] ← ReadString[filename];
WriteLine[""L];
vmD.InsertFileInMessage[targetIndex: pointer, targetMessage: buffer, file: filename
! opD.FileError =>
BEGIN
WriteLine[IF reason = notFound THEN "File not found"L ELSE "File error."L];
GO TO Return;
END;
vmD.MessageOverflow =>
{WriteLine["Can’t yank: message would get too big."L]; GO TO Return}];
WriteLine["OK"L];
edited ← TRUE;
EXITS
Return => NULL;
END; -- of SemiYCommand --

SemiUCommand: PROCEDURE =
BEGIN
WriteString["Output file: "L];
[] ← ReadString[filename];
WriteLine[""L];
vmD.PutRangeInFile[from: vmD.MessageRange[0, vmD.GetMessageSize[buffer], buffer],
file: filename, concurrenceNeeded: TRUE, UserConcurs: UserConcurs
! opD.FileError =>
{WriteLine[IF reason = cancel THEN "Cancelled."L ELSE "File error"L]; GO TO done}];
WriteLine["OK"L];
edited ← FALSE;
EXITS
done => NULL;
END; -- of SemiUCommand --

StartIterationCommand: PROCEDURE =
BEGIN
IF iterationLevel = maxIterationLevel THEN
{WriteLine["Iteration level too deep!"L]; ERROR ErrorNoted};
iterationLevel ← iterationLevel + 1;
IF haveArg1 THEN
BEGIN
IF arg1 <= 0 THEN {WriteLine["Non-positive iteration constant!"L]; ERROR ErrorNoted};
iterationStack[iterationLevel].times ← Inline.LowHalf[arg1] - 1;
END
ELSE iterationStack[iterationLevel].times ← LAST[CARDINAL];
iterationStack[iterationLevel].index ← index;
END; -- of StartIterationCommand --

StopIterationCommand: PROCEDURE =
BEGIN
IF iterationLevel = 0 THEN ERROR SyntaxError;
IF iterationStack[iterationLevel].times = 0 THEN
{iterationLevel ← iterationLevel - 1; RETURN};
IF iterationStack[iterationLevel].times # LAST[CARDINAL] THEN
iterationStack[iterationLevel].times ← iterationStack[iterationLevel].times - 1;
index ← iterationStack[iterationLevel].index;
END; -- of StopIterationCommand --

IterationSkipCommand: PROCEDURE =
BEGIN
IF ~haveArg1 OR iterationLevel = 0 THEN ERROR SyntaxError;
IF arg1 >= 0 THEN
BEGIN
iterationLevel ← iterationLevel - 1;
UNTIL index >= vmD.GetMessageSize[cm] DO
char: CHARACTER ← vmD.GetMessageChar[cm, index];
index ← index + 1;
IF char = ’> THEN EXIT;
REPEAT
FINISHED => ERROR SyntaxError;
ENDLOOP;
END;
END; -- of IterationSkipCommand --

UserConcurs: PROCEDURE [exception: exD.Exception] RETURNS [confirmed: BOOLEAN] =
BEGIN
string: STRING ← [100];
exD.GetExceptionString[exception, string];
WriteString[string];
WriteString[" [Confirm]"L];
RETURN[NextCharConfirms[]]
END; -- of UserConcurs --

NextCharConfirms: PROCEDURE RETURNS [BOOLEAN] =
BEGIN
SELECT ReadChar[] FROM
Ascii.CR, Ascii.SP, Ascii.TAB, Ascii.ESC, ’y, ’Y => RETURN[TRUE];
ENDCASE;
RETURN[FALSE];
END; -- of NextCharConfirms --

ScanAndExecuteCommand: PROCEDURE =
BEGIN
char: CHARACTER;
haveArg2 ← FALSE;
[arg1, haveArg1] ← Expression[];
IF index >= vmD.GetMessageSize[cm] THEN RETURN;
char ← vmD.GetMessageChar[cm, index];
SELECT char FROM
’H, ’h =>
BEGIN
IF haveArg1 THEN ERROR SyntaxError;
arg1 ← 0;
arg2 ← vmD.GetMessageSize[buffer];
haveArg1 ← haveArg2 ← TRUE;
index ← index + 1;
END;
’, =>
BEGIN
IF ~haveArg1 THEN ERROR SyntaxError;
index ← index + 1;
[arg2, haveArg2] ← Expression[];
IF ~haveArg2 THEN ERROR SyntaxError;
END;
ENDCASE;
char ← vmD.GetMessageChar[cm, index];
index ← index + 1;
SELECT char FROM
’; =>
BEGIN
IF index >= vmD.GetMessageSize[cm] THEN ERROR UnrecognizedCommand;
char ← vmD.GetMessageChar[cm, index];
index ← index + 1;
SELECT char FROM
’G, ’g => SemiGCommand[];
’T, ’t => SemiTCommand[];
’U, ’u => SemiUCommand[];
’Y, ’y => SemiYCommand[];
Ascii.SP => IterationSkipCommand[];
ENDCASE => ERROR UnrecognizedCommand;
END;
’A, ’a => ACommand[];
’C, ’c => CCommand[];
’D, ’d => DCommand[];
’E, ’e =>
BEGIN
IF index >= vmD.GetMessageSize[cm] THEN ERROR UnrecognizedCommand;
char ← vmD.GetMessageChar[cm, index];
index ← index + 1;
SELECT char FROM
’X, ’x => EXCommand[];
ENDCASE => ERROR UnrecognizedCommand;
END;
’G, ’g => GCommand[];
’I, ’i => ICommand[];
’J, ’j => JCommand[];
’K, ’k => KCommand[];
’L, ’l => LCommand[];
’M, ’m => MCommand[];
’N, ’n => NCommand[];
’R, ’r => RCommand[];
’S, ’s => SCommand[];
’T, ’t => TCommand[];
’U, ’u => UCommand[];
’V, ’v => VCommand[];
’X, ’x => XCommand[];
Ascii.LF => LineFeedCommand[];
Ascii.BS => BackSpaceCommand[];
Ascii.ControlD => NULL;
’\ => BackSlashCommand[];
’< => StartIterationCommand[];
’> => StopIterationCommand[];
’= =>
BEGIN
IF ~haveArg1 OR haveArg2 THEN ERROR SyntaxError;
WriteLongDecimal[arg1]; WriteLine[""L]; SendNow[];
END;
’? =>
BEGIN
WriteLine["Commands are:"L];
WriteLine["? (help message), A(nswer message), nC(haracters advance pointer),"L];
WriteLine["nD(elete n characters),"L];
WriteLine["↑D(o nothing), EX(it editor), Gq(Get q buffer - insert q at pointer),"L];
WriteLine["I(nsert <text> ↑D), nI(insert Ascii char n), nJ(ump pointer),"L];
WriteLine["nK(ill n lines), n,mK(ill characters n up to m),"L];
WriteLine["nL(ines-move pointer to start of current+nth line),"L];
WriteLine["Mq(Macro from q buffer), N(ew form),"L];
WriteLine["nR(eplace <text> ↑D by <text> ↑D n times),"L];
WriteLine["nS(earch for <text> ↑D n times), nT(ype n lines), nUq(Set q to n),"L];
WriteLine["m,nV(iew lines), m,nT(ype characters m up to n),"L];
WriteLine["nXq(Extract n lines to q buffer), m,nXq(Extract chars m,n to q buffer),"L];
WriteLine["n;G(et message n into buffer) ;T(ype <text> ↑D), ;U(nget file-Save file),"L];
WriteLine[";Y(ank file-Get file)."L];
WriteLine["LF(Line Feed-type next line), BS(Back Space, ↑H-type previous line),"L];
WriteLine["=(type value of argument), \(Pick up next number),"L];
WriteLine["n\(Insert string for n), <(Start interation), >(Stop iteration)."L];
SendNow[];
END;
ENDCASE => ERROR UnrecognizedCommand;
END; -- of ScanAndExecuteCommand --

SyntaxError: ERROR = CODE;
Exit: ERROR = CODE;
UnrecognizedCommand: ERROR = CODE;
ErrorNoted: ERROR = CODE;

index: vmD.CharIndex ← 0;
IterationRecord: TYPE = RECORD [times: CARDINAL, index: vmD.CharIndex];
iterationStack: ARRAY [1 .. maxIterationLevel] OF IterationRecord;
iterationLevel: CARDINAL ← 0;
maxIterationLevel: CARDINAL = 10;
UNTIL index >= vmD.GetMessageSize[cm] DO
ScanAndExecuteCommand
[ ! SyntaxError => GO TO syntaxError;
Exit => GO TO exitExit;
UnrecognizedCommand => GO TO unrecognizedCommand;
ErrorNoted => GO TO done];
REPEAT
unrecognizedCommand =>
{WriteString["Unrecognized command at "L]; WriteDecimal[index]; RETURN[FALSE]};
exitExit => RETURN[TRUE];
syntaxError => {WriteString["Syntax error at "L]; WriteDecimal[index]; RETURN[FALSE]};
done => RETURN[FALSE];
ENDLOOP;
IF iterationLevel > 0 THEN WriteLine["Missing right iteration bracket!"L];
RETURN[FALSE];
END; -- of ExecuteCommandString --


ZeroBuffer: PUBLIC PROCEDURE [cm: POINTER TO vmD.ComposedMessagePtr] =
BEGIN
IF cm↑ = NIL THEN cm↑ ← vmD.AllocateComposedMessageObject[];
vmD.InitComposedMessage[cm↑, ""L];
END; -- of ZeroBuffer --


CleanupTTYEditor: PUBLIC PROCEDURE =
BEGIN
IF commandBuffer # NIL THEN
{vmD.FreeVirtualMessageObject[commandBuffer]; commandBuffer ← NIL};
IF buffer # NIL THEN {vmD.FreeVirtualMessageObject[buffer]; buffer ← NIL};
FOR i: CARDINAL IN [0 .. 9] DO
IF qBuffer[i] # NIL THEN {vmD.FreeVirtualMessageObject[qBuffer[i]]; qBuffer[i] ← NIL};
ENDLOOP;
END; -- of CleanupTTYEditor --


END. -- of LaurelTTYEditor --