[paludis-commits] r4110 - in trunk: . paludis paludis/environments/paludis paludis/repositories/e paludis/repositories/e/ebuild paludis/repositories/gems paludis/util src/clients/gtkpaludis/libgtkpaludis

ciaranm at svn.pioto.org ciaranm at svn.pioto.org
Sun Dec 30 22:38:15 UTC 2007


Author: ciaranm
Date: 2007-12-30 22:38:14 +0000 (Sun, 30 Dec 2007)
New Revision: 4110

Removed:
   trunk/paludis/util/pstream.cc
   trunk/paludis/util/pstream.hh
   trunk/paludis/util/pstream_TEST.cc
Modified:
   trunk/ChangeLog
   trunk/paludis/environments/paludis/bashable_conf.cc
   trunk/paludis/environments/paludis/paludis_config.cc
   trunk/paludis/hooker.cc
   trunk/paludis/repositories/e/ebuild.cc
   trunk/paludis/repositories/e/ebuild/ebuild.bash
   trunk/paludis/repositories/e/vdb_repository.cc
   trunk/paludis/repositories/gems/installed_gems_repository.cc
   trunk/paludis/set_file.cc
   trunk/paludis/util/files.m4
   trunk/paludis/util/output_wrapper_TEST.cc
   trunk/paludis/util/system.cc
   trunk/paludis/util/system.hh
   trunk/paludis/util/system_TEST.cc
   trunk/paludis/util/system_TEST_setup.sh
   trunk/src/clients/gtkpaludis/libgtkpaludis/messages_page.cc
   trunk/src/clients/gtkpaludis/libgtkpaludis/task_window.cc
Log:
Kill PStream in favour of Command.with_captured_output_stream. Provide sneaky pipe callback support.


Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog	2007-12-30 22:38:02 UTC (rev 4109)
+++ trunk/ChangeLog	2007-12-30 22:38:14 UTC (rev 4110)
@@ -5,6 +5,12 @@
 only listed in SVN log. For a summary of what has changed between releases,
 see the NEWS file. This file is occasionally pruned to ChangeLog.old.bz2.
 
+2007-12-30 Ciaran McCreesh
+
+	* paludis/: Kill PStream in favour of
+	Command.with_captured_stdout_stream. Provide sneaky pipe callback
+	support.
+
 2007-12-30 Piotr Jaroszyński
 
 	* python/: (python) Fix UseRequirements after r4105.

Modified: trunk/paludis/environments/paludis/bashable_conf.cc
===================================================================
--- trunk/paludis/environments/paludis/bashable_conf.cc	2007-12-30 22:38:02 UTC (rev 4109)
+++ trunk/paludis/environments/paludis/bashable_conf.cc	2007-12-30 22:38:14 UTC (rev 4110)
@@ -20,10 +20,10 @@
 #include "bashable_conf.hh"
 #include <paludis/util/config_file.hh>
 #include <paludis/util/fs_entry.hh>
-#include <paludis/util/pstream.hh>
 #include <paludis/util/is_file_with_extension.hh>
 #include <paludis/util/stringify.hh>
 #include <paludis/util/log.hh>
+#include <paludis/util/system.hh>
 
 using namespace paludis;
 using namespace paludis::paludis_environment;
@@ -37,17 +37,19 @@
 
     if (is_file_with_extension(f, ".bash", IsFileWithOptions()))
     {
+        std::stringstream s;
         Command cmd(Command("bash '" + stringify(f) + "'")
                 .with_setenv("PALUDIS_LOG_LEVEL", stringify(Log::get_instance()->log_level()))
                 .with_setenv("PALUDIS_EBUILD_DIR", getenv_with_default("PALUDIS_EBUILD_DIR", LIBEXECDIR "/paludis"))
-                .with_stderr_prefix(f.basename() + "> "));
-        PStream s(cmd);
+                .with_stderr_prefix(f.basename() + "> ")
+                .with_captured_stdout_stream(&s));
+        int exit_status(run_command(cmd));
         result.reset(new LineConfigFile(s, LineConfigFileOptions()));
 
-        if (s.exit_status() != 0)
+        if (exit_status != 0)
         {
             Log::get_instance()->message(ll_warning, lc_context, "Script '" + stringify(f)
-                    + "' returned non-zero exit status '" + stringify(s.exit_status()) + "'");
+                    + "' returned non-zero exit status '" + stringify(exit_status) + "'");
             result.reset();
         }
     }

Modified: trunk/paludis/environments/paludis/paludis_config.cc
===================================================================
--- trunk/paludis/environments/paludis/paludis_config.cc	2007-12-30 22:38:02 UTC (rev 4109)
+++ trunk/paludis/environments/paludis/paludis_config.cc	2007-12-30 22:38:14 UTC (rev 4110)
@@ -32,7 +32,6 @@
 #include <paludis/util/fs_entry.hh>
 #include <paludis/util/is_file_with_extension.hh>
 #include <paludis/util/log.hh>
-#include <paludis/util/pstream.hh>
 #include <paludis/util/stringify.hh>
 #include <paludis/util/sr.hh>
 #include <paludis/util/set.hh>
@@ -148,17 +147,19 @@
             kv.reset(new KeyValueConfigFile(FSEntry(config_dir) / "environment.conf", KeyValueConfigFileOptions()));
         else if ((FSEntry(config_dir) / "environment.bash").exists())
         {
+            std::stringstream s;
             Command cmd(Command("bash '" + stringify(FSEntry(config_dir) / "environment.bash") + "'")
                     .with_setenv("PALUDIS_LOG_LEVEL", stringify(Log::get_instance()->log_level()))
                     .with_setenv("PALUDIS_EBUILD_DIR", getenv_with_default("PALUDIS_EBUILD_DIR", LIBEXECDIR "/paludis"))
-                    .with_stderr_prefix("environment.bash> "));
-            PStream s(cmd);
+                    .with_stderr_prefix("environment.bash> ")
+                    .with_captured_stdout_stream(&s));
+            int exit_status(run_command(cmd));
             kv.reset(new KeyValueConfigFile(s, KeyValueConfigFileOptions()));
 
-            if (s.exit_status() != 0)
+            if (exit_status != 0)
             {
                 Log::get_instance()->message(ll_warning, lc_context, "Script '" + stringify(FSEntry(config_dir) / "environment.bash")
-                        + "' returned non-zero exit status '" + stringify(s.exit_status()) + "'");
+                        + "' returned non-zero exit status '" + stringify(exit_status) + "'");
                 kv.reset();
             }
         }
@@ -300,13 +301,18 @@
         }
         else if ((local_config_dir / "repository_defaults.bash").exists())
         {
+            std::stringstream s;
             Command cmd(Command("bash '" + stringify(local_config_dir / "repository_defaults.bash") + "'")
                     .with_setenv("PALUDIS_LOG_LEVEL", stringify(Log::get_instance()->log_level()))
                     .with_setenv("PALUDIS_EBUILD_DIR", getenv_with_default("PALUDIS_EBUILD_DIR", LIBEXECDIR "/paludis"))
-                    .with_stderr_prefix("repository_defaults.bash> "));
-            PStream s(cmd);
+                    .with_stderr_prefix("repository_defaults.bash> ")
+                    .with_captured_stdout_stream(&s));
+            int exit_status(run_command(cmd));
             KeyValueConfigFile defaults_file(s, KeyValueConfigFileOptions(), KeyValueConfigFile::Defaults(conf_vars));
             std::copy(defaults_file.begin(), defaults_file.end(), conf_vars->inserter());
+            if (exit_status != 0)
+                Log::get_instance()->message(ll_warning, lc_context, "Script '" + stringify(local_config_dir / "repository_defaults.bash")
+                        + "' returned non-zero exit status '" + stringify(exit_status) + "'");
         }
 
         std::list<FSEntry> dirs;
@@ -334,17 +340,19 @@
             tr1::shared_ptr<KeyValueConfigFile> kv;
             if (is_file_with_extension(*repo_file, ".bash", IsFileWithOptions()))
             {
+                std::stringstream s;
                 Command cmd(Command("bash '" + stringify(*repo_file) + "'")
                         .with_setenv("PALUDIS_LOG_LEVEL", stringify(Log::get_instance()->log_level()))
                         .with_setenv("PALUDIS_EBUILD_DIR", getenv_with_default("PALUDIS_EBUILD_DIR", LIBEXECDIR "/paludis"))
-                        .with_stderr_prefix(repo_file->basename() + "> "));
-                PStream s(cmd);
+                        .with_stderr_prefix(repo_file->basename() + "> ")
+                        .with_captured_stdout_stream(&s));
+                int exit_status(run_command(cmd));
                 kv.reset(new KeyValueConfigFile(s, KeyValueConfigFileOptions(), KeyValueConfigFile::Defaults(conf_vars)));
 
-                if (s.exit_status() != 0)
+                if (exit_status != 0)
                 {
                     Log::get_instance()->message(ll_warning, lc_context, "Script '" + stringify(*repo_file)
-                            + "' returned non-zero exit status '" + stringify(s.exit_status()) + "'");
+                            + "' returned non-zero exit status '" + stringify(exit_status) + "'");
                     kv.reset();
                 }
             }

Modified: trunk/paludis/hooker.cc
===================================================================
--- trunk/paludis/hooker.cc	2007-12-30 22:38:02 UTC (rev 4109)
+++ trunk/paludis/hooker.cc	2007-12-30 22:38:14 UTC (rev 4110)
@@ -32,7 +32,6 @@
 #include <paludis/util/private_implementation_pattern-impl.hh>
 #include <paludis/util/strip.hh>
 #include <paludis/util/graph-impl.hh>
-#include <paludis/util/pstream.hh>
 #include <paludis/util/tokeniser.hh>
 #include <paludis/util/mutex.hh>
 #include <paludis/about.hh>
@@ -159,10 +158,11 @@
     std::string output("");
     if (hook.output_dest == hod_grab)
     {
-        PStream s(cmd);
+        std::stringstream s;
+        cmd.with_captured_stdout_stream(&s);
+        exit_status = run_command(cmd);
         output = strip_trailing(std::string((std::istreambuf_iterator<char>(s)), std::istreambuf_iterator<char>()),
                 " \t\n");
-        exit_status = s.exit_status();
     }
     else
         exit_status = run_command(cmd);
@@ -210,10 +210,10 @@
     std::string output("");
     if (hook.output_dest == hod_grab)
     {
-        PStream s(cmd);
+        std::stringstream s;
+        exit_status = run_command(cmd.with_captured_stdout_stream(&s));
         output = strip_trailing(std::string((std::istreambuf_iterator<char>(s)), std::istreambuf_iterator<char>()),
                 " \t\n");
-        exit_status = s.exit_status();
     }
     else
         exit_status = run_command(cmd);
@@ -265,10 +265,13 @@
     for (Hook::ConstIterator x(hook.begin()), x_end(hook.end()) ; x != x_end ; ++x)
         cmd.with_setenv(x->first, x->second);
 
-    PStream s(cmd);
+    std::stringstream s;
+    cmd
+        .with_captured_stdout_stream(&s);
+    int exit_status(run_command(cmd));
+
     std::string deps((std::istreambuf_iterator<char>(s)), std::istreambuf_iterator<char>());
 
-    int exit_status(s.exit_status());
     if (0 == exit_status)
     {
         Log::get_instance()->message(ll_debug, lc_no_context, "Hook dependencies for '" + stringify(file_name())

Modified: trunk/paludis/repositories/e/ebuild/ebuild.bash
===================================================================
--- trunk/paludis/repositories/e/ebuild/ebuild.bash	2007-12-30 22:38:02 UTC (rev 4109)
+++ trunk/paludis/repositories/e/ebuild/ebuild.bash	2007-12-30 22:38:14 UTC (rev 4110)
@@ -221,7 +221,7 @@
             echo "\${!${PALUDIS_CLIENT_UPPER}_CMDLINE_*} ${PALUDIS_CLIENT_UPPER}_OPTIONS" )
         unset -v PALUDIS_CLIENT
 
-        unset -v PALUDIS_HOME PALUDIS_PID ROOT
+        unset -v PALUDIS_HOME PALUDIS_PID PALUDIS_PIPE_COMMAND_WRITE_FD PALUDIS_PIPE_COMMAND_READ_FD ROOT
         unset -v CATEGORY PN PV P PVR PF ${!LD_*}
 
         unset -v ebuild EBUILD

Modified: trunk/paludis/repositories/e/ebuild.cc
===================================================================
--- trunk/paludis/repositories/e/ebuild.cc	2007-12-30 22:38:02 UTC (rev 4109)
+++ trunk/paludis/repositories/e/ebuild.cc	2007-12-30 22:38:14 UTC (rev 4110)
@@ -25,7 +25,6 @@
 
 #include <paludis/util/system.hh>
 #include <paludis/util/strip.hh>
-#include <paludis/util/pstream.hh>
 #include <paludis/util/log.hh>
 #include <paludis/util/sequence.hh>
 #include <paludis/util/options.hh>
@@ -269,7 +268,9 @@
     {
         Context context("When running ebuild command to generate metadata for '" + stringify(*params.package_id) + "':");
 
-        PStream prog(cmd);
+        std::stringstream prog;
+        Command real_cmd(cmd);
+        int exit_status(run_command(real_cmd.with_captured_stdout_stream(&prog)));
         input.assign((std::istreambuf_iterator<char>(prog)), std::istreambuf_iterator<char>());
         std::stringstream input_stream(input);
         KeyValueConfigFile f(input_stream, KeyValueConfigFileOptions() + kvcfo_disallow_continuations + kvcfo_disallow_comments
@@ -277,7 +278,7 @@
                 + kvcfo_disallow_variables + kvcfo_preserve_whitespace);
 
         std::copy(f.begin(), f.end(), keys->inserter());
-        if (0 == prog.exit_status())
+        if (0 == exit_status)
             ok = true;
     }
     catch (const Exception & e)
@@ -433,12 +434,14 @@
 bool
 EbuildVariableCommand::do_run_command(const Command & cmd)
 {
-    PStream prog(cmd);
+    std::stringstream prog;
+    Command real_cmd(cmd);
+    int exit_status(run_command(real_cmd.with_captured_stdout_stream(&prog)));
     _result = strip_trailing_string(
             std::string((std::istreambuf_iterator<char>(prog)),
                 std::istreambuf_iterator<char>()), "\n");
 
-    return (0 == prog.exit_status());
+    return (0 == exit_status);
 }
 
 std::string

Modified: trunk/paludis/repositories/e/vdb_repository.cc
===================================================================
--- trunk/paludis/repositories/e/vdb_repository.cc	2007-12-30 22:38:02 UTC (rev 4109)
+++ trunk/paludis/repositories/e/vdb_repository.cc	2007-12-30 22:38:14 UTC (rev 4110)
@@ -57,7 +57,6 @@
 #include <paludis/util/fs_entry.hh>
 #include <paludis/util/is_file_with_extension.hh>
 #include <paludis/util/log.hh>
-#include <paludis/util/pstream.hh>
 #include <paludis/util/set.hh>
 #include <paludis/util/sequence.hh>
 #include <paludis/util/map.hh>
@@ -778,12 +777,14 @@
     }
     else if ((vdb_dir / "environment.bz2").is_regular_file_or_symlink_to_regular_file())
     {
-        PStream p("bash -c '( bunzip2 < " + stringify(vdb_dir / "environment.bz2" ) +
-                " ; echo echo \\$" + var + " ) | bash 2>/dev/null'");
+        std::stringstream p;
+        Command cmd(Command("bash -c '( bunzip2 < " + stringify(vdb_dir / "environment.bz2" ) +
+                    " ; echo echo \\$" + var + " ) | bash 2>/dev/null'").with_captured_stdout_stream(&p));
+        int exit_status(run_command(cmd));
         std::string result(strip_trailing_string(std::string(
                         (std::istreambuf_iterator<char>(p)),
                         std::istreambuf_iterator<char>()), "\n"));
-        if (0 != p.exit_status())
+        if (0 != exit_status)
             throw ActionError("Could not load environment.bz2");
         return result;
     }

Modified: trunk/paludis/repositories/gems/installed_gems_repository.cc
===================================================================
--- trunk/paludis/repositories/gems/installed_gems_repository.cc	2007-12-30 22:38:02 UTC (rev 4109)
+++ trunk/paludis/repositories/gems/installed_gems_repository.cc	2007-12-30 22:38:14 UTC (rev 4110)
@@ -30,7 +30,6 @@
 #include <paludis/util/sequence.hh>
 #include <paludis/util/is_file_with_extension.hh>
 #include <paludis/util/make_shared_ptr.hh>
-#include <paludis/util/pstream.hh>
 #include <paludis/util/visitor-impl.hh>
 #include <paludis/util/system.hh>
 #include <paludis/util/mutex.hh>

Modified: trunk/paludis/set_file.cc
===================================================================
--- trunk/paludis/set_file.cc	2007-12-30 22:38:02 UTC (rev 4109)
+++ trunk/paludis/set_file.cc	2007-12-30 22:38:14 UTC (rev 4110)
@@ -21,13 +21,13 @@
 #include <paludis/util/fs_entry.hh>
 #include <paludis/util/log.hh>
 #include <paludis/util/tokeniser.hh>
-#include <paludis/util/pstream.hh>
 #include <paludis/util/private_implementation_pattern-impl.hh>
 #include <paludis/util/sequence.hh>
 #include <paludis/util/visitor-impl.hh>
 #include <paludis/util/options.hh>
 #include <paludis/util/mutex.hh>
 #include <paludis/util/config_file.hh>
+#include <paludis/util/system.hh>
 #include <paludis/environment.hh>
 #include <paludis/query.hh>
 #include <paludis/package_database.hh>
@@ -421,14 +421,16 @@
     Context context("When loading paludis bash set file '" + stringify(_p.file_name) + "':");
     _contents.reset(new ConstTreeSequence<SetSpecTree, AllDepSpec>(tr1::shared_ptr<AllDepSpec>(new AllDepSpec)));
 
+    std::stringstream s;
     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);
+            .with_stderr_prefix(_p.file_name.basename() + "> ")
+            .with_captured_stdout_stream(&s));
+    int exit_status(run_command(cmd));
 
     LineConfigFile ff(s, LineConfigFileOptions() + lcfo_disallow_continuations + lcfo_disallow_comments
             + lcfo_no_skip_blank_lines);
@@ -436,10 +438,10 @@
             line != line_end ; ++line)
         do_one_conf_line(*line, _contents, _p);
 
-    if (s.exit_status() != 0)
+    if (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()) + "'");
+                "' returned non-zero exit status '" + stringify(exit_status) + "'");
         _contents.reset(new ConstTreeSequence<SetSpecTree, AllDepSpec>(tr1::shared_ptr<AllDepSpec>(new AllDepSpec)));
     }
 }

Modified: trunk/paludis/util/files.m4
===================================================================
--- trunk/paludis/util/files.m4	2007-12-30 22:38:02 UTC (rev 4109)
+++ trunk/paludis/util/files.m4	2007-12-30 22:38:14 UTC (rev 4110)
@@ -40,7 +40,6 @@
 add(`output_wrapper',                    `test', `testscript')
 add(`pipe',                              `hh', `cc')
 add(`private_implementation_pattern',    `hh', `impl')
-add(`pstream',                           `hh', `cc', `test')
 add(`random',                            `hh', `cc', `test')
 add(`remove_shared_ptr',                 `hh')
 add(`rmd160',                            `hh', `cc', `test')

Modified: trunk/paludis/util/output_wrapper_TEST.cc
===================================================================
--- trunk/paludis/util/output_wrapper_TEST.cc	2007-12-30 22:38:02 UTC (rev 4109)
+++ trunk/paludis/util/output_wrapper_TEST.cc	2007-12-30 22:38:14 UTC (rev 4110)
@@ -20,7 +20,6 @@
 #include <test/test_framework.hh>
 #include <test/test_runner.hh>
 
-#include <paludis/util/pstream.hh>
 #include <paludis/util/system.hh>
 #include <paludis/util/join.hh>
 
@@ -39,11 +38,12 @@
 
         void run()
         {
-            PStream p(Command("./outputwrapper --stdout-prefix 'o p ' -- "
-                        "bash output_wrapper_TEST_dir/stdout_prefix.bash"));
+            std::stringstream p;
+            TEST_CHECK_EQUAL(run_command(Command("./outputwrapper --stdout-prefix 'o p ' -- "
+                            "bash output_wrapper_TEST_dir/stdout_prefix.bash")
+                        .with_captured_stdout_stream(&p)), 0);
             std::string s((std::istreambuf_iterator<char>(p)), std::istreambuf_iterator<char>());
             TEST_CHECK_EQUAL(s, "o p one\no p two\no p three\n");
-            TEST_CHECK_EQUAL(0, p.exit_status());
         }
     } test_stdout_prefix;
 
@@ -53,11 +53,12 @@
 
         void run()
         {
-            PStream p(Command("./outputwrapper --stderr-prefix 'e p ' -- "
-                        "bash output_wrapper_TEST_dir/stderr_prefix.bash 2>&1"));
+            std::stringstream p;
+            TEST_CHECK_EQUAL(0, run_command(Command("./outputwrapper --stderr-prefix 'e p ' -- "
+                            "bash output_wrapper_TEST_dir/stderr_prefix.bash 2>&1")
+                        .with_captured_stdout_stream(&p)));
             std::string s((std::istreambuf_iterator<char>(p)), std::istreambuf_iterator<char>());
             TEST_CHECK_EQUAL(s, "e p one\ne p two\ne p three\n");
-            TEST_CHECK_EQUAL(0, p.exit_status());
         }
     } test_stderr_prefix;
 
@@ -67,8 +68,10 @@
 
         void run()
         {
-            PStream p(Command("./outputwrapper --stdout-prefix 'o p ' --stderr-prefix 'e p ' -- "
-                        "bash output_wrapper_TEST_dir/mixed_prefix.bash 2>&1"));
+            std::stringstream p;
+            TEST_CHECK_EQUAL(run_command(Command("./outputwrapper --stdout-prefix 'o p ' --stderr-prefix 'e p ' -- "
+                            "bash output_wrapper_TEST_dir/mixed_prefix.bash 2>&1")
+                        .with_captured_stdout_stream(&p)), 0);
             std::multiset<std::string> lines;
             std::string line;
             while (std::getline(p, line))
@@ -79,7 +82,6 @@
             TEST_CHECK(lines.count("e p one"));
             TEST_CHECK(lines.count("e p three"));
             TEST_CHECK(lines.count("o p two"));
-            TEST_CHECK_EQUAL(0, p.exit_status());
         }
     } test_mixed_prefix;
 
@@ -89,7 +91,9 @@
 
         void run()
         {
-            PStream p(Command("bash output_wrapper_TEST_dir/long_lines.bash"));
+            std::stringstream p;
+            TEST_CHECK_EQUAL(run_command(Command("bash output_wrapper_TEST_dir/long_lines.bash")
+                        .with_captured_stdout_stream(&p)), 0);
             std::multiset<std::string> lines;
             std::string line;
             while (std::getline(p, line))
@@ -99,7 +103,6 @@
             TEST_CHECK_EQUAL(lines.size(), static_cast<std::size_t>(2));
             TEST_CHECK(lines.count("e p " + std::string(10000, 'e')));
             TEST_CHECK(lines.count("o p " + std::string(10000, 'o')));
-            TEST_CHECK_EQUAL(0, p.exit_status());
         }
     } test_long_lines;
 
@@ -109,7 +112,9 @@
 
         void run()
         {
-            PStream p(Command("bash output_wrapper_TEST_dir/no_trailing_newlines.bash 2>&1"));
+            std::stringstream p;
+            TEST_CHECK_EQUAL(0, run_command(Command("bash output_wrapper_TEST_dir/no_trailing_newlines.bash 2>&1")
+                        .with_captured_stdout_stream(&p)));
             std::multiset<std::string> lines;
             std::string line;
             while (std::getline(p, line))
@@ -118,7 +123,6 @@
             TestMessageSuffix s("lines=(" + join(lines.begin(), lines.end(), ",") + ")");
             TEST_CHECK_EQUAL(lines.size(), static_cast<std::size_t>(1));
             TEST_CHECK(lines.count("o p monkeye p pants"));
-            TEST_CHECK_EQUAL(0, p.exit_status());
         }
     } test_no_trailing_newline;
 
@@ -128,8 +132,10 @@
 
         void run()
         {
-            PStream p(Command("./outputwrapper --stdout-prefix 'o p ' --stderr-prefix 'e p ' -- "
-                        "bash output_wrapper_TEST_dir/exit_status.bash 2>&1"));
+            std::stringstream p;
+            TEST_CHECK_EQUAL(5, run_command(Command("./outputwrapper --stdout-prefix 'o p ' --stderr-prefix 'e p ' -- "
+                            "bash output_wrapper_TEST_dir/exit_status.bash 2>&1")
+                        .with_captured_stdout_stream(&p)));
             std::multiset<std::string> lines;
             std::string line;
             while (std::getline(p, line))
@@ -139,7 +145,6 @@
             TEST_CHECK_EQUAL(lines.size(), static_cast<std::size_t>(2));
             TEST_CHECK(lines.count("o p lorem ipsum dolor"));
             TEST_CHECK(lines.count("e p sit amet"));
-            TEST_CHECK_EQUAL(5, p.exit_status());
         }
     } test_exit_status;
 
@@ -149,8 +154,10 @@
 
         void run()
         {
-            PStream p(Command("./outputwrapper --stdout-prefix 'o p ' --stderr-prefix 'e p ' -- "
-                        "bash output_wrapper_TEST_dir/no_wrap_blanks.bash 2>&1"));
+            std::stringstream p;
+            TEST_CHECK_EQUAL(0, run_command(Command("./outputwrapper --stdout-prefix 'o p ' --stderr-prefix 'e p ' -- "
+                            "bash output_wrapper_TEST_dir/no_wrap_blanks.bash 2>&1")
+                        .with_captured_stdout_stream(&p)));
             std::multiset<std::string> lines;
             std::string line;
             while (std::getline(p, line))
@@ -162,7 +169,6 @@
             TEST_CHECK(lines.count("e p three"));
             TEST_CHECK(lines.count("o p two"));
             TEST_CHECK_EQUAL(lines.count(""), static_cast<std::size_t>(4));
-            TEST_CHECK_EQUAL(0, p.exit_status());
         }
     } test_no_wrap_blanks;
 
@@ -172,8 +178,10 @@
 
         void run()
         {
-            PStream p(Command("./outputwrapper --stdout-prefix 'o p ' --stderr-prefix 'e p ' --wrap-blanks -- "
-                        "bash output_wrapper_TEST_dir/wrap_blanks.bash 2>&1"));
+            std::stringstream p;
+            TEST_CHECK_EQUAL(0, run_command(Command("./outputwrapper --stdout-prefix 'o p ' --stderr-prefix 'e p ' --wrap-blanks -- "
+                            "bash output_wrapper_TEST_dir/wrap_blanks.bash 2>&1")
+                        .with_captured_stdout_stream(&p)));
             std::multiset<std::string> lines;
             std::string line;
             while (std::getline(p, line))
@@ -186,7 +194,6 @@
             TEST_CHECK(lines.count("o p two"));
             TEST_CHECK_EQUAL(lines.count("e p "), static_cast<std::size_t>(2));
             TEST_CHECK_EQUAL(lines.count("o p "), static_cast<std::size_t>(2));
-            TEST_CHECK_EQUAL(0, p.exit_status());
         }
     } test_wrap_blanks;
 
@@ -196,8 +203,10 @@
 
         void run()
         {
-            PStream p(Command("./outputwrapper --stdout-prefix 'o p ' --stderr-prefix 'e p ' --discard-blank-output -- "
-                        "bash output_wrapper_TEST_dir/discard_blank_output.bash 2>&1"));
+            std::stringstream p;
+            TEST_CHECK_EQUAL(0, run_command(Command("./outputwrapper --stdout-prefix 'o p ' --stderr-prefix 'e p ' --discard-blank-output -- "
+                            "bash output_wrapper_TEST_dir/discard_blank_output.bash 2>&1")
+                        .with_captured_stdout_stream(&p)));
             std::multiset<std::string> lines;
             std::string line;
             while (std::getline(p, line))
@@ -205,7 +214,6 @@
 
             TestMessageSuffix s("lines=(" + join(lines.begin(), lines.end(), ",") + ")");
             TEST_CHECK_EQUAL(lines.size(), static_cast<std::size_t>(0));
-            TEST_CHECK_EQUAL(0, p.exit_status());
         }
     } test_discard_blank_output;
 
@@ -215,8 +223,10 @@
 
         void run()
         {
-            PStream p(Command("./outputwrapper --stdout-prefix 'o p ' --stderr-prefix 'e p ' --discard-blank-output -- "
-                        "bash output_wrapper_TEST_dir/discard_blank_output_not_blank.bash 2>&1"));
+            std::stringstream p;
+            TEST_CHECK_EQUAL(0, run_command(Command("./outputwrapper --stdout-prefix 'o p ' --stderr-prefix 'e p ' --discard-blank-output -- "
+                            "bash output_wrapper_TEST_dir/discard_blank_output_not_blank.bash 2>&1")
+                        .with_captured_stdout_stream(&p)));
             std::multiset<std::string> lines;
             std::string line;
             while (std::getline(p, line))
@@ -226,7 +236,6 @@
             TEST_CHECK_EQUAL(lines.size(), static_cast<std::size_t>(4));
             TEST_CHECK(lines.count("o p monkey"));
             TEST_CHECK_EQUAL(lines.count(""), static_cast<std::size_t>(3));
-            TEST_CHECK_EQUAL(0, p.exit_status());
         }
     } test_discard_blank_output_not_blank;
 
@@ -236,8 +245,11 @@
 
         void run()
         {
-            PStream p(Command("./outputwrapper --stdout-prefix 'o p ' --stderr-prefix 'e p ' --discard-blank-output --wrap-blanks -- "
-                        "bash output_wrapper_TEST_dir/discard_wrap_blank_output.bash 2>&1"));
+            std::stringstream p;
+            TEST_CHECK_EQUAL(0, run_command(Command(
+                            "./outputwrapper --stdout-prefix 'o p ' --stderr-prefix 'e p ' --discard-blank-output --wrap-blanks -- "
+                            "bash output_wrapper_TEST_dir/discard_wrap_blank_output.bash 2>&1")
+                        .with_captured_stdout_stream(&p)));
             std::multiset<std::string> lines;
             std::string line;
             while (std::getline(p, line))
@@ -245,7 +257,6 @@
 
             TestMessageSuffix s("lines=(" + join(lines.begin(), lines.end(), ",") + ")");
             TEST_CHECK_EQUAL(lines.size(), static_cast<std::size_t>(0));
-            TEST_CHECK_EQUAL(0, p.exit_status());
         }
     } test_discard_wrap_blank_output;
 
@@ -255,8 +266,11 @@
 
         void run()
         {
-            PStream p(Command("./outputwrapper --stdout-prefix 'o p ' --stderr-prefix 'e p ' --discard-blank-output --wrap-blanks -- "
-                        "bash output_wrapper_TEST_dir/discard_wrap_blank_output_not_blank.bash 2>&1"));
+            std::stringstream p;
+            TEST_CHECK_EQUAL(0, run_command(
+                        Command("./outputwrapper --stdout-prefix 'o p ' --stderr-prefix 'e p ' --discard-blank-output --wrap-blanks -- "
+                            "bash output_wrapper_TEST_dir/discard_wrap_blank_output_not_blank.bash 2>&1")
+                        .with_captured_stdout_stream(&p)));
             std::multiset<std::string> lines;
             std::string line;
             while (std::getline(p, line))
@@ -266,7 +280,6 @@
             TEST_CHECK_EQUAL(lines.size(), static_cast<std::size_t>(4));
             TEST_CHECK(lines.count("o p monkey"));
             TEST_CHECK_EQUAL(lines.count("o p "), static_cast<std::size_t>(3));
-            TEST_CHECK_EQUAL(0, p.exit_status());
         }
     } test_discard_wrap_blank_output_not_blank;
 
@@ -276,8 +289,10 @@
 
         void run()
         {
-            PStream p(Command("./outputwrapper --stdout-prefix 'o p ' --stderr-prefix 'e p ' -- "
-                        "bash output_wrapper_TEST_dir/carriage_return.bash 2>&1"));
+            std::stringstream p;
+            TEST_CHECK_EQUAL(0, run_command(Command("./outputwrapper --stdout-prefix 'o p ' --stderr-prefix 'e p ' -- "
+                            "bash output_wrapper_TEST_dir/carriage_return.bash 2>&1")
+                        .with_captured_stdout_stream(&p)));
             std::multiset<std::string> lines;
             std::string line;
             while (std::getline(p, line))
@@ -299,7 +314,6 @@
             TEST_CHECK(lines.count("e p foo\\r\\re p bar"));
             TEST_CHECK(lines.count("\\re p foo"));
             TEST_CHECK_EQUAL(lines.count("\\r"), static_cast<std::size_t>(2));
-            TEST_CHECK_EQUAL(0, p.exit_status());
         }
     } test_carriage_return;
 
@@ -309,8 +323,10 @@
 
         void run()
         {
-            PStream p(Command("./outputwrapper --stdout-prefix 'o p ' --stderr-prefix 'e p ' --wrap-blanks -- "
-                        "bash output_wrapper_TEST_dir/carriage_return.bash 2>&1"));
+            std::stringstream p;
+            TEST_CHECK_EQUAL(0, run_command(Command("./outputwrapper --stdout-prefix 'o p ' --stderr-prefix 'e p ' --wrap-blanks -- "
+                        "bash output_wrapper_TEST_dir/carriage_return.bash 2>&1")
+                        .with_captured_stdout_stream(&p)));
             std::multiset<std::string> lines;
             std::string line;
             while (std::getline(p, line))
@@ -333,7 +349,6 @@
             TEST_CHECK(lines.count("e p foo\\re p \\re p bar"));
             TEST_CHECK(lines.count("e p \\re p foo"));
             TEST_CHECK(lines.count("e p \\re p "));
-            TEST_CHECK_EQUAL(0, p.exit_status());
         }
     } test_carriage_return_wrap_blank;
 
@@ -343,8 +358,10 @@
 
         void run()
         {
-            PStream p(Command("./outputwrapper --stdout-prefix 'o p ' --stderr-prefix 'e p ' --discard-blank-output -- "
-                        "bash output_wrapper_TEST_dir/carriage_return_blank.bash 2>&1"));
+            std::stringstream p;
+            TEST_CHECK_EQUAL(0, run_command(Command("./outputwrapper --stdout-prefix 'o p ' --stderr-prefix 'e p ' --discard-blank-output -- "
+                            "bash output_wrapper_TEST_dir/carriage_return_blank.bash 2>&1")
+                        .with_captured_stdout_stream(&p)));
             std::multiset<std::string> lines;
             std::string line;
             while (std::getline(p, line))
@@ -366,8 +383,11 @@
 
         void run()
         {
-            PStream p(Command("./outputwrapper --stdout-prefix 'o p ' --stderr-prefix 'e p ' --discard-blank-output --wrap-blanks -- "
-                        "bash output_wrapper_TEST_dir/carriage_return_blank.bash 2>&1"));
+            std::stringstream p;
+            TEST_CHECK_EQUAL(0, run_command(
+                        Command("./outputwrapper --stdout-prefix 'o p ' --stderr-prefix 'e p ' --discard-blank-output --wrap-blanks -- "
+                            "bash output_wrapper_TEST_dir/carriage_return_blank.bash 2>&1")
+                        .with_captured_stdout_stream(&p)));
             std::multiset<std::string> lines;
             std::string line;
             while (std::getline(p, line))
@@ -389,8 +409,10 @@
 
         void run()
         {
-            PStream p(Command("./outputwrapper --stdout-prefix 'o p ' --stderr-prefix 'e p ' --discard-blank-output -- "
-                        "bash output_wrapper_TEST_dir/carriage_return_nonblank.bash 2>&1"));
+            std::stringstream p;
+            TEST_CHECK_EQUAL(0, run_command(Command("./outputwrapper --stdout-prefix 'o p ' --stderr-prefix 'e p ' --discard-blank-output -- "
+                            "bash output_wrapper_TEST_dir/carriage_return_nonblank.bash 2>&1")
+                        .with_captured_stdout_stream(&p)));
             std::multiset<std::string> lines;
             std::string line;
             while (std::getline(p, line))
@@ -415,8 +437,11 @@
 
         void run()
         {
-            PStream p(Command("./outputwrapper --stdout-prefix 'o p ' --stderr-prefix 'e p ' --discard-blank-output --wrap-blanks -- "
-                        "bash output_wrapper_TEST_dir/carriage_return_nonblank.bash 2>&1"));
+            std::stringstream p;
+            TEST_CHECK_EQUAL(0, run_command(
+                        Command("./outputwrapper --stdout-prefix 'o p ' --stderr-prefix 'e p ' --discard-blank-output --wrap-blanks -- "
+                            "bash output_wrapper_TEST_dir/carriage_return_nonblank.bash 2>&1")
+                        .with_captured_stdout_stream(&p)));
             std::multiset<std::string> lines;
             std::string line;
             while (std::getline(p, line))

Deleted: trunk/paludis/util/pstream.cc
===================================================================
--- trunk/paludis/util/pstream.cc	2007-12-30 22:38:02 UTC (rev 4109)
+++ trunk/paludis/util/pstream.cc	2007-12-30 22:38:14 UTC (rev 4110)
@@ -1,209 +0,0 @@
-/* vim: set sw=4 sts=4 et foldmethod=syntax : */
-
-/*
- * Copyright (c) 2006, 2007 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/util/log.hh>
-#include <paludis/util/pstream.hh>
-#include <paludis/util/stringify.hh>
-#include <paludis/util/wrapped_forward_iterator.hh>
-
-#include <cstring>
-#include <iostream>
-#include <errno.h>
-#include <sys/utsname.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <grp.h>
-
-using namespace paludis;
-
-PStreamError::PStreamError(const std::string & our_message) throw () :
-    Exception(our_message)
-{
-}
-
-PStreamInBuf::int_type
-PStreamInBuf::underflow()
-{
-    if (gptr() < egptr())
-        return *gptr();
-
-    int num_putback = gptr() - eback();
-    if (num_putback > putback_size)
-        num_putback = putback_size;
-    std::memmove(buffer + putback_size - num_putback,
-            gptr() - num_putback, num_putback);
-
-    ssize_t n = read(fd, buffer + putback_size, buffer_size - putback_size);
-    if (n == 0)
-        return EOF;
-    else if (n < 0)
-        throw PStreamError("read returned error " + stringify(strerror(errno)) + ", fd is " + stringify(fd));
-
-    setg(buffer + putback_size - num_putback, buffer + putback_size, buffer + putback_size + n);
-
-    return *gptr();
-}
-
-PStreamInBuf::PStreamInBuf(const Command & cmd) :
-    _command(cmd)
-{
-    Context context("When running command '" + stringify(cmd.command()) + "' asynchronously:");
-
-    std::string extras;
-
-    if (! cmd.chdir().empty())
-        extras.append(" [chdir " + cmd.chdir() + "]");
-
-    for (Command::ConstIterator s(cmd.begin_setenvs()), s_end(cmd.end_setenvs()) ; s != s_end ; ++s)
-        extras.append(" [setenv " + s->first + "=" + s->second + "]");
-
-    if (cmd.gid() && *cmd.gid() != getgid())
-        extras.append(" [setgid " + stringify(*cmd.gid()) + "]");
-
-    if (cmd.uid() && *cmd.uid() != getuid())
-        extras.append(" [setuid " + stringify(*cmd.uid()) + "]");
-
-    extras.append(" [stdout_pipe " + stringify(stdout_pipe.read_fd()) + ", " + stringify(stdout_pipe.write_fd()) + "]");
-
-    std::string c(cmd.command());
-
-    if ((! cmd.stdout_prefix().empty()) || (! cmd.stderr_prefix().empty()))
-        c = getenv_with_default("PALUDIS_OUTPUTWRAPPER_DIR", LIBEXECDIR "/paludis/utils") + "/outputwrapper --stdout-prefix '"
-            + cmd.stdout_prefix() + "' --stderr-prefix '" + cmd.stderr_prefix() + "' "
-            + (cmd.prefix_discard_blank_output() ? " --discard-blank-output " : "")
-            + (cmd.prefix_blank_lines() ? " --wrap-blanks " : "")
-            + " -- " + c;
-
-    cmd.echo_to_stderr();
-    Log::get_instance()->message(ll_debug, lc_context, "execl /bin/sh -c " + c + " " + extras);
-
-    child = fork();
-
-    if (0 == child)
-    {
-        try
-        {
-            if (! cmd.chdir().empty())
-                if (-1 == chdir(cmd.chdir().c_str()))
-                    throw RunCommandError("chdir failed: " + stringify(strerror(errno)));
-
-            for (Command::ConstIterator s(cmd.begin_setenvs()), s_end(cmd.end_setenvs()) ; s != s_end ; ++s)
-                setenv(s->first.c_str(), s->second.c_str(), 1);
-
-            if (-1 == dup2(stdout_pipe.write_fd(), 1))
-                throw PStreamError("dup2 failed");
-            close(stdout_pipe.read_fd());
-
-            if (-1 != PStream::stderr_fd)
-            {
-                if (-1 == dup2(PStream::stderr_fd, 2))
-                    throw PStreamError("dup2 failed");
-
-                if (-1 != PStream::stderr_close_fd)
-                    close(PStream::stderr_close_fd);
-            }
-
-            if (cmd.gid() && *cmd.gid() != getgid())
-            {
-                gid_t g(*cmd.gid());
-
-                if (0 != ::setgid(*cmd.gid()))
-                    std::cerr << "setgid(" << *cmd.gid() << ") failed for exec of '" << c << "': "
-                        << strerror(errno) << std::endl;
-                else if (0 != ::setgroups(1, &g))
-                    std::cerr << "setgroups failed for exec of '" << c << "': " << strerror(errno) << std::endl;
-            }
-
-            if (cmd.uid() && *cmd.uid() != getuid())
-                if (0 != ::setuid(*cmd.uid()))
-                    std::cerr << "setuid(" << *cmd.uid() << ") failed for exec of '" << c << "': "
-                        << strerror(errno) << std::endl;
-
-            execl("/bin/sh", "sh", "-c", c.c_str(), static_cast<char *>(0));
-            throw PStreamError("execl /bin/sh -c '" + c + "' failed:"
-                    + stringify(strerror(errno)));
-        }
-        catch (const Exception & e)
-        {
-            std::cerr << "exec of '" << c << "' failed due to exception '" << e.message()
-                << "' (" << e.what() << ")" << std::endl;
-            exit(123);
-        }
-        catch (...)
-        {
-            std::cerr << "exec of '" << c << "' failed due to unknown exception" << std::endl;
-            exit(124);
-        }
-    }
-    else if (-1 == child)
-        throw PStreamError("fork failed: " + stringify(strerror(errno)));
-    else
-    {
-        close(stdout_pipe.write_fd());
-        stdout_pipe.clear_write_fd();
-        fd = stdout_pipe.read_fd();
-    }
-
-    setg(buffer + putback_size, buffer + putback_size, buffer + putback_size);
-}
-
-PStreamInBuf::~PStreamInBuf()
-{
-    Context context("When destroying PStream process with fd '" + stringify(fd) + "':");
-
-    if (0 != fd)
-    {
-        int fdn(fd), x;
-        if (-1 == waitpid(child, &x, 0))
-            throw PStreamError("waitpid returned error " + stringify(strerror(errno)) + ", fd is " + stringify(fd));
-        Log::get_instance()->message(ll_debug, lc_context) << "waitpid " << fdn << " for destructor -> " <<
-            (WIFSIGNALED(x) ? "signal " + stringify(WTERMSIG(x) + 128) : "exit status " + stringify(WEXITSTATUS(x)));
-    }
-}
-
-int
-PStreamInBuf::exit_status()
-{
-    Context context("When requesting exit status for PStream process with fd '" + stringify(fd) + "':");
-    if (0 != fd)
-    {
-        int fdn(fd);
-        if (-1 == waitpid(child, &_exit_status, 0))
-            throw PStreamError("waitpid returned error " + stringify(strerror(errno)) + ", fd is " + stringify(fd));
-        fd = 0;
-        Log::get_instance()->message(ll_debug, lc_context) << "waitpid " << fdn << " for exit_status() -> " <<
-            (WIFSIGNALED(_exit_status) ? "signal " + stringify(WTERMSIG(_exit_status) + 128) : "exit status " + stringify(WEXITSTATUS(_exit_status)));
-    }
-    return WIFSIGNALED(_exit_status) ? WTERMSIG(_exit_status) + 128 : WEXITSTATUS(_exit_status);
-}
-
-void
-PStream::set_stderr_fd(const int fd, const int fd2)
-{
-    _stderr_fd = fd;
-    _stderr_close_fd = fd2;
-}
-
-int PStream::_stderr_fd(-1);
-const int & PStream::stderr_fd(_stderr_fd);
-
-int PStream::_stderr_close_fd(-1);
-const int & PStream::stderr_close_fd(_stderr_close_fd);
-

Deleted: trunk/paludis/util/pstream.hh
===================================================================
--- trunk/paludis/util/pstream.hh	2007-12-30 22:38:02 UTC (rev 4109)
+++ trunk/paludis/util/pstream.hh	2007-12-30 22:38:14 UTC (rev 4110)
@@ -1,236 +0,0 @@
-/* vim: set sw=4 sts=4 et foldmethod=syntax : */
-
-/*
- * Copyright (c) 2006, 2007 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
- */
-
-#ifndef PALUDIS_GUARD_PALUDIS_PSTREAM_HH
-#define PALUDIS_GUARD_PALUDIS_PSTREAM_HH 1
-
-#include <cstdio>
-#include <istream>
-#include <limits>
-#include <paludis/util/exception.hh>
-#include <paludis/util/instantiation_policy.hh>
-#include <paludis/util/pipe.hh>
-#include <paludis/util/system.hh>
-#include <streambuf>
-#include <string>
-
-/** \file
- * Declarations for the PStream and PStreamInBuf classes, and related
- * utilities.
- *
- * \ingroup g_system
- *
- * \section Examples
- *
- * - None at this time.
- */
-
-namespace paludis
-{
-    /**
-     * Thrown if a PStream or PStreamInBuf encounters an error.
-     *
-     * \ingroup g_system
-     * \ingroup g_exceptions
-     * \nosubgrouping
-     */
-    class PALUDIS_VISIBLE PStreamError :
-        public Exception
-    {
-        public:
-            ///\name Basic operations
-            ///\{
-
-            PStreamError(const std::string & message) throw ();
-
-            ///\}
-    };
-
-    /**
-     * Input buffer class for a process, invoked using popen(3).
-     *
-     * Bidirectional I/O isn't supported since we haven't needed it yet, and
-     * because popen on Linux is unidirectional.
-     *
-     * See \ref TCppSL Ch. 13.13 for what we're doing here. The buffer code is
-     * based upon the "io/inbuf1.hpp" example in section 13.13.3.
-     *
-     * \ingroup g_system
-     * \nosubgrouping
-     */
-    class PALUDIS_VISIBLE PStreamInBuf :
-        public std::streambuf,
-        private InstantiationPolicy<PStreamInBuf, instantiation_method::NonCopyableTag>
-    {
-        private:
-            Pipe stdout_pipe;
-
-            Command _command;
-
-            int _exit_status;
-
-            pid_t child;
-
-        protected:
-            /**
-             * Our file descriptor.
-             */
-            int fd;
-
-            /**
-             * At most how many characters can we put back?
-             */
-            static const int putback_size = std::numeric_limits<unsigned>::digits >> 3;
-
-            /**
-             * How large is our internal buffer?
-             */
-            static const int buffer_size = 3 * putback_size;
-
-            /**
-             * Internal buffer.
-             */
-            char buffer[buffer_size];
-
-            /**
-             * Called when an underflow occurs.
-             */
-            virtual int_type underflow();
-
-        public:
-            ///\name Basic operations
-            ///\{
-
-            /**
-             * Constructor.
-             *
-             * \param command The command to run. See PStream for discussion.
-             */
-            PStreamInBuf(const Command & command);
-
-            ~PStreamInBuf();
-
-            ///\}
-
-            /**
-             * What was our command?
-             */
-            const Command & command() const
-            {
-                return _command;
-            }
-
-            /**
-             * What is our exit status?
-             */
-            int exit_status();
-    };
-
-    /**
-     * For internal use by PStream classes.
-     *
-     * \ingroup g_system
-     */
-    namespace pstream_internals
-    {
-        /**
-         * Avoid base from member issues for PStream.
-         *
-         * \ingroup g_system
-         */
-        struct PALUDIS_VISIBLE PStreamInBufBase :
-            private paludis::InstantiationPolicy<PStreamInBufBase, instantiation_method::NonCopyableTag>
-        {
-            /**
-             * Our buffer.
-             */
-            PStreamInBuf buf;
-
-            /**
-             * Constructor.
-             */
-            PStreamInBufBase(const Command & command) :
-                buf(command)
-            {
-            }
-        };
-    }
-
-    /**
-     * A PStream class is a standard input stream class whose contents comes
-     * from the output of an executed command.
-     *
-     * \ingroup g_system
-     * \nosubgrouping
-     */
-    class PALUDIS_VISIBLE PStream :
-        private InstantiationPolicy<PStream, instantiation_method::NonCopyableTag>,
-        protected pstream_internals::PStreamInBufBase,
-        public std::istream
-    {
-        private:
-            static int _stderr_fd;
-            static int _stderr_close_fd;
-
-        public:
-            ///\name Basic operations
-            ///\{
-
-            /**
-             * Constructor.
-             *
-             * \param command The command to execute. PATH is used, so there is
-             * usually no need to specify a full path. Arguments can be passed
-             * as part of the command.
-             */
-            PStream(const Command & command) :
-                PStreamInBufBase(command),
-                std::istream(&buf)
-            {
-            }
-
-            ///\}
-
-            /**
-             * What is our exit status?
-             */
-            int exit_status()
-            {
-                return buf.exit_status();
-            }
-
-            /**
-             * Set a file descriptors to use for stderr and close on stderr
-             * (for all PStream instances).
-             */
-            static void set_stderr_fd(const int, const int);
-
-            /**
-             * File descriptor to use for stderr.
-             */
-            static const int & stderr_fd;
-
-            /**
-             * File descriptor to close for stderr.
-             */
-            static const int & stderr_close_fd;
-    };
-}
-
-#endif

Deleted: trunk/paludis/util/pstream_TEST.cc
===================================================================
--- trunk/paludis/util/pstream_TEST.cc	2007-12-30 22:38:02 UTC (rev 4109)
+++ trunk/paludis/util/pstream_TEST.cc	2007-12-30 22:38:14 UTC (rev 4110)
@@ -1,122 +0,0 @@
-/* vim: set sw=4 sts=4 et foldmethod=syntax : */
-
-/*
- * Copyright (c) 2006, 2007 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/util/pstream.hh>
-#include <test/test_framework.hh>
-#include <test/test_runner.hh>
-
-using namespace paludis;
-using namespace test;
-
-/** \file
- * Tests for PStream.
- *
- */
-
-namespace test_cases
-{
-    /**
-     * \test Test PStream on a normal command.
-     *
-     */
-    struct PStreamTest : TestCase
-    {
-        PStreamTest() : TestCase("pstream") { }
-
-        void run()
-        {
-            PStream * p;
-            TEST_CHECK((p = new PStream("echo hi")));
-            std::string line;
-            TEST_CHECK(std::getline(*p, line));
-            TEST_CHECK_EQUAL(line, "hi");
-            TEST_CHECK(! std::getline(*p, line));
-            TEST_CHECK_EQUAL(p->exit_status(), 0);
-            delete p;
-        }
-    } test_pstream;
-
-    /**
-     * \test Test PStream on a command that doesn't exist.
-     *
-     */
-    struct PStreamNoExistTest : TestCase
-    {
-        PStreamNoExistTest() : TestCase("pstream nonexistent command") { }
-
-        void run()
-        {
-            PStream p("thiscommanddoesnotexist 2>/dev/null");
-            TEST_CHECK(p.exit_status() != 0);
-        }
-    } test_pstream_no_exist;
-
-    /**
-     * \test Test PStream on a command that returns a failure with no output.
-     *
-     */
-    struct PStreamSilentFailTest : TestCase
-    {
-        PStreamSilentFailTest() : TestCase("pstream silent fail") { }
-
-        void run()
-        {
-            PStream p("test -e /doesnotexist");
-            TEST_CHECK(p.exit_status() != 0);
-        }
-    } test_pstream_silent_fail;
-
-    /**
-     * \test Test PStream on a command that fails with output.
-     *
-     */
-    struct PStreamFailTest : TestCase
-    {
-        PStreamFailTest() : TestCase("pstream fail") { }
-
-        void run()
-        {
-            PStream * p;
-            TEST_CHECK((p = new PStream("cat /doesnotexist 2>&1")));
-            std::string line;
-            TEST_CHECK(std::getline(*p, line));
-            TEST_CHECK(! line.empty());
-            TEST_CHECK(! std::getline(*p, line));
-            TEST_CHECK(p->exit_status() != 0);
-            delete p;
-        }
-    } test_pstream_fail;
-
-    struct PStreamParallelTest : TestCase
-    {
-        PStreamParallelTest() : TestCase("pstream parallel") { }
-
-        void run()
-        {
-            PStream one("echo one ; sleep 3 ; echo one"),
-                    two("echo two ; sleep 2 ; echo two "),
-                    three("echo three ; sleep 1 ; echo three");
-
-            TEST_CHECK_EQUAL(std::string((std::istreambuf_iterator<char>(three)), std::istreambuf_iterator<char>()), "three\nthree\n");
-            TEST_CHECK_EQUAL(std::string((std::istreambuf_iterator<char>(one)), std::istreambuf_iterator<char>()), "one\none\n");
-            TEST_CHECK_EQUAL(std::string((std::istreambuf_iterator<char>(two)), std::istreambuf_iterator<char>()), "two\ntwo\n");
-        }
-    } test_pstream_parallel;
-}
-

Modified: trunk/paludis/util/system.cc
===================================================================
--- trunk/paludis/util/system.cc	2007-12-30 22:38:02 UTC (rev 4109)
+++ trunk/paludis/util/system.cc	2007-12-30 22:38:14 UTC (rev 4110)
@@ -25,10 +25,15 @@
 #include <paludis/util/destringify.hh>
 #include <paludis/util/private_implementation_pattern-impl.hh>
 #include <paludis/util/wrapped_forward_iterator-impl.hh>
+#include <paludis/util/destringify.hh>
+#include <paludis/util/strip.hh>
+#include <paludis/util/fd_output_stream.hh>
+#include <paludis/util/pipe.hh>
 
 #include <sys/utsname.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <sys/select.h>
 #include <unistd.h>
 #include <errno.h>
 #include <grp.h>
@@ -66,7 +71,7 @@
     }
 
 
-    static pid_t paludis_pid(get_paludis_pid());
+    static pid_t paludis_pid PALUDIS_ATTRIBUTE((used)) = get_paludis_pid();
 }
 
 void
@@ -147,6 +152,8 @@
         std::string stderr_prefix;
         bool prefix_discard_blank_output;
         bool prefix_blank_lines;
+        tr1::function<std::string (const std::string &)> pipe_command_handler;
+        std::ostream * captured_stdout_stream;
 
         Implementation(const std::string & c,
                 const std::map<std::string, std::string> & s = (std::map<std::string, std::string>()),
@@ -154,7 +161,9 @@
                 tr1::shared_ptr<uid_t> u = tr1::shared_ptr<uid_t>(),
                 tr1::shared_ptr<gid_t> g = tr1::shared_ptr<gid_t>(),
                 const std::string & p = "", const std::string & q = "",
-                const bool b = false, const bool bb = false) :
+                const bool b = false, const bool bb = false,
+                const tr1::function<std::string (const std::string &)> & h = tr1::function<std::string (const std::string &)>(),
+                std::ostream * cs = 0) :
             command(c),
             setenv_values(s),
             chdir(d),
@@ -164,7 +173,9 @@
             stdout_prefix(p),
             stderr_prefix(q),
             prefix_discard_blank_output(b),
-            prefix_blank_lines(bb)
+            prefix_blank_lines(bb),
+            pipe_command_handler(h),
+            captured_stdout_stream(cs)
         {
         }
     };
@@ -185,7 +196,7 @@
                 other._imp->setenv_values, other._imp->chdir, other._imp->echo_to_stderr,
                 other._imp->uid, other._imp->gid, other._imp->stdout_prefix, other._imp->stderr_prefix,
                 other._imp->prefix_discard_blank_output,
-                other._imp->prefix_blank_lines))
+                other._imp->prefix_blank_lines, other._imp->pipe_command_handler, other._imp->captured_stdout_stream))
 {
 }
 
@@ -201,7 +212,8 @@
                     other._imp->stdout_prefix,
                     other._imp->stderr_prefix,
                     other._imp->prefix_discard_blank_output,
-                    other._imp->prefix_blank_lines));
+                    other._imp->prefix_blank_lines,
+                    other._imp->pipe_command_handler, other._imp->captured_stdout_stream));
         if (other.uid() && other.gid())
             with_uid_gid(*other.uid(), *other.gid());
     }
@@ -236,6 +248,13 @@
 }
 
 Command &
+Command::with_captured_stdout_stream(std::ostream * const c)
+{
+    _imp->captured_stdout_stream = c;
+    return *this;
+}
+
+Command &
 Command::with_sandbox()
 {
 #if HAVE_SANDBOX
@@ -296,83 +315,304 @@
     Log::get_instance()->message(ll_debug, lc_no_context, "execl /bin/sh -c " + command
             + " " + extras);
 
+    tr1::shared_ptr<Pipe> internal_command_reader(new Pipe), pipe_command_reader, pipe_command_response, captured_stdout;
+    if (cmd.pipe_command_handler())
+    {
+        pipe_command_reader.reset(new Pipe);
+        pipe_command_response.reset(new Pipe);
+    }
+    if (cmd.captured_stdout_stream())
+        captured_stdout.reset(new Pipe);
+
     pid_t child(fork());
     if (0 == child)
     {
-        try
+        pid_t child_child(fork());
+        if (0 == child_child)
         {
-            if (! cmd.chdir().empty())
-                if (-1 == chdir(stringify(cmd.chdir()).c_str()))
-                    throw RunCommandError("chdir failed: " + stringify(strerror(errno)));
+            /* The pid that does the exec */
+            try
+            {
+                if (cmd.pipe_command_handler())
+                {
+                    close(pipe_command_reader->read_fd());
+                    pipe_command_reader->clear_read_fd();
 
-            for (Command::ConstIterator s(cmd.begin_setenvs()), s_end(cmd.end_setenvs()) ; s != s_end ; ++s)
-                setenv(s->first.c_str(), s->second.c_str(), 1);
-            setenv("PATH_NOT_CLOBBERED_BY_SANDBOX", getenv_with_default("PATH", "").c_str(), 1);
+                    close(pipe_command_response->write_fd());
+                    pipe_command_response->clear_write_fd();
+                }
 
-            if (-1 != stdout_write_fd)
-            {
-                if (-1 == dup2(stdout_write_fd, 1))
-                    throw RunCommandError("dup2 failed: " + stringify(strerror(errno)));
+                if (cmd.captured_stdout_stream())
+                {
+                    close(captured_stdout->read_fd());
+                    captured_stdout->clear_read_fd();
+                }
 
-                if (-1 != stdout_close_fd)
-                    close(stdout_close_fd);
-            }
+                close(internal_command_reader->read_fd());
+                internal_command_reader->clear_read_fd();
+                close(internal_command_reader->write_fd());
+                internal_command_reader->write_fd();
 
-            if (-1 != stderr_write_fd)
-            {
-                if (-1 == dup2(stderr_write_fd, 2))
-                    throw RunCommandError("dup2 failed: " + stringify(strerror(errno)));
+                if (! cmd.chdir().empty())
+                    if (-1 == chdir(stringify(cmd.chdir()).c_str()))
+                        throw RunCommandError("chdir failed: " + stringify(strerror(errno)));
 
-                if (-1 != stderr_close_fd)
-                    close(stderr_close_fd);
-            }
+                for (Command::ConstIterator s(cmd.begin_setenvs()), s_end(cmd.end_setenvs()) ; s != s_end ; ++s)
+                    setenv(s->first.c_str(), s->second.c_str(), 1);
 
-            if (cmd.gid() && *cmd.gid() != getgid())
-            {
-                gid_t g(*cmd.gid());
+                setenv("PATH_NOT_CLOBBERED_BY_SANDBOX", getenv_with_default("PATH", "").c_str(), 1);
 
-                if (0 != ::setgid(*cmd.gid()))
-                    std::cerr << "setgid(" << *cmd.uid() << ") failed for exec of '" << command << "': "
-                        << strerror(errno) << std::endl;
-                else if (0 != ::setgroups(1, &g))
-                    std::cerr << "setgroups failed for exec of '" << command << "': " << strerror(errno) << std::endl;
-            }
+                if (cmd.pipe_command_handler())
+                {
+                    setenv("PALUDIS_PIPE_COMMAND_WRITE_FD", stringify(pipe_command_reader->write_fd()).c_str(), 1);
+                    setenv("PALUDIS_PIPE_COMMAND_READ_FD", stringify(pipe_command_response->read_fd()).c_str(), 1);
+                }
 
-            if (cmd.uid() && *cmd.uid() != getuid())
-                if (0 != ::setuid(*cmd.uid()))
-                    std::cerr << "setuid(" << *cmd.uid() << ") failed for exec of '" << command << "': "
-                        << strerror(errno) << std::endl;
+                if (cmd.captured_stdout_stream())
+                {
+                    if (-1 == dup2(captured_stdout->write_fd(), 1))
+                        throw RunCommandError("dup2 failed: " + stringify(strerror(errno)));
+                }
+                else if (-1 != stdout_write_fd)
+                {
+                    if (-1 == dup2(stdout_write_fd, 1))
+                        throw RunCommandError("dup2 failed: " + stringify(strerror(errno)));
 
-            execl("/bin/sh", "sh", "-c", command.c_str(), static_cast<char *>(0));
-            throw RunCommandError("execl /bin/sh -c '" + command + "' failed:"
-                    + stringify(strerror(errno)));
+                    if (-1 != stdout_close_fd)
+                        close(stdout_close_fd);
+                }
+
+                if (-1 != stderr_write_fd)
+                {
+                    if (-1 == dup2(stderr_write_fd, 2))
+                        throw RunCommandError("dup2 failed: " + stringify(strerror(errno)));
+
+                    if (-1 != stderr_close_fd)
+                        close(stderr_close_fd);
+                }
+
+                if (cmd.gid() && *cmd.gid() != getgid())
+                {
+                    gid_t g(*cmd.gid());
+
+                    if (0 != ::setgid(*cmd.gid()))
+                        std::cerr << "setgid(" << *cmd.uid() << ") failed for exec of '" << command << "': "
+                            << strerror(errno) << std::endl;
+                    else if (0 != ::setgroups(1, &g))
+                        std::cerr << "setgroups failed for exec of '" << command << "': " << strerror(errno) << std::endl;
+                }
+
+                if (cmd.uid() && *cmd.uid() != getuid())
+                    if (0 != ::setuid(*cmd.uid()))
+                        std::cerr << "setuid(" << *cmd.uid() << ") failed for exec of '" << command << "': "
+                            << strerror(errno) << std::endl;
+
+                execl("/bin/sh", "sh", "-c", command.c_str(), static_cast<char *>(0));
+                throw RunCommandError("execl /bin/sh -c '" + command + "' failed:"
+                        + stringify(strerror(errno)));
+            }
+            catch (const Exception & e)
+            {
+                std::cerr << "exec of '" << command << "' failed due to exception '" << e.message()
+                    << "' (" << e.what() << ")" << std::endl;
+                exit(123);
+            }
+            catch (...)
+            {
+                std::cerr << "exec of '" << command << "' failed due to unknown exception" << std::endl;
+                exit(124);
+            }
         }
-        catch (const Exception & e)
+        else if (-1 == child_child)
         {
-            std::cerr << "exec of '" << command << "' failed due to exception '" << e.message()
-                << "' (" << e.what() << ")" << std::endl;
-            exit(123);
+            std::cerr << "fork failed: " + stringify(strerror(errno)) + "'" << std::endl;
+            exit(125);
         }
-        catch (...)
+        else
         {
-            std::cerr << "exec of '" << command << "' failed due to unknown exception" << std::endl;
-            exit(124);
+            /* The pid that waits for the exec pid and then writes to the done pipe */
+            if (cmd.pipe_command_handler())
+            {
+                close(pipe_command_reader->read_fd());
+                pipe_command_reader->clear_read_fd();
+
+                close(pipe_command_response->read_fd());
+                pipe_command_response->clear_read_fd();
+                close(pipe_command_response->write_fd());
+                pipe_command_response->clear_write_fd();
+            }
+
+            if (cmd.captured_stdout_stream())
+            {
+                close(captured_stdout->read_fd());
+                captured_stdout->clear_read_fd();
+                close(captured_stdout->write_fd());
+                captured_stdout->clear_write_fd();
+            }
+
+            close(internal_command_reader->read_fd());
+            internal_command_reader->clear_read_fd();
+
+            int status(-1);
+
+            stdout_write_fd = -1;
+            stdout_close_fd = -1;
+            stderr_write_fd = -1;
+            stderr_close_fd = -1;
+
+            int ret(-1);
+            if (-1 == waitpid(child_child, &status, 0))
+                std::cerr << "wait failed: " + stringify(strerror(errno)) + "'" << std::endl;
+            else
+                ret = (WIFSIGNALED(status) ? WTERMSIG(status) + 128 : WEXITSTATUS(status));
+
+            {
+                FDOutputStream stream(internal_command_reader->write_fd());
+                stream << "EXIT " << ret << std::endl;
+            }
         }
+
+        _exit(0);
     }
     else if (-1 == child)
         throw RunCommandError("fork failed: " + stringify(strerror(errno)));
     else
     {
-        int status(-1);
+        /* Our original pid */
+        if (cmd.pipe_command_handler())
+        {
+            close(pipe_command_reader->write_fd());
+            pipe_command_reader->clear_write_fd();
+            close(pipe_command_response->read_fd());
+            pipe_command_response->clear_read_fd();
+        }
 
-        stdout_write_fd = -1;
-        stdout_close_fd = -1;
-        stderr_write_fd = -1;
-        stderr_close_fd = -1;
+        if (cmd.captured_stdout_stream())
+        {
+            close(captured_stdout->write_fd());
+            captured_stdout->clear_write_fd();
+        }
 
-        if (-1 == waitpid(child, &status, 0))
-            throw RunCommandError("wait failed: " + stringify(strerror(errno)));
-        return WIFSIGNALED(status) ? WTERMSIG(status) + 128 : WEXITSTATUS(status);
+        close(internal_command_reader->write_fd());
+        internal_command_reader->clear_write_fd();
+
+        std::string pipe_command_buffer, internal_command_buffer;
+        while (true)
+        {
+            fd_set read_fds;
+            FD_ZERO(&read_fds);
+            int max_fd(0);
+
+            if (cmd.pipe_command_handler())
+            {
+                FD_SET(pipe_command_reader->read_fd(), &read_fds);
+                max_fd = std::max(max_fd, pipe_command_reader->read_fd());
+            }
+
+            if (cmd.captured_stdout_stream())
+            {
+                FD_SET(captured_stdout->read_fd(), &read_fds);
+                max_fd = std::max(max_fd, captured_stdout->read_fd());
+            }
+
+            FD_SET(internal_command_reader->read_fd(), &read_fds);
+            max_fd = std::max(max_fd, internal_command_reader->read_fd());
+
+            timespec tv;
+            tv.tv_sec = 5;
+            tv.tv_nsec = 0;
+
+            int retval(pselect(max_fd + 1, &read_fds, 0, 0, &tv, 0));
+            if (-1 == retval)
+                throw RunCommandError("select failed: " + stringify(strerror(errno)));
+            else if (0 == retval)
+            {
+                Log::get_instance()->message(ll_debug, lc_context) << "Waiting for child " << child << " to finish";
+                continue;
+            }
+            else
+            {
+                char buf[1024];
+                if (cmd.pipe_command_handler() && FD_ISSET(pipe_command_reader->read_fd(), &read_fds))
+                {
+                    int r;
+                    if (((r = read(pipe_command_reader->read_fd(), buf, 1024))) > 0)
+                        pipe_command_buffer.append(std::string(buf, r));
+                }
+
+                if (FD_ISSET(internal_command_reader->read_fd(), &read_fds))
+                {
+                    int r;
+                    if (((r = read(internal_command_reader->read_fd(), buf, 1024))) > 0)
+                        internal_command_buffer.append(std::string(buf, r));
+                }
+
+                if (cmd.captured_stdout_stream() && FD_ISSET(captured_stdout->read_fd(), &read_fds))
+                {
+                    int r;
+                    if (((r = read(captured_stdout->read_fd(), buf, 1024))) > 0)
+                        *cmd.captured_stdout_stream() << std::string(buf, r);
+                }
+            }
+
+            if (! pipe_command_buffer.empty())
+                Log::get_instance()->message(ll_debug, lc_context) << "pipe_command_buffer is '" << pipe_command_buffer << "'";
+            if (! internal_command_buffer.empty())
+                Log::get_instance()->message(ll_debug, lc_context) << "internal_command_buffer is '" << internal_command_buffer << "'";
+
+            while (! pipe_command_buffer.empty())
+            {
+                std::string::size_type n_p(pipe_command_buffer.find('\n'));
+                if (std::string::npos == n_p)
+                    break;
+
+                std::string op(pipe_command_buffer.substr(0, n_p));
+                pipe_command_buffer.erase(0, n_p + 1);
+
+                std::string response;
+                if (cmd.pipe_command_handler())
+                {
+                    response = cmd.pipe_command_handler()(op);
+                    Log::get_instance()->message(ll_debug, lc_context) << "Pipe command op '" << op << "' response '"
+                        << response << "'";
+                }
+                else
+                    Log::get_instance()->message(ll_warning, lc_context) << "Pipe command op '" << op <<
+                        "' was requested but no handler defined. This is probably a bug...";
+
+                response = strip_trailing(response, "\n") + "\n";
+                ssize_t n(0);
+                while (! response.empty())
+                {
+                    n = write(pipe_command_response->write_fd(), response.c_str(), response.length());
+                    if (-1 == n)
+                        throw InternalError(PALUDIS_HERE, "write failed: " + stringify(strerror(errno)));
+                    else
+                        response.erase(0, n);
+                }
+            }
+
+            while (! internal_command_buffer.empty())
+            {
+                std::string::size_type n_p(internal_command_buffer.find('\n'));
+                if (std::string::npos == n_p)
+                    break;
+
+                std::string op(internal_command_buffer.substr(0, n_p));
+                internal_command_buffer.erase(0, n_p + 1);
+                if (0 == op.compare(0, 4, "EXIT"))
+                {
+                    op.erase(0, 4);
+                    int status(-1);
+                    Log::get_instance()->message(ll_debug, lc_context) << "Got exit op '" << op << "'";
+                    if (-1 == waitpid(child, &status, 0))
+                        std::cerr << "wait failed: " + stringify(strerror(errno)) + "'" << std::endl;
+                    return destringify<int>(strip_leading(strip_trailing(op, " \r\n\t"), " \r\n\t"));
+                }
+                else
+                    throw InternalError(PALUDIS_HERE, "unknown op '" + op + "' on internal_command_buffer");
+            }
+        }
     }
 
     throw InternalError(PALUDIS_HERE, "should never be reached");
@@ -446,6 +686,13 @@
     return *this;
 }
 
+Command &
+Command::with_pipe_command_handler(const tr1::function<std::string (const std::string &)> & f)
+{
+    _imp->pipe_command_handler = f;
+    return *this;
+}
+
 std::string
 Command::stdout_prefix() const
 {
@@ -470,6 +717,18 @@
     return _imp->prefix_blank_lines;
 }
 
+const tr1::function<std::string (const std::string &)> &
+Command::pipe_command_handler() const
+{
+    return _imp->pipe_command_handler;
+}
+
+std::ostream *
+Command::captured_stdout_stream() const
+{
+    return _imp->captured_stdout_stream;
+}
+
 std::string
 paludis::get_user_name(const uid_t u)
 {

Modified: trunk/paludis/util/system.hh
===================================================================
--- trunk/paludis/util/system.hh	2007-12-30 22:38:02 UTC (rev 4109)
+++ trunk/paludis/util/system.hh	2007-12-30 22:38:14 UTC (rev 4110)
@@ -23,6 +23,7 @@
 #include <paludis/util/exception.hh>
 #include <paludis/util/private_implementation_pattern.hh>
 #include <paludis/util/tr1_memory.hh>
+#include <paludis/util/tr1_functional.hh>
 #include <paludis/util/wrapped_forward_iterator-fwd.hh>
 #include <string>
 #include <sys/types.h>
@@ -170,6 +171,16 @@
              */
             Command & with_prefix_blank_lines();
 
+            /**
+             * Specify a pipe command handler.
+             */
+            Command & with_pipe_command_handler(const tr1::function<std::string (const std::string &)> &);
+
+            /**
+             * Specify a stream to which stdout is captured and written.
+             */
+            Command & with_captured_stdout_stream(std::ostream * const);
+
             ///\}
 
             ///\name Fetch command execution options
@@ -221,6 +232,16 @@
              */
             bool prefix_blank_lines() const;
 
+            /**
+             * The pipe command handler.
+             */
+            const tr1::function<std::string (const std::string &)> & pipe_command_handler() const;
+
+            /**
+             * The captured stdout stream, or null.
+             */
+            std::ostream * captured_stdout_stream() const;
+
             ///\}
 
             ///\name Iterate over our setenvs.

Modified: trunk/paludis/util/system_TEST.cc
===================================================================
--- trunk/paludis/util/system_TEST.cc	2007-12-30 22:38:02 UTC (rev 4109)
+++ trunk/paludis/util/system_TEST.cc	2007-12-30 22:38:14 UTC (rev 4110)
@@ -18,7 +18,6 @@
  */
 
 #include <paludis/util/system.hh>
-#include <paludis/util/pstream.hh>
 #include <paludis/util/fs_entry.hh>
 #include <paludis/util/log.hh>
 #include <paludis/util/thread_pool.hh>
@@ -30,11 +29,6 @@
 #  include <sched.h>
 #endif
 
-/** \file
- * Test cases for system.hh .
- *
- */
-
 using namespace test;
 using namespace paludis;
 
@@ -70,15 +64,24 @@
             if (0 != run_command("/bin/true"))
                 throw InternalError(PALUDIS_HERE, "true isn't");
     }
+
+    std::string response_handler(const std::string & s)
+    {
+        if (s == "ONE")
+            return "1";
+        else if (s == "TWO")
+            return "2";
+        else if (s == "THREE")
+            return "3";
+        else if (s == "FOUR")
+            return "4";
+        else
+            return "9";
+    }
 }
 
 namespace test_cases
 {
-
-    /**
-     * \test Test getenv_with_default.
-     *
-     */
     struct GetenvWithDefaultTest : TestCase
     {
         GetenvWithDefaultTest() : TestCase("getenv_with_default") { }
@@ -91,10 +94,6 @@
         }
     } test_getenv_with_default;
 
-    /**
-     * \test Test getenv_or_error.
-     *
-     */
     struct GetenvOrErrorTest : TestCase
     {
         GetenvOrErrorTest() : TestCase("getenv_or_error") { }
@@ -106,10 +105,6 @@
         }
     } test_getenv_or_error;
 
-    /**
-     * \test Test kernel_version.
-     *
-     */
     struct KernelVersionTest : TestCase
     {
         KernelVersionTest() : TestCase("kernel version") { }
@@ -121,18 +116,14 @@
             TEST_CHECK('2' == kernel_version().at(0));
             TEST_CHECK('.' == kernel_version().at(1));
 #elif defined(__FreeBSD__)
-	    TEST_CHECK('6' == kernel_version().at(0));
-	    TEST_CHECK('.' == kernel_version().at(1));
+            TEST_CHECK('6' == kernel_version().at(0));
+            TEST_CHECK('.' == kernel_version().at(1));
 #else
 #  error You need to write a sanity test for kernel_version() for your platform.
 #endif
         }
     } test_kernel_version;
 
-    /**
-     * \test Test run_command.
-     *
-     */
     struct RunCommandTest : TestCase
     {
         RunCommandTest() : TestCase("run_command") { }
@@ -159,10 +150,6 @@
         }
     } test_run_command_mutex;
 
-    /**
-     * \test Test run_command_in_directory.
-     *
-     */
     struct RunCommandInDirectoryTest : TestCase
     {
         RunCommandInDirectoryTest() : TestCase("run_command_in_directory") { }
@@ -179,10 +166,6 @@
         }
     } test_run_command_in_directory;
 
-    /**
-     * \test Test make_env_command.
-     *
-     */
     struct MakeEnvCommandTest : TestCase
     {
         MakeEnvCommandTest() : TestCase("make_env_command") { }
@@ -210,10 +193,6 @@
         }
     } test_make_env_command;
 
-    /**
-     * \test Test make_env_command with quotes.
-     *
-     */
     struct MakeEnvCommandQuoteTest : TestCase
     {
         MakeEnvCommandQuoteTest() : TestCase("make_env_command quotes") { }
@@ -228,5 +207,93 @@
                         .with_setenv("PALUDUS_TEST_ENV_VAR", "..'..")));
         }
     } test_make_env_command_quotes;
+
+    struct PipeCommandTest : TestCase
+    {
+        PipeCommandTest() : TestCase("pipe command") { }
+
+        void run()
+        {
+            TEST_CHECK_EQUAL(run_command(Command("bash system_TEST_dir/pipe_test.bash ONE TWO")
+                        .with_pipe_command_handler(&response_handler)), 12);
+            TEST_CHECK_EQUAL(run_command(Command("bash system_TEST_dir/pipe_test.bash THREE FOUR")
+                        .with_pipe_command_handler(&response_handler)), 34);
+        }
+    } test_pipe_command;
+
+    struct CapturedTest : TestCase
+    {
+        CapturedTest() : TestCase("captured stdout") { }
+
+        void run()
+        {
+            std::stringstream s;
+            TEST_CHECK_EQUAL(run_command(Command("echo hi").with_captured_stdout_stream(&s)), 0);
+            std::string line;
+            TEST_CHECK(std::getline(s, line));
+            TEST_CHECK_EQUAL(line, "hi");
+            TEST_CHECK(! std::getline(s, line));
+        }
+    } test_captured;
+
+    struct CapturedNoExistTest : TestCase
+    {
+        CapturedNoExistTest() : TestCase("captured nonexistent command") { }
+
+        void run()
+        {
+            std::stringstream s;
+            TEST_CHECK(run_command(Command("thiscommanddoesnotexist 2>/dev/null").with_captured_stdout_stream(&s)) != 0);
+            std::string line;
+            TEST_CHECK(! std::getline(s, line));
+        }
+    } test_captured_no_exist;
+
+    struct CapturedSilentFailTest : TestCase
+    {
+        CapturedSilentFailTest() : TestCase("captured silent fail") { }
+
+        void run()
+        {
+            std::stringstream s;
+            TEST_CHECK(run_command(Command("test -e /doesnotexist").with_captured_stdout_stream(&s)) != 0);
+            std::string line;
+            TEST_CHECK(! std::getline(s, line));
+        }
+    } test_captured_silent_fail;
+
+    struct CapturedFailTest : TestCase
+    {
+        CapturedFailTest() : TestCase("captured fail") { }
+
+        void run()
+        {
+            std::stringstream s;
+            TEST_CHECK(run_command(Command("cat /doesnotexist 2>&1").with_captured_stdout_stream(&s)) != 0);
+            std::string line;
+            TEST_CHECK(std::getline(s, line));
+            TEST_CHECK(! line.empty());
+            TEST_CHECK(! std::getline(s, line));
+        }
+    } test_captured_fail;
+
+
+    struct CapturedPipeCommandTest : TestCase
+    {
+        CapturedPipeCommandTest() : TestCase("captured pipe command") { }
+
+        void run()
+        {
+            std::stringstream s;
+            TEST_CHECK_EQUAL(run_command(Command("bash system_TEST_dir/captured_pipe_test.bash ONE TWO THREE")
+                        .with_pipe_command_handler(&response_handler)
+                        .with_captured_stdout_stream(&s)),
+                    13);
+            std::string line;
+            TEST_CHECK(std::getline(s, line));
+            TEST_CHECK_EQUAL(line, "2");
+            TEST_CHECK(! std::getline(s, line));
+        }
+    } test_captured_pipe_command;
 }
 

Modified: trunk/paludis/util/system_TEST_setup.sh
===================================================================
--- trunk/paludis/util/system_TEST_setup.sh	2007-12-30 22:38:02 UTC (rev 4109)
+++ trunk/paludis/util/system_TEST_setup.sh	2007-12-30 22:38:14 UTC (rev 4110)
@@ -3,3 +3,33 @@
 
 mkdir system_TEST_dir || exit 2
 cd system_TEST_dir || exit 3
+
+cat <<'END' > pipe_test.bash
+#!/bin/bash
+
+echo $1 1>&$PALUDIS_PIPE_COMMAND_WRITE_FD
+read -u$PALUDIS_PIPE_COMMAND_READ_FD response1
+
+echo $2 1>&$PALUDIS_PIPE_COMMAND_WRITE_FD
+read -u$PALUDIS_PIPE_COMMAND_READ_FD response2
+
+exit $response1$response2
+END
+
+cat <<'END' > captured_pipe_test.bash
+#!/bin/bash
+
+echo $1 1>&$PALUDIS_PIPE_COMMAND_WRITE_FD
+read -u$PALUDIS_PIPE_COMMAND_READ_FD response1
+
+echo $2 1>&$PALUDIS_PIPE_COMMAND_WRITE_FD
+read -u$PALUDIS_PIPE_COMMAND_READ_FD response2
+
+echo $3 1>&$PALUDIS_PIPE_COMMAND_WRITE_FD
+read -u$PALUDIS_PIPE_COMMAND_READ_FD response3
+
+echo $response2
+
+exit $response1$response3
+END
+

Modified: trunk/src/clients/gtkpaludis/libgtkpaludis/messages_page.cc
===================================================================
--- trunk/src/clients/gtkpaludis/libgtkpaludis/messages_page.cc	2007-12-30 22:38:02 UTC (rev 4109)
+++ trunk/src/clients/gtkpaludis/libgtkpaludis/messages_page.cc	2007-12-30 22:38:14 UTC (rev 4110)
@@ -6,7 +6,6 @@
 #include <paludis/query.hh>
 #include <paludis/util/fd_output_stream.hh>
 #include <paludis/util/system.hh>
-#include <paludis/util/pstream.hh>
 #include <paludis/util/private_implementation_pattern-impl.hh>
 #include <paludis/util/log.hh>
 #include <vtemm/terminal_widget.hh>
@@ -90,7 +89,6 @@
 {
     set_run_command_stdout_fds(_imp->slave_fd, _imp->master_fd);
     set_run_command_stderr_fds(_imp->slave_fd, _imp->master_fd);
-    PStream::set_stderr_fd(_imp->slave_fd, _imp->master_fd);
     Log::get_instance()->set_log_stream(_imp->messages_stream.get());
 }
 

Modified: trunk/src/clients/gtkpaludis/libgtkpaludis/task_window.cc
===================================================================
--- trunk/src/clients/gtkpaludis/libgtkpaludis/task_window.cc	2007-12-30 22:38:02 UTC (rev 4109)
+++ trunk/src/clients/gtkpaludis/libgtkpaludis/task_window.cc	2007-12-30 22:38:14 UTC (rev 4110)
@@ -7,7 +7,6 @@
 #include <paludis/util/private_implementation_pattern-impl.hh>
 #include <paludis/util/tr1_memory.hh>
 #include <paludis/util/fd_output_stream.hh>
-#include <paludis/util/pstream.hh>
 #include <paludis/util/system.hh>
 #include <paludis/util/log.hh>
 #include <gtkmm/table.h>
@@ -85,7 +84,6 @@
     _imp->terminal.set_pty(dup(_imp->master_fd));
     set_run_command_stdout_fds(_imp->slave_fd, _imp->master_fd);
     set_run_command_stderr_fds(_imp->slave_fd, _imp->master_fd);
-    PStream::set_stderr_fd(_imp->slave_fd, _imp->master_fd);
     Log::get_instance()->set_log_stream(_imp->messages_stream.get());
 
     _imp->terminal.set_scroll_on_output(true);



More information about the paludis-commits mailing list