-- Grapevine: pattern matching on names -- [Ivy]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.