diff options
-rw-r--r-- | ChangeLog | 7 | ||||
-rw-r--r-- | NEWS | 7 | ||||
-rw-r--r-- | configure.ac | 3 | ||||
-rw-r--r-- | src/gpg-error.h | 8 | ||||
-rw-r--r-- | src/strerror.c | 208 |
5 files changed, 231 insertions, 2 deletions
@@ -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. @@ -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)])); +} |