pstreams-0.8.0/pstream.h 0000664 0000764 0000764 00000205523 12125352470 013653 0 ustar redi redi /*
PStreams - POSIX Process I/O for C++
Copyright (C) 2001-2013 Jonathan Wakely
This file is part of PStreams.
PStreams is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
PStreams 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see .
*/
/**
* @file pstream.h
* @brief Declares all PStreams classes.
* @author Jonathan Wakely
*
* Defines classes redi::ipstream, redi::opstream, redi::pstream
* and redi::rpstream.
*/
#ifndef REDI_PSTREAM_H_SEEN
#define REDI_PSTREAM_H_SEEN
#include
#include
#include
#include
#include
#include
#include // for min()
#include // for errno
#include // for size_t, NULL
#include // for exit()
#include // for pid_t
#include // for waitpid()
#include // for ioctl() and FIONREAD
#if defined(__sun)
# include // for FIONREAD on Solaris 2.5
#endif
#include // for pipe() fork() exec() and filedes functions
#include // for kill()
#include // for fcntl()
#if REDI_EVISCERATE_PSTREAMS
# include // for FILE, fdopen()
#endif
/// The library version.
#define PSTREAMS_VERSION 0x0080 // 0.8.0
/**
* @namespace redi
* @brief All PStreams classes are declared in namespace redi.
*
* Like the standard iostreams, PStreams is a set of class templates,
* taking a character type and traits type. As with the standard streams
* they are most likely to be used with @c char and the default
* traits type, so typedefs for this most common case are provided.
*
* The @c pstream_common class template is not intended to be used directly,
* it is used internally to provide the common functionality for the
* other stream classes.
*/
namespace redi
{
/// Common base class providing constants and typenames.
struct pstreams
{
/// Type used to specify how to connect to the process.
typedef std::ios_base::openmode pmode;
/// Type used to hold the arguments for a command.
typedef std::vector argv_type;
/// Type used for file descriptors.
typedef int fd_type;
static const pmode pstdin = std::ios_base::out; ///< Write to stdin
static const pmode pstdout = std::ios_base::in; ///< Read from stdout
static const pmode pstderr = std::ios_base::app; ///< Read from stderr
protected:
enum { bufsz = 32 }; ///< Size of pstreambuf buffers.
enum { pbsz = 2 }; ///< Number of putback characters kept.
};
/// Class template for stream buffer.
template >
class basic_pstreambuf
: public std::basic_streambuf
, public pstreams
{
public:
// Type definitions for dependent types
typedef CharT char_type;
typedef Traits traits_type;
typedef typename traits_type::int_type int_type;
typedef typename traits_type::off_type off_type;
typedef typename traits_type::pos_type pos_type;
/** @deprecated use pstreams::fd_type instead. */
typedef fd_type fd_t;
/// Default constructor.
basic_pstreambuf();
/// Constructor that initialises the buffer with @a command.
basic_pstreambuf(const std::string& command, pmode mode);
/// Constructor that initialises the buffer with @a file and @a argv.
basic_pstreambuf( const std::string& file,
const argv_type& argv,
pmode mode );
/// Destructor.
~basic_pstreambuf();
/// Initialise the stream buffer with @a command.
basic_pstreambuf*
open(const std::string& command, pmode mode);
/// Initialise the stream buffer with @a file and @a argv.
basic_pstreambuf*
open(const std::string& file, const argv_type& argv, pmode mode);
/// Close the stream buffer and wait for the process to exit.
basic_pstreambuf*
close();
/// Send a signal to the process.
basic_pstreambuf*
kill(int signal = SIGTERM);
/// Send a signal to the process' process group.
basic_pstreambuf*
killpg(int signal = SIGTERM);
/// Close the pipe connected to the process' stdin.
void
peof();
/// Change active input source.
bool
read_err(bool readerr = true);
/// Report whether the stream buffer has been initialised.
bool
is_open() const;
/// Report whether the process has exited.
bool
exited();
#if REDI_EVISCERATE_PSTREAMS
/// Obtain FILE pointers for each of the process' standard streams.
std::size_t
fopen(FILE*& in, FILE*& out, FILE*& err);
#endif
/// Return the exit status of the process.
int
status() const;
/// Return the error number (errno) for the most recent failed operation.
int
error() const;
protected:
/// Transfer characters to the pipe when character buffer overflows.
int_type
overflow(int_type c);
/// Transfer characters from the pipe when the character buffer is empty.
int_type
underflow();
/// Make a character available to be returned by the next extraction.
int_type
pbackfail(int_type c = traits_type::eof());
/// Write any buffered characters to the stream.
int
sync();
/// Insert multiple characters into the pipe.
std::streamsize
xsputn(const char_type* s, std::streamsize n);
/// Insert a sequence of characters into the pipe.
std::streamsize
write(const char_type* s, std::streamsize n);
/// Extract a sequence of characters from the pipe.
std::streamsize
read(char_type* s, std::streamsize n);
/// Report how many characters can be read from active input without blocking.
std::streamsize
showmanyc();
protected:
/// Enumerated type to indicate whether stdout or stderr is to be read.
enum buf_read_src { rsrc_out = 0, rsrc_err = 1 };
/// Initialise pipes and fork process.
pid_t
fork(pmode mode);
/// Wait for the child process to exit.
int
wait(bool nohang = false);
/// Return the file descriptor for the output pipe.
fd_type&
wpipe();
/// Return the file descriptor for the active input pipe.
fd_type&
rpipe();
/// Return the file descriptor for the specified input pipe.
fd_type&
rpipe(buf_read_src which);
void
create_buffers(pmode mode);
void
destroy_buffers(pmode mode);
/// Writes buffered characters to the process' stdin pipe.
bool
empty_buffer();
bool
fill_buffer(bool non_blocking = false);
/// Return the active input buffer.
char_type*
rbuffer();
buf_read_src
switch_read_buffer(buf_read_src);
private:
basic_pstreambuf(const basic_pstreambuf&);
basic_pstreambuf& operator=(const basic_pstreambuf&);
void
init_rbuffers();
pid_t ppid_; // pid of process
fd_type wpipe_; // pipe used to write to process' stdin
fd_type rpipe_[2]; // two pipes to read from, stdout and stderr
char_type* wbuffer_;
char_type* rbuffer_[2];
char_type* rbufstate_[3];
/// Index into rpipe_[] to indicate active source for read operations.
buf_read_src rsrc_;
int status_; // hold exit status of child process
int error_; // hold errno if fork() or exec() fails
};
/// Class template for common base class.
template >
class pstream_common
: virtual public std::basic_ios
, virtual public pstreams
{
protected:
typedef basic_pstreambuf streambuf_type;
typedef pstreams::pmode pmode;
typedef pstreams::argv_type argv_type;
/// Default constructor.
pstream_common();
/// Constructor that initialises the stream by starting a process.
pstream_common(const std::string& command, pmode mode);
/// Constructor that initialises the stream by starting a process.
pstream_common(const std::string& file, const argv_type& argv, pmode mode);
/// Pure virtual destructor.
virtual
~pstream_common() = 0;
/// Start a process.
void
do_open(const std::string& command, pmode mode);
/// Start a process.
void
do_open(const std::string& file, const argv_type& argv, pmode mode);
public:
/// Close the pipe.
void
close();
/// Report whether the stream's buffer has been initialised.
bool
is_open() const;
/// Return the command used to initialise the stream.
const std::string&
command() const;
/// Return a pointer to the stream buffer.
streambuf_type*
rdbuf() const;
#if REDI_EVISCERATE_PSTREAMS
/// Obtain FILE pointers for each of the process' standard streams.
std::size_t
fopen(FILE*& in, FILE*& out, FILE*& err);
#endif
protected:
std::string command_; ///< The command used to start the process.
streambuf_type buf_; ///< The stream buffer.
};
/**
* @class basic_ipstream
* @brief Class template for Input PStreams.
*
* Reading from an ipstream reads the command's standard output and/or
* standard error (depending on how the ipstream is opened)
* and the command's standard input is the same as that of the process
* that created the object, unless altered by the command itself.
*/
template >
class basic_ipstream
: public std::basic_istream
, public pstream_common
, virtual public pstreams
{
typedef std::basic_istream istream_type;
typedef pstream_common pbase_type;
using pbase_type::buf_; // declare name in this scope
pmode readable(pmode mode)
{
if (!(mode & (pstdout|pstderr)))
mode |= pstdout;
return mode;
}
public:
/// Type used to specify how to connect to the process.
typedef typename pbase_type::pmode pmode;
/// Type used to hold the arguments for a command.
typedef typename pbase_type::argv_type argv_type;
/// Default constructor, creates an uninitialised stream.
basic_ipstream()
: istream_type(NULL), pbase_type()
{ }
/**
* @brief Constructor that initialises the stream by starting a process.
*
* Initialises the stream buffer by calling do_open() with the supplied
* arguments.
*
* @param command a string containing a shell command.
* @param mode the I/O mode to use when opening the pipe.
* @see do_open(const std::string&, pmode)
*/
basic_ipstream(const std::string& command, pmode mode = pstdout)
: istream_type(NULL), pbase_type(command, readable(mode))
{ }
/**
* @brief Constructor that initialises the stream by starting a process.
*
* Initialises the stream buffer by calling do_open() with the supplied
* arguments.
*
* @param file a string containing the pathname of a program to execute.
* @param argv a vector of argument strings passed to the new program.
* @param mode the I/O mode to use when opening the pipe.
* @see do_open(const std::string&, const argv_type&, pmode)
*/
basic_ipstream( const std::string& file,
const argv_type& argv,
pmode mode = pstdout )
: istream_type(NULL), pbase_type(file, argv, readable(mode))
{ }
/**
* @brief Constructor that initialises the stream by starting a process.
*
* Initialises the stream buffer by calling
* @c do_open(argv[0],argv,mode|pstdout)
*
* @param argv a vector of argument strings passed to the new program.
* @param mode the I/O mode to use when opening the pipe.
* @see do_open(const std::string&, const argv_type&, pmode)
*/
basic_ipstream(const argv_type& argv, pmode mode = pstdout)
: istream_type(NULL), pbase_type(argv.at(0), argv, mode|pstdout)
{ }
/**
* @brief Destructor.
*
* Closes the stream and waits for the child to exit.
*/
~basic_ipstream()
{ }
/**
* @brief Start a process.
*
* Calls do_open( @a %command , @a mode|pstdout ).
*
* @param command a string containing a shell command.
* @param mode the I/O mode to use when opening the pipe.
* @see do_open(const std::string&, pmode)
*/
void
open(const std::string& command, pmode mode = pstdout)
{
this->do_open(command, readable(mode));
}
/**
* @brief Start a process.
*
* Calls do_open( @a file , @a argv , @a mode|pstdout ).
*
* @param file a string containing the pathname of a program to execute.
* @param argv a vector of argument strings passed to the new program.
* @param mode the I/O mode to use when opening the pipe.
* @see do_open(const std::string&, const argv_type&, pmode)
*/
void
open( const std::string& file,
const argv_type& argv,
pmode mode = pstdout )
{
this->do_open(file, argv, readable(mode));
}
/**
* @brief Set streambuf to read from process' @c stdout.
* @return @c *this
*/
basic_ipstream&
out()
{
this->buf_.read_err(false);
return *this;
}
/**
* @brief Set streambuf to read from process' @c stderr.
* @return @c *this
*/
basic_ipstream&
err()
{
this->buf_.read_err(true);
return *this;
}
};
/**
* @class basic_opstream
* @brief Class template for Output PStreams.
*
* Writing to an open opstream writes to the standard input of the command;
* the command's standard output is the same as that of the process that
* created the pstream object, unless altered by the command itself.
*/
template >
class basic_opstream
: public std::basic_ostream
, public pstream_common
, virtual public pstreams
{
typedef std::basic_ostream ostream_type;
typedef pstream_common pbase_type;
using pbase_type::buf_; // declare name in this scope
public:
/// Type used to specify how to connect to the process.
typedef typename pbase_type::pmode pmode;
/// Type used to hold the arguments for a command.
typedef typename pbase_type::argv_type argv_type;
/// Default constructor, creates an uninitialised stream.
basic_opstream()
: ostream_type(NULL), pbase_type()
{ }
/**
* @brief Constructor that initialises the stream by starting a process.
*
* Initialises the stream buffer by calling do_open() with the supplied
* arguments.
*
* @param command a string containing a shell command.
* @param mode the I/O mode to use when opening the pipe.
* @see do_open(const std::string&, pmode)
*/
basic_opstream(const std::string& command, pmode mode = pstdin)
: ostream_type(NULL), pbase_type(command, mode|pstdin)
{ }
/**
* @brief Constructor that initialises the stream by starting a process.
*
* Initialises the stream buffer by calling do_open() with the supplied
* arguments.
*
* @param file a string containing the pathname of a program to execute.
* @param argv a vector of argument strings passed to the new program.
* @param mode the I/O mode to use when opening the pipe.
* @see do_open(const std::string&, const argv_type&, pmode)
*/
basic_opstream( const std::string& file,
const argv_type& argv,
pmode mode = pstdin )
: ostream_type(NULL), pbase_type(file, argv, mode|pstdin)
{ }
/**
* @brief Constructor that initialises the stream by starting a process.
*
* Initialises the stream buffer by calling
* @c do_open(argv[0],argv,mode|pstdin)
*
* @param argv a vector of argument strings passed to the new program.
* @param mode the I/O mode to use when opening the pipe.
* @see do_open(const std::string&, const argv_type&, pmode)
*/
basic_opstream(const argv_type& argv, pmode mode = pstdin)
: ostream_type(NULL), pbase_type(argv.at(0), argv, mode|pstdin)
{ }
/**
* @brief Destructor
*
* Closes the stream and waits for the child to exit.
*/
~basic_opstream() { }
/**
* @brief Start a process.
*
* Calls do_open( @a %command , @a mode|pstdin ).
*
* @param command a string containing a shell command.
* @param mode the I/O mode to use when opening the pipe.
* @see do_open(const std::string&, pmode)
*/
void
open(const std::string& command, pmode mode = pstdin)
{
this->do_open(command, mode|pstdin);
}
/**
* @brief Start a process.
*
* Calls do_open( @a file , @a argv , @a mode|pstdin ).
*
* @param file a string containing the pathname of a program to execute.
* @param argv a vector of argument strings passed to the new program.
* @param mode the I/O mode to use when opening the pipe.
* @see do_open(const std::string&, const argv_type&, pmode)
*/
void
open( const std::string& file,
const argv_type& argv,
pmode mode = pstdin)
{
this->do_open(file, argv, mode|pstdin);
}
};
/**
* @class basic_pstream
* @brief Class template for Bidirectional PStreams.
*
* Writing to a pstream opened with @c pmode @c pstdin writes to the
* standard input of the command.
* Reading from a pstream opened with @c pmode @c pstdout and/or @c pstderr
* reads the command's standard output and/or standard error.
* Any of the process' @c stdin, @c stdout or @c stderr that is not
* connected to the pstream (as specified by the @c pmode)
* will be the same as the process that created the pstream object,
* unless altered by the command itself.
*/
template >
class basic_pstream
: public std::basic_iostream
, public pstream_common
, virtual public pstreams
{
typedef std::basic_iostream iostream_type;
typedef pstream_common pbase_type;
using pbase_type::buf_; // declare name in this scope
public:
/// Type used to specify how to connect to the process.
typedef typename pbase_type::pmode pmode;
/// Type used to hold the arguments for a command.
typedef typename pbase_type::argv_type argv_type;
/// Default constructor, creates an uninitialised stream.
basic_pstream()
: iostream_type(NULL), pbase_type()
{ }
/**
* @brief Constructor that initialises the stream by starting a process.
*
* Initialises the stream buffer by calling do_open() with the supplied
* arguments.
*
* @param command a string containing a shell command.
* @param mode the I/O mode to use when opening the pipe.
* @see do_open(const std::string&, pmode)
*/
basic_pstream(const std::string& command, pmode mode = pstdout|pstdin)
: iostream_type(NULL), pbase_type(command, mode)
{ }
/**
* @brief Constructor that initialises the stream by starting a process.
*
* Initialises the stream buffer by calling do_open() with the supplied
* arguments.
*
* @param file a string containing the pathname of a program to execute.
* @param argv a vector of argument strings passed to the new program.
* @param mode the I/O mode to use when opening the pipe.
* @see do_open(const std::string&, const argv_type&, pmode)
*/
basic_pstream( const std::string& file,
const argv_type& argv,
pmode mode = pstdout|pstdin )
: iostream_type(NULL), pbase_type(file, argv, mode)
{ }
/**
* @brief Constructor that initialises the stream by starting a process.
*
* Initialises the stream buffer by calling
* @c do_open(argv[0],argv,mode)
*
* @param argv a vector of argument strings passed to the new program.
* @param mode the I/O mode to use when opening the pipe.
* @see do_open(const std::string&, const argv_type&, pmode)
*/
basic_pstream(const argv_type& argv, pmode mode = pstdout|pstdin)
: iostream_type(NULL), pbase_type(argv.at(0), argv, mode)
{ }
/**
* @brief Destructor
*
* Closes the stream and waits for the child to exit.
*/
~basic_pstream() { }
/**
* @brief Start a process.
*
* Calls do_open( @a %command , @a mode ).
*
* @param command a string containing a shell command.
* @param mode the I/O mode to use when opening the pipe.
* @see do_open(const std::string&, pmode)
*/
void
open(const std::string& command, pmode mode = pstdout|pstdin)
{
this->do_open(command, mode);
}
/**
* @brief Start a process.
*
* Calls do_open( @a file , @a argv , @a mode ).
*
* @param file a string containing the pathname of a program to execute.
* @param argv a vector of argument strings passed to the new program.
* @param mode the I/O mode to use when opening the pipe.
* @see do_open(const std::string&, const argv_type&, pmode)
*/
void
open( const std::string& file,
const argv_type& argv,
pmode mode = pstdout|pstdin )
{
this->do_open(file, argv, mode);
}
/**
* @brief Set streambuf to read from process' @c stdout.
* @return @c *this
*/
basic_pstream&
out()
{
this->buf_.read_err(false);
return *this;
}
/**
* @brief Set streambuf to read from process' @c stderr.
* @return @c *this
*/
basic_pstream&
err()
{
this->buf_.read_err(true);
return *this;
}
};
/**
* @class basic_rpstream
* @brief Class template for Restricted PStreams.
*
* Writing to an rpstream opened with @c pmode @c pstdin writes to the
* standard input of the command.
* It is not possible to read directly from an rpstream object, to use
* an rpstream as in istream you must call either basic_rpstream::out()
* or basic_rpstream::err(). This is to prevent accidental reads from
* the wrong input source. If the rpstream was not opened with @c pmode
* @c pstderr then the class cannot read the process' @c stderr, and
* basic_rpstream::err() will return an istream that reads from the
* process' @c stdout, and vice versa.
* Reading from an rpstream opened with @c pmode @c pstdout and/or
* @c pstderr reads the command's standard output and/or standard error.
* Any of the process' @c stdin, @c stdout or @c stderr that is not
* connected to the pstream (as specified by the @c pmode)
* will be the same as the process that created the pstream object,
* unless altered by the command itself.
*/
template >
class basic_rpstream
: public std::basic_ostream
, private std::basic_istream
, private pstream_common
, virtual public pstreams
{
typedef std::basic_ostream ostream_type;
typedef std::basic_istream istream_type;
typedef pstream_common pbase_type;
using pbase_type::buf_; // declare name in this scope
public:
/// Type used to specify how to connect to the process.
typedef typename pbase_type::pmode pmode;
/// Type used to hold the arguments for a command.
typedef typename pbase_type::argv_type argv_type;
/// Default constructor, creates an uninitialised stream.
basic_rpstream()
: ostream_type(NULL), istream_type(NULL), pbase_type()
{ }
/**
* @brief Constructor that initialises the stream by starting a process.
*
* Initialises the stream buffer by calling do_open() with the supplied
* arguments.
*
* @param command a string containing a shell command.
* @param mode the I/O mode to use when opening the pipe.
* @see do_open(const std::string&, pmode)
*/
basic_rpstream(const std::string& command, pmode mode = pstdout|pstdin)
: ostream_type(NULL) , istream_type(NULL) , pbase_type(command, mode)
{ }
/**
* @brief Constructor that initialises the stream by starting a process.
*
* Initialises the stream buffer by calling do_open() with the supplied
* arguments.
*
* @param file a string containing the pathname of a program to execute.
* @param argv a vector of argument strings passed to the new program.
* @param mode the I/O mode to use when opening the pipe.
* @see do_open(const std::string&, const argv_type&, pmode)
*/
basic_rpstream( const std::string& file,
const argv_type& argv,
pmode mode = pstdout|pstdin )
: ostream_type(NULL), istream_type(NULL), pbase_type(file, argv, mode)
{ }
/**
* @brief Constructor that initialises the stream by starting a process.
*
* Initialises the stream buffer by calling
* @c do_open(argv[0],argv,mode)
*
* @param argv a vector of argument strings passed to the new program.
* @param mode the I/O mode to use when opening the pipe.
* @see do_open(const std::string&, const argv_type&, pmode)
*/
basic_rpstream(const argv_type& argv, pmode mode = pstdout|pstdin)
: ostream_type(NULL), istream_type(NULL),
pbase_type(argv.at(0), argv, mode)
{ }
/// Destructor
~basic_rpstream() { }
/**
* @brief Start a process.
*
* Calls do_open( @a %command , @a mode ).
*
* @param command a string containing a shell command.
* @param mode the I/O mode to use when opening the pipe.
* @see do_open(const std::string&, pmode)
*/
void
open(const std::string& command, pmode mode = pstdout|pstdin)
{
this->do_open(command, mode);
}
/**
* @brief Start a process.
*
* Calls do_open( @a file , @a argv , @a mode ).
*
* @param file a string containing the pathname of a program to execute.
* @param argv a vector of argument strings passed to the new program.
* @param mode the I/O mode to use when opening the pipe.
* @see do_open(const std::string&, const argv_type&, pmode)
*/
void
open( const std::string& file,
const argv_type& argv,
pmode mode = pstdout|pstdin )
{
this->do_open(file, argv, mode);
}
/**
* @brief Obtain a reference to the istream that reads
* the process' @c stdout.
* @return @c *this
*/
istream_type&
out()
{
this->buf_.read_err(false);
return *this;
}
/**
* @brief Obtain a reference to the istream that reads
* the process' @c stderr.
* @return @c *this
*/
istream_type&
err()
{
this->buf_.read_err(true);
return *this;
}
};
/// Type definition for common template specialisation.
typedef basic_pstreambuf pstreambuf;
/// Type definition for common template specialisation.
typedef basic_ipstream ipstream;
/// Type definition for common template specialisation.
typedef basic_opstream opstream;
/// Type definition for common template specialisation.
typedef basic_pstream pstream;
/// Type definition for common template specialisation.
typedef basic_rpstream rpstream;
/**
* When inserted into an output pstream the manipulator calls
* basic_pstreambuf::peof() to close the output pipe,
* causing the child process to receive the end-of-file indicator
* on subsequent reads from its @c stdin stream.
*
* @brief Manipulator to close the pipe connected to the process' stdin.
* @param s An output PStream class.
* @return The stream object the manipulator was invoked on.
* @see basic_pstreambuf::peof()
* @relates basic_opstream basic_pstream basic_rpstream
*/
template
inline std::basic_ostream&
peof(std::basic_ostream& s)
{
typedef basic_pstreambuf pstreambuf;
if (pstreambuf* p = dynamic_cast(s.rdbuf()))
p->peof();
return s;
}
/*
* member definitions for pstreambuf
*/
/**
* @class basic_pstreambuf
* Provides underlying streambuf functionality for the PStreams classes.
*/
/** Creates an uninitialised stream buffer. */
template
inline
basic_pstreambuf::basic_pstreambuf()
: ppid_(-1) // initialise to -1 to indicate no process run yet.
, wpipe_(-1)
, wbuffer_(NULL)
, rsrc_(rsrc_out)
, status_(-1)
, error_(0)
{
init_rbuffers();
}
/**
* Initialises the stream buffer by calling open() with the supplied
* arguments.
*
* @param command a string containing a shell command.
* @param mode the I/O mode to use when opening the pipe.
* @see open()
*/
template
inline
basic_pstreambuf::basic_pstreambuf(const std::string& command, pmode mode)
: ppid_(-1) // initialise to -1 to indicate no process run yet.
, wpipe_(-1)
, wbuffer_(NULL)
, rsrc_(rsrc_out)
, status_(-1)
, error_(0)
{
init_rbuffers();
open(command, mode);
}
/**
* Initialises the stream buffer by calling open() with the supplied
* arguments.
*
* @param file a string containing the name of a program to execute.
* @param argv a vector of argument strings passsed to the new program.
* @param mode the I/O mode to use when opening the pipe.
* @see open()
*/
template
inline
basic_pstreambuf::basic_pstreambuf( const std::string& file,
const argv_type& argv,
pmode mode )
: ppid_(-1) // initialise to -1 to indicate no process run yet.
, wpipe_(-1)
, wbuffer_(NULL)
, rsrc_(rsrc_out)
, status_(-1)
, error_(0)
{
init_rbuffers();
open(file, argv, mode);
}
/**
* Closes the stream by calling close().
* @see close()
*/
template
inline
basic_pstreambuf::~basic_pstreambuf()
{
close();
}
/**
* Starts a new process by passing @a command to the shell (/bin/sh)
* and opens pipes to the process with the specified @a mode.
*
* If @a mode contains @c pstdout the initial read source will be
* the child process' stdout, otherwise if @a mode contains @c pstderr
* the initial read source will be the child's stderr.
*
* Will duplicate the actions of the shell in searching for an
* executable file if the specified file name does not contain a slash (/)
* character.
*
* @warning
* There is no way to tell whether the shell command succeeded, this
* function will always succeed unless resource limits (such as
* memory usage, or number of processes or open files) are exceeded.
* This means is_open() will return true even if @a command cannot
* be executed.
* Use pstreambuf::open(const std::string&, const argv_type&, pmode)
* if you need to know whether the command failed to execute.
*
* @param command a string containing a shell command.
* @param mode a bitwise OR of one or more of @c out, @c in, @c err.
* @return NULL if the shell could not be started or the
* pipes could not be opened, @c this otherwise.
* @see execl(3)
*/
template
basic_pstreambuf*
basic_pstreambuf::open(const std::string& command, pmode mode)
{
const char * shell_path = "/bin/sh";
#if 0
const std::string argv[] = { "sh", "-c", command };
return this->open(shell_path, argv_type(argv, argv+3), mode);
#else
basic_pstreambuf* ret = NULL;
if (!is_open())
{
switch(fork(mode))
{
case 0 :
// this is the new process, exec command
::execl(shell_path, "sh", "-c", command.c_str(), (char*)NULL);
// can only reach this point if exec() failed
// parent can get exit code from waitpid()
::_exit(errno);
// using std::exit() would make static dtors run twice
case -1 :
// couldn't fork, error already handled in pstreambuf::fork()
break;
default :
// this is the parent process
// activate buffers
create_buffers(mode);
ret = this;
}
}
return ret;
#endif
}
/**
* @brief Helper function to close a file descriptor.
*
* Inspects @a fd and calls close(3) if it has a non-negative value.
*
* @param fd a file descriptor.
* @relates basic_pstreambuf
*/
inline void
close_fd(pstreams::fd_type& fd)
{
if (fd >= 0 && ::close(fd) == 0)
fd = -1;
}
/**
* @brief Helper function to close an array of file descriptors.
*
* Calls @c close_fd() on each member of the array.
* The length of the array is determined automatically by
* template argument deduction to avoid errors.
*
* @param fds an array of file descriptors.
* @relates basic_pstreambuf
*/
template
inline void
close_fd_array(pstreams::fd_type (&fds)[N])
{
for (std::size_t i = 0; i < N; ++i)
close_fd(fds[i]);
}
/**
* Starts a new process by executing @a file with the arguments in
* @a argv and opens pipes to the process with the specified @a mode.
*
* By convention @c argv[0] should be the file name of the file being
* executed.
*
* If @a mode contains @c pstdout the initial read source will be
* the child process' stdout, otherwise if @a mode contains @c pstderr
* the initial read source will be the child's stderr.
*
* Will duplicate the actions of the shell in searching for an
* executable file if the specified file name does not contain a slash (/)
* character.
*
* Iff @a file is successfully executed then is_open() will return true.
* Otherwise, pstreambuf::error() can be used to obtain the value of
* @c errno that was set by execvp(3) in the child process.
*
* The exit status of the new process will be returned by
* pstreambuf::status() after pstreambuf::exited() returns true.
*
* @param file a string containing the pathname of a program to execute.
* @param argv a vector of argument strings passed to the new program.
* @param mode a bitwise OR of one or more of @c out, @c in and @c err.
* @return NULL if a pipe could not be opened or if the program could
* not be executed, @c this otherwise.
* @see execvp(3)
*/
template
basic_pstreambuf*
basic_pstreambuf::open( const std::string& file,
const argv_type& argv,
pmode mode )
{
basic_pstreambuf* ret = NULL;
if (!is_open())
{
// constants for read/write ends of pipe
enum { RD, WR };
// open another pipe and set close-on-exec
fd_type ck_exec[] = { -1, -1 };
if (-1 == ::pipe(ck_exec)
|| -1 == ::fcntl(ck_exec[RD], F_SETFD, FD_CLOEXEC)
|| -1 == ::fcntl(ck_exec[WR], F_SETFD, FD_CLOEXEC))
{
error_ = errno;
close_fd_array(ck_exec);
}
else
{
switch(fork(mode))
{
case 0 :
// this is the new process, exec command
{
char** arg_v = new char*[argv.size()+1];
for (std::size_t i = 0; i < argv.size(); ++i)
{
const std::string& src = argv[i];
char*& dest = arg_v[i];
dest = new char[src.size()+1];
dest[ src.copy(dest, src.size()) ] = '\0';
}
arg_v[argv.size()] = NULL;
::execvp(file.c_str(), arg_v);
// can only reach this point if exec() failed
// parent can get error code from ck_exec pipe
error_ = errno;
while (::write(ck_exec[WR], &error_, sizeof(error_)) == -1
&& errno == EINTR)
{ }
::close(ck_exec[WR]);
::close(ck_exec[RD]);
::_exit(error_);
// using std::exit() would make static dtors run twice
}
case -1 :
// couldn't fork, error already handled in pstreambuf::fork()
close_fd_array(ck_exec);
break;
default :
// this is the parent process
// check child called exec() successfully
::close(ck_exec[WR]);
switch (::read(ck_exec[RD], &error_, sizeof(error_)))
{
case 0:
// activate buffers
create_buffers(mode);
ret = this;
break;
case -1:
error_ = errno;
break;
default:
// error_ contains error code from child
// call wait() to clean up and set ppid_ to 0
this->wait();
break;
}
::close(ck_exec[RD]);
}
}
}
return ret;
}
/**
* Creates pipes as specified by @a mode and calls @c fork() to create
* a new process. If the fork is successful the parent process stores
* the child's PID and the opened pipes and the child process replaces
* its standard streams with the opened pipes.
*
* If an error occurs the error code will be set to one of the possible
* errors for @c pipe() or @c fork().
* See your system's documentation for these error codes.
*
* @param mode an OR of pmodes specifying which of the child's
* standard streams to connect to.
* @return On success the PID of the child is returned in the parent's
* context and zero is returned in the child's context.
* On error -1 is returned and the error code is set appropriately.
*/
template
pid_t
basic_pstreambuf::fork(pmode mode)
{
pid_t pid = -1;
// Three pairs of file descriptors, for pipes connected to the
// process' stdin, stdout and stderr
// (stored in a single array so close_fd_array() can close all at once)
fd_type fd[] = { -1, -1, -1, -1, -1, -1 };
fd_type* const pin = fd;
fd_type* const pout = fd+2;
fd_type* const perr = fd+4;
// constants for read/write ends of pipe
enum { RD, WR };
// N.B.
// For the pstreambuf pin is an output stream and
// pout and perr are input streams.
if (!error_ && mode&pstdin && ::pipe(pin))
error_ = errno;
if (!error_ && mode&pstdout && ::pipe(pout))
error_ = errno;
if (!error_ && mode&pstderr && ::pipe(perr))
error_ = errno;
if (!error_)
{
pid = ::fork();
switch (pid)
{
case 0 :
{
// this is the new process
// for each open pipe close one end and redirect the
// respective standard stream to the other end
if (*pin >= 0)
{
::close(pin[WR]);
::dup2(pin[RD], STDIN_FILENO);
::close(pin[RD]);
}
if (*pout >= 0)
{
::close(pout[RD]);
::dup2(pout[WR], STDOUT_FILENO);
::close(pout[WR]);
}
if (*perr >= 0)
{
::close(perr[RD]);
::dup2(perr[WR], STDERR_FILENO);
::close(perr[WR]);
}
#ifdef _POSIX_JOB_CONTROL
// Change to a new process group
::setpgid(0, 0);
#endif
break;
}
case -1 :
{
// couldn't fork for some reason
error_ = errno;
// close any open pipes
close_fd_array(fd);
break;
}
default :
{
// this is the parent process, store process' pid
ppid_ = pid;
// store one end of open pipes and close other end
if (*pin >= 0)
{
wpipe_ = pin[WR];
::close(pin[RD]);
}
if (*pout >= 0)
{
rpipe_[rsrc_out] = pout[RD];
::close(pout[WR]);
}
if (*perr >= 0)
{
rpipe_[rsrc_err] = perr[RD];
::close(perr[WR]);
}
}
}
}
else
{
// close any pipes we opened before failure
close_fd_array(fd);
}
return pid;
}
/**
* Closes all pipes and calls wait() to wait for the process to finish.
* If an error occurs the error code will be set to one of the possible
* errors for @c waitpid().
* See your system's documentation for these errors.
*
* @return @c this on successful close or @c NULL if there is no
* process to close or if an error occurs.
*/
template
basic_pstreambuf*
basic_pstreambuf::close()
{
const bool running = is_open();
sync(); // this might call wait() and reap the child process
// rather than trying to work out whether or not we need to clean up
// just do it anyway, all cleanup functions are safe to call twice.
destroy_buffers(pstdin|pstdout|pstderr);
// close pipes before wait() so child gets EOF/SIGPIPE
close_fd(wpipe_);
close_fd_array(rpipe_);
do
{
error_ = 0;
} while (wait() == -1 && error() == EINTR);
return running ? this : NULL;
}
/**
* Called on construction to initialise the arrays used for reading.
*/
template
inline void
basic_pstreambuf::init_rbuffers()
{
rpipe_[rsrc_out] = rpipe_[rsrc_err] = -1;
rbuffer_[rsrc_out] = rbuffer_[rsrc_err] = NULL;
rbufstate_[0] = rbufstate_[1] = rbufstate_[2] = NULL;
}
template
void
basic_pstreambuf::create_buffers(pmode mode)
{
if (mode & pstdin)
{
delete[] wbuffer_;
wbuffer_ = new char_type[bufsz];
this->setp(wbuffer_, wbuffer_ + bufsz);
}
if (mode & pstdout)
{
delete[] rbuffer_[rsrc_out];
rbuffer_[rsrc_out] = new char_type[bufsz];
rsrc_ = rsrc_out;
this->setg(rbuffer_[rsrc_out] + pbsz, rbuffer_[rsrc_out] + pbsz,
rbuffer_[rsrc_out] + pbsz);
}
if (mode & pstderr)
{
delete[] rbuffer_[rsrc_err];
rbuffer_[rsrc_err] = new char_type[bufsz];
if (!(mode & pstdout))
{
rsrc_ = rsrc_err;
this->setg(rbuffer_[rsrc_err] + pbsz, rbuffer_[rsrc_err] + pbsz,
rbuffer_[rsrc_err] + pbsz);
}
}
}
template
void
basic_pstreambuf::destroy_buffers(pmode mode)
{
if (mode & pstdin)
{
this->setp(NULL, NULL);
delete[] wbuffer_;
wbuffer_ = NULL;
}
if (mode & pstdout)
{
if (rsrc_ == rsrc_out)
this->setg(NULL, NULL, NULL);
delete[] rbuffer_[rsrc_out];
rbuffer_[rsrc_out] = NULL;
}
if (mode & pstderr)
{
if (rsrc_ == rsrc_err)
this->setg(NULL, NULL, NULL);
delete[] rbuffer_[rsrc_err];
rbuffer_[rsrc_err] = NULL;
}
}
template
typename basic_pstreambuf::buf_read_src
basic_pstreambuf::switch_read_buffer(buf_read_src src)
{
if (rsrc_ != src)
{
char_type* tmpbufstate[] = {this->eback(), this->gptr(), this->egptr()};
this->setg(rbufstate_[0], rbufstate_[1], rbufstate_[2]);
for (std::size_t i = 0; i < 3; ++i)
rbufstate_[i] = tmpbufstate[i];
rsrc_ = src;
}
return rsrc_;
}
/**
* Suspends execution and waits for the associated process to exit, or
* until a signal is delivered whose action is to terminate the current
* process or to call a signal handling function. If the process has
* already exited (i.e. it is a "zombie" process) then wait() returns
* immediately. Waiting for the child process causes all its system
* resources to be freed.
*
* error() will return EINTR if wait() is interrupted by a signal.
*
* @param nohang true to return immediately if the process has not exited.
* @return 1 if the process has exited and wait() has not yet been called.
* 0 if @a nohang is true and the process has not exited yet.
* -1 if no process has been started or if an error occurs,
* in which case the error can be found using error().
*/
template
int
basic_pstreambuf::wait(bool nohang)
{
int exited = -1;
if (is_open())
{
int status;
switch(::waitpid(ppid_, &status, nohang ? WNOHANG : 0))
{
case 0 :
// nohang was true and process has not exited
exited = 0;
break;
case -1 :
error_ = errno;
break;
default :
// process has exited
ppid_ = 0;
status_ = status;
exited = 1;
// Close wpipe, would get SIGPIPE if we used it.
destroy_buffers(pstdin);
close_fd(wpipe_);
// Must free read buffers and pipes on destruction
// or next call to open()/close()
break;
}
}
return exited;
}
/**
* Sends the specified signal to the process. A signal can be used to
* terminate a child process that would not exit otherwise.
*
* If an error occurs the error code will be set to one of the possible
* errors for @c kill(). See your system's documentation for these errors.
*
* @param signal A signal to send to the child process.
* @return @c this or @c NULL if @c kill() fails.
*/
template
inline basic_pstreambuf*
basic_pstreambuf::kill(int signal)
{
basic_pstreambuf* ret = NULL;
if (is_open())
{
if (::kill(ppid_, signal))
error_ = errno;
else
{
#if 0
// TODO call exited() to check for exit and clean up? leave to user?
if (signal==SIGTERM || signal==SIGKILL)
this->exited();
#endif
ret = this;
}
}
return ret;
}
/**
* Sends the specified signal to the process group of the child process.
* A signal can be used to terminate a child process that would not exit
* otherwise, or to kill the process and its own children.
*
* If an error occurs the error code will be set to one of the possible
* errors for @c getpgid() or @c kill(). See your system's documentation
* for these errors. If the child is in the current process group then
* NULL will be returned and the error code set to EPERM.
*
* @param signal A signal to send to the child process.
* @return @c this on success or @c NULL on failure.
*/
template
inline basic_pstreambuf*
basic_pstreambuf::killpg(int signal)
{
basic_pstreambuf* ret = NULL;
#ifdef _POSIX_JOB_CONTROL
if (is_open())
{
pid_t pgid = ::getpgid(ppid_);
if (pgid == -1)
error_ = errno;
else if (pgid == ::getpgrp())
error_ = EPERM; // Don't commit suicide
else if (::killpg(pgid, signal))
error_ = errno;
else
ret = this;
}
#endif
return ret;
}
/**
* This function can call pstreambuf::wait() and so may change the
* object's state if the child process has already exited.
*
* @return True if the associated process has exited, false otherwise.
* @see basic_pstreambuf::wait()
*/
template
inline bool
basic_pstreambuf::exited()
{
return ppid_ == 0 || wait(true)==1;
}
/**
* @return The exit status of the child process, or -1 if wait()
* has not yet been called to wait for the child to exit.
* @see basic_pstreambuf::wait()
*/
template
inline int
basic_pstreambuf::status() const
{
return status_;
}
/**
* @return The error code of the most recently failed operation, or zero.
*/
template
inline int
basic_pstreambuf::error() const
{
return error_;
}
/**
* Closes the output pipe, causing the child process to receive the
* end-of-file indicator on subsequent reads from its @c stdin stream.
*/
template
inline void
basic_pstreambuf::peof()
{
sync();
destroy_buffers(pstdin);
close_fd(wpipe_);
}
/**
* Unlike pstreambuf::exited(), this function will not call wait() and
* so will not change the object's state. This means that once a child
* process is executed successfully this function will continue to
* return true even after the process exits (until wait() is called.)
*
* @return true if a previous call to open() succeeded and wait() has
* not been called and determined that the process has exited,
* false otherwise.
*/
template
inline bool
basic_pstreambuf::is_open() const
{
return ppid_ > 0;
}
/**
* Toggle the stream used for reading. If @a readerr is @c true then the
* process' @c stderr output will be used for subsequent extractions, if
* @a readerr is false the the process' stdout will be used.
* @param readerr @c true to read @c stderr, @c false to read @c stdout.
* @return @c true if the requested stream is open and will be used for
* subsequent extractions, @c false otherwise.
*/
template
inline bool
basic_pstreambuf::read_err(bool readerr)
{
buf_read_src src = readerr ? rsrc_err : rsrc_out;
if (rpipe_[src]>=0)
{
switch_read_buffer(src);
return true;
}
return false;
}
/**
* Called when the internal character buffer is not present or is full,
* to transfer the buffer contents to the pipe.
*
* @param c a character to be written to the pipe.
* @return @c traits_type::eof() if an error occurs, otherwise if @a c
* is not equal to @c traits_type::eof() it will be buffered and
* a value other than @c traits_type::eof() returned to indicate
* success.
*/
template
typename basic_pstreambuf::int_type
basic_pstreambuf::overflow(int_type c)
{
if (!empty_buffer())
return traits_type::eof();
else if (!traits_type::eq_int_type(c, traits_type::eof()))
return this->sputc(c);
else
return traits_type::not_eof(c);
}
template
int
basic_pstreambuf::sync()
{
return !exited() && empty_buffer() ? 0 : -1;
}
/**
* @param s character buffer.
* @param n buffer length.
* @return the number of characters written.
*/
template
std::streamsize
basic_pstreambuf::xsputn(const char_type* s, std::streamsize n)
{
std::streamsize done = 0;
while (done < n)
{
if (std::streamsize nbuf = this->epptr() - this->pptr())
{
nbuf = std::min(nbuf, n - done);
traits_type::copy(this->pptr(), s + done, nbuf);
this->pbump(nbuf);
done += nbuf;
}
else if (!empty_buffer())
break;
}
return done;
}
/**
* @return true if the buffer was emptied, false otherwise.
*/
template
bool
basic_pstreambuf::empty_buffer()
{
const std::streamsize count = this->pptr() - this->pbase();
if (count > 0)
{
const std::streamsize written = this->write(this->wbuffer_, count);
if (written > 0)
{
if (const std::streamsize unwritten = count - written)
traits_type::move(this->pbase(), this->pbase()+written, unwritten);
this->pbump(-written);
return true;
}
}
return false;
}
/**
* Called when the internal character buffer is is empty, to re-fill it
* from the pipe.
*
* @return The first available character in the buffer,
* or @c traits_type::eof() in case of failure.
*/
template
typename basic_pstreambuf::int_type
basic_pstreambuf::underflow()
{
if (this->gptr() < this->egptr() || fill_buffer())
return traits_type::to_int_type(*this->gptr());
else
return traits_type::eof();
}
/**
* Attempts to make @a c available as the next character to be read by
* @c sgetc().
*
* @param c a character to make available for extraction.
* @return @a c if the character can be made available,
* @c traits_type::eof() otherwise.
*/
template
typename basic_pstreambuf::int_type
basic_pstreambuf::pbackfail(int_type c)
{
if (this->gptr() != this->eback())
{
this->gbump(-1);
if (!traits_type::eq_int_type(c, traits_type::eof()))
*this->gptr() = traits_type::to_char_type(c);
return traits_type::not_eof(c);
}
else
return traits_type::eof();
}
template
std::streamsize
basic_pstreambuf::showmanyc()
{
int avail = 0;
if (sizeof(char_type) == 1)
avail = fill_buffer(true) ? this->egptr() - this->gptr() : -1;
#ifdef FIONREAD
else
{
if (::ioctl(rpipe(), FIONREAD, &avail) == -1)
avail = -1;
else if (avail)
avail /= sizeof(char_type);
}
#endif
return std::streamsize(avail);
}
/**
* @return true if the buffer was filled, false otherwise.
*/
template
bool
basic_pstreambuf::fill_buffer(bool non_blocking)
{
const std::streamsize pb1 = this->gptr() - this->eback();
const std::streamsize pb2 = pbsz;
const std::streamsize npb = std::min(pb1, pb2);
char_type* const rbuf = rbuffer();
traits_type::move(rbuf + pbsz - npb, this->gptr() - npb, npb);
std::streamsize rc = -1;
if (non_blocking)
{
const int flags = ::fcntl(rpipe(), F_GETFL);
if (flags != -1)
{
const bool blocking = !(flags & O_NONBLOCK);
if (blocking)
::fcntl(rpipe(), F_SETFL, flags | O_NONBLOCK); // set non-blocking
error_ = 0;
rc = read(rbuf + pbsz, bufsz - pbsz);
if (rc == -1 && error_ == EAGAIN) // nothing available
rc = 0;
else if (rc == 0) // EOF
rc = -1;
if (blocking)
::fcntl(rpipe(), F_SETFL, flags); // restore
}
}
else
rc = read(rbuf + pbsz, bufsz - pbsz);
if (rc > 0 || (rc == 0 && non_blocking))
{
this->setg( rbuf + pbsz - npb,
rbuf + pbsz,
rbuf + pbsz + rc );
return true;
}
else
{
this->setg(NULL, NULL, NULL);
return false;
}
}
/**
* Writes up to @a n characters to the pipe from the buffer @a s.
*
* @param s character buffer.
* @param n buffer length.
* @return the number of characters written.
*/
template
inline std::streamsize
basic_pstreambuf::write(const char_type* s, std::streamsize n)
{
std::streamsize nwritten = 0;
if (wpipe() >= 0)
{
nwritten = ::write(wpipe(), s, n * sizeof(char_type));
if (nwritten == -1)
error_ = errno;
else
nwritten /= sizeof(char_type);
}
return nwritten;
}
/**
* Reads up to @a n characters from the pipe to the buffer @a s.
*
* @param s character buffer.
* @param n buffer length.
* @return the number of characters read.
*/
template
inline std::streamsize
basic_pstreambuf::read(char_type* s, std::streamsize n)
{
std::streamsize nread = 0;
if (rpipe() >= 0)
{
nread = ::read(rpipe(), s, n * sizeof(char_type));
if (nread == -1)
error_ = errno;
else
nread /= sizeof(char_type);
}
return nread;
}
/** @return a reference to the output file descriptor */
template
inline pstreams::fd_type&
basic_pstreambuf::wpipe()
{
return wpipe_;
}
/** @return a reference to the active input file descriptor */
template
inline pstreams::fd_type&
basic_pstreambuf::rpipe()
{
return rpipe_[rsrc_];
}
/** @return a reference to the specified input file descriptor */
template
inline pstreams::fd_type&
basic_pstreambuf::rpipe(buf_read_src which)
{
return rpipe_[which];
}
/** @return a pointer to the start of the active input buffer area. */
template
inline typename basic_pstreambuf::char_type*
basic_pstreambuf::rbuffer()
{
return rbuffer_[rsrc_];
}
/*
* member definitions for pstream_common
*/
/**
* @class pstream_common
* Abstract Base Class providing common functionality for basic_ipstream,
* basic_opstream and basic_pstream.
* pstream_common manages the basic_pstreambuf stream buffer that is used
* by the derived classes to initialise an iostream class.
*/
/** Creates an uninitialised stream. */
template
inline
pstream_common::pstream_common()
: std::basic_ios(NULL)
, command_()
, buf_()
{
this->std::basic_ios::rdbuf(&buf_);
}
/**
* Initialises the stream buffer by calling
* do_open( @a command , @a mode )
*
* @param command a string containing a shell command.
* @param mode the I/O mode to use when opening the pipe.
* @see do_open(const std::string&, pmode)
*/
template
inline
pstream_common::pstream_common(const std::string& command, pmode mode)
: std::basic_ios(NULL)
, command_(command)
, buf_()
{
this->std::basic_ios::rdbuf(&buf_);
do_open(command, mode);
}
/**
* Initialises the stream buffer by calling
* do_open( @a file , @a argv , @a mode )
*
* @param file a string containing the pathname of a program to execute.
* @param argv a vector of argument strings passed to the new program.
* @param mode the I/O mode to use when opening the pipe.
* @see do_open(const std::string&, const argv_type&, pmode)
*/
template
inline
pstream_common::pstream_common( const std::string& file,
const argv_type& argv,
pmode mode )
: std::basic_ios(NULL)
, command_(file)
, buf_()
{
this->std::basic_ios::rdbuf(&buf_);
do_open(file, argv, mode);
}
/**
* This is a pure virtual function to make @c pstream_common abstract.
* Because it is the destructor it will be called by derived classes
* and so must be defined. It is also protected, to discourage use of
* the PStreams classes through pointers or references to the base class.
*
* @sa If defining a pure virtual seems odd you should read
* http://www.gotw.ca/gotw/031.htm (and the rest of the site as well!)
*/
template
inline
pstream_common::~pstream_common()
{
}
/**
* Calls rdbuf()->open( @a command , @a mode )
* and sets @c failbit on error.
*
* @param command a string containing a shell command.
* @param mode the I/O mode to use when opening the pipe.
* @see basic_pstreambuf::open(const std::string&, pmode)
*/
template
inline void
pstream_common::do_open(const std::string& command, pmode mode)
{
if (!buf_.open((command_=command), mode))
this->setstate(std::ios_base::failbit);
}
/**
* Calls rdbuf()->open( @a file, @a argv, @a mode )
* and sets @c failbit on error.
*
* @param file a string containing the pathname of a program to execute.
* @param argv a vector of argument strings passed to the new program.
* @param mode the I/O mode to use when opening the pipe.
* @see basic_pstreambuf::open(const std::string&, const argv_type&, pmode)
*/
template
inline void
pstream_common::do_open( const std::string& file,
const argv_type& argv,
pmode mode )
{
if (!buf_.open((command_=file), argv, mode))
this->setstate(std::ios_base::failbit);
}
/** Calls rdbuf->close() and sets @c failbit on error. */
template
inline void
pstream_common::close()
{
if (!buf_.close())
this->setstate(std::ios_base::failbit);
}
/**
* @return rdbuf()->is_open().
* @see basic_pstreambuf::is_open()
*/
template
inline bool
pstream_common::is_open() const
{
return buf_.is_open();
}
/** @return a string containing the command used to initialise the stream. */
template
inline const std::string&
pstream_common::command() const
{
return command_;
}
/** @return a pointer to the private stream buffer member. */
// TODO document behaviour if buffer replaced.
template
inline typename pstream_common::streambuf_type*
pstream_common::rdbuf() const
{
return const_cast(&buf_);
}
#if REDI_EVISCERATE_PSTREAMS
/**
* @def REDI_EVISCERATE_PSTREAMS
* If this macro has a non-zero value then certain internals of the
* @c basic_pstreambuf template class are exposed. In general this is
* a Bad Thing, as the internal implementation is largely undocumented
* and may be subject to change at any time, so this feature is only
* provided because it might make PStreams useful in situations where
* it is necessary to do Bad Things.
*/
/**
* @warning This function exposes the internals of the stream buffer and
* should be used with caution. It is the caller's responsibility
* to flush streams etc. in order to clear any buffered data.
* The POSIX.1 function fdopen(3) is used to obtain the
* @c FILE pointers from the streambuf's private file descriptor
* members so consult your system's documentation for
* fdopen(3).
*
* @param in A FILE* that will refer to the process' stdin.
* @param out A FILE* that will refer to the process' stdout.
* @param err A FILE* that will refer to the process' stderr.
* @return An OR of zero or more of @c pstdin, @c pstdout, @c pstderr.
*
* For each open stream shared with the child process a @c FILE* is
* obtained and assigned to the corresponding parameter. For closed
* streams @c NULL is assigned to the parameter.
* The return value can be tested to see which parameters should be
* @c !NULL by masking with the corresponding @c pmode value.
*
* @see fdopen(3)
*/
template
std::size_t
basic_pstreambuf::fopen(FILE*& in, FILE*& out, FILE*& err)
{
in = out = err = NULL;
std::size_t open_files = 0;
if (wpipe() > -1)
{
if ((in = ::fdopen(wpipe(), "w")))
{
open_files |= pstdin;
}
}
if (rpipe(rsrc_out) > -1)
{
if ((out = ::fdopen(rpipe(rsrc_out), "r")))
{
open_files |= pstdout;
}
}
if (rpipe(rsrc_err) > -1)
{
if ((err = ::fdopen(rpipe(rsrc_err), "r")))
{
open_files |= pstderr;
}
}
return open_files;
}
/**
* @warning This function exposes the internals of the stream buffer and
* should be used with caution.
*
* @param in A FILE* that will refer to the process' stdin.
* @param out A FILE* that will refer to the process' stdout.
* @param err A FILE* that will refer to the process' stderr.
* @return A bitwise-or of zero or more of @c pstdin, @c pstdout, @c pstderr.
* @see basic_pstreambuf::fopen()
*/
template
inline std::size_t
pstream_common::fopen(FILE*& fin, FILE*& fout, FILE*& ferr)
{
return buf_.fopen(fin, fout, ferr);
}
#endif // REDI_EVISCERATE_PSTREAMS
} // namespace redi
/**
* @mainpage PStreams Reference
* @htmlinclude mainpage.html
*/
#endif // REDI_PSTREAM_H_SEEN
// vim: ts=2 sw=2 expandtab
pstreams-0.8.0/ChangeLog 0000664 0000764 0000764 00000127514 12125363352 013605 0 ustar redi redi Author: Jonathan Wakely
Date: Fri Mar 29 19:07:19 2013 +0000
Makefile: Fix rule to not fail if no .git dir
Author: Jonathan Wakely
Date: Fri Mar 29 17:50:32 2013 +0000
.gitignore: Add dist files
Author: Jonathan Wakely
Date: Fri Mar 29 17:42:22 2013 +0000
pstream.h: Doc tweak
Author: Jonathan Wakely
Date: Fri Mar 29 15:48:29 2013 +0000
Makefile: Generate ChangeLog from Git
Author: Jonathan Wakely
Date: Fri Mar 29 15:16:39 2013 +0000
README: Note another improvement.
Author: Jonathan Wakely
Date: Wed Jan 23 00:43:11 2013 +0000
pstream.h: Put child in new process group and define pstreambuf::killpg()
Thanks to Hein-Pieter van Braam for the suggestion.
Author: Jonathan Wakely
Date: Sun Jan 20 19:15:41 2013 +0000
pstream.h: Retry interrupted writes
Author: Jonathan Wakely
Date: Sun Jan 20 18:23:49 2013 +0000
Makefile: remove unused variable
Author: Jonathan Wakely
Date: Sun Jan 20 17:11:27 2013 +0000
pstream.h: Overload constructors for convenience.
Bump version to 0.7.3
Author: Jonathan Wakely
Date: Mon Jun 25 22:57:26 2012 +0100
pstream.h: Update copyright years and remove RCSID.
Author: Jonathan Wakely
Date: Mon Jun 25 22:48:53 2012 +0100
pstream.h (pstreambuf::xsputn): Optimize.
Author: Jonathan Wakely
Date: Mon Jun 25 22:20:48 2012 +0100
Remove outdated TODO comments.
Author: Jonathan Wakely
Date: Mon Jun 25 10:39:29 2012 +0000
Makefile: use order-only prerequisite to generate test file.
Author: Jonathan Wakely
Date: Sun Jun 24 13:03:43 2012 +0100
pstream.h (basic_pstreambuf::open): Work with _FORTIFY_SOURCE.
Author: Jonathan Wakely
Date: Sun Jun 24 13:02:03 2012 +0100
mainpage.html: Bump version.
.gitignore: Add.
Author: Jonathan Wakely
Date: Sat Apr 14 14:23:51 2012 +0100
* Makefile: Fix typo in comment.
Author: Jonathan Wakely
Date: Tue Nov 15 11:12:10 2011 +0000
(basic_pstreambuf::wpipe, basic_pstreambuf::rpipe): Fix for clang.
(PSTREAMS_VERSION): Bump to 0.7.2
Author: Jonathan Wakely
Date: Thu Oct 14 21:38:38 2010 +0000
* pstreams-devel.spec: Update version and change make variable.
Author: Jonathan Wakely
Date: Thu Oct 14 21:35:22 2010 +0000
* Makefile: Extract version from header.
* mainpage.html: Update version.
Author: Jonathan Wakely
Date: Thu Oct 14 19:57:41 2010 +0000
* pstream.h, test_pstreams.cc: Update copyright dates.
Author: Jonathan Wakely
Date: Thu Oct 14 19:55:19 2010 +0000
* pstream.h (pstreams_common::pstreams_common): Use basic_ios::rdbuf
to set the streambuf, basic_ios::init(0) has already been called.
Author: Jonathan Wakely
Date: Wed May 12 15:45:21 2010 +0000
Add spec file to tarball
Author: Jonathan Wakely
Date: Wed May 12 15:37:34 2010 +0000
Fix spec file
Author: Jonathan Wakely
Date: Wed May 12 13:36:17 2010 +0000
Update.
Author: Jonathan Wakely
Date: Wed May 12 13:33:54 2010 +0000
Update version to 0.7.0 for release.
Author: Jonathan Wakely
Date: Wed May 12 13:17:14 2010 +0000
Use new name for cvs2cl script.
Author: Jonathan Wakely
Date: Wed May 12 12:45:25 2010 +0000
Add spec file from Fedora 12
Author: Jonathan Wakely
Date: Sat Mar 20 14:54:35 2010 +0000
Test initial mode set correctly for ipstream.
Author: Jonathan Wakely
Date: Sat Mar 20 14:50:47 2010 +0000
Improve documentation for semantics of overflow.
Author: Jonathan Wakely
Date: Sat Mar 20 14:47:09 2010 +0000
Remove preprocessor check for Sun CC and define typedefs unconditionally.
Author: Jonathan Wakely
Date: Sat Mar 20 14:40:59 2010 +0000
Document previous change to initial read source.
Author: Jonathan Wakely
Date: Sat Mar 20 14:36:50 2010 +0000
Don't add pstdout to ipstream mode if pstderr given.
Set initial mode correctly on open.
Bump version to 0.7.0 due to change in behaviour.
Author: Jonathan Wakely
Date: Wed Sep 23 15:53:09 2009 +0000
Remove std qualifiers on FILE as is included and not .
Author: Jonathan Wakely
Date: Wed Sep 23 15:50:34 2009 +0000
Add 'check' target and make 'test' a synonym.
Author: Jonathan Wakely
Date: Mon Dec 15 00:02:04 2008 +0000
Add optimization to CXXFLAGS to get uninitialized warnings from gcc.
Author: Jonathan Wakely
Date: Mon Dec 15 00:00:18 2008 +0000
Replace 'whoami' with 'id -un' for Solaris portability.
Author: Jonathan Wakely
Date: Fri Nov 21 01:26:17 2008 +0000
Follow GNU make conventions.
Author: Jonathan Wakely
Date: Fri Nov 21 01:05:44 2008 +0000
Support DESTDIR and preserve timestamps, thanks to Till Maas.
Author: Jonathan Wakely
Date: Mon Sep 22 20:04:38 2008 +0000
Bug 2118980: Use more portable UTF-32 as iconv target.
Author: Jonathan Wakely
Date: Mon Sep 22 19:58:58 2008 +0000
Fix bug 2118980: generate dependency for test_pstreams
Author: Jonathan Wakely
Date: Sat Jul 12 17:25:07 2008 +0000
Change capitalistion.
Author: Jonathan Wakely
Date: Wed Jul 9 22:39:41 2008 +0000
Mention licence change!
Author: Jonathan Wakely
Date: Wed Jul 9 22:12:08 2008 +0000
Tweak comments.
Author: Jonathan Wakely
Date: Wed Jul 9 22:05:08 2008 +0000
Add example.
Author: Jonathan Wakely
Date: Mon Jul 7 22:33:17 2008 +0000
Only show trunk in ChangeLog.
Author: Jonathan Wakely
Date: Mon Jul 7 22:25:47 2008 +0000
Version to 0.6.0
Author: Jonathan Wakely
Date: Mon Jul 7 22:24:14 2008 +0000
Fix typo.
Author: Jonathan Wakely
Date: Mon Jul 7 22:15:14 2008 +0000
Combine rules, set VERS=0.6.0
Author: Jonathan Wakely
Date: Mon Jul 7 22:12:17 2008 +0000
Release notes for 0.6.0
Author: Jonathan Wakely
Date: Mon Jul 7 21:38:14 2008 +0000
Change licence to LGPLv3.
Author: Jonathan Wakely
Date: Mon Dec 17 00:02:47 2007 +0000
Adjust copyright date.
Author: Jonathan Wakely
Date: Mon Dec 17 00:00:28 2007 +0000
Get rid of CONDITIONAL_SLEEP.
Author: Jonathan Wakely
Date: Sun Dec 16 23:49:21 2007 +0000
Remove extra tests left in from debugging.
Author: Jonathan Wakely
Date: Sun Dec 16 23:34:22 2007 +0000
(pstreambuf::fill_buffer): Option to work in non-blocking mode.
(pstreambuf::showmanyc): New implementation using non-blocking fill_buffer.
Author: Jonathan Wakely
Date: Sun Dec 16 23:32:29 2007 +0000
Test streambuf::in_avail() and wide chars.
Author: Jonathan Wakely
Date: Sun Jul 15 16:39:08 2007 +0000
Fix missing qualification in example code (bug 1754057 - thanks to Brian Ray)
Author: Jonathan Wakely
Date: Sat Aug 19 16:37:21 2006 +0000
Replace memcpy/memmove with equivalent char_traits operations.
Author: Jonathan Wakely
Date: Sat Jul 22 01:24:18 2006 +0000
Update copyright year.
Author: Jonathan Wakely
Date: Sat Jul 22 01:20:43 2006 +0000
(pstreambuf::empty_buffer()): Adjust buffer on incomplete writes.
(pstreambuf::write(),pstreambuf::read()): Adjust return value for wide chars.
Author: Jonathan Wakely
Date: Fri Jul 21 20:30:12 2006 +0000
(pstreambuf::fill_buffer()): Only call rbuffer() once.
Author: Jonathan Wakely
Date: Fri Jul 21 16:07:24 2006 +0000
Remove misleading comments, there are worse problems when sizeof(char_type)!=1
Author: Jonathan Wakely
Date: Fri Jul 21 15:21:53 2006 +0000
(pstreambuf::showmanyc()): Fix overflow error.
Author: Jonathan Wakely
Date: Fri Jul 21 15:00:19 2006 +0000
Typo in comment.
Author: Jonathan Wakely
Date: Wed Jun 21 20:56:27 2006 +0000
Support Sun CC compiler (bug 1509533)
Author: Jonathan Wakely
Date: Sat Jan 21 21:02:06 2006 +0000
(pstreambuf::showmanyc()): Adjust result for sizeof(char_type) > 1
Author: Jonathan Wakely
Date: Mon Jan 9 23:58:30 2006 +0000
Repeat file descriptor tests for vector open. Remove assumptions about what
fd values will be found because pstreambuf can use extra pipes.
Author: Jonathan Wakely
Date: Sun Jan 8 23:08:19 2006 +0000
Update copyright date and fix signed/unsigned comparison warning.
Author: Jonathan Wakely
Date: Tue Sep 20 19:48:25 2005 +0000
Fix incorrect test numbering, caused by unintended template specialisations
being created. Fix by keeping the counters with the labels. (Bug 650883)
Author: Jonathan Wakely
Date: Mon Sep 19 21:06:49 2005 +0000
Signal handler must have C linkage.
Author: Jonathan Wakely
Date: Mon Sep 5 01:15:27 2005 +0000
Use typedef name.
Author: Jonathan Wakely
Date: Mon Sep 5 00:39:27 2005 +0000
Use execl("/bin/sh", "sh", "-c", cmd) instead of execlp("sh", "sh", "-c", cmd)
to avoid executing some other "sh" found in PATH (potential security hole).
Version to 0.5.3 as this could change the behaviour of some programs.
Author: Jonathan Wakely
Date: Mon Sep 5 00:28:17 2005 +0000
Update fd leak test to match new behaviour (read pipes not closed right away).
Test signal safety and exception-safety.
Author: Jonathan Wakely
Date: Mon Sep 5 00:18:21 2005 +0000
Delay cleaning up read buffers and pipes so that it is still possible to
read from the pipes after the child has exited. Previously, sending peof
could reap a zombie child and discard any unread data in the read buffers
and/or pipes. Now the child is still reaped but the unread data is still
available (bug 1163001). Clarify docs for wait() and exited().
Author: Jonathan Wakely
Date: Sun Sep 4 15:31:41 2005 +0000
(pstreambuf::showmanyc): Fix broken logic (bug 1232564).
Author: Jonathan Wakely
Date: Tue Jul 19 08:24:29 2005 +0000
Apply improvements suggested by Antonio S. de A. Terceiro.
Author: Jonathan Wakely