/* vim: set sw=4 sts=4 et foldmethod=syntax : *//* * Copyright (c) 2008, 2009 Ciaran McCreesh * * This file is part of the Paludis package manager. Paludis is free software; * you can redistribute it and/or modify it under the terms of the GNU General * Public License version 2, as published by the Free Software Foundation. * * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */#include<paludis/elike_package_dep_spec.hh>#include<paludis/elike_use_requirement.hh>#include<paludis/elike_slot_requirement.hh>#include<paludis/util/options.hh>#include<paludis/util/log.hh>#include<paludis/util/make_shared_ptr.hh>#include<paludis/util/make_named_values.hh>#include<paludis/dep_spec.hh>#include<paludis/version_operator.hh>#include<paludis/version_spec.hh>#include<paludis/version_requirements.hh>#include<strings.h>usingnamespacepaludis;#include<paludis/elike_package_dep_spec-se.cc>PartiallyMadePackageDepSpecpaludis::partial_parse_generic_elike_package_dep_spec(conststd::string&ss,constGenericELikePackageDepSpecParseFunctions&fns){Contextcontext("When parsing generic package dep spec '"+ss+"':");/* Check that it's not, e.g. a set with updso_throw_if_set, or empty. */fns.check_sanity()(ss);std::strings(ss);PartiallyMadePackageDepSpecresult;/* Remove trailing [use], [version] etc parts. */while(fns.remove_trailing_square_bracket_if_exists()(s,result)){}/* Remove trailing ::repo and :slot parts. */fns.remove_trailing_repo_if_exists()(s,result);fns.remove_trailing_slot_if_exists()(s,result);if(fns.has_version_operator()(s)){/* Leading (or maybe =*) operator, so trailing version. */VersionOperatorop(fns.get_remove_version_operator()(s));VersionSpecspec(fns.get_remove_trailing_version()(s));fns.add_version_requirement()(op,spec,result);fns.add_package_requirement()(s,result);}else{/* No leading operator, so no version. */fns.add_package_requirement()(s,result);}returnresult;}PackageDepSpecpaludis::parse_generic_elike_package_dep_spec(conststd::string&ss,constGenericELikePackageDepSpecParseFunctions&fns){returnpartial_parse_generic_elike_package_dep_spec(ss,fns);}voidpaludis::elike_check_sanity(conststd::string&s){if(s.empty())throwPackageDepSpecError("Got empty dep spec");}boolpaludis::elike_remove_trailing_square_bracket_if_exists(std::string&s,PartiallyMadePackageDepSpec&result,constELikePackageDepSpecOptions&options,constVersionSpecOptions&version_options,bool&had_bracket_version_requirements,bool&had_use_requirements,conststd::tr1::shared_ptr<constPackageID>&id){std::string::size_typeuse_group_p;if(std::string::npos==((use_group_p=s.rfind('['))))returnfalse;if(s.at(s.length()-1)!=']')throwPackageDepSpecError("Mismatched []");std::stringflag(s.substr(use_group_p+1));if(flag.length()<2)throwPackageDepSpecError("Invalid [] contents");flag.erase(flag.length()-1);switch(flag.at(0)){case'<':case'>':case'=':case'~':if(!options[epdso_allow_ranged_deps]){if(options[epdso_strict_parsing])throwPackageDepSpecError("Version range dependencies not safe for use here");elseLog::get_instance()->message("e.package_dep_spec.range_not_allowed",ll_warning,lc_context)<<"Version range dependencies not safe for use here";}{charneeded_mode(0);while(!flag.empty()){Contextcc("When parsing [] segment '"+flag+"':");std::stringop;std::string::size_typeopos(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);if(op.empty())throwPackageDepSpecError("Missing operator inside []");VersionOperatorvop(op);std::stringver;opos=flag.find_first_of("|&");if(std::string::npos==opos){ver=flag;flag.clear();}else{if(0==needed_mode)needed_mode=flag.at(opos);elseif(needed_mode!=flag.at(opos))throwPackageDepSpecError("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())throwPackageDepSpecError("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;elsethrowPackageDepSpecError("Invalid use of * with operator '"+stringify(vop)+" inside []");}VersionSpecvs(ver,version_options);result.version_requirement(make_named_values<VersionRequirement>(value_for<n::version_operator>(vop),value_for<n::version_spec>(vs)));had_bracket_version_requirements=true;}}break;default:if(!options[epdso_allow_use_deps]&&!options[epdso_allow_use_deps_portage]){if(options[epdso_strict_parsing])throwPackageDepSpecError("USE dependencies not safe for use here");else{Log::get_instance()->message("e.package_dep_spec.use_not_allowed",ll_warning,lc_context)<<"USE dependencies not safe for use here";}}if(!options[epdso_allow_use_deps]&&had_use_requirements){if(options[epdso_strict_parsing])throwPackageDepSpecError("multiple sets of USE dependencies not safe for use here");else{Log::get_instance()->message("e.package_dep_spec.use_multiple_not_allowed",ll_warning,lc_context)<<"multiple sets of USE dependencies not safe for use here";}}had_use_requirements=true;ELikeUseRequirementOptionseuro;euro+=euro_allow_self_deps;if(options[epdso_allow_use_deps_portage])euro+=options[epdso_allow_use_deps]?euro_both_syntaxes:euro_portage_syntax;if(options[epdso_allow_use_dep_defaults])euro+=euro_allow_default_values;if(options[epdso_strict_parsing])euro+=euro_strict_parsing;std::tr1::shared_ptr<constAdditionalPackageDepSpecRequirement>req(parse_elike_use_requirement(flag,id,euro));result.additional_requirement(req);break;};s.erase(use_group_p);returntrue;}voidpaludis::elike_remove_trailing_repo_if_exists(std::string&s,PartiallyMadePackageDepSpec&result,constELikePackageDepSpecOptions&options){std::string::size_typerepo_p;if(std::string::npos==((repo_p=s.rfind("::"))))return;if(!options[epdso_allow_repository_deps]){if(options[epdso_strict_parsing])throwPackageDepSpecError("Repository dependencies not safe for use here");elseLog::get_instance()->message("e.package_dep_spec.repository_not_allowed",ll_warning,lc_context)<<"Repository dependencies not safe for use here";}result.in_repository(RepositoryName(s.substr(repo_p+2)));s.erase(repo_p);}voidpaludis::elike_remove_trailing_slot_if_exists(std::string&s,PartiallyMadePackageDepSpec&result,constELikePackageDepSpecOptions&options){std::string::size_typeslot_p;if(std::string::npos==((slot_p=s.rfind(':'))))return;std::stringmatch(s.substr(slot_p+1));if(match.empty())throwPackageDepSpecError("Empty slot dependency specified");if("*"==match){if(!options[epdso_allow_slot_star_deps]){if(options[epdso_strict_parsing])throwPackageDepSpecError("Slot '*' dependencies not safe for use here");elseLog::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(newELikeSlotAnyUnlockedRequirement));}elseif('='==match.at(0)){if(!options[epdso_allow_slot_equal_deps]){if(options[epdso_strict_parsing])throwPackageDepSpecError("Slot '=' dependencies not safe for use here");elseLog::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(newELikeSlotAnyLockedRequirement));elseresult.slot_requirement(make_shared_ptr(newELikeSlotExactRequirement(SlotName(s.substr(slot_p+2)),true)));}else{if(!options[epdso_allow_slot_deps]){if(options[epdso_strict_parsing])throwPackageDepSpecError("Slot dependencies not safe for use here");elseLog::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(newELikeSlotExactRequirement(SlotName(s.substr(slot_p+1)),false)));}s.erase(slot_p);}boolpaludis::elike_has_version_operator(conststd::string&s,constboolhad_bracket_version_requirements){if((!s.empty())&&std::string::npos!=std::string("<>=~").find(s.at(0))){if(had_bracket_version_requirements)throwPackageDepSpecError("Cannot mix [] and traditional version specifications");returntrue;}elsereturnfalse;}VersionOperatorpaludis::elike_get_remove_version_operator(std::string&s,constELikePackageDepSpecOptions&options){std::string::size_typep(1);if(s.length()>1&&std::string::npos!=std::string("<>=~").find(s.at(1)))++p;VersionOperatorop(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])throwPackageDepSpecError("~> dependencies not safe for use here");elseLog::get_instance()->message("e.package_dep_spec.tilde_greater_not_allowed",ll_warning,lc_context)<<"~> dependencies not safe for use here";}if((!s.empty())&&('*'==s.at(s.length()-1))){if(op!=vo_equal)throwPackageDepSpecError("Package dep spec uses * with operator '"+stringify(op)+"'");op=vo_equal_star;s.erase(s.length()-1);}returnop;}VersionSpecpaludis::elike_get_remove_trailing_version(std::string&s,constVersionSpecOptions&version_options){/* 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_typehyphen_pos(s.rfind('-')),last_hyphen_pos(std::string::npos);while(true){if(std::string::npos==hyphen_pos||0==hyphen_pos){/* - 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;}elsethrowPackageDepSpecError("No version found");}/* make sure we've got room for the match */if(!(hyphen_pos+1>=s.length())){if(std::string::npos!=std::string("0123456789").find(s.at(hyphen_pos+1))){/* can't have an -scm before this */break;}elseif(0==s.compare(hyphen_pos+1,3,"scm")||(version_options[vso_ignore_case]&&0==strncasecmp(s.c_str()+hyphen_pos+1,"scm",3))){if(std::string::npos==last_hyphen_pos){/* 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;}}}hyphen_pos=s.rfind('-',hyphen_pos-1);}VersionSpecresult(s.substr(hyphen_pos+1),version_options);s.erase(hyphen_pos);returnresult;}voidpaludis::elike_add_version_requirement(constVersionOperator&op,constVersionSpec&spec,PartiallyMadePackageDepSpec&result){result.version_requirement(make_named_values<VersionRequirement>(value_for<n::version_operator>(op),value_for<n::version_spec>(spec)));}voidpaludis::elike_add_package_requirement(conststd::string&s,PartiallyMadePackageDepSpec&result){if(std::string::npos==s.find('/'))throwPackageDepSpecError("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,"*/"))){throwPackageDepSpecError("Wildcard '*' not allowed here");if(0!=s.compare(s.length()-2,2,"/*"))result.package_name_part(PackageNamePart(s.substr(2)));}elseif(s.length()>=3&&(0==s.compare(s.length()-2,2,"/*"))){throwPackageDepSpecError("Wildcard '*' not allowed here");result.category_name_part(CategoryNamePart(s.substr(0,s.length()-2)));}elseresult.package(QualifiedPackageName(s));}PartiallyMadePackageDepSpecpaludis::partial_parse_elike_package_dep_spec(conststd::string&ss,constELikePackageDepSpecOptions&options,constVersionSpecOptions&version_options,conststd::tr1::shared_ptr<constPackageID>&id){usingnamespacestd::tr1::placeholders;Contextcontext("When parsing elike package dep spec '"+ss+"':");boolhad_bracket_version_requirements(false),had_use_requirements(false);returnpartial_parse_generic_elike_package_dep_spec(ss,make_named_values<GenericELikePackageDepSpecParseFunctions>(value_for<n::add_package_requirement>(std::tr1::bind(&elike_add_package_requirement,_1,_2)),value_for<n::add_version_requirement>(std::tr1::bind(&elike_add_version_requirement,_1,_2,_3)),value_for<n::check_sanity>(&elike_check_sanity),value_for<n::get_remove_trailing_version>(std::tr1::bind(&elike_get_remove_trailing_version,_1,version_options)),value_for<n::get_remove_version_operator>(std::tr1::bind(&elike_get_remove_version_operator,_1,options)),value_for<n::has_version_operator>(std::tr1::bind(&elike_has_version_operator,_1,std::tr1::cref(had_bracket_version_requirements))),value_for<n::remove_trailing_repo_if_exists>(std::tr1::bind(&elike_remove_trailing_repo_if_exists,_1,_2,options)),value_for<n::remove_trailing_slot_if_exists>(std::tr1::bind(&elike_remove_trailing_slot_if_exists,_1,_2,options)),value_for<n::remove_trailing_square_bracket_if_exists>(std::tr1::bind(&elike_remove_trailing_square_bracket_if_exists,_1,_2,options,version_options,std::tr1::ref(had_bracket_version_requirements),std::tr1::ref(had_use_requirements),id))));}PackageDepSpecpaludis::parse_elike_package_dep_spec(conststd::string&ss,constELikePackageDepSpecOptions&options,constVersionSpecOptions&version_options,conststd::tr1::shared_ptr<constPackageID>&id){returnpartial_parse_elike_package_dep_spec(ss,options,version_options,id);}