aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2016-07-12 17:24:00 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2016-07-23 19:42:48 +0300
commit6c8e3f09c185d7fa4664ccd9e5c4f623a17b84cc (patch)
tree513f523dba31f275994d8152c02db82f3380c56e
parent09bedede7116961fbfb298a6a6cfa933af7af682 (diff)
Extend fdstream
-rw-r--r--butl/fdstream310
-rw-r--r--butl/fdstream.cxx331
-rw-r--r--butl/fdstream.ixx196
-rw-r--r--butl/filesystem.cxx37
-rw-r--r--butl/pager4
-rw-r--r--butl/pager.cxx22
-rw-r--r--butl/process15
-rw-r--r--butl/process.cxx30
-rw-r--r--tests/cpfile/driver.cxx21
-rw-r--r--tests/fdstream/driver.cxx192
-rw-r--r--tests/link/driver.cxx12
-rw-r--r--tests/pager/driver.cxx5
-rw-r--r--tests/process/driver.cxx26
13 files changed, 1012 insertions, 189 deletions
diff --git a/butl/fdstream b/butl/fdstream
index 23cc58c..bb14f25 100644
--- a/butl/fdstream
+++ b/butl/fdstream
@@ -5,24 +5,37 @@
#ifndef BUTL_FDSTREAM
#define BUTL_FDSTREAM
+#include <string>
#include <istream>
#include <ostream>
#include <cstdint> // uint16_t
#include <butl/path>
-#include <butl/filesystem> // permissions
+#include <butl/filesystem> // permissions
namespace butl
{
- // An iostream that is initialized with a file descriptor rather than
- // a file name.
+ // An [io]fstream that can be initialized with a file descriptor in addition
+ // to a file name and that also by default enables exceptions on badbit and
+ // failbit. So instead of a dance like this:
+ //
+ // ifstream ifs;
+ // ifs.exceptions (ifstream::badbit | ifstream::failbit);
+ // ifs.open (path.string ());
+ //
+ // You can simply do:
+ //
+ // ifdstream ifs (path);
//
// Notes and limitations:
//
// - char only
// - input or output but not both
// - no support for put back
- // - throws std::system_error in case of a read()/write() error
+ // - throws ios::failure in case of open()/read()/write()/close() errors
+ // - exception mask has at least badbit
+ // - after catching an exception caused by badbit the stream is no longer
+ // used
// - not movable, though can be easily supported
//
class fdbuf: public std::basic_streambuf<char>
@@ -80,81 +93,262 @@ namespace butl
char buf_[4096];
};
- // File descriptor translation mode. It has the same semantics as the
- // binary/text opening modes in std::fstream. Specifically, this is a
- // noop for POSIX systems where the two modes are the same.
+ // File stream mode.
+ //
+ // The text/binary flags have the same semantics as those in std::fstream.
+ // Specifically, this is a noop for POSIX systems where the two modes are
+ // the same.
//
- enum class fdtranslate
+ // The skip flag instructs the stream to skip to the end before closing the
+ // file descriptor. This is primarily useful when working with pipes where
+ // you may want not to "offend" the other end by closing your end before
+ // reading all the data.
+ //
+ enum class fdstream_mode: std::uint16_t
{
- text,
- binary
+ text = 0x01,
+ binary = 0x02,
+ skip = 0x04
};
+ fdstream_mode operator& (fdstream_mode, fdstream_mode);
+ fdstream_mode operator| (fdstream_mode, fdstream_mode);
+ fdstream_mode operator&= (fdstream_mode&, fdstream_mode);
+ fdstream_mode operator|= (fdstream_mode&, fdstream_mode);
+
+ // Extended (compared to ios::openmode) file open flags.
+ //
+ enum class fdopen_mode: std::uint16_t
+ {
+ in = 0x01, // Open for reading.
+ out = 0x02, // Open for writing.
+ append = 0x04, // Seek to the end of file before each write.
+ truncate = 0x08, // Discard the file contents on open.
+ create = 0x10, // Create a file if not exists.
+ exclusive = 0x20, // Fail if the file exists and the create flag is set.
+ binary = 0x40, // Set binary translation mode.
+ at_end = 0x80, // Seek to the end of stream immediately after open.
+
+ none = 0 // Usefull when build the mode incrementally.
+ };
+
+ fdopen_mode operator& (fdopen_mode, fdopen_mode);
+ fdopen_mode operator| (fdopen_mode, fdopen_mode);
+ fdopen_mode operator&= (fdopen_mode&, fdopen_mode);
+ fdopen_mode operator|= (fdopen_mode&, fdopen_mode);
+
class fdstream_base
{
protected:
fdstream_base () = default;
fdstream_base (int fd): buf_ (fd) {}
- fdstream_base (int, fdtranslate);
+ fdstream_base (int, fdstream_mode);
protected:
fdbuf buf_;
};
- // Note that the destructor may throw.
+ // iofdstream constructors and open() functions that take openmode as an
+ // argument mimic the corresponding iofstream functions in terms of the
+ // openmode mask interpretation. They throw std::invalid_argument for an
+ // invalid combination of flags (as per the standard). Note that the in and
+ // out flags are always added implicitly for ifdstream and ofdstream,
+ // respectively.
+ //
+ // iofdstream constructors and open() functions that take fdopen_mode as an
+ // argument interpret the mask literally just ignoring some flags which are
+ // meaningless in the absense of others (read more on that in the comment
+ // for fdopen()). Note that the in and out flags are always added implicitly
+ // for ifdstream and ofdstream, respectively.
+ //
+ // iofdstream constructors and open() functions that take file path as a
+ // const std::string& or const char* may throw the invalid_path exception.
+ //
+ // Passing -1 as a file descriptor is valid and results in the creation of
+ // an unopened object.
+ //
+ // Also note that open() and close() functions can be successfully called
+ // for an opened and unopened objects respectively. That is in contrast with
+ // iofstream that sets failbit in such cases.
+ //
+ // @@ Need to make sure performance is on par with fstream on both
+ // Linux and Windows.
+ //
+ // @@ Do we need to increase default buffer size? Make it customizable?
+ // Wonder what it is in libstdc++ and MSVC?
+
+ // Note that ifdstream destructor will close an open file descriptor but
+ // will ignore any errors. To detect such errors, call close() explicitly.
//
class ifdstream: fdstream_base, public std::istream
{
public:
- ifdstream (): std::istream (&buf_) {}
- ifdstream (int fd): fdstream_base (fd), std::istream (&buf_) {}
- ifdstream (int fd, fdtranslate m)
- : fdstream_base (fd, m), std::istream (&buf_) {}
+ // Create an unopened object with iostate = badbit | failbit (we cannot
+ // have the iostate as an argument since it clashes with int fd) To create
+ // an unopened object with non-default exception mask one can do:
+ //
+ // ifdstream (-1, ...);
+ //
+ ifdstream ();
+
+ explicit
+ ifdstream (int fd, iostate e = badbit | failbit);
+ ifdstream (int fd, fdstream_mode m, iostate e = badbit | failbit);
+
+ explicit
+ ifdstream (const char*,
+ openmode = in,
+ iostate e = badbit | failbit);
+
+ explicit
+ ifdstream (const std::string&,
+ openmode = in,
+ iostate e = badbit | failbit);
+
+ explicit
+ ifdstream (const path&,
+ openmode = in,
+ iostate e = badbit | failbit);
+
+ ifdstream (const char*,
+ fdopen_mode,
+ iostate e = badbit | failbit);
+
+ ifdstream (const std::string&,
+ fdopen_mode,
+ iostate e = badbit | failbit);
+
+ ifdstream (const path&,
+ fdopen_mode,
+ iostate e = badbit | failbit);
+
+ ~ifdstream () override;
+
+ void
+ open (const char*, openmode = in);
+
+ void
+ open (const std::string&, openmode = in);
+
+ void
+ open (const path&, openmode = in);
+
+ void
+ open (const char*, fdopen_mode);
- void close () {buf_.close ();}
- void open (int fd) {buf_.open (fd);}
+ void
+ open (const std::string&, fdopen_mode);
+
+ void
+ open (const path&, fdopen_mode);
+
+ void
+ open (int fd) {buf_.open (fd); clear();}
+
+ void close ();
bool is_open () const {return buf_.is_open ();}
+
+ private:
+ bool skip_ = false;
};
- // Note that the destructor flushes the stream and may throw.
+ // Note that ofdstream requires that you explicitly call close() before
+ // destroying it. Or, more specifically, the ofdstream object should not be
+ // in the opened state by the time its destructor is called, unless it is in
+ // the "not good" state (good() == false) or the destructor is being called
+ // during the stack unwinding due to an exception being thrown
+ // (std::uncaught_exception() == true). This is enforced with assert() in
+ // the ofdstream destructor.
//
class ofdstream: fdstream_base, public std::ostream
{
public:
- ofdstream (): std::ostream (&buf_) {}
- ofdstream (int fd): fdstream_base (fd), std::ostream (&buf_) {}
- ofdstream (int fd, fdtranslate m)
- : fdstream_base (fd, m), std::ostream (&buf_) {}
+ // Create an unopened object with iostate = badbit | failbit (we cannot
+ // have the iostate as an argument since it clashes with int fd). To create
+ // an unopened object with non-default exception mask one can do:
+ //
+ // ofdstream (-1, ...);
+ //
+ ofdstream ();
- ~ofdstream () override {if (is_open () && good ()) buf_.sync ();}
+ explicit
+ ofdstream (int fd, iostate e = badbit | failbit);
+ ofdstream (int fd, fdstream_mode m, iostate e = badbit | failbit);
- void close () {flush (); buf_.close ();}
- void open (int fd) {buf_.open (fd);}
- bool is_open () const {return buf_.is_open ();}
- };
+ explicit
+ ofdstream (const char*,
+ openmode = out,
+ iostate e = badbit | failbit);
- // File open flags.
- //
- enum class fdopen_mode: std::uint16_t
- {
- in = 0x01, // Open for reading.
- out = 0x02, // Open for writing.
- append = 0x04, // Seek to the end of file before each write.
- truncate = 0x08, // Discard the file contents on open.
- create = 0x10, // Create a file if not exists.
- exclusive = 0x20, // Fail if the file exists and the create flag is set.
- binary = 0x40, // Set binary translation mode.
+ explicit
+ ofdstream (const std::string&,
+ openmode = out,
+ iostate e = badbit | failbit);
- none = 0 // Usefull when build the mode incrementally.
+ explicit
+ ofdstream (const path&,
+ openmode = out,
+ iostate e = badbit | failbit);
+
+ ofdstream (const char*,
+ fdopen_mode,
+ iostate e = badbit | failbit);
+
+ ofdstream (const std::string&,
+ fdopen_mode,
+ iostate e = badbit | failbit);
+
+ ofdstream (const path&,
+ fdopen_mode,
+ iostate e = badbit | failbit);
+
+ ~ofdstream () override;
+
+ void
+ open (const char*, openmode = out);
+
+ void
+ open (const std::string&, openmode = out);
+
+ void
+ open (const path&, openmode = out);
+
+ void
+ open (const char*, fdopen_mode);
+
+ void
+ open (const std::string&, fdopen_mode);
+
+ void
+ open (const path&, fdopen_mode);
+
+ void
+ open (int fd) {buf_.open (fd); clear ();}
+
+ void close () {if (is_open ()) flush (); buf_.close ();}
+ bool is_open () const {return buf_.is_open ();}
};
- fdopen_mode operator& (fdopen_mode, fdopen_mode);
- fdopen_mode operator| (fdopen_mode, fdopen_mode);
- fdopen_mode operator&= (fdopen_mode&, fdopen_mode);
- fdopen_mode operator|= (fdopen_mode&, fdopen_mode);
+ // The std::getline() replacement that provides a workaround for libstdc++'s
+ // ios::failure ABI fiasco (#66145) by throwing ios::failure, as it is
+ // defined at libbutl build time (new ABI on recent distributions) rather
+ // than libstdc++ build time (still old ABI on most distributions).
+ //
+ // Notes:
+ //
+ // - This relies of ADL so if the stream is used via the std::istream
+ // interface, then std::getline() will still be used. To put it another
+ // way, this is "the best we can do" until GCC folks get their act
+ // together.
+ //
+ // - The fail and eof bits may be left cleared in the stream exception mask
+ // when the function throws because of badbit.
+ //
+ ifdstream&
+ getline (ifdstream&, std::string&, char delim = '\n');
// Open a file returning the file descriptor on success and throwing
- // std::system_error otherwise.
+ // ios:failure otherwise.
//
// The mode argument should have at least one of the in or out flags set.
// The append and truncate flags are meaningless in the absense of the out
@@ -170,6 +364,20 @@ namespace butl
// Windows permissions other than ru and wu are unlikelly to have effect.
//
int
+ fdopen (const char*,
+ fdopen_mode,
+ permissions = permissions::ru | permissions::wu |
+ permissions::rg | permissions::wg |
+ permissions::ro | permissions::wo);
+
+ int
+ fdopen (const std::string&,
+ fdopen_mode,
+ permissions = permissions::ru | permissions::wu |
+ permissions::rg | permissions::wg |
+ permissions::ro | permissions::wo);
+
+ int
fdopen (const path&,
fdopen_mode,
permissions = permissions::ru | permissions::wu |
@@ -177,17 +385,17 @@ namespace butl
permissions::ro | permissions::wo);
// Set the translation mode for the file descriptor. Return the previous
- // mode on success, throw std::system_error otherwise.
+ // mode on success, throw ios::failure otherwise.
//
- fdtranslate
- fdmode (int, fdtranslate);
+ fdstream_mode
+ fdmode (int, fdstream_mode);
// Convenience functions for setting the translation mode for standard
// streams.
//
- fdtranslate stdin_fdmode (fdtranslate);
- fdtranslate stdout_fdmode (fdtranslate);
- fdtranslate stderr_fdmode (fdtranslate);
+ fdstream_mode stdin_fdmode (fdstream_mode);
+ fdstream_mode stdout_fdmode (fdstream_mode);
+ fdstream_mode stderr_fdmode (fdstream_mode);
// Low-level, nothrow file descriptor API.
//
diff --git a/butl/fdstream.cxx b/butl/fdstream.cxx
index e4d11ba..e2095ba 100644
--- a/butl/fdstream.cxx
+++ b/butl/fdstream.cxx
@@ -5,27 +5,69 @@
#include <butl/fdstream>
#ifndef _WIN32
-# include <fcntl.h> // open(), O_*
-# include <unistd.h> // close(), read(), write()
-# include <sys/stat.h> // S_I*
+# include <fcntl.h> // open(), O_*
+# include <unistd.h> // close(), read(), write(), lseek()
+# include <sys/stat.h> // S_I*
+# include <sys/types.h> // off_t
#else
-# include <io.h> // _close(), _read(), _write(), _setmode(), _sopen()
+# include <io.h> // _close(), _read(), _write(), _setmode(), _sopen(),
+ // _lseek()
# include <share.h> // _SH_DENYNO
# include <stdio.h> // _fileno(), stdin, stdout, stderr
# include <fcntl.h> // _O_*
# include <sys/stat.h> // S_I*
#endif
+#include <errno.h> // errno, E*
+
+#include <ios> // ios_base::openmode, ios_base::failure
+#include <limits> // numeric_limits
+#include <cassert>
+#include <exception> // uncaught_exception()
+#include <stdexcept> // invalid_argument
+#include <type_traits>
#include <system_error>
using namespace std;
namespace butl
{
+ // throw_ios_failure
+ //
+ template <bool = is_base_of<system_error, ios_base::failure>::value>
+ struct throw_ios
+ {
+ static void impl (error_code e, const char* m) {
+ throw ios_base::failure (m, e);}
+ };
+
+ template <>
+ struct throw_ios<false>
+ {
+ static void impl (error_code, const char* m) {throw ios_base::failure (m);}
+ };
+
+ inline void
+ throw_ios_failure (int ev)
+ {
+ error_code ec (ev, system_category ());
+ throw_ios<>::impl (ec, ec.message ().c_str ());
+ }
+
+ inline void
+ throw_ios_failure (int ev, const char* m)
+ {
+ throw_ios<>::impl (error_code (ev, system_category ()), m);
+ }
+
// fdbuf
//
fdbuf::
- ~fdbuf () {close ();}
+ ~fdbuf ()
+ {
+ if (is_open ())
+ fdclose (fd_); // Don't check for an error as not much we can do here.
+ }
void fdbuf::
open (int fd)
@@ -42,7 +84,7 @@ namespace butl
if (is_open ())
{
if (!fdclose (fd_))
- throw system_error (errno, system_category ());
+ throw_ios_failure (errno);
fd_ = -1;
}
@@ -78,7 +120,7 @@ namespace butl
#endif
if (n == -1)
- throw system_error (errno, system_category ());
+ throw_ios_failure (errno);
setg (buf_, buf_, buf_ + n);
return n != 0;
@@ -117,6 +159,10 @@ namespace butl
if (n != 0)
{
+ // Note that for MinGW GCC (5.2.0) _write() returns 0 for a file
+ // descriptor opened for read-only access (while -1 with errno EBADF is
+ // expected). This is in contrast with VC's _write() and POSIX's write().
+ //
#ifndef _WIN32
ssize_t m (write (fd_, buf_, n));
#else
@@ -124,7 +170,7 @@ namespace butl
#endif
if (m == -1)
- throw system_error (errno, system_category ());
+ throw_ios_failure (errno);
if (n != static_cast<size_t> (m))
return false;
@@ -138,19 +184,188 @@ namespace butl
// fdstream_base
//
fdstream_base::
- fdstream_base (int fd, fdtranslate m)
+ fdstream_base (int fd, fdstream_mode m)
: fdstream_base (fd) // Delegate.
{
// Note that here we rely on fdstream_base() (and fdbuf() which it calls)
- // to note read from the file.
+ // to not read from the file.
+ //
+ if (fd != -1 &&
+ ((m & fdstream_mode::text) == fdstream_mode::text ||
+ (m & fdstream_mode::binary) == fdstream_mode::binary))
+ fdmode (fd, m);
+ }
+
+ static fdopen_mode
+ translate_mode (ios_base::openmode m)
+ {
+ enum
+ {
+ in = ios_base::in,
+ out = ios_base::out,
+ app = ios_base::app,
+ bin = ios_base::binary,
+ trunc = ios_base::trunc,
+ ate = ios_base::ate
+ };
+
+ const fdopen_mode fd_in (fdopen_mode::in);
+ const fdopen_mode fd_out (fdopen_mode::out);
+ const fdopen_mode fd_inout (fdopen_mode::in | fdopen_mode::out);
+ const fdopen_mode fd_app (fdopen_mode::append);
+ const fdopen_mode fd_trunc (fdopen_mode::truncate);
+ const fdopen_mode fd_create (fdopen_mode::create);
+ const fdopen_mode fd_bin (fdopen_mode::binary);
+ const fdopen_mode fd_ate (fdopen_mode::at_end);
+
+ fdopen_mode r;
+ switch (m & ~(ate | bin))
+ {
+ case in : r = fd_in ; break;
+ case out :
+ case out | trunc : r = fd_out | fd_trunc | fd_create ; break;
+ case app :
+ case out | app : r = fd_out | fd_app | fd_create ; break;
+ case out | in : r = fd_inout ; break;
+ case out | in | trunc : r = fd_inout | fd_trunc | fd_create ; break;
+ case out | in | app :
+ case in | app : r = fd_inout | fd_app | fd_create ; break;
+
+ default: throw invalid_argument ("invalid open mode");
+ }
+
+ if (m & ate)
+ r |= fd_ate;
+
+ if (m & bin)
+ r |= fd_bin;
+
+ return r;
+ }
+
+ // ifdstream
+ //
+ ifdstream::
+ ifdstream (const char* f, openmode m, iostate e)
+ : ifdstream (f, translate_mode (m | in), e) // Delegate.
+ {
+ }
+
+ ifdstream::
+ ifdstream (const char* f, fdopen_mode m, iostate e)
+ : ifdstream (fdopen (f, m | fdopen_mode::in), e) // Delegate.
+ {
+ }
+
+ ifdstream::
+ ~ifdstream ()
+ {
+ if (skip_ && is_open () && good ())
+ {
+ // Clear the exception mask to prevent ignore() from throwing.
+ //
+ exceptions (goodbit);
+ ignore (numeric_limits<streamsize>::max ());
+ }
+
+ // Underlying file descriptor is closed by fdbuf dtor with errors (if any)
+ // being ignored.
+ //
+ }
+
+ void ifdstream::
+ open (const char* f, openmode m)
+ {
+ open (f, translate_mode (m | in));
+ }
+
+ void ifdstream::
+ open (const char* f, fdopen_mode m)
+ {
+ open (fdopen (f, m | fdopen_mode::in));
+ }
+
+ void ifdstream::
+ close ()
+ {
+ if (skip_ && is_open () && good ())
+ ignore (numeric_limits<streamsize>::max ());
+
+ buf_.close ();
+ }
+
+ ifdstream&
+ getline (ifdstream& is, string& s, char delim)
+ {
+ ifdstream::iostate eb (is.exceptions ());
+ assert (eb & ifdstream::badbit);
+
+ // Amend the exception mask to prevent exceptions being thrown by the C++
+ // IO runtime to avoid incompatibility issues due to ios_base::failure ABI
+ // fiasco (#66145). We will not restore the mask when ios_base::failure is
+ // thrown by fdbuf since there is no way to "silently" restore it if the
+ // corresponding bits are in the error state without the exceptions() call
+ // throwing ios_base::failure. Not restoring exception mask on throwing
+ // because of badbit should probably be ok since the stream is no longer
+ // usable.
//
- fdmode (fd, m);
+ if (eb != ifdstream::badbit)
+ is.exceptions (ifdstream::badbit);
+
+ std::getline (is, s, delim);
+
+ // Throw if any of the newly set bits are present in the exception mask.
+ //
+ if ((is.rdstate () & eb) != ifdstream::goodbit)
+ throw_ios_failure (EIO, "getline failure");
+
+ if (eb != ifdstream::badbit)
+ is.exceptions (eb); // Restore exception mask.
+
+ return is;
+ }
+
+ // ofdstream
+ //
+ ofdstream::
+ ofdstream (const char* f, openmode m, iostate e)
+ : ofdstream (f, translate_mode (m | out), e) // Delegate.
+ {
+ }
+
+ ofdstream::
+ ofdstream (const char* f, fdopen_mode m, iostate e)
+ : ofdstream (fdopen (f, m | fdopen_mode::out), e) // Delegate.
+ {
+ }
+
+ ofdstream::
+ ~ofdstream ()
+ {
+ // Enforce explicit close(). Note that we may have false negatives but not
+ // false positives. Specifically, we will fail to enforce if someone is
+ // using ofdstream in a dtor being called while unwinding the stack due to
+ // an exception.
+ //
+ assert (!is_open () || !good () || uncaught_exception ());
+ }
+
+ void ofdstream::
+ open (const char* f, openmode m)
+ {
+ open (f, translate_mode (m | out));
+ }
+
+ void ofdstream::
+ open (const char* f, fdopen_mode m)
+ {
+ open (fdopen (f, m | fdopen_mode::out));
}
// Utility functions
//
int
- fdopen (const path& f, fdopen_mode m, permissions p)
+ fdopen (const char* f, fdopen_mode m, permissions p)
{
mode_t pf (S_IREAD | S_IWRITE | S_IEXEC);
@@ -202,7 +417,7 @@ namespace butl
of |= O_LARGEFILE;
#endif
- int fd (open (f.string ().c_str (), of, pf));
+ int fd (open (f, of, pf));
#else
@@ -240,27 +455,46 @@ namespace butl
//
bool pass_perm (of & _O_CREAT);
- if (pass_perm && file_exists (f))
+ if (pass_perm && file_exists (path (f)))
{
// If the _O_CREAT flag is set then we need to clear it so that we can
// omit the permissions. But if the _O_EXCL flag is set as well we can't
// do that as fdopen() wouldn't fail as expected.
//
if (of & _O_EXCL)
- throw system_error (EEXIST, system_category ());
+ throw_ios_failure (EEXIST);
of &= ~_O_CREAT;
pass_perm = false;
}
int fd (pass_perm
- ? _sopen (f.string ().c_str (), of, _SH_DENYNO, pf)
- : _sopen (f.string ().c_str (), of, _SH_DENYNO));
+ ? _sopen (f, of, _SH_DENYNO, pf)
+ : _sopen (f, of, _SH_DENYNO));
#endif
if (fd == -1)
- throw system_error (errno, system_category ());
+ throw_ios_failure (errno);
+
+ if (mode (fdopen_mode::at_end))
+ {
+#ifndef _WIN32
+ bool r (lseek(fd, 0, SEEK_END) != static_cast<off_t>(-1));
+#else
+ bool r (_lseek(fd, 0, SEEK_END) != -1);
+#endif
+
+ // Note that in the case of an error we don't delete the newly created
+ // file as we have no indication if it is a new one.
+ //
+ if (!r)
+ {
+ int e (errno);
+ fdclose (fd); // Doesn't throw, but can change errno.
+ throw_ios_failure (e);
+ }
+ }
return fd;
}
@@ -279,28 +513,28 @@ namespace butl
return open ("/dev/null", O_RDWR);
}
- fdtranslate
- fdmode (int, fdtranslate)
+ fdstream_mode
+ fdmode (int, fdstream_mode)
{
- return fdtranslate::binary;
+ return fdstream_mode::binary;
}
- fdtranslate
- stdin_fdmode (fdtranslate)
+ fdstream_mode
+ stdin_fdmode (fdstream_mode)
{
- return fdtranslate::binary;
+ return fdstream_mode::binary;
}
- fdtranslate
- stdout_fdmode (fdtranslate)
+ fdstream_mode
+ stdout_fdmode (fdstream_mode)
{
- return fdtranslate::binary;
+ return fdstream_mode::binary;
}
- fdtranslate
- stderr_fdmode (fdtranslate)
+ fdstream_mode
+ stderr_fdmode (fdstream_mode)
{
- return fdtranslate::binary;
+ return fdstream_mode::binary;
}
#else
@@ -317,44 +551,51 @@ namespace butl
return _sopen ("nul", _O_RDWR, _SH_DENYNO);
}
- fdtranslate
- fdmode (int fd, fdtranslate m)
+ fdstream_mode
+ fdmode (int fd, fdstream_mode m)
{
- int r (_setmode (fd, m == fdtranslate::binary ? _O_BINARY : _O_TEXT));
+ m &= fdstream_mode::text | fdstream_mode::binary;
+
+ // Should be exactly one translation flag specified.
+ //
+ if (m != fdstream_mode::binary && m != fdstream_mode::text)
+ throw invalid_argument ("invalid translation mode");
+
+ int r (_setmode (fd, m == fdstream_mode::binary ? _O_BINARY : _O_TEXT));
if (r == -1)
- throw system_error (errno, system_category ());
+ throw_ios_failure (errno);
return (r & _O_BINARY) == _O_BINARY
- ? fdtranslate::binary
- : fdtranslate::text;
+ ? fdstream_mode::binary
+ : fdstream_mode::text;
}
- fdtranslate
- stdin_fdmode (fdtranslate m)
+ fdstream_mode
+ stdin_fdmode (fdstream_mode m)
{
int fd (_fileno (stdin));
if (fd == -1)
- throw system_error (errno, system_category ());
+ throw_ios_failure (errno);
return fdmode (fd, m);
}
- fdtranslate
- stdout_fdmode (fdtranslate m)
+ fdstream_mode
+ stdout_fdmode (fdstream_mode m)
{
int fd (_fileno (stdout));
if (fd == -1)
- throw system_error (errno, system_category ());
+ throw_ios_failure (errno);
return fdmode (fd, m);
}
- fdtranslate
- stderr_fdmode (fdtranslate m)
+ fdstream_mode
+ stderr_fdmode (fdstream_mode m)
{
int fd (_fileno (stderr));
if (fd == -1)
- throw system_error (errno, system_category ());
+ throw_ios_failure (errno);
return fdmode (fd, m);
}
diff --git a/butl/fdstream.ixx b/butl/fdstream.ixx
index a3ea83c..c7c329b 100644
--- a/butl/fdstream.ixx
+++ b/butl/fdstream.ixx
@@ -2,8 +2,204 @@
// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
// license : MIT; see accompanying LICENSE file
+#include <cassert>
+
namespace butl
{
+ // ifdstream
+ //
+ inline ifdstream::
+ ifdstream ()
+ : std::istream (&buf_)
+ {
+ exceptions (badbit | failbit);
+ }
+
+ inline ifdstream::
+ ifdstream (int fd, iostate e)
+ : fdstream_base (fd), std::istream (&buf_)
+ {
+ assert (e & badbit);
+ exceptions (e);
+ }
+
+ inline ifdstream::
+ ifdstream (int fd, fdstream_mode m, iostate e)
+ : fdstream_base (fd, m),
+ std::istream (&buf_),
+ skip_ ((m & fdstream_mode::skip) == fdstream_mode::skip)
+ {
+ assert (e & badbit);
+ exceptions (e);
+ }
+
+ inline ifdstream::
+ ifdstream (const std::string& f, openmode m, iostate e)
+ : ifdstream (f.c_str (), m, e) // Delegate.
+ {
+ }
+
+ inline ifdstream::
+ ifdstream (const path& f, openmode m, iostate e)
+ : ifdstream (f.string (), m, e) // Delegate.
+ {
+ }
+
+ inline ifdstream::
+ ifdstream (const std::string& f, fdopen_mode m, iostate e)
+ : ifdstream (f.c_str (), m, e) // Delegate.
+ {
+ }
+
+ inline ifdstream::
+ ifdstream (const path& f, fdopen_mode m, iostate e)
+ : ifdstream (f.string (), m, e) // Delegate.
+ {
+ }
+
+ inline void ifdstream::
+ open (const std::string& f, openmode m)
+ {
+ open (f.c_str (), m);
+ }
+
+ inline void ifdstream::
+ open (const path& f, openmode m)
+ {
+ open (f.string (), m);
+ }
+
+ inline void ifdstream::
+ open (const std::string& f, fdopen_mode m)
+ {
+ open (f.c_str (), m);
+ }
+
+ inline void ifdstream::
+ open (const path& f, fdopen_mode m)
+ {
+ open (f.string (), m);
+ }
+
+ // ofdstream
+ //
+ inline ofdstream::
+ ofdstream ()
+ : std::ostream (&buf_)
+ {
+ exceptions (badbit | failbit);
+ }
+
+ inline ofdstream::
+ ofdstream (int fd, iostate e)
+ : fdstream_base (fd), std::ostream (&buf_)
+ {
+ assert (e & badbit);
+ exceptions (e);
+ }
+
+ inline ofdstream::
+ ofdstream (int fd, fdstream_mode m, iostate e)
+ : fdstream_base (fd, m), std::ostream (&buf_)
+ {
+ assert (e & badbit);
+ exceptions (e);
+ }
+
+ inline ofdstream::
+ ofdstream (const std::string& f, openmode m, iostate e)
+ : ofdstream (f.c_str (), m, e) // Delegate.
+ {
+ }
+
+ inline ofdstream::
+ ofdstream (const path& f, openmode m, iostate e)
+ : ofdstream (f.string (), m, e) // Delegate.
+ {
+ }
+
+ inline ofdstream::
+ ofdstream (const std::string& f, fdopen_mode m, iostate e)
+ : ofdstream (f.c_str (), m, e) // Delegate.
+ {
+ }
+
+ inline ofdstream::
+ ofdstream (const path& f, fdopen_mode m, iostate e)
+ : ofdstream (f.string (), m, e) // Delegate.
+ {
+ }
+
+ inline void ofdstream::
+ open (const std::string& f, openmode m)
+ {
+ open (f.c_str (), m);
+ }
+
+ inline void ofdstream::
+ open (const path& f, openmode m)
+ {
+ open (f.string (), m);
+ }
+
+ inline void ofdstream::
+ open (const std::string& f, fdopen_mode m)
+ {
+ open (f.c_str (), m);
+ }
+
+ inline void ofdstream::
+ open (const path& f, fdopen_mode m)
+ {
+ open (f.string (), m);
+ }
+
+ // fdopen()
+ //
+ inline int
+ fdopen (const std::string& f, fdopen_mode m, permissions p)
+ {
+ return fdopen (f.c_str (), m, p);
+ }
+
+ inline int
+ fdopen (const path& f, fdopen_mode m, permissions p)
+ {
+ return fdopen (f.string (), m, p);
+ }
+
+ // fdstream_mode
+ //
+ inline fdstream_mode
+ operator& (fdstream_mode x, fdstream_mode y)
+ {
+ return x &= y;
+ }
+
+ inline fdstream_mode
+ operator| (fdstream_mode x, fdstream_mode y)
+ {
+ return x |= y;
+ }
+
+ inline fdstream_mode
+ operator&= (fdstream_mode& x, fdstream_mode y)
+ {
+ return x = static_cast<fdstream_mode> (
+ static_cast<std::uint16_t> (x) &
+ static_cast<std::uint16_t> (y));
+ }
+
+ inline fdstream_mode
+ operator|= (fdstream_mode& x, fdstream_mode y)
+ {
+ return x = static_cast<fdstream_mode> (
+ static_cast<std::uint16_t> (x) |
+ static_cast<std::uint16_t> (y));
+ }
+
+ // fdopen_mode
+ //
inline fdopen_mode operator& (fdopen_mode x, fdopen_mode y) {return x &= y;}
inline fdopen_mode operator| (fdopen_mode x, fdopen_mode y) {return x |= y;}
diff --git a/butl/filesystem.cxx b/butl/filesystem.cxx
index eab62dc..cabe306 100644
--- a/butl/filesystem.cxx
+++ b/butl/filesystem.cxx
@@ -227,13 +227,7 @@ namespace butl
cpfile (const path& from, const path& to, cpflags fl)
{
permissions perm (path_permissions (from));
-
- // We do not enable exceptions to be thrown when badbit/failbit are set for
- // the ifs stream nor check the bits manually down the road as there is no
- // input functions being called for the stream. Input functions are called
- // directly for the input stream buffer, which is our fdbuf that throws.
- //
- ifdstream ifs (fdopen (from, fdopen_mode::in | fdopen_mode::binary));
+ ifdstream ifs (from, fdopen_mode::binary);
fdopen_mode om (fdopen_mode::out |
fdopen_mode::truncate |
@@ -249,34 +243,15 @@ namespace butl
auto_rmfile rm;
ofdstream ofs (fdopen (to, om, perm));
- rm = auto_rmfile (to);
- // Setting badbit for the ofs stream is the only way to make sure the
- // original std::system_error, that is thrown on the output operation
- // failure, is retrown by the output stream's operator<<() and flush()
- // calls (the latter is called from close()). Note that both of the
- // functions behave as UnformattedOutputFunction.
- //
- ofs.exceptions (ofdstream::badbit);
+ rm = auto_rmfile (to);
- // If the output operation ends up with the badbit set for a reason other
- // than std::system_error being thrown (by fdbuf), then ofdstream::failure
- // will be thrown instead. We need to convert it to std::system_error to
- // comply with the cpfile() interface specification.
+ // Throws ios::failure on fdbuf read/write failures.
//
- try
- {
- // Throws std::system_error on fdbuf read/write failures.
- //
- ofs << ifs.rdbuf ();
+ ofs << ifs.rdbuf ();
- ifs.close ();
- ofs.close (); // Throws std::system_error on flush failure.
- }
- catch (const ofdstream::failure& e)
- {
- throw system_error (EIO, system_category (), e.what ());
- }
+ ifs.close (); // Throws ios::failure on failure.
+ ofs.close (); // Throws ios::failure on flush/close failure.
if ((fl & cpflags::overwrite_permissions) ==
cpflags::overwrite_permissions)
diff --git a/butl/pager b/butl/pager
index 4b39c2e..0414e0a 100644
--- a/butl/pager
+++ b/butl/pager
@@ -45,7 +45,7 @@ namespace butl
class pager: protected std::streambuf
{
public:
- ~pager () {wait ();}
+ ~pager () {wait (true);}
// If verbose is true, then print (to STDERR) the pager command line.
//
@@ -58,7 +58,7 @@ namespace butl
stream () {return os_.is_open () ? os_ : std::cout;}
bool
- wait ();
+ wait (bool ignore_errors = false);
// The streambuf output interface that implements indentation. You can
// override it to implement custom output pre-processing.
diff --git a/butl/pager.cxx b/butl/pager.cxx
index 0f29bb4..3d429f5 100644
--- a/butl/pager.cxx
+++ b/butl/pager.cxx
@@ -5,20 +5,20 @@
#include <butl/pager>
#ifndef _WIN32
-# include <unistd.h> // close(), STDOUT_FILENO
+# include <unistd.h> // STDOUT_FILENO
# include <sys/ioctl.h> // ioctl()
# include <chrono>
# include <thread> // this_thread::sleep_for()
#else
# include <butl/win32-utility>
-
-# include <io.h> // _close()
#endif
#include <cstring> // strchr()
#include <system_error>
+#include <butl/fdstream> // fdclose()
+
using namespace std;
namespace butl
@@ -130,11 +130,8 @@ namespace butl
bool r;
if (p_.try_wait (r))
{
-#ifndef _WIN32
- close (p_.out_fd);
-#else
- _close (p_.out_fd);
-#endif
+ fdclose (p_.out_fd);
+
if (pager != nullptr)
throw system_error (ECHILD, system_category ());
}
@@ -162,7 +159,7 @@ namespace butl
}
bool pager::
- wait ()
+ wait (bool ie)
{
// Teardown the indentation machinery.
//
@@ -172,8 +169,13 @@ namespace butl
buf_ = nullptr;
}
+ // Prevent ofdstream::close() from throwing in the ignore errors mode.
+ //
+ if (ie)
+ os_.exceptions (ofdstream::goodbit);
+
os_.close ();
- return p_.wait ();
+ return p_.wait (ie);
}
pager::int_type pager::
diff --git a/butl/process b/butl/process
index 54a8dae..e864b53 100644
--- a/butl/process
+++ b/butl/process
@@ -88,13 +88,13 @@ namespace butl
process (const char* cwd, char const* const[], process&, int = 1, int = 2);
// Wait for the process to terminate. Return true if the process
- // terminated normally and with the zero exit status. Throw
- // process_error if anything goes wrong. This function can be
- // called multiple times with subsequent calls simply returning
- // the status.
+ // terminated normally and with the zero exit status. Unless ignore_error
+ // is true, throw process_error if anything goes wrong. This function can
+ // be called multiple times with subsequent calls simply returning the
+ // status.
//
bool
- wait ();
+ wait (bool ignore_errors = false);
// Return true if the process has already terminated in which case
// the argument is set to the result of wait().
@@ -102,7 +102,10 @@ namespace butl
bool
try_wait (bool&);
- ~process () {if (handle != 0) wait ();}
+ // Note that the destructor will wait for the process but will ignore
+ // any errors and the exit status.
+ //
+ ~process () {if (handle != 0) wait (true);}
// Moveable-only type.
//
diff --git a/butl/process.cxx b/butl/process.cxx
index e67e12e..9bb0ea2 100644
--- a/butl/process.cxx
+++ b/butl/process.cxx
@@ -196,7 +196,7 @@ namespace butl
}
bool process::
- wait ()
+ wait (bool ie)
{
if (handle != 0)
{
@@ -204,7 +204,18 @@ namespace butl
handle = 0; // We have tried.
if (r == -1)
- throw process_error (errno, false);
+ {
+ if (!ie)
+ throw process_error (errno, false);
+ else
+ // Fold into status, so this and subsequent wait() calls return
+ // false. There is no portable way to update the status bits
+ // representing a process exit code specifically. So we set all bits
+ // to 1 and recon on getting non-zero exit status wherever the exact
+ // bits are.
+ //
+ status = ~0;
+ }
}
return WIFEXITED (status) && WEXITSTATUS (status) == 0;
@@ -571,7 +582,7 @@ namespace butl
}
bool process::
- wait ()
+ wait (bool ie)
{
if (handle != 0)
{
@@ -584,10 +595,15 @@ namespace butl
auto_handle h (handle); // Auto-deleter.
handle = 0; // We have tried.
- if (e != NO_ERROR)
- throw process_error (error_msg (e));
-
- status = s;
+ if (e == NO_ERROR)
+ status = s;
+ else
+ {
+ if (!ie)
+ throw process_error (error_msg (e));
+ else
+ status = 1; // Fold into status.
+ }
}
return status == 0;
diff --git a/tests/cpfile/driver.cxx b/tests/cpfile/driver.cxx
index 669ac26..23b6fc3 100644
--- a/tests/cpfile/driver.cxx
+++ b/tests/cpfile/driver.cxx
@@ -2,12 +2,13 @@
// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
// license : MIT; see accompanying LICENSE file
+#include <ios>
#include <string>
#include <cassert>
-#include <fstream>
#include <system_error>
#include <butl/path>
+#include <butl/fdstream>
#include <butl/filesystem>
using namespace std;
@@ -20,22 +21,20 @@ static const char text3[] = "XAB\r\n9";
static string
from_file (const path& f)
{
- ifstream ifs;
- ifs.exceptions (fstream::badbit | fstream::failbit);
- ifs.open (f.string (), ios::binary);
+ ifdstream ifs (f, ios::binary);
string s;
getline (ifs, s, '\0');
+ ifs.close (); // Not to miss failed close of the underlying file descriptor.
return s;
}
static void
to_file (const path& f, const char* s)
{
- ofstream ofs;
- ofs.exceptions (fstream::badbit | fstream::failbit);
- ofs.open (f.string (), ios::binary);
+ ofdstream ofs (f, ios::binary);
ofs << s;
+ ofs.close ();
}
int
@@ -89,7 +88,7 @@ main ()
cpfile (from, to, cpflags::none);
assert (false);
}
- catch (const system_error&)
+ catch (const ios::failure&)
{
}
@@ -162,11 +161,13 @@ main ()
cpfile (from, tslink, cpflags::none);
assert (false);
}
- catch (const system_error&)
+ catch (const ios::failure&)
{
}
- // Check that copy fail if 'from' symlink points to non-existent file.
+ // Check that copy fail if 'from' symlink points to non-existent file. The
+ // std::system_error is thrown as cpfile() fails to obtain permissions for
+ // the 'from' symlink target.
//
try
{
diff --git a/tests/fdstream/driver.cxx b/tests/fdstream/driver.cxx
index 3972473..0c6c480 100644
--- a/tests/fdstream/driver.cxx
+++ b/tests/fdstream/driver.cxx
@@ -2,9 +2,11 @@
// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
// license : MIT; see accompanying LICENSE file
+#include <ios>
#include <string>
#include <cassert>
-#include <system_error>
+#include <sstream>
+#include <exception>
#include <butl/path>
#include <butl/fdstream>
@@ -20,20 +22,20 @@ static const string text3 ("ABCDEF\r\nXYZ");
static string
from_file (const path& f, fdopen_mode m = fdopen_mode::none)
{
- ifdstream ifs (fdopen (f, m | fdopen_mode::in));
- ifs.exceptions (ifdstream::badbit);
+ ifdstream ifs (f, m, ifdstream::badbit);
string s;
getline (ifs, s, '\0');
+ ifs.close (); // Not to miss failed close of the underlying file descriptor.
return s;
}
static void
to_file (const path& f, const string& s, fdopen_mode m = fdopen_mode::none)
{
- ofdstream ofs (fdopen (f, m | fdopen_mode::out));
- ofs.exceptions (ofdstream::badbit | ofdstream::failbit);
+ ofdstream ofs (f, m);
ofs << s;
+ ofs.close ();
}
int
@@ -55,7 +57,7 @@ main ()
fdopen (f, fdopen_mode::out); // fdopen_mode::create is missed.
assert (false);
}
- catch (const system_error&)
+ catch (const ios::failure&)
{
}
@@ -64,13 +66,45 @@ main ()
assert (from_file (f, fdopen_mode::create) == "");
assert (try_rmfile (f) == rmfile_status::success);
+ // Read from the newly created non-empty file.
+ //
to_file (f, text1, fdopen_mode::create);
assert (from_file (f) == text1);
+ // Check that skip on close as requested.
+ //
+ {
+ ifdstream ifs (fdopen (f, fdopen_mode::in), fdstream_mode::skip);
+
+ string s;
+ getline (ifs, s);
+ assert (!ifs.eof ());
+
+ ifs.close ();
+ assert (ifs.eof ());
+ }
+
+ // Check that don't skip on close by default.
+ //
+ {
+ ifdstream ifs (fdopen (f, fdopen_mode::in));
+
+ string s;
+ getline (ifs, s);
+ assert (!ifs.eof ());
+
+ ifs.close ();
+ assert (!ifs.eof ());
+ }
+
// Read from the file opened in R/W mode.
//
assert (from_file (f, fdopen_mode::out) == text1);
+ // Read starting from the file's end.
+ //
+ assert (from_file (f, fdopen_mode::at_end) == "");
+
try
{
// Fail to create if the file already exists.
@@ -80,7 +114,7 @@ main ()
assert (false);
}
- catch (const system_error&)
+ catch (const ios::failure&)
{
}
@@ -101,6 +135,148 @@ main ()
to_file (f, text2, fdopen_mode::append);
assert (from_file (f) == text1 + text2);
+ // Append to the file with the yet another way.
+ //
+ to_file (f, text1, fdopen_mode::truncate);
+ to_file (f, text2, fdopen_mode::at_end);
+ assert (from_file (f) == text1 + text2);
+
+ // Check creating unopened ifdstream with a non-default exception mask.
+ //
+ to_file (f, "", fdopen_mode::truncate);
+
+ {
+ ifdstream ifs (-1, ifdstream::badbit);
+ ifs.open (f);
+
+ string s;
+ assert (!getline (ifs, s));
+ }
+
+ {
+ ifdstream ifs (-1, fdstream_mode::text, ifdstream::badbit);
+ ifs.open (f);
+
+ string s;
+ assert (!getline (ifs, s));
+ }
+
+ // Check creating unopened ofdstream with a non-default exception mask.
+ //
+ {
+ ofdstream ofs (-1, ifdstream::badbit);
+ ofs.open (f);
+
+ istringstream is;
+ ofs << is.rdbuf (); // Sets failbit if no characters is inserted.
+ ofs.close ();
+ }
+
+ {
+ ofdstream ofs (-1, fdstream_mode::binary, ifdstream::badbit);
+ ofs.open (f);
+
+ istringstream is;
+ ofs << is.rdbuf (); // Sets failbit if no characters is inserted.
+ ofs.close ();
+ }
+
+ // Fail to write to a read-only file.
+ //
+ // Don't work well for MinGW GCC (5.2.0) that throws ios::failure, which in
+ // combination with libstdc++'s ios::failure ABI fiasco (#66145) make it
+ // impossible to properly catch in this situation.
+ //
+ try
+ {
+ {
+ ofdstream ofs (fdopen (f, fdopen_mode::in));
+ ofs << text1;
+ ofs.flush ();
+ }
+
+ assert (false);
+ }
+#if !defined(_WIN32) || !defined(__GLIBCXX__)
+ catch (const ios::failure&)
+ {
+ }
+#else
+ catch (const std::exception&)
+ {
+ }
+#endif
+
+ try
+ {
+ ofdstream ofs;
+ ofs.open (fdopen (f, fdopen_mode::in));
+ ofs << text1;
+ ofs.close ();
+
+ assert (false);
+ }
+#if !defined(_WIN32) || !defined(__GLIBCXX__)
+ catch (const ios::failure&)
+ {
+ }
+#else
+ catch (const std::exception&)
+ {
+ }
+#endif
+
+ // Fail to read from a write-only file.
+ //
+ try
+ {
+ ifdstream ifs (fdopen (f, fdopen_mode::out));
+ ifs.peek ();
+
+ assert (false);
+ }
+ catch (const ios::failure&)
+ {
+ }
+
+ try
+ {
+ ifdstream ifs;
+ ifs.open (fdopen (f, fdopen_mode::out));
+ ifs.peek ();
+
+ assert (false);
+ }
+ catch (const ios::failure&)
+ {
+ }
+
+ // Dtor of a not opened ofdstream doesn't terminate a program.
+ //
+ {
+ ofdstream ofs;
+ }
+
+ // Dtor of an opened ofdstream doesn't terminate a program during the stack
+ // unwinding.
+ //
+ try
+ {
+ ofdstream ofs (f);
+ throw true;
+ }
+ catch (bool)
+ {
+ }
+
+ // Dtor of an opened but being in a bad state ofdstream doesn't terminate a
+ // program.
+ //
+ {
+ ofdstream ofs (f, fdopen_mode::out, ofdstream::badbit);
+ ofs.clear (ofdstream::failbit);
+ }
+
#ifndef _WIN32
// Fail for an existing symlink to unexistent file.
@@ -115,7 +291,7 @@ main ()
assert (false);
}
- catch (const system_error&)
+ catch (const ios::failure&)
{
}
diff --git a/tests/link/driver.cxx b/tests/link/driver.cxx
index 7ec4ac2..a32a8dc 100644
--- a/tests/link/driver.cxx
+++ b/tests/link/driver.cxx
@@ -4,11 +4,11 @@
#include <set>
#include <cassert>
-#include <fstream>
#include <utility> // pair
#include <system_error>
#include <butl/path>
+#include <butl/fdstream>
#include <butl/filesystem>
using namespace std;
@@ -35,10 +35,9 @@ link_file (const path& target, const path& link, bool hard, bool check_content)
return true;
string s;
- ifstream ifs;
- ifs.exceptions (fstream::badbit | fstream::failbit);
- ifs.open (link.string ());
+ ifdstream ifs (link);
ifs >> s;
+ ifs.close (); // Not to miss failed close of the underlying file descriptor.
return s == text;
}
@@ -93,10 +92,9 @@ main ()
path fp (td / fn);
{
- ofstream ofs;
- ofs.exceptions (fstream::badbit | fstream::failbit);
- ofs.open (fp.string ());
+ ofdstream ofs (fp);
ofs << text;
+ ofs.close ();
}
// Create the file hard link.
diff --git a/tests/pager/driver.cxx b/tests/pager/driver.cxx
index cab3078..e921533 100644
--- a/tests/pager/driver.cxx
+++ b/tests/pager/driver.cxx
@@ -2,6 +2,7 @@
// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
// license : MIT; see accompanying LICENSE file
+#include <ios> // ios_base::failure
#include <vector>
#include <string>
#include <utility> // move()
@@ -104,6 +105,10 @@ public:
assert (p.wait ());
}
+ catch (const ios_base::failure&)
+ {
+ assert (false);
+ }
catch (const system_error&)
{
assert (false);
diff --git a/tests/process/driver.cxx b/tests/process/driver.cxx
index 741340c..5c43fb5 100644
--- a/tests/process/driver.cxx
+++ b/tests/process/driver.cxx
@@ -68,13 +68,12 @@ exec (const path& p,
auto bin_mode = [bin](int fd) -> int
{
if (bin)
- fdmode (fd, fdtranslate::binary);
+ fdmode (fd, fdstream_mode::binary);
return fd;
};
ofdstream os (bin_mode (pr.out_fd));
- os.exceptions (ofdstream::badbit);
copy (in.begin (), in.end (), ostream_iterator<char> (os));
os.close ();
@@ -166,7 +165,6 @@ exec (const path& p,
int
main (int argc, const char* argv[])
-try
{
bool child (false);
bool bin (false);
@@ -201,8 +199,7 @@ try
if (i != argc)
{
if (!child)
- cerr << "usage: " << argv[0] << " [-c] [-i] [-o] [-e] [-b] [<dir>]"
- << endl;
+ cerr << "usage: " << argv[0] << " [-c] [-b] [<dir>]" << endl;
return 1;
}
@@ -235,9 +232,9 @@ try
{
if (bin)
{
- stdin_fdmode (fdtranslate::binary);
- stdout_fdmode (fdtranslate::binary);
- stderr_fdmode (fdtranslate::binary);
+ stdin_fdmode (fdstream_mode::binary);
+ stdout_fdmode (fdstream_mode::binary);
+ stderr_fdmode (fdstream_mode::binary);
}
vector<char> data
@@ -317,8 +314,13 @@ try
//
assert (exec (
fp.leaf (), vector<char> (), false, false, false, true, fp.directory ()));
-}
-catch (const system_error&)
-{
- assert (false);
+
+#ifndef _WIN32
+ // Check that wait() works properly if the underlying low-level wait
+ // operation fails.
+ //
+ process pr;
+ pr.handle = reinterpret_cast<process::handle_type>(-1);
+ assert (!pr.wait (true) && !pr.wait (false));
+#endif
}