-- Laurel subsystem - chat program --

-- chat.mesa

-- Mike Schroeder, March 30, 1982 2:33 PM
-- Edited by Brotz, January 16, 1981 11:56 AM

DIRECTORY
ImageDefs,
InlineDefs,
IODefs,
LaurelExecDefs,
NameInfoDefs,
ProcessDefs,
PupDefs,
PupStream,
PupTypes,
Stream,
StreamDefs,
TimeDefs;

Chat: MONITOR

IMPORTS ImageDefs, InlineDefs, IODefs, LaurelExecDefs, NameInfoDefs,
ProcessDefs, PupStream, Stream, StreamDefs, TimeDefs =

BEGIN

OPEN IODefs;

ServerToUser: PROCEDURE =

BEGIN

char: CHARACTER;
serverName: STRING ← [64];
connect: STRING ← [64];
addr: PupDefs.PupAddress;
serverStream: Stream.Handle ← NIL;
keyStream: StreamDefs.StreamHandle = GetInputStream[];
displayStream: StreamDefs.StreamHandle = GetOutputStream[];

setPageLength: Stream.SubSequenceType = 3;
timingMark: Stream.SubSequenceType = 5;
timingMarkReply: Stream.SubSequenceType = 6;

UserToServer: PROCEDURE [c: CHARACTER] =
BEGIN
char: CHARACTER;
BEGIN ENABLE ABORTED, PupStream.StreamClosing => GOTO return;
--Stream.SetSST[serverStream, setPageLength];
--Stream.PutByte[serverStream, 255]; kludge to minimize BELLS from Juniper
IF c=’l OR c=’x THEN
BEGIN
credential: STRING ← [64];
SendStringToServer["Login "];
LaurelExecDefs.GetUserCredentials[name: credential]; --gets name--
SendStringToServer[credential];
IF c=’l THEN BEGIN
SendStringToServer["."L];
credential.length ← 0;
LaurelExecDefs.GetUserCredentials[registry: credential]; --gets registry--
SendStringToServer[credential]
END;
SendStringToServer[" "L];
credential.length ← 0;
LaurelExecDefs.GetUserCredentials[password: credential]; --gets password--
SendStringToServer[credential];
SendStringToServer["
"L];
END;
DO
char ← ReadChar[];
DO
Stream.PutChar[serverStream, char];
IF keyStream.endof[keyStream] THEN EXIT;
char ← ReadChar[];
ENDLOOP;
Stream.SendNow[serverStream];
ENDLOOP;
EXITS return => NULL;
END;
END;


AcceptFromServer: PROC[c: CHARACTER, serverStream: Stream.Handle] =
BEGIN
userToServer: PROCESS = FORK UserToServer[c];
DO
ENABLE
BEGIN
ABORTED, PupStream.StreamClosing =>
{ NotifyState[starting]; ProcessDefs.Abort[userToServer] };
UNWIND => JOIN userToServer-- join outside the Pup pkg monitor! --;
END;
buffer: STRING ← [200];
why: Stream.CompletionCode;
mySST: Stream.SubSequenceType;
[buffer.length, why, mySST] ←
Stream.GetBlock[serverStream, [@buffer.text, 0, buffer.maxlength]];
WriteString[buffer];
IF why = sstChange AND mySST = timingMark
THEN Stream.SetSST[serverStream, timingMarkReply];
ENDLOOP;
END;


SendStringToServer: PROCEDURE[s: STRING] =
BEGIN
i: CARDINAL;
FOR i IN [0 .. s.length) DO
Stream.PutChar[serverStream, s[i]];
ENDLOOP;
Stream.SendNow[serverStream];
END;


WriteHostPrompt: PROCEDURE[c: STRING] =
BEGIN
WriteChar[CR];
WriteString[c];
WriteString[" to host: "L];
END;


WriteCRStringHost: PROCEDURE[s: STRING] =
BEGIN
WriteChar[CR];
WriteString[s];
WriteString[serverName];
END;


FinishStringWithErrorMsg: PROCEDURE[errorMsg: STRING] =
BEGIN
IF errorMsg # NIL THEN
BEGIN
WriteString[": "L];
WriteString[errorMsg];
END;
WriteChar[’.];
END;

WriteHerald: PROCEDURE =
BEGIN
time: STRING ← [25];
TimeDefs.AppendDayTime[time, TimeDefs.UnpackDT[ImageDefs.BcdVersion[].time]];
WriteChar[CR];
WriteString["Laurel Chat of "L];
WriteLine[time];
WriteString["(CTRL DEL closes connection and returns to Chat command level.)"L];
END; -- of WriteHerald --


LaurelExecDefs.MakeMenuCommandCallable[newMail];
LaurelExecDefs.MakeMenuCommandCallable[user];
LaurelExecDefs.MakeMenuCommandCallable[mailFile];
LaurelExecDefs.MakeMenuCommandCallable[display];
LaurelExecDefs.MakeMenuCommandCallable[delete];
LaurelExecDefs.MakeMenuCommandCallable[undelete];
LaurelExecDefs.MakeMenuCommandCallable[moveTo];
LaurelExecDefs.MakeMenuCommandCallable[copy];
WriteHerald[];
DO
ENABLE
BEGIN
ABORTED =>
BEGIN
NotifyState[starting];
IF serverStream # NIL THEN
BEGIN
WriteCRStringHost["Closing connection to "L];
WriteChar[’.];
END;
LOOP;
END;
PupStream.StreamClosing =>
BEGIN
NotifyState[starting];
IF serverStream # NIL THEN
BEGIN
WriteCRStringHost["Connection closed by "L];
FinishStringWithErrorMsg[text];
END;
keyStream.reset[keyStream];
LOOP;
END;
END;
IF serverStream # NIL THEN
BEGIN
serverStream.delete[serverStream];
serverStream ← NIL;
END;
DO
WriteChar[CR];
WriteString["
C(onnect to), L(ogin to), or Q(uit)? "L];
char ← InlineDefs.BITOR[40B, ReadChar[]];
SELECT char FROM
’c => WriteHostPrompt["Connect"L];
’l => WriteHostPrompt["Login"L];
’x => WriteHostPrompt["Login without registry"L];
’q => {WriteChar[CR]; WriteChar[CR]};
ENDCASE => {keyStream.reset[keyStream]; LOOP};
EXIT;
ENDLOOP;
IF char = ’q THEN EXIT;
ReadID[serverName !
Rubout =>
{WriteString[" ... XXX"L]; LOOP};
LineOverflow =>
{WriteString[" ... name too long!"L]; keyStream.reset[keyStream]; LOOP} ];
NotifyState[running];
WriteString[" ... "L];
connect.length ← 0;
FOR i:CARDINAL DECREASING IN [0 .. serverName.length) DO
IF serverName[i] = ’. THEN GOTO gvName;
REPEAT gvName =>
BEGIN
SELECT NameInfoDefs.GetConnect[serverName, connect] FROM
individual =>
BEGIN
hashSeen: BOOLEAN ← FALSE;
IF connect.length=0 THEN
BEGIN
NotifyState[starting];
WriteString["No connect site in Grapevine data base."L];
keyStream.reset[keyStream];
LOOP;
END;
FOR j: CARDINAL IN [0 .. connect.length) DO
IF connect[j] = ’# THEN
{IF hashSeen
THEN {connect.length ← j+1; EXIT} ELSE hashSeen ← TRUE};
ENDLOOP;
END;
group, notFound =>
BEGIN
NotifyState[starting];
WriteString["Invalid RName."L];
keyStream.reset[keyStream];
LOOP;
END;
allDown =>
BEGIN
NotifyState[starting];
WriteString["Can’t find RName: no RServer available."L];
keyStream.reset[keyStream];
LOOP;
END;
ENDCASE => ERROR;
END;
ENDLOOP;
addr.socket ← PupTypes.telnetSoc; -- default value
PupStream.GetPupAddress
[@addr, IF connect.length=0 THEN serverName ELSE connect
! PupStream.PupNameTrouble =>
BEGIN
NotifyState[starting];
WriteString["PUP name error"L];
FinishStringWithErrorMsg[e];
keyStream.reset[keyStream];
LOOP
END];
serverStream ← PupStream.PupByteStreamCreate
[addr, PupDefs.veryLongWait
! PupStream.StreamClosing =>
BEGIN
NotifyState[starting];
WriteString["Can’t connect"L];
FinishStringWithErrorMsg[text];
keyStream.reset[keyStream];
LOOP;
END];
Stream.SetInputOptions
[serverStream, Stream.InputOptions[TRUE,FALSE,FALSE,FALSE,FALSE]];
WriteLine["open."L];
AcceptFromServer[char, serverStream];
ENDLOOP;
NotifyState[quitTyped];
RETURN;
END;

State: TYPE = {starting, running, quitTyped};
serverToUserState: State ← starting;

NotifyState: ENTRY PROCEDURE [s: State] =
BEGIN
IF s = running AND serverToUserState # running THEN StreamDefs.ResetControlDEL[];
serverToUserState ← s;
NOTIFY pause;
END;


WaitForQuit: ENTRY PROCEDURE =
BEGIN
UNTIL serverToUserState = quitTyped DO
IF StreamDefs.ControlDELtyped[] AND serverToUserState = running THEN
BEGIN
StreamDefs.ResetControlDEL[];
ProcessDefs.Abort[serverToUser];
END;
WAIT pause;
ENDLOOP;
END;


-- main program for chat

pause: CONDITION;
serverToUser: PROCESS;

ProcessDefs.SetTimeout[@pause, ProcessDefs.MsecToTicks[250]];
serverToUser ← FORK ServerToUser[];
WaitForQuit[];
JOIN serverToUser;

END.