aboutsummaryrefslogtreecommitdiff
path: root/libbutl/utility.cxx
blob: 8e2422aa2ce40f12341dbe6c295383b289d65652 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
// file      : libbutl/utility.cxx -*- C++ -*-
// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
// license   : MIT; see accompanying LICENSE file

#ifndef __cpp_modules
#include <libbutl/utility.mxx>
#endif

#ifdef _WIN32
#include <libbutl/win32-utility.hxx>
#endif

#ifndef __cpp_lib_modules
#include <string>
#include <cstddef>
#include <utility>

#include <ostream>
#include <system_error>
#endif

#include <libbutl/ft/lang.hxx>
#include <libbutl/ft/exception.hxx>

#ifdef __cpp_modules
module butl.utility;

// Only imports additional to interface.
#ifdef __clang__
#ifdef __cpp_lib_modules
import std.core;
import std.io;
#endif
#endif

#endif

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
  }

  string&
  trim (string& l)
  {
    /*
    assert (trim (r = "") == "");
    assert (trim (r = " ") == "");
    assert (trim (r = " \t\r") == "");
    assert (trim (r = "a") == "a");
    assert (trim (r = " a") == "a");
    assert (trim (r = "a ") == "a");
    assert (trim (r = " \ta") == "a");
    assert (trim (r = "a \r") == "a");
    assert (trim (r = " a ") == "a");
    assert (trim (r = " \ta \r") == "a");
    */

    size_t i (0), n (l.size ());

    for (char c;
         i != n && ((c = l[i]) == ' ' || c == '\t' || c == '\r');
         ++i) ;

    for (char c;
         n != i && ((c = l[n - 1]) == ' ' || c == '\t' || c == '\r');
         --n) ;

    if (i != 0)
    {
      string s (l, i, n - i);
      l.swap (s);
    }
    else if (n != l.size ())
      l.resize (n);

    return l;
  }
}

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' or 'Invalid data. : No error' for MinGW libstdc++ and
    // 'Invalid data. : Success.' or ". : The operation completed
    // successfully." for msvcrt.
    //
    // Check if the string ends with the specified suffix and return its
    // length if that's the case. So can be used as bool.
    //
    auto suffix = [s, n] (const char* v) -> size_t
    {
      size_t nv (string::traits_type::length (v));
      return (n >= nv && string::traits_type::compare (s + n - nv, v, nv) == 0
              ? nv
              : 0);
    };

    size_t ns;
    if ((ns = suffix (". : Success"))  ||
        (ns = suffix (". : No error")) ||
        (ns = suffix (". : The operation completed successfully")))
      n -= ns;

    // 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;
  }
}