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:
parent
6ec3beab75
commit
daab2e94da
19
src/intf.c
19
src/intf.c
@ -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;
|
||||
|
53
src/utils.c
53
src/utils.c
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
11
src/utils.h
11
src/utils.h
@ -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);
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user