0
0
mirror of https://git.zap.org.au/git/trader.git synced 2025-06-30 22:19:26 -04:00

Convert xstrfmon() into a wide-character version xwcsfmon()

Also work around buggy implementations of strfmon() that do not copy
complete multibyte sequences that may be part of a locale's
mon_thousands_sep, thousands_sep, mon_decimal_point or decimal_point.
This commit is contained in:
John Zaitseff 2018-08-24 13:20:51 +10:00
parent 6ec3beab75
commit daab2e94da
3 changed files with 52 additions and 31 deletions

View File

@ -1467,21 +1467,10 @@ int vmkchstr (chtype *restrict chbuf, int chbufsize, chtype attr_norm,
case L'N':
// Insert a monetary amount (double) into the output
{
/* strfmon() is not available in a wide-char
version, so we need a multibyte char buffer */
char *buf = xmalloc(BUFSIZE);
if (xstrfmon(buf, BUFSIZE, spec->flag_nosym ? "%!n" : "%n",
format_arg[spec->arg_num].a.a_double) < 0) {
saved_errno = errno;
free(buf);
errno = saved_errno;
goto error;
}
xmbstowcs(fmtbuf, buf, BUFSIZE);
free(buf);
if (xwcsfmon(fmtbuf, BUFSIZE, spec->flag_nosym ?
"%!n" : "%n",
format_arg[spec->arg_num].a.a_double) < 0) {
goto error;
}
str = fmtbuf;

View File

@ -570,22 +570,26 @@ void init_locale (void)
/***********************************************************************/
// xstrfmon: Convert monetary value to a string
// xwcsfmon: Convert monetary value to a wide-character string
ssize_t xstrfmon (char *restrict buf, size_t maxsize,
ssize_t xwcsfmon (wchar_t *restrict buf, size_t maxsize,
const char *restrict format, double val)
{
/* Current and previous versions of ISO/IEC 9945-1 (POSIX), particularly
ssize_t ret;
char *s = xmalloc(BUFSIZE);
/* Current and previous versions of ISO/IEC 9945-1 (POSIX), namely
SUSv3 (2001) and SUSv4 (2008), require strfmon() to return rather
meaningless strings when used with the POSIX "C" locale. In
particular, the standard POSIX locale does not define a currency
symbol, a monetary radix symbol (decimal point) or a negative
sign. This means strfmon(..., "%n", -123.45) is supposed to
produce "12345" instead of something like "$-123.45"! This
function overcomes these limitations by using snprintf(). */
produce "12345" instead of something like "-$123.45"! The
following code overcomes these limitations by using snprintf(). */
if (! is_posix_locale) {
return strfmon(buf, maxsize, format, val);
ret = strfmon(s, BUFSIZE, format, val);
} else {
/* The current implementation assumes the monetary decimal point
is overridden to "." (ie, MOD_POSIX_MON_DECIMAL_POINT == "."),
@ -609,22 +613,49 @@ ssize_t xstrfmon (char *restrict buf, size_t maxsize,
if (strcmp(format, "%n") == 0) {
if (val >= 0.0) {
return snprintf(buf, maxsize, MOD_POSIX_FMT_POS, val);
ret = snprintf(s, BUFSIZE, MOD_POSIX_FMT_POS, val);
} else {
return snprintf(buf, maxsize, MOD_POSIX_FMT_NEG, -val);
ret = snprintf(s, BUFSIZE, MOD_POSIX_FMT_NEG, -val);
}
} else if (strcmp(format, "%!n") == 0) {
if (val >= 0.0) {
return snprintf(buf, maxsize, MOD_POSIX_FMT_POS_NOSYM, val);
ret = snprintf(s, BUFSIZE, MOD_POSIX_FMT_POS_NOSYM, val);
} else {
return snprintf(buf, maxsize, MOD_POSIX_FMT_NEG_NOSYM, -val);
ret = snprintf(s, BUFSIZE, MOD_POSIX_FMT_NEG_NOSYM, -val);
}
} else {
// Other strfmon() formats are not supported
errno = EINVAL;
return -1;
ret = -1;
}
}
if (ret >= BUFSIZE) {
// Truncate the too-long output with a terminating NUL
s[BUFSIZE - 1] = '\0';
}
if (ret >= 0) {
xmbstowcs(buf, s, maxsize);
/* Some buggy implementations of strfmon(), such as that on
FreeBSD and Cygwin, assume localeconv.mon_thousands_sep and
similar strings contain either a single char or NUL instead of
a multibyte character string. However, this assumption fails
on locales such as ru_RU.UTF-8 which use U+00A0 NO-BREAK SPACE
for mon_thousands_sep (stored in UTF-8 as 0xC2 0xA0. As a
result, incomplete character sequences are copied, which are
translated to EILSEQ_REPL characters by xmbstowcs() above.
Fix such characters by replacing them with a space. */
for (wchar_t *p = buf; *p != L'\0'; p++) {
if (*p == EILSEQ_REPL) {
*p = L' ';
}
}
}
free(s);
return ret;
}

View File

@ -239,18 +239,19 @@ extern void init_locale (void);
/*
Function: xstrfmon - Convert monetary value to a string
Function: xwcsfmon - Convert monetary value to a wide-character string
Parameters: buf - Buffer to receive result
maxsize - Maximum size of buffer
maxsize - Maximum size of buffer, in multiples of wchar_t
format - strfmon() format to use
val - Monetary value to convert
Returns: ssize_t - Size of returned string
This function calls strfmon() to convert val to a suitable monetary
value string, making appropriate adjustments if the POSIX locale is in
effect.
value string, then converts the result to a wide-character string and
places it in buf. It makes appropriate adjustments to the output if
the POSIX locale is in effect or if the locale uses no-break spaces.
*/
extern ssize_t xstrfmon (char *restrict buf, size_t maxsize,
extern ssize_t xwcsfmon (wchar_t *restrict buf, size_t maxsize,
const char *restrict format, double val);