DIRECTORY Booting USING [switches, RollbackProc, RegisterProcs], Convert USING [IntFromRope], File USING [IsDebugger, SystemVolume], FS USING [FileInfo, Error, StreamOpen], IO USING [ Backup, BreakProc, Close, CR, EndOfStream, Error, GetChar, GetIndex, GetRopeLiteral, GetTokenRope, int, PeekChar, PutF, PutFR, rope, SP, STREAM, TAB, time], Process USING [Detach], Rope USING [Cat, Concat, Equal, ROPE, SkipTo, Substr], RuntimeError USING [UNCAUGHT], UserCredentials USING [CredentialsChangeProc, Get, RegisterForChange], UserProfile USING [ProfileChangeReason, ProfileChangedProc] ; UserProfileImpl: CEDAR MONITOR IMPORTS Booting, Convert, File, FS, IO, Process, Rope, RuntimeError, UserCredentials EXPORTS UserProfile = 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 ]; Boolean: PUBLIC PROC [key: ROPE, default: BOOL _ FALSE] RETURNS [value: BOOL] = { entry: ProfileEntry = Lookup[key]; val: ROPE = 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 = Lookup[key]; val: ROPE = 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 = Lookup[key]; val: ROPE = 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 = Lookup[key]; IF entry = NIL THEN RETURN[default] ELSE RETURN[entry.tokens]; }; Line: PUBLIC PROC [key: ROPE, default: ROPE] RETURNS [value: ROPE] = { entry: ProfileEntry = 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 File.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] = { RETURN[profileName]; }; profileList: ProfileList _ NIL; profileName: ROPE _ NIL; ParseProfile: ENTRY PROC [startNewErrorLog: BOOL _ FALSE] = { OPEN IO; name: ROPE; stream: IO.STREAM; CheckForFile: PROC [file: ROPE] RETURNS [found: BOOL _ TRUE] = { [] _ FS.FileInfo[file ! FS.Error => {found _ FALSE; CONTINUE}]; }; IF startNewErrorLog THEN errorLog _ NIL; profileList _ NIL; profileName _ NIL; IF Booting.switches[p] THEN RETURN; -- return default values for the profile name _ UserCredentials.Get[].name; name _ name.Substr[0, name.SkipTo[0, "."]]; profileName _ name.Concat[".profile"]; IF NOT CheckForFile[profileName] THEN profileName _ "User.profile"; { ENABLE RuntimeError.UNCAUGHT => GOTO Out; stream _ FS.StreamOpen[fileName: profileName, accessOptions: $read]; DO 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; EXITS Out => NULL; }; IF stream # NIL THEN stream.Close[]; }; Report: ENTRY PROC [entry: ProfileEntry _ NIL, msg: ROPE] = { 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] = { IF errorLog # NIL THEN { errorLog.Close[]; errorLog _ NIL; RETURN["UserProfile.log"]; } ELSE RETURN[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--, clientData: REF ANY _ NIL--] = { itemL: LIST OF ProfileChangedItem = CONS[[proc, --clientData--NIL], NIL]; AcquireList[]; IF profileChangedList = NIL THEN profileChangedList _ itemL ELSE profileChangedListLast.rest _ itemL; profileChangedListLast _ itemL; ReleaseList[]; DoIt[itemL.first, firstTime]; }; ProfileChanged: PUBLIC PROC [reason: UserProfile.ProfileChangeReason] = { 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 } ]; }; NoticeCredentialsChange: UserCredentials.CredentialsChangeProc = { TRUSTED{Process.Detach[FORK ProfileChanged[rollBack]]}; }; NoticeRollback: Booting.RollbackProc = {ProfileChanged[rollBack]}; AcquireList: ENTRY PROC = { WHILE listInUse DO WAIT listAvailable; ENDLOOP; listInUse _ TRUE; }; ReleaseList: ENTRY PROC = { listInUse _ FALSE; BROADCAST listAvailable; }; UserCredentials.RegisterForChange[proc: NoticeCredentialsChange, clientData: NIL]; TRUSTED {Booting.RegisterProcs[r: NoticeRollback]}; ParseProfile[! RuntimeError.UNCAUGHT => CONTINUE]; END. tUserProfileImpl.mesa; Teitelman on January 13, 1983 1:52 pm Levin, October 6, 1983 12:03 pm Russ Atkinson, October 4, 1983 11:17 am Paul Rovner on December 12, 1983 8:45 pm Types Accessing profile Building profileList 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 wer registered (otherwise deadlocks may occur at rollback time). Initialization Κ ƒ– "Cedar" style˜šΟc™Jš œ™%J™J™'Jšœ™(—J˜šΟk ˜ Jšœžœ)˜6Jšœžœ˜Jšœžœ˜&Jšžœžœ˜'šžœžœ˜ Jš œžœižœžœžœ˜œ—Jšœžœ ˜Jšœžœžœ˜6Jšœ žœžœ˜Jšœžœ1˜FJšœ žœ*˜;J˜J˜—J˜JšΠblœžœž˜J˜Jšžœžœžœ0˜VJ˜Jšžœ ˜J˜Jšœž˜headšœ™JšΟn œž œžœ˜)Jš  œžœžœ˜'Jš  œžœžœžœ žœžœžœ žœ˜MJšžœžœžœ˜J˜šœžœžœ˜#Jšœ%˜%Jšœ žœž˜Jšœ˜——™procš œžœžœžœ žœžœžœ žœ˜QJ˜"Jšœžœ˜Jšžœžœžœžœ ˜"Jš žœžœžœžœžœ˜.Jš žœžœžœžœžœ˜0J˜/Jšžœ ˜J˜—š  œž œžœ žœžœ žœ˜FJ˜"Jšœžœ˜Jšžœžœžœžœ ˜"šœ˜šœž œ˜Jšœ˜J˜,Jšž ˜ Jšœ˜—Jšœ˜—J˜—J˜š  œž œžœ žœžœ žœ˜GJ˜"Jšœžœ˜Jšžœžœžœžœ ˜"J˜—š  œž œžœ žœžœžœžœ žœžœžœ˜^J˜"Jšžœ žœžœžœ ˜#Jšžœžœ˜J˜—š  œž œžœ žœžœ žœ˜FJ˜"Jšžœ žœžœžœ ˜$š žœžœžœžœžœžœž˜;Jšžœ žœžœž˜(J˜ Jšžœ˜—J˜—š  œžœžœžœžœ˜9Lšžœžœžœ˜Lšžœ˜Lšœ˜—š  œžœžœžœžœ˜Dšžœ&žœ˜.Jšœžœ!˜)šžœ&žœžœž˜9Jšžœ+žœžœžœ ˜IJšžœ˜—J˜—šžœ&žœžœž˜9Jšžœ,žœžœžœ ˜JJšžœ˜—Jšžœžœ˜ J˜—š  œžœžœ žœžœ˜DJšžœ žœžœžœžœžœžœ˜6J˜Jšžœžœžœ)˜HJ˜—š œž œžœžœ˜.Jšžœ˜Jšœ˜——™Jš  œžœ˜Jš  œžœžœ˜š   œžœžœžœžœ˜>Jšž˜Jšœžœ˜ Jšœžœžœ˜š   œžœžœžœ žœžœ˜@Jš œžœžœžœžœ˜?Jšœ˜—Jšžœžœ žœ˜(Jšœžœ˜Jšœžœ˜Jšžœžœžœ(˜LJ˜"Jšœ,˜,Jšœ&˜&Jšžœžœžœ˜Cšœ˜Jšžœžœžœ˜)Jšœ žœ9˜Dšž˜š   œžœžœžœžœžœ˜@šž˜Jš žœžœžœžœžœ˜.Jšœ˜šžœž˜ Jš œžœžœ žœžœ˜%Jšœ ˜ šœ˜Jšœ˜šžœžœ˜ Jšœ™Jšœ˜Jšžœ˜ J˜—šž˜Jšœ2™2Jšœ˜šžœž˜ šœ˜Jšœ%™%Jšœ˜Jšžœ žœžœ˜Jšžœ˜Jšœ˜—Jšœžœžœžœ˜*Jšžœ˜—Jšžœ˜—J˜—Jšžœžœ˜—Jšœ˜Jšžœ˜—Jšžœ˜J˜—š  œžœžœžœ˜/Jšœžœ˜#Jšœ˜Jšœžœ˜ šžœž˜Jšœ žœ˜Jšœ&˜&Jšžœ1˜8—J˜—šœ žœ˜šžœžœž˜Jšžœžœžœžœ ˜Jšžœžœ˜Jšžœ ˜J˜—Jšœ˜—Jšœžœžœ˜Jš œžœžœžœžœ˜!Jšœ žœ˜Jšœžœžœ˜Jšœ žœ˜Jšžœžœžœžœ˜!šžœ ž˜Jšœ˜-šžœ˜ JšœL™Lšž˜Jš žœžœžœžœžœ˜=Jšžœ˜—Jšœžœžœ˜EJšžœ˜Jšœ˜——šž˜Jš œžœžœžœžœ˜Jšœ ˜ Jšžœ žœžœžœ˜Jšœžœ˜šžœž˜ Jšžœ˜Jšžœ!˜%—Jšžœ˜—šžœžœž˜!šœ˜Jšœ˜Jšœžœ"žœ žœ˜HJšœ˜——Jšœžœžœ8˜NJšžœ˜—šž˜Jšœžœ˜ —J˜—Jšžœ žœžœ˜$Jšœ˜——™š  œžœžœžœžœ˜=Jšœ˜Jšœ˜—J˜š  œžœžœžœžœ˜HJšžœžœžœ˜)šžœ žœžœ˜Jšœ žœA˜NJšœ%žœžœ ˜FJ˜—Jšœžœ ˜'šžœ žœž˜Jšœžœžœ˜K—Jšœ˜J˜—J˜š  œžœžœ žœ˜;šžœ žœžœ˜Jšœ˜Jšœ žœ˜Jšžœ˜Jšœ˜—Jšžœžœžœ˜J˜—J˜Jš œžœžœžœ˜—šœ™J™ Jš œžœžœžœ˜5Jšœžœžœžœ˜9Jšœ žœžœ˜Jšœž œ˜(J˜š œžœžœ˜%Jšœ$œ˜HJš œžœžœžœžœžœ˜IJ˜Jšžœžœžœ˜;Jšžœ%˜)Jšœ˜J˜Jšœ˜J˜—J˜š œžœžœ.˜IJ˜Jšœžœžœžœ˜Iš žœžœžœ1žœžœž˜OJ˜Jšžœ˜—J˜J˜—J˜š œžœH˜Ršœ˜%šœžœ˜Jšœ:˜:Jšž˜Jšœ˜—Jšœ˜—Jšœ˜—J˜šœB˜BJšžœžœ˜7Jšœ˜—J˜JšœB˜BJ˜š  œžœžœ˜Jšžœ žœžœžœ˜/Jšœ žœ˜Jšœ˜—J˜š  œžœžœ˜Jšœ žœ˜Jšž œ˜Jšœ˜—J˜—šœ™J˜JšœMžœ˜RJšžœ,˜3Jšœž œžœ˜3—Jšžœ˜J˜—…—Τ/Λ