-- CedarDLMapper.mesa; Edited by Schroeder on March 21, 1983 8:32 am
-- Last Edited by: RWeaver, February 10, 1984 10:50 am
-- Hal Murray May 6, 1985 9:25:03 pm PDT
-- John Larson, November 24, 1985 7:17:08 pm PST

DIRECTORY

Basics USING [Comparison],
Buttons USING [Button, ButtonProc, Create],
Containers USING [ChildXBound, ChildYBound, Container, Create],
Labels USING [Create, Label, Set],
Menus USING [CreateEntry, CreateMenu, InsertMenuEntry, Menu, MenuProc],
Rules USING [Create, Rule],
ViewerIO USING [CreateViewerStreams],
ViewerOps USING [CreateViewer, PaintViewer, SetMenu, SetOpenHeight],
ViewerClasses USING [Viewer],
ViewerTools USING [MakeNewTextViewer, GetContents, SetContents, SetSelection],

FS USING [StreamOpen],
GVBasics USING [oldestTime],
GVNames USING [CheckStamp, GetList, GetMembers,
GetRemark, ListType, MemberInfo, RemarkInfo, RListHandle],
Histogram,
IO USING [card, Flush, noWhereStream, PutF, PutFR, PutRope, STREAM, time],
RedBlackTree USING [Create, EnumerateIncreasing, Insert, Key, Lookup, Table, UserData],
Rope USING [Cat, Compare, Find, Length, ROPE, SkipOver, SkipTo, Substr];

CedarDLMapper: CEDAR MONITOR IMPORTS

Buttons, Containers, Labels, Menus, Rules, ViewerIO, ViewerOps, ViewerTools,

FS, GVNames, Histogram, IO, RedBlackTree, Rope

= BEGIN

ROPE: TYPE = Rope.ROPE;

entryHeight: CARDINAL = 15;
entryVSpace: CARDINAL = 10;
entryHSpace: CARDINAL = 10;

heightSoFar: CARDINAL ← 0;

container: Containers.Container;
currentGroups, totalGroups: Labels.Label;
currentName: Labels.Label;
exceptions: ViewerClasses.Viewer;
inStr, outStr: IO.STREAM;

fullFileName, conFileName, ARPAFileName, registries: ViewerClasses.Viewer;

BuildOuter: PROC =
BEGIN
myMenu: Menus.Menu ← Menus.CreateMenu[];
container ← Containers.Create[ info: [name: "DLMap", scrollable: FALSE] ];
Menus.InsertMenuEntry[menu: myMenu,
entry: Menus.CreateEntry[name: "DoIt", proc: DoIt]
];
Menus.InsertMenuEntry[menu: myMenu,
entry: Menus.CreateEntry[name: "Stop", proc: StopIt]
];
ViewerOps.SetMenu[viewer: container, menu: myMenu];
BuildUserInput[];
BuildUserFeedback[];
BuildExceptions[];
ViewerOps.SetOpenHeight[viewer: container, clientHeight: heightSoFar];
ViewerOps.PaintViewer[viewer: container, hint: all];
END;

BuildUserInput: PROC =
BEGIN
Bl1, Bl2, Bl3, Bl4: Buttons.Button;
heightSoFar ← heightSoFar+entryVSpace/2;
Bl1 ← Buttons.Create[info: [name: "Full map file name:", parent: container, border: FALSE,
wy: heightSoFar], proc: ForceSelToFullFileName];
fullFileName ← ViewerTools.MakeNewTextViewer[info: [parent: container,
wx: Bl1.wx+Bl1.ww+entryHSpace, wy: heightSoFar, ww: 0, wh: entryHeight,
data: defaultFullFileName, scrollable: FALSE, border: FALSE], paint: FALSE];
Containers.ChildXBound[container, fullFileName];

heightSoFar ← heightSoFar+entryVSpace+Bl1.wh;
Bl2 ← Buttons.Create[info: [name: "Condensed map file name:", parent: container,
border: FALSE, wy: heightSoFar], proc: ForceSelToConFileName];
conFileName ← ViewerTools.MakeNewTextViewer[info: [parent: container,
wx: Bl2.wx+Bl2.ww+entryHSpace, wy: heightSoFar, ww: 0, wh: entryHeight,
data: defaultConFileName, scrollable: FALSE, border: FALSE], paint: FALSE];
Containers.ChildXBound[container, conFileName];

heightSoFar ← heightSoFar+entryVSpace+Bl2.wh;
Bl3 ← Buttons.Create[info: [name: "Arpa DL map file name:", parent: container,
border: FALSE, wy: heightSoFar], proc: ForceSelToARPAFileName];
ARPAFileName ← ViewerTools.MakeNewTextViewer[info: [parent: container,
wx: Bl3.wx+Bl3.ww+entryHSpace, wy: heightSoFar, ww: 0, wh: entryHeight,
data: defaultARPAFileName, scrollable: FALSE, border: FALSE], paint: FALSE];
Containers.ChildXBound[container, ARPAFileName];

heightSoFar ← heightSoFar+entryVSpace+Bl3.wh;
Bl4 ← Buttons.Create[info: [name: "Registries:", parent: container, border: FALSE,
wy: heightSoFar], proc: ForceSelToRegistries];
registries ← ViewerTools.MakeNewTextViewer[info: [parent: container,
wx: Bl4.wx+Bl4.ww+entryHSpace, wy: heightSoFar, ww: 0, wh: 2*entryHeight,
data: defaultRegistries, scrollable: TRUE, border: FALSE], paint: FALSE];
Containers.ChildXBound[container, registries];
heightSoFar ← heightSoFar+entryVSpace+registries.wh;
END;

ForceSelToFullFileName: Buttons.ButtonProc =
BEGIN
IF mouseButton#red
THEN ViewerTools.SetContents[fullFileName, defaultFullFileName]
ELSE ViewerTools.SetSelection[fullFileName];
END;

ForceSelToConFileName: Buttons.ButtonProc =
BEGIN
IF mouseButton#red
THEN ViewerTools.SetContents[conFileName, defaultConFileName]
ELSE ViewerTools.SetSelection[conFileName];
END;

ForceSelToARPAFileName: Buttons.ButtonProc =
BEGIN
IF mouseButton#red
THEN ViewerTools.SetContents[ARPAFileName, defaultARPAFileName]
ELSE ViewerTools.SetSelection[ARPAFileName];
END;


ForceSelToRegistries: Buttons.ButtonProc =
BEGIN
IF mouseButton#red
THEN ViewerTools.SetContents[registries, defaultRegistries]
ELSE ViewerTools.SetSelection[registries];
END;

BuildUserFeedback: PROC =
BEGIN
lg, ln, of: Labels.Label;
r1: Rules.Rule;
r1 ← Rules.Create[info: [parent: container, wx: 0, wy: heightSoFar, ww: 0, wh: 1]];
Containers.ChildXBound[container, r1];
heightSoFar ← heightSoFar+entryVSpace;
lg ← Labels.Create[info: [name: "DL's:", parent: container,
wx: 0, wy: heightSoFar, border: FALSE]];
currentGroups ← Labels.Create[info: [name: "0000", parent: container,
wx: lg.wx+lg.ww+entryHSpace, wy: heightSoFar, border: TRUE]];
Labels.Set[currentGroups, "0"];
of ← Labels.Create[info: [name: "of", parent: container,
wx: currentGroups.wx+currentGroups.ww+entryHSpace, wy: heightSoFar, border: FALSE]];
totalGroups ← Labels.Create[info: [name: "0000", parent: container,
wx: of.wx+of.ww+entryHSpace, wy: heightSoFar, border: TRUE]];
Labels.Set[totalGroups, "0"];
heightSoFar ← heightSoFar+entryVSpace+of.wh;
ln ← Labels.Create[info: [name: "Current Name:", parent: container,
wx: 0, wy: heightSoFar, border: FALSE]];
currentName ← Labels.Create[info: [name: "", parent: container,
wx: ln.wx+ln.ww+entryHSpace, wy: heightSoFar, border: FALSE]];
Labels.Set[currentName, "none"];
Containers.ChildXBound[container, currentName];
heightSoFar ← heightSoFar+entryVSpace+of.wh;
END;

BuildExceptions: PROC =
BEGIN
r2: Rules.Rule;
r2 ← Rules.Create[info: [parent: container,
wx: 0, wy: heightSoFar, ww: 0, wh: 1]];
Containers.ChildXBound[container, r2];
heightSoFar ← heightSoFar+entryVSpace;
exceptions ← ViewerOps.CreateViewer[flavor: $Typescript,
info: [name: "", parent: container, wx: 0, wy: heightSoFar, wh: 5*entryHeight,
border: FALSE]];
heightSoFar ← heightSoFar+entryVSpace+exceptions.wh;
Containers.ChildXBound[container, exceptions];
Containers.ChildYBound[container, exceptions];
[in: inStr, out: outStr] ← ViewerIO.CreateViewerStreams[name: "", viewer: exceptions,
backingFile: "DLMap.log"];
END;



--**************** start of operational code ****************


mapperStopped: ERROR = CODE;
mappingInProgress: BOOLEANFALSE;
allRegistries: ROPENIL;
defaultRegistries: ROPE = "DC DLOS EOSA ES FX Henr LB OGC OSDA PA PASA RX Siemens STHQ SV WBST X XRCC";
defaultFullFileName: ROPE = "///GV/FullDLMap.txt";
defaultConFileName: ROPE = "///GV/DLMap.txt";
defaultARPAFileName: ROPE = "///GV/ARPA-DLMap.txt";
stop: BOOLEANFALSE;

DoIt: Menus.MenuProc =
BEGIN
IF ReserveMapper[] THEN
BEGIN
Map[ ! mapperStopped => {outStr.PutF["\nStopped at %t.\n\n", IO.time[]]; CONTINUE} ];
ReleaseMapper[];
END;
END; -- DoIt--

StopIt: Menus.MenuProc =
{SetStop[]};

ReserveMapper: ENTRY PROCEDURE RETURNS [BOOLEAN] =
BEGIN
IF mappingInProgress
THEN RETURN [FALSE]
ELSE {mappingInProgress ← TRUE; RETURN[TRUE]};
END;

ReleaseMapper: ENTRY PROCEDURE =
{mappingInProgress ← FALSE; stop ← FALSE};

SetStop: ENTRY PROCEDURE =
{stop ← TRUE};

StopSet: ENTRY PROCEDURE RETURNS [BOOLEAN] = INLINE
{RETURN [stop]};

Stopped: PROCEDURE RETURNS [BOOLEAN] =
{IF StopSet[] THEN ERROR mapperStopped ELSE RETURN[FALSE]};

ReportAllDown: PROCEDURE [dot: BOOLEAN, name: ROPE] =
BEGIN
IF dot
THEN outStr.PutRope["."]
ELSE outStr.PutF["\nAll down: %g. ", [rope[name]]];
END;

ForWordsInRopeDo: PROCEDURE [r: ROPE, work: PROCEDURE[ROPE] RETURNS[done: BOOLEAN]]
RETURNS [stopped: BOOLEAN] =
BEGIN
parsePosition: CARDINAL ← Rope.SkipOver[r, 0, " "]; -- skip initial blanks
UNTIL parsePosition = Rope.Length[r] DO --parse registry input--
start: CARDINAL = parsePosition;
parsePosition ← Rope.SkipTo[r, parsePosition, " "];
IF work[Rope.Substr[r, start, parsePosition-start]] THEN RETURN [stopped: TRUE];
parsePosition ← Rope.SkipOver[r, parsePosition, " "];
ENDLOOP;
RETURN [stopped: FALSE]
END; -- ForWordsInRopeDo --

CheckRegistryValidity: PROCEDURE [reg: ROPE] RETURNS [done: BOOLEAN] =
BEGIN
printDot: BOOLEANFALSE;
UNTIL Stopped[] DO
SELECT GVNames.CheckStamp[Rope.Cat[reg, ".gv"]] FROM
group =>
NULL;
individual, notFound =>
BEGIN
outStr.PutF["%g%g\n\n", [rope[reg]], [rope[" is not a valid registry."]]];
ERROR mapperStopped
END;
allDown =>
{ReportAllDown[printDot, reg]; printDot←TRUE; LOOP};
ENDCASE =>
ERROR;
EXIT;
ENDLOOP;
RETURN [done: FALSE]
END; --CheckRegistryValidity--

CreateOutputFile: PROCEDURE[n: ROPE] RETURNS [IO.STREAM] =
{ RETURN [IF Rope.Length[n] = 0 THEN IO.noWhereStream
ELSE FS.StreamOpen[n, create]]};

GroupNotFound: ERROR = CODE;

GetRList: PROCEDURE [n: ROPE, t: GVNames.ListType] RETURNS [l: ROPE] =
BEGIN
printDot: BOOLEANFALSE;
UNTIL Stopped[] DO
WITH GVNames.GetList[n, GVBasics.oldestTime, t] SELECT FROM
g: GVNames.MemberInfo[group] =>
BEGIN
AddNameToRope: PROCEDURE[m: ROPE] RETURNS [done: BOOLEAN] =
BEGIN
IF l # NIL THEN l ← Rope.Cat[l, ", "];
l ← Rope.Cat[l, m];
done ← FALSE
END;
l ← NIL;
EnumerateRList[g.members, AddNameToRope];
END;
a: GVNames.MemberInfo[allDown] =>
{ReportAllDown[printDot, n]; printDot←TRUE; LOOP};
ENDCASE =>
ERROR GroupNotFound;
EXIT;
ENDLOOP;
END; --GetRList--

GetMembers: PROCEDURE [g: ROPE] RETURNS [rLH: GVNames.RListHandle, c: CARDINAL] =
BEGIN
printDot: BOOLEANFALSE;
UNTIL Stopped[] DO
WITH GVNames.GetMembers[g] SELECT FROM
g: GVNames.MemberInfo[group] => {rLH ← g.members; c ← g.count};
a: GVNames.MemberInfo[allDown] =>
{ReportAllDown[printDot, g]; printDot←TRUE; LOOP};
ENDCASE => ERROR GroupNotFound;
EXIT;
ENDLOOP;
END; --GetMembers--

GetRemark: PROCEDURE [g: ROPE] RETURNS [r: ROPE] =
BEGIN
printDot: BOOLEANFALSE;
UNTIL Stopped[] DO
info: GVNames.RemarkInfo;
[info, r] ← GVNames.GetRemark[g];
SELECT info FROM
group => IF Rope.Length[r] = 0 THEN r ← NIL;
allDown =>
{ReportAllDown[printDot, g]; printDot←TRUE; LOOP};
ENDCASE => ERROR GroupNotFound;
EXIT;
ENDLOOP;
END; --GetRemark--

CardToRope: PROCEDURE [c: CARDINAL] RETURNS [ROPE] = INLINE
{RETURN [IO.PutFR[v1: IO.card[c]]]};

DLInfo: TYPE = RECORD [
name: ROPE,
printable, notFound: BOOLEAN,
in, ind: CARDINAL,
remark, owners, friends: ROPE,
sub: LIST OF REF DLInfo ];

GetKey: SAFE PROC [data: RedBlackTree.UserData] RETURNS [key: RedBlackTree.Key] =
CHECKED BEGIN
WITH data SELECT FROM
r: ROPE => key ← r;
m: REF DLInfo => key ← m.name;
ENDCASE => ERROR;
END;


MyCompare: SAFE PROC [k: RedBlackTree.Key, data: RedBlackTree.UserData] RETURNS [Basics.Comparison] =
CHECKED BEGIN
a: ROPENARROW[k];
b: ROPENARROW[GetKey[data]];
RETURN [Rope.Compare[a, b, FALSE]]
END; --MyCompare--


EnumerateRList: PROCEDURE [r: GVNames.RListHandle,
p: PROCEDURE [ROPE] RETURNS [BOOLEAN] ] =
{FOR r ← r, r.rest UNTIL r=NIL OR p[r.first] DO ENDLOOP};

Map: PROCEDURE =
BEGIN

GetEntry: PROCEDURE [n: ROPE] RETURNS [d: REF DLInfo] =
BEGIN
d ← NARROW[RedBlackTree.Lookup[tree, n]];
IF d = NIL THEN BEGIN
d ← NEW [DLInfo ← [name:n, printable:FALSE, notFound:FALSE, in:0, ind:0,
remark:NIL, owners:NIL, friends:NIL, sub:NIL]];
RedBlackTree.Insert[tree, d, n];
END;
END; --GetEntry--

EnumerateRegistry: PROCEDURE [reg: ROPE] RETURNS [BOOLEAN] =
BEGIN
rG: ROPE = Rope.Cat["groups.", reg];
regRLH: GVNames.RListHandle;
Labels.Set[currentName, rG];
[regRLH, ] ← GetMembers[rG ! GroupNotFound =>
{outStr.PutF["\n%g not found?", [rope[rG]]]; ERROR mapperStopped}];
EnumerateRList[regRLH, GroupWork];
RETURN[FALSE]
END; --EnumerateRegsitry--

GroupWork: PROCEDURE [group: ROPE] RETURNS [done: BOOLEAN] =
BEGIN
ContentWork: PROCEDURE [m: ROPE] RETURNS [done: BOOLEAN] =
BEGIN
done ← FALSE;
IF Rope.SkipTo[m, 0, "^"] = Rope.Length[m]
THEN gDI.ind ← gDI.ind + 1
ELSE BEGIN
end: LIST OF REF DLInfo;
mDI: REF DLInfo = GetEntry[m];
mDI.in ← mDI.in + 1;
IF gDI.sub = NIL
THEN gDI.sub ← CONS[mDI, NIL]
ELSE FOR end ← gDI.sub, end.rest UNTIL end.rest = NIL DO
REPEAT FINISHED => end.rest ← CONS[mDI, NIL];
ENDLOOP;
END;
END; --ContentWork--
gDI: REF DLInfo ← NIL;
rLH: GVNames.RListHandle;
count: CARDINAL;
notFound: BOOLEANFALSE;
Labels.Set[currentName, group];
Labels.Set[totalGroups, CardToRope[totalGrp ← totalGrp + 1]];
[rLH, count] ← GetMembers[group ! GroupNotFound => {notFound ← TRUE; CONTINUE}];
IF NOT notFound THEN gram.DataPoint[count];
IF Rope.SkipTo[group, 0, "^"] = Rope.Length[group] THEN RETURN [done: FALSE];
gDI ← GetEntry[group];
gDI.printable ← TRUE;
IF notFound
THEN gDI.notFound ← TRUE
ELSE BEGIN
IF fFH#IO.noWhereStream THEN BEGIN -- need extra info for the full listing --
EnumerateRList[rLH, ContentWork];
gDI.owners ← GetRList[group, owners];
END;
gDI.remark ← GetRemark[group];
gDI.friends ← GetRList[group, friends];
END;
RETURN [done: FALSE]
END; -- GroupWork--

AlreadyDoneThis: SIGNAL [new: REF DLInfo] RETURNS [BOOLEAN] = CODE;

WC: PROCEDURE [r1, r2, r3, r4: ROPENIL] =
BEGIN
IF r1#NIL THEN cFH.PutRope[r1];
IF r2#NIL THEN cFH.PutRope[r2];
IF r3#NIL THEN cFH.PutRope[r3];
IF r4#NIL THEN cFH.PutRope[r4]

END;

WF: PROCEDURE [r1, r2, r3, r4: ROPENIL] =
BEGIN
IF r1#NIL THEN fFH.PutRope[r1];
IF r2#NIL THEN fFH.PutRope[r2];
IF r3#NIL THEN fFH.PutRope[r3];
IF r4#NIL THEN fFH.PutRope[r4]
END;

WA: PROCEDURE [r1, r2, r3, r4: ROPENIL] =
BEGIN
IF r1#NIL THEN aFH.PutRope[r1];
IF r2#NIL THEN aFH.PutRope[r2];
IF r3#NIL THEN aFH.PutRope[r3];
IF r4#NIL THEN aFH.PutRope[r4]
END;

W: PROCEDURE [r1, r2, r3, r4: ROPENIL] = {WF[r1, r2, r3, r4]; WC[r1, r2, r3, r4]};

PrintDLEntry: SAFE PROCEDURE [dl: RedBlackTree.UserData] RETURNS [BOOLEAN] =
TRUSTED BEGIN
dlInfo: REF DLInfo = NARROW [dl];
IF NOT dlInfo.printable OR dlInfo.notFound THEN RETURN[FALSE];
Labels.Set[currentName, dlInfo.name];
Labels.Set[currentGroups, CardToRope[currentGrp ← currentGrp+1]];

W["\n"];

IF Rope.Find[dlInfo.remark, "@"] > -1 THEN {
WA[dlInfo.name]; WA[" - ", dlInfo.remark, "\n"]};

W[dlInfo.name];

IF dlInfo.remark#NIL THEN {W[" - ", dlInfo.remark]; dlInfo.remark ← NIL};

WF["\n\t", CardToRope[dlInfo.ind], " individual", IF dlInfo.ind=1 THEN NIL ELSE "s"];
IF dlInfo.in#0 THEN
WF["; in ", CardToRope[dlInfo.in], " other DL", IF dlInfo.in=1 THEN NIL ELSE "s"];
WF["\n"];

WF["\tOwners: "];
IF dlInfo.owners=NIL
THEN WF[" Default", "\n"]
ELSE {WF[dlInfo.owners, "\n"]; dlInfo.owners ← NIL};

WF["\tFriends: "];
IF dlInfo.friends=NIL
THEN WF[" None", "\n"]
ELSE {WF[dlInfo.friends, "\n"]; dlInfo.friends ← NIL};

PrintSubstructure[dlInfo ! AlreadyDoneThis => RESUME [new=dlInfo]];
RETURN[FALSE]
END;

PrintSubstructure: PROCEDURE[top: REF DLInfo] =
BEGIN
dots: ROPE = ". . . . . . . . . . "; -- 10 dot/space for indenting --
indention ← indention+1;
FOR dlList: LIST OF REF DLInfo ← top.sub, dlList.rest UNTIL dlList = NIL DO
dl: REF DLInfo = dlList.first;
WF[Rope.Substr[dots, 0, MIN[indention*2, Rope.Length[dots]]], dl.name, " - "];
SELECT TRUE FROM
(NOT dl.printable) =>
WF["not expanded.\n"];
(dl.notFound) =>
WF["not found.\n"];
(indention=1 AND top.ind=0 AND dlList=top.sub AND dlList.rest=NIL) =>
WF["is the only contained group; see that entry.\n"];
(SIGNAL AlreadyDoneThis[dl]) =>
WF["**loop**\n"];
ENDCASE =>
BEGIN
WF[CardToRope[dl.ind], "\n"];
PrintSubstructure[dl ! AlreadyDoneThis => IF new=dl THEN RESUME[TRUE] ];
END;
ENDLOOP;
indention ← indention-1;
END; --PrintSubstructure--

tree: RedBlackTree.Table = RedBlackTree.Create[GetKey, MyCompare];
inputRegs: ROPE = ViewerTools.GetContents[registries];
aFH: IO.STREAM = CreateOutputFile[ViewerTools.GetContents[ARPAFileName]];
cFH: IO.STREAM = CreateOutputFile[ViewerTools.GetContents[conFileName]];
fFH: IO.STREAM = CreateOutputFile[ViewerTools.GetContents[fullFileName]];
currentGrp, totalGrp: CARDINAL ← 0;
indention: CARDINAL ← 0;
gram: Histogram.Gram = Histogram.NewGram[0, 2000];

IF cFH=IO.noWhereStream AND fFH=IO.noWhereStream AND aFH=IO.noWhereStream THEN
{outStr.PutRope["\nNo output files."]; RETURN};

outStr.PutF["Running at %t.", IO.time[]];
Labels.Set[currentName, "none"];
Labels.Set[totalGroups, "0"];
Labels.Set[currentGroups, "0"];

[] ← ForWordsInRopeDo[inputRegs, CheckRegistryValidity];
[] ← ForWordsInRopeDo[inputRegs, EnumerateRegistry];

W["Distribution list map of ", inputRegs];
WA["Arpanet redistribution list map of ", inputRegs];
W[".\n\nProduced at ", IO.PutFR[v1: IO.time[]]];
WA[".\n\nProduced at ", IO.PutFR[v1: IO.time[]]];
WC["\n\nTry using the ""Maintain"" program to add/remove yourself to/from a DL. "];
WC["If ""Maintain"" responds with ""you're not allowed to do that"", then send a message to ""Owners-firstPart^.reg"" requesting to be added/removed."];
W["\n\n"];
WA["\n\n"];

[] ← RedBlackTree.EnumerateIncreasing[tree, PrintDLEntry];
W["\n\nEnd of listing.\n"];
WA["\n\nEnd of listing.\n"];

fFH.Flush[];
cFH.Flush[];
aFH.Flush[];

gram.Print[outStr, "\n\nNumber of members in a group."];
outStr.PutF["\nDone at %t.\n\n", IO.time[]];

END; --Map--

BuildOuter[];

END.