From: Frank Stettner Date: Sun, 11 Feb 2018 14:59:26 +0000 (+0100) Subject: strutil: Locale independent sprintf() and vsprintf() functions X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=21ef355e503278a3d2a215c87b0d48112276330a;p=libsigrok.git strutil: Locale independent sprintf() and vsprintf() functions --- diff --git a/include/libsigrok/proto.h b/include/libsigrok/proto.h index 2017f712..3690ca0b 100644 --- a/include/libsigrok/proto.h +++ b/include/libsigrok/proto.h @@ -248,6 +248,8 @@ SR_API uint64_t sr_parse_timestring(const char *timestring); SR_API gboolean sr_parse_boolstring(const char *boolstring); SR_API int sr_parse_period(const char *periodstr, uint64_t *p, uint64_t *q); SR_API int sr_parse_voltage(const char *voltstr, uint64_t *p, uint64_t *q); +SR_API int sr_sprintf_ascii(char *buf, const char *format, ...); +SR_API int sr_vsprintf_ascii(char *buf, const char *format, va_list args); SR_API int sr_snprintf_ascii(char *buf, size_t buf_size, const char *format, ...); SR_API int sr_vsnprintf_ascii(char *buf, size_t buf_size, diff --git a/src/strutil.c b/src/strutil.c index 1b573028..5f48e87b 100644 --- a/src/strutil.c +++ b/src/strutil.c @@ -259,6 +259,165 @@ SR_PRIV int sr_atof_ascii(const char *str, float *ret) return SR_OK; } +/** + * Compose a string with a format string in the buffer pointed to by buf. + * + * It is up to the caller to ensure that the allocated buffer is large enough + * to hold the formatted result. + * + * A terminating NUL character is automatically appended after the content + * written. + * + * After the format parameter, the function expects at least as many additional + * arguments as needed for format. + * + * This version ignores the current locale and uses the locale "C" for Linux, + * FreeBSD, OSX and Android. + * + * @param buf Pointer to a buffer where the resulting C string is stored. + * @param format C string that contains a format string (see printf). + * @param ... A sequence of additional arguments, each containing a value to be + * used to replace a format specifier in the format string. + * + * @return On success, the number of characters that would have been written, + * not counting the terminating NUL character. + * + * @since 0.6.0 + */ +SR_API int sr_sprintf_ascii(char *buf, const char *format, ...) +{ + int ret; + va_list args; + + va_start(args, format); + ret = sr_vsprintf_ascii(buf, format, args); + va_end(args); + + return ret; +} + +/** + * Compose a string with a format string in the buffer pointed to by buf. + * + * It is up to the caller to ensure that the allocated buffer is large enough + * to hold the formatted result. + * + * Internally, the function retrieves arguments from the list identified by + * args as if va_arg was used on it, and thus the state of args is likely to + * be altered by the call. + * + * In any case, args should have been initialized by va_start at some point + * before the call, and it is expected to be released by va_end at some point + * after the call. + * + * This version ignores the current locale and uses the locale "C" for Linux, + * FreeBSD, OSX and Android. + * + * @param buf Pointer to a buffer where the resulting C string is stored. + * @param format C string that contains a format string (see printf). + * @param args A value identifying a variable arguments list initialized with + * va_start. + * + * @return On success, the number of characters that would have been written, + * not counting the terminating NUL character. + * + * @since 0.6.0 + */ +SR_API int sr_vsprintf_ascii(char *buf, const char *format, va_list args) +{ +#if defined(_WIN32) + int ret; + +#if 0 + /* + * TODO: This part compiles with mingw-w64 but doesn't run with Win7. + * Doesn't start because of "Procedure entry point _create_locale + * not found in msvcrt.dll". + * mingw-w64 should link to msvcr100.dll not msvcrt.dll! + * See: https://msdn.microsoft.com/en-us/en-en/library/1kt27hek.aspx + */ + _locale_t locale; + + locale = _create_locale(LC_NUMERIC, "C"); + ret = _vsprintf_l(buf, format, locale, args); + _free_locale(locale); +#endif + + /* vsprintf() uses the current locale, may not work correctly for floats. */ + ret = vsprintf(buf, format, args); + + return ret; +#elif defined(__APPLE__) + /* + * See: + * https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/printf_l.3.html + * https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man3/xlocale.3.html + */ + int ret; + locale_t locale; + + locale = newlocale(LC_NUMERIC_MASK, "C", NULL); + ret = vsprintf_l(buf, locale, format, args); + freelocale(locale); + + return ret; +#elif defined(__FreeBSD__) && __FreeBSD_version >= 901000 + /* + * See: + * https://www.freebsd.org/cgi/man.cgi?query=printf_l&apropos=0&sektion=3&manpath=FreeBSD+9.1-RELEASE + * https://www.freebsd.org/cgi/man.cgi?query=xlocale&apropos=0&sektion=3&manpath=FreeBSD+9.1-RELEASE + */ + int ret; + locale_t locale; + + locale = newlocale(LC_NUMERIC_MASK, "C", NULL); + ret = vsprintf_l(buf, locale, format, args); + freelocale(locale); + + return ret; +#elif defined(__ANDROID__) + /* + * The Bionic libc only has two locales ("C" aka "POSIX" and "C.UTF-8" + * aka "en_US.UTF-8"). The decimal point is hard coded as "." + * See: https://android.googlesource.com/platform/bionic/+/master/libc/bionic/locale.cpp + */ + int ret; + + ret = vsprintf(buf, format, args); + + return ret; +#elif defined(__linux__) + int ret; + locale_t old_locale, temp_locale; + + /* Switch to C locale for proper float/double conversion. */ + temp_locale = newlocale(LC_NUMERIC, "C", NULL); + old_locale = uselocale(temp_locale); + + ret = vsprintf(buf, format, args); + + /* Switch back to original locale. */ + uselocale(old_locale); + freelocale(temp_locale); + + return ret; +#elif defined(__unix__) || defined(__unix) + /* + * This is a fallback for all other BSDs, *nix and FreeBSD <= 9.0, by + * using the current locale for snprintf(). This may not work correctly + * for floats! + */ + int ret; + + ret = vsprintf(buf, format, args); + + return ret; +#else + /* No implementation for unknown systems! */ + return -1; +#endif +} + /** * Composes a string with a format string (like printf) in the buffer pointed * by buf (taking buf_size as the maximum buffer capacity to fill). @@ -270,7 +429,7 @@ SR_PRIV int sr_atof_ascii(const char *str, float *ret) * After the format parameter, the function expects at least as many additional * arguments as needed for format. * - * This version ignores the actual locale and uses the locale "C" for Linux, + * This version ignores the current locale and uses the locale "C" for Linux, * FreeBSD, OSX and Android. * * @param buf Pointer to a buffer where the resulting C string is stored. @@ -317,7 +476,7 @@ SR_API int sr_snprintf_ascii(char *buf, size_t buf_size, * before the call, and it is expected to be released by va_end at some point * after the call. * - * This version ignores the actual locale and uses the locale "C" for Linux, + * This version ignores the current locale and uses the locale "C" for Linux, * FreeBSD, OSX and Android. * * @param buf Pointer to a buffer where the resulting C string is stored. @@ -348,6 +507,7 @@ SR_API int sr_vsnprintf_ascii(char *buf, size_t buf_size, * Doesn't start because of "Procedure entry point _create_locale * not found in msvcrt.dll". * mingw-w64 should link to msvcr100.dll not msvcrt.dll!. + * See: https://msdn.microsoft.com/en-us/en-en/library/1kt27hek.aspx */ _locale_t locale;