UserProfileImpl.mesa;
Edited by Teitelman on January 13, 1983 1:52 pm
DIRECTORY
AMBridge USING [TVForProc],
AMEvents USING [Debugging, Debugged],
AMTypes USING [GlobalParent, TVToName, TV],
CedarSnapshot USING [CheckpointProc, Register, RollbackProc],
Convert USING [Value, Parse, ValueToRope],
Directory USING [Lookup, ignore, Error],
IO USING [Close, CR, CreateFilterCommentsStream, CreateOutputStreamToRope, EndOf, GetChar, GetIndex, GetOutputStreamRope, int, PeekChar, PutF, PutSignal, rope, STREAM, time, TAB, SP,GetRope, GetToken, SkipOver, TokenProc, BreakProc, WhiteSpace],
FileIO USING [Open],
PilotSwitches USING [switches],
Rope USING [Cat, Concat, Equal, Find, ROPE, Substr, ToRefText],
UserCredentials USING [GetUserCredentials],
UserProfile USING [ProfileChangeReason, ProfileChangedProc]
;
UserProfileImpl: CEDAR MONITOR
IMPORTS CedarSnapshot, Convert, FileIO, UserCredentials, IO, Rope, Directory, PilotSwitches, AMTypes, AMBridge, AMEvents
EXPORTS UserProfile
= BEGIN OPEN UserProfile;
Types
ProfileList: TYPE = LIST OF ProfileEntry;
ProfileEntry: TYPE = REF ProfileRecord;
ProfileRecord: TYPE = RECORD[key: Rope.ROPE, tokens: LIST OF Rope.ROPE, position: INT];
Accessing profile
Boolean: PUBLIC PROCEDURE [key: Rope.ROPE, default: BOOLEANFALSE] RETURNS [value: BOOLEAN] = {
entry: ProfileEntry = Lookup[key];
val: Rope.ROPE = GetRope[entry];
IF val = NIL THEN RETURN[default];
IF Rope.Equal[val, "TRUE", FALSE] THEN RETURN[TRUE];
IF Rope.Equal[val, "FALSE", FALSE] THEN RETURN[FALSE];
Report[entry, Rope.Concat[val, " is not a Boolean"]];
RETURN[default];
};
Number: PUBLIC PROCEDURE [key: Rope.ROPE, default: INT] RETURNS [value: INT] = {
entry: ProfileEntry = Lookup[key];
val: Rope.ROPE = GetRope[entry];
IntFromRope: PROCEDURE [rope: Rope.ROPE] RETURNS [INT] = {
v: Convert.Value = Convert.Parse[[rope[rope]]].value;
RETURN [NARROW[v, Convert.Value[signed]].signed];
};
IF val = NIL THEN RETURN[default];
value ← IntFromRope[val ! ANY =>
{value ← default;
Report[entry, Rope.Concat[val, " is not an INT"]];
CONTINUE;
}]
};
Token: PUBLIC PROCEDURE [key: Rope.ROPE, default: Rope.ROPE] RETURNS [value: Rope.ROPE] = {
entry: ProfileEntry = Lookup[key];
val: Rope.ROPE = GetRope[entry];
IF val = NIL THEN RETURN[default];
value ← val;
};
ListOfTokens: PUBLIC PROCEDURE [key: Rope.ROPE, default: LIST OF Rope.ROPE] RETURNS [value: LIST OF Rope.ROPE] = {
entry: ProfileEntry = Lookup[key];
IF entry = NIL THEN RETURN[default]
ELSE RETURN[entry.tokens];
};
Line: PUBLIC PROCEDURE [key: Rope.ROPE, default: Rope.ROPE] RETURNS [value: Rope.ROPE] = {
entry: ProfileEntry = Lookup[key];
IF entry = NIL THEN RETURN[default];
FOR l: LIST OF Rope.ROPE ← entry.tokens, l.rest UNTIL l = NIL DO
IF value = NIL THEN value ← l.first ELSE
value ← Rope.Cat[value, " ", l.first];
ENDLOOP;
};
Lookup: PROCEDURE [key: Rope.ROPE] RETURNS [ProfileEntry ← NIL] = INLINE {
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;
};
GetRope: PROCEDURE [entry: ProfileEntry] RETURNS [value: Rope.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.ROPE] = {
RETURN[profileName];
};
Bulding profileList
profileList: ProfileList ← NIL;
profileName: Rope.ROPENIL;
ParseProfile: ENTRY PROCEDURE = {
OPEN IO;
name: Rope.ROPE;
i: INT;
stream: IO.STREAM;
CheckForFile: PROC [file: Rope.ROPE] RETURNS [found: BOOLEAN] = TRUSTED { -- Directory
fName: LONG STRING;
fName ← LOOPHOLE[Rope.ToRefText[file]];
found ← TRUE;
[] ← Directory.Lookup[fileName: fName, permissions: Directory.ignore
! Directory.Error =>
{found ← FALSE; CONTINUE}];
};
profileList ← NIL;
profileName ← NIL;
IF PilotSwitches.switches.n = down THEN RETURN; -- return default values
name ← UserCredentials.GetUserCredentials[].name;
i ← Rope.Find[name, "."];
IF i # -1 THEN name ← Rope.Substr[base: name, len: i];
profileName ← Rope.Concat[name, ".profile"];
IF NOT CheckForFile[profileName] THEN profileName ← "User.profile";
{
ENABLE {
UNWIND, AMEvents.Debugging, AMEvents.Debugged => NULL;
ANY => GOTO Out;
};
stream ← CreateFilterCommentsStream[FileIO.Open[fileName: profileName,
accessOptions: read, createOptions: oldOnly]];
DO
tokenProc: IO.BreakProc = {
RETURN[SELECT char FROM
IO.SP, IO.TAB, ', => sepr,
IO.CR => break,
ENDCASE => other
];
};
whiteSpace: IO.BreakProc = {
RETURN[IF char = CR THEN break ELSE IO.WhiteSpace[char]]
};
isACR: IO.BreakProc = {
RETURN[IF char = CR THEN break ELSE other]
};
key, token: Rope.ROPE;
tokens, tail: LIST OF Rope.ROPE;
position: INT;
position ← stream.GetIndex[];
key ← IO.GetToken[stream];
-- see if key is followed by a :
IO.SkipOver[stream, whiteSpace]; -- skips over spaces, tabs, but stops at CR
IF stream.PeekChar[] # ': THEN
{IO.SkipOver[stream, isACR];
Report1[msg: Rope.Cat["missing : at [", Convert.ValueToRope[[signed[position]]], "]"]];
LOOP;
};
[] ← stream.GetChar[]; -- the :
IO.SkipOver[stream, whiteSpace];
IF stream.PeekChar[] = '" THEN tokens ← LIST[IO.GetRope[stream]]
ELSE UNTIL stream.EndOf[] OR Rope.Equal[(token ← IO.GetToken[stream, tokenProc]), "\n"] DO
l: LIST OF Rope.ROPE = LIST[token];
IF tail = NIL THEN {tail ← l; tokens ← l}
ELSE {tail.rest ← l; tail ← l};
ENDLOOP;
IF Lookup[key] # NIL THEN Report1[entry: Lookup[key], msg: Rope.Cat[key, " also appears at [", Convert.ValueToRope[[signed[position]]], "]"]];
profileList ← CONS[NEW[ProfileRecord ← [key, tokens, position]], profileList];
ENDLOOP;
EXITS
Out => NULL;
};
IF stream # NIL THEN stream.Close[];
};
Reporting errors
Report: ENTRY PROCEDURE [entry: ProfileEntry ← NIL, msg: Rope.ROPE] = {
Report1[entry, msg];
};
Report1: INTERNAL PROCEDURE [entry: ProfileEntry ← NIL, msg: Rope.ROPE] = {
OPEN IO;
ENABLE {
UNWIND, AMEvents.Debugging, AMEvents.Debugged => NULL;
ANY => CONTINUE;
};
IF errorLog = NIL THEN
{errorLog ← FileIO.Open[fileName: "UserProfile.log", accessOptions: overwrite];
errorLog.PutF["Processing %g at %t", rope[profileName], time[]];
};
errorLog.PutF["\n\n%g", rope[msg]];
IF entry # NIL THEN errorLog.PutF[", at %g [%d]", rope[entry.key], int[entry.position]];
};
GetErrorLog: PUBLIC ENTRY PROC RETURNS [fileName: Rope.ROPE] = {
IF errorLog # NIL THEN
{errorLog.Close[];
errorLog ← NIL;
RETURN["UserProfile.log"];
}
ELSE RETURN[NIL]
};
errorLog: IO.STREAMNIL;
When the profile changes
profileChangedList: LIST OF ProfileChangedProc ← NIL;
CallWhenProfileChanges: PUBLIC PROC [proc: ProfileChangedProc] = {
profileChangedList ← CONS[proc, profileChangedList];
DoIt[proc, firstTime];
};
ProfileChanged: PUBLIC PROC [reason: ProfileChangeReason] = {
errorLog ← NIL;
ParseProfile[!
UNWIND, AMEvents.Debugging, AMEvents.Debugged => NULL;
ANY => CONTINUE];
FOR l: LIST OF ProfileChangedProc ← profileChangedList, l.rest UNTIL l = NIL DO
DoIt[l.first, reason];
ENDLOOP;
};
DoIt: PROC [proc: ProfileChangedProc, reason: ProfileChangeReason] = {
inner: PROC = {
proc[reason !
UNWIND, AMEvents.Debugging, AMEvents.Debugged => NULL;
ANY => {
OPEN IO;
stream: STREAM;
procTV: AMTypes.TV;
stream ← CreateOutputStreamToRope[];
TRUSTED {procTV ← AMBridge.TVForProc[proc]};
stream.PutF["Problem while executing ProfileChangedProc %g.%g: ", rope[AMTypes.TVToName[AMTypes.GlobalParent[procTV]]], rope[AMTypes.TVToName[procTV]]];
IO.PutSignal[stream];
Report[msg: GetOutputStreamRope[stream]];
CONTINUE;
};
];
};
inner[!
UNWIND, AMEvents.Debugging, AMEvents.Debugged => NULL;
ANY => CONTINUE; -- to bulletproof, in case something goes wrong with the AMTypes stuff in the ANY inside of DoIt1.
];
};
NoticeChanges: CedarSnapshot.RollbackProc = {ProfileChanged[rollBack]};
Initialization
TRUSTED
{CedarSnapshot.Register[c: NIL, r: NoticeChanges];
};
ParseProfile[! ANY => CONTINUE];
END.
April 27, 1982 4:52 pm fixed Open to first check for username.profile and use User.Profile only if no profile for that user name
June 2, 1982 2:24 pm When booting with n switches down, always return default value without looking at disk
June 8, 1982 5:49 pm added CallWhenProfileChanges and ProfileChangedProc. Changed GetLineValue to recognize " and read entire rope to matching ".
June 17, 1982 10:24 pm added ENABLE UNWIND => NULL to entry procedures. Changed OPEN not to remember username if no specific username.profile, so that when one appears, it is not necessary for user to boot, or login as somebody else and then login as himself again, in order to get the profile noticed.
July 17, 1982 11:53 pm added checkpoint proc to check to see if profile is open and if so, close it. Fixed DoIt to restore openCount in case of problem with ProfileProc.
September 15, 1982 9:41 pm defined CreateFilterCommentsStream to filter out comments
December 3, 1982 1:57 pm major changes: parse userprofile into data structure. Add mechanism for reporting errors to log., December
Edited on December 3, 1982 1:57 pm, by Teitelman
fixed bug in parse profile for case of file not ending in CR. fixed bulletproof logic in DoIt
changes to: inner (local of DoIt), DoIt, ParseProfile
Edited on December 20, 1982 12:45 pm, by Teitelman
changes to: DIRECTORY, IMPORTS, tokenProc (local of ParseProfile), whiteSpace (local of ParseProfile), isACR (local of ParseProfile), ParseProfile
Edited on December 21, 1982 2:26 pm, by Teitelman
changes to: ParseProfile, DIRECTORY, tokenProc (local of ParseProfile), whiteSpace (local of ParseProfile), isACR (local of ParseProfile), ProfileChanged, inner (local of DoIt), DoIt, IMPORTS