]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/numformatter.cpp
fixing overrelease and out-of-bounds write, fixes #13725
[wxWidgets.git] / src / common / numformatter.cpp
index 851259cbfd6f9d7e3bc249ca8c089330fd60ed69..0c3a6a7a1ab6c5a1ea635fead91c94e870bbdc86 100644 (file)
@@ -1,5 +1,5 @@
 /////////////////////////////////////////////////////////////////////////////
 /////////////////////////////////////////////////////////////////////////////
-// Name:        numformatter.cpp
+// Name:        src/common/numformatter.cpp
 // Purpose:     wxNumberFormatter
 // Author:      Fulvio Senore, Vadim Zeitlin
 // Created:     2010-11-06
 // Purpose:     wxNumberFormatter
 // Author:      Fulvio Senore, Vadim Zeitlin
 // Created:     2010-11-06
 #include "wx/numformatter.h"
 #include "wx/intl.h"
 
 #include "wx/numformatter.h"
 #include "wx/intl.h"
 
+#include <locale.h> // for setlocale and LC_ALL
+
+// ----------------------------------------------------------------------------
+// local helpers
+// ----------------------------------------------------------------------------
+
+namespace
+{
+
+// Contains information about the locale which was used to initialize our
+// cached values of the decimal and thousands separators. Notice that it isn't
+// enough to store just wxLocale because the user code may call setlocale()
+// directly and storing just C locale string is not enough because we can use
+// the OS API directly instead of the CRT ones on some platforms. So just store
+// both.
+class LocaleId
+{
+public:
+    LocaleId()
+    {
+#if wxUSE_INTL
+        m_wxloc = NULL;
+#endif // wxUSE_INTL
+        m_cloc = NULL;
+    }
+
+    ~LocaleId()
+    {
+        Free();
+    }
+
+#if wxUSE_INTL
+    // Return true if this is the first time this function is called for this
+    // object or if the program locale has changed since the last time it was
+    // called. Otherwise just return false indicating that updating locale-
+    // dependent information is not necessary.
+    bool NotInitializedOrHasChanged()
+    {
+        wxLocale * const wxloc = wxGetLocale();
+        const char * const cloc = setlocale(LC_ALL, NULL);
+        if ( m_wxloc || m_cloc )
+        {
+            if ( m_wxloc == wxloc && strcmp(m_cloc, cloc) == 0 )
+                return false;
+
+            Free();
+        }
+        //else: Not initialized yet.
+
+        m_wxloc = wxloc;
+        m_cloc = wxCRT_StrdupA(cloc);
+
+        return true;
+    }
+#endif // wxUSE_INTL
+
+private:
+    void Free()
+    {
+#if wxUSE_INTL
+        free(m_cloc);
+#endif // wxUSE_INTL
+    }
+
+#if wxUSE_INTL
+    // Non-owned pointer to wxLocale which was used.
+    wxLocale *m_wxloc;
+#endif // wxUSE_INTL
+
+    // Owned pointer to the C locale string.
+    char *m_cloc;
+
+    wxDECLARE_NO_COPY_CLASS(LocaleId);
+};
+
+} // anonymous namespace
+
 // ============================================================================
 // wxNumberFormatter implementation
 // ============================================================================
 // ============================================================================
 // wxNumberFormatter implementation
 // ============================================================================
 
 wxChar wxNumberFormatter::GetDecimalSeparator()
 {
 
 wxChar wxNumberFormatter::GetDecimalSeparator()
 {
+#if wxUSE_INTL
     // Notice that while using static variable here is not MT-safe, the worst
     // that can happen is that we redo the initialization if we're called
     // concurrently from more than one thread so it's not a real problem.
     static wxChar s_decimalSeparator = 0;
 
     // Notice that while using static variable here is not MT-safe, the worst
     // that can happen is that we redo the initialization if we're called
     // concurrently from more than one thread so it's not a real problem.
     static wxChar s_decimalSeparator = 0;
 
-    if ( !s_decimalSeparator )
+    // Remember the locale which was current when we initialized, we must redo
+    // the initialization if the locale changed.
+    static LocaleId s_localeUsedForInit;
+
+    if ( s_localeUsedForInit.NotInitializedOrHasChanged() )
     {
         const wxString
             s = wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER);
     {
         const wxString
             s = wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER);
@@ -57,14 +139,18 @@ wxChar wxNumberFormatter::GetDecimalSeparator()
     }
 
     return s_decimalSeparator;
     }
 
     return s_decimalSeparator;
+#else // !wxUSE_INTL
+    return wxT('.');
+#endif // wxUSE_INTL/!wxUSE_INTL
 }
 
 bool wxNumberFormatter::GetThousandsSeparatorIfUsed(wxChar *sep)
 {
 }
 
 bool wxNumberFormatter::GetThousandsSeparatorIfUsed(wxChar *sep)
 {
+#if wxUSE_INTL
     static wxChar s_thousandsSeparator = 0;
     static wxChar s_thousandsSeparator = 0;
-    static bool s_initialized = false;
+    static LocaleId s_localeUsedForInit;
 
 
-    if ( !s_initialized )
+    if ( s_localeUsedForInit.NotInitializedOrHasChanged() )
     {
         const wxString
             s = wxLocale::GetInfo(wxLOCALE_THOUSANDS_SEP, wxLOCALE_CAT_NUMBER);
     {
         const wxString
             s = wxLocale::GetInfo(wxLOCALE_THOUSANDS_SEP, wxLOCALE_CAT_NUMBER);
@@ -77,8 +163,6 @@ bool wxNumberFormatter::GetThousandsSeparatorIfUsed(wxChar *sep)
         }
         //else: Unlike above it's perfectly fine for the thousands separator to
         //      be empty if grouping is not used, so just leave it as 0.
         }
         //else: Unlike above it's perfectly fine for the thousands separator to
         //      be empty if grouping is not used, so just leave it as 0.
-
-        s_initialized = true;
     }
 
     if ( !s_thousandsSeparator )
     }
 
     if ( !s_thousandsSeparator )
@@ -88,16 +172,18 @@ bool wxNumberFormatter::GetThousandsSeparatorIfUsed(wxChar *sep)
         *sep = s_thousandsSeparator;
 
     return true;
         *sep = s_thousandsSeparator;
 
     return true;
+#else // !wxUSE_INTL
+    wxUnusedVar(sep);
+    return false;
+#endif // wxUSE_INTL/!wxUSE_INTL
 }
 
 // ----------------------------------------------------------------------------
 // Conversion to string and helpers
 // ----------------------------------------------------------------------------
 
 }
 
 // ----------------------------------------------------------------------------
 // Conversion to string and helpers
 // ----------------------------------------------------------------------------
 
-wxString wxNumberFormatter::ToString(long val, int style)
+wxString wxNumberFormatter::PostProcessIntString(wxString s, int style)
 {
 {
-    wxString s = wxString::Format("%ld", val);
-
     if ( style & Style_WithThousandsSep )
         AddThousandsSeparators(s);
 
     if ( style & Style_WithThousandsSep )
         AddThousandsSeparators(s);
 
@@ -107,6 +193,21 @@ wxString wxNumberFormatter::ToString(long val, int style)
     return s;
 }
 
     return s;
 }
 
+wxString wxNumberFormatter::ToString(long val, int style)
+{
+    return PostProcessIntString(wxString::Format("%ld", val), style);
+}
+
+#ifdef wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
+
+wxString wxNumberFormatter::ToString(wxLongLong_t val, int style)
+{
+    return PostProcessIntString(wxString::Format("%" wxLongLongFmtSpec "d", val),
+                                style);
+}
+
+#endif // wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
+
 wxString wxNumberFormatter::ToString(double val, int precision, int style)
 {
     const wxString fmt = wxString::Format("%%.%df", precision);
 wxString wxNumberFormatter::ToString(double val, int precision, int style)
 {
     const wxString fmt = wxString::Format("%%.%df", precision);
@@ -134,6 +235,10 @@ void wxNumberFormatter::AddThousandsSeparators(wxString& s)
         pos = s.length();
     }
 
         pos = s.length();
     }
 
+    // End grouping at the beginning of the digits -- there could be at a sign
+    // before their start.
+    const size_t start = s.find_first_of("0123456789");
+
     // We currently group digits by 3 independently of the locale. This is not
     // the right thing to do and we should use lconv::grouping (under POSIX)
     // and GetLocaleInfo(LOCALE_SGROUPING) (under MSW) to get information about
     // We currently group digits by 3 independently of the locale. This is not
     // the right thing to do and we should use lconv::grouping (under POSIX)
     // and GetLocaleInfo(LOCALE_SGROUPING) (under MSW) to get information about
@@ -141,7 +246,7 @@ void wxNumberFormatter::AddThousandsSeparators(wxString& s)
     // wxLocale level first and then used here in the future (TODO).
     const size_t GROUP_LEN = 3;
 
     // wxLocale level first and then used here in the future (TODO).
     const size_t GROUP_LEN = 3;
 
-    while ( pos > GROUP_LEN )
+    while ( pos > start + GROUP_LEN )
     {
         pos -= GROUP_LEN;
         s.insert(pos, thousandsSep);
     {
         pos -= GROUP_LEN;
         s.insert(pos, thousandsSep);
@@ -151,7 +256,8 @@ void wxNumberFormatter::AddThousandsSeparators(wxString& s)
 void wxNumberFormatter::RemoveTrailingZeroes(wxString& s)
 {
     const size_t posDecSep = s.find(GetDecimalSeparator());
 void wxNumberFormatter::RemoveTrailingZeroes(wxString& s)
 {
     const size_t posDecSep = s.find(GetDecimalSeparator());
-    wxCHECK_RET( posDecSep != wxString::npos, "No decimal separator" );
+    wxCHECK_RET( posDecSep != wxString::npos,
+                 wxString::Format("No decimal separator in \"%s\"", s) );
     wxCHECK_RET( posDecSep, "Can't start with decimal separator" );
 
     // Find the last character to keep.
     wxCHECK_RET( posDecSep, "Can't start with decimal separator" );
 
     // Find the last character to keep.
@@ -183,6 +289,16 @@ bool wxNumberFormatter::FromString(wxString s, long *val)
     return s.ToLong(val);
 }
 
     return s.ToLong(val);
 }
 
+#ifdef wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
+
+bool wxNumberFormatter::FromString(wxString s, wxLongLong_t *val)
+{
+    RemoveThousandsSeparators(s);
+    return s.ToLongLong(val);
+}
+
+#endif // wxHAS_LONG_LONG_T_DIFFERENT_FROM_LONG
+
 bool wxNumberFormatter::FromString(wxString s, double *val)
 {
     RemoveThousandsSeparators(s);
 bool wxNumberFormatter::FromString(wxString s, double *val)
 {
     RemoveThousandsSeparators(s);