-- Grapevine: Lily: Command interface -- [Indigo]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[", 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 { WriteChar[Ascii.CR]; WriteChar['#]; WriteDecimal[this]; WriteString[": "L] }]; IF msgState = ok THEN IF work[pos 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. (635)\f1