//
// Automated Testing Framework (atf)
//
// Copyright (c) 2007 The NetBSD Foundation, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
// CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
// IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//

#if !defined(TOOLS_IO_HPP)
#define TOOLS_IO_HPP

#include <istream>
#include <ostream>
#include <streambuf>

#include "auto_array.hpp"
#include "fs.hpp"

namespace tools {
namespace io {

// ------------------------------------------------------------------------
// The "file_handle" class.
// ------------------------------------------------------------------------

//!
//! \brief Simple RAII model for system file handles.
//!
//! The \a file_handle class is a simple RAII model for native system file
//! handles.  This class wraps one of such handles grabbing its ownership,
//! and automaticaly closes it upon destruction.  It is basically used
//! inside the library to avoid leaking open file handles, shall an
//! unexpected execution trace occur.
//!
//! A \a file_handle object can be copied but doing so invalidates the
//! source object.  There can only be a single valid \a file_handle object
//! for a given system file handle.  This is similar to std::unique_ptr\<\>'s
//! semantics.
//!
//! This class also provides some convenience methods to issue special file
//! operations under their respective platforms.
//!
class file_handle
{
public:
   //!
   //! \brief Opaque name for the native handle type.
   //!
   //! Each operating system identifies file handles using a specific type.
   //! The \a handle_type type is used to transparently refer to file
   //! handles regarless of the operating system in which this class is
   //! used.
   //!
   //! If this class is used in a POSIX system, \a NativeSystemHandle is
   //! an integer type while it is a \a HANDLE in a Win32 system.
   //!
   typedef int handle_type;

   //!
   //! \brief Constructs an invalid file handle.
   //!
   //! This constructor creates a new \a file_handle object that represents
   //! an invalid file handle.  An invalid file handle can be copied but
   //! cannot be manipulated in any way (except checking for its validity).
   //!
   //! \see is_valid()
   //!
   file_handle(void);

   //!
   //! \brief Constructs a new file handle from a native file handle.
   //!
   //! This constructor creates a new \a file_handle object that takes
   //! ownership of the given \a h native file handle.  The user must not
   //! close \a h on his own during the lifetime of the new object.
   //! Ownership can be reclaimed using disown().
   //!
   //! \pre The native file handle must be valid; a close operation must
   //!      succeed on it.
   //!
   //! \see disown()
   //!
   file_handle(handle_type h);

   //!
   //! \brief Copy constructor; invalidates the source handle.
   //!
   //! This copy constructor creates a new file handle from a given one.
   //! Ownership of the native file handle is transferred to the new
   //! object, effectively invalidating the source file handle.  This
   //! avoids having two live \a file_handle objects referring to the
   //! same native file handle.  The source file handle need not be
   //! valid in the name of simplicity.
   //!
   //! \post The source file handle is invalid.
   //! \post The new file handle owns the source's native file handle.
   //!
   file_handle(const file_handle& fh);

   //!
   //! \brief Releases resources if the handle is valid.
   //!
   //! If the file handle is valid, the destructor closes it.
   //!
   //! \see is_valid()
   //!
   ~file_handle(void);

   //!
   //! \brief Assignment operator; invalidates the source handle.
   //!
   //! This assignment operator transfers ownership of the RHS file
   //! handle to the LHS one, effectively invalidating the source file
   //! handle.  This avoids having two live \a file_handle objects
   //! referring to the same native file handle.  The source file
   //! handle need not be valid in the name of simplicity.
   //!
   //! \post The RHS file handle is invalid.
   //! \post The LHS file handle owns RHS' native file handle.
   //! \return A reference to the LHS file handle.
   //!
   file_handle& operator=(const file_handle& fh);

   //!
   //! \brief Checks whether the file handle is valid or not.
   //!
   //! Returns a boolean indicating whether the file handle is valid or
   //! not.  If the file handle is invalid, no other applications can be
   //! executed other than the destructor.
   //!
   //! \return True if the file handle is valid; false otherwise.
   //!
   bool is_valid(void) const;

   //!
   //! \brief Closes the file handle.
   //!
   //! Explicitly closes the file handle, which must be valid.  Upon
   //! exit, the handle is not valid any more.
   //!
   //! \pre The file handle is valid.
   //! \post The file handle is invalid.
   //! \post The native file handle is closed.
   //!
   void close(void);

   //!
   //! \brief Reclaims ownership of the native file handle.
   //!
   //! Explicitly reclaims ownership of the native file handle contained
   //! in the \a file_handle object, returning the native file handle.
   //! The caller is responsible of closing it later on.
   //!
   //! \pre The file handle is valid.
   //! \post The file handle is invalid.
   //! \return The native file handle.
   //!
   handle_type disown(void);

   //!
   //! \brief Gets the native file handle.
   //!
   //! Returns the native file handle for the \a file_handle object.
   //! The caller can issue any operation on it except closing it.
   //! If closing is required, disown() shall be used.
   //!
   //! \pre The file handle is valid.
   //! \return The native file handle.
   //!
   handle_type get(void) const;

   //!
   //! \brief Changes the native file handle to the given one.
   //!
   //! Given a new native file handle \a h, this operation assigns this
   //! handle to the current object, closing its old native file handle.
   //! In other words, it first calls dup2() to remap the old handle to
   //! the new one and then closes the old handle.
   //!
   //! If \a h matches the current value of the handle, this is a no-op.
   //! This is done for simplicity, to avoid the caller having to check
   //! this condition on its own.
   //!
   //! If \a h is open, it is automatically closed by dup2().
   //!
   //! This operation is only available in POSIX systems.
   //!
   //! \pre The file handle is valid.
   //! \pre The native file handle \a h is valid; i.e., it must be
   //!      closeable.
   //! \post The file handle's native file handle is \a h.
   //! \throw system_error If the internal remapping operation fails.
   //!
   void posix_remap(handle_type h);

private:
   //!
   //! \brief Internal handle value.
   //!
   //! This variable holds the native handle value for the file handle
   //! hold by this object.  It is interesting to note that this needs
   //! to be mutable because the copy constructor and the assignment
   //! operator invalidate the source object.
   //!
   mutable handle_type m_handle;

   //!
   //! \brief Constant function representing an invalid handle value.
   //!
   //! Returns the platform-specific handle value that represents an
   //! invalid handle.  This is a constant function rather than a regular
   //! constant because, in the latter case, we cannot define it under
   //! Win32 due to the value being of a complex type.
   //!
   static handle_type invalid_value(void);
};

// ------------------------------------------------------------------------
// The "systembuf" class.
// ------------------------------------------------------------------------

//!
//! \brief std::streambuf implementation for system file handles.
//!
//! systembuf provides a std::streambuf implementation for system file
//! handles.  Contrarywise to file_handle, this class does \b not take
//! ownership of the native file handle; this should be taken care of
//! somewhere else.
//!
//! This class follows the expected semantics of a std::streambuf object.
//! However, it is not copyable to avoid introducing inconsistences with
//! the on-disk file and the in-memory buffers.
//!
class systembuf : public std::streambuf
{
   // Non-copyable.
   systembuf(const systembuf&);
   systembuf& operator=(const systembuf&);

public:
   typedef int handle_type;

   //!
   //! \brief Constructs a new systembuf for the given file handle.
   //!
   //! This constructor creates a new systembuf object that reads or
   //! writes data from/to the \a h native file handle.  This handle
   //! is \b not owned by the created systembuf object; the code
   //! should take care of it externally.
   //!
   //! This class buffers input and output; the buffer size may be
   //! tuned through the \a bufsize parameter, which defaults to 8192
   //! bytes.
   //!
   //! \see pistream.
   //!
   explicit systembuf(handle_type h, std::size_t bufsize = 8192);
   ~systembuf(void);

private:
   //!
   //! \brief Native file handle used by the systembuf object.
   //!
   handle_type m_handle;

   //!
   //! \brief Internal buffer size used during read and write operations.
   //!
   std::size_t m_bufsize;

   //!
   //! \brief Internal buffer used during read operations.
   //!
   char* m_read_buf;

   //!
   //! \brief Internal buffer used during write operations.
   //!
   char* m_write_buf;

protected:
   //!
   //! \brief Reads new data from the native file handle.
   //!
   //! This operation is called by input methods when there are no more
   //! data in the input buffer.  The function fills the buffer with new
   //! data, if available.
   //!
   //! \pre All input positions are exhausted (gptr() >= egptr()).
   //! \post The input buffer has new data, if available.
   //! \returns traits_type::eof() if a read error occurrs or there are
   //!          no more data to be read.  Otherwise returns
   //!          traits_type::to_int_type(*gptr()).
   //!
   virtual int_type underflow(void);

   //!
   //! \brief Makes room in the write buffer for additional data.
   //!
   //! This operation is called by output methods when there is no more
   //! space in the output buffer to hold a new element.  The function
   //! first flushes the buffer's contents to disk and then clears it to
   //! leave room for more characters.  The given \a c character is
   //! stored at the beginning of the new space.
   //!
   //! \pre All output positions are exhausted (pptr() >= epptr()).
   //! \post The output buffer has more space if no errors occurred
   //!       during the write to disk.
   //! \post *(pptr() - 1) is \a c.
   //! \returns traits_type::eof() if a write error occurrs.  Otherwise
   //!          returns traits_type::not_eof(c).
   //!
   virtual int_type overflow(int c);

   //!
   //! \brief Flushes the output buffer to disk.
   //!
   //! Synchronizes the systembuf buffers with the contents of the file
   //! associated to this object through the native file handle.  The
   //! output buffer is flushed to disk and cleared to leave new room
   //! for more data.
   //!
   //! \returns 0 on success, -1 if an error occurred.
   //!
   virtual int sync(void);
};

// ------------------------------------------------------------------------
// The "pistream" class.
// ------------------------------------------------------------------------

//!
//! \brief Child process' output stream.
//!
//! The pistream class represents an output communication channel with the
//! child process.  The child process writes data to this stream and the
//! parent process can read it through the pistream object.  In other
//! words, from the child's point of view, the communication channel is an
//! output one, but from the parent's point of view it is an input one;
//! hence the confusing pistream name.
//!
//! pistream objects cannot be copied because they own the file handle
//! they use to communicate with the child and because they buffer data
//! that flows through the communication channel.
//!
//! A pistream object behaves as a std::istream stream in all senses.
//! The class is only provided because it must provide a method to let
//! the caller explicitly close the communication channel.
//!
//! \remark <b>Blocking remarks</b>: Functions that read data from this
//! stream can block if the associated file handle blocks during the read.
//! As this class is used to communicate with child processes through
//! anonymous pipes, the most typical blocking condition happens when the
//! child has no more data to send to the pipe's system buffer.  When
//! this happens, the buffer eventually empties and the system blocks
//! until the writer generates some data.
//!
class pistream : public std::istream
{
   // Non-copyable.
   pistream(const pistream&);
   pistream& operator=(const pistream&);

   //!
   //! \brief The systembuf object used to manage this stream's data.
   //!
   systembuf m_systembuf;

public:
   //!
   //! \brief Creates a new process' output stream.
   //!
   //! Given a file handle, this constructor creates a new pistream
   //! object that owns the given file handle \a fh.  Ownership of
   //! \a fh is transferred to the created pistream object.
   //!
   //! \pre \a fh is valid.
   //! \post \a fh is invalid.
   //! \post The new pistream object owns \a fh.
   //!
   explicit pistream(const int);
};

// ------------------------------------------------------------------------
// The "muxer" class.
// ------------------------------------------------------------------------

class muxer {
   // Non-copyable.
   muxer(const muxer&);
   muxer& operator=(const muxer&);

   const int* m_fds;
   const size_t m_nfds;

   const size_t m_bufsize;
   tools::auto_array< std::string > m_buffers;

protected:
   virtual void line_callback(const size_t, const std::string&) = 0;

   size_t read_one(const size_t, const int, std::string&, const bool);

public:
   muxer(const int*, const size_t, const size_t bufsize = 1024);
   virtual ~muxer(void);

   void mux(volatile const bool&);
   void flush(void);
};

} // namespace io
} // namespace tools

#endif // !defined(TOOLS_IO_HPP)