Exheredludis/paludis/repositories/e/pipe_command_handler.cc
2013-09-17 13:22:18 +01:00

1031 lines
42 KiB
C++

/* vim: set sw=4 sts=4 et foldmethod=syntax : */
/*
* Copyright (c) 2007, 2008, 2009, 2010, 2011, 2012, 2013 Ciaran McCreesh
* Copyright (c) 2009 Ingmar Vanhassel
* Copyright (c) 2013 Saleem Abdulrasool <compnerd@compnerd.org>
*
* 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/repositories/e/pipe_command_handler.hh>
#include <paludis/repositories/e/eapi.hh>
#include <paludis/repositories/e/fix_locked_dependencies.hh>
#include <paludis/repositories/e/dep_parser.hh>
#include <paludis/repositories/e/spec_tree_pretty_printer.hh>
#include <paludis/repositories/e/e_repository_id.hh>
#include <paludis/repositories/e/permitted_directories.hh>
#include <paludis/util/log.hh>
#include <paludis/util/join.hh>
#include <paludis/util/exception.hh>
#include <paludis/util/tokeniser.hh>
#include <paludis/util/destringify.hh>
#include <paludis/util/iterator_funcs.hh>
#include <paludis/util/sequence.hh>
#include <paludis/util/wrapped_forward_iterator.hh>
#include <paludis/util/visitor_cast.hh>
#include <paludis/util/set.hh>
#include <paludis/util/indirect_iterator-impl.hh>
#include <paludis/util/save.hh>
#include <paludis/output_manager.hh>
#include <paludis/package_id.hh>
#include <paludis/environment.hh>
#include <paludis/metadata_key.hh>
#include <paludis/selection.hh>
#include <paludis/generator.hh>
#include <paludis/filter.hh>
#include <paludis/filtered_generator.hh>
#include <paludis/choice.hh>
#include <paludis/dep_spec_annotations.hh>
#include <paludis/unformatted_pretty_printer.hh>
#include <paludis/version_spec.hh>
#include <paludis/partitioning.hh>
#include <paludis/repository.hh>
#include <paludis/elike_choices.hh>
#include <list>
#include <vector>
#include <limits>
#include <sstream>
#include <algorithm>
using namespace paludis;
namespace
{
std::string name_and_version(const PackageID & id)
{
return stringify(id.name()) + "-" + stringify(id.version());
}
struct MyOptionsRewriter
{
UnformattedPrettyPrinter f;
std::stringstream str;
std::string prefix;
const std::shared_ptr<const PackageID> id;
const std::string description_annotation;
const std::string joiner;
MyOptionsRewriter(
const std::shared_ptr<const PackageID> & i,
const std::string & d,
const std::string & j) :
id(i),
description_annotation(d),
joiner(j)
{
}
void visit(const PlainTextSpecTree::NodeType<AllDepSpec>::Type & node)
{
Save<std::string> save_prefix(&prefix);
str << "( ";
std::for_each(indirect_iterator(node.begin()), indirect_iterator(node.end()), accept_visitor(*this));
str << " ) ";
do_annotations(*node.spec(), "");
}
void visit(const PlainTextSpecTree::NodeType<ConditionalDepSpec>::Type & node)
{
Save<std::string> save_prefix(&prefix);
str << f.prettify(*node.spec()) << " ( ";
std::for_each(indirect_iterator(node.begin()), indirect_iterator(node.end()), accept_visitor(*this));
str << " ) ";
do_annotations(*node.spec(), "");
}
void visit(const PlainTextSpecTree::NodeType<PlainTextDepSpec>::Type & node)
{
str << f.prettify(*node.spec()) << " ";
do_annotations(*node.spec(), (prefix.empty() ? "" : prefix + joiner) + stringify(*node.spec()));
}
void visit(const PlainTextSpecTree::NodeType<PlainTextLabelDepSpec>::Type & node)
{
prefix = node.spec()->label();
str << f.prettify(*node.spec()) << " ";
do_annotations(*node.spec(), "");
}
void do_annotations(const DepSpec & p, const std::string & desc_from)
{
bool seen_description(false), done_brackets(false);
if (p.maybe_annotations())
for (auto m(p.maybe_annotations()->begin()), m_end(p.maybe_annotations()->end()) ;
m != m_end ; ++m)
{
switch (m->kind())
{
case dsak_literal:
case dsak_expandable:
break;
case dsak_synthetic:
case dsak_expanded:
continue;
case last_dsak:
throw InternalError(PALUDIS_HERE, "bad kind");
}
if (! done_brackets)
{
str << " [[ ";
done_brackets = true;
}
str << m->key() << " = [" << (m->value().empty() ? " " : " " + m->value() + " ") << "] ";
if (m->role() == dsar_general_description)
seen_description = true;
}
if ((! seen_description) && (id->choices_key()) && (! desc_from.empty()))
{
auto choice(id->choices_key()->parse_value()->find_by_name_with_prefix(ChoiceNameWithPrefix(desc_from)));
if (choice && (! choice->description().empty()) && (! description_annotation.empty()))
{
if (! done_brackets)
{
str << " [[ ";
done_brackets = true;
}
str << description_annotation << " = [ " << choice->description() << " ] ";
}
}
if (done_brackets)
str << "]] ";
}
};
}
std::string
paludis::erepository::pipe_command_handler(const Environment * const environment,
const std::shared_ptr<const ERepositoryID> & package_id,
const std::shared_ptr<PermittedDirectories> & maybe_permitted_directories,
const std::shared_ptr<Partitioning> & maybe_partitioning,
const std::shared_ptr<FSPathSet> & maybe_volatiles,
bool in_metadata_generation,
const std::string & s, const std::shared_ptr<OutputManager> & maybe_output_manager)
{
Context context("In ebuild pipe command handler for '" + stringify(*package_id) + "':");
try
{
std::vector<std::string> tokens;
if (std::string::npos == s.find('\2'))
tokenise_whitespace(s, std::back_inserter(tokens));
else
{
std::string t(s);
std::string::size_type p(t.find('\2'));
while (std::string::npos != p)
{
tokens.push_back(t.substr(0, p));
t.erase(0, p + 1);
p = t.find('\2');
}
}
if (tokens.empty())
{
Log::get_instance()->message("e.pipe_commands.empty", ll_warning, lc_context) << "Got empty pipe command";
return "Eempty pipe command";
}
if (tokens[0] == "PING")
{
if (tokens.size() != 3)
{
Log::get_instance()->message("e.pipe_commands.ping.bad", ll_warning, lc_context) << "Got bad PING command, tokens are { '"
<< join(tokens.begin(), tokens.end(), "', '") << "' }";
return "Ebad PING command";
}
else
return "OPONG " + tokens[2];
}
else if (tokens[0] == "LOG")
{
if (tokens.size() < 4)
{
Log::get_instance()->message("e.pipe_commands.log.bad", ll_warning, lc_context) << "Got too short LOG pipe command";
return "Ebad LOG command";
}
else
{
if (tokens[2] == "status")
{
if (maybe_output_manager)
maybe_output_manager->message(mt_status, join(next(next(next(tokens.begin()))), tokens.end(), " "));
return "O0;";
}
else
{
Log::get_instance()->message("e.child.message", destringify<LogLevel>(tokens[2]), lc_context)
<< join(next(next(next(tokens.begin()))), tokens.end(), " ");
return "O0;";
}
}
}
else if (tokens[0] == "KEEP_LOGS")
{
if (maybe_output_manager)
maybe_output_manager->ignore_succeeded();
return "O0;";
}
else if (tokens[0] == "SET_SCM_REVISION")
{
if (tokens.size() != 3)
{
Log::get_instance()->message("e.pipe_commands.set_scm_revision.bad", ll_warning, lc_context) << "Got too short SET_SCM_REVISION pipe command";
return "Ebad SET_SCM_REVISION command";
}
else
{
try
{
package_id->set_scm_revision(tokens[2]);
return "O0;";
}
catch (const Exception & e)
{
return "Egot error '" + e.message() + "' (" + e.what() + ") when trying to SET_SCM_REVISION";
}
}
}
else if (tokens[0] == "MESSAGE")
{
if (tokens.size() == 3)
{
/* don't barf on empty messages */
tokens.push_back(" ");
}
if (tokens.size() < 4)
{
Log::get_instance()->message("e.pipe_commands.message.bad", ll_warning, lc_context) << "Got bad MESSAGE pipe command";
return "Ebad MESSAGE command";
}
else
{
MessageType m;
if (tokens[2] == "einfo" || tokens[2] == "einfon" || tokens[2] == "ebegin")
m = mt_info;
else if (tokens[2] == "ewarn")
m = mt_warn;
else if (tokens[2] == "eerror")
m = mt_error;
else if (tokens[2] == "elog")
m = mt_log;
else
return "EUnknown message type " + tokens[2] + "";
if (maybe_output_manager)
maybe_output_manager->message(m, join(next(next(next(tokens.begin()))), tokens.end(), " "));
return "O0;";
}
}
else if (tokens[0] == "BEST_VERSION")
{
if (tokens.size() != 4)
{
Log::get_instance()->message("e.pipe_commands.best_version.bad", ll_warning, lc_context) << "Got bad BEST_VERSION pipe command";
return "Ebad BEST_VERSION command";
}
else
{
std::shared_ptr<const EAPI> eapi(EAPIData::get_instance()->eapi_from_string(tokens[1]));
if (! eapi->supported())
return "EBEST_VERSION EAPI " + tokens[1] + " unsupported";
Filter root((filter::All()));
if (tokens[2] == "--slash")
root = filter::InstalledAtRoot(environment->system_root_key()->parse_value());
else if (tokens[2] == "--root")
root = filter::InstalledAtRoot(environment->preferred_root_key()->parse_value());
else
return "Ebad BEST_VERSION " + tokens[2] + " argument";
PackageDepSpec spec(parse_elike_package_dep_spec(tokens[3],
eapi->supported()->package_dep_spec_parse_options(),
eapi->supported()->version_spec_options()));
std::shared_ptr<const PackageIDSequence> entries((*environment)[selection::AllVersionsSorted(
generator::Matches(spec, package_id, { }) | root)]);
if (entries->empty())
return "O1;";
else
{
if (eapi->supported()->pipe_commands()->no_slot_or_repo())
return "O0;" + name_and_version(**entries->last());
else
return "O0;" + stringify(**entries->last());
}
}
}
else if (tokens[0] == "HAS_VERSION")
{
if (tokens.size() != 4)
{
Log::get_instance()->message("e.pipe_commands.has_version.bad", ll_warning, lc_context) << "Got bad HAS_VERSION pipe command";
return "Ebad HAS_VERSION command";
}
else
{
std::shared_ptr<const EAPI> eapi(EAPIData::get_instance()->eapi_from_string(tokens[1]));
if (! eapi->supported())
return "EHAS_VERSION EAPI " + tokens[1] + " unsupported";
Filter root((filter::All()));
if (tokens[2] == "--slash")
root = filter::InstalledAtRoot(environment->system_root_key()->parse_value());
else if (tokens[2] == "--root")
root = filter::InstalledAtRoot(environment->preferred_root_key()->parse_value());
else
return "Ebad HAS_VERSION " + tokens[2] + " argument";
PackageDepSpec spec(parse_elike_package_dep_spec(tokens[3],
eapi->supported()->package_dep_spec_parse_options(),
eapi->supported()->version_spec_options()));
std::shared_ptr<const PackageIDSequence> entries((*environment)[selection::SomeArbitraryVersion(
generator::Matches(spec, package_id, { }) | root)]);
if (entries->empty())
return "O1;";
else
return "O0;";
}
}
else if (tokens[0] == "MATCH")
{
if (tokens.size() != 3)
{
Log::get_instance()->message("e.pipe_commands.match.bad", ll_warning, lc_context) << "Got bad MATCH pipe command";
return "Ebad MATCH command";
}
else
{
std::shared_ptr<const EAPI> eapi(EAPIData::get_instance()->eapi_from_string(tokens[1]));
if (! eapi->supported())
return "EMATCH EAPI " + tokens[1] + " unsupported";
PackageDepSpec spec(parse_elike_package_dep_spec(tokens[2],
eapi->supported()->package_dep_spec_parse_options(),
eapi->supported()->version_spec_options()));
std::shared_ptr<const PackageIDSequence> entries((*environment)[selection::AllVersionsSorted(
generator::Matches(spec, package_id, { }) | filter::InstalledAtRoot(environment->preferred_root_key()->parse_value()))]);
if (entries->empty())
return "O1;";
else
{
if (eapi->supported()->pipe_commands()->no_slot_or_repo())
return "O0;" + join(indirect_iterator(entries->begin()), indirect_iterator(entries->end()), "\n", &name_and_version);
else
return "O0;" + join(indirect_iterator(entries->begin()), indirect_iterator(entries->end()), "\n");
}
}
}
else if (tokens[0] == "VDB_PATH")
{
if (tokens.size() != 2)
{
Log::get_instance()->message("e.pipe_commands.vdb_path.bad", ll_warning, lc_context) << "Got bad VDB_PATH pipe command";
return "Ebad VDB_PATH command";
}
else
{
if (! environment->has_repository_named(RepositoryName("installed")))
return "Eno installed repository available";
std::shared_ptr<const Repository> repo(environment->fetch_repository(RepositoryName("installed")));
Repository::MetadataConstIterator key(repo->find_metadata("location"));
if (repo->end_metadata() == key)
return "Einstalled repository has no location key";
if (! visitor_cast<const MetadataValueKey<FSPath> >(**key))
return "Einstalled repository location key is not a MetadataValueKey<FSPath> ";
return "O0;" + stringify(visitor_cast<const MetadataValueKey<FSPath> >(**key)->parse_value());
}
}
else if (tokens[0] == "OPTIONQ")
{
if (tokens.size() != 3)
{
Log::get_instance()->message("e.pipe_commands.has_version.bad", ll_warning, lc_context) << "Got bad OPTIONQ pipe command";
return "Ebad OPTIONQ command";
}
else
{
std::shared_ptr<const EAPI> eapi(EAPIData::get_instance()->eapi_from_string(tokens[1]));
if (! eapi->supported())
return "EOPTIONQ EAPI " + tokens[1] + " unsupported";
if (in_metadata_generation)
return "Ecannot query options during metadata generation";
if (! package_id->choices_key())
return "EOPTIONQ ID " + stringify(*package_id) + " has no choices";
ChoiceNameWithPrefix name(tokens[2]);
auto choices(package_id->choices_key()->parse_value());
auto value(choices->find_by_name_with_prefix(name));
if (! value)
{
if (choices->has_matching_contains_every_value_prefix(name))
return "O1;";
return "EOPTIONQ ID " + stringify(*package_id) + " has no choice named '" + stringify(name) + "'";
}
if (co_explicit != value->origin())
return "Ecannot query option '" + stringify(name) + "' for ID " + stringify(*package_id);
if (value->enabled())
return "O0;";
else
return "O1;";
}
}
else if (tokens[0] == "EXPECTING_TESTS")
{
if (tokens.size() != 3)
{
Log::get_instance()->message("e.pipe_commands.expecting_tests.bad", ll_warning, lc_context) << "Got bad EXPECTING_TESTS pipe command";
return "Ebad EXPECTING_TESTS command";
}
else
{
std::shared_ptr<const EAPI> eapi(EAPIData::get_instance()->eapi_from_string(tokens[1]));
if (! eapi->supported())
return "EEXPECTING_TESTS EAPI " + tokens[1] + " unsupported";
if (in_metadata_generation)
return "Ecannot query expecting tests during metadata generation";
if (! package_id->choices_key())
return "EEXPECTING_TESTS ID " + stringify(*package_id) + " has no choices";
bool expensive(true), recommended(true), optional(true), any(true);
if (tokens[2].empty() || tokens[2] == "--any")
{
}
else if (tokens[2] == "--expensive")
any = recommended = optional = false;
else if (tokens[2] == "--recommended")
any = expensive = optional = false;
else if (tokens[2] == "--optional")
any = recommended = expensive = false;
auto choices(package_id->choices_key()->parse_value());
if (optional)
{
const auto name(ELikeOptionalTestsChoiceValue::canonical_name_with_prefix());
const auto value(choices->find_by_name_with_prefix(name));
if (value && value->enabled())
return "O0;";
if ((! value) && (! any) && (! choices->has_matching_contains_every_value_prefix(name)))
return "EOPTIONQ ID " + stringify(*package_id) + " has no choice named '" + stringify(name) + "'";
}
if (recommended)
{
const auto name(ELikeRecommendedTestsChoiceValue::canonical_name_with_prefix());
const auto value(choices->find_by_name_with_prefix(name));
if (value && value->enabled())
return "O0;";
if ((! value) && (! any) && (! choices->has_matching_contains_every_value_prefix(name)))
return "EOPTIONQ ID " + stringify(*package_id) + " has no choice named '" + stringify(name) + "'";
}
if (expensive)
{
const auto name(ELikeExpensiveTestsChoiceValue::canonical_name_with_prefix());
const auto value(choices->find_by_name_with_prefix(name));
if (value && value->enabled())
return "O0;";
if ((! value) && (! any) && (! choices->has_matching_contains_every_value_prefix(name)))
return "EOPTIONQ ID " + stringify(*package_id) + " has no choice named '" + stringify(name) + "'";
}
return "O1;";
}
}
else if (tokens[0] == "PERMIT_DIRECTORY")
{
if (tokens.size() != 4)
{
Log::get_instance()->message("e.pipe_commands.permit_directory.bad", ll_warning, lc_context) << "Got bad PERMIT_DIRECTORY pipe command";
return "Ebad PERMIT_DIRECTORY command";
}
bool permit(true);
if (tokens[2] == "--allow")
permit = true;
else if (tokens[2] == "--forbid")
permit = false;
else
{
Log::get_instance()->message("e.pipe_commands.permit_directory.bad_argument", ll_warning, lc_context)
<< "Got bad PERMIT_DIRECTORY pipe command argument";
return "Ebad PERMIT_DIRECTORY command argument";
}
if (! maybe_permitted_directories)
{
Log::get_instance()->message("e.pipe_commands.permit_directory.not_allowed", ll_warning, lc_context)
<< "Got bad PERMIT_DIRECTORY pipe command: not allowed here";
return "Ebad PERMIT_DIRECTORY command: not allowed here";
}
if (0 != tokens[3].compare(0, 1, "/", 0, 1))
{
Log::get_instance()->message("e.pipe_commands.permit_directory.bad_argument", ll_warning, lc_context)
<< "Got bad PERMIT_DIRECTORY pipe command argument";
return "Ebad PERMIT_DIRECTORY command argument";
}
maybe_permitted_directories->add(FSPath(tokens[3]), permit);
return "O0;";
}
else if (tokens[0] == "PARTITION")
{
bool exclude(false);
auto eapi = EAPIData::get_instance()->eapi_from_string(tokens[1]);
if (! eapi->supported())
return "EPARTITION EAPI " + tokens[1] + " unsupported";
if (tokens.size() < 4 ||
((exclude = (tokens[2] == "--exclude")) && tokens.size() < 5))
{
Log::get_instance()->message("e.pipe_commands.partition.bad",
ll_warning, lc_context)
<< "Got bad PARTITION pipe command";
return "Ebad PARTITION command";
}
const std::string partition_id(tokens[2]);
std::vector<FSPath> contents;
for (auto token = tokens.begin() + (exclude ? 3 : 2),
last = tokens.end();
token != last; ++token)
contents.push_back(FSPath(*token));
try
{
if (! maybe_partitioning)
{
Log::get_instance()->message("e.pipe_commands.partitioning.unsupported",
ll_warning, lc_context)
<< "parts not supported by destination repository";
return "EPARTITION EXPART not supported by destination repository";
}
if (! package_id->choices_key())
return "EPARTITION ID " + stringify(*package_id) + " has no choices";
if (! exclude)
{
bool specified(false);
auto prefix = package_id->eapi()->supported()->parts_prefix();
auto choices = package_id->choices_key()->parse_value();
auto parts = choices->find(ChoicePrefixName(prefix->value()));
if (parts == choices->end())
return "EPARTITION ID " + stringify(*package_id) + " has no partitions";
for (const auto & part : **parts)
if ((specified = part->unprefixed_name().value() == partition_id))
break;
if (! specified)
{
Log::get_instance()->message("e.pipe_commands.partitioning.invalid_package",
ll_warning, lc_context)
<< "'" << partition_id << "' is not specified by the package as a partition identifier";
return "EPARTITION ID " + stringify(*package_id) + " has no partition named '" + partition_id + "'";
}
}
maybe_partitioning->mark(contents,
exclude ? PartName("")
: PartName(partition_id));
return "O0;";
}
catch (const Exception & e)
{
return "Egot error '" + e.message() + "' (" + e.what() + ") "
"when trying to create partition";
}
}
else if (tokens[0] == "VOLATILE")
{
auto eapi = EAPIData::get_instance()->eapi_from_string(tokens[1]);
if (! eapi->supported())
return "EPARTITION EAPI " + tokens[1] + " unsupported";
if (tokens.size() != 3)
{
Log::get_instance()->message("e.pipe_commands.volatile.bad", ll_warning, lc_context)
<< "Got bad VOLATILE pipe command";
return "Ebad VOLATILE command";
}
try
{
if (! maybe_volatiles)
{
Log::get_instance()->message("e.pipe_commands.partitioning.unsupported", ll_warning, lc_context)
<< "volatile files not supported here";
return "EVOLATILE not supported here";
}
if (0 != tokens[2].compare(0, 1, "/", 0, 1)) {
Log::get_instance()->message("e.pipe_commands.partitioning.bad", ll_warning, lc_context)
<< "Badly formatted volatile path '" + tokens[2] + "'";
return "EVOLATILE path '" + tokens[2] + "' is not an absolute path";
}
maybe_volatiles->insert(FSPath(tokens[2]));
return "O0;";
}
catch (const Exception & e)
{
return "Egot error '" + e.message() + "' (" + e.what() + ") "
"when trying to create volatile";
}
}
else if (tokens[0] == "REWRITE_VAR")
{
if (tokens.size() < 4)
{
Log::get_instance()->message("e.pipe_commands.rewrite_var.bad", ll_warning, lc_context) << "Got bad REWRITE_VAR pipe command";
return "Ebad REWRITE_VAR command";
}
std::shared_ptr<const EAPI> eapi(EAPIData::get_instance()->eapi_from_string(tokens[1]));
if (! eapi->supported())
return "EREWRITE_VAR EAPI " + tokens[1] + " unsupported";
std::string var(tokens[3]);
if ((var == eapi->supported()->ebuild_metadata_variables()->build_depend()->name()) ||
(var == eapi->supported()->ebuild_metadata_variables()->run_depend()->name()) ||
(var == eapi->supported()->ebuild_metadata_variables()->pdepend()->name()) ||
(var == eapi->supported()->ebuild_metadata_variables()->dependencies()->name()))
{
/* use the value in the metadata key, since RDEPEND in the
* ebuild env isn't modified for DEPEND=RDEPEND */
PackageID::MetadataConstIterator m(package_id->find_metadata(var));
if (m == package_id->end_metadata())
{
/* no key is the same as empty */
return "O0;";
}
const MetadataSpecTreeKey<DependencySpecTree> * mm(
visitor_cast<const MetadataSpecTreeKey<DependencySpecTree> >(**m));
if (! mm)
throw InternalError(PALUDIS_HERE, "oops. key '" + var + "' isn't a DependencySpecTree key");
std::shared_ptr<const DependencySpecTree> before(mm->parse_value());
std::shared_ptr<const DependencySpecTree> after(fix_locked_dependencies(environment, *eapi, package_id, before));
UnformattedPrettyPrinter ff;
SpecTreePrettyPrinter p(ff, { });
after->top()->accept(p);
return "O0;" + stringify(p);
}
else if (var == eapi->supported()->ebuild_metadata_variables()->myoptions()->name())
{
/* if options have a global description and no local description, rewrite it as
* a local description so that descriptions carry on working for installed stuff. */
PackageID::MetadataConstIterator m(package_id->find_metadata(var));
if (m == package_id->end_metadata())
{
/* no key is the same as empty */
return "O0;";
}
const MetadataSpecTreeKey<PlainTextSpecTree> * mm(
visitor_cast<const MetadataSpecTreeKey<PlainTextSpecTree> >(**m));
if (! mm)
throw InternalError(PALUDIS_HERE, "oops. key '" + var + "' isn't a PlainTextSpecTree key");
MyOptionsRewriter p(package_id,
eapi->supported()->annotations()->general_description(),
std::string(1, eapi->supported()->choices_options()->use_expand_separator()));
mm->parse_value()->top()->accept(p);
return "O0;" + p.str.str();
}
return "O0;" + join(tokens.begin() + 4, tokens.end(), " ");
}
else if (tokens[0] == "EVER")
{
std::shared_ptr<const EAPI> eapi(EAPIData::get_instance()->eapi_from_string(tokens[1]));
if (! eapi->supported())
return "EVER EAPI " + tokens[1] + " unsupported";
if (tokens.size() < 3)
{
Log::get_instance()->message("e.pipe_commands.ever.bad", ll_warning, lc_context) << "Got bad EVER pipe command";
return "Ebad EVER command";
}
if (tokens[2] == "AT_LEAST")
{
if (tokens.size() != 5)
{
Log::get_instance()->message("e.pipe_commands.ever.at_least.bad", ll_warning, lc_context) << "Got bad EVER AT_LEAST pipe command";
return "Ebad EVER AT_LEAST command {'" + join(tokens.begin(), tokens.end(), "', '") + "'}";
}
VersionSpec v1(tokens[3], eapi->supported()->version_spec_options()),
v2(tokens[4], eapi->supported()->version_spec_options());
return v2 >= v1 ? "O0;" : "O1;";
}
else if (tokens[2] == "IS_SCM")
{
if (tokens.size() != 4)
{
Log::get_instance()->message("e.pipe_commands.ever.is_scm.bad", ll_warning, lc_context) << "Got bad EVER IS_SCM pipe command";
return "Ebad EVER " + tokens[2] + " command {'" + join(tokens.begin(), tokens.end(), "', '") + "'}";
}
VersionSpec v(tokens[3], eapi->supported()->version_spec_options());
return v.is_scm() ? "O0;" : "O1;";
}
else if (tokens[2] == "SPLIT" || tokens[2] == "SPLIT_ALL")
{
bool all(tokens[2] == "SPLIT_ALL");
if (tokens.size() != 4)
{
Log::get_instance()->message("e.pipe_commands.ever.split.bad", ll_warning, lc_context) << "Got bad EVER " + tokens[2] + " pipe command";
return "Ebad EVER " + tokens[2] + " command {'" + join(tokens.begin(), tokens.end(), "', '") + "'}";
}
VersionSpec v(tokens[3], eapi->supported()->version_spec_options());
std::string result;
for (VersionSpec::ConstIterator c(v.begin()), c_end(v.end()) ;
c != c_end ; ++c)
{
if (c->text().empty())
continue;
if (! result.empty())
result.append(" ");
switch (c->text().at(0))
{
case '.':
case '-':
case '_':
{
if (all)
result.append(c->text().substr(0, 1));
if (c->text().length() > 1)
{
if (all)
result.append(" ");
result.append(c->text().substr(1));
}
}
break;
default:
result.append(c->text());
break;
}
}
return "O0;" + result;
}
else if (tokens[2] == "RANGE")
{
if (tokens.size() != 5)
{
Log::get_instance()->message("e.pipe_commands.ever.major.bad", ll_warning, lc_context) << "Got bad EVER " + tokens[2] + " pipe command";
return "Ebad EVER " + tokens[2] + " command {'" + join(tokens.begin(), tokens.end(), "', '") + "'}";
}
std::string range_s(tokens[3]);
std::string::size_type hyphen_pos(range_s.find('-'));
int range_start, range_end;
if (std::string::npos == hyphen_pos)
range_start = range_end = destringify<int>(range_s);
else
{
std::string range_start_s(range_s.substr(0, hyphen_pos));
std::string range_end_s(range_s.substr(hyphen_pos + 1));
if (range_start_s.empty())
range_start = 0;
else
range_start = destringify<int>(range_start_s);
if (range_end_s.empty())
range_end = std::numeric_limits<int>::max();
else
range_end = destringify<int>(range_end_s);
}
VersionSpec v(tokens[4], eapi->supported()->version_spec_options());
int current_pos(0);
std::string result;
for (VersionSpec::ConstIterator c(v.begin()), c_end(v.end()) ;
c != c_end ; ++c)
{
++current_pos;
if (current_pos > range_end)
break;
if (current_pos < range_start)
continue;
if (c->text().empty())
continue;
switch (c->text().at(0))
{
case '.':
case '-':
case '_':
{
if (! result.empty())
result.append(c->text().substr(0, 1));
if (c->text().length() > 1)
result.append(c->text().substr(1));
}
break;
default:
result.append(c->text());
break;
}
}
return "O0;" + result;
}
else if (tokens[2] == "REPLACE")
{
if (tokens.size() != 6)
{
Log::get_instance()->message("e.pipe_commands.ever.replace.bad", ll_warning, lc_context) << "Got bad EVER " + tokens[2] + " pipe command";
return "Ebad EVER " + tokens[2] + " command {'" + join(tokens.begin(), tokens.end(), "', '") + "'}";
}
VersionSpec v(tokens[5], eapi->supported()->version_spec_options());
int current_pos(0);
std::string replacement(tokens[4]);
std::string result;
switch (tokens[3].at(0))
{
case '.':
case '-':
case '_':
{
bool replacing_done(false);
char replace_separator(tokens[3].at(0));
for (VersionSpec::ConstIterator c(v.begin()), c_end(v.end()) ;
c != c_end ; ++c)
{
if (c->text().empty())
continue;
if (! replacing_done && replace_separator == c->text().at(0))
{
result.append(replacement + c->text().substr(1));
replacing_done = true;
}
else
result.append(c->text());
}
}
break;
default:
int replace_pos(destringify<int>(tokens[3]));
for (VersionSpec::ConstIterator c(v.begin()), c_end(v.end()) ;
c != c_end ; ++c)
{
if (c->text().empty())
continue;
switch (c->text().at(0))
{
case '.':
case '-':
case '_':
{
if (current_pos == replace_pos)
result.append(replacement + c->text().substr(1));
else
result.append(c->text());
}
break;
default:
result.append(c->text());
break;
}
++current_pos;
}
break;
}
return "O0;" + result;
}
else if (tokens[2] == "REPLACE_ALL")
{
if (tokens.size() != 5)
{
Log::get_instance()->message("e.pipe_commands.ever.replace_all.bad", ll_warning, lc_context) << "Got bad EVER " + tokens[2] + " pipe command";
return "Ebad EVER " + tokens[2] + " command {'" + join(tokens.begin(), tokens.end(), "', '") + "'}";
}
VersionSpec v(tokens[4], eapi->supported()->version_spec_options());
std::string replacement(tokens[3]);
std::string result;
for (VersionSpec::ConstIterator c(v.begin()), c_end(v.end()) ;
c != c_end ; ++c)
{
if (c->text().empty())
continue;
switch (c->text().at(0))
{
case '.':
case '-':
case '_':
{
result.append(replacement);
if (c->text().length() > 1)
result.append(c->text().substr(1));
}
break;
default:
result.append(c->text());
break;
}
}
return "O0;" + result;
}
else
{
Log::get_instance()->message("e.pipe_commands.ever.unknown", ll_warning, lc_context) << "Got unknown EVER pipe subcommand";
return "Ebad EVER subcommand";
}
}
else
{
Log::get_instance()->message("e.pipe_commands.unknown", ll_warning, lc_context) << "Got unknown ebuild pipe command '" + s + "'";
return "Eunknown pipe command";
}
}
catch (const Exception & e)
{
return "Eexception '" + e.message() + "' (" + e.what() + ")";
}
catch (const std::exception & e)
{
return "Eexception " + stringify(e.what());
}
catch (...)
{
return "Eexception ???";
}
}