public inbox for libstdc++@gcc.gnu.org
 help / color / mirror / Atom feed
* [committed] libstdc++: Make std::system_category() recognize Windows error codes
@ 2021-09-23 15:09 Jonathan Wakely
  0 siblings, 0 replies; only message in thread
From: Jonathan Wakely @ 2021-09-23 15:09 UTC (permalink / raw)
  To: libstdc++, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1217 bytes --]

The std::system_category error category should be used for
system-specific error codes, which means on Windows it should be used
for Windows error codes.  Currently that category assumes that the error
numbers it deals with are errno numbers, which means that
ERROR_ACCESS_DENIED (which has value 0x5) gets treated as whichever
errno number happens to have that value (EIO on mingw32-w64).

This adds a mapping from known Windows error codes to generic errno
ones. This means we correctly treat ERROR_ACCESS_DENIED as corresponding
to EACCES.

Also make std::system_category().message(int) return the right message
for Windows errors, by using FormatMessage instead of strerror. The
output of FormatMessage includes ".\r\n" at the end, so we strip that
off to allow the message to be used in contexts where that would be
problematic.

Signed-off-by: Jonathan Wakely <jwakely@redhat.com>

libstdc++-v3/ChangeLog:

	* src/c++11/system_error.cc (system_error_category) [_WIN32]:
	Map Windows error codes to generic POSIX error numbers. Use
	FormatMessage instead of strerror.
	* testsuite/19_diagnostics/error_category/system_category.cc:
	Adjust for new behaviour on Windows.

Tested x86_64-linux. Committed to trunk.


[-- Attachment #2: patch.txt --]
[-- Type: text/plain, Size: 8610 bytes --]

commit 477897451e46d67acb46f3ac45585e6eb9e7dde5
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Wed Feb 10 16:10:46 2021

    libstdc++: Make std::system_category() recognize Windows error codes
    
    The std::system_category error category should be used for
    system-specific error codes, which means on Windows it should be used
    for Windows error codes.  Currently that category assumes that the error
    numbers it deals with are errno numbers, which means that
    ERROR_ACCESS_DENIED (which has value 0x5) gets treated as whichever
    errno number happens to have that value (EIO on mingw32-w64).
    
    This adds a mapping from known Windows error codes to generic errno
    ones. This means we correctly treat ERROR_ACCESS_DENIED as corresponding
    to EACCES.
    
    Also make std::system_category().message(int) return the right message
    for Windows errors, by using FormatMessage instead of strerror. The
    output of FormatMessage includes ".\r\n" at the end, so we strip that
    off to allow the message to be used in contexts where that would be
    problematic.
    
    Signed-off-by: Jonathan Wakely <jwakely@redhat.com>
    
    libstdc++-v3/ChangeLog:
    
            * src/c++11/system_error.cc (system_error_category) [_WIN32]:
            Map Windows error codes to generic POSIX error numbers. Use
            FormatMessage instead of strerror.
            * testsuite/19_diagnostics/error_category/system_category.cc:
            Adjust for new behaviour on Windows.

diff --git a/libstdc++-v3/src/c++11/system_error.cc b/libstdc++-v3/src/c++11/system_error.cc
index 7fc178a4deb..f1cfc03c3de 100644
--- a/libstdc++-v3/src/c++11/system_error.cc
+++ b/libstdc++-v3/src/c++11/system_error.cc
@@ -32,6 +32,11 @@
 #include <errno.h>
 #undef __sso_string
 
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#include <memory>
+#include <windows.h>
+#endif
+
 namespace
 {
   using std::string;
@@ -81,9 +86,33 @@ namespace
     string
     message(int i) const final
     {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+      char* buf = nullptr;
+      auto len
+	= FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
+			| FORMAT_MESSAGE_ALLOCATE_BUFFER,
+			nullptr,
+			i,
+			LANG_USER_DEFAULT,
+			reinterpret_cast<LPTSTR>(&buf),
+			0,
+			nullptr);
+      if (len > 0)
+      {
+	struct deleter {
+	  void operator()(void* p) const { ::LocalFree(p); }
+	};
+	std::unique_ptr<char[], deleter> guard(buf);
+	if (len > 3 && !__builtin_memcmp(buf + len - 3, ".\r\n", 3)) [[likely]]
+	  len -= 3;
+	return string(buf, len);
+      }
+      return string("Unknown error code");
+#else
       // XXX locale issues: how does one get or set loc.
       // _GLIBCXX_HAVE_STRERROR_L, strerror_l(i, cloc)
       return string(strerror(i));
+#endif
     }
 
     std::error_condition
@@ -93,6 +122,132 @@ namespace
       // and system category otherwise.
       switch (ev)
       {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+      case 0:
+	return {0, generic_category_instance.obj};
+	// Convert Windows error code into a corresponding POSIX errno value.
+#define X(w, e) case ERROR_##w: return {e, generic_category_instance.obj};
+	// This list is based on Cygwin's winsup/cygwin/errno.cc
+	X (ACCESS_DENIED,		EACCES);
+	X (ACTIVE_CONNECTIONS,		EAGAIN);
+	X (ALREADY_EXISTS,		EEXIST);
+	X (BAD_DEVICE,			ENODEV);
+	X (BAD_EXE_FORMAT,		ENOEXEC);
+	X (BAD_NETPATH,			ENOENT);
+	X (BAD_NET_NAME,		ENOENT);
+	X (BAD_NET_RESP,		ENOSYS);
+	X (BAD_PATHNAME,		ENOENT);
+	X (BAD_PIPE,			EINVAL);
+	X (BAD_UNIT,			ENODEV);
+	X (BAD_USERNAME,		EINVAL);
+	X (BEGINNING_OF_MEDIA,		EIO);
+	X (BROKEN_PIPE,			EPIPE);
+	X (BUSY,			EBUSY);
+	X (BUS_RESET,			EIO);
+	X (CALL_NOT_IMPLEMENTED,	ENOSYS);
+	X (CANCELLED,			EINTR);
+	X (CANNOT_MAKE,			EPERM);
+	X (CHILD_NOT_COMPLETE,		EBUSY);
+	X (COMMITMENT_LIMIT,		EAGAIN);
+	X (CONNECTION_REFUSED,		ECONNREFUSED);
+	X (CRC,				EIO);
+	X (DEVICE_DOOR_OPEN,		EIO);
+	X (DEVICE_IN_USE,		EAGAIN);
+	X (DEVICE_REQUIRES_CLEANING,	EIO);
+	X (DEV_NOT_EXIST,		ENOENT);
+	X (DIRECTORY,			ENOTDIR);
+	X (DIR_NOT_EMPTY,		ENOTEMPTY);
+	X (DISK_CORRUPT,		EIO);
+#ifdef ENOSPC
+	X (DISK_FULL,			ENOSPC);
+#endif
+	X (DS_GENERIC_ERROR,		EIO);
+#ifdef ENOSPC
+	X (END_OF_MEDIA,		ENOSPC);
+#endif
+	X (EOM_OVERFLOW,		EIO);
+	X (EXE_MACHINE_TYPE_MISMATCH,	ENOEXEC);
+	X (EXE_MARKED_INVALID,		ENOEXEC);
+	X (FILEMARK_DETECTED,		EIO);
+	X (FILENAME_EXCED_RANGE,	ENAMETOOLONG);
+	X (FILE_CORRUPT,		EEXIST);
+	X (FILE_EXISTS,			EEXIST);
+	X (FILE_INVALID,		ENXIO);
+	X (FILE_NOT_FOUND,		ENOENT);
+#ifdef ENOSPC
+	X (HANDLE_DISK_FULL,		ENOSPC);
+#endif
+	X (INVALID_ADDRESS,		EINVAL);
+	X (INVALID_AT_INTERRUPT_TIME,	EINTR);
+	X (INVALID_BLOCK_LENGTH,	EIO);
+	X (INVALID_DATA,		EINVAL);
+	X (INVALID_DRIVE,		ENODEV);
+	X (INVALID_EA_NAME,		EINVAL);
+	X (INVALID_EXE_SIGNATURE,	ENOEXEC);
+	X (INVALID_HANDLE,		EBADF);
+	X (INVALID_NAME,		ENOENT);
+	X (INVALID_PARAMETER,		EINVAL);
+	X (INVALID_SIGNAL_NUMBER,	EINVAL);
+	X (IOPL_NOT_ENABLED,		ENOEXEC);
+	X (IO_DEVICE,			EIO);
+	X (IO_INCOMPLETE,		EAGAIN);
+	X (IO_PENDING,			EAGAIN);
+	X (LOCK_VIOLATION,		EBUSY);
+	X (MAX_THRDS_REACHED,		EAGAIN);
+	X (META_EXPANSION_TOO_LONG,	EINVAL);
+	X (MOD_NOT_FOUND,		ENOENT);
+	X (MORE_DATA,			EMSGSIZE);
+	X (NEGATIVE_SEEK,		EINVAL);
+	X (NETNAME_DELETED,		ENOENT);
+	X (NOACCESS,			EFAULT);
+	X (NONE_MAPPED,			EINVAL);
+	X (NONPAGED_SYSTEM_RESOURCES,	EAGAIN);
+	X (NOT_ENOUGH_MEMORY,		ENOMEM);
+	X (NOT_ENOUGH_QUOTA,		EIO);
+#ifdef EPERM
+	X (NOT_OWNER,			EPERM);
+#else
+	X (NOT_OWNER,			EACCES);
+#endif
+	X (NOT_SAME_DEVICE,		EXDEV);
+	X (NOT_SUPPORTED,		ENOSYS);
+	X (NO_DATA,			EPIPE);
+	X (NO_DATA_DETECTED,		EIO);
+	X (NO_MORE_SEARCH_HANDLES,	ENFILE);
+	X (NO_PROC_SLOTS,		EAGAIN);
+	X (NO_SIGNAL_SENT,		EIO);
+	X (NO_SYSTEM_RESOURCES,		EFBIG);
+	X (NO_TOKEN,			EINVAL);
+	X (OPEN_FAILED,			EIO);
+	X (OPEN_FILES,			EAGAIN);
+	X (OUTOFMEMORY,			ENOMEM);
+	X (PAGED_SYSTEM_RESOURCES,	EAGAIN);
+	X (PAGEFILE_QUOTA,		EAGAIN);
+	X (PATH_NOT_FOUND,		ENOENT);
+	X (PIPE_BUSY,			EBUSY);
+	X (PIPE_CONNECTED,		EBUSY);
+	X (POSSIBLE_DEADLOCK,		EDEADLK);
+	X (PRIVILEGE_NOT_HELD,		EPERM);
+	X (PROCESS_ABORTED,		EFAULT);
+	X (PROC_NOT_FOUND,		ESRCH);
+	X (SECTOR_NOT_FOUND,		EINVAL);
+	X (SEEK,			EINVAL);
+	X (SERVICE_REQUEST_TIMEOUT,	EBUSY);
+	X (SETMARK_DETECTED,		EIO);
+	X (SHARING_BUFFER_EXCEEDED,	ENOLCK);
+	X (SHARING_VIOLATION,		EBUSY);
+	X (SIGNAL_PENDING,		EBUSY);
+	X (SIGNAL_REFUSED,		EIO);
+	X (THREAD_1_INACTIVE,		EINVAL);
+	X (TIMEOUT,			EBUSY);
+	X (TOO_MANY_LINKS,		EMLINK);
+	X (TOO_MANY_OPEN_FILES,		EMFILE);
+	X (UNEXP_NET_ERR,		EIO);
+	X (WORKING_SET_QUOTA,		EAGAIN);
+	X (WRITE_PROTECT,		EROFS);
+#undef X
+
+#else
       // List of errno macros from [cerrno.syn].
       // C11 only defines EDOM, EILSEQ and ERANGE, the rest are from POSIX.
       // They expand to integer constant expressions with type int,
@@ -340,6 +495,7 @@ namespace
 	return std::error_condition(EINVAL, std::generic_category());
        */
 
+#endif
       default:
 	return std::error_condition(ev, *this);
       }
diff --git a/libstdc++-v3/testsuite/19_diagnostics/error_category/system_category.cc b/libstdc++-v3/testsuite/19_diagnostics/error_category/system_category.cc
index 855f528a8aa..c289d532d4c 100644
--- a/libstdc++-v3/testsuite/19_diagnostics/error_category/system_category.cc
+++ b/libstdc++-v3/testsuite/19_diagnostics/error_category/system_category.cc
@@ -34,6 +34,19 @@ test02()
   const std::error_category& cat = std::system_category();
   std::error_condition cond;
 
+#if defined __MING32__ || defined __MINGW64__
+  cond = cat.default_error_condition(8); // ERROR_NOT_ENOUGH_MEMORY
+  VERIFY( cond.value() == ENOMEM );
+  VERIFY( cond.category() == std::generic_category() );
+  VERIFY( cond == std::errc::not_enough_memory );
+
+  cond = cat.default_error_condition(5); // ERROR_ACCESS_DENIED
+  VERIFY( cond.value() == EACCES );
+  VERIFY( cond.category() == std::generic_category() );
+  VERIFY( cond == std::errc::permission_denied );
+  return;
+#endif
+
   // As of 2011, ISO C only defines EDOM, EILSEQ and ERANGE:
   cond = cat.default_error_condition(EDOM);
   VERIFY( cond.value() == EDOM );
@@ -99,8 +112,13 @@ test03()
   // set "C" locale to get expected message
   auto loc = std::locale::global(std::locale::classic());
 
+#if defined __MING32__ || defined __MINGW64__
+  std::string msg = std::system_category().message(5); // ERROR_ACCESS_DENIED
+  VERIFY(msg == "Access denied");
+#else
   std::string msg = std::system_category().message(EBADF);
   VERIFY( msg.find("file") != std::string::npos );
+#endif
 
   std::locale::global(loc);
 }

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2021-09-23 15:10 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-09-23 15:09 [committed] libstdc++: Make std::system_category() recognize Windows error codes Jonathan Wakely

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).