LOCALE_IO rework to fix issues on linux when using env vars with non ASCII7 chars.

We need to use a "C" locate to read/write files, and therefore switch the locale.
Removing use of setlocale( LC_NUMERIC, "C" ) and use only
wxLocale( "C", "C", "C", false ) was too fast: on linux it breaks the env vars.
on MSW
    using setlocale( LC_NUMERIC, "C" ) generates an alert message in debug mode,
    and this message ("Decimal separator mismatch") must be disabled.
    But calling wxLocale( "C", "C", "C", false ) works fine
On unix:
    calling wxLocale( "C", "C", "C", false ) breaks env vars containing non ASCII7 chars.
    these env vars return a empty string from wxGetEnv() in many cases, and if such a
    var must be read after calling wxLocale( "C", "C", "C", false ), it looks like empty

So use wxLocale on Windows and setlocale on unix
This commit is contained in:
jean-pierre charras 2021-09-23 14:30:40 +02:00
parent 157c21d325
commit 0cf81b6d4e
1 changed files with 73 additions and 5 deletions

View File

@ -1,7 +1,7 @@
/* /*
* This program source code file is part of KiCad, a free EDA CAD application. * This program source code file is part of KiCad, a free EDA CAD application.
* *
* Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors. * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -24,15 +24,74 @@
#include <locale_io.h> #include <locale_io.h>
#include <wx/intl.h> #include <wx/intl.h>
std::atomic<unsigned int> LOCALE_IO::m_c_count( 0 ); // When reading/writing files, we need to swtich to setlocale( LC_NUMERIC, "C" ).
// Works fine to read/write files with floating point numbers.
// We can call setlocale( LC_NUMERIC, "C" ) or wxLocale( "C", "C", "C", false )
// wxWidgets discourage a direct call to setlocale
// However, for us, calling wxLocale( "C", "C", "C", false ) has a unwanted effect:
// The I18N translations are no longer active, because the English dictionary is selected.
// To read files, this is not a major issues, but the resul can differ
// from using setlocale(xx, "C").
// Previouly, we used only setlocale( LC_NUMERIC, "C" )
//
// Known issues are
// on MSW
// using setlocale( LC_NUMERIC, "C" ) generates an alert message in debug mode,
// and this message ("Decimal separator mismatch") must be disabled.
// But calling wxLocale( "C", "C", "C", false ) works fine
// On unix:
// calling wxLocale( "C", "C", "C", false ) breaks env vars containing non ASCII7 chars.
// these env vars return a empty string from wxGetEnv() in many cases, and if such a
// var must be read after calling wxLocale( "C", "C", "C", false ), it looks like empty
//
// So use wxLocale on Windows and setlocale on unix
// set USE_WXLOCALE 0 to use setlocale, 1 to use wxLocale:
#if defined( _WIN32 )
#define USE_WXLOCALE 1
#else
#define USE_WXLOCALE 0
#endif
// On Windows, when using setlocale, a wx alert is generated
// in some cases (reading a bitmap for instance)
// So we disable alerts during the time a file is read or written
#if !USE_WXLOCALE
#if defined( _WIN32 ) && defined( DEBUG )
// a wxAssertHandler_t function to filter wxWidgets alert messages when reading/writing a file
// when switching the locale to LC_NUMERIC, "C"
// It is used in class LOCALE_IO to hide a useless (in kicad) wxWidgets alert message
void KiAssertFilter( const wxString &file, int line,
const wxString &func, const wxString &cond,
const wxString &msg)
{
if( !msg.Contains( "Decimal separator mismatch" ) )
wxTheApp->OnAssertFailure( file.c_str(), line, func.c_str(), cond.c_str(), msg.c_str() );
}
#endif
#endif
std::atomic<unsigned int> LOCALE_IO::m_c_count( 0 );
LOCALE_IO::LOCALE_IO() : m_wxLocale( nullptr ) LOCALE_IO::LOCALE_IO() : m_wxLocale( nullptr )
{ {
// use thread safe, atomic operation // use thread safe, atomic operation
if( m_c_count++ == 0 ) if( m_c_count++ == 0 )
{ {
m_wxLocale = new wxLocale( "C", "C", "C", false ); #if USE_WXLOCALE
#define C_LANG "C"
m_wxLocale = new wxLocale( C_LANG, C_LANG, C_LANG, false );
#else
// Store the user locale name, to restore this locale later, in dtor
m_user_locale = setlocale( LC_NUMERIC, nullptr );
#if defined( _WIN32 ) && defined( DEBUG )
// Disable wxWidgets alerts
wxSetAssertHandler( KiAssertFilter );
#endif
// Switch the locale to C locale, to read/write files with fp numbers
setlocale( LC_NUMERIC, "C" );
#endif
} }
} }
@ -42,7 +101,16 @@ LOCALE_IO::~LOCALE_IO()
// use thread safe, atomic operation // use thread safe, atomic operation
if( --m_c_count == 0 ) if( --m_c_count == 0 )
{ {
delete m_wxLocale; // Deleting m_wxLocale restored previous locale // revert to the user locale
#if USE_WXLOCALE
delete m_wxLocale; // Deleting m_wxLocale restored previous locale
m_wxLocale = nullptr; m_wxLocale = nullptr;
#else
setlocale( LC_NUMERIC, m_user_locale.c_str() );
#if defined( _WIN32 ) && defined( DEBUG )
// Enable wxWidgets alerts
wxSetDefaultAssertHandler();
#endif
#endif
} }
} }