aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2008-07-03 18:32:14 +0100
committerAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2008-07-06 14:46:06 +0100
commit975ba82125af306c60f2aa47cb547f5923a103bd (patch)
tree3efd9f8971c488168d25e5a9a6281585157f3841
parente03c947815655875d93107066863995aa47de675 (diff)
downloadpaludis-975ba82125af306c60f2aa47cb547f5923a103bd.tar.gz
paludis-975ba82125af306c60f2aa47cb547f5923a103bd.tar.xz
Refactor, fix elike and user dep spec parsing.
Refactor elike and user dep spec parsing to avoid having very similar code in multiple places. Fix bug where package names ending in exactly a single hyphen couldn't be used with a version in a spec. Fixes: ticket:590
-rw-r--r--paludis/elike_package_dep_spec-fwd.hh56
-rw-r--r--paludis/elike_package_dep_spec.cc540
-rw-r--r--paludis/user_dep_spec.cc171
-rw-r--r--paludis/user_dep_spec_TEST.cc17
-rw-r--r--paludis/util/keys.hh9
5 files changed, 472 insertions, 321 deletions
diff --git a/paludis/elike_package_dep_spec-fwd.hh b/paludis/elike_package_dep_spec-fwd.hh
index 2c05fbe..8bdb614 100644
--- a/paludis/elike_package_dep_spec-fwd.hh
+++ b/paludis/elike_package_dep_spec-fwd.hh
@@ -22,8 +22,12 @@
#include <paludis/util/attributes.hh>
#include <paludis/util/options-fwd.hh>
+#include <paludis/util/keys.hh>
+#include <paludis/util/kc-fwd.hh>
#include <paludis/dep_spec-fwd.hh>
#include <paludis/package_id-fwd.hh>
+#include <paludis/version_operator-fwd.hh>
+#include <paludis/version_spec-fwd.hh>
#include <tr1/functional>
#include <iosfwd>
@@ -34,11 +38,55 @@ namespace paludis
typedef Options<ELikePackageDepSpecOption> ELikePackageDepSpecOptions;
- PackageDepSpec parse_elike_package_dep_spec(const std::string &, const ELikePackageDepSpecOptions &,
- const std::tr1::shared_ptr<const PackageID> &) PALUDIS_ATTRIBUTE((warn_unused_result)) PALUDIS_VISIBLE;
+ typedef kc::KeyedClass<
+ kc::Field<k::check_sanity, std::tr1::function<void (const std::string &)> >,
+ kc::Field<k::remove_trailing_square_bracket_if_exists, std::tr1::function<bool (std::string &, PartiallyMadePackageDepSpec &)> >,
+ kc::Field<k::remove_trailing_slot_if_exists, std::tr1::function<void (std::string &, PartiallyMadePackageDepSpec &)> >,
+ kc::Field<k::remove_trailing_repo_if_exists, std::tr1::function<void (std::string &, PartiallyMadePackageDepSpec &)> >,
+ kc::Field<k::has_version_operator, std::tr1::function<bool (const std::string &)> >,
+ kc::Field<k::get_remove_version_operator, std::tr1::function<VersionOperator (std::string &)> >,
+ kc::Field<k::get_remove_trailing_version, std::tr1::function<VersionSpec (std::string &)> >,
+ kc::Field<k::add_version_requirement, std::tr1::function<void (const VersionOperator &, const VersionSpec &, PartiallyMadePackageDepSpec &)> >,
+ kc::Field<k::add_package_requirement, std::tr1::function<void (const std::string &, PartiallyMadePackageDepSpec &)> >
+ > GenericELikePackageDepSpecParseFunctions;
- PartiallyMadePackageDepSpec partial_parse_elike_package_dep_spec(const std::string &, const ELikePackageDepSpecOptions &,
- const std::tr1::shared_ptr<const PackageID> &) PALUDIS_VISIBLE;
+ PackageDepSpec parse_generic_elike_package_dep_spec(const std::string & ss, const GenericELikePackageDepSpecParseFunctions & fns)
+ PALUDIS_ATTRIBUTE((warn_unused_result)) PALUDIS_VISIBLE;
+
+ PartiallyMadePackageDepSpec partial_parse_generic_elike_package_dep_spec(const std::string & ss,
+ const GenericELikePackageDepSpecParseFunctions & fns)
+ PALUDIS_ATTRIBUTE((warn_unused_result)) PALUDIS_VISIBLE;
+
+ PackageDepSpec parse_elike_package_dep_spec(const std::string & ss, const ELikePackageDepSpecOptions &,
+ const std::tr1::shared_ptr<const PackageID> &)
+ PALUDIS_ATTRIBUTE((warn_unused_result)) PALUDIS_VISIBLE;
+
+ PartiallyMadePackageDepSpec partial_parse_elike_package_dep_spec(const std::string & ss, const ELikePackageDepSpecOptions &,
+ const std::tr1::shared_ptr<const PackageID> &)
+ PALUDIS_ATTRIBUTE((warn_unused_result)) PALUDIS_VISIBLE;
+
+ void elike_check_sanity(const std::string & s) PALUDIS_VISIBLE;
+
+ bool elike_remove_trailing_square_bracket_if_exists(std::string & s, PartiallyMadePackageDepSpec & result,
+ const ELikePackageDepSpecOptions & options, bool & had_bracket_version_requirements,
+ const std::tr1::shared_ptr<const PackageID> & id) PALUDIS_VISIBLE;
+
+ void elike_remove_trailing_repo_if_exists(std::string & s, PartiallyMadePackageDepSpec & result,
+ const ELikePackageDepSpecOptions & options) PALUDIS_VISIBLE;
+
+ void elike_remove_trailing_slot_if_exists(std::string & s, PartiallyMadePackageDepSpec & result,
+ const ELikePackageDepSpecOptions & options) PALUDIS_VISIBLE;
+
+ bool elike_has_version_operator(const std::string & s, const bool had_bracket_version_requirements) PALUDIS_VISIBLE;
+
+ VersionOperator elike_get_remove_version_operator(std::string & s, const ELikePackageDepSpecOptions & options) PALUDIS_VISIBLE;
+
+ VersionSpec elike_get_remove_trailing_version(std::string & s) PALUDIS_VISIBLE;
+
+ void elike_add_version_requirement(const VersionOperator & op, const VersionSpec & spec, PartiallyMadePackageDepSpec & result)
+ PALUDIS_VISIBLE;
+
+ void elike_add_package_requirement(const std::string & s, PartiallyMadePackageDepSpec & result) PALUDIS_VISIBLE;
}
#endif
diff --git a/paludis/elike_package_dep_spec.cc b/paludis/elike_package_dep_spec.cc
index ed81186..9ed06e3 100644
--- a/paludis/elike_package_dep_spec.cc
+++ b/paludis/elike_package_dep_spec.cc
@@ -23,6 +23,7 @@
#include <paludis/util/options.hh>
#include <paludis/util/log.hh>
#include <paludis/util/make_shared_ptr.hh>
+#include <paludis/util/kc.hh>
#include <paludis/dep_spec.hh>
#include <paludis/version_operator.hh>
#include <paludis/version_spec.hh>
@@ -33,289 +34,398 @@ using namespace paludis;
#include <paludis/elike_package_dep_spec-se.cc>
PartiallyMadePackageDepSpec
-paludis::partial_parse_elike_package_dep_spec(
- const std::string & ss, const ELikePackageDepSpecOptions & options, const std::tr1::shared_ptr<const PackageID> & id)
+paludis::partial_parse_generic_elike_package_dep_spec(const std::string & ss, const GenericELikePackageDepSpecParseFunctions & fns)
{
- Context context("When parsing package dep spec '" + ss + "':");
+ Context context("When parsing generic package dep spec '" + ss + "':");
- if (ss.empty())
- throw PackageDepSpecError("Got empty dep spec");
+ /* Check that it's not, e.g. a set with updso_throw_if_set, or empty. */
+ fns[k::check_sanity()](ss);
- PartiallyMadePackageDepSpec result;
std::string s(ss);
- bool had_bracket_version_requirements(false);
+ PartiallyMadePackageDepSpec result;
+
+ /* Remove trailing [use], [version] etc parts. */
+ while (fns[k::remove_trailing_square_bracket_if_exists()](s, result))
+ {
+ }
+
+ /* Remove trailing ::repo and :slot parts. */
+ fns[k::remove_trailing_repo_if_exists()](s, result);
+ fns[k::remove_trailing_slot_if_exists()](s, result);
+
+ if (fns[k::has_version_operator()](s))
+ {
+ /* Leading (or maybe =*) operator, so trailing version. */
+ VersionOperator op(fns[k::get_remove_version_operator()](s));
+ VersionSpec spec(fns[k::get_remove_trailing_version()](s));
+ fns[k::add_version_requirement()](op, spec, result);
+ fns[k::add_package_requirement()](s, result);
+ }
+ else
+ {
+ /* No leading operator, so no version. */
+ fns[k::add_package_requirement()](s, result);
+ }
+ return result;
+}
+
+PackageDepSpec
+paludis::parse_generic_elike_package_dep_spec(const std::string & ss, const GenericELikePackageDepSpecParseFunctions & fns)
+{
+ return partial_parse_generic_elike_package_dep_spec(ss, fns);
+}
+
+void
+paludis::elike_check_sanity(const std::string & s)
+{
+ if (s.empty())
+ throw PackageDepSpecError("Got empty dep spec");
+}
+
+bool
+paludis::elike_remove_trailing_square_bracket_if_exists(std::string & s, PartiallyMadePackageDepSpec & result,
+ const ELikePackageDepSpecOptions & options, bool & had_bracket_version_requirements,
+ const std::tr1::shared_ptr<const PackageID> & id)
+{
std::string::size_type use_group_p;
- while (std::string::npos != ((use_group_p = s.rfind('['))))
+ if (std::string::npos == ((use_group_p = s.rfind('['))))
+ return false;
+
+ if (! options[epdso_allow_square_bracket_deps])
{
- if (! options[epdso_allow_square_bracket_deps])
- {
- if (options[epdso_strict_parsing])
- throw PackageDepSpecError("[] dependencies not safe for use here");
- else
- Log::get_instance()->message("e.package_dep_spec.brackets_not_allowed", ll_warning, lc_context)
- << "[] dependencies not safe for use here";
- }
+ if (options[epdso_strict_parsing])
+ throw PackageDepSpecError("[] dependencies not safe for use here");
+ else
+ Log::get_instance()->message("e.package_dep_spec.brackets_not_allowed", ll_warning, lc_context)
+ << "[] dependencies not safe for use here";
+ }
- if (s.at(s.length() - 1) != ']')
- throw PackageDepSpecError("Mismatched []");
+ if (s.at(s.length() - 1) != ']')
+ throw PackageDepSpecError("Mismatched []");
- std::string flag(s.substr(use_group_p + 1));
- if (flag.length() < 2)
- throw PackageDepSpecError("Invalid [] contents");
+ std::string flag(s.substr(use_group_p + 1));
+ if (flag.length() < 2)
+ throw PackageDepSpecError("Invalid [] contents");
- flag.erase(flag.length() - 1);
+ flag.erase(flag.length() - 1);
- switch (flag.at(0))
- {
- case '<':
- case '>':
- case '=':
- case '~':
+ switch (flag.at(0))
+ {
+ case '<':
+ case '>':
+ case '=':
+ case '~':
+ {
+ char needed_mode(0);
+
+ while (! flag.empty())
{
- char needed_mode(0);
+ Context cc("When parsing [] segment '" + flag + "':");
- while (! flag.empty())
- {
- Context cc("When parsing [] segment '" + flag + "':");
+ std::string op;
+ std::string::size_type opos(0);
+ while (opos < flag.length())
+ if (std::string::npos == std::string("><=~").find(flag.at(opos)))
+ break;
+ else
+ ++opos;
- std::string op;
- std::string::size_type opos(0);
- while (opos < flag.length())
- if (std::string::npos == std::string("><=~").find(flag.at(opos)))
- break;
- else
- ++opos;
+ op = flag.substr(0, opos);
+ flag.erase(0, opos);
- op = flag.substr(0, opos);
- flag.erase(0, opos);
+ if (op.empty())
+ throw PackageDepSpecError("Missing operator inside []");
- if (op.empty())
- throw PackageDepSpecError("Missing operator inside []");
+ VersionOperator vop(op);
- VersionOperator vop(op);
+ std::string ver;
+ opos = flag.find_first_of("|&");
+ if (std::string::npos == opos)
+ {
+ ver = flag;
+ flag.clear();
+ }
+ else
+ {
+ if (0 == needed_mode)
+ needed_mode = flag.at(opos);
+ else if (needed_mode != flag.at(opos))
+ throw PackageDepSpecError("Mixed & and | inside []");
- std::string ver;
- opos = flag.find_first_of("|&");
- if (std::string::npos == opos)
- {
- ver = flag;
- flag.clear();
- }
+ result.version_requirements_mode((flag.at(opos) == '|' ? vr_or : vr_and));
+ ver = flag.substr(0, opos++);
+ flag.erase(0, opos);
+ }
+
+ if (ver.empty())
+ throw PackageDepSpecError("Missing version after operator '" + stringify(vop) + " inside []");
+
+ if ('*' == ver.at(ver.length() - 1))
+ {
+ ver.erase(ver.length() - 1);
+ if (vop == vo_equal)
+ vop = vo_equal_star;
else
- {
- if (0 == needed_mode)
- needed_mode = flag.at(opos);
- else if (needed_mode != flag.at(opos))
- throw PackageDepSpecError("Mixed & and | inside []");
-
- result.version_requirements_mode((flag.at(opos) == '|' ? vr_or : vr_and));
- ver = flag.substr(0, opos++);
- flag.erase(0, opos);
- }
-
- if (ver.empty())
- throw PackageDepSpecError("Missing version after operator '" + stringify(vop) + " inside []");
-
- if ('*' == ver.at(ver.length() - 1))
- {
- ver.erase(ver.length() - 1);
- if (vop == vo_equal)
- vop = vo_equal_star;
- else
- throw PackageDepSpecError("Invalid use of * with operator '" + stringify(vop) + " inside []");
- }
-
- VersionSpec vs(ver);
- result.version_requirement(VersionRequirement(vop, vs));
- had_bracket_version_requirements = true;
+ throw PackageDepSpecError("Invalid use of * with operator '" + stringify(vop) + " inside []");
}
- }
- break;
- default:
- {
- std::tr1::shared_ptr<const AdditionalPackageDepSpecRequirement> req(parse_elike_use_requirement(flag,
- id, ELikeUseRequirementOptions() + euro_allow_self_deps));
- result.additional_requirement(req);
+ VersionSpec vs(ver);
+ result.version_requirement(VersionRequirement(vop, vs));
+ had_bracket_version_requirements = true;
}
- break;
- };
+ }
+ break;
- s.erase(use_group_p);
- }
+ default:
+ {
+ std::tr1::shared_ptr<const AdditionalPackageDepSpecRequirement> req(parse_elike_use_requirement(flag,
+ id, ELikeUseRequirementOptions() + euro_allow_self_deps));
+ result.additional_requirement(req);
+ }
+ break;
+ };
+
+ s.erase(use_group_p);
+ return true;
+}
+
+void
+paludis::elike_remove_trailing_repo_if_exists(std::string & s, PartiallyMadePackageDepSpec & result,
+ const ELikePackageDepSpecOptions & options)
+{
std::string::size_type repo_p;
- if (std::string::npos != ((repo_p = s.rfind("::"))))
- {
- if (! options[epdso_allow_repository_deps])
- {
- if (options[epdso_strict_parsing])
- throw PackageDepSpecError("Repository dependencies not safe for use here");
- else
- Log::get_instance()->message("e.package_dep_spec.repository_not_allowed", ll_warning, lc_context)
- << "Repository dependencies not safe for use here";
- }
+ if (std::string::npos == ((repo_p = s.rfind("::"))))
+ return;
- result.repository(RepositoryName(s.substr(repo_p + 2)));
- s.erase(repo_p);
+ if (! options[epdso_allow_repository_deps])
+ {
+ if (options[epdso_strict_parsing])
+ throw PackageDepSpecError("Repository dependencies not safe for use here");
+ else
+ Log::get_instance()->message("e.package_dep_spec.repository_not_allowed", ll_warning, lc_context)
+ << "Repository dependencies not safe for use here";
}
+ result.repository(RepositoryName(s.substr(repo_p + 2)));
+ s.erase(repo_p);
+}
+
+void
+paludis::elike_remove_trailing_slot_if_exists(std::string & s, PartiallyMadePackageDepSpec & result,
+ const ELikePackageDepSpecOptions & options)
+{
std::string::size_type slot_p;
- if (std::string::npos != ((slot_p = s.rfind(':'))))
- {
- std::string match(s.substr(slot_p + 1));
- if (match.empty())
- throw PackageDepSpecError("Empty slot dependency specified");
+ if (std::string::npos == ((slot_p = s.rfind(':'))))
+ return;
- if ("*" == match)
+ std::string match(s.substr(slot_p + 1));
+ if (match.empty())
+ throw PackageDepSpecError("Empty slot dependency specified");
+
+ if ("*" == match)
+ {
+ if (! options[epdso_allow_slot_star_deps])
{
- if (! options[epdso_allow_slot_star_deps])
- {
- if (options[epdso_strict_parsing])
- throw PackageDepSpecError("Slot '*' dependencies not safe for use here");
- else
- Log::get_instance()->message("e.package_dep_spec.slot_star_not_allowed", ll_warning, lc_context)
- << "Slot '*' dependencies not safe for use here";
- }
- result.slot_requirement(make_shared_ptr(new ELikeSlotAnyUnlockedRequirement));
+ if (options[epdso_strict_parsing])
+ throw PackageDepSpecError("Slot '*' dependencies not safe for use here");
+ else
+ Log::get_instance()->message("e.package_dep_spec.slot_star_not_allowed", ll_warning, lc_context)
+ << "Slot '*' dependencies not safe for use here";
}
- else if ('=' == match.at(0))
+ result.slot_requirement(make_shared_ptr(new ELikeSlotAnyUnlockedRequirement));
+ }
+ else if ('=' == match.at(0))
+ {
+ if (! options[epdso_allow_slot_equal_deps])
{
- if (! options[epdso_allow_slot_equal_deps])
- {
- if (options[epdso_strict_parsing])
- throw PackageDepSpecError("Slot '=' dependencies not safe for use here");
- else
- Log::get_instance()->message("e.package_dep_spec.slot_equals_not_allowed", ll_warning, lc_context)
- << "Slot '=' dependencies not safe for use here";
- }
-
- if (1 == match.length())
- result.slot_requirement(make_shared_ptr(new ELikeSlotAnyLockedRequirement));
+ if (options[epdso_strict_parsing])
+ throw PackageDepSpecError("Slot '=' dependencies not safe for use here");
else
- result.slot_requirement(make_shared_ptr(new ELikeSlotExactRequirement(SlotName(s.substr(slot_p + 2)), true)));
+ Log::get_instance()->message("e.package_dep_spec.slot_equals_not_allowed", ll_warning, lc_context)
+ << "Slot '=' dependencies not safe for use here";
}
+
+ if (1 == match.length())
+ result.slot_requirement(make_shared_ptr(new ELikeSlotAnyLockedRequirement));
else
+ result.slot_requirement(make_shared_ptr(new ELikeSlotExactRequirement(SlotName(s.substr(slot_p + 2)), true)));
+ }
+ else
+ {
+ if (! options[epdso_allow_slot_deps])
{
- if (! options[epdso_allow_slot_deps])
- {
- if (options[epdso_strict_parsing])
- throw PackageDepSpecError("Slot dependencies not safe for use here");
- else
- Log::get_instance()->message("e.package_dep_spec.slot_not_allowed", ll_warning, lc_context)
- << "Slot dependencies not safe for use here";
- }
- result.slot_requirement(make_shared_ptr(new ELikeSlotExactRequirement(SlotName(s.substr(slot_p + 1)), false)));
+ if (options[epdso_strict_parsing])
+ throw PackageDepSpecError("Slot dependencies not safe for use here");
+ else
+ Log::get_instance()->message("e.package_dep_spec.slot_not_allowed", ll_warning, lc_context)
+ << "Slot dependencies not safe for use here";
}
- s.erase(slot_p);
+ result.slot_requirement(make_shared_ptr(new ELikeSlotExactRequirement(SlotName(s.substr(slot_p + 1)), false)));
}
+ s.erase(slot_p);
+}
- if (std::string::npos != std::string("<>=~").find(s.at(0)))
+bool
+paludis::elike_has_version_operator(const std::string & s, const bool had_bracket_version_requirements)
+{
+ if ((! s.empty()) && std::string::npos != std::string("<>=~").find(s.at(0)))
{
if (had_bracket_version_requirements)
throw PackageDepSpecError("Cannot mix [] and traditional version specifications");
+ return true;
+ }
+ else
+ return false;
+}
- std::string::size_type p(1);
- if (s.length() > 1 && std::string::npos != std::string("<>=~").find(s.at(1)))
- ++p;
- VersionOperator op(s.substr(0, p));
+VersionOperator
+paludis::elike_get_remove_version_operator(std::string & s, const ELikePackageDepSpecOptions & options)
+{
+ std::string::size_type p(1);
+ if (s.length() > 1 && std::string::npos != std::string("<>=~").find(s.at(1)))
+ ++p;
+ VersionOperator op(s.substr(0, p));
+ s.erase(0, p);
+
+ if (op == vo_tilde_greater)
+ if (! options[epdso_allow_tilde_greater_deps])
+ {
+ if (options[epdso_strict_parsing])
+ throw PackageDepSpecError("~> dependencies not safe for use here");
+ else
+ Log::get_instance()->message("e.package_dep_spec.tilde_greater_not_allowed", ll_warning, lc_context)
+ << "~> dependencies not safe for use here";
+ }
- if (op == vo_tilde_greater)
- if (! options[epdso_allow_tilde_greater_deps])
+ if ((! s.empty()) && ('*' == s.at(s.length() - 1)))
+ {
+ if (op != vo_equal)
+ {
+ if (! options[epdso_strict_star_operator])
{
if (options[epdso_strict_parsing])
- throw PackageDepSpecError("~> dependencies not safe for use here");
+ throw PackageDepSpecError("Package dep spec uses * with operator '" + stringify(op) + "'");
else
- Log::get_instance()->message("e.package_dep_spec.tilde_greater_not_allowed", ll_warning, lc_context)
- << "~> dependencies not safe for use here";
+ Log::get_instance()->message("e.package_dep_spec.bad_operator", ll_qa, lc_context)
+ << "Package dep spec uses * with operator '" << op << "', pretending it uses the equals operator instead";
}
+ }
+ op = vo_equal_star;
+ s.erase(s.length() - 1);
+ }
- std::string::size_type q(p);
+ return op;
+}
- while (true)
+VersionSpec
+paludis::elike_get_remove_trailing_version(std::string & s)
+{
+ /* find the last place a version spec could start (that is, a hyphen
+ * followed by a digit, or a hyphen followed by 'scm'). if it's the scm
+ * thing, find the second last place instead, if it exists. */
+ std::string::size_type hyphen_pos(s.rfind('-')), last_hyphen_pos(std::string::npos);
+ while (true)
+ {
+ if (std::string::npos == hyphen_pos || 0 == hyphen_pos)
{
- if (p >= s.length())
- throw PackageDepSpecError("Couldn't parse dep spec '" + ss + "'");
- q = s.find('-', q + 1);
- if ((std::string::npos == q) || (++q >= s.length()))
- throw PackageDepSpecError("Couldn't parse dep spec '" + ss + "'");
- if ((s.at(q) >= '0' && s.at(q) <= '9') || (0 == s.compare(q, 3, "scm")))
+ /* - at start or no - is an error. but if we've already found a
+ * trailing -scm, use that. */
+ if (std::string::npos != last_hyphen_pos)
+ {
+ hyphen_pos = last_hyphen_pos;
break;
+ }
+ else
+ throw PackageDepSpecError("No version found");
}
- std::string::size_type new_q(q);
- while (true)
+ /* make sure we've got room for the match */
+ if (! (hyphen_pos + 1 >= s.length()))
{
- if (new_q >= s.length())
- break;
- new_q = s.find('-', new_q + 1);
- if ((std::string::npos == new_q) || (++new_q >= s.length()))
+ if (std::string::npos != std::string("0123456789").find(s.at(hyphen_pos + 1)))
+ {
+ /* can't have an -scm before this */
break;
- if (s.at(new_q) >= '0' && s.at(new_q) <= '9')
- q = new_q;
- }
-
- std::string t(s.substr(p, q - p - 1));
- if (t.length() >= 3 && (0 == t.compare(0, 2, "*/")))
- {
- throw PackageDepSpecError("Wildcard '*' not allowed in '" + stringify(ss) + "'");
-
- if (0 != t.compare(t.length() - 2, 2, "/*"))
- result.package_name_part(PackageNamePart(t.substr(2)));
- }
- else if (t.length() >= 3 && (0 == t.compare(t.length() - 2, 2, "/*")))
- {
- throw PackageDepSpecError("Wildcard '*' not allowed in '" + stringify(ss) + "'");
-
- result.category_name_part(CategoryNamePart(t.substr(0, t.length() - 2)));
- }
- else
- result.package(QualifiedPackageName(t));
-
- if ('*' == s.at(s.length() - 1))
- {
- if (op != vo_equal)
+ }
+ else if (0 == s.compare(hyphen_pos + 1, 3, "scm"))
{
- if (! options[epdso_strict_star_operator])
+ if (std::string::npos == last_hyphen_pos)
{
- if (options[epdso_strict_parsing])
- throw PackageDepSpecError(
- "Package dep spec '" + ss + "' uses * "
- "with operator '" + stringify(op) + "'");
- else
- Log::get_instance()->message("e.package_dep_spec.bad_operator", ll_qa, lc_context)
- << "Package dep spec '" << ss << "' uses * with operator '" << op <<
- "', pretending it uses the equals operator instead";
+ /* we can still go back further, but we don't have to */
+ last_hyphen_pos = hyphen_pos;
+ }
+ else
+ {
+ /* -scm-scm not allowed, use our later match */
+ hyphen_pos = last_hyphen_pos;
+ break;
}
}
- op = vo_equal_star;
-
- result.version_requirement(VersionRequirement(op, VersionSpec(s.substr(q, s.length() - q - 1))));
}
- else
- result.version_requirement(VersionRequirement(op, VersionSpec(s.substr(q))));
+
+ hyphen_pos = s.rfind('-', hyphen_pos - 1);
}
- else
+
+ VersionSpec result(s.substr(hyphen_pos + 1));
+ s.erase(hyphen_pos);
+ return result;
+}
+
+void
+paludis::elike_add_version_requirement(const VersionOperator & op, const VersionSpec & spec, PartiallyMadePackageDepSpec & result)
+{
+ result.version_requirement(VersionRequirement(op, spec));
+}
+
+void
+paludis::elike_add_package_requirement(const std::string & s, PartiallyMadePackageDepSpec & result)
+{
+ if (std::string::npos == s.find('/'))
+ throw PackageDepSpecError("No category/ found in '" + s + "' (cat/pkg is required, a simple pkg is not allowed here)");
+
+ if (s.length() >= 3 && (0 == s.compare(0, 2, "*/")))
{
- if (s.length() >= 3 && (0 == s.compare(0, 2, "*/")))
- {
- throw PackageDepSpecError("Wildcard '*' not allowed in '" + stringify(ss) + "'");
+ throw PackageDepSpecError("Wildcard '*' not allowed here");
- if (0 != s.compare(s.length() - 2, 2, "/*"))
- result.package_name_part(PackageNamePart(s.substr(2)));
- }
- else if (s.length() >= 3 && (0 == s.compare(s.length() - 2, 2, "/*")))
- {
- throw PackageDepSpecError("Wildcard '*' not allowed in '" + stringify(ss) + "'");
+ if (0 != s.compare(s.length() - 2, 2, "/*"))
+ result.package_name_part(PackageNamePart(s.substr(2)));
+ }
+ else if (s.length() >= 3 && (0 == s.compare(s.length() - 2, 2, "/*")))
+ {
+ throw PackageDepSpecError("Wildcard '*' not allowed here");
- result.category_name_part(CategoryNamePart(s.substr(0, s.length() - 2)));
- }
- else
- result.package(QualifiedPackageName(s));
+ result.category_name_part(CategoryNamePart(s.substr(0, s.length() - 2)));
}
+ else
+ result.package(QualifiedPackageName(s));
+}
- return result;
+PartiallyMadePackageDepSpec
+paludis::partial_parse_elike_package_dep_spec(
+ const std::string & ss, const ELikePackageDepSpecOptions & options, const std::tr1::shared_ptr<const PackageID> & id)
+{
+ using namespace std::tr1::placeholders;
+
+ Context context("When parsing elike package dep spec '" + ss + "':");
+
+ bool had_bracket_version_requirements(false);
+
+ return partial_parse_generic_elike_package_dep_spec(ss, GenericELikePackageDepSpecParseFunctions::named_create()
+ (k::check_sanity(), &elike_check_sanity)
+ (k::remove_trailing_square_bracket_if_exists(), std::tr1::bind(&elike_remove_trailing_square_bracket_if_exists,
+ _1, _2, options, std::tr1::ref(had_bracket_version_requirements), id))
+ (k::remove_trailing_repo_if_exists(), std::tr1::bind(&elike_remove_trailing_repo_if_exists,
+ _1, _2, options))
+ (k::remove_trailing_slot_if_exists(), std::tr1::bind(&elike_remove_trailing_slot_if_exists,
+ _1, _2, options))
+ (k::has_version_operator(), std::tr1::bind(&elike_has_version_operator, _1, std::tr1::cref(had_bracket_version_requirements)))
+ (k::get_remove_version_operator(), std::tr1::bind(&elike_get_remove_version_operator, _1, options))
+ (k::get_remove_trailing_version(), std::tr1::bind(&elike_get_remove_trailing_version, _1))
+ (k::add_version_requirement(), std::tr1::bind(&elike_add_version_requirement, _1, _2, _3))
+ (k::add_package_requirement(), std::tr1::bind(&elike_add_package_requirement, _1, _2))
+ );
}
PackageDepSpec
diff --git a/paludis/user_dep_spec.cc b/paludis/user_dep_spec.cc
index d2c1c49..6879c8a 100644
--- a/paludis/user_dep_spec.cc
+++ b/paludis/user_dep_spec.cc
@@ -18,6 +18,7 @@
*/
#include <paludis/user_dep_spec.hh>
+#include <paludis/elike_package_dep_spec.hh>
#include <paludis/environment.hh>
#include <paludis/elike_use_requirement.hh>
#include <paludis/version_operator.hh>
@@ -36,65 +37,62 @@ using namespace paludis;
namespace
{
- void parse_package_bit(PartiallyMadePackageDepSpec & result, const std::string & ss, const std::string & t,
+ void user_add_package_requirement(const std::string & s, PartiallyMadePackageDepSpec & result,
const Environment * const env, const UserPackageDepSpecOptions & options,
const Filter & filter)
{
- if (t.length() >= 3 && (0 == t.compare(0, 2, "*/")))
+ if (s.length() >= 3 && (0 == s.compare(0, 2, "*/")))
{
if (! options[updso_allow_wildcards])
- throw PackageDepSpecError("Wildcard '*' not allowed in '" + stringify(ss) + "'");
+ throw PackageDepSpecError("Wildcard '*' not allowed");
- if (0 != t.compare(t.length() - 2, 2, "/*"))
- result.package_name_part(PackageNamePart(t.substr(2)));
+ if (0 != s.compare(s.length() - 2, 2, "/*"))
+ result.package_name_part(PackageNamePart(s.substr(2)));
}
- else if (t.length() >= 3 && (0 == t.compare(t.length() - 2, 2, "/*")))
+ else if (s.length() >= 3 && (0 == s.compare(s.length() - 2, 2, "/*")))
{
if (! options[updso_allow_wildcards])
- throw PackageDepSpecError("Wildcard '*' not allowed in '" + stringify(ss) + "'");
+ throw PackageDepSpecError("Wildcard '*' not allowed in '" + stringify(s) + "'");
- result.category_name_part(CategoryNamePart(t.substr(0, t.length() - 2)));
+ result.category_name_part(CategoryNamePart(s.substr(0, s.length() - 2)));
}
- else if (t == "*")
- throw PackageDepSpecError("Use '*/*' not '*' to match everything in '" + stringify(ss) + "'");
- else if (std::string::npos != t.find('/'))
- result.package(QualifiedPackageName(t));
+ else if (s == "*")
+ throw PackageDepSpecError("Use '*/*' not '*' to match everything");
+ else if (std::string::npos != s.find('/'))
+ result.package(QualifiedPackageName(s));
else
{
if (options[updso_no_disambiguation])
- throw PackageDepSpecError("Need an explicit category specified in '" + stringify(ss) + "'");
- result.package(env->package_database()->fetch_unique_qualified_package_name(PackageNamePart(t), filter));
+ throw PackageDepSpecError("Need an explicit category specified");
+ result.package(env->package_database()->fetch_unique_qualified_package_name(PackageNamePart(s), filter));
}
}
-}
-
-PackageDepSpec
-paludis::parse_user_package_dep_spec(const std::string & ss, const Environment * const env,
- const UserPackageDepSpecOptions & options, const Filter & filter)
-{
- Context context("When parsing package dep spec '" + ss + "':");
- if (ss.empty())
- throw PackageDepSpecError("Got empty dep spec");
-
- if (options[updso_throw_if_set] && std::string::npos == ss.find_first_of("/:[<>=~"))
- try
- {
- SetName sn(ss);
- if (options[updso_no_disambiguation] || env->set(sn))
- throw GotASetNotAPackageDepSpec(ss);
- }
- catch (const SetNameError &)
- {
- }
-
- std::string s(ss);
- PartiallyMadePackageDepSpec result;
- bool had_bracket_version_requirements(false);
+ void user_check_sanity(const std::string & s, const UserPackageDepSpecOptions & options,
+ const Environment * const env)
+ {
+ if (s.empty())
+ throw PackageDepSpecError("Got empty dep spec");
+
+ if (options[updso_throw_if_set] && std::string::npos == s.find_first_of("/:[<>=~"))
+ try
+ {
+ SetName sn(s);
+ if (options[updso_no_disambiguation] || env->set(sn))
+ throw GotASetNotAPackageDepSpec(s);
+ }
+ catch (const SetNameError &)
+ {
+ }
+ }
- std::string::size_type use_group_p;
- while (std::string::npos != ((use_group_p = s.rfind('['))))
+ bool user_remove_trailing_square_bracket_if_exists(std::string & s, PartiallyMadePackageDepSpec & result,
+ bool & had_bracket_version_requirements)
{
+ std::string::size_type use_group_p;
+ if (std::string::npos == ((use_group_p = s.rfind('['))))
+ return false;
+
if (s.at(s.length() - 1) != ']')
throw PackageDepSpecError("Mismatched []");
@@ -147,7 +145,7 @@ paludis::parse_user_package_dep_spec(const std::string & ss, const Environment *
else if (needed_mode != flag.at(opos))
throw PackageDepSpecError("Mixed & and | inside []");
- result.version_requirements_mode(flag.at(opos) == '|' ? vr_or : vr_and);
+ result.version_requirements_mode((flag.at(opos) == '|' ? vr_or : vr_and));
ver = flag.substr(0, opos++);
flag.erase(0, opos);
}
@@ -181,76 +179,45 @@ paludis::parse_user_package_dep_spec(const std::string & ss, const Environment *
};
s.erase(use_group_p);
- }
- std::string::size_type repo_p;
- if (std::string::npos != ((repo_p = s.rfind("::"))))
- {
- result.repository(RepositoryName(s.substr(repo_p + 2)));
- s.erase(repo_p);
- }
-
- std::string::size_type slot_p;
- if (std::string::npos != ((slot_p = s.rfind(':'))))
- {
- result.slot_requirement(make_shared_ptr(new UserSlotExactRequirement(SlotName(s.substr(slot_p + 1)))));
- s.erase(slot_p);
+ return true;
}
- if (std::string::npos != std::string("<>=~").find(s.at(0)))
+ void
+ user_remove_trailing_slot_if_exists(std::string & s, PartiallyMadePackageDepSpec & result)
{
- if (had_bracket_version_requirements)
- throw PackageDepSpecError("Cannot mix [] and traditional version specifications");
-
- std::string::size_type p(1);
- if (s.length() > 1 && std::string::npos != std::string("<>=~").find(s.at(1)))
- ++p;
- VersionOperator op(s.substr(0, p));
- std::string::size_type q(p);
-
- while (true)
- {
- if (p >= s.length())
- throw PackageDepSpecError("Couldn't parse dep spec '" + ss + "'");
- q = s.find('-', q + 1);
- if ((std::string::npos == q) || (++q >= s.length()))
- throw PackageDepSpecError("Couldn't parse dep spec '" + ss + "'");
- if ((s.at(q) >= '0' && s.at(q) <= '9') || (0 == s.compare(q, 3, "scm")))
- break;
- }
-
- std::string::size_type new_q(q);
- while (true)
+ std::string::size_type slot_p(s.rfind(':'));
+ if (std::string::npos != slot_p)
{
- if (new_q >= s.length())
- break;
- new_q = s.find('-', new_q + 1);
- if ((std::string::npos == new_q) || (++new_q >= s.length()))
- break;
- if (s.at(new_q) >= '0' && s.at(new_q) <= '9')
- q = new_q;
+ result.slot_requirement(make_shared_ptr(new UserSlotExactRequirement(SlotName(s.substr(slot_p + 1)))));
+ s.erase(slot_p);
}
+ }
+}
- std::string t(s.substr(p, q - p - 1));
- parse_package_bit(result, ss, t, env, options, filter);
-
- if ('*' == s.at(s.length() - 1))
- {
- if (op != vo_equal)
- Log::get_instance()->message("user_dep_spec.bad_operator", ll_qa, lc_context) <<
- "Package dep spec '" << ss << "' uses * "
- "with operator '" << op << "', pretending it uses the equals operator instead";
- op = vo_equal_star;
+PackageDepSpec
+paludis::parse_user_package_dep_spec(const std::string & ss, const Environment * const env,
+ const UserPackageDepSpecOptions & options, const Filter & filter)
+{
+ using namespace std::tr1::placeholders;
- result.version_requirement(VersionRequirement(op, VersionSpec(s.substr(q, s.length() - q - 1))));
- }
- else
- result.version_requirement(VersionRequirement(op, VersionSpec(s.substr(q))));
- }
- else
- parse_package_bit(result, ss, s, env, options, filter);
+ Context context("When parsing user package dep spec '" + ss + "':");
- return result;
+ bool had_bracket_version_requirements(false);
+ return partial_parse_generic_elike_package_dep_spec(ss, GenericELikePackageDepSpecParseFunctions::named_create()
+ (k::check_sanity(), std::tr1::bind(&user_check_sanity, _1, options, env))
+ (k::remove_trailing_square_bracket_if_exists(), std::tr1::bind(&user_remove_trailing_square_bracket_if_exists,
+ _1, _2, std::tr1::ref(had_bracket_version_requirements)))
+ (k::remove_trailing_repo_if_exists(), std::tr1::bind(&elike_remove_trailing_repo_if_exists,
+ _1, _2, ELikePackageDepSpecOptions() + epdso_allow_repository_deps))
+ (k::remove_trailing_slot_if_exists(), std::tr1::bind(&user_remove_trailing_slot_if_exists, _1, _2))
+ (k::has_version_operator(), std::tr1::bind(&elike_has_version_operator, _1, std::tr1::cref(had_bracket_version_requirements)))
+ (k::get_remove_version_operator(), std::tr1::bind(&elike_get_remove_version_operator, _1,
+ ELikePackageDepSpecOptions() + epdso_allow_tilde_greater_deps + epdso_strict_star_operator))
+ (k::get_remove_trailing_version(), std::tr1::bind(&elike_get_remove_trailing_version, _1))
+ (k::add_version_requirement(), std::tr1::bind(&elike_add_version_requirement, _1, _2, _3))
+ (k::add_package_requirement(), std::tr1::bind(&user_add_package_requirement, _1, _2, env, options, filter))
+ );
}
UserSlotExactRequirement::UserSlotExactRequirement(const SlotName & s) :
diff --git a/paludis/user_dep_spec_TEST.cc b/paludis/user_dep_spec_TEST.cc
index 5a06209..7642193 100644
--- a/paludis/user_dep_spec_TEST.cc
+++ b/paludis/user_dep_spec_TEST.cc
@@ -153,6 +153,23 @@ namespace test_cases
TEST_CHECK_STRINGIFY_EQUAL(next(next(m.version_requirements_ptr()->begin()))->version_spec, "1.4");
TEST_CHECK_EQUAL(next(next(m.version_requirements_ptr()->begin()))->version_operator, vo_tilde);
TEST_CHECK(! m.slot_requirement_ptr());
+
+ PackageDepSpec n(parse_user_package_dep_spec("=foo/bar--1.2.3", &env, UserPackageDepSpecOptions()));
+ TEST_CHECK_STRINGIFY_EQUAL(n, "=foo/bar--1.2.3");
+ TEST_CHECK_STRINGIFY_EQUAL(*n.package_ptr(), "foo/bar-");
+ TEST_CHECK(n.version_requirements_ptr());
+ TEST_CHECK_STRINGIFY_EQUAL(n.version_requirements_ptr()->begin()->version_spec, "1.2.3");
+ TEST_CHECK_EQUAL(n.version_requirements_ptr()->begin()->version_operator, vo_equal);
+
+ TEST_CHECK_THROWS(parse_user_package_dep_spec("=foo/bar--", &env, UserPackageDepSpecOptions()), PackageDepSpecError);
+
+ PackageDepSpec o(parse_user_package_dep_spec("=foo/bar---1.2.3", &env, UserPackageDepSpecOptions()));
+ TEST_CHECK_STRINGIFY_EQUAL(o, "=foo/bar---1.2.3");
+ TEST_CHECK_STRINGIFY_EQUAL(*o.package_ptr(), "foo/bar--");
+ TEST_CHECK(o.version_requirements_ptr());
+ TEST_CHECK_STRINGIFY_EQUAL(o.version_requirements_ptr()->begin()->version_spec, "1.2.3");
+ TEST_CHECK_EQUAL(o.version_requirements_ptr()->begin()->version_operator, vo_equal);
+
}
} test_user_package_dep_spec;
diff --git a/paludis/util/keys.hh b/paludis/util/keys.hh
index ab8a9c8..67e7d49 100644
--- a/paludis/util/keys.hh
+++ b/paludis/util/keys.hh
@@ -188,6 +188,15 @@ namespace paludis
typedef kc::Key<159> importance;
typedef kc::Key<160> keys;
typedef kc::Key<161> format;
+ typedef kc::Key<162> remove_trailing_square_bracket_if_exists;
+ typedef kc::Key<163> remove_trailing_slot_if_exists;
+ typedef kc::Key<164> has_version_operator;
+ typedef kc::Key<165> get_remove_version_operator;
+ typedef kc::Key<166> get_remove_trailing_version;
+ typedef kc::Key<167> add_version_requirement;
+ typedef kc::Key<168> add_package_requirement;
+ typedef kc::Key<169> check_sanity;
+ typedef kc::Key<170> remove_trailing_repo_if_exists;
}
}