-- 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.