Discussion:
strfmon in the C locale
Bruno Haible
2017-09-23 08:50:07 UTC
Permalink
Test program:
=======================================================================
#include <locale.h>
#include <monetary.h>
#include <stdio.h>
int main ()
{
char buf[80];
printf ("currency = |%s|\n", localeconv()->currency_symbol);
printf ("positive sign = |%s|\n", localeconv()->positive_sign);
printf ("negative sign = |%s|\n", localeconv()->negative_sign);

strfmon (buf, sizeof (buf), "%^#5.0n", 123.4);
printf ("strfmon result for positive value = |%s|\n", buf);

strfmon (buf, sizeof (buf), "%^#5.0n", -123.4);
printf ("strfmon result for negative value = |%s|\n", buf);
}
=======================================================================

Results
=======

Result on glibc 2.23:
currency = ||
positive sign = ||
negative sign = ||
strfmon result for positive value = | 123|
strfmon result for negative value = |- 123|

Result on Solaris 10:
currency = ||
positive sign = ||
negative sign = ||
strfmon result for positive value = | 123|
strfmon result for negative value = |- 123|

Result on Mac OS X 10.12:
currency = ||
positive sign = ||
negative sign = ||
strfmon result for positive value = | 123 |
strfmon result for negative value = |( 123)|


Discussion
==========

The POSIX spec is at [1].

I don't think the Mac OS X 10.12 result is correct because the test
program does not specify the '+' nor '(' flag, and POSIX says

If '+' is specified, the locale's equivalent of '+' and '-' are
used (for example, in many locales, the empty string if positive and '-'
if negative). If '(' is specified, negative amounts are enclosed within
parentheses. If neither flag is specified, the '+' style is used.

I don't think the glibc and Solaris results are correct because they
show a '-' sign, although localeconv()->negative_sign is the empty
string (which is mandated by POSIX [2]).

The Solaris result is worse than the glibc one, because it produces
a different number of characters in the negative and in the positive
case, which is against what POSIX says for the '#' flag:

To ensure alignment, any characters appearing before or after the
number in the formatted output such as currency or sign symbols are
padded as necessary with <space> characters to make their positive
and negative formats an equal length.


The result I would have expected is therefore:
currency = ||
positive sign = ||
negative sign = ||
strfmon result for positive value = | 123|
strfmon result for negative value = | 123|


Conclusion
==========

strfmon in the C locale is unusable. strfmon in territory locales is
probably not much better.

I won't spend time to add workarounds for it, because

* C is a system programming language, while strfmon is for
applications. Applications nowadays are not being written in C
any more (except in GNOME).

* Especially applications that deal with money. Given the amount
of programming mistakes one can do in C and the amount of
security vulnerabilities that C programs have on average
(compared to other programming languages), who would want to
to write monetary applications in C?

Bruno

[1] http://pubs.opengroup.org/onlinepubs/9699919799/functions/strfmon.html
[2] http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap07.html#tag_07_03_03_01
Bruno Haible
2017-09-23 16:01:39 UTC
Permalink
Post by Bruno Haible
currency = ||
positive sign = ||
negative sign = ||
strfmon result for positive value = | 123|
strfmon result for negative value = | 123|
AIX indeed produces this output.

AIX additionally, for the call
strfmon (buf, sizeof (buf), "%.2n", 123.5);
produces the result
"12350"

So, this implementation of strfmon is very standards-compliant.
But without a distinction between positive and negative values,
and without a visible decimal separator, this behaviour is useless,
or even dangerous (if a programmer was not aware of this behaviour
and has not worked around it).

I would vote for removing strfmon() and strfmon_l() from POSIX.

Bruno
Eric Blake
2017-09-25 20:32:36 UTC
Permalink
Post by Bruno Haible
So, this implementation of strfmon is very standards-compliant.
But without a distinction between positive and negative values,
and without a visible decimal separator, this behaviour is useless,
or even dangerous (if a programmer was not aware of this behaviour
and has not worked around it).
I would vote for removing strfmon() and strfmon_l() from POSIX.
Are you going to open up a ticket on
http://austingroupbugs.net/main_page.php with your proposal of
deprecating the interfaces?
--
Eric Blake, Principal Software Engineer
Red Hat, Inc. +1-919-301-3266
Virtualization: qemu.org | libvirt.org
Loading...