Maintain.mesa
Copyright © 1984 by Xerox Corporation. All rights reserved.
Andrew Birrell August 25, 1983 11:10 am
Russ Atkinson, August 29, 1984 7:18:41 pm PDT
Hal Murray May 23, 1985 6:12:46 pm PDT
Bob Hagmann January 29, 1986 8:22:18 am PST
Hal Murray, March 20, 1986 0:45:38 am PST
DIRECTORY
Atom USING[ GetPName ],
BasicTime USING[ FromPupTime ],
Buttons USING[ Button, ButtonProc, Create, Destroy, SetDisplayStyle ],
Commander USING[ CommandProc, Register ],
Containers USING[ ChildXBound, ChildYBound, Create ],
GVBasics USING[ GVString, MakeKey, oldestTime, Password, RName, Timestamp ],
GVNames USING[ CheckStamp, GetEntry, GetEntryInfo, GetEntryList, GetMembers, MemberInfo, NameType, Outcome, RListHandle, ReporterProc, RName, RSOperation, SetServer, SetServerInfo, Update],
IO USING[ Put, PutChar, PutF, PutFR, PutRope, STREAM, Value ],
Labels USING[ Create ],
Rope USING[ Cat, Equal, Fetch, Find, Length, Match, ROPE, SkipOver, SkipTo, Substr ],
Rules USING[ Create ],
TypeScript USING[ Create ],
UserCredentials USING[ Get ],
ViewerClasses USING[ Viewer ],
ViewerIO USING[ CreateViewerStreams ],
ViewerOps USING[ ComputeColumn, DestroyViewer, MoveViewer, SetOpenHeight ],
ViewerTools USING[ GetContents, MakeNewTextViewer, SetContents, SetSelection];
Maintain: CEDAR MONITOR LOCKS d USING d: MyData
IMPORTS Atom, BasicTime, Buttons, Commander, Containers, GVBasics, GVNames, IO, Labels, Rope, Rules, TypeScript, UserCredentials, ViewerIO, ViewerOps, ViewerTools
SHARES GVNames--Update-- =
{
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
Viewer: TYPE = ViewerClasses.Viewer;
-- ******** Enquiry operations ******** --
Amount: TYPE = {members, finks, summary, details, names};
TypeGroupMatchesProc: Buttons.ButtonProc = {
parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL
TypeGroup[clientData, names] };
TypeGroupMembersProc: Buttons.ButtonProc = {
parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL
TypeGroup[clientData, members] };
TypeGroupSummaryProc: Buttons.ButtonProc = {
parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL
TypeGroup[clientData, summary] };
TypeGroupDetailsProc: Buttons.ButtonProc = {
parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL
TypeGroup[clientData, details] };
TypeGroupFinksProc: Buttons.ButtonProc = {
parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL
TypeGroup[clientData, finks] };
TypeIndividualMatchesProc: Buttons.ButtonProc = {
parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL
TypeIndividual[clientData, names] };
TypeIndividualSummaryProc: Buttons.ButtonProc = {
parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL
TypeIndividual[clientData, summary] };
TypeIndividualDetailsProc: Buttons.ButtonProc = {
parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL
TypeIndividual[clientData, details] };
TypeDeadMatchesProc: Buttons.ButtonProc = {
parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL
TypeDead[clientData, names] };
TypeDeadDetailsProc: Buttons.ButtonProc = {
parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL
TypeDead[clientData, details] };
TypeGroup: PROC [clientData: REF, which: Amount] = {
d: MyData = NARROW[clientData];
name: ROPE = ViewerTools.GetContents[d.groupT];
IF ~CheckForm[clientData, name, "Group"] THEN RETURN;
DoEnumerate[d, name, "Groups", which];
};
TypeIndividual: PROC [clientData: REF, which: Amount] = {
d: MyData = NARROW[clientData];
name: ROPE = ViewerTools.GetContents[d.indivT];
IF ~CheckForm[clientData, name, "Individual"] THEN RETURN;
DoEnumerate[d, name, "Individuals", which];
};
TypeDead: PROC [clientData: REF, which: Amount] = {
d: MyData = NARROW[clientData];
name: ROPE = ViewerTools.GetContents[d.deadT];
IF ~CheckForm[clientData, name, "Dead"] THEN RETURN;
DoEnumerate[d, name, "Dead", which];
};
CallForItems: PROC [d: MyData, names: ROPE, proc: PROC [ROPE], evenIfNull: BOOLFALSE] = {
len: INT = Rope.Length[names];
pos: INT ← 0;
IF Quote[d] THEN { IF len # 0 OR evenIfNull THEN proc[names]; RETURN; };
DO
startPos: INT ← Rope.SkipOver[names, pos, ","];
startPos ← Rope.SkipOver[names, startPos, " "];
IF startPos = len THEN EXIT;
pos ← MIN[Rope.SkipTo[names, startPos, ","], Rope.SkipTo[names, startPos, " "]];
evenIfNull ← FALSE;
proc[Rope.Substr[names, startPos, pos-startPos]];
ENDLOOP;
IF evenIfNull THEN proc[NIL];
};
DoEnumerate: ENTRY PROC [d: MyData, pattern, type: ROPE, which: Amount] = {
ENABLE UNWIND => NULL;
EnumerateOne: INTERNAL PROC [pattern: ROPE] = {
IF Rope.Find[pattern, "*"] >= 0 THEN {
lastDotPos: INT ← -1;
DO
thisDot: INT = Rope.Find[pattern, ".", lastDotPos+1];
IF thisDot < 0 THEN EXIT;
lastDotPos ← thisDot;
ENDLOOP;
d.out.PutF["Enumerate%g[%g] ... ", [rope[type]], [rope[pattern]] ];
IF lastDotPos < 0 THEN {
d.out.PutRope["the pattern must have an explicit registry\n"]; RETURN };
IF Rope.Find[pattern, "*", lastDotPos+1] > 0 THEN {
d.out.PutRope["the registry must not contain \"*\"\n"]; RETURN };
{
registry: ROPE = Rope.Substr[pattern, lastDotPos+1];
enumName: ROPE = Rope.Cat[type, ".", registry];
info: GVNames.MemberInfo = GVNames.GetMembers[enumName];
TRUSTED {
WITH i: info SELECT FROM
notFound =>
d.out.PutF["\"%g\" is not a registry\n", [rope[registry]] ];
noChange, individual =>
d.out.PutRope["internal error - consult a wizard\n"];
allDown =>
d.out.PutF["no R-Server for registry \"%g\" is available\n", [rope[registry]] ];
group => {
first: BOOLTRUE;
FOR l: GVNames.RListHandle ← i.members, l.rest UNTIL l = NIL DO
IF Rope.Match[pattern, l.first, FALSE] THEN {
IF which = names THEN {
IF NOT first THEN d.out.PutRope[", "]; d.out.PutRope[l.first] }
ELSE DoEnquiry[d, l.first, which];
first ← FALSE; };
ENDLOOP;
IF first THEN d.out.PutRope["no matches\n"]
ELSE IF which = names THEN d.out.PutChar['\n];
};
ENDCASE => ERROR; };
};
}
ELSE { IF which = names THEN which ← summary; DoEnquiry[d, pattern, which] };
};
d.stop ← FALSE;
CallForItems[d, pattern, EnumerateOne, FALSE];
IO.PutRope[d.out, "\n"];
};
DoEnquiry: INTERNAL PROC [d: MyData, name: ROPE, which: Amount[members..details]] = {
Reporter: PROC [report: ROPE] = {
d.out.PutF["%g ... ", [rope[report]] ];
};
info: REF GVNames.GetEntryInfo;
rc: GVNames.NameType[group..allDown];
d.out.PutF["%g[%g] ... ",
[rope[SELECT which FROM
members => "TypeMembers",
finks => "TypeFinks",
summary => "TypeSummary",
details => "TypeDetails",
ENDCASE => ERROR]],
[rope[name]] ];
[rc, info] ← GVNames.GetEntry[name, Reporter];
IF info.type = dead THEN d.out.PutRope["recently deleted name"];
IF info.type # notFound AND which = details THEN
d.out.PutF["\nPrefix: %g, %g, %g",
[rope[info.name]],
[rope[SELECT info.type FROM
individual => "individual",
group => "group",
dead => "dead",
ENDCASE => ERROR]],
Stamp[info.stamp] ];
TRUSTED {
WITH i: info SELECT FROM
individual => {
IF which = details
THEN d.out.PutF["\nPassword: %b %b %b %b, %g",
[cardinal[i.password[0]]],
[cardinal[i.password[1]]],
[cardinal[i.password[2]]],
[cardinal[i.password[3]]],
Stamp[i.passwordStamp] ];
d.out.PutF["\nConnect: \"%g\"", [rope[i.connect]] ];
IF which = details THEN d.out.PutF[", %g", Stamp[i.connectStamp]];
IF which = details OR i.forward.current # NIL
THEN TypeEntryList[d, i.forward, "Forwarding",
IF which = details THEN details ELSE members];
TypeEntryList[d, i.sites, "Mailboxes", IF which = details THEN details ELSE members];
d.out.PutChar['\n];
};
group => {
IF which # members AND which # finks THEN d.out.PutF["\nRemark: \"%g\"", [rope[i.remark]] ];
IF which = details THEN d.out.PutF[", %g", Stamp[i.remarkStamp]];
IF which = finks THEN TypeFinks[d, i.members.current]
ELSE TypeEntryList[d, i.members, "Members", which];
IF which # members AND which # finks THEN {
TypeEntryList[d, i.owners, "Owners", IF which = details THEN details ELSE members];
TypeEntryList[d, i.friends, "Friends", IF which = details THEN details ELSE members];
};
d.out.PutChar['\n];
};
dead => d.out.PutChar['\n];
ENDCASE => TypeRC[d, rc, ReadEntry, name, NIL]; };
IF d.stop THEN { IO.PutRope[d.out, "Stopping.\n"]; ERROR ABORTED; };
};
Stamp: PROC [stamp: GVBasics.Timestamp] RETURNS [IO.Value] = {
time: ROPE = IO.PutFR[NIL, [time[BasicTime.FromPupTime[stamp.time]]] ];
RETURN[ [rope[IO.PutFR["[%b#%b,%g]",
[cardinal[stamp.net]],
[cardinal[stamp.host]],
[rope[Rope.Substr[time, 0, Rope.Length[time]-4]]]]]]]
};
TypeEntryList: PROC [d: MyData, list: GVNames.GetEntryList, text: ROPE, which: Amount] = {
TypeEntrySublist[d, list.current, list.currentStamps, text, which];
IF which = details THEN
TypeEntrySublist[d, list.deleted, list.deletedStamps, Rope.Cat["Del", text], which];
};
TypeEntrySublist: PROC [d: MyData, names: GVNames.RListHandle, stamps: LIST OF GVBasics.Timestamp, text: ROPE, which: Amount] = {
count: INT ← 0;
stampList: LIST OF GVBasics.Timestamp ← stamps;
d.out.PutF["\n%g: ", [rope[text]] ];
FOR c: GVNames.RListHandle ← names, c.rest UNTIL c = NIL DO
IF which # summary AND count # 0 THEN d.out.PutRope[", "];
count ← count+1;
IF which # summary THEN d.out.PutRope[c.first];
IF which = details THEN d.out.Put[Stamp[stampList.first]];
stampList ← stampList.rest;
ENDLOOP;
IF which = summary THEN d.out.Put[[integer[count]]]
ELSE { IF count = 0 THEN d.out.PutRope["none"] };
};
TypeFinks: PROC [d: MyData, names: GVNames.RListHandle] = {
members, finks: INT ← 0;
badGuys, last: GVNames.RListHandle ← NIL;
FOR c: GVNames.RListHandle ← names, c.rest UNTIL c = NIL DO
who: GVNames.RName ← c.first;
rc: GVNames.NameType[group..allDown];
info: REF GVNames.GetEntryInfo;
members ← members + 1;
[rc, info] ← GVNames.GetEntry[who];
TRUSTED {
WITH i: info SELECT FROM
group => LOOP;
individual => {
temp: GVNames.RListHandle;
forwarding: INT ← 0;
IF i.forward.current = NIL THEN LOOP;
temp ← CONS[who, NIL];
IF badGuys = NIL THEN badGuys ← temp ELSE last.rest ← temp;
last ← temp;
d.out.PutF["\n%G => is forwarded to ", [rope[who]] ];
FOR c: GVNames.RListHandle ← i.forward.current, c.rest UNTIL c = NIL DO
IF forwarding # 0 THEN d.out.PutRope[", "];
forwarding ← forwarding+1;
d.out.PutRope[c.first];
ENDLOOP; };
dead => d.out.PutF["\n%G => recently deleted", [rope[who]] ];
notFound => d.out.PutF["\n%G => invalid", [rope[who]] ];
ENDCASE => d.out.PutF["\n%G => ????", [rope[who]] ];
finks ← finks+1; };
ENDLOOP;
d.out.PutF["\nFinks: "];
FOR c: GVNames.RListHandle ← badGuys, c.rest UNTIL c = NIL DO
IF c # badGuys THEN d.out.PutRope[", "];
d.out.PutRope[c.first];
ENDLOOP;
d.out.PutF["\n%G members, %G finks.", [integer[members]], [integer[finks]] ];
};
-- ******** Update operations ******** --
UpdateOp: TYPE = REF UpdateOpRec;
UpdateOpRec: TYPE = RECORD[
d: MyData,
op: GVNames.RSOperation ];
UpdateGroup: Buttons.ButtonProc = {
parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL
rec: UpdateOp = NARROW[clientData];
name: ROPE = ViewerTools.GetContents[rec.d.groupT];
data: ROPE = IF rec.d.dataGT = NIL THEN NIL ELSE ViewerTools.GetContents[rec.d.dataGT];
IF ~CheckForm[rec.d, name, "Group"] THEN RETURN;
SELECT rec.op FROM
CreateGroup, DeleteGroup, AddSelf, DeleteSelf => NULL;
ENDCASE => IF ~CheckForm[rec.d, data, "Argument"] THEN RETURN;
DoUpdate[rec.d, group, rec.op, name, data];
};
UpdateIndividual: Buttons.ButtonProc = {
parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL
rec: UpdateOp = NARROW[clientData];
name: ROPE = ViewerTools.GetContents[rec.d.indivT];
data: ROPE = ViewerTools.GetContents[rec.d.dataIT];
IF ~CheckForm[rec.d, name, "Individual"] THEN RETURN;
SELECT rec.op FROM
DeleteIndividual => NULL;
ENDCASE => IF ~CheckForm[rec.d, data, "Argument"] THEN RETURN;
DoUpdate[rec.d, individual, rec.op, name, data];
};
DoUpdate: ENTRY PROC [d: MyData, expected: GVNames.NameType, op: GVNames.RSOperation, target: GVBasics.RName, value: GVBasics.GVString] = {
ENABLE UNWIND => NULL;
EnumerateValue: PROC [rope1: ROPE] = {
AddDeleteOne: PROC [rope2: ROPE] = {
DoOneUpdate[d, expected, op, rope1, rope2];
};
CallForItems[d, value, AddDeleteOne, TRUE];
};
UpdateSelf: PROC [rope: ROPE] = {
DoOneUpdate[d, expected, op, rope, NIL];
};
d.stop ← FALSE;
SELECT op FROM
AddSelf, DeleteSelf => CallForItems[d, target, UpdateSelf, TRUE];
CreateIndividual, DeleteIndividual, ChangeConnect, ChangePassword =>
DoOneUpdate[d, expected, op, target, value];
CreateGroup, DeleteGroup, ChangeRemark, NewName =>
DoOneUpdate[d, expected, op, target, value];
AddMember, AddMailBox, AddForward, AddOwner, AddFriend,
DeleteMember, DeleteMailBox, DeleteForward, DeleteOwner, DeleteFriend =>
CallForItems[d, target, EnumerateValue, TRUE];
ENDCASE => ERROR;
IO.PutRope[d.out, "\n"];
};
DoOneUpdate: PROC [d: MyData, expected: GVNames.NameType, op: GVNames.RSOperation, target: GVBasics.RName, value: GVBasics.GVString] = {
outcome: GVNames.Outcome;
Reporter: SAFE PROC [report: ROPE] = {
d.out.PutF["%g ... ", [rope[report]] ];
};
user, password: ROPE;
[name: user, password: password] ← UserCredentials.Get[];
-- Log command --
IO.PutF[d.out, "%g%g[%g,%g] ... ",
[rope[SELECT op FROM
CreateIndividual, CreateGroup => "Create",
DeleteIndividual, DeleteGroup => "Delete",
AddMember, AddOwner, AddFriend,
AddMailBox, AddForward, AddSelf => "Add",
DeleteMember, DeleteOwner, DeleteFriend,
DeleteMailBox, DeleteForward, DeleteSelf => "Remove",
ChangeRemark, ChangePassword, ChangeConnect => "Set",
ENDCASE => ""]],
[rope[SELECT op FROM
CreateIndividual, DeleteIndividual => "Individual",
CreateGroup, DeleteGroup => "Group",
NewName => "NewName",
AddMember, DeleteMember => "Member",
AddOwner, DeleteOwner => "Owner",
AddFriend, DeleteFriend => "Friend",
AddMailBox, DeleteMailBox => "Mailbox",
AddForward, DeleteForward => "Forwarding",
AddSelf, DeleteSelf => "Self",
ChangeRemark => "Remark",
ChangePassword => "Password",
ChangeConnect => "Connect",
ENDCASE => "UnknownOp"]],
[rope[target]],
[rope[SELECT op FROM
CreateIndividual, CreateGroup, ChangePassword,
DeleteIndividual, DeleteGroup,
AddSelf, DeleteSelf => "",
ENDCASE => value]] ];
-- validate argument --
IF (SELECT op FROM
AddMember, AddOwner, AddFriend, AddForward => CheckName[d, value, Reporter],
AddMailBox => CheckInbox[d, value, Reporter],
ChangePassword => CheckPwd[d, user, target, value],
ENDCASE => TRUE)
AND NotGVMS[d, target] THEN {
newPwd: GVBasics.Password;
SELECT op FROM
CreateIndividual, ChangePassword => newPwd ← GVBasics.MakeKey[value];
ENDCASE => NULL;
SELECT op FROM -- patch because of inadequate return codes from Update --
CreateIndividual, CreateGroup, NewName => {
outcome ← GVNames.CheckStamp[target, GVBasics.oldestTime--, Reporter--];
IF outcome # notFound THEN GOTO Bad;
};
DeleteIndividual, DeleteGroup => {
outcome ← GVNames.CheckStamp[target, GVBasics.oldestTime--, Reporter--];
IF outcome = notFound THEN GOTO Bad;
expected ← notFound;
};
ENDCASE => NULL;
outcome ← GVNames.Update[user: user, password: GVBasics.MakeKey[password],
op: op, target: target, value: value, newPwd: newPwd, reporter: Reporter];
IF outcome # expected THEN GOTO Bad;
IO.PutRope[d.out, "ok\n"]
EXITS Bad => TypeRC[d, outcome, op, target, value];
};
IF d.stop THEN { IO.PutRope[d.out, "Stopping.\n"]; ERROR ABORTED; };
};
verifyOn: ATOM = $on;
Verify: PROC [d: MyData] RETURNS [BOOL] = {
IF d.verify = NIL THEN RETURN[TRUE];
IF d.verify^ = verifyOn THEN RETURN[TRUE];
RETURN[FALSE];
};
quoteOff: ATOM = $off;
Quote: PROC [d: MyData] RETURNS [BOOL] = {
IF d.quote = NIL THEN RETURN[FALSE];
IF d.quote^ = quoteOff THEN RETURN[FALSE];
RETURN[TRUE];
};
CheckName: PROC [d: MyData, name: GVBasics.RName, reporter: GVNames.ReporterProc]
RETURNS [BOOL] = {
length: INT = Rope.Length[name];
IF length = 0 THEN GOTO Bad;
IF length = 1 AND Rope.Fetch[name, 0] = '* THEN RETURN[TRUE];
IF length > 1 AND Rope.Fetch[name, 0] = '* AND Rope.Fetch[name, 1] = '. THEN RETURN[TRUE];
IF GVNames.CheckStamp[name, GVBasics.oldestTime--, reporter--] = notFound
THEN {
FOR i: INT DECREASING IN [0..length)
DO IF Rope.Fetch[name, i] = '.
THEN {
f: GVBasics.RName = Rope.Cat[Rope.Substr[name, i+1, length-i], ".foreign"];
IF ~CheckIndividual[f] THEN GOTO Bad;
IO.PutRope[d.out, "(foreign) ... "];
RETURN[TRUE];
};
REPEAT FINISHED => GOTO Bad
ENDLOOP
};
RETURN[TRUE]
EXITS Bad =>
{ IF d.verify # NIL AND d.verify^ # verifyOn
THEN { IO.PutRope[d.out, "(invalid) ... "]; RETURN[TRUE] };
IO.PutF[d.out, "\"%g\" is not a valid name\n", [rope[name]] ];
RETURN[FALSE] }
};
CheckIndividual: PROC [name: GVBasics.RName] RETURNS [BOOL] = {
IF GVNames.CheckStamp[name, GVBasics.oldestTime] # individual THEN RETURN[FALSE];
RETURN[TRUE];
};
CheckInbox: PROC [d: MyData, name: GVBasics.RName, reporter: GVNames.ReporterProc]
RETURNS [BOOL] = {
IF ~EndsWith[name, ".ms"] OR ~CheckIndividual[name] THEN {
IF ~Verify[d] THEN { IO.PutRope[d.out, "(invalid) ... "]; RETURN[TRUE] }
ELSE {
IO.PutF[d.out, "\"%g\" is not a mail server\n", [rope[name]] ];
RETURN[FALSE]; }; };
RETURN[TRUE];
};
CheckPwd: PROC [d: MyData, user, name, pwd: GVBasics.RName]
RETURNS [BOOL] = {
IF Rope.Length[pwd] # 0 THEN RETURN[TRUE];
IF ~Rope.Equal[user, name, FALSE] THEN RETURN[TRUE];
IO.PutRope[d.out, " You don't want an empty password.\n"];
RETURN[FALSE];
};
gvmsAllowed: ATOM = $yes;
NotGVMS: PROC [d: MyData, a: ROPE] RETURNS [BOOL] = {
IF d.gvms # NIL AND d.gvms^ = gvmsAllowed THEN RETURN[TRUE];
IF ~EndsWith[a, ".gv"] AND ~EndsWith[a, ".ms"] THEN RETURN[TRUE];
IO.PutRope[d.out, " Use wizard's GV/MS switch first.\n"];
RETURN[FALSE];
};
EndsWith: PROC [a, b: ROPE] RETURNS [BOOL] = {
bLength: INT = Rope.Length[b];
aLength: INT = Rope.Length[a];
IF aLength < bLength THEN RETURN[FALSE];
RETURN[Rope.Equal[Rope.Substr[a, aLength - bLength, bLength], b, FALSE]];
};
-- ******** SetServer operation ******** --
SetServerProc: Buttons.ButtonProc = {
parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL
d: MyData = NARROW[clientData];
name: ROPE = ViewerTools.GetContents[d.serverT];
IF ~CheckForm[clientData, name, "Host"] THEN RETURN;
DoSetServer[d, name];
};
DoSetServer: ENTRY PROC [d: MyData, name: ROPE] = {
ENABLE UNWIND => NULL;
info: GVNames.SetServerInfo;
d.out.PutF["\nSetServer[%g] ... ", [rope[name]] ];
info ← GVNames.SetServer[name];
d.out.PutRope[SELECT info FROM
ok => "ok\n",
allDown => "can't look up name - allDown\n",
noRoute => "no route to that host at present\n",
badName => "bad server name\n",
ENDCASE => ERROR];
};
-- ******** Miscellaneous sub-routines for the operations ******** --
CheckForm: SAFE PROC [clientData: REF, value, label: ROPE] RETURNS[ BOOL ] = {
d: MyData = NARROW[clientData];
IF Rope.Length[value] > 0 AND Rope.Fetch[value, 0] = ' THEN {
IO.PutF[d.out, "\nPlease fill in the \"%g\" field\n", [rope[label]] ];
RETURN[FALSE] }
ELSE RETURN[TRUE]
};
TypeRC: PROC
[d: MyData, info: GVNames.Outcome, op: GVNames.RSOperation, name: ROPE, value: ROPE] = {
create: BOOL = SELECT op FROM
CreateIndividual, CreateGroup => TRUE,
ENDCASE => FALSE;
SELECT info FROM
group, individual =>
IO.PutF[d.out, "\"%g\" is %ga %g",
[rope[name]],
[rope[IF create THEN "already " ELSE NIL]],
[rope[IF info = group THEN "group" ELSE "individual"]] ];
notFound =>
IO.PutF[d.out, "\"%g\" %g",
[rope[name]],
[rope[SELECT TRUE FROM
Rope.Find[name, "."] < 0 => "needs an explicit registry",
create => "has an invalid registry or is recently deleted"
ENDCASE => "doesn't exist"]] ];
protocolError =>
IO.PutF[d.out, "protocol error - consult expert." ];
wrongServer =>
IO.PutF[d.out, "wrong server - consult expert." ];
allDown =>
IO.PutF[d.out, "couldn't contact needed server. Try later." ];
badPwd =>
IO.PutF[d.out, "your password is now incorrect. Please login again." ];
outOfDate =>
IO.PutF[d.out, "there's newer information in the database - consult expert." ];
notAllowed =>
IO.PutF[d.out, "you're not allowed to do that - ask an administrator to help you." ];
noChange => {
IsFooOfBaz: PROC [s: ROPE] =
{ IO.PutF[d.out, "\"%g\" is %g of \"%g\"",
[rope[value]], [rope[s]], [rope[name]] ] };
SELECT op FROM
CreateIndividual, CreateGroup =>
IO.PutF[d.out, "\"%g\" already exists", [rope[name]] ];
AddMember => IsFooOfBaz["already a member"];
AddMailBox => IsFooOfBaz["already a mailbox-site"];
AddForward => IsFooOfBaz["already a forwardee"];
AddOwner => IsFooOfBaz["already an owner"];
AddFriend => IsFooOfBaz["already a friend"];
DeleteMember => IsFooOfBaz["not a member"];
DeleteMailBox => IsFooOfBaz["not a mailbox-site"];
DeleteForward => IsFooOfBaz["not a forwardee"];
DeleteOwner => IsFooOfBaz["not an owner"];
DeleteFriend => IsFooOfBaz["not a friend"];
AddSelf =>
IO.PutF[d.out, "you're already a member of \"%g\"", [rope[name]]];
DeleteSelf =>
IO.PutF[d.out, "you're not a member of \"%g\"", [rope[name]]];
ENDCASE =>
IO.PutF[d.out, "no-change: consult LaurelSupport.pa."];
};
ENDCASE =>
IO.PutF[d.out, "unknown return code!"];
IO.PutRope[d.out, "\n"];
};
-- ******** Viewer management ******** --
MyData: TYPE = REF MyDataObject;
MyDataObject: TYPE = MONITORED RECORD[
in: STREAM,
out: STREAM,
level: { normal, owner, admin, wizard } ← normal,
verify: REF ATOMNIL,
quote: REF ATOMNIL,
gvms: REF ATOMNIL,
topChild,
kids,
groupT,
dataGT,
indivT,
dataIT,
deadT,
serverT,
script: Viewer ← NIL,
maxW: INTEGER,
buttH: INTEGER,
stop: BOOLFALSE ];
Create: Commander.CommandProc = {
[cmd: Handle]
d: MyData = NEW[MyDataObject];
v: Viewer = Containers.Create[
info: [name: "Maintain", column: right, scrollable: FALSE, iconic: TRUE]];
{
-- kludge to find max button size! --
temp: Buttons.Button = Buttons.Create[
info: [name: "Forwarding", parent: v, border: FALSE,
wx: 0, wy: 0],
proc: NIL, clientData: d, fork: FALSE, paint: FALSE];
d.maxW ← temp.ww;
d.buttH ← temp.wh;
Buttons.Destroy[temp];
};
d.topChild ← CreateSelector[name: "Level:",
values: LIST[$Normal, $Owner, $Administrator, $Wizard],
change: ChangeLevel,
clientData: d,
viewer: v,
x: 2, y: 1].child;
d.topChild ← Buttons.Create[
info: [name: "Stop", parent: v, border: TRUE,
wy: d.topChild.wy, wx: d.topChild.wx + d.topChild.ww + 20],
proc: Stop, clientData: d, fork: TRUE, paint: TRUE];
d.script ← TypeScript.Create[
info: [parent: v, wh: v.ch - (d.topChild.wy + d.topChild.wh + 2), ww: v.cw,
border: FALSE,
wy: d.topChild.wy + d.topChild.wh + 2, wx: 0] ];
Containers.ChildXBound[v, d.script];
Containers.ChildYBound[v, d.script];
[in: d.in, out: d.out] ← ViewerIO.CreateViewerStreams[
name: "Maintain", viewer: d.script, backingFile: "///Temp/Maintain.log", editedStream: FALSE];
CreateButtons[d, v];
};
ChangeLevel: PROC [parent: Viewer, clientData: REF, value: ATOM] = {
d: MyData = NARROW[clientData];
SELECT value FROM
$Normal => d.level ← normal;
$Owner => d.level ← owner;
$Administrator => d.level ← admin;
$Wizard => d.level ← wizard;
ENDCASE => ERROR;
CreateButtons[d, parent]
};
Stop: Buttons.ButtonProc = {
parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL
d: MyData = NARROW[clientData];
d.stop ← TRUE;
};
CreateButtons: ENTRY PROC [d: MyData, parent: Viewer] = {
ENABLE UNWIND => NULL;
child: Viewer ← NIL;
EnquiryButton: PROC [name: ROPE, proc: Buttons.ButtonProc] =
{
child ← Buttons.Create[
info: [name: name, parent: kids, border: TRUE,
wy: child.wy, wx: child.wx + d.maxW - 1, ww: d.maxW],
proc: proc,
clientData: d,
fork: TRUE,
paint: FALSE];
};
updateProc: Buttons.ButtonProc ← UpdateGroup;
UpdateButton: PROC
[name: ROPE, op: GVNames.RSOperation, guarded: BOOLFALSE] = {
child ← Buttons.Create[
info: [name: name, parent: kids, border: TRUE,
wy: child.wy, wx: child.wx + d.maxW - 1, ww: d.maxW],
proc: updateProc,
clientData: NEW[UpdateOpRec ← [d,op]],
fork: TRUE,
guarded: guarded,
paint: FALSE];
};
LabelText: PROC
[name, data: ROPE, prev: Viewer, newline: BOOLTRUE] RETURNS[Viewer] = {
x: INTEGER = IF newline THEN 2 ELSE child.wx + d.maxW - 1;
y: INTEGER = IF newline THEN child.wy + child.wh + 1 ELSE child.wy;
child ← ViewerTools.MakeNewTextViewer[
info: [parent: kids, wh: d.buttH, ww: 999, scrollable: TRUE,
data: IF prev = NIL THEN data ELSE ViewerTools.GetContents[prev],
border: FALSE,
wx: x + d.maxW + 2, wy: y],
paint: FALSE ];
Containers.ChildXBound[kids, child];
[] ← Buttons.Create[
info: [name: name, parent: kids, wh: d.buttH,
border: FALSE, wx: x, wy: y],
proc: TextLabelProc, clientData: child, fork: FALSE, paint: FALSE];
RETURN[child]
};
Label: PROC [name: ROPE] = {
child ← Labels.Create[
info: [name: name, parent: kids, border: FALSE,
wy: child.wy + child.wh + (IF child.class.flavor = $Button THEN -1 ELSE 2),
wx: 2],
paint: FALSE ];
};
Rule: PROC = {
child ← Rules.Create[
info: [parent: kids, border: FALSE,
wy: IF child = NIL THEN 0 ELSE child.wy + child.wh + 2, wx: 0, ww: kids.ww, wh: 1],
paint: FALSE ];
Containers.ChildXBound[kids, child];
};
kids: Viewer = Containers.Create[
info: [parent: parent, border: FALSE, scrollable: FALSE, wx: 0, wy: -9999, ww: 9999, wh: 0] ];
Containers.ChildXBound[parent, kids];
Rule[];
d.groupT ← LabelText[
name: "Group:",
data: "group name or pattern",
prev: d.groupT ];
IF d.level IN [owner..wizard]
THEN d.dataGT ← LabelText[
name: "Argument:",
data: "remark string, or member/owner/friend name",
prev: d.dataGT ]
ELSE d.dataGT ← NIL;
Label["Type: "];
EnquiryButton[name: "Matches", proc: TypeGroupMatchesProc];
EnquiryButton[name: "Members", proc: TypeGroupMembersProc];
EnquiryButton[name: "Summary", proc: TypeGroupSummaryProc];
IF d.level IN [admin..wizard] THEN
EnquiryButton[name: "Details", proc: TypeGroupDetailsProc];
EnquiryButton[name: "Finks", proc: TypeGroupFinksProc];
IF d.level IN [owner..wizard] THEN {
Label["Set:"];
UpdateButton[name: "Remark", op: ChangeRemark];
};
Label["Add:"];
UpdateButton[name: "Self", op: AddSelf];
IF d.level IN [owner..wizard] THEN {
UpdateButton[name: "Member", op: AddMember];
UpdateButton[name: "Owner", op: AddOwner];
UpdateButton[name: "Friend", op: AddFriend];
};
Label["Remove:"];
UpdateButton[name: "Self", op: DeleteSelf];
IF d.level IN [owner..wizard] THEN {
UpdateButton[name: "Member", op: DeleteMember];
UpdateButton[name: "Owner", op: DeleteOwner];
UpdateButton[name: "Friend", op: DeleteFriend];
};
IF d.level IN [admin..wizard] THEN {
Label[""];
UpdateButton[name: "Create", op: CreateGroup, guarded: FALSE];
UpdateButton[name: "Delete", op: DeleteGroup, guarded: TRUE];
UpdateButton[name: "NewName", op: NewName, guarded: FALSE];
};
Rule[];
updateProc ← UpdateIndividual;
d.indivT ← LabelText[
name: "Individual:",
data: UserCredentials.Get[].name,
prev: d.indivT ];
d.dataIT ← LabelText[
name: "Argument:",
data: "password, connect-site, etc.",
prev: d.dataIT ];
Label["Type:"];
EnquiryButton[name: "Matches", proc: TypeIndividualMatchesProc];
EnquiryButton[name: "Summary", proc: TypeIndividualSummaryProc];
IF d.level IN [admin..wizard] THEN
EnquiryButton[name: "Details", proc: TypeIndividualDetailsProc];
Label["Set:"];
UpdateButton[name: "Password", op: ChangePassword];
UpdateButton[name: "Connect", op: ChangeConnect];
IF d.level IN [admin..wizard] THEN {
Label["Add: "];
UpdateButton[name: "Mailbox", op: AddMailBox, guarded: FALSE];
UpdateButton[name: "Forwarding", op: AddForward, guarded: FALSE];
Label["Remove: "];
UpdateButton[name: "Mailbox", op: DeleteMailBox, guarded: FALSE];
UpdateButton[name: "Forwarding", op: DeleteForward, guarded: FALSE];
Label[""];
UpdateButton[name: "Create", op: CreateIndividual, guarded: FALSE];
UpdateButton[name: "Delete", op: DeleteIndividual, guarded: TRUE];
};
Rule[];
IF d.level = wizard
THEN {
d.deadT ← LabelText[
name: "Dead:",
data: "dead name or pattern",
prev: d.deadT];
Label["Type:"];
EnquiryButton[name: "Matches", proc: TypeDeadMatchesProc];
EnquiryButton[name: "Details", proc: TypeDeadDetailsProc];
Rule[];
[child, d.verify] ← CreateSelector[
name: "Verify:",
values: LIST[verifyOn, $off],
init: d.verify,
viewer: kids,
x: 2, y: child.wy + child.wh + 2];
[child, d.quote] ← CreateSelector[
name: "Quote:",
values: LIST[quoteOff, $on],
init: d.quote,
viewer: kids,
x: child.wx + child.ww + 10, y: child.wy];
[child, d.gvms] ← CreateSelector[
name: "GV/MS updates:",
values: LIST[$no, gvmsAllowed],
init: d.gvms,
viewer: kids,
x: child.wx + child.ww + 10, y: child.wy];
child ← Buttons.Create[
info: [name: "SetServer", parent: kids, wh: d.buttH,
border: TRUE, wx: 2, wy: child.wy + child.wh + 2],
proc: SetServerProc, clientData: d, fork: TRUE, paint: FALSE];
d.serverT ← LabelText[
name: " Host:",
data: "RName, NLS name, or Pup address",
prev: d.serverT,
newline: FALSE ];
Rule[];
}
ELSE { d.serverT ← d.deadT ← NIL };
{
kidsY: INTEGER = d.topChild.wy + d.topChild.wh + 2;
kidsH: INTEGER = child.wy + child.wh + 2;
IF d.kids # NIL THEN ViewerOps.DestroyViewer[d.kids, FALSE];
d.kids ← kids;
ViewerOps.MoveViewer[viewer: d.script, x: 0, y: kidsY + kidsH, w: d.script.ww, h: parent.ch - (kids.wy + kidsH), paint: FALSE];
ViewerOps.SetOpenHeight[parent, kidsY + kidsH + 8 * d.buttH];
IF NOT parent.iconic THEN ViewerOps.ComputeColumn[parent.column];
ViewerOps.MoveViewer[viewer: kids, x: kids.wx, y: kidsY, w: kids.ww, h: kidsH];
};
};
TextLabelProc: Buttons.ButtonProc = {
parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL
text: Viewer = NARROW[clientData];
SELECT mouseButton FROM
red => ViewerTools.SetSelection[text, NIL];
blue => { ViewerTools.SetContents[text, NIL]; ViewerTools.SetSelection[text, NIL] };
yellow => NULL;
ENDCASE => ERROR;
};
Selector: TYPE = REF SelectorRec;
SelectorRec: TYPE = RECORD[
value: REF ATOM,
change: PROC [parent: Viewer, clientData: REF, value: ATOM],
clientData: REF,
buttons: LIST OF Buttons.Button,
values: LIST OF ATOM ];
CreateSelector: PROC
[name: ROPE, values: LIST OF ATOM, init: REF ATOMNIL, change: PROC [parent: Viewer, clientData: REF, value: ATOM] ← NIL, clientData: REFNIL, viewer: Viewer, x, y: INTEGER]
RETURNS [child: Viewer, value: REF ATOM] = {
selector: Selector ← NEW[ SelectorRec ← [
value: IF init # NIL THEN init ELSE NEW[ATOM←values.first],
change: change,
clientData: clientData,
buttons: NIL,
values: values ] ];
last: LIST OF Buttons.Button ← NIL;
value ← selector.value;
child ← Labels.Create[info: [name: name, parent: viewer, border: FALSE, wx: x, wy: y] ];
FOR a: LIST OF ATOM ← values, a.rest UNTIL a = NIL DO
child ← Buttons.Create[
info: [name: Atom.GetPName[a.first], parent: viewer, border: TRUE, wy: child.wy, wx: child.wx + child.ww + 2],
proc: SelectorProc, clientData: selector, fork: TRUE, paint: TRUE];
IF last = NIL
THEN last ← selector.buttons ← CONS[first: child, rest: NIL]
ELSE { last.rest ← CONS[first: child, rest: NIL]; last ← last.rest };
IF a.first = selector.value^ THEN Buttons.SetDisplayStyle[child, $WhiteOnBlack];
ENDLOOP;
};
SelectorProc: Buttons.ButtonProc = {
parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL
self: Buttons.Button = NARROW[parent];
selector: Selector = NARROW[clientData];
buttons: LIST OF Buttons.Button ← selector.buttons;
FOR a: LIST OF ATOM ← selector.values, a.rest UNTIL a = NIL DO
IF self = buttons.first THEN {
selector.value^ ← a.first;
IF selector.change # NIL THEN selector.change[self.parent, selector.clientData, a.first];
Buttons.SetDisplayStyle[buttons.first, $WhiteOnBlack];
}
ELSE Buttons.SetDisplayStyle[buttons.first, $BlackOnWhite];
buttons ← buttons.rest;
ENDLOOP;
};
-- ******** Main program ******** --
Commander.Register[key: "Maintain", proc: Create,
doc: "Performs enquiries and updates to the Grapevine database"];
}.
Bob Hagmann January 29, 1986 8:21:42 am PST
Moved the "log" file from the current directory to ///Temp/
changes to: Create