From 35a0e7774e798063aae488ba956a5b57afe8f0c6 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Thu, 30 May 2024 15:34:11 -0700 Subject: [PATCH] Bug 799320 - GNUCash Immediately Exits on Startup Some excellent and insightful debugging by the reporter found that the cause was specifying a locale as en_US.utf8 instead of en_US.utf-8. GnuLib's locale functions and so Guile's are apparently quite picky on Win32. To address this move reading the environment file to before we set the mac and Windows locales and ensure that UTF/UCS and ISO-8859 character sets are specified with minus separators on Windows. I tested on macOS and didn't experience the crash. --- gnucash/gnucash-core-app.cpp | 2 +- gnucash/gnucash-locale-windows.c | 79 ++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/gnucash/gnucash-core-app.cpp b/gnucash/gnucash-core-app.cpp index 08ae1fc9cb7..e35055a2749 100644 --- a/gnucash/gnucash-core-app.cpp +++ b/gnucash/gnucash-core-app.cpp @@ -175,10 +175,10 @@ Gnucash::CoreApp::CoreApp (const char* app_name) : m_app_name {app_name} * The user may have configured a different language via * the environment file. */ + gnc_environment_setup(); #if defined MAC_INTEGRATION || defined __MINGW32__ sys_locale = set_platform_locale(); #endif - gnc_environment_setup(); #if ! defined MAC_INTEGRATION && ! defined __MINGW32__/* setlocale already done */ sys_locale = g_strdup (setlocale (LC_ALL, "")); if (!sys_locale) diff --git a/gnucash/gnucash-locale-windows.c b/gnucash/gnucash-locale-windows.c index 753af7c2044..6e7666ed4ac 100644 --- a/gnucash/gnucash-locale-windows.c +++ b/gnucash/gnucash-locale-windows.c @@ -27,6 +27,74 @@ #include #include "gnucash-locale-platform.h" +static void +rectify_utf(const char* envvar, const char* locale, + const char* dot, const char* rest) +{ + char *new_locale; + if (rest && *rest) + new_locale = g_strdup_printf ("%s.UTF-%s@%s", + locale, dot + 3, rest); + else + new_locale = g_strdup_printf ("%s.UTF-%s", locale, dot + 3); + + _putenv_s (envvar, new_locale); + g_free(new_locale); +} + +static void +rectify_iso(const char* envvar, const char* locale, + const char* dot, const char* rest) +{ + + char *eefn = strstr (dot, "8859"); + + if (!(eefn && *eefn)) + return; + + char* isonum = (*(eefn + 4) == '-') ? eefn + 5 : eefn + 4; + + if (*isonum == '\0') + return; + + char *new_locale; + if (rest && *rest) + new_locale = g_strdup_printf ("%s.ISO-8859-%s@%s", locale, isonum, rest); + else + new_locale = g_strdup_printf ("%s.ISO-8859-%s", locale, isonum); + + _putenv_s (envvar, new_locale); + g_free (new_locale); +} + +static void +rectify_environment_charset(const char* envvar) +{ + if (!(envvar && *envvar)) + return; + if (strcmp (envvar, "LANG") && strncmp (envvar, "LC_", 3)) + return; + char* saveptr; + char* varval = getenv (envvar); + char* locale = strtok_r (varval, ".", &saveptr); + char* dot = strtok_r (NULL, "@", &saveptr); + + if (!dot) //strsep didn't find a . + return; + + char* rest = strtok_r (NULL, "@", &saveptr); + + if ((strncasecmp (dot, "utf", 3) == 0 || strncasecmp (dot, "ucs", 3) == 0) && + dot[3] != '-') + return rectify_utf (envvar, locale, dot, rest); + + if (strncasecmp (dot, "iso", 3) == 0 && strlen (dot) >= 8 && + dot[3] != '-' && dot[8] != '-') + return rectify_iso (envvar, locale, dot, rest); + + //Otherwise do nothing +} + /* If one of the Unix locale variables LC_ALL, LC_MESSAGES, or LANG is * set in the environment check to see if it's a valid locale and if * it is set both the Windows and POSIX locales to that. If not @@ -37,6 +105,17 @@ set_platform_locale(void) { WCHAR lpLocaleName[LOCALE_NAME_MAX_LENGTH]; char *locale = NULL; + /* Prevent Bug 799320 by ensuring that the localization + environment variables of interest have well-formed UTF or + ISO-8859 specifiers for GnuLib's interpretation of well-formed, + which means having minuses in the right places. This only + protects agains missing hyphens, it won't help if you specify + utf42 as a charset when you meant utf32. + */ + rectify_environment_charset ("LANG"); + rectify_environment_charset ("LC_ALL"); + rectify_environment_charset ("LC_MESSAGES"); + rectify_environment_charset ("LC_CTYPE"); if (((locale = getenv ("LC_ALL")) != NULL && locale[0] != '\0') || ((locale = getenv ("LC_MESSAGES")) != NULL && locale[0] != '\0') ||