aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2007-04-15 00:47:47 +0000
committerAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2007-04-15 00:47:47 +0000
commit111493b021f369e994a0db73b90817082b83cffc (patch)
tree4c05770c535d5bd594a6c0816b1608c007641bab
parenta597404b65591b2edfce4428713f6ccd184b582f (diff)
downloadpaludis-111493b021f369e994a0db73b90817082b83cffc.tar.gz
paludis-111493b021f369e994a0db73b90817082b83cffc.tar.xz
Common set code. New dynamic sets.
-rw-r--r--doc/sets.html.skel6
-rw-r--r--paludis/Makefile.am.m42
-rw-r--r--paludis/environments/paludis/paludis_environment.cc80
-rw-r--r--paludis/files.m41
-rw-r--r--paludis/repositories/gentoo/portage_repository_sets.cc58
-rw-r--r--paludis/repositories/gentoo/vdb_repository.cc147
-rw-r--r--paludis/set_file.cc491
-rw-r--r--paludis/set_file.hh102
-rw-r--r--paludis/set_file.se23
-rw-r--r--paludis/set_file.sr21
-rw-r--r--paludis/set_file_TEST.cc154
-rwxr-xr-xpaludis/set_file_TEST_cleanup.sh11
-rwxr-xr-xpaludis/set_file_TEST_setup.sh24
13 files changed, 900 insertions, 220 deletions
diff --git a/doc/sets.html.skel b/doc/sets.html.skel
index 99bee85..53b8d95 100644
--- a/doc/sets.html.skel
+++ b/doc/sets.html.skel
@@ -46,7 +46,7 @@ a collection of packages.</p>
<h3>User Defined Sets</h3>
-<p>The <code>DefaultEnvironment</code> environment class allows user defined package sets via
+<p>The <code>PaludisEnvironment</code> environment class allows user defined package sets via
text files. These should be named <code><em>setname</em>.conf</code> and placed in
<code><em>confdir</em>/sets/</code>, where <code><em>confdir</em></code> is the configuration directory
in which <code>use.conf</code> et al. reside. The format is as follows:</p>
@@ -65,6 +65,10 @@ in which <code>use.conf</code> et al. reside. The format is as follows:</p>
<p>Most users will only have use for <code>*</code> lines.</p>
+<p>In addition, dynamic sets are possible. These should use a <code>.bash</code> extension instead
+of <code>.conf</code>, and when executed should output what would be valid content for a
+<code>.conf</code> set.</p>
+
<h3>Portage Repositories Defined Sets</h3>
<p>Ebuild format repositories can supply their own sets. The <code>system</code> and
diff --git a/paludis/Makefile.am.m4 b/paludis/Makefile.am.m4
index 870a0d5..0b477cd 100644
--- a/paludis/Makefile.am.m4
+++ b/paludis/Makefile.am.m4
@@ -62,7 +62,7 @@ ifelse(`$2', `impl', `addimpl(`$1')', `')dnl
ifelse(`$2', `test', `addtest(`$1')', `')dnl
ifelse(`$2', `testscript', `addtestscript(`$1')', `')')dnl
define(`add', `addthis(`$1',`$2')addthis(`$1',`$3')addthis(`$1',`$4')dnl
-addthis(`$1',`$5')addthis(`$1',`$6')')dnl
+addthis(`$1',`$5')addthis(`$1',`$6')addthis(`$1',`$7')addthis(`$1',`$8')')dnl
include(`paludis/files.m4')
diff --git a/paludis/environments/paludis/paludis_environment.cc b/paludis/environments/paludis/paludis_environment.cc
index a92f957..902a80b 100644
--- a/paludis/environments/paludis/paludis_environment.cc
+++ b/paludis/environments/paludis/paludis_environment.cc
@@ -24,6 +24,7 @@
#include <paludis/match_package.hh>
#include <paludis/package_database.hh>
#include <paludis/query.hh>
+#include <paludis/set_file.hh>
#include <paludis/repository.hh>
#include <paludis/repositories/repository_maker.hh>
#include <paludis/util/collection_concrete.hh>
@@ -640,64 +641,31 @@ PaludisEnvironment::local_package_set(const SetName & s) const
{
Context context("When looking for package set '" + stringify(s) + "' in paludis environment:");
- FSEntry ff(FSEntry(_imp->config->config_dir()) / "sets" / (stringify(s) + ".conf"));
- if (ff.exists())
- {
- LineConfigFile f(ff, LineConfigFileOptions());
- std::tr1::shared_ptr<AllDepSpec> result(new AllDepSpec);
- std::tr1::shared_ptr<GeneralSetDepTag> tag(new GeneralSetDepTag(s, stringify(s) + ".conf"));
-
- for (LineConfigFile::Iterator line(f.begin()), line_end(f.end()) ;
- line != line_end ; ++line)
- {
- std::vector<std::string> tokens;
- WhitespaceTokeniser::get_instance()->tokenise(*line, std::back_inserter(tokens));
- if (tokens.empty())
- continue;
-
- if (1 == tokens.size())
- {
- Log::get_instance()->message(ll_warning, lc_context, "Line '" + *line + "' in set file '"
- + stringify(ff) + "' does not specify '*' or '?', assuming '*'");
- std::tr1::shared_ptr<PackageDepSpec> spec(new PackageDepSpec(tokens.at(0), pds_pm_unspecific));
- spec->set_tag(tag);
- result->add_child(spec);
- }
- else if ("*" == tokens.at(0))
- {
- std::tr1::shared_ptr<PackageDepSpec> spec(new PackageDepSpec(tokens.at(1), pds_pm_unspecific));
- spec->set_tag(tag);
- result->add_child(spec);
- }
- else if ("?" == tokens.at(0))
- {
- std::tr1::shared_ptr<PackageDepSpec> p(new PackageDepSpec(tokens.at(1), pds_pm_unspecific));
- p->set_tag(tag);
+ FSEntry dir(FSEntry(_imp->config->config_dir()) / "sets");
+ std::tr1::shared_ptr<GeneralSetDepTag> tag(new GeneralSetDepTag(s, stringify(s) + ".conf"));
- if (p->package_ptr())
- {
- if (! package_database()->query(
- query::Package(*p->package_ptr()) & query::InstalledAtRoot(root()),
- qo_whatever)->empty())
- result->add_child(p);
- }
- else
- Log::get_instance()->message(ll_warning, lc_context, "Line '" + *line + "' in set file '"
- + stringify(ff) + "' uses ? operator but does not specify an unambiguous package");
- }
- else
- Log::get_instance()->message(ll_warning, lc_context, "Line '" + *line + "' in set file '"
- + stringify(ff) + "' does not start with '*' or '?' token, skipping");
-
- if (tokens.size() > 2)
- Log::get_instance()->message(ll_warning, lc_context, "Line '" + *line + "' in set file '"
- + stringify(ff) + "' has trailing garbage");
- }
-
- return result;
+ if ((dir / (stringify(s) + ".bash")).exists())
+ {
+ SetFile f(SetFileParams::create()
+ .file_name(dir / (stringify(s) + ".bash"))
+ .type(sft_paludis_bash)
+ .parse_mode(pds_pm_unspecific)
+ .tag(tag)
+ .environment(this));
+ return f.contents();
}
-
- return std::tr1::shared_ptr<AllDepSpec>();
+ else if ((dir / (stringify(s) + ".conf")).exists())
+ {
+ SetFile f(SetFileParams::create()
+ .file_name(dir / (stringify(s) + ".conf"))
+ .type(sft_paludis_conf)
+ .parse_mode(pds_pm_unspecific)
+ .tag(tag)
+ .environment(this));
+ return f.contents();
+ }
+ else
+ return std::tr1::shared_ptr<AllDepSpec>();
}
std::tr1::shared_ptr<const SetsCollection>
diff --git a/paludis/files.m4 b/paludis/files.m4
index f8afe24..02a91f0 100644
--- a/paludis/files.m4
+++ b/paludis/files.m4
@@ -30,6 +30,7 @@ add(`portage_dep_parser', `hh', `cc', `test')
add(`query', `hh', `cc')
add(`repository', `hh', `cc', `sr')
add(`repository_name_cache', `hh', `cc', `test', `testscript')
+add(`set_file', `hh', `cc', `se', `sr', `test', 'testscript')
add(`syncer', `hh', `cc', `sr')
add(`version_metadata', `hh', `cc', `sr')
add(`version_operator', `hh', `cc', `test')
diff --git a/paludis/repositories/gentoo/portage_repository_sets.cc b/paludis/repositories/gentoo/portage_repository_sets.cc
index c351295..acc2bc5 100644
--- a/paludis/repositories/gentoo/portage_repository_sets.cc
+++ b/paludis/repositories/gentoo/portage_repository_sets.cc
@@ -26,6 +26,7 @@
#include <paludis/environment.hh>
#include <paludis/config_file.hh>
#include <paludis/query.hh>
+#include <paludis/set_file.hh>
#include <paludis/portage_dep_parser.hh>
#include <paludis/util/collection_concrete.hh>
#include <paludis/util/dir_iterator.hh>
@@ -92,58 +93,15 @@ PortageRepositorySets::package_set(const SetName & s) const
FSEntry ff(_imp->params.setsdir / (stringify(s) + ".conf"));
Context context("When loading package set '" + stringify(s) + "' from '" + stringify(ff) + "':");
- std::tr1::shared_ptr<AllDepSpec> result(new AllDepSpec);
- LineConfigFile f(ff, LineConfigFileOptions());
- for (LineConfigFile::Iterator line(f.begin()), line_end(f.end()) ;
- line != line_end ; ++line)
- {
- std::vector<std::string> tokens;
- WhitespaceTokeniser::get_instance()->tokenise(*line, std::back_inserter(tokens));
- if (tokens.empty())
- continue;
+ SetFile f(SetFileParams::create()
+ .file_name(ff)
+ .environment(_imp->environment)
+ .type(sft_paludis_conf)
+ .parse_mode(pds_pm_eapi_0)
+ .tag(tag));
- if (1 == tokens.size())
- {
- Log::get_instance()->message(ll_warning, lc_context,
- "Line '" + *line + "' in set file '"
- + stringify(ff) + "' does not specify '*' or '?', assuming '*'");
- std::tr1::shared_ptr<PackageDepSpec> spec(new PackageDepSpec(tokens.at(0), pds_pm_eapi_0));
- spec->set_tag(tag);
- result->add_child(spec);
- }
- else if ("*" == tokens.at(0))
- {
- std::tr1::shared_ptr<PackageDepSpec> spec(new PackageDepSpec(tokens.at(1), pds_pm_eapi_0));
- spec->set_tag(tag);
- result->add_child(spec);
- }
- else if ("?" == tokens.at(0))
- {
- std::tr1::shared_ptr<PackageDepSpec> p(new PackageDepSpec(tokens.at(1), pds_pm_eapi_0));
- p->set_tag(tag);
- if (p->package_ptr())
- {
- if (! _imp->environment->package_database()->query(
- query::Package(*p->package_ptr()) & query::InstalledAtRoot(
- _imp->params.environment->root()), qo_whatever)->empty())
- result->add_child(p);
- }
- else
- Log::get_instance()->message(ll_warning, lc_context, "Line '" + *line + "' in set file '"
- + stringify(ff) + "' uses ? operator but does not specify an unambiguous package");
- }
- else
- Log::get_instance()->message(ll_warning, lc_context,
- "Line '" + *line + "' in set file '"
- + stringify(ff) + "' does not start with '*' or '?' token, skipping");
-
- if (tokens.size() > 2)
- Log::get_instance()->message(ll_warning, lc_context,
- "Line '" + *line + "' in set file '"
- + stringify(ff) + "' has trailing garbage");
- }
- return result;
+ return f.contents();
}
else
return std::tr1::shared_ptr<DepSpec>();
diff --git a/paludis/repositories/gentoo/vdb_repository.cc b/paludis/repositories/gentoo/vdb_repository.cc
index f202d20..7e2b23f 100644
--- a/paludis/repositories/gentoo/vdb_repository.cc
+++ b/paludis/repositories/gentoo/vdb_repository.cc
@@ -31,6 +31,7 @@
#include <paludis/match_package.hh>
#include <paludis/package_database.hh>
#include <paludis/repository_name_cache.hh>
+#include <paludis/set_file.hh>
#include <paludis/util/collection_concrete.hh>
#include <paludis/util/dir_iterator.hh>
@@ -1077,50 +1078,24 @@ VDBRepository::do_package_set(const SetName & s) const
}
else if ("world" == s.data())
{
- std::tr1::shared_ptr<AllDepSpec> result(new AllDepSpec);
std::tr1::shared_ptr<GeneralSetDepTag> tag(new GeneralSetDepTag(SetName("world"), stringify(name())));
if (_imp->world_file.exists())
{
- LineConfigFile world(_imp->world_file, LineConfigFileOptions());
-
- for (LineConfigFile::Iterator line(world.begin()), line_end(world.end()) ;
- line != line_end ; ++line)
- {
- try
- {
- if (std::string::npos == line->find('/'))
- {
- std::tr1::shared_ptr<DepSpec> spec(_imp->env->package_set(SetName(*line)));
- if (spec)
- result->add_child(spec);
- else
- Log::get_instance()->message(ll_warning, lc_no_context, "World file '"
- + stringify(_imp->world_file) + "' entry '" + *line +
- " is not a known package set");
- }
- else
- {
- std::tr1::shared_ptr<PackageDepSpec> spec(new PackageDepSpec(
- std::tr1::shared_ptr<QualifiedPackageName>(new QualifiedPackageName(*line))));
- spec->set_tag(tag);
- result->add_child(spec);
- }
- }
- catch (const NameError & n)
- {
- Log::get_instance()->message(ll_warning, lc_no_context, "World file '"
- + stringify(_imp->world_file) + "' entry '" + *line + " is broken: '"
- + n.message() + "' (" + n.what() + ")");
- }
- }
+ SetFile world(SetFileParams::create()
+ .file_name(_imp->world_file)
+ .type(sft_simple)
+ .parse_mode(pds_pm_unspecific)
+ .tag(tag)
+ .environment(_imp->env));
+ return world.contents();
}
else
Log::get_instance()->message(ll_warning, lc_no_context,
"World file '" + stringify(_imp->world_file) +
"' doesn't exist");
- return result;
+ return std::tr1::shared_ptr<AllDepSpec>(new AllDepSpec);
}
else
return std::tr1::shared_ptr<DepSpec>();
@@ -1146,98 +1121,46 @@ VDBRepository::invalidate()
void
VDBRepository::add_string_to_world(const std::string & n) const
{
- Context context("When adding '" + n + "' to world file '" +
- stringify(_imp->world_file) + "':");
-
- bool found(false);
+ Context context("When adding '" + n + "' to world file '" + stringify(_imp->world_file) + "':");
- if (_imp->world_file.exists())
+ if (! _imp->world_file.exists())
{
- LineConfigFile world(_imp->world_file, LineConfigFileOptions());
-
- for (LineConfigFile::Iterator line(world.begin()), line_end(world.end()) ;
- line != line_end ; ++line)
- if (*line == n)
- {
- found = true;
- break;
- }
- }
-
- if (! found)
- {
- /* portage is retarded, and doesn't ensure that the last entry in world has
- * a newline character after it. */
- bool world_file_needs_newline(false);
- {
- std::ifstream world(stringify(_imp->world_file).c_str(), std::ios::in);
- if (world)
- {
- world.seekg(0, std::ios::end);
- if (0 != world.tellg())
- {
- world.seekg(-1, std::ios::end);
- if ('\n' != world.get())
- world_file_needs_newline = true;
- }
- }
- }
-
- if (world_file_needs_newline)
- Log::get_instance()->message(ll_warning, lc_no_context, "World file '"
- + stringify(_imp->world_file) + "' lacks final newline");
-
- std::ofstream world(stringify(_imp->world_file).c_str(), std::ios::out | std::ios::app);
- if (! world)
- Log::get_instance()->message(ll_warning, lc_no_context, "Cannot append to world file '"
- + stringify(_imp->world_file) + "', skipping world update");
- else
+ std::ofstream f(stringify(_imp->world_file).c_str());
+ if (! f)
{
- if (world_file_needs_newline)
- world << std::endl;
- world << n << std::endl;
+ Log::get_instance()->message(ll_warning, lc_no_context, "Cannot create world file '"
+ + stringify(_imp->world_file) + "'");
+ return;
}
}
+
+ SetFile world(SetFileParams::create()
+ .file_name(_imp->world_file)
+ .type(sft_simple)
+ .parse_mode(pds_pm_unspecific)
+ .tag(std::tr1::shared_ptr<DepTag>())
+ .environment(_imp->env));
+ world.add(n);
+ world.rewrite();
}
void
VDBRepository::remove_string_from_world(const std::string & n) const
{
- std::list<std::string> world_lines;
+ Context context("When removing '" + n + "' from world file '" + stringify(_imp->world_file) + "':");
if (_imp->world_file.exists())
{
- std::ifstream world_file(stringify(_imp->world_file).c_str());
-
- if (! world_file)
- {
- Log::get_instance()->message(ll_warning, lc_no_context, "Cannot read world file '"
- + stringify(_imp->world_file) + "', skipping world update");
- return;
- }
-
- std::string line;
- while (std::getline(world_file, line))
- {
- if (strip_leading(strip_trailing(line, " \t"), "\t") != stringify(n))
- world_lines.push_back(line);
- else
- Log::get_instance()->message(ll_debug, lc_no_context, "Removing line '"
- + line + "' from world file '" + stringify(_imp->world_file));
- }
- }
-
- std::ofstream world_file(stringify(_imp->world_file).c_str());
-
- if (! world_file)
- {
- Log::get_instance()->message(ll_warning, lc_no_context, "Cannot write world file '"
- + stringify(_imp->world_file) + "', skipping world update");
- return;
+ SetFile world(SetFileParams::create()
+ .file_name(_imp->world_file)
+ .type(sft_simple)
+ .parse_mode(pds_pm_unspecific)
+ .tag(std::tr1::shared_ptr<DepTag>())
+ .environment(_imp->env));
+
+ world.remove(n);
+ world.rewrite();
}
-
- std::copy(world_lines.begin(), world_lines.end(),
- std::ostream_iterator<std::string>(world_file, "\n"));
}
void
diff --git a/paludis/set_file.cc b/paludis/set_file.cc
new file mode 100644
index 0000000..5a1f210
--- /dev/null
+++ b/paludis/set_file.cc
@@ -0,0 +1,491 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Ciaran McCreesh <ciaranm@ciaranm.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 "set_file.hh"
+#include <paludis/util/fs_entry.hh>
+#include <paludis/util/log.hh>
+#include <paludis/util/tokeniser.hh>
+#include <paludis/util/pstream.hh>
+#include <paludis/config_file.hh>
+#include <paludis/environment.hh>
+#include <paludis/query.hh>
+#include <list>
+#include <vector>
+#include <fstream>
+
+using namespace paludis;
+
+#include <paludis/set_file-se.cc>
+#include <paludis/set_file-sr.cc>
+
+SetFileError::SetFileError(const FSEntry & f, const std::string & m) throw () :
+ ConfigurationError("In set file '" + stringify(f) + "': " + m)
+{
+}
+
+namespace
+{
+ class SetFileHandler
+ {
+ protected:
+ SetFileHandler();
+
+ public:
+ virtual ~SetFileHandler();
+
+ virtual std::tr1::shared_ptr<CompositeDepSpec> contents() const = 0;
+ virtual void add(const std::string &) = 0;
+ virtual void remove(const std::string &) = 0;
+ virtual void rewrite() const = 0;
+ };
+
+ class PaludisConfHandler :
+ public SetFileHandler
+ {
+ private:
+ const SetFileParams _p;
+ std::list<std::string> _lines;
+ mutable std::tr1::shared_ptr<CompositeDepSpec> _contents;
+
+ void _create_contents() const;
+
+ public:
+ PaludisConfHandler(const SetFileParams &);
+
+ virtual std::tr1::shared_ptr<CompositeDepSpec> contents() const;
+ virtual void add(const std::string &);
+ virtual void remove(const std::string &);
+ virtual void rewrite() const;
+ };
+
+ class PaludisBashHandler :
+ public SetFileHandler
+ {
+ private:
+ const SetFileParams _p;
+ std::tr1::shared_ptr<CompositeDepSpec> _contents;
+
+ public:
+ PaludisBashHandler(const SetFileParams &);
+
+ virtual std::tr1::shared_ptr<CompositeDepSpec> contents() const;
+ virtual void add(const std::string &) PALUDIS_ATTRIBUTE((noreturn));
+ virtual void remove(const std::string &) PALUDIS_ATTRIBUTE((noreturn));
+ virtual void rewrite() const PALUDIS_ATTRIBUTE((noreturn));
+ };
+
+ class SimpleHandler :
+ public SetFileHandler
+ {
+ private:
+ const SetFileParams _p;
+ std::list<std::string> _lines;
+ mutable std::tr1::shared_ptr<CompositeDepSpec> _contents;
+
+ void _create_contents() const;
+
+ public:
+ SimpleHandler(const SetFileParams &);
+
+ virtual std::tr1::shared_ptr<CompositeDepSpec> contents() const;
+ virtual void add(const std::string &);
+ virtual void remove(const std::string &);
+ virtual void rewrite() const;
+ };
+
+ std::tr1::shared_ptr<SetFileHandler>
+ make_handler(const SetFileParams & p)
+ {
+ Context context("When making SetFileHandler for '" + stringify(p.file_name) + "':");
+
+ switch (p.type)
+ {
+ case sft_simple:
+ return std::tr1::shared_ptr<SetFileHandler>(new SimpleHandler(p));
+
+ case sft_paludis_conf:
+ return std::tr1::shared_ptr<SetFileHandler>(new PaludisConfHandler(p));
+
+ case sft_paludis_bash:
+ return std::tr1::shared_ptr<SetFileHandler>(new PaludisBashHandler(p));
+
+ case last_sft:
+ break;
+ }
+
+ throw InternalError(PALUDIS_HERE, "Bad SetFileType");
+ }
+
+ struct TokenOneIs
+ {
+ const std::string query;
+
+ TokenOneIs(const std::string & q) :
+ query(q)
+ {
+ }
+
+ bool operator() (const std::string & l) const
+ {
+ std::vector<std::string> tokens;
+ WhitespaceTokeniser::get_instance()->tokenise(l, std::back_inserter(tokens));
+
+ return (tokens.size() >= 1) && (tokens.at(1) == query);
+ }
+ };
+
+ void
+ do_one_conf_line(const std::string & line, std::tr1::shared_ptr<CompositeDepSpec> result,
+ const SetFileParams & params)
+ {
+ if (line.empty())
+ return;
+
+ if ('#' == line.at(0))
+ return;
+
+ Context c("When handling line '" + stringify(line) + "':");
+
+ try
+ {
+ std::vector<std::string> tokens;
+ WhitespaceTokeniser::get_instance()->tokenise(line, std::back_inserter(tokens));
+
+ if (tokens.empty())
+ return;
+
+ if (tokens.size() == 1)
+ {
+ Log::get_instance()->message(ll_warning, lc_context, "Line '" + stringify(line) +
+ "' should start with '?' or '*', assuming '*'");
+
+ std::tr1::shared_ptr<PackageDepSpec> spec(new PackageDepSpec(tokens.at(0), params.parse_mode));
+ if (params.tag)
+ spec->set_tag(params.tag);
+ result->add_child(spec);
+ }
+ else if ("*" == tokens.at(0))
+ {
+ std::tr1::shared_ptr<PackageDepSpec> spec(new PackageDepSpec(tokens.at(1), params.parse_mode));
+ if (params.tag)
+ spec->set_tag(params.tag);
+ result->add_child(spec);
+ }
+ else if ("?" == tokens.at(0))
+ {
+ std::tr1::shared_ptr<PackageDepSpec> spec(new PackageDepSpec(tokens.at(1), pds_pm_unspecific));
+ if (params.tag)
+ spec->set_tag(params.tag);
+
+ if (spec->package_ptr())
+ {
+ if (! params.environment)
+ Log::get_instance()->message(ll_warning, lc_context, "Line '" + stringify(line) +
+ "' uses ? operator but no environment is available");
+ else if (! params.environment->package_database()->query(query::Package(*spec->package_ptr()) &
+ query::InstalledAtRoot(params.environment->root()), qo_whatever)->empty())
+ result->add_child(spec);
+ }
+ else
+ Log::get_instance()->message(ll_warning, lc_context, "Line '" + stringify(line) +
+ "' uses ? operator but does not specify an unambiguous package");
+ }
+ else
+ Log::get_instance()->message(ll_warning, lc_context, "Ignoring line '" + stringify(line) +
+ "' because it does not start with '?' or '*'");
+ }
+ catch (const Exception & e)
+ {
+ Log::get_instance()->message(ll_warning, lc_context, "Ignoring line '" + stringify(line) +
+ "' due to exception '" + e.message() + "' (" + e.what() + ")");
+ }
+ }
+}
+
+SetFileHandler::SetFileHandler()
+{
+}
+
+SetFileHandler::~SetFileHandler()
+{
+}
+
+SimpleHandler::SimpleHandler(const SetFileParams & p) :
+ _p(p)
+{
+ Context context("When loading simple set file '" + stringify(_p.file_name) + "':");
+
+ LineConfigFile ff(_p.file_name, LineConfigFileOptions() + lcfo_disallow_continuations + lcfo_disallow_comments
+ + lcfo_no_skip_blank_lines);
+ for (LineConfigFile::Iterator line(ff.begin()), line_end(ff.end()) ;
+ line != line_end ; ++line)
+ _lines.push_back(*line);
+}
+
+void
+SimpleHandler::_create_contents() const
+{
+ Context context("When parsing atoms in simple set file '" + stringify(_p.file_name) + "':");
+
+ _contents.reset(new AllDepSpec);
+ for (std::list<std::string>::const_iterator i(_lines.begin()), i_end(_lines.end()) ;
+ i != i_end ; ++i)
+ {
+ if (i->empty())
+ continue;
+
+ if ('#' == i->at(0))
+ continue;
+
+ Context c("When handling line '" + stringify(*i) + "':");
+
+ try
+ {
+ if (_p.environment && std::string::npos == i->find('/'))
+ {
+ std::tr1::shared_ptr<DepSpec> p(_p.environment->package_set(SetName(*i)));
+ if (p)
+ _contents->add_child(p);
+ else
+ Log::get_instance()->message(ll_warning, lc_context, "Ignoring line '" + stringify(*i) +
+ "' because it does not contain a known set name");
+ }
+ else
+ {
+ std::tr1::shared_ptr<PackageDepSpec> p(new PackageDepSpec(stringify(*i), _p.parse_mode));
+ if (_p.tag)
+ p->set_tag(_p.tag);
+ _contents->add_child(p);
+ }
+ }
+ catch (const Exception & e)
+ {
+ Log::get_instance()->message(ll_warning, lc_context, "Ignoring line '" + stringify(*i) +
+ "' due to exception '" + e.message() + "' (" + e.what() + "'");
+ }
+ }
+}
+
+std::tr1::shared_ptr<CompositeDepSpec>
+SimpleHandler::contents() const
+{
+ if (! _contents)
+ _create_contents();
+
+ return _contents;
+}
+
+void
+SimpleHandler::add(const std::string & p)
+{
+ if (_lines.end() == std::find(_lines.begin(), _lines.end(), p))
+ _lines.push_back(p);
+
+ _contents.reset();
+}
+
+void
+SimpleHandler::remove(const std::string & p)
+{
+ Context context("When removing '" + stringify(p) + "' from simple set file '" + stringify(_p.file_name) + "':");
+
+ _contents.reset();
+ _lines.remove(p);
+}
+
+void
+SimpleHandler::rewrite() const
+{
+ Context context("When rewriting simple set file '" + stringify(_p.file_name) + "':");
+
+ std::ofstream f(stringify(_p.file_name).c_str());
+ if (! f)
+ throw SetFileError(_p.file_name, "Cannot write to '" + stringify(_p.file_name) + "'");
+
+ for (std::list<std::string>::const_iterator i(_lines.begin()), i_end(_lines.end()) ;
+ i != i_end ; ++i)
+ f << *i << std::endl;
+}
+
+PaludisConfHandler::PaludisConfHandler(const SetFileParams & p) :
+ _p(p)
+{
+ Context context("When loading paludis conf set file '" + stringify(_p.file_name) + "':");
+
+ LineConfigFile ff(_p.file_name, LineConfigFileOptions() + lcfo_disallow_continuations + lcfo_disallow_comments
+ + lcfo_no_skip_blank_lines);
+ for (LineConfigFile::Iterator line(ff.begin()), line_end(ff.end()) ;
+ line != line_end ; ++line)
+ _lines.push_back(*line);
+}
+
+void
+PaludisConfHandler::_create_contents() const
+{
+ Context context("When parsing atoms in paludis conf set file '" + stringify(_p.file_name) + "':");
+
+ _contents.reset(new AllDepSpec);
+ for (std::list<std::string>::const_iterator i(_lines.begin()), i_end(_lines.end()) ;
+ i != i_end ; ++i)
+ do_one_conf_line(*i, _contents, _p);
+}
+
+std::tr1::shared_ptr<CompositeDepSpec>
+PaludisConfHandler::contents() const
+{
+ if (! _contents)
+ _create_contents();
+
+ return _contents;
+}
+
+void
+PaludisConfHandler::add(const std::string & p)
+{
+ if (_lines.end() == std::find_if(_lines.begin(), _lines.end(), TokenOneIs(p)))
+ _lines.push_back("* " + p);
+
+ _contents.reset();
+}
+
+void
+PaludisConfHandler::remove(const std::string & p)
+{
+ Context context("When removing '" + stringify(p) + "' from paludis conf set file '" + stringify(_p.file_name) + "':");
+
+ _contents.reset();
+ _lines.remove_if(TokenOneIs(p));
+}
+
+void
+PaludisConfHandler::rewrite() const
+{
+ Context context("When rewriting paludis conf set file '" + stringify(_p.file_name) + "':");
+
+ std::ofstream f(stringify(_p.file_name).c_str());
+ if (! f)
+ throw SetFileError(_p.file_name, "Cannot write to '" + stringify(_p.file_name) + "'");
+
+ for (std::list<std::string>::const_iterator i(_lines.begin()), i_end(_lines.end()) ;
+ i != i_end ; ++i)
+ f << *i << std::endl;
+}
+
+PaludisBashHandler::PaludisBashHandler(const SetFileParams & p) :
+ _p(p)
+{
+ Context context("When loading paludis bash set file '" + stringify(_p.file_name) + "':");
+ _contents.reset(new AllDepSpec);
+
+ Command cmd(Command("bash '" + stringify(_p.file_name) + "'")
+ .with_setenv("ROOT", _p.environment ? stringify(_p.environment->root()) : "/")
+ .with_setenv("SET", stringify(_p.file_name))
+ .with_setenv("SET_LOG_LEVEL", stringify(Log::get_instance()->log_level()))
+ .with_setenv("PALUDIS_EBUILD_DIR", getenv_with_default("PALUDIS_EBUILD_DIR", LIBEXECDIR "/paludis"))
+ .with_setenv("PALUDIS_COMMAND", _p.environment ? _p.environment->paludis_command() : "")
+ .with_stderr_prefix(_p.file_name.basename() + "> "));
+ PStream s(cmd);
+
+ LineConfigFile ff(s, LineConfigFileOptions() + lcfo_disallow_continuations + lcfo_disallow_comments
+ + lcfo_no_skip_blank_lines);
+ for (LineConfigFile::Iterator line(ff.begin()), line_end(ff.end()) ;
+ line != line_end ; ++line)
+ do_one_conf_line(*line, _contents, _p);
+
+ if (s.exit_status() != 0)
+ {
+ Log::get_instance()->message(ll_warning, lc_context, "Set file script '" + stringify(_p.file_name) +
+ "' returned non-zero exit status '" + stringify(s.exit_status()) + "'");
+ _contents.reset(new AllDepSpec);
+ }
+}
+
+std::tr1::shared_ptr<CompositeDepSpec>
+PaludisBashHandler::contents() const
+{
+ return _contents;
+}
+
+void
+PaludisBashHandler::add(const std::string & p)
+{
+ throw SetFileError(_p.file_name, "Cannot add entry '" + p + "' to bash script '" + stringify(_p.file_name) + "'");
+}
+
+void
+PaludisBashHandler::remove(const std::string & p)
+{
+ throw SetFileError(_p.file_name, "Cannot remove entry '" + p + "' from bash script '" + stringify(_p.file_name) + "'");
+}
+
+void
+PaludisBashHandler::rewrite() const
+{
+ throw SetFileError(_p.file_name, "Cannot modify bash script '" + stringify(_p.file_name) + "'");
+}
+
+namespace paludis
+{
+ template<>
+ struct Implementation<SetFile>
+ {
+ const SetFileParams params;
+ std::tr1::shared_ptr<SetFileHandler> handler;
+
+ Implementation(const SetFileParams & p) :
+ params(p),
+ handler(make_handler(p))
+ {
+ }
+ };
+}
+
+SetFile::SetFile(const SetFileParams & p) :
+ PrivateImplementationPattern<SetFile>(new Implementation<SetFile>(p))
+{
+}
+
+SetFile::~SetFile()
+{
+}
+
+std::tr1::shared_ptr<CompositeDepSpec>
+SetFile::contents() const
+{
+ return _imp->handler->contents();
+}
+
+void
+SetFile::rewrite() const
+{
+ _imp->handler->rewrite();
+}
+
+void
+SetFile::add(const std::string & p)
+{
+ _imp->handler->add(p);
+}
+
+void
+SetFile::remove(const std::string & p)
+{
+ return _imp->handler->remove(p);
+}
+
diff --git a/paludis/set_file.hh b/paludis/set_file.hh
new file mode 100644
index 0000000..8274c60
--- /dev/null
+++ b/paludis/set_file.hh
@@ -0,0 +1,102 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Ciaran McCreesh <ciaranm@ciaranm.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
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_SET_FILE_HH
+#define PALUDIS_GUARD_PALUDIS_SET_FILE_HH 1
+
+#include <paludis/util/instantiation_policy.hh>
+#include <paludis/util/private_implementation_pattern.hh>
+#include <paludis/util/exception.hh>
+#include <paludis/util/fs_entry.hh>
+#include <paludis/name.hh>
+#include <paludis/dep_spec.hh>
+#include <iosfwd>
+
+namespace paludis
+{
+ class Environment;
+
+#include <paludis/set_file-se.hh>
+#include <paludis/set_file-sr.hh>
+
+ /**
+ * Thrown if there is a problem reading or writing a SetFile.
+ *
+ * \ingroup grpsetfile
+ * \nosubgrouping
+ */
+ class SetFileError :
+ public ConfigurationError
+ {
+ public:
+ SetFileError(const FSEntry &, const std::string &) throw ();
+ };
+
+ /**
+ * Shared code for files containing a package set.
+ *
+ * Various set file formats are supported:
+ *
+ * - sft_paludis_conf, a line-based set file with prefixed entries
+ * - sft_paludis_bash, a bash script that outputs an sft_paludis_conf
+ * - sft_simple, a simple line-based file
+ *
+ * The file can be modified if it is sft_paludis_conf or sft_simple.
+ *
+ * \ingroup grpsetfile
+ * \nosubgrouping
+ */
+ class SetFile :
+ private InstantiationPolicy<SetFile, instantiation_method::NonCopyableTag>,
+ private PrivateImplementationPattern<SetFile>
+ {
+ public:
+ ///\name Basic operations
+ ///\{
+
+ SetFile(const SetFileParams &);
+ ~SetFile();
+
+ ///\}
+
+ /**
+ * Fetch our contents.
+ */
+ std::tr1::shared_ptr<CompositeDepSpec> contents() const;
+
+ /**
+ * Rewrite our contents.
+ */
+ void rewrite() const;
+
+ /**
+ * Add an item to our contents, if it is not there already.
+ */
+ void add(const std::string &);
+
+ /**
+ * Remove any matching lines.
+ *
+ * \return True if we removed anything.
+ */
+ void remove(const std::string &);
+ };
+}
+
+#endif
diff --git a/paludis/set_file.se b/paludis/set_file.se
new file mode 100644
index 0000000..12832ca
--- /dev/null
+++ b/paludis/set_file.se
@@ -0,0 +1,23 @@
+#!/bin/bash
+# vim: set sw=4 sts=4 et ft=sh :
+
+make_enum_SetFileType()
+{
+ prefix sft
+
+ key sft_paludis_conf "A Paludis style .conf file"
+ key sft_paludis_bash "A Paludis style .bash file"
+ key sft_simple "A simple line-based file"
+
+ doxygen_comment << "END"
+ /**
+ * The type of a SetFile.
+ *
+ * \see SetFile
+ * \ingroup grpsetfile
+ */
+END
+}
+
+
+
diff --git a/paludis/set_file.sr b/paludis/set_file.sr
new file mode 100644
index 0000000..04b3596
--- /dev/null
+++ b/paludis/set_file.sr
@@ -0,0 +1,21 @@
+make_class_SetFileParams()
+{
+ key file_name FSEntry
+ key type SetFileType
+ key parse_mode PackageDepSpecParseMode
+ key tag "std::tr1::shared_ptr<const DepTag>"
+ key environment "const Environment *"
+
+ allow_named_args
+
+ doxygen_comment << "END"
+ /**
+ * Parameters for a SetFile.
+ *
+ * \see SetFile
+ * \ingroup grpsetfile
+ * \nosubgrouping
+ */
+END
+}
+
diff --git a/paludis/set_file_TEST.cc b/paludis/set_file_TEST.cc
new file mode 100644
index 0000000..3519308
--- /dev/null
+++ b/paludis/set_file_TEST.cc
@@ -0,0 +1,154 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2007 Ciaran McCreesh <ciaranm@ciaranm.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 "set_file.hh"
+#include <test/test_runner.hh>
+#include <test/test_framework.hh>
+#include <paludis/dep_spec_pretty_printer.hh>
+#include <paludis/util/fs_entry.hh>
+#include <fstream>
+
+using namespace test;
+using namespace paludis;
+
+namespace test_cases
+{
+ struct SimpleTest : TestCase
+ {
+ SimpleTest() : TestCase("simple set file") { }
+
+ void run()
+ {
+ SetFile f(SetFileParams::create()
+ .file_name(FSEntry("set_file_TEST_dir/simple1"))
+ .type(sft_simple)
+ .parse_mode(pds_pm_eapi_0)
+ .tag(std::tr1::shared_ptr<DepTag>())
+ .environment(0));
+
+ {
+ DepSpecPrettyPrinter p(0, false);
+ f.contents()->accept(&p);
+ TEST_CHECK_STRINGIFY_EQUAL(p, "( foo/bar >=bar/baz-1.23 ) ");
+ }
+
+ f.add("foo/bar");
+ f.add("moo/oink");
+ {
+ DepSpecPrettyPrinter p(0, false);
+ f.contents()->accept(&p);
+ TEST_CHECK_STRINGIFY_EQUAL(p, "( foo/bar >=bar/baz-1.23 moo/oink ) ");
+ }
+
+ f.rewrite();
+
+ {
+ std::ifstream ff("set_file_TEST_dir/simple1");
+ TEST_CHECK(ff);
+ std::string g((std::istreambuf_iterator<char>(ff)), std::istreambuf_iterator<char>());
+ TEST_CHECK_EQUAL(g, "# this is a comment\n\nfoo/bar\n>=bar/baz-1.23\n\n# the end\nmoo/oink\n");
+ }
+
+ f.remove(">=bar/baz-1.23");
+ f.remove("bar/cow");
+
+ {
+ DepSpecPrettyPrinter p(0, false);
+ f.contents()->accept(&p);
+ TEST_CHECK_STRINGIFY_EQUAL(p, "( foo/bar moo/oink ) ");
+ }
+
+ f.rewrite();
+
+ {
+ std::ifstream ff("set_file_TEST_dir/simple1");
+ TEST_CHECK(ff);
+ std::string g((std::istreambuf_iterator<char>(ff)), std::istreambuf_iterator<char>());
+ TEST_CHECK_EQUAL(g, "# this is a comment\n\nfoo/bar\n\n# the end\nmoo/oink\n");
+ }
+ }
+
+ bool repeatable() const
+ {
+ return false;
+ }
+ } test_simple;
+
+ struct PaludisConfTest : TestCase
+ {
+ PaludisConfTest() : TestCase("paludis .conf set file") { }
+
+ void run()
+ {
+ SetFile f(SetFileParams::create()
+ .file_name(FSEntry("set_file_TEST_dir/paludisconf1"))
+ .type(sft_paludis_conf)
+ .parse_mode(pds_pm_eapi_0)
+ .tag(std::tr1::shared_ptr<DepTag>())
+ .environment(0));
+
+ {
+ DepSpecPrettyPrinter p(0, false);
+ f.contents()->accept(&p);
+ TEST_CHECK_STRINGIFY_EQUAL(p, "( >=bar/baz-1.23 ) ");
+ }
+
+ f.add("foo/bar");
+ f.add("moo/oink");
+ {
+ DepSpecPrettyPrinter p(0, false);
+ f.contents()->accept(&p);
+ TEST_CHECK_STRINGIFY_EQUAL(p, "( >=bar/baz-1.23 moo/oink ) ");
+ }
+
+ f.rewrite();
+
+ {
+ std::ifstream ff("set_file_TEST_dir/paludisconf1");
+ TEST_CHECK(ff);
+ std::string g((std::istreambuf_iterator<char>(ff)), std::istreambuf_iterator<char>());
+ TEST_CHECK_EQUAL(g, "# this is a comment\n\n? foo/bar\n* >=bar/baz-1.23\n\n# the end\n* moo/oink\n");
+ }
+
+ f.remove(">=bar/baz-1.23");
+ f.remove("bar/cow");
+
+ {
+ DepSpecPrettyPrinter p(0, false);
+ f.contents()->accept(&p);
+ TEST_CHECK_STRINGIFY_EQUAL(p, "( moo/oink ) ");
+ }
+
+ f.rewrite();
+
+ {
+ std::ifstream ff("set_file_TEST_dir/paludisconf1");
+ TEST_CHECK(ff);
+ std::string g((std::istreambuf_iterator<char>(ff)), std::istreambuf_iterator<char>());
+ TEST_CHECK_EQUAL(g, "# this is a comment\n\n? foo/bar\n\n# the end\n* moo/oink\n");
+ }
+ }
+
+ bool repeatable() const
+ {
+ return false;
+ }
+ } test_paludis_conf;
+}
+
diff --git a/paludis/set_file_TEST_cleanup.sh b/paludis/set_file_TEST_cleanup.sh
new file mode 100755
index 0000000..d43acb2
--- /dev/null
+++ b/paludis/set_file_TEST_cleanup.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+if [ -d set_file_TEST_dir ] ; then
+ rm -fr set_file_TEST_dir
+else
+ true
+fi
+
+
+
diff --git a/paludis/set_file_TEST_setup.sh b/paludis/set_file_TEST_setup.sh
new file mode 100755
index 0000000..3e2b7dc
--- /dev/null
+++ b/paludis/set_file_TEST_setup.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+mkdir set_file_TEST_dir || exit 2
+cd set_file_TEST_dir
+
+cat <<"END" > simple1
+# this is a comment
+
+foo/bar
+ >=bar/baz-1.23
+
+ # the end
+END
+
+cat <<"END" > paludisconf1
+# this is a comment
+
+? foo/bar
+ * >=bar/baz-1.23
+
+ # the end
+END
+