From b15362e3ba1fe8f15ca06c83dcaac5cf1b6d143f Mon Sep 17 00:00:00 2001 From: Daniel Burrows Date: Sun, 31 Aug 2008 19:17:44 -0700 Subject: Add support for separately customizing the sub-policies of the branches of a pattern policy. The syntax looks something like this: pattern(matcher => title {policy}, matcher {policy}, matcher || {policy}) --- doc/en/aptitude.xml | 69 ++++++++++++++++++++++++++++++++--- src/load_grouppolicy.cc | 96 +++++++++++++++++++++++++++++++++++++++++-------- src/pkg_grouppolicy.cc | 26 ++++++++------ src/pkg_grouppolicy.h | 30 +++++++++++----- 4 files changed, 183 insertions(+), 38 deletions(-) diff --git a/doc/en/aptitude.xml b/doc/en/aptitude.xml index 7ece47bf..1d9e0c3b 100644 --- a/doc/en/aptitude.xml +++ b/doc/en/aptitude.xml @@ -6422,7 +6422,7 @@ iuAU wesnoth-data +930kB 0.8.7-1 0.8.8-1.0w - pattern(pattern => title, ...) + pattern(pattern => title { policy } , ...) @@ -6437,7 +6437,10 @@ iuAU wesnoth-data +930kB 0.8.7-1 0.8.8-1.0w title will be replaced by the Nth result of the match. If title is not present, it - is assumed to be \1. + is assumed to be \1. Note that + packages which do not match any + patterns will not appear + in the tree at all. @@ -6462,11 +6465,67 @@ iuAU wesnoth-data +930kB 0.8.7-1 0.8.8-1.0w pattern(&Saction;(remove) => Packages Being Removed, &Strue; ||) will place packages that are being removed into a subtree, and place all - the other packages at the current level. Any later - grouping policies will apply to both sets of - packages, of course. + the other packages at the current level, grouped + according to the policies that follow + pattern. + + By default, all the packages that match each pattern + are grouped according to the rules that follow the + pattern policy. To specify a + different policy for some packages, write the policy + in braces ({}) after the title of + the group, after the ||, or after + the pattern if neither is present. For instance: + + + + + Use of the <literal>pattern</literal> grouping policy with sub-policies + + + + pattern(&Saction;(remove) => Packages + Being Removed {}, &Saction;(install) => Packages + Being Installed, &Strue; || {status}) + + + + + The policy in the above example has the following + effects: + + + + + + Packages that are being removed are placed into + a subtree labeled Packages Being + Removed; the grouping policy for this + subtree is empty, so the packages are placed + into a flat list. + + + + + + Packages that are being installed are placed + into a subtree labeled Packages Being + Installed and grouped according to the + policies that follow pattern. + + + + + + All remaining packages are placed at the top + level of the tree, grouped according to their + status. + + + + See for more information on the format of diff --git a/src/load_grouppolicy.cc b/src/load_grouppolicy.cc index 8ee6e469..c88ce4c4 100644 --- a/src/load_grouppolicy.cc +++ b/src/load_grouppolicy.cc @@ -107,6 +107,14 @@ public: const string::const_iterator &end)=0; }; +/** \brief A global table of all the parsers of group policies + * according to their names. + * + * Used to parse group policy lists; could actually just as well be a + * member of the list parser. + */ +static map parse_types; + template @@ -222,8 +230,14 @@ public: /** Parse a grouping policy based on a list. Looks for policy names, * then starts parsing their parameters as necessary according to a - * table of parsers. Names must be alphanumeric; the occurance - * of a nonalphanumeric at the top level will terminate the parse. + * table of parsers. Names must be alphanumeric; the occurance of a + * nonalphanumeric other than the terminator (see below) at the top + * level will terminate the parse. + * + * The list can be terminated by a distinguished character (typically + * something like ")" or "}"); if this appears where a policy name + * would normally start, it will be consumed and the parser will + * return. */ class list_policy_parser : public group_policy_parser { @@ -232,9 +246,17 @@ public: private: const parsemap &parsers; + const bool allow_terminator; + const char terminator; public: list_policy_parser(const parsemap &_parsers) - :parsers(_parsers) + : parsers(_parsers), allow_terminator(false), terminator(-1) + { + } + + list_policy_parser(const parsemap &_parsers, + char _terminator) + : parsers(_parsers), allow_terminator(true), terminator(_terminator) { } @@ -254,6 +276,14 @@ public: while(begin != end && (isspace(*begin) || *begin == ',')) ++begin; + // If we hit the terminator (e.g., "}" for pattern policies), + // eat it and stop. + if(allow_terminator && begin != end && *begin == terminator) + { + ++begin; + break; + } + if(begin != end && !isalnum(*begin)) throw GroupParseException(_("Expected policy identifier, got '%c'"), *begin); @@ -267,7 +297,16 @@ public: ++begin; if(begin != end && *begin != ',' && *begin != '(') - throw GroupParseException(_("Expected ',' or '(', got '%c'"), *begin); + { + if(allow_terminator) + { + if(*begin != terminator) + throw GroupParseException(_("Expected ',', '%c', or '('; got '%c'"), + terminator, *begin); + } + else + throw GroupParseException(_("Expected ',' or '(', got '%c'"), *begin); + } if(!name.empty()) { @@ -590,7 +629,7 @@ class pattern_policy_parser : public group_policy_parser if(begin == end || *begin == ')') throw GroupParseException(_("Missing arguments to 'pattern'")); - vector subgroups; + vector subgroups; vector terminators; terminators.push_back(","); @@ -610,6 +649,7 @@ class pattern_policy_parser : public group_policy_parser false, true, false)); bool passthrough = false; + std::auto_ptr chain; if(matcher.get() == NULL) throw GroupParseException(_("Unable to parse pattern after \"%s\""), @@ -625,10 +665,26 @@ class pattern_policy_parser : public group_policy_parser format.clear(); - while(begin != end && *begin != ',' && *begin != ')') + while(begin != end && *begin != ',' && *begin != ')' && *begin != '{') { format += *begin; - ++begin; + // Allow the user to backslash-escape terminating + // characters. The backslashes are preserved so + // that they can be used to detect substitutions + // (the pattern group policy will handle + // backslashed non-numbers by dropping the + // backslash). + if(*begin == '\\') + { + ++begin; + if(begin != end) + { + format += *begin; + ++begin; + } + } + else + ++begin; } stripws(format); @@ -652,13 +708,27 @@ class pattern_policy_parser : public group_policy_parser if(begin != end) { - if(*begin != ',' && *begin != ')') - throw GroupParseException(_("Expected ',' or ')' following '||', got '%s'"), + if(*begin != ',' && *begin != ')' && *begin != '{') + throw GroupParseException(_("Expected '{', ')', or ',' following '||', got '%s'"), string(begin, begin + 1).c_str()); } } - subgroups.push_back(pkg_grouppolicy_matchers_factory::match_pair(matcher.release(), cw::util::transcode(format), passthrough)); + // A brace-delimited list gives the sub-policy for this + // particular branch. + if(begin != end && *begin == '{') + { + ++begin; + + list_policy_parser subpolicy_tail_parser(parse_types, '}'); + + std::auto_ptr + sub_parse_node(subpolicy_tail_parser.parse(begin, end)); + + chain.reset(sub_parse_node->instantiate(NULL)); + } + + subgroups.push_back(pkg_grouppolicy_matchers_factory::match_entry(matcher.release(), chain.release(), cw::util::transcode(format), passthrough)); if(begin != end && *begin == ',') ++begin; @@ -672,11 +742,11 @@ class pattern_policy_parser : public group_policy_parser ++begin; } - return new policy_node1 >(subgroups); + return new policy_node1 >(subgroups); } catch(...) { - for(vector::const_iterator i = subgroups.begin(); + for(vector::const_iterator i = subgroups.begin(); i != subgroups.end(); ++i) delete i->matcher; @@ -685,8 +755,6 @@ class pattern_policy_parser : public group_policy_parser } }; -static map parse_types; - static void init_parse_types() { static bool initted_parse_types=false; diff --git a/src/pkg_grouppolicy.cc b/src/pkg_grouppolicy.cc index fc4178e5..09bfe2aa 100644 --- a/src/pkg_grouppolicy.cc +++ b/src/pkg_grouppolicy.cc @@ -1135,7 +1135,7 @@ pkg_grouppolicy_task_factory::~pkg_grouppolicy_task_factory() class pkg_grouppolicy_matchers : public pkg_grouppolicy { public: - typedef pkg_grouppolicy_matchers_factory::match_pair match_pair; + typedef pkg_grouppolicy_matchers_factory::match_entry match_entry; struct subtree_pair { @@ -1154,7 +1154,7 @@ public: private: pkg_grouppolicy_factory *chain; pkg_grouppolicy *passthrough_policy; - const vector &subgroups; + const vector &subgroups; typedef map subtree_map; subtree_map subtrees; @@ -1242,7 +1242,7 @@ private: public: pkg_grouppolicy_matchers(pkg_grouppolicy_factory *_chain, pkg_signal *_sig, desc_signal *_desc_sig, - const vector &_subgroups) + const vector &_subgroups) :pkg_grouppolicy(_sig, _desc_sig), chain(_chain), passthrough_policy(NULL), subgroups(_subgroups) @@ -1260,17 +1260,20 @@ public: void add_package(const pkgCache::PkgIterator &pkg, pkg_subtree *root) { - for(vector::const_iterator i = subgroups.begin(); + for(vector::const_iterator i = subgroups.begin(); i != subgroups.end(); ++i) { match::pkg_match_result *res = match::get_match(i->matcher, pkg, *apt_cache_file, *apt_package_records); if(res != NULL) { + pkg_grouppolicy_factory * const local_chain = + i->chain != NULL ? i->chain : chain; + if(i->passthrough) { if(passthrough_policy == NULL) - passthrough_policy = chain->instantiate(get_sig(), - get_desc_sig()); + passthrough_policy = local_chain->instantiate(get_sig(), + get_desc_sig()); passthrough_policy->add_package(pkg, root); break; } @@ -1286,8 +1289,8 @@ public: else { pkg_subtree *tree = new pkg_subtree(title, L"", get_desc_sig()); - pkg_grouppolicy *policy = chain->instantiate(get_sig(), - get_desc_sig()); + pkg_grouppolicy *policy = local_chain->instantiate(get_sig(), + get_desc_sig()); root->add_child(tree); tree->set_num_packages_parent(root); @@ -1310,9 +1313,12 @@ pkg_grouppolicy *pkg_grouppolicy_matchers_factory :: instantiate(pkg_signal *sig pkg_grouppolicy_matchers_factory :: ~pkg_grouppolicy_matchers_factory() { - for(std::vector::const_iterator i = subgroups.begin(); + for(std::vector::const_iterator i = subgroups.begin(); i != subgroups.end(); ++i) - delete i->matcher; + { + delete i->matcher; + delete i->chain; + } delete chain; } diff --git a/src/pkg_grouppolicy.h b/src/pkg_grouppolicy.h index 055a743a..ac595604 100644 --- a/src/pkg_grouppolicy.h +++ b/src/pkg_grouppolicy.h @@ -298,21 +298,33 @@ public: virtual ~pkg_grouppolicy_task_factory(); }; -// Groups packages using the given list of matchers/tree names. Match -// results can be substituted into tree names using \N notation. +/** \brief Groups packages using the given list of matchers/tree names. + * + * Match results can be substituted into tree names using \N + * notation. Each branch of the tree can have its own policy chain; + * if none is specified, the default chain will be used. + */ class pkg_grouppolicy_matchers_factory:public pkg_grouppolicy_factory { public: - struct match_pair + struct match_entry { aptitude::matching::pkg_matcher *matcher; + /** \brief A pointer to the specialized sub-factory for + * this entry, or NULL to use the default chain. + */ + pkg_grouppolicy_factory *chain; std::wstring tree_name; bool passthrough; - match_pair(aptitude::matching::pkg_matcher *_matcher, - const std::wstring &_tree_name, - bool _passthrough) - :matcher(_matcher), tree_name(_tree_name), passthrough(_passthrough) + match_entry(aptitude::matching::pkg_matcher *_matcher, + pkg_grouppolicy_factory *_chain, + const std::wstring &_tree_name, + bool _passthrough) + : matcher(_matcher), + chain(_chain), + tree_name(_tree_name), + passthrough(_passthrough) { } }; @@ -320,10 +332,10 @@ public: private: pkg_grouppolicy_factory *chain; - std::vector subgroups; + std::vector subgroups; public: - pkg_grouppolicy_matchers_factory(const std::vector &_subgroups, + pkg_grouppolicy_matchers_factory(const std::vector &_subgroups, pkg_grouppolicy_factory *_chain) :chain(_chain), subgroups(_subgroups) { -- cgit v1.2.3