summaryrefslogtreecommitdiffstats
path: root/src/port/strerror.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/port/strerror.c')
-rw-r--r--src/port/strerror.c314
1 files changed, 314 insertions, 0 deletions
diff --git a/src/port/strerror.c b/src/port/strerror.c
new file mode 100644
index 0000000..c07c983
--- /dev/null
+++ b/src/port/strerror.c
@@ -0,0 +1,314 @@
+/*-------------------------------------------------------------------------
+ *
+ * strerror.c
+ * Replacements for standard strerror() and strerror_r() functions
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/port/strerror.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "c.h"
+
+/*
+ * Within this file, "strerror" means the platform's function not pg_strerror,
+ * and likewise for "strerror_r"
+ */
+#undef strerror
+#undef strerror_r
+
+static char *gnuish_strerror_r(int errnum, char *buf, size_t buflen);
+static char *get_errno_symbol(int errnum);
+#ifdef WIN32
+static char *win32_socket_strerror(int errnum, char *buf, size_t buflen);
+#endif
+
+
+/*
+ * A slightly cleaned-up version of strerror()
+ */
+char *
+pg_strerror(int errnum)
+{
+ static char errorstr_buf[PG_STRERROR_R_BUFLEN];
+
+ return pg_strerror_r(errnum, errorstr_buf, sizeof(errorstr_buf));
+}
+
+/*
+ * A slightly cleaned-up version of strerror_r()
+ */
+char *
+pg_strerror_r(int errnum, char *buf, size_t buflen)
+{
+ char *str;
+
+ /* If it's a Windows Winsock error, that needs special handling */
+#ifdef WIN32
+ /* Winsock error code range, per WinError.h */
+ if (errnum >= 10000 && errnum <= 11999)
+ return win32_socket_strerror(errnum, buf, buflen);
+#endif
+
+ /* Try the platform's strerror_r(), or maybe just strerror() */
+ str = gnuish_strerror_r(errnum, buf, buflen);
+
+ /*
+ * Some strerror()s return an empty string for out-of-range errno. This
+ * is ANSI C spec compliant, but not exactly useful. Also, we may get
+ * back strings of question marks if libc cannot transcode the message to
+ * the codeset specified by LC_CTYPE. If we get nothing useful, first try
+ * get_errno_symbol(), and if that fails, print the numeric errno.
+ */
+ if (str == NULL || *str == '\0' || *str == '?')
+ str = get_errno_symbol(errnum);
+
+ if (str == NULL)
+ {
+ snprintf(buf, buflen, _("operating system error %d"), errnum);
+ str = buf;
+ }
+
+ return str;
+}
+
+/*
+ * Simple wrapper to emulate GNU strerror_r if what the platform provides is
+ * POSIX. Also, if platform lacks strerror_r altogether, fall back to plain
+ * strerror; it might not be very thread-safe, but tough luck.
+ */
+static char *
+gnuish_strerror_r(int errnum, char *buf, size_t buflen)
+{
+#ifdef HAVE_STRERROR_R
+#ifdef STRERROR_R_INT
+ /* POSIX API */
+ if (strerror_r(errnum, buf, buflen) == 0)
+ return buf;
+ return NULL; /* let caller deal with failure */
+#else
+ /* GNU API */
+ return strerror_r(errnum, buf, buflen);
+#endif
+#else /* !HAVE_STRERROR_R */
+ char *sbuf = strerror(errnum);
+
+ if (sbuf == NULL) /* can this still happen anywhere? */
+ return NULL;
+ /* To minimize thread-unsafety hazard, copy into caller's buffer */
+ strlcpy(buf, sbuf, buflen);
+ return buf;
+#endif
+}
+
+/*
+ * Returns a symbol (e.g. "ENOENT") for an errno code.
+ * Returns NULL if the code is unrecognized.
+ */
+static char *
+get_errno_symbol(int errnum)
+{
+ switch (errnum)
+ {
+ case E2BIG:
+ return "E2BIG";
+ case EACCES:
+ return "EACCES";
+ case EADDRINUSE:
+ return "EADDRINUSE";
+ case EADDRNOTAVAIL:
+ return "EADDRNOTAVAIL";
+ case EAFNOSUPPORT:
+ return "EAFNOSUPPORT";
+#ifdef EAGAIN
+ case EAGAIN:
+ return "EAGAIN";
+#endif
+#ifdef EALREADY
+ case EALREADY:
+ return "EALREADY";
+#endif
+ case EBADF:
+ return "EBADF";
+#ifdef EBADMSG
+ case EBADMSG:
+ return "EBADMSG";
+#endif
+ case EBUSY:
+ return "EBUSY";
+ case ECHILD:
+ return "ECHILD";
+ case ECONNABORTED:
+ return "ECONNABORTED";
+ case ECONNREFUSED:
+ return "ECONNREFUSED";
+ case ECONNRESET:
+ return "ECONNRESET";
+ case EDEADLK:
+ return "EDEADLK";
+ case EDOM:
+ return "EDOM";
+ case EEXIST:
+ return "EEXIST";
+ case EFAULT:
+ return "EFAULT";
+ case EFBIG:
+ return "EFBIG";
+ case EHOSTDOWN:
+ return "EHOSTDOWN";
+ case EHOSTUNREACH:
+ return "EHOSTUNREACH";
+ case EIDRM:
+ return "EIDRM";
+ case EINPROGRESS:
+ return "EINPROGRESS";
+ case EINTR:
+ return "EINTR";
+ case EINVAL:
+ return "EINVAL";
+ case EIO:
+ return "EIO";
+ case EISCONN:
+ return "EISCONN";
+ case EISDIR:
+ return "EISDIR";
+#ifdef ELOOP
+ case ELOOP:
+ return "ELOOP";
+#endif
+ case EMFILE:
+ return "EMFILE";
+ case EMLINK:
+ return "EMLINK";
+ case EMSGSIZE:
+ return "EMSGSIZE";
+ case ENAMETOOLONG:
+ return "ENAMETOOLONG";
+ case ENETDOWN:
+ return "ENETDOWN";
+ case ENETRESET:
+ return "ENETRESET";
+ case ENETUNREACH:
+ return "ENETUNREACH";
+ case ENFILE:
+ return "ENFILE";
+ case ENOBUFS:
+ return "ENOBUFS";
+ case ENODEV:
+ return "ENODEV";
+ case ENOENT:
+ return "ENOENT";
+ case ENOEXEC:
+ return "ENOEXEC";
+ case ENOMEM:
+ return "ENOMEM";
+ case ENOSPC:
+ return "ENOSPC";
+ case ENOSYS:
+ return "ENOSYS";
+ case ENOTCONN:
+ return "ENOTCONN";
+ case ENOTDIR:
+ return "ENOTDIR";
+#if defined(ENOTEMPTY) && (ENOTEMPTY != EEXIST) /* same code on AIX */
+ case ENOTEMPTY:
+ return "ENOTEMPTY";
+#endif
+ case ENOTSOCK:
+ return "ENOTSOCK";
+#ifdef ENOTSUP
+ case ENOTSUP:
+ return "ENOTSUP";
+#endif
+ case ENOTTY:
+ return "ENOTTY";
+ case ENXIO:
+ return "ENXIO";
+#if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (EOPNOTSUPP != ENOTSUP))
+ case EOPNOTSUPP:
+ return "EOPNOTSUPP";
+#endif
+#ifdef EOVERFLOW
+ case EOVERFLOW:
+ return "EOVERFLOW";
+#endif
+ case EPERM:
+ return "EPERM";
+ case EPIPE:
+ return "EPIPE";
+ case EPROTONOSUPPORT:
+ return "EPROTONOSUPPORT";
+ case ERANGE:
+ return "ERANGE";
+#ifdef EROFS
+ case EROFS:
+ return "EROFS";
+#endif
+ case ESRCH:
+ return "ESRCH";
+#ifdef ETIMEDOUT
+ case ETIMEDOUT:
+ return "ETIMEDOUT";
+#endif
+#ifdef ETXTBSY
+ case ETXTBSY:
+ return "ETXTBSY";
+#endif
+#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
+ case EWOULDBLOCK:
+ return "EWOULDBLOCK";
+#endif
+ case EXDEV:
+ return "EXDEV";
+ }
+
+ return NULL;
+}
+
+
+#ifdef WIN32
+
+/*
+ * Windows' strerror() doesn't know the Winsock codes, so handle them this way
+ */
+static char *
+win32_socket_strerror(int errnum, char *buf, size_t buflen)
+{
+ static HANDLE handleDLL = INVALID_HANDLE_VALUE;
+
+ if (handleDLL == INVALID_HANDLE_VALUE)
+ {
+ handleDLL = LoadLibraryEx("netmsg.dll", NULL,
+ DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
+ if (handleDLL == NULL)
+ {
+ snprintf(buf, buflen,
+ "winsock error %d (could not load netmsg.dll to translate: error code %lu)",
+ errnum, GetLastError());
+ return buf;
+ }
+ }
+
+ ZeroMemory(buf, buflen);
+ if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_FROM_HMODULE,
+ handleDLL,
+ errnum,
+ MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
+ buf,
+ buflen - 1,
+ NULL) == 0)
+ {
+ /* Failed to get id */
+ snprintf(buf, buflen, "unrecognized winsock error %d", errnum);
+ }
+
+ return buf;
+}
+
+#endif /* WIN32 */