aboutsummaryrefslogtreecommitdiff
path: root/butl
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2017-05-01 16:08:43 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2017-05-01 16:59:24 +0300
commit61377c582e0f2675baa5f5e6e30a35d1a4164b33 (patch)
tree11cdca992834d7f7f197f72856712fbcb3020e3d /butl
parent442c1a6790e52baa0c081f310d4d9e9b6f1ff638 (diff)
Add hxx extension for headers and lib prefix for library dir
Diffstat (limited to 'butl')
-rw-r--r--butl/.gitignore1
-rw-r--r--butl/base6450
-rw-r--r--butl/base64.cxx211
-rw-r--r--butl/buildfile93
-rw-r--r--butl/char-scanner90
-rw-r--r--butl/char-scanner.cxx91
-rw-r--r--butl/const-ptr80
-rw-r--r--butl/curl173
-rw-r--r--butl/curl.cxx166
-rw-r--r--butl/curl.ixx29
-rw-r--r--butl/curl.txx99
-rw-r--r--butl/diagnostics226
-rw-r--r--butl/diagnostics.cxx80
-rw-r--r--butl/export41
-rw-r--r--butl/fdstream643
-rw-r--r--butl/fdstream.cxx1085
-rw-r--r--butl/fdstream.ixx266
-rw-r--r--butl/filesystem567
-rw-r--r--butl/filesystem.cxx1396
-rw-r--r--butl/filesystem.ixx144
-rw-r--r--butl/ft/exception39
-rw-r--r--butl/ft/lang29
-rw-r--r--butl/ft/shared_mutex58
-rw-r--r--butl/manifest-forward15
-rw-r--r--butl/manifest-parser94
-rw-r--r--butl/manifest-parser.cxx387
-rw-r--r--butl/manifest-serializer75
-rw-r--r--butl/manifest-serializer.cxx238
-rw-r--r--butl/multi-index59
-rw-r--r--butl/optional96
-rw-r--r--butl/pager88
-rw-r--r--butl/pager.cxx207
-rw-r--r--butl/path1136
-rw-r--r--butl/path-io29
-rw-r--r--butl/path-map120
-rw-r--r--butl/path.cxx395
-rw-r--r--butl/path.ixx508
-rw-r--r--butl/path.txx377
-rw-r--r--butl/prefix-map138
-rw-r--r--butl/prefix-map.txx39
-rw-r--r--butl/process569
-rw-r--r--butl/process-details49
-rw-r--r--butl/process-io28
-rw-r--r--butl/process-run.cxx32
-rw-r--r--butl/process-run.txx226
-rw-r--r--butl/process.cxx1440
-rw-r--r--butl/process.ixx207
-rw-r--r--butl/sendmail91
-rw-r--r--butl/sendmail.cxx40
-rw-r--r--butl/sendmail.ixx68
-rw-r--r--butl/sha256101
-rw-r--r--butl/sha256.cxx142
-rw-r--r--butl/sha256c.c393
-rw-r--r--butl/sha256c.c.orig316
-rw-r--r--butl/small-vector283
-rw-r--r--butl/standard-version257
-rw-r--r--butl/standard-version.cxx632
-rw-r--r--butl/standard-version.ixx100
-rw-r--r--butl/string-parser56
-rw-r--r--butl/string-parser.cxx132
-rw-r--r--butl/string-table98
-rw-r--r--butl/string-table.txx33
-rw-r--r--butl/strptime.c629
-rw-r--r--butl/strptime.c.orig689
-rw-r--r--butl/tab-parser72
-rw-r--r--butl/tab-parser.cxx88
-rw-r--r--butl/target-triplet155
-rw-r--r--butl/target-triplet.cxx145
-rw-r--r--butl/timelocal.c157
-rw-r--r--butl/timelocal.c.orig153
-rw-r--r--butl/timelocal.h65
-rw-r--r--butl/timelocal.h.orig61
-rw-r--r--butl/timestamp146
-rw-r--r--butl/timestamp.cxx612
-rw-r--r--butl/utility264
-rw-r--r--butl/utility.cxx143
-rw-r--r--butl/utility.ixx136
-rw-r--r--butl/vector-view120
-rw-r--r--butl/version.in40
-rw-r--r--butl/win32-utility54
-rw-r--r--butl/win32-utility.cxx54
81 files changed, 0 insertions, 18734 deletions
diff --git a/butl/.gitignore b/butl/.gitignore
deleted file mode 100644
index 088eda4..0000000
--- a/butl/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-version
diff --git a/butl/base64 b/butl/base64
deleted file mode 100644
index 1ba2fa1..0000000
--- a/butl/base64
+++ /dev/null
@@ -1,50 +0,0 @@
-// file : butl/base64 -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_BASE64
-#define BUTL_BASE64
-
-#include <iosfwd>
-#include <string>
-#include <vector>
-
-#include <butl/export>
-
-namespace butl
-{
- // Base64-encode a stream or a buffer. Split the output into 76 char-long
- // lines (new line is the 77th). If reading from a stream, check if it has
- // badbit, failbit, or eofbit set and throw invalid_argument if that's the
- // case. Otherwise, set eofbit on completion. If writing to a stream, check
- // if it has badbit, failbit, or eofbit set and throw invalid_argument if
- // that's the case. Otherwise set badbit if the write operation fails.
- //
- LIBBUTL_EXPORT void
- base64_encode (std::ostream&, std::istream&);
-
- LIBBUTL_EXPORT std::string
- base64_encode (std::istream&);
-
- LIBBUTL_EXPORT std::string
- base64_encode (const std::vector<char>&);
-
- // Base64-decode a stream or a string. Throw invalid_argument if the input
- // is not a valid base64 representation. If reading from a stream, check if
- // it has badbit, failbit, or eofbit set and throw invalid_argument if
- // that's the case. Otherwise, set eofbit on completion. If writing to a
- // stream, check if it has badbit, failbit, or eofbit set and throw
- // invalid_argument if that's the case. Otherwise set badbit if the write
- // operation fails.
- //
- LIBBUTL_EXPORT void
- base64_decode (std::ostream&, std::istream&);
-
- LIBBUTL_EXPORT void
- base64_decode (std::ostream&, const std::string&);
-
- LIBBUTL_EXPORT std::vector<char>
- base64_decode (const std::string&);
-}
-
-#endif // BUTL_BASE64
diff --git a/butl/base64.cxx b/butl/base64.cxx
deleted file mode 100644
index ee44700..0000000
--- a/butl/base64.cxx
+++ /dev/null
@@ -1,211 +0,0 @@
-// file : butl/base64.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <butl/base64>
-
-#include <cstddef> // size_t
-#include <istream>
-#include <ostream>
-#include <iterator> // {istreambuf, ostreambuf, back_insert}_iterator
-#include <stdexcept> // invalid_argument
-
-using namespace std;
-
-namespace butl
-{
- static const char codes[] =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
- // base64-encode the data in the iterator range [i, e). Write the encoded
- // data starting at the iterator position o.
- //
- template <typename I, typename O>
- static void
- base64_encode (I& i, const I& e, O& o)
- {
- const size_t un (65); // Non-existing index of the codes string.
- for (size_t n (0); i != e; ++n)
- {
- if (n && n % 19 == 0)
- *o++ = '\n'; // Split into lines, like the base64 utility does.
-
- char c (*i++);
- size_t i1 ((c >> 2) & 0x3F);
- size_t i2 ((c << 4) & 0x30);
-
- size_t i3 (un);
- if (i != e)
- {
- c = *i++;
- i2 |= (c >> 4) & 0xF;
- i3 = (c << 2) & 0x3C;
- }
-
- size_t i4 (un);
- if (i != e)
- {
- c = *i++;
- i3 |= (c >> 6) & 0x3;
- i4 = c & 0x3F;
- }
-
- *o++ = codes[i1];
- *o++ = codes[i2];
- *o++ = i3 == un ? '=' : codes[i3];
- *o++ = i4 == un ? '=' : codes[i4];
- }
- }
-
- static char
- index (char c)
- {
- switch (c)
- {
- case '/': return 63;
- case '+': return 62;
- default:
- {
- if (c >= 'A' && c <= 'Z')
- return c - 'A';
- else if (c >= 'a' && c <= 'z')
- return c - 'a' + 26;
- else if (c >= '0' && c <= '9')
- return c - '0' + 52;
- else
- throw invalid_argument ("invalid input");
- }
- }
- }
-
- // base64-decode the data in the iterator range [i, e). Write the decoded
- // data starting at the iterator position o. Throw invalid_argument if the
- // input data is invalid.
- //
- template <typename I, typename O>
- static void
- base64_decode (I& i, const I& e, O& o)
- {
- auto bad = []() {throw invalid_argument ("invalid input");};
-
- auto next = [&i, &e, &bad]() -> char
- {
- if (i == e)
- bad ();
- return *i++;
- };
-
- while (i != e)
- {
- char c (next ());
- if (c == '\n') // @@ Should we check for '\r' as well ?
- continue;
-
- char i1 = index (c);
- char i2 = index (next ());
- *o++ = (i1 << 2) | (i2 >> 4);
-
- c = next ();
- if (c == '=')
- {
- if (next () != '=' || i != e)
- bad ();
- }
- else
- {
- char i3 = index (c);
- *o++ = (i2 << 4) | (i3 >> 2);
-
- c = next ();
- if (c == '=')
- {
- if (i != e)
- bad ();
- }
- else
- *o++ = (i3 << 6) | index (c);
- }
- }
- }
-
- string
- base64_encode (istream& is)
- {
- if (!is.good ())
- throw invalid_argument ("bad stream");
-
- string r;
- istreambuf_iterator<char> i (is);
- back_insert_iterator<string> o (r);
-
- base64_encode (i, istreambuf_iterator<char> (), o);
- is.setstate (istream::eofbit);
- return r;
- }
-
- void
- base64_encode (ostream& os, istream& is)
- {
- if (!os.good () || !is.good ())
- throw invalid_argument ("bad stream");
-
- istreambuf_iterator<char> i (is);
- ostreambuf_iterator<char> o (os);
- base64_encode (i, istreambuf_iterator<char> (), o);
-
- if (o.failed ())
- os.setstate (istream::badbit);
-
- is.setstate (istream::eofbit);
- }
-
- string
- base64_encode (const vector<char>& v)
- {
- string r;
- back_insert_iterator<string> o (r);
- auto i (v.begin ());
- base64_encode (i, v.end (), o);
- return r;
- }
-
- void
- base64_decode (ostream& os, istream& is)
- {
- if (!os.good () || !is.good ())
- throw invalid_argument ("bad stream");
-
- istreambuf_iterator<char> i (is);
- ostreambuf_iterator<char> o (os);
- base64_decode (i, istreambuf_iterator<char> (), o);
-
- if (o.failed ())
- os.setstate (istream::badbit);
-
- is.setstate (istream::eofbit);
- }
-
- void
- base64_decode (ostream& os, const string& s)
- {
- if (!os.good ())
- throw invalid_argument ("bad stream");
-
- ostreambuf_iterator<char> o (os);
- auto i (s.cbegin ());
- base64_decode (i, s.cend (), o);
-
- if (o.failed ())
- os.setstate (istream::badbit);
- }
-
- vector<char>
- base64_decode (const string& s)
- {
- vector<char> r;
- back_insert_iterator<vector<char>> o (r);
- auto i (s.cbegin ());
- base64_decode (i, s.cend (), o);
- return r;
- }
-}
diff --git a/butl/buildfile b/butl/buildfile
deleted file mode 100644
index 53fa588..0000000
--- a/butl/buildfile
+++ /dev/null
@@ -1,93 +0,0 @@
-# file : butl/buildfile
-# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-# license : MIT; see accompanying LICENSE file
-
-lib{butl}: \
- {hxx cxx}{ base64 } \
- {hxx cxx}{ char-scanner } \
- {hxx }{ const-ptr } \
- {hxx ixx txx cxx}{ curl } \
- {hxx cxx}{ diagnostics } \
- {hxx }{ export } \
- {hxx ixx cxx}{ fdstream } \
- {hxx ixx cxx}{ filesystem } \
- {hxx }{ manifest-forward } \
- {hxx cxx}{ manifest-parser } \
- {hxx cxx}{ manifest-serializer } \
- {hxx }{ multi-index } \
- {hxx }{ optional } \
- {hxx cxx}{ pager } \
- {hxx ixx txx cxx}{ path } \
- {hxx }{ path-io } \
- {hxx }{ path-map } \
- {hxx txx }{ prefix-map } \
- {hxx ixx cxx}{ process } \
- {hxx }{ process-details } \
- {hxx }{ process-io } \
- { txx cxx}{ process-run } \
- {hxx ixx cxx}{ sendmail } \
- {hxx cxx}{ sha256 } \
- {hxx }{ small-vector } \
- {hxx ixx cxx}{ standard-version } \
- {hxx cxx}{ string-parser } \
- {hxx txx }{ string-table } \
- {hxx cxx}{ tab-parser } \
- {hxx cxx}{ target-triplet } \
- {hxx cxx}{ timestamp } \
- {hxx ixx cxx}{ utility } \
- {hxx }{ vector-view } \
- {hxx }{ version } \
-ft/{hxx }{ exception } \
-ft/{hxx }{ lang } \
-ft/{hxx }{ shared_mutex }
-
-# Exclude these from compilation on non-Windows targets.
-#
-if ($cxx.target.class == "windows")
- lib{butl}: {hxx cxx}{ win32-utility }
-else
- lib{butl}: file{ win32-utility. win32-utility.cxx }
-
-# This one is included into sha256.cxx so treat it as file to exclude
-# from the compilation.
-#
-lib{butl}: file{sha256c.c}
-
-# These ones are included into timestamp.cxx so treat them as files to exclude
-# from the compilation.
-#
-lib{butl}: file{strptime.c timelocal.h timelocal.c}
-
-hxx{version}: in{version} $src_root/file{manifest}
-hxx{version}: dist = true
-
-# For pre-releases use the complete version to make sure they cannot be used
-# in place of another pre-release or the final version.
-#
-if $version.pre_release
- lib{butl}: bin.lib.version = @"-$version.project_id"
-else
- lib{butl}: bin.lib.version = @"-$version.major.$version.minor"
-
-cxx.poptions =+ "-I$out_root" "-I$src_root"
-obja{*}: cxx.poptions += -DLIBBUTL_STATIC_BUILD
-objs{*}: cxx.poptions += -DLIBBUTL_SHARED_BUILD
-
-lib{butl}: cxx.export.poptions = "-I$out_root" "-I$src_root"
-liba{butl}: cxx.export.poptions += -DLIBBUTL_STATIC
-libs{butl}: cxx.export.poptions += -DLIBBUTL_SHARED
-
-if ($cxx.target.class == "windows")
-{
- if ($cxx.target.system == "mingw32")
- cxx.libs += -lpsapi
- else
- cxx.libs += psapi.lib
-}
-else
- cxx.libs += -lpthread
-
-# Install into the butl/ subdirectory of, say, /usr/include/.
-#
-install.include = $install.include/butl/
-install.include.subdirs = true # Recreate subdirectories.
diff --git a/butl/char-scanner b/butl/char-scanner
deleted file mode 100644
index cbeca96..0000000
--- a/butl/char-scanner
+++ /dev/null
@@ -1,90 +0,0 @@
-// file : butl/char-scanner -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_CHAR_SCANNER
-#define BUTL_CHAR_SCANNER
-
-#include <string> // char_traits
-#include <iosfwd>
-#include <cstdint> // uint64_t
-
-#include <butl/export>
-
-namespace butl
-{
- // Low-level character stream scanner. Normally used as a base for
- // higher-level lexers.
- //
- class LIBBUTL_EXPORT char_scanner
- {
- public:
- // If the crlf argument is true, then recognize Windows newlines (0x0D
- // 0x0A) and convert them to just '\n' (0x0A). Note that a standalone
- // 0x0D is treated "as if" it was followed by 0x0A.
- //
- char_scanner (std::istream& is, bool crlf = true)
- : is_ (is), crlf_ (crlf) {}
-
- char_scanner (const char_scanner&) = delete;
- char_scanner& operator= (const char_scanner&) = delete;
-
- // Scanner interface.
- //
- public:
-
- // Extended character. It includes line/column information
- // and is capable of representing EOF.
- //
- class xchar
- {
- public:
- typedef std::char_traits<char> traits_type;
- typedef traits_type::int_type int_type;
- typedef traits_type::char_type char_type;
-
- int_type value;
- std::uint64_t line;
- std::uint64_t column;
-
- operator char_type () const {return static_cast<char_type> (value);}
-
- xchar (int_type v, std::uint64_t l = 0, std::uint64_t c = 0)
- : value (v), line (l), column (c) {}
- };
-
- xchar
- get ();
-
- void
- unget (const xchar&);
-
- // Note that if there is an "ungot" character, peek() will return
- // that.
- //
- xchar
- peek ();
-
- // Tests. In the future we can add tests line alpha(), alnum(),
- // etc.
- //
- static bool
- eos (const xchar& c) {return c.value == xchar::traits_type::eof ();}
-
- // Line and column of the furthest seen (either via get() or
- // peek()) character.
- //
- std::uint64_t line {1};
- std::uint64_t column {1};
-
- protected:
- std::istream& is_;
- bool crlf_;
-
- bool unget_ {false};
- xchar buf_ = '\0';
- bool eos_ {false};
- };
-}
-
-#endif // BUTL_CHAR_SCANNER
diff --git a/butl/char-scanner.cxx b/butl/char-scanner.cxx
deleted file mode 100644
index 8be7520..0000000
--- a/butl/char-scanner.cxx
+++ /dev/null
@@ -1,91 +0,0 @@
-// file : butl/char-scanner.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <butl/char-scanner>
-
-#include <istream>
-
-using namespace std;
-
-namespace butl
-{
- auto char_scanner::
- peek () -> xchar
- {
- if (unget_)
- return buf_;
- else
- {
- if (eos_)
- return xchar (xchar::traits_type::eof (), line, column);
- else
- {
- xchar::int_type v (is_.peek ());
-
- if (v == xchar::traits_type::eof ())
- eos_ = true;
- else if (crlf_ && v == 0x0D)
- {
- is_.get ();
- xchar::int_type v1 (is_.peek ());
-
- if (v1 != '\n')
- {
- unget_ = true;
- buf_ = '\n';
- }
-
- v = '\n';
- }
-
- return xchar (v, line, column);
- }
- }
- }
-
- auto char_scanner::
- get () -> xchar
- {
- if (unget_)
- {
- unget_ = false;
- return buf_;
- }
- else
- {
- // When is_.get () returns eof, the failbit is also set (stupid,
- // isn't?) which may trigger an exception. To work around this
- // we will call peek() first and only call get() if it is not
- // eof. But we can only call peek() on eof once; any subsequent
- // calls will spoil the failbit (even more stupid).
- //
- xchar c (peek ());
-
- if (!eos (c))
- {
- is_.get ();
-
- if (c == '\n')
- {
- line++;
- column = 1;
- }
- else
- column++;
- }
-
- return c;
- }
- }
-
- void char_scanner::
- unget (const xchar& c)
- {
- // Because iostream::unget cannot work once eos is reached,
- // we have to provide our own implementation.
- //
- buf_ = c;
- unget_ = true;
- }
-}
diff --git a/butl/const-ptr b/butl/const-ptr
deleted file mode 100644
index 2790253..0000000
--- a/butl/const-ptr
+++ /dev/null
@@ -1,80 +0,0 @@
-// file : butl/const-ptr -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_CONST_PTR
-#define BUTL_CONST_PTR
-
-#include <cstddef> // nullptr_t
-
-namespace butl
-{
- // Const-propagating pointer.
- //
- // It has the semantics of a raw pointer except that it passes on its own
- // const-ness to the pointed-to object. In other words, if you have a const
- // instance of this pointer, then you can only obtain a const raw pointer to
- // the underlying object. It is normally used as a data member, for example:
- //
- // struct tree
- // {
- // const_ptr<tree> left;
- // const_ptr<tree> right;
- //
- // void modify ();
- // };
- //
- // tree* x = ...;
- // const tree* y = ...;
- //
- // x.left->modify (); // Ok.
- // y.left->modify (); // Error.
- //
- // Note that due to this semantics, copy construction/assignment requires
- // a non-const instance of const_ptr.
- //
- // Note that this type is standard layout (which means we can reinterpret
- // it as a raw pointer).
- //
- // Known drawbacks/issues:
- //
- // 1. Cannot do static_cast<mytree*> (x.left).
- //
- template <typename T>
- class const_ptr
- {
- public:
- const_ptr () = default;
- explicit const_ptr (T* p): p_ (p) {}
- const_ptr (std::nullptr_t): p_ (nullptr) {}
-
- const_ptr& operator= (T* p) {p_ = p; return *this;}
- const_ptr& operator= (std::nullptr_t) {p_ = nullptr; return *this;}
-
- template <class T1> explicit const_ptr (T1* p): p_ (p) {}
- template <class T1> const_ptr (const_ptr<T1>& p): p_ (p.p_) {}
-
- template <class T1> const_ptr& operator= (T1* p) {p_ = p; return *this;}
- template <class T1> const_ptr& operator= (const_ptr<T1>& p) {
- p_ = p.p_; return *this;}
-
- T* operator-> () {return p_;}
- const T* operator-> () const {return p_;}
-
- T& operator* () {return *p_;}
- const T& operator* () const {return *p_;}
-
- operator T* () {return p_;}
- operator const T* () const {return p_;}
-
- explicit operator bool () const {return p_ != nullptr;}
-
- T* get () {return p_;}
- const T* get () const {return p_;}
-
- private:
- T* p_;
- };
-}
-
-#endif // BUTL_CONST_PTR
diff --git a/butl/curl b/butl/curl
deleted file mode 100644
index c8285ea..0000000
--- a/butl/curl
+++ /dev/null
@@ -1,173 +0,0 @@
-// file : butl/curl -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_CURL
-#define BUTL_CURL
-
-#include <string>
-#include <type_traits>
-
-#include <butl/export>
-
-#include <butl/process>
-#include <butl/fdstream>
-#include <butl/small-vector>
-
-namespace butl
-{
- // Perform a method (GET, POST, PUT) on a URL using the curl(1) program.
- // Throw process_error and io_error (both derive from system_error) in case
- // of errors.
- //
- // The I (in) and O (out) can be of the following types/values:
- //
- // nullfd Signal that no input/output is expected.
- //
- // path Read input/write output from/to a file. If the special "-"
- // value is used, then instead input is connected to the curl::out
- // ofdstream member and output -- to the curl::in ifdstream member.
- // Note that the argument type should be path, not string (i.e.,
- // pass path("-")).
- //
- // other Forwarded as is to process_start(). Normally either int or
- // auto_fd.
- //
- // For example:
- //
- // curl (nullfd, // No input expected for GET.
- // path ("-"), // Write response to curl::in.
- // 2,
- // curl::get,
- // "http://example.org");
- //
- // curl (path ("-"), // Read request from curl::out.
- // path::temp_path (), // Write result to a file.
- // 2,
- // curl::post,
- // "http://example.org");
- //
- // curl (nullfd,
- // fdnull (), // Write result to /dev/null.
- // 2,
- // curl::get,
- // "tftp://localhost/foo");
- //
- // Typical usage:
- //
- // try
- // {
- // curl c (nullfd, // No input expected.
- // path ("-"), // Output to curl::in.
- // 2, // Diagnostics to stderr.
- // curl::get, // GET method.
- // "https://example.org",
- // "-A", "foobot/1.2.3"); // Additional curl(1) options.
- //
- // for (string s; getline (c.in, s); )
- // cout << s << endl;
- //
- // c.in.close ();
- //
- // if (!c.wait ())
- // ... // curl returned non-zero status.
- // }
- // catch (const std::system_error& e)
- // {
- // cerr << "curl error: " << e << endl;
- // }
- //
- // Notes:
- //
- // 1. If opened, in/out streams are in the binary mode.
- //
- // 2. If opened, in/out must be explicitly closed before calling wait().
- //
- // 3. Only binary data HTTP POST is currently supported (the --data-binary
- // curl option).
- //
- class LIBBUTL_EXPORT curl: public process
- {
- public:
- enum method_type {get, put, post};
-
- ifdstream in;
- ofdstream out;
-
- template <typename I,
- typename O,
- typename E,
- typename... A>
- curl (I&& in,
- O&& out,
- E&& err,
- method_type,
- const std::string& url,
- A&&... options);
-
- // Version with the command line callback (see process_run() for details).
- //
- template <typename C,
- typename I,
- typename O,
- typename E,
- typename... A>
- curl (const C&,
- I&& in,
- O&& out,
- E&& err,
- method_type,
- const std::string& url,
- A&&... options);
-
- private:
- enum method_proto {ftp_get, ftp_put, http_get, http_post};
- using method_proto_options = small_vector<const char*, 2>;
-
- method_proto
- translate (method_type, const std::string& url, method_proto_options&);
-
- private:
- template <typename T>
- struct is_other
- {
- using type = typename std::remove_reference<
- typename std::remove_cv<T>::type>::type;
-
- static const bool value = !(std::is_same<type, nullfd_t>::value ||
- std::is_same<type, path>::value);
- };
-
- struct io_data
- {
- fdpipe pipe;
- method_proto_options options;
- std::string storage;
- };
-
- int
- map_in (nullfd_t, method_proto, io_data&);
-
- int
- map_in (const path&, method_proto, io_data&);
-
- template <typename I>
- typename std::enable_if<is_other<I>::value, I>::type
- map_in (I&&, method_proto, io_data&);
-
- int
- map_out (nullfd_t, method_proto, io_data&);
-
- int
- map_out (const path&, method_proto, io_data&);
-
- template <typename O>
- typename std::enable_if<is_other<O>::value, O>::type
- map_out (O&&, method_proto, io_data&);
- };
-}
-
-#include <butl/curl.ixx>
-#include <butl/curl.txx>
-
-#endif // BUTL_CURL
diff --git a/butl/curl.cxx b/butl/curl.cxx
deleted file mode 100644
index 12a1d54..0000000
--- a/butl/curl.cxx
+++ /dev/null
@@ -1,166 +0,0 @@
-// file : butl/curl.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <butl/curl>
-
-#include <utility> // move(), forward()
-#include <exception> // invalid_argument
-
-#include <butl/utility> // casecmp()
-
-using namespace std;
-
-namespace butl
-{
- int curl::
- map_in (nullfd_t, method_proto mp, io_data& d)
- {
- switch (mp)
- {
- case ftp_put:
- throw invalid_argument ("no input specified for PUT method");
- case http_post:
- throw invalid_argument ("no input specified for POST method");
- case ftp_get:
- case http_get:
- {
- d.pipe.in = fdnull (); // /dev/null
- return d.pipe.in.get ();
- }
- }
-
- return -1;
- }
-
- int curl::
- map_in (const path& f, method_proto mp, io_data& d)
- {
- switch (mp)
- {
- case ftp_put:
- case http_post:
- {
- if (mp == ftp_put)
- {
- d.options.push_back ("--upload-file");
- d.options.push_back (f.string ().c_str ());
- }
- else
- {
- d.storage = '@' + f.string ();
-
- d.options.push_back ("--data-binary");
- d.options.push_back (d.storage.c_str ());
- }
-
- if (f.string () == "-")
- {
- d.pipe = fdopen_pipe (fdopen_mode::binary);
- out.open (move (d.pipe.out));
- }
- else
- d.pipe.in = fdnull (); // /dev/null
-
- return d.pipe.in.get ();
- }
- case ftp_get:
- case http_get:
- {
- throw invalid_argument ("file input specified for GET method");
- }
- }
-
- return -1;
- }
-
- int curl::
- map_out (nullfd_t, method_proto mp, io_data& d)
- {
- switch (mp)
- {
- case ftp_get:
- case http_get:
- throw invalid_argument ("no output specified for GET method");
- case ftp_put:
- case http_post: // May or may not produce output.
- {
- d.pipe.out = fdnull ();
- return d.pipe.out.get (); // /dev/null
- }
- }
-
- return -1;
- }
-
- int curl::
- map_out (const path& f, method_proto mp, io_data& d)
- {
- switch (mp)
- {
- case ftp_get:
- case http_get:
- case http_post:
- {
- if (f.string () == "-")
- {
- // Note: no need for any options, curl writes to stdout by default.
- //
- d.pipe = fdopen_pipe (fdopen_mode::binary);
- in.open (move (d.pipe.in));
- }
- else
- {
- d.options.push_back ("-o");
- d.options.push_back (f.string ().c_str ());
- d.pipe.out = fdnull (); // /dev/null
- }
-
- return d.pipe.out.get ();
- }
- case ftp_put:
- {
- throw invalid_argument ("file output specified for PUT method");
- }
- }
-
- return -1;
- }
-
- curl::method_proto curl::
- translate (method_type m, const string& u, method_proto_options& o)
- {
- size_t n (u.find ("://"));
-
- if (n == string::npos)
- throw invalid_argument ("no protocol in URL");
-
- if (casecmp (u, "ftp", n) == 0 ||
- casecmp (u, "tftp", n) == 0)
- {
- switch (m)
- {
- case method_type::get: return method_proto::ftp_get;
- case method_type::put: return method_proto::ftp_put;
- case method_type::post:
- throw invalid_argument ("POST method with FTP protocol");
- }
- }
- else if (casecmp (u, "http", n) == 0 ||
- casecmp (u, "https", n) == 0)
- {
- o.push_back ("--fail"); // Fail on HTTP errors (e.g., 404).
- o.push_back ("--location"); // Follow redirects.
-
- switch (m)
- {
- case method_type::get: return method_proto::http_get;
- case method_type::post: return method_proto::http_post;
- case method_type::put:
- throw invalid_argument ("PUT method with HTTP protocol");
- }
- }
-
- throw invalid_argument ("unsupported protocol");
- }
-}
diff --git a/butl/curl.ixx b/butl/curl.ixx
deleted file mode 100644
index 83b388b..0000000
--- a/butl/curl.ixx
+++ /dev/null
@@ -1,29 +0,0 @@
-// file : butl/curl.ixx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <utility> // move(), forward()
-
-namespace butl
-{
- template <typename I,
- typename O,
- typename E,
- typename... A>
- inline curl::
- curl (I&& in,
- O&& out,
- E&& err,
- method_type m,
- const std::string& url,
- A&&... options)
- : curl ([] (const char* [], std::size_t) {},
- std::forward<I> (in),
- std::forward<O> (out),
- std::forward<E> (err),
- m,
- url,
- std::forward<A> (options)...)
- {
- }
-}
diff --git a/butl/curl.txx b/butl/curl.txx
deleted file mode 100644
index fe8a25f..0000000
--- a/butl/curl.txx
+++ /dev/null
@@ -1,99 +0,0 @@
-// file : butl/curl.txx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <utility> // move(), forward()
-#include <exception> // invalid_argument
-
-namespace butl
-{
- template <typename I>
- typename std::enable_if<curl::is_other<I>::value, I>::type curl::
- map_in (I&& in, method_proto mp, io_data& d)
- {
- switch (mp)
- {
- case ftp_put:
- {
- d.options.push_back ("--upload-file");
- d.options.push_back ("-");
- break;
- }
- case http_post:
- {
- d.options.push_back ("--data-binary");
- d.options.push_back ("@-");
- break;
- }
- case ftp_get:
- case http_get:
- {
- throw std::invalid_argument ("input specified for GET method");
- }
- }
-
- return std::forward<I> (in);
- }
-
- template <typename O>
- typename std::enable_if<curl::is_other<O>::value, O>::type curl::
- map_out (O&& out, method_proto mp, io_data&)
- {
- switch (mp)
- {
- case ftp_get:
- case http_get:
- case http_post:
- {
- // Note: no need for any options, curl writes to stdout by default.
- //
- break;
- }
- case ftp_put:
- {
- throw std::invalid_argument ("output specified for PUT method");
- }
- }
-
- return std::forward<O> (out);
- }
-
- template <typename C,
- typename I,
- typename O,
- typename E,
- typename... A>
- curl::
- curl (const C& cmdc,
- I&& in,
- O&& out,
- E&& err,
- method_type m,
- const std::string& url,
- A&&... options)
- {
- method_proto_options mpo;
- method_proto mp (translate (m, url, mpo));
-
- io_data in_data;
- io_data out_data;
-
- process& p (*this);
- p = process_start (
- cmdc,
- map_in (std::forward<I> (in), mp, in_data),
- map_out (std::forward<O> (out), mp, out_data),
- std::forward<E> (err),
- dir_path (),
- "curl",
- "-s", // Silent.
- "-S", // But do show diagnostics.
- mpo,
- in_data.options,
- out_data.options,
- std::forward<A> (options)...,
- url);
-
- // Note: leaving this scope closes any open ends of the pipes in io_data.
- }
-}
diff --git a/butl/diagnostics b/butl/diagnostics
deleted file mode 100644
index 18008d5..0000000
--- a/butl/diagnostics
+++ /dev/null
@@ -1,226 +0,0 @@
-// file : butl/diagnostics -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_DIAGNOSTICS
-#define BUTL_DIAGNOSTICS
-
-#include <cassert>
-#include <ostream>
-#include <sstream>
-#include <utility> // move(), forward()
-#include <butl/ft/exception> // uncaught_exceptions
-#include <exception> // uncaught_exception(s)()
-
-#include <butl/export>
-#include <butl/utility>
-
-namespace butl
-{
- // Diagnostic facility base infrastructure.
- //
-
- // Diagnostics destination stream (std::cerr by default). Note that its
- // modification is not MT-safe. Also note that concurrent writing to the
- // stream from multiple threads can result in interleaved characters. To
- // prevent this an object of diag_lock type (see below) must be created prior
- // to write operation.
- //
- LIBBUTL_EXPORT extern std::ostream* diag_stream;
-
- // Acquire the diagnostics exclusive access mutex in ctor, release in dtor.
- // An object of the type must be created prior to writing to diag_stream (see
- // above).
- //
- struct LIBBUTL_EXPORT diag_lock
- {
- diag_lock ();
- ~diag_lock ();
- };
-
- struct diag_record;
- template <typename> struct diag_prologue;
- template <typename> struct diag_mark;
-
- using diag_epilogue = void (const diag_record&);
-
- struct LIBBUTL_EXPORT diag_record
- {
- template <typename T>
- friend const diag_record&
- operator<< (const diag_record& r, const T& x)
- {
- r.os << x;
- return r;
- }
-
- diag_record ()
- :
-#ifdef __cpp_lib_uncaught_exceptions
- uncaught_ (std::uncaught_exceptions ()),
-#endif
- empty_ (true),
- epilogue_ (nullptr) {}
-
- template <typename B>
- explicit
- diag_record (const diag_prologue<B>& p): diag_record () { *this << p;}
-
- template <typename B>
- explicit
- diag_record (const diag_mark<B>& m): diag_record () { *this << m;}
-
- ~diag_record () noexcept (false);
-
- bool
- empty () const {return empty_;}
-
- bool
- full () const {return !empty_;}
-
- void
- flush () const;
-
- void
- append (const char* indent, diag_epilogue* e) const
- {
- // Ignore subsequent epilogues (e.g., from nested marks, etc).
- //
- if (empty_)
- {
- epilogue_ = e;
- empty_ = false;
- }
- else if (indent != nullptr)
- os << indent;
- }
-
- // Move constructible-only type.
- //
- // Older versions of libstdc++ don't have the ostringstream move support
- // and accuratly detecting its version is non-trivial. So we always use
- // the pessimized implementation with libstdc++. Luckily, GCC doesn't seem
- // to be needing move due to copy/move elision.
- //
-#ifdef __GLIBCXX__
- diag_record (diag_record&&);
-#else
- diag_record (diag_record&& r)
- :
-#ifdef __cpp_lib_uncaught_exceptions
- uncaught_ (r.uncaught_),
-#endif
- empty_ (r.empty_),
- epilogue_ (r.epilogue_),
- os (std::move (r.os))
- {
- if (!empty_)
- {
- r.empty_ = true;
- r.epilogue_ = nullptr;
- }
- }
-#endif
-
- diag_record& operator= (diag_record&&) = delete;
-
- diag_record (const diag_record&) = delete;
- diag_record& operator= (const diag_record&) = delete;
-
- protected:
-#ifdef __cpp_lib_uncaught_exceptions
- const int uncaught_;
-#endif
- mutable bool empty_;
- mutable diag_epilogue* epilogue_;
-
- public:
- mutable std::ostringstream os;
- };
-
- template <typename B>
- struct diag_prologue: B
- {
- diag_prologue (const char* i = "\n ", diag_epilogue* e = nullptr)
- : B (), indent_ (i), epilogue_ (e) {}
-
- template <typename... A>
- diag_prologue (A&&... a)
- : B (std::forward<A> (a)...), indent_ ("\n "), epilogue_ (nullptr) {}
-
- template <typename... A>
- diag_prologue (diag_epilogue* e, A&&... a)
- : B (std::forward<A> (a)...), indent_ ("\n "), epilogue_ (e) {}
-
- template <typename... A>
- diag_prologue (const char* i, diag_epilogue* e, A&&... a)
- : B (std::forward<A> (a)...), indent_ (i), epilogue_ (e) {}
-
- template <typename T>
- diag_record
- operator<< (const T& x) const
- {
- diag_record r;
- r.append (indent_, epilogue_);
- B::operator() (r);
- r << x;
- return r;
- }
-
- friend const diag_record&
- operator<< (const diag_record& r, const diag_prologue& p)
- {
- r.append (p.indent_, p.epilogue_);
- p (r);
- return r;
- }
-
- private:
- const char* indent_;
- diag_epilogue* epilogue_;
- };
-
- template <typename B>
- struct diag_mark: B
- {
- diag_mark (): B () {}
-
- template <typename... A>
- diag_mark (A&&... a): B (std::forward<A> (a)...) {}
-
- template <typename T>
- diag_record
- operator<< (const T& x) const
- {
- return B::operator() () << x;
- }
-
- friend const diag_record&
- operator<< (const diag_record& r, const diag_mark& m)
- {
- return r << m ();
- }
- };
-
- template <typename B>
- struct diag_noreturn_end: B
- {
- diag_noreturn_end (): B () {}
-
- template <typename... A>
- diag_noreturn_end (A&&... a): B (std::forward<A> (a)...) {}
-
- [[noreturn]] friend void
- operator<< (const diag_record& r, const diag_noreturn_end& e)
- {
- // We said that we never return which means this end mark cannot be used
- // to "maybe not return". And not returning without any diagnostics is
- // probably a mistake.
- //
- assert (r.full ());
- e.B::operator() (r);
- }
- };
-}
-
-#endif // BUTL_DIAGNOSTICS
diff --git a/butl/diagnostics.cxx b/butl/diagnostics.cxx
deleted file mode 100644
index b692777..0000000
--- a/butl/diagnostics.cxx
+++ /dev/null
@@ -1,80 +0,0 @@
-// file : butl/diagnostics.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <butl/diagnostics>
-
-#include <mutex>
-#include <iostream> // cerr
-
-using namespace std;
-
-namespace butl
-{
- ostream* diag_stream = &cerr;
-
- static mutex diag_mutex;
-
- diag_lock::diag_lock ()
- {
- diag_mutex.lock ();
- }
-
- diag_lock::~diag_lock ()
- {
- diag_mutex.unlock ();
- }
-
- void diag_record::
- flush () const
- {
- if (!empty_)
- {
- if (epilogue_ == nullptr)
- {
- os.put ('\n');
-
- {
- diag_lock l;
- *diag_stream << os.str ();
- }
-
- // We can endup flushing the result of several writes. The last one may
- // possibly be incomplete, but that's not a problem as it will also be
- // followed by the flush() call.
- //
- diag_stream->flush ();
-
- empty_ = true;
- }
- else
- {
- // Clear the epilogue in case it calls us back.
- //
- auto e (epilogue_);
- epilogue_ = nullptr;
- e (*this); // Can throw.
- flush (); // Call ourselves to write the data in case it returns.
- }
- }
- }
-
- diag_record::
- ~diag_record () noexcept (false)
- {
- // Don't flush the record if this destructor was called as part of the
- // stack unwinding.
- //
-#ifdef __cpp_lib_uncaught_exceptions
- if (uncaught_ == std::uncaught_exceptions ())
- flush ();
-#else
- // Fallback implementation. Right now this means we cannot use this
- // mechanism in destructors, which is not a big deal, except for one
- // place: exception_guard. Thus the ugly special check.
- //
- if (!std::uncaught_exception () || exception_unwinding_dtor ())
- flush ();
-#endif
- }
-}
diff --git a/butl/export b/butl/export
deleted file mode 100644
index 229e203..0000000
--- a/butl/export
+++ /dev/null
@@ -1,41 +0,0 @@
-// file : butl/export -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_EXPORT
-#define BUTL_EXPORT
-
-// Normally we don't export class templates (but do complete specializations),
-// inline functions, and classes with only inline member functions. Exporting
-// classes that inherit from non-exported/imported bases (e.g., std::string)
-// will end up badly. The only known workarounds are to not inherit or to not
-// export. Also, MinGW GCC doesn't like seeing non-exported function being
-// used before their inline definition. The workaround is to reorder code. In
-// the end it's all trial and error.
-
-#if defined(LIBBUTL_STATIC) // Using static.
-# define LIBBUTL_EXPORT
-#elif defined(LIBBUTL_STATIC_BUILD) // Building static.
-# define LIBBUTL_EXPORT
-#elif defined(LIBBUTL_SHARED) // Using shared.
-# ifdef _WIN32
-# define LIBBUTL_EXPORT __declspec(dllimport)
-# else
-# define LIBBUTL_EXPORT
-# endif
-#elif defined(LIBBUTL_SHARED_BUILD) // Building shared.
-# ifdef _WIN32
-# define LIBBUTL_EXPORT __declspec(dllexport)
-# else
-# define LIBBUTL_EXPORT
-# endif
-#else
-// If none of the above macros are defined, then we assume we are being used
-// by some third-party build system that cannot/doesn't signal the library
-// type. Note that this fallback works for both static and shared but in case
-// of shared will be sub-optimal compared to having dllimport.
-//
-# define LIBBUTL_EXPORT // Using static or shared.
-#endif
-
-#endif // BUTL_EXPORT
diff --git a/butl/fdstream b/butl/fdstream
deleted file mode 100644
index df39808..0000000
--- a/butl/fdstream
+++ /dev/null
@@ -1,643 +0,0 @@
-// file : butl/fdstream -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_FDSTREAM
-#define BUTL_FDSTREAM
-
-#include <string>
-#include <istream>
-#include <ostream>
-#include <utility> // move()
-#include <cstdint> // uint16_t
-
-#include <butl/export>
-
-#include <butl/path>
-#include <butl/filesystem> // permissions
-
-namespace butl
-{
- // RAII type for file descriptors. Note that failure to close the descriptor
- // is silently ignored by both the destructor and reset().
- //
- // The descriptor can be negative. Such a descriptor is treated as unopened
- // and is not closed.
- //
- struct nullfd_t {constexpr explicit nullfd_t (int) {}};
- constexpr const nullfd_t nullfd (-1);
-
- class LIBBUTL_EXPORT auto_fd
- {
- public:
- auto_fd (nullfd_t = nullfd) noexcept: fd_ (-1) {}
-
- explicit
- auto_fd (int fd) noexcept: fd_ (fd) {}
-
- auto_fd (auto_fd&& fd) noexcept: fd_ (fd.release ()) {}
- auto_fd& operator= (auto_fd&&) noexcept;
-
- auto_fd (const auto_fd&) = delete;
- auto_fd& operator= (const auto_fd&) = delete;
-
- ~auto_fd () noexcept;
-
- int
- get () const noexcept {return fd_;}
-
- void
- reset (int fd = -1) noexcept;
-
- int
- release () noexcept
- {
- int r (fd_);
- fd_ = -1;
- return r;
- }
-
- // Close an open file descriptor. Throw ios::failure on the underlying OS
- // error. Reset the descriptor to -1 whether the exception is thrown or
- // not.
- //
- void
- close ();
-
- private:
- int fd_;
- };
-
- // 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
- // - non-blocking file descriptor is supported only by showmanyc() function
- // and only on POSIX
- // - 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
- // - passing to constructor auto_fd with a negative file descriptor is valid
- // and results in the creation of an unopened object
- //
- class LIBBUTL_EXPORT fdbuf: public std::basic_streambuf<char>
- {
- public:
- fdbuf () = default;
- fdbuf (auto_fd&&);
-
- // Before we invented auto_fd into fdstreams we keept fdbuf opened on
- // faulty close attempt. Now fdbuf is always closed by close() function.
- // This semantics change seems to be the right one as there is no reason to
- // expect fdclose() to succeed after it has already failed once.
- //
- void
- close () {fd_.close ();}
-
- auto_fd
- release ();
-
- void
- open (auto_fd&&);
-
- bool
- is_open () const {return fd_.get () >= 0;}
-
- int
- fd () const {return fd_.get ();}
-
- public:
- using int_type = std::basic_streambuf<char>::int_type;
- using traits_type = std::basic_streambuf<char>::traits_type;
-
- // basic_streambuf input interface.
- //
- public:
- virtual std::streamsize
- showmanyc ();
-
- virtual int_type
- underflow ();
-
- private:
- bool
- load ();
-
- // basic_streambuf output interface.
- //
- public:
- virtual int_type
- overflow (int_type);
-
- virtual int
- sync ();
-
- virtual std::streamsize
- xsputn (const char_type*, std::streamsize);
-
- private:
- bool
- save ();
-
- private:
- auto_fd fd_;
- char buf_[8192];
- bool non_blocking_ = false;
- };
-
- // 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. On Windows, when reading in the text mode the sequence of 0xD,
- // 0xA characters is translated into the single OxA character and 0x1A is
- // interpreted as EOF. When writing in the text mode the OxA character is
- // translated into the 0xD, 0xA sequence.
- //
- // 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.
- //
- // The blocking/non_blocking flags determine whether the IO operation should
- // block or return control if currently there is no data to read or no room
- // to write. Only the istream::readsome() function supports the semantics of
- // non-blocking operations. We also only support this on POSIX (Windows does
- // not provide means for the non-blocking reading from a file descriptor so
- // these flags are noop there). IO stream operations other than readsome()
- // are illegal for non_blocking mode and result in the badbit being set.
- //
- enum class fdstream_mode: std::uint16_t
- {
- text = 0x01,
- binary = 0x02,
- skip = 0x04,
- blocking = 0x08,
- non_blocking = 0x10
- };
-
- inline fdstream_mode operator& (fdstream_mode, fdstream_mode);
- inline fdstream_mode operator| (fdstream_mode, fdstream_mode);
- inline fdstream_mode operator&= (fdstream_mode&, fdstream_mode);
- inline 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.
- };
-
- inline fdopen_mode operator& (fdopen_mode, fdopen_mode);
- inline fdopen_mode operator| (fdopen_mode, fdopen_mode);
- inline fdopen_mode operator&= (fdopen_mode&, fdopen_mode);
- inline fdopen_mode operator|= (fdopen_mode&, fdopen_mode);
-
- class LIBBUTL_EXPORT fdstream_base
- {
- protected:
- fdstream_base () = default;
- fdstream_base (auto_fd&& fd): buf_ (std::move (fd)) {}
- fdstream_base (auto_fd&&, fdstream_mode);
-
- public:
- int
- fd () const {return buf_.fd ();}
-
- protected:
- fdbuf buf_;
- };
-
- // 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 auto_fd with a negative 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.
- //
-
- // Note that ifdstream destructor will close an open file descriptor but
- // will ignore any errors. To detect such errors, call close() explicitly.
- //
- // This is a sample usage of iofdstreams with process. Note that here it is
- // expected that the child process reads from STDIN first and writes to
- // STDOUT afterwards.
- //
- // try
- // {
- // process pr (args, -1, -1);
- //
- // try
- // {
- // // In case of exception, skip and close input after output.
- // //
- // ifdstream is (move (pr.in_ofd), fdstream_mode::skip);
- // ofdstream os (move (pr.out_fd));
- //
- // // Write.
- //
- // os.close (); // Don't block the other end.
- //
- // // Read.
- //
- // is.close (); // Skip till end and close.
- //
- // if (pr.wait ())
- // {
- // return ...; // Good.
- // }
- //
- // // Non-zero exit, diagnostics presumably issued, fall through.
- // }
- // catch (const failure&)
- // {
- // // IO failure, child exit status doesn't matter. Just wait for the
- // // process completion and fall through.
- // //
- // // Note that this is optional if the process_error handler simply
- // // falls through since process destructor will wait (but will ignore
- // // any errors).
- // //
- // pr.wait ();
- // }
- //
- // error << .... ;
- //
- // // Fall through.
- // }
- // catch (const process_error& e)
- // {
- // error << ... << e;
- //
- // if (e.child ())
- // exit (1);
- //
- // // Fall through.
- // }
- //
- // throw failed ();
- //
- class LIBBUTL_EXPORT ifdstream: public fdstream_base, public std::istream
- {
- public:
- // Create an unopened object.
- //
- explicit
- ifdstream (iostate e = badbit | failbit);
-
- explicit
- ifdstream (auto_fd&&, iostate e = badbit | failbit);
- ifdstream (auto_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
- open (const std::string&, fdopen_mode);
-
- void
- open (const path&, fdopen_mode);
-
- void
- open (auto_fd&& fd) {buf_.open (std::move (fd)); clear ();}
-
- void close ();
- auto_fd release (); // Note: no skipping.
- bool is_open () const {return buf_.is_open ();}
-
- private:
- bool skip_ = false;
- };
-
- // 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 LIBBUTL_EXPORT ofdstream: public fdstream_base, public std::ostream
- {
- public:
- // Create an unopened object.
- //
- explicit
- ofdstream (iostate e = badbit | failbit);
-
- explicit
- ofdstream (auto_fd&&, iostate e = badbit | failbit);
- ofdstream (auto_fd&&, fdstream_mode m, iostate e = badbit | failbit);
-
- explicit
- ofdstream (const char*,
- openmode = out,
- iostate e = badbit | failbit);
-
- explicit
- ofdstream (const std::string&,
- openmode = out,
- iostate e = badbit | failbit);
-
- 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 (auto_fd&& fd) {buf_.open (std::move (fd)); clear ();}
-
- void close () {if (is_open ()) flush (); buf_.close ();}
- auto_fd release ();
- bool is_open () const {return buf_.is_open ();}
- };
-
- // 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.
- //
- LIBBUTL_EXPORT ifdstream&
- getline (ifdstream&, std::string&, char delim = '\n');
-
- // Open a file returning an auto_fd that holds its file descriptor on
- // success and throwing 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
- // flag and are ignored without it. The exclusive flag is meaningless in the
- // absense of the create flag and is ignored without it. Note also that if
- // the exclusive flag is specified then a dangling symbolic link is treated
- // as an existing file.
- //
- // The permissions argument is taken into account only if the file is
- // created. Note also that permissions can be adjusted while being set in a
- // way specific for the OS. On POSIX systems they are modified with the
- // process' umask, so effective permissions are permissions & ~umask. On
- // Windows permissions other than ru and wu are unlikelly to have effect.
- //
- // Also note that on POSIX the FD_CLOEXEC flag is set for the file descriptor
- // to prevent its leakage into child processes. On Windows, for the same
- // purpose, the _O_NOINHERIT flag is set. Note that the process class, that
- // passes such a descriptor to the child, makes it inheritable for a while.
- //
- LIBBUTL_EXPORT auto_fd
- fdopen (const char*,
- fdopen_mode,
- permissions = permissions::ru | permissions::wu |
- permissions::rg | permissions::wg |
- permissions::ro | permissions::wo);
-
- LIBBUTL_EXPORT auto_fd
- fdopen (const std::string&,
- fdopen_mode,
- permissions = permissions::ru | permissions::wu |
- permissions::rg | permissions::wg |
- permissions::ro | permissions::wo);
-
- LIBBUTL_EXPORT auto_fd
- fdopen (const path&,
- fdopen_mode,
- permissions = permissions::ru | permissions::wu |
- permissions::rg | permissions::wg |
- permissions::ro | permissions::wo);
-
- // Duplicate an open file descriptor. Throw ios::failure on the underlying
- // OS error.
- //
- // Note that on POSIX the FD_CLOEXEC flag is set for the new descriptor if it
- // is present for the source one. That's in contrast to POSIX dup() that
- // doesn't copy file descriptor flags. Also note that duplicating descriptor
- // and setting the flag is not an atomic operation generally, but it is in
- // regards to child process spawning (to prevent file descriptor leakage into
- // a child process).
- //
- // Note that on Windows the _O_NOINHERIT flag is set for the new descriptor
- // if it is present for the source one. That's in contrast to Windows _dup()
- // that doesn't copy the flag. Also note that duplicating descriptor and
- // setting the flag is not an atomic operation generally, but it is in
- // regards to child process spawning (to prevent file descriptor leakage into
- // a child process).
- //
- LIBBUTL_EXPORT auto_fd
- fddup (int fd);
-
- // Set the translation mode for the file descriptor. Throw invalid_argument
- // for an invalid combination of flags. Return the previous mode on success,
- // throw ios::failure otherwise.
- //
- // The text and binary flags are mutually exclusive on Windows. Due to
- // implementation details at least one of them should be specified. On POSIX
- // system the two modes are the same and so no check is performed.
- //
- // The blocking and non-blocking flags are mutually exclusive on POSIX
- // system. Non-blocking mode is not supported on Windows and so the blocking
- // mode is assumed regardless of the flags.
- //
- LIBBUTL_EXPORT fdstream_mode
- fdmode (int, fdstream_mode);
-
- // Convenience functions for setting the translation mode for standard
- // streams.
- //
- LIBBUTL_EXPORT fdstream_mode stdin_fdmode (fdstream_mode);
- LIBBUTL_EXPORT fdstream_mode stdout_fdmode (fdstream_mode);
- LIBBUTL_EXPORT fdstream_mode stderr_fdmode (fdstream_mode);
-
- // Low-level, nothrow file descriptor API.
- //
-
- // Close the file descriptor. Return true on success, set errno and return
- // false otherwise.
- //
- LIBBUTL_EXPORT bool
- fdclose (int) noexcept;
-
- // Open the null device (e.g., /dev/null) that discards all data written to
- // it and provides no data for read operations (i.e., yelds EOF on read).
- // Return an auto_fd that holds its file descriptor on success and throwing
- // ios::failure otherwise.
- //
- // On Windows the null device is NUL and writing anything substantial to it
- // (like redirecting a process' output) is extremely slow, as in, an order
- // of magnitude slower than writing to disk. If you are using the descriptor
- // yourself this can be mitigated by setting the binary mode (already done
- // by fdopen()) and using a buffer of around 64K. However, sometimes you
- // have no control of how the descriptor will be used. For instance, it can
- // be used to redirect a child's stdout and the way the child sets up its
- // stdout is out of your control (on Windows). For such cases, there is an
- // emulation via a temporary file. Mostly it functions as a proper null
- // device with the file automatically removed once the descriptor is
- // closed. One difference, however, would be if you were to both write to
- // and read from the descriptor.
- //
- // Note that on POSIX the FD_CLOEXEC flag is set for the file descriptor to
- // prevent its leakage into child processes. On Windows, for the same
- // purpose, the _O_NOINHERIT flag is set.
- //
-#ifndef _WIN32
- LIBBUTL_EXPORT auto_fd
- fdnull () noexcept;
-#else
- LIBBUTL_EXPORT auto_fd
- fdnull (bool temp = false) noexcept;
-#endif
-
- struct fdpipe
- {
- auto_fd in;
- auto_fd out;
-
- void
- close ()
- {
- in.close ();
- out.close ();
- }
- };
-
- // Create a pipe. Throw ios::failure on the underlying OS error. By default
- // both ends of the pipe are opened in the text mode. Pass the binary flag
- // to instead open them in the binary mode. Passing a mode other than none
- // or binary is illegal.
- //
- // Note that on Windows both ends of the created pipe are not inheritable.
- // In particular, the process class that uses fdpipe underneath makes the
- // appropriate end (the one being passed to the child) inheritable.
- //
- // Note that on POSIX the FD_CLOEXEC flag is set for both ends, so they get
- // automatically closed by the child process to prevent undesired behaviors
- // (such as child deadlock on read from a pipe due to the write-end leakage
- // into the child process). Opening a pipe and setting the flag is not an
- // atomic operation generally, but it is in regards to child process spawning
- // (to prevent file descriptor leakage into child processes spawned from
- // other threads). Also note that you don't need to reset the flag for a pipe
- // end being passed to the process class ctor.
- //
- LIBBUTL_EXPORT fdpipe
- fdopen_pipe (fdopen_mode = fdopen_mode::none);
-}
-
-#include <butl/fdstream.ixx>
-
-#endif // BUTL_FDSTREAM
diff --git a/butl/fdstream.cxx b/butl/fdstream.cxx
deleted file mode 100644
index 68168f3..0000000
--- a/butl/fdstream.cxx
+++ /dev/null
@@ -1,1085 +0,0 @@
-// file : butl/fdstream.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <butl/fdstream>
-
-#ifndef _WIN32
-# include <fcntl.h> // open(), O_*, fcntl()
-# include <unistd.h> // close(), read(), write(), lseek(), dup(), pipe(),
- // ssize_t, STD*_FILENO
-# include <sys/uio.h> // writev(), iovec
-# include <sys/stat.h> // S_I*
-# include <sys/types.h> // off_t
-#else
-# include <butl/win32-utility>
-
-# include <io.h> // _close(), _read(), _write(), _setmode(), _sopen(),
- // _lseek(), _dup(), _pipe(), _get_osfhandle()
-# 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 <new> // bad_alloc
-#include <limits> // numeric_limits
-#include <cassert>
-#include <cstring> // memcpy(), memmove()
-#include <exception> // uncaught_exception()
-#include <stdexcept> // invalid_argument
-#include <type_traits>
-#include <system_error>
-
-#include <butl/process-details>
-
-using namespace std;
-
-#ifdef _WIN32
-using namespace butl::win32;
-#endif
-
-namespace butl
-{
- // throw_ios_failure
- //
- template <bool v>
- [[noreturn]] static inline typename enable_if<v>::type
- throw_ios_failure (error_code e, const char* m)
- {
- // The idea here is to make an error code to be saved into failure
- // exception and to make a string returned by what() to contain the error
- // description plus an optional custom message if provided. Unfortunatelly
- // there is no way to say that the custom message is absent. Passing an
- // empty string results for libstdc++ (as of version 5.3.1) with a
- // description like this (note the ': ' prefix):
- //
- // : No such file or directory
- //
- // Note that our custom operator<<(ostream, exception) strips this prefix.
- //
- throw ios_base::failure (m != nullptr ? m : "", e);
- }
-
- template <bool v>
- [[noreturn]] static inline typename enable_if<!v>::type
- throw_ios_failure (error_code e, const char* m)
- {
- throw ios_base::failure (m != nullptr ? m : e.message ().c_str ());
- }
-
- // Throw system_error with generic_category.
- //
- [[noreturn]] static inline void
- throw_ios_failure (int errno_code, const char* m = nullptr)
- {
- error_code ec (errno_code, generic_category ());
- throw_ios_failure<is_base_of<system_error, ios_base::failure>::value> (
- ec, m);
- }
-
-#ifdef _WIN32
- // Throw system_error with system_category.
- //
- static inline void
- throw_ios_system_failure (int system_code)
- {
- // Here we work around MinGW libstdc++ that interprets Windows system error
- // codes (for example those returned by GetLastError()) as errno codes.
- //
- // Note that the resulting system_error description will have ': Success.'
- // suffix that is stripped by our custom operator<<(ostream, exception).
- //
- error_code ec (0, system_category ());
- string m (win32::error_msg (system_code));
-
- throw_ios_failure<is_base_of<system_error, ios_base::failure>::value> (
- ec, m.c_str ());
- }
-#endif
-
- // auto_fd
- //
- void auto_fd::
- close ()
- {
- if (fd_ >= 0)
- {
- bool r (fdclose (fd_));
-
- // If fdclose() failed then no reason to expect it to succeed the next
- // time.
- //
- fd_ = -1;
-
- if (!r)
- throw_ios_failure (errno);
- }
- }
-
- // fdbuf
- //
- fdbuf::
- fdbuf (auto_fd&& fd)
- {
- if (fd.get () >= 0)
- open (move (fd));
- }
-
- void fdbuf::
- open (auto_fd&& fd)
- {
- close ();
-
-#ifndef _WIN32
- int flags (fcntl (fd.get (), F_GETFL));
-
- if (flags == -1)
- throw_ios_failure (errno);
-
- non_blocking_ = (flags & O_NONBLOCK) == O_NONBLOCK;
-#endif
-
- setg (buf_, buf_, buf_);
- setp (buf_, buf_ + sizeof (buf_) - 1); // Keep space for overflow's char.
-
- fd_ = move (fd);
- }
-
- streamsize fdbuf::
- showmanyc ()
- {
- if (!is_open ())
- return -1;
-
- streamsize n (egptr () - gptr ());
-
- if (n > 0)
- return n;
-
-#ifndef _WIN32
- if (non_blocking_)
- {
- ssize_t n (read (fd_.get (), buf_, sizeof (buf_)));
-
- if (n == -1)
- {
- if (errno == EAGAIN || errno == EINTR)
- return 0;
-
- throw_ios_failure (errno);
- }
-
- if (n == 0) // EOF.
- return -1;
-
- setg (buf_, buf_, buf_ + n);
- return n;
- }
-#endif
-
- return 0;
- }
-
- fdbuf::int_type fdbuf::
- underflow ()
- {
- int_type r (traits_type::eof ());
-
- if (is_open ())
- {
- // The underflow() function interface doesn't support the non-blocking
- // semantics as it must return either the next character or EOF. In the
- // future we may implement the blocking behavior for a non-blocking file
- // descriptor.
- //
- if (non_blocking_)
- throw_ios_failure (ENOTSUP);
-
- if (gptr () < egptr () || load ())
- r = traits_type::to_int_type (*gptr ());
- }
-
- return r;
- }
-
- bool fdbuf::
- load ()
- {
- // Doesn't handle blocking mode and so should not be called.
- //
- assert (!non_blocking_);
-
-#ifndef _WIN32
- ssize_t n (read (fd_.get (), buf_, sizeof (buf_)));
-#else
- int n (_read (fd_.get (), buf_, sizeof (buf_)));
-#endif
-
- if (n == -1)
- throw_ios_failure (errno);
-
- setg (buf_, buf_, buf_ + n);
- return n != 0;
- }
-
- fdbuf::int_type fdbuf::
- overflow (int_type c)
- {
- int_type r (traits_type::eof ());
-
- if (is_open () && c != traits_type::eof ())
- {
- // The overflow() function interface doesn't support the non-blocking
- // semantics since being unable to serialize the character is supposed
- // to be an error. In the future we may implement the blocking behavior
- // for a non-blocking file descriptor.
- //
- if (non_blocking_)
- throw_ios_failure (ENOTSUP);
-
- // Store last character in the space we reserved in open(). Note
- // that pbump() doesn't do any checks.
- //
- *pptr () = traits_type::to_char_type (c);
- pbump (1);
-
- if (save ())
- r = c;
- }
-
- return r;
- }
-
- int fdbuf::
- sync ()
- {
- if (!is_open ())
- return -1;
-
- // The sync() function interface doesn't support the non-blocking
- // semantics since it should either completely sync the data or fail. In
- // the future we may implement the blocking behavior for a non-blocking
- // file descriptor.
- //
- if (non_blocking_)
- throw_ios_failure (ENOTSUP);
-
- return save () ? 0 : -1;
- }
-
- bool fdbuf::
- save ()
- {
- size_t n (pptr () - pbase ());
-
- 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_.get (), buf_, n));
-#else
- int m (_write (fd_.get (), buf_, n));
-#endif
-
- if (m == -1)
- throw_ios_failure (errno);
-
- if (n != static_cast<size_t> (m))
- return false;
-
- setp (buf_, buf_ + sizeof (buf_) - 1);
- }
-
- return true;
- }
-
- streamsize fdbuf::
- xsputn (const char_type* s, streamsize sn)
- {
- // The xsputn() function interface doesn't support the non-blocking
- // semantics since the only excuse not to fully serialize the data is
- // encountering EOF (the default behaviour is defined as a sequence of
- // sputc() calls which stops when either sn characters are written or a
- // call would have returned EOF). In the future we may implement the
- // blocking behavior for a non-blocking file descriptor.
- //
- if (non_blocking_)
- throw_ios_failure (ENOTSUP);
-
- // To avoid futher 'signed/unsigned comparison' compiler warnings.
- //
- size_t n (static_cast<size_t> (sn));
-
- // Buffer the data if there is enough space.
- //
- size_t an (epptr () - pptr ()); // Amount of free space in the buffer.
- if (n <= an)
- {
- memcpy (pptr (), s, n);
- pbump (n);
- return n;
- }
-
- size_t bn (pptr () - pbase ()); // Buffered data size.
-
-#ifndef _WIN32
-
- ssize_t r;
- if (bn > 0)
- {
- // Write both buffered and new data with a single system call.
- //
- iovec iov[2] = {{pbase (), bn}, {const_cast<char*> (s), n}};
- r = writev (fd_.get (), iov, 2);
- }
- else
- r = write (fd_.get (), s, n);
-
- if (r == -1)
- throw_ios_failure (errno);
-
- size_t m (static_cast<size_t> (r));
-
- // If the buffered data wasn't fully written then move the unwritten part
- // to the beginning of the buffer.
- //
- if (m < bn)
- {
- memmove (pbase (), pbase () + m, bn - m);
- pbump (-m); // Note that pbump() accepts negatives.
- return 0;
- }
-
- setp (buf_, buf_ + sizeof (buf_) - 1);
- return m - bn;
-
-#else
-
- // On Windows there is no writev() available so sometimes we will make two
- // system calls. Fill and flush the buffer, then try to fit the data tail
- // into the empty buffer. If the data tail is too long then just write it
- // to the file and keep the buffer empty.
- //
- // We will end up with two _write() calls if the total data size to be
- // written exceeds double the buffer size. In this case the buffer filling
- // is redundant so let's pretend there is no free space in the buffer, and
- // so buffered and new data will be written separatelly.
- //
- if (bn + n > 2 * (bn + an))
- an = 0;
- else
- {
- memcpy (pptr (), s, an);
- pbump (an);
- }
-
- // Flush the buffer.
- //
- size_t wn (bn + an);
- int r (wn > 0 ? _write (fd_.get (), buf_, wn) : 0);
-
- if (r == -1)
- throw_ios_failure (errno);
-
- size_t m (static_cast<size_t> (r));
-
- // If the buffered data wasn't fully written then move the unwritten part
- // to the beginning of the buffer.
- //
- if (m < wn)
- {
- memmove (pbase (), pbase () + m, wn - m);
- pbump (-m); // Note that pbump() accepts negatives.
- return m < bn ? 0 : m - bn;
- }
-
- setp (buf_, buf_ + sizeof (buf_) - 1);
-
- // Now 'an' holds the size of the data portion written as a part of the
- // buffer flush.
- //
- s += an;
- n -= an;
-
- // Buffer the data tail if it fits the buffer.
- //
- if (n <= static_cast<size_t> (epptr () - pbase ()))
- {
- memcpy (pbase (), s, n);
- pbump (n);
- return sn;
- }
-
- // The data tail doesn't fit the buffer so write it to the file.
- //
- r = _write (fd_.get (), s, n);
-
- if (r == -1)
- throw_ios_failure (errno);
-
- return an + r;
-#endif
- }
-
- inline static bool
- flag (fdstream_mode m, fdstream_mode flag)
- {
- return (m & flag) == flag;
- }
-
- inline static auto_fd
- mode (auto_fd fd, fdstream_mode m)
- {
- if (fd.get () >= 0 &&
- (flag (m, fdstream_mode::text) ||
- flag (m, fdstream_mode::binary) ||
- flag (m, fdstream_mode::blocking) ||
- flag (m, fdstream_mode::non_blocking)))
- fdmode (fd.get (), m);
-
- return fd;
- }
-
- // fdstream_base
- //
- fdstream_base::
- fdstream_base (auto_fd&& fd, fdstream_mode m)
- : fdstream_base (mode (move (fd), m)) // Delegate.
- {
- }
-
- 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.
- //
- 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
- //
- auto_fd
- fdopen (const char* f, fdopen_mode m, permissions p)
- {
- mode_t pf (S_IREAD | S_IWRITE | S_IEXEC);
-
-#ifdef S_IRWXG
- pf |= S_IRWXG;
-#endif
-
-#ifdef S_IRWXO
- pf |= S_IRWXO;
-#endif
-
- pf &= static_cast<mode_t> (p);
-
- // Return true if the open mode contains a specific flag.
- //
- auto mode = [m](fdopen_mode flag) -> bool {return (m & flag) == flag;};
-
- int of (0);
- bool in (mode (fdopen_mode::in));
- bool out (mode (fdopen_mode::out));
-
-#ifndef _WIN32
-
- if (in && out)
- of |= O_RDWR;
- else if (in)
- of |= O_RDONLY;
- else if (out)
- of |= O_WRONLY;
-
- if (out)
- {
- if (mode (fdopen_mode::append))
- of |= O_APPEND;
-
- if (mode (fdopen_mode::truncate))
- of |= O_TRUNC;
- }
-
- if (mode (fdopen_mode::create))
- {
- of |= O_CREAT;
-
- if (mode (fdopen_mode::exclusive))
- of |= O_EXCL;
- }
-
-#ifdef O_LARGEFILE
- of |= O_LARGEFILE;
-#endif
-
- int fd (open (f, of | O_CLOEXEC, pf));
-
-#else
-
- if (in && out)
- of |= _O_RDWR;
- else if (in)
- of |= _O_RDONLY;
- else if (out)
- of |= _O_WRONLY;
-
- if (out)
- {
- if (mode (fdopen_mode::append))
- of |= _O_APPEND;
-
- if (mode (fdopen_mode::truncate))
- of |= _O_TRUNC;
- }
-
- if (mode (fdopen_mode::create))
- {
- of |= _O_CREAT;
-
- if (mode (fdopen_mode::exclusive))
- of |= _O_EXCL;
- }
-
- of |= mode (fdopen_mode::binary) ? _O_BINARY : _O_TEXT;
-
- // According to Microsoft _sopen() should not change the permissions of an
- // existing file. However it does if we pass them (reproduced on Windows
- // XP, 7, and 8). And we must pass them if we have _O_CREATE. So we need
- // to take care of preserving the permissions ourselves. Note that Wine's
- // implementation of _sopen() works properly.
- //
- bool pass_perm (of & _O_CREAT);
-
- 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_ios_failure (EEXIST);
-
- of &= ~_O_CREAT;
- pass_perm = false;
- }
-
- // Make sure the file descriptor is not inheritable by default.
- //
- of |= _O_NOINHERIT;
-
- int fd (pass_perm
- ? _sopen (f, of, _SH_DENYNO, pf)
- : _sopen (f, of, _SH_DENYNO));
-
-#endif
-
- if (fd == -1)
- 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 auto_fd (fd);
- }
-
-#ifndef _WIN32
-
- auto_fd
- fddup (int fd)
- {
- // dup() doesn't copy FD_CLOEXEC flag, so we need to do it ourselves. Note
- // that the new descriptor can leak into child processes before we copy the
- // flag. To prevent this we will acquire the process_spawn_mutex (see
- // process-details header) prior to duplicating the descriptor. Also note
- // there is dup3() (available on Linux and FreeBSD but not on Max OS) that
- // takes flags, but it's usage tends to be hairy (need to preopen a dummy
- // file descriptor to pass as a second argument).
- //
- auto dup = [fd] () -> auto_fd
- {
- auto_fd nfd (::dup (fd));
- if (nfd.get () == -1)
- throw_ios_failure (errno);
-
- return nfd;
- };
-
- int f (fcntl (fd, F_GETFD));
- if (f == -1)
- throw_ios_failure (errno);
-
- // If the source descriptor has no FD_CLOEXEC flag set then no flag copy is
- // required (as the duplicate will have no flag by default).
- //
- if ((f & FD_CLOEXEC) == 0)
- return dup ();
-
- slock l (process_spawn_mutex);
- auto_fd nfd (dup ());
-
- f = fcntl (nfd.get (), F_GETFD);
- if (f == -1 || fcntl (nfd.get (), F_SETFD, f | FD_CLOEXEC) == -1)
- throw_ios_failure (errno);
-
- return nfd;
- }
-
- bool
- fdclose (int fd) noexcept
- {
- return close (fd) == 0;
- }
-
- auto_fd
- fdnull () noexcept
- {
- int fd (open ("/dev/null", O_RDWR | O_CLOEXEC));
-
- if (fd == -1)
- throw_ios_failure (errno);
-
- return auto_fd (fd);
- }
-
- fdstream_mode
- fdmode (int fd, fdstream_mode m)
- {
- int flags (fcntl (fd, F_GETFL));
-
- if (flags == -1)
- throw_ios_failure (errno);
-
- if (flag (m, fdstream_mode::blocking) ||
- flag (m, fdstream_mode::non_blocking))
- {
- m &= fdstream_mode::blocking | fdstream_mode::non_blocking;
-
- // Should be exactly one blocking mode flag specified.
- //
- if (m != fdstream_mode::blocking && m != fdstream_mode::non_blocking)
- throw invalid_argument ("invalid blocking mode");
-
- int new_flags (
- m == fdstream_mode::non_blocking
- ? flags | O_NONBLOCK
- : flags & ~O_NONBLOCK);
-
- if (fcntl (fd, F_SETFL, new_flags) == -1)
- throw_ios_failure (errno);
- }
-
- return fdstream_mode::binary |
- ((flags & O_NONBLOCK) == O_NONBLOCK
- ? fdstream_mode::non_blocking
- : fdstream_mode::blocking);
- }
-
- fdstream_mode
- stdin_fdmode (fdstream_mode m)
- {
- return fdmode (STDIN_FILENO, m);
- }
-
- fdstream_mode
- stdout_fdmode (fdstream_mode m)
- {
- return fdmode (STDOUT_FILENO, m);
- }
-
- fdstream_mode
- stderr_fdmode (fdstream_mode m)
- {
- return fdmode (STDERR_FILENO, m);
- }
-
- fdpipe
- fdopen_pipe (fdopen_mode m)
- {
- assert (m == fdopen_mode::none || m == fdopen_mode::binary);
-
- // Note that the pipe file descriptors can leak into child processes before
- // we set FD_CLOEXEC flag for them. To prevent this we will acquire the
- // process_spawn_mutex (see process-details header) prior to creating the
- // pipe. Also note there is pipe2() (available on Linux and FreeBSD but not
- // on Max OS) that takes flags.
- //
- slock l (process_spawn_mutex);
-
- int pd[2];
- if (pipe (pd) == -1)
- throw_ios_failure (errno);
-
- fdpipe r {auto_fd (pd[0]), auto_fd (pd[1])};
-
- for (size_t i (0); i < 2; ++i)
- {
- int f (fcntl (pd[i], F_GETFD));
- if (f == -1 || fcntl (pd[i], F_SETFD, f | FD_CLOEXEC) == -1)
- throw_ios_failure (errno);
- }
-
- return r;
- }
-
-#else
-
- auto_fd
- fddup (int fd)
- {
- // _dup() doesn't copy _O_NOINHERIT flag, so we need to do it ourselves.
- // Note that the new descriptor can leak into child processes before we
- // copy the flag. To prevent this we will acquire the process_spawn_mutex
- // (see process-details header) prior to duplicating the descriptor.
- //
- // We can not ammend file descriptors directly (nor obtain the flag value),
- // so need to resolve them to Windows HANDLE first. Such handles are closed
- // either when CloseHandle() is called for them or when _close() is called
- // for the associated file descriptors. Make sure that either the original
- // file descriptor or the resulting HANDLE is closed but not both of them.
- //
- auto handle = [] (int fd) -> HANDLE
- {
- HANDLE h (reinterpret_cast<HANDLE> (_get_osfhandle (fd)));
- if (h == INVALID_HANDLE_VALUE)
- throw_ios_failure (errno); // EBADF (POSIX value).
-
- return h;
- };
-
- auto dup = [fd] () -> auto_fd
- {
- auto_fd nfd (_dup (fd));
- if (nfd.get () == -1)
- throw_ios_failure (errno);
-
- return nfd;
- };
-
- DWORD f;
- if (!GetHandleInformation (handle (fd), &f))
- throw_ios_system_failure (GetLastError ());
-
- // If the source handle is inheritable then no flag copy is required (as
- // the duplicate handle will be inheritable by default).
- //
- if (f & HANDLE_FLAG_INHERIT)
- return dup ();
-
- slock l (process_spawn_mutex);
-
- auto_fd nfd (dup ());
- if (!SetHandleInformation (handle (nfd.get ()), HANDLE_FLAG_INHERIT, 0))
- throw_ios_system_failure (GetLastError ());
-
- return nfd;
- }
-
- bool
- fdclose (int fd) noexcept
- {
- return _close (fd) == 0;
- }
-
- auto_fd
- fdnull (bool temp) noexcept
- {
- // No need to translate \r\n before sending it to void.
- //
- if (!temp)
- {
- int fd (_sopen ("nul", _O_RDWR | _O_BINARY | _O_NOINHERIT, _SH_DENYNO));
-
- if (fd == -1)
- throw_ios_failure (errno);
-
- return auto_fd (fd);
- }
-
- try
- {
- // We could probably implement a Windows-specific version of getting
- // the temporary file that avoid any allocations and exceptions.
- //
- path p (path::temp_path ("null")); // Can throw.
- int fd (_sopen (p.string ().c_str (),
- (_O_CREAT |
- _O_RDWR |
- _O_BINARY | // Don't translate.
- _O_TEMPORARY | // Remove on close.
- _O_SHORT_LIVED | // Don't flush to disk.
- _O_NOINHERIT), // Don't inherit by child process.
- _SH_DENYNO,
- _S_IREAD | _S_IWRITE));
-
- if (fd == -1)
- throw_ios_failure (errno);
-
- return auto_fd (fd);
- }
- catch (const bad_alloc&)
- {
- throw_ios_failure (ENOMEM);
- }
- catch (const system_error& e)
- {
- // Make sure that the error denotes errno portable code.
- //
- assert (e.code ().category () == generic_category ());
-
- throw_ios_failure (e.code ().value ());
- }
- }
-
- fdstream_mode
- fdmode (int fd, fdstream_mode m)
- {
- m &= fdstream_mode::text | fdstream_mode::binary;
-
- // Should be exactly one translation flag specified.
- //
- // It would have been natural not to change translation mode if none of
- // text or binary flags are passed. Unfortunatelly there is no (easy) way
- // to obtain the current mode for the file descriptor without setting a
- // new one. This is why not specifying one of the modes is an error.
- //
- if (m != fdstream_mode::binary && m != fdstream_mode::text)
- throw invalid_argument ("invalid translation mode");
-
- // Note that _setmode() preserves the _O_NOINHERIT flag value.
- //
- int r (_setmode (fd, m == fdstream_mode::binary ? _O_BINARY : _O_TEXT));
- if (r == -1)
- throw_ios_failure (errno); // EBADF or EINVAL (POSIX values).
-
- return fdstream_mode::blocking |
- ((r & _O_BINARY) == _O_BINARY
- ? fdstream_mode::binary
- : fdstream_mode::text);
- }
-
- fdstream_mode
- stdin_fdmode (fdstream_mode m)
- {
- int fd (_fileno (stdin));
- if (fd == -1)
- throw_ios_failure (errno);
-
- return fdmode (fd, m);
- }
-
- fdstream_mode
- stdout_fdmode (fdstream_mode m)
- {
- int fd (_fileno (stdout));
- if (fd == -1)
- throw_ios_failure (errno);
-
- return fdmode (fd, m);
- }
-
- fdstream_mode
- stderr_fdmode (fdstream_mode m)
- {
- int fd (_fileno (stderr));
- if (fd == -1)
- throw_ios_failure (errno);
-
- return fdmode (fd, m);
- }
-
- fdpipe
- fdopen_pipe (fdopen_mode m)
- {
- assert (m == fdopen_mode::none || m == fdopen_mode::binary);
-
- int pd[2];
- if (_pipe (
- pd,
- 64 * 1024, // Set buffer size to 64K.
- _O_NOINHERIT | (m == fdopen_mode::none ? _O_TEXT : _O_BINARY)) == -1)
- throw_ios_failure (errno);
-
- return {auto_fd (pd[0]), auto_fd (pd[1])};
- }
-#endif
-}
diff --git a/butl/fdstream.ixx b/butl/fdstream.ixx
deleted file mode 100644
index 884d190..0000000
--- a/butl/fdstream.ixx
+++ /dev/null
@@ -1,266 +0,0 @@
-// file : butl/fdstream.ixx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <cassert>
-
-namespace butl
-{
- // auto_fd
- //
- inline void auto_fd::
- reset (int fd) noexcept
- {
- if (fd_ >= 0)
- fdclose (fd_); // Don't check for an error as not much we can do here.
-
- fd_ = fd;
- }
-
- inline auto_fd& auto_fd::
- operator= (auto_fd&& fd) noexcept
- {
- reset (fd.release ());
- return *this;
- }
-
- inline auto_fd::
- ~auto_fd () noexcept
- {
- reset ();
- }
-
- // fdbuf
- //
- inline auto_fd fdbuf::
- release ()
- {
- return std::move (fd_);
- }
-
- // ifdstream
- //
- inline ifdstream::
- ifdstream (auto_fd&& fd, iostate e)
- : fdstream_base (std::move (fd)), std::istream (&buf_)
- {
- assert (e & badbit);
- exceptions (e);
- }
-
- inline ifdstream::
- ifdstream (iostate e)
- : ifdstream (auto_fd (), e) // Delegate.
- {
- }
-
- inline ifdstream::
- ifdstream (auto_fd&& fd, fdstream_mode m, iostate e)
- : fdstream_base (std::move (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);
- }
-
- inline auto_fd ifdstream::
- release ()
- {
- return buf_.release ();
- }
-
- // ofdstream
- //
- inline ofdstream::
- ofdstream (auto_fd&& fd, iostate e)
- : fdstream_base (std::move (fd)), std::ostream (&buf_)
- {
- assert (e & badbit);
- exceptions (e);
- }
-
- inline ofdstream::
- ofdstream (iostate e)
- : ofdstream (auto_fd (), e) // Delegate.
- {
- }
-
- inline ofdstream::
- ofdstream (auto_fd&& fd, fdstream_mode m, iostate e)
- : fdstream_base (std::move (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);
- }
-
- inline auto_fd ofdstream::
- release ()
- {
- if (is_open ())
- flush ();
-
- return buf_.release ();
- }
-
- // fdopen()
- //
- inline auto_fd
- fdopen (const std::string& f, fdopen_mode m, permissions p)
- {
- return fdopen (f.c_str (), m, p);
- }
-
- inline auto_fd
- 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;}
-
- inline fdopen_mode
- operator&= (fdopen_mode& x, fdopen_mode y)
- {
- return x = static_cast<fdopen_mode> (
- static_cast<std::uint16_t> (x) &
- static_cast<std::uint16_t> (y));
- }
-
- inline fdopen_mode
- operator|= (fdopen_mode& x, fdopen_mode y)
- {
- return x = static_cast<fdopen_mode> (
- static_cast<std::uint16_t> (x) |
- static_cast<std::uint16_t> (y));
- }
-}
diff --git a/butl/filesystem b/butl/filesystem
deleted file mode 100644
index 0f0c71c..0000000
--- a/butl/filesystem
+++ /dev/null
@@ -1,567 +0,0 @@
-// file : butl/filesystem -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_FILESYSTEM
-#define BUTL_FILESYSTEM
-
-#ifndef _WIN32
-# include <dirent.h> // DIR
-#else
-# include <stddef.h> // intptr_t
-#endif
-
-// VC's sys/types.h header file doesn't define mode_t type. So let's define it
-// ourselves according to the POSIX specification.
-//
-#ifndef _MSC_VER
-# include <sys/types.h> // mode_t
-#else
- typedef int mode_t;
-#endif
-
-#include <cstddef> // ptrdiff_t
-#include <cstdint> // uint16_t
-#include <utility> // move(), pair
-#include <iterator>
-#include <functional>
-
-#include <butl/export>
-
-#include <butl/path>
-#include <butl/timestamp>
-
-namespace butl
-{
- // Return true if the path is to an existing regular file. Note that by
- // default this function follows symlinks.
- //
- LIBBUTL_EXPORT bool
- file_exists (const char*, bool follow_symlinks = true);
-
- inline bool
- file_exists (const path& p, bool fs = true) {
- return file_exists (p.string ().c_str (), fs);}
-
- // Return true if the path is to an existing directory. Note that this
- // function follows symlinks.
- //
- LIBBUTL_EXPORT bool
- dir_exists (const char*);
-
- inline bool
- dir_exists (const path& p) {return dir_exists (p.string ().c_str ());}
-
- // Return true if the path is to an existing file system entry. Note that by
- // default this function doesn't follow symlinks.
- //
- LIBBUTL_EXPORT bool
- entry_exists (const char*, bool follow_symlinks = false);
-
- inline bool
- entry_exists (const path& p, bool fs = false) {
- return entry_exists (p.string ().c_str (), fs);}
-
- // Filesystem entry type.
- //
- enum class entry_type
- {
- unknown,
- regular,
- directory,
- symlink,
- other
- };
-
- // Return a flag indicating if the path is to an existing file system entry
- // and its type if so. Note that by default this function doesn't follow
- // symlinks.
- //
- LIBBUTL_EXPORT std::pair<bool, entry_type>
- path_entry (const char*, bool follow_symlinks = false);
-
- inline std::pair<bool, entry_type>
- path_entry (const path& p, bool fs = false) {
- return path_entry (p.string ().c_str (), fs);}
-
- // Return true if the directory is empty. Note that the path must exist
- // and be a directory.
- //
- LIBBUTL_EXPORT bool
- dir_empty (const dir_path&);
-
- // Try to create a directory unless it already exists. If you expect
- // the directory to exist and performance is important, then you
- // should first call dir_exists() above since that's what this
- // implementation will do to make sure the path is actually a
- // directory.
- //
- // You should also probably use the default mode 0777 and let the
- // umask mechanism adjust it to the user's preferences.
- //
- // Errors are reported by throwing std::system_error.
- //
- enum class mkdir_status {success, already_exists};
-
- LIBBUTL_EXPORT mkdir_status
- try_mkdir (const dir_path&, mode_t = 0777);
-
- // The '-p' version of the above (i.e., it creates the parent
- // directories if necessary).
- //
- LIBBUTL_EXPORT mkdir_status
- try_mkdir_p (const dir_path&, mode_t = 0777);
-
- // Try to remove the directory returning not_exist if it does not exist
- // and not_empty if it is not empty. Unless ignore_error is true, all
- // other errors are reported by throwing std::system_error.
- //
- enum class rmdir_status {success, not_exist, not_empty};
-
- LIBBUTL_EXPORT rmdir_status
- try_rmdir (const dir_path&, bool ignore_error = false);
-
- // The '-r' (recursive) version of the above. Note that it will
- // never return not_empty.
- //
- LIBBUTL_EXPORT rmdir_status
- try_rmdir_r (const dir_path&, bool ignore_error = false);
-
- // As above but throws rather than returns not_exist if the directory
- // does not exist (unless ignore_error is true), so check before calling.
- // If the second argument is false, then the directory itself is not removed.
- //
- LIBBUTL_EXPORT void
- rmdir_r (const dir_path&, bool dir = true, bool ignore_error = false);
-
- // Try to remove the file (or symlinks) returning not_exist if
- // it does not exist. Unless ignore_error is true, all other
- // errors are reported by throwing std::system_error.
- //
- enum class rmfile_status {success, not_exist};
-
- LIBBUTL_EXPORT rmfile_status
- try_rmfile (const path&, bool ignore_error = false);
-
- // Automatically try to remove the path on destruction unless cancelled.
- // Since the non-cancelled destruction will normally happen as a result
- // of an exception, the failure to remove the path is silently ignored.
- //
- template <typename P>
- struct auto_rm
- {
- explicit
- auto_rm (P p = P ()): path_ (std::move (p)) {}
-
- void
- cancel () {path_ = P ();}
-
- const P&
- path () const {return path_;}
-
- // Movable-only type. Move-assignment cancels the lhs object.
- //
- auto_rm (auto_rm&&);
- auto_rm& operator= (auto_rm&&);
- auto_rm (const auto_rm&) = delete;
- auto_rm& operator= (const auto_rm&) = delete;
-
- ~auto_rm ();
-
- private:
- P path_;
- };
-
- using auto_rmfile = auto_rm<path>;
- using auto_rmdir = auto_rm<dir_path>; // Note: recursive (rm_r).
-
- // Create a symbolic link to a file (default) or directory (third argument
- // is true). Throw std::system_error on failures.
- //
- // Note that Windows symlinks are currently not supported.
- //
- LIBBUTL_EXPORT void
- mksymlink (const path& target, const path& link, bool dir = false);
-
- // Create a symbolic link to a directory. Throw std::system_error on
- // failures.
- //
- inline void
- mksymlink (const dir_path& target, const dir_path& link)
- {
- mksymlink (target, link, true);
- }
-
- // Create a hard link to a file (default) or directory (third argument is
- // true). Throw std::system_error on failures.
- //
- // Note that on Linix, FreeBSD and some other platforms the target can not
- // be a directory. While Windows support directories (via junktions), this
- // is currently not implemented.
- //
- LIBBUTL_EXPORT void
- mkhardlink (const path& target, const path& link, bool dir = false);
-
- // Create a hard link to a directory. Throw std::system_error on failures.
- //
- inline void
- mkhardlink (const dir_path& target, const dir_path& link)
- {
- mkhardlink (target, link, true);
- }
-
- // File copy flags.
- //
- enum class cpflags: std::uint16_t
- {
- overwrite_content = 0x1,
- overwrite_permissions = 0x2,
-
- none = 0
- };
-
- inline cpflags operator& (cpflags, cpflags);
- inline cpflags operator| (cpflags, cpflags);
- inline cpflags operator&= (cpflags&, cpflags);
- inline cpflags operator|= (cpflags&, cpflags);
-
- // Copy a regular file, including its permissions. Throw std::system_error
- // on failure. Fail if the destination file exists and the overwrite_content
- // flag is not set. Leave permissions of an existing destination file intact
- // unless the overwrite_permissions flag is set. Delete incomplete copies
- // before throwing.
- //
- // Note that in case of overwriting, the existing destination file gets
- // truncated (not deleted) prior to being overwritten. As a side-effect,
- // hard link to the destination file will still reference the same file
- // system node after the copy.
- //
- // Also note that if the overwrite_content flag is not set and the
- // destination is a dangling symbolic link, then this function will still
- // fail.
- //
- LIBBUTL_EXPORT void
- cpfile (const path& from, const path& to, cpflags = cpflags::none);
-
- // Copy a regular file into (inside) an existing directory.
- //
- inline void
- cpfile_into (const path& from,
- const dir_path& into,
- cpflags fl = cpflags::none)
- {
- cpfile (from, into / from.leaf (), fl);
- }
-
- // Rename a filesystem entry (file, symlink, or directory). Throw
- // std::system_error on failure.
- //
- // If the source path refers to a directory, then the destination path must
- // either not exist, or refer to an empty directory. If the source path
- // refers to an entry that is not a directory, then the destination path must
- // not exist or not refer to a directory.
- //
- // If the source path refers to a symlink, then the link is renamed. If the
- // destination path refers to a symlink, then the link will be overwritten.
- //
- // If the source and destination paths are on different file systems (or
- // different drives on Windows) and the underlying OS does not support move
- // for the source entry, then fail unless the source paths refers to a file
- // or a file symlink. In this case fall back to copying the source file
- // (content, permissions, access and modification times) and removing the
- // source entry afterwards.
- //
- // Note that the operation is atomic only on POSIX, only if source and
- // destination paths are on the same file system, and only if the
- // overwrite_content flag is specified.
- //
- LIBBUTL_EXPORT void
- mventry (const path& from,
- const path& to,
- cpflags = cpflags::overwrite_permissions);
-
- // Move a filesystem entry into (inside) an existing directory.
- //
- inline void
- mventry_into (const path& from,
- const dir_path& into,
- cpflags f = cpflags::overwrite_permissions)
- {
- mventry (from, into / from.leaf (), f);
- }
-
- // Raname file or file symlink.
- //
- inline void
- mvfile (const path& from,
- const path& to,
- cpflags f = cpflags::overwrite_permissions)
- {
- mventry (from, to, f);
- }
-
- inline void
- mvfile_into (const path& from,
- const dir_path& into,
- cpflags f = cpflags::overwrite_permissions)
- {
- mventry_into (from, into, f);
- }
-
- // Raname directory or directory symlink.
- //
- inline void
- mvdir (const dir_path& from,
- const dir_path& to,
- cpflags f = cpflags::overwrite_permissions)
- {
- mventry (from, to, f);
- }
-
- inline void
- mvdir_into (const path& from,
- const dir_path& into,
- cpflags f = cpflags::overwrite_permissions)
- {
- mventry_into (from, into, f);
- }
-
- // Return timestamp_nonexistent if the entry at the specified path
- // does not exist or is not a path. All other errors are reported
- // by throwing std::system_error. Note that this function resolves
- // symlinks.
- //
- LIBBUTL_EXPORT timestamp
- file_mtime (const char*);
-
- inline timestamp
- file_mtime (const path& p) {return file_mtime (p.string ().c_str ());}
-
- // Path permissions.
- //
- enum class permissions: std::uint16_t
- {
- // Note: matching POSIX values.
- //
- xo = 0001,
- wo = 0002,
- ro = 0004,
-
- xg = 0010,
- wg = 0020,
- rg = 0040,
-
- xu = 0100,
- wu = 0200,
- ru = 0400,
-
- none = 0
- };
-
- inline permissions operator& (permissions, permissions);
- inline permissions operator| (permissions, permissions);
- inline permissions operator&= (permissions&, permissions);
- inline permissions operator|= (permissions&, permissions);
-
- // Get path permissions. Throw std::system_error on failure. Note that this
- // function resolves symlinks.
- //
- LIBBUTL_EXPORT permissions
- path_permissions (const path&);
-
- // Set path permissions. Throw std::system_error on failure. Note that this
- // function resolves symlinks.
- //
- LIBBUTL_EXPORT void
- path_permissions (const path&, permissions);
-
- // Directory entry iteration.
- //
- class LIBBUTL_EXPORT dir_entry
- {
- public:
- typedef butl::path path_type;
-
- // Symlink target type in case of the symlink, ltype() otherwise.
- //
- entry_type
- type () const;
-
- entry_type
- ltype () const;
-
- // Entry path (excluding the base). To get the full path, do
- // base () / path ().
- //
- const path_type&
- path () const {return p_;}
-
- const dir_path&
- base () const {return b_;}
-
- dir_entry () = default;
- dir_entry (entry_type t, path_type p, dir_path b)
- : t_ (t), p_ (std::move (p)), b_ (std::move (b)) {}
-
- private:
- entry_type
- type (bool link) const;
-
- private:
- friend class dir_iterator;
-
- mutable entry_type t_ = entry_type::unknown; // Lazy evaluation.
- mutable entry_type lt_ = entry_type::unknown; // Lazy evaluation.
- path_type p_;
- dir_path b_;
- };
-
- class LIBBUTL_EXPORT dir_iterator
- {
- public:
- typedef dir_entry value_type;
- typedef const dir_entry* pointer;
- typedef const dir_entry& reference;
- typedef std::ptrdiff_t difference_type;
- typedef std::input_iterator_tag iterator_category;
-
- ~dir_iterator ();
- dir_iterator () = default;
-
- explicit
- dir_iterator (const dir_path&);
-
- dir_iterator (const dir_iterator&) = delete;
- dir_iterator& operator= (const dir_iterator&) = delete;
-
- dir_iterator (dir_iterator&& x);
- dir_iterator& operator= (dir_iterator&&);
-
- dir_iterator& operator++ () {next (); return *this;}
-
- reference operator* () const {return e_;}
- pointer operator-> () const {return &e_;}
-
- friend bool operator== (const dir_iterator&, const dir_iterator&);
- friend bool operator!= (const dir_iterator&, const dir_iterator&);
-
- private:
- void
- next ();
-
- private:
- dir_entry e_;
-
-#ifndef _WIN32
- DIR* h_ = nullptr;
-#else
- intptr_t h_ = -1;
-#endif
- };
-
- // Range-based for loop support.
- //
- // for (const auto& de: dir_iterator (dir_path ("/tmp"))) ...
- //
- // Note that the "range" (which is the "begin" iterator), is no
- // longer usable. In other words, don't do this:
- //
- // dir_iterator i (...);
- // for (...: i) ...
- // ++i; // Invalid.
- //
- inline dir_iterator begin (dir_iterator&);
- inline dir_iterator end (const dir_iterator&);
-
- // Wildcard pattern match and search (aka glob).
- //
-
- // Return true if name matches pattern. Both must be single path components,
- // possibly with a trailing directory separator to indicate a directory.
- //
- // If the pattern ends with a directory separator, then it only matches a
- // directory name (i.e., ends with a directory separator, but potentially
- // different). Otherwise, it only matches a non-directory name (no trailing
- // directory separator).
- //
- // Currently the following wildcard characters are supported:
- //
- // * - match any number of characters (including zero)
- // ? - match any single character
- //
- LIBBUTL_EXPORT bool
- path_match (const std::string& pattern, const std::string& name);
-
- // Search for paths matching the pattern calling the specified function for
- // each matching path (see below for details).
- //
- // If the pattern is relative, then search in the start directory. If the
- // start directory is empty, then search in the current working directory.
- // Searching in non-existent directories is not an error. Throw
- // std::system_error in case of a failure (insufficient permissions, etc).
- //
- // The pattern may contain multiple components that include wildcards. On
- // Windows the drive letter may not be a wildcard.
- //
- // In addition to the wildcard characters listed in path_match(),
- // path_search() also recognizes the ** and *** wildcard sequences. If a
- // path component contains **, then it is matched just like * but in all the
- // subdirectories, recursively. The *** wildcard behaves like ** but also
- // matches the start directory itself.
- //
- // So, for example, foo/bar-**.txt will return all the files matching the
- // bar-*.txt pattern in all the subdirectoris of foo/. And foo/f***/ will
- // return all the subdirectories matching the f*/ pattern plus foo/ itself.
- //
- // Note that having multiple recursive components in the pattern we can end
- // up with calling func() multiple times (once per such a component) for the
- // same path. For example the search with pattern f***/b**/ starting in
- // directory foo, that has the foo/fox/box/ structure, will result in
- // calling func(foo/fox/box/) twice: first time for being a child of fox/,
- // second time for being a child of foo/.
- //
- // The callback function is called for both intermediate matches (interm is
- // true) and final matches (interm is false). Pattern is what matched the
- // last component in the path and is empty if the last component is not a
- // pattern (final match only; say as in */foo.txt).
- //
- // If the callback function returns false for an intermediate path, then no
- // further search is performed at or below this path. If false is returned
- // for a final match, then the entire search is stopped.
- //
- // The path can be moved for the final match or for an intermediate match
- // but only if false is returned.
- //
- // As an example, consider pattern f*/bar/b*/*.txt and path
- // foo/bar/baz/x.txt. The sequence of calls in this case will be:
- //
- // (foo/, f*/, true)
- // (foo/bar/baz/, b*/, true)
- // (foo/bar/baz/x.txt, *.txt, false)
- //
- // If the pattern contains a recursive wildcard, then the callback function
- // can be called for the same directory twice: first time as an intermediate
- // match with */ pattern to decide if to recursively traverse the directory,
- // and the second time if the directory matches the pattern component (either
- // as an intermediate or a final match). As an example, consider pattern
- // b**/c* and directory tree a/b/c/. The sequence of calls in this case will
- // be:
- //
- // (a/, */, true)
- // (a/b/, */ true)
- // (a/b/c/, */, true)
- // (a/b/, b*/, true)
- // (a/b/c/, c*/, false)
- //
- LIBBUTL_EXPORT void
- path_search (const path& pattern,
- const std::function<bool (path&&,
- const std::string& pattern,
- bool interm)>&,
- const dir_path& start = dir_path (),
- bool follow_symlinks = true);
-}
-
-#include <butl/filesystem.ixx>
-
-#endif // BUTL_FILESYSTEM
diff --git a/butl/filesystem.cxx b/butl/filesystem.cxx
deleted file mode 100644
index d6e1f63..0000000
--- a/butl/filesystem.cxx
+++ /dev/null
@@ -1,1396 +0,0 @@
-// file : butl/filesystem.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <butl/filesystem>
-
-#ifndef _WIN32
-# include <stdio.h> // rename()
-# include <dirent.h> // struct dirent, *dir()
-# include <unistd.h> // symlink(), link(), stat(), rmdir(), unlink()
-# include <sys/time.h> // utimes()
-# include <sys/types.h> // stat
-# include <sys/stat.h> // stat(), lstat(), S_I*, mkdir(), chmod()
-#else
-# include <butl/win32-utility>
-
-# include <io.h> // _find*(), _unlink(), _chmod()
-# include <direct.h> // _mkdir(), _rmdir()
-# include <sys/types.h> // _stat
-# include <sys/stat.h> // _stat(), S_I*
-
-# ifdef _MSC_VER // Unlikely to be fixed in newer versions.
-# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
-# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
-# endif
-
-# include <butl/utility> // lcase()
-#endif
-
-#include <errno.h> // errno, E*
-
-#include <string>
-#include <vector>
-#include <memory> // unique_ptr
-#include <utility> // pair
-#include <iterator> // reverse_iterator
-#include <system_error>
-
-#include <butl/path>
-#include <butl/utility> // throw_generic_error()
-#include <butl/fdstream>
-#include <butl/small-vector>
-
-using namespace std;
-
-namespace butl
-{
- bool
- file_exists (const char* p, bool fl)
- {
- auto pe (path_entry (p, fl));
- return pe.first && (pe.second == entry_type::regular ||
- (!fl && pe.second == entry_type::symlink));
- }
-
- bool
- entry_exists (const char* p, bool fl)
- {
- return path_entry (p, fl).first;
- }
-
- bool
- dir_exists (const char* p)
- {
- auto pe (path_entry (p, true));
- return pe.first && pe.second == entry_type::directory;
- }
-
-#ifndef _WIN32
- pair<bool, entry_type>
- path_entry (const char* p, bool fl)
- {
- struct stat s;
- if ((fl ? stat (p, &s) : lstat (p, &s)) != 0)
- {
- if (errno == ENOENT || errno == ENOTDIR)
- return make_pair (false, entry_type::unknown);
- else
- throw_generic_error (errno);
- }
-
- auto m (s.st_mode);
- entry_type t (entry_type::unknown);
-
- if (S_ISREG (m))
- t = entry_type::regular;
- else if (S_ISDIR (m))
- t = entry_type::directory;
- else if (S_ISLNK (m))
- t = entry_type::symlink;
- else if (S_ISBLK (m) || S_ISCHR (m) || S_ISFIFO (m) || S_ISSOCK (m))
- t = entry_type::other;
-
- return make_pair (true, t);
- }
-#else
- pair<bool, entry_type>
- path_entry (const char* p, bool)
- {
- // A path like 'C:', while being a root path in our terminology, is not as
- // such for Windows, that maintains current directory for each drive, and
- // so C: means the current directory on the drive C. This is not what we
- // mean here, so need to append the trailing directory separator in such a
- // case.
- //
- string d;
- if (path::traits::root (p, string::traits_type::length (p)))
- {
- d = p;
- d += path::traits::directory_separator;
- p = d.c_str ();
- }
-
- DWORD attr (GetFileAttributesA (p));
- if (attr == INVALID_FILE_ATTRIBUTES) // Presumably not exists.
- return make_pair (false, entry_type::unknown);
-
- entry_type t (entry_type::unknown);
-
- // S_ISLNK/S_IFDIR are not defined for Win32 but it does have symlinks.
- // We will consider symlink entry to be of the unknown type. Note that
- // S_ISREG() and S_ISDIR() return as they would do for a symlink target.
- //
- if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
- {
- struct _stat s;
-
- if (_stat (p, &s) != 0)
- {
- if (errno == ENOENT || errno == ENOTDIR)
- return make_pair (false, entry_type::unknown);
- else
- throw_generic_error (errno);
- }
-
- auto m (s.st_mode);
-
- if (S_ISREG (m))
- t = entry_type::regular;
- else if (S_ISDIR (m))
- t = entry_type::directory;
- //
- //else if (S_ISLNK (m))
- // t = entry_type::symlink;
- }
-
- return make_pair (true, t);
- }
-#endif
-
- mkdir_status
-#ifndef _WIN32
- try_mkdir (const dir_path& p, mode_t m)
- {
- if (mkdir (p.string ().c_str (), m) != 0)
-#else
- try_mkdir (const dir_path& p, mode_t)
- {
- if (_mkdir (p.string ().c_str ()) != 0)
-#endif
- {
- int e (errno);
-
- // EEXIST means the path already exists but not necessarily as
- // a directory.
- //
- if (e == EEXIST && dir_exists (p))
- return mkdir_status::already_exists;
- else
- throw_generic_error (e);
- }
-
- return mkdir_status::success;
- }
-
- mkdir_status
- try_mkdir_p (const dir_path& p, mode_t m)
- {
- if (!p.root ())
- {
- dir_path d (p.directory ());
-
- if (!d.empty () && !dir_exists (d))
- try_mkdir_p (d, m);
- }
-
- return try_mkdir (p, m);
- }
-
- rmdir_status
- try_rmdir (const dir_path& p, bool ignore_error)
- {
- rmdir_status r (rmdir_status::success);
-
-#ifndef _WIN32
- if (rmdir (p.string ().c_str ()) != 0)
-#else
- if (_rmdir (p.string ().c_str ()) != 0)
-#endif
- {
- if (errno == ENOENT)
- r = rmdir_status::not_exist;
- else if (errno == ENOTEMPTY || errno == EEXIST)
- r = rmdir_status::not_empty;
- else if (!ignore_error)
- throw_generic_error (errno);
- }
-
- return r;
- }
-
- void
- rmdir_r (const dir_path& p, bool dir, bool ignore_error)
- {
- // An nftw()-based implementation (for platforms that support it)
- // might be a faster way.
- //
- for (const dir_entry& de: dir_iterator (p))
- {
- path ep (p / de.path ()); //@@ Would be good to reuse the buffer.
-
- if (de.ltype () == entry_type::directory)
- rmdir_r (path_cast<dir_path> (move (ep)), true, ignore_error);
- else
- try_rmfile (ep, ignore_error);
- }
-
- if (dir)
- {
- rmdir_status r (try_rmdir (p));
-
- if (r != rmdir_status::success && !ignore_error)
- throw_generic_error (r == rmdir_status::not_empty
- ? ENOTEMPTY
- : ENOENT);
- }
- }
-
- rmfile_status
- try_rmfile (const path& p, bool ignore_error)
- {
- rmfile_status r (rmfile_status::success);
-
-#ifndef _WIN32
- if (unlink (p.string ().c_str ()) != 0)
-#else
- if (_unlink (p.string ().c_str ()) != 0)
-#endif
- {
- // Strangely on Linux unlink() removes a dangling symlink but returns
- // ENOENT.
- //
- if (errno == ENOENT || errno == ENOTDIR)
- r = rmfile_status::not_exist;
- else if (!ignore_error)
- throw_generic_error (errno);
- }
-
- return r;
- }
-
-#ifndef _WIN32
- void
- mksymlink (const path& target, const path& link, bool)
- {
- if (symlink (target.string ().c_str (), link.string ().c_str ()) == -1)
- throw_generic_error (errno);
- }
-
- void
- mkhardlink (const path& target, const path& link, bool)
- {
- if (::link (target.string ().c_str (), link.string ().c_str ()) == -1)
- throw_generic_error (errno);
- }
-
-#else
-
- void
- mksymlink (const path&, const path&, bool)
- {
- throw_generic_error (ENOSYS, "symlinks not supported");
- }
-
- void
- mkhardlink (const path& target, const path& link, bool dir)
- {
- if (!dir)
- {
- if (!CreateHardLinkA (link.string ().c_str (),
- target.string ().c_str (),
- nullptr))
- throw_system_error (GetLastError ());
- }
- else
- throw_generic_error (ENOSYS, "directory hard links not supported");
- }
-#endif
-
- // For I/O operations cpfile() can throw ios_base::failure exception that is
- // not derived from system_error for old versions of g++ (as of 4.9). From
- // the other hand cpfile() must throw system_error only. Let's catch
- // ios_base::failure and rethrow as system_error in such a case.
- //
- template <bool v>
- static inline typename enable_if<v>::type
- cpfile (const path& from, const path& to,
- cpflags fl,
- permissions perm,
- auto_rmfile& rm)
- {
- ifdstream ifs (from, fdopen_mode::binary);
-
- fdopen_mode om (fdopen_mode::out |
- fdopen_mode::truncate |
- fdopen_mode::create |
- fdopen_mode::binary);
-
- if ((fl & cpflags::overwrite_content) != cpflags::overwrite_content)
- om |= fdopen_mode::exclusive;
-
- ofdstream ofs (fdopen (to, om, perm));
-
- rm = auto_rmfile (to);
-
- // Throws ios::failure on fdbuf read/write failures.
- //
- // Note that the eof check is important: if the stream is at eof (empty
- // file) then this write will fail.
- //
- if (ifs.peek () != ifdstream::traits_type::eof ())
- ofs << ifs.rdbuf ();
-
- ifs.close (); // Throws ios::failure on failure.
- ofs.close (); // Throws ios::failure on flush/close failure.
- }
-
- template <bool v>
- static inline typename enable_if<!v>::type
- cpfile (const path& from, const path& to,
- cpflags fl,
- permissions perm,
- auto_rmfile& rm)
- {
- try
- {
- cpfile<true> (from, to, fl, perm, rm);
- }
- catch (const ios_base::failure& e)
- {
- // While we try to preserve the original error information, we can not
- // make the description to be exactly the same, for example
- //
- // Is a directory
- //
- // becomes
- //
- // Is a directory: Input/output error
- //
- // Note that our custom operator<<(ostream, exception) doesn't strip this
- // suffix. This is a temporary code after all.
- //
- throw_generic_error (EIO, e.what ());
- }
- }
-
- void
- cpfile (const path& from, const path& to, cpflags fl)
- {
- permissions perm (path_permissions (from));
- auto_rmfile rm;
-
- cpfile<is_base_of<system_error, ios_base::failure>::value> (
- from, to, fl, perm, rm);
-
- if ((fl & cpflags::overwrite_permissions) ==
- cpflags::overwrite_permissions)
- path_permissions (to, perm);
-
- rm.cancel ();
- }
-
- // Figuring out whether we have the nanoseconds in struct stat. Some
- // platforms (e.g., FreeBSD), may provide some "compatibility" #define's,
- // so use the second argument to not end up with the same signatures.
- //
- template <typename S>
- inline constexpr auto
- mnsec (const S* s, bool) -> decltype(s->st_mtim.tv_nsec)
- {
- return s->st_mtim.tv_nsec; // POSIX (GNU/Linux, Solaris).
- }
-
- template <typename S>
- inline constexpr auto
- mnsec (const S* s, int) -> decltype(s->st_mtimespec.tv_nsec)
- {
- return s->st_mtimespec.tv_nsec; // *BSD, MacOS.
- }
-
- template <typename S>
- inline constexpr auto
- mnsec (const S* s, float) -> decltype(s->st_mtime_n)
- {
- return s->st_mtime_n; // AIX 5.2 and later.
- }
-
- // Things are not going to end up well with only seconds resolution so
- // let's make it a compile error.
- //
- // template <typename S>
- // inline constexpr int
- // mnsec (...) {return 0;}
-
- template <typename S>
- inline constexpr auto
- ansec (const S* s, bool) -> decltype(s->st_atim.tv_nsec)
- {
- return s->st_atim.tv_nsec; // POSIX (GNU/Linux, Solaris).
- }
-
- template <typename S>
- inline constexpr auto
- ansec (const S* s, int) -> decltype(s->st_atimespec.tv_nsec)
- {
- return s->st_atimespec.tv_nsec; // *BSD, MacOS.
- }
-
- template <typename S>
- inline constexpr auto
- ansec (const S* s, float) -> decltype(s->st_atime_n)
- {
- return s->st_atime_n; // AIX 5.2 and later.
- }
-
- // template <typename S>
- // inline constexpr int
- // ansec (...) {return 0;}
-
- void
- mventry (const path& from, const path& to, cpflags fl)
- {
- assert ((fl & cpflags::overwrite_permissions) ==
- cpflags::overwrite_permissions);
-
- bool ovr ((fl & cpflags::overwrite_content) == cpflags::overwrite_content);
-
- const char* f (from.string ().c_str ());
- const char* t (to.string ().c_str ());
-
-#ifndef _WIN32
-
- if (!ovr && path_entry (to).first)
- throw_generic_error (EEXIST);
-
- if (::rename (f, t) == 0) // POSIX implementation.
- return;
-
- // If source and destination paths are on different file systems we need to
- // move the file ourselves.
- //
- if (errno != EXDEV)
- throw_generic_error (errno);
-
- // Note that cpfile() follows symlinks, so we need to remove destination if
- // exists.
- //
- try_rmfile (to);
-
- // Note that permissions are copied unconditionally to a new file.
- //
- cpfile (from, to, cpflags::none);
-
- // Copy file access and modification times.
- //
- struct stat s;
- if (stat (f, &s) != 0)
- throw_generic_error (errno);
-
- timeval times[2];
- times[0].tv_sec = s.st_atime;
- times[0].tv_usec = ansec<struct stat> (&s, true) / 1000;
- times[1].tv_sec = s.st_mtime;
- times[1].tv_usec = mnsec<struct stat> (&s, true) / 1000;
-
- if (utimes (t, times) != 0)
- throw_generic_error (errno);
-
- // Finally, remove the source file.
- //
- try_rmfile (from);
-
-#else
-
- // While ::rename() is present on Windows, it is not POSIX but ISO C
- // implementation, that doesn't fit our needs well.
- //
- auto te (path_entry (to));
-
- if (!ovr && te.first)
- throw_generic_error (EEXIST);
-
- bool td (te.first && te.second == entry_type::directory);
-
- auto fe (path_entry (from));
- bool fd (fe.first && fe.second == entry_type::directory);
-
- // If source and destination filesystem entries exist, they both must be
- // either directories or not directories.
- //
- if (fe.first && te.first && fd != td)
- throw_generic_error (ENOTDIR);
-
- DWORD mfl (fd ? 0 : (MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING));
-
- if (MoveFileExA (f, t, mfl))
- return;
-
- // If the destination already exists, then MoveFileExA() succeeds only if
- // it is a regular file or a symlink. Lets also support an empty directory
- // special case to comply with POSIX. If the destination is an empty
- // directory we will just remove it and retry the move operation.
- //
- // Note that under Wine we endup with ERROR_ACCESS_DENIED error code in
- // that case, and with ERROR_ALREADY_EXISTS when run natively.
- //
- DWORD ec (GetLastError ());
- if ((ec == ERROR_ALREADY_EXISTS || ec == ERROR_ACCESS_DENIED) && td &&
- try_rmdir (path_cast<dir_path> (to)) != rmdir_status::not_empty &&
- MoveFileExA (f, t, mfl))
- return;
-
- throw_system_error (ec);
-
-#endif
- }
-
- timestamp
- file_mtime (const char* p)
- {
-#ifndef _WIN32
- struct stat s;
- if (stat (p, &s) != 0)
- {
- if (errno == ENOENT || errno == ENOTDIR)
- return timestamp_nonexistent;
- else
- throw_generic_error (errno);
- }
-
- if (!S_ISREG (s.st_mode))
- return timestamp_nonexistent;
-
- return system_clock::from_time_t (s.st_mtime) +
- chrono::duration_cast<duration> (
- chrono::nanoseconds (mnsec<struct stat> (&s, true)));
-#else
-
- WIN32_FILE_ATTRIBUTE_DATA s;
-
- if (!GetFileAttributesExA (p, GetFileExInfoStandard, &s))
- {
- DWORD ec (GetLastError ());
-
- if (ec == ERROR_FILE_NOT_FOUND ||
- ec == ERROR_PATH_NOT_FOUND ||
- ec == ERROR_INVALID_NAME ||
- ec == ERROR_INVALID_DRIVE ||
- ec == ERROR_BAD_PATHNAME ||
- ec == ERROR_BAD_NETPATH)
- return timestamp_nonexistent;
-
- throw_system_error (ec);
- }
-
- if ((s.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
- return timestamp_nonexistent;
-
- // Time in FILETIME is in 100 nanosecond "ticks" since "Windows epoch"
- // (1601-01-01T00:00:00Z). To convert it to "UNIX epoch"
- // (1970-01-01T00:00:00Z) we need to subtract 11644473600 seconds.
- //
- const FILETIME& t (s.ftLastWriteTime);
-
- uint64_t ns ((static_cast<uint64_t> (t.dwHighDateTime) << 32) |
- t.dwLowDateTime);
-
- ns -= 11644473600ULL * 10000000; // Now in UNIX epoch.
- ns *= 100; // Now in nanoseconds.
-
- return timestamp (
- chrono::duration_cast<duration> (
- chrono::nanoseconds (ns)));
-#endif
- }
-
- permissions
- path_permissions (const path& p)
- {
-#ifndef _WIN32
- struct stat s;
- if (stat (p.string ().c_str (), &s) != 0)
-#else
- struct _stat s;
- if (_stat (p.string ().c_str (), &s) != 0)
-#endif
- throw_generic_error (errno);
-
- // VC++ has no S_IRWXU defined. MINGW GCC <= 4.9 has no S_IRWXG, S_IRWXO
- // defined.
- //
- // We could extrapolate user permissions to group/other permissions if
- // S_IRWXG/S_IRWXO are undefined. That is, we could consider their absence
- // as meaning that the platform does not distinguish between permissions
- // for different kinds of users. Let's wait for a use-case first.
- //
- mode_t f (S_IREAD | S_IWRITE | S_IEXEC);
-
-#ifdef S_IRWXG
- f |= S_IRWXG;
-#endif
-
-#ifdef S_IRWXO
- f |= S_IRWXO;
-#endif
-
- return static_cast<permissions> (s.st_mode & f);
- }
-
- void
- path_permissions (const path& p, permissions f)
- {
- mode_t m (S_IREAD | S_IWRITE | S_IEXEC);
-
-#ifdef S_IRWXG
- m |= S_IRWXG;
-#endif
-
-#ifdef S_IRWXO
- m |= S_IRWXO;
-#endif
-
- m &= static_cast<mode_t> (f);
-
-#ifndef _WIN32
- if (chmod (p.string ().c_str (), m) == -1)
-#else
- if (_chmod (p.string ().c_str (), m) == -1)
-#endif
- throw_generic_error (errno);
- }
-
- // dir_{entry,iterator}
- //
-#ifndef _WIN32
-
- // dir_entry
- //
- dir_iterator::
- ~dir_iterator ()
- {
- if (h_ != nullptr)
- closedir (h_); // Ignore any errors.
- }
-
- dir_iterator& dir_iterator::
- operator= (dir_iterator&& x)
- {
- if (this != &x)
- {
- e_ = move (x.e_);
-
- if (h_ != nullptr && closedir (h_) == -1)
- throw_generic_error (errno);
-
- h_ = x.h_;
- x.h_ = nullptr;
- }
- return *this;
- }
-
- entry_type dir_entry::
- type (bool link) const
- {
- path_type p (b_ / p_);
- struct stat s;
- if ((link
- ? stat (p.string ().c_str (), &s)
- : lstat (p.string ().c_str (), &s)) != 0)
- {
- throw_generic_error (errno);
- }
-
- entry_type r;
-
- if (S_ISREG (s.st_mode))
- r = entry_type::regular;
- else if (S_ISDIR (s.st_mode))
- r = entry_type::directory;
- else if (S_ISLNK (s.st_mode))
- r = entry_type::symlink;
- else
- r = entry_type::other;
-
- return r;
- }
-
- // dir_iterator
- //
- struct dir_deleter
- {
- void operator() (DIR* p) const {if (p != nullptr) closedir (p);}
- };
-
- dir_iterator::
- dir_iterator (const dir_path& d)
- {
- unique_ptr<DIR, dir_deleter> h (opendir (d.string ().c_str ()));
- h_ = h.get ();
-
- if (h_ == nullptr)
- throw_generic_error (errno);
-
- next ();
-
- if (h_ != nullptr)
- e_.b_ = d;
-
- h.release ();
- }
-
- template <typename D>
- inline /*constexpr*/ entry_type d_type (const D* d, decltype(d->d_type)*)
- {
- switch (d->d_type)
- {
-#ifdef DT_DIR
- case DT_DIR: return entry_type::directory;
-#endif
-#ifdef DT_REG
- case DT_REG: return entry_type::regular;
-#endif
-#ifdef DT_LNK
- case DT_LNK: return entry_type::symlink;
-#endif
-#ifdef DT_BLK
- case DT_BLK:
-#endif
-#ifdef DT_CHR
- case DT_CHR:
-#endif
-#ifdef DT_FIFO
- case DT_FIFO:
-#endif
-#ifdef DT_SOCK
- case DT_SOCK:
-#endif
- return entry_type::other;
-
- default: return entry_type::unknown;
- }
- }
-
- template <typename D>
- inline constexpr entry_type d_type (...) {return entry_type::unknown;}
-
- void dir_iterator::
- next ()
- {
- for (;;)
- {
- errno = 0;
- if (struct dirent* de = readdir (h_))
- {
- // We can accept some overhead for '.' and '..' (relying on short
- // string optimization) in favor of a more compact code.
- //
- path p (de->d_name);
-
- // Skip '.' and '..'.
- //
- if (p.current () || p.parent ())
- continue;
-
- e_.p_ = move (p);
- e_.t_ = d_type<struct dirent> (de, nullptr);
- e_.lt_ = entry_type::unknown;
- }
- else if (errno == 0)
- {
- // End of stream.
- //
- closedir (h_);
- h_ = nullptr;
- }
- else
- throw_generic_error (errno);
-
- break;
- }
- }
-
-#else
-
- // dir_entry
- //
- dir_iterator::
- ~dir_iterator ()
- {
- if (h_ != -1)
- _findclose (h_); // Ignore any errors.
- }
-
- dir_iterator& dir_iterator::
- operator= (dir_iterator&& x)
- {
- if (this != &x)
- {
- e_ = move (x.e_);
-
- if (h_ != -1 && _findclose (h_) == -1)
- throw_generic_error (errno);
-
- h_ = x.h_;
- x.h_ = -1;
- }
- return *this;
- }
-
- entry_type dir_entry::
- type (bool) const
- {
- // Note that we currently do not support symlinks (yes, there is symlink
- // support since Vista).
- //
- path_type p (b_ / p_);
-
- struct _stat s;
- if (_stat (p.string ().c_str (), &s) != 0)
- throw_generic_error (errno);
-
- entry_type r;
- if (S_ISREG (s.st_mode))
- r = entry_type::regular;
- else if (S_ISDIR (s.st_mode))
- r = entry_type::directory;
- else
- r = entry_type::other;
-
- return r;
- }
-
- // dir_iterator
- //
- struct auto_dir
- {
- explicit
- auto_dir (intptr_t& h): h_ (&h) {}
-
- auto_dir (const auto_dir&) = delete;
- auto_dir& operator= (const auto_dir&) = delete;
-
- ~auto_dir ()
- {
- if (h_ != nullptr && *h_ != -1)
- _findclose (*h_);
- }
-
- void release () {h_ = nullptr;}
-
- private:
- intptr_t* h_;
- };
-
- dir_iterator::
- dir_iterator (const dir_path& d)
- {
- auto_dir h (h_);
- e_.b_ = d; // Used by next() to call _findfirst().
-
- next ();
- h.release ();
- }
-
- void dir_iterator::
- next ()
- {
- for (;;)
- {
- bool r;
- _finddata_t fi;
-
- if (h_ == -1)
- {
- // The call is made from the constructor. Any other call with h_ == -1
- // is illegal.
- //
-
- // Check to distinguish non-existent vs empty directories.
- //
- if (!dir_exists (e_.b_))
- throw_generic_error (ENOENT);
-
- h_ = _findfirst ((e_.b_ / path ("*")).string ().c_str (), &fi);
- r = h_ != -1;
- }
- else
- r = _findnext (h_, &fi) == 0;
-
- if (r)
- {
- // We can accept some overhead for '.' and '..' (relying on short
- // string optimization) in favor of a more compact code.
- //
- path p (fi.name);
-
- // Skip '.' and '..'.
- //
- if (p.current () || p.parent ())
- continue;
-
- e_.p_ = move (p);
-
- // We do not support symlinks at the moment.
- //
- e_.t_ = fi.attrib & _A_SUBDIR
- ? entry_type::directory
- : entry_type::regular;
-
- e_.lt_ = entry_type::unknown;
- }
- else if (errno == ENOENT)
- {
- // End of stream.
- //
- if (h_ != -1)
- {
- _findclose (h_);
- h_ = -1;
- }
- }
- else
- throw_generic_error (errno);
-
- break;
- }
- }
-#endif
-
- // Match the name [ni, ne) to the pattern [pi, pe). Ranges can be empty.
- //
- static bool
- match (string::const_iterator pi, string::const_iterator pe,
- string::const_iterator ni, string::const_iterator ne)
- {
- using reverse_iterator = std::reverse_iterator<string::const_iterator>;
-
- reverse_iterator rpi (pe);
- reverse_iterator rpe (pi);
-
- reverse_iterator rni (ne);
- reverse_iterator rne (ni);
-
- // Match the pattern suffix (follows the last *) to the name trailing
- // characters.
- //
- char pc;
- for (; rpi != rpe && (pc = *rpi) != '*' && rni != rne; ++rpi, ++rni)
- {
-#ifndef _WIN32
- if (*rni != pc && pc != '?')
-#else
- if (lcase (*rni) != lcase (pc) && pc != '?')
-#endif
- return false;
- }
-
- // If we got to the (reversed) end of the pattern (no * is encountered)
- // than we are done. The success depends on if we got to the (reversed) end
- // of the name as well.
- //
- if (rpi == rpe)
- return rni == rne;
-
- // If we didn't reach * in the pattern then we reached the (reversed) end
- // of the name. That means we have unmatched non-star characters in the
- // pattern, and so match failed.
- //
- if (pc != '*')
- {
- assert (rni == rne);
- return false;
- }
-
- // Match the pattern prefix (ends with the first *) to the name leading
- // characters. If they mismatch we failed. Otherwise if this is an only *
- // in the pattern (matches whatever is left in the name) then we succeed,
- // otherwise we perform backtracking (recursively).
- //
- pe = rpi.base ();
- ne = rni.base ();
-
- // Compare the pattern and the name char by char until the name suffix or
- // * is encountered in the pattern (whichever happens first). Fail if a
- // char mismatches.
- //
- for (; (pc = *pi) != '*' && ni != ne; ++pi, ++ni)
- {
-#ifndef _WIN32
- if (*ni != pc && pc != '?')
-#else
- if (lcase (*ni) != lcase (pc) && pc != '?')
-#endif
- return false;
- }
-
- // If we didn't get to * in the pattern then we got to the name suffix.
- // That means that the pattern has unmatched non-star characters, and so
- // match failed.
- //
- if (pc != '*')
- {
- assert (ni == ne);
- return false;
- }
-
- // If * that we have reached is the last one, then it matches whatever is
- // left in the name (including an empty range).
- //
- if (++pi == pe)
- return true;
-
- // Perform backtracking.
- //
- // From now on, we will call the pattern not-yet-matched part (starting
- // the leftmost * and ending the rightmost one inclusively) as pattern, and
- // the name not-yet-matched part as name.
- //
- // Here we sequentially assume that * that starts the pattern matches the
- // name leading part (staring from an empty one and iterating till the full
- // name). So if, at some iteration, the pattern trailing part (that follows
- // the leftmost *) matches the name trailing part, then the pattern matches
- // the name.
- //
- bool r;
- for (; !(r = match (pi, pe, ni, ne)) && ni != ne; ++ni) ;
- return r;
- }
-
- bool
- path_match (const string& pattern, const string& name)
- {
- // Implementation notes:
- //
- // - This has a good potential of becoming hairy quickly so need to strive
- // for an elegant way to implement this.
- //
- // - Most patterns will contains a single * wildcard with a prefix and/or
- // suffix (e.g., *.txt, foo*, f*.txt). Something like this is not very
- // common: *foo*.
- //
- // So it would be nice to have a clever implementation that first
- // "anchors" itself with a literal prefix and/or suffix and only then
- // continue with backtracking. In other words, reduce:
- //
- // *.txt vs foo.txt -> * vs foo
- // foo* vs foo.txt -> * vs .txt
- // f*.txt vs foo.txt -> * vs oo
- //
-
- auto pi (pattern.rbegin ());
- auto pe (pattern.rend ());
-
- auto ni (name.rbegin ());
- auto ne (name.rend ());
-
- // The name doesn't match the pattern if it is of a different type than the
- // pattern is.
- //
- bool pd (pi != pe && path::traits::is_separator (*pi));
- bool nd (ni != ne && path::traits::is_separator (*ni));
-
- if (pd != nd)
- return false;
-
- // Skip trailing separators if present.
- //
- if (pd)
- {
- ++pi;
- ++ni;
- }
-
- return match (pattern.begin (), pi.base (), name.begin (), ni.base ());
- }
-
- // Iterate over directory sub-entries, recursively and including itself if
- // requested. Note that recursive iterating goes depth-first which make
- // sense for the cleanup use cases (@@ maybe this should be controllable
- // since for directory creation it won't make sense).
- //
- // Prior to recursively opening a directory for iterating the preopen
- // callback function is called. If false is returned, then the directory is
- // not traversed but still returned by the next() call.
- //
- // Note that iterating over non-existent directory is not en error. The
- // subsequent next() call returns false for such a directory.
- //
- using preopen = std::function<bool (const dir_path&)>;
-
- class recursive_dir_iterator
- {
- public:
- recursive_dir_iterator (dir_path p,
- bool recursive,
- bool self,
- bool fs,
- preopen po)
- : start_ (move (p)),
- recursive_ (recursive),
- self_ (self),
- follow_symlinks_ (fs),
- preopen_ (move (po))
- {
- open (dir_path (), self_);
- }
-
- // Non-copyable, non-movable type.
- //
- recursive_dir_iterator (const recursive_dir_iterator&) = delete;
- recursive_dir_iterator& operator= (const recursive_dir_iterator&) = delete;
-
- // Return false if no more entries left. Otherwise save the next entry path
- // and return true. The path is relative against the directory being
- // traversed and contains a trailing separator for sub-directories. Throw
- // std::system_error in case of a failure (insufficient permissions,
- // dangling symlink encountered, etc).
- //
- bool
- next (path& p)
- {
- if (iters_.empty ())
- return false;
-
- auto& i (iters_.back ());
-
- // If we got to the end of directory sub-entries, then go one level up
- // and return this directory path.
- //
- if (i.first == dir_iterator ())
- {
- path d (move (i.second));
- iters_.pop_back ();
-
- // Return the path unless it is the last one (the directory we started
- // to iterate from) and the self flag is not set.
- //
- if (iters_.empty () && !self_)
- return false;
-
- p = move (d);
- return true;
- }
-
- const dir_entry& de (*i.first);
-
- // Append separator if a directory. Note that dir_entry::type() can
- // throw.
- //
- entry_type et (follow_symlinks_ ? de.type () : de.ltype ());
- path pe (et == entry_type::directory
- ? path_cast<dir_path> (i.second / de.path ())
- : i.second / de.path ());
-
- ++i.first;
-
- if (recursive_ && pe.to_directory ())
- {
- open (path_cast<dir_path> (move (pe)), true);
- return next (p);
- }
-
- p = move (pe);
- return true;
- }
-
- private:
- void
- open (dir_path p, bool preopen)
- {
- // We should consider a racing condition here. The directory can be
- // removed before we create an iterator for it. In this case we just do
- // nothing, so the directory is silently skipped.
- //
- try
- {
- // If preopen_() returns false, then the directory will not be
- // traversed (as we leave iterator with end semantics) but still be
- // returned by the next() call as a sub-entry.
- //
- dir_iterator i;
- if (!preopen || preopen_ (p))
- {
- dir_path d (start_ / p);
- i = dir_iterator (!d.empty () ? d : dir_path ("."));
- }
-
- iters_.emplace_back (move (i), move (p));
- }
- catch (const system_error& e)
- {
- // Ignore non-existent directory (ENOENT or ENOTDIR). Rethrow for any
- // other error. We consider ENOTDIR as a variety of removal, with a
- // new filesystem entry being created afterwards.
- //
- // Make sure that the error denotes errno portable code.
- //
- assert (e.code ().category () == generic_category ());
-
- int ec (e.code ().value ());
- if (ec != ENOENT && ec != ENOTDIR)
- throw;
- }
- }
-
- private:
- dir_path start_;
- bool recursive_;
- bool self_;
- bool follow_symlinks_;
- preopen preopen_;
- small_vector<pair<dir_iterator, dir_path>, 1> iters_;
- };
-
- // Search for paths matching the pattern and call the specified function for
- // each matching path. Return false if the underlying func() call returns
- // false. Otherwise the function conforms to the path_search() description.
- //
- static const string any_dir ("*/");
-
- static bool
- search (
- path pattern,
- dir_path pattern_dir,
- const dir_path start_dir,
- bool follow_symlinks,
- const function<bool (path&&, const string& pattern, bool interm)>& func)
- {
- // Fast-forward the leftmost pattern non-wildcard components. So, for
- // example, search for foo/f* in /bar/ becomes search for f* in /bar/foo/.
- //
- {
- auto b (pattern.begin ());
- auto e (pattern.end ());
- auto i (b);
- for (; i != e && (*i).find_first_of ("*?") == string::npos; ++i) ;
-
- // If the pattern has no wildcards then we reduce to checking for the
- // filesystem entry existence. It matches if exists and is of the proper
- // type.
- //
- if (i == e)
- {
- path p (pattern_dir / pattern);
- auto pe (path_entry (start_dir / p, true));
-
- if (pe.first &&
- ((pe.second == entry_type::directory) == p.to_directory ()))
- return func (move (p), string (), false);
-
- return true;
- }
- else if (i != b) // There are non-wildcard components, so fast-forward.
- {
- path p (b, i);
- pattern = pattern.leaf (p);
- pattern_dir /= path_cast<dir_path> (move (p));
- }
- }
-
- assert (!pattern.empty ());
-
- // The pattern leftmost component. Will use it to match the start directory
- // sub-entries.
- //
- path pc (pattern.begin (), ++pattern.begin ());
- string pcr (pc.representation ());
-
- // Note that if the pattern has multiple components (is not a simple path),
- // then the leftmost one has a trailing separator, and so will match
- // sub-directories only.
- //
- bool simple (pattern.simple ());
-
- // Note that we rely on "small function object" optimization here.
- //
- recursive_dir_iterator i (
- start_dir / pattern_dir,
- pcr.find ("**") != string::npos, // Recursive.
- pcr.find ("***") != string::npos, // Self-inclusive.
- follow_symlinks,
- [&pattern_dir, &func] (const dir_path& p) -> bool // Preopen.
- {
- return func (pattern_dir / p, any_dir, true);
- });
-
- // Canonicalize the pattern component collapsing consecutive stars (used to
- // express that it is recursive) into a single one.
- //
- size_t j (0);
- size_t n (pcr.size ());
- for (size_t i (0); i < n; ++i)
- {
- char c (pcr[i]);
- if (!(c == '*' && i > 0 && pcr[i - 1] == '*'))
- pcr[j++] = c;
- }
-
- if (j != n)
- pcr.resize (j);
-
- // Note that the callback function can be called for the same directory
- // twice: first time as intermediate match from iterator's preopen() call,
- // and then, if the first call succeed, from the iterating loop (possibly
- // as the final match).
- //
- path p;
- while (i.next (p))
- {
- // Skip sub-entry if its name doesn't match the pattern leftmost
- // component.
- //
- // Matching the directory we are iterating through (as for a pattern
- // component containing ***) is a bit tricky. This directory is
- // represented by the iterator as an empty path, and so we need to
- // compute it (the leaf would actually be enough) for matching. This
- // leaf can be aquired from the pattern_dir / start_dir path except the
- // case when both directories are empty. This is the case when we search
- // in the current directory (start_dir is empty) with a pattern that
- // starts with *** wildcard (for example f***/bar). All we can do here is
- // to fallback to path::current_directory() call. Note that this will be
- // the only call per path_search() as the next time pattern_dir will not
- // be empty.
- //
- const path& se (!p.empty ()
- ? p
- : path_cast<path> (!pattern_dir.empty ()
- ? pattern_dir
- : !start_dir.empty ()
- ? start_dir
- : path::current_directory ()));
-
- if (!path_match (pcr, se.leaf ().representation ()))
- continue;
-
- // If the callback function returns false, then we stop the entire search
- // for the final match, or do not search below the path for the
- // intermediate one.
- //
- if (!func (pattern_dir / p, pcr, !simple))
- {
- if (simple) // Final match.
- return false;
- else
- continue;
- }
-
- // If the pattern is not a simple one, and it's leftmost component
- // matches the sub-entry, then the sub-entry is a directory (see the note
- // above), and we search in it using the trailing part of the pattern.
- //
- if (!simple && !search (pattern.leaf (pc),
- pattern_dir / path_cast<dir_path> (move (p)),
- start_dir,
- follow_symlinks,
- func))
- return false;
- }
-
- return true;
- }
-
- void
- path_search (
- const path& pattern,
- const function<bool (path&&, const string& pattern, bool interm)>& func,
- const dir_path& start,
- bool follow_symlinks)
- {
- search (pattern,
- dir_path (),
- pattern.relative () ? start : dir_path (),
- follow_symlinks,
- func);
- }
-}
diff --git a/butl/filesystem.ixx b/butl/filesystem.ixx
deleted file mode 100644
index 90f1715..0000000
--- a/butl/filesystem.ixx
+++ /dev/null
@@ -1,144 +0,0 @@
-// file : butl/filesystem.ixx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-namespace butl
-{
- inline bool
- dir_empty (const dir_path& d)
- {
- return dir_iterator (d) == dir_iterator ();
- }
-
- inline rmdir_status
- try_rmdir_r (const dir_path& p, bool ignore_error)
- {
- bool e (dir_exists (p)); //@@ What if it exists but is not a directory?
-
- if (e)
- rmdir_r (p, true, ignore_error);
-
- return e ? rmdir_status::success : rmdir_status::not_exist;
- }
-
- // auto_rm
- //
- template <typename P>
- inline auto_rm<P>::
- auto_rm (auto_rm&& x)
- : path_ (std::move (x.path_))
- {
- x.cancel ();
- }
-
- template <typename P>
- inline auto_rm<P>& auto_rm<P>::
- operator= (auto_rm&& x)
- {
- if (this != &x)
- {
- path_ = std::move (x.path_);
- x.cancel ();
- }
-
- return *this;
- }
-
- template <>
- inline auto_rm<path>::
- ~auto_rm () {if (!path_.empty ()) try_rmfile (path_, true);}
-
- template <>
- inline auto_rm<dir_path>::
- ~auto_rm () {if (!path_.empty ()) try_rmdir_r (path_, true);}
-
- // cpflags
- //
- inline cpflags operator& (cpflags x, cpflags y) {return x &= y;}
- inline cpflags operator| (cpflags x, cpflags y) {return x |= y;}
- inline cpflags operator&= (cpflags& x, cpflags y)
- {
- return x = static_cast<cpflags> (
- static_cast<std::uint16_t> (x) &
- static_cast<std::uint16_t> (y));
- }
-
- inline cpflags operator|= (cpflags& x, cpflags y)
- {
- return x = static_cast<cpflags> (
- static_cast<std::uint16_t> (x) |
- static_cast<std::uint16_t> (y));
- }
-
- // permissions
- //
- inline permissions operator& (permissions x, permissions y) {return x &= y;}
- inline permissions operator| (permissions x, permissions y) {return x |= y;}
- inline permissions operator&= (permissions& x, permissions y)
- {
- return x = static_cast<permissions> (
- static_cast<std::uint16_t> (x) &
- static_cast<std::uint16_t> (y));
- }
-
- inline permissions operator|= (permissions& x, permissions y)
- {
- return x = static_cast<permissions> (
- static_cast<std::uint16_t> (x) |
- static_cast<std::uint16_t> (y));
- }
-
- // dir_entry
- //
- inline entry_type dir_entry::
- ltype () const
- {
- return t_ != entry_type::unknown ? t_ : (t_ = type (false));
- }
-
- inline entry_type dir_entry::
- type () const
- {
- entry_type t (ltype ());
- return t != entry_type::symlink
- ? t
- : lt_ != entry_type::unknown ? lt_ : (lt_ = type (true));
- }
-
- // dir_iterator
- //
- inline dir_iterator::
- dir_iterator (dir_iterator&& x)
- : e_ (std::move (x.e_)), h_ (x.h_)
- {
-#ifndef _WIN32
- x.h_ = nullptr;
-#else
- x.h_ = -1;
-#endif
- }
-
- inline bool
- operator== (const dir_iterator& x, const dir_iterator& y)
- {
- return x.h_ == y.h_;
- }
-
- inline bool
- operator!= (const dir_iterator& x, const dir_iterator& y)
- {
- return !(x == y);
- }
-
- inline dir_iterator
- begin (dir_iterator& i)
- {
- return std::move (i);
- }
-
- inline dir_iterator
- end (const dir_iterator&)
- {
- return dir_iterator ();
- }
-}
diff --git a/butl/ft/exception b/butl/ft/exception
deleted file mode 100644
index 2dd3a38..0000000
--- a/butl/ft/exception
+++ /dev/null
@@ -1,39 +0,0 @@
-// file : butl/ft/exception -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_FT_EXCEPTION
-#define BUTL_FT_EXCEPTION
-
-#include <cstddef> // _LIBCPP_VERSION
-#include <exception>
-
-// __cpp_lib_uncaught_exceptions
-//
-#ifndef __cpp_lib_uncaught_exceptions
- //
- // VC has it since 1900.
- //
-# if defined(_MSC_VER)
-# if _MSC_VER >= 1900
-# define __cpp_lib_uncaught_exceptions 201411
-# endif
- //
- // Clang's libc++ seems to have it for a while (but not before 1200) so we
- // assume it's there from 1200. But not for MacOS, where it is explicitly
- // marked as unavailable until MacOS 10.12.
- //
-# elif defined(_LIBCPP_VERSION)
-# if _LIBCPP_VERSION >= 1200 && \
- (!defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) || \
- __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101200)
-# define __cpp_lib_uncaught_exceptions 201411
-# endif
- //
- // GCC libstdc++ has it since GCC 6 and it defines the feature test macro.
- // We will also use this for any other runtime.
- //
-# endif
-#endif
-
-#endif // BUTL_FT_EXCEPTION
diff --git a/butl/ft/lang b/butl/ft/lang
deleted file mode 100644
index 8cfc441..0000000
--- a/butl/ft/lang
+++ /dev/null
@@ -1,29 +0,0 @@
-// file : butl/ft/lang -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_FT_LANG
-#define BUTL_FT_LANG
-
-// __cpp_thread_local (extension)
-//
-// If this macro is undefined then one may choose to fallback to __thread.
-// Note, however, that it only for values that do not require dynamic
-// (runtime) initialization.
-//
-#ifndef __cpp_thread_local
- //
- // Apparently Apple's Clang "temporarily disabled" C++11 thread_local until
- // they can implement a "fast" version, which reportedly happened in XCode
- // 8.
- //
-# if defined(__apple_build_version__)
-# if __apple_build_version__ >= 8000000
-# define __cpp_thread_local 201103
-# endif
-# else
-# define __cpp_thread_local 201103
-# endif
-#endif
-
-#endif // BUTL_FT_LANG
diff --git a/butl/ft/shared_mutex b/butl/ft/shared_mutex
deleted file mode 100644
index 18692c8..0000000
--- a/butl/ft/shared_mutex
+++ /dev/null
@@ -1,58 +0,0 @@
-// file : butl/ft/shared_mutex -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_FT_SHARED_MUTEX
-#define BUTL_FT_SHARED_MUTEX
-
-#include <cstddef> // _LIBCPP_VERSION
-#include <shared_mutex>
-
-// __cpp_lib_shared_mutex
-//
-#ifndef __cpp_lib_shared_mutex
- //
- // VC has it since 1900.
- //
-# if defined(_MSC_VER)
-# if _MSC_VER >= 1900
-# define __cpp_lib_shared_mutex 201505
-# endif
- //
- // Clang's libc++ seems to have it for a while (but not before 1200) so we
- // assume it's there from 1200. It's also only enabled after C++14. But not
- // for MacOS, where it is explicitly marked as unavailable until MacOS
- // 10.12.
- //
-# elif defined(_LIBCPP_VERSION) && defined(_LIBCPP_STD_VER)
-# if _LIBCPP_VERSION >= 1200 && \
- _LIBCPP_STD_VER > 14 && \
- (!defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) || \
- __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101200)
-# define __cpp_lib_shared_mutex 201505
-# endif
- //
- // GCC libstdc++ has it since GCC 6 and it defines the feature test macro.
- // We will also use this for any other runtime.
- //
-# endif
-#endif
-
-// __cpp_lib_shared_timed_mutex
-//
-#ifndef __cpp_lib_shared_timed_mutex
- //
- // On MacOS shared_timed_mutex is marked as unavailable until MacOS
- // 10.12.
- //
-# if defined(_LIBCPP_VERSION)
-# if !defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) || \
- __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101200
-# define __cpp_lib_shared_timed_mutex 201402
-# endif
-# else
-# define __cpp_lib_shared_timed_mutex 201402
-# endif
-#endif
-
-#endif // BUTL_FT_SHARED_MUTEX
diff --git a/butl/manifest-forward b/butl/manifest-forward
deleted file mode 100644
index eb8f130..0000000
--- a/butl/manifest-forward
+++ /dev/null
@@ -1,15 +0,0 @@
-// file : butl/manifest-forward -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_MANIFEST_FORWARD
-#define BUTL_MANIFEST_FORWARD
-
-namespace butl
-{
- class manifest_parser;
- class manifest_serializer;
- class manifest_name_value;
-}
-
-#endif // BUTL_MANIFEST_FORWARD
diff --git a/butl/manifest-parser b/butl/manifest-parser
deleted file mode 100644
index 94ecb7e..0000000
--- a/butl/manifest-parser
+++ /dev/null
@@ -1,94 +0,0 @@
-// file : butl/manifest-parser -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_MANIFEST_PARSER
-#define BUTL_MANIFEST_PARSER
-
-#include <string>
-#include <iosfwd>
-#include <cstdint> // uint64_t
-#include <stdexcept> // runtime_error
-
-#include <butl/export>
-
-#include <butl/char-scanner>
-
-namespace butl
-{
- class LIBBUTL_EXPORT manifest_parsing: public std::runtime_error
- {
- public:
- manifest_parsing (const std::string& name,
- std::uint64_t line,
- std::uint64_t column,
- const std::string& description);
-
- std::string name;
- std::uint64_t line;
- std::uint64_t column;
- std::string description;
- };
-
- class manifest_name_value
- {
- public:
- std::string name;
- std::string value;
-
- std::uint64_t name_line;
- std::uint64_t name_column;
-
- std::uint64_t value_line;
- std::uint64_t value_column;
-
- bool
- empty () const {return name.empty () && value.empty ();}
- };
-
- class LIBBUTL_EXPORT manifest_parser: protected butl::char_scanner
- {
- public:
- manifest_parser (std::istream& is, const std::string& name)
- : char_scanner (is), name_ (name) {}
-
- const std::string&
- name () const {return name_;}
-
- // The first returned pair is special "start-of-manifest" with
- // empty name and value being the format version: {"", "<ver>"}.
- // After that we have a sequence of ordinary pairs which are
- // the manifest. At the end of the manifest we have the special
- // "end-of-manifest" pair with empty name and value: {"", ""}.
- // After that we can either get another start-of-manifest pair
- // (in which case the whole sequence repeats from the beginning)
- // or we get another end-of-manifest pair which signals the end
- // of stream (aka EOF). To put it another way, the parse sequence
- // always has the following form:
- //
- // ({"", "<ver>"} {"<name>", "<value>"}* {"", ""})* {"", ""}
- //
- manifest_name_value
- next ();
-
- private:
- void
- parse_name (manifest_name_value&);
-
- void
- parse_value (manifest_name_value&);
-
- // Skip spaces and return the first peeked non-space character.
- //
- xchar
- skip_spaces ();
-
- private:
- const std::string name_;
-
- enum {start, body, end} s_ = start;
- std::string version_; // Current format version.
- };
-}
-
-#endif // BUTL_MANIFEST_PARSER
diff --git a/butl/manifest-parser.cxx b/butl/manifest-parser.cxx
deleted file mode 100644
index 6b50d6b..0000000
--- a/butl/manifest-parser.cxx
+++ /dev/null
@@ -1,387 +0,0 @@
-// file : butl/manifest-parser.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <butl/manifest-parser>
-
-#include <cassert>
-#include <sstream>
-
-using namespace std;
-
-namespace butl
-{
- using parsing = manifest_parsing;
- using name_value = manifest_name_value;
-
- name_value manifest_parser::
- next ()
- {
- if (s_ == end)
- return name_value {"", "", line, column, line, column};
-
- xchar c (skip_spaces ());
-
- // Here is the problem: if we are in the 'body' state (that is,
- // we are parsing inside the manifest) and we see the special
- // empty name, then before returning the "start" pair for the
- // next manifest, we have to return the "end" pair. One way
- // would be to cache the "start" pair and return it on the
- // next call of next(). But that would require quite a bit
- // of extra logic. The alternative is to detect the beginning
- // of the empty name before parsing too far. This way, the
- // next call to next() will start parsing where we left of
- // and return the "start" pair naturally.
- //
- if (s_ == body && c == ':')
- {
- s_ = start;
- return name_value {"", "", c.line, c.column, c.line, c.column};
- }
-
- // Regardless of the state, what should come next is a name,
- // potentially the special empty one.
- //
- name_value r;
- parse_name (r);
-
- skip_spaces ();
- c = get ();
-
- if (eos (c))
- {
- // This is ok as long as the name is empty.
- //
- if (!r.name.empty ())
- throw parsing (name_, c.line, c.column, "':' expected after name");
-
- s_ = end;
-
- // The "end" pair.
- //
- r.value_line = r.name_line;
- r.value_column = r.name_column;
- return r;
- }
-
- if (c != ':')
- throw parsing (name_, c.line, c.column, "':' expected after name");
-
- skip_spaces ();
- parse_value (r);
-
- c = peek ();
-
- // The character after the value should be either a newline or eos.
- //
- assert (c == '\n' || eos (c));
-
- if (c == '\n')
- get ();
-
- // Now figure out whether what we've got makes sense, depending
- // on the state we are in.
- //
- if (s_ == start)
- {
- // Start of the (next) manifest. The first pair should be the
- // special empty name/format version.
- //
- if (!r.name.empty ())
- throw parsing (name_, r.name_line, r.name_column,
- "format version pair expected");
-
- // The version value is only mandatory for the first manifest in
- // a sequence.
- //
- if (r.value.empty ())
- {
- if (version_.empty ())
- throw parsing (name_, r.value_line, r.value_column,
- "format version value expected");
- r.value = version_;
- }
- else
- {
- version_ = r.value; // Update with the latest.
-
- if (version_ != "1")
- throw parsing (name_, r.value_line, r.value_column,
- "unsupported format version " + version_);
- }
-
- s_ = body;
- }
- else
- {
- // Parsing the body of the manifest.
- //
-
- // Should have been handled by the special case above.
- //
- assert (!r.name.empty ());
- }
-
- return r;
- }
-
- void manifest_parser::
- parse_name (name_value& r)
- {
- xchar c (peek ());
-
- r.name_line = c.line;
- r.name_column = c.column;
-
- for (; !eos (c); c = peek ())
- {
- if (c == ':' || c == ' ' || c == '\t' || c == '\n')
- break;
-
- r.name += c;
- get ();
- }
- }
-
- void manifest_parser::
- parse_value (name_value& r)
- {
- xchar c (peek ());
-
- r.value_line = c.line;
- r.value_column = c.column;
-
- string& v (r.value);
- string::size_type n (0); // Size of last non-space character (simple mode).
-
- // Detect the multi-line mode introductor.
- //
- bool ml (false);
- if (c == '\\')
- {
- get ();
- xchar p (peek ());
-
- if (p == '\n')
- {
- get (); // Newline is not part of the value so skip it.
- c = peek ();
- ml = true;
- }
- else if (eos (p))
- ml = true;
- else
- unget (c);
- }
-
- // Multi-line value starts from the line that follows the name.
- //
- if (ml)
- {
- r.value_line = c.line;
- r.value_column = c.column;
- }
-
- // The nl flag signals that the preceding character was a "special
- // newline", that is, a newline that was part of the milti-line mode
- // introductor or an escape sequence.
- //
- for (bool nl (ml); !eos (c); c = peek ())
- {
- // Detect the special "\n\\\n" sequence. In the multi-line mode,
- // this is a "terminator". In the simple mode, this is a way to
- // specify a newline.
- //
- // The key idea here is this: if we "swallowed" any characters
- // (i.e., called get() without a matching unget()), then we
- // have to restart the loop in order to do all the tests for
- // the next character. Also, for this to work, we can only
- // add one character to v, which limits us to maximum three
- // characters look-ahead: one in v, one "ungot", and one
- // peeked.
- //
- // The first block handles the special sequence that starts with
- // a special newline. In multi-line mode, this is an "immediate
- // termination" where we "use" the newline from the introductor.
- // Note also that in the simple mode the special sequence can
- // only start with a special (i.e., escaped) newline.
- //
- if (nl)
- {
- nl = false;
-
- if (c == '\\')
- {
- get ();
- xchar c1 (peek ());
-
- if (c1 == '\n' || eos (c1))
- {
- if (ml)
- break;
- else
- {
- if (c1 == '\n')
- get ();
-
- v += '\n'; // Literal newline.
- n = v.size ();
- continue; // Restart from the next character.
- }
- }
- else
- unget (c); // Fall through.
- }
- }
-
- if (c == '\n')
- {
- if (ml)
- {
- get ();
- xchar c1 (peek ());
-
- if (c1 == '\\')
- {
- get ();
- xchar c2 (peek ());
-
- if (c2 == '\n' || eos (c2))
- break;
- else
- {
- v += '\n';
- unget (c1);
- continue; // Restart from c1 (slash).
- }
- }
- else
- unget (c); // Fall through.
- }
- else
- break; // Simple value terminator.
- }
-
- // Detect the newline escape sequence. The same look-ahead
- // approach as above.
- //
- if (c == '\\')
- {
- get ();
- xchar c1 (peek ());
-
- if (c1 == '\n' || eos (c1))
- {
- if (c1 == '\n')
- {
- get ();
- nl = true; // This is a special newline.
- }
- continue; // Restart from the next character.
- }
- else if (c1 == '\\')
- {
- get ();
- xchar c2 (peek ());
-
- if (c2 == '\n' || eos (c1))
- {
- v += '\\';
- n = v.size ();
- // Restart from c2 (newline/eos).
- }
- else
- {
- v += '\\';
- n = v.size ();
- unget (c1); // Restart from c1 (second slash).
- }
-
- continue;
- }
- else
- unget (c); // Fall through.
- }
-
- get ();
- v += c;
-
- if (!ml && c != ' ' && c != '\t')
- n = v.size ();
- }
-
- // Cut off trailing whitespaces.
- //
- if (!ml)
- v.resize (n);
- }
-
- manifest_parser::xchar manifest_parser::
- skip_spaces ()
- {
- xchar c (peek ());
- bool start (c.column == 1);
-
- for (; !eos (c); c = peek ())
- {
- switch (c)
- {
- case ' ':
- case '\t':
- break;
- case '\n':
- {
- // Skip empty lines.
- //
- if (!start)
- return c;
-
- break;
- }
- case '#':
- {
- // We only recognize '#' as a start of a comment at the beginning
- // of the line (sans leading spaces).
- //
- if (!start)
- return c;
-
- get ();
-
- // Read until newline or eos.
- //
- for (c = peek (); !eos (c) && c != '\n'; c = peek ())
- get ();
-
- continue;
- }
- default:
- return c; // Not a space.
- }
-
- get ();
- }
-
- return c;
- }
-
- // manifest_parsing
- //
-
- static string
- format (const string& n, uint64_t l, uint64_t c, const string& d)
- {
- ostringstream os;
- if (!n.empty ())
- os << n << ':';
- os << l << ':' << c << ": error: " << d;
- return os.str ();
- }
-
- manifest_parsing::
- manifest_parsing (const string& n, uint64_t l, uint64_t c, const string& d)
- : runtime_error (format (n, l, c, d)),
- name (n), line (l), column (c), description (d)
- {
- }
-}
diff --git a/butl/manifest-serializer b/butl/manifest-serializer
deleted file mode 100644
index a4ba0bc..0000000
--- a/butl/manifest-serializer
+++ /dev/null
@@ -1,75 +0,0 @@
-// file : butl/manifest-serializer -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_MANIFEST_SERIALIZER
-#define BUTL_MANIFEST_SERIALIZER
-
-#include <string>
-#include <iosfwd>
-#include <cstddef> // size_t
-#include <stdexcept> // runtime_error
-
-#include <butl/export>
-
-namespace butl
-{
- class LIBBUTL_EXPORT manifest_serialization: public std::runtime_error
- {
- public:
- manifest_serialization (const std::string& name,
- const std::string& description);
-
- std::string name;
- std::string description;
- };
-
- class LIBBUTL_EXPORT manifest_serializer
- {
- public:
- manifest_serializer (std::ostream& os, const std::string& name)
- : os_ (os), name_ (name) {}
-
- const std::string&
- name () const {return name_;}
-
- // The first name-value pair should be the special "start-of-manifest"
- // with empty name and value being the format version. After that we
- // have a sequence of ordinary pairs which are the manifest. At the
- // end of the manifest we have the special "end-of-manifest" pair
- // with empty name and value. After that we can either have another
- // start-of-manifest pair (in which case the whole sequence repeats
- // from the beginning) or we get another end-of-manifest pair which
- // signals the end of stream.
- //
- void
- next (const std::string& name, const std::string& value);
-
- // Write a comment. The supplied text is prefixed with "# " and
- // terminated with a newline.
- //
- void
- comment (const std::string&);
-
- private:
- void
- check_name (const std::string&);
-
- // Write 'n' characters from 's' (assuming there are no newlines)
- // split into multiple lines at or near the 78 characters
- // boundary. The first line starts at the 'column' offset.
- //
- void
- write_value (std::size_t column, const char* s, std::size_t n);
-
- private:
- enum {start, body, end} s_ = start;
- std::string version_; // Current format version.
-
- private:
- std::ostream& os_;
- const std::string name_;
- };
-}
-
-#endif // BUTL_MANIFEST_SERIALIZER
diff --git a/butl/manifest-serializer.cxx b/butl/manifest-serializer.cxx
deleted file mode 100644
index 4255ee1..0000000
--- a/butl/manifest-serializer.cxx
+++ /dev/null
@@ -1,238 +0,0 @@
-// file : butl/manifest-serializer.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <butl/manifest-serializer>
-
-#include <ostream>
-#include <cassert>
-
-using namespace std;
-
-namespace butl
-{
- using serialization = manifest_serialization;
-
- void manifest_serializer::
- next (const string& n, const string& v)
- {
- switch (s_)
- {
- case start:
- {
- if (!n.empty ())
- throw serialization (name_, "format version pair expected");
-
- if (v.empty ())
- {
- // End of manifests.
- //
- os_.flush ();
- s_ = end;
- break;
- }
-
- if (v != "1")
- throw serialization (name_, "unsupported format version " + v);
-
- os_ << ':';
-
- if (v != version_)
- {
- os_ << ' ' << v;
- version_ = v;
- }
-
- os_ << endl;
- s_ = body;
- break;
- }
- case body:
- {
- if (n.empty ())
- {
- if (!v.empty ())
- throw serialization (name_, "non-empty value in end pair");
-
- s_ = start;
- break;
- }
-
- check_name (n);
-
- os_ << n << ':';
-
- if (!v.empty ())
- {
- os_ << ' ';
-
- // Use the multi-line mode in any of the following cases:
- //
- // - name is too long (say longer than 37 (78/2 - 2) characters;
- // we cannot start on the next line since that would start the
- // multi-line mode)
- // - value contains newlines
- // - value contains leading/trailing whitespaces
- //
- if (n.size () > 37 ||
- v.find ('\n') != string::npos ||
- v.front () == ' ' || v.front () == '\t' ||
- v.back () == ' ' || v.back () == '\t')
- {
- os_ << "\\" << endl; // Multi-line mode introductor.
-
- // Chunk the value into fragments separated by newlines.
- //
- for (size_t i (0), p (v.find ('\n')); ; p = v.find ('\n', i))
- {
- if (p == string::npos)
- {
- // Last chunk.
- //
- write_value (0, v.c_str () + i, v.size () - i);
- break;
- }
-
- write_value (0, v.c_str () + i, p - i);
- os_ << endl;
- i = p + 1;
- }
-
- os_ << endl << "\\"; // Multi-line mode terminator.
- }
- else
- write_value (n.size () + 2, v.c_str (), v.size ());
- }
-
- os_ << endl;
- break;
- }
- case end:
- {
- throw serialization (name_, "serialization after eos");
- }
- }
- }
-
- void manifest_serializer::
- comment (const string& t)
- {
- if (s_ == end)
- throw serialization (name_, "serialization after eos");
-
- os_ << '#';
-
- if (!t.empty ())
- os_ << ' ' << t;
-
- os_ << endl;
- }
-
- void manifest_serializer::
- check_name (const string& n)
- {
- if (n[0] == '#')
- throw serialization (name_, "name starts with '#'");
-
- for (char c: n)
- {
- switch (c)
- {
- case ' ':
- case '\t':
- case '\n': throw serialization (name_, "name contains whitespace");
- case ':': throw serialization (name_, "name contains ':'");
- default: break;
- }
- }
- }
-
- void manifest_serializer::
- write_value (size_t cl, const char* s, size_t n)
- {
- char c ('\0');
-
- // The idea is to break on the 77th character (i.e., write it
- // on the next line) which means we have written 76 characters
- // on this line plus 2 for '\' and '\n', which gives us 78.
- //
- for (const char* e (s + n); s != e; s++, cl++)
- {
- c = *s;
- bool br (false); // Break the line.
-
- // If this is a whitespace, see if it's a good place to break the
- // line.
- //
- if (c == ' ' || c == '\t')
- {
- // Find the next whitespace (or the end) and see if it is a better
- // place.
- //
- for (const char* w (s + 1); ; w++)
- {
- if (w == e || *w == ' ' || *w == '\t')
- {
- // Is this whitespace past where we need to break? Also see
- // below the "hard" break case for why we use 78 at the end.
- //
- if (cl + static_cast<size_t> (w - s) > (w != e ? 77 : 78))
- {
- // Only break if this whitespace is close enough to
- // the end of the line.
- //
- br = (cl > 57);
- }
-
- break;
- }
- }
- }
-
- // Do we have to do a "hard" break (i.e., without a whitespace)?
- // If there is just one character left, then instead of writing
- // '\' and then the character on the next line, we might as well
- // write it on this line.
- //
- if (cl == (s + 1 != e ? 77 : 78))
- br = true;
-
- if (br)
- {
- os_ << '\\' << endl;
- cl = 0;
- }
-
- os_ << c;
- }
-
- // What comes next is always a newline. I the last character that
- // we have written is a backslash, escape it.
- //
- if (c == '\\')
- os_ << '\\';
- }
-
- // manifest_serialization
- //
-
- static string
- format (const string& n, const string& d)
- {
- string r;
- if (!n.empty ())
- {
- r += n;
- r += ": ";
- }
- r += "error: ";
- r += d;
- return r;
- }
-
- manifest_serialization::
- manifest_serialization (const string& n, const string& d)
- : runtime_error (format (n, d)), name (n), description (d)
- {
- }
-}
diff --git a/butl/multi-index b/butl/multi-index
deleted file mode 100644
index 15d8c14..0000000
--- a/butl/multi-index
+++ /dev/null
@@ -1,59 +0,0 @@
-// file : butl/multi-index -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_MULTI_INDEX
-#define BUTL_MULTI_INDEX
-
-#include <utility> // declval()
-#include <functional> // hash
-
-namespace butl
-{
- // Google the "Emulating Boost.MultiIndex with Standard Containers" blog
- // post for details.
- //
-
- template <typename T>
- struct map_key
- {
- mutable const T* p;
-
- map_key (const T* v = 0): p (v) {}
- bool operator< (const map_key& x) const {return *p < *x.p;}
- bool operator== (const map_key& x) const {return *p == *x.p;}
- };
-
- template <typename I>
- struct map_iterator_adapter: I
- {
- typedef const typename I::value_type::second_type value_type;
- typedef value_type* pointer;
- typedef value_type& reference;
-
- map_iterator_adapter () {}
- map_iterator_adapter (I i): I (i) {}
-
- map_iterator_adapter&
- operator= (I i) {static_cast<I&> (*this) = i; return *this;}
-
- reference operator* () const {return I::operator* ().second;}
- pointer operator-> () const {return &I::operator-> ()->second;}
- };
-}
-
-namespace std
-{
- template <typename T>
- struct hash<butl::map_key<T>>: hash<T>
- {
- size_t
- operator() (butl::map_key<T> x) const
- noexcept (noexcept (declval<hash<T>> () (*x.p)))
- {
- return hash<T>::operator() (*x.p);
- }
- };
-}
-
-#endif // BUTL_MULTI_INDEX
diff --git a/butl/optional b/butl/optional
deleted file mode 100644
index f6cc15e..0000000
--- a/butl/optional
+++ /dev/null
@@ -1,96 +0,0 @@
-// file : butl/optional -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_OPTIONAL
-#define BUTL_OPTIONAL
-
-#include <utility> // move()
-#include <functional> // hash
-
-namespace butl
-{
- // Simple optional class template while waiting for std::optional.
- //
- struct nullopt_t {constexpr explicit nullopt_t (int) {}};
- constexpr const nullopt_t nullopt (1);
-
- template <typename T>
- class optional
- {
- public:
- typedef T value_type;
-
- constexpr optional (): value_ (), null_ (true) {} // VC14 needs value_().
- constexpr optional (nullopt_t): value_ (), null_ (true) {}
- constexpr optional (const T& v): value_ (v), null_ (false) {}
- constexpr optional (T&& v): value_ (std::move (v)), null_ (false) {}
-
- optional& operator= (nullopt_t) {value_ = T (); null_ = true; return *this;}
- optional& operator= (const T& v) {value_ = v; null_ = false; return *this;}
- optional& operator= (T&& v) {value_ = std::move (v); null_ = false; return *this;}
-
- T& value () {return value_;}
- const T& value () const {return value_;}
-
- T* operator-> () {return &value_;}
- const T* operator-> () const {return &value_;}
-
- T& operator* () {return value_;}
- const T& operator* () const {return value_;}
-
- bool has_value () const {return !null_;}
- explicit operator bool () const {return !null_;}
-
- private:
- T value_;
- bool null_;
- };
-
- template <typename T>
- inline auto
- operator== (const optional<T>& x, const optional<T>& y) -> decltype (*x == *y)
- {
- return static_cast<bool> (x) == static_cast<bool> (y) && (!x || *x == *y);
- }
-
- template <typename T>
- inline auto
- operator!= (const optional<T>& x, const optional<T>& y) -> decltype (x == y)
- {
- return !(x == y);
- }
-
- template <typename T>
- inline auto
- operator< (const optional<T>& x, const optional<T>& y) -> decltype (*x < *y)
- {
- bool px (x), py (y);
- return px < py || (px && py && *x < *y);
- }
-
- template <typename T>
- inline auto
- operator> (const optional<T>& x, const optional<T>& y) -> decltype (*x > *y)
- {
- return y < x;
- }
-}
-
-namespace std
-{
- template <typename T>
- struct hash<butl::optional<T>>: hash<T>
- {
- using argument_type = butl::optional<T>;
-
- size_t
- operator() (const butl::optional<T>& o) const
- noexcept (noexcept (hash<T> {} (*o)))
- {
- return o ? hash<T>::operator() (*o) : static_cast<size_t> (-3333);
- }
- };
-}
-
-#endif // BUTL_OPTIONAL
diff --git a/butl/pager b/butl/pager
deleted file mode 100644
index 320fe48..0000000
--- a/butl/pager
+++ /dev/null
@@ -1,88 +0,0 @@
-// file : butl/pager -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_PAGER
-#define BUTL_PAGER
-
-#include <string>
-#include <vector>
-#include <iostream>
-
-#include <butl/export>
-
-#include <butl/process>
-#include <butl/fdstream>
-
-namespace butl
-{
- // Try to run the output through a pager program, such as more or less (no
- // pun intended, less is used by default). If the default pager program is
- // used, then the output is indented so that 80-character long lines will
- // appear centered in the terminal. If the default pager program fails to
- // start, then the output is sent directly to STDOUT.
- //
- // If the pager program is specified and is empty, then no pager is used
- // and the output is sent directly to STDOUT.
- //
- // Throw std::system_error if there are problems with the pager program.
- //
- // Typical usage:
- //
- // try
- // {
- // pager p ("help for foo");
- // ostream& os (p.stream ());
- //
- // os << "Foo is such and so ...";
- //
- // if (!p.wait ())
- // ... // Pager program returned non-zero status.
- // }
- // catch (const std::system_error& e)
- // {
- // cerr << "pager error: " << e << endl;
- // }
- //
- class LIBBUTL_EXPORT pager: protected std::streambuf
- {
- public:
- ~pager () {wait (true);}
-
- // If verbose is true, then print (to STDERR) the pager command line.
- //
- pager (const std::string& name,
- bool verbose = false,
- const std::string* pager = nullptr,
- const std::vector<std::string>* pager_options = nullptr);
-
- std::ostream&
- stream () {return os_.is_open () ? os_ : std::cout;}
-
- bool
- wait (bool ignore_errors = false);
-
- // The streambuf output interface that implements indentation. You can
- // override it to implement custom output pre-processing.
- //
- protected:
- using int_type = std::streambuf::int_type;
- using traits_type = std::streambuf::traits_type;
-
- virtual int_type
- overflow (int_type);
-
- virtual int
- sync ();
-
- private:
- process p_;
- ofdstream os_;
-
- std::string indent_;
- int_type prev_ = '\n'; // Previous character.
- std::streambuf* buf_ = nullptr;
- };
-}
-
-#endif // BUTL_PAGER
diff --git a/butl/pager.cxx b/butl/pager.cxx
deleted file mode 100644
index 003c65c..0000000
--- a/butl/pager.cxx
+++ /dev/null
@@ -1,207 +0,0 @@
-// file : butl/pager.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <butl/pager>
-
-#ifndef _WIN32
-# include <unistd.h> // STDOUT_FILENO
-# include <sys/ioctl.h> // ioctl()
-
-# include <chrono>
-# include <thread> // this_thread::sleep_for()
-#else
-# include <butl/win32-utility>
-#endif
-
-#include <cstring> // strchr()
-#include <utility> // move()
-
-#include <butl/utility> // operator<<(ostream, exception),
- // throw_generic_error()
-#include <butl/fdstream> // fdclose()
-
-using namespace std;
-
-namespace butl
-{
- pager::
- pager (const string& name,
- bool verbose,
- const string* pager,
- const vector<string>* pager_options)
- // Create successfully exited process. Will "wait" for it if fallback to
- // non-interactive execution path.
- //
- : p_ (process_exit (0))
- {
- // If we are using the default pager, try to get the terminal width
- // so that we can center the output.
- //
- if (pager == nullptr)
- {
- size_t col (0);
-
-#ifndef _WIN32
-# ifdef TIOCGWINSZ
- struct winsize w;
- if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &w) == 0)
- col = static_cast<size_t> (w.ws_col);
-# endif
-#else
- // This works properly on PowerShell, while for the regular console
- // (cmd.exe) it always returns 80 columns.
- //
- CONSOLE_SCREEN_BUFFER_INFO w;
- if (GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &w))
- col = static_cast<size_t> (w.srWindow.Right - w.srWindow.Left + 1);
-#endif
- if (col > 80)
- indent_.assign ((col - 80) / 2, ' ');
- }
-
- vector<const char*> args;
- string prompt;
-
- if (pager != nullptr)
- {
- if (pager->empty ())
- return; // No pager should be used.
-
- args.push_back (pager->c_str ());
- }
- else
- {
- // By default try less (again, no pun intended).
- //
- prompt = "-Ps" + name + " (press q to quit, h for help)";
-
- args.push_back ("less");
- args.push_back ("-R"); // Handle ANSI color.
-
- args.push_back (prompt.c_str ());
- }
-
- // Add extra pager options.
- //
- if (pager_options != nullptr)
- for (const string& o: *pager_options)
- args.push_back (o.c_str ());
-
- args.push_back (nullptr);
-
- if (verbose)
- {
- for (const char* const* p (args.data ()); *p != nullptr; ++p)
- {
- if (p != args.data ())
- cerr << ' ';
-
- // Quote if empty or contains spaces.
- //
- bool q (**p == '\0' || strchr (*p, ' ') != nullptr);
-
- if (q)
- cerr << '"';
-
- cerr << *p;
-
- if (q)
- cerr << '"';
- }
- cerr << endl;
- }
-
- // Ignore errors and go without a pager unless the pager was specified
- // by the user.
- //
- try
- {
- p_ = process (args.data (), -1); // Redirect child's STDIN to a pipe.
-
- // Wait a bit and see if the pager has exited before reading anything
- // (e.g., because exec() couldn't find the program). If you know a
- // cleaner way to handle this, let me know (and no, a select()-based
- // approach doesn't work; the pipe is buffered and therefore is always
- // ready for writing).
- //
- // MINGW GCC 4.9 doesn't implement this_thread so use Win32 Sleep().
- //
-#ifndef _WIN32
- this_thread::sleep_for (chrono::milliseconds (50));
-#else
- Sleep (50);
-#endif
-
- bool r;
- if (p_.try_wait (r))
- {
- p_.out_fd.reset ();
-
- if (pager != nullptr)
- throw_generic_error (ECHILD);
- }
- else
- os_.open (move (p_.out_fd));
- }
- catch (const process_error& e)
- {
- if (e.child)
- {
- cerr << args[0] << ": unable to execute: " << e << endl;
- exit (1);
- }
-
- // Ignore unless it was a user-specified pager.
- //
- if (pager != nullptr)
- throw; // Re-throw as system_error.
- }
-
- // Setup the indentation machinery.
- //
- if (!indent_.empty ())
- buf_ = stream ().rdbuf (this);
- }
-
- bool pager::
- wait (bool ie)
- {
- // Teardown the indentation machinery.
- //
- if (buf_ != nullptr)
- {
- stream ().rdbuf (buf_);
- buf_ = nullptr;
- }
-
- // Prevent ofdstream::close() from throwing in the ignore errors mode.
- //
- if (ie)
- os_.exceptions (ofdstream::goodbit);
-
- os_.close ();
- return p_.wait (ie);
- }
-
- pager::int_type pager::
- overflow (int_type c)
- {
- if (prev_ == '\n' && c != '\n') // Don't indent blanks.
- {
- auto n (static_cast<streamsize> (indent_.size ()));
-
- if (buf_->sputn (indent_.c_str (), n) != n)
- return traits_type::eof ();
- }
-
- prev_ = c;
- return buf_->sputc (c);
- }
-
- int pager::
- sync ()
- {
- return buf_->pubsync ();
- }
-}
diff --git a/butl/path b/butl/path
deleted file mode 100644
index 6d054d5..0000000
--- a/butl/path
+++ /dev/null
@@ -1,1136 +0,0 @@
-// file : butl/path -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_PATH
-#define BUTL_PATH
-
-#include <string>
-#include <cstddef> // ptrdiff_t
-#include <utility> // move(), swap()
-#include <iterator>
-#include <exception>
-#include <functional> // hash
-
-#include <butl/export>
-
-#include <butl/utility>
-
-namespace butl
-{
- // Wish list/ideas for improvements.
- //
- // - Ability to convert to directory/leaf/base in-place, without dynamic
- // allocation. One idea is something like this:
- //
- // p -= "/*"; // directory
- // p -= "*/"; // leaf
- // p -= ".*"; // base
- //
- // - Faster normalize() implementation. In many cases (e.g., in build2)
- // the path is either already normal or the difference is just slashes
- // (i.e., there are no '.' or '..' components). So a fast path case
- // might be in order.
- //
- // - We duplicate the interface for path and dir_path while most of it
- // is common. Also, we can implicit-cast dir_path& to path& and use
- // non-dir-adapted implementation (see where we call K::cast()).
- //
-
- struct LIBBUTL_EXPORT invalid_path_base: public std::exception
- {
- virtual char const*
- what () const throw ();
- };
-
- template <typename C>
- struct invalid_basic_path: invalid_path_base
- {
- using string_type = std::basic_string<C>;
-
- string_type path;
-
- invalid_basic_path (const C* p): path (p) {}
- invalid_basic_path (const string_type& p): path (p) {}
- };
-
- template <typename C>
- struct path_traits
- {
- using string_type = std::basic_string<C>;
- using size_type = typename string_type::size_type;
-
- // Canonical directory and path seperators.
- //
-#ifdef _WIN32
- static const C directory_separator = '\\';
- static const C path_separator = ';';
-#else
- static C const directory_separator = '/';
- static C const path_separator = ':';
-#endif
-
- // Canonical and alternative directory separators. Canonical should be
- // first.
- //
-#ifdef _WIN32
- static constexpr const char* const directory_separators = "\\/";
-#else
- static constexpr const char* const directory_separators = "/";
-#endif
-
- // Directory separator tests. On some platforms there could be multiple
- // seperators. For example, on Windows we check for both '/' and '\'.
- //
- static bool
- is_separator (C c)
- {
-#ifdef _WIN32
- return c == '\\' || c == '/';
-#else
- return c == '/';
-#endif
- }
-
- // Return 1-based index in directory_separators string or 0 if not a
- // separator.
- //
- static size_type
- separator_index (C c)
- {
-#ifdef _WIN32
- return c == '\\' ? 1 : c == '/' ? 2 : 0;
-#else
- return c == '/' ? 1 : 0;
-#endif
- }
-
- static bool
- absolute (const string_type& s)
- {
- return absolute (s.c_str (), s.size ());
- }
-
- static bool
- absolute (const C* s, size_type n)
- {
-#ifdef _WIN32
- return n > 1 && s[1] == ':';
-#else
- return n != 0 && is_separator (s[0]);
-#endif
- }
-
- static bool
- current (const string_type& s)
- {
- return current (s.c_str (), s.size ());
- }
-
- static bool
- current (const C* s, size_type n)
- {
- return n == 1 && s[0] == '.';
- }
-
- static bool
- parent (const string_type& s)
- {
- return parent (s.c_str (), s.size ());
- }
-
- static bool
- parent (const C* s, size_type n)
- {
- return n == 2 && s[0] == '.' && s[1] == '.';
- }
-
- static bool
- root (const string_type& s)
- {
- return root (s.c_str (), s.size ());
- }
-
- static bool
- root (const C* s, size_type n)
- {
-#ifdef _WIN32
- return n == 2 && s[1] == ':';
-#else
- return n == 1 && is_separator (s[0]);
-#endif
- }
-
- static size_type
- find_separator (string_type const& s,
- size_type pos = 0,
- size_type n = string_type::npos)
- {
- if (n == string_type::npos)
- n = s.size ();
-
- const C* r (find_separator (s.c_str () + pos, n - pos));
- return r != nullptr ? r - s.c_str () : string_type::npos;
- }
-
- static const C*
- find_separator (const C* s, size_type n)
- {
- for (const C* e (s + n); s != e; ++s)
- {
- if (is_separator (*s))
- return s;
- }
-
- return nullptr;
- }
-
- static size_type
- rfind_separator (string_type const& s, size_type pos = string_type::npos)
- {
- if (pos == string_type::npos)
- pos = s.size ();
- else
- pos++;
-
- const C* r (rfind_separator (s.c_str (), pos));
- return r != nullptr ? r - s.c_str () : string_type::npos;
- }
-
- static const C*
- rfind_separator (const C* s, size_type n)
- {
- for (; n != 0; --n)
- {
- if (is_separator (s[n - 1]))
- return s + n - 1;
- }
-
- return nullptr;
- }
-
- // Return the position of '.' or npos if there is no extension.
- //
- static size_type
- find_extension (string_type const& s)
- {
- const C* r (find_extension (s.c_str (), s.size ()));
- return r != nullptr ? r - s.c_str () : string_type::npos;
- }
-
- static const C*
- find_extension (const C* s, size_type n)
- {
- size_type i (n);
-
- for (; i > 0; --i)
- {
- C c (s[i - 1]);
-
- if (c == '.')
- break;
-
- if (is_separator (c))
- {
- i = 0;
- break;
- }
- }
-
- // Weed out paths like ".txt" (and "/.txt") and "txt.".
- //
- if (i > 1 && !is_separator (s[i - 2]) && i != n)
- return s + i - 1;
- else
- return nullptr;
- }
-
- // Return the start of the leaf (last path component) in the path. Note
- // that the leaf will include the trailing separator, if any (i.e., the
- // leaf of /tmp/bar/ is bar/).
- //
- static size_type
- find_leaf (string_type const& s)
- {
- const C* r (find_leaf (s.c_str (), s.size ()));
- return r != nullptr ? r - s.c_str () : string_type::npos;
- }
-
- static const C*
- find_leaf (const C* s, size_type n)
- {
- const C* p;
- return n == 0
- ? nullptr
- : (p = rfind_separator (s, n - 1)) == nullptr ? s : ++p;
- }
-
- static int
- compare (string_type const& l, string_type const& r)
- {
- return compare (l.c_str (), l.size (), r.c_str (), r.size ());
- }
-
- // @@ Currently for case-insensitive filesystems (Windows) compare()
- // works properly only for ASCII.
- //
- static int
- compare (const C* l, size_type ln, const C* r, size_type rn)
- {
- for (size_type i (0), n (ln < rn ? ln : rn); i != n; ++i)
- {
-#ifdef _WIN32
- C lc (lcase (l[i])), rc (lcase (r[i]));
-#else
- C lc (l[i]), rc (r[i]);
-#endif
- if (is_separator (lc) && is_separator (rc))
- continue;
-
- if (lc < rc) return -1;
- if (lc > rc) return 1;
- }
-
- return ln < rn ? -1 : (ln > rn ? 1 : 0);
- }
-
- static void
- canonicalize (string_type& s)
- {
- //canonicalize (s.data (), s.size ()); // C++17
-
- for (size_t i (0), n (s.size ()); i != n; ++i)
- if (is_separator (s[i]) && s[i] != directory_separator)
- s[i] = directory_separator;
- }
-
- static void
- canonicalize (C* s, size_type n)
- {
- for (const C* e (s + n); s != e; ++s)
- if (is_separator (*s) && *s != directory_separator)
- *s = directory_separator;
- }
-
- // Get/set current working directory. Throw std::system_error to report
- // the underlying OS errors.
- //
- static string_type
- current_directory ();
-
- static void
- current_directory (string_type const&);
-
- // Return the user home directory. Throw std::system_error to report the
- // underlying OS errors.
- //
- static string_type
- home_directory ();
-
- // Return the temporary directory. Throw std::system_error to report the
- // underlying OS errors.
- //
- static string_type
- temp_directory ();
-
- // Return a temporary name. The name is constructed by starting with the
- // prefix followed by the process id following by a unique counter value
- // inside the process (MT-safe). Throw std::system_error to report the
- // underlying OS errors.
- //
- static string_type
- temp_name (string_type const& prefix);
-
- // Make the path real (by calling realpath(3)). Throw invalid_basic_path
- // if the path is invalid (e.g., some components do not exist) and
- // std::system_error to report other underlying OS errors.
- //
-#ifndef _WIN32
- static void
- realize (string_type&);
-#endif
-
- // Utilities.
- //
-#ifdef _WIN32
- static C
- tolower (C);
-
- static C
- toupper (C);
-#endif
- };
-
- // This implementation of a filesystem path has two types: path, which can
- // represent any path (file, directory, etc.) and dir_path, which is derived
- // from path. The internal representation of directories maintains a
- // trailing slash. However, it is ignored in path comparison, size, and
- // string spelling. For example:
- //
- // path p1 ("foo"); // File path.
- // path p2 ("bar/"); // Directory path.
- //
- // path p3 (p1 / p2); // Throw: p1 is not a directory.
- // path p4 (p2 / p1); // Ok, file "bar/foo".
- // path p5 (p2 / p2); // Ok, directory "bar/bar/".
- //
- // dir_path d1 ("foo"); // Directory path "foo/".
- // dir_path d2 ("bar\\"); // Directory path "bar\".
- //
- // dir_path d3 (d2 / d1); // "bar\\foo/"
- //
- // (p4 == d3); // true
- // d3.string (); // "bar\\foo"
- // d3.representation (); // "bar\\foo/"
- //
- template <typename C, typename K>
- class basic_path;
-
- template <typename C> struct any_path_kind;
- template <typename C> struct dir_path_kind;
-
- using path = basic_path<char, any_path_kind<char>>;
- using dir_path = basic_path<char, dir_path_kind<char>>;
- using invalid_path = invalid_basic_path<char>;
-
- // Cast from one path kind to another. Note that no checking is performed
- // (e.g., that there is a trailing slash if casting to dir_path) but the
- // representation is adjusted if necessary (e.g., the trailing slash is
- // added to dir_path if missing).
- //
- template <class P, class C, class K> P path_cast (const basic_path<C, K>&);
- template <class P, class C, class K> P path_cast (basic_path<C, K>&&);
-
- // Low-level path data storage. It is also by the implementation to pass
- // around initialized/valid paths.
- //
- template <typename C>
- struct path_data
- {
- using string_type = std::basic_string<C>;
- using size_type = typename string_type::size_type;
- using difference_type = typename string_type::difference_type;
-
- // The idea is as follows: path_ is always the "traditional" form; that
- // is, "/" for the root directory and "/tmp" (no trailing slash) for the
- // rest. This means we can return/store references to path_.
- //
- // Then we have tsep_ ("trailing separator") which is the size difference
- // between path_ and its "pure" part, that is, without any trailing
- // slashes, even for "/". So:
- //
- // tsep_ == -1 -- trailing slash in path_ (the "/" case)
- // tsep_ == 0 -- no trailing slash
- //
- // Finally, to represent non-root ("/") trailing slashes we use positive
- // tsep_ values. In this case tsep_ is interpreted as a 1-based index in
- // the path_traits::directory_separators string.
- //
- // Notes:
- // - If path_ is empty, then tsep_ can only be 0.
- // - We could have used a much narrower integer for tsep_.
- //
- string_type path_;
- difference_type tsep_;
-
- size_type
- _size () const {return path_.size () + (tsep_ < 0 ? -1 : 0);}
-
- void
- _swap (path_data& d) {path_.swap (d.path_); std::swap (tsep_, d.tsep_);}
-
- void
- _clear () {path_.clear (); tsep_ = 0;}
-
- // Constructors.
- //
- path_data (): tsep_ (0) {}
-
- path_data (string_type&& p, difference_type ts)
- : path_ (std::move (p)), tsep_ (path_.empty () ? 0 : ts) {}
-
- explicit
- path_data (string_type&& p)
- : path_ (std::move (p)), tsep_ (0)
- {
- size_type n (path_.size ()), i;
-
- if (n != 0 && (i = path_traits<C>::separator_index (path_[n - 1])) != 0)
- {
- if (n == 1) // The "/" case.
- tsep_ = -1;
- else
- {
- tsep_ = i;
- path_.pop_back ();
- }
- }
- }
- };
-
- template <typename C>
- struct any_path_kind
- {
- class base_type: protected path_data<C> // In essence protected path_data.
- {
- protected:
- using path_data<C>::path_data;
-
- base_type () = default;
- base_type (path_data<C>&& d): path_data<C> (std::move (d)) {}
- };
-
- using dir_type = basic_path<C, dir_path_kind<C>>;
-
- // Init and cast.
- //
- // If exact is true, return the path if the initialization was successful,
- // that is, the passed string is a valid path and no modifications were
- // necessary. Otherwise, return the empty object and leave the passed
- // string untouched.
- //
- // If extact is false, throw invalid_path if the string is not a valid
- // path (e.g., uses an unsupported path notation on Windows).
- //
- using data_type = path_data<C>;
- using string_type = std::basic_string<C>;
-
- static data_type
- init (string_type&&, bool exact = false);
-
- static void
- cast (data_type&) {}
- };
-
- template <typename C>
- struct dir_path_kind
- {
- using base_type = basic_path<C, any_path_kind<C>>;
- using dir_type = basic_path<C, dir_path_kind<C>>;
-
- // Init and cast.
- //
- using data_type = path_data<C>;
- using string_type = std::basic_string<C>;
-
- static data_type
- init (string_type&&, bool exact = false);
-
- static void
- cast (data_type&);
- };
-
- template <typename C, typename K>
- class basic_path: public K::base_type
- {
- public:
- using string_type = std::basic_string<C>;
- using size_type = typename string_type::size_type;
- using difference_type = typename string_type::difference_type;
- using traits = path_traits<C>;
-
- struct iterator;
- using reverse_iterator = std::reverse_iterator<iterator>;
-
- using base_type = typename K::base_type;
- using dir_type = typename K::dir_type;
-
- // Create a special empty path. Note that we have to provide our own
- // implementation rather than using '=default' to make clang allow
- // default-initialized const instances of this type.
- //
- basic_path () {}
-
- // Constructors that initialize a path from a string argument throw the
- // invalid_path exception if the string is not a valid path (e.g., uses
- // unsupported path notations on Windows).
- //
- explicit
- basic_path (C const* s): base_type (K::init (s)) {}
-
- basic_path (C const* s, size_type n)
- : base_type (K::init (string_type (s, n))) {}
-
- explicit
- basic_path (string_type s): base_type (K::init (std::move (s))) {}
-
- basic_path (const string_type& s, size_type n)
- : base_type (K::init (string_type (s, 0, n))) {}
-
- basic_path (const string_type& s, size_type p, size_type n)
- : base_type (K::init (string_type (s, p, n))) {}
-
- // Create a path using the exact string representation. If the string is
- // not a valid path or if it would require a modification, then empty path
- // is created instead and the passed string rvalue-reference is left
- // untouched. Note that no exception is thrown if the path is invalid. See
- // also representation()&& below.
- //
- enum exact_type {exact};
- basic_path (string_type&& s, exact_type)
- : base_type (K::init (std::move (s), true)) {}
-
- // Create a path as a sub-path identified by the [begin, end) range of
- // components.
- //
- basic_path (const iterator& begin, const iterator& end);
-
- basic_path (const reverse_iterator& rbegin, const reverse_iterator& rend)
- : basic_path (rend.base (), rbegin.base ()) {}
-
- void
- swap (basic_path& p) {this->_swap (p);}
-
- void
- clear () {this->_clear ();}
-
- // Get/set current working directory. Throw std::system_error to report
- // the underlying OS errors.
- //
- static dir_type
- current_directory () {return dir_type (traits::current_directory ());}
-
- static void
- current_directory (basic_path const&);
-
- // Return the user home directory. Throw std::system_error to report the
- // underlying OS errors.
- //
- static dir_type
- home_directory () {return dir_type (traits::home_directory ());}
-
- // Return the temporary directory. Throw std::system_error to report the
- // underlying OS errors.
- //
- static dir_type
- temp_directory () {return dir_type (traits::temp_directory ());}
-
- // Return a temporary path. The path is constructed by starting with the
- // temporary directory and then appending a path component consisting of
- // the prefix followed by the process id following by a unique counter
- // value inside the process. Throw std::system_error to report the
- // underlying OS errors.
- //
- static basic_path
- temp_path (const string_type& prefix)
- {
- return temp_directory () / traits::temp_name (prefix);
- }
-
- public:
- bool
- empty () const {return this->path_.empty ();}
-
- // Note that size does not include the trailing separator except for
- // the root case.
- //
- size_type
- size () const {return this->path_.size ();}
-
- // Return true if this path doesn't have any directories. Note that "/foo"
- // is not a simple path (it is "foo" in root directory) while "/" is (it
- // is the root directory).
- //
- bool
- simple () const;
-
- bool
- absolute () const;
-
- bool
- relative () const {return !absolute ();}
-
- bool
- root () const;
-
- // The following predicates return true for the "." and ".." paths,
- // respectively. Note that the result doesn't depend on the presence or
- // spelling of the trailing directory separator.
- //
- // Also note that the path must literally match the specified values rather
- // than be semantically current or parent. For example for paths "foo/.."
- // or "bar/../.." the predicates return false.
- //
- bool
- current () const;
-
- bool
- parent () const;
-
- // Test, based on the presence/absence of the trailing separator, if the
- // path is to a directory.
- //
- bool
- to_directory () const {return this->tsep_ != 0;}
-
- // Return true if *this is a sub-path of the specified path (i.e.,
- // the specified path is a prefix). Expects both paths to be
- // normalized. Note that this function returns true if the paths
- // are equal. Empty path is considered a prefix of any path.
- //
- bool
- sub (const basic_path&) const;
-
- // Return true if *this is a super-path of the specified path (i.e.,
- // the specified path is a suffix). Expects both paths to be
- // normalized. Note that this function returns true if the paths
- // are equal. Empty path is considered a suffix of any path.
- //
- bool
- sup (const basic_path&) const;
-
- public:
- // Return the path without the directory part. Leaf of a directory is
- // itself a directory (contains trailing slash). Leaf of a root is the
- // path itself.
- //
- basic_path
- leaf () const;
-
- // Return the path without the specified directory part. Throws
- // invalid_path if the directory is not a prefix of *this. Expects both
- // paths to be normalized.
- //
- basic_path
- leaf (basic_path const&) const;
-
- // Return the directory part of the path or empty path if there is no
- // directory. Directory of a root is an empty path.
- //
- dir_type
- directory () const;
-
- // Return the directory part of the path without the specified leaf part.
- // Throws invalid_path if the leaf is not a suffix of *this. Expects both
- // paths to be normalized.
- //
- dir_type
- directory (basic_path const&) const;
-
- // Return the root directory of the path or empty path if the directory is
- // not absolute.
- //
- dir_type
- root_directory () const;
-
- // Return the path without the extension, if any.
- //
- basic_path
- base () const;
-
- // Return the extension or NULL if not present. If not empty, then the
- // result starts with the character past the dot.
- //
- string_type
- extension () const;
-
- // Return the in-place pointer to extension or NULL if not present. If not
- // NULL, then the result points to the character past the dot but it is
- // legal to decrement it once to obtain the value with the dot.
- //
- const C*
- extension_cstring () const;
-
- // Return a path relative to the specified path that is equivalent
- // to *this. Throws invalid_path if a relative path cannot be derived
- // (e.g., paths are on different drives on Windows).
- //
- basic_path
- relative (basic_path) const;
-
- // Iteration over path components.
- //
- public:
- struct iterator
- {
- using value_type = string_type ;
- using pointer = string_type*;
- using reference = string_type ;
- using size_type = typename string_type::size_type;
- using difference_type = std::ptrdiff_t ;
- using iterator_category = std::bidirectional_iterator_tag ;
-
- using data_type = path_data<C>;
-
- iterator (): p_ (nullptr) {}
- iterator (const data_type* p, size_type b, size_type e)
- : p_ (p), b_ (b), e_ (e) {}
-
- iterator&
- operator++ ()
- {
- const string_type& s (p_->path_);
-
- // Position past trailing separator, if any.
- //
- b_ = e_ != string_type::npos && ++e_ != s.size ()
- ? e_
- : string_type::npos;
-
- // Find next trailing separator.
- //
- e_ = b_ != string_type::npos ? traits::find_separator (s, b_) : b_;
-
- return *this;
- }
-
- iterator&
- operator-- ()
- {
- const string_type& s (p_->path_);
-
- // Find the new end.
- //
- e_ = b_ == string_type::npos // Past end?
- ? (traits::is_separator (s.back ()) // Have trailing slash?
- ? s.size () - 1
- : string_type::npos)
- : b_ - 1;
-
- // Find the new begin.
- //
- b_ = e_ == 0 // Empty component?
- ? string_type::npos
- : traits::rfind_separator (s, e_ != string_type::npos ? e_ - 1 : e_);
-
- b_ = b_ == string_type::npos // First component?
- ? 0
- : b_ + 1;
-
- return *this;
- }
-
- iterator
- operator++ (int) {iterator r (*this); operator++ (); return r;}
-
- iterator
- operator-- (int) {iterator r (*this); operator-- (); return r;}
-
- string_type
- operator* () const
- {
- return string_type (p_->path_,
- b_,
- e_ != string_type::npos ? e_ - b_ : e_);
- }
-
- // Return the directory separator after this component or '\0' if there
- // is none. This, for example, can be used to determine if the last
- // component is a directory.
- //
- C
- separator () const
- {
- return e_ != string_type::npos
- ? p_->path_[e_]
- : (p_->tsep_ > 0
- ? path_traits<C>::directory_separators[p_->tsep_ - 1]
- : 0);
- }
-
- pointer operator-> () const = delete;
-
- friend bool
- operator== (const iterator& x, const iterator& y)
- {
- return x.p_ == y.p_ && x.b_ == y.b_ && x.e_ == y.e_;
- }
-
- friend bool
- operator!= (const iterator& x, const iterator& y) {return !(x == y);}
-
- private:
- friend class basic_path;
-
- // b - first character of component
- // e - separator after component (or npos if none)
- // b == npos && e == npos - one past last component (end)
- //
- const data_type* p_;
- size_type b_;
- size_type e_;
- };
-
- iterator begin () const;
- iterator end () const;
-
- reverse_iterator rbegin () const {return reverse_iterator (end ());}
- reverse_iterator rend () const {return reverse_iterator (begin ());}
-
- public:
- // Canonicalize the path and return *this. Canonicalization involves
- // converting all directory separators to the canonical form. Note that
- // multiple directory separators are not collapsed.
- //
- basic_path&
- canonicalize ();
-
- // Normalize the path and return *this. Normalization involves collapsing
- // the '.' and '..' directories if possible, collapsing multiple
- // directory separators, and converting all directory separators to the
- // canonical form. If cur_empty is true then collapse relative paths
- // representing the current directory (for example, '.', './', 'foo/..')
- // to an empty path. Otherwise convert it to the canonical form (./ on
- // POSIX systems). Note that a non-empty path cannot become an empty one
- // in the latter case.
- //
- // If actual is true, then for case-insensitive filesystems obtain the
- // actual spelling of the path. Only an absolute path can be actualized.
- // If a path component does not exist, then its (and all subsequent)
- // spelling is unchanged. This is a potentially expensive operation.
- // Normally one can assume that "well-known" directories (current, home,
- // etc.) are returned in their actual spelling.
- //
- basic_path&
- normalize (bool actual = false, bool cur_empty = false);
-
- // Make the path absolute using the current directory unless it is already
- // absolute. Return *this.
- //
- basic_path&
- complete ();
-
- // Make the path real, that is, absolute, normalized, and with resolved
- // symlinks. On POSIX systems this is accomplished with the call to
- // realpath(3). On Windows -- complete() and normalize(). Return *this.
- //
- basic_path&
- realize ();
-
- public:
- basic_path&
- operator/= (basic_path const&);
-
- // Combine a single path component (must not contain directory separators)
- // as a string, without first constructing the path object.
- //
- basic_path&
- operator/= (string_type const&);
-
- basic_path&
- operator/= (const C*);
-
- // Append to the end of the path (normally an extension, etc).
- //
- basic_path&
- operator+= (string_type const&);
-
- basic_path&
- operator+= (const C*);
-
- basic_path&
- operator+= (C);
-
- void
- append (const C*, size_type);
-
- // Note that comparison is case-insensitive if the filesystem is not
- // case-sensitive (e.g., Windows). And it ignored trailing slashes
- // except for the root case.
- //
- template <typename K1>
- int
- compare (const basic_path<C, K1>& x) const {
- return traits::compare (this->path_, x.path_);}
-
- public:
- // Path string and representation. The string does not contain the
- // trailing slash except for the root case. In other words, it is the
- // "traditional" spelling of the path that can be passed to system calls,
- // etc. Representation, on the other hand is the "precise" spelling that
- // includes the trailing slash, if any. One cannot always round-trip a
- // path using string() but can using representation(). Note also that
- // representation() returns a copy while string() returns a (tracking)
- // reference.
- //
- const string_type&
- string () const& {return this->path_;}
-
- string_type
- representation () const&;
-
- // Moves the underlying path string out of the path object. The path
- // object becomes empty. Usage: std::move (p).string ().
- //
- string_type
- string () && {string_type r; r.swap (this->path_); return r;}
-
- string_type
- representation () &&;
-
- // Trailing directory separator or '\0' if there is none.
- //
- C
- separator () const;
-
- // As above but return it as a (potentially empty) string.
- //
- string_type
- separator_string () const;
-
- // If possible, return a POSIX version of the path. For example, for a
- // Windows path in the form foo\bar this function will return foo/bar. If
- // it is not possible to create a POSIX version for this path (e.g.,
- // c:\foo), this function will throw the invalid_path exception.
- //
- string_type
- posix_string () const&;
-
- string_type
- posix_representation () const&;
-
- string_type
- posix_string () &&;
-
- string_type
- posix_representation () &&;
-
- // Implementation details.
- //
- protected:
- using data_type = path_data<C>;
-
- // Direct initialization without init()/cast().
- //
- explicit
- basic_path (data_type&& d): base_type (std::move (d)) {}
-
- using base_type::_size;
-
- // Common implementation for operator/= and operator+=.
- //
- void
- combine (const C*, size_type, difference_type);
-
- void
- combine (const C*, size_type);
-
- // Friends.
- //
- template <class C1, class K1>
- friend class basic_path;
-
- template <class C1, class K1, class K2>
- friend basic_path<C1, K1>
- path_cast_impl (const basic_path<C1, K2>&, basic_path<C1, K1>*);
-
- template <class C1, class K1, class K2>
- friend basic_path<C1, K1>
- path_cast_impl (basic_path<C1, K2>&&, basic_path<C1, K1>*);
- };
-
- template <typename C, typename K>
- inline basic_path<C, K>
- operator/ (const basic_path<C, K>& x, const basic_path<C, K>& y)
- {
- basic_path<C, K> r (x);
- r /= y;
- return r;
- }
-
- template <typename C, typename K>
- inline basic_path<C, K>
- operator+ (const basic_path<C, K>& x, const std::basic_string<C>& y)
- {
- basic_path<C, K> r (x);
- r += y;
- return r;
- }
-
- template <typename C, typename K>
- inline basic_path<C, K>
- operator+ (const basic_path<C, K>& x, const C* y)
- {
- basic_path<C, K> r (x);
- r += y;
- return r;
- }
-
- template <typename C, typename K>
- inline basic_path<C, K>
- operator+ (const basic_path<C, K>& x, C y)
- {
- basic_path<C, K> r (x);
- r += y;
- return r;
- }
-
- template <typename C, typename K1, typename K2>
- inline bool
- operator== (const basic_path<C, K1>& x, const basic_path<C, K2>& y)
- {
- return x.compare (y) == 0;
- }
-
- template <typename C, typename K1, typename K2>
- inline bool
- operator!= (const basic_path<C, K1>& x, const basic_path<C, K2>& y)
- {
- return !(x == y);
- }
-
- template <typename C, typename K1, typename K2>
- inline bool
- operator< (const basic_path<C, K1>& x, const basic_path<C, K2>& y)
- {
- return x.compare (y) < 0;
- }
-
- // Additional operators for certain path kind combinations.
- //
- template <typename C>
- inline basic_path<C, any_path_kind<C>>
- operator/ (const basic_path<C, dir_path_kind<C>>& x,
- const basic_path<C, any_path_kind<C>>& y)
- {
- basic_path<C, any_path_kind<C>> r (x);
- r /= y;
- return r;
- }
-
-
- // Note that the result of (foo / "bar") is always a path, even if foo
- // is dir_path. An idiom to force it dir_path is this:
- //
- // dir_path foo_bar (dir_path (foo) /= "bar");
- //
- template <typename C, typename K>
- inline basic_path<C, any_path_kind<C>>
- operator/ (const basic_path<C, K>& x, const std::basic_string<C>& y)
- {
- basic_path<C, any_path_kind<C>> r (x);
- r /= y;
- return r;
- }
-
- template <typename C, typename K>
- inline basic_path<C, any_path_kind<C>>
- operator/ (const basic_path<C, K>& x, const C* y)
- {
- basic_path<C, any_path_kind<C>> r (x);
- r /= y;
- return r;
- }
-
- // For operator<< (ostream) see the path-io header.
-}
-
-namespace std
-{
- template <typename C, typename K>
- struct hash<butl::basic_path<C, K>>: hash<basic_string<C>>
- {
- using argument_type = butl::basic_path<C, K>;
-
- size_t
- operator() (const butl::basic_path<C, K>& p) const noexcept
- {
- return hash<basic_string<C>>::operator() (p.string ());
- }
- };
-}
-
-#include <butl/path.ixx>
-#include <butl/path.txx>
-
-#endif // BUTL_PATH
diff --git a/butl/path-io b/butl/path-io
deleted file mode 100644
index 1909dd9..0000000
--- a/butl/path-io
+++ /dev/null
@@ -1,29 +0,0 @@
-// file : butl/path-io -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_PATH_IO
-#define BUTL_PATH_IO
-
-#include <ostream>
-
-#include <butl/path>
-
-namespace butl
-{
- // This is the default path IO implementation. The reason it is
- // separate is because one often wants a custom implementation.
- // For example, we may want to print paths as relative to the
- // working directory. Or we may want to print '~' for the home
- // directory prefix. Or we may want to print dir_path with a
- // trailing '/'.
- //
- template <typename C, typename K>
- inline std::basic_ostream<C>&
- operator<< (std::basic_ostream<C>& os, basic_path<C, K> const& p)
- {
- return os << p.string ();
- }
-}
-
-#endif // BUTL_PATH_IO
diff --git a/butl/path-map b/butl/path-map
deleted file mode 100644
index 0b6fd6b..0000000
--- a/butl/path-map
+++ /dev/null
@@ -1,120 +0,0 @@
-// file : butl/path-map -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_PATH_MAP
-#define BUTL_PATH_MAP
-
-#include <algorithm> // min()
-
-#include <butl/path>
-#include <butl/prefix-map>
-
-namespace butl
-{
- // prefix_map for filesystem paths
- //
- // Important: the paths should be normalized but can use different case
- // on case-insensitive platforms.
- //
- // Note that the path's representation of POSIX root ('/') is
- // inconsistent in that we have a trailing delimiter at the end of
- // the path (its "proper" representation would have been an empty
- // string but that would have clashed with empty paths). To work
- // around this snag, this implementation, during key comparison,
- // detects '/' and treats it as empty. Note that the map will
- // still store the key as you have first inserted it. So if you
- // want a particular representation (i.e., empty or '/'), pre-
- // populate the map with it.
- //
- template <typename C, typename K>
- struct compare_prefix<basic_path<C, K>>
- {
- typedef basic_path<C, K> key_type;
-
- typedef C delimiter_type;
- typedef typename key_type::string_type string_type;
- typedef typename key_type::size_type size_type;
- typedef typename key_type::traits traits_type;
-
- explicit
- compare_prefix (delimiter_type) {}
-
- bool
- operator() (const key_type& x, const key_type& y) const
- {
- const string_type& xs (x.string ());
- const string_type& ys (y.string ());
-
- return compare (xs.c_str (),
- root (xs) ? 0 : xs.size (),
- ys.c_str (),
- root (ys) ? 0 : ys.size ()) < 0;
- }
-
- bool
- prefix (const key_type& p, const key_type& k) const
- {
- const string_type& ps (p.string ());
- const string_type& ks (k.string ());
-
- return prefix (root (ps) ? string_type () : ps,
- root (ks) ? string_type () : ks);
- }
-
- protected:
- bool
- prefix (const string_type& p, const string_type& k) const
- {
- // The same code as in prefix_map but using our compare().
- //
- size_type pn (p.size ()), kn (k.size ());
- return pn == 0 || // Empty key is always a prefix.
- (pn <= kn &&
- compare (p.c_str (), pn, k.c_str (), pn == kn ? pn : pn + 1) == 0);
- }
-
- int
- compare (const C* x, size_type xn,
- const C* y, size_type yn) const
- {
- size_type n (std::min (xn, yn));
- int r (traits_type::compare (x, n, y, n));
-
- if (r == 0)
- {
- // Pretend there is a delimiter characters at the end of the
- // shorter string.
- //
- char xc (xn > n ? x[n] : (xn++, traits_type::directory_separator));
- char yc (yn > n ? y[n] : (yn++, traits_type::directory_separator));
- r = traits_type::compare (&xc, 1, &yc, 1);
-
- // If we are still equal, then compare the lengths.
- //
- if (r == 0)
- r = (xn == yn ? 0 : (xn < yn ? -1 : 1));
- }
-
- return r;
- }
-
- static bool
- root (const string_type& p)
- {
- return p.size () == 1 && key_type::traits::is_separator (p[0]);
- }
- };
-
- // Note that the delimiter character is not used (is_delimiter() from
- // path_traits is used instead).
- //
- template <typename T>
- using path_map = prefix_map<path, T, path::traits::directory_separator>;
-
- template <typename T>
- using dir_path_map =
- prefix_map<dir_path, T, dir_path::traits::directory_separator>;
-}
-
-#endif // BUTL_PATH_MAP
diff --git a/butl/path.cxx b/butl/path.cxx
deleted file mode 100644
index dfa812b..0000000
--- a/butl/path.cxx
+++ /dev/null
@@ -1,395 +0,0 @@
-// file : butl/path.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <butl/path>
-
-#ifdef _WIN32
-# include <butl/win32-utility>
-
-# include <io.h> // _find*()
-# include <stdlib.h> // _MAX_PATH, _wgetenv()
-# include <direct.h> // _[w]getcwd(), _[w]chdir()
-# include <shlobj.h> // SHGetFolderPath*(), CSIDL_PROFILE
-# include <winerror.h> // SUCCEEDED()
-#else
-# include <pwd.h> // struct passwd, getpwuid_r()
-# include <errno.h> // EINVAL
-# include <stdlib.h> // mbstowcs(), wcstombs(), realpath(), getenv()
-# include <limits.h> // PATH_MAX
-# include <unistd.h> // getcwd(), chdir()
-# include <string.h> // strlen(), strcpy()
-# include <sys/stat.h> // stat(), S_IS*
-# include <sys/types.h> // stat
-
-# include <vector>
-#endif
-
-#include <atomic>
-#include <cassert>
-#include <cstring> // strcpy()
-
-#include <butl/export>
-
-#include <butl/utility> // throw_*_error()
-#include <butl/process>
-
-#ifndef _WIN32
-# ifndef PATH_MAX
-# define PATH_MAX 4096
-# endif
-#endif
-
-using namespace std;
-
-#ifdef _WIN32
-using namespace butl::win32;
-#endif
-
-namespace butl
-{
- char const* invalid_path_base::
- what () const throw ()
- {
- return "invalid filesystem path";
- }
-
- //
- // char
- //
-
- template <>
- LIBBUTL_EXPORT path_traits<char>::string_type path_traits<char>::
- current_directory ()
- {
-#ifdef _WIN32
- char cwd[_MAX_PATH];
- if (_getcwd (cwd, _MAX_PATH) == 0)
- throw_generic_error (errno);
- cwd[0] = toupper (cwd[0]); // Canonicalize.
-#else
- char cwd[PATH_MAX];
- if (getcwd (cwd, PATH_MAX) == 0)
- throw_generic_error (errno);
-#endif
-
- return cwd;
- }
-
- template <>
- LIBBUTL_EXPORT void path_traits<char>::
- current_directory (string_type const& s)
- {
-#ifdef _WIN32
- // A path like 'C:', while being a root path in our terminology, is not as
- // such for Windows, that maintains current directory for each drive, and
- // so "change current directory to C:" means "change the process current
- // directory to current directory on the C drive". Changing it to the root
- // one of the drive requires the trailing directory separator to be
- // present.
- //
- string_type const& d (!root (s)
- ? s
- : string_type (s + directory_separator));
-
- if (_chdir (d.c_str ()) != 0)
- throw_generic_error (errno);
-#else
- if (chdir (s.c_str ()) != 0)
- throw_generic_error (errno);
-#endif
- }
-
-#ifndef _WIN32
- static const char*
- temp_directory ()
- {
- const char* dir (nullptr);
- const char* env[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr};
-
- for (auto e (env); dir == nullptr && *e != nullptr; ++e)
- dir = getenv (*e);
-
- if (dir == nullptr)
- dir = "/tmp";
-
- struct stat s;
- if (stat (dir, &s) != 0)
- throw_generic_error (errno);
-
- if (!S_ISDIR (s.st_mode))
- throw_generic_error (ENOTDIR);
-
- return dir;
- }
-
- static string
- home ()
- {
- if (const char* h = getenv ("HOME"))
- return h;
-
- // Struct passwd has 5 members that will use this buffer. Two are the
- // home directory and shell paths. The other three are the user login
- // name, password, and real name (comment). We expect them to fit into
- // PATH_MAX * 2.
- //
- char buf[PATH_MAX * 4];
-
- passwd pw;
- passwd* rpw;
-
- int r (getpwuid_r (getuid (), &pw, buf, sizeof (buf), &rpw));
- if (r == -1)
- throw_generic_error (errno);
-
- if (r == 0 && rpw == nullptr)
- // According to POSIX errno should be left unchanged if an entry is not
- // found.
- //
- throw_generic_error (ENOENT);
-
- return pw.pw_dir;
- }
-#endif
-
- template <>
- LIBBUTL_EXPORT path_traits<char>::string_type path_traits<char>::
- temp_directory ()
- {
-#ifdef _WIN32
- char d[_MAX_PATH + 1];
- if (GetTempPathA (_MAX_PATH + 1, d) == 0)
- throw_system_error (GetLastError ());
-
- return d;
-#else
- return butl::temp_directory ();
-#endif
- }
-
- static atomic<size_t> temp_name_count;
-
- template <>
- LIBBUTL_EXPORT path_traits<char>::string_type path_traits<char>::
- temp_name (string_type const& prefix)
- {
- return prefix
- + "-" + to_string (process::current_id ())
- + "-" + to_string (temp_name_count++);
- }
-
- template <>
- LIBBUTL_EXPORT path_traits<char>::string_type path_traits<char>::
- home_directory ()
- {
-#ifndef _WIN32
- return home ();
-#else
- // Could be set by, e.g., MSYS and Cygwin shells.
- //
- if (const char* h = getenv ("HOME"))
- return h;
-
- char h[_MAX_PATH];
- HRESULT r (SHGetFolderPathA (NULL, CSIDL_PROFILE, NULL, 0, h));
-
- if (!SUCCEEDED (r))
- throw_system_error (r);
-
- return h;
-#endif
- }
-
-#ifndef _WIN32
- template <>
- LIBBUTL_EXPORT void path_traits<char>::
- realize (string_type& s)
- {
- char r[PATH_MAX];
- if (realpath (s.c_str (), r) == nullptr)
- {
- // @@ If there were a message in invalid_basic_path, we could have said
- // what exactly is wrong with the path.
- //
- if (errno == EACCES || errno == ENOENT || errno == ENOTDIR)
- throw invalid_basic_path<char> (s);
- else
- throw_generic_error (errno);
- }
-
- s = r;
- }
-#endif
-
- //
- // wchar_t
- //
-
- template <>
- LIBBUTL_EXPORT path_traits<wchar_t>::string_type path_traits<wchar_t>::
- current_directory ()
- {
-#ifdef _WIN32
- wchar_t wcwd[_MAX_PATH];
- if (_wgetcwd (wcwd, _MAX_PATH) == 0)
- throw_generic_error (errno);
- wcwd[0] = toupper (wcwd[0]); // Canonicalize.
-#else
- char cwd[PATH_MAX];
- if (getcwd (cwd, PATH_MAX) == 0)
- throw_generic_error (errno);
-
- wchar_t wcwd[PATH_MAX];
- if (mbstowcs (wcwd, cwd, PATH_MAX) == size_type (-1))
- throw_generic_error (EINVAL);
-#endif
-
- return wcwd;
- }
-
- template <>
- LIBBUTL_EXPORT void path_traits<wchar_t>::
- current_directory (string_type const& s)
- {
-#ifdef _WIN32
- // Append the trailing directory separator for the root directory (read
- // the comment in path_traits<char>::current_directory() for
- // justification).
- //
- string_type const& d (!root (s)
- ? s
- : string_type (s + directory_separator));
-
- if (_wchdir (d.c_str ()) != 0)
- throw_generic_error (errno);
-#else
- char ns[PATH_MAX + 1];
-
- if (wcstombs (ns, s.c_str (), PATH_MAX) == size_type (-1))
- throw_generic_error (EINVAL);
-
- ns[PATH_MAX] = '\0';
-
- if (chdir (ns) != 0)
- throw_generic_error (errno);
-#endif
- }
-
- template <>
- LIBBUTL_EXPORT path_traits<wchar_t>::string_type path_traits<wchar_t>::
- temp_directory ()
- {
-#ifdef _WIN32
- wchar_t d[_MAX_PATH + 1];
- if (GetTempPathW (_MAX_PATH + 1, d) == 0)
- throw_system_error (GetLastError ());
-#else
- wchar_t d[PATH_MAX];
-
- // The usage of mbstowcs() supposes the program's C-locale is set to the
- // proper locale before the call (can be done with setlocale(LC_ALL, "...")
- // call). Otherwise mbstowcs() fails with EILSEQ errno for non-ASCII
- // directory paths.
- //
- size_t r (mbstowcs (d, butl::temp_directory (), PATH_MAX));
-
- if (r == size_t (-1))
- throw_generic_error (EINVAL);
-
- if (r == PATH_MAX)
- throw_generic_error (ENOTSUP);
-#endif
-
- return d;
- }
-
- template <>
- LIBBUTL_EXPORT path_traits<wchar_t>::string_type path_traits<wchar_t>::
- temp_name (string_type const& prefix)
- {
- return prefix +
- L"-" + to_wstring (process::current_id ()) +
- L"-" + to_wstring (temp_name_count++);
- }
-
- template <>
- LIBBUTL_EXPORT path_traits<wchar_t>::string_type path_traits<wchar_t>::
- home_directory ()
- {
-#ifndef _WIN32
- wchar_t d[PATH_MAX];
- size_t r (mbstowcs (d, home ().c_str (), PATH_MAX));
-
- if (r == size_t (-1))
- throw_generic_error (EINVAL);
-
- if (r == PATH_MAX)
- throw_generic_error (ENOTSUP);
-
- return d;
-#else
- // Could be set by, e.g., MSYS and Cygwin shells.
- //
- if (const wchar_t* h = _wgetenv (L"HOME"))
- return h;
-
- wchar_t h[_MAX_PATH];
- HRESULT r (SHGetFolderPathW (NULL, CSIDL_PROFILE, NULL, 0, h));
-
- if (!SUCCEEDED (r))
- throw_system_error (r);
-
- return h;
-#endif
- }
-
-#ifndef _WIN32
- template <>
- LIBBUTL_EXPORT void path_traits<wchar_t>::
- realize (string_type&)
- {
- assert (false); // Implement if/when needed.
- }
-#endif
-
-#ifdef _WIN32
- template <>
- LIBBUTL_EXPORT bool
- basic_path_append_actual_name<char> (string& r,
- const string& d,
- const string& n)
- {
- assert (d.size () + n.size () + 1 < _MAX_PATH);
-
- char p[_MAX_PATH];
- strcpy (p, d.c_str ());
- p[d.size ()] = '\\';
- strcpy (p + d.size () + 1, n.c_str ());
-
- // It could be that using FindFirstFile() is faster.
- //
- _finddata_t fi;
- intptr_t h (_findfirst (p, &fi));
-
- if (h == -1 && errno == ENOENT)
- return false;
-
- if (h == -1 || _findclose (h) == -1)
- throw_generic_error (errno);
-
- r += fi.name;
- return true;
- }
-
- template <>
- LIBBUTL_EXPORT bool
- basic_path_append_actual_name<wchar_t> (wstring&,
- const wstring&,
- const wstring&)
- {
- assert (false); // Implement if/when needed.
- return false;
- }
-#endif
-}
diff --git a/butl/path.ixx b/butl/path.ixx
deleted file mode 100644
index b60f206..0000000
--- a/butl/path.ixx
+++ /dev/null
@@ -1,508 +0,0 @@
-// file : butl/path.ixx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifdef _WIN32
-# include <cwctype> // towlower(), towupper()
-#endif
-
-namespace butl
-{
-#ifdef _WIN32
- template <>
- inline char path_traits<char>::
- tolower (char c)
- {
- return lcase (c);
- }
-
- template <>
- inline wchar_t path_traits<wchar_t>::
- tolower (wchar_t c)
- {
- return std::towlower (c);
- }
-
- template <>
- inline char path_traits<char>::
- toupper (char c)
- {
- return ucase (c);
- }
-
- template <>
- inline wchar_t path_traits<wchar_t>::
- toupper (wchar_t c)
- {
- return std::towupper (c);
- }
-#endif
-
- template <class C, class K1, class K2>
- inline basic_path<C, K1>
- path_cast_impl (const basic_path<C, K2>& p, basic_path<C, K1>*)
- {
- typename basic_path<C, K1>::data_type d (
- typename basic_path<C, K1>::string_type (p.path_), p.tsep_);
- K1::cast (d);
- return basic_path<C, K1> (std::move (d));
- }
-
- template <class C, class K1, class K2>
- inline basic_path<C, K1>
- path_cast_impl (basic_path<C, K2>&& p, basic_path<C, K1>*)
- {
- typename basic_path<C, K1>::data_type d (std::move (p.path_), p.tsep_);
- K1::cast (d);
- return basic_path<C, K1> (std::move (d));
- }
-
- template <class P, class C, class K>
- inline P
- path_cast (const basic_path<C, K>& p)
- {
- return path_cast_impl (p, static_cast<P*> (nullptr));
- }
-
- template <class P, class C, class K>
- inline P
- path_cast (basic_path<C, K>&& p)
- {
- return path_cast_impl (std::move (p), static_cast<P*> (nullptr));
- }
-
- template <typename C, typename K>
- inline bool basic_path<C, K>::
- simple () const
- {
- return empty () ||
- traits::rfind_separator (this->path_, _size () - 1) == string_type::npos;
- }
-
- template <typename C, typename K>
- inline bool basic_path<C, K>::
- absolute () const
- {
- return traits::absolute (this->path_);
- }
-
- template <typename C, typename K>
- inline bool basic_path<C, K>::
- current () const
- {
- return traits::current (this->path_);
- }
-
- template <typename C, typename K>
- inline bool basic_path<C, K>::
- parent () const
- {
- return traits::parent (this->path_);
- }
-
- template <typename C, typename K>
- inline bool basic_path<C, K>::
- root () const
- {
- return traits::root (this->path_);
- }
-
- template <typename C, typename K>
- inline bool basic_path<C, K>::
- sub (const basic_path& p) const
- {
- // The thinking here is that we can use the full string representations
- // (including the trailing slash in "/").
- //
- const string_type& ps (p.path_);
- size_type pn (ps.size ());
-
- if (pn == 0)
- return true;
-
- const string_type& s (this->path_);
- size_type n (s.size ());
-
- // The second condition guards against the /foo-bar vs /foo case.
- //
- return n >= pn &&
- traits::compare (s.c_str (), pn, ps.c_str (), pn) == 0 &&
- (traits::is_separator (ps.back ()) || // p ends with a separator
- n == pn || // *this == p
- traits::is_separator (s[pn])); // next char is a separator
- }
-
- template <typename C, typename K>
- inline bool basic_path<C, K>::
- sup (const basic_path& p) const
- {
- // The thinking here is that we can use the full string representations
- // (including the trailing slash in "/").
- //
- const string_type& ps (p.path_);
- size_type pn (ps.size ());
-
- if (pn == 0)
- return true;
-
- const string_type& s (this->path_);
- size_type n (s.size ());
-
- // The second condition guards against the /foo-bar vs bar case.
- //
- return n >= pn &&
- traits::compare (s.c_str () + n - pn, pn, ps.c_str (), pn) == 0 &&
- (n == pn || // *this == p
- traits::is_separator (s[n - pn - 1])); // previous char is a separator
- }
-
- template <typename C, typename K>
- inline basic_path<C, K> basic_path<C, K>::
- leaf () const
- {
- const string_type& s (this->path_);
- size_type n (_size ());
-
- size_type p (n != 0
- ? traits::rfind_separator (s, n - 1)
- : string_type::npos);
-
- return p != string_type::npos
- ? basic_path (data_type (string_type (s, p + 1), this->tsep_))
- : *this;
- }
-
- template <typename C, typename K>
- inline typename basic_path<C, K>::dir_type basic_path<C, K>::
- directory () const
- {
- const string_type& s (this->path_);
- size_type n (_size ());
-
- size_type p (n != 0
- ? traits::rfind_separator (s, n - 1)
- : string_type::npos);
-
- return p != string_type::npos
- ? dir_type (data_type (string_type (s, 0, p + 1))) // Include slash.
- : dir_type ();
- }
-
- template <typename C, typename K>
- inline auto basic_path<C, K>::
- begin () const -> iterator
- {
- const string_type& s (this->path_);
-
- size_type b (s.empty () ? string_type::npos : 0);
- size_type e (b == 0 ? traits::find_separator (s) : b);
-
- return iterator (this, b, e);
- }
-
- template <typename C, typename K>
- inline auto basic_path<C, K>::
- end () const -> iterator
- {
- return iterator (this, string_type::npos, string_type::npos);
- }
-
- template <typename C, typename K>
- inline basic_path<C, K>::
- basic_path (const iterator& b, const iterator& e)
- : base_type (
- b == e
- ? data_type ()
- // We need to include the trailing separator but it is implied if
- // e == end().
- //
- : (e.b_ != string_type::npos
- ? data_type (string_type (b.p_->path_, b.b_, e.b_ - b.b_))
- : data_type (string_type (b.p_->path_, b.b_), b.p_->tsep_)))
- {
- //assert (b.p_ == e.p_);
- }
-
- template <typename C, typename K>
- inline basic_path<C, K>& basic_path<C, K>::
- canonicalize ()
- {
- traits::canonicalize (this->path_);
-
- if (this->tsep_ > 1) // Non-canonical trailing separator.
- this->tsep_ = 1;
-
- return *this;
- }
-
- template <typename C, typename K>
- inline basic_path<C, K>& basic_path<C, K>::
- complete ()
- {
- if (relative ())
- *this = current_directory () / *this;
-
- return *this;
- }
-
- template <typename C, typename K>
- inline basic_path<C, K>& basic_path<C, K>::
- realize ()
- {
-#ifdef _WIN32
- // This is not exactly the semantics of realpath(3). In particular, we
- // don't fail if the path does not exist. But we could have seeing that
- // we actualize it.
- //
- complete ();
- normalize (true);
-#else
- traits::realize (this->path_); // Note: we retain the trailing slash.
-#endif
- return *this;
- }
-
- template <typename C, typename K>
- inline typename basic_path<C, K>::dir_type basic_path<C, K>::
- root_directory () const
- {
-#ifdef _WIN32
- // Note: on Windows we may have "c:" but still need to return "c:\".
- //
- const string_type& s (this->path_);
-
- return absolute ()
- ? dir_type (
- s.size () > 2
- ? data_type (string_type (s, 0, 3))
- : data_type (string_type (s), this->tsep_ != 0 ? this->tsep_ : 1))
- : dir_type ();
-#else
- return absolute ()
- ? dir_type (data_type ("/", -1))
- : dir_type ();
-#endif
-
- }
-
- template <typename C, typename K>
- inline basic_path<C, K> basic_path<C, K>::
- base () const
- {
- const string_type& s (this->path_);
- size_type p (traits::find_extension (s));
-
- return p != string_type::npos
- ? basic_path (data_type (string_type (s, 0, p), this->tsep_))
- : *this;
- }
-
- template <typename C, typename K>
- inline typename basic_path<C, K>::string_type basic_path<C, K>::
- extension () const
- {
- const string_type& s (this->path_);
- size_type p (traits::find_extension (s));
- return p != string_type::npos
- ? string_type (s.c_str () + p + 1)
- : string_type ();
- }
-
- template <typename C, typename K>
- inline const C* basic_path<C, K>::
- extension_cstring () const
- {
- const string_type& s (this->path_);
- size_type p (traits::find_extension (s));
- return p != string_type::npos ? s.c_str () + p + 1 : nullptr;
- }
-
-#ifndef _WIN32
- template <typename C, typename K>
- inline typename basic_path<C, K>::string_type basic_path<C, K>::
- posix_string () const&
- {
- return string ();
- }
-
- template <typename C, typename K>
- inline typename basic_path<C, K>::string_type basic_path<C, K>::
- posix_string () &&
- {
- return std::move (*this).string ();
- }
-
- template <typename C, typename K>
- inline typename basic_path<C, K>::string_type basic_path<C, K>::
- posix_representation () const&
- {
- return representation ();
- }
-
- template <typename C, typename K>
- inline typename basic_path<C, K>::string_type basic_path<C, K>::
- posix_representation () &&
- {
- return std::move (*this).representation ();
- }
-#endif
-
- template <typename C, typename K>
- inline void basic_path<C, K>::
- combine (const C* r, size_type rn, difference_type rts)
- {
- //assert (rn != 0);
-
- string_type& l (this->path_);
- difference_type& ts (this->tsep_);
-
- // Handle the separator. LHS should be empty or already have one.
- //
- switch (ts)
- {
- case 0: if (!l.empty ()) throw invalid_basic_path<C> (l); break;
- case -1: break; // Already in the string.
- default: l += path_traits<C>::directory_separators[ts - 1];
- }
-
- l.append (r, rn);
- ts = rts; // New trailing separator from RHS.
- }
-
- template <typename C, typename K>
- inline void basic_path<C, K>::
- combine (const C* r, size_type rn)
- {
- // If we do (dir_path / path) then we will end up with path. What should
- // we end up if we do (dir_path / "foo") vs (dir_path / "foo/")? We cannot
- // choose at runtime what kind of path to return. One (elaborate) option
- // would be to handle the trailing slash but also call K::cast() so that
- // dir_path gets the canonical trailing slash if one wasn't there.
- //
- // For now we won't allow the slash and will always add the canonical one
- // for dir_path (via cast()).
- //
- if (traits::find_separator (r, rn) != nullptr)
- throw invalid_basic_path<C> (r);
-
- combine (r, rn, 0);
- K::cast (*this);
- }
-
- template <typename C, typename K>
- inline basic_path<C, K>& basic_path<C, K>::
- operator/= (basic_path<C, K> const& r)
- {
- if (r.absolute () && !empty ()) // Allow ('' / '/foo').
- throw invalid_basic_path<C> (r.path_);
-
- if (!r.empty ())
- combine (r.path_.c_str (), r.path_.size (), r.tsep_);
-
- return *this;
- }
-
- template <typename C, typename K>
- inline basic_path<C, K>& basic_path<C, K>::
- operator/= (string_type const& r)
- {
- if (size_type rn = r.size ())
- combine (r.c_str (), rn);
-
- return *this;
- }
-
- template <typename C, typename K>
- inline basic_path<C, K>& basic_path<C, K>::
- operator/= (const C* r)
- {
- if (size_type rn = string_type::traits_type::length (r))
- combine (r, rn);
-
- return *this;
- }
-
- template <typename C, typename K>
- inline void basic_path<C, K>::
- append (const C* r, size_type rn)
- {
- //assert (this->tsep_ != -1); // Append to root?
- this->path_.append (r, rn);
- }
-
- template <typename C, typename K>
- inline basic_path<C, K>& basic_path<C, K>::
- operator+= (string_type const& s)
- {
- append (s.c_str (), s.size ());
- return *this;
- }
-
- template <typename C, typename K>
- inline basic_path<C, K>& basic_path<C, K>::
- operator+= (const C* s)
- {
- append (s, string_type::traits_type::length (s));
- return *this;
- }
-
- template <typename C, typename K>
- inline basic_path<C, K>& basic_path<C, K>::
- operator+= (C c)
- {
- append (&c, 1);
- return *this;
- }
-
- template <typename C, typename K>
- inline auto basic_path<C, K>::
- representation () const& -> string_type
- {
- string_type r (this->path_);
-
- if (this->tsep_ > 0)
- r += path_traits<C>::directory_separators[this->tsep_ - 1];
-
- return r;
- }
-
- template <typename C, typename K>
- inline auto basic_path<C, K>::
- representation () && -> string_type
- {
- string_type r;
- r.swap (this->path_);
-
- if (this->tsep_ > 0)
- r += path_traits<C>::directory_separators[this->tsep_ - 1];
-
- return r;
- }
-
- template <typename C, typename K>
- inline C basic_path<C, K>::
- separator () const
- {
- return (this->tsep_ == 0 ? 0 :
- this->tsep_ == -1 ? this->path_[0] :
- path_traits<C>::directory_separators[this->tsep_ - 1]);
- }
-
- template <typename C, typename K>
- inline auto basic_path<C, K>::
- separator_string () const -> string_type
- {
- C c (separator ());
- return c == 0 ? string_type () : string_type (1, c);
- }
-
- template <typename C>
- inline void dir_path_kind<C>::
- cast (data_type& d)
- {
- // Add trailing slash if one isn't already there.
- //
- if (!d.path_.empty () && d.tsep_ == 0)
- d.tsep_ = 1; // Canonical separator is always first.
- }
-}
diff --git a/butl/path.txx b/butl/path.txx
deleted file mode 100644
index 3edd076..0000000
--- a/butl/path.txx
+++ /dev/null
@@ -1,377 +0,0 @@
-// file : butl/path.txx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <vector>
-#include <cassert>
-
-#ifdef _WIN32
-# include <algorithm> // replace()
-#endif
-
-namespace butl
-{
- template <typename C, typename K>
- basic_path<C, K> basic_path<C, K>::
- leaf (basic_path<C, K> const& d) const
- {
- size_type dn (d.path_.size ());
-
- if (dn == 0)
- return *this;
-
- const string_type& s (this->path_);
-
- if (!sub (d))
- throw invalid_basic_path<C> (s);
-
- // If there is implied trailing slash, add it to count. Unless it is
- // "matched" by the implied slash on the other side.
- //
- if (d.tsep_ > 0 && dn < s.size ())
- dn++;
-
- // Preserve trailing slash.
- //
- return basic_path (data_type (string_type (s, dn, s.size () - dn),
- this->tsep_));
- }
-
- template <typename C, typename K>
- typename basic_path<C, K>::dir_type basic_path<C, K>::
- directory (basic_path<C, K> const& l) const
- {
- size_type ln (l.path_.size ());
-
- const string_type& s (this->path_);
-
- if (ln == 0)
- {
- if (this->tsep_ == 0) // Must be a directory.
- throw invalid_basic_path<C> (s);
-
- return dir_type (data_type (string_type (s), this->tsep_));
- }
-
- if (!sup (l))
- throw invalid_basic_path<C> (s);
-
- return dir_type (
- data_type (string_type (s, 0, s.size () - ln))); // Include slash.
- }
-
-#ifdef _WIN32
- template <typename C, typename K>
- typename basic_path<C, K>::string_type basic_path<C, K>::
- posix_string () const&
- {
- if (absolute ())
- throw invalid_basic_path<C> (this->path_);
-
- string_type r (string ());
- replace (r.begin (), r.end (), '\\', '/');
- return r;
- }
-
- template <typename C, typename K>
- typename basic_path<C, K>::string_type basic_path<C, K>::
- posix_string () &&
- {
- if (absolute ())
- throw invalid_basic_path<C> (this->path_);
-
- string_type r (std::move (*this).string ());
- replace (r.begin (), r.end (), '\\', '/');
- return r;
- }
-
- template <typename C, typename K>
- typename basic_path<C, K>::string_type basic_path<C, K>::
- posix_representation () const&
- {
- if (absolute ())
- throw invalid_basic_path<C> (this->path_);
-
- string_type r (representation ());
- replace (r.begin (), r.end (), '\\', '/');
- return r;
- }
-
- template <typename C, typename K>
- typename basic_path<C, K>::string_type basic_path<C, K>::
- posix_representation () &&
- {
- if (absolute ())
- throw invalid_basic_path<C> (this->path_);
-
- string_type r (std::move (*this).representation ());
- replace (r.begin (), r.end (), '\\', '/');
- return r;
- }
-#endif
-
- template <typename C, typename K>
- basic_path<C, K> basic_path<C, K>::
- relative (basic_path<C, K> d) const
- {
- dir_type r;
-
- for (;; d = d.directory ())
- {
- if (sub (d))
- break;
-
- r /= "..";
-
- // Roots of the paths do not match.
- //
- if (d.root ())
- throw invalid_basic_path<C> (this->path_);
- }
-
- return r / leaf (d);
- }
-
-#ifdef _WIN32
- // Find the actual spelling of a name in the specified dir. If the name is
- // found, append it to the result and return true. Otherwise, return false.
- // Throw system_error in case of other failures. Result and dir can be the
- // same instance.
- //
- template <typename C>
- bool
- basic_path_append_actual_name (std::basic_string<C>& result,
- const std::basic_string<C>& dir,
- const std::basic_string<C>& name);
-#endif
-
- template <typename C, typename K>
- basic_path<C, K>& basic_path<C, K>::
- normalize (bool actual, bool cur_empty)
- {
- if (empty ())
- return *this;
-
- bool abs (absolute ());
- assert (!actual || abs); // Only absolue can be actualized.
-
- string_type& s (this->path_);
- difference_type& ts (this->tsep_);
-
- typedef std::vector<string_type> paths;
- paths ps;
-
- bool tsep (ts != 0); // Trailing directory separator.
- {
- size_type n (_size ());
-
- for (size_type b (0), e (traits::find_separator (s, 0, n));
- ;
- e = traits::find_separator (s, b, n))
- {
- ps.push_back (
- string_type (s, b, (e == string_type::npos ? n : e) - b));
-
- if (e == string_type::npos)
- break;
-
- ++e;
-
- // Skip consecutive directory separators.
- //
- while (e != n && traits::is_separator (s[e]))
- ++e;
-
- if (e == n)
- break;
-
- b = e;
- }
-
- // If the last component is "." or ".." then this is a directory.
- //
- if (!tsep)
- {
- const string_type& l (ps.back ());
- if (traits::current (l) || traits::parent (l))
- tsep = true;
- }
- }
-
- // Collapse "." and "..".
- //
- paths r;
-
- for (typename paths::iterator i (ps.begin ()), e (ps.end ()); i != e; ++i)
- {
- string_type& s (*i);
-
- if (traits::current (s))
- continue;
-
- // If '..' then pop the last directory from r unless it is '..'.
- //
- if (traits::parent (s) && !r.empty () && !traits::parent (r.back ()))
- {
- // Cannot go past the root directory.
- //
- if (abs && r.size () == 1)
- throw invalid_basic_path<C> (this->path_);
-
- r.pop_back ();
- continue;
- }
-
- r.push_back (std::move (s));
- }
-
- // Reassemble the path, actualizing each component if requested.
- //
- string_type p;
-
- for (typename paths::const_iterator b (r.begin ()), i (b), e (r.end ());
- i != e;)
- {
-#ifdef _WIN32
- if (actual)
- {
- if (i == b)
- {
- // The first component (the drive letter) we have to actualize
- // ourselves. Capital seems to be canonical. This is, for example,
- // what getcwd() returns.
- //
- p = *i;
- p[0] = traits::toupper (p[0]);
- }
- else
- {
- if (!basic_path_append_actual_name (p, p, *i))
- {
- p += *i;
- actual = false; // Ignore for all subsequent components.
- }
- }
- }
- else
-#endif
- p += *i;
-
- if (++i != e)
- p += traits::directory_separator;
- }
-
- if (tsep)
- {
- if (p.empty ())
- {
- // Distinguish "/"-empty and "."-empty.
- //
- if (abs)
- {
- p += traits::directory_separator;
- ts = -1;
- }
- else if (!cur_empty) // Collapse to canonical current directory.
- {
- p.assign (1, '.');
- ts = 1; // Canonical separator is always first.
- }
- else // Collapse to empty path.
- ts = 0;
- }
- else
- ts = 1; // Canonical separator is always first.
- }
- else
- ts = 0;
-
- s.swap (p);
- return *this;
- }
-
- template <typename C, typename K>
- void basic_path<C, K>::
- current_directory (basic_path const& p)
- {
- const string_type& s (p.string ());
-
- if (s.empty ())
- throw invalid_basic_path<char> (s);
-
- traits::current_directory (s);
- }
-
- template <typename C>
- auto any_path_kind<C>::
- init (string_type&& s, bool exact) -> data_type
- {
- using size_type = typename string_type::size_type;
- using difference_type = typename string_type::difference_type;
-
- size_type n (s.size ());
-
-#ifdef _WIN32
- // We do not support any special Windows path name notations like in C:abc,
- // /, \, /abc, \abc, \\?\c:\abc, \\server\abc and \\?\UNC\server\abc (more
- // about them in "Naming Files, Paths, and Namespaces" MSDN article).
- //
- if ((n > 2 && s[1] == ':' && s[2] != '\\' && s[2] != '/') ||
- (n > 0 && (s[0] == '\\' || s[0] == '/')))
- {
- if (exact)
- return data_type ();
- else
- throw invalid_basic_path<C> (s);
- }
-#endif
-
- // Strip trailing slashes.
- //
- size_type m (n), di (0);
- for (size_type i;
- m != 0 && (i = path_traits<C>::separator_index (s[m - 1])) != 0;
- --m) di = i;
-
- difference_type ts (0);
- if (size_t k = n - m)
- {
- // We can only accomodate one trailing slash in the exact mode.
- //
- if (exact && k > 1)
- return data_type ();
-
- if (m == 0) // The "/" case.
- {
- ++m; // Keep one slash in the string.
- ts = -1;
- }
- else
- ts = di;
-
- s.resize (m);
- }
-
- return data_type (std::move (s), ts);
- }
-
- template <typename C>
- auto dir_path_kind<C>::
- init (string_type&& s, bool exact) -> data_type
- {
- // If we don't already have the separator then this can't be the exact
- // initialization.
- //
- if (exact && !s.empty () && !path_traits<C>::is_separator (s.back ()))
- return data_type ();
-
- data_type r (any_path_kind<C>::init (std::move (s), exact));
-
- // Unless the result is empty, make sure we have the trailing slash.
- //
- if (!r.path_.empty () && r.tsep_ == 0)
- r.tsep_ = 1; // Canonical separator is always first.
-
- return r;
- }
-}
diff --git a/butl/prefix-map b/butl/prefix-map
deleted file mode 100644
index b350752..0000000
--- a/butl/prefix-map
+++ /dev/null
@@ -1,138 +0,0 @@
-// file : butl/prefix-map -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_PREFIX_MAP
-#define BUTL_PREFIX_MAP
-
-#include <map>
-#include <string>
-#include <utility> // move()
-#include <algorithm> // min()
-
-namespace butl
-{
- // A map of hierarchical "paths", e.g., 'foo.bar' or 'foo/bar' with
- // the ability to retrieve a range of entries that have a specific
- // prefix. The '.' and '/' above are the delimiter characters.
- //
- // Note that as a special rule, the default implementation of
- // compare_prefix treats empty key as everyone's prefix even if
- // the paths don't start with the delimiter (useful to represent
- // a "root path").
- //
- // Implementation-wise, the idea is to pretend that each key ends
- // with the delimiter. This way we automatically avoid matching
- // 'foobar' as having a prefix 'foo'.
- //
- template <typename K>
- struct compare_prefix;
-
- template <typename C>
- struct compare_prefix<std::basic_string<C>>
- {
- typedef std::basic_string<C> K;
-
- typedef C delimiter_type;
- typedef typename K::size_type size_type;
- typedef typename K::traits_type traits_type;
-
- explicit
- compare_prefix (delimiter_type d): d_ (d) {}
-
- bool
- operator() (const K& x, const K& y) const
- {
- return compare (x.c_str (), x.size (), y.c_str (), y.size ()) < 0;
- }
-
- bool
- prefix (const K& p, const K& k) const
- {
- size_type pn (p.size ()), kn (k.size ());
- return pn == 0 || // Empty key is always a prefix.
- (pn <= kn &&
- compare (p.c_str (), pn, k.c_str (), pn == kn ? pn : pn + 1) == 0);
- }
-
- protected:
- int
- compare (const C* x, size_type xn,
- const C* y, size_type yn) const
- {
- size_type n (std::min (xn, yn));
- int r (traits_type::compare (x, y, n));
-
- if (r == 0)
- {
- // Pretend there is the delimiter characters at the end of the
- // shorter string.
- //
- char xc (xn > n ? x[n] : (xn++, d_));
- char yc (yn > n ? y[n] : (yn++, d_));
- r = traits_type::compare (&xc, &yc, 1);
-
- // If we are still equal, then compare the lengths.
- //
- if (r == 0)
- r = (xn == yn ? 0 : (xn < yn ? -1 : 1));
- }
-
- return r;
- }
-
- private:
- delimiter_type d_;
- };
-
- template <typename M>
- struct prefix_map_common: M
- {
- typedef M map_type;
- typedef typename map_type::key_type key_type;
- typedef typename map_type::value_type value_type;
- typedef typename map_type::key_compare compare_type;
- typedef typename compare_type::delimiter_type delimiter_type;
-
- typedef typename map_type::iterator iterator;
- typedef typename map_type::const_iterator const_iterator;
-
- explicit
- prefix_map_common (delimiter_type d)
- : map_type (compare_type (d)) {}
-
- prefix_map_common (std::initializer_list<value_type> i, delimiter_type d)
- : map_type (std::move (i), compare_type (d)) {}
-
- std::pair<iterator, iterator>
- find_prefix (const key_type&);
-
- std::pair<const_iterator, const_iterator>
- find_prefix (const key_type&) const;
- };
-
- template <typename M, typename prefix_map_common<M>::delimiter_type D>
- struct prefix_map_impl: prefix_map_common<M>
- {
- typedef typename prefix_map_common<M>::value_type value_type;
-
- prefix_map_impl (): prefix_map_common<M> (D) {}
- prefix_map_impl (std::initializer_list<value_type> i)
- : prefix_map_common<M> (std::move (i), D) {}
- };
-
- template <typename K,
- typename T,
- typename compare_prefix<K>::delimiter_type D>
- using prefix_map = prefix_map_impl<std::map<K, T, compare_prefix<K>>, D>;
-
- template <typename K,
- typename T,
- typename compare_prefix<K>::delimiter_type D>
- using prefix_multimap =
- prefix_map_impl<std::multimap<K, T, compare_prefix<K>>, D>;
-}
-
-#include <butl/prefix-map.txx>
-
-#endif // BUTL_PREFIX_MAP
diff --git a/butl/prefix-map.txx b/butl/prefix-map.txx
deleted file mode 100644
index 76aba4d..0000000
--- a/butl/prefix-map.txx
+++ /dev/null
@@ -1,39 +0,0 @@
-// file : butl/prefix-map.txx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-namespace butl
-{
- template <typename M>
- auto prefix_map_common<M>::
- find_prefix (const key_type& k) -> std::pair<iterator, iterator>
- {
- std::pair<iterator, iterator> r;
- r.first = this->lower_bound (k);
-
- for (r.second = r.first; r.second != this->end (); ++r.second)
- {
- if (!this->key_comp ().prefix (k, r.second->first))
- break;
- }
-
- return r;
- }
-
- template <typename M>
- auto prefix_map_common<M>::
- find_prefix (const key_type& k) const ->
- std::pair<const_iterator, const_iterator>
- {
- std::pair<const_iterator, const_iterator> r;
- r.first = this->lower_bound (k);
-
- for (r.second = r.first; r.second != this->end (); ++r.second)
- {
- if (!this->key_comp ().prefix (k, r.second->first))
- break;
- }
-
- return r;
- }
-}
diff --git a/butl/process b/butl/process
deleted file mode 100644
index 9c0d060..0000000
--- a/butl/process
+++ /dev/null
@@ -1,569 +0,0 @@
-// file : butl/process -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_PROCESS
-#define BUTL_PROCESS
-
-#ifndef _WIN32
-# include <sys/types.h> // pid_t
-#endif
-
-#include <vector>
-#include <iosfwd>
-#include <cassert>
-#include <cstdint> // uint32_t
-#include <system_error>
-
-#include <butl/path>
-#include <butl/export>
-#include <butl/optional>
-#include <butl/fdstream> // auto_fd, fdpipe
-#include <butl/vector-view>
-#include <butl/small-vector>
-
-namespace butl
-{
- struct process_error: std::system_error
- {
- const bool child;
-
- process_error (int e, bool child = false)
- : system_error (e, std::generic_category ()), child (child) {}
-
-#ifdef _WIN32
- process_error (const std::string& d, int fallback_errno_code = 0)
- : system_error (fallback_errno_code, std::system_category (), d),
- child (false) {}
-#endif
- };
-
- struct process_child_error: process_error
- {
- explicit
- process_child_error (int e): process_error (e, true) {}
- };
-
- // Process arguments (i.e., the command line). The first must be an
- // executable name and the last element should be NULL. Can also be the
- // multi-process piped command line (see process::print() for details).
- //
- struct process_args
- {
- const char* const* argv;
- std::size_t argc;
- };
-
- // A process executable has three paths: initial, recall, and effective.
- // Initial is the original "command" that you specify in argv[0] and on
- // POSIX that's what ends up in the child's argv[0]. But not on Windows. On
- // Windows the command is first searched for in the parent executable's
- // directory and if found then that's what should end up in child's argv[0].
- // So this is the recall path. It is called recall because this is what the
- // caller of the parent process will be able to execute if you printed the
- // command line (provided you haven't changed the CWD). Finally, effective
- // is the absolute path to the executable that will include the directory
- // part if found in PATH, the .exe extension if one is missing, etc.
- //
- // As an example, let's say we run foo\foo.exe that itself spawns bar which
- // is found as foo\bar.exe. The paths will then be:
- //
- // initial: bar
- // recall: foo\bar
- // effective: c:\...\foo\bar.exe
- //
- // In most cases, at least on POSIX, the first two paths will be the same.
- // As an optimization, if the recall path is empty, then it means it is the
- // same as initial. Similarly, if the effective path is empty then, it is
- // the same as recall (and if that is empty, as initial).
- //
- // Note that the call to path_search() below adjust args[0] to point to the
- // recall path which brings up lifetime issues. To address this this class
- // also implements an RAII-based auto-restore of args[0] to its initial
- // value.
- //
- class process_path
- {
- public:
- const char* initial = nullptr;
- path recall;
- path effect;
-
- // Handle empty recall/effect.
- //
- const char* recall_string () const;
- const char* effect_string () const;
-
- bool empty () const
- {
- return initial == nullptr && recall.empty () && effect.empty ();
- }
-
- // Moveable-only type.
- //
- process_path (process_path&&);
- process_path& operator= (process_path&&);
-
- process_path (const process_path&) = delete;
- process_path& operator= (const process_path&) = delete;
-
- process_path () = default;
- process_path (const char* i, path&& r, path&& e);
- ~process_path ();
-
- private:
- friend class process;
- const char** args0_ = nullptr;
- };
-
- // Process exit information.
- //
- struct LIBBUTL_EXPORT process_exit
- {
- // Status type is the raw exit value as returned by GetExitCodeProcess()
- // (NTSTATUS value that represents exit or error codes; MSDN refers to the
- // error code as "value of the exception that caused the termination") or
- // waitpid(1). Code type is the return value if the process exited
- // normally.
- //
-#ifndef _WIN32
- using status_type = int;
- using code_type = std::uint8_t;
-#else
- using status_type = std::uint32_t; // Win32 DWORD
- using code_type = std::uint16_t; // Win32 WORD
-#endif
-
- status_type status;
-
- process_exit () = default;
-
- explicit
- process_exit (code_type);
-
- enum as_status_type {as_status};
- process_exit (status_type s, as_status_type): status (s) {}
-
- // Return false if the process exited abnormally.
- //
- bool
- normal () const;
-
- code_type
- code () const;
-
- explicit operator bool () const {return normal () && code () == 0;}
-
- // Abnormal termination information.
- //
-
- // Return the signal number that caused the termination or 0 if no such
- // information is available.
- //
- int
- signal () const;
-
- // Return true if the core file was generated.
- //
- bool
- core () const;
-
- // Return a description of the reason that caused the process to terminate
- // abnormally. On POSIX this is the signal name, on Windows -- the summary
- // produced from the corresponding error identifier defined in ntstatus.h.
- //
- std::string
- description () const;
- };
-
- class LIBBUTL_EXPORT process
- {
- public:
-#ifndef _WIN32
- using handle_type = pid_t;
- using id_type = pid_t;
-#else
- using handle_type = void*; // Win32 HANDLE
- using id_type = std::uint32_t; // Win32 DWORD
-#endif
-
- // Start another process using the specified command line. The default
- // values to the in, out and err arguments indicate that the child process
- // should inherit the parent process stdin, stdout, and stderr,
- // respectively. If -1 is passed instead, then the corresponding child
- // process descriptor is connected (via a pipe) to out_fd for stdin,
- // in_ofd for stdout, and in_efd for stderr (see data members below). If
- // -2 is passed, then the corresponding child process descriptor is
- // replaced with the null device descriptor (e.g., /dev/null). This
- // results in the child process not being able to read anything from stdin
- // (gets immediate EOF) and all data written to stdout/stderr being
- // discarded.
- //
- // On Windows parent process pipe descriptors are set to text mode to be
- // consistent with the default (text) mode of standard file descriptors of
- // the child process. When reading in the text mode the sequence of 0xD,
- // 0xA characters is translated into the single OxA character and 0x1A is
- // interpreted as EOF. When writing in the text mode the OxA character is
- // translated into the 0xD, 0xA sequence. Use the fdmode() function to
- // change the mode, if required.
- //
- // Instead of passing -1, -2 or the default value, you can also pass your
- // own descriptors. Note, however, that in this case they are not closed by
- // the parent. So you should do this yourself, if required. For example,
- // to redirect the child process stdout to stderr, you can do:
- //
- // process p (..., 0, 2);
- //
- // Throw process_error if anything goes wrong. Note that some of the
- // exceptions (e.g., if exec() failed) can be thrown in the child
- // version of us (as process_child_error).
- //
- // Note that the versions without the the process_path argument may
- // temporarily change args[0] (see path_search() for details).
- //
- process (const char* args[], int in = 0, int out = 1, int err = 2);
-
- process (const process_path&, const char* args[],
- int in = 0, int out = 1, int err = 2);
-
- // The "piping" constructor, for example:
- //
- // process lhs (..., 0, -1); // Redirect stdout to a pipe.
- // process rhs (..., lhs); // Redirect stdin to lhs's pipe.
- //
- // rhs.wait (); // Wait for last first.
- // lhs.wait ();
- //
- process (const char* args[], process& in, int out = 1, int err = 2);
-
- process (const process_path&, const char* args[],
- process& in, int out = 1, int err = 2);
-
- // Versions of the above constructors that allow us to change the
- // current working directory of the child process. NULL and empty
- // cwd arguments are ignored.
- //
- process (const char* cwd, const char* [], int = 0, int = 1, int = 2);
-
- process (const char* cwd,
- const process_path&, const char* [],
- int = 0, int = 1, int = 2);
-
- process (const char* cwd, const char* [], process&, int = 1, int = 2);
-
- process (const char* cwd,
- const process_path&, const char* [],
- process&, int = 1, int = 2);
-
- // Wait for the process to terminate. Return true if the process
- // terminated normally and with the zero exit code. 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 (bool ignore_errors = false);
-
- // Return true if the process has already terminated in which case
- // optionally set the argument to the result of wait().
- //
- bool
- try_wait ();
-
- bool
- try_wait (bool&);
-
- // 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.
- //
- process (process&&);
- process& operator= (process&&);
-
- process (const process&) = delete;
- process& operator= (const process&) = delete;
-
- // Create an empty or "already terminated" process. By default the
- // termination status is unknown but you can change that.
- //
- explicit
- process (optional<process_exit> = nullopt);
-
- // Resolve process' paths based on the initial path in args0. If recall
- // differs from initial, adjust args0 to point to the recall path. If
- // resolution fails, throw process_error. Normally, you will use this
- // function like this:
- //
- // const char* args[] = {"foo", ..., nullptr};
- //
- // process_path pp (process::path_search (args[0]))
- //
- // ... // E.g., print args[0].
- //
- // process p (pp, args);
- //
- // You can also specify the fallback directory which will be tried last.
- // This, for example, can be used to implement the Windows "search in the
- // parent executable's directory" semantics across platforms.
- //
- static process_path
- path_search (const char*& args0, const dir_path& fallback = dir_path ());
-
- // This version is primarily useful when you want to pre-search the
- // executable before creating the args[] array. In this case you will
- // use the recall path for args[0].
- //
- // The init argument determines whether to initialize the initial path to
- // the shallow copy of file. If it is true, then initial is the same as
- // file and recall is either empty or contain a different path. If it is
- // false then initial contains a shallow copy of recall, and recall is
- // either a different path or a deep copy of file. Normally you don't care
- // about initial once you got recall and the main reason to pass true to
- // this argument is to save a copy (since initial and recall are usually
- // the same).
- //
- static process_path
- path_search (const char* file, bool init, const dir_path& = dir_path ());
-
- static process_path
- path_search (const std::string&, bool, const dir_path& = dir_path ());
-
- static process_path
- path_search (const path&, bool, const dir_path& = dir_path ());
-
- // As above but if not found return empty process_path instead of
- // throwing.
- //
- static process_path
- try_path_search (const char*, bool, const dir_path& = dir_path ());
-
- static process_path
- try_path_search (const std::string&, bool, const dir_path& = dir_path ());
-
- static process_path
- try_path_search (const path&, bool, const dir_path& = dir_path ());
-
- // Print process commmand line. If the number of elements is specified,
- // then it will print the piped multi-process command line, if present.
- // In this case, the expected format is as follows:
- //
- // name1 arg arg ... nullptr
- // name2 arg arg ... nullptr
- // ...
- // nameN arg arg ... nullptr nullptr
- //
- static void
- print (std::ostream&, const char* const args[], size_t n = 0);
-
- public:
- id_type
- id () const;
-
- static id_type
- current_id ();
-
- public:
- handle_type handle;
-
- // Absence means that the exit information is not (yet) known. This can be
- // because you haven't called wait() yet or because wait() failed.
- //
- optional<process_exit> exit;
-
- // Use the following file descriptors to communicate with the new process's
- // standard streams.
- //
- auto_fd out_fd; // Write to it to send to stdin.
- auto_fd in_ofd; // Read from it to receive from stdout.
- auto_fd in_efd; // Read from it to receive from stderr.
- };
-
- // Higher-level process running interface that aims to make executing a
- // process for the common cases as simple as calling a functions. Normally
- // it is further simplified by project-specific wrapper functions that
- // handle the process_error exception as well as abnormal and/or non-zero
- // exit status.
- //
- // The I/O/E arguments determine the child's stdin/stdout/stderr. They can
- // be of type int, auto_fd (and, in the future, perhaps also fd_pipe,
- // string, buffer, etc). For example, the following call will make stdin
- // read from /dev/null, stdout redirect to stderr, and inherit the parent's
- // stderr.
- //
- // process_run (..., fdnull (), 2, 2, ...)
- //
- // The P argument is the program path. It can be anything that can be passed
- // to process::path_search() (const char*, std::string, path) or the
- // process_path itself.
- //
- // The A arguments can be anything convertible to const char* via the
- // overloaded process_arg_as() (see below). Out of the box you can use const
- // char*, std::string, path/dir_path, (as well as [small_]vector[_view] of
- // these), and numeric types.
- //
- template <typename I,
- typename O,
- typename E,
- typename P,
- typename... A>
- process_exit
- process_run (I&& in,
- O&& out,
- E&& err,
- const dir_path& cwd,
- const P&,
- A&&... args);
-
- // The version with the command callback that can be used for printing the
- // command line or similar. It should be callable with the following
- // signature:
- //
- // void (const char*[], std::size_t)
- //
- template <typename C,
- typename I,
- typename O,
- typename E,
- typename P,
- typename... A>
- process_exit
- process_run (const C&,
- I&& in,
- O&& out,
- E&& err,
- const dir_path& cwd,
- const P&,
- A&&... args);
-
- // Versions that start the process without waiting.
- //
- template <typename I,
- typename O,
- typename E,
- typename P,
- typename... A>
- process
- process_start (I&& in,
- O&& out,
- E&& err,
- const dir_path& cwd,
- const P&,
- A&&... args);
-
- template <typename C,
- typename I,
- typename O,
- typename E,
- typename P,
- typename... A>
- process
- process_start (const C&,
- I&& in,
- O&& out,
- E&& err,
- const dir_path& cwd,
- const P&,
- A&&... args);
-
- // Conversion of types to their C string representations. Can be overloaded
- // (including via ADL) for custom types. The default implementation calls
- // to_string() which covers all the numeric values via std::to_string () and
- // also any type that defines to_string() (via ADL).
- //
- template <typename T>
- inline const char*
- process_arg_as (const T& x, std::string& storage)
- {
- using namespace std;
- return (storage = to_string (x)).c_str ();
- }
-
- inline const char*
- process_arg_as (const std::string& s, std::string&) {return s.c_str ();}
-
- template <typename K>
- inline const char*
- process_arg_as (const basic_path<char, K>& p, std::string&)
- {
- return p.string ().c_str ();
- }
-
- // char[N]
- //
- inline const char*
- process_arg_as (const char* s, std::string&) {return s;}
-
- template <std::size_t N>
- inline const char*
- process_arg_as (char (&s)[N], std::string&) {return s;}
-
- template <std::size_t N>
- inline const char*
- process_arg_as (const char (&s)[N], std::string&) {return s;}
-
- template <typename V, typename T>
- inline void
- process_args_as (V& v, const T& x, std::string& storage)
- {
- v.push_back (process_arg_as (x, storage));
- }
-
- // [small_]vector[_view]<>
- //
- template <typename V>
- inline void
- process_args_as (V& v, const std::vector<std::string>& vs, std::string&)
- {
- for (const std::string& s: vs)
- v.push_back (s.c_str ());
- }
-
- template <typename V, std::size_t N>
- inline void
- process_args_as (V& v, const small_vector<std::string, N>& vs, std::string&)
- {
- for (const std::string& s: vs)
- v.push_back (s.c_str ());
- }
-
- template <typename V>
- inline void
- process_args_as (V& v, const vector_view<std::string>& vs, std::string&)
- {
- for (const std::string& s: vs)
- v.push_back (s.c_str ());
- }
-
- template <typename V>
- inline void
- process_args_as (V& v, const std::vector<const char*>& vs, std::string&)
- {
- for (const char* s: vs)
- v.push_back (s);
- }
-
- template <typename V, std::size_t N>
- inline void
- process_args_as (V& v, const small_vector<const char*, N>& vs, std::string&)
- {
- for (const char* s: vs)
- v.push_back (s);
- }
-
- template <typename V>
- inline void
- process_args_as (V& v, const vector_view<const char*>& vs, std::string&)
- {
- for (const char* s: vs)
- v.push_back (s);
- }
-}
-
-#include <butl/process.ixx>
-
-#include <butl/process-run.txx>
-
-#endif // BUTL_PROCESS
diff --git a/butl/process-details b/butl/process-details
deleted file mode 100644
index b07ceff..0000000
--- a/butl/process-details
+++ /dev/null
@@ -1,49 +0,0 @@
-// file : butl/process-details -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_PROCESS_DETAILS
-#define BUTL_PROCESS_DETAILS
-
-#include <mutex>
-
-#include <butl/ft/shared_mutex>
-#if defined(__cpp_lib_shared_mutex) || defined(__cpp_lib_shared_timed_mutex)
-# include <shared_mutex>
-#endif
-
-namespace butl
-{
-#if defined(__cpp_lib_shared_mutex)
- using shared_mutex = std::shared_mutex;
- using ulock = std::unique_lock<shared_mutex>;
- using slock = std::shared_lock<shared_mutex>;
-#elif defined(__cpp_lib_shared_timed_mutex)
- using shared_mutex = std::shared_timed_mutex;
- using ulock = std::unique_lock<shared_mutex>;
- using slock = std::shared_lock<shared_mutex>;
-#else
- // Because we have this fallback, we need to be careful not to create
- // multiple shared locks in the same thread.
- //
- struct shared_mutex: std::mutex
- {
- using mutex::mutex;
-
- void lock_shared () { lock (); }
- void try_lock_shared () { try_lock (); }
- void unlock_shared () { unlock (); }
- };
-
- using ulock = std::unique_lock<shared_mutex>;
- using slock = ulock;
-#endif
-
- // Mutex that is acquired to make a sequence of operations atomic in regards
- // to child process spawning. Must be aquired for exclusive access for child
- // process startup, and for shared access otherwise. Defined in process.cxx.
- //
- extern shared_mutex process_spawn_mutex;
-}
-
-#endif // BUTL_PROCESS_DETAILS
diff --git a/butl/process-io b/butl/process-io
deleted file mode 100644
index 1212b22..0000000
--- a/butl/process-io
+++ /dev/null
@@ -1,28 +0,0 @@
-// file : butl/process-io -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_PROCESS_IO
-#define BUTL_PROCESS_IO
-
-#include <ostream>
-
-#include <butl/process>
-
-namespace butl
-{
- inline std::ostream&
- operator<< (std::ostream& o, const process_path& p)
- {
- return o << p.recall_string ();
- }
-
- inline std::ostream&
- operator<< (std::ostream& o, const process_args& a)
- {
- process::print (o, a.argv, a.argc);
- return o;
- }
-}
-
-#endif // BUTL_PROCESS_IO
diff --git a/butl/process-run.cxx b/butl/process-run.cxx
deleted file mode 100644
index c3b2856..0000000
--- a/butl/process-run.cxx
+++ /dev/null
@@ -1,32 +0,0 @@
-// file : butl/process-run.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <butl/process>
-
-#include <cstdlib> // exit()
-#include <iostream> // cerr
-
-using namespace std;
-
-namespace butl
-{
- process
- process_start (const dir_path& cwd,
- const process_path& pp,
- const char* cmd[],
- int in,
- int out,
- int err)
- {
- try
- {
- return process (cwd.string ().c_str (), pp, cmd, in, out, err);
- }
- catch (const process_child_error& e)
- {
- cerr << "unable to execute " << cmd[0] << ": " << e << endl;
- exit (1);
- }
- }
-}
diff --git a/butl/process-run.txx b/butl/process-run.txx
deleted file mode 100644
index 1d25af1..0000000
--- a/butl/process-run.txx
+++ /dev/null
@@ -1,226 +0,0 @@
-// file : butl/process-run.txx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <cassert>
-#include <utility> // move(), forward(), index_sequence
-
-namespace butl
-{
- inline int process_stdin (int v) {assert (v >= 0); return v;}
- inline int process_stdout (int v) {assert (v >= 0); return v;}
- inline int process_stderr (int v) {assert (v >= 0); return v;}
-
- inline int
- process_stdin (const auto_fd& v) {assert (v.get () >= 0); return v.get ();}
-
- inline int
- process_stdout (const auto_fd& v) {assert (v.get () >= 0); return v.get ();}
-
- inline int
- process_stderr (const auto_fd& v) {assert (v.get () >= 0); return v.get ();}
-
- LIBBUTL_EXPORT process
- process_start (const dir_path& cwd,
- const process_path& pp,
- const char* cmd[],
- int in,
- int out,
- int err);
-
- template <typename V, typename T>
- inline const char*
- process_args_as_wrapper (V& v, const T& x, std::string& storage)
- {
- process_args_as (v, x, storage);
- return nullptr;
- }
-
- template <typename C,
- typename I,
- typename O,
- typename E,
- typename... A,
- typename std::size_t... index>
- process
- process_start (std::index_sequence<index...>,
- const C& cmdc,
- I&& in,
- O&& out,
- E&& err,
- const dir_path& cwd,
- const process_path& pp,
- A&&... args)
- {
- // Map stdin/stdout/stderr arguments to their integer values, as expected
- // by the process constructor.
- //
- int in_i (process_stdin (std::forward<I> (in)));
- int out_i (process_stdout (std::forward<O> (out)));
- int err_i (process_stderr (std::forward<E> (err)));
-
- // Construct the command line array.
- //
- const std::size_t args_size (sizeof... (args));
-
- small_vector<const char*, args_size + 2> cmd;
- cmd.push_back (pp.recall_string ());
-
- std::string storage[args_size != 0 ? args_size : 1];
-
- const char* dummy[] = {
- nullptr, process_args_as_wrapper (cmd, args, storage[index])... };
-
- cmd.push_back (dummy[0]); // NULL (and get rid of unused warning).
-
- cmdc (cmd.data (), cmd.size ());
-
- // @@ Do we need to make sure certain fd's are closed before calling
- // wait()? Is this only the case with pipes? Needs thinking.
-
- return process_start (cwd, pp, cmd.data (), in_i, out_i, err_i);
- }
-
- template <typename C,
- typename I,
- typename O,
- typename E,
- typename... A>
- inline process
- process_start (const C& cmdc,
- I&& in,
- O&& out,
- E&& err,
- const dir_path& cwd,
- const process_path& pp,
- A&&... args)
- {
- return process_start (std::index_sequence_for<A...> (),
- cmdc,
- std::forward<I> (in),
- std::forward<O> (out),
- std::forward<E> (err),
- cwd,
- pp,
- std::forward<A> (args)...);
- }
-
- template <typename I,
- typename O,
- typename E,
- typename P,
- typename... A>
- inline process
- process_start (I&& in,
- O&& out,
- E&& err,
- const dir_path& cwd,
- const P& p,
- A&&... args)
- {
- return process_start ([] (const char* [], std::size_t) {},
- std::forward<I> (in),
- std::forward<O> (out),
- std::forward<E> (err),
- cwd,
- process::path_search (p, true),
- std::forward<A> (args)...);
- }
-
- template <typename C,
- typename I,
- typename O,
- typename E,
- typename P,
- typename... A>
- inline process
- process_start (const C& cmdc,
- I&& in,
- O&& out,
- E&& err,
- const dir_path& cwd,
- const P& p,
- A&&... args)
- {
- return process_start (cmdc,
- std::forward<I> (in),
- std::forward<O> (out),
- std::forward<E> (err),
- cwd,
- process::path_search (p, true),
- std::forward<A> (args)...);
- }
-
- template <typename C,
- typename I,
- typename O,
- typename E,
- typename... A>
- inline process_exit
- process_run (const C& cmdc,
- I&& in,
- O&& out,
- E&& err,
- const dir_path& cwd,
- const process_path& pp,
- A&&... args)
- {
- process pr (
- process_start (cmdc,
- std::forward<I> (in),
- std::forward<O> (out),
- std::forward<E> (err),
- cwd,
- pp,
- std::forward<A> (args)...));
-
- pr.wait ();
- return *pr.exit;
- }
-
- template <typename I,
- typename O,
- typename E,
- typename P,
- typename... A>
- inline process_exit
- process_run (I&& in,
- O&& out,
- E&& err,
- const dir_path& cwd,
- const P& p,
- A&&... args)
- {
- return process_run ([] (const char* [], std::size_t) {},
- std::forward<I> (in),
- std::forward<O> (out),
- std::forward<E> (err),
- cwd,
- process::path_search (p, true),
- std::forward<A> (args)...);
- }
-
- template <typename C,
- typename I,
- typename O,
- typename E,
- typename P,
- typename... A>
- inline process_exit
- process_run (const C& cmdc,
- I&& in,
- O&& out,
- E&& err,
- const dir_path& cwd,
- const P& p,
- A&&... args)
- {
- return process_run (cmdc,
- std::forward<I> (in),
- std::forward<O> (out),
- std::forward<E> (err),
- cwd,
- process::path_search (p, true),
- std::forward<A> (args)...);
- }
-}
diff --git a/butl/process.cxx b/butl/process.cxx
deleted file mode 100644
index 940560c..0000000
--- a/butl/process.cxx
+++ /dev/null
@@ -1,1440 +0,0 @@
-// file : butl/process.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <butl/process>
-
-#ifndef _WIN32
-# include <unistd.h> // execvp, fork, dup2, pipe, chdir, *_FILENO, getpid
-# include <sys/wait.h> // waitpid
-# include <sys/types.h> // _stat
-# include <sys/stat.h> // _stat(), S_IS*
-#else
-# include <butl/win32-utility>
-
-# include <psapi.h> // EnumProcessModules(), etc
-
-# include <io.h> // _get_osfhandle(), _close()
-# include <stdlib.h> // _MAX_PATH
-# include <sys/types.h> // stat
-# include <sys/stat.h> // stat(), S_IS*
-
-# ifdef _MSC_VER // Unlikely to be fixed in newer versions.
-# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
-
-# define STDIN_FILENO 0
-# define STDOUT_FILENO 1
-# define STDERR_FILENO 2
-# endif // _MSC_VER
-
-# include <cstdlib> // getenv(), __argv[]
-
-# include <butl/small-vector>
-#endif
-
-#include <errno.h>
-
-#include <ios> // ios_base::failure
-#include <cassert>
-#include <cstddef> // size_t
-#include <cstring> // strlen(), strchr()
-#include <utility> // move()
-#include <ostream>
-
-#include <butl/utility> // casecmp()
-#include <butl/fdstream> // fdnull()
-#include <butl/process-details>
-
-#include <iostream>
-
-using namespace std;
-
-#ifdef _WIN32
-using namespace butl::win32;
-#endif
-
-namespace butl
-{
- shared_mutex process_spawn_mutex;
-
- // process
- //
- static process_path
- path_search (const char*, const dir_path&);
-
- process_path process::
- path_search (const char* f, bool init, const dir_path& fb)
- {
- process_path r (try_path_search (f, init, fb));
-
- if (r.empty ())
- throw process_error (ENOENT);
-
- return r;
- }
-
- process_path process::
- try_path_search (const char* f, bool init, const dir_path& fb)
- {
- process_path r (butl::path_search (f, fb));
-
- if (!init && !r.empty ())
- {
- path& rp (r.recall);
- r.initial = (rp.empty () ? (rp = path (f)) : rp).string ().c_str ();
- }
-
- return r;
- }
-
- void process::
- print (ostream& o, const char* const args[], size_t n)
- {
- size_t m (0);
- const char* const* p (args);
- do
- {
- if (m != 0)
- o << " |"; // Trailing space will be added inside the loop.
-
- for (m++; *p != nullptr; p++, m++)
- {
- if (p != args)
- o << ' ';
-
- // Quote if empty or contains spaces.
- //
- bool q (**p == '\0' || strchr (*p, ' ') != nullptr);
-
- if (q)
- o << '"';
-
- o << *p;
-
- if (q)
- o << '"';
- }
-
- if (m < n) // Can we examine the next element?
- {
- p++;
- m++;
- }
-
- } while (*p != nullptr);
- }
-
- process::
- process (const char* cwd,
- const process_path& pp, const char* args[],
- process& in, int out, int err)
- : process (cwd, pp, args, in.in_ofd.get (), out, err)
- {
- assert (in.in_ofd.get () != -1); // Should be a pipe.
- in.in_ofd.reset (); // Close it on our side.
- }
-
-#ifndef _WIN32
-
- static process_path
- path_search (const char* f, const dir_path& fb)
- {
- // Note that there is a similar version for Win32.
-
- typedef path::traits traits;
-
- size_t fn (strlen (f));
-
- process_path r (f, path (), path ()); // Make sure it is not empty.
- path& rp (r.recall);
- path& ep (r.effect);
-
- // Check that the file exists and has at least one executable bit set.
- // This way we get a bit closer to the "continue search on EACCES"
- // semantics (see below).
- //
- auto exists = [] (const char* f) -> bool
- {
- struct stat si;
- return (stat (f, &si) == 0 &&
- S_ISREG (si.st_mode) &&
- (si.st_mode & (S_IEXEC | S_IXGRP | S_IXOTH)) != 0);
- };
-
- auto search = [&ep, f, fn, &exists] (const char* d,
- size_t dn,
- bool norm = false) -> bool
- {
- string s (move (ep).string ()); // Reuse buffer.
-
- if (dn != 0)
- {
- s.assign (d, dn);
-
- if (!traits::is_separator (s.back ()))
- s += traits::directory_separator;
- }
-
- s.append (f, fn);
- ep = path (move (s)); // Move back into result.
-
- if (norm)
- ep.normalize ();
-
- return exists (ep.string ().c_str ());
- };
-
- // If there is a directory component in the file, then the PATH search
- // does not apply. If the path is relative, then prepend CWD. In both
- // cases make sure the file actually exists.
- //
- if (traits::find_separator (f, fn) != nullptr)
- {
- if (traits::absolute (f, fn))
- {
- if (exists (f))
- return r;
- }
- else
- {
- const string& d (traits::current_directory ());
-
- if (search (d.c_str (), d.size (), true))
- return r;
- }
-
- return process_path ();
- }
-
- // The search order is documented in exec(3). Some of the differences
- // compared to exec*p() functions:
- //
- // 1. If there no PATH, we don't default to current directory/_CS_PATH.
- // 2. We do not continue searching on EACCES from execve().
- // 3. We do not execute via default shell on ENOEXEC from execve().
- //
- for (const char* b (getenv ("PATH")), *e;
- b != nullptr;
- b = (e != nullptr ? e + 1 : e))
- {
- e = strchr (b, traits::path_separator);
-
- // Empty path (i.e., a double colon or a colon at the beginning or end
- // of PATH) means search in the current dirrectory. Silently skip
- // invalid paths.
- //
- try
- {
- if (search (b, e != nullptr ? e - b : strlen (b)))
- return r;
- }
- catch (const invalid_path&)
- {
- }
- }
-
- // If we were given a fallback, try that.
- //
- if (!fb.empty ())
- {
- if (search (fb.string ().c_str (), fb.string ().size ()))
- {
- // In this case we have to set the recall path. And we know from
- // search() implementation that it will be the same as effective.
- // Which means we can just move effective to recall.
- //
- rp.swap (ep);
-
- return r;
- }
- }
-
- // Did not find anything.
- //
- return process_path ();
- }
-
- process::
- process (const char* cwd,
- const process_path& pp, const char* args[],
- int in, int out, int err)
- {
- fdpipe out_fd;
- fdpipe in_ofd;
- fdpipe in_efd;
-
- auto fail = [] (bool child)
- {
- if (child)
- throw process_child_error (errno);
- else
- throw process_error (errno);
- };
-
- auto open_pipe = [] () -> fdpipe
- {
- try
- {
- return fdopen_pipe ();
- }
- catch (const ios_base::failure&)
- {
- // Translate to process_error.
- //
- // For old versions of g++ (as of 4.9) ios_base::failure is not derived
- // from system_error and so we cannot recover the errno value. On the
- // other hand the only possible values are EMFILE and ENFILE. Lets use
- // EMFILE as the more probable. This is a temporary code after all.
- //
- throw process_error (EMFILE);
- }
- };
-
- auto open_null = [] () -> auto_fd
- {
- try
- {
- return fdnull ();
- }
- catch (const ios_base::failure& e)
- {
- // Translate to process_error.
- //
- // For old versions of g++ (as of 4.9) ios_base::failure is not derived
- // from system_error and so we cannot recover the errno value. Lets use
- // EIO in this case. This is a temporary code after all.
- //
- const system_error* se (dynamic_cast<const system_error*> (&e));
-
- throw process_error (se != nullptr
- ? se->code ().value ()
- : EIO);
- }
- };
-
- // If we are asked to open null (-2) then open "half-pipe".
- //
- if (in == -1)
- out_fd = open_pipe ();
- else if (in == -2)
- out_fd.in = open_null ();
-
- if (out == -1)
- in_ofd = open_pipe ();
- else if (out == -2)
- in_ofd.out = open_null ();
-
- if (err == -1)
- in_efd = open_pipe ();
- else if (err == -2)
- in_efd.out = open_null ();
-
- {
- ulock l (process_spawn_mutex); // Will not be released in child.
- handle = fork ();
-
- if (handle == -1)
- fail (false);
-
- if (handle == 0)
- {
- // Child.
- //
- // Duplicate the user-supplied (fd > -1) or the created pipe descriptor
- // to the standard stream descriptor (read end for STDIN_FILENO, write
- // end otherwise). Close the pipe afterwards.
- //
- auto duplicate = [&fail] (int sd, int fd, fdpipe& pd)
- {
- if (fd == -1 || fd == -2)
- fd = (sd == STDIN_FILENO ? pd.in : pd.out).get ();
-
- assert (fd > -1);
- if (dup2 (fd, sd) == -1)
- fail (true);
-
- pd.in.reset (); // Silently close.
- pd.out.reset (); // Silently close.
- };
-
- if (in != STDIN_FILENO)
- duplicate (STDIN_FILENO, in, out_fd);
-
- // If stdout is redirected to stderr (out == 2) we need to duplicate it
- // after duplicating stderr to pickup the proper fd. Otherwise keep the
- // "natual" order of duplicate() calls, so if stderr is redirected to
- // stdout it picks up the proper fd as well.
- //
- if (out == STDERR_FILENO)
- {
- if (err != STDERR_FILENO)
- duplicate (STDERR_FILENO, err, in_efd);
-
- if (out != STDOUT_FILENO)
- duplicate (STDOUT_FILENO, out, in_ofd);
- }
- else
- {
- if (out != STDOUT_FILENO)
- duplicate (STDOUT_FILENO, out, in_ofd);
-
- if (err != STDERR_FILENO)
- duplicate (STDERR_FILENO, err, in_efd);
- }
-
- // Change current working directory if requested.
- //
- if (cwd != nullptr && *cwd != '\0' && chdir (cwd) != 0)
- fail (true);
-
- if (execv (pp.effect_string (), const_cast<char**> (&args[0])) == -1)
- fail (true);
- }
- } // Release the lock in parent.
-
- assert (handle != 0); // Shouldn't get here unless in the parent process.
-
- this->out_fd = move (out_fd.out);
- this->in_ofd = move (in_ofd.in);
- this->in_efd = move (in_efd.in);
- }
-
- bool process::
- wait (bool ie)
- {
- if (handle != 0)
- {
- int es;
- int r (waitpid (handle, &es, 0));
- handle = 0; // We have tried.
-
- if (r == -1)
- {
- // If ignore errors then just leave exit nullopt, so it has "no exit
- // information available" semantics.
- //
- if (!ie)
- throw process_error (errno);
- }
- else
- exit = process_exit (es, process_exit::as_status);
- }
-
- return exit && exit->normal () && exit->code () == 0;
- }
-
- bool process::
- try_wait ()
- {
- if (handle != 0)
- {
- int es;
- int r (waitpid (handle, &es, WNOHANG));
-
- if (r == 0) // Not exited yet.
- return false;
-
- handle = 0; // We have tried.
-
- if (r == -1)
- throw process_error (errno);
-
- exit = process_exit (es, process_exit::as_status);
- }
-
- return true;
- }
-
- process::id_type process::
- current_id ()
- {
- return getpid ();
- }
-
- // process_exit
- //
- process_exit::
- process_exit (code_type c)
- //
- // Note that such an initialization is not portable as POSIX doesn't
- // specify the bits layout for the value returned by waitpid(). However
- // for the major POSIX systems (Linux, FreeBSD, MacOS) it is the
- // following:
- //
- // [0, 7) - terminating signal
- // [7, 8) - coredump flag
- // [8, 16) - program exit code
- //
- // Also the lowest 7 bits value is used to distinguish the normal and
- // abnormal process terminations. If it is zero then the program exited
- // normally and the exit code is available.
- //
- : status (c << 8)
- {
- }
-
- // Make sure the bits layout we stick to (read above) correlates to the W*()
- // macros implementations for the current platform.
- //
- namespace details
- {
- // W* macros may require an argument to be lvalue (for example for glibc).
- //
- static const process_exit::status_type status_code (0xFF00);
-
- static_assert (WIFEXITED (status_code) &&
- WEXITSTATUS (status_code) == 0xFF &&
- !WIFSIGNALED (status_code),
- "unexpected process exit status bits layout");
- }
-
- bool process_exit::
- normal () const
- {
- return WIFEXITED (status);
- }
-
- process_exit::code_type process_exit::
- code () const
- {
- assert (normal ());
- return WEXITSTATUS (status);
- }
-
- int process_exit::
- signal () const
- {
- assert (!normal ());
-
- // WEXITSTATUS() and WIFSIGNALED() can both return false for the same
- // status, so we have neither exit code nor signal. We return zero for
- // such a case.
- //
- return WIFSIGNALED (status) ? WTERMSIG (status) : 0;
- }
-
- bool process_exit::
- core () const
- {
- assert (!normal ());
-
- // Not a POSIX macro (available on Linux, FreeBSD, MacOS).
- //
-#ifdef WCOREDUMP
- return WIFSIGNALED (status) && WCOREDUMP (status);
-#else
- return false;
-#endif
- }
-
- string process_exit::
- description () const
- {
- assert (!normal ());
-
- // It would be convenient to use strsignal() or sys_siglist[] to obtain a
- // signal name for the number, but the function is not thread-safe and the
- // array is not POSIX. So we will use the custom mapping of POSIX signals
- // (IEEE Std 1003.1-2008, 2016 Edition) to their names (as they appear in
- // glibc).
- //
- switch (signal ())
- {
- case SIGHUP: return "hangup (SIGHUP)";
- case SIGINT: return "interrupt (SIGINT)";
- case SIGQUIT: return "quit (SIGQUIT)";
- case SIGILL: return "illegal instruction (SIGILL)";
- case SIGABRT: return "aborted (SIGABRT)";
- case SIGFPE: return "floating point exception (SIGFPE)";
- case SIGKILL: return "killed (SIGKILL)";
- case SIGSEGV: return "segmentation fault (SIGSEGV)";
- case SIGPIPE: return "broken pipe (SIGPIPE)";
- case SIGALRM: return "alarm clock (SIGALRM)";
- case SIGTERM: return "terminated (SIGTERM)";
- case SIGUSR1: return "user defined signal 1 (SIGUSR1)";
- case SIGUSR2: return "user defined signal 2 (SIGUSR2)";
- case SIGCHLD: return "child exited (SIGCHLD)";
- case SIGCONT: return "continued (SIGCONT)";
- case SIGSTOP: return "stopped (process; SIGSTOP)";
- case SIGTSTP: return "stopped (typed at terminal; SIGTSTP)";
- case SIGTTIN: return "stopped (tty input; SIGTTIN)";
- case SIGTTOU: return "stopped (tty output; SIGTTOU)";
- case SIGBUS: return "bus error (SIGBUS)";
-
- // Unavailabe on MacOS 10.11.
- //
-#ifdef SIGPOLL
- case SIGPOLL: return "I/O possible (SIGPOLL)";
-#endif
-
- case SIGPROF: return "profiling timer expired (SIGPROF)";
- case SIGSYS: return "bad system call (SIGSYS)";
- case SIGTRAP: return "trace/breakpoint trap (SIGTRAP)";
- case SIGURG: return "urgent I/O condition (SIGURG)";
- case SIGVTALRM: return "virtual timer expired (SIGVTALRM)";
- case SIGXCPU: return "CPU time limit exceeded (SIGXCPU)";
- case SIGXFSZ: return "file size limit exceeded (SIGXFSZ)";
-
- case 0: return "status unknown";
- default: return "unknown signal " + to_string (signal ());
- }
- }
-
-#else // _WIN32
-
- static process_path
- path_search (const char* f, const dir_path& fb)
- {
- // Note that there is a similar version for Win32.
-
- typedef path::traits traits;
-
- size_t fn (strlen (f));
-
- // Unless there is already the .exe/.bat extension, then we will need to
- // add it.
- //
- bool ext;
- {
- const char* e (traits::find_extension (f, fn));
- ext = (e == nullptr ||
- (casecmp (e, ".exe") != 0 &&
- casecmp (e, ".bat") != 0 &&
- casecmp (e, ".cmd") != 0));
- }
-
- process_path r (f, path (), path ()); // Make sure it is not empty.
- path& rp (r.recall);
- path& ep (r.effect);
-
- // Check that the file exists. Since the executable mode is set according
- // to the file extension, we don't check for that.
- //
- auto exists = [] (const char* f) -> bool
- {
- struct _stat si;
- return _stat (f, &si) == 0 && S_ISREG (si.st_mode);
- };
-
- // Check with extensions: .exe, .cmd, and .bat.
- //
- auto exists_ext = [&exists] (string& s) -> bool
- {
- size_t i (s.size () + 1); // First extension letter.
-
- s += ".exe";
- if (exists (s.c_str ()))
- return true;
-
- s[i] = 'c'; s[i + 1] = 'm'; s[i + 2] = 'd';
- if (exists (s.c_str ()))
- return true;
-
- s[i] = 'b'; s[i + 1] = 'a'; s[i + 2] = 't';
- return exists (s.c_str ());
- };
-
- auto search = [&ep, f, fn, ext, &exists, &exists_ext] (
- const char* d, size_t dn, bool norm = false) -> bool
- {
- string s (move (ep).string ()); // Reuse buffer.
-
- if (dn != 0)
- {
- s.assign (d, dn);
-
- if (!traits::is_separator (s.back ()))
- s += traits::directory_separator;
- }
-
- s.append (f, fn);
- ep = path (move (s)); // Move back into result.
-
- if (norm)
- ep.normalize ();
-
- if (!ext)
- return exists (ep.string ().c_str ());
-
- // Try with the extensions.
- //
- s = move (ep).string ();
- bool e (exists_ext (s));
- ep = path (move (s));
- return e;
- };
-
- // If there is a directory component in the file, then the PATH search
- // does not apply. If the path is relative, then prepend CWD. In both
- // cases we may still need to append the extension and make sure the file
- // actually exists.
- //
- if (traits::find_separator (f, fn) != nullptr)
- {
- if (traits::absolute (f, fn))
- {
- bool e;
- if (!ext)
- e = exists (r.effect_string ());
- else
- {
- string s (f, fn);
- e = exists_ext (s);
- ep = path (move (s));
- }
-
- if (e)
- return r;
- }
- else
- {
- const string& d (traits::current_directory ());
-
- if (search (d.c_str (), d.size (), true)) // Appends extension.
- return r;
- }
-
- return process_path ();
- }
-
- // The search order is documented in CreateProcess(). First we look in the
- // directory of the parent executable.
- //
- {
- char d[_MAX_PATH + 1];
- DWORD n (GetModuleFileName (NULL, d, _MAX_PATH + 1));
-
- if (n == 0 || n == _MAX_PATH + 1) // Failed or truncated.
- throw process_error (last_error_msg ());
-
- const char* p (traits::rfind_separator (d, n));
- assert (p != nullptr);
-
- if (search (d, p - d + 1)) // Include trailing slash.
- {
- // In this case we have to set the recall path.
- //
- // Note that the directory we have extracted is always absolute but
- // the parent's recall path (argv[0]) might be relative. It seems,
- // ideally, we would want to use parent's argv[0] dir (if any) to form
- // the recall path. In particular, if the parent has no directory,
- // then it means it was found via the standard search (e.g., PATH) and
- // then so should the child.
- //
- // How do we get the parent's argv[0]? Luckily, here is __argv on
- // Windows.
- //
- const char* d (__argv[0]);
- size_t n (strlen (d));
- if (const char* p = traits::rfind_separator (d, n))
- {
- string s (d, p - d + 1); // Include trailing slash.
- s.append (f, fn);
- rp = path (move (s));
-
- // If recall is the same as effective, then set effective to empty.
- //
- if (rp == ep)
- ep.clear ();
- }
-
- return r;
- }
- }
-
- // Next look in the current working directory. Crazy, I know.
- //
- // The recall path is the same as initial, though it might not be a bad
- // idea to prepend .\ for clarity.
- //
- {
- const string& d (traits::current_directory ());
-
- if (search (d.c_str (), d.size ()))
- return r;
- }
-
- // Now search in PATH. Recall is unchanged.
- //
- for (const char* b (getenv ("PATH")), *e;
- b != nullptr;
- b = (e != nullptr ? e + 1 : e))
- {
- e = strchr (b, traits::path_separator);
-
- // Empty path (i.e., a double colon or a colon at the beginning or end
- // of PATH) means search in the current directory. Silently skip invalid
- // paths.
- //
- try
- {
- if (search (b, e != nullptr ? e - b : strlen (b)))
- return r;
- }
- catch (const invalid_path&)
- {
- }
- }
-
- // Finally, if we were given a fallback, try that. This case is similar to
- // searching in the parent executable's directory.
- //
- if (!fb.empty ())
- {
- // I would have been nice to preserve trailing slash (by using
- // representation() instead of string()), but that would involve a
- // copy. Oh, well, can't always win.
- //
- if (search (fb.string ().c_str (), fb.string ().size ()))
- {
- // In this case we have to set the recall path. At least here we got
- // to keep the original slash.
- //
- rp = fb;
- rp /= f;
-
- // If recall is the same as effective, then set effective to empty.
- //
- if (rp == ep)
- ep.clear ();
-
- return r;
- }
- }
-
- // Did not find anything.
- //
- return process_path ();
- }
-
- class auto_handle
- {
- public:
- explicit
- auto_handle (HANDLE h = INVALID_HANDLE_VALUE) noexcept: handle_ (h) {}
-
- auto_handle (const auto_handle&) = delete;
- auto_handle& operator= (const auto_handle&) = delete;
-
- ~auto_handle () noexcept {reset ();}
-
- HANDLE
- get () const noexcept {return handle_;}
-
- HANDLE
- release () noexcept
- {
- HANDLE r (handle_);
- handle_ = INVALID_HANDLE_VALUE;
- return r;
- }
-
- void
- reset (HANDLE h = INVALID_HANDLE_VALUE) noexcept
- {
- if (handle_ != INVALID_HANDLE_VALUE)
- {
- bool r (CloseHandle (handle_));
-
- // The valid process, thread or file handle that has no IO operations
- // being performed on it should close successfully, unless something
- // is severely damaged.
- //
- assert (r);
- }
-
- handle_ = h;
- }
-
- private:
- HANDLE handle_;
- };
-
- // Make handles inheritable. The process_spawn_mutex must be pre-acquired for
- // exclusive access. Revert handles inheritability state in destructor.
- //
- // There is a period of time when the process ctor makes file handles it
- // passes to the child to be inheritable, that otherwise are not inheritable
- // by default. During this time these handles can also be inherited by other
- // (irrelevant) child processed spawned from other threads. That can lead to
- // some unwanted consequences, such as inability to delete a file
- // corresponding to such a handle until all childs, that the handle leaked
- // into, terminate. To prevent this behavior the specific sequence of steps
- // (that involves making handles inheritable, spawning process and reverting
- // handles to non-inheritable state back) will be performed after aquiring
- // the process_spawn_mutex (that is released afterwards).
- //
- class inheritability_guard
- {
- public:
- // Require the proof that the mutex is pre-acquired for exclusive access.
- //
- inheritability_guard (const ulock&) {}
-
- ~inheritability_guard ()
- {
- for (auto h: handles_)
- inheritable (h, false); // Can't throw.
- }
-
- void
- inheritable (HANDLE h)
- {
- inheritable (h, true); // Can throw.
- handles_.push_back (h);
- }
-
- private:
- void
- inheritable (HANDLE h, bool state)
- {
- if (!SetHandleInformation (
- h, HANDLE_FLAG_INHERIT, state ? HANDLE_FLAG_INHERIT : 0))
- {
- if (state)
- throw process_error (last_error_msg ());
-
- // We should be able to successfully reset the HANDLE_FLAG_INHERIT flag
- // that we successfully set, unless something is severely damaged.
- //
- assert (false);
- }
- }
-
- private:
- small_vector<HANDLE, 3> handles_;
- };
-
- process::
- process (const char* cwd,
- const process_path& pp, const char* args[],
- int in, int out, int err)
- {
- // Figure out if this is a batch file since running them requires starting
- // cmd.exe and passing the batch file as an argument (see CreateProcess()
- // for deails).
- //
- const char* batch (nullptr);
- {
- const char* p (pp.effect_string ());
- const char* e (path::traits::find_extension (p, strlen (p)));
- if (e != nullptr && (casecmp (e, ".bat") == 0 ||
- casecmp (e, ".cmd") == 0))
- {
- batch = getenv ("COMSPEC");
-
- if (batch == nullptr)
- batch = "C:\\Windows\\System32\\cmd.exe";
- }
- }
-
- fdpipe out_fd;
- fdpipe in_ofd;
- fdpipe in_efd;
-
- auto open_pipe = [] () -> fdpipe
- {
- try
- {
- return fdopen_pipe ();
- }
- catch (const ios_base::failure&)
- {
- // Translate to process_error.
- //
- // For old versions of g++ (as of 4.9) ios_base::failure is not derived
- // from system_error and so we cannot recover the errno value. On the
- // other hand the only possible values are EMFILE and ENFILE. Lets use
- // EMFILE as the more probable. Also let's make no distinction for VC.
- // This is a temporary code after all.
- //
- throw process_error (EMFILE);
- }
- };
-
- auto fail = [](const char* m = nullptr)
- {
- throw process_error (m == nullptr ? last_error_msg () : m);
- };
-
- auto open_null = [] () -> auto_fd
- {
- // Note that we are using a faster, temporary file-based emulation of
- // NUL since we have no way of making sure the child buffers things
- // properly (and by default they seem no to).
- //
- try
- {
- return fdnull (true);
- }
- catch (const ios_base::failure& e)
- {
- // Translate to process_error.
- //
- // For old versions of g++ (as of 4.9) ios_base::failure is not derived
- // from system_error and so we cannot recover the errno value. Lets use
- // EIO in this case. This is a temporary code after all.
- //
- const system_error* se (dynamic_cast<const system_error*> (&e));
-
- throw process_error (se != nullptr
- ? se->code ().value ()
- : EIO);
- }
- };
-
- // If we are asked to open null (-2) then open "half-pipe".
- //
- if (in == -1)
- out_fd = open_pipe ();
- else if (in == -2)
- out_fd.in = open_null ();
-
- if (out == -1)
- in_ofd = open_pipe ();
- else if (out == -2)
- in_ofd.out = open_null ();
-
- if (err == -1)
- in_efd = open_pipe ();
- else if (err == -2)
- in_efd.out = open_null ();
-
- // Create the process.
- //
-
- // Serialize the arguments to string.
- //
- string cmd_line;
- {
- auto append = [&cmd_line] (const string& a)
- {
- if (!cmd_line.empty ())
- cmd_line += ' ';
-
- // On Windows we need to protect values with spaces using quotes.
- // Since there could be actual quotes in the value, we need to escape
- // them.
- //
- bool quote (a.empty () || a.find (' ') != string::npos);
-
- if (quote)
- cmd_line += '"';
-
- for (size_t i (0); i < a.size (); ++i)
- {
- if (a[i] == '"')
- cmd_line += "\\\"";
- else
- cmd_line += a[i];
- }
-
- if (quote)
- cmd_line += '"';
- };
-
- if (batch != nullptr)
- {
- append (batch);
- append ("/c");
- append (pp.effect_string ());
- }
-
- for (const char* const* p (args + (batch != nullptr ? 1 : 0));
- *p != 0;
- ++p)
- append (*p);
- }
-
- // Prepare other process information.
- //
- STARTUPINFO si;
- PROCESS_INFORMATION pi;
- memset (&si, 0, sizeof (STARTUPINFO));
- memset (&pi, 0, sizeof (PROCESS_INFORMATION));
-
- si.cb = sizeof (STARTUPINFO);
- si.dwFlags |= STARTF_USESTDHANDLES;
-
- {
- ulock l (process_spawn_mutex);
- inheritability_guard ig (l);
-
- // Resolve file descriptor to HANDLE and make sure it is inherited. Note
- // that the handle is closed either when CloseHandle() is called for it
- // or when _close() is called for the associated file descriptor. Make
- // sure that either the original file descriptor or the resulting HANDLE
- // is closed but not both of them.
- //
- auto get_osfhandle = [&fail, &ig] (int fd) -> HANDLE
- {
- HANDLE h (reinterpret_cast<HANDLE> (_get_osfhandle (fd)));
- if (h == INVALID_HANDLE_VALUE)
- fail ("unable to obtain file handle");
-
- // Make the handle inheritable by the child unless it is already
- // inheritable.
- //
- DWORD f;
- if (!GetHandleInformation (h, &f))
- fail ();
-
- // Note that the flag check is essential as SetHandleInformation()
- // fails for standard handles and their duplicates.
- //
- if ((f & HANDLE_FLAG_INHERIT) == 0)
- ig.inheritable (h);
-
- return h;
- };
-
- si.hStdInput = in == -1 || in == -2
- ? get_osfhandle (out_fd.in.get ())
- : (in == STDIN_FILENO
- ? GetStdHandle (STD_INPUT_HANDLE)
- : get_osfhandle (in));
-
- si.hStdOutput = out == -1 || out == -2
- ? get_osfhandle (in_ofd.out.get ())
- : (out == STDOUT_FILENO
- ? GetStdHandle (STD_OUTPUT_HANDLE)
- : get_osfhandle (out));
-
- si.hStdError = err == -1 || err == -2
- ? get_osfhandle (in_efd.out.get ())
- : (err == STDERR_FILENO
- ? GetStdHandle (STD_ERROR_HANDLE)
- : get_osfhandle (err));
-
- // Perform standard stream redirection if requested.
- //
- if (err == STDOUT_FILENO)
- si.hStdError = si.hStdOutput;
- else if (out == STDERR_FILENO)
- si.hStdOutput = si.hStdError;
-
- if (err == STDIN_FILENO ||
- out == STDIN_FILENO ||
- in == STDOUT_FILENO ||
- in == STDERR_FILENO)
- fail ("invalid file descriptor");
-
- // Ready for some "Fun with Windows"(TM)? Here is what's in today's
- // episode: MSYS2 (actually, Cygwin) tries to emulate POSIX fork() on
- // Win32 via some pretty heavy hackery. As a result it makes a bunch of
- // assumptions such as that the child process will have the same virtual
- // memory position as the parent and that nobody interferes in its
- // child-parent dance.
- //
- // This, however, doesn't always pan out: for reasons unknown Windows
- // sometimes decides to start the child somewhere else (or, as Cygwin
- // FAQ puts it: "sometimes Windows sets up a process environment that is
- // even more hostile to fork() than usual"). Also things like Windows
- // Defender (collectively called Big List Of Dodgy Apps/BLODA in Cygwin
- // speak) do interfere in all kinds of ways.
- //
- // We also observe another issue that seem related: if we run multiple
- // MSYS2-based applications in parallel (either from the same process
- // or from several processes), then they sometimes terminate abnormally
- // (but quietly, without printing any of the cygheap/fork diagnostics)
- // with status 0xC0000142 (STATUS_DLL_INIT_FAILED).
- //
- // Cygwin FAQ suggests the following potential solutions:
- //
- // 1. Restart the process hoping things will pan out next time around.
- //
- // 2. Eliminate/disable programs from BLODA (disabling Defender helps
- // a lot but not entirely).
- //
- // 3. Apparently switching from 32 to 64-bit should help (less chance
- // for address collisions).
- //
- // 4. Rebase all the Cygwin DLLs (this is a topic for a another episode).
- //
- // To add to this list, we also have our own remedy (which is not
- // generally applicable):
- //
- // 5. Make sure processes that you start don't need to fork. A good
- // example would be tar that runs gz/bzip2/xz. Instead, we start and
- // pipe them ourselves.
- //
- // So what's coming next is a hack that implements remedy #1: after
- // starting the process we wait a bit (50ms) and check if it has
- // terminated with STATUS_DLL_INIT_FAILED (the assumption here is that
- // if this happens, it happens quickly). We then retry starting the
- // process for up to a second.
- //
- // One way to improve this implementation would be to only do it for
- // MSYS2-based programs, for example, by checking (EnumProcessModules())
- // if the process loaded the msys-2.0.dll (not clear though if it will
- // be in the returned list if it has failed to initialize). With this
- // improvement we could then wait longer and try harder.
- //
- optional<bool> msys; // Absent if we don't know.
-
- for (size_t ret (0); ret != 5; ++ret)
- {
- if (!CreateProcess (
- batch != nullptr ? batch : pp.effect_string (),
- const_cast<char*> (cmd_line.c_str ()),
- 0, // Process security attributes.
- 0, // Primary thread security attributes.
- true, // Inherit handles.
- 0, // Creation flags.
- 0, // Use our environment.
- cwd != nullptr && *cwd != '\0' ? cwd : nullptr,
- &si,
- &pi))
- fail ();
-
- auto_handle (pi.hThread).reset (); // Close.
-
- // Detect if this is an MSYS2 process by checking if the process has
- // loaded msys-2.0.dll.
- //
- size_t wait (200);
-
- if (!msys)
- {
- // Wait a bit for the process to load its DLLs.
- //
- if (WaitForSingleObject (pi.hProcess, 50) == WAIT_TIMEOUT)
- {
- wait -= 50;
-
- DWORD mn;
- HMODULE ms[32]; // Normally it is one of the first.
-
- if (EnumProcessModules (pi.hProcess, ms, sizeof (ms), &mn))
- {
- for (DWORD i (0); !msys && i != mn / sizeof (HMODULE); ++i)
- {
- char p[_MAX_PATH + 1];
- if (GetModuleFileNameExA (pi.hProcess, ms[i], p, sizeof (p)))
- {
- size_t n (strlen (p));
- if (n >= 12 && casecmp (p + n - 12, "msys-2.0.dll") == 0)
- msys = true;
- }
- }
-
- if (!msys)
- msys = false;
- }
- // EnumProcessModules() failed (presumably because the process has
- // already exited), fall through.
- }
- // Process exited, fall through.
- }
-
- if (msys && !*msys)
- break;
-
- // Wait a bit longer and check if the process has terminated. If it is
- // still running then we assume all is good. Otherwise, retry if this
- // is the DLL initialization error.
- //
- DWORD s;
- if (WaitForSingleObject (pi.hProcess, wait) != WAIT_OBJECT_0 ||
- !GetExitCodeProcess (pi.hProcess, &s) ||
- s != STATUS_DLL_INIT_FAILED)
- break;
- }
- } // Revert handles back to non-inheritable and release the lock.
-
- // 0 has a special meaning denoting a terminated process handle.
- //
- this->handle = pi.hProcess;
- assert (this->handle != 0 && this->handle != INVALID_HANDLE_VALUE);
-
- this->out_fd = move (out_fd.out);
- this->in_ofd = move (in_ofd.in);
- this->in_efd = move (in_efd.in);
- }
-
- bool process::
- wait (bool ie)
- {
- if (handle != 0)
- {
- DWORD es;
- DWORD e (NO_ERROR);
- if (WaitForSingleObject (handle, INFINITE) != WAIT_OBJECT_0 ||
- !GetExitCodeProcess (handle, &es))
- e = GetLastError ();
-
- auto_handle h (handle); // Auto-deleter.
- handle = 0; // We have tried.
-
- if (e == NO_ERROR)
- {
- exit = process_exit ();
- exit->status = es;
- }
- else
- {
- // If ignore errors then just leave exit nullopt, so it has "no exit
- // information available" semantics.
- //
- if (!ie)
- throw process_error (error_msg (e));
- }
- }
-
- return exit && exit->normal () && exit->code () == 0;
- }
-
- bool process::
- try_wait ()
- {
- if (handle != 0)
- {
- DWORD r (WaitForSingleObject (handle, 0));
- if (r == WAIT_TIMEOUT)
- return false;
-
- DWORD es;
- DWORD e (NO_ERROR);
- if (r != WAIT_OBJECT_0 || !GetExitCodeProcess (handle, &es))
- e = GetLastError ();
-
- auto_handle h (handle);
- handle = 0; // We have tried.
-
- if (e != NO_ERROR)
- throw process_error (error_msg (e));
-
- exit = process_exit ();
- exit->status = es;
- }
-
- return true;
- }
-
- process::id_type process::
- id () const
- {
- id_type r (GetProcessId (handle));
-
- if (r == 0)
- throw process_error (last_error_msg ());
-
- return r;
- }
-
- process::id_type process::
- current_id ()
- {
- return GetCurrentProcessId ();
- }
-
- // process_exit
- //
- process_exit::
- process_exit (code_type c)
- //
- // The NTSTATUS value returned by GetExitCodeProcess() has the following
- // layout of bits:
- //
- // [ 0, 16) - program exit code or exception code
- // [16, 29) - facility
- // [29, 30) - flag indicating if the status value is customer-defined
- // [30, 31) - severity (00 -success, 01 - informational, 10 - warning,
- // 11 - error)
- //
- : status (c)
- {
- }
-
- bool process_exit::
- normal () const
- {
- // We consider status values with severities other than 0 not being
- // returned by the process and so denoting the abnormal termination.
- //
- return ((status >> 30) & 0x3) == 0;
- }
-
- process_exit::code_type process_exit::
- code () const
- {
- assert (normal ());
- return status & 0xFFFF;
- }
-
- string process_exit::
- description () const
- {
- assert (!normal ());
-
- // Error codes (or, as MSDN calls them, exception codes) are defined in
- // ntstatus.h. It is possible to obtain message descriptions for them
- // using FormatMessage() with the FORMAT_MESSAGE_FROM_HMODULE flag and the
- // handle returned by LoadLibrary("NTDLL.DLL") call. However, the returned
- // messages are pretty much useless being format strings. For example for
- // STATUS_ACCESS_VIOLATION error code the message string is "The
- // instruction at 0x%p referenced memory at 0x%p. The memory could not be
- // %s.". Also under Wine (1.9.8) it is not possible to obtain such a
- // descriptions at all for some reason.
- //
- // Let's use a custom code-to-message mapping for the most common error
- // codes, and extend it as needed.
- //
- // Note that the error code most likely will be messed up if the abnormal
- // termination of a process is intercepted with the "searching for
- // available solution" message box or debugger invocation. Also note that
- // the same failure can result in different exit codes for a process being
- // run on Windows nativelly and under Wine. For example under Wine 1.9.8 a
- // process that fails due to the stack overflow exits normally with 0
- // status but prints the "err:seh:setup_exception stack overflow ..."
- // message to stderr.
- //
- switch (status)
- {
- case STATUS_ACCESS_VIOLATION: return "access violation";
- case STATUS_DLL_INIT_FAILED: return "DLL initialization failed";
- case STATUS_INTEGER_DIVIDE_BY_ZERO: return "integer divided by zero";
-
- // VC-compiled program that calls abort() terminates with this error code
- // (0xC0000409). That differs from MinGW GCC-compiled one, that exits
- // normally with status 3 (conforms to MSDN). Under Wine (1.9.8) such a
- // program exits with status 3 for both VC and MinGW GCC. Sounds weird.
- //
- case STATUS_STACK_BUFFER_OVERRUN: return "stack buffer overrun";
- case STATUS_STACK_OVERFLOW: return "stack overflow";
-
- default:
- {
- string desc ("unknown error 0x");
-
- // Add error code hex representation (as it is defined in ntstatus.h).
- //
- // Strange enough, there is no easy way to convert a number into the
- // hex string representation (not using streams).
- //
- const char digits[] = "0123456789ABCDEF";
- bool skip (true); // Skip leading zeros.
-
- auto add = [&desc, &digits, &skip] (unsigned char d, bool force)
- {
- if (d != 0 || !skip || force)
- {
- desc += digits[d];
- skip = false;
- }
- };
-
- for (int i (sizeof (status) - 1); i >= 0 ; --i)
- {
- unsigned char c ((status >> (i * 8)) & 0xFF);
- add ((c >> 4) & 0xF, false); // Convert the high 4 bits to a digit.
- add (c & 0xF, i == 0); // Convert the low 4 bits to a digit.
- }
-
- return desc;
- }
- }
- }
-
-#endif // _WIN32
-}
diff --git a/butl/process.ixx b/butl/process.ixx
deleted file mode 100644
index 40f6361..0000000
--- a/butl/process.ixx
+++ /dev/null
@@ -1,207 +0,0 @@
-// file : butl/process.ixx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <utility> // move()
-
-namespace butl
-{
- // process_path
- //
- inline process_path::
- ~process_path ()
- {
- if (args0_ != nullptr)
- *args0_ = initial;
- }
-
- inline process_path::
- process_path (const char* i, path&& r, path&& e)
- : initial (i),
- recall (std::move (r)),
- effect (std::move (e)),
- args0_ (nullptr) {}
-
- inline process_path::
- process_path (process_path&& p)
- : initial (p.initial),
- recall (std::move (p.recall)),
- effect (std::move (p.effect)),
- args0_ (p.args0_)
- {
- p.args0_ = nullptr;
- }
-
- inline process_path& process_path::
- operator= (process_path&& p)
- {
- if (this != &p)
- {
- if (args0_ != nullptr)
- *args0_ = initial;
-
- initial = p.initial;
- recall = std::move (p.recall);
- effect = std::move (p.effect);
- args0_ = p.args0_;
-
- p.args0_ = nullptr;
- }
-
- return *this;
- }
-
- inline const char* process_path::
- recall_string () const
- {
- return recall.empty () ? initial : recall.string ().c_str ();
- }
-
- inline const char* process_path::
- effect_string () const
- {
- return effect.empty () ? recall_string () : effect.string ().c_str ();
- }
-
- // process_exit
- //
-#ifdef _WIN32
- inline int process_exit::
- signal () const
- {
- return 0;
- }
-
- inline bool process_exit::
- core () const
- {
- return false;
- }
-#endif
-
- // process
- //
-#ifndef _WIN32
- inline process::id_type process::
- id () const
- {
- return handle;
- }
-#endif
-
- inline process_path process::
- path_search (const char*& a0, const dir_path& fb)
- {
- process_path r (path_search (a0, true, fb));
-
- if (!r.recall.empty ())
- {
- r.args0_ = &a0;
- a0 = r.recall.string ().c_str ();
- }
-
- return r;
- }
-
- inline process_path process::
- path_search (const std::string& f, bool i, const dir_path& fb)
- {
- return path_search (f.c_str (), i, fb);
- }
-
- inline process_path process::
- path_search (const path& f, bool i, const dir_path& fb)
- {
- return path_search (f.string ().c_str (), i, fb);
- }
-
- inline process_path process::
- try_path_search (const std::string& f, bool i, const dir_path& fb)
- {
- return try_path_search (f.c_str (), i, fb);
- }
-
- inline process_path process::
- try_path_search (const path& f, bool i, const dir_path& fb)
- {
- return try_path_search (f.string ().c_str (), i, fb);
- }
-
- inline process::
- process (optional<process_exit> e)
- : handle (0),
- exit (std::move (e)),
- out_fd (-1),
- in_ofd (-1),
- in_efd (-1)
- {
- }
-
- inline process::
- process (const char* args[], int in, int out, int err)
- : process (nullptr, path_search (args[0]), args, in, out, err) {}
-
- inline process::
- process (const process_path& pp, const char* args[],
- int in, int out, int err)
- : process (nullptr, pp, args, in, out, err) {}
-
- inline process::
- process (const char* args[], process& in, int out, int err)
- : process (nullptr, path_search (args[0]), args, in, out, err) {}
-
- inline process::
- process (const process_path& pp, const char* args[],
- process& in, int out, int err)
- : process (nullptr, pp, args, in, out, err) {}
-
- inline process::
- process (const char* cwd, const char* args[], int in, int out, int err)
- : process (cwd, path_search (args[0]), args, in, out, err) {}
-
- inline process::
- process (const char* cwd, const char* args[], process& in, int out, int err)
- : process (cwd, path_search (args[0]), args, in, out, err) {}
-
- inline process::
- process (process&& p)
- : handle (p.handle),
- exit (std::move (p.exit)),
- out_fd (std::move (p.out_fd)),
- in_ofd (std::move (p.in_ofd)),
- in_efd (std::move (p.in_efd))
- {
- p.handle = 0;
- }
-
- inline process& process::
- operator= (process&& p)
- {
- if (this != &p)
- {
- if (handle != 0)
- wait ();
-
- handle = p.handle;
- exit = std::move (p.exit);
- out_fd = std::move (p.out_fd);
- in_ofd = std::move (p.in_ofd);
- in_efd = std::move (p.in_efd);
-
- p.handle = 0;
- }
-
- return *this;
- }
-
- inline bool process::
- try_wait (bool& s)
- {
- bool r (try_wait ());
-
- if (r)
- s = exit && exit->normal () && exit->code () == 0;
-
- return r;
- }
-}
diff --git a/butl/sendmail b/butl/sendmail
deleted file mode 100644
index fe86285..0000000
--- a/butl/sendmail
+++ /dev/null
@@ -1,91 +0,0 @@
-// file : butl/sendmail -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_SENDMAIL
-#define BUTL_SENDMAIL
-
-#include <string>
-
-#include <butl/export>
-
-#include <butl/process>
-#include <butl/fdstream>
-#include <butl/small-vector>
-
-namespace butl
-{
- // Send email using the sendmail(1) program.
- //
- // Write the body of the email to out. Note that you must explicitly close
- // it before calling wait(). Throw process_error and io_error (both derive
- // from system_error) in case of errors.
- //
- // Typical usage:
- //
- // try
- // {
- // sendmail sm (2, // Diagnostics to stderr.
- // "", // Default From: address.
- // "Test subject",
- // {"test@example.com"});
- //
- // sm.out << "Test body" << endl;
- //
- // sm.out.close ();
- //
- // if (!sm.wait ())
- // ... // sendmail returned non-zero status.
- // }
- // catch (const std::system_error& e)
- // {
- // cerr << "sendmail error: " << e << endl;
- // }
- //
- class LIBBUTL_EXPORT sendmail: public process
- {
- public:
- ofdstream out;
-
- // Notes:
- //
- // - If from is empty then the process user's address is used.
- //
- // - The to/cc/bcc addressed should already be quoted if required.
- //
- using recipients_type = small_vector<std::string, 1>;
-
- template <typename E, typename... O>
- sendmail (E&& err,
- const std::string& from,
- const std::string& subject,
- const recipients_type& to,
- const recipients_type& cc = recipients_type (),
- const recipients_type& bcc = recipients_type (),
- O&&... options);
-
- // Version with the command line callback (see process_run() for details).
- //
- template <typename C, typename E, typename... O>
- sendmail (const C&,
- E&& err,
- const std::string& from,
- const std::string& subject,
- const recipients_type& to,
- const recipients_type& cc = recipients_type (),
- const recipients_type& bcc = recipients_type (),
- O&&... options);
-
- private:
- void
- headers (const std::string& from,
- const std::string& subj,
- const recipients_type& to,
- const recipients_type& cc,
- const recipients_type& bcc);
- };
-}
-
-#include <butl/sendmail.ixx>
-
-#endif // BUTL_SENDMAIL
diff --git a/butl/sendmail.cxx b/butl/sendmail.cxx
deleted file mode 100644
index 103bab5..0000000
--- a/butl/sendmail.cxx
+++ /dev/null
@@ -1,40 +0,0 @@
-// file : butl/sendmail.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <butl/sendmail>
-
-using namespace std;
-
-namespace butl
-{
- void sendmail::
- headers (const std::string& from,
- const std::string& subj,
- const recipients_type& to,
- const recipients_type& cc,
- const recipients_type& bcc)
- {
- if (!from.empty ())
- out << "From: " << from << endl;
-
- auto rcp =[this] (const char* h, const recipients_type& rs)
- {
- if (!rs.empty ())
- {
- bool f (true);
- out << h << ": ";
- for (const string& r: rs)
- out << (f ? (f = false, "") : ", ") << r;
- out << endl;
- }
- };
-
- rcp ("To", to);
- rcp ("Cc", cc);
- rcp ("Bcc", bcc);
-
- out << "Subject: " << subj << endl
- << endl; // Header/body separator.
- }
-}
diff --git a/butl/sendmail.ixx b/butl/sendmail.ixx
deleted file mode 100644
index 2e982c1..0000000
--- a/butl/sendmail.ixx
+++ /dev/null
@@ -1,68 +0,0 @@
-// file : butl/sendmail.ixx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <utility> // move(), forward()
-
-namespace butl
-{
- template <typename E, typename... O>
- inline sendmail::
- sendmail (E&& err,
- const std::string& from,
- const std::string& subj,
- const recipients_type& to,
- const recipients_type& cc,
- const recipients_type& bcc,
- O&&... options)
- : sendmail ([] (const char* [], std::size_t) {},
- std::forward<E> (err),
- from,
- subj,
- to,
- cc,
- bcc,
- std::forward<O> (options)...)
- {
- }
-
- template <typename C, typename E, typename... O>
- inline sendmail::
- sendmail (const C& cmdc,
- E&& err,
- const std::string& from,
- const std::string& subj,
- const recipients_type& to,
- const recipients_type& cc,
- const recipients_type& bcc,
- O&&... options)
- {
- fdpipe pipe (fdopen_pipe ()); // Text mode seems appropriate.
-
- process& p (*this);
- p = process_start (cmdc,
- pipe.in,
- 2, // No output expected so redirect to stderr.
- std::forward<E> (err),
- dir_path (),
- "sendmail",
- "-i", // Don't treat '.' as the end of input.
- "-t", // Read recipients from headers.
- std::forward<O> (options)...);
-
- // Close the reading end of the pipe not to block on writing if sendmail
- // terminates before that.
- //
- pipe.in.close ();
-
- out.open (std::move (pipe.out));
-
- // Write headers.
- //
- // Note that if this throws, then the ofdstream will be closed first
- // (which should signal to the process we are done). Then the process's
- // destructor will wait for its termination ignoring any errors.
- //
- headers (from, subj, to, cc, bcc);
- }
-}
diff --git a/butl/sha256 b/butl/sha256
deleted file mode 100644
index 1c20a57..0000000
--- a/butl/sha256
+++ /dev/null
@@ -1,101 +0,0 @@
-// file : butl/sha256 -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_SHA256
-#define BUTL_SHA256
-
-#include <string>
-#include <cstring> // strlen()
-#include <cstdint>
-#include <cstddef> // size_t
-
-#include <butl/export>
-
-namespace butl
-{
- // SHA256 checksum calculator.
- //
- // For a single chunk of data a sum can be obtained in one line, for
- // example:
- //
- // cerr << sha256 ("123").string () << endl;
- //
- class LIBBUTL_EXPORT sha256
- {
- public:
- sha256 ();
-
- // Append binary data.
- //
- void
- append (const void*, std::size_t);
-
- sha256 (const void* b, std::size_t n): sha256 () {append (b, n);}
-
- // Append string.
- //
- // Note that the hash includes the '\0' terminator. Failed that, a call
- // with an empty string will be indistinguishable from no call at all.
- //
- void
- append (const std::string& s) {append (s.c_str (), s.size () + 1);}
-
- void
- append (const char* s) {append (s, std::strlen (s) + 1);}
-
- explicit
- sha256 (const std::string& s): sha256 () {append (s);}
-
- explicit
- sha256 (const char* s): sha256 () {append (s);}
-
- // Extract result.
- //
- // It can be obtained as either a 32-byte binary digest or as a 64-
- // character hex-encoded C-string.
- //
- using digest_type = std::uint8_t[32];
-
- const digest_type&
- binary () const;
-
- const char*
- string () const;
-
- public:
- struct context
- {
- std::uint32_t state[8];
- std::uint64_t count;
- std::uint8_t buf[64];
- };
-
- private:
- union
- {
- mutable context ctx_;
- mutable char str_[65];
- };
-
- mutable digest_type bin_;
- mutable bool done_;
- };
-
- // Convert a SHA256 string representation (64 hex digits) to the fingerprint
- // canonical representation (32 colon-separated upper case hex digit pairs,
- // like 01:AB:CD:...). Throw invalid_argument if the argument is not a valid
- // SHA256 string.
- //
- LIBBUTL_EXPORT std::string
- sha256_to_fingerprint (const std::string&);
-
- // Convert a fingerprint (32 colon-separated hex digit pairs) to the SHA256
- // string representation (64 lower case hex digits). Throw invalid_argument
- // if the argument is not a valid fingerprint.
- //
- LIBBUTL_EXPORT std::string
- fingerprint_to_sha256 (const std::string&);
-}
-
-#endif // BUTL_SHA256
diff --git a/butl/sha256.cxx b/butl/sha256.cxx
deleted file mode 100644
index fa14d0e..0000000
--- a/butl/sha256.cxx
+++ /dev/null
@@ -1,142 +0,0 @@
-// file : butl/sha256.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <butl/sha256>
-
-// C interface for sha256c.
-//
-#include <stdint.h>
-#include <stddef.h> // size_t
-
-#include <cctype> // isxdigit()
-#include <stdexcept> // invalid_argument
-
-#include <butl/utility> // ucase(), lcase()
-
-using SHA256_CTX = butl::sha256::context;
-
-extern "C"
-{
- static void SHA256_Init (SHA256_CTX*);
- static void SHA256_Update (SHA256_CTX*, const void*, size_t);
- static void SHA256_Final (uint8_t[32], SHA256_CTX*);
-
-#include "sha256c.c"
-}
-
-using namespace std;
-
-namespace butl
-{
- // sha256
- //
- sha256::
- sha256 ()
- : done_ (false)
- {
- SHA256_Init (&ctx_);
- }
-
- void sha256::
- append (const void* b, size_t n)
- {
- SHA256_Update (&ctx_, b, n);
- }
-
- const sha256::digest_type& sha256::
- binary () const
- {
- if (!done_)
- {
- SHA256_Final (bin_, &ctx_);
- done_ = true;
- str_[0] = '\0'; // Indicate we haven't computed the string yet.
- }
-
- return bin_;
- }
-
- static const char hex_map[16] = {
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- 'a', 'b', 'c', 'd', 'e', 'f'};
-
- const char* sha256::
- string () const
- {
- if (!done_)
- binary ();
-
- if (str_[0] == '\0')
- {
- for (size_t i (0); i != 32; ++i)
- {
- str_[i * 2] = hex_map[bin_[i] >> 4];
- str_[i * 2 + 1] = hex_map[bin_[i] & 0x0f];
- }
-
- str_[64] = '\0';
- }
-
- return str_;
- }
-
- // Conversion functions
- //
- string
- sha256_to_fingerprint (const string& s)
- {
- auto bad = []() {throw invalid_argument ("invalid SHA256 string");};
-
- size_t n (s.size ());
- if (n != 64)
- bad ();
-
- string f;
- f.reserve (n + 31);
- for (size_t i (0); i < n; ++i)
- {
- char c (s[i]);
- if (!isxdigit (c))
- bad ();
-
- if (i > 0 && i % 2 == 0)
- f += ":";
-
- f += ucase (c);
- }
-
- return f;
- }
-
- string
- fingerprint_to_sha256 (const string& f)
- {
- auto bad = []() {throw invalid_argument ("invalid fingerprint");};
-
- size_t n (f.size ());
- if (n != 32 * 3 - 1)
- bad ();
-
- string s;
- s.reserve (64);
- for (size_t i (0); i < n; ++i)
- {
- char c (f[i]);
- if ((i + 1) % 3 == 0)
- {
- if (c != ':')
- bad ();
- }
- else
- {
- if (!isxdigit (c))
- bad ();
-
- s += lcase (c);
- }
- }
-
- return s;
- }
-}
diff --git a/butl/sha256c.c b/butl/sha256c.c
deleted file mode 100644
index 156751c..0000000
--- a/butl/sha256c.c
+++ /dev/null
@@ -1,393 +0,0 @@
-/*-
- * Copyright 2005 Colin Percival
- * Copyright (c) 2017 Code Synthesis Ltd
- * 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 AUTHOR 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 AUTHOR 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.
- */
-
-#include <stdint.h>
-#include <stddef.h> /* size_t */
-
-#ifdef SHA256_TEST_DRIVER
-
-struct context
-{
- uint32_t state[8];
- uint64_t count;
- uint8_t buf[64];
-};
-
-typedef struct context SHA256_CTX;
-
-static void SHA256_Init (SHA256_CTX*);
-static void SHA256_Update (SHA256_CTX*, const void*, size_t);
-static void SHA256_Final (uint8_t[32], SHA256_CTX*);
-
-#include <string.h>
-#include <assert.h>
-
-int
-main ()
-{
- SHA256_CTX c;
- uint8_t r[32];
-
- /* "" e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 */
- SHA256_Init (&c);
- SHA256_Final (r, &c);
- assert (r[0] == 0xe3 && r[31] == 0x55);
-
- /* "123" a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3 */
- SHA256_Init (&c);
- SHA256_Update (&c, "123", 3);
- SHA256_Final (r, &c);
- assert (r[0] == 0xa6 && r[31] == 0xe3);
-
- return 0;
-}
-
-#endif /* SHA256_TEST_DRIVER */
-
-#ifdef __FreeBSD__
-# include <sys/endian.h> /* BYTE_ORDER, be32dec(), be32enc(), be64enc */
-#else
-# if defined(_WIN32)
-# ifndef BYTE_ORDER
-# define BIG_ENDIAN 4321
-# define LITTLE_ENDIAN 1234
-# define BYTE_ORDER LITTLE_ENDIAN
-# endif
-# else
-# include <sys/param.h> /* BYTE_ORDER/__BYTE_ORDER */
-# ifndef BYTE_ORDER
-# ifdef __BYTE_ORDER
-# define BYTE_ORDER __BYTE_ORDER
-# define BIG_ENDIAN __BIG_ENDIAN
-# define LITTLE_ENDIAN __LITTLE_ENDIAN
-# else
-# error no BYTE_ORDER/__BYTE_ORDER define
-# endif
-# endif
-# endif
-
-static uint32_t
-be32dec(const void *pp)
-{
- unsigned char const *p = (unsigned char const *)pp;
-
- return ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
-}
-
-static void
-be32enc(void *pp, uint32_t u)
-{
- unsigned char *p = (unsigned char *)pp;
-
- p[0] = (u >> 24) & 0xff;
- p[1] = (u >> 16) & 0xff;
- p[2] = (u >> 8) & 0xff;
- p[3] = u & 0xff;
-}
-
-static void
-be64enc(void *pp, uint64_t u)
-{
- unsigned char *p = (unsigned char *)pp;
-
- p[0] = (u >> 56) & 0xff;
- p[1] = (u >> 48) & 0xff;
- p[2] = (u >> 40) & 0xff;
- p[3] = (u >> 32) & 0xff;
- p[4] = (u >> 24) & 0xff;
- p[5] = (u >> 16) & 0xff;
- p[6] = (u >> 8) & 0xff;
- p[7] = u & 0xff;
-}
-#endif
-
-/* The rest is the unmodified (except for a few explicit casts to make it
- compilable in C++), latest implementation from FreeBSD. */
-
-#include <string.h>
-
-#if BYTE_ORDER == BIG_ENDIAN
-
-/* Copy a vector of big-endian uint32_t into a vector of bytes */
-#define be32enc_vect(dst, src, len) \
- memcpy((void *)dst, (const void *)src, (size_t)len)
-
-/* Copy a vector of bytes into a vector of big-endian uint32_t */
-#define be32dec_vect(dst, src, len) \
- memcpy((void *)dst, (const void *)src, (size_t)len)
-
-#else /* BYTE_ORDER != BIG_ENDIAN */
-
-/*
- * Encode a length len/4 vector of (uint32_t) into a length len vector of
- * (unsigned char) in big-endian form. Assumes len is a multiple of 4.
- */
-static void
-be32enc_vect(unsigned char *dst, const uint32_t *src, size_t len)
-{
- size_t i;
-
- for (i = 0; i < len / 4; i++)
- be32enc(dst + i * 4, src[i]);
-}
-
-/*
- * Decode a big-endian length len vector of (unsigned char) into a length
- * len/4 vector of (uint32_t). Assumes len is a multiple of 4.
- */
-static void
-be32dec_vect(uint32_t *dst, const unsigned char *src, size_t len)
-{
- size_t i;
-
- for (i = 0; i < len / 4; i++)
- dst[i] = be32dec(src + i * 4);
-}
-
-#endif /* BYTE_ORDER != BIG_ENDIAN */
-
-/* Elementary functions used by SHA256 */
-#define Ch(x, y, z) ((x & (y ^ z)) ^ z)
-#define Maj(x, y, z) ((x & (y | z)) | (y & z))
-#define SHR(x, n) (x >> n)
-#define ROTR(x, n) ((x >> n) | (x << (32 - n)))
-#define S0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22))
-#define S1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25))
-#define s0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3))
-#define s1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10))
-
-/* SHA256 round function */
-#define RND(a, b, c, d, e, f, g, h, k) \
- t0 = h + S1(e) + Ch(e, f, g) + k; \
- t1 = S0(a) + Maj(a, b, c); \
- d += t0; \
- h = t0 + t1;
-
-/* Adjusted round function for rotating state */
-#define RNDr(S, W, i, k) \
- RND(S[(64 - i) % 8], S[(65 - i) % 8], \
- S[(66 - i) % 8], S[(67 - i) % 8], \
- S[(68 - i) % 8], S[(69 - i) % 8], \
- S[(70 - i) % 8], S[(71 - i) % 8], \
- W[i] + k)
-
-/*
- * SHA256 block compression function. The 256-bit state is transformed via
- * the 512-bit input block to produce a new state.
- */
-static void
-SHA256_Transform(uint32_t * state, const unsigned char block[64])
-{
- uint32_t W[64];
- uint32_t S[8];
- uint32_t t0, t1;
- int i;
-
- /* 1. Prepare message schedule W. */
- be32dec_vect(W, block, 64);
- for (i = 16; i < 64; i++)
- W[i] = s1(W[i - 2]) + W[i - 7] + s0(W[i - 15]) + W[i - 16];
-
- /* 2. Initialize working variables. */
- memcpy(S, state, 32);
-
- /* 3. Mix. */
- RNDr(S, W, 0, 0x428a2f98);
- RNDr(S, W, 1, 0x71374491);
- RNDr(S, W, 2, 0xb5c0fbcf);
- RNDr(S, W, 3, 0xe9b5dba5);
- RNDr(S, W, 4, 0x3956c25b);
- RNDr(S, W, 5, 0x59f111f1);
- RNDr(S, W, 6, 0x923f82a4);
- RNDr(S, W, 7, 0xab1c5ed5);
- RNDr(S, W, 8, 0xd807aa98);
- RNDr(S, W, 9, 0x12835b01);
- RNDr(S, W, 10, 0x243185be);
- RNDr(S, W, 11, 0x550c7dc3);
- RNDr(S, W, 12, 0x72be5d74);
- RNDr(S, W, 13, 0x80deb1fe);
- RNDr(S, W, 14, 0x9bdc06a7);
- RNDr(S, W, 15, 0xc19bf174);
- RNDr(S, W, 16, 0xe49b69c1);
- RNDr(S, W, 17, 0xefbe4786);
- RNDr(S, W, 18, 0x0fc19dc6);
- RNDr(S, W, 19, 0x240ca1cc);
- RNDr(S, W, 20, 0x2de92c6f);
- RNDr(S, W, 21, 0x4a7484aa);
- RNDr(S, W, 22, 0x5cb0a9dc);
- RNDr(S, W, 23, 0x76f988da);
- RNDr(S, W, 24, 0x983e5152);
- RNDr(S, W, 25, 0xa831c66d);
- RNDr(S, W, 26, 0xb00327c8);
- RNDr(S, W, 27, 0xbf597fc7);
- RNDr(S, W, 28, 0xc6e00bf3);
- RNDr(S, W, 29, 0xd5a79147);
- RNDr(S, W, 30, 0x06ca6351);
- RNDr(S, W, 31, 0x14292967);
- RNDr(S, W, 32, 0x27b70a85);
- RNDr(S, W, 33, 0x2e1b2138);
- RNDr(S, W, 34, 0x4d2c6dfc);
- RNDr(S, W, 35, 0x53380d13);
- RNDr(S, W, 36, 0x650a7354);
- RNDr(S, W, 37, 0x766a0abb);
- RNDr(S, W, 38, 0x81c2c92e);
- RNDr(S, W, 39, 0x92722c85);
- RNDr(S, W, 40, 0xa2bfe8a1);
- RNDr(S, W, 41, 0xa81a664b);
- RNDr(S, W, 42, 0xc24b8b70);
- RNDr(S, W, 43, 0xc76c51a3);
- RNDr(S, W, 44, 0xd192e819);
- RNDr(S, W, 45, 0xd6990624);
- RNDr(S, W, 46, 0xf40e3585);
- RNDr(S, W, 47, 0x106aa070);
- RNDr(S, W, 48, 0x19a4c116);
- RNDr(S, W, 49, 0x1e376c08);
- RNDr(S, W, 50, 0x2748774c);
- RNDr(S, W, 51, 0x34b0bcb5);
- RNDr(S, W, 52, 0x391c0cb3);
- RNDr(S, W, 53, 0x4ed8aa4a);
- RNDr(S, W, 54, 0x5b9cca4f);
- RNDr(S, W, 55, 0x682e6ff3);
- RNDr(S, W, 56, 0x748f82ee);
- RNDr(S, W, 57, 0x78a5636f);
- RNDr(S, W, 58, 0x84c87814);
- RNDr(S, W, 59, 0x8cc70208);
- RNDr(S, W, 60, 0x90befffa);
- RNDr(S, W, 61, 0xa4506ceb);
- RNDr(S, W, 62, 0xbef9a3f7);
- RNDr(S, W, 63, 0xc67178f2);
-
- /* 4. Mix local working variables into global state */
- for (i = 0; i < 8; i++)
- state[i] += S[i];
-}
-
-static unsigned char PAD[64] = {
- 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-};
-
-/* Add padding and terminating bit-count. */
-static void
-SHA256_Pad(SHA256_CTX * ctx)
-{
- unsigned char len[8];
- uint32_t r, plen;
-
- /*
- * Convert length to a vector of bytes -- we do this now rather
- * than later because the length will change after we pad.
- */
- be64enc(len, ctx->count);
-
- /* Add 1--64 bytes so that the resulting length is 56 mod 64 */
- r = (ctx->count >> 3) & 0x3f;
- plen = (r < 56) ? (56 - r) : (120 - r);
- SHA256_Update(ctx, PAD, (size_t)plen);
-
- /* Add the terminating bit-count */
- SHA256_Update(ctx, len, 8);
-}
-
-/* SHA-256 initialization. Begins a SHA-256 operation. */
-void
-SHA256_Init(SHA256_CTX * ctx)
-{
-
- /* Zero bits processed so far */
- ctx->count = 0;
-
- /* Magic initialization constants */
- ctx->state[0] = 0x6A09E667;
- ctx->state[1] = 0xBB67AE85;
- ctx->state[2] = 0x3C6EF372;
- ctx->state[3] = 0xA54FF53A;
- ctx->state[4] = 0x510E527F;
- ctx->state[5] = 0x9B05688C;
- ctx->state[6] = 0x1F83D9AB;
- ctx->state[7] = 0x5BE0CD19;
-}
-
-/* Add bytes into the hash */
-void
-SHA256_Update(SHA256_CTX * ctx, const void *in, size_t len)
-{
- uint64_t bitlen;
- uint32_t r;
- const unsigned char *src = (const unsigned char *) (in);
-
- /* Number of bytes left in the buffer from previous updates */
- r = (ctx->count >> 3) & 0x3f;
-
- /* Convert the length into a number of bits */
- bitlen = len << 3;
-
- /* Update number of bits */
- ctx->count += bitlen;
-
- /* Handle the case where we don't need to perform any transforms */
- if (len < 64 - r) {
- memcpy(&ctx->buf[r], src, len);
- return;
- }
-
- /* Finish the current block */
- memcpy(&ctx->buf[r], src, 64 - r);
- SHA256_Transform(ctx->state, ctx->buf);
- src += 64 - r;
- len -= 64 - r;
-
- /* Perform complete blocks */
- while (len >= 64) {
- SHA256_Transform(ctx->state, src);
- src += 64;
- len -= 64;
- }
-
- /* Copy left over data into buffer */
- memcpy(ctx->buf, src, len);
-}
-
-/*
- * SHA-256 finalization. Pads the input data, exports the hash value,
- * and clears the context state.
- */
-void
-SHA256_Final(uint8_t digest[32], SHA256_CTX * ctx)
-{
-
- /* Add padding */
- SHA256_Pad(ctx);
-
- /* Write the hash */
- be32enc_vect(digest, ctx->state, 32);
-
- /* Clear the context state */
- memset((void *)ctx, 0, sizeof(*ctx));
-}
diff --git a/butl/sha256c.c.orig b/butl/sha256c.c.orig
deleted file mode 100644
index da9b02c..0000000
--- a/butl/sha256c.c.orig
+++ /dev/null
@@ -1,316 +0,0 @@
-/*-
- * Copyright 2005 Colin Percival
- * 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 AUTHOR 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 AUTHOR 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.
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include <sys/endian.h>
-#include <sys/types.h>
-
-#ifdef _KERNEL
-#include <sys/systm.h>
-#else
-#include <string.h>
-#endif
-
-#include "sha256.h"
-
-#if BYTE_ORDER == BIG_ENDIAN
-
-/* Copy a vector of big-endian uint32_t into a vector of bytes */
-#define be32enc_vect(dst, src, len) \
- memcpy((void *)dst, (const void *)src, (size_t)len)
-
-/* Copy a vector of bytes into a vector of big-endian uint32_t */
-#define be32dec_vect(dst, src, len) \
- memcpy((void *)dst, (const void *)src, (size_t)len)
-
-#else /* BYTE_ORDER != BIG_ENDIAN */
-
-/*
- * Encode a length len/4 vector of (uint32_t) into a length len vector of
- * (unsigned char) in big-endian form. Assumes len is a multiple of 4.
- */
-static void
-be32enc_vect(unsigned char *dst, const uint32_t *src, size_t len)
-{
- size_t i;
-
- for (i = 0; i < len / 4; i++)
- be32enc(dst + i * 4, src[i]);
-}
-
-/*
- * Decode a big-endian length len vector of (unsigned char) into a length
- * len/4 vector of (uint32_t). Assumes len is a multiple of 4.
- */
-static void
-be32dec_vect(uint32_t *dst, const unsigned char *src, size_t len)
-{
- size_t i;
-
- for (i = 0; i < len / 4; i++)
- dst[i] = be32dec(src + i * 4);
-}
-
-#endif /* BYTE_ORDER != BIG_ENDIAN */
-
-/* Elementary functions used by SHA256 */
-#define Ch(x, y, z) ((x & (y ^ z)) ^ z)
-#define Maj(x, y, z) ((x & (y | z)) | (y & z))
-#define SHR(x, n) (x >> n)
-#define ROTR(x, n) ((x >> n) | (x << (32 - n)))
-#define S0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22))
-#define S1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25))
-#define s0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3))
-#define s1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10))
-
-/* SHA256 round function */
-#define RND(a, b, c, d, e, f, g, h, k) \
- t0 = h + S1(e) + Ch(e, f, g) + k; \
- t1 = S0(a) + Maj(a, b, c); \
- d += t0; \
- h = t0 + t1;
-
-/* Adjusted round function for rotating state */
-#define RNDr(S, W, i, k) \
- RND(S[(64 - i) % 8], S[(65 - i) % 8], \
- S[(66 - i) % 8], S[(67 - i) % 8], \
- S[(68 - i) % 8], S[(69 - i) % 8], \
- S[(70 - i) % 8], S[(71 - i) % 8], \
- W[i] + k)
-
-/*
- * SHA256 block compression function. The 256-bit state is transformed via
- * the 512-bit input block to produce a new state.
- */
-static void
-SHA256_Transform(uint32_t * state, const unsigned char block[64])
-{
- uint32_t W[64];
- uint32_t S[8];
- uint32_t t0, t1;
- int i;
-
- /* 1. Prepare message schedule W. */
- be32dec_vect(W, block, 64);
- for (i = 16; i < 64; i++)
- W[i] = s1(W[i - 2]) + W[i - 7] + s0(W[i - 15]) + W[i - 16];
-
- /* 2. Initialize working variables. */
- memcpy(S, state, 32);
-
- /* 3. Mix. */
- RNDr(S, W, 0, 0x428a2f98);
- RNDr(S, W, 1, 0x71374491);
- RNDr(S, W, 2, 0xb5c0fbcf);
- RNDr(S, W, 3, 0xe9b5dba5);
- RNDr(S, W, 4, 0x3956c25b);
- RNDr(S, W, 5, 0x59f111f1);
- RNDr(S, W, 6, 0x923f82a4);
- RNDr(S, W, 7, 0xab1c5ed5);
- RNDr(S, W, 8, 0xd807aa98);
- RNDr(S, W, 9, 0x12835b01);
- RNDr(S, W, 10, 0x243185be);
- RNDr(S, W, 11, 0x550c7dc3);
- RNDr(S, W, 12, 0x72be5d74);
- RNDr(S, W, 13, 0x80deb1fe);
- RNDr(S, W, 14, 0x9bdc06a7);
- RNDr(S, W, 15, 0xc19bf174);
- RNDr(S, W, 16, 0xe49b69c1);
- RNDr(S, W, 17, 0xefbe4786);
- RNDr(S, W, 18, 0x0fc19dc6);
- RNDr(S, W, 19, 0x240ca1cc);
- RNDr(S, W, 20, 0x2de92c6f);
- RNDr(S, W, 21, 0x4a7484aa);
- RNDr(S, W, 22, 0x5cb0a9dc);
- RNDr(S, W, 23, 0x76f988da);
- RNDr(S, W, 24, 0x983e5152);
- RNDr(S, W, 25, 0xa831c66d);
- RNDr(S, W, 26, 0xb00327c8);
- RNDr(S, W, 27, 0xbf597fc7);
- RNDr(S, W, 28, 0xc6e00bf3);
- RNDr(S, W, 29, 0xd5a79147);
- RNDr(S, W, 30, 0x06ca6351);
- RNDr(S, W, 31, 0x14292967);
- RNDr(S, W, 32, 0x27b70a85);
- RNDr(S, W, 33, 0x2e1b2138);
- RNDr(S, W, 34, 0x4d2c6dfc);
- RNDr(S, W, 35, 0x53380d13);
- RNDr(S, W, 36, 0x650a7354);
- RNDr(S, W, 37, 0x766a0abb);
- RNDr(S, W, 38, 0x81c2c92e);
- RNDr(S, W, 39, 0x92722c85);
- RNDr(S, W, 40, 0xa2bfe8a1);
- RNDr(S, W, 41, 0xa81a664b);
- RNDr(S, W, 42, 0xc24b8b70);
- RNDr(S, W, 43, 0xc76c51a3);
- RNDr(S, W, 44, 0xd192e819);
- RNDr(S, W, 45, 0xd6990624);
- RNDr(S, W, 46, 0xf40e3585);
- RNDr(S, W, 47, 0x106aa070);
- RNDr(S, W, 48, 0x19a4c116);
- RNDr(S, W, 49, 0x1e376c08);
- RNDr(S, W, 50, 0x2748774c);
- RNDr(S, W, 51, 0x34b0bcb5);
- RNDr(S, W, 52, 0x391c0cb3);
- RNDr(S, W, 53, 0x4ed8aa4a);
- RNDr(S, W, 54, 0x5b9cca4f);
- RNDr(S, W, 55, 0x682e6ff3);
- RNDr(S, W, 56, 0x748f82ee);
- RNDr(S, W, 57, 0x78a5636f);
- RNDr(S, W, 58, 0x84c87814);
- RNDr(S, W, 59, 0x8cc70208);
- RNDr(S, W, 60, 0x90befffa);
- RNDr(S, W, 61, 0xa4506ceb);
- RNDr(S, W, 62, 0xbef9a3f7);
- RNDr(S, W, 63, 0xc67178f2);
-
- /* 4. Mix local working variables into global state */
- for (i = 0; i < 8; i++)
- state[i] += S[i];
-}
-
-static unsigned char PAD[64] = {
- 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-};
-
-/* Add padding and terminating bit-count. */
-static void
-SHA256_Pad(SHA256_CTX * ctx)
-{
- unsigned char len[8];
- uint32_t r, plen;
-
- /*
- * Convert length to a vector of bytes -- we do this now rather
- * than later because the length will change after we pad.
- */
- be64enc(len, ctx->count);
-
- /* Add 1--64 bytes so that the resulting length is 56 mod 64 */
- r = (ctx->count >> 3) & 0x3f;
- plen = (r < 56) ? (56 - r) : (120 - r);
- SHA256_Update(ctx, PAD, (size_t)plen);
-
- /* Add the terminating bit-count */
- SHA256_Update(ctx, len, 8);
-}
-
-/* SHA-256 initialization. Begins a SHA-256 operation. */
-void
-SHA256_Init(SHA256_CTX * ctx)
-{
-
- /* Zero bits processed so far */
- ctx->count = 0;
-
- /* Magic initialization constants */
- ctx->state[0] = 0x6A09E667;
- ctx->state[1] = 0xBB67AE85;
- ctx->state[2] = 0x3C6EF372;
- ctx->state[3] = 0xA54FF53A;
- ctx->state[4] = 0x510E527F;
- ctx->state[5] = 0x9B05688C;
- ctx->state[6] = 0x1F83D9AB;
- ctx->state[7] = 0x5BE0CD19;
-}
-
-/* Add bytes into the hash */
-void
-SHA256_Update(SHA256_CTX * ctx, const void *in, size_t len)
-{
- uint64_t bitlen;
- uint32_t r;
- const unsigned char *src = in;
-
- /* Number of bytes left in the buffer from previous updates */
- r = (ctx->count >> 3) & 0x3f;
-
- /* Convert the length into a number of bits */
- bitlen = len << 3;
-
- /* Update number of bits */
- ctx->count += bitlen;
-
- /* Handle the case where we don't need to perform any transforms */
- if (len < 64 - r) {
- memcpy(&ctx->buf[r], src, len);
- return;
- }
-
- /* Finish the current block */
- memcpy(&ctx->buf[r], src, 64 - r);
- SHA256_Transform(ctx->state, ctx->buf);
- src += 64 - r;
- len -= 64 - r;
-
- /* Perform complete blocks */
- while (len >= 64) {
- SHA256_Transform(ctx->state, src);
- src += 64;
- len -= 64;
- }
-
- /* Copy left over data into buffer */
- memcpy(ctx->buf, src, len);
-}
-
-/*
- * SHA-256 finalization. Pads the input data, exports the hash value,
- * and clears the context state.
- */
-void
-SHA256_Final(unsigned char digest[32], SHA256_CTX * ctx)
-{
-
- /* Add padding */
- SHA256_Pad(ctx);
-
- /* Write the hash */
- be32enc_vect(digest, ctx->state, 32);
-
- /* Clear the context state */
- memset((void *)ctx, 0, sizeof(*ctx));
-}
-
-#ifdef WEAK_REFS
-/* When building libmd, provide weak references. Note: this is not
- activated in the context of compiling these sources for internal
- use in libcrypt.
- */
-#undef SHA256_Init
-__weak_reference(_libmd_SHA256_Init, SHA256_Init);
-#undef SHA256_Update
-__weak_reference(_libmd_SHA256_Update, SHA256_Update);
-#undef SHA256_Final
-__weak_reference(_libmd_SHA256_Final, SHA256_Final);
-#undef SHA256_Transform
-__weak_reference(_libmd_SHA256_Transform, SHA256_Transform);
-#endif
diff --git a/butl/small-vector b/butl/small-vector
deleted file mode 100644
index 3e50735..0000000
--- a/butl/small-vector
+++ /dev/null
@@ -1,283 +0,0 @@
-// file : butl/small-vector -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_SMALL_VECTOR
-#define BUTL_SMALL_VECTOR
-
-#include <vector>
-#include <cassert>
-#include <cstddef> // size_t
-#include <utility> // more(), forward()
-#include <type_traits> // true_type
-
-namespace butl
-{
- template <typename T, std::size_t N>
- struct small_vector_buffer
- {
- // Size keeps track of the number of elements that are constructed in
- // the buffer. Size equal N + 1 means the buffer is not allocated.
- //
- // Note that the names are decorated in order no to conflict with
- // std::vector interface.
- //
- alignas (alignof (T)) char data_[sizeof (T) * N];
- bool free_ = true;
-
- // Note that the buffer should be constructed before std::vector and
- // destroyed after (since std::vector's destructor will be destroying
- // elements potentially residing in the buffer). This means that the
- // buffer should be inherited from and before std::vector.
- //
- small_vector_buffer () = default;
-
- small_vector_buffer (small_vector_buffer&&) = delete;
- small_vector_buffer (const small_vector_buffer&) = delete;
-
- small_vector_buffer& operator= (small_vector_buffer&&) = delete;
- small_vector_buffer& operator= (const small_vector_buffer&) = delete;
- };
-
- template <typename T, std::size_t N>
- class small_vector_allocator
- {
- public:
- using buffer_type = small_vector_buffer<T, N>;
-
- explicit
- small_vector_allocator (buffer_type* b) noexcept: buf_ (b) {}
-
- // Allocator interface.
- //
- public:
- using value_type = T;
-
- T*
- allocate(std::size_t n)
- {
- assert (n >= N); // We should never be asked for less than N.
-
- if (n <= N)
- {
- buf_->free_ = false;
- return reinterpret_cast<T*> (buf_->data_);
- }
- else
- return static_cast<T*> (::operator new (sizeof (T) * n));
- }
-
- void
- deallocate (void* p, std::size_t) noexcept
- {
- if (p == buf_->data_)
- buf_->free_ = true;
- else
- ::operator delete (p);
- }
-
- friend bool
- operator== (small_vector_allocator x, small_vector_allocator y) noexcept
- {
- // We can use y to deallocate x's allocations if they use the same small
- // buffer or neither uses its small buffer (which means all allocations,
- // if any, have been from the shared heap). Of course this assumes no
- // copy will be called to deallocate what has been allocated after said
- // copy was made:
- //
- // A x;
- // A y (x);
- // p = x.allocate ();
- // y.deallocate (p); // Ouch.
- //
- return (x.buf_ == y.buf_) || (x.buf_->free_ && y.buf_->free_);
- }
-
- friend bool
- operator!= (small_vector_allocator x, small_vector_allocator y) noexcept
- {
- return !(x == y);
- }
-
- // It might get instantiated but should not be called.
- //
- small_vector_allocator
- select_on_container_copy_construction () const noexcept
- {
- return small_vector_allocator (nullptr);
- }
-
- // propagate_on_container_copy_assignment = false
- // propagate_on_container_move_assignment = false
-
- // Swap is not supported (see explanation in small_vector::swap()).
- //
- using propagate_on_container_swap = std::true_type;
-
- void
- swap (small_vector_allocator&) = delete;
-
- // Shouldn't be needed except to satisfy some static_assert's.
- //
- template <typename U>
- struct rebind {using other = small_vector_allocator<U, N>;};
-
- private:
- buffer_type* buf_;
- };
-
- // Issues and limitations.
- //
- // - vector::reserve() may allocate more per the spec. But the three main
- // C++ runtimes (libstdc++, libc++, and msvc) all seem to do the right
- // thing.
- //
- // - What if in most cases the vector is empty? How can we avoid initial
- // reserve? Provide no_reserve flag or some such? Is it really worth it?
- //
- // - swap() is deleted (see notes below).
- //
- template <typename T, std::size_t N>
- class small_vector: private small_vector_buffer<T, N>,
- public std::vector<T, small_vector_allocator<T, N>>
- {
- public:
- using allocator_type = small_vector_allocator<T, N>;
- using buffer_type = small_vector_buffer<T, N>;
- using base_type = std::vector<T, small_vector_allocator<T, N>>;
-
- small_vector ()
- : base_type (allocator_type (this))
- {
- reserve ();
- }
-
- small_vector (std::initializer_list<T> v)
- : base_type (allocator_type (this))
- {
- if (v.size () <= N)
- reserve ();
-
- static_cast<base_type&> (*this) = v;
- }
-
- template <typename I>
- small_vector (I b, I e)
- : base_type (allocator_type (this))
- {
- // While we could optimize this for random access iterators, N will
- // usually be pretty small. Let's hope the compiler sees this and does
- // some magic for us.
- //
- std::size_t n (0);
- for (I i (b); i != e && n <= N; ++i) ++n;
-
- if (n <= N)
- reserve ();
-
- this->assign (b, e);
- }
-
- explicit
- small_vector (std::size_t n)
- : base_type (allocator_type (this))
- {
- if (n <= N)
- reserve ();
-
- this->resize (n);
- }
-
- small_vector (std::size_t n, const T& x)
- : base_type (allocator_type (this))
- {
- if (n <= N)
- reserve ();
-
- this->assign (n, x);
- }
-
- small_vector (const small_vector& v)
- : buffer_type (), base_type (allocator_type (this))
- {
- if (v.size () <= N)
- reserve ();
-
- static_cast<base_type&> (*this) = v;
- }
-
- small_vector&
- operator= (const small_vector& v)
- {
- // Note: propagate_on_container_copy_assignment = false
- //
- static_cast<base_type&> (*this) = v;
- return *this;
- }
-
- small_vector (small_vector&& v)
- : base_type (allocator_type (this))
- {
- if (v.size () <= N)
- reserve ();
-
- *this = std::move (v); // Delegate to operator=(&&).
- }
-
- small_vector&
- operator= (small_vector&& v)
- {
- // VC's implementation of operator=(&&) (both 14 and 15) frees the
- // memory and then reallocated with capacity equal to v.size(). This is
- // clearly sub-optimal (the existing buffer could be reused) so we hope
- // this will be fixed eventually (VSO#367146; reportedly fixed for
- // VC15U1).
- //
-#if defined(_MSC_VER) && _MSC_VER <= 1910
- if (v.size () < N)
- {
- clear ();
- for (T& x: v)
- push_back (std::move (x));
- v.clear ();
- }
- else
-#endif
-
- // Note: propagate_on_container_move_assignment = false
- //
- static_cast<base_type&> (*this) = std::move (v);
-
- return *this;
- }
-
- small_vector&
- operator= (std::initializer_list<T> v)
- {
- static_cast<base_type&> (*this) = v;
- return *this;
- }
-
- // Implementing swap() under small buffer optimization is not trivial, to
- // say the least (think of swapping two such buffers of different sizes).
- // One easy option would be to force both in to the heap.
- //
- void
- swap (small_vector&) = delete;
-
- void
- reserve (std::size_t n = N)
- {
- base_type::reserve (n < N ? N : n);
- }
-
- void
- shrink_to_fit ()
- {
- if (this->capacity () > N)
- base_type::shrink_to_fit ();
- }
- };
-}
-
-#endif // BUTL_SMALL_VECTOR
diff --git a/butl/standard-version b/butl/standard-version
deleted file mode 100644
index f609215..0000000
--- a/butl/standard-version
+++ /dev/null
@@ -1,257 +0,0 @@
-// file : butl/standard-version -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_STANDARD_VERSION
-#define BUTL_STANDARD_VERSION
-
-#include <string>
-#include <cstdint> // uint*_t
-#include <cstddef> // size_t
-#include <ostream>
-
-#include <butl/export>
-
-#include <butl/optional>
-
-namespace butl
-{
- // The build2 "standard version" (specific, earliest and stub):
- //
- // [<epoch>~]<maj>.<min>.<patch>[-(a|b).<num>[.<snapsn>[.<snapid>]]][+<rev>]
- // [<epoch>~]<maj>.<min>.<patch>-
- // 0[+<revision>]
- //
- struct LIBBUTL_EXPORT standard_version
- {
- // Invariants:
- //
- // 1. allow_earliest
- // ? (E == 1) || (snapshot_sn == 0)
- // : (E == 0) == (snapshot_sn == 0)
- //
- // 2. version != 0 || allow_stub && epoch == 0 && snapshot_sn == 0
- //
- // 3. snapshot_sn != latest_sn && snapshot_sn != 0 || snapshot_id.empty ()
- //
- static const std::uint64_t latest_sn = std::uint64_t (~0);
-
- std::uint16_t epoch = 0; // 0 if not specified.
- std::uint64_t version = 0; // AAABBBCCCDDDE
- std::uint64_t snapshot_sn = 0; // 0 if not specifed, latest_sn if 'z'.
- std::string snapshot_id; // Empty if not specified.
- std::uint16_t revision = 0; // 0 if not specified.
-
- std::uint16_t major () const noexcept;
- std::uint16_t minor () const noexcept;
- std::uint16_t patch () const noexcept;
-
- // Note: 0 is ambiguous (-a.0.z).
- //
- std::uint16_t pre_release () const noexcept;
-
- // Note: return empty if the corresponding component is unspecified.
- //
- std::string string () const; // Package version.
- std::string string_project () const; // Project version (no epoch/rev).
- std::string string_project_id () const; // Project version id (no snapsn).
- std::string string_version () const; // Version only (no snapshot).
- std::string string_pre_release () const; // Pre-release part only (a.1).
- std::string string_snapshot () const; // Snapshot part only (1234.1f23).
-
- bool empty () const noexcept {return version == 0;}
-
- bool alpha () const noexcept;
- bool beta () const noexcept;
- bool snapshot () const noexcept {return snapshot_sn != 0;}
-
- // Represented by DDDE in version being 0001 and snapshot_sn being 0.
- //
- // Note that the earliest version is a final alpha pre-release.
- //
- bool
- earliest () const noexcept;
-
- bool
- stub () const noexcept {return version == std::uint64_t (~0);}
-
- // Comparison of empty or stub versions doesn't make sense.
- //
- int
- compare (const standard_version& v) const noexcept
- {
- if (epoch != v.epoch)
- return epoch < v.epoch ? -1 : 1;
-
- if (version != v.version)
- return version < v.version ? -1 : 1;
-
- if (snapshot_sn != v.snapshot_sn)
- return snapshot_sn < v.snapshot_sn ? -1 : 1;
-
- if (revision != v.revision)
- return revision < v.revision ? -1 : 1;
-
- return 0;
- }
-
- // Parse the version. Throw std::invalid_argument if the format is not
- // recognizable or components are invalid.
- //
- enum flags
- {
- none = 0,
- allow_earliest = 0x01, // Allow <major>.<minor>.<patch>- form.
- allow_stub = 0x02 // Allow 0[+<revision>] form.
- };
-
- explicit
- standard_version (const std::string&, flags = none);
-
- explicit
- standard_version (std::uint64_t version, flags = none);
-
- standard_version (std::uint64_t version,
- const std::string& snapshot,
- flags = none);
-
- standard_version (std::uint16_t epoch,
- std::uint64_t version,
- const std::string& snapshot,
- std::uint16_t revision,
- flags = none);
-
- standard_version (std::uint16_t epoch,
- std::uint64_t version,
- std::uint64_t snapshot_sn,
- std::string snapshot_id,
- std::uint16_t revision,
- flags = none);
-
- // Create empty version.
- //
- standard_version () = default;
-
- private:
- void
- parse_snapshot (const std::string&, std::size_t&);
- };
-
- inline bool
- operator< (const standard_version& x, const standard_version& y) noexcept
- {
- return x.compare (y) < 0;
- }
-
- inline bool
- operator> (const standard_version& x, const standard_version& y) noexcept
- {
- return x.compare (y) > 0;
- }
-
- inline bool
- operator== (const standard_version& x, const standard_version& y) noexcept
- {
- return x.compare (y) == 0;
- }
-
- inline bool
- operator<= (const standard_version& x, const standard_version& y) noexcept
- {
- return x.compare (y) <= 0;
- }
-
- inline bool
- operator>= (const standard_version& x, const standard_version& y) noexcept
- {
- return x.compare (y) >= 0;
- }
-
- inline bool
- operator!= (const standard_version& x, const standard_version& y) noexcept
- {
- return !(x == y);
- }
-
- inline std::ostream&
- operator<< (std::ostream& o, const standard_version& x)
- {
- return o << x.string ();
- }
-
- inline standard_version::flags
- operator& (standard_version::flags, standard_version::flags);
-
- inline standard_version::flags
- operator| (standard_version::flags, standard_version::flags);
-
- inline standard_version::flags
- operator&= (standard_version::flags&, standard_version::flags);
-
- inline standard_version::flags
- operator|= (standard_version::flags&, standard_version::flags);
-
- // The build2 "standard version" constraint:
- //
- // ('==' | '>' | '<' | '>=' | '<=') <version>
- // ('(' | '[') <version> <version> (')' | ']')
- //
- struct LIBBUTL_EXPORT standard_version_constraint
- {
- butl::optional<standard_version> min_version;
- butl::optional<standard_version> max_version;
- bool min_open;
- bool max_open;
-
- // Parse the version constraint. Throw std::invalid_argument on error.
- //
- explicit
- standard_version_constraint (const std::string&);
-
- // Throw std::invalid_argument if the specified version range is invalid.
- //
- standard_version_constraint (
- butl::optional<standard_version> min_version, bool min_open,
- butl::optional<standard_version> max_version, bool max_open);
-
- explicit
- standard_version_constraint (const standard_version& v)
- : standard_version_constraint (v, false, v, false) {}
-
- standard_version_constraint () = default;
-
- std::string
- string () const;
-
- bool
- empty () const noexcept {return !min_version && !max_version;}
-
- bool
- satisfies (const standard_version&) const noexcept;
- };
-
- inline bool
- operator== (const standard_version_constraint& x,
- const standard_version_constraint& y)
- {
- return x.min_version == y.min_version && x.max_version == y.max_version &&
- x.min_open == y.min_open && x.max_open == y.max_open;
- }
-
- inline bool
- operator!= (const standard_version_constraint& x,
- const standard_version_constraint& y)
- {
- return !(x == y);
- }
-
- inline std::ostream&
- operator<< (std::ostream& o, const standard_version_constraint& x)
- {
- return o << x.string ();
- }
-}
-
-#include <butl/standard-version.ixx>
-
-#endif // BUTL_STANDARD_VERSION
diff --git a/butl/standard-version.cxx b/butl/standard-version.cxx
deleted file mode 100644
index cc91f19..0000000
--- a/butl/standard-version.cxx
+++ /dev/null
@@ -1,632 +0,0 @@
-// file : butl/standard-version.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <butl/standard-version>
-
-#include <cassert>
-#include <cstdlib> // strtoull()
-#include <cstddef> // size_t
-#include <utility> // move()
-#include <stdexcept> // invalid_argument
-
-#include <butl/utility> // alnum()
-
-using namespace std;
-
-namespace butl
-{
- // Utility functions
- //
- static uint64_t
- parse_num (const string& s, size_t& p,
- const char* m,
- uint64_t min = 0, uint64_t max = 999)
- {
- if (s[p] == '-' || s[p] == '+') // strtoull() allows these.
- throw invalid_argument (m);
-
- const char* b (s.c_str () + p);
- char* e (nullptr);
- uint64_t r (strtoull (b, &e, 10));
-
- if (b == e || r < min || r > max)
- throw invalid_argument (m);
-
- p = e - s.c_str ();
- return static_cast<uint64_t> (r);
- }
-
- static void
- check_version (uint64_t vr, bool sn, standard_version::flags fl)
- {
- bool r;
- if (vr == uint64_t (~0) && (fl & standard_version::allow_stub) != 0)
- {
- // Stub.
- //
- // Check that the snapshot flag is false.
- //
- r = !sn;
- }
- else
- {
- // Check that the version isn't too large, unless represents stub.
- //
- // AAABBBCCCDDDE
- r = vr < 10000000000000ULL;
-
- // Check that E version component is consistent with the snapshot flag.
- // Note that if the allow_earliest flag is set, then E can be 1 for the
- // snapshot flag being false, denoting the earliest pre-release of the
- // version.
- //
- if (r)
- {
- uint64_t e (vr % 10);
- if ((fl & standard_version::allow_earliest) == 0)
- r = e == (sn ? 1 : 0);
- else
- r = e == 1 || (e == 0 && !sn);
- }
-
- // Check that pre-release number is consistent with the snapshot flag.
- //
- if (r)
- {
- uint64_t ab (vr / 10 % 1000);
-
- // Note that if ab is 0, it can either mean non-pre-release version in
- // the absence of snapshot number, or 'a.0' pre-release otherwise. If
- // ab is 500, it can only mean 'b.0', that must be followed by a
- // snapshot number.
- //
- if (ab != 0)
- r = ab != 500 || sn;
- }
-
- // Check that the major, the minor and the patch versions are not
- // simultaneously zeros.
- //
- if (r)
- r = (vr / 10000) != 0;
- }
-
- if (!r)
- throw invalid_argument ("invalid project version");
- }
-
- // standard_version
- //
- standard_version::
- standard_version (const std::string& s, flags f)
- {
- auto bail = [] (const char* m) {throw invalid_argument (m);};
-
- // Pre-parse the first component to see if the version starts with epoch,
- // to keep the subsequent parsing straightforward.
- //
- bool ep (false);
- {
- char* e (nullptr);
- strtoull (s.c_str (), &e, 10);
- ep = *e == '~';
- }
-
- // Note that here and below p is less or equal n, and so s[p] is always
- // valid.
- //
- size_t p (0), n (s.size ());
-
- if (ep)
- {
- epoch = parse_num (s, p, "invalid epoch", 1, uint16_t (~0));
- ++p; // Skip '~'.
- }
-
- uint16_t ma, mi, bf, ab (0);
- bool earliest (false);
-
- ma = parse_num (s, p, "invalid major version");
-
- // The only valid version that has no epoch, contains only the major
- // version being equal to zero, that is optionally followed by the plus
- // character, is the stub version, unless forbidden.
- //
- bool stub ((f & allow_stub) != 0 && !ep && ma == 0 &&
- (p == n || s[p] == '+'));
-
- if (stub)
- version = uint64_t (~0);
- else
- {
- if (s[p] != '.')
- bail ("'.' expected after major version");
-
- mi = parse_num (s, ++p, "invalid minor version");
-
- if (s[p] != '.')
- bail ("'.' expected after minor version");
-
- bf = parse_num (s, ++p, "invalid patch version");
-
- // AAABBBCCCDDDE
- version = ma * 10000000000ULL +
- mi * 10000000ULL +
- bf * 10000ULL;
-
- if (version == 0)
- bail ("0.0.0 version");
-
- // Parse the pre-release component if present.
- //
- if (s[p] == '-')
- {
- char k (s[++p]);
-
- // If the last character in the string is dash, then this is the
- // earliest version pre-release, unless forbidden.
- //
- if (k == '\0' && (f & allow_earliest) != 0)
- earliest = true;
- else
- {
- if (k != 'a' && k != 'b')
- bail ("'a' or 'b' expected in pre-release");
-
- if (s[++p] != '.')
- bail ("'.' expected after pre-release letter");
-
- ab = parse_num (s, ++p, "invalid pre-release", 0, 499);
-
- if (k == 'b')
- ab += 500;
-
- // Parse the snapshot components if present. Note that pre-release
- // number can't be zero for the final pre-release.
- //
- if (s[p] == '.')
- parse_snapshot (s, ++p);
- else if (ab == 0 || ab == 500)
- bail ("invalid final pre-release");
- }
- }
- }
-
- if (s[p] == '+')
- {
- assert (!earliest); // Would bail out earlier (a or b expected after -).
-
- revision = parse_num (s, ++p, "invalid revision", 1, uint16_t (~0));
- }
-
- if (p != n)
- bail ("junk after version");
-
- if (ab != 0 || snapshot_sn != 0 || earliest)
- version -= 10000 - ab * 10;
-
- if (snapshot_sn != 0 || earliest)
- version += 1;
- }
-
- standard_version::
- standard_version (uint64_t v, flags f)
- : version (v)
- {
- check_version (v, false, f);
- }
-
- standard_version::
- standard_version (uint64_t v, const std::string& s, flags f)
- : version (v)
- {
- bool snapshot (!s.empty ());
- check_version (version, snapshot, f);
-
- if (snapshot)
- {
- size_t p (0);
- parse_snapshot (s, p);
-
- if (p != s.size ())
- throw invalid_argument ("junk after snapshot");
- }
- }
-
- standard_version::
- standard_version (uint16_t e,
- uint64_t v,
- const std::string& s,
- uint16_t r,
- flags f)
- : standard_version (v, s, f)
- {
- if (stub () && e != 0)
- throw invalid_argument ("epoch for stub");
-
- // Can't initialize above due to ctor delegating.
- //
- epoch = e;
- revision = r;
- }
-
- standard_version::
- standard_version (uint16_t ep,
- uint64_t vr,
- uint64_t sn,
- std::string si,
- uint16_t rv,
- flags fl)
- : epoch (ep),
- version (vr),
- snapshot_sn (sn),
- snapshot_id (move (si)),
- revision (rv)
- {
- check_version (vr, true, fl);
-
- if (stub ())
- {
- if (ep != 0)
- throw invalid_argument ("epoch for stub");
-
- if (sn != 0)
- throw invalid_argument ("snapshot for stub");
- }
-
- if (!snapshot_id.empty () && (snapshot_id.size () > 16 ||
- snapshot_sn == 0 ||
- snapshot_sn == latest_sn))
- throw invalid_argument ("invalid snapshot");
- }
-
- void standard_version::
- parse_snapshot (const std::string& s, size_t& p)
- {
- // Note that snapshot id must be empty for 'z' snapshot number.
- //
- if (s[p] == 'z')
- {
- snapshot_sn = latest_sn;
- ++p;
- return;
- }
-
- uint64_t sn (parse_num (s,
- p,
- "invalid snapshot number",
- 1, latest_sn - 1));
- std::string id;
- if (s[p] == '.')
- {
- char c;
- for (++p; alnum (c = s[p]); ++p)
- id += c;
-
- if (id.empty () || id.size () > 16)
- throw invalid_argument ("invalid snapshot id");
- }
-
- snapshot_sn = sn;
- snapshot_id = move (id);
- }
-
- string standard_version::
- string_pre_release () const
- {
- std::string r;
-
- if ((alpha () && !earliest ()) || beta ())
- {
- uint64_t ab (version / 10 % 1000);
-
- if (ab < 500)
- {
- r += "a.";
- r += to_string (ab);
- }
- else
- {
- r += "b.";
- r += to_string (ab - 500);
- }
- }
-
- return r;
- }
-
- string standard_version::
- string_version () const
- {
- if (empty ())
- return "";
-
- if (stub ())
- return "0";
-
- std::string r (to_string (major ()) + '.' + to_string (minor ()) + '.' +
- to_string (patch ()));
-
- if (alpha () || beta ())
- {
- r += '-';
- r += string_pre_release ();
-
- if (snapshot ())
- r += '.';
- }
-
- return r;
- }
-
- string standard_version::
- string_snapshot () const
- {
- std::string r;
-
- if (snapshot ())
- {
- r = snapshot_sn == latest_sn ? "z" : to_string (snapshot_sn);
-
- if (!snapshot_id.empty ())
- {
- r += '.';
- r += snapshot_id;
- }
- }
-
- return r;
- }
-
- string standard_version::
- string_project () const
- {
- std::string r (string_version ());
-
- if (snapshot ())
- r += string_snapshot (); // string_version() includes trailing dot.
-
- return r;
- }
-
- string standard_version::
- string_project_id () const
- {
- std::string r (string_version ());
-
- if (snapshot ()) // Trailing dot already in id.
- {
- r += (snapshot_sn == latest_sn ? "z" :
- snapshot_id.empty () ? to_string (snapshot_sn) :
- snapshot_id);
- }
-
- return r;
- }
-
- string standard_version::
- string () const
- {
- std::string r;
-
- if (epoch != 0)
- {
- r = to_string (epoch);
- r += '~';
- }
-
- r += string_project ();
-
- if (revision != 0)
- {
- r += '+';
- r += to_string (revision);
- }
-
- return r;
- }
-
- // standard_version_constraint
- //
- standard_version_constraint::
- standard_version_constraint (const std::string& s)
- {
- using std::string; // Not to confuse with string().
-
- auto bail = [] (const string& m) {throw invalid_argument (m);};
- const char* spaces (" \t");
-
- size_t p (0);
- char c (s[p]);
-
- if (c == '(' || c == '[') // Can be '\0'.
- {
- bool min_open = c == '(';
-
- p = s.find_first_not_of (spaces, ++p);
- if (p == string::npos)
- bail ("no min version");
-
- size_t e (s.find_first_of (spaces, p));
-
- standard_version min_version;
-
- try
- {
- min_version = standard_version (s.substr (p, e - p),
- standard_version::allow_earliest);
- }
- catch (const invalid_argument& e)
- {
- bail (string ("invalid min version: ") + e.what ());
- }
-
- p = s.find_first_not_of (spaces, e);
- if (p == string::npos)
- bail ("no max version");
-
- e = s.find_first_of (" \t])", p);
-
- standard_version max_version;
-
- try
- {
- max_version = standard_version (s.substr (p, e - p),
- standard_version::allow_earliest);
- }
- catch (const invalid_argument& e)
- {
- bail (string ("invalid max version: ") + e.what ());
- }
-
- p = s.find_first_of ("])", e);
- if (p == string::npos)
- bail ("no closing bracket");
-
- bool max_open = s[p] == ')';
-
- if (++p != s.size ())
- bail ("junk after constraint");
-
- // Verify and copy the constraint.
- //
- *this = standard_version_constraint (min_version, min_open,
- max_version, max_open);
- }
- else
- {
- enum comparison {eq, lt, gt, le, ge};
- comparison operation (eq); // Uninitialized warning.
-
- if (s.compare (0, p = 2, "==") == 0)
- operation = eq;
- else if (s.compare (0, p = 2, ">=") == 0)
- operation = ge;
- else if (s.compare (0, p = 2, "<=") == 0)
- operation = le;
- else if (s.compare (0, p = 1, ">") == 0)
- operation = gt;
- else if (s.compare (0, p = 1, "<") == 0)
- operation = lt;
- else
- bail ("invalid constraint");
-
- p = s.find_first_not_of (spaces, p);
-
- if (p == string::npos)
- bail ("no version");
-
- standard_version v;
-
- try
- {
- v = standard_version (s.substr (p),
- operation != comparison::eq
- ? standard_version::allow_earliest
- : standard_version::none);
- }
- catch (const invalid_argument& e)
- {
- bail (string ("invalid version: ") + e.what ());
- }
-
- // Verify and copy the constraint.
- //
- switch (operation)
- {
- case comparison::eq:
- *this = standard_version_constraint (v);
- break;
- case comparison::lt:
- *this = standard_version_constraint (nullopt, true, move (v), true);
- break;
- case comparison::le:
- *this = standard_version_constraint (nullopt, true, move (v), false);
- break;
- case comparison::gt:
- *this = standard_version_constraint (move (v), true, nullopt, true);
- break;
- case comparison::ge:
- *this = standard_version_constraint (move (v), false, nullopt, true);
- break;
- }
- }
- }
-
- standard_version_constraint::
- standard_version_constraint (optional<standard_version> mnv, bool mno,
- optional<standard_version> mxv, bool mxo)
- : min_version (move (mnv)),
- max_version (move (mxv)),
- min_open (mno),
- max_open (mxo)
- {
- assert (
- // Min and max versions can't both be absent.
- //
- (min_version || max_version) &&
-
- // Version should be non-empty and not a stub.
- //
- (!min_version || (!min_version->empty () && !min_version->stub ())) &&
- (!max_version || (!max_version->empty () && !max_version->stub ())) &&
-
- // Absent version endpoint (infinity) should be open.
- //
- (min_version || min_open) && (max_version || max_open));
-
- if (min_version && max_version)
- {
- if (*min_version > *max_version)
- throw invalid_argument ("min version is greater than max version");
-
- if (*min_version == *max_version)
- {
- if (min_open || max_open)
- throw invalid_argument ("equal version endpoints not closed");
-
- if (min_version->earliest ())
- throw invalid_argument ("equal version endpoints are earliest");
- }
- }
- }
-
- string standard_version_constraint::
- string () const
- {
- assert (!empty ());
-
- if (!min_version)
- return (max_open ? "< " : "<= ") + max_version->string ();
-
- if (!max_version)
- return (min_open ? "> " : ">= ") + min_version->string ();
-
- if (*min_version == *max_version)
- return "== " + min_version->string ();
-
- return (min_open ? '(' : '[') + min_version->string () + ' ' +
- max_version->string () + (max_open ? ')' : ']');
- }
-
- bool standard_version_constraint::
- satisfies (const standard_version& v) const noexcept
- {
- bool s (true);
-
- if (min_version)
- {
- int i (v.compare (*min_version));
- s = min_open ? i > 0 : i >= 0;
- }
-
- if (s && max_version)
- {
- int i (v.compare (*max_version));
- s = max_open ? i < 0 : i <= 0;
- }
-
- return s;
- }
-}
diff --git a/butl/standard-version.ixx b/butl/standard-version.ixx
deleted file mode 100644
index 6908ed5..0000000
--- a/butl/standard-version.ixx
+++ /dev/null
@@ -1,100 +0,0 @@
-// file : butl/standard-version.ixx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-namespace butl
-{
- inline std::uint16_t standard_version::
- major () const noexcept
- {
- std::uint64_t e (version % 10);
- std::uint64_t v (version / 10);
- std::uint64_t ab (v % 1000);
- if (ab != 0 || e == 1)
- v += 1000 - ab;
-
- return static_cast<std::uint16_t> (v / 1000000000 % 1000);
- }
-
- inline std::uint16_t standard_version::
- minor () const noexcept
- {
- std::uint64_t e (version % 10);
- std::uint64_t v (version / 10);
- std::uint64_t ab (v % 1000);
- if (ab != 0 || e == 1)
- v += 1000 - ab;
-
- return static_cast<std::uint16_t> (v / 1000000 % 1000);
- }
-
- inline std::uint16_t standard_version::
- patch () const noexcept
- {
- std::uint64_t e (version % 10);
- std::uint64_t v (version / 10);
- std::uint64_t ab (v % 1000);
- if (ab != 0 || e == 1)
- v += 1000 - ab;
-
- return static_cast<std::uint16_t> (v / 1000 % 1000);
- }
-
- inline std::uint16_t standard_version::
- pre_release () const noexcept
- {
- std::uint64_t ab (version / 10 % 1000);
- if (ab > 500)
- ab -= 500;
-
- return static_cast<std::uint16_t> (ab);
- }
-
- inline bool standard_version::
- alpha () const noexcept
- {
- std::uint64_t abe (version % 10000);
- return abe > 0 && abe < 5000 && !stub ();
- }
-
- inline bool standard_version::
- beta () const noexcept
- {
- std::uint64_t abe (version % 10000);
- return abe > 5000 && !stub ();
- }
-
- inline bool standard_version::
- earliest () const noexcept
- {
- return version % 10000 == 1 && !snapshot () && !stub ();
- }
-
- inline standard_version::flags
- operator& (standard_version::flags x, standard_version::flags y)
- {
- return x &= y;
- }
-
- inline standard_version::flags
- operator| (standard_version::flags x, standard_version::flags y)
- {
- return x |= y;
- }
-
- inline standard_version::flags
- operator&= (standard_version::flags& x, standard_version::flags y)
- {
- return x = static_cast<standard_version::flags> (
- static_cast<std::uint16_t> (x) &
- static_cast<std::uint16_t> (y));
- }
-
- inline standard_version::flags
- operator|= (standard_version::flags& x, standard_version::flags y)
- {
- return x = static_cast<standard_version::flags> (
- static_cast<std::uint16_t> (x) |
- static_cast<std::uint16_t> (y));
- }
-}
diff --git a/butl/string-parser b/butl/string-parser
deleted file mode 100644
index 74391cd..0000000
--- a/butl/string-parser
+++ /dev/null
@@ -1,56 +0,0 @@
-// file : butl/string-parser -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_STRING_PARSER
-#define BUTL_STRING_PARSER
-
-#include <string>
-#include <vector>
-#include <cstddef> // size_t
-#include <utility> // pair
-#include <stdexcept> // invalid_argument
-
-#include <butl/export>
-
-namespace butl
-{
- class LIBBUTL_EXPORT invalid_string: public std::invalid_argument
- {
- public:
- invalid_string (std::size_t p, const std::string& d)
- : invalid_argument (d), position (p) {}
-
- std::size_t position; // Zero-based.
- };
-
- class LIBBUTL_EXPORT string_parser
- {
- public:
- // Parse a whitespace-separated list of strings. Can contain single or
- // double quoted substrings. No escaping is supported. If unquote is true,
- // return one-level unquoted values. Throw invalid_string in case of
- // invalid quoting.
- //
- static std::vector<std::string>
- parse_quoted (const std::string&, bool unquote);
-
- // As above but return a list of string and zero-based position pairs.
- // Position is useful for issuing diagnostics about an invalid string
- // during second-level parsing.
- //
- static std::vector<std::pair<std::string, std::size_t>>
- parse_quoted_position (const std::string&, bool unquote);
-
- // Remove a single level of quotes. Note that the format or the
- // correctness of the quotation is not validated.
- //
- static std::string
- unquote (const std::string&);
-
- static std::vector<std::string>
- unquote (const std::vector<std::string>&);
- };
-}
-
-#endif // BUTL_STRING_PARSER
diff --git a/butl/string-parser.cxx b/butl/string-parser.cxx
deleted file mode 100644
index 9228d66..0000000
--- a/butl/string-parser.cxx
+++ /dev/null
@@ -1,132 +0,0 @@
-// file : butl/string-parser.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <butl/string-parser>
-
-#include <utility> // move()
-
-using namespace std;
-
-namespace butl
-{
- // Utility functions
- //
- inline static bool
- space (char c) noexcept
- {
- return c == ' ' || c == '\t';
- }
-
- // string_parser
- //
- vector<pair<string, size_t>> string_parser::
- parse_quoted_position (const string& s, bool unquote)
- {
- vector<pair<string, size_t>> r;
- for (auto b (s.begin ()), i (b), e (s.end ()); i != e; )
- {
- for (; i != e && space (*i); ++i) ; // Skip spaces.
-
- if (i == e) // No more strings.
- break;
-
- string s;
- char quoting ('\0'); // Current quoting mode, can be used as bool.
- size_t pos (i - b); // String position.
-
- for (; i != e; ++i)
- {
- char c (*i);
-
- if (!quoting)
- {
- if (space (c)) // End of string.
- break;
-
- if (c == '"' || c == '\'') // Begin of quoted substring.
- {
- quoting = c;
-
- if (!unquote)
- s += c;
-
- continue;
- }
- }
- else if (c == quoting) // End of quoted substring.
- {
- quoting = '\0';
-
- if (!unquote)
- s += c;
-
- continue;
- }
-
- s += c;
- }
-
- if (quoting)
- throw invalid_string (i - b, "unterminated quoted string");
-
- r.emplace_back (move (s), pos);
- }
-
- return r;
- }
-
- vector<string> string_parser::
- parse_quoted (const string& s, bool unquote)
- {
- vector<pair<string, size_t>> sp (parse_quoted_position (s, unquote));
-
- vector<string> r;
- r.reserve (sp.size ());
- for (auto& s: sp)
- r.emplace_back (move (s.first));
-
- return r;
- }
-
- string string_parser::
- unquote (const string& s)
- {
- string r;
- char quoting ('\0'); // Current quoting mode, can be used as bool.
-
- for (auto i (s.begin ()), e (s.end ()); i != e; ++i)
- {
- char c (*i);
-
- if (!quoting)
- {
- if (c == '"' || c == '\'') // Begin of quoted substring.
- {
- quoting = c;
- continue;
- }
- }
- else if (c == quoting) // End of quoted substring.
- {
- quoting = '\0';
- continue;
- }
-
- r += c;
- }
-
- return r;
- }
-
- vector<string> string_parser::
- unquote (const vector<string>& v)
- {
- vector<string> r;
- r.reserve (v.size ());
- for (auto& s: v)
- r.emplace_back (unquote (s));
-
- return r;
- }
-}
diff --git a/butl/string-table b/butl/string-table
deleted file mode 100644
index a1d60c3..0000000
--- a/butl/string-table
+++ /dev/null
@@ -1,98 +0,0 @@
-// file : butl/string-table -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_STRING_TABLE
-#define BUTL_STRING_TABLE
-
-#include <vector>
-#include <string>
-#include <unordered_map>
-
-#include <butl/export>
-
-#include <butl/multi-index>
-
-namespace butl
-{
- // A pool of strings and, optionally, other accompanying data in which
- // each entry is assigned an individual index (or id) of type I (e.g.,
- // uint8_t, uint16_t, etc., depending on how many entries are expected).
- // Index value 0 is reserved to indicate the "no entry" condition.
- //
- template <typename I, typename D>
- struct string_table_element
- {
- const I i;
- const D d;
- };
-
- template <typename I>
- struct string_table_element<I, std::string>
- {
- const I i;
- const std::string d;
- };
-
- // For custom data the options are to call the data member 'key' or to
- // specialize this traits.
- //
- template <typename D>
- struct string_table_traits
- {
- static const std::string&
- key (const D& d) {return d.key;}
- };
-
- template <>
- struct string_table_traits<std::string>
- {
- static const std::string&
- key (const std::string& d) {return d;}
- };
-
- template <typename I, typename D = std::string>
- struct string_table
- {
- // Insert new entry unless one already exists.
- //
- I
- insert (const D&);
-
- // Find existing.
- //
- I
- find (const std::string& k) const
- {
- auto i (map_.find (key_type (&k)));
- return i != map_.end () ? i->second.i : 0;
- }
-
- // Reverse lookup.
- //
- const D&
- operator[] (I i) const {assert (i > 0); return vec_[i - 1]->second.d;}
-
- I
- size () const {return static_cast<I> (vec_.size ());}
-
- bool
- empty () const {return vec_.empty ();}
-
- void
- clear () {vec_.clear (); map_.clear ();}
-
- private:
- using key_type = butl::map_key<std::string>;
- using value_type = string_table_element<I, D>;
- using map_type = std::unordered_map<key_type, value_type>;
- using traits = string_table_traits<D>;
-
- map_type map_;
- std::vector<typename map_type::const_iterator> vec_;
- };
-}
-
-#include <butl/string-table.txx>
-
-#endif // BUTL_STRING_TABLE
diff --git a/butl/string-table.txx b/butl/string-table.txx
deleted file mode 100644
index 8c18f71..0000000
--- a/butl/string-table.txx
+++ /dev/null
@@ -1,33 +0,0 @@
-// file : butl/string-table.txx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <limits> // numeric_limits
-#include <cstddef> // size_t
-#include <cassert>
-
-namespace butl
-{
- template <typename I, typename D>
- I string_table<I, D>::
- insert (const D& d)
- {
- std::size_t i (vec_.size () + 1);
-
- // Note: move(d) would be tricky since the key still points to it.
- //
- auto r (map_.emplace (
- key_type (&traits::key (d)),
- value_type {static_cast<I> (i), d}));
-
- if (r.second)
- {
- assert (i <= std::numeric_limits<I>::max ());
-
- r.first->first.p = &traits::key (r.first->second.d); // Update key.
- vec_.push_back (r.first);
- }
-
- return r.first->second.i;
- }
-}
diff --git a/butl/strptime.c b/butl/strptime.c
deleted file mode 100644
index 8bbfac5..0000000
--- a/butl/strptime.c
+++ /dev/null
@@ -1,629 +0,0 @@
-/*-
- * Copyright (c) 2014 Gary Mills
- * Copyright 2011, Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 1994 Powerdog Industries. All rights reserved.
- *
- * Copyright (c) 2011 The FreeBSD Foundation
- * All rights reserved.
- * Portions of this software were developed by David Chisnall
- * under sponsorship from the FreeBSD Foundation.
- *
- * 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 POWERDOG INDUSTRIES ``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 POWERDOG INDUSTRIES 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.
- *
- * The views and conclusions contained in the software and documentation
- * are those of the authors and should not be interpreted as representing
- * official policies, either expressed or implied, of Powerdog Industries.
- */
-
-// Fallback implementation of strptime() designed for Windows/gcc combination
-// when neither POSIX strptime() nor proper std::get_time() are available. No
-// locale support is provided, so the function works as if "C" locale is set.
-// FreeBSD libc strptime.c source file is taken as a basis (saved in
-// strptime.orig). POSIX non-compliant extensions are removed. Most of
-// #include directives are removed as reference internal headers, some
-// #define directives are added instead. The content of included
-// timelocal.{h,c} files is mostly commented out, having just required
-// lc_time_T struct and _C_time_locale constant definitions left. Otherwise the
-// code is kept untouched as much as possible.
-//
-
-#include <ctype.h> // isspace(), isdigit()
-#include <string.h> // _strnicmp()
-
-#include "timelocal.h" // lc_time_T
-#include "timelocal.c" // _C_time_locale
-
-#define isspace_l(c, l) isspace(c)
-#define isdigit_l(c, l) isdigit(c)
-#define strncasecmp_l(s1, s2, n, l) _strnicmp(s1, s2, n)
-
-#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
-#define FIX_LOCALE(l) l=l
-
-#define TM_YEAR_BASE 1900
-#define TM_SUNDAY 0
-#define TM_MONDAY 1
-
-typedef unsigned char u_char;
-
-// Stubs replacing the real locale support.
-//
-typedef const lc_time_T* locale_t;
-
-static locale_t
-__get_locale ()
-{
- return &_C_time_locale;
-}
-
-static const struct lc_time_T *
-__get_current_time_locale (locale_t l)
-{
- return l;
-}
-
-// From this point the code is unchanged, if not to count non-standard
-// specifiers support removal and the replacement of tabs with spaces.
-//
-static char *
-_strptime(const char *, const char *, struct tm *, int *, locale_t);
-
-#define asizeof(a) ((int)(sizeof(a) / sizeof((a)[0])))
-
-#define FLAG_NONE (1 << 0)
-#define FLAG_YEAR (1 << 1)
-#define FLAG_MONTH (1 << 2)
-#define FLAG_YDAY (1 << 3)
-#define FLAG_MDAY (1 << 4)
-#define FLAG_WDAY (1 << 5)
-
-/*
- * Calculate the week day of the first day of a year. Valid for
- * the Gregorian calendar, which began Sept 14, 1752 in the UK
- * and its colonies. Ref:
- * http://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week
- */
-
-static int
-first_wday_of(int year)
-{
- return (((2 * (3 - (year / 100) % 4)) + (year % 100) +
- ((year % 100) / 4) + (isleap(year) ? 6 : 0) + 1) % 7);
-}
-
-// Remove Glibc extensions (%F, %Z, %z, %s) to make implementation compliant
-// with POSIX strptime() and to simplify porting.
-//
-static char *
-_strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp,
- locale_t locale)
-{
- char c;
- const char *ptr;
- int day_offset = -1, wday_offset;
- int week_offset;
- int i, len;
- int flags;
- int Ealternative, Oalternative;
- const struct lc_time_T *tptr = __get_current_time_locale(locale);
- static int start_of_month[2][13] = {
- {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
- {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
- };
-
- flags = FLAG_NONE;
-
- ptr = fmt;
- while (*ptr != 0) {
- c = *ptr++;
-
- if (c != '%') {
- if (isspace_l((unsigned char)c, locale))
- while (*buf != 0 &&
- isspace_l((unsigned char)*buf, locale))
- buf++;
- else if (c != *buf++)
- return (NULL);
- continue;
- }
-
- // Skip according to
- // http://pubs.opengroup.org/onlinepubs/9699919799/functions/strptime.html
- //
- c = *ptr;
- if (c == '+' || c == '0')
- ptr++;
-
- Ealternative = 0;
- Oalternative = 0;
-label:
- c = *ptr++;
- switch (c) {
- case '%':
- if (*buf++ != '%')
- return (NULL);
- break;
-
- case 'C':
- if (!isdigit_l((unsigned char)*buf, locale))
- return (NULL);
-
- /* XXX This will break for 3-digit centuries. */
- len = 2;
- for (i = 0; len && *buf != 0 &&
- isdigit_l((unsigned char)*buf, locale); buf++) {
- i *= 10;
- i += *buf - '0';
- len--;
- }
- if (i < 19)
- return (NULL);
-
- tm->tm_year = i * 100 - TM_YEAR_BASE;
- flags |= FLAG_YEAR;
-
- break;
-
- case 'c':
- buf = _strptime(buf, tptr->c_fmt, tm, GMTp, locale);
- if (buf == NULL)
- return (NULL);
- flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
- break;
-
- case 'D':
- buf = _strptime(buf, "%m/%d/%y", tm, GMTp, locale);
- if (buf == NULL)
- return (NULL);
- flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
- break;
-
- case 'E':
- if (Ealternative || Oalternative)
- break;
- Ealternative++;
- goto label;
-
- case 'O':
- if (Ealternative || Oalternative)
- break;
- Oalternative++;
- goto label;
-
- case 'R':
- buf = _strptime(buf, "%H:%M", tm, GMTp, locale);
- if (buf == NULL)
- return (NULL);
- break;
-
- case 'r':
- buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp, locale);
- if (buf == NULL)
- return (NULL);
- break;
-
- case 'T':
- buf = _strptime(buf, "%H:%M:%S", tm, GMTp, locale);
- if (buf == NULL)
- return (NULL);
- break;
-
- case 'X':
- buf = _strptime(buf, tptr->X_fmt, tm, GMTp, locale);
- if (buf == NULL)
- return (NULL);
- break;
-
- case 'x':
- buf = _strptime(buf, tptr->x_fmt, tm, GMTp, locale);
- if (buf == NULL)
- return (NULL);
- flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
- break;
-
- case 'j':
- if (!isdigit_l((unsigned char)*buf, locale))
- return (NULL);
-
- len = 3;
- for (i = 0; len && *buf != 0 &&
- isdigit_l((unsigned char)*buf, locale); buf++){
- i *= 10;
- i += *buf - '0';
- len--;
- }
- if (i < 1 || i > 366)
- return (NULL);
-
- tm->tm_yday = i - 1;
- flags |= FLAG_YDAY;
-
- break;
-
- case 'M':
- case 'S':
- if (*buf == 0 ||
- isspace_l((unsigned char)*buf, locale))
- break;
-
- if (!isdigit_l((unsigned char)*buf, locale))
- return (NULL);
-
- len = 2;
- for (i = 0; len && *buf != 0 &&
- isdigit_l((unsigned char)*buf, locale); buf++){
- i *= 10;
- i += *buf - '0';
- len--;
- }
-
- if (c == 'M') {
- if (i > 59)
- return (NULL);
- tm->tm_min = i;
- } else {
- if (i > 60)
- return (NULL);
- tm->tm_sec = i;
- }
-
- break;
-
- case 'H':
- case 'I':
- case 'k':
- case 'l':
- /*
- * Of these, %l is the only specifier explicitly
- * documented as not being zero-padded. However,
- * there is no harm in allowing zero-padding.
- *
- * XXX The %l specifier may gobble one too many
- * digits if used incorrectly.
- */
- if (!isdigit_l((unsigned char)*buf, locale))
- return (NULL);
-
- len = 2;
- for (i = 0; len && *buf != 0 &&
- isdigit_l((unsigned char)*buf, locale); buf++) {
- i *= 10;
- i += *buf - '0';
- len--;
- }
- if (c == 'H' || c == 'k') {
- if (i > 23)
- return (NULL);
- } else if (i > 12)
- return (NULL);
-
- tm->tm_hour = i;
-
- break;
-
- case 'p':
- /*
- * XXX This is bogus if parsed before hour-related
- * specifiers.
- */
- len = strlen(tptr->am);
- if (strncasecmp_l(buf, tptr->am, len, locale) == 0) {
- if (tm->tm_hour > 12)
- return (NULL);
- if (tm->tm_hour == 12)
- tm->tm_hour = 0;
- buf += len;
- break;
- }
-
- len = strlen(tptr->pm);
- if (strncasecmp_l(buf, tptr->pm, len, locale) == 0) {
- if (tm->tm_hour > 12)
- return (NULL);
- if (tm->tm_hour != 12)
- tm->tm_hour += 12;
- buf += len;
- break;
- }
-
- return (NULL);
-
- case 'A':
- case 'a':
- for (i = 0; i < asizeof(tptr->weekday); i++) {
- len = strlen(tptr->weekday[i]);
- if (strncasecmp_l(buf, tptr->weekday[i],
- len, locale) == 0)
- break;
- len = strlen(tptr->wday[i]);
- if (strncasecmp_l(buf, tptr->wday[i],
- len, locale) == 0)
- break;
- }
- if (i == asizeof(tptr->weekday))
- return (NULL);
-
- buf += len;
- tm->tm_wday = i;
- flags |= FLAG_WDAY;
- break;
-
- case 'U':
- case 'W':
- /*
- * XXX This is bogus, as we can not assume any valid
- * information present in the tm structure at this
- * point to calculate a real value, so just check the
- * range for now.
- */
- if (!isdigit_l((unsigned char)*buf, locale))
- return (NULL);
-
- len = 2;
- for (i = 0; len && *buf != 0 &&
- isdigit_l((unsigned char)*buf, locale); buf++) {
- i *= 10;
- i += *buf - '0';
- len--;
- }
- if (i > 53)
- return (NULL);
-
- if (c == 'U')
- day_offset = TM_SUNDAY;
- else
- day_offset = TM_MONDAY;
-
-
- week_offset = i;
-
- break;
-
- case 'w':
- if (!isdigit_l((unsigned char)*buf, locale))
- return (NULL);
-
- i = *buf - '0';
- if (i > 6)
- return (NULL);
-
- tm->tm_wday = i;
- flags |= FLAG_WDAY;
-
- break;
-
- case 'e':
- /*
- * With %e format, our strftime(3) adds a blank space
- * before single digits.
- */
- if (*buf != 0 &&
- isspace_l((unsigned char)*buf, locale))
- buf++;
- /* FALLTHROUGH */
- case 'd':
- /*
- * The %e specifier was once explicitly documented as
- * not being zero-padded but was later changed to
- * equivalent to %d. There is no harm in allowing
- * such padding.
- *
- * XXX The %e specifier may gobble one too many
- * digits if used incorrectly.
- */
- if (!isdigit_l((unsigned char)*buf, locale))
- return (NULL);
-
- len = 2;
- for (i = 0; len && *buf != 0 &&
- isdigit_l((unsigned char)*buf, locale); buf++) {
- i *= 10;
- i += *buf - '0';
- len--;
- }
- if (i > 31)
- return (NULL);
-
- tm->tm_mday = i;
- flags |= FLAG_MDAY;
-
- break;
-
- case 'B':
- case 'b':
- case 'h':
- for (i = 0; i < asizeof(tptr->month); i++) {
- if (Oalternative) {
- if (c == 'B') {
- len = strlen(tptr->alt_month[i]);
- if (strncasecmp_l(buf,
- tptr->alt_month[i],
- len, locale) == 0)
- break;
- }
- } else {
- len = strlen(tptr->month[i]);
- if (strncasecmp_l(buf, tptr->month[i],
- len, locale) == 0)
- break;
- }
- }
- /*
- * Try the abbreviated month name if the full name
- * wasn't found and Oalternative was not requested.
- */
- if (i == asizeof(tptr->month) && !Oalternative) {
- for (i = 0; i < asizeof(tptr->month); i++) {
- len = strlen(tptr->mon[i]);
- if (strncasecmp_l(buf, tptr->mon[i],
- len, locale) == 0)
- break;
- }
- }
- if (i == asizeof(tptr->month))
- return (NULL);
-
- tm->tm_mon = i;
- buf += len;
- flags |= FLAG_MONTH;
-
- break;
-
- case 'm':
- if (!isdigit_l((unsigned char)*buf, locale))
- return (NULL);
-
- len = 2;
- for (i = 0; len && *buf != 0 &&
- isdigit_l((unsigned char)*buf, locale); buf++) {
- i *= 10;
- i += *buf - '0';
- len--;
- }
- if (i < 1 || i > 12)
- return (NULL);
-
- tm->tm_mon = i - 1;
- flags |= FLAG_MONTH;
-
- break;
-
- case 'Y':
- case 'y':
- if (*buf == 0 ||
- isspace_l((unsigned char)*buf, locale))
- break;
-
- if (!isdigit_l((unsigned char)*buf, locale))
- return (NULL);
-
- len = (c == 'Y') ? 4 : 2;
- for (i = 0; len && *buf != 0 &&
- isdigit_l((unsigned char)*buf, locale); buf++) {
- i *= 10;
- i += *buf - '0';
- len--;
- }
- if (c == 'Y')
- i -= TM_YEAR_BASE;
- if (c == 'y' && i < 69)
- i += 100;
- if (i < 0)
- return (NULL);
-
- tm->tm_year = i;
- flags |= FLAG_YEAR;
-
- break;
-
- case 'n':
- case 't':
- while (isspace_l((unsigned char)*buf, locale))
- buf++;
- break;
-
- default:
- return (NULL);
- }
- }
-
- if (!(flags & FLAG_YDAY) && (flags & FLAG_YEAR)) {
- if ((flags & (FLAG_MONTH | FLAG_MDAY)) ==
- (FLAG_MONTH | FLAG_MDAY)) {
- tm->tm_yday = start_of_month[isleap(tm->tm_year +
- TM_YEAR_BASE)][tm->tm_mon] + (tm->tm_mday - 1);
- flags |= FLAG_YDAY;
- } else if (day_offset != -1) {
- /* Set the date to the first Sunday (or Monday)
- * of the specified week of the year.
- */
- if (!(flags & FLAG_WDAY)) {
- tm->tm_wday = day_offset;
- flags |= FLAG_WDAY;
- }
- tm->tm_yday = (7 -
- first_wday_of(tm->tm_year + TM_YEAR_BASE) +
- day_offset) % 7 + (week_offset - 1) * 7 +
- tm->tm_wday - day_offset;
- flags |= FLAG_YDAY;
- }
- }
-
- if ((flags & (FLAG_YEAR | FLAG_YDAY)) == (FLAG_YEAR | FLAG_YDAY)) {
- if (!(flags & FLAG_MONTH)) {
- i = 0;
- while (tm->tm_yday >=
- start_of_month[isleap(tm->tm_year +
- TM_YEAR_BASE)][i])
- i++;
- if (i > 12) {
- i = 1;
- tm->tm_yday -=
- start_of_month[isleap(tm->tm_year +
- TM_YEAR_BASE)][12];
- tm->tm_year++;
- }
- tm->tm_mon = i - 1;
- flags |= FLAG_MONTH;
- }
- if (!(flags & FLAG_MDAY)) {
- tm->tm_mday = tm->tm_yday -
- start_of_month[isleap(tm->tm_year + TM_YEAR_BASE)]
- [tm->tm_mon] + 1;
- flags |= FLAG_MDAY;
- }
- if (!(flags & FLAG_WDAY)) {
- i = 0;
- wday_offset = first_wday_of(tm->tm_year);
- while (i++ <= tm->tm_yday) {
- if (wday_offset++ >= 6)
- wday_offset = 0;
- }
- tm->tm_wday = wday_offset;
- flags |= FLAG_WDAY;
- }
- }
-
- return ((char *)buf);
-}
-
-static char *
-strptime_l(const char * __restrict buf, const char * __restrict fmt,
- struct tm * __restrict tm, locale_t loc)
-{
- char *ret;
- int gmt;
- FIX_LOCALE(loc);
-
- gmt = 0;
- ret = _strptime(buf, fmt, tm, &gmt, loc);
-
- return (ret);
-}
-
-static char *
-strptime(const char * __restrict buf, const char * __restrict fmt,
- struct tm * __restrict tm)
-{
- return strptime_l(buf, fmt, tm, __get_locale());
-}
diff --git a/butl/strptime.c.orig b/butl/strptime.c.orig
deleted file mode 100644
index 2be6358..0000000
--- a/butl/strptime.c.orig
+++ /dev/null
@@ -1,689 +0,0 @@
-/*-
- * Copyright (c) 2014 Gary Mills
- * Copyright 2011, Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 1994 Powerdog Industries. All rights reserved.
- *
- * Copyright (c) 2011 The FreeBSD Foundation
- * All rights reserved.
- * Portions of this software were developed by David Chisnall
- * under sponsorship from the FreeBSD Foundation.
- *
- * 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 POWERDOG INDUSTRIES ``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 POWERDOG INDUSTRIES 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.
- *
- * The views and conclusions contained in the software and documentation
- * are those of the authors and should not be interpreted as representing
- * official policies, either expressed or implied, of Powerdog Industries.
- */
-
-#include <sys/cdefs.h>
-#ifndef lint
-#ifndef NOID
-static char copyright[] __unused =
-"@(#) Copyright (c) 1994 Powerdog Industries. All rights reserved.";
-static char sccsid[] __unused = "@(#)strptime.c 0.1 (Powerdog) 94/03/27";
-#endif /* !defined NOID */
-#endif /* not lint */
-__FBSDID("$FreeBSD$");
-
-#include "namespace.h"
-#include <time.h>
-#include <ctype.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <pthread.h>
-#include "un-namespace.h"
-#include "libc_private.h"
-#include "timelocal.h"
-#include "tzfile.h"
-
-static char * _strptime(const char *, const char *, struct tm *, int *, locale_t);
-
-#define asizeof(a) (sizeof(a) / sizeof((a)[0]))
-
-#define FLAG_NONE (1 << 0)
-#define FLAG_YEAR (1 << 1)
-#define FLAG_MONTH (1 << 2)
-#define FLAG_YDAY (1 << 3)
-#define FLAG_MDAY (1 << 4)
-#define FLAG_WDAY (1 << 5)
-
-/*
- * Calculate the week day of the first day of a year. Valid for
- * the Gregorian calendar, which began Sept 14, 1752 in the UK
- * and its colonies. Ref:
- * http://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week
- */
-
-static int
-first_wday_of(int year)
-{
- return (((2 * (3 - (year / 100) % 4)) + (year % 100) +
- ((year % 100) / 4) + (isleap(year) ? 6 : 0) + 1) % 7);
-}
-
-static char *
-_strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp,
- locale_t locale)
-{
- char c;
- const char *ptr;
- int day_offset = -1, wday_offset;
- int week_offset;
- int i, len;
- int flags;
- int Ealternative, Oalternative;
- const struct lc_time_T *tptr = __get_current_time_locale(locale);
- static int start_of_month[2][13] = {
- {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
- {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
- };
-
- flags = FLAG_NONE;
-
- ptr = fmt;
- while (*ptr != 0) {
- c = *ptr++;
-
- if (c != '%') {
- if (isspace_l((unsigned char)c, locale))
- while (*buf != 0 &&
- isspace_l((unsigned char)*buf, locale))
- buf++;
- else if (c != *buf++)
- return (NULL);
- continue;
- }
-
- Ealternative = 0;
- Oalternative = 0;
-label:
- c = *ptr++;
- switch (c) {
- case '%':
- if (*buf++ != '%')
- return (NULL);
- break;
-
- case '+':
- buf = _strptime(buf, tptr->date_fmt, tm, GMTp, locale);
- if (buf == NULL)
- return (NULL);
- flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
- break;
-
- case 'C':
- if (!isdigit_l((unsigned char)*buf, locale))
- return (NULL);
-
- /* XXX This will break for 3-digit centuries. */
- len = 2;
- for (i = 0; len && *buf != 0 &&
- isdigit_l((unsigned char)*buf, locale); buf++) {
- i *= 10;
- i += *buf - '0';
- len--;
- }
- if (i < 19)
- return (NULL);
-
- tm->tm_year = i * 100 - TM_YEAR_BASE;
- flags |= FLAG_YEAR;
-
- break;
-
- case 'c':
- buf = _strptime(buf, tptr->c_fmt, tm, GMTp, locale);
- if (buf == NULL)
- return (NULL);
- flags |= FLAG_WDAY | FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
- break;
-
- case 'D':
- buf = _strptime(buf, "%m/%d/%y", tm, GMTp, locale);
- if (buf == NULL)
- return (NULL);
- flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
- break;
-
- case 'E':
- if (Ealternative || Oalternative)
- break;
- Ealternative++;
- goto label;
-
- case 'O':
- if (Ealternative || Oalternative)
- break;
- Oalternative++;
- goto label;
-
- case 'F':
- buf = _strptime(buf, "%Y-%m-%d", tm, GMTp, locale);
- if (buf == NULL)
- return (NULL);
- flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
- break;
-
- case 'R':
- buf = _strptime(buf, "%H:%M", tm, GMTp, locale);
- if (buf == NULL)
- return (NULL);
- break;
-
- case 'r':
- buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp, locale);
- if (buf == NULL)
- return (NULL);
- break;
-
- case 'T':
- buf = _strptime(buf, "%H:%M:%S", tm, GMTp, locale);
- if (buf == NULL)
- return (NULL);
- break;
-
- case 'X':
- buf = _strptime(buf, tptr->X_fmt, tm, GMTp, locale);
- if (buf == NULL)
- return (NULL);
- break;
-
- case 'x':
- buf = _strptime(buf, tptr->x_fmt, tm, GMTp, locale);
- if (buf == NULL)
- return (NULL);
- flags |= FLAG_MONTH | FLAG_MDAY | FLAG_YEAR;
- break;
-
- case 'j':
- if (!isdigit_l((unsigned char)*buf, locale))
- return (NULL);
-
- len = 3;
- for (i = 0; len && *buf != 0 &&
- isdigit_l((unsigned char)*buf, locale); buf++){
- i *= 10;
- i += *buf - '0';
- len--;
- }
- if (i < 1 || i > 366)
- return (NULL);
-
- tm->tm_yday = i - 1;
- flags |= FLAG_YDAY;
-
- break;
-
- case 'M':
- case 'S':
- if (*buf == 0 ||
- isspace_l((unsigned char)*buf, locale))
- break;
-
- if (!isdigit_l((unsigned char)*buf, locale))
- return (NULL);
-
- len = 2;
- for (i = 0; len && *buf != 0 &&
- isdigit_l((unsigned char)*buf, locale); buf++){
- i *= 10;
- i += *buf - '0';
- len--;
- }
-
- if (c == 'M') {
- if (i > 59)
- return (NULL);
- tm->tm_min = i;
- } else {
- if (i > 60)
- return (NULL);
- tm->tm_sec = i;
- }
-
- break;
-
- case 'H':
- case 'I':
- case 'k':
- case 'l':
- /*
- * Of these, %l is the only specifier explicitly
- * documented as not being zero-padded. However,
- * there is no harm in allowing zero-padding.
- *
- * XXX The %l specifier may gobble one too many
- * digits if used incorrectly.
- */
- if (!isdigit_l((unsigned char)*buf, locale))
- return (NULL);
-
- len = 2;
- for (i = 0; len && *buf != 0 &&
- isdigit_l((unsigned char)*buf, locale); buf++) {
- i *= 10;
- i += *buf - '0';
- len--;
- }
- if (c == 'H' || c == 'k') {
- if (i > 23)
- return (NULL);
- } else if (i > 12)
- return (NULL);
-
- tm->tm_hour = i;
-
- break;
-
- case 'p':
- /*
- * XXX This is bogus if parsed before hour-related
- * specifiers.
- */
- len = strlen(tptr->am);
- if (strncasecmp_l(buf, tptr->am, len, locale) == 0) {
- if (tm->tm_hour > 12)
- return (NULL);
- if (tm->tm_hour == 12)
- tm->tm_hour = 0;
- buf += len;
- break;
- }
-
- len = strlen(tptr->pm);
- if (strncasecmp_l(buf, tptr->pm, len, locale) == 0) {
- if (tm->tm_hour > 12)
- return (NULL);
- if (tm->tm_hour != 12)
- tm->tm_hour += 12;
- buf += len;
- break;
- }
-
- return (NULL);
-
- case 'A':
- case 'a':
- for (i = 0; i < asizeof(tptr->weekday); i++) {
- len = strlen(tptr->weekday[i]);
- if (strncasecmp_l(buf, tptr->weekday[i],
- len, locale) == 0)
- break;
- len = strlen(tptr->wday[i]);
- if (strncasecmp_l(buf, tptr->wday[i],
- len, locale) == 0)
- break;
- }
- if (i == asizeof(tptr->weekday))
- return (NULL);
-
- buf += len;
- tm->tm_wday = i;
- flags |= FLAG_WDAY;
- break;
-
- case 'U':
- case 'W':
- /*
- * XXX This is bogus, as we can not assume any valid
- * information present in the tm structure at this
- * point to calculate a real value, so just check the
- * range for now.
- */
- if (!isdigit_l((unsigned char)*buf, locale))
- return (NULL);
-
- len = 2;
- for (i = 0; len && *buf != 0 &&
- isdigit_l((unsigned char)*buf, locale); buf++) {
- i *= 10;
- i += *buf - '0';
- len--;
- }
- if (i > 53)
- return (NULL);
-
- if (c == 'U')
- day_offset = TM_SUNDAY;
- else
- day_offset = TM_MONDAY;
-
-
- week_offset = i;
-
- break;
-
- case 'w':
- if (!isdigit_l((unsigned char)*buf, locale))
- return (NULL);
-
- i = *buf - '0';
- if (i > 6)
- return (NULL);
-
- tm->tm_wday = i;
- flags |= FLAG_WDAY;
-
- break;
-
- case 'e':
- /*
- * With %e format, our strftime(3) adds a blank space
- * before single digits.
- */
- if (*buf != 0 &&
- isspace_l((unsigned char)*buf, locale))
- buf++;
- /* FALLTHROUGH */
- case 'd':
- /*
- * The %e specifier was once explicitly documented as
- * not being zero-padded but was later changed to
- * equivalent to %d. There is no harm in allowing
- * such padding.
- *
- * XXX The %e specifier may gobble one too many
- * digits if used incorrectly.
- */
- if (!isdigit_l((unsigned char)*buf, locale))
- return (NULL);
-
- len = 2;
- for (i = 0; len && *buf != 0 &&
- isdigit_l((unsigned char)*buf, locale); buf++) {
- i *= 10;
- i += *buf - '0';
- len--;
- }
- if (i > 31)
- return (NULL);
-
- tm->tm_mday = i;
- flags |= FLAG_MDAY;
-
- break;
-
- case 'B':
- case 'b':
- case 'h':
- for (i = 0; i < asizeof(tptr->month); i++) {
- if (Oalternative) {
- if (c == 'B') {
- len = strlen(tptr->alt_month[i]);
- if (strncasecmp_l(buf,
- tptr->alt_month[i],
- len, locale) == 0)
- break;
- }
- } else {
- len = strlen(tptr->month[i]);
- if (strncasecmp_l(buf, tptr->month[i],
- len, locale) == 0)
- break;
- }
- }
- /*
- * Try the abbreviated month name if the full name
- * wasn't found and Oalternative was not requested.
- */
- if (i == asizeof(tptr->month) && !Oalternative) {
- for (i = 0; i < asizeof(tptr->month); i++) {
- len = strlen(tptr->mon[i]);
- if (strncasecmp_l(buf, tptr->mon[i],
- len, locale) == 0)
- break;
- }
- }
- if (i == asizeof(tptr->month))
- return (NULL);
-
- tm->tm_mon = i;
- buf += len;
- flags |= FLAG_MONTH;
-
- break;
-
- case 'm':
- if (!isdigit_l((unsigned char)*buf, locale))
- return (NULL);
-
- len = 2;
- for (i = 0; len && *buf != 0 &&
- isdigit_l((unsigned char)*buf, locale); buf++) {
- i *= 10;
- i += *buf - '0';
- len--;
- }
- if (i < 1 || i > 12)
- return (NULL);
-
- tm->tm_mon = i - 1;
- flags |= FLAG_MONTH;
-
- break;
-
- case 's':
- {
- char *cp;
- int sverrno;
- long n;
- time_t t;
-
- sverrno = errno;
- errno = 0;
- n = strtol_l(buf, &cp, 10, locale);
- if (errno == ERANGE || (long)(t = n) != n) {
- errno = sverrno;
- return (NULL);
- }
- errno = sverrno;
- buf = cp;
- if (gmtime_r(&t, tm) == NULL)
- return (NULL);
- *GMTp = 1;
- flags |= FLAG_YDAY | FLAG_WDAY | FLAG_MONTH |
- FLAG_MDAY | FLAG_YEAR;
- }
- break;
-
- case 'Y':
- case 'y':
- if (*buf == 0 ||
- isspace_l((unsigned char)*buf, locale))
- break;
-
- if (!isdigit_l((unsigned char)*buf, locale))
- return (NULL);
-
- len = (c == 'Y') ? 4 : 2;
- for (i = 0; len && *buf != 0 &&
- isdigit_l((unsigned char)*buf, locale); buf++) {
- i *= 10;
- i += *buf - '0';
- len--;
- }
- if (c == 'Y')
- i -= TM_YEAR_BASE;
- if (c == 'y' && i < 69)
- i += 100;
- if (i < 0)
- return (NULL);
-
- tm->tm_year = i;
- flags |= FLAG_YEAR;
-
- break;
-
- case 'Z':
- {
- const char *cp;
- char *zonestr;
-
- for (cp = buf; *cp &&
- isupper_l((unsigned char)*cp, locale); ++cp) {
- /*empty*/}
- if (cp - buf) {
- zonestr = alloca(cp - buf + 1);
- strncpy(zonestr, buf, cp - buf);
- zonestr[cp - buf] = '\0';
- tzset();
- if (0 == strcmp(zonestr, "GMT") ||
- 0 == strcmp(zonestr, "UTC")) {
- *GMTp = 1;
- } else if (0 == strcmp(zonestr, tzname[0])) {
- tm->tm_isdst = 0;
- } else if (0 == strcmp(zonestr, tzname[1])) {
- tm->tm_isdst = 1;
- } else {
- return (NULL);
- }
- buf += cp - buf;
- }
- }
- break;
-
- case 'z':
- {
- int sign = 1;
-
- if (*buf != '+') {
- if (*buf == '-')
- sign = -1;
- else
- return (NULL);
- }
-
- buf++;
- i = 0;
- for (len = 4; len > 0; len--) {
- if (isdigit_l((unsigned char)*buf, locale)) {
- i *= 10;
- i += *buf - '0';
- buf++;
- } else
- return (NULL);
- }
-
- tm->tm_hour -= sign * (i / 100);
- tm->tm_min -= sign * (i % 100);
- *GMTp = 1;
- }
- break;
-
- case 'n':
- case 't':
- while (isspace_l((unsigned char)*buf, locale))
- buf++;
- break;
-
- default:
- return (NULL);
- }
- }
-
- if (!(flags & FLAG_YDAY) && (flags & FLAG_YEAR)) {
- if ((flags & (FLAG_MONTH | FLAG_MDAY)) ==
- (FLAG_MONTH | FLAG_MDAY)) {
- tm->tm_yday = start_of_month[isleap(tm->tm_year +
- TM_YEAR_BASE)][tm->tm_mon] + (tm->tm_mday - 1);
- flags |= FLAG_YDAY;
- } else if (day_offset != -1) {
- /* Set the date to the first Sunday (or Monday)
- * of the specified week of the year.
- */
- if (!(flags & FLAG_WDAY)) {
- tm->tm_wday = day_offset;
- flags |= FLAG_WDAY;
- }
- tm->tm_yday = (7 -
- first_wday_of(tm->tm_year + TM_YEAR_BASE) +
- day_offset) % 7 + (week_offset - 1) * 7 +
- tm->tm_wday - day_offset;
- flags |= FLAG_YDAY;
- }
- }
-
- if ((flags & (FLAG_YEAR | FLAG_YDAY)) == (FLAG_YEAR | FLAG_YDAY)) {
- if (!(flags & FLAG_MONTH)) {
- i = 0;
- while (tm->tm_yday >=
- start_of_month[isleap(tm->tm_year +
- TM_YEAR_BASE)][i])
- i++;
- if (i > 12) {
- i = 1;
- tm->tm_yday -=
- start_of_month[isleap(tm->tm_year +
- TM_YEAR_BASE)][12];
- tm->tm_year++;
- }
- tm->tm_mon = i - 1;
- flags |= FLAG_MONTH;
- }
- if (!(flags & FLAG_MDAY)) {
- tm->tm_mday = tm->tm_yday -
- start_of_month[isleap(tm->tm_year + TM_YEAR_BASE)]
- [tm->tm_mon] + 1;
- flags |= FLAG_MDAY;
- }
- if (!(flags & FLAG_WDAY)) {
- i = 0;
- wday_offset = first_wday_of(tm->tm_year);
- while (i++ <= tm->tm_yday) {
- if (wday_offset++ >= 6)
- wday_offset = 0;
- }
- tm->tm_wday = wday_offset;
- flags |= FLAG_WDAY;
- }
- }
-
- return ((char *)buf);
-}
-
-char *
-strptime_l(const char * __restrict buf, const char * __restrict fmt,
- struct tm * __restrict tm, locale_t loc)
-{
- char *ret;
- int gmt;
- FIX_LOCALE(loc);
-
- gmt = 0;
- ret = _strptime(buf, fmt, tm, &gmt, loc);
- if (ret && gmt) {
- time_t t = timegm(tm);
-
- localtime_r(&t, tm);
- }
-
- return (ret);
-}
-
-char *
-strptime(const char * __restrict buf, const char * __restrict fmt,
- struct tm * __restrict tm)
-{
- return strptime_l(buf, fmt, tm, __get_locale());
-}
diff --git a/butl/tab-parser b/butl/tab-parser
deleted file mode 100644
index f140b71..0000000
--- a/butl/tab-parser
+++ /dev/null
@@ -1,72 +0,0 @@
-// file : butl/tab-parser -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_TAB_PARSER
-#define BUTL_TAB_PARSER
-
-#include <string>
-#include <vector>
-#include <istream>
-#include <cstdint> // uint64_t
-#include <stdexcept> // runtime_error
-
-#include <butl/export>
-
-namespace butl
-{
- class LIBBUTL_EXPORT tab_parsing: public std::runtime_error
- {
- public:
- tab_parsing (const std::string& name,
- std::uint64_t line,
- std::uint64_t column,
- const std::string& description);
-
- std::string name;
- std::uint64_t line;
- std::uint64_t column;
- std::string description;
- };
-
- // Line and columns are useful for issuing diagnostics about invalid or
- // missing fields.
- //
- struct tab_field
- {
- std::string value; // Field string (quoting preserved).
- std::uint64_t column; // Field start column number (one-based).
- };
-
- struct tab_fields: std::vector<tab_field>
- {
- std::uint64_t line; // Line number (one-based).
- std::uint64_t end_column; // End-of-line column (line length).
- };
-
- // Read and parse lines consisting of space-separated fields. Field can
- // contain single or double quoted substrings (with spaces) which are
- // interpreted but preserved. No escaping of the quote characters is
- // supported. Blank lines and lines that start with # (collectively called
- // empty lines) are ignored.
- //
- class LIBBUTL_EXPORT tab_parser
- {
- public:
- tab_parser (std::istream& is, const std::string& name)
- : is_ (is), name_ (name) {}
-
- // Return next line of fields. Skip empty lines. Empty result denotes the
- // end of stream.
- //
- tab_fields
- next ();
-
- private:
- std::istream& is_;
- const std::string name_;
- std::uint64_t line_ = 0;
- };
-}
-
-#endif // BUTL_TAB_PARSER
diff --git a/butl/tab-parser.cxx b/butl/tab-parser.cxx
deleted file mode 100644
index 4743e69..0000000
--- a/butl/tab-parser.cxx
+++ /dev/null
@@ -1,88 +0,0 @@
-// file : butl/tab-parser.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <butl/tab-parser>
-
-#include <cassert>
-#include <sstream>
-
-#include <butl/string-parser>
-
-using namespace std;
-
-namespace butl
-{
- using parsing = tab_parsing;
-
- // tab_parser
- //
- tab_fields tab_parser::
- next ()
- {
- tab_fields r;
-
- // Read lines until a non-empty one or EOF is encountered. In the first
- // case parse the line and bail out.
- //
- // Note that we check for character presence in the stream prior to the
- // getline() call, to prevent it from setting the failbit.
- //
- while (!is_.eof () && is_.peek () != istream::traits_type::eof ())
- {
- string s;
- getline (is_, s);
-
- ++line_;
-
- // Skip empty line.
- //
- auto i (s.begin ());
- auto e (s.end ());
- for (; i != e && (*i == ' ' || *i == '\t'); ++i) ; // Skip spaces.
-
- if (i == e || *i == '#')
- continue;
-
- r.line = line_;
- r.end_column = s.size () + 1; // Newline position.
-
- vector<std::pair<string, size_t>> sp;
-
- try
- {
- sp = string_parser::parse_quoted_position (s, false);
- }
- catch (const invalid_string& e)
- {
- throw parsing (name_, line_, e.position + 1, e.what ());
- }
-
- for (auto& s: sp)
- r.emplace_back (tab_field ({move (s.first), s.second + 1}));
-
- break;
- }
-
- return r;
- }
-
- // tab_parsing
- //
- static string
- format (const string& n, uint64_t l, uint64_t c, const string& d)
- {
- ostringstream os;
- if (!n.empty ())
- os << n << ':';
- os << l << ':' << c << ": error: " << d;
- return os.str ();
- }
-
- tab_parsing::
- tab_parsing (const string& n, uint64_t l, uint64_t c, const string& d)
- : runtime_error (format (n, l, c, d)),
- name (n), line (l), column (c), description (d)
- {
- }
-}
diff --git a/butl/target-triplet b/butl/target-triplet
deleted file mode 100644
index 284b47f..0000000
--- a/butl/target-triplet
+++ /dev/null
@@ -1,155 +0,0 @@
-// file : butl/target-triplet -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_TARGET_TRIPLET
-#define BUTL_TARGET_TRIPLET
-
-#include <string>
-#include <ostream>
-
-#include <butl/export>
-
-namespace butl
-{
- // This is the ubiquitous 'target triplet' that loosely has the CPU-VENDOR-OS
- // form which, these days, quite often takes the CPU-VENDOR-OS-ABI form. Plus
- // some fields can sometimes be omitted. This looseness makes it hard to base
- // any kind of decisions on the triplet without canonicalizing it and then
- // splitting it into components. The way we are going to split it is like
- // this:
- //
- // CPU
- //
- // This one is reasonably straightforward. Note that we always expect at
- // least two components with the first being the CPU. In other words, we
- // don't try to guess what just 'mingw32' might mean like config.sub does.
- //
- // VENDOR
- //
- // This can be a machine vendor as in i686-apple-darwin8, a toolchain vendor
- // as in i686-lfs-linux-gnu, or something else as in arm-softfloat-linux-gnu.
- // Just as we think vendor is pretty irrelevant and can be ignored, comes
- // MinGW-W64 and calls itself *-w64-mingw32. While it is tempting to
- // attribute w64 to OS-ABI, the MinGW-W64 folks insist it is a (presumably
- // toolchain) vendor.
- //
- // Another example where the vendor seems to be reused for something else
- // entirely is the Intel's MIC architecture: x86_64-k1om-linux.
- //
- // To make things more regular we also convert the information-free vendor
- // names 'pc', 'unknown' and 'none' to the empty name.
- //
- // OS/KERNEL-OS/OS-ABI
- //
- // This is where things get really messy and instead of trying to guess, we
- // call the entire thing SYSTEM. Except, in certain cases, we factor out the
- // trailing version, again, to make SYSTEM easier to compare to. For example,
- // *-darwin14.5.0 becomes 'darwin' and '14.5.0'.
- //
- // Again, to make things more regular, if the first component in SYSTEM is
- // none, then it is removed (so *-none-eabi becomes just 'eabi').
- //
- // Values for two-component systems (e.g., linux-gnu) that don't specify
- // VENDOR explicitly are inherently ambiguous: is 'linux' VENDOR or part of
- // SYSTEM? The only way to handle this is to recognize their specific names
- // as special cases and this is what we do for some of the more common
- // ones. The alternative would be to first run such names through config.sub
- // which adds explicit VENDOR and this could be a reasonable fallback
- // strategy for (presumably less common) cases were we don't split things
- // correctly.
- //
- // Note also that the version splitting is only done for certain commonly-
- // used targets.
- //
- // Some examples of canonicalization and splitting:
- //
- // x86_64-apple-darwin14.5.0 x86_64 apple darwin 14.5.0
- // x86_64-unknown-freebsd10.2 x86_64 freebsd 10.2
- // i686-elf i686 elf
- // arm-eabi arm eabi
- // arm-none-eabi arm eabi
- // arm-none-linux-gnueabi arm linux-gnueabi
- // arm-softfloat-linux-gnu arm softfloat linux-gnu
- // i686-pc-mingw32 i686 mingw32
- // i686-w64-mingw32 i686 w64 mingw32
- // i686-lfs-linux-gnu i686 lfs linux-gnu
- // x86_64-unknown-linux-gnu x86_64 linux-gnu
- // x86_64-linux-gnux32 x86_64 linux-gnux32
- // x86_64-microsoft-win32-msvc14.0 x86_64 microsoft win32-msvc 14.0
- //
- // Similar to version splitting, for certain commonly-used targets we also
- // derive the "target class" which can be used as a shorthand, more
- // convenient way to identify a targets. If the target is not recognized,
- // then the special 'other' value is used. Currently the following classes
- // are recognized:
- //
- // linux *-*-linux-*
- // macos *-apple-darwin*
- // bsd *-*-(freebsd|openbsd|netbsd)*
- // windows *-*-win32-* | *-*-mingw32
- //
- // References:
- //
- // 1. The libtool repository contains the PLATFORM file that lists many known
- // triplets.
- //
- // 2. LLVM has the Triple class with similar goals.
- //
- struct LIBBUTL_EXPORT target_triplet
- {
- std::string cpu;
- std::string vendor;
- std::string system;
- std::string version;
- std::string class_;
-
- // Assemble and returning the canonical (i.e., the one we round-trip)
- // target triplet string.
- //
- std::string
- string () const;
-
- bool
- empty () const {return cpu.empty ();}
-
- int
- compare (const target_triplet& y) const
- {
- int r;
- return
- (r = cpu.compare (y.cpu)) != 0 ? r :
- (r = vendor.compare (y.vendor)) != 0 ? r :
- (r = system.compare (y.system)) != 0 ? r :
- ( version.compare (y.version));
- }
-
- // Parse the triplet throw std::invalid_argument if the triplet is not
- // recognizable.
- //
- explicit
- target_triplet (const std::string&);
-
- target_triplet () = default;
- };
-
- inline bool
- operator== (const target_triplet& x, const target_triplet& y)
- {
- return x.compare (y) == 0;
- }
-
- inline bool
- operator!= (const target_triplet& x, const target_triplet& y)
- {
- return !(x == y);
- }
-
- inline std::ostream&
- operator<< (std::ostream& o, const target_triplet& x)
- {
- return o << x.string ();
- }
-}
-
-#endif // BUTL_TARGET_TRIPLET
diff --git a/butl/target-triplet.cxx b/butl/target-triplet.cxx
deleted file mode 100644
index 7ebe9ec..0000000
--- a/butl/target-triplet.cxx
+++ /dev/null
@@ -1,145 +0,0 @@
-// file : butl/target-triplet.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <butl/target-triplet>
-
-#include <stdexcept> // invalid_argument
-
-using namespace std;
-
-namespace butl
-{
- target_triplet::
- target_triplet (const std::string& s)
- {
- using std::string;
-
- auto bad = [](const char* m) {throw invalid_argument (m);};
-
- // Find the first and the last components. The first is CPU and the last is
- // (part of) SYSTEM, that we know for sure.
- //
- string::size_type f (s.find ('-')), l (s.rfind ('-'));
-
- if (f == 0 || f == string::npos)
- bad ("missing cpu");
-
- cpu.assign (s, 0, f);
-
- // If we have something in between, then the first component after CPU is
- // VENDOR. Unless it is a first component of two-component system, as in
- // i686-linux-gnu.
- //
- if (f != l)
- {
- // [f, p) is VENDOR.
- //
- string::size_type p (s.find ('-', ++f)), n (p - f);
-
- if (n == 0)
- bad ("empty vendor");
-
- // Do we have all four components? If so, then we don't need to do any
- // special recognition of two-component systems.
- //
- if (l != p)
- {
- l = s.rfind ('-', --l);
-
- if (l != p)
- bad ("too many components");
-
- // Handle the none-* case here.
- //
- if (s.compare (l + 1, 5, "none-") == 0)
- l += 5;
- }
- else
- {
- // See if this is one of the well-known non-vendors.
- //
- if (s.compare (f, n, "linux") == 0 ||
- s.compare (f, n, "kfreebsd") == 0)
- {
- l = f - 1;
- n = 0; // No VENDOR.
- }
- }
-
- // Handle special VENDOR values.
- //
- if (n != 0)
- {
- if (s.compare (f, n, "pc") != 0 &&
- s.compare (f, n, "none") != 0 &&
- s.compare (f, n, "unknown") != 0)
- vendor.assign (s, f, n);
- }
- }
-
- // (l, npos) is SYSTEM
- //
- system.assign (s, ++l, string::npos);
-
- if (system.empty ())
- bad ("missing os/kernel/abi");
-
- if (system.front () == '-' || system.back () == '-')
- bad ("invalid os/kernel/abi");
-
- // Extract VERSION for some recognized systems.
- //
- string::size_type v (0);
- if (system.compare (0, (v = 6), "darwin") == 0 ||
- system.compare (0, (v = 7), "freebsd") == 0 ||
- system.compare (0, (v = 7), "openbsd") == 0 ||
- system.compare (0, (v = 6), "netbsd") == 0 ||
- system.compare (0, (v = 7), "solaris") == 0 ||
- system.compare (0, (v = 3), "aix") == 0 ||
- system.compare (0, (v = 4), "hpux") == 0 ||
- system.compare (0, (v = 10), "win32-msvc") == 0)
- {
- version.assign (system, v, string::npos);
- system.resize (system.size () - version.size ());
- }
-
- // Determine class for some recognized systems.
- //
- if (system.compare (0, 5, "linux") == 0)
- class_ = "linux";
- else if (vendor == "apple" && system == "darwin")
- class_ = "macos";
- else if (system == "freebsd" || system == "openbsd" || system == "netbsd")
- class_ = "bsd";
- else if (system.compare (0, 5, "win32") == 0 || system == "mingw32")
- class_ = "windows";
- else
- class_ = "other";
- }
-
- std::string target_triplet::
- string () const
- {
- std::string r (cpu);
-
- if (!vendor.empty ())
- {
- if (!r.empty ()) r += '-';
- r += vendor;
- }
-
- if (!system.empty ())
- {
- if (!r.empty ()) r += '-';
- r += system;
- }
-
- if (!version.empty ())
- {
- r += version;
- }
-
- return r;
- }
-}
diff --git a/butl/timelocal.c b/butl/timelocal.c
deleted file mode 100644
index 7194341..0000000
--- a/butl/timelocal.c
+++ /dev/null
@@ -1,157 +0,0 @@
-/*-
- * Copyright (c) 2001 Alexey Zelkin <phantom@FreeBSD.org>
- * Copyright (c) 1997 FreeBSD Inc.
- * All rights reserved.
- *
- * Copyright (c) 2011 The FreeBSD Foundation
- * All rights reserved.
- * Portions of this software were developed by David Chisnall
- * under sponsorship from the FreeBSD Foundation.
- *
- * 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 AUTHOR 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 AUTHOR 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.
- */
-
-/*
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include <stddef.h>
-
-#include "ldpart.h"
-#include "timelocal.h"
-
-struct xlocale_time {
- struct xlocale_component header;
- char *buffer;
- struct lc_time_T locale;
-};
-
-struct xlocale_time __xlocale_global_time;
-
-#define LCTIME_SIZE (sizeof(struct lc_time_T) / sizeof(char *))
-
-*/
-static const struct lc_time_T _C_time_locale = {
- {
- "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
- }, {
- "January", "February", "March", "April", "May", "June",
- "July", "August", "September", "October", "November", "December"
- }, {
- "Sun", "Mon", "Tue", "Wed",
- "Thu", "Fri", "Sat"
- }, {
- "Sunday", "Monday", "Tuesday", "Wednesday",
- "Thursday", "Friday", "Saturday"
- },
-
- /* X_fmt */
- "%H:%M:%S",
-
- /*
- * x_fmt
- * Since the C language standard calls for
- * "date, using locale's date format," anything goes.
- * Using just numbers (as here) makes Quakers happier;
- * it's also compatible with SVR4.
- */
- "%m/%d/%y",
-
- /*
- * c_fmt
- */
- "%a %b %e %H:%M:%S %Y",
-
- /* am */
- "AM",
-
- /* pm */
- "PM",
-
- /* date_fmt */
- "%a %b %e %H:%M:%S %Z %Y",
-
- /* alt_month
- * Standalone months forms for %OB
- */
- {
- "January", "February", "March", "April", "May", "June",
- "July", "August", "September", "October", "November", "December"
- },
-
- /* md_order
- * Month / day order in dates
- */
- "md",
-
- /* ampm_fmt
- * To determine 12-hour clock format time (empty, if N/A)
- */
- "%I:%M:%S %p"
-};
-
-/*
-static void destruct_time(void *v)
-{
- struct xlocale_time *l = v;
- if (l->buffer)
- free(l->buffer);
- free(l);
-}
-
-#include <stdio.h>
-struct lc_time_T *
-__get_current_time_locale(locale_t loc)
-{
- return (loc->using_time_locale
- ? &((struct xlocale_time *)loc->components[XLC_TIME])->locale
- : (struct lc_time_T *)&_C_time_locale);
-}
-
-static int
-time_load_locale(struct xlocale_time *l, int *using_locale, const char *name)
-{
- struct lc_time_T *time_locale = &l->locale;
- return (__part_load_locale(name, using_locale,
- &l->buffer, "LC_TIME",
- LCTIME_SIZE, LCTIME_SIZE,
- (const char **)time_locale));
-}
-int
-__time_load_locale(const char *name)
-{
- return time_load_locale(&__xlocale_global_time,
- &__xlocale_global_locale.using_time_locale, name);
-}
-void* __time_load(const char* name, locale_t loc)
-{
- struct xlocale_time *new = calloc(sizeof(struct xlocale_time), 1);
- new->header.header.destructor = destruct_time;
- if (time_load_locale(new, &loc->using_time_locale, name) == _LDP_ERROR)
- {
- xlocale_release(new);
- return NULL;
- }
- return new;
-}
-*/
diff --git a/butl/timelocal.c.orig b/butl/timelocal.c.orig
deleted file mode 100644
index 48c3509..0000000
--- a/butl/timelocal.c.orig
+++ /dev/null
@@ -1,153 +0,0 @@
-/*-
- * Copyright (c) 2001 Alexey Zelkin <phantom@FreeBSD.org>
- * Copyright (c) 1997 FreeBSD Inc.
- * All rights reserved.
- *
- * Copyright (c) 2011 The FreeBSD Foundation
- * All rights reserved.
- * Portions of this software were developed by David Chisnall
- * under sponsorship from the FreeBSD Foundation.
- *
- * 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 AUTHOR 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 AUTHOR 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.
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include <stddef.h>
-
-#include "ldpart.h"
-#include "timelocal.h"
-
-struct xlocale_time {
- struct xlocale_component header;
- char *buffer;
- struct lc_time_T locale;
-};
-
-struct xlocale_time __xlocale_global_time;
-
-#define LCTIME_SIZE (sizeof(struct lc_time_T) / sizeof(char *))
-
-static const struct lc_time_T _C_time_locale = {
- {
- "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
- }, {
- "January", "February", "March", "April", "May", "June",
- "July", "August", "September", "October", "November", "December"
- }, {
- "Sun", "Mon", "Tue", "Wed",
- "Thu", "Fri", "Sat"
- }, {
- "Sunday", "Monday", "Tuesday", "Wednesday",
- "Thursday", "Friday", "Saturday"
- },
-
- /* X_fmt */
- "%H:%M:%S",
-
- /*
- * x_fmt
- * Since the C language standard calls for
- * "date, using locale's date format," anything goes.
- * Using just numbers (as here) makes Quakers happier;
- * it's also compatible with SVR4.
- */
- "%m/%d/%y",
-
- /*
- * c_fmt
- */
- "%a %b %e %H:%M:%S %Y",
-
- /* am */
- "AM",
-
- /* pm */
- "PM",
-
- /* date_fmt */
- "%a %b %e %H:%M:%S %Z %Y",
-
- /* alt_month
- * Standalone months forms for %OB
- */
- {
- "January", "February", "March", "April", "May", "June",
- "July", "August", "September", "October", "November", "December"
- },
-
- /* md_order
- * Month / day order in dates
- */
- "md",
-
- /* ampm_fmt
- * To determine 12-hour clock format time (empty, if N/A)
- */
- "%I:%M:%S %p"
-};
-
-static void destruct_time(void *v)
-{
- struct xlocale_time *l = v;
- if (l->buffer)
- free(l->buffer);
- free(l);
-}
-
-#include <stdio.h>
-struct lc_time_T *
-__get_current_time_locale(locale_t loc)
-{
- return (loc->using_time_locale
- ? &((struct xlocale_time *)loc->components[XLC_TIME])->locale
- : (struct lc_time_T *)&_C_time_locale);
-}
-
-static int
-time_load_locale(struct xlocale_time *l, int *using_locale, const char *name)
-{
- struct lc_time_T *time_locale = &l->locale;
- return (__part_load_locale(name, using_locale,
- &l->buffer, "LC_TIME",
- LCTIME_SIZE, LCTIME_SIZE,
- (const char **)time_locale));
-}
-int
-__time_load_locale(const char *name)
-{
- return time_load_locale(&__xlocale_global_time,
- &__xlocale_global_locale.using_time_locale, name);
-}
-void* __time_load(const char* name, locale_t loc)
-{
- struct xlocale_time *new = calloc(sizeof(struct xlocale_time), 1);
- new->header.header.destructor = destruct_time;
- if (time_load_locale(new, &loc->using_time_locale, name) == _LDP_ERROR)
- {
- xlocale_release(new);
- return NULL;
- }
- return new;
-}
diff --git a/butl/timelocal.h b/butl/timelocal.h
deleted file mode 100644
index d9b77b9..0000000
--- a/butl/timelocal.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*-
- * Copyright (c) 1997-2002 FreeBSD Project.
- * All rights reserved.
- *
- * Copyright (c) 2011 The FreeBSD Foundation
- * All rights reserved.
- * Portions of this software were developed by David Chisnall
- * under sponsorship from the FreeBSD Foundation.
- *
- * 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 AUTHOR 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 AUTHOR 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.
- *
- * $FreeBSD$
- */
-
-#ifndef _TIMELOCAL_H_
-#define _TIMELOCAL_H_
-/*
-#include "xlocale_private.h"
-*/
-
-/*
- * Private header file for the strftime and strptime localization
- * stuff.
- */
-struct lc_time_T {
- const char *mon[12];
- const char *month[12];
- const char *wday[7];
- const char *weekday[7];
- const char *X_fmt;
- const char *x_fmt;
- const char *c_fmt;
- const char *am;
- const char *pm;
- const char *date_fmt;
- const char *alt_month[12];
- const char *md_order;
- const char *ampm_fmt;
-};
-
-/*
-struct lc_time_T *__get_current_time_locale(locale_t);
-int __time_load_locale(const char *);
-
-*/
-#endif /* !_TIMELOCAL_H_ */
diff --git a/butl/timelocal.h.orig b/butl/timelocal.h.orig
deleted file mode 100644
index 2e44415..0000000
--- a/butl/timelocal.h.orig
+++ /dev/null
@@ -1,61 +0,0 @@
-/*-
- * Copyright (c) 1997-2002 FreeBSD Project.
- * All rights reserved.
- *
- * Copyright (c) 2011 The FreeBSD Foundation
- * All rights reserved.
- * Portions of this software were developed by David Chisnall
- * under sponsorship from the FreeBSD Foundation.
- *
- * 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 AUTHOR 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 AUTHOR 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.
- *
- * $FreeBSD$
- */
-
-#ifndef _TIMELOCAL_H_
-#define _TIMELOCAL_H_
-#include "xlocale_private.h"
-
-/*
- * Private header file for the strftime and strptime localization
- * stuff.
- */
-struct lc_time_T {
- const char *mon[12];
- const char *month[12];
- const char *wday[7];
- const char *weekday[7];
- const char *X_fmt;
- const char *x_fmt;
- const char *c_fmt;
- const char *am;
- const char *pm;
- const char *date_fmt;
- const char *alt_month[12];
- const char *md_order;
- const char *ampm_fmt;
-};
-
-struct lc_time_T *__get_current_time_locale(locale_t);
-int __time_load_locale(const char *);
-
-#endif /* !_TIMELOCAL_H_ */
diff --git a/butl/timestamp b/butl/timestamp
deleted file mode 100644
index fde789f..0000000
--- a/butl/timestamp
+++ /dev/null
@@ -1,146 +0,0 @@
-// file : butl/timestamp -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_TIMESTAMP
-#define BUTL_TIMESTAMP
-
-#include <chrono>
-#include <iosfwd>
-
-#include <butl/export>
-
-#include <butl/path>
-
-namespace butl
-{
- // On all three main platforms that we target (GNU/Linux, Windows (both
- // VC++ and GCC/MinGW64), and MacOS X) with recent C++ runtimes,
- // system_clock has nanoseconds resolution and counts from the UNIX
- // epoch. The latter is important since struct stat also returns times
- // based on UNIX epoch.
- //
- // The underlying type for nanoseconds duration is signed integer type
- // of at least 64 bits (currently int64_t, available as duration::rep).
- // Because it is signed, we will overflow in year 2262 but by then the
- // underlying type will most likely have changed to something larger
- // than 64-bit.
- //
- // So to support other platforms that could possibly use a different
- // system_clock resolutions (e.g., microseconds), we actually not going
- // to assume anywhere (except perhaps timestamp.cxx) that we are dealing
- // with nanoseconds or the 64-bit underlying type.
- //
- using std::chrono::system_clock;
-
- // Note that the default-initialized timestamp has the timestamp_nonexistent
- // value.
- //
- using timestamp = system_clock::time_point;
- using duration = system_clock::duration;
-
- // Generally-useful special values. Note that unknown is less than
- // nonexistent and is less than any non-special value.
- //
- const timestamp::rep timestamp_unknown_rep = -1;
- const timestamp timestamp_unknown = timestamp (duration (-1));
- const timestamp::rep timestamp_nonexistent_rep = 0;
- const timestamp timestamp_nonexistent = timestamp (duration (0));
-
- // Print human-readable representation of the timestamp.
- //
- // By default the timestamp is printed by localtime_r() in the local
- // timezone, so tzset() from <time.h> should be called prior to using the
- // corresponding operator or the to_stream() function (normally from main()
- // or equivalent).
- //
- // The format argument in the to_stream() function is the put_time() format
- // string except that it also supports the nanoseconds conversion specifier
- // in the form %[<d>N] where <d> is the optional single delimiter character,
- // for example '.'. If the nanoseconds part is 0, then it is not printed
- // (nor the delimiter character). Otherwise, if necessary, the nanoseconds
- // part is padded to 9 characters with leading zeros.
- //
- // The special argument in the to_stream() function indicates whether the
- // special timestamp_unknown and timestamp_nonexistent values should be
- // printed as '<unknown>' and '<nonexistent>', respectively.
- //
- // The local argument in the to_stream() function indicates whether to use
- // localtime_r() or gmtime_r().
- //
- // Note also that these operators/function may throw std::system_error.
- //
- // Finally, padding is not fully supported by these operators/function. They
- // throw runtime_error if nanoseconds conversion specifier is present and
- // the stream's width field has been set to non-zero value before the call.
- //
- // Potential improvements:
- // - add flag to to_stream() to use
- // - support %[<d>U] (microseconds) and %[<d>M] (milliseconds).
- // - make to_stream() a manipulator, similar to put_time()
- // - support %(N) version for non-optional printing
- // - support for suffix %[<d>N<s>], for example %[N nsec]
- //
- LIBBUTL_EXPORT std::ostream&
- to_stream (std::ostream&,
- const timestamp&,
- const char* format,
- bool special,
- bool local);
-
- inline std::ostream&
- operator<< (std::ostream& os, const timestamp& ts)
- {
- return to_stream (os, ts, "%Y-%m-%d %H:%M:%S%[.N]", true, true);
- }
-
- LIBBUTL_EXPORT std::ostream&
- operator<< (std::ostream&, const duration&);
-
- // Parse human-readable representation of the timestamp.
- //
- // The format argument is the strptime() format string except that it also
- // supports the fraction of a second specifier in the form %[<d><f>], where
- // <d> is the optional single delimiter character, for example '.', and <f>
- // is one of the 'N', 'U', 'M' characters, denoting nanoseconds,
- // microseconds and milliseconds, respectively.
- //
- // The delimiter <d> is mandatory. If no such character is encountered at
- // the corresponding position of the input string, the function behaves as
- // if no %[] specifier were provided. Only single %[] specifier in the
- // format string is currently supported.
- //
- // If the delimiter is present, then it should be followed by 9 (N), 6 (U),
- // or 3 (M) digit value padded with leading zeros if necessary.
- //
- // If the local argument is true, then the input is assume to be local time
- // and the result is returned as local time as well. Otherwise, UCT is used
- // in both cases.
- //
- // If the end argument is not NULL, then it points to the first character
- // that was not parsed. Otherwise, throw invalid_argument in case of any
- // unparsed characters.
- //
- // Throw std::system_error on input/format mismatch and underlying time
- // conversion function failures.
- //
- // Note that internally from_string() calls strptime(), which behaves
- // according to the process' C locale (set with std::setlocale()) and not
- // the C++ locale (set with std::locale::global()). However the behaviour
- // can be affected by std::locale::global() as well, as it itself calls
- // std::setlocale() for the locale with a name.
- //
- // Potential improvements:
- // - support %() version for non-optional component but with optional
- // delimiter
- // - ability to parse local, return UTC and vice-versa
- // - handle timezone parsing
- //
- LIBBUTL_EXPORT timestamp
- from_string (const char* input,
- const char* format,
- bool local,
- const char** end = nullptr);
-}
-
-#endif // BUTL_TIMESTAMP
diff --git a/butl/timestamp.cxx b/butl/timestamp.cxx
deleted file mode 100644
index 971f1ef..0000000
--- a/butl/timestamp.cxx
+++ /dev/null
@@ -1,612 +0,0 @@
-// file : butl/timestamp.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <butl/timestamp>
-
-#include <time.h> // localtime_{r,s}(), gmtime_{r,s}(), strptime(), timegm()
-#include <errno.h> // EINVAL
-
-#include <ctime> // tm, time_t, mktime()
-#include <cstdlib> // strtoull()
-#include <cassert>
-#include <iomanip> // put_time(), setw(), dec, right
-#include <cstring> // strlen(), memcpy()
-#include <ostream>
-#include <utility> // pair, make_pair()
-#include <stdexcept> // runtime_error
-
-#include <butl/utility> // throw_generic_error()
-
-using namespace std;
-
-// libstdc++ prior to GCC 5 does not have std::put_time() so we have to invent
-// our own. Detecting the "prior to GCC 5" condition, however, is not easy:
-// libstdc++ is used by other compilers (e.g., Clang) so we cannot just use
-// __GNUC__. There is __GLIBCXX__ but it is a date which is updated with
-// every release, including bugfixes (so, there can be some 4.7.X release with
-// a date greater than 5.0.0).
-//
-// So what we going to do here is "offer" our implementation and let the ADL
-// pick one. If there is std::put_time(), then it will be preferred because
-// of the std::tm argument.
-//
-#ifdef __GLIBCXX__
-
-#include <ctime> // tm, strftime()
-#include <ostream>
-
-namespace details
-{
- struct put_time_data
- {
- const std::tm* tm;
- const char* fmt;
- };
-
- inline put_time_data
- put_time (const std::tm* tm, const char* fmt)
- {
- return put_time_data {tm, fmt};
- }
-
- inline ostream&
- operator<< (ostream& os, const put_time_data& d)
- {
- char buf[256];
- if (strftime (buf, sizeof (buf), d.fmt, d.tm) != 0)
- os << buf;
- else
- os.setstate (ostream::badbit);
- return os;
- }
-}
-
-using namespace details;
-
-#endif
-
-// Thread-safe implementations of gmtime() and localtime().
-//
-// Normally we would provide POSIX function replacement for Windows if the
-// original function is absent. However, MinGW GCC can sometimes provide them.
-// And so to avoid name clashes we hide them in the details namespace.
-//
-// Previously we have used gmtime_s() and localtime_s() for gmtime() and
-// localtime() implementations for Windows, but that required Security-Enhanced
-// version of CRT to be present, which is not always the case. In particular if
-// MinGW is configured with --disable-secure-api option then declarations of
-// *_s() functions are not available. So we use ::gmtime() and ::localtime()
-// for that purpose. Note that according to MSDN "gmtime and localtime all use
-// one common tm structure per thread for the conversion", which mean that they
-// are thread-safe.
-//
-namespace details
-{
- static tm*
- gmtime (const time_t* t, tm* r)
- {
-#ifdef _WIN32
- const tm* gt (::gmtime (t));
- if (gt == nullptr)
- return nullptr;
-
- *r = *gt;
- return r;
-#else
- return gmtime_r (t, r);
-#endif
- }
-
- static tm*
- localtime (const time_t* t, tm* r)
- {
-#ifdef _WIN32
- const tm* lt (::localtime (t));
- if (lt == nullptr)
- return nullptr;
-
- *r = *lt;
- return r;
-#else
- return localtime_r (t, r);
-#endif
- }
-}
-
-namespace butl
-{
- ostream&
- to_stream (ostream& os,
- const timestamp& ts,
- const char* format,
- bool special,
- bool local)
- {
- if (special)
- {
- if (ts == timestamp_unknown)
- return os << "<unknown>";
-
- if (ts == timestamp_nonexistent)
- return os << "<nonexistent>";
- }
-
- time_t t (system_clock::to_time_t (ts));
-
- std::tm tm;
- if ((local
- ? details::localtime (&t, &tm)
- : details::gmtime (&t, &tm)) == nullptr)
- throw_generic_error (errno);
-
- using namespace chrono;
-
- timestamp sec (system_clock::from_time_t (t));
- nanoseconds ns (duration_cast<nanoseconds> (ts - sec));
-
- char fmt[256];
- size_t n (strlen (format));
- if (n + 1 > sizeof (fmt))
- throw_generic_error (EINVAL);
- memcpy (fmt, format, n + 1);
-
- // Chunk the format string into fragments that we feed to put_time() and
- // those that we handle ourselves. Watch out for the escapes (%%).
- //
- size_t i (0), j (0); // put_time()'s range.
- for (; j != n; ++j)
- {
- if (fmt[j] == '%' && j + 1 != n)
- {
- if (fmt[j + 1] == '[')
- {
- if (os.width () != 0)
- throw runtime_error (
- "padding is not supported when printing nanoseconds");
-
- // Our fragment. First see if we need to call put_time().
- //
- if (i != j)
- {
- fmt[j] = '\0';
- if (!(os << put_time (&tm, fmt + i)))
- return os;
- }
-
- j += 2; // Character after '['.
- if (j == n)
- throw_generic_error (EINVAL);
-
- char d ('\0');
- if (fmt[j] != 'N')
- {
- d = fmt[j];
- if (++j == n || fmt[j] != 'N')
- throw_generic_error (EINVAL);
- }
-
- if (++j == n || fmt[j] != ']')
- throw_generic_error (EINVAL);
-
- if (ns != nanoseconds::zero ())
- {
- if (d != '\0')
- os << d;
-
- ostream::fmtflags fl (os.flags ());
- char fc (os.fill ('0'));
- os << dec << right << setw (9) << ns.count ();
- os.fill (fc);
- os.flags (fl);
- }
-
- i = j + 1; // j is incremented in the for-loop header.
- }
- else
- ++j; // Skip % and the next character to handle %%.
- }
- }
-
- // Do we need to call put_time() one last time?
- //
- if (i != j)
- {
- if (!(os << put_time (&tm, fmt + i)))
- return os;
- }
-
- return os;
- }
-
- ostream&
- operator<< (ostream& os, const duration& d)
- {
- if (os.width () != 0) // We always print nanosecond.
- throw runtime_error (
- "padding is not supported when printing nanoseconds");
-
- timestamp ts; // Epoch.
- ts += d;
-
- time_t t (system_clock::to_time_t (ts));
-
- const char* fmt (nullptr);
- const char* unt ("nanoseconds");
- if (t >= 365 * 24 * 60 * 60)
- {
- fmt = "%Y-%m-%d %H:%M:%S";
- unt = "years";
- }
- else if (t >= 31 * 24 * 60 * 60)
- {
- fmt = "%m-%d %H:%M:%S";
- unt = "months";
- }
- else if (t >= 24 * 60 * 60)
- {
- fmt = "%d %H:%M:%S";
- unt = "days";
- }
- else if (t >= 60 * 60)
- {
- fmt = "%H:%M:%S";
- unt = "hours";
- }
- else if (t >= 60)
- {
- fmt = "%M:%S";
- unt = "minutes";
- }
- else if (t >= 1)
- {
- fmt = "%S";
- unt = "seconds";
- }
-
- if (fmt != nullptr)
- {
- std::tm tm;
- if (details::gmtime (&t, &tm) == nullptr)
- throw_generic_error (errno);
-
- if (t >= 24 * 60 * 60)
- tm.tm_mday -= 1; // Make day of the month to be a zero-based number.
-
- if (t >= 31 * 24 * 60 * 60)
- tm.tm_mon -= 1; // Make month of the year to be a zero-based number.
-
- if (t >= 365 * 24 * 60 * 60)
- // Make the year to be a 1970-based number. Negative values allowed
- // according to the POSIX specification.
- //
- tm.tm_year -= 1970;
-
- if (!(os << put_time (&tm, fmt)))
- return os;
- }
-
- using namespace chrono;
-
- timestamp sec (system_clock::from_time_t (t));
- nanoseconds ns (duration_cast<nanoseconds> (ts - sec));
-
- if (ns != nanoseconds::zero ())
- {
- if (fmt != nullptr)
- {
- ostream::fmtflags fl (os.flags ());
- char fc (os.fill ('0'));
- os << '.' << dec << right << setw (9) << ns.count ();
- os.fill (fc);
- os.flags (fl);
- }
- else
- os << ns.count ();
-
- os << ' ' << unt;
- }
- else if (fmt == nullptr)
- os << '0';
-
- return os;
- }
-}
-
-// Implementation of strptime() and timegm() for Windows.
-//
-// Here we have several cases. If this is VC++, then we implement strptime()
-// via C++11 std::get_time(). And if this is MINGW GCC (or, more precisely,
-// libstdc++), then we have several problems. Firstly, GCC prior to 5 doesn't
-// implement std::get_time(). Secondly, GCC 5 and even 6 have buggy
-// std::get_time() (it cannot parse single-digit days). So what we are going
-// to do in this case is use a FreeBSD-based strptime() implementation.
-//
-#ifdef _WIN32
-
-#ifdef __GLIBCXX__
-
-// Fallback to a FreeBSD-based implementation.
-//
-extern "C"
-{
-#include "strptime.c"
-}
-
-#else // NOT __GLIBCXX__
-
-#include <ctime> // tm
-#include <locale>
-#include <clocale>
-#include <sstream>
-#include <iomanip>
-#include <cstring> // strlen()
-
-// VC++ std::get_time()-based implementation.
-//
-static char*
-strptime (const char* input, const char* format, tm* time)
-{
- istringstream is (input);
-
- // The original strptime() function behaves according to the process' C
- // locale (set with std::setlocale()), which can differ from the process C++
- // locale (set with std::locale::global()).
- //
- is.imbue (locale (setlocale (LC_ALL, nullptr)));
-
- if (!(is >> get_time (time, format)))
- return nullptr;
- else
- // tellg() behaves as UnformattedInputFunction, so returns failure status
- // if eofbit is set.
- //
- return const_cast<char*> (
- input + (is.eof ()
- ? strlen (input)
- : static_cast<size_t> (is.tellg ())));
-}
-
-#endif // __GLIBCXX__
-
-#include <ctime> // time_t, tm, mktime()
-
-static time_t
-timegm (tm* ctm)
-{
- const time_t e (static_cast<time_t> (-1));
-
- // We will use an example to explain how it works. Say *ctm contains 9 AM of
- // some day. Note that no time zone information is available.
- //
- // Convert it to the time from Epoch as if it's in the local time zone.
- //
- ctm->tm_isdst = -1;
- time_t t (mktime (ctm));
- if (t == e)
- return e;
-
- // Let's say we are in Moscow, and t contains the time passed from Epoch till
- // 9 AM MSK. But that is not what we need. What we need is the time passed
- // from Epoch till 9 AM GMT. This is some bigger number, as it takes longer
- // to achieve the same calendar time for more Western location. So we need to
- // find that offset, and increment t with it to obtain the desired value. The
- // offset is effectively the time difference between MSK and GMT time zones.
- //
- tm gtm;
- if (details::gmtime (&t, &gtm) == nullptr)
- return e;
-
- // gmtime() being called for the timepoint t returns 6 AM. So now we have
- // *ctm and gtm, which value difference (3 hours) reflects the desired
- // offset. The only problem is that we can not deduct gtm from *ctm, to get
- // the offset expressed as time_t. To do that we need to apply to both of
- // them the same conversion function transforming std::tm to std::time_t. The
- // mktime() can do that, so the expression (mktime(ctm) - mktime(&gtm))
- // calculates the desired offset.
- //
- // To ensure mktime() works exactly the same way for both cases, we need to
- // reset Daylight Saving Time flag for each of *ctm and gtm.
- //
- ctm->tm_isdst = 0;
- time_t lt (mktime (ctm));
- if (lt == e)
- return e;
-
- gtm.tm_isdst = 0;
- time_t gt (mktime (&gtm));
- if (gt == e)
- return e;
-
- // C11 standard specifies time_t to be a real type (integer and real floating
- // types are collectively called real types). So we can not consider it to be
- // signed.
- //
- return lt > gt ? t + (lt - gt) : t - (gt - lt);
-}
-
-#endif // _WIN32
-
-namespace butl
-{
- static pair<tm, chrono::nanoseconds>
- from_string (const char* input, const char* format, const char** end)
- {
- auto bad_val = [] () {throw_generic_error (EINVAL);};
-
- // See if we have our specifier.
- //
- size_t i (0);
- size_t n (strlen (format));
- for (; i != n; ++i)
- {
- if (format[i] == '%' && i + 1 != n)
- {
- if (format[i + 1] == '[')
- break;
- else
- ++i; // To handle %%.
- }
- }
-
- // Call the fraction of a second as just fraction from now on.
- //
- using namespace chrono;
- nanoseconds ns (nanoseconds::zero ());
-
- if (i == n)
- {
- // No %[], so just parse with strptime().
- //
- tm t = tm ();
- const char* p (strptime (input, format, &t));
- if (p == nullptr)
- bad_val ();
-
- if (end != nullptr)
- *end = p;
- else if (*p != '\0')
- bad_val (); // Input is not fully read.
-
- t.tm_isdst = -1;
- return make_pair (t, ns);
- }
-
- // Now the overall plan is:
- //
- // 1. Parse the fraction part of the input string to obtain nanoseconds.
- //
- // 2. Remove fraction part from the input string.
- //
- // 3. Remove %[] from the format string.
- //
- // 4. Re-parse the modified input with the modified format to fill the
- // std::tm structure.
- //
- // Parse the %[] specifier.
- //
- assert (format[i] == '%');
- string fm (format, i++); // Start assembling the new format string.
-
- assert (format[i] == '[');
- if (++i == n)
- bad_val ();
-
- char d (format[i]); // Delimiter character.
- if (++i == n)
- bad_val ();
-
- char f (format[i]); // Fraction specifier character.
- if ((f != 'N' && f != 'U' && f != 'M') || ++i == n)
- bad_val ();
-
- if (format[i++] != ']')
- bad_val ();
-
- // Parse the input with the initial part of the format string, the one
- // that preceeds the %[] specifier. The returned pointer will be the
- // position we need to start from to parse the fraction.
- //
- tm t = tm ();
-
- // What if %[] is first, there is nothing before it? According to the
- // strptime() documentation an empty format string is a valid one.
- //
- const char* p (strptime (input, fm.c_str (), &t));
- if (p == nullptr)
- bad_val ();
-
- // Start assembling the new input string.
- //
- string in (input, p - input);
- size_t fn (0); // Fraction size.
-
- if (d == *p)
- {
- // Fraction present in the input.
- //
-
- // Read fraction digits.
- //
- char buf [10];
- size_t i (0);
- size_t n (f == 'N' ? 9 : (f == 'U' ? 6 : 3));
- for (++p; i < n && *p >= '0' && *p <= '9'; ++i, ++p)
- buf[i] = *p;
-
- if (i < n)
- bad_val ();
-
- buf[n] = '\0';
- fn = n;
-
- // Calculate nanoseconds.
- //
- char* e (nullptr);
- unsigned long long t (strtoull (buf, &e, 10));
- assert (e == buf + n);
-
- switch (f)
- {
- case 'N': ns = nanoseconds (t); break;
- case 'U': ns = microseconds (t); break;
- case 'M': ns = milliseconds (t); break;
- default: assert (false);
- }
-
- // Actually the idea to fully remove the fraction from the input string,
- // and %[] from the format string, has a flaw. After the fraction removal
- // the spaces around it will be "swallowed" with a single space in the
- // format string. So, as an example, for the input:
- //
- // 2016-02-21 19:31:10 .384902285 GMT
- //
- // And the format:
- //
- // %Y-%m-%d %H:%M:%S %[.N]
- //
- // The unparsed tail of the input will be 'GMT' while expected to be
- // ' GMT'. To fix that we will not remove, but replace the mentioned
- // parts with some non-space character.
- //
- fm += '-';
- in += '-';
- }
-
- fm += format + i;
- in += p;
-
- // Reparse the modified input with the modified format.
- //
- t = tm ();
- const char* b (in.c_str ());
- p = strptime (b, fm.c_str (), &t);
-
- if (p == nullptr)
- bad_val ();
-
- if (end != nullptr)
- *end = input + (p - b + fn);
- else if (*p != '\0')
- bad_val (); // Input is not fully read.
-
- t.tm_isdst = -1;
- return make_pair (t, ns);
- }
-
- timestamp
- from_string (const char* input,
- const char* format,
- bool local,
- const char** end)
- {
- pair<tm, chrono::nanoseconds> t (from_string (input, format, end));
-
- time_t time (local ? mktime (&t.first) : timegm (&t.first));
- if (time == -1)
- throw_generic_error (errno);
-
- return timestamp::clock::from_time_t (time) +
- chrono::duration_cast<duration> (t.second);
- }
-}
diff --git a/butl/utility b/butl/utility
deleted file mode 100644
index 673b715..0000000
--- a/butl/utility
+++ /dev/null
@@ -1,264 +0,0 @@
-// file : butl/utility -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_UTILITY
-#define BUTL_UTILITY
-
-#include <string>
-#include <iosfwd> // ostream
-#include <cstddef> // size_t
-#include <utility> // move(), forward()
-#include <cstring> // strcmp(), strlen()
-#include <butl/ft/exception> // uncaught_exceptions
-#include <exception> // exception, uncaught_exception(s)()
-#include <butl/ft/lang> // thread_local
-
-//#include <functional> // hash
-
-#include <butl/export>
-
-namespace butl
-{
- // Throw std::system_error with generic_category or system_category,
- // respectively.
- //
- // The generic version should be used for portable errno codes (those that
- // are mapped to std::errc). The system version should be used for platform-
- // specific codes, for example, additional errno codes on POSIX systems or
- // the result of GetLastError() on Windows.
- //
- // See also the exception sanitization below.
- //
- [[noreturn]] LIBBUTL_EXPORT void
- throw_generic_error (int errno_code, const char* what = nullptr);
-
- [[noreturn]] LIBBUTL_EXPORT void
- throw_system_error (int system_code, int fallback_errno_code = 0);
-
- // Convert ASCII character/string case. If there is no upper/lower case
- // counterpart, leave the character unchanged. The POSIX locale (also known
- // as C locale) must be the current application locale. Otherwise the
- // behavior is undefined.
- //
- // Note that the POSIX locale specifies behaviour on data consisting
- // entirely of characters from the portable character set (subset of ASCII
- // including 103 non-negative characters and English alphabet letters in
- // particular) and the control character set (more about them at
- // http://pubs.opengroup.org/onlinepubs/009696899/basedefs/xbd_chap06.html).
- //
- // Also note that according to the POSIX locale definition the case
- // conversion can be applied only to [A-Z] and [a-z] character ranges being
- // translated to each other (more about that at
- // http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap07.html#tag_07_02)
- //
- char ucase (char);
- std::string ucase (const char*, std::size_t = std::string::npos);
- std::string ucase (const std::string&);
- std::string& ucase (std::string&);
- void ucase (char*, std::size_t);
-
- char lcase (char);
- std::string lcase (const char*, std::size_t = std::string::npos);
- std::string lcase (const std::string&);
- std::string& lcase (std::string&);
- void lcase (char*, std::size_t);
-
- // Compare ASCII characters/strings ignoring case. Behave as if characters
- // had been converted to the lower case and then byte-compared. Return a
- // negative, zero or positive value if the left hand side is less, equal or
- // greater than the right hand side, respectivelly. The POSIX locale (also
- // known as C locale) must be the current application locale. Otherwise the
- // behavior is undefined.
- //
- // The optional size argument specifies the maximum number of characters
- // to compare.
- //
- int casecmp (char, char);
-
- int casecmp (const std::string&, const std::string&,
- std::size_t = std::string::npos);
-
- int casecmp (const std::string&, const char*,
- std::size_t = std::string::npos);
-
- int casecmp (const char*, const char*, std::size_t = std::string::npos);
-
- // Case-insensitive key comparators (i.e., to be used in sets, maps, etc).
- //
- struct case_compare_string
- {
- bool operator() (const std::string& x, const std::string& y) const
- {
- return casecmp (x, y) < 0;
- }
- };
-
- struct case_compare_c_string
- {
- bool operator() (const char* x, const char* y) const
- {
- return casecmp (x, y) < 0;
- }
- };
-
- bool
- alpha (char);
-
- bool
- digit (char);
-
- bool
- alnum (char);
-
- // Key comparators (i.e., to be used in sets, maps, etc).
- //
- struct compare_c_string
- {
- bool operator() (const char* x, const char* y) const noexcept
- {
- return std::strcmp (x, y) < 0;
- }
- };
-
- struct compare_pointer_target
- {
- template <typename P>
- bool operator() (const P& x, const P& y) const {return *x < *y;}
- };
-
- //struct hash_pointer_target
- //{
- // template <typename P>
- // std::size_t operator() (const P& x) const {return std::hash (*x);}
- //};
-
- // Combine one or more hash values.
- //
- inline std::size_t
- combine_hash (std::size_t s, std::size_t h)
- {
- // Magic formula from boost::hash_combine().
- //
- return s ^ (h + 0x9e3779b9 + (s << 6) + (s >> 2));
- }
-
- template <typename... S>
- inline std::size_t
- combine_hash (std::size_t s, std::size_t h, S... hs)
- {
- return combine_hash (combine_hash (s, h), hs...);
- }
-
- // Support for reverse iteration using range-based for-loop:
- //
- // for (... : reverse_iterate (x)) ...
- //
- template <typename T>
- class reverse_range
- {
- T x_;
-
- public:
- reverse_range (T&& x): x_ (std::forward<T> (x)) {}
-
- auto begin () const -> decltype (this->x_.rbegin ()) {return x_.rbegin ();}
- auto end () const -> decltype (this->x_.rend ()) {return x_.rend ();}
- };
-
- template <typename T>
- inline reverse_range<T>
- reverse_iterate (T&& x) {return reverse_range<T> (std::forward<T> (x));}
-
- // Call a function if there is an exception.
- //
-
- template <typename F>
- struct exception_guard;
-
- template <typename F>
- inline exception_guard<F>
- make_exception_guard (F f)
- {
- return exception_guard<F> (std::move (f));
- }
-
-#ifdef __cpp_lib_uncaught_exceptions
- template <typename F>
- struct exception_guard
- {
- exception_guard (F f)
- : f_ (std::move (f)),
- u_ (std::uncaught_exceptions ()) {}
-
- ~exception_guard ()
- {
- if (u_ != std::uncaught_exceptions ())
- f_ ();
- }
-
- private:
- F f_;
- int u_;
- };
-#else
- // Fallback implementation using a TLS flag.
- //
- // True means we are in the body of a destructor that is being called as
- // part of the exception stack unwindining.
- //
- extern
-#ifdef __cpp_thread_local
- thread_local
-#else
- __thread
-#endif
- bool exception_unwinding_dtor_;
-
- // On Windows one cannot export a thread-local variable so we have to
- // use a wrapper functions.
- //
-#ifdef _WIN32
- LIBBUTL_EXPORT bool&
- exception_unwinding_dtor ();
-#else
- inline bool&
- exception_unwinding_dtor () {return exception_unwinding_dtor_;}
-#endif
-
- template <typename F>
- struct exception_guard
- {
- exception_guard (F f): f_ (std::move (f)) {}
- ~exception_guard ()
- {
- if (std::uncaught_exception ())
- {
- exception_unwinding_dtor () = true;
- f_ ();
- exception_unwinding_dtor () = false;
- }
- }
-
- private:
- F f_;
- };
-#endif
-}
-
-namespace std
-{
- // Sanitize the exception description before printing. This includes:
- //
- // - stripping leading colons and spaces (see fdstream.cxx)
- // - stripping trailing newlines, periods, and spaces
- // - stripping system error redundant suffix (see utility.cxx)
- // - lower-case the first letter if the beginning looks like a word
- //
- LIBBUTL_EXPORT ostream&
- operator<< (ostream&, const exception&);
-}
-
-#include <butl/utility.ixx>
-
-#endif // BUTL_UTILITY
diff --git a/butl/utility.cxx b/butl/utility.cxx
deleted file mode 100644
index 03045da..0000000
--- a/butl/utility.cxx
+++ /dev/null
@@ -1,143 +0,0 @@
-// file : butl/utility.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <butl/utility>
-
-#ifdef _WIN32
-# include <butl/win32-utility>
-#endif
-
-#include <string>
-#include <ostream>
-#include <system_error>
-
-namespace butl
-{
- using namespace std;
-
-#ifndef __cpp_lib_uncaught_exceptions
-
-#ifdef __cpp_thread_local
- thread_local
-#else
- __thread
-#endif
- bool exception_unwinding_dtor_ = false;
-
-#ifdef _WIN32
- bool&
- exception_unwinding_dtor () {return exception_unwinding_dtor_;}
-#endif
-
-#endif
-
- [[noreturn]] void
- throw_generic_error (int errno_code, const char* what)
- {
- if (what == nullptr)
- throw system_error (errno_code, generic_category ());
- else
- throw system_error (errno_code, generic_category (), what);
- }
-
- [[noreturn]] void
-#ifndef _WIN32
- throw_system_error (int system_code, int)
- {
- throw system_error (system_code, system_category ());
-#else
- throw_system_error (int system_code, int fallback_errno_code)
- {
- // Here we work around MinGW libstdc++ that interprets Windows system error
- // codes (for example those returned by GetLastError()) as errno codes. The
- // resulting system_error description will have the following form:
- //
- // <system_code description>: <fallback_errno_code description>
- //
- // Also note that the fallback-related description suffix is stripped by
- // our custom operator<<(ostream, exception) for the common case (see
- // below).
- //
- throw system_error (fallback_errno_code,
- system_category (),
- win32::error_msg (system_code));
-#endif
- }
-}
-
-namespace std
-{
- using namespace butl;
-
- ostream&
- operator<< (ostream& o, const exception& e)
- {
- const char* d (e.what ());
- const char* s (d);
-
- // Strip the leading junk (colons and spaces).
- //
- // Note that error descriptions for ios_base::failure exceptions thrown by
- // fdstream can have the ': ' prefix for libstdc++ (read more in comment
- // for throw_ios_failure()).
- //
- for (; *s == ' ' || *s == ':'; ++s) ;
-
- // Strip the trailing junk (periods, spaces, newlines).
- //
- // Note that msvcrt adds some junk like this:
- //
- // Invalid data.\r\n
- //
- size_t n (string::traits_type::length (s));
- for (; n > 0; --n)
- {
- switch (s[n-1])
- {
- case '\r':
- case '\n':
- case '.':
- case ' ': continue;
- }
-
- break;
- }
-
- // Strip the suffix for system_error thrown by
- // throw_system_error(system_code) on Windows. For example for the
- // ERROR_INVALID_DATA error code the original description will be
- // 'Invalid data. : Success' for MinGW libstdc++ and
- // 'Invalid data. : Success.' for msvcrt.
- //
- if (n >= 11 &&
- string::traits_type::compare (s + n - 11, ". : Success", 11) == 0)
- n -= 11;
-
- // Lower-case the first letter if the beginning looks like a word (the
- // second character is the lower-case letter or space).
- //
- char c;
- bool lc (n > 0 && alpha (c = s[0]) && c == ucase (c) &&
- (n == 1 || (alpha (c = s[1]) && c == lcase (c)) || c == ' '));
-
- // Print the description as is if no adjustment is required.
- //
- if (!lc && s == d && s[n] == '\0')
- o << d;
- else
- {
- // We need to produce the resulting description and then write it
- // with a single formatted output operation.
- //
- string r (s, n);
-
- if (lc)
- r[0] = lcase (r[0]);
-
- o << r;
- }
-
- return o;
- }
-}
diff --git a/butl/utility.ixx b/butl/utility.ixx
deleted file mode 100644
index 88d57b3..0000000
--- a/butl/utility.ixx
+++ /dev/null
@@ -1,136 +0,0 @@
-// file : butl/utility.ixx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef _WIN32
-# include <strings.h> // strcasecmp(), strncasecmp()
-#else
-# include <string.h> // _stricmp(), _strnicmp()
-#endif
-
-#include <cctype> // toupper(), tolower(), isalpha(), isdigit(), isalnum()
-
-namespace butl
-{
- inline char
- ucase (char c)
- {
- return std::toupper (c);
- }
-
- inline void
- ucase (char* s, std::size_t n)
- {
- for (const char* e (s + n); s != e; ++s)
- *s = ucase (*s);
- }
-
- inline std::string&
- ucase (std::string& s)
- {
- if (size_t n = s.size ())
- {
- s.front () = s.front (); // Force copy in CoW.
- ucase (const_cast<char*> (s.data ()), n);
- }
- return s;
- }
-
- inline std::string
- ucase (const char* s, std::size_t n)
- {
- std::string r (s, n == std::string::npos ? std::strlen (s) : n);
- return ucase (r);
- }
-
- inline std::string
- ucase (const std::string& s)
- {
- return ucase (s.c_str (), s.size ());
- }
-
- inline char
- lcase (char c)
- {
- return std::tolower (c);
- }
-
- inline void
- lcase (char* s, std::size_t n)
- {
- for (const char* e (s + n); s != e; ++s)
- *s = lcase (*s);
- }
-
- inline std::string&
- lcase (std::string& s)
- {
- if (size_t n = s.size ())
- {
- s.front () = s.front (); // Force copy in CoW.
- lcase (const_cast<char*> (s.data ()), n);
- }
- return s;
- }
-
- inline std::string
- lcase (const char* s, std::size_t n)
- {
- std::string r (s, n == std::string::npos ? std::strlen (s) : n);
- return lcase (r);
- }
-
- inline std::string
- lcase (const std::string& s)
- {
- return lcase (s.c_str (), s.size ());
- }
-
- inline int
- casecmp (char l, char r)
- {
- l = lcase (l);
- r = lcase (r);
- return l < r ? -1 : (l > r ? 1 : 0);
- }
-
- inline int
- casecmp (const char* l, const char* r, std::size_t n)
- {
-#ifndef _WIN32
- return n == std::string::npos ? strcasecmp (l, r) : strncasecmp (l, r, n);
-#else
- return n == std::string::npos ? _stricmp (l, r) : _strnicmp (l, r, n);
-#endif
- }
-
- inline int
- casecmp (const std::string& l, const std::string& r, std::size_t n)
- {
- return casecmp (l.c_str (), r.c_str (), n);
- }
-
- inline int
- casecmp (const std::string& l, const char* r, std::size_t n)
- {
- return casecmp (l.c_str (), r, n);
- }
-
- inline bool
- alpha (char c)
- {
- return std::isalpha (c);
- }
-
- inline bool
- digit (char c)
- {
- return std::isdigit (c);
- }
-
- inline bool
- alnum (char c)
- {
- return std::isalnum (c);
- }
-}
diff --git a/butl/vector-view b/butl/vector-view
deleted file mode 100644
index e8a24b5..0000000
--- a/butl/vector-view
+++ /dev/null
@@ -1,120 +0,0 @@
-// file : butl/vector-view -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_VECTOR_VIEW
-#define BUTL_VECTOR_VIEW
-
-#include <vector>
-#include <cstddef> // size_t, ptrdiff_t
-#include <utility> // swap()
-#include <iterator> // reverse_iterator
-#include <stdexcept> // out_of_range
-
-namespace butl
-{
- // In our version a const view allows the modification of the elements
- // unless T is made const (the same semantics as in smart pointers).
- //
- // @@ If T is const T1, could be useful to have a c-tor from vector<T1>.
- //
- template <typename T>
- class vector_view
- {
- public:
- using value_type = T;
- using pointer = T*;
- using reference = T&;
- using const_pointer = const T*;
- using const_reference = const T&;
- using size_type = std::size_t;
- using difference_type = std::ptrdiff_t;
-
- using iterator = T*;
- using const_iterator = const T*;
- using reverse_iterator = std::reverse_iterator<iterator>;
- using const_reverse_iterator = std::reverse_iterator<const_iterator>;
-
- // construct/copy/destroy:
- //
- vector_view (): data_ (nullptr), size_ (0) {}
- vector_view (T* d, size_type s): data_ (d), size_ (s) {}
-
- template <typename T1, typename A>
- vector_view (std::vector<T1, A>& v)
- : data_ (v.data ()), size_ (v.size ()) {}
-
- template <typename T1, typename A>
- vector_view (const std::vector<T1, A>& v)
- : data_ (v.data ()), size_ (v.size ()) {}
-
- template <typename T1>
- vector_view (const vector_view<T1>& v)
- : data_ (v.data ()), size_ (v.size ()) {}
-
- vector_view (vector_view&&) = default;
- vector_view (const vector_view&) = default;
- vector_view& operator= (vector_view&&) = default;
- vector_view& operator= (const vector_view&) = default;
-
- // iterators:
- //
- iterator begin() const {return data_;}
- iterator end() const {return data_ + size_;}
-
- const_iterator cbegin() const {return data_;}
- const_iterator cend() const {return data_ + size_;}
-
- reverse_iterator rbegin() const {return reverse_iterator (end ());}
- reverse_iterator rend() const {return reverse_iterator (begin ());}
-
- const_reverse_iterator crbegin() const {
- return const_reverse_iterator (cend ());}
- const_reverse_iterator crend() const {
- return const_reverse_iterator (cbegin ());}
-
- // capacity:
- //
- size_type size() const {return size_;}
- bool empty() const {return size_ == 0;}
-
- // element access:
- //
- reference operator[](size_type n) const {return data_[n];}
- reference front() const {return data_[0];}
- reference back() const {return data_[size_ - 1];}
-
- reference at(size_type n) const
- {
- if (n >= size_)
- throw std::out_of_range ("index out of range");
- return data_[n];
- }
-
- // data access:
- //
- T* data() const {return data_;}
-
- // modifiers:
- //
- void assign (T* d, size_type s) {data_ = d; size_ = s;}
- void clear () {data_ = nullptr; size_ = 0;}
- void swap (vector_view& v) {
- std::swap (data_, v.data_); std::swap (size_, v.size_);}
-
- private:
- T* data_;
- size_type size_;
- };
-
- //@@ TODO.
- //
- template<typename T> bool operator== (vector_view<T> l, vector_view<T> r);
- template<typename T> bool operator!= (vector_view<T> l, vector_view<T> r);
- template<typename T> bool operator< (vector_view<T> l, vector_view<T> r);
- template<typename T> bool operator> (vector_view<T> l, vector_view<T> r);
- template<typename T> bool operator<= (vector_view<T> l, vector_view<T> r);
- template<typename T> bool operator>= (vector_view<T> l, vector_view<T> r);
-}
-
-#endif // BUTL_VECTOR_VIEW
diff --git a/butl/version.in b/butl/version.in
deleted file mode 100644
index ef6b7f7..0000000
--- a/butl/version.in
+++ /dev/null
@@ -1,40 +0,0 @@
-// file : butl/version.in -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef LIBBUTL_VERSION // Note: using the version macro itself.
-
-// Note: using build2 standard versioning scheme. The numeric version format
-// is AAABBBCCCDDDE where:
-//
-// AAA - major version number
-// BBB - minor version number
-// CCC - bugfix version number
-// DDD - alpha / beta (DDD + 500) version number
-// E - final (0) / snapshot (1)
-//
-// When DDDE is not 0, 1 is subtracted from AAABBBCCC. For example:
-//
-// Version AAABBBCCCDDDE
-//
-// 0.1.0 0000010000000
-// 0.1.2 0000010010000
-// 1.2.3 0010020030000
-// 2.2.0-a.1 0020019990010
-// 3.0.0-b.2 0029999995020
-// 2.2.0-a.1.z 0020019990011
-//
-#define LIBBUTL_VERSION $libbutl.version.project_number$ULL
-#define LIBBUTL_VERSION_STR "$libbutl.version.project$"
-#define LIBBUTL_VERSION_ID "$libbutl.version.project_id$"
-
-#define LIBBUTL_VERSION_MAJOR $libbutl.version.major$
-#define LIBBUTL_VERSION_MINOR $libbutl.version.minor$
-#define LIBBUTL_VERSION_PATCH $libbutl.version.patch$
-
-#define LIBBUTL_PRE_RELEASE $libbutl.version.pre_release$
-
-#define LIBBUTL_SNAPSHOT $libbutl.version.snapshot_sn$ULL
-#define LIBBUTL_SNAPSHOT_ID "$libbutl.version.snapshot_id$"
-
-#endif // LIBBUTL_VERSION
diff --git a/butl/win32-utility b/butl/win32-utility
deleted file mode 100644
index fb0146f..0000000
--- a/butl/win32-utility
+++ /dev/null
@@ -1,54 +0,0 @@
-// file : butl/win32-utility -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BUTL_WIN32_UTILITY
-#define BUTL_WIN32_UTILITY
-
-// Use this header to include <windows.h> and a couple of Win32-specific
-// utilities.
-//
-
-#ifdef _WIN32
-
-// Try to include <windows.h> so that it doesn't mess other things up.
-//
-#ifndef WIN32_LEAN_AND_MEAN
-# define WIN32_LEAN_AND_MEAN
-# ifndef NOMINMAX // No min and max macros.
-# define NOMINMAX
-# include <windows.h>
-# undef NOMINMAX
-# else
-# include <windows.h>
-# endif
-# undef WIN32_LEAN_AND_MEAN
-#else
-# ifndef NOMINMAX
-# define NOMINMAX
-# include <windows.h>
-# undef NOMINMAX
-# else
-# include <windows.h>
-# endif
-#endif
-
-#include <string>
-
-#include <butl/export>
-
-namespace butl
-{
- namespace win32
- {
- LIBBUTL_EXPORT std::string
- error_msg (DWORD code);
-
- LIBBUTL_EXPORT std::string
- last_error_msg ();
- }
-};
-
-#endif // _WIN32
-
-#endif // BUTL_WIN32_UTILITY
diff --git a/butl/win32-utility.cxx b/butl/win32-utility.cxx
deleted file mode 100644
index 10b1947..0000000
--- a/butl/win32-utility.cxx
+++ /dev/null
@@ -1,54 +0,0 @@
-// file : butl/win32-utility.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <butl/win32-utility>
-
-// Note that while we exclude it from the buildfile-based builds, we still
-// need these guards for build2 bootstrap.
-//
-#ifdef _WIN32
-
-#include <memory> // unique_ptr
-
-using namespace std;
-
-namespace butl
-{
- namespace win32
- {
- struct msg_deleter
- {
- void operator() (char* p) const {LocalFree (p);}
- };
-
- string
- error_msg (DWORD code)
- {
- char* msg;
- if (!FormatMessageA (
- FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS |
- FORMAT_MESSAGE_MAX_WIDTH_MASK,
- 0,
- code,
- MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
- (char*)&msg,
- 0,
- 0))
- return "unknown error code " + to_string (code);
-
- unique_ptr<char, msg_deleter> m (msg);
- return msg;
- }
-
- string
- last_error_msg ()
- {
- return error_msg (GetLastError ());
- }
- }
-}
-
-#endif // _WIN32