-- Grapevine: Lily: Command interface

-- [Indigo]<Grapevine>Lily>LilyCommands.mesa

-- Andrew Birrell 4-Mar-82 15:34:02
-- Michael Schroeder September 14, 1982 9:41 AM

DIRECTORY
Ascii,
BodyDefs,
GlassDefs,
LilyAccessDefs,
LilyCommandDefs
--USING everything--,
LilyIODefs
USING[ AppendFromInput, Confirm, LogAction, Type ],
MailParse,
MaintainDefs
USING[ DoIt ],
ProtocolDefs
USING[ Init, spareSocket ],
PupDefs
USING[ AppendMyName ],
Runtime
USING[ IsBound ],
String
USING[ AppendChar, AppendString,
EquivalentString, InvalidNumber, LowerCase,
StringToNumber ],
Time
USING[ Append, Current, Unpack ];

LilyCommands: MONITOR
IMPORTS GlassDefs, LilyAccessDefs, LilyCommandDefs, LilyIODefs,
MailParse, MaintainDefs, ProtocolDefs, PupDefs, Runtime, String,
Time
EXPORTS LilyCommandDefs--PROGRAM-- =

BEGIN

MaybeSlow: PROC[str: GlassDefs.Handle] =
{ str.WriteString[" ... "L]; str.SendNow[] };

CheckImpl: PROC[str: GlassDefs.Handle, proc: UNSPECIFIED]
RETURNS[ ok: BOOLEAN ] =
BEGIN
ok ← Runtime.IsBound[proc];
IF NOT ok
THEN str.WriteString[" command not implemented on this server"L];
END;

InitialMatch: PROC[str: GlassDefs.Handle, c: CHARACTER, pattern: STRING]
RETURNS[ yes: BOOLEAN] =
BEGIN
yes ← String.LowerCase[c] = String.LowerCase[pattern[0]];
IF yes THEN { str.WriteString[pattern]; str.SendNow[] };
END;

names: ARRAY LilyCommandDefs.Command OF STRING = [
"Answer",
"Characteristics",
"Delete",
"Examine",
"Forward",
"Help",
"Ignore",
"Login",
"Maintain",
"Next",
"Quit",
"Send",
"Type",
"?" ];

ChooseCommand: PROC[ str: GlassDefs.Handle, prompt: STRING ]
RETURNS[ command: LilyCommandDefs.Command, del: BOOLEAN ] =
BEGIN
OPEN str;
del ← FALSE;
DO c: CHARACTER;
WriteString[prompt];
c ← ReadChar[];
IF c # Ascii.CR AND c # Ascii.SP
THEN FOR comm: LilyCommandDefs.Command IN LilyCommandDefs.Command
DO IF InitialMatch[str, c, names[comm]]
THEN { command ← comm; GOTO found };
REPEAT
FINISHED =>
BEGIN
DO IF c = Ascii.DEL THEN EXIT;
WriteChar[c]; WriteChar[’?]; c ← ReadChar[];
ENDLOOP;
del ← TRUE; GOTO found
END
ENDLOOP;
WriteChar[Ascii.CR];
REPEAT
found => NULL;
ENDLOOP;
END;

AppendSubject: PROC[toc: STRING,
readChar: PROC RETURNS[ CHARACTER ],
backup: PROC] =
BEGIN
pHandle: MailParse.ParseHandle =
MailParse.InitializeParse[readChar, backup, FALSE];
BEGIN
ENABLE MailParse.ParseError => GOTO badHeader;
field: STRING = [MailParse.maxFieldNameSize];
String.AppendString[toc, " "L];
WHILE MailParse.GetFieldName[pHandle, field]
DO fieldBody: STRING = [28--or so--];
MailParse.GetFieldBody[pHandle, fieldBody, TRUE];
IF String.EquivalentString[field, "Subject"L]
THEN BEGIN
WHILE toc.length < toc.maxlength-fieldBody.maxlength
DO String.AppendChar[toc, Ascii.SP]; ENDLOOP;
fieldBody.length ← MIN[fieldBody.length,
toc.maxlength-toc.length];
String.AppendString[toc, fieldBody];
EXIT
END;
ENDLOOP;
EXITS badHeader => NULL;
END;
MailParse.FinalizeParse[pHandle];
END;

defaultReg: STRING = ".pa";

connections: CARDINAL ← 0;

NewConnection: ENTRY PROC RETURNS[ CARDINAL ] =
{ RETURN[ connections ← connections+1 ] };

LogUser: PROC[n: CARDINAL, user: STRING] =
BEGIN
log: STRING = [80];
IF n = 0 THEN RETURN;
String.AppendString[log, "user = "L];
String.AppendString[log, user];
LilyIODefs.LogAction[n, log];
END;

ArgType: TYPE = {current, all, last, new, old, rem, misc, del};

ReadArg: PROC[ str: GlassDefs.Handle, multiple: BOOLEAN,
buffer: STRING ]
RETURNS[ which: ArgType ] =
BEGIN
OPEN str;
Found: ERROR[value: ArgType] = CODE;
Question: ERROR = CODE;
MyWrite: PROC[c: CHARACTER] =
BEGIN
IF multiple AND buffer.length = 0
THEN SELECT TRUE FROM
multiple AND InitialMatch[str, c, "All"L] => ERROR Found[all];
multiple AND InitialMatch[str, c, "Last msg-list "L] => ERROR Found[last];
multiple AND InitialMatch[str, c, "New"L] => ERROR Found[new];
multiple AND InitialMatch[str, c, "Old"L] => ERROR Found[old];
multiple AND InitialMatch[str, c, "Remainder"L] => ERROR Found[rem];
ENDCASE => NULL;
IF c = ’? THEN ERROR Question[];
IF buffer.length < buffer.maxlength
THEN { buffer[buffer.length] ← c; buffer.length←buffer.length+1 }
ELSE WriteChar[Ascii.BEL];
END;
MyUnwrite: PROC RETURNS[c: CHARACTER] =
BEGIN
IF buffer.length > 0
THEN { buffer.length←buffer.length-1; c ← buffer[buffer.length] }
ELSE c ← MailParse.endOfInput;
END;
DO WriteString[" msg"L]; IF multiple THEN WriteString["(s)"L];
WriteString[": "L]; WriteString[buffer];
BEGIN
end: CHARACTER = LilyIODefs.AppendFromInput[str, ReadChar[],
MyWrite, MyUnwrite, word !
Found => { which ← value; GOTO basic };
Question => GOTO help];
SELECT end FROM
Ascii.DEL => which ← del;
Ascii.ControlR => LOOP;
ENDCASE =>
IF buffer.length = 0
THEN which ← current
ELSE which ← misc;
EXIT
EXITS help =>
BEGIN
WriteString["? one of: "L];
IF multiple
THEN WriteString["A(ll), L(ast), N(ew), O(ld), R(emainder), "L];
WriteString["<CR>, number"L];
IF multiple
THEN WriteString[", number:number, number-number, list of items"L];
WriteChar[Ascii.CR];
END;
END;
ENDLOOP;
EXITS basic => NULL;
END;

Help: PROC[str: GlassDefs.Handle, command: LilyCommandDefs.Command] =
BEGIN
SELECT command FROM
answer => LilyCommandDefs.HelpAnswer[str];
characteristics => LilyCommandDefs.HelpCharacteristics[str];
delete => LilyCommandDefs.HelpDelete[str];
examine => LilyCommandDefs.HelpExamine[str];
forward => LilyCommandDefs.HelpForward[str];
help => LilyCommandDefs.HelpHelp[str];
ignore => LilyCommandDefs.HelpIgnore[str];
login => LilyCommandDefs.HelpLogin[str];
maintain => LilyCommandDefs.HelpMaintain[str];
next => LilyCommandDefs.HelpNext[str];
quit => LilyCommandDefs.HelpQuit[str];
send => LilyCommandDefs.HelpSend[str];
type => LilyCommandDefs.HelpType[str];
none =>
BEGIN
LilyCommandDefs.HelpHelp[str];
LilyCommandDefs.GeneralDocumentation[str];
FOR other: LilyCommandDefs.Command IN LilyCommandDefs.Command
UNTIL str.DelTyped[]
DO IF other # none THEN Help[str, other] ENDLOOP;
END;
ENDCASE => ERROR;
END;

Receiver: PROC[ str: GlassDefs.Handle ] =
BEGIN
OPEN str;
thisConnection: CARDINAL ← 0;
handle: LilyAccessDefs.Handle;
msg: CARDINAL;
msgSet: LONG CARDINAL ← 0; -- time at which SetCurrent was called --
toc: STRING ← NIL;
user: STRING = [64];
password: STRING = [16];
userOK: BOOLEAN ← FALSE;
msgState: {none, deleted, ok};
tocTyped: BOOLEAN ← FALSE;
SetCurrent: PROC[new: CARDINAL, skipping: {forward, dontSkip, backward}] =
BEGIN
IF Time.Current[] > msgSet+100 --KLUDGE--
THEN -- Server might have timed out our connection;
-- This code is to avoid getting spurious "Failed" signals
-- while avoiding excessive calls of "Position".
{ msgState ← none;
[] ← LilyAccessDefs.Position[handle, 1 !
LilyAccessDefs.Failed => CONTINUE] };
IF new # msg OR msgState = none
THEN BEGIN
DO archived, deleted: BOOLEAN;
[archived, deleted] ← LilyAccessDefs.Position[handle, new !
UNWIND => msgState ← none ];
msgState ← IF deleted THEN deleted ELSE ok;
IF skipping = dontSkip OR msgState = ok THEN EXIT;
new ← IF skipping = forward
THEN new + 1
ELSE new - 1;
ENDLOOP;
msgSet ← Time.Current[];
IF new # msg THEN tocTyped ← FALSE;
msg ← new;
IF msgState = ok
THEN BEGIN
toc ← LilyAccessDefs.ReadTOC[handle];
IF toc.length = 0
THEN LilyAccessDefs.Read[handle, ConstructTOC];
END;
END;
-- msgState # none --
END;
ConstructTOC: PROC[postmark: BodyDefs.Timestamp,
sender: BodyDefs.RName,
readChar: PROC RETURNS[ CHARACTER ],
backup: PROC ] =
BEGIN
toc.length ← 0;
String.AppendString[toc, "? "L];
Time.Append[toc, Time.Unpack[LOOPHOLE[postmark.time]]];
toc.length ← toc.length-3 -- ":ss" --;
String.AppendString[toc, " "L];
FOR i: CARDINAL IN [0..MIN[20,sender.length])
DO toc[toc.length] ← sender[i]; toc.length ← toc.length+1 ENDLOOP;
AppendSubject[toc, readChar, backup];
LilyAccessDefs.WriteTOC[handle];
END;
TypeTOC: PROC =
BEGIN
IF msgState = none THEN ERROR;
WriteChar[Ascii.CR]; WriteChar[’#];
WriteDecimal[msg];
WriteString[": "L]; IF msg < 10 THEN WriteChar[Ascii.SP];
WriteString[IF msgState=deleted THEN "Deleted message"L ELSE toc];
tocTyped ← TRUE;
END;
Typer: PROC[postmark: BodyDefs.Timestamp,
sender: BodyDefs.RName,
readChar: PROC RETURNS[ CHARACTER ],
backup: PROC ] =
{ LilyIODefs.Type[str, readChar, LilyAccessDefs.lastChar] };
MarkSeen: PROC =
BEGIN
IF toc # NIL AND toc.length > 0 AND toc[0] = ’?
THEN { toc[0] ← Ascii.SP; LilyAccessDefs.WriteTOC[handle] };
END;
Answer: PROC[multiple: BOOLEAN] RETURNS[exit: BOOLEAN] =
BEGIN
IF msgState # ok THEN ERROR;
LilyCommandDefs.Answer[str, handle, user, password];
RETURN[TRUE]
END;
Delete: PROC[multiple: BOOLEAN] RETURNS[exit: BOOLEAN] =
BEGIN
IF msgState # ok THEN ERROR;
IF multiple OR NOT tocTyped THEN TypeTOC[];
SELECT LilyIODefs.Confirm[str] FROM
yes =>
BEGIN
SendNow[];
LilyAccessDefs.Delete[handle];
msgState ← deleted;
WriteString["deleted."L];
RETURN[FALSE];
END;
no => RETURN[FALSE];
del => RETURN[TRUE];
ENDCASE => ERROR;
END;
Examine: PROC[multiple: BOOLEAN] RETURNS[exit: BOOLEAN] =
{ TypeTOC[]; RETURN[DelTyped[]] };
Forward: PROC[multiple: BOOLEAN] RETURNS[exit: BOOLEAN] =
BEGIN
IF msgState # ok THEN ERROR;
LilyCommandDefs.Forward[str, handle, user, password];
RETURN[TRUE]
END;
Ignore: PROC[multiple: BOOLEAN] RETURNS[exit: BOOLEAN] =
{ WriteChar[Ascii.SP]; WriteDecimal[msg]; SendNow[];
MarkSeen[]; RETURN[DelTyped[]] };
typeAborted: BOOLEAN ← FALSE;-- whether previous call aborted--
Type: PROC[multiple: BOOLEAN] RETURNS[exit: BOOLEAN] =
BEGIN
IF msgState # ok THEN ERROR;
IF multiple
THEN BEGIN
-- synchronize to ensure we see any DEL’s that the user types
-- before he has seen the TOC --
DO delFound: BOOLEAN ← FALSE;
WriteChar[Ascii.CR];
TypeTOC[];
Synch[];
DO IF ReadChar[! GlassDefs.SynchReply => EXIT] = Ascii.DEL
THEN { delFound ← TRUE; WriteString[" XXX"L] };
ENDLOOP;
IF delFound THEN typeAborted ← TRUE ELSE EXIT;
ENDLOOP;
IF typeAborted
THEN BEGIN
typeAborted ← FALSE;
WriteChar[Ascii.CR];
WriteString["Continue with this message?"L];
IF LilyIODefs.Confirm[str] # yes THEN RETURN[TRUE];
END
END
ELSE TypeTOC[];
LilyAccessDefs.Read[handle, Typer]; MarkSeen[];
IF multiple AND DelTyped[]
THEN BEGIN
typeAborted ← TRUE;
Flush[]; WriteString[" XXX"L]; WriteChar[Ascii.CR];
END;
RETURN[FALSE]
END;

msgListLength: CARDINAL = 64;
msgList: STRING = [msgListLength];

WorkWithArgs: PROC[ work: PROC[BOOLEAN]RETURNS[exit: BOOLEAN],
multiple: BOOLEAN ]
RETURNS[ notDel: BOOLEAN ] =
BEGIN
msgBuffer: STRING = [msgListLength];
arg: ArgType ← ReadArg[str, multiple, msgBuffer];
notDel ← TRUE;
typeAborted ← FALSE --ugh!--;
IF arg = last
THEN { arg ← misc; WriteString[msgList] }
ELSE IF arg = misc
THEN {msgList.length←0;String.AppendString[msgList, msgBuffer]};
SELECT arg FROM
del => notDel ← FALSE;
current =>
{ WriteDecimal[msg]; MaybeSlow[str]; SetCurrent[msg, dontSkip];
IF msgState # ok THEN TypeTOC[] ELSE [] ← work[FALSE] };
misc =>
BEGIN
pos: CARDINAL ← 0;
ReadN: PROC RETURNS[n: CARDINAL] =
BEGIN
n ← 0;
WHILE pos < msgList.length
AND msgList[pos] IN [’0..’9]
DO n ← n*10 + (msgList[pos]-’0); pos←pos+1; ENDLOOP;
END;
MaybeSlow[str];
WHILE pos < msgList.length
DO low: CARDINAL;
high: CARDINAL;
IF msgList[pos] IN [’0..’9]
THEN low ← ReadN[]
ELSE GOTO badArg;
IF pos < msgList.length AND multiple
AND ( msgList[pos] = ’: OR msgList[pos] = ’>
OR msgList[pos]=’- )
THEN BEGIN
minus: BOOLEAN = msgList[pos] = ’-;
pos←pos+1;
IF pos < msgList.length AND msgList[pos] IN [’0..’9]
THEN high ← ReadN[]
ELSE GOTO badArg;
IF minus
THEN -- calculate correct value for "high" --
-- "27-9" means "27:29"
-- "127-35" means "127:135"
BEGIN
IF high < 10
THEN high ← high + (low/10)*10
ELSE IF high < 100
THEN high ← high + (low/100)*100
ELSE high ← high + (low/1000)*1000;
END;
END
ELSE high ← low;
IF pos < msgList.length
THEN BEGIN
IF msgList[pos] = ’, AND multiple
THEN pos←pos+1
ELSE GOTO badArg;
END;
FOR this: CARDINAL ← low, IF low<high THEN this+1 ELSE this-1
DO SetCurrent[this, dontSkip ! LilyAccessDefs.Failed =>
{ WriteChar[Ascii.CR]; WriteChar[’#];
WriteDecimal[this]; WriteString[": "L] }];
IF msgState = ok
THEN IF work[pos<msgList.length OR this#high]
THEN GOTO done;
IF this = high THEN EXIT;
SendNow[];
ENDLOOP;
REPEAT
badArg =>
BEGIN
WriteChar[Ascii.CR];
WriteString["Error in command argument at """L];
FOR i: CARDINAL IN [0..MIN[msgList.length,pos+1])
DO WriteChar[msgList[i]] ENDLOOP;
WriteChar[’"];
END;
done => NULL;
ENDLOOP;
END;
ENDCASE => -- all, new, old, rem --
BEGIN
lastMsg: CARDINAL ← msg;
MaybeSlow[str];
IF arg # rem THEN msg ← 0;
DO ENABLE LilyAccessDefs.Failed =>
BEGIN
IF msg # lastMsg THEN { msg ← lastMsg; msgState ← none };
IF why = notFound THEN EXIT;
END;
SetCurrent[msg+1, forward];
IF msgState = ok
AND( arg = all OR arg = rem
OR (arg=new AND toc[0]=’?)
OR (arg=old AND toc[0]#’?) )
THEN { lastMsg ← msg; IF work[TRUE] THEN EXIT };
SendNow[];
ENDLOOP;
END;
END; --WorkWithArgs--
WriteGreeting: PROC =
BEGIN
n: STRING = [64];
WriteChar[Ascii.CR];
WriteString["Grapevine’s Lily Terminal Service on host """L];
PupDefs.AppendMyName[n];
WriteString[n];
WriteChar[’"];
WriteChar[Ascii.CR];
WriteString["Type ""H?"" for documentation"L];
WriteChar[Ascii.CR];
END; --WriteGreeting--
WriteGreeting[];
DO ENABLE UNWIND => -- this is outside the BEGIN to cover the EXIT’s --
BEGIN
IF userOK THEN LilyAccessDefs.Destroy[handle];
LilyIODefs.LogAction[thisConnection, "abandoned"L];
END;
BEGIN
ENABLE
BEGIN
GlassDefs.TimeOut => GOTO timeOut;
LilyAccessDefs.Failed =>
BEGIN
DescribeFailure: PROC[reason: LilyAccessDefs.FailureReason]=
BEGIN
WriteString["Failed: "L];
SELECT reason FROM
communications =>
WriteString["communication failure: please re-try"L];
credentials =>
WriteString["your name/password is incorrect!"L];
notFound =>
WriteString["message not found"L];
ENDCASE => WriteString["unknown reason!"L];
END; --DescribeFailure--
msgState ← none;
DescribeFailure[why];
CONTINUE
END;
END;
command: LilyCommandDefs.Command;
WriteChar[Ascii.CR];
BEGIN
del: BOOLEAN;
[command,del] ← ChooseCommand[str, "-> "L];
IF del THEN GOTO del;
END;
IF thisConnection = 0
THEN LilyIODefs.LogAction[thisConnection←NewConnection[], "created"L];
SELECT command FROM
answer =>
BEGIN
IF NOT userOK THEN GOTO noUser;
IF NOT WorkWithArgs[Answer, FALSE] THEN GOTO del;
END;
characteristics =>
BEGIN
GetCharacteristics: PROC =
BEGIN
buffer: STRING = [7] --177777B--;
WriteChar[Ascii.CR];
IF ReadString["Line width (or DEL): "L,
buffer, word] # Ascii.DEL
THEN SetWidth[ String.StringToNumber[buffer, 10] ];
WriteChar[Ascii.CR]; buffer.length←0;
IF ReadString["Page height (or DEL): "L,
buffer, word] # Ascii.DEL
THEN SetHeight[ String.StringToNumber[buffer, 10] ];
END; --GetCharacteristics--
GetCharacteristics[ ! String.InvalidNumber =>
{ WriteString["? number expected"L]; CONTINUE }];
END;
delete =>
BEGIN
IF NOT userOK THEN GOTO noUser;
IF NOT WorkWithArgs[Delete, TRUE] THEN GOTO del;
END;
examine =>
BEGIN
IF NOT userOK THEN GOTO noUser;
IF NOT WorkWithArgs[Examine, TRUE] THEN GOTO del;
END;
forward =>
BEGIN
IF NOT userOK THEN GOTO noUser;
IF NOT WorkWithArgs[Forward, FALSE] THEN GOTO del;
END;
help =>
BEGIN
command: LilyCommandDefs.Command;
delTyped: BOOLEAN;
[command,delTyped] ← ChooseCommand[str, " command name: "L];
IF delTyped THEN GOTO del;
Help[str, command];
END;
ignore =>
BEGIN
IF NOT userOK THEN GOTO noUser;
IF NOT WorkWithArgs[Ignore, TRUE] THEN GOTO del;
END;
login =>
BEGIN
DoLogin: PROC RETURNS [noDel: BOOLEAN] =
BEGIN
state: LilyAccessDefs.MBXState;
WriteChar[Ascii.CR];
IF ReadString["Your name please: "L,
user, word] = Ascii.DEL
THEN RETURN [FALSE];
IF String.EquivalentString[user, "ogin"L] -- ugh! --
THEN BEGIN
WriteString["← "L];
user.length ← 0;
IF ReadString[""L,
user, word] = Ascii.DEL
THEN RETURN [FALSE];
END;
FOR index: CARDINAL DECREASING IN [0..user.length)
DO IF user[index] = ’. THEN EXIT; REPEAT
FINISHED =>
BEGIN
user.length ← MIN[user.length,user.maxlength-defaultReg.length];
String.AppendString[user, defaultReg];
WriteString[defaultReg];
END;
ENDLOOP;
WriteChar[Ascii.CR];
SELECT ReadString["Your password: "L,
password, pwd] FROM
Ascii.DEL => RETURN [FALSE];
Ascii.SP =>
BEGIN
acc: STRING = [8];
[] ← ReadString[" (account): "L, acc, word];
END;
ENDCASE => NULL;
MaybeSlow[str];
IF userOK THEN LilyAccessDefs.Destroy[handle];
[state,handle] ←
LilyAccessDefs.Create[user, password, thisConnection];
IF state IN [allDown..notEmpty]
THEN BEGIN
userOK ← TRUE;
LogUser[thisConnection, user];
END
ELSE userOK ← FALSE;
IF NOT userOK THEN LilyAccessDefs.Destroy[handle];
msg ← 1; msgState ← none; tocTyped ← FALSE;
SELECT state FROM
badName, badPwd, cantAuth, allDown, someEmpty =>
WriteLoginError[state];
allEmpty =>
WriteString["no mail"L];
notEmpty =>
WriteString["you have mail"L];
ENDCASE => ERROR;
RETURN[TRUE];
END; --DoLogin--
WriteLoginError: PROC [state: LilyAccessDefs.MBXState] =
BEGIN -- write uncommon error messages --
SELECT state FROM
badName =>
WriteString["incorrect name"L];
badPwd =>
WriteString["incorrect password (or name)"L];
cantAuth =>
WriteString["can’t contact any authentication server"L];
allDown =>
WriteString["no mail; can’t contact any in-box server"L];
someEmpty =>
WriteString["no mail; but couldn’t contact all your inbox servers"L];
ENDCASE;
END; --WriteLoginError--
IF NOT DoLogin[] THEN GOTO del
END;
maintain =>
BEGIN
WriteExitMsg: PROC =
BEGIN
WriteChar[Ascii.CR];
WriteString["Exit from Maintain sub-system"L];
WriteChar[Ascii.CR]
END; --WriteExitMsg--
WriteTimeOutMsg: PROC =
BEGIN
WriteChar[Ascii.CR];
WriteString["Time-out: exit from Maintain sub-system"L];
END; --WriteTimeOutMsg--
WriteString[" sub-system"L];
IF LilyIODefs.Confirm[str] = yes
AND CheckImpl[str, MaintainDefs.DoIt]
THEN BEGIN
LilyIODefs.LogAction[thisConnection, "maintain"L];
MaintainDefs.DoIt[str, user, password
! GlassDefs.TimeOut => GOTO maintainTimeOut];
WriteExitMsg[];
EXITS maintainTimeOut => {WriteTimeOutMsg[]; GOTO timeOut};
END;
END;
next =>
BEGIN
IF NOT userOK THEN GOTO noUser;
MaybeSlow[str];
SetCurrent[msg+1, forward];
IF msgState # ok
THEN TypeTOC[]
ELSE { typeAborted ← FALSE --ugh!--; [] ← Type[FALSE] };
END;
quit =>
IF LilyIODefs.Confirm[str] = yes THEN EXIT;
send =>
BEGIN
IF NOT userOK THEN GOTO noUser;
MaybeSlow[str];
LilyCommandDefs.Send[str, user, password];
END;
type =>
BEGIN
IF NOT userOK THEN GOTO noUser;
IF NOT WorkWithArgs[Type, TRUE] THEN GOTO del;
END;
ENDCASE =>
LilyCommandDefs.HelpCommands[str];
IF DelTyped[] THEN GOTO del;
EXITS
noUser =>
BEGIN
WriteLoginPleaseMsg: PROC = {WriteString[" ... please login first"L]};
WriteLoginPleaseMsg[]
END;
timeOut =>
BEGIN
WriteTypeAnyCharMsg: PROC =
BEGIN
WriteChar[Ascii.CR];
WriteString["Type any character to maintain connection ... "L]
END; --WriteTypeAnyCharMsg--
WriteTypeAnyCharMsg[];
[] ← ReadChar[ ! GlassDefs.TimeOut => GOTO end ];
EXITS end =>
BEGIN
WriteGoodByes: PROC =
BEGIN
WriteString["good-bye"L];
LilyIODefs.LogAction[thisConnection, "timeout"L]
END; --WriteGoodByes--
WriteGoodByes[];
EXIT
END;
END;
del => { Flush[]; WriteString[" XXX"L] };
END;
ENDLOOP;
LilyIODefs.LogAction[thisConnection, "ended"L];
IF userOK THEN LilyAccessDefs.Destroy[handle];
WriteChar[Ascii.CR];
END;

ListenerProcess: PROC =
{ ProtocolDefs.Init[];
DO GlassDefs.Listen[Receiver, ProtocolDefs.spareSocket] ENDLOOP };

listener: PROCESS = FORK ListenerProcess[];

END.