aboutsummaryrefslogtreecommitdiff
path: root/butl/utility.cxx
blob: 03045daa7a684d3f00ac98deb5ef6c2e51dbaef8 (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
// 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;
  }
}