gnucash maint: Multiple changes pushed

John Ralls jralls at code.gnucash.org
Sun Jan 6 13:13:28 EST 2019


Updated	 via  https://github.com/Gnucash/gnucash/commit/b4fedff9 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/cee97be8 (commit)
	from  https://github.com/Gnucash/gnucash/commit/793fb1a3 (commit)



commit b4fedff90e8b35e69620922f27376ef78ade6d0e
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Jan 6 09:52:43 2019 -0800

    Provide a single static instance of C++ locale.
    
    We can't use std::locale::global because all streams imbue it by
    default and if it's not 'C' (aka std::locale::classic) then we
    must imbue all the streams that we don't want localized, and that's
    most of them.
    
    Provides error checking for setting the C++ locale from the environment.
    This is necessary both because the environment might have an invalid
    locale, which would cause an unhandled exception crash.
    
    On windows std::locale("") can't handle some Microsoft-style locale
    strings (e.g. Spanish_Spain) so we use boost::locale's gen("") function
    to set the locale--though even that can't handle a Microsoft-style
    locale string with an appended charset (e.g. Spanish_Spain.1252) and
    that's what glibc's setlocale(LC_ALL, NULL) emits.

diff --git a/gnucash/gnome/assistant-loan.cpp b/gnucash/gnome/assistant-loan.cpp
index 3509b18..5505623 100644
--- a/gnucash/gnome/assistant-loan.cpp
+++ b/gnucash/gnome/assistant-loan.cpp
@@ -49,6 +49,7 @@ extern "C"
 #include "gnc-engine.h"
 }
 
+#include <gnc-locale-utils.hpp>
 #include <boost/locale.hpp>
 #include <string>
 #include <sstream>
@@ -2320,7 +2321,7 @@ struct cust_prec_punct : std::moneypunct_byname<wchar_t, false> {
 template<int prec>
 std::string to_str_with_prec (const gdouble val)
 {
-    auto loc = std::locale(std::locale(""), new cust_prec_punct<prec>(""));
+    auto loc = std::locale(gnc_get_locale(), new cust_prec_punct<prec>(""));
     std::wstringstream valstr;
     valstr.imbue(loc);
     valstr << std::put_money(val * pow(10, prec));
diff --git a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
index 777caf4..00c1a12 100644
--- a/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
+++ b/gnucash/import-export/csv-imp/assistant-csv-trans-import.cpp
@@ -64,6 +64,7 @@ extern "C"
 #include "gnc-tokenizer-fw.hpp"
 #include "gnc-tokenizer-csv.hpp"
 
+#include <gnc-locale-utils.hpp>
 #include <boost/locale.hpp>
 
 namespace bl = boost::locale;
@@ -2068,7 +2069,7 @@ CsvImpTransAssist::assist_summary_page_prepare ()
     try
     {
     /* Translators: {1} will be replaced with a filename */
-        text += (bl::format (bl::translate ("The transactions were imported from file '{1}'.")) % m_file_name).str(gen(""));
+      text += (bl::format (bl::translate ("The transactions were imported from file '{1}'.")) % m_file_name).str(gnc_get_locale());
         text += "</b></span>";
     }
     catch (const bl::conv::conversion_error& err)
diff --git a/libgnucash/core-utils/CMakeLists.txt b/libgnucash/core-utils/CMakeLists.txt
index b08e38b..6d02f48 100644
--- a/libgnucash/core-utils/CMakeLists.txt
+++ b/libgnucash/core-utils/CMakeLists.txt
@@ -25,6 +25,7 @@ set (core_utils_SOURCES
   gnc-guile-utils.c
   gnc-jalali.c
   gnc-locale-utils.c
+  gnc-locale-utils.cpp
   gnc-path.c
 )
 
@@ -116,6 +117,7 @@ set(core_utils_noinst_HEADERS
   gnc-guile-utils.h
   gnc-jalali.h
   gnc-locale-utils.h
+  gnc-locale-utils.hpp
   gnc-path.h
 )
 
diff --git a/libgnucash/core-utils/gnc-filepath-utils.cpp b/libgnucash/core-utils/gnc-filepath-utils.cpp
index d421bbc..7371534 100644
--- a/libgnucash/core-utils/gnc-filepath-utils.cpp
+++ b/libgnucash/core-utils/gnc-filepath-utils.cpp
@@ -64,6 +64,7 @@ extern "C" {
 #endif
 }
 
+#include "gnc-locale-utils.hpp"
 #include <boost/filesystem.hpp>
 #include <boost/locale.hpp>
 #include <iostream>
@@ -587,9 +588,8 @@ static std::string migrate_gnc_datahome()
     gen.add_messages_path(gnc_path_get_datadir());
     gen.add_messages_domain(PACKAGE);
 
-//    std::locale::global(gen(""));
     std::stringstream migration_msg;
-    migration_msg.imbue(gen(""));
+    migration_msg.imbue(gnc_get_locale());
 
     /* Step 1: copy directory $HOME/.gnucash to $GNC_DATA_HOME */
     auto full_copy = copy_recursive (old_dir, gnc_userdata_home);
diff --git a/libgnucash/core-utils/gnc-locale-utils.cpp b/libgnucash/core-utils/gnc-locale-utils.cpp
new file mode 100644
index 0000000..e3de961
--- /dev/null
+++ b/libgnucash/core-utils/gnc-locale-utils.cpp
@@ -0,0 +1,78 @@
+/********************************************************************\
+ * gnc-locale-utils.cpp -- provide a default locale for C++         *
+ * Copyright (C) 2019 John Ralls <jralls at ceridwen.us                *
+ *                                                                  *
+ * This program is free software; you can redistribute it and/or    *
+ * modify it under the terms of the GNU General Public License as   *
+ * published by the Free Software Foundation; either version 2 of   *
+ * the License, or (at your option) any later version.              *
+ *                                                                  *
+ * This program is distributed in the hope that it will be useful,  *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
+ * GNU General Public License for more details.                     *
+ *                                                                  *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+\********************************************************************/
+
+#include <clocale>
+#include <boost/locale.hpp>
+#include "gnc-locale-utils.hpp"
+
+/* This function addresses two separate problems: First, if we set
+ * std::locale::global then all streams automagically imbue
+ * themselves with it and we have to re-imbue all of the backends and
+ * logging streams with std::locale::classic() so that data and log
+ * files aren't localized. Second, calling std::locale("") is slow,
+ * so we want to do it only once. Worse, the standard C++ library in
+ * Mingw64 chokes on at least some Microsoft-style locale strings
+ * (e.g. "Spanish_Spain") but libc's setlocale(LC_ALL, NULL) emits
+ * them even if originally fed a Unix-style locale ("es_ES").
+ *
+ * The solution is this function which caches the setlocale() locale
+ * the first time it's called and which uses a boost::locale
+ * generator, which does know what to do with (sometimes adjusted)
+ * Microsoft locale strings.
+ */
+const std::locale&
+gnc_get_locale()
+{
+  static std::locale cached;
+  static bool tried_already = false;
+  if (!tried_already)
+  {
+    boost::locale::generator gen;
+    tried_already = true;
+      try
+      {
+	  cached = gen("");
+      }
+      catch (const std::runtime_error& err)
+      {
+	  std::string c_locale(setlocale(LC_ALL, nullptr));
+	  std::cerr << "[gnc_get_locale] Failed to create app-default locale from " << c_locale << " because " << err.what() << "\n";
+	  auto dot = c_locale.find(".");
+	  if (dot != std::string::npos)
+	  {
+	      try
+	      {
+		  cached = gen(c_locale.substr(0, dot));
+	      }
+	      catch (std::runtime_error& err2)
+	      {
+		std::cerr << "[gnc_get_locale] Failed to create app-default locale from " << c_locale << " because " << err.what() << " so using the 'C' locale for C++.\n";
+	      }
+	  }
+	  else
+	  {
+	      std::cerr << "[gnc_get_locale] Using the 'C' locale for C++\n";
+	  }
+      }
+  }
+  return cached;
+}
diff --git a/libgnucash/core-utils/gnc-locale-utils.hpp b/libgnucash/core-utils/gnc-locale-utils.hpp
new file mode 100644
index 0000000..c16467e
--- /dev/null
+++ b/libgnucash/core-utils/gnc-locale-utils.hpp
@@ -0,0 +1,39 @@
+/********************************************************************\
+ * gnc-locale-utils.hpp -- provide a default locale for C++         *
+ * Copyright (C) 2019 John Ralls <jralls at ceridwen.us                *
+ *                                                                  *
+ * This program is free software; you can redistribute it and/or    *
+ * modify it under the terms of the GNU General Public License as   *
+ * published by the Free Software Foundation; either version 2 of   *
+ * the License, or (at your option) any later version.              *
+ *                                                                  *
+ * This program is distributed in the hope that it will be useful,  *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
+ * GNU General Public License for more details.                     *
+ *                                                                  *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+\********************************************************************/
+#ifndef GNC_LOCALE_UTILS_HPP
+#define GNC_LOCALE_UTILS_HPP
+
+#include <locale>
+
+/** Get the default application locale.
+ *
+ *  If we set std::locale::global we have to imbue every stream that
+ *  we want in the C locale, and that's a lot more than we want imbued
+ *  with the application locale. Calling std::locale("") is expensive,
+ *  so call this instead.
+ *
+ *  @returns A static std::locale representing the one set with
+ *  setlocale() in main().
+ */
+const std::locale& gnc_get_locale();
+
+#endif /* GNC_LOCALE_UTILS_HPP */
diff --git a/libgnucash/core-utils/test/CMakeLists.txt b/libgnucash/core-utils/test/CMakeLists.txt
index fe92a2c..3420787 100644
--- a/libgnucash/core-utils/test/CMakeLists.txt
+++ b/libgnucash/core-utils/test/CMakeLists.txt
@@ -46,6 +46,7 @@ set(test_gnc_path_util_SOURCES
   ${MODULEPATH}/gnc-path.c
   ${MODULEPATH}/binreloc.c
   ${MODULEPATH}/gnc-filepath-utils.cpp
+  ${MODULEPATH}/gnc-locale-utils.cpp
   gtest-path-utilities.cpp
   ${GTEST_SRC})
 
diff --git a/libgnucash/engine/gnc-datetime.cpp b/libgnucash/engine/gnc-datetime.cpp
index a9c4f6b..c73fc0a 100644
--- a/libgnucash/engine/gnc-datetime.cpp
+++ b/libgnucash/engine/gnc-datetime.cpp
@@ -32,14 +32,19 @@ extern "C"
 #include <boost/date_time/local_time/local_time.hpp>
 #include <boost/regex.hpp>
 #include <libintl.h>
+#include <locale.h>
 #include <map>
 #include <memory>
 #include <iostream>
 #include <sstream>
 #include <string>
 #include <vector>
+#include <gnc-locale-utils.hpp>
 #include "gnc-timezone.hpp"
 #include "gnc-datetime.hpp"
+#include "qoflog.h"
+
+static const char* log_module = "gnc.engine";
 
 #define N_(string) string //So that xgettext will find it
 
@@ -440,8 +445,7 @@ GncDateTimeImpl::format(const char* format) const
     //The stream destructor frees the facet, so it must be heap-allocated.
     auto output_facet(new Facet(normalize_format(format).c_str()));
     // FIXME Rather than imbueing a locale below we probably should set std::locale::global appropriately somewhere.
-    // At that point the use of cachedLocale mechanism should be removed.
-    ss.imbue(std::locale(cachedLocale, output_facet));
+    ss.imbue(std::locale(gnc_get_locale(), output_facet));
     ss << m_time;
     return ss.str();
 }
@@ -454,7 +458,7 @@ GncDateTimeImpl::format_zulu(const char* format) const
     //The stream destructor frees the facet, so it must be heap-allocated.
     auto output_facet(new Facet(normalize_format(format).c_str())); 
     // FIXME Rather than imbueing a locale below we probably should set std::locale::global appropriately somewhere.
-    ss.imbue(std::locale(std::locale(""), output_facet));
+    ss.imbue(std::locale(gnc_get_locale(), output_facet));
     ss << m_time.utc_time();
     return ss.str();
 }
@@ -530,7 +534,7 @@ GncDateImpl::format(const char* format) const
     //The stream destructor frees the facet, so it must be heap-allocated.
     auto output_facet(new Facet(normalize_format(format).c_str()));
     // FIXME Rather than imbueing a locale below we probably should set std::locale::global appropriately somewhere.
-    ss.imbue(std::locale(std::locale(""), output_facet));
+    ss.imbue(std::locale(gnc_get_locale(), output_facet));
     ss << m_greg;
     return ss.str();
 }
diff --git a/libgnucash/engine/test/CMakeLists.txt b/libgnucash/engine/test/CMakeLists.txt
index 5acda90..1a27ea7 100644
--- a/libgnucash/engine/test/CMakeLists.txt
+++ b/libgnucash/engine/test/CMakeLists.txt
@@ -104,6 +104,7 @@ set(gtest_qof_LIBS
 
 set(gtest_engine_INCLUDES
   ${MODULEPATH}
+  ${CMAKE_SOURCE_DIR}/libgnucash/core-utils
   ${CMAKE_BINARY_DIR}/common # for config.h
   ${CMAKE_SOURCE_DIR}/common # for platform.h
   ${GLIB2_INCLUDE_DIRS}
@@ -156,6 +157,7 @@ set(test_gnc_rational_SOURCES
   ${MODULEPATH}/gnc-timezone.cpp
   ${MODULEPATH}/gnc-date.cpp
   ${MODULEPATH}/qoflog.cpp
+  ${CMAKE_SOURCE_DIR}/libgnucash/core-utils/gnc-locale-utils.cpp
   ${gtest_engine_win32_SOURCES}
   gtest-gnc-rational.cpp
   ${GTEST_SRC})
@@ -171,6 +173,7 @@ set(test_gnc_numeric_SOURCES
   ${MODULEPATH}/gnc-timezone.cpp
   ${MODULEPATH}/gnc-date.cpp
   ${MODULEPATH}/qoflog.cpp
+  ${CMAKE_SOURCE_DIR}/libgnucash/core-utils/gnc-locale-utils.cpp
   ${gtest_engine_win32_SOURCES}
   gtest-gnc-numeric.cpp
   ${GTEST_SRC})
@@ -189,6 +192,7 @@ set(test_gnc_datetime_SOURCES
   ${MODULEPATH}/gnc-timezone.cpp
   ${MODULEPATH}/gnc-date.cpp
   ${MODULEPATH}/qoflog.cpp
+  ${CMAKE_SOURCE_DIR}/libgnucash/core-utils/gnc-locale-utils.cpp
   ${gtest_engine_win32_SOURCES}
   gtest-gnc-datetime.cpp
   ${GTEST_SRC})
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 8e327ea..e8c56ac 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -613,6 +613,7 @@ libgnucash/core-utils/gnc-glib-utils.c
 libgnucash/core-utils/gnc-guile-utils.c
 libgnucash/core-utils/gnc-jalali.c
 libgnucash/core-utils/gnc-locale-utils.c
+libgnucash/core-utils/gnc-locale-utils.cpp
 libgnucash/core-utils/gnc-path.c
 libgnucash/core-utils/gnc-prefs.c
 libgnucash/doc/doxygen_main_page.c

commit cee97be8d444441256f3b26db2c8061baa767894
Author: John Ralls <jralls at ceridwen.us>
Date:   Sat Jan 5 14:53:25 2019 -0800

    Add GncDateTime::timestamp().
    
    To provide a C++ implementation of gnc_date_timestamp and to avoid
    using the expensive and localized GncDateTime::format().

diff --git a/libgnucash/engine/gnc-date.cpp b/libgnucash/engine/gnc-date.cpp
index cacc35f..1e3236d 100644
--- a/libgnucash/engine/gnc-date.cpp
+++ b/libgnucash/engine/gnc-date.cpp
@@ -1075,7 +1075,7 @@ qof_strftime(gchar *buf, gsize max, const gchar *format, const struct tm *tm)
 gchar *
 gnc_date_timestamp (void)
 {
-    return gnc_print_time64(gnc_time(nullptr), "%Y%m%d%H%M%S");
+    return g_strdup(GncDateTime::timestamp().c_str());
 }
 
 /********************************************************************\
diff --git a/libgnucash/engine/gnc-datetime.cpp b/libgnucash/engine/gnc-datetime.cpp
index 70d9275..a9c4f6b 100644
--- a/libgnucash/engine/gnc-datetime.cpp
+++ b/libgnucash/engine/gnc-datetime.cpp
@@ -236,6 +236,7 @@ public:
     std::string format(const char* format) const;
     std::string format_zulu(const char* format) const;
     std::string format_iso8601() const;
+    static std::string timestamp();
 private:
     LDT m_time;
     static const TD time_of_day[3];
@@ -466,6 +467,14 @@ GncDateTimeImpl::format_iso8601() const
     return str.substr(0, 19);
 }
 
+std::string
+GncDateTimeImpl::timestamp()
+{
+    GncDateTimeImpl gdt;
+    auto str = boost::posix_time::to_iso_string(gdt.m_time.local_time());
+    return str.substr(0, 8) + str.substr(9, 15);
+}
+
 /* Member function definitions for GncDateImpl.
  */
 GncDateImpl::GncDateImpl(const std::string str, const std::string fmt) :
@@ -600,6 +609,12 @@ GncDateTime::format_iso8601() const
     return m_impl->format_iso8601();
 }
 
+std::string
+GncDateTime::timestamp()
+{
+    return GncDateTimeImpl::timestamp();
+}
+
 /* GncDate */
 GncDate::GncDate() : m_impl{new GncDateImpl} {}
 GncDate::GncDate(int year, int month, int day) :
diff --git a/libgnucash/engine/gnc-datetime.hpp b/libgnucash/engine/gnc-datetime.hpp
index 8f714f5..58d1f32 100644
--- a/libgnucash/engine/gnc-datetime.hpp
+++ b/libgnucash/engine/gnc-datetime.hpp
@@ -152,7 +152,11 @@ public:
  *  @return a std::string in the format YYYY-MM-DD HH:MM:SS.
  */
     std::string format_iso8601() const;
-
+/** Get an undelimited string representing the current date and time.
+ *  @return a std::string in the format YYYYMMDDHHMMSS.
+ */
+    static std::string timestamp();
+    
 private:
     std::unique_ptr<GncDateTimeImpl> m_impl;
 };



Summary of changes:
 gnucash/gnome/assistant-loan.cpp                   |  3 +-
 .../csv-imp/assistant-csv-trans-import.cpp         |  3 +-
 libgnucash/core-utils/CMakeLists.txt               |  2 +
 libgnucash/core-utils/gnc-filepath-utils.cpp       |  4 +-
 libgnucash/core-utils/gnc-locale-utils.cpp         | 78 ++++++++++++++++++++++
 .../escape.h => core-utils/gnc-locale-utils.hpp}   | 36 +++++-----
 libgnucash/core-utils/test/CMakeLists.txt          |  1 +
 libgnucash/engine/gnc-date.cpp                     |  2 +-
 libgnucash/engine/gnc-datetime.cpp                 | 27 ++++++--
 libgnucash/engine/gnc-datetime.hpp                 |  6 +-
 libgnucash/engine/test/CMakeLists.txt              |  4 ++
 po/POTFILES.in                                     |  1 +
 12 files changed, 136 insertions(+), 31 deletions(-)
 create mode 100644 libgnucash/core-utils/gnc-locale-utils.cpp
 copy libgnucash/{backend/sql/escape.h => core-utils/gnc-locale-utils.hpp} (67%)



More information about the gnucash-changes mailing list