DIRECTORY Booting USING [switches], Convert USING [IntFromRope], File USING [SystemVolume], FileBackdoor USING [IsDebugger], FS USING [Error, GetName, OpenFileFromStream, StreamOpen], IO USING [Backup, BreakProc, Close, CR, EndOfStream, Error, GetChar, GetIndex, GetRopeLiteral, GetTokenRope, int, PeekChar, PutF, PutFR, rope, SP, STREAM, TAB, time], Rope USING [Cat, Concat, Equal, ROPE, SkipTo, Substr], RuntimeError USING [UNCAUGHT], ThisMachine USING [Name], UserCredentials USING [Get], UserProfile USING [ProfileChangeReason, ProfileChangedProc], UserProfileBackdoor USING [GuestProcsRec]; UserProfileImpl: CEDAR MONITOR IMPORTS Booting, Convert, File, FileBackdoor, FS, IO, Rope, RuntimeError, ThisMachine, UserCredentials EXPORTS UserProfile, UserProfileBackdoor = BEGIN ProfileList: TYPE = LIST OF ProfileEntry; ProfileEntry: TYPE = REF ProfileRecord; ProfileRecord: TYPE = RECORD[key: ROPE, tokens: LIST OF ROPE, position: INT]; ROPE: TYPE = Rope.ROPE; ProfileChangedItem: TYPE = RECORD [ proc: UserProfile.ProfileChangedProc, clientData: REF ANY ]; IsGuestProcess: PROC [] RETURNS [isGuest: BOOL] _ FalseProc; GuestProcs: REF UserProfileBackdoor.GuestProcsRec _ NEW[UserProfileBackdoor.GuestProcsRec _ [FalseProc]] ; Boolean: PUBLIC PROC [key: ROPE, default: BOOL _ FALSE] RETURNS [value: BOOL] = { entry: ProfileEntry; val: ROPE; IF IsGuestProcess[] THEN RETURN GuestProcs.Boolean[key, default]; entry _ Lookup[key]; val _ GetRope[entry]; IF val = NIL THEN RETURN[default]; IF val.Equal["TRUE", FALSE] THEN RETURN[TRUE]; IF val.Equal["FALSE", FALSE] THEN RETURN[FALSE]; Report[entry, val.Concat[" is not a Boolean"]]; RETURN[default]; }; Number: PUBLIC PROC [key: ROPE, default: INT] RETURNS [value: INT] = { entry: ProfileEntry; val: ROPE; IF IsGuestProcess[] THEN RETURN GuestProcs.Number[key, default]; entry _ Lookup[key]; val _ GetRope[entry]; IF val = NIL THEN RETURN[default]; value _ Convert.IntFromRope[val ! RuntimeError.UNCAUGHT => { value _ default; Report[entry, val.Concat[" is not an INT"]]; CONTINUE; } ]; }; Token: PUBLIC PROC [key: ROPE, default: ROPE] RETURNS [value: ROPE] = { entry: ProfileEntry; val: ROPE; IF IsGuestProcess[] THEN RETURN GuestProcs.Token[key, default]; entry _ Lookup[key]; val _ GetRope[entry]; IF val = NIL THEN RETURN[default]; value _ val; }; ListOfTokens: PUBLIC PROC [key: ROPE, default: LIST OF ROPE] RETURNS [value: LIST OF ROPE] = { entry: ProfileEntry; IF IsGuestProcess[] THEN RETURN GuestProcs.ListOfTokens[key, default]; entry _ Lookup[key]; IF entry = NIL THEN RETURN[default] ELSE RETURN[entry.tokens]; }; Line: PUBLIC PROC [key: ROPE, default: ROPE] RETURNS [value: ROPE] = { entry: ProfileEntry; IF IsGuestProcess[] THEN RETURN GuestProcs.Line[key, default]; entry _ Lookup[key]; IF entry = NIL THEN RETURN[default]; FOR l: LIST OF ROPE _ entry.tokens, l.rest UNTIL l = NIL DO IF value = NIL THEN value _ l.first ELSE value _ value.Cat[" ", l.first]; ENDLOOP; }; Lookup: ENTRY PROC [key: ROPE] RETURNS [ProfileEntry] = { ENABLE UNWIND => NULL; RETURN[LookupInternal[key]] }; LookupInternal: INTERNAL PROC [key: ROPE] RETURNS [ProfileEntry] = { IF FileBackdoor.IsDebugger[File.SystemVolume[]] THEN { s2: ROPE = Rope.Concat["Debugger.", key]; FOR l: ProfileList _ profileList, l.rest UNTIL l = NIL DO IF Rope.Equal[s1: l.first.key, s2: s2, case: FALSE] THEN RETURN[l.first]; ENDLOOP; }; FOR l: ProfileList _ profileList, l.rest UNTIL l = NIL DO IF Rope.Equal[s1: l.first.key, s2: key, case: FALSE] THEN RETURN[l.first]; ENDLOOP; RETURN[NIL]; }; GetRope: PROC [entry: ProfileEntry] RETURNS [value: ROPE] = INLINE { IF entry = NIL OR entry.tokens = NIL THEN RETURN[NIL]; value _ entry.tokens.first; IF entry.tokens.rest # NIL THEN Report[entry, "extra material on line"]; }; GetProfileName: PUBLIC PROC RETURNS [ROPE] = { IF IsGuestProcess[] THEN RETURN GuestProcs.GetProfileName[] ELSE RETURN[profileName]; }; profileList: ProfileList _ NIL; profileName: ROPE _ NIL; ParseProfile: ENTRY PROC [startNewErrorLog: BOOL _ FALSE] = { ENABLE UNWIND => NULL; stream: IO.STREAM _ NIL; name: ROPE _ UserCredentials.Get[].name; IF startNewErrorLog THEN errorLog _ NIL; profileList _ NIL; profileName _ NIL; IF Booting.switches[p] THEN RETURN; -- return default values for the profile name _ Rope.Cat["///", name.Substr[0, name.SkipTo[0, "."]], ".profile"]; stream _ FS.StreamOpen[fileName: Rope.Cat["///", ThisMachine.Name[], ".machineProfile"] ! FS.Error => CONTINUE ]; IF stream # NIL THEN { ParseProfileInternal[stream]; stream _ NIL; }; stream _ FS.StreamOpen[fileName: "///Server.profile" ! FS.Error => CONTINUE ]; IF stream # NIL THEN { profileName _ "///Server.profile"; ParseProfileInternal[stream]; RETURN; }; stream _ FS.StreamOpen[fileName: name ! FS.Error => CONTINUE ]; IF stream # NIL THEN { profileName _ name; ParseProfileInternal[stream]; RETURN; }; stream _ FS.StreamOpen[fileName: "///User.profile" ! FS.Error => CONTINUE ]; IF stream # NIL THEN { profileName _ "///User.profile"; ParseProfileInternal[stream]; }; }; ParseProfileInternal: INTERNAL PROC [stream: IO.STREAM] = { DO ENABLE { RuntimeError.UNCAUGHT => EXIT; UNWIND => IO.Close[stream]; }; SkipWhite: PROC [flushLines: BOOL _ FALSE] RETURNS [c: CHAR] = { DO ENABLE IO.Error, IO.EndOfStream => GO TO stop; c _ stream.PeekChar[]; SELECT c FROM '\n => IF NOT flushLines THEN RETURN; <= 40C => {}; '- => {-- could be a comment [] _ stream.GetChar[]; IF stream.PeekChar[] # '- THEN { stream.Backup[c]; RETURN [c]; }; DO c _ stream.GetChar[]; SELECT c FROM '\n => { stream.Backup[c]; IF flushLines THEN EXIT; RETURN; }; '- => IF stream.PeekChar[] = '- THEN EXIT; ENDCASE; ENDLOOP; }; ENDCASE => RETURN; [] _ stream.GetChar[]; ENDLOOP; EXITS stop => {c _ 0C}; }; LocalToken: PROC [flushLines: BOOL _ FALSE] = { stop: CHAR _ SkipWhite[flushLines]; position _ stream.GetIndex[]; token _ NIL; SELECT stop FROM 0C, '\n => RETURN; '" => token _ stream.GetRopeLiteral[]; ENDCASE => token _ stream.GetTokenRope[tokenProc].token; }; tokenProc: IO.BreakProc = { RETURN[SELECT char FROM IO.SP, IO.TAB, ', => sepr, IO.CR, ': => break, ENDCASE => other ]; }; token: ROPE _ NIL; tokens, tail: LIST OF ROPE _ NIL; position: INT; key: ROPE _ NIL; LocalToken[TRUE]; IF (key _ token) = NIL THEN EXIT; SELECT SkipWhite[] FROM ': => [] _ stream.GetChar[]; -- flush the ': ENDCASE => { DO IF stream.GetChar[ ! IO.EndOfStream => EXIT] = '\n THEN EXIT; ENDLOOP; ReportInternal[msg: IO.PutFR["missing : at [%d]", IO.int[position]]]; LOOP; }; DO list: LIST OF ROPE _ NIL; LocalToken[]; IF token = NIL THEN EXIT; list _ LIST[token]; IF tail = NIL THEN {tail _ tokens _ list} ELSE {tail.rest _ list; tail _ list}; ENDLOOP; IF LookupInternal[key] # NIL THEN ReportInternal[ entry: LookupInternal[key], msg: IO.PutFR["%g also appears at [%d]", IO.rope[key], IO.int[position]] ]; profileList _ CONS[NEW[ProfileRecord _ [key, tokens, position]], profileList]; ENDLOOP; IO.Close[stream]; }; Report: ENTRY PROC [entry: ProfileEntry _ NIL, msg: ROPE] = { ENABLE UNWIND => NULL; ReportInternal[entry, msg]; }; ReportInternal: INTERNAL PROC [entry: ProfileEntry _ NIL, msg: ROPE] = { ENABLE RuntimeError.UNCAUGHT => CONTINUE; IF errorLog = NIL THEN { errorLog _ FS.StreamOpen[fileName: "///UserProfile.log", accessOptions: $create]; errorLog.PutF["Processing %g at %t", IO.rope[profileName], IO.time[]]; }; errorLog.PutF["\n\n%g", IO.rope[msg]]; IF entry # NIL THEN errorLog.PutF[", at %g [%d]", IO.rope[entry.key], IO.int[entry.position]]; }; GetErrorLog: PUBLIC ENTRY PROC RETURNS [fileName: ROPE _ NIL] = { ENABLE UNWIND => {errorLog _ NIL; NULL}; IF errorLog # NIL THEN { fileName _ FS.GetName[FS.OpenFileFromStream[errorLog]].fullFName; errorLog.Close[]; errorLog _ NIL; }; }; errorLog: IO.STREAM _ NIL; profileChangedList: LIST OF ProfileChangedItem _ NIL; profileChangedListLast: LIST OF ProfileChangedItem _ NIL; listInUse: BOOL _ FALSE; listAvailable: CONDITION _ [timeout: 0]; CallWhenProfileChanges: PUBLIC PROC [proc: UserProfile.ProfileChangedProc] = { itemL: LIST OF ProfileChangedItem = CONS[[proc, --clientData--NIL], NIL]; IF IsGuestProcess[] THEN {GuestProcs.CallWhenProfileChanges[proc]; RETURN}; AcquireList[]; IF profileChangedList = NIL THEN profileChangedList _ itemL ELSE profileChangedListLast.rest _ itemL; profileChangedListLast _ itemL; ReleaseList[]; DoIt[itemL.first, firstTime]; }; ProfileChanged: PUBLIC PROC [reason: UserProfile.ProfileChangeReason] = { IF IsGuestProcess[] THEN {GuestProcs.ProfileChanged[reason]; RETURN}; AcquireList[]; ParseProfile[startNewErrorLog: TRUE ! RuntimeError.UNCAUGHT => CONTINUE]; FOR l: LIST OF ProfileChangedItem _ profileChangedList, l.rest UNTIL l = NIL DO DoIt[l.first, reason]; ENDLOOP; ReleaseList[]; }; DoIt: PROC [item: ProfileChangedItem, reason: UserProfile.ProfileChangeReason] = { item.proc[reason--, item.clientData-- ! RuntimeError.UNCAUGHT => { Report[msg: "Problem while executing ProfileChangedProc"]; CONTINUE } ]; }; AcquireList: ENTRY PROC = { WHILE listInUse DO WAIT listAvailable; ENDLOOP; listInUse _ TRUE; }; ReleaseList: ENTRY PROC = { listInUse _ FALSE; BROADCAST listAvailable; }; RegisterGuestProcs: PUBLIC PROC [newProcs: REF UserProfileBackdoor.GuestProcsRec] = { GuestProcs _ newProcs; IsGuestProcess _ newProcs.IsGuestProcess; }; FalseProc: PROC RETURNS [isGuest: BOOL] = { isGuest _ FALSE; }; END. ΖUserProfileImpl.mesa; Copyright c 1984, 1985 by Xerox Corporation. All rights reserved. Bob Hagmann May 22, 1985 4:50:10 pm PDT Russ Atkinson (RRA) July 3, 1985 8:40:09 am PDT Hal Murray, January 14, 1986 4:22:44 am PST Types Variables Accessing profile Building profileList There is a machine profile for machine-dependent stuff The presence of "///Server.profile" overrides a user's profile (for use by servers). The user has supplied a profile Use the default profile (if any) Given an open stream, parse the profile and close the stream. Successive calls "overwrite" previous entries. it is not a comment the end of a comment is either a '\n or a double - Only flush the \n if it was requested key was NOT followed by ':, so flush to the end of line and report the error Reporting errors When the profile changes Note that registration order is important; procs must be called in the same order in which they were registered (otherwise deadlocks may occur at rollback time). Exported to UserProfileBackdoor default IsGuestProcess (not really exported) NO initialization, since InstallerImpl has that responsibility! Bob Hagmann May 3, 1985 11:38:36 am PDT changes to: DIRECTORY, UserProfileImpl, Boolean, Number, CallWhenProfileChanges, ProfileChanged, FalseProc Russ Atkinson (RRA) May 13, 1985 4:33:37 pm PDT Eliminated UserCredentials change watcher (InstallerImpl has this responsibility), UserProfileImpl Bob Hagmann May 22, 1985 4:50:11 pm PDT changes to: Token, ListOfTokens, Line, GetProfileName, UserProfileImpl, ProfileList, ProfileEntry, ProfileRecord, ProfileChangedItem, IsGuestProcess, Boolean, Number, Lookup, LookupInternal, GetRope, profileList, profileName, ParseProfile, ParseProfileInternal, SkipWhite (local of ParseProfileInternal), LocalToken (local of ParseProfileInternal), Report, ReportInternal, GetErrorLog, errorLog, profileChangedList, CallWhenProfileChanges, ProfileChanged, DoIt, AcquireList, ReleaseList, RegisterGuestProcs, FalseProc Κ – "Cedar" style˜codešΟc™Kšœ Οmœ7™BK™'K™/K™+—K˜šΟk ˜ KšœŸœ ˜KšœŸœ˜KšœŸœ˜Kšœ Ÿœ˜ KšŸœŸœ2˜:Kš ŸœŸœŸœiŸœŸœŸœ˜¦KšœŸœŸœ˜6Kšœ ŸœŸœ˜Kšœ Ÿœ˜KšœŸœ˜Kšœ Ÿœ+˜K˜K˜—š’œŸœŸœŸœ ŸœŸœ Ÿœ˜FK˜KšŸœŸœŸœ˜>K˜KšŸœ ŸœŸœŸœ ˜$š ŸœŸœŸœŸœŸœŸœŸ˜;KšŸœ ŸœŸœŸ˜(K˜ KšŸœ˜—K˜K˜—š ’œŸœŸœŸœŸœ˜9KšŸœŸœŸœ˜KšŸœ˜Kšœ˜K˜—š ’œŸœŸœŸœŸœ˜DšŸœ.Ÿœ˜6KšœŸœ!˜)šŸœ&ŸœŸœŸ˜9KšŸœ+ŸœŸœŸœ ˜IKšŸœ˜—K˜—šŸœ&ŸœŸœŸ˜9KšŸœ,ŸœŸœŸœ ˜JKšŸœ˜—KšŸœŸœ˜ K˜K˜—š ’œŸœŸœ ŸœŸœ˜DKšŸœ ŸœŸœŸœŸœŸœŸœ˜6K˜KšŸœŸœŸœ)˜HK˜K˜—š ’œŸœŸœŸœŸœ˜.šŸœ˜KšŸœŸœ˜'KšŸœŸœ˜—Kšœ˜——K™™K˜KšœŸœ˜Kšœ ŸœŸœ˜š ’ œŸœŸœŸœŸœ˜>KšŸœŸœŸœ˜KšœŸœŸœŸœ˜KšœŸœ ˜*KšŸœŸœ Ÿœ˜(KšœŸœ˜KšœŸœ˜KšŸœŸœŸœ(˜LKšœI˜Išœ ŸœL˜WKšœŸœ Ÿ˜Kšœ˜—šŸœ ŸœŸœ˜Kšœ6™6Kšœ˜Kšœ Ÿœ˜ K˜—šœ Ÿœ)˜4KšœŸœ Ÿ˜Kšœ˜—šŸœ ŸœŸœ˜KšœT™TKšœ"˜"Kšœ˜KšŸœ˜K˜—šœ Ÿœ˜%KšœŸœ Ÿ˜Kšœ˜—šŸœ Ÿœ˜Kšœ™Kšœ˜Kšœ˜KšŸœ˜K˜—K™ šœ Ÿœ'˜2KšœŸœ Ÿ˜Kšœ˜—šŸœ ŸœŸœ˜Kšœ ˜ Kšœ˜K˜—K˜K˜—š ’œŸœŸœ ŸœŸœ˜