/** @file

    $Id$

    $Date$
    $Author$

    @copy &copy; Marc W&auml;ckerlin
    @license LGPL, see file <a href="license.html">COPYING</a>

    $Log$
    Revision 1.8  2005/11/29 12:39:43  marc
    make it compilable with gcc 4.0.2 and newer doxygen

    Revision 1.7  2004/12/18 20:58:19  marc
    pipes can be blocking, non blocking or blocking on one side only
    default is again blocking, because it causes less trouble

    Revision 1.6  2004/12/14 20:32:10  marc
    support for connect_cin
    pipe is now non blocking by default

    Revision 1.5  2004/08/28 16:21:25  marc
    mrw-c++-0.92 (mrw)
    - new file: version.cpp
    - new file header for all sources
    - work around warning in mrw::auto<T>
    - possibility to compile without log4cxx
    - work around bugs in demangle.h and libiberty.h
    - corrections in documentation
    - added simple tracing mechanism
    - more warnings
    - small corrections in Auto<>::Free and a new test for it
    - possibility to compile without stack trace

    Revision 1.4  2004/08/25 08:35:09  marc
    change in file header

    Revision 1.3  2004/08/25 08:22:19  marc
    added file header

*/
#ifndef __MRW_UNISTD_HPP__
#define __MRW_UNISTD_HPP__

#include <unistd.h> // pipe, close
#include <fcntl.h> // fcntl (O_NONBLOCK)
#include <errno.h> // errno

namespace mrw {
  /** @addtogroup AutoTools */
  //@{
  
  /** @brief class that implements an unnamed UNIX pipe
      @pre \#include <mrw/unistd.hpp>

      Implements a UNIX pipe that is automatically closed in
      destructor and offers some facilities. */
  class Pipe {
  private:
    /// the filedescriptor, [0] to read and [1] to write
    int _fd[2];
    int _lastError;
  public:
    /// blocking mode
    enum BlockingMode {
      non_blocking,             ///< both sides are non blocking
      block_input,              ///< pipe input is blocking
      block_output,             ///< pipe output is blocking
      blocking                  ///< both sides are blocking
    };
    /// creates a unix pipe
    /** @param mode Flag whether the pipe is blocking (default: yes) */
    Pipe(BlockingMode mode=blocking) throw(std::bad_exception):
      _lastError(-1) {
      _fd[0] = -1;
      _fd[1] = -1;
      if (::pipe(_fd)==-1) {
        _lastError=errno;
        return;
      }
      if (mode==non_blocking || mode==block_output) {
        int val(fcntl(_fd[0], F_GETFL, 0));
        if (fcntl(_fd[0], F_SETFL, (val!=-1?val:0)|O_NONBLOCK) == -1)
          _lastError=errno;
      }
      if (mode==non_blocking || mode==block_input) {
        int val(fcntl(_fd[1], F_GETFL, 0));
        if (fcntl(_fd[1], F_SETFL, (val!=-1?val:0)|O_NONBLOCK) == -1)
          _lastError=errno;
      }
    }
    /// destructor closes pipe if still open
    ~Pipe() throw(std::bad_exception) {
      close();
    }
    /// closes pipe if open
    void close() throw(std::bad_exception) {
      close_in();
      close_out();
    }
    /// closes input pipe if open
    void close_in() throw(std::bad_exception) {
      if (_fd[0]!=-1) while (::close(_fd[0])==-1) if (errno!=EINTR) {
        _lastError = errno;
        break;
      }
      _fd[0] = -1;
    }
    /// closes output pipe if open
    void close_out() throw(std::bad_exception) {
      if (_fd[1]!=-1) while (::close(_fd[1])==-1) if (errno!=EINTR) {
        _lastError = errno;
        break;
      }
      _fd[1] = -1;
    }
    /** @return true if no error occured */
    operator bool() throw() {
      return _lastError == -1;
    }
    /** @return last error code, -1 if no error */
    int error() throw() {
      return _lastError;
    }
    /// connect input stream to @c stdin
    void connect_cin() throw(std::bad_exception) {
      while (::dup2(_fd[0], 0)==-1) if (errno!=EINTR) {
        _lastError = errno;
        return;
      }
    }
    /// connect output stream to @c stdout
    void connect_cout() throw(std::bad_exception) {
      while (::dup2(_fd[1], 1)==-1) if (errno!=EINTR) {
        _lastError = errno;
        return;
      }
    }
    /// connect output stream to @c stderr
    void connect_cerr() throw(std::bad_exception) {
      while (::dup2(_fd[1], 2)==-1) if (errno!=EINTR) {
        _lastError = errno;
        return;
      }
    }
    /// get an input stream
    /** @return stream to read from
        @note invalid after destruction or @c close or @c close_in */
    int istream() throw(std::bad_exception) {
      return _fd[0];
    }
    /// get an output stream
    /** @return stream to write to
        @note invalid after destruction or @c close or @c close_out */
    int ostream() throw(std::bad_exception) {
      return _fd[1];
    }
  };

  //@}
  
}
#endif
