UserProfileImpl.mesa
Copyright Ó 1984, 1985, 1986, 1987, 1990, 1991 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
Doug Wyatt, June 10, 1987 5:09:27 pm PDT
Eric Nickell, October 29, 1987 1:34:17 pm PST
Wes Irish, May 3, 1988 12:15:48 pm PDT
Andy Litman August 12, 1988 1:33:08 am PDT
JKF October 26, 1988 7:35:02 am PDT
Michael Plass, November 22, 1991 5:27 pm PST
Willie-s, June 9, 1993 12:29 pm PDT
Chauser, November 6, 1991 4:40 pm PST
Swen Johnson, May 2, 1992 6:48 pm PDT
Christian Jacobi, October 30, 1992 10:20 am PST
DIRECTORY
BasicTime USING [Now],
Convert USING [IntFromRope],
Commander USING [CommandProc, Register],
CommanderOps USING [DoCommand, NextArgument],
InstallationBasicComforts,
IO,
PFS,
PFSNames,
Rope USING [Cat, Concat, Equal, ROPE],
RuntimeError USING [UNCAUGHT],
SimpleFeedback USING [Append],
SystemNames USING [MachineName, ReleaseName, SimpleHomeDirectory],
UserProfile USING [ProfileChangedProc, ProfileChangeReason],
UserProfileBackdoor USING [GuestProcsRec];
UserProfileImpl:
CEDAR
MONITOR
IMPORTS BasicTime, Convert, Commander, CommanderOps, InstallationBasicComforts, IO, PFS, PFSNames, Rope, RuntimeError, SimpleFeedback, SystemNames
EXPORTS UserProfile, UserProfileBackdoor
= BEGIN
Types
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
];
Variables
IsGuestProcess: PROC [] RETURNS [isGuest: BOOL] ¬ FalseProc;
GuestProcs: REF UserProfileBackdoor.GuestProcsRec ¬ NEW[UserProfileBackdoor.GuestProcsRec ¬ [FalseProc]] ;
Accessing profile
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, Rope.Cat["\t", val, " 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, Rope.Cat["\t", val, " 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;
};
Utillity PROCs
Lookup:
ENTRY
PROC [key:
ROPE]
RETURNS [ProfileEntry] = {
ENABLE UNWIND => NULL;
RETURN[LookupInternal[key]]
};
LookupInternal:
INTERNAL
PROC [key:
ROPE]
RETURNS [ProfileEntry] = {
<< Until we sort out what it means to have a Debugger volume in PCedar
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, "\textra material on line"];
};
GetProfileName:
PUBLIC
PROC
RETURNS [
ROPE] = {
IF IsGuestProcess[]
THEN RETURN GuestProcs.GetProfileName[]
ELSE RETURN[IF profileNameList = NIL THEN NIL ELSE profileNameList.first];
};
Building profileList
profileDir: PFS.PATH ~ PFS.PathFromRope[SystemNames.SimpleHomeDirectory[]];
profileList: ProfileList ¬ NIL;
profileName: ROPE ¬ NIL;
profileNameList: LIST OF ROPE ¬ NIL;
ropeProfile: ROPE ¬ NIL;
userSetProfileName:
ROPE ¬
NIL;
ParseProfile:
ENTRY
PROC [startNewErrorLog:
BOOL ¬
FALSE] = {
ENABLE UNWIND => NULL;
profileList ¬ NIL;
profileName ¬ NIL;
profileNameList ¬ NIL;
<< (until something like the Booting interface comes along for PCedar
IF Booting.switches[p] THEN RETURN -- return default values for the profile
ELSE>> {
dot: ROPE ~ ".";
machineName: ROPE ~ SystemNames.MachineName[];
machineProfileName: ROPE ~ Rope.Cat[dot, machineName, dot, SystemNames.ReleaseName[], ".machineProfile"];
userProfileName: ROPE ~ Rope.Cat[".cedar.", SystemNames.ReleaseName[], ".profile"];
serverProfileName: ROPE ~ Rope.Cat[".server.", SystemNames.ReleaseName[], ".profile"];
defaultProfileName: ROPE ~ Rope.Cat[".user.", SystemNames.ReleaseName[], ".profile"];
oldMachineProfileName: ROPE ~ dot.Cat[machineName, ".machineProfile"];
oldUserProfileName: ROPE ~ ".cedar.profile";
oldServerProfileName: ROPE ~ ".server.profile";
oldDefaultProfileName: ROPE ~ ".user.profile";
TryFileProfile:
INTERNAL
PROC [shortName:
ROPE, wDir: PFSNames.
PATH]
RETURNS [found:
BOOL ¬
FALSE] ~ {
stream: IO.STREAM ¬ NIL;
pPath: PFS.PATH;
pPath ¬ PFSNames.ExpandName[name: PFS.PathFromRope[shortName], wDir: wDir ! PFS.Error => CONTINUE];
stream ¬ PFS.StreamOpen[pPath ! PFS.Error => CONTINUE];
IF stream#
NIL
THEN {
profileName ¬ PFS.RopeFromPath[pPath];
profileNameList ¬ CONS[profileName, profileNameList];
ParseProfileInternal[stream];
found ¬ TRUE;
};
};
TryFiles:
INTERNAL
PROC [] ~ {
--Parse machine profile, if any, for machine-dependent stuff
IF TryFileProfile[machineProfileName, profileDir].found
THEN
[] ¬ TryFileProfile[oldMachineProfileName, profileDir];
--The presence of "Server.profile" overrides a user's profile (for use by servers).
IF TryFileProfile[serverProfileName, profileDir].found THEN RETURN;
IF TryFileProfile[oldServerProfileName, profileDir].found THEN RETURN;
--Try explicitely set profile.
IF userSetProfileName #
NIL
THEN
IF TryFileProfile[userSetProfileName, NIL].found THEN RETURN;
--Try for user's personal profile.
IF TryFileProfile[userProfileName, profileDir].found THEN RETURN;
IF TryFileProfile[oldUserProfileName, profileDir].found THEN RETURN;
--Use the default profile (if any).
IF TryFileProfile[defaultProfileName, profileDir].found THEN RETURN;
IF TryFileProfile[oldDefaultProfileName, profileDir].found THEN RETURN;
};
TryRopeProfile:
INTERNAL
PROC ~ {
stream: IO.STREAM;
IF ropeProfile = NIL THEN RETURN;
stream ¬ IO.RIS[ropeProfile];
IF stream #
NIL
THEN {
profileName ¬ "RopeProfile"; -- may need for error reporting
ParseProfileInternal[stream, FALSE];
};
};
IF startNewErrorLog THEN errorLog ¬ NIL;
TryFiles[];
TryRopeProfile[]; -- make any requested additions/changes.
};
CheckErrorLogInternal["parsing your profile"];
};
ParseProfileInternal:
INTERNAL
PROC [stream:
IO.
STREAM, reportSameKeys:
BOOL ¬
TRUE] = {
Given an open stream, parse the profile and close the stream. Successive calls "overwrite" previous entries.
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, '\r, '\l => IF NOT flushLines THEN RETURN;
<= 40C => {};
'- => {
-- could be a comment
[] ¬ stream.GetChar[];
IF stream.PeekChar[] # '-
THEN {
it is not a comment
stream.Backup[c];
RETURN [c];
};
DO
the end of a comment is either a '\n or a double -
c ¬ stream.GetChar[];
SELECT c
FROM
'\n, '\r, '\l => {
Only flush the \n if it was requested
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, '\r, '\l => RETURN;
'" => token ¬ stream.GetRopeLiteral[];
ENDCASE => token ¬ stream.GetTokenRope[tokenProc].token;
};
tokenProc:
IO.BreakProc = {
RETURN[
SELECT char
FROM
IO.SP, IO.TAB, ', => sepr,
'\n, '\r, '\l, ': => break,
ENDCASE => other
];
};
Cat:
PROC [a, b:
LIST
OF
ROPE]
RETURNS [
LIST
OF
ROPE] ~ {
RETURN [IF a=NIL THEN b ELSE CONS[a.first, Cat[a.rest, b]]];
};
LookupTokens:
INTERNAL
PROC [key:
ROPE]
RETURNS [tokens:
LIST
OF
ROPE] ~ {
entry: ProfileEntry ~ LookupInternal[key];
RETURN [IF entry=NIL THEN NIL ELSE entry.tokens];
};
token: ROPE ¬ NIL;
tokens, tail: LIST OF ROPE ¬ NIL;
position: INT;
key: ROPE ¬ NIL;
additive: {none, prefix, suffix};
LocalToken[TRUE];
IF (key ¬ token) = NIL THEN EXIT;
SELECT SkipWhite[]
FROM
': => {
[] ¬ stream.GetChar[]; -- flush the ':
IF (additive ¬
SELECT stream.PeekChar[]
FROM
'< => prefix,
'> => suffix,
ENDCASE => none)#none THEN [] ¬ stream.GetChar[]; --flush '< or '> if needed
};
': => [] ← stream.GetChar[]; -- flush the ':
ENDCASE => {
key was NOT followed by ':, so flush to the end of line and report the error
position: INT ¬ stream.GetIndex[];
DO
IF stream.GetChar[ ! IO.EndOfStream => EXIT] = '\n THEN EXIT;
ENDLOOP;
ReportInternal[msg: IO.PutFR1["\tmissing : at [%d]\n", [integer[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 key.Equal["Include",
FALSE]
THEN {
--Insert a profile slice
position ¬ stream.GetIndex[];
FOR each:
LIST
OF
ROPE ¬ tokens, each.rest
UNTIL each=
NIL
DO
profileSlice: IO.STREAM ¬ NIL;
profileSlice ¬ PFS.StreamOpen[fileName: PFS.AbsoluteName[short: PFS.PathFromRope[each.first], wDir: profileDir] ! PFS.Error => CONTINUE];
IF profileSlice#
NIL
THEN ParseProfileInternal[profileSlice]
ELSE {
ReportInternal[
entry: NEW[ProfileRecord ¬ [each.first, tokens, position]],
msg: "\tFile not found."
];
};
ENDLOOP;
}
ELSE {
--Add a profile entry
SELECT additive
FROM
prefix => tokens ¬ Cat[tokens, LookupTokens[key]];
suffix => tokens ¬ Cat[LookupTokens[key], tokens];
ENDCASE =>
IF reportSameKeys
AND LookupInternal[key] #
NIL
THEN
ReportInternal[
entry: LookupInternal[key],
msg: IO.PutFR["\t%g also appears at [%d]", [rope[key]], [integer[position]]]
];
profileList ¬ CONS[NEW[ProfileRecord ¬ [key, tokens, stream.GetIndex[]]], profileList];
};
ENDLOOP;
IO.Close[stream];
};
Reporting errors
Report:
ENTRY
PROC [entry: ProfileEntry ¬
NIL, msg:
ROPE] = {
ENABLE UNWIND => NULL;
ReportInternal[entry, msg];
};
logPath: PFS.PATH ~ PFS.PathFromRope["UserProfile.log"];
createNewErrorLog: BOOL ¬ TRUE;
ReportInternal:
INTERNAL
PROC [entry: ProfileEntry ¬
NIL, msg:
ROPE] = {
ENABLE RuntimeError.UNCAUGHT => CONTINUE;
IF errorLog =
NIL
THEN {
errorLog ¬ PFS.StreamOpen[fileName: PFSNames.Cat[profileDir, logPath],
accessOptions: IF createNewErrorLog THEN $create ELSE $write];
IF NOT createNewErrorLog THEN errorLog.SetIndex[errorLog.GetLength[]];
createNewErrorLog ¬ FALSE; -- once per startup
errorLog.PutF["\n\n*** %g - processing %g\n", [time[BasicTime.Now[]]], [rope[profileName]]];
};
errorLog.PutF1["\n %g", [rope[msg]] ];
IF entry #
NIL
THEN
errorLog.PutF[", at %g [%d]", [rope[entry.key]], [integer[entry.position]]];
errorLog.Flush[];
};
GetErrorLog:
PUBLIC
ENTRY
PROC
RETURNS [fileName:
ROPE ¬
NIL] = {
ENABLE UNWIND => { errorLog ¬ NIL; NULL };
RETURN[InternalErrorLogName[]];
};
CheckErrorLog:
ENTRY PROC[msg:
ROPE] ~ {
CheckErrorLogInternal[msg]
};
CheckErrorLogInternal:
INTERNAL PROC[msg:
ROPE] ~ {
IF errorLog #
NIL
THEN
SimpleFeedback.Append[$UserProfile, $begin, $info, IO.PutFR["** There were errors %g; see %g for details (%g)\n", [rope[msg]], [rope[InternalErrorLogName[]]], [time[BasicTime.Now[]]] ]];
};
InternalErrorLogName:
INTERNAL
PROC
RETURNS[fileName:
ROPE ¬
NIL] = {
IF errorLog #
NIL
THEN {
fileName ¬ PFS.RopeFromPath[PFS.GetName[PFS.OpenFileFromStream[errorLog]].fullFName];
errorLog.Close[];
errorLog ¬ NIL;
};
};
errorLog: IO.STREAM ¬ NIL;
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).
profileChangedList: LIST OF ProfileChangedItem ¬ NIL;
profileChangedListLast: LIST OF ProfileChangedItem ¬ NIL;
AddToList:
ENTRY
PROC [item: ProfileChangedItem] ~ {
itemL: LIST OF ProfileChangedItem = CONS[item, NIL];
IF profileChangedList = NIL THEN profileChangedList ¬ itemL
ELSE profileChangedListLast.rest ¬ itemL;
profileChangedListLast ¬ itemL;
};
CopyList:
ENTRY
PROC
RETURNS [
LIST
OF ProfileChangedItem] ~ {
head, tail: LIST OF ProfileChangedItem ¬ NIL;
FOR list:
LIST
OF ProfileChangedItem ¬ profileChangedList, list.rest
UNTIL list=
NIL
DO
copy: LIST OF ProfileChangedItem ~ CONS[list.first, NIL];
IF head=NIL THEN head ¬ copy ELSE tail.rest ¬ copy;
tail ¬ copy;
ENDLOOP;
RETURN [head];
};
CallWhenProfileChanges:
PUBLIC
PROC [proc: UserProfile.ProfileChangedProc] = {
IF IsGuestProcess[]
THEN GuestProcs.CallWhenProfileChanges[proc]
ELSE {
item: ProfileChangedItem ~ [proc, --clientData--NIL];
AddToList[item];
DoIt[item, firstTime];
CheckErrorLog["during add ProfileChangedProc"];
};
};
ProfileChanged:
PUBLIC
PROC [reason: UserProfile.ProfileChangeReason] = {
ProfileChangedInternal[reason, reason # firstTime];
};
ProfileChangedInternal:
PROC [reason: UserProfile.ProfileChangeReason,
nillRopeProfile:
BOOL ¬
TRUE] = {
IF IsGuestProcess[] THEN GuestProcs.ProfileChanged[reason]
ELSE {
IF nillRopeProfile THEN ropeProfile ¬ NIL;
ParseProfile[startNewErrorLog: TRUE ! RuntimeError.UNCAUGHT => CONTINUE];
FOR l:
LIST
OF ProfileChangedItem ¬ CopyList[], l.rest
UNTIL l=
NIL
DO
DoIt[l.first, reason];
ENDLOOP;
};
};
DoIt:
PROC [item: ProfileChangedItem, reason: UserProfile.ProfileChangeReason] = {
item.proc[reason
--, item.clientData--
! RuntimeError.
UNCAUGHT => {
Report[msg: Rope.Concat["\tUncaught error while executing ProfileChangedProc: ", InstallationBasicComforts.BasicProcName[item.proc]]];
CONTINUE
}
];
};
RopeProfile Interface
GetRopeProfile:
PUBLIC
PROC
RETURNS [
ROPE] = {
RETURN[ropeProfile];
};
SetRopeProfile:
PUBLIC
PROC [rope:
ROPE ¬
NIL] = {
ropeProfile ¬ rope;
ProfileChangedInternal[edit, FALSE];
};
Exported to UserProfileBackdoor
RegisterGuestProcs:
PUBLIC
PROC [newProcs:
REF UserProfileBackdoor.GuestProcsRec] = {
GuestProcs ¬ newProcs;
IsGuestProcess ¬ newProcs.IsGuestProcess;
};
default IsGuestProcess (not really exported)
FalseProc:
PROC
RETURNS [isGuest:
BOOL] = {
isGuest ¬ FALSE;
};
Watching for profile changes
<< Until something like the Booting interface comes along for PCedar
CheckpointWatcher:
ENTRY Booting.CheckpointProc ~ {
ENABLE UNWIND => {errorLog ¬ NIL; NULL};
IF errorLog #
NIL
THEN {
errorLog.Close[];
errorLog ¬ NIL;
};
};>>
<< Until threads and filing come along for PCedar
ProfileWatcher:
PROC ~ {
FOR event:
REF
READONLY FSBackdoor.CreateEvent ¬
FSBackdoor.NextCreateEvent[
NIL], FSBackdoor.NextCreateEvent[event]
DO
fileName: ROPE ~ event.fName;
bang: INT ~ Rope.SkipTo[s: fileName, skip: "!"];
FOR list:
LIST
OF
ROPE ¬
profileNameList, list.rest
UNTIL list=
NIL
DO
run: INT ~ Rope.Run[s1: fileName, s2: list.first, case: FALSE];
IF run>bang THEN { ProfileChanged[edit]; EXIT };
ENDLOOP;
ENDLOOP;
}; >>
Commander commands
ParseProfileCommand: Commander.CommandProc ~ {
ProfileChanged[edit]
};
NotePerLogin: Commander.CommandProc ~ {
line: ROPE ¬ Line["CommandTool.PerLogin", NIL];
IF line # NIL THEN [] ¬ CommanderOps.DoCommand[line, cmd];
};
NoteNewUser: Commander.CommandProc ~ {
line: ROPE ¬ Line["CommandTool.NewUser", NIL];
IF line # NIL THEN [] ¬ CommanderOps.DoCommand[line, cmd];
};
NotePerCommandTool: Commander.CommandProc ~ {
line: ROPE ¬ Line["CommandTool.PerCommandTool", NIL];
IF line # NIL THEN [] ¬ CommanderOps.DoCommand[line, cmd];
};
NoteBootCommands: Commander.CommandProc ~ {
line: ROPE ¬ Line["CommandTool.BootCommands", NIL];
IF line # NIL THEN [] ¬ CommanderOps.DoCommand[line, cmd];
};
GetProfileEntryAsRope: Commander.CommandProc = {
key: ROPE = CommanderOps.NextArgument[cmd];
IF key = NIL THEN RETURN;
cmd.out.PutF["ProfileEntryAsRope: key: %g, value: %g\n",
[rope[key]], [rope[Line[key, NIL]]] ];
};
SetFileNameForUserProfile:
ENTRY Commander.CommandProc ~ {
userSetProfileName ¬ CommanderOps.NextArgument[cmd];
};
partialRopeProfile:
ROPE ¬
NIL;
SetProfileEntry: Commander.CommandProc = {
partialRopeProfile ¬ Rope.Concat[partialRopeProfile, cmd.commandLine];
Notice that this necessarily includes the CR. Also notice that this won't take effect until UpdateProfileEntries.
};
UpdateProfileEntries: Commander.CommandProc = {
SetRopeProfile[Rope.Cat[GetRopeProfile[], partialRopeProfile, cmd.commandLine]];
partialRopeProfile ¬ NIL;
};
UndoProfileChanges: Commander.CommandProc = {
SetRopeProfile[partialRopeProfile ¬ NIL];
};
PrintProfileChanges: Commander.CommandProc = {
cmd.out.PutRope[ GetRopeProfile[] ];
};
******** Main program ********
Commander.Register["NoteBootCommands", NoteBootCommands, "Executes CommandTool.NoteBootCommands commands"];
Commander.Register["NoteNewUser", NoteNewUser, "Executes CommandTool.NoteNewUser commands"];
Commander.Register["NotePerCommandTool", NotePerCommandTool, "Executes CommandTool.NotePerCommandTool commands"];
Commander.Register["NotePerLogin", NotePerLogin, "Executes CommandTool.PerLogin commands"];
Commander.Register["GetProfileEntryAsRope", GetProfileEntryAsRope, "Prints the rope value of the asked for profile entry"];
Commander.Register["PrintProfileChanges", PrintProfileChanges, "Print the entries that are currently layered over existing profile entries."];
Commander.Register["ProfileChanged", ParseProfileCommand, "Same as UserProfile.ProfileChanged[edit]"];
Commander.Register["SetFileNameForUserProfile", SetFileNameForUserProfile, "Tells UserProfile a specific file to use - no checking done"];
Commander.Register["SetProfileEntry", SetProfileEntry, "Reads a new value for the indicated profile entry. Syntax is identical to UserProfile, don't forget the ': following the key. NOTE: this will not take effect until the command UpdateProfileEntries is executed."];
Commander.Register["UndoProfileChanges", UndoProfileChanges, "Resets all UserProfile entries to their file specified values."];
Commander.Register["UpdateProfileEntries", UpdateProfileEntries, "Causes SetProfileEntry commands to take effect."];
ProfileChanged[firstTime];
--Booting.RegisterProcs[c: CheckpointWatcher];
--TRUSTED { Process.Detach[FORK ProfileWatcher] };
END.
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
Christian Jacobi, October 23, 1987 11:56:16 am PDT
changes: Added checkpoint watcher to close log file.
Eric Nickell, October 29, 1987 1:32:19 pm PST
changes: Added ability to Include other profiles. Allowed '< and '> placed after ': to mean to prepend and append tokens on the line to tokens previously stored under this key.
Wesley Irish, April 29, 1988 4:15:47 pm PDT
changes: Added RopeProfile feature.
Jim Foote, August 11, 1988 10:55:53 am PDT
changes: Ported to PCedar. Removed references to Booting until something like it shows up for PCedar. Removed references to the debugger volume, until we figure out what that means for PCedar. Removed references to threads and FSBackDoor.
Swen Johnson, May 2, 1992 6:48:10 pm PDT
Bug fix: code to open stream on Include file was unconditionally Cat'ing specified filename to directory name, even when specified filename was absolute.
changes to: ParseProfileInternal: Use PFS.AbsoluteName instead of PFSNames.Cat.
Christian Jacobi, October 30, 1992 10:12:04 am PST
changes: Added release version number to profile file names. For compatibility not yet removed the old way.