DIRECTORY Ascii, Basics USING [BITAND], Commander USING [CommandProc, Register], CommandTool USING [ArgumentVector, Failed, Parse], DefaultRemoteNames USING [Get], EditedStream USING [SetEcho], FS USING [Error, StreamOpen], IO, IOClasses USING [CreateDribbleOutputStream], List USING [Length, Remove], Loader USING [BCDBuildTime], Menus USING [AppendMenuEntry, CreateEntry, FindEntry, MenuEntry, MenuProc, ReplaceMenuEntry], Process USING [Detach], Pup USING [Address], PupName USING [Error, NameLookup], PupStream USING [Create, ConsumeMark, SendAttention, SendMark, StreamClosing, Timeout, waitForever], PupWKS USING [telnet], Rope USING [Cat, Concat, Fetch, Find, Length, ROPE, Substr], TiogaOps USING [GetCaret, GetRope, Location], TIPUser USING [InstantiateNewTIPTable, RegisterTIPPredicate, TIPPredicate, TIPTable], TypeScript USING [BackSpace, ChangeLooks, Create, InsertCharAtFrontOfBuffer, TS], UserCredentials USING [Get], UserProfile USING [Boolean, Token], ViewerClasses, ViewerEvents USING [EventProc, EventRegistration, RegisterEventProc, UnRegisterEventProc], ViewerIO USING [CreateViewerStreams], ViewerOps USING [AddProp, BlinkViewer, DestroyViewer, FetchProp, PaintViewer], ViewerTools USING [GetSelectedViewer, GetSelectionContents, SetSelection]; Chat: CEDAR MONITOR LOCKS h.LOCK USING h: Handle IMPORTS Basics, Commander, CommandTool, DefaultRemoteNames, EditedStream, FS, IO, IOClasses, List, Loader, Menus, Process, PupName, PupStream, Rope, TiogaOps, TIPUser, TypeScript, UserCredentials, UserProfile, ViewerEvents, ViewerIO, ViewerOps, ViewerTools SHARES Menus, ViewerClasses = { Handle: TYPE = REF ChatInstanceRecord; State: TYPE = {idle, starting, running, closing, destroy}; DisconnectChar: CHAR = 220C; AbortChar: CHAR = 221C; ConnectChar: CHAR = 222C; LoginChar: CHAR = 223C; RemoteCloseChar: CHAR = 224C; MarkByte: TYPE = [0..256); syncMark: MarkByte = 1; setLineWidth: MarkByte = 2; setPageLength: MarkByte = 3; timingMark: MarkByte = 5; timingMarkReply: MarkByte = 6; ChatInstanceRecord: TYPE = MONITORED RECORD [ ts: TypeScript.TS, -- the primary typescript state: State _ idle, lorc: CHAR _ 'c, logFileName: Rope.ROPE, logStream: IO.STREAM, debugging: BOOL _ FALSE, rawLogStream: IO.STREAM, keyFile: Rope.ROPE, argv: CommandTool.ArgumentVector, pleaseStop: BOOL _ FALSE, uToSStopped: BOOL _ FALSE, sToUStopped: BOOL _ FALSE, inDestroy: BOOL _ FALSE, serverToUserProcess: PROCESS, userToServerProcess: PROCESS, serverName: Rope.ROPE _ "Ivy", useOldHost: BOOL _ FALSE, keyStream: IO.STREAM, destroyOnClose: BOOL _ FALSE, synchronous: BOOL _ FALSE, in: IO.STREAM, origOut: IO.STREAM, out: IO.STREAM, tipTable: TIPUser.TIPTable, serverStream: IO.STREAM _ NIL, oldSplit: Menus.MenuEntry _ NIL ]; chatInstanceList: LIST OF REF ANY _ NIL; destroyEvent: ViewerEvents.EventRegistration _ NIL; closeEvent: ViewerEvents.EventRegistration _ NIL; chatTipTable: TIPUser.TIPTable; ServerToUser: PROC [h: Handle] = { c: CHAR; mySST: MarkByte; DO ENABLE { ABORTED => GOTO Cleanup; PupStream.StreamClosing => { IF NOT h.pleaseStop THEN TypeScript.InsertCharAtFrontOfBuffer[ts: h.ts, char: RemoteCloseChar]; GOTO Cleanup; }; IO.Error => GOTO Cleanup; }; IF h.serverStream.EndOf[] THEN { mySST _ PupStream.ConsumeMark[h.serverStream]; IF mySST = timingMark THEN PupStream.SendMark[h.serverStream, timingMarkReply]; }; c _ h.serverStream.GetChar[! PupStream.Timeout => { IF h.pleaseStop OR h.state # running THEN GOTO Cleanup ELSE RESUME; }; IO.EndOfStream => { LOOP; } ]; IF h.pleaseStop OR h.state # running THEN GOTO Cleanup; IF h.debugging THEN { h.rawLogStream.PutChar[c]; h.rawLogStream.Flush[]; }; c _ LOOPHOLE[Basics.BITAND[LOOPHOLE[c, CARDINAL], 177B], CHAR]; SELECT c FROM Ascii.BEL => Flash[h]; Ascii.ControlA, Ascii.BS => TypeScript.BackSpace[h.ts]; Ascii.TAB, IN[Ascii.SP..0176C] => h.out.PutChar[c]; Ascii.LF => h.out.PutChar[Ascii.CR]; ENDCASE => NULL; ENDLOOP; EXITS Cleanup => h.sToUStopped _ TRUE; }; Flash: PROC [h: Handle] = { viewer: ViewerClasses.Viewer ~ h.ts; ViewerOps.BlinkViewer[viewer, 100]; }; InitialNegotiations: PROC [h: Handle] = TRUSTED { out: IO.STREAM _ h.serverStream; PupStream.SendMark[h.serverStream, setLineWidth]; out.PutChar[0C]; out.Flush[]; IF h.lorc='l OR h.lorc='x THEN { name, password: Rope.ROPE _ NIL; name _ UserProfile.Token[key: "Chat.Name", default: NIL]; IF name = NIL THEN name _ UserProfile.Token[key: Rope.Cat["Chat.", h.serverName, ".Name"], default: NIL]; IF name = NIL THEN { name _ UserCredentials.Get[].name; IF Rope.Find[s1: name, s2: "."] = -1 THEN name _ Rope.Cat[name, ".", DefaultRemoteNames.Get[].registry]; }; out.PutRope["Login "]; out.PutRope[name]; IF UserProfile.Boolean[Rope.Cat["Chat.", h.serverName, ".usePassword"], TRUE] THEN { out.PutRope[" "]; out.PutRope[UserCredentials.Get[].password]; }; out.PutRope[" \n"]; out.Flush[]; }; }; FinishStringWithErrorMsg: PROC [h: Handle, errorMsg: Rope.ROPE] = { IF errorMsg # NIL THEN h.out.PutF[": %s.\n", [rope[errorMsg]]] ELSE h.out.PutF[".\n"]; }; FromKeys: PROC [h: Handle] = TRUSTED { count: NAT _ 0; { WHILE h.keyStream # NIL AND ~h.keyStream.EndOf[] DO IF h.pleaseStop OR h.state # starting THEN GOTO CloseKeyStream; h.serverStream.PutChar[h.keyStream.GetChar[]]; count _ count + 1; IF count >= 50 THEN { h.serverStream.Flush[]; count _ 0; }; ENDLOOP; GOTO CloseKeyStream; EXITS CloseKeyStream => { IF h.keyStream # NIL THEN{ h.keyStream.Close[]; h.keyStream _ NIL; }; IF count # 0 THEN h.serverStream.Flush[]; }; }; }; StartUp: PROC [h: Handle] = { ENABLE UNWIND => h.state _ idle; h.state _ starting; IF NOT h.useOldHost OR h.serverName.Length[] = 0 THEN h.serverName _ FindHostName[h]; h.useOldHost _ FALSE; h.pleaseStop _ FALSE; h.sToUStopped _ FALSE; h.uToSStopped _ FALSE; ViewerTools.SetSelection[h.ts, NIL]; h.out.PutF["\nViewers Chat of %t.\n", [time[Loader.BCDBuildTime[FindHostName]]]]; IF h.logStream # NIL THEN h.out.PutF["Log file: %g\n", IO.rope[h.logFileName]] ELSE h.out.PutF["No log file.\n"]; OpenConnection[h]; IF h.serverStream = NIL THEN { h.state _ idle; RETURN; }; SetName[h, Rope.Concat["Chat ", h.serverName]]; InitialNegotiations[h]; FromKeys[h]; h.state _ running; h.serverToUserProcess _ FORK ServerToUser[h]; }; FindHostName: PROC [h: Handle] RETURNS [host: Rope.ROPE] = { caret: TiogaOps.Location; r: Rope.ROPE; i: INT; r _ ViewerTools.GetSelectionContents[]; IF r.Length[] > 1 THEN RETURN[r]; caret _ TiogaOps.GetCaret[]; r _ TiogaOps.GetRope[caret.node]; i _ caret.where; WHILE i > 0 DO char: CHARACTER = Rope.Fetch[r, i - 1]; IF -- char # '* AND -- NOT ChatTokenProc[char] = other THEN EXIT; i _ i -1; ENDLOOP; host _ Rope.Substr[base: r, start: i, len: caret.where - i]; }; ChatTokenProc: IO.BreakProc = TRUSTED { IF IO.TokenProc[char] = other THEN RETURN [other]; IF char = '+ THEN RETURN [other]; RETURN [sepr]; }; OpenConnection: PROC [h: Handle] = TRUSTED { addr: Pup.Address; { h.out.PutF["Opening connection to %g ... ", [rope[h.serverName]]]; addr _ PupName.NameLookup[h.serverName, PupWKS.telnet ! PupName.Error => { h.out.PutF["PUP name error"]; FinishStringWithErrorMsg[h, text]; GOTO Return; }]; h.serverStream _ PupStream.Create[addr, 1000, PupStream.waitForever ! PupStream.StreamClosing => { h.out.PutF["Can't connect"]; FinishStringWithErrorMsg[h, text]; GOTO Return; }]; h.out.PutF["open.\n"]; EXITS Return => NULL; }; }; CloseConnection: PROC [h: Handle, print: BOOL] = TRUSTED { h.pleaseStop _ TRUE; IF h.serverStream # NIL THEN { IF print THEN h.out.PutF["\nClosing connection to %s", IO.rope[h.serverName] ! IO.Error => CONTINUE]; h.serverStream.Close[]; h.serverStream _ NIL; -- could cause pointer faults! IF print THEN h.out.PutF[" ... Closed\n" ! IO.Error => CONTINUE]; }; IF h.keyStream # NIL THEN { h.keyStream.Close[]; h.keyStream _ NIL; }; IF h.state = running THEN JOIN h.serverToUserProcess; IF h.logStream # NIL THEN h.logStream.Flush[]; h.state _ idle; SetName[h, "Chat"]; }; SetName: PROC [h: Handle, r: Rope.ROPE] = { InternalSetName: PROC [v: ViewerClasses.Viewer] = { v.name _ r; ViewerOps.PaintViewer[viewer: v, hint: caption]; }; EnumerateSplits[h.ts, InternalSetName ! ANY => CONTINUE]; }; ChatMain: Commander.CommandProc = TRUSTED { h: Handle _ NEW[ChatInstanceRecord _ []]; execOut: IO.STREAM _ cmd.out; switchChar: CHAR; i: NAT _ 2; h.argv _ CommandTool.Parse[cmd ! CommandTool.Failed => {msg _ errorMsg; CONTINUE; }]; IF h.argv = NIL THEN RETURN; h.lorc _ 'l; -- default GV login WHILE i < h.argv.argc DO IF h.argv[i].Length[] > 1 THEN SELECT h.argv[i].Fetch[0] FROM '- => { switchChar _ h.argv[i].Fetch[1]; SELECT switchChar FROM 'd => h.destroyOnClose _ TRUE; 'k => { IF i+1 < h.argv.argc THEN { h.keyStream _ IO.RIS[h.argv[i+1]]; i _ i + 1; }; }; 's => h.synchronous _ TRUE; 'D => { h.debugging _ TRUE; h.rawLogStream _ FS.StreamOpen[fileName: "Chat.rawLog", accessOptions: $create ! FS.Error => IF error.group = user THEN { execOut.PutF["Chat: Cannot open raw log Chat.rawLog\n"]; CONTINUE; } ]; }; ENDCASE => h.lorc _ switchChar; }; '> => h.logFileName _ Rope.Substr[base: h.argv[i], start: 1]; '< => h.keyFile _ Rope.Substr[base: h.argv[i], start: 1]; ENDCASE => execOut.PutF["chat: unknown command: %s.\n", IO.rope[h.argv[i]]]; i _ i + 1; ENDLOOP; IF h.logFileName.Length[] = 0 THEN h.logFileName _ "Chat.log"; IF h.keyFile.Length[] > 0 THEN h.keyStream _ FS.StreamOpen[fileName: h.keyFile, keep: 2 ! FS.Error => IF error.group = user THEN { execOut.PutF["Chat: Cannot open %s\n", IO.rope[h.keyFile]]; CONTINUE; }]; h.ts _ TypeScript.Create[info: [name: "Chat", iconic: h.argv.argc <= 1 OR h.lorc = 'i], paint: TRUE]; TypeScript.ChangeLooks[h.ts, 'f]; h.ts.file _ h.logFileName; h.ts.icon _ typescript; h.logStream _ FS.StreamOpen[fileName: h.logFileName, accessOptions: $create ! FS.Error => IF error.group = user THEN { execOut.PutF["Chat: Cannot open %s\n", IO.rope[h.logFileName]]; CONTINUE; }]; chatTipTable.link _ h.ts.tipTable; chatTipTable.opaque _ FALSE; h.tipTable _ h.ts.tipTable _ chatTipTable; [in: h.in, out: h.origOut] _ ViewerIO.CreateViewerStreams[name: "Chat.log", viewer: h.ts, editedStream: FALSE]; h.out _ h.origOut; IF h.logStream # NIL THEN h.out _ IOClasses.CreateDribbleOutputStream[output1: h.origOut, output2: h.logStream]; IF h.debugging THEN h.out.PutF["Debugging Mode On ... raw log on Chat.rawLog\n"]; EditedStream.SetEcho[h.in, NIL]; IF h.argv.argc > 1 THEN { h.serverName _ h.argv[1]; h.useOldHost _ TRUE; IF h.lorc = 'i THEN h.lorc _ 'c ELSE TypeScript.InsertCharAtFrontOfBuffer[ts: h.ts, char: IF h.lorc = 'c THEN ConnectChar ELSE LoginChar]; }; IF List.Length[chatInstanceList] = 0 THEN { destroyEvent _ ViewerEvents.RegisterEventProc[proc: MyDestroy, event: destroy] }; chatInstanceList _ CONS[h, chatInstanceList]; Menus.AppendMenuEntry[menu: h.ts.menu, entry: Menus.CreateEntry[name: "Disconnect", proc: MyDisconnect, clientData: h, fork: TRUE, documentation: "Close Ethernet connection"]]; Menus.AppendMenuEntry[menu: h.ts.menu, entry: Menus.CreateEntry[name: "Login", proc: MyLogin, clientData: h, documentation: "Open Ethernet Connection to selected host and send Login sequence"]]; Menus.AppendMenuEntry[menu: h.ts.menu, entry: Menus.CreateEntry[name: "Connect", proc: MyConnect, clientData: h, documentation: "Open Ethernet Connection to selected host"]]; Menus.AppendMenuEntry[menu: h.ts.menu, entry: Menus.CreateEntry[name: "BreakKey", proc: MyBreakKey, clientData: h, documentation: "Transmit Ascii.NULL (DLS interprets as RS-232 Line Break)"]]; Menus.AppendMenuEntry[menu: h.ts.menu, entry: Menus.CreateEntry[name: "Interrupt", proc: MyInterrupt, clientData: h, documentation: "Send Interrupt sequence (STOP, or ^C)"]]; Menus.AppendMenuEntry[menu: h.ts.menu, entry: Menus.CreateEntry[name: "FlushLog", proc: MyFlushLog, clientData: h, documentation: "Flush log file to disk."]]; h.oldSplit _ Menus.FindEntry[menu: h.ts.menu, entryName: "Split"]; Menus.ReplaceMenuEntry[menu: h.ts.menu, oldEntry: h.oldSplit, newEntry: Menus.CreateEntry[name: "Split", proc: MySplit, fork: TRUE, clientData: h, documentation: "Split window"]]; ViewerOps.AddProp[h.ts, $ChatToolData, h]; ViewerOps.PaintViewer[viewer: h.ts, hint: all]; IF h.synchronous THEN UserToServer[h] ELSE TRUSTED { Process.Detach[h.userToServerProcess _ FORK UserToServer[h]]; }; }; UserToServer: PROC [h: Handle] = TRUSTED { char: CHAR; count: NAT _ 0; DO ENABLE { ABORTED => ERROR; -- ever happen? IO.Error => { CloseConnection[h, FALSE]; GOTO Die; }; PupStream.StreamClosing => { IF NOT h.inDestroy THEN { h.out.PutF["\nConnection being closed by %s", IO.rope[h.serverName]]; FinishStringWithErrorMsg[h, text]; }; h.sToUStopped _ TRUE; CONTINUE; }; }; IF h.sToUStopped THEN { IF h.serverStream # NIL THEN CloseConnection[h, FALSE]; IF h.destroyOnClose THEN { ViewerOps.DestroyViewer[h.ts]; GOTO Die}; }; IF h.inDestroy THEN { CloseConnection[h, FALSE]; GOTO Die; }; char _ h.in.GetChar[]; SELECT char FROM AbortChar => { IF h.state = running THEN CloseConnection[h, TRUE]; }; DisconnectChar => { IF h.state = running THEN CloseConnection[h, TRUE]; }; RemoteCloseChar => { ERROR PupStream.StreamClosing[why: remoteClose, text: NIL]; }; ConnectChar => { IF h.state = idle THEN { h.lorc _ 'c; StartUp[h]; count _ 0; }; }; LoginChar => { IF h.state = idle THEN { h.lorc _ 'l; StartUp[h]; count _ 0; }; }; ENDCASE => { SELECT h.state FROM running => { h.serverStream.PutChar[char]; IF count >= 50 OR h.in.CharsAvail[] = 0 THEN { h.serverStream.Flush[]; count _ 0; } ELSE count _ count + 1; }; ENDCASE => h.out.PutChar[char]; }; ENDLOOP; EXITS Die => { IF h.logStream # NIL THEN h.logStream.Close[]; IF h.debugging THEN h.rawLogStream.Close[]; }; }; MyDestroy: ViewerEvents.EventProc = { h: Handle _ NARROW[ViewerOps.FetchProp[viewer, $ChatToolData]]; IF h = NIL THEN RETURN; SELECT NumSplit[viewer] FROM 0 => { }; 1 => { h.inDestroy _ TRUE; chatInstanceList _ List.Remove[h, chatInstanceList]; IF List.Length[chatInstanceList] = 0 THEN { ViewerEvents.UnRegisterEventProc[proc: destroyEvent, event: destroy]; }; } ENDCASE => { IF viewer = h.ts THEN { Another: PROC [v: ViewerClasses.Viewer] = { IF v # viewer THEN h.ts _ v; }; EnumerateSplits[viewer, Another]; }; }; }; MyClose: ViewerEvents.EventProc = { h: Handle _ NARROW[ViewerOps.FetchProp[viewer, $ChatToolData]]; IF h = NIL THEN RETURN; TypeScript.InsertCharAtFrontOfBuffer[ts: h.ts, char: DisconnectChar]; }; MyConnect: Menus.MenuProc = { viewer: TypeScript.TS _ NARROW[parent]; h: Handle _ NARROW[clientData]; h.ts _ viewer; -- "primary" copy h.useOldHost _ mouseButton # red; TypeScript.InsertCharAtFrontOfBuffer[ts: h.ts, char: ConnectChar]; }; MyLogin: Menus.MenuProc = { viewer: TypeScript.TS _ NARROW[parent]; h: Handle _ NARROW[clientData]; h.ts _ viewer; -- "primary" copy h.useOldHost _ mouseButton # red; TypeScript.InsertCharAtFrontOfBuffer[ts: h.ts, char: LoginChar]; }; MyDisconnect: Menus.MenuProc = { h: Handle _ NARROW[clientData]; TypeScript.InsertCharAtFrontOfBuffer[ts: h.ts, char: DisconnectChar]; }; MyBreakKey: Menus.MenuProc = TRUSTED { h: Handle _ NARROW[clientData]; IF h.state # running THEN RETURN; IF h.serverStream # NIL THEN { ENABLE PupStream.StreamClosing => CONTINUE; h.serverStream.PutChar['\000]; h.serverStream.Flush[]; }; }; MyInterrupt: Menus.MenuProc = TRUSTED { h: Handle _ NARROW[clientData]; IF h.state # running THEN RETURN; IF h.serverStream # NIL THEN { ENABLE PupStream.StreamClosing => CONTINUE; PupStream.SendAttention[h.serverStream]; PupStream.SendMark[h.serverStream, syncMark]; }; }; MyFlushLog: Menus.MenuProc = { h: Handle _ NARROW[clientData]; IF h.logStream # NIL THEN h.logStream.Flush[]; }; MySplit: Menus.MenuProc = { h: Handle _ NARROW[clientData]; CheckChatProperties: PROC [v: ViewerClasses.Viewer] = { IF ViewerOps.FetchProp[v, $ChatToolData] = NIL THEN ViewerOps.AddProp[v, $ChatToolData, h]; v.tipTable _ h.tipTable; }; h.oldSplit.proc[parent: parent, clientData: h.oldSplit.clientData, mouseButton: mouseButton, shift: shift, control: control]; EnumerateSplits[NARROW[parent, ViewerClasses.Viewer], CheckChatProperties]; }; ConnectionOpen: TIPUser.TIPPredicate = { h: Handle; viewer: ViewerClasses.Viewer _ ViewerTools.GetSelectedViewer[]; IF viewer=NIL THEN RETURN [FALSE]; -- no primary selection h _ NARROW[ViewerOps.FetchProp[viewer, $ChatToolData]]; IF h=NIL THEN RETURN [FALSE]; -- not a chat tool RETURN [h.state = running]; -- connection open? }; EnumerateSplits: PROC [v: ViewerClasses.Viewer, p: PROC [v: ViewerClasses.Viewer]] = { v2: ViewerClasses.Viewer _ v; IF v = NIL THEN RETURN; DO p[v2]; IF v2.link = NIL OR v2.link = v THEN RETURN; v2 _ v2.link; ENDLOOP; }; NumSplit: PROC [v: ViewerClasses.Viewer] RETURNS [count: INT _ 0] = { Counter: PROC [v2: ViewerClasses.Viewer] = { count _ count + 1; }; EnumerateSplits[v, Counter]; }; Init: PROC = { Commander.Register[key: "Chat", proc: ChatMain, doc: "Pup User Telnet, see ChatDoc.tioga", interpreted: FALSE]; chatTipTable _ TIPUser.InstantiateNewTIPTable["Chat.TIP"]; TIPUser.RegisterTIPPredicate[$ConnectionOpen, ConnectionOpen]; }; Init[]; }. March 31, 1982 9:27 pm, Stewart, copied from Laurel Chat April 1, 1982 4:06 pm, Stewart, own viewer class & TIP table April 4, 1982 5:18 pm, Stewart, Menu April 6, 1982 10:27 pm, Stewart, command line stuff 18-Apr-82 17:42:24, Use TypeScript again June 6, 1982 4:44 pm, Stewart, fix Menu instantiation to add Split June 6, 1982 8:08 pm, Stewart, using own Split code until viewers copies PropList September 21, 1982 4:26 pm, Stewart, Cedar 3.4 November 3, 1982 5:53 pm, Warren Teitelman January 23, 1983 10:18 pm, Stewart, Cedar 3.5, major rework January 24, 1983 5:49 pm, Stewart, Cedar 3.6 January 25, 1983 3:09 pm, Maxwell April 6, 1983 6:43 pm, Larry Stewart June 13, 1983 1:41 pm, Stewart, Cedar 4.2, added synchronous feature September 7, 1983 6:19 pm, Stewart, Cedar 5 January 6, 1984 1:13 pm, Stewart, TypeScript.BackSpace on Control-A and BS March 4, 1984 4:15:26 pm PST, Pavel, Cleanup, flash on Control-G, grab input focus on start up and connection. March 11, 1984 5:59:56 pm PST, Pavel, Map received back-quotes (140C) into normal single quotes. March 12, 1984 1:14:30 pm PST, Pavel, Gacha font changed to add a back-quote, so previous hack removed. February 26, 1985 2:50:10 pm PST, Pavel, Added -D switch for making a raw log of characters received from the server. Also changed it to make a newline on receipt of LF instead of CR May 31, 1985 0:52:39 am PDT, JLarson. Put features back in (ie. Vaxc auto-login, -D switch above) August 4, 1985 4:23:31 pm PDT, JLarson. Fixed Monitor lock problem. September 12, 1985 12:35:51 pm PDT, Pavel, Put newline change from February 26, 1985 back in; it seems to have been lost at some point. ^Chat.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Larry Stewart, January 6, 1984 1:28 pm Warren Teitelman, November 3, 1982 5:53 pm Last Edited by: Maxwell, January 25, 1983 3:09 pm Last Edited by: Paul Rovner, November 29, 1983 10:56 am Last Edited by: Pavel Curtis on September 12, 1985 12:44:59 pm PDT Last Edited by: Russ Atkinson (RRA) April 26, 1985 7:10:14 pm PST Last Edited by: Doug Wyatt, April 8, 1985 6:48:35 pm PST Last Edited by: Hal Murray June 15, 1985 6:15:32 pm PDT Last Edited by: John Larson, August 7, 1985 3:56:06 pm PDT Hal Murray, February 23, 1986 3:48:24 am PST This procedure is FORKed by the UserToServer process at the time a connection is opened. It is JOINED whenever the connection is closed. It also goes away if the Chat viewer is destroyed. Use the current user's logged-in password UserAbort is active because Chat.TIP is not activated until h.state = running (due to the TIP predicate), so control-DEL sets UserAbort. However, while in this routine, UserAbort is used only to stop taking characters from the keyStream, not to close the connection. If the user types control-DEL here, she will have to type it again to really close the connection. [char: CHAR] RETURNS [IO.CharClass] This procedure includes '+ in order to handle Pup names of the form "dls+100004" iconic unless command line not empty create log file plug in Chat TIP table. IF -i then we are just initializing the host name, else really connect closeEvent _ ViewerEvents.RegisterEventProc[proc: MyClose, event: close]; last viewer destroyed ! Close connection and exit This EventProc exits only to keep h.ts pointing to a valid copy of the typescript viewer. It is only needed for the use of TypeScript.InsertCharAtFrontOfBuffer. Race? ViewerEvents.UnRegisterEventProc[proc: closeEvent, event: close]; PROC [] RETURNS [BOOL] main program for chat Ê`˜codešœ ™ Kšœ Ïmœ1™˜>—K˜—Kšœ˜Kšœ˜šžœFžœžœ˜TKšœ)™)Kšœ˜Kšœ,˜,K˜—Kšœ˜Kšœ ˜ K˜—K˜K˜—š¡œžœžœ˜Cšžœ ž˜Kšžœ(˜,Kšžœ˜—K˜K˜—Kšœï™ïK˜š¡œžœžœ˜&Kšœžœ˜˜šžœžœžœž˜3Kšžœžœžœžœ˜?K˜.K˜šžœ žœ˜K˜K˜ K˜—Kšžœ˜—Kšžœ˜Kšž˜˜šžœžœžœ˜K˜Kšœžœ˜K˜—Kšžœ žœ˜)K˜—K˜—K˜K˜—š¡œžœ˜Kšžœžœ˜ K˜Kšžœžœžœžœ ˜UKšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜$KšœQ˜QKšžœžœžœžœ˜NKšžœ˜"K˜šžœžœžœ˜K˜Kšžœ˜K˜—K˜/K˜K˜ K˜Kšœžœ˜-Kšœ˜K˜—š¡ œžœ žœ žœ˜K˜—šžœžœžœ(˜Wšœžœ žœžœ˜*Kšœ'žœ˜;Kšžœ˜ K˜——Kšœ$™$KšœGžœžœ˜eK˜!K˜K˜Kšœ™šœžœ;˜Kšœžœ žœžœ˜*Kšœ'žœ˜?Kšžœ˜ K˜K˜——Kšœ™K˜"Kšœžœ˜K˜*Kšœhžœ˜oK˜šžœžœž˜KšœV˜V—šžœ žœ˜Jšœ=˜=—Kšœžœ˜ šžœžœ˜K˜Kšœžœ˜KšœF™Fšžœ ˜Kšžœ ˜Kšžœ6žœ žœ žœ ˜j—K˜—šžœ#žœ˜+KšœI™IK˜NK˜—Kšœžœ˜-Kšœ}žœ/˜°K˜ÂK˜®K˜ÀK˜®K˜žK˜K˜BKšœ~žœ1˜³K˜*K˜/šžœ˜Kšžœ˜šžœžœ˜Kšœ'žœ˜=K˜——K˜K˜—š¡ œžœžœ˜*Kšœžœ˜ Kšœžœ˜šž˜šžœ˜Kšžœžœ ˜"šžœ ˜ Kšœ™Kšœ™Kšœžœ˜Kšžœ˜ Kšœ˜—šœ˜Kšžœžœ žœ1žœ;˜…Kšœžœ˜Kšžœ˜ Kšœ˜—K˜K˜K˜—šžœžœ˜Kšžœžœžœžœ˜7šžœžœ˜Kšœ˜Kšžœ˜ —Kšœ˜—šžœ žœ˜Kšœžœ˜Kšžœ˜ K˜—K˜šžœž˜˜Kšžœžœžœ˜3K˜—˜Kšžœžœžœ˜3K˜—˜Kšžœ1žœ˜;K˜—˜šžœžœ˜K˜ K˜ K˜ K˜—K˜—˜šžœžœ˜K˜ K˜ K˜ K˜—K˜—šžœ˜ šžœ ž˜˜ K˜šžœ žœžœ˜.K˜K˜ K˜—Kšžœ˜K˜—Kšžœ˜—K˜——Kšžœ˜—šžœ˜šœ˜Kšžœžœžœ˜.Jšžœ žœ˜+K˜——J˜—˜J˜˜%Kšœ¡™¡Kšœ žœ-˜?Kšžœžœžœžœ˜šžœž˜˜Kšœ™K˜—˜Kšœžœ˜K˜4šžœ#žœ˜+K˜EKšœA™AK˜—K˜—šžœ˜ šžœžœ˜š¡œžœ˜+Kšžœ žœ ˜K˜—K˜!K˜—K˜——K˜K˜—˜#Kšœ žœ-˜?Kšžœžœžœžœ˜K˜EK˜K˜—˜Kšœžœžœ ˜'Kšœ žœ ˜Kšœ ˜ K˜"K˜BK˜K˜—˜Kšœžœžœ ˜'Kšœ žœ ˜Kšœ ˜ K˜"K˜@K˜K˜—˜ Kšœ žœ ˜K˜EK˜K˜—šœžœ˜&Kšœ žœ ˜Kšžœžœžœ˜!šžœžœžœ˜Kšžœžœ˜+K˜K˜K˜—K˜K˜—šœžœ˜'Kšœ žœ ˜Kšžœžœžœ˜!šžœžœžœ˜Kšžœžœ˜+Kšœ(˜(Kšœ0˜0—K˜K˜—˜Kšœ žœ ˜Kšžœžœžœ˜.K˜K˜——šœ˜Kšœ žœ ˜š¡œžœ˜7Kšžœ)žœžœ(˜[K˜K˜—K˜}Kšœžœ5˜KK˜K˜—– -- [BOOL]šœ(˜(Kšž¢™K˜ K˜?Kš žœžœžœžœžœ ˜:Kšœžœ-˜7Kš žœžœžœžœžœ ˜0Kšžœ ˜/K˜K˜—š¡œžœžœ˜VK˜Kšžœžœžœžœ˜šž˜K˜Kš žœ žœžœ žœžœ˜,K˜ Kšžœ˜—K˜K˜—š¡œžœžœ žœ ˜Eš¡œžœ˜,K˜K˜—K˜K˜K˜—š¡œžœ˜Kšœhžœ˜oK˜:K˜>K˜K˜—Kšœ™K˜K˜K˜Kšœ˜K˜K˜8Kšœ3žœ˜