Fl_Preferences issues

14 views
Skip to first unread message

Ian MacArthur

unread,
Sep 23, 2020, 3:42:12 PM9/23/20
to coredev fltk
All,

In an off-list discussion elsewhere, a potential issue was flagged with Fl_Preferences interacting oddly with locale settings, specifically with the handling of the decimal separator.
In this case a FI locale was used but ISTR that uses the same decimal separator as, say, DE uses, so I imagine that several of the fltk regulars would have seen this (like, maybe, Matt who wrote Fl_preferences, for example...)

Anyway, I’ll post parts of the original below for the details, though I recall that we had some discussion about locale settings recently but can’t recall the outcome.

I wondered if we have seen this before and what the thinking is on non-C-locale settings etc...?

Cheers,

>
> I'm pretty sure one of my fldigi users (http://www.w1hkj.com/files/fldigi/) has found a bug in Fl_Preferences.
>
> Fl_Preferences::get, fails if the system locale does not use a period as the decimal separator character. The problem was discovered by a user in the FI_fi locale.
>
> Symptom:
>
> method set correctly writes the double value with the locale decimal separator
> method get fails.
>
> My code does not execute a setenv(LC_NUMERIC,..., but relies on that environment variable set in the user's system (Fedora F32). I'm not sure why the set works and the get does not.
>
>

> /**
> Reads an entry from the group. A default value must be
> supplied. The return value indicates if the value was available
> (non-zero) or the default was used (0).
>
> \param[in] key name of entry
> \param[out] value returned from preferences or default value if none was set
> \param[in] defaultValue default value to be used if no preference was set
> \return 0 if the default value was used
> */
> char Fl_Preferences::get( const char *key, double &value, double defaultValue ) {
> const char *v = node->get( key );
> value = v ? atof( v ) : defaultValue;
> return ( v != 0 );
> }
>
> /**
> Sets an entry (name/value pair). The return value indicates if there
> was a problem storing the data in memory. However it does not
> reflect if the value was actually stored in the preferences
> file.
>
> \param[in] key name of entry
> \param[in] value set this entry to \p value
> \return 0 if setting the value failed
> */
> char Fl_Preferences::set( const char *key, double value ) {
> sprintf( nameBuffer, "%g", value );
> node->set( key, nameBuffer );
> return 1;
> }
>
>

Albrecht Schlosser

unread,
Sep 23, 2020, 10:14:59 PM9/23/20
to fltkc...@googlegroups.com
On 9/23/20 9:42 PM Ian MacArthur wrote:
> All,
>
> In an off-list discussion elsewhere, a potential issue was flagged with Fl_Preferences interacting oddly with locale settings, specifically with the handling of the decimal separator.
> In this case a FI locale was used but ISTR that uses the same decimal separator as, say, DE uses, so I imagine that several of the fltk regulars would have seen this (like, maybe, Matt who wrote Fl_preferences, for example...)
>
> Anyway, I’ll post parts of the original below for the details, though I recall that we had some discussion about locale settings recently but can’t recall the outcome.

Maybe regarding localized error messages? See below.

> I wondered if we have seen this before and what the thinking is on non-C-locale settings etc...?

Hmm, I just did a small test with test/preferences and I *believe* it
works as /expected/. The questions is: what are the expectations...
(again, see more below).

>> I'm pretty sure one of my fldigi users (http://www.w1hkj.com/files/fldigi/) has found a bug in Fl_Preferences.
>>
>> Fl_Preferences::get, fails if the system locale does not use a period as the decimal separator character. The problem was discovered by a user in the FI_fi locale.
>>
>> Symptom:
>>
>> method set correctly writes the double value with the locale decimal separator
>> method get fails.

I tried this with de_DE locale in test/preferences that includes two
float values. The unmodified program works as if it uses the C locale
(and in fact it does). Hence all float values are written with '.' as
decimal separator and read back correctly.

Then I modified test/preferences.cxx (I edited the .cxx file for
simplicity) to include:

#include <locale.h>

and as the first line in main():

setlocale(LC_ALL, ""); // enable multilanguage behavior

With these modifications it works correctly writing float values with
decimal separator ',' and reads them back correctly.

This is what I expected.

>> My code does not execute a setenv(LC_NUMERIC,..., but relies on that environment variable set in the user's system (Fedora F32). I'm not sure why the set works and the get does not.

I don't know either. To know that we'd need to see more code.

The point is, however, that the locale environment variables are only
used if setlocale(LC_*, ""); is called, i.e. with an empty string as the
second argument (see `man setlocale`). The man page says among other things:

>>>
On startup of the main program, the portable "C" locale is selected as
default. A program may be made portable to all locales by calling:

setlocale(LC_ALL, "");

after program initialization, ...
<<<

So it's maybe a misunderstanding of the original error reporter how the
locale stuff works. It took me some time to find that out myself when we
tried to return localized error messages recently but I'm still not an
expert.

So I'd suggest the error reporter (author of fldigi?) to add the
setlocale(LC_ALL, ""); call right at the beginning of main() and see how
that works. A good and simple test is test/preferences(.fl/.cxx) which
can be simplified for a local test if required.

Calling setenv(LC_*,...) inside the program would be wrong anyway unless
setlocale(...) would be called *after* that. The right thing to do would
be to call setlocale(LC_NUMERIC, ...) *if* the program needed to use a
specific locale and *not* the user's locale (after the initialization
with `setlocale(LC_ALL, "");`.

So *if* the author of the program decided to use the user's locale
settings for everything except the numeric locale (and to use "C" for
the latter) to always use the decimal separator "." in preferences they
could use (see fluid/fluid.c in 1.4):


setlocale(LC_ALL, ""); // enable multilanguage errors in file
chooser
setlocale(LC_NUMERIC, "C"); // make sure numeric values are written
correctly

Note that fluid writes and reads the fluid/fltk version number as a
numeric (float) value to/from the .fl file which is why we need to set
LC_NUMERIC to "C".

As I said, I'm not an expert, but I believe I got the basics of how it
works and how we should expect it to work.
Reply all
Reply to author
Forward
0 new messages