allow path lookup for pipestream
[libi2ncommon] / src / pipestream.hxx
index 991e65c..f53f474 100644 (file)
+ /*
+The software in this package is distributed under the GNU General
+Public License version 2 (with a special exception described below).
+
+A copy of GNU General Public License (GPL) is included in this distribution,
+in the file COPYING.GPL.
+
+As a special exception, if other files instantiate templates or use macros
+or inline functions from this file, or you compile this file and link it
+with other works to produce a work based on this file, this file
+does not by itself cause the resulting work to be covered
+by the GNU General Public License.
+
+However the source code for this file must still be made available
+in accordance with section (3) of the GNU General Public License.
+
+This exception does not invalidate any other reasons why a work based
+on this file might be covered by the GNU General Public License.
+*/
 /***************************************************************************
               inpipestream.hxx  -  C++ streambuffer wrapper 
                              -------------------
     begin                : Thu Dec 27 2001
     copyright            : (C) 2001 by Intra2net AG
-    email                : intranator@intra2net.com
  ***************************************************************************/
 
 #ifndef _PIPESTREAM
 #define _PIPESTREAM
 
+#include <stdio.h>
+
+#include <cstring>
 #include <string>
 #include <streambuf>
-#include <cstdio>
+#include <istream>
+#include <ostream>
+#include <vector>
+
+#include <stringfunc.hxx>
+
+struct ExecResult
+{
+    /** if the program exited normally and returned a return code */
+    bool normal_exit;
+
+    /** the real return code of the program, only set when normal_exit true */
+    char return_code;
+
+    /** if the program was terminated by a signal */
+    bool terminated_by_signal;
+
+    /** number of the signal that terminated the program, only valid when terminated_by_signal true */
+    int signal;
+
+    /** errormessage if we have one */
+    std::string error_message;
+
+    inline std::string format (void) const
+    {
+        return std::string ("(")
+            + "(normal_exit " + (this->normal_exit ? "T" : "F") + ") "
+              "(return_code '" + I2n::to_string ((int)this->return_code) + "') "
+              "(signal " + (this->terminated_by_signal
+                            ? strsignal (this->signal)
+                            : "<nil>") + "))"
+            ;
+    };
+};
+typedef struct ExecResult ExecResult;
 
-#include "exception.hxx"
+std::string capture_exec(const std::string& command, ExecResult &rescode);
+std::string capture_exec(const char *const *command, ExecResult &rescode,
+                         const bool out=true, const bool err=false,
+                         const bool path=false);
+std::string capture_exec(const std::vector<std::string>& command, ExecResult &rescode,
+                         const bool out=true, const bool err=false,
+                         const bool path=false);
 
-// ATTENTION: A lot of mysterious STL bugs occured
-//            with a "real" buffer (buffer larger than 1 byte and up to 100 bytes)
-//            -> Keep it slow and working!
+inline std::string capture_exec (const std::string &command)
+{
+    ExecResult r;
+    return capture_exec(command,r);
+}
 
+inline std::string capture_exec(const char *const *command)
+{
+    ExecResult r;
+    return capture_exec(command,r);
+}
+
+inline std::string capture_exec(const std::vector<std::string>& command)
+{
+    ExecResult r;
+    return capture_exec(command,r);
+}
+
+/** @brief runs command and provides buffered input for it through pipe
+ *
+ * opens pipe to command using popen; exit status available after destruction
+ * (use WEXITSTATUS to get the "regular" return code (lowest byte))
+ *
+ * ATTENTION: A lot of mysterious STL bugs occured
+ *            with a "real" buffer (buffer larger than 1 byte and up to 100 bytes)
+ *            -> Keep it slow and working!
+ */
 class inpipebuf : public std::streambuf
 {
-   protected:
-   char buffer;
-   FILE *pipe;
-
-   // "callback" variables for destructor to store exit status   
-   bool *status_set;
-   int *exit_status;
-
-   public:
-   inpipebuf(const std::string& command)
-   {
-      status_set = NULL;
-      exit_status = NULL;
-   
-      pipe = popen (command.c_str(), "r");
-      if (pipe == NULL)
-         throw EXCEPTION (pipestream_error, "can't open program or permission denied");
-
-      // force underflow
-      setg (&buffer, &buffer, &buffer);
-   }
-
-   ~inpipebuf()
-   {
-      if (pipe != NULL) {
-         int pclose_exit = pclose (pipe);
-
-        if (exit_status && pclose_exit != -1) {
-           *status_set = true;
-           *exit_status = pclose_exit;
-        }
-                
-         pipe = NULL;
-      }
-   }
-
-   void store_exit_status(bool *_status_set, int *_exit_status)
-      { status_set = _status_set; exit_status = _exit_status; }
-
-   protected:
-   virtual int_type underflow()
-   {
-      if (gptr() < egptr())
-         return traits_type::to_int_type(*gptr());
-
-      buffer = fgetc (pipe);
-      if (feof (pipe))
-      {
-         // ERROR or EOF
-         return EOF;
-      }
-
-      setg (&buffer, &buffer, &buffer+sizeof(char));
-
-      return traits_type::to_int_type(*gptr());
-   }
-}; 
+protected:
+    char buffer;
+
+    FILE *pipe;
+    pid_t pid;
 
+    // "callback" variables for destructor to store exit status
+    bool *status_set;
+    int *exit_status;
+
+public:
+    inpipebuf(const std::string& command,
+              const bool out, const bool err, const bool path);
+    inpipebuf(const char *const *command,
+              const bool out, const bool err, const bool path);
+    inpipebuf(const std::vector<std::string> &command,
+              const bool out, const bool err, const bool path);
+
+    ~inpipebuf();
+
+    void store_exit_status(bool *_status_set, int *_exit_status);
+
+protected:
+    virtual int_type underflow();
+
+private:
+    std::pair <pid_t, FILE *>
+    init_without_shell (const char *const *argv,
+                        const bool out, const bool err,
+                        const bool path) const;
+};
+
+/** @brief stream around inpipebuf -- see comment there */
 class inpipestream : public std::istream
 {
-   protected:
-      inpipebuf buf;
-      
-   public:
-      inpipestream(const std::string& command)
-         : buf(command), std::istream(&buf)
-         {}
-      
-      void store_exit_status(bool *_status_set, int *_exit_status)
-         { buf.store_exit_status(_status_set, _exit_status); }
+protected:
+    inpipebuf buf;
+
+public:
+    inpipestream(const std::string& command,
+                 const bool out=true, const bool err=false,
+                 const bool path=false)
+            : std::istream(&buf), buf(command, out, err, path)
+    {}
+
+    inpipestream(const char *const command[],
+                 const bool out=true, const bool err=false,
+                 const bool path=false)
+            : std::istream(&buf), buf(command, out, err, path)
+    {}
+
+    inpipestream(const std::vector<std::string> &command,
+                 const bool out=true, const bool err=false,
+                 const bool path=false)
+            : std::istream(&buf), buf(command, out, err, path)
+    {}
+
+    void store_exit_status(bool *_status_set, int *_exit_status)
+    { buf.store_exit_status(_status_set, _exit_status); }
 };
 
+/** @brief runs command and provides buffered ouptput from it through pipe
+ *
+ * opens pipe to command using popen; exit status available after destruction
+ * (use WEXITSTATUS to get the "regular" return code (lowest byte))
+ */
 class outpipebuf : public std::streambuf
 {
-   protected:
-   FILE *pipe;
-
-   // "callback" variables for destructor to store exit status   
-   bool *status_set;
-   int *exit_status;
-
-   public:
-   outpipebuf(const std::string& command)
-   {
-      status_set = NULL;
-      exit_status = NULL;
-
-      pipe = popen (command.c_str(), "w");
-      if (pipe == NULL)
-         throw EXCEPTION (pipestream_error, "can't open program or permission denied");
-   }
-
-   ~outpipebuf()
-   {
-      if (pipe != NULL) {
-         int pclose_exit = pclose (pipe);
-
-        if (exit_status && pclose_exit != -1) {
-           *status_set = true;
-           *exit_status = pclose_exit;
-        }
-
-         pipe = NULL;
-      }
-   }
-
-   void store_exit_status(bool *_status_set, int *_exit_status)
-      { status_set = _status_set; exit_status = _exit_status; }
-      
-   protected:
-   virtual int_type overflow(int_type c)
-   {
-      if (c != EOF)
-      {
-         if (fputc(c,pipe)==EOF)
-            return EOF;
-      }
-      return c;
-   }
-
-   virtual std::streamsize xsputn(const char* s, std::streamsize num)
-   {
-      return fwrite(s,num,1,pipe);
-   }
+protected:
+    FILE *pipe;
+
+    // "callback" variables for destructor to store exit status
+    bool *status_set;
+    int *exit_status;
+
+public:
+    outpipebuf(const std::string& command);
+
+    ~outpipebuf();
+
+    /** note: exit status only available after destruction */
+    void store_exit_status(bool *_status_set, int *_exit_status);
+
+protected:
+    virtual int_type overflow(int_type c);
+
+    virtual std::streamsize xsputn(const char* s, std::streamsize num);
 };
 
+
+/** @brief stream around outpipebuf -- see comment there */
 class outpipestream : public std::ostream
 {
-   protected:
-      outpipebuf buf;
-   public:
-      outpipestream(const std::string& command)
-         : buf(command), std::ostream(&buf)
-         {}
-
-      void store_exit_status(bool *_status_set, int *_exit_status)
-         { buf.store_exit_status(_status_set, _exit_status); }
+protected:
+    outpipebuf buf;
+public:
+    outpipestream(const std::string& command)
+            : std::ostream(&buf), buf(command)
+    {}
+
+    void store_exit_status(bool *_status_set, int *_exit_status)
+    { buf.store_exit_status(_status_set, _exit_status); }
 };
 
 #endif