This patch adds the much requested libusb_strerror() function, taking into account all issues people raised wrt previous attempts.
Criteria / Decisions underlying this implementation: -Must support translated messages -Must not use gettext as that does not work well in combination with Windows (when building with Visual C, or for Windows CE) -API compatible with FreeBSD and various patched libusb-s floating around -KISS: -Do not add any (other) library dependencies -Do not try to deal with message encodings (iconv), simply always return UTF-8 making encoding the problem of the application using libusb_strerror. Apps which don't want to deal with encoding can opt-out with a simple: libusb_set_strerror_locale("en") call, after which the messages returned by libusb_strerror are guaranteed to be pure ASCII. This patch includes a Dutch tranlation for the messages to show how further languages can be added in a simple manner. Signed-off-by: Hans de Goede <hdego...@redhat.com> --- libusb/Makefile.am | 5 +- libusb/libusb.h | 11 ++- libusb/strerror.c | 250 ++++++++++++++++++++++++++++++++++++++++++++++++++ libusb/version_nano.h | 2 +- 4 files changed, 261 insertions(+), 7 deletions(-) create mode 100644 libusb/strerror.c diff --git a/libusb/Makefile.am b/libusb/Makefile.am index cd6db9c..7f9c1f9 100644 --- a/libusb/Makefile.am +++ b/libusb/Makefile.am @@ -59,9 +59,10 @@ endif libusb_1_0_la_CFLAGS = $(AM_CFLAGS) libusb_1_0_la_LDFLAGS = $(LTLDFLAGS) -libusb_1_0_la_SOURCES = libusbi.h core.c descriptor.c io.c sync.c $(OS_SRC) \ +libusb_1_0_la_SOURCES = libusbi.h core.c descriptor.c io.c strerror.c sync.c \ os/linux_usbfs.h os/darwin_usb.h os/windows_usb.h os/windows_common.h \ - hotplug.h hotplug.c $(THREADS_SRC) os/poll_posix.h os/poll_windows.h + hotplug.h hotplug.c $(THREADS_SRC) $(OS_SRC) \ + os/poll_posix.h os/poll_windows.h hdrdir = $(includedir)/libusb-1.0 hdr_HEADERS = libusb.h diff --git a/libusb/libusb.h b/libusb/libusb.h index 7b68917..c768551 100644 --- a/libusb/libusb.h +++ b/libusb/libusb.h @@ -1032,8 +1032,9 @@ enum libusb_bos_type { /** \ingroup misc * Error codes. Most libusbx functions return 0 on success or one of these * codes on failure. - * You can call \ref libusb_error_name() to retrieve a string representation - * of an error code. + * You can call libusb_error_name() to retrieve a string representation of an + * error code or libusb_strerror() to get an end-user suitable description of + * an error code. */ enum libusb_error { /** Success (no error) */ @@ -1075,8 +1076,8 @@ enum libusb_error { /** Operation not supported or unimplemented on this platform */ LIBUSB_ERROR_NOT_SUPPORTED = -12, - /* NB! Remember to update libusb_error_name() - when adding new error codes here. */ + /* NB! Remember to update libusb_error_name() and + libusb_strerror() when adding new error codes here. */ /** Other error */ LIBUSB_ERROR_OTHER = -99, @@ -1287,6 +1288,8 @@ void LIBUSB_CALL libusb_set_debug(libusb_context *ctx, int level); const struct libusb_version * LIBUSB_CALL libusb_get_version(void); int LIBUSB_CALL libusb_has_capability(uint32_t capability); const char * LIBUSB_CALL libusb_error_name(int errcode); +int LIBUSB_CALL libusb_set_strerror_locale(const char *locale); +const char * LIBUSB_CALL libusb_strerror(enum libusb_error errcode); ssize_t LIBUSB_CALL libusb_get_device_list(libusb_context *ctx, libusb_device ***list); diff --git a/libusb/strerror.c b/libusb/strerror.c new file mode 100644 index 0000000..15c748b --- /dev/null +++ b/libusb/strerror.c @@ -0,0 +1,250 @@ +/* + * libusb strerror code + * Copyright © 2013 Hans de Goede <hdego...@redhat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include "config.h" + +#include <locale.h> +#include <stdlib.h> +#include <string.h> + +#include "libusb.h" +#include "libusbi.h" + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +enum strerror_locale_enum { + LANG_EN, + LANG_NL, +}; + +struct locale_str_to_locale_enum { + const char *locale_str; + enum strerror_locale_enum locale_enum; +}; +#endif + +/* Windows has a gazillion ways to set the same language, see: + * http://msdn.microsoft.com/en-us/library/39cwe7zf%28v=vs.80%29.aspx */ +static struct locale_str_to_locale_enum locale_to_enum[] = { + { "C", LANG_EN }, + { "POSIX", LANG_EN }, + { "en", LANG_EN }, + { "English", LANG_EN }, + { "english", LANG_EN }, + { "australian", LANG_EN }, + { "ena", LANG_EN }, + { "english-aus", LANG_EN }, + { "canadian", LANG_EN }, + { "enc", LANG_EN }, + { "english-can", LANG_EN }, + { "english-nz", LANG_EN }, + { "enz", LANG_EN }, + { "eng", LANG_EN }, + { "english-uk", LANG_EN }, + { "uk", LANG_EN }, + { "american", LANG_EN }, + { "american english", LANG_EN }, + { "american-english", LANG_EN }, + { "english-american", LANG_EN }, + { "english-us", LANG_EN }, + { "english-usa", LANG_EN }, + { "enu", LANG_EN }, + { "us", LANG_EN }, + { "usa", LANG_EN }, + { "nl", LANG_NL }, + { "Dutch", LANG_NL }, + { "dutch", LANG_NL }, + { "nld", LANG_NL }, + { "belgian", LANG_NL }, + { "dutch-belgian", LANG_NL }, + { "nlb", LANG_NL }, + { NULL } +}; + +static usbi_mutex_static_t strerror_locale_lock = USBI_MUTEX_INITIALIZER; +static int strerror_locale = -1; + +static int libusb_set_strerror_locale_unlocked(const char *locale) +{ + char *lang; + int i, ret = LIBUSB_SUCCESS; + + lang = malloc(strlen(locale) + 1); /* no strdup under windows */ + if (!lang) + return LIBUSB_ERROR_NO_MEM; + + strcpy(lang, locale); + for (i = 0; lang[i] && lang[i] != '_' && lang[i] != '.'; i++) {} + lang[i] = 0; + + for (i = 0; locale_to_enum[i].locale_str; i++) { + if (strcmp(lang, locale_to_enum[i].locale_str) == 0) + break; + } + + if (locale_to_enum[i].locale_str) { + strerror_locale = locale_to_enum[i].locale_enum; + } else { + usbi_warn(NULL, "Unsupported language: %s", lang); + strerror_locale = LANG_EN; + ret = LIBUSB_ERROR_NOT_FOUND; + } + + free(lang); + return ret; +} + +/** \ingroup misc + * Set the language, and only the language, not the encoding! used for + * libusb_strerror messages. This takes a locale string in the default + * setlocale format. Both Windows and Posix locale strings are excepted, ie + * "English" or "en_US.utf8". Note the optional codeset part is ignored, + * as libusb_strerror always returns UTF-8 strings! + * + * \param locale locale-string in the form of lang[_country_region][.codeset] + * \returns 0 on success + * \returns LIBUSB_ERROR_NOT_FOUND if the requested language is not supported + * \returns a LIBUSB_ERROR code on other errors + */ +int libusb_set_strerror_locale(const char *locale) +{ + int ret; + + usbi_mutex_static_lock(&strerror_locale_lock); + ret = libusb_set_strerror_locale_unlocked(locale); + usbi_mutex_static_unlock(&strerror_locale_lock); + + return ret; +} + +static const char *libusb_strerror_en(enum libusb_error errcode) +{ + switch (errcode) { + case LIBUSB_SUCCESS: + return "Success"; + case LIBUSB_ERROR_IO: + return "Input/output error"; + case LIBUSB_ERROR_INVALID_PARAM: + return "Invalid parameter"; + case LIBUSB_ERROR_ACCESS: + return "Access denied (insufficient permissions)"; + case LIBUSB_ERROR_NO_DEVICE: + return "No such device (it may have been disconnected)"; + case LIBUSB_ERROR_NOT_FOUND: + return "Entity not found"; + case LIBUSB_ERROR_BUSY: + return "Resource busy"; + case LIBUSB_ERROR_TIMEOUT: + return "Operation timed out"; + case LIBUSB_ERROR_OVERFLOW: + return "Overflow"; + case LIBUSB_ERROR_PIPE: + return "Pipe error"; + case LIBUSB_ERROR_INTERRUPTED: + return "System call interrupted (perhaps due to signal)"; + case LIBUSB_ERROR_NO_MEM: + return "Insufficient memory"; + case LIBUSB_ERROR_NOT_SUPPORTED: + return "Operation not supported or unimplemented on this platform"; + case LIBUSB_ERROR_OTHER: + return "Other error"; + default: + return "Unknown error"; + } +} + +static const char *libusb_strerror_nl(enum libusb_error errcode) +{ + switch (errcode) { + case LIBUSB_SUCCESS: + return "Gelukt"; + case LIBUSB_ERROR_IO: + return "Invoer-/uitvoerfout"; + case LIBUSB_ERROR_INVALID_PARAM: + return "Ongeldig argument"; + case LIBUSB_ERROR_ACCESS: + return "Toegang geweigerd (onvoldoende toegangsrechten)"; + case LIBUSB_ERROR_NO_DEVICE: + return "Apparaat bestaat niet (verbinding met apparaat verbroken?)"; + case LIBUSB_ERROR_NOT_FOUND: + return "Niet gevonden"; + case LIBUSB_ERROR_BUSY: + return "Apparaat of hulpbron is bezig"; + case LIBUSB_ERROR_TIMEOUT: + return "Bewerking verlopen"; + case LIBUSB_ERROR_OVERFLOW: + return "Waarde is te groot"; + case LIBUSB_ERROR_PIPE: + return "Gebroken pijp"; + case LIBUSB_ERROR_INTERRUPTED: + return "Onderbroken systeemaanroep"; + case LIBUSB_ERROR_NO_MEM: + return "Onvoldoende geheugen beschikbaar"; + case LIBUSB_ERROR_NOT_SUPPORTED: + return "Bewerking wordt niet ondersteund"; + case LIBUSB_ERROR_OTHER: + return "Andere fout"; + default: + return NULL; + } +} + +/** \ingroup misc + * Returns a constant string with a short description of the given error code, + * this description is intended for displaying to the end user and will be in + * the language set by libusb_set_strerror_locale(). + * + * If libusb_set_strerror_locale() has not been called before calling + * libusb_strerror(), libusb_strerror() will call + * libusb_set_strerror_locale(setlocale(LC_MESSAGES, NULL)), to set it to + * the current locale. + * + * The returned string is encoded in UTF-8, except for English messages, which + * are guaranteed to be limited to ASCII. So if your app cannot handle UTF-8, + * it should call libusb_set_strerror_locale("English") before using + * libusb_strerror(). + * + * The messages always start with a capital letter and ends without any dot. + * The caller must not free() the returned string. + * + * \param errcode the error code whose description is desired + * \returns a short description of the error code in UTF-8 encoding + */ +DEFAULT_VISIBILITY const char* libusb_strerror(enum libusb_error errcode) +{ + const char *msg = NULL; + + usbi_mutex_static_lock(&strerror_locale_lock); + if (strerror_locale == -1) + libusb_set_strerror_locale_unlocked( + setlocale(LC_MESSAGES, NULL)); + switch (strerror_locale) { + case LANG_EN: + msg = libusb_strerror_en(errcode); + break; + case LANG_NL: + msg = libusb_strerror_nl(errcode); + break; + } + usbi_mutex_static_unlock(&strerror_locale_lock); + + if (!msg) { + /* No translated msg for this errcode, fall back to English */ + msg = libusb_strerror_en(errcode); + } + return msg; +} diff --git a/libusb/version_nano.h b/libusb/version_nano.h index 2804c7d..520a082 100644 --- a/libusb/version_nano.h +++ b/libusb/version_nano.h @@ -1 +1 @@ -#define LIBUSB_NANO 10728 +#define LIBUSB_NANO 10729 -- 1.8.2.1 ------------------------------------------------------------------------------ Try New Relic Now & We'll Send You this Cool Shirt New Relic is the only SaaS-based application performance monitoring service that delivers powerful full stack analytics. Optimize and monitor your browser, app, & servers with just a few lines of code. Try New Relic and get this awesome Nerd Life shirt! http://p.sf.net/sfu/newrelic_d2d_may _______________________________________________ libusbx-devel mailing list libusbx-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/libusbx-devel