aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog7
-rw-r--r--NEWS7
-rw-r--r--configure.ac3
-rw-r--r--src/gpg-error.h8
-rw-r--r--src/strerror.c208
5 files changed, 231 insertions, 2 deletions
diff --git a/ChangeLog b/ChangeLog
index 580f473..57103fd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2003-09-13 Marcus Brinkmann <[email protected]>
+
+ * configure.ac: Invoke AC_FUNC_STRERROR_R.
+ * src/gpg-error.h (gpg_strerror_r): New prototype.
+ * src/strerror.c (system_strerror_r): New function.
+ (gpg_strerror_r): New function.
+
2003-09-04 Marcus Brinkmann <[email protected]>
* libgpg-error.spec.in: New file.
diff --git a/NEWS b/NEWS
index 2df8f97..9e6f989 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,13 @@
Noteworthy changes in version 0.5 (unreleased)
----------------------------------------------
+ * New thread safe interface gpg_strerror_r.
+
+ * Interface changes relative to the 0.4 release:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+gpg_strerror_r NEW
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
Noteworthy changes in version 0.4 (2003-09-03)
----------------------------------------------
diff --git a/configure.ac b/configure.ac
index d5872bf..9405902 100644
--- a/configure.ac
+++ b/configure.ac
@@ -51,6 +51,9 @@ AM_GNU_GETTEXT_VERSION(0.11.5)
# Checks for header files.
AC_HEADER_STDC
AC_CHECK_HEADERS([stdlib.h])
+AC_FUNC_STRERROR_R
+AC_CHECK_FUNC([strerror_r], [],
+ AC_MSG_WARN([[Without strerror_r, gpg_strerror_r might not be thread-safe]]))
# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
diff --git a/src/gpg-error.h b/src/gpg-error.h
index 96eaf9a..08e20f6 100644
--- a/src/gpg-error.h
+++ b/src/gpg-error.h
@@ -492,10 +492,16 @@ gpg_err_source (gpg_error_t err)
/* String functions. */
/* Return a pointer to a string containing a description of the error
- code in the error value ERR. */
+ code in the error value ERR. This function is not thread-safe. */
const char *gpg_strerror (gpg_error_t err);
/* Return a pointer to a string containing a description of the error
+ code in the error value ERR. The buffer for the string is
+ allocated with malloc(), and has to be released by the user. On
+ error, NULL is returned. */
+char *gpg_strerror_r (gpg_error_t err);
+
+/* Return a pointer to a string containing a description of the error
source in the error value ERR. */
const char *gpg_strsource (gpg_error_t err);
diff --git a/src/strerror.c b/src/strerror.c
index 0498b48..a8a5a63 100644
--- a/src/strerror.c
+++ b/src/strerror.c
@@ -22,6 +22,7 @@
#include <config.h>
#endif
+#include <stdlib.h>
#include <string.h>
#include <gpg-error.h>
@@ -30,7 +31,7 @@
#include "err-codes.h"
/* Return a pointer to a string containing a description of the error
- code in the error value ERR. */
+ code in the error value ERR. This function is not thread-safe. */
const char *
gpg_strerror (gpg_error_t err)
{
@@ -46,3 +47,208 @@ gpg_strerror (gpg_error_t err)
}
return dgettext (PACKAGE, msgstr + msgidx[msgidxof (code)]);
}
+
+
+#ifdef HAVE_STRERROR_R
+#ifdef STRERROR_R_CHAR_P
+/* The GNU C library and probably some other systems have this weird
+ variant of strerror_r. */
+
+/* Return a dynamically allocated string in *STR describing the system
+ error NO. If this call succeeds, return 1. If this call fails due
+ to a resource shortage, set *STR to NULL and return 1. If this
+ call fails because the error number is not valid, don't set *STR
+ and return 0. */
+int
+system_strerror_r (int no, char **str)
+{
+ int err;
+ char *buffer;
+ size_t buffer_len = 128;
+
+ buffer = malloc (buffer_len);
+ if (!buffer)
+ {
+ *str = NULL;
+ return 1;
+ }
+
+ do
+ {
+ char *msg = strerror_r (no, buffer, buffer_len);
+
+ if (!msg)
+ {
+ /* Possibly this means that the error code is unknown. */
+ free (buffer);
+ return 0;
+ }
+ else if (msg != buffer)
+ {
+ free (buffer);
+ *str = strdup (msg);
+ return 1;
+ }
+
+ buffer[buffer_len - 1] = '\0';
+ if (strlen (buffer) == buffer_len - 1)
+ {
+ /* We might need more space. */
+ size_t new_buffer_len = buffer_len * 2;
+ char *new_buffer;
+
+ if (new_buffer_len < buffer_len)
+ {
+ /* Overflow. Now, this is serious. */
+ free (buffer);
+ *str = NULL;
+ return 1;
+ }
+
+ new_buffer = realloc (buffer, 2 * buffer_len);
+ if (!new_buffer)
+ {
+ free (buffer);
+ *str = NULL;
+ return 1;
+ }
+ buffer = new_buffer;
+ buffer_len = new_buffer_len;
+ }
+ else
+ {
+ *str = buffer;
+ return 1;
+ }
+ }
+ while (1);
+}
+
+#else /* STRERROR_R_CHAR_P */
+/* Now the POSIX version. */
+
+/* Return a dynamically allocated string in *STR describing the system
+ error NO. If this call succeeds, return 1. If this call fails due
+ to a resource shortage, set *STR to NULL and return 1. If this
+ call fails because the error number is not valid, don't set *STR
+ and return 0. */
+int
+system_strerror_r (int no, char **str)
+{
+ int err;
+ char *buffer;
+ size_t buffer_len = 128;
+
+ buffer = malloc (buffer_len);
+ if (!buffer)
+ {
+ *str = NULL;
+ return 1;
+ }
+
+ do
+ {
+ err = strerror_r (no, buffer, buffer_len);
+
+ if (err == ERANGE)
+ {
+ size_t new_buffer_len = buffer_len * 2;
+ char *new_buffer;
+
+ if (new_buffer_len < buffer_len)
+ {
+ /* Overflow. Now, this is serious. */
+ free (buffer);
+ *str = NULL;
+ return 1;
+ }
+
+ new_buffer = realloc (buffer, 2 * buffer_len);
+ if (!new_buffer)
+ {
+ free (buffer);
+ *str = NULL;
+ return 1;
+ }
+ buffer = new_buffer;
+ buffer_len = new_buffer_len;
+ }
+ }
+ while (err == ERANGE);
+
+ if (err == EINVAL)
+ {
+ /* This system error is not known. */
+ free (buffer);
+ return 0;
+ }
+ else if (err)
+ {
+ /* strerror_r() failed, but we don't know why. */
+ free (buffer);
+ *str = NULL;
+ return 1;
+ }
+
+ *str = buffer;
+ return 1;
+}
+#endif /* STRERROR_R_CHAR_P */
+
+#else /* HAVE_STRERROR_H */
+/* Without strerror_r(), we can still provide a non-thread-safe
+ version. Maybe we are even lucky and the system's strerror() is
+ already thread-safe. */
+
+/* Return a dynamically allocated string in *STR describing the system
+ error NO. If this call succeeds, return 1. If this call fails due
+ to a resource shortage, set *STR to NULL and return 1. If this
+ call fails because the error number is not valid, don't set *STR
+ and return 0. */
+int
+system_strerror_r (int no, char **str)
+{
+ char *msg = strerror (no);
+
+ if (!msg)
+ {
+ if (errno == EINVAL)
+ return 0;
+ else
+ {
+ *str = NULL;
+ return 1;
+ }
+ }
+ else
+ {
+ *str = strdup (msg);
+ return 1;
+ }
+}
+#endif
+
+
+/* Return a pointer to a string containing a description of the error
+ code in the error value ERR. The buffer for the string is
+ allocated with malloc(), and has to be released by the user. On
+ error, NULL is returned. */
+char *
+gpg_strerror_r (gpg_error_t err)
+{
+ gpg_err_code_t code = gpg_err_code (err);
+
+ if (code & GPG_ERR_SYSTEM_ERROR)
+ {
+ int no = gpg_err_code_to_errno (code);
+ if (no)
+ {
+ char *str;
+
+ if (system_strerror_r (no, &str))
+ return str;
+ }
+ code = GPG_ERR_UNKNOWN_ERRNO;
+ }
+ return strdup (dgettext (PACKAGE, msgstr + msgidx[msgidxof (code)]));
+}