-- Grapevine: pattern matching on names
-- [Ivy]<Birrell>NameMatch.mesa
-- Andrew Birrell 28-Jan-82 11:52:35
DIRECTORY
BodyDefs,
FrameDefs,
IODefs,
LaurelExecDefs,
NameInfoDefs,
StringDefs,
String,
SystemDefs,
Time;
NameMatch: PROGRAM
IMPORTS FrameDefs, IODefs, LaurelExecDefs, NameInfoDefs, StringDefs,
String, SystemDefs, Time =
BEGIN
LowerCase: PROC[c: CHARACTER] RETURNS[CHARACTER] = INLINE
{ RETURN[ IF c IN ['A..'Z] THEN 'a + (c-'A) ELSE c ] };
ReadString: PROC[prompt, s: STRING] =
BEGIN
DO BEGIN
IODefs.WriteString[prompt];
IODefs.ReadID[s ! IODefs.Rubout => GOTO del;
IODefs.LineOverflow => GOTO long];
EXIT
EXITS
del => IODefs.WriteLine[" XXX"L];
long => IODefs.WriteLine[" too long!"L];
END;
ENDLOOP;
END;
InfoType: TYPE = { stamp, mailboxes, owners, friends, members,
remark, none };
Match: PROC[pattern: STRING, set: NameInfoDefs.RListHandle,
info: InfoType] =
BEGIN
count: CARDINAL ← 0;
TestMatch: PROC[name: STRING] RETURNS[done: BOOLEAN] =
BEGIN
pPos: CARDINAL ← 0;
pBase: CARDINAL ← 0;
nPos: CARDINAL ← 0;
literal: BOOLEAN ← FALSE;
star: BOOLEAN ← FALSE;
match: BOOLEAN ← FALSE;
DO IF nPos = name.length THEN { match ← (pPos=pattern.length); EXIT };
SELECT TRUE FROM
literal, pPos = pattern.length => NULL;
pattern[pPos] = '' => { literal ← TRUE; pPos←pPos+1; LOOP };
pattern[pPos] = '* =>
{ star ← TRUE; pPos←pPos+1 ; pBase←pPos; literal←FALSE; LOOP };
ENDCASE => NULL;
IF pPos = pattern.length
OR LowerCase[pattern[pPos]] # LowerCase[name[nPos]]
THEN { IF star THEN pPos ← pBase ELSE EXIT }
ELSE pPos ← pPos+1;
literal ← FALSE; nPos ← nPos+1;
ENDLOOP;
IF match
THEN BEGIN
-- This statement dedicated to MDS --
IF info # stamp
THEN { IODefs.WriteString[name]; IODefs.WriteString[" ... "L] };
SELECT info FROM
stamp =>
BEGIN
info: NameInfoDefs.ExpandInfo = NameInfoDefs.Expand[name];
stamp: BodyDefs.Timestamp;
WITH i: info SELECT FROM
individual =>
{ NameInfoDefs.Close[i.sites]; stamp ← i.stamp };
group =>
{ NameInfoDefs.Close[i.members]; stamp ← i.stamp };
ENDCASE;
IF info.type NOT IN [group..individual]
THEN BEGIN
IODefs.WriteString[name];
IODefs.WriteString[" ... "L];
BadName[info.type];
END
ELSE BEGIN
s: STRING = [24];
String.AppendLongNumber[s, stamp.time, 10];
IODefs.WriteString[s];
IODefs.WriteString[" "L];
IODefs.WriteString[name];
IODefs.WriteString[" ... "L];
s.length ← 0;
Time.Append[s, Time.Unpack[stamp.time]];
IODefs.WriteLine[s];
END;
END;
mailboxes =>
BEGIN
info: NameInfoDefs.ExpandInfo = NameInfoDefs.Expand[name];
WITH i: info SELECT FROM
individual =>
TypeRList[i.sites, full, "invalid recipient"L];
group =>
{ IODefs.WriteString["(fwd) "L];
TypeRList[i.members, full, "silly"L] };
ENDCASE => BadName[info.type];
IODefs.WriteLine[""L];
END;
owners =>
BEGIN
TypeMembers[NameInfoDefs.GetOwners[name], full, "default"L];
IODefs.WriteLine[""L];
END;
friends =>
BEGIN
TypeMembers[NameInfoDefs.GetFriends[name], full, "none"L];
IODefs.WriteLine[""L];
END;
members =>
BEGIN
TypeMembers[NameInfoDefs.GetMembers[name], brief, "empty"L];
IODefs.WriteLine[" member(s)"L];
END;
remark =>
BEGIN
remark: STRING = [64];
nameInfo: NameInfoDefs.RemarkInfo =
NameInfoDefs.GetRemark[name, remark];
SELECT nameInfo FROM
group => IODefs.WriteLine[remark];
ENDCASE => BadName[nameInfo];
END;
none => IODefs.WriteLine[""L];
ENDCASE => ERROR;
count ← count+1;
END;
done ← FALSE;
END;
IODefs.WriteLine[" ..."L];
NameInfoDefs.Enumerate[set, TestMatch];
IODefs.WriteDecimal[count]; IODefs.WriteLine[" matches"L];
END;
BadName: PROC[nameInfo: NameInfoDefs.NameType] =
BEGIN
IODefs.WriteString[ SELECT nameInfo FROM
group => "is a group!"L,
individual => "is an individual"L,
allDown => "can't contact R-Server"L,
notFound => "doesn't exist!"L,
ENDCASE => "I don't understand!" ];
END;
Amount: TYPE = {full,brief};
TypeMembers: PROC[info: NameInfoDefs.MemberInfo, amount: Amount,
default: STRING] =
BEGIN
WITH i: info SELECT FROM
group => TypeRList[i.members, amount, default];
ENDCASE => BadName[info.type];
END;
TypeRList: PROC[list: NameInfoDefs.RListHandle, amount: Amount,
default: STRING] =
BEGIN
total: CARDINAL ← 0;
Work: PROC[name: STRING] RETURNS[done: BOOLEAN] =
BEGIN
done ← FALSE;
IF amount = full
THEN BEGIN
IF total # 0 THEN IODefs.WriteString[", "L];
IODefs.WriteString[name];
END;
total ← total+1;
END;
NameInfoDefs.Enumerate[list, Work];
IF total = 0
THEN IODefs.WriteString[default]
ELSE IF amount = brief THEN IODefs.WriteDecimal[total];
NameInfoDefs.Close[list];
END;
DefaultRegistry: PROC[default, to: STRING] =
BEGIN
defPos: CARDINAL ← default.length;
WHILE defPos > 0
DO defPos ← defPos-1;
IF default[defPos] = '. THEN { defPos←defPos+1; EXIT };
ENDLOOP;
FOR i: CARDINAL DECREASING IN [0..to.length)
DO IF to[i] = '. THEN EXIT;
REPEAT
FINISHED =>
BEGIN
IF to.length < to.maxlength
THEN { to[to.length] ← '.; to.length ← to.length+1 };
FOR j: CARDINAL IN [defPos..default.length)
WHILE to.length < to.maxlength
DO to[to.length] ← default[j]; to.length ← to.length+1; ENDLOOP;
END;
ENDLOOP;
END;
NameType: TYPE = { individual, group, dead };
mode: NameType;
info: InfoType;
pattern: STRING = [64];
regName: STRING = [64];
IF FrameDefs.IsBound[LaurelExecDefs.MakeMenuCommandCallable]
THEN BEGIN
LaurelExecDefs.MakeMenuCommandCallable[user];
LaurelExecDefs.MakeMenuCommandCallable[newMail];
LaurelExecDefs.MakeMenuCommandCallable[mailFile];
LaurelExecDefs.MakeMenuCommandCallable[display];
LaurelExecDefs.MakeMenuCommandCallable[delete];
LaurelExecDefs.MakeMenuCommandCallable[undelete];
LaurelExecDefs.MakeMenuCommandCallable[moveTo];
LaurelExecDefs.MakeMenuCommandCallable[copy];
END;
DO regGroupName: STRING = [64];
groupInfo: NameInfoDefs.MemberInfo;
[] ← SystemDefs.PruneHeap[];
DO IODefs.WriteString["Groups, Individuals, or Quit? "L];
SELECT LowerCase[IODefs.ReadChar[]] FROM
'g => { mode ← group; IODefs.WriteLine["Groups"L]; EXIT };
'i => { mode ← individual; IODefs.WriteLine["Individuals"L]; EXIT };
'd => { mode ← dead; IODefs.WriteLine["Deleted names"L]; EXIT };
'q => { IODefs.WriteLine["Quit"L]; GOTO ended };
ENDCASE => IODefs.WriteLine["?"L];
ENDLOOP;
BEGIN
ReadString["Registry: "L, regName];
StringDefs.AppendString[regGroupName,
SELECT mode FROM
group => "Groups."L,
individual => "Individuals.",
dead => "Dead.",
ENDCASE => ERROR];
StringDefs.AppendString[regGroupName, regName];
END;
IODefs.WriteString[". Enumerating registry ... "L];
groupInfo ← NameInfoDefs.GetMembers[regGroupName];
WITH g: groupInfo SELECT FROM
group =>
BEGIN
IODefs.WriteLine["ok"L];
DO ReadString["Pattern: "L, pattern];
IF pattern.length = 0 THEN EXIT;
DefaultRegistry[regGroupName, pattern];
IODefs.WriteLine[""L];
SELECT mode FROM
group =>
DO IODefs.WriteString[
"Stamp, Friends, Members, Owners, Remark, None, or Exit? "L];
SELECT LowerCase[IODefs.ReadChar[]] FROM
's => { info ← stamp; IODefs.WriteString["Stamp"L]; EXIT };
'f => { info ← friends; IODefs.WriteString["Friends"L]; EXIT };
'm => { info ← members; IODefs.WriteString["Members"L]; EXIT };
'o => { info ← owners; IODefs.WriteString["Owners"L]; EXIT };
'r => { info ← remark; IODefs.WriteString["Remark"L]; EXIT };
'n => { info ← none; IODefs.WriteString["None"L]; EXIT };
'e => { IODefs.WriteString["Exit"L]; GOTO enumerateAgain };
ENDCASE => IODefs.WriteLine["?"L];
ENDLOOP;
individual =>
DO IODefs.WriteString[
"Mailboxes, None, or Exit? "L];
SELECT LowerCase[IODefs.ReadChar[]] FROM
'm => { info ← mailboxes; IODefs.WriteString["Mailboxes"L]; EXIT };
'n => { info ← none; IODefs.WriteString["None"L]; EXIT };
'e => { IODefs.WriteString["Exit"L]; GOTO enumerateAgain };
ENDCASE => IODefs.WriteLine["?"L];
ENDLOOP;
ENDCASE => info ← none;
Match[pattern, g.members, info];
REPEAT
enumerateAgain => NULL;
ENDLOOP;
NameInfoDefs.Close[g.members];
IODefs.WriteLine[""L];
END;
notFound => IODefs.WriteLine["Unknown registry"L];
allDown => IODefs.WriteLine["Can't contact R-Server"L];
ENDCASE => ERROR;
REPEAT
ended => NULL;
ENDLOOP;
END.