From 615d333787c1d8dc08df5e30c60ec20600a74b85 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Wed, 22 Mar 2017 00:36:27 +0300 Subject: Implement throw_generic_error() and throw_system_error() --- butl/fdstream.cxx | 38 ++++++++++++++++++++---- butl/filesystem.cxx | 85 +++++++++++++++++++++++------------------------------ butl/pager.cxx | 6 ++-- butl/path.cxx | 62 ++++++++++++++++---------------------- butl/process | 9 +++--- butl/timestamp.cxx | 19 ++++++------ butl/utility | 7 +++-- butl/utility.cxx | 55 ++++++++++++++++++++++++++++++++-- 8 files changed, 169 insertions(+), 112 deletions(-) diff --git a/butl/fdstream.cxx b/butl/fdstream.cxx index 41dd1f5..d841cc2 100644 --- a/butl/fdstream.cxx +++ b/butl/fdstream.cxx @@ -71,14 +71,36 @@ namespace butl throw ios_base::failure (m != nullptr ? m : e.message ().c_str ()); } + // Throw system_error with generic_category. + // static inline void - throw_ios_failure (int ev, const char* m = nullptr) + throw_ios_failure (int errno_code, const char* m = nullptr) { - error_code ec (ev, system_category ()); + error_code ec (errno_code, generic_category ()); throw_ios_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::value> ( + ec, m.c_str ()); + } +#endif + // auto_fd // void auto_fd:: @@ -905,7 +927,7 @@ namespace butl { HANDLE h (reinterpret_cast (_get_osfhandle (fd))); if (h == INVALID_HANDLE_VALUE) - throw_ios_failure (EIO, "unable to obtain file handle"); + throw_ios_failure (errno); // EBADF (POSIX value). return h; }; @@ -921,7 +943,7 @@ namespace butl DWORD f; if (!GetHandleInformation (handle (fd), &f)) - throw_ios_failure (EIO, last_error_msg ().c_str ()); + 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). @@ -933,7 +955,7 @@ namespace butl auto_fd nfd (dup ()); if (!SetHandleInformation (handle (nfd.get ()), HANDLE_FLAG_INHERIT, 0)) - throw_ios_failure (EIO, last_error_msg ().c_str ()); + throw_ios_system_failure (GetLastError ()); return nfd; } @@ -975,6 +997,10 @@ namespace butl } catch (const system_error& e) { + // Make sure that the error denotes errno portable code. + // + assert (e.code ().category () == generic_category ()); + errno = e.code ().value (); return -1; } @@ -999,7 +1025,7 @@ namespace butl // int r (_setmode (fd, m == fdstream_mode::binary ? _O_BINARY : _O_TEXT)); if (r == -1) - throw_ios_failure (errno); + throw_ios_failure (errno); // EBADF or EINVAL (POSIX values). return fdstream_mode::blocking | ((r & _O_BINARY) == _O_BINARY diff --git a/butl/filesystem.cxx b/butl/filesystem.cxx index e881417..1873bfa 100644 --- a/butl/filesystem.cxx +++ b/butl/filesystem.cxx @@ -37,6 +37,7 @@ #include #include +#include // throw_generic_error() #include #include @@ -75,7 +76,7 @@ namespace butl if (errno == ENOENT || errno == ENOTDIR) return make_pair (false, entry_type::unknown); else - throw system_error (errno, system_category ()); + throw_generic_error (errno); } auto m (s.st_mode); @@ -129,7 +130,7 @@ namespace butl if (errno == ENOENT || errno == ENOTDIR) return make_pair (false, entry_type::unknown); else - throw system_error (errno, system_category ()); + throw_generic_error (errno); } auto m (s.st_mode); @@ -166,7 +167,7 @@ namespace butl if (e == EEXIST && dir_exists (p)) return mkdir_status::already_exists; else - throw system_error (e, system_category ()); + throw_generic_error (e); } return mkdir_status::success; @@ -202,7 +203,7 @@ namespace butl else if (errno == ENOTEMPTY || errno == EEXIST) r = rmdir_status::not_empty; else if (!ignore_error) - throw system_error (errno, system_category ()); + throw_generic_error (errno); } return r; @@ -229,8 +230,9 @@ namespace butl rmdir_status r (try_rmdir (p)); if (r != rmdir_status::success && !ignore_error) - throw system_error (r == rmdir_status::not_empty ? ENOTEMPTY : ENOENT, - system_category ()); + throw_generic_error (r == rmdir_status::not_empty + ? ENOTEMPTY + : ENOENT); } } @@ -251,7 +253,7 @@ namespace butl if (errno == ENOENT || errno == ENOTDIR) r = rmfile_status::not_exist; else if (!ignore_error) - throw system_error (errno, system_category ()); + throw_generic_error (errno); } return r; @@ -262,14 +264,14 @@ namespace butl mksymlink (const path& target, const path& link, bool) { if (symlink (target.string ().c_str (), link.string ().c_str ()) == -1) - throw system_error (errno, system_category ()); + 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 system_error (errno, system_category ()); + throw_generic_error (errno); } #else @@ -277,7 +279,7 @@ namespace butl void mksymlink (const path&, const path&, bool) { - throw system_error (ENOSYS, system_category (), "symlinks not supported"); + throw_generic_error (ENOSYS, "symlinks not supported"); } void @@ -288,14 +290,10 @@ namespace butl if (!CreateHardLinkA (link.string ().c_str (), target.string ().c_str (), nullptr)) - { - string e (win32::last_error_msg ()); - throw system_error (EIO, system_category (), e); - } + throw_system_error (GetLastError ()); } else - throw system_error ( - ENOSYS, system_category (), "directory hard links not supported"); + throw_generic_error (ENOSYS, "directory hard links not supported"); } #endif @@ -362,7 +360,7 @@ namespace butl // Note that our custom operator<<(ostream, exception) doesn't strip this // suffix. This is a temporary code after all. // - throw system_error (EIO, system_category (), e.what ()); + throw_generic_error (EIO, e.what ()); } } @@ -450,7 +448,7 @@ namespace butl #ifndef _WIN32 if (!ovr && path_entry (to).first) - throw system_error (EEXIST, system_category ()); + throw_generic_error (EEXIST); if (::rename (f, t) == 0) // POSIX implementation. return; @@ -459,7 +457,7 @@ namespace butl // move the file ourselves. // if (errno != EXDEV) - throw system_error (errno, system_category ()); + throw_generic_error (errno); // Note that cpfile() follows symlinks, so we need to remove destination if // exists. @@ -474,7 +472,7 @@ namespace butl // struct stat s; if (stat (f, &s) != 0) - throw system_error (errno, system_category ()); + throw_generic_error (errno); timeval times[2]; times[0].tv_sec = s.st_atime; @@ -483,7 +481,7 @@ namespace butl times[1].tv_usec = mnsec (&s, true) / 1000; if (utimes (t, times) != 0) - throw system_error (errno, system_category ()); + throw_generic_error (errno); // Finally, remove the source file. // @@ -496,13 +494,8 @@ namespace butl // auto te (path_entry (to)); - // Note that it would be nicer to just pass standard error codes to - // system_error ctors below, but their error descriptions horrifies for - // msvcrt. Actually they just don't match the semantics. The resulted error - // description is also not ideal (see below). - // if (!ovr && te.first) - throw system_error (EEXIST, system_category (), "file exists"); + throw_generic_error (EEXIST); bool td (te.first && te.second == entry_type::directory); @@ -513,7 +506,7 @@ namespace butl // either directories or not directories. // if (fe.first && te.first && fd != td) - throw system_error (EIO, system_category (), "not a directory"); + throw_generic_error (ENOTDIR); DWORD mfl (fd ? 0 : (MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING)); @@ -534,15 +527,7 @@ namespace butl MoveFileExA (f, t, mfl)) return; - // @@ The exception description will look like: - // - // file not found. : Access denied - // - // Probably need to consider such discriptions for sanitizing in - // operator<<(ostream,exception). - // - string e (win32::error_msg (ec)); - throw system_error (EIO, system_category (), e); + throw_system_error (ec); #endif } @@ -561,7 +546,7 @@ namespace butl if (errno == ENOENT || errno == ENOTDIR) return timestamp_nonexistent; else - throw system_error (errno, system_category ()); + throw_generic_error (errno); } return S_ISREG (s.st_mode) @@ -581,7 +566,7 @@ namespace butl struct _stat s; if (_stat (p.string ().c_str (), &s) != 0) #endif - throw system_error (errno, system_category ()); + throw_generic_error (errno); // VC++ has no S_IRWXU defined. MINGW GCC <= 4.9 has no S_IRWXG, S_IRWXO // defined. @@ -624,7 +609,7 @@ namespace butl #else if (_chmod (p.string ().c_str (), m) == -1) #endif - throw system_error (errno, system_category ()); + throw_generic_error (errno); } // dir_{entry,iterator} @@ -648,7 +633,7 @@ namespace butl e_ = move (x.e_); if (h_ != nullptr && closedir (h_) == -1) - throw system_error (errno, system_category ()); + throw_generic_error (errno); h_ = x.h_; x.h_ = nullptr; @@ -665,7 +650,7 @@ namespace butl ? stat (p.string ().c_str (), &s) : lstat (p.string ().c_str (), &s)) != 0) { - throw system_error (errno, system_category ()); + throw_generic_error (errno); } entry_type r; @@ -696,7 +681,7 @@ namespace butl h_ = h.get (); if (h_ == nullptr) - throw system_error (errno, system_category ()); + throw_generic_error (errno); next (); @@ -771,7 +756,7 @@ namespace butl h_ = nullptr; } else - throw system_error (errno, system_category ()); + throw_generic_error (errno); break; } @@ -796,7 +781,7 @@ namespace butl e_ = move (x.e_); if (h_ != -1 && _findclose (h_) == -1) - throw system_error (errno, system_category ()); + throw_generic_error (errno); h_ = x.h_; x.h_ = -1; @@ -814,7 +799,7 @@ namespace butl struct _stat s; if (_stat (p.string ().c_str (), &s) != 0) - throw system_error (errno, system_category ()); + throw_generic_error (errno); entry_type r; if (S_ISREG (s.st_mode)) @@ -876,7 +861,7 @@ namespace butl // Check to distinguish non-existent vs empty directories. // if (!dir_exists (e_.b_)) - throw system_error (ENOENT, system_category ()); + throw_generic_error (ENOENT); h_ = _findfirst ((e_.b_ / path ("*")).string ().c_str (), &fi); r = h_ != -1; @@ -917,7 +902,7 @@ namespace butl } } else - throw system_error (errno, system_category ()); + throw_generic_error (errno); break; } @@ -1187,6 +1172,10 @@ namespace butl // 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; diff --git a/butl/pager.cxx b/butl/pager.cxx index e327960..fe59e5b 100644 --- a/butl/pager.cxx +++ b/butl/pager.cxx @@ -16,9 +16,9 @@ #include // strchr() #include // move() -#include -#include // operator<<(ostream, exception) +#include // operator<<(ostream, exception), + // throw_generic_error() #include // fdclose() using namespace std; @@ -139,7 +139,7 @@ namespace butl p_.out_fd.reset (); if (pager != nullptr) - throw system_error (ECHILD, system_category ()); + throw_generic_error (ECHILD); } else os_.open (move (p_.out_fd)); diff --git a/butl/path.cxx b/butl/path.cxx index 9947bbe..dfa812b 100644 --- a/butl/path.cxx +++ b/butl/path.cxx @@ -28,10 +28,10 @@ #include #include #include // strcpy() -#include #include +#include // throw_*_error() #include #ifndef _WIN32 @@ -65,12 +65,12 @@ namespace butl #ifdef _WIN32 char cwd[_MAX_PATH]; if (_getcwd (cwd, _MAX_PATH) == 0) - throw system_error (errno, system_category ()); + throw_generic_error (errno); cwd[0] = toupper (cwd[0]); // Canonicalize. #else char cwd[PATH_MAX]; if (getcwd (cwd, PATH_MAX) == 0) - throw system_error (errno, system_category ()); + throw_generic_error (errno); #endif return cwd; @@ -93,10 +93,10 @@ namespace butl : string_type (s + directory_separator)); if (_chdir (d.c_str ()) != 0) - throw system_error (errno, system_category ()); + throw_generic_error (errno); #else if (chdir (s.c_str ()) != 0) - throw system_error (errno, system_category ()); + throw_generic_error (errno); #endif } @@ -115,10 +115,10 @@ namespace butl struct stat s; if (stat (dir, &s) != 0) - throw system_error (errno, system_category ()); + throw_generic_error (errno); if (!S_ISDIR (s.st_mode)) - throw system_error (ENOTDIR, system_category ()); + throw_generic_error (ENOTDIR); return dir; } @@ -141,13 +141,13 @@ namespace butl int r (getpwuid_r (getuid (), &pw, buf, sizeof (buf), &rpw)); if (r == -1) - throw system_error (errno, system_category ()); + throw_generic_error (errno); if (r == 0 && rpw == nullptr) // According to POSIX errno should be left unchanged if an entry is not // found. // - throw system_error (ENOENT, system_category ()); + throw_generic_error (ENOENT); return pw.pw_dir; } @@ -160,10 +160,7 @@ namespace butl #ifdef _WIN32 char d[_MAX_PATH + 1]; if (GetTempPathA (_MAX_PATH + 1, d) == 0) - { - string e (last_error_msg ()); - throw system_error (ENOTDIR, system_category (), e); - } + throw_system_error (GetLastError ()); return d; #else @@ -198,10 +195,7 @@ namespace butl HRESULT r (SHGetFolderPathA (NULL, CSIDL_PROFILE, NULL, 0, h)); if (!SUCCEEDED (r)) - { - string e (error_msg (r)); - throw system_error (ENOTDIR, system_category (), e); - } + throw_system_error (r); return h; #endif @@ -221,7 +215,7 @@ namespace butl if (errno == EACCES || errno == ENOENT || errno == ENOTDIR) throw invalid_basic_path (s); else - throw system_error (errno, system_category ()); + throw_generic_error (errno); } s = r; @@ -239,16 +233,16 @@ namespace butl #ifdef _WIN32 wchar_t wcwd[_MAX_PATH]; if (_wgetcwd (wcwd, _MAX_PATH) == 0) - throw system_error (errno, system_category ()); + throw_generic_error (errno); wcwd[0] = toupper (wcwd[0]); // Canonicalize. #else char cwd[PATH_MAX]; if (getcwd (cwd, PATH_MAX) == 0) - throw system_error (errno, system_category ()); + throw_generic_error (errno); wchar_t wcwd[PATH_MAX]; if (mbstowcs (wcwd, cwd, PATH_MAX) == size_type (-1)) - throw system_error (EINVAL, system_category ()); + throw_generic_error (EINVAL); #endif return wcwd; @@ -268,17 +262,17 @@ namespace butl : string_type (s + directory_separator)); if (_wchdir (d.c_str ()) != 0) - throw system_error (errno, system_category ()); + throw_generic_error (errno); #else char ns[PATH_MAX + 1]; if (wcstombs (ns, s.c_str (), PATH_MAX) == size_type (-1)) - throw system_error (EINVAL, system_category ()); + throw_generic_error (EINVAL); ns[PATH_MAX] = '\0'; if (chdir (ns) != 0) - throw system_error (errno, system_category ()); + throw_generic_error (errno); #endif } @@ -289,10 +283,7 @@ namespace butl #ifdef _WIN32 wchar_t d[_MAX_PATH + 1]; if (GetTempPathW (_MAX_PATH + 1, d) == 0) - { - string e (last_error_msg ()); - throw system_error (ENOTDIR, system_category (), e); - } + throw_system_error (GetLastError ()); #else wchar_t d[PATH_MAX]; @@ -304,10 +295,10 @@ namespace butl size_t r (mbstowcs (d, butl::temp_directory (), PATH_MAX)); if (r == size_t (-1)) - throw system_error (EINVAL, system_category ()); + throw_generic_error (EINVAL); if (r == PATH_MAX) - throw system_error (ENOTSUP, system_category ()); + throw_generic_error (ENOTSUP); #endif return d; @@ -331,10 +322,10 @@ namespace butl size_t r (mbstowcs (d, home ().c_str (), PATH_MAX)); if (r == size_t (-1)) - throw system_error (EINVAL, system_category ()); + throw_generic_error (EINVAL); if (r == PATH_MAX) - throw system_error (ENOTSUP, system_category ()); + throw_generic_error (ENOTSUP); return d; #else @@ -347,10 +338,7 @@ namespace butl HRESULT r (SHGetFolderPathW (NULL, CSIDL_PROFILE, NULL, 0, h)); if (!SUCCEEDED (r)) - { - string e (error_msg (r)); - throw system_error (ENOTDIR, system_category (), e); - } + throw_system_error (r); return h; #endif @@ -388,7 +376,7 @@ namespace butl return false; if (h == -1 || _findclose (h) == -1) - throw system_error (errno, system_category ()); + throw_generic_error (errno); r += fi.name; return true; diff --git a/butl/process b/butl/process index 19bea3a..31cb681 100644 --- a/butl/process +++ b/butl/process @@ -30,13 +30,14 @@ namespace butl #ifndef _WIN32 process_error (int e, bool child) - : system_error (e, std::system_category ()), child_ (child) {} + : system_error (e, std::generic_category ()), child_ (child) {} #else process_error (int e, bool child = false) - : system_error (e, std::system_category ()), child_ (child) {} + : system_error (e, std::generic_category ()), child_ (child) {} - process_error (const std::string& d, int e = ECHILD) - : system_error (e, std::system_category (), d), child_ (false) {} + process_error (const std::string& d, int fallback_errno_code = 0) + : system_error (fallback_errno_code, std::system_category (), d), + child_ (false) {} #endif private: diff --git a/butl/timestamp.cxx b/butl/timestamp.cxx index d38c018..971f1ef 100644 --- a/butl/timestamp.cxx +++ b/butl/timestamp.cxx @@ -15,7 +15,8 @@ #include #include // pair, make_pair() #include // runtime_error -#include + +#include // throw_generic_error() using namespace std; @@ -137,7 +138,7 @@ namespace butl if ((local ? details::localtime (&t, &tm) : details::gmtime (&t, &tm)) == nullptr) - throw system_error (errno, system_category ()); + throw_generic_error (errno); using namespace chrono; @@ -147,7 +148,7 @@ namespace butl char fmt[256]; size_t n (strlen (format)); if (n + 1 > sizeof (fmt)) - throw system_error (EINVAL, system_category ()); + throw_generic_error (EINVAL); memcpy (fmt, format, n + 1); // Chunk the format string into fragments that we feed to put_time() and @@ -175,18 +176,18 @@ namespace butl j += 2; // Character after '['. if (j == n) - throw system_error (EINVAL, system_category ()); + throw_generic_error (EINVAL); char d ('\0'); if (fmt[j] != 'N') { d = fmt[j]; if (++j == n || fmt[j] != 'N') - throw system_error (EINVAL, system_category ()); + throw_generic_error (EINVAL); } if (++j == n || fmt[j] != ']') - throw system_error (EINVAL, system_category ()); + throw_generic_error (EINVAL); if (ns != nanoseconds::zero ()) { @@ -267,7 +268,7 @@ namespace butl { std::tm tm; if (details::gmtime (&t, &tm) == nullptr) - throw system_error (errno, system_category ()); + throw_generic_error (errno); if (t >= 24 * 60 * 60) tm.tm_mday -= 1; // Make day of the month to be a zero-based number. @@ -431,7 +432,7 @@ namespace butl static pair from_string (const char* input, const char* format, const char** end) { - auto bad_val = []() {throw system_error (EINVAL, system_category ());}; + auto bad_val = [] () {throw_generic_error (EINVAL);}; // See if we have our specifier. // @@ -603,7 +604,7 @@ namespace butl time_t time (local ? mktime (&t.first) : timegm (&t.first)); if (time == -1) - throw system_error (errno, system_category ()); + throw_generic_error (errno); return timestamp::clock::from_time_t (time) + chrono::duration_cast (t.second); diff --git a/butl/utility b/butl/utility index 7dc4d73..673b715 100644 --- a/butl/utility +++ b/butl/utility @@ -30,10 +30,10 @@ namespace butl // // See also the exception sanitization below. // - [[noreturn]] void - throw_generic_error (int errno_code); + [[noreturn]] LIBBUTL_EXPORT void + throw_generic_error (int errno_code, const char* what = nullptr); - [[noreturn]] void + [[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 @@ -252,6 +252,7 @@ namespace std // // - 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& diff --git a/butl/utility.cxx b/butl/utility.cxx index 84c8a74..03045da 100644 --- a/butl/utility.cxx +++ b/butl/utility.cxx @@ -2,12 +2,20 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include - #include +#ifdef _WIN32 +# include +#endif + +#include +#include +#include + namespace butl { + using namespace std; + #ifndef __cpp_lib_uncaught_exceptions #ifdef __cpp_thread_local @@ -23,6 +31,39 @@ namespace butl #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: + // + // : + // + // 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 @@ -63,6 +104,16 @@ namespace std 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). // -- cgit v1.1