aboutsummaryrefslogtreecommitdiff
path: root/libbutl/utility.ixx
diff options
context:
space:
mode:
Diffstat (limited to 'libbutl/utility.ixx')
-rw-r--r--libbutl/utility.ixx134
1 files changed, 124 insertions, 10 deletions
diff --git a/libbutl/utility.ixx b/libbutl/utility.ixx
index fa37a14..fda1ce5 100644
--- a/libbutl/utility.ixx
+++ b/libbutl/utility.ixx
@@ -1,13 +1,10 @@
// file : libbutl/utility.ixx -*- C++ -*-
// license : MIT; see accompanying LICENSE file
-#ifndef __cpp_lib_modules_ts
#include <cctype> // toupper(), tolower(), is*()
#include <cwctype> // isw*()
-#include <cstdlib> // getenv()
#include <algorithm> // for_each()
#include <stdexcept> // invalid_argument
-#endif
namespace butl
{
@@ -146,6 +143,12 @@ namespace butl
}
inline bool
+ wspace (char c)
+ {
+ return std::isspace (c);
+ }
+
+ inline bool
alpha (wchar_t c)
{
return std::iswalpha (c);
@@ -169,6 +172,12 @@ namespace butl
return std::iswxdigit (c);
}
+ inline bool
+ wspace (wchar_t c)
+ {
+ return std::iswspace (c);
+ }
+
inline std::size_t
next_word (const std::string& s, std::size_t& b, std::size_t& e,
char d1, char d2)
@@ -176,7 +185,7 @@ namespace butl
return next_word (s, s.size (), b, e, d1, d2);
}
- inline size_t
+ inline std::size_t
next_word (const std::string& s,
std::size_t n, std::size_t& b, std::size_t& e,
char d1, char d2)
@@ -201,6 +210,66 @@ namespace butl
return e - b;
}
+ inline std::size_t
+ next_word (const std::string& s,
+ std::size_t n, std::size_t& b, std::size_t& e, std::size_t& m,
+ char d1, char d2)
+ {
+ // An empty word will necessarily be represented as b and e being the
+ // position of a delimiter. Consider these corner cases (in all three we
+ // should produce two words):
+ //
+ // \n
+ // a\n
+ // \na
+ //
+ // It feels sensible to represent an empty word as the position of the
+ // trailing delimiter except if it is the last character (the first two
+ // cases). Thus the additional m state, which, if 0 or 1 indicates the
+ // number of delimiters to skip before parsing the next word and 2 if
+ // this is a trailing delimiter for which we need to fake an empty word
+ // with the leading delimiter.
+
+ if (b != e)
+ b = e;
+
+ if (m > 1)
+ {
+ --m;
+ return 0;
+ }
+
+ // Skip the leading delimiter, if any.
+ //
+ b += m;
+
+ if (b == n)
+ {
+ e = n;
+ return 0;
+ }
+
+ // Find first trailing delimiter.
+ //
+ m = 0;
+ for (e = b; e != n; ++e)
+ {
+ if (s[e] == d1 || s[e] == d2)
+ {
+ m = 1;
+
+ // Handle the special delimiter as the last character case.
+ //
+ if (e + 1 == n)
+ ++m;
+
+ break;
+ }
+ }
+
+ return e - b;
+ }
+
inline std::string&
sanitize_identifier (std::string& s)
{
@@ -228,7 +297,7 @@ namespace butl
inline void
sanitize_strlit (const std::string& s, std::string& o)
{
- for (size_t i (0), j;; i = j + 1)
+ for (std::size_t i (0), j;; i = j + 1)
{
j = s.find_first_of ("\\\"\n", i);
o.append (s.c_str () + i, (j == std::string::npos ? s.size () : j) - i);
@@ -333,13 +402,58 @@ namespace butl
return utf8_length_impl (s, nullptr, ts, wl).has_value ();
}
- inline optional<std::string>
- getenv (const std::string& name)
+#ifndef _WIN32
+ inline const char* const*
+ thread_env ()
+ {
+ return thread_env_;
+ }
+
+ inline void
+ thread_env (const char* const* v)
+ {
+ thread_env_ = v;
+ }
+#endif
+
+ // auto_thread_env
+ //
+ inline auto_thread_env::
+ auto_thread_env (const char* const* new_env)
{
- if (const char* r = std::getenv (name.c_str ()))
- return std::string (r);
+ const char* const* cur_env (thread_env ());
- return nullopt;
+ if (cur_env != new_env)
+ {
+ prev_env = cur_env;
+ thread_env (new_env);
+ }
+ }
+
+ inline auto_thread_env::
+ auto_thread_env (auto_thread_env&& x) noexcept
+ : prev_env (std::move (x.prev_env))
+ {
+ x.prev_env = nullopt;
+ }
+
+ inline auto_thread_env& auto_thread_env::
+ operator= (auto_thread_env&& x) noexcept
+ {
+ if (this != &x)
+ {
+ prev_env = std::move (x.prev_env);
+ x.prev_env = nullopt;
+ }
+
+ return *this;
+ }
+
+ inline auto_thread_env::
+ ~auto_thread_env ()
+ {
+ if (prev_env)
+ thread_env (*prev_env);
}
template <typename F, typename P>