Hello, 2013/5/28 Hans de Goede <hdego...@redhat.com>: > 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.
In my case I will use libusb_strerror() to get a human readable message in the log trace. Since the log is for me (and not for the end user) I do not want to get a message in Dutch so I will call libusb_set_strerror_locale("en") libusb_set_strerror_locale("en") is not only used to avoid UTF-8 problems. I have 2 comments bellow: > 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 excepted -> accepted. > + * "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; The English version returns "Unknown error" instead of NULL. I would not like to get a NULL pointer instead of a valid C-string. I now understand why after reading the code of libusb_strerror(). Maybe "Unknown error" should also be translated. No? Or maybe add a comment like /* fall back to English translation */ before the 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 -- Dr. Ludovic Rousseau ------------------------------------------------------------------------------ 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