strutil: Locale independent sprintf() and vsprintf() functions

This commit is contained in:
Frank Stettner 2018-02-11 15:59:26 +01:00 committed by Uwe Hermann
parent 49a468ed8b
commit 21ef355e50
2 changed files with 164 additions and 2 deletions

View File

@ -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,

View File

@ -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;