mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Extract common setup bits from gnucash and gnucash-cli into a separate class Gnucash::Base
This is just a first start, more refactoring will follow in later commits. The idea is to have an application class that provides the basic framework gnucash and gnucash-cli will become specializations of this class adding their specific functionality. I'm splitting this over several commits to be able to keep track of all the many changes.
This commit is contained in:
parent
3cd0de8ce4
commit
c58cfdb87d
@ -31,14 +31,20 @@ else()
|
||||
|
||||
endif()
|
||||
|
||||
set(gnucash_noinst_HEADERS
|
||||
gnucash-base.hpp
|
||||
)
|
||||
|
||||
set (gnucash_SOURCES
|
||||
gnucash.cpp
|
||||
gnucash-base.cpp
|
||||
gnucash-gresources.c
|
||||
${GNUCASH_RESOURCE_FILE}
|
||||
)
|
||||
|
||||
add_executable (gnucash
|
||||
${gnucash_SOURCES}
|
||||
${gnucash_SOURCES}
|
||||
${gnucash_noinst_HEADERS}
|
||||
)
|
||||
|
||||
add_dependencies (gnucash gnucash-manpage)
|
||||
@ -55,7 +61,9 @@ target_link_libraries (gnucash
|
||||
|
||||
add_executable (gnucash-cli
|
||||
gnucash-cli.cpp
|
||||
gnucash-base.cpp
|
||||
${GNUCASH_RESOURCE_FILE}
|
||||
${gnucash_noinst_HEADERS}
|
||||
)
|
||||
|
||||
add_dependencies (gnucash-cli gnucash)
|
||||
@ -275,8 +283,8 @@ gnc_add_scheme_targets(price-quotes
|
||||
DEPENDS "scm-engine;scm-app-utils;scm-gnome-utils")
|
||||
|
||||
set_local_dist(gnucash_DIST_local CMakeLists.txt environment.in generate-gnc-script
|
||||
gnucash.cpp gnucash-cli.cpp gnucash.rc.in gnucash-valgrind.in gnucash-gresources.xml ${gresource_files}
|
||||
price-quotes.scm ${gnucash_EXTRA_DIST})
|
||||
gnucash.cpp gnucash-cli.cpp gnucash-base.cpp gnucash.rc.in gnucash-valgrind.in gnucash-gresources.xml ${gresource_files}
|
||||
price-quotes.scm ${gnucash_noinst_HEADERS} ${gnucash_EXTRA_DIST})
|
||||
|
||||
set (gnucash_DIST ${gnucash_DIST_local} ${gnome_DIST} ${gnome_search_DIST} ${gnome_utils_DIST}
|
||||
${gschemas_DIST} ${gtkbuilder_DIST} ${html_DIST} ${import_export_DIST} ${python_DIST} ${register_DIST}
|
||||
|
714
gnucash/gnucash-base.cpp
Normal file
714
gnucash/gnucash-base.cpp
Normal file
@ -0,0 +1,714 @@
|
||||
/*
|
||||
* gnucash-base.cpp -- Basic application object for gnucash binaries
|
||||
*
|
||||
* Copyright (C) 2020 Geert Janssens <geert@kobaltwit.be>
|
||||
*
|
||||
* 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@gnu.org
|
||||
*/
|
||||
#include <config.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <libguile.h>
|
||||
#include <glib/gi18n.h>
|
||||
#include <glib.h>
|
||||
#include <binreloc.h>
|
||||
#include <gnc-locale-utils.h>
|
||||
#include <gnc-engine.h>
|
||||
#include <gnc-ui-util.h>
|
||||
#include <gnc-commodity.h>
|
||||
#include <swig-runtime.h>
|
||||
#include <guile-mappings.h>
|
||||
#ifdef __MINGW32__
|
||||
#include <Windows.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#include "gnucash-base.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <gfec.h>
|
||||
#include <gnc-engine-guile.h>
|
||||
#include <gnc-environment.h>
|
||||
#include <gnc-filepath-utils.h>
|
||||
#include <gnc-hooks.h>
|
||||
#include <gnc-path.h>
|
||||
#include <gnc-prefs.h>
|
||||
#include <gnc-prefs-utils.h>
|
||||
#include <gnc-gnome-utils.h>
|
||||
#include <gnc-gsettings.h>
|
||||
#include <gnc-report.h>
|
||||
#include <gnc-session.h>
|
||||
#include <gnc-splash.h>
|
||||
#include <gnc-version.h>
|
||||
}
|
||||
|
||||
/* This static indicates the debugging module that this .o belongs to. */
|
||||
static QofLogModule log_module = GNC_MOD_GUI;
|
||||
|
||||
/* Change the following to have a console window attached to GnuCash
|
||||
* for displaying stdout and stderr on Windows.
|
||||
*/
|
||||
#define __MSWIN_CONSOLE__ 0
|
||||
|
||||
#include <libintl.h>
|
||||
#include <locale.h>
|
||||
|
||||
#ifdef MAC_INTEGRATION
|
||||
# include <Foundation/Foundation.h>
|
||||
#endif
|
||||
|
||||
/* GNC_VCS is defined whenever we're building from an svn/svk/git/bzr tree */
|
||||
#ifdef GNC_VCS
|
||||
static int is_development_version = TRUE;
|
||||
#else
|
||||
static int is_development_version = FALSE;
|
||||
#define GNC_VCS ""
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
static gchar *userdata_migration_msg = NULL;
|
||||
|
||||
static void
|
||||
gnc_print_unstable_message(void)
|
||||
{
|
||||
if (!is_development_version) return;
|
||||
|
||||
g_print("\n\n%s\n%s\n%s %s\n%s %s\n",
|
||||
_("This is a development version. It may or may not work."),
|
||||
_("Report bugs and other problems to gnucash-devel@gnucash.org"),
|
||||
/* Translators: An URLs follows*/
|
||||
_("You can also lookup and file bug reports at"), PACKAGE_BUGREPORT,
|
||||
/* Translators: An URLs follows*/
|
||||
_("To find the last stable version, please refer to"), PACKAGE_URL);
|
||||
}
|
||||
|
||||
#ifdef MAC_INTEGRATION
|
||||
static void
|
||||
mac_set_currency_locale(NSLocale *locale, NSString *locale_str)
|
||||
{
|
||||
/* If the currency doesn't match the base locale, we need to find a locale that does match, because setlocale won't know what to do with just a currency identifier. */
|
||||
NSLocale *cur_locale = [[NSLocale alloc] initWithLocaleIdentifier: locale_str];
|
||||
if (![[locale objectForKey: NSLocaleCurrencyCode] isEqualToString:
|
||||
[cur_locale objectForKey: NSLocaleCurrencyCode]])
|
||||
{
|
||||
NSArray *all_locales = [NSLocale availableLocaleIdentifiers];
|
||||
NSEnumerator *locale_iter = [all_locales objectEnumerator];
|
||||
NSString *this_locale;
|
||||
NSString *currency = [locale objectForKey: NSLocaleCurrencyCode];
|
||||
NSString *money_locale = nil;
|
||||
while ((this_locale = (NSString*)[locale_iter nextObject]))
|
||||
{
|
||||
NSLocale *templocale = [[NSLocale alloc]
|
||||
initWithLocaleIdentifier: this_locale];
|
||||
if ([[templocale objectForKey: NSLocaleCurrencyCode]
|
||||
isEqualToString: currency])
|
||||
{
|
||||
money_locale = this_locale;
|
||||
[templocale release];
|
||||
break;
|
||||
}
|
||||
[templocale release];
|
||||
}
|
||||
if (money_locale)
|
||||
setlocale(LC_MONETARY, [money_locale UTF8String]);
|
||||
}
|
||||
[cur_locale release];
|
||||
}
|
||||
/* The locale that we got from AppKit isn't a supported POSIX one, so we need to
|
||||
* find something close. First see if we can find another locale for the
|
||||
* country; failing that, try the language. Ultimately fall back on en_US.
|
||||
*/
|
||||
static NSString*
|
||||
mac_find_close_country(NSString *locale_str, NSString *country_str,
|
||||
NSString *lang_str)
|
||||
{
|
||||
NSArray *all_locales = [NSLocale availableLocaleIdentifiers];
|
||||
NSEnumerator *locale_iter = [all_locales objectEnumerator];
|
||||
NSString *this_locale, *new_locale = nil;
|
||||
PWARN("Apple Locale is set to a value %s not supported"
|
||||
" by the C runtime", [locale_str UTF8String]);
|
||||
while ((this_locale = (NSString*)[locale_iter nextObject]))
|
||||
if ([[[NSLocale componentsFromLocaleIdentifier: this_locale]
|
||||
objectForKey: NSLocaleCountryCode]
|
||||
isEqualToString: country_str] &&
|
||||
setlocale (LC_ALL, [this_locale UTF8String]))
|
||||
{
|
||||
new_locale = this_locale;
|
||||
break;
|
||||
}
|
||||
if (!new_locale)
|
||||
while ((this_locale = (NSString*)[locale_iter nextObject]))
|
||||
if ([[[NSLocale componentsFromLocaleIdentifier: this_locale]
|
||||
objectForKey: NSLocaleLanguageCode]
|
||||
isEqualToString: lang_str] &&
|
||||
setlocale (LC_ALL, [this_locale UTF8String]))
|
||||
{
|
||||
new_locale = this_locale;
|
||||
break;
|
||||
}
|
||||
if (new_locale)
|
||||
locale_str = new_locale;
|
||||
else
|
||||
{
|
||||
locale_str = @"en_US";
|
||||
setlocale(LC_ALL, [locale_str UTF8String]);
|
||||
}
|
||||
PWARN("Using %s instead.", [locale_str UTF8String]);
|
||||
return locale_str;
|
||||
}
|
||||
|
||||
/* Language subgroups (e.g., US English) are reported in the form "ll-SS"
|
||||
* (e.g. again, "en-US"), not what gettext wants. We convert those to
|
||||
* old-style locales, which is easy for most cases. There are two where it
|
||||
* isn't, though: Simplified Chinese (zh-Hans) and traditional Chinese
|
||||
* (zh-Hant), which are normally assigned the locales zh_CN and zh_TW,
|
||||
* respectively. Those are handled specially.
|
||||
*/
|
||||
static NSString*
|
||||
mac_convert_complex_language(NSString* this_lang)
|
||||
{
|
||||
NSArray *elements = [this_lang componentsSeparatedByString: @"-"];
|
||||
if ([elements count] == 1)
|
||||
return this_lang;
|
||||
if ([[elements objectAtIndex: 0] isEqualToString: @"zh"]) {
|
||||
if ([[elements objectAtIndex: 1] isEqualToString: @"Hans"])
|
||||
this_lang = @"zh_CN";
|
||||
else
|
||||
this_lang = @"zh_TW";
|
||||
}
|
||||
else
|
||||
this_lang = [elements componentsJoinedByString: @"_"];
|
||||
return this_lang;
|
||||
}
|
||||
|
||||
static void
|
||||
mac_set_languages(NSArray* languages, NSString *lang_str)
|
||||
{
|
||||
/* Process the language list. */
|
||||
|
||||
const gchar *langs = NULL;
|
||||
NSEnumerator *lang_iter = [languages objectEnumerator];
|
||||
NSArray *new_languages = [NSArray array];
|
||||
NSString *this_lang = NULL;
|
||||
NSRange not_found = {NSNotFound, 0};
|
||||
while ((this_lang = [lang_iter nextObject])) {
|
||||
this_lang = [this_lang stringByTrimmingCharactersInSet:
|
||||
[NSCharacterSet characterSetWithCharactersInString: @"\""]];
|
||||
this_lang = mac_convert_complex_language(this_lang);
|
||||
new_languages = [new_languages arrayByAddingObject: this_lang];
|
||||
/* If it's an English language, add the "C" locale after it so that
|
||||
* any messages can default to it */
|
||||
if (!NSEqualRanges([this_lang rangeOfString: @"en"], not_found))
|
||||
new_languages = [new_languages arrayByAddingObject: @"C"];
|
||||
if (![new_languages containsObject: lang_str]) {
|
||||
NSArray *temp_array = [NSArray arrayWithObject: lang_str];
|
||||
new_languages = [temp_array arrayByAddingObjectsFromArray: new_languages];
|
||||
}
|
||||
langs = [[new_languages componentsJoinedByString:@":"] UTF8String];
|
||||
}
|
||||
if (langs && strlen(langs) > 0)
|
||||
{
|
||||
PWARN("Language list: %s", langs);
|
||||
g_setenv("LANGUAGE", langs, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
set_mac_locale()
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
|
||||
NSLocale *locale = [NSLocale currentLocale];
|
||||
NSString *lang_str, *country_str, *locale_str;
|
||||
NSArray *languages = [[defs arrayForKey: @"AppleLanguages"] retain];
|
||||
@try
|
||||
{
|
||||
lang_str = [locale objectForKey: NSLocaleLanguageCode];
|
||||
country_str = [locale objectForKey: NSLocaleCountryCode];
|
||||
locale_str = [[lang_str stringByAppendingString: @"_"]
|
||||
stringByAppendingString: country_str];
|
||||
}
|
||||
@catch (NSException *err)
|
||||
{
|
||||
PWARN("Locale detection raised error %s: %s. "
|
||||
"Check that your locale settings in "
|
||||
"System Preferences>Languages & Text are set correctly.",
|
||||
[[err name] UTF8String], [[err reason] UTF8String]);
|
||||
locale_str = @"_";
|
||||
}
|
||||
/* If we didn't get a valid current locale, the string will be just "_" */
|
||||
if ([locale_str isEqualToString: @"_"])
|
||||
locale_str = @"en_US";
|
||||
|
||||
lang_str = mac_convert_complex_language(lang_str);
|
||||
if (!setlocale(LC_ALL, [locale_str UTF8String]))
|
||||
locale_str = mac_find_close_country(locale_str, country_str, lang_str);
|
||||
if (g_getenv("LANG") == NULL)
|
||||
g_setenv("LANG", [locale_str UTF8String], TRUE);
|
||||
mac_set_currency_locale(locale, locale_str);
|
||||
/* Now call gnc_localeconv() to force creation of the app locale
|
||||
* before another call to setlocale messes it up. */
|
||||
gnc_localeconv ();
|
||||
/* Process the languages, including the one from the Apple locale. */
|
||||
if ([languages count] > 0)
|
||||
mac_set_languages(languages, lang_str);
|
||||
else
|
||||
g_setenv("LANGUAGE", [lang_str UTF8String], TRUE);
|
||||
[languages release];
|
||||
[pool drain];
|
||||
}
|
||||
#endif /* MAC_INTEGRATION */
|
||||
|
||||
static gboolean
|
||||
try_load_config_array(const gchar *fns[])
|
||||
{
|
||||
gchar *filename;
|
||||
int i;
|
||||
|
||||
for (i = 0; fns[i]; i++)
|
||||
{
|
||||
filename = gnc_build_userdata_path(fns[i]);
|
||||
if (gfec_try_load(filename))
|
||||
{
|
||||
g_free(filename);
|
||||
return TRUE;
|
||||
}
|
||||
g_free(filename);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
update_message(const gchar *msg)
|
||||
{
|
||||
gnc_update_splash_screen(msg, GNC_SPLASH_PERCENTAGE_UNKNOWN);
|
||||
g_message("%s", msg);
|
||||
}
|
||||
|
||||
static void
|
||||
load_system_config(void)
|
||||
{
|
||||
static int is_system_config_loaded = FALSE;
|
||||
gchar *system_config_dir;
|
||||
gchar *system_config;
|
||||
|
||||
if (is_system_config_loaded) return;
|
||||
|
||||
update_message("loading system configuration");
|
||||
system_config_dir = gnc_path_get_pkgsysconfdir();
|
||||
system_config = g_build_filename(system_config_dir, "config", NULL);
|
||||
is_system_config_loaded = gfec_try_load(system_config);
|
||||
g_free(system_config_dir);
|
||||
g_free(system_config);
|
||||
}
|
||||
|
||||
static void
|
||||
load_user_config(void)
|
||||
{
|
||||
/* Don't continue adding to this list. When 3.0 rolls around bump
|
||||
the 2.4 files off the list. */
|
||||
static const gchar *saved_report_files[] =
|
||||
{
|
||||
SAVED_REPORTS_FILE, SAVED_REPORTS_FILE_OLD_REV, NULL
|
||||
};
|
||||
static const gchar *stylesheet_files[] = { "stylesheets-2.0", NULL};
|
||||
static int is_user_config_loaded = FALSE;
|
||||
|
||||
if (is_user_config_loaded)
|
||||
return;
|
||||
else is_user_config_loaded = TRUE;
|
||||
|
||||
update_message("loading user configuration");
|
||||
{
|
||||
gchar *config_filename;
|
||||
config_filename = g_build_filename (gnc_userconfig_dir (),
|
||||
"config-user.scm", (char *)NULL);
|
||||
gfec_try_load(config_filename);
|
||||
g_free(config_filename);
|
||||
}
|
||||
|
||||
update_message("loading saved reports");
|
||||
try_load_config_array(saved_report_files);
|
||||
update_message("loading stylesheets");
|
||||
try_load_config_array(stylesheet_files);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gnc_log_init (gchar **log_flags, gchar *log_to_filename)
|
||||
{
|
||||
if (log_to_filename != NULL)
|
||||
qof_log_init_filename_special(log_to_filename);
|
||||
else
|
||||
{
|
||||
/* initialize logging to our file. */
|
||||
gchar *tracefilename;
|
||||
tracefilename = g_build_filename(g_get_tmp_dir(), "gnucash.trace",
|
||||
(gchar *)NULL);
|
||||
qof_log_init_filename(tracefilename);
|
||||
g_free(tracefilename);
|
||||
}
|
||||
|
||||
// set a reasonable default.
|
||||
qof_log_set_default(QOF_LOG_WARNING);
|
||||
|
||||
gnc_log_default();
|
||||
|
||||
if (gnc_prefs_is_debugging_enabled())
|
||||
{
|
||||
qof_log_set_level("", QOF_LOG_INFO);
|
||||
qof_log_set_level("qof", QOF_LOG_INFO);
|
||||
qof_log_set_level("gnc", QOF_LOG_INFO);
|
||||
}
|
||||
|
||||
{
|
||||
gchar *log_config_filename;
|
||||
log_config_filename = g_build_filename (gnc_userconfig_dir (),
|
||||
"log.conf", (char *)NULL);
|
||||
if (g_file_test(log_config_filename, G_FILE_TEST_EXISTS))
|
||||
qof_log_parse_log_config(log_config_filename);
|
||||
g_free(log_config_filename);
|
||||
}
|
||||
|
||||
if (log_flags != NULL)
|
||||
{
|
||||
int i = 0;
|
||||
for (; log_flags[i] != NULL; i++)
|
||||
{
|
||||
QofLogLevel level;
|
||||
gchar **parts = NULL;
|
||||
|
||||
gchar *log_opt = log_flags[i];
|
||||
parts = g_strsplit(log_opt, "=", 2);
|
||||
if (parts == NULL || parts[0] == NULL || parts[1] == NULL)
|
||||
{
|
||||
g_warning("string [%s] not parseable", log_opt);
|
||||
continue;
|
||||
}
|
||||
|
||||
level = qof_log_level_from_string(parts[1]);
|
||||
qof_log_set_level(parts[0], level);
|
||||
g_strfreev(parts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __MINGW32__
|
||||
/* 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
|
||||
* retrieve the Windows locale and set POSIX to match.
|
||||
*/
|
||||
static void
|
||||
set_win32_thread_locale()
|
||||
{
|
||||
WCHAR lpLocaleName[LOCALE_NAME_MAX_LENGTH];
|
||||
char *locale = NULL;
|
||||
|
||||
if (((locale = getenv ("LC_ALL")) != NULL && locale[0] != '\0') ||
|
||||
((locale = getenv ("LC_MESSAGES")) != NULL && locale[0] != '\0') ||
|
||||
((locale = getenv ("LANG")) != NULL && locale[0] != '\0'))
|
||||
{
|
||||
gunichar2* wlocale = NULL;
|
||||
int len = 0;
|
||||
len = strchr(locale, '.') - locale;
|
||||
locale[2] = '-';
|
||||
wlocale = g_utf8_to_utf16 (locale, len, NULL, NULL, NULL);
|
||||
if (IsValidLocaleName(wlocale))
|
||||
{
|
||||
LCID lcid = LocaleNameToLCID(wlocale, LOCALE_ALLOW_NEUTRAL_NAMES);
|
||||
SetThreadLocale(lcid);
|
||||
locale[2] = '_';
|
||||
setlocale (LC_ALL, locale);
|
||||
sys_locale = locale;
|
||||
g_free(wlocale);
|
||||
return;
|
||||
}
|
||||
g_free(locale);
|
||||
g_free(wlocale);
|
||||
}
|
||||
if (GetUserDefaultLocaleName(lpLocaleName, LOCALE_NAME_MAX_LENGTH))
|
||||
{
|
||||
sys_locale = g_utf16_to_utf8((gunichar2*)lpLocaleName,
|
||||
LOCALE_NAME_MAX_LENGTH,
|
||||
NULL, NULL, NULL);
|
||||
sys_locale[2] = '_';
|
||||
setlocale (LC_ALL, sys_locale);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Creates a console window on MSWindows to display stdout and stderr
|
||||
* when __MSWIN_CONSOLE__ is defined at the top of the file.
|
||||
*
|
||||
* Useful for displaying the diagnostics printed before logging is
|
||||
* started and if logging is redirected with --logto=stderr.
|
||||
*/
|
||||
static void
|
||||
redirect_stdout (void)
|
||||
{
|
||||
#if defined __MINGW32__ && __MSWIN_CONSOLE__
|
||||
static const WORD MAX_CONSOLE_LINES = 500;
|
||||
int hConHandle;
|
||||
long lStdHandle;
|
||||
CONSOLE_SCREEN_BUFFER_INFO coninfo;
|
||||
FILE *fp;
|
||||
|
||||
// allocate a console for this app
|
||||
AllocConsole();
|
||||
|
||||
// set the screen buffer to be big enough to let us scroll text
|
||||
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
|
||||
coninfo.dwSize.Y = MAX_CONSOLE_LINES;
|
||||
SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
|
||||
|
||||
// redirect unbuffered STDOUT to the console
|
||||
lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
|
||||
fp = _fdopen( hConHandle, "w" );
|
||||
*stdout = *fp;
|
||||
setvbuf( stdout, NULL, _IONBF, 0 );
|
||||
|
||||
// redirect unbuffered STDIN to the console
|
||||
lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE);
|
||||
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
|
||||
fp = _fdopen( hConHandle, "r" );
|
||||
*stdin = *fp;
|
||||
setvbuf( stdin, NULL, _IONBF, 0 );
|
||||
|
||||
// redirect unbuffered STDERR to the console
|
||||
lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE);
|
||||
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
|
||||
fp = _fdopen( hConHandle, "w" );
|
||||
*stderr = *fp;
|
||||
setvbuf( stderr, NULL, _IONBF, 0 );
|
||||
#endif
|
||||
}
|
||||
|
||||
Gnucash::Base::Base ()
|
||||
{
|
||||
#if !defined(G_THREADS_ENABLED) || defined(G_THREADS_IMPL_NONE)
|
||||
# error "No GLib thread implementation available!"
|
||||
#endif
|
||||
#ifdef ENABLE_BINRELOC
|
||||
{
|
||||
GError *binreloc_error = NULL;
|
||||
if (!gnc_gbr_init(&binreloc_error))
|
||||
{
|
||||
g_print("main: Error on gnc_gbr_init: %s\n", binreloc_error->message);
|
||||
g_error_free(binreloc_error);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
redirect_stdout ();
|
||||
|
||||
/* This should be called before gettext is initialized
|
||||
* The user may have configured a different language via
|
||||
* the environment file.
|
||||
*/
|
||||
#ifdef MAC_INTEGRATION
|
||||
set_mac_locale();
|
||||
#elif defined __MINGW32__
|
||||
set_win32_thread_locale();
|
||||
#endif
|
||||
gnc_environment_setup();
|
||||
#if ! defined MAC_INTEGRATION && ! defined __MINGW32__/* setlocale already done */
|
||||
sys_locale = g_strdup (setlocale (LC_ALL, ""));
|
||||
if (!sys_locale)
|
||||
{
|
||||
g_print ("The locale defined in the environment isn't supported. "
|
||||
"Falling back to the 'C' (US English) locale\n");
|
||||
g_setenv ("LC_ALL", "C", TRUE);
|
||||
setlocale (LC_ALL, "C");
|
||||
}
|
||||
#endif
|
||||
|
||||
auto localedir = gnc_path_get_localedir ();
|
||||
bindtextdomain(PROJECT_NAME, localedir);
|
||||
bindtextdomain("iso_4217", localedir); // For win32 to find currency name translations
|
||||
bind_textdomain_codeset("iso_4217", "UTF-8");
|
||||
textdomain(PROJECT_NAME);
|
||||
bind_textdomain_codeset(PROJECT_NAME, "UTF-8");
|
||||
g_free(localedir);
|
||||
}
|
||||
|
||||
/* Parse command line options, using GOption interface.
|
||||
* We can't let gtk_init_with_args do it because it fails
|
||||
* before parsing any arguments if the GUI can't be initialized.
|
||||
*/
|
||||
void
|
||||
Gnucash::Base::parse_command_line (int *argc, char ***argv)
|
||||
{
|
||||
#ifdef __MINGW64__
|
||||
wchar_t *tmp_log_to_filename = NULL;
|
||||
#else
|
||||
char *tmp_log_to_filename = NULL;
|
||||
#endif
|
||||
|
||||
GOptionEntry options[] =
|
||||
{
|
||||
{
|
||||
"version", 'v', 0, G_OPTION_ARG_NONE, &gnucash_show_version,
|
||||
N_("Show GnuCash version"), NULL
|
||||
},
|
||||
|
||||
{
|
||||
"debug", '\0', 0, G_OPTION_ARG_NONE, &debugging,
|
||||
N_("Enable debugging mode: provide deep detail in the logs.\nThis is equivalent to: --log \"=info\" --log \"qof=info\" --log \"gnc=info\""), NULL
|
||||
},
|
||||
|
||||
{
|
||||
"extra", '\0', 0, G_OPTION_ARG_NONE, &extra,
|
||||
N_("Enable extra/development/debugging features."), NULL
|
||||
},
|
||||
|
||||
{
|
||||
"log", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &log_flags,
|
||||
N_("Log level overrides, of the form \"modulename={debug,info,warn,crit,error}\"\nExamples: \"--log qof=debug\" or \"--log gnc.backend.file.sx=info\"\nThis can be invoked multiple times."),
|
||||
NULL
|
||||
},
|
||||
|
||||
{
|
||||
"logto", '\0', 0, G_OPTION_ARG_STRING, &tmp_log_to_filename,
|
||||
N_("File to log into; defaults to \"/tmp/gnucash.trace\"; can be \"stderr\" or \"stdout\"."),
|
||||
NULL
|
||||
},
|
||||
|
||||
{
|
||||
"nofile", '\0', 0, G_OPTION_ARG_NONE, &nofile,
|
||||
N_("Do not load the last file opened"), NULL
|
||||
},
|
||||
{
|
||||
"gsettings-prefix", '\0', 0, G_OPTION_ARG_STRING, &gsettings_prefix,
|
||||
N_("Set the prefix for gsettings schemas for gsettings queries. This can be useful to have a different settings tree while debugging."),
|
||||
/* Translators: Argument description for autohelp; see
|
||||
* http://developer.gnome.org/doc/API/2.0/glib/glib-Commandline-option-parser.html */
|
||||
N_("GSETTINGSPREFIX")
|
||||
},
|
||||
{
|
||||
"add-price-quotes", '\0', 0, G_OPTION_ARG_STRING, &add_quotes_file,
|
||||
N_("Add price quotes to given GnuCash datafile"),
|
||||
/* Translators: Argument description for autohelp; see
|
||||
* http://developer.gnome.org/doc/API/2.0/glib/glib-Commandline-option-parser.html */
|
||||
N_("FILE")
|
||||
},
|
||||
{
|
||||
"namespace", '\0', 0, G_OPTION_ARG_STRING, &namespace_regexp,
|
||||
N_("Regular expression determining which namespace commodities will be retrieved"),
|
||||
/* Translators: Argument description for autohelp; see
|
||||
* http://developer.gnome.org/doc/API/2.0/glib/glib-Commandline-option-parser.html */
|
||||
N_("REGEXP")
|
||||
},
|
||||
{
|
||||
G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &args_remaining, NULL, N_("[datafile]") },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
GError *error = NULL;
|
||||
GOptionContext *context = g_option_context_new (_("- GnuCash, accounting for personal and small business finance"));
|
||||
|
||||
g_option_context_add_main_entries (context, options, PROJECT_NAME);
|
||||
g_option_context_add_group (context, gtk_get_option_group(FALSE));
|
||||
if (!g_option_context_parse (context, argc, argv, &error))
|
||||
{
|
||||
g_printerr (_("%s\nRun '%s --help' to see a full list of available command line options.\n"),
|
||||
error->message, *argv[0]);
|
||||
g_error_free (error);
|
||||
exit (1);
|
||||
}
|
||||
g_option_context_free (context);
|
||||
|
||||
if (tmp_log_to_filename != NULL)
|
||||
{
|
||||
#ifdef __MINGW64__
|
||||
log_to_filename = g_utf16_to_utf8(tmp_log_to_filename, -1, NULL, NULL, NULL);
|
||||
g_free (tmp_log_to_filename);
|
||||
#else
|
||||
log_to_filename = tmp_log_to_filename;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (gnucash_show_version)
|
||||
{
|
||||
const char *format_string;
|
||||
if (is_development_version)
|
||||
format_string = _("GnuCash %s development version");
|
||||
else
|
||||
format_string = _("GnuCash %s");
|
||||
|
||||
g_print (format_string, gnc_version());
|
||||
g_print ("\n%s: %s\n", _("Build ID"), gnc_build_id());
|
||||
exit(0);
|
||||
}
|
||||
|
||||
gnc_prefs_set_debugging(debugging);
|
||||
gnc_prefs_set_extra(extra);
|
||||
|
||||
if (gsettings_prefix)
|
||||
gnc_gsettings_set_prefix(g_strdup(gsettings_prefix));
|
||||
|
||||
if (namespace_regexp)
|
||||
gnc_prefs_set_namespace_regexp(namespace_regexp);
|
||||
|
||||
if (args_remaining)
|
||||
file_to_load = args_remaining[0];
|
||||
}
|
||||
|
||||
const char*
|
||||
Gnucash::Base::get_file_to_load (void)
|
||||
{
|
||||
return file_to_load;
|
||||
}
|
||||
|
||||
int
|
||||
Gnucash::Base::get_no_file (void)
|
||||
{
|
||||
return nofile;
|
||||
}
|
||||
|
||||
const char*
|
||||
Gnucash::Base::get_quotes_file (void)
|
||||
{
|
||||
return add_quotes_file;
|
||||
}
|
||||
|
||||
void
|
||||
Gnucash::Base::start (void)
|
||||
{
|
||||
gnc_print_unstable_message();
|
||||
|
||||
/* Make sure gnucash' user data directory is properly set up
|
||||
* This must be done before any guile code is called as that would
|
||||
* fail the migration message */
|
||||
userdata_migration_msg = gnc_filepath_init();
|
||||
if (userdata_migration_msg)
|
||||
g_print("\n\n%s\n", userdata_migration_msg);
|
||||
|
||||
gnc_log_init (log_flags, log_to_filename);
|
||||
gnc_engine_init (0, NULL);
|
||||
|
||||
/* Write some locale details to the log to simplify debugging */
|
||||
PINFO ("System locale returned %s", sys_locale ? sys_locale : "(null)");
|
||||
PINFO ("Effective locale set to %s.", setlocale (LC_ALL, NULL));
|
||||
g_free (sys_locale);
|
||||
sys_locale = NULL;
|
||||
}
|
59
gnucash/gnucash-base.hpp
Normal file
59
gnucash/gnucash-base.hpp
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* gnucash-base.hpp -- Basic application object for gnucash binaries
|
||||
*
|
||||
* Copyright (C) 2020 Geert Janssens <geert@kobaltwit.be>
|
||||
*
|
||||
* 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@gnu.org
|
||||
*/
|
||||
|
||||
#ifndef GNUCASH_BASE_HPP
|
||||
#define GNUCASH_BASE_HPP
|
||||
|
||||
namespace Gnucash {
|
||||
|
||||
class Base
|
||||
{
|
||||
public:
|
||||
Base ();
|
||||
|
||||
void parse_command_line (int *argc, char ***argv);
|
||||
void start (void);
|
||||
|
||||
const char *get_file_to_load (void);
|
||||
int get_no_file (void);
|
||||
const char *get_quotes_file (void);
|
||||
|
||||
private:
|
||||
|
||||
/* Command-line option variables */
|
||||
int gnucash_show_version = 0;
|
||||
int debugging = 0;
|
||||
int extra = 0;
|
||||
gchar **log_flags = NULL;
|
||||
char *log_to_filename = NULL;
|
||||
int nofile = 0;
|
||||
const gchar *gsettings_prefix = NULL;
|
||||
const char *add_quotes_file = NULL;
|
||||
char *namespace_regexp = NULL;
|
||||
const char *file_to_load = NULL;
|
||||
gchar **args_remaining = NULL;
|
||||
gchar *sys_locale = NULL;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
@ -40,6 +40,8 @@
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#include "gnucash-base.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <gfec.h>
|
||||
#include <gnc-engine-guile.h>
|
||||
@ -80,83 +82,6 @@ static int is_development_version = FALSE;
|
||||
#define GNC_VCS ""
|
||||
#endif
|
||||
|
||||
/* Command-line option variables */
|
||||
static int gnucash_show_version = 0;
|
||||
static int debugging = 0;
|
||||
static int extra = 0;
|
||||
static gchar **log_flags = NULL;
|
||||
#ifdef __MINGW64__
|
||||
static wchar_t *log_to_filename = NULL;
|
||||
#else
|
||||
static char *log_to_filename = NULL;
|
||||
#endif
|
||||
static int nofile = 0;
|
||||
static const gchar *gsettings_prefix = NULL;
|
||||
static const char *add_quotes_file = NULL;
|
||||
static char *namespace_regexp = NULL;
|
||||
static const char *file_to_load = NULL;
|
||||
static gchar **args_remaining = NULL;
|
||||
static gchar *sys_locale = NULL;
|
||||
|
||||
static GOptionEntry options[] =
|
||||
{
|
||||
{
|
||||
"version", 'v', 0, G_OPTION_ARG_NONE, &gnucash_show_version,
|
||||
N_("Show GnuCash version"), NULL
|
||||
},
|
||||
|
||||
{
|
||||
"debug", '\0', 0, G_OPTION_ARG_NONE, &debugging,
|
||||
N_("Enable debugging mode: provide deep detail in the logs.\nThis is equivalent to: --log \"=info\" --log \"qof=info\" --log \"gnc=info\""), NULL
|
||||
},
|
||||
|
||||
{
|
||||
"extra", '\0', 0, G_OPTION_ARG_NONE, &extra,
|
||||
N_("Enable extra/development/debugging features."), NULL
|
||||
},
|
||||
|
||||
{
|
||||
"log", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &log_flags,
|
||||
N_("Log level overrides, of the form \"modulename={debug,info,warn,crit,error}\"\nExamples: \"--log qof=debug\" or \"--log gnc.backend.file.sx=info\"\nThis can be invoked multiple times."),
|
||||
NULL
|
||||
},
|
||||
|
||||
{
|
||||
"logto", '\0', 0, G_OPTION_ARG_STRING, &log_to_filename,
|
||||
N_("File to log into; defaults to \"/tmp/gnucash.trace\"; can be \"stderr\" or \"stdout\"."),
|
||||
NULL
|
||||
},
|
||||
|
||||
{
|
||||
"nofile", '\0', 0, G_OPTION_ARG_NONE, &nofile,
|
||||
N_("Do not load the last file opened"), NULL
|
||||
},
|
||||
{
|
||||
"gsettings-prefix", '\0', 0, G_OPTION_ARG_STRING, &gsettings_prefix,
|
||||
N_("Set the prefix for gsettings schemas for gsettings queries. This can be useful to have a different settings tree while debugging."),
|
||||
/* Translators: Argument description for autohelp; see
|
||||
http://developer.gnome.org/doc/API/2.0/glib/glib-Commandline-option-parser.html */
|
||||
N_("GSETTINGSPREFIX")
|
||||
},
|
||||
{
|
||||
"add-price-quotes", '\0', 0, G_OPTION_ARG_STRING, &add_quotes_file,
|
||||
N_("Add price quotes to given GnuCash datafile"),
|
||||
/* Translators: Argument description for autohelp; see
|
||||
http://developer.gnome.org/doc/API/2.0/glib/glib-Commandline-option-parser.html */
|
||||
N_("FILE")
|
||||
},
|
||||
{
|
||||
"namespace", '\0', 0, G_OPTION_ARG_STRING, &namespace_regexp,
|
||||
N_("Regular expression determining which namespace commodities will be retrieved"),
|
||||
/* Translators: Argument description for autohelp; see
|
||||
http://developer.gnome.org/doc/API/2.0/glib/glib-Commandline-option-parser.html */
|
||||
N_("REGEXP")
|
||||
},
|
||||
{
|
||||
G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &args_remaining, NULL, N_("[datafile]") },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static gchar *userdata_migration_msg = NULL;
|
||||
|
||||
static void
|
||||
@ -173,183 +98,6 @@ gnc_print_unstable_message(void)
|
||||
_("To find the last stable version, please refer to"), PACKAGE_URL);
|
||||
}
|
||||
|
||||
#ifdef MAC_INTEGRATION
|
||||
static void
|
||||
mac_set_currency_locale(NSLocale *locale, NSString *locale_str)
|
||||
{
|
||||
/* If the currency doesn't match the base locale, we need to find a locale that does match, because setlocale won't know what to do with just a currency identifier. */
|
||||
NSLocale *cur_locale = [[NSLocale alloc] initWithLocaleIdentifier: locale_str];
|
||||
if (![[locale objectForKey: NSLocaleCurrencyCode] isEqualToString:
|
||||
[cur_locale objectForKey: NSLocaleCurrencyCode]])
|
||||
{
|
||||
NSArray *all_locales = [NSLocale availableLocaleIdentifiers];
|
||||
NSEnumerator *locale_iter = [all_locales objectEnumerator];
|
||||
NSString *this_locale;
|
||||
NSString *currency = [locale objectForKey: NSLocaleCurrencyCode];
|
||||
NSString *money_locale = nil;
|
||||
while ((this_locale = (NSString*)[locale_iter nextObject]))
|
||||
{
|
||||
NSLocale *templocale = [[NSLocale alloc]
|
||||
initWithLocaleIdentifier: this_locale];
|
||||
if ([[templocale objectForKey: NSLocaleCurrencyCode]
|
||||
isEqualToString: currency])
|
||||
{
|
||||
money_locale = this_locale;
|
||||
[templocale release];
|
||||
break;
|
||||
}
|
||||
[templocale release];
|
||||
}
|
||||
if (money_locale)
|
||||
setlocale(LC_MONETARY, [money_locale UTF8String]);
|
||||
}
|
||||
[cur_locale release];
|
||||
}
|
||||
/* The locale that we got from AppKit isn't a supported POSIX one, so we need to
|
||||
* find something close. First see if we can find another locale for the
|
||||
* country; failing that, try the language. Ultimately fall back on en_US.
|
||||
*/
|
||||
static NSString*
|
||||
mac_find_close_country(NSString *locale_str, NSString *country_str,
|
||||
NSString *lang_str)
|
||||
{
|
||||
NSArray *all_locales = [NSLocale availableLocaleIdentifiers];
|
||||
NSEnumerator *locale_iter = [all_locales objectEnumerator];
|
||||
NSString *this_locale, *new_locale = nil;
|
||||
PWARN("Apple Locale is set to a value %s not supported"
|
||||
" by the C runtime", [locale_str UTF8String]);
|
||||
while ((this_locale = (NSString*)[locale_iter nextObject]))
|
||||
if ([[[NSLocale componentsFromLocaleIdentifier: this_locale]
|
||||
objectForKey: NSLocaleCountryCode]
|
||||
isEqualToString: country_str] &&
|
||||
setlocale (LC_ALL, [this_locale UTF8String]))
|
||||
{
|
||||
new_locale = this_locale;
|
||||
break;
|
||||
}
|
||||
if (!new_locale)
|
||||
while ((this_locale = (NSString*)[locale_iter nextObject]))
|
||||
if ([[[NSLocale componentsFromLocaleIdentifier: this_locale]
|
||||
objectForKey: NSLocaleLanguageCode]
|
||||
isEqualToString: lang_str] &&
|
||||
setlocale (LC_ALL, [this_locale UTF8String]))
|
||||
{
|
||||
new_locale = this_locale;
|
||||
break;
|
||||
}
|
||||
if (new_locale)
|
||||
locale_str = new_locale;
|
||||
else
|
||||
{
|
||||
locale_str = @"en_US";
|
||||
setlocale(LC_ALL, [locale_str UTF8String]);
|
||||
}
|
||||
PWARN("Using %s instead.", [locale_str UTF8String]);
|
||||
return locale_str;
|
||||
}
|
||||
|
||||
/* Language subgroups (e.g., US English) are reported in the form "ll-SS"
|
||||
* (e.g. again, "en-US"), not what gettext wants. We convert those to
|
||||
* old-style locales, which is easy for most cases. There are two where it
|
||||
* isn't, though: Simplified Chinese (zh-Hans) and traditional Chinese
|
||||
* (zh-Hant), which are normally assigned the locales zh_CN and zh_TW,
|
||||
* respectively. Those are handled specially.
|
||||
*/
|
||||
static NSString*
|
||||
mac_convert_complex_language(NSString* this_lang)
|
||||
{
|
||||
NSArray *elements = [this_lang componentsSeparatedByString: @"-"];
|
||||
if ([elements count] == 1)
|
||||
return this_lang;
|
||||
if ([[elements objectAtIndex: 0] isEqualToString: @"zh"]) {
|
||||
if ([[elements objectAtIndex: 1] isEqualToString: @"Hans"])
|
||||
this_lang = @"zh_CN";
|
||||
else
|
||||
this_lang = @"zh_TW";
|
||||
}
|
||||
else
|
||||
this_lang = [elements componentsJoinedByString: @"_"];
|
||||
return this_lang;
|
||||
}
|
||||
|
||||
static void
|
||||
mac_set_languages(NSArray* languages, NSString *lang_str)
|
||||
{
|
||||
/* Process the language list. */
|
||||
|
||||
const gchar *langs = NULL;
|
||||
NSEnumerator *lang_iter = [languages objectEnumerator];
|
||||
NSArray *new_languages = [NSArray array];
|
||||
NSString *this_lang = NULL;
|
||||
NSRange not_found = {NSNotFound, 0};
|
||||
while ((this_lang = [lang_iter nextObject])) {
|
||||
this_lang = [this_lang stringByTrimmingCharactersInSet:
|
||||
[NSCharacterSet characterSetWithCharactersInString: @"\""]];
|
||||
this_lang = mac_convert_complex_language(this_lang);
|
||||
new_languages = [new_languages arrayByAddingObject: this_lang];
|
||||
/* If it's an English language, add the "C" locale after it so that
|
||||
* any messages can default to it */
|
||||
if (!NSEqualRanges([this_lang rangeOfString: @"en"], not_found))
|
||||
new_languages = [new_languages arrayByAddingObject: @"C"];
|
||||
if (![new_languages containsObject: lang_str]) {
|
||||
NSArray *temp_array = [NSArray arrayWithObject: lang_str];
|
||||
new_languages = [temp_array arrayByAddingObjectsFromArray: new_languages];
|
||||
}
|
||||
langs = [[new_languages componentsJoinedByString:@":"] UTF8String];
|
||||
}
|
||||
if (langs && strlen(langs) > 0)
|
||||
{
|
||||
PWARN("Language list: %s", langs);
|
||||
g_setenv("LANGUAGE", langs, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
set_mac_locale()
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
|
||||
NSLocale *locale = [NSLocale currentLocale];
|
||||
NSString *lang_str, *country_str, *locale_str;
|
||||
NSArray *languages = [[defs arrayForKey: @"AppleLanguages"] retain];
|
||||
@try
|
||||
{
|
||||
lang_str = [locale objectForKey: NSLocaleLanguageCode];
|
||||
country_str = [locale objectForKey: NSLocaleCountryCode];
|
||||
locale_str = [[lang_str stringByAppendingString: @"_"]
|
||||
stringByAppendingString: country_str];
|
||||
}
|
||||
@catch (NSException *err)
|
||||
{
|
||||
PWARN("Locale detection raised error %s: %s. "
|
||||
"Check that your locale settings in "
|
||||
"System Preferences>Languages & Text are set correctly.",
|
||||
[[err name] UTF8String], [[err reason] UTF8String]);
|
||||
locale_str = @"_";
|
||||
}
|
||||
/* If we didn't get a valid current locale, the string will be just "_" */
|
||||
if ([locale_str isEqualToString: @"_"])
|
||||
locale_str = @"en_US";
|
||||
|
||||
lang_str = mac_convert_complex_language(lang_str);
|
||||
if (!setlocale(LC_ALL, [locale_str UTF8String]))
|
||||
locale_str = mac_find_close_country(locale_str, country_str, lang_str);
|
||||
if (g_getenv("LANG") == NULL)
|
||||
g_setenv("LANG", [locale_str UTF8String], TRUE);
|
||||
mac_set_currency_locale(locale, locale_str);
|
||||
/* Now call gnc_localeconv() to force creation of the app locale
|
||||
* before another call to setlocale messes it up. */
|
||||
gnc_localeconv ();
|
||||
/* Process the languages, including the one from the Apple locale. */
|
||||
if ([languages count] > 0)
|
||||
mac_set_languages(languages, lang_str);
|
||||
else
|
||||
g_setenv("LANGUAGE", [lang_str UTF8String], TRUE);
|
||||
[languages release];
|
||||
[pool drain];
|
||||
}
|
||||
#endif /* MAC_INTEGRATION */
|
||||
|
||||
static gboolean
|
||||
try_load_config_array(const gchar *fns[])
|
||||
{
|
||||
@ -424,55 +172,10 @@ load_user_config(void)
|
||||
try_load_config_array(stylesheet_files);
|
||||
}
|
||||
|
||||
/* Parse command line options, using GOption interface.
|
||||
* We can't let gtk_init_with_args do it because it fails
|
||||
* before parsing any arguments if the GUI can't be initialized.
|
||||
*/
|
||||
static void
|
||||
gnc_parse_command_line(int *argc, char ***argv)
|
||||
{
|
||||
GError *error = NULL;
|
||||
GOptionContext *context = g_option_context_new (_("- GnuCash, accounting for personal and small business finance"));
|
||||
|
||||
g_option_context_add_main_entries (context, options, PROJECT_NAME);
|
||||
g_option_context_add_group (context, gtk_get_option_group(FALSE));
|
||||
if (!g_option_context_parse (context, argc, argv, &error))
|
||||
{
|
||||
g_printerr (_("%s\nRun '%s --help' to see a full list of available command line options.\n"),
|
||||
error->message, *argv[0]);
|
||||
g_error_free (error);
|
||||
exit (1);
|
||||
}
|
||||
g_option_context_free (context);
|
||||
if (gnucash_show_version)
|
||||
{
|
||||
const char *format_string;
|
||||
if (is_development_version)
|
||||
format_string = _("GnuCash %s development version");
|
||||
else
|
||||
format_string = _("GnuCash %s");
|
||||
|
||||
g_print (format_string, gnc_version());
|
||||
g_print ("\n%s: %s\n", _("Build ID"), gnc_build_id());
|
||||
exit(0);
|
||||
}
|
||||
|
||||
gnc_prefs_set_debugging(debugging);
|
||||
gnc_prefs_set_extra(extra);
|
||||
|
||||
if (gsettings_prefix)
|
||||
gnc_gsettings_set_prefix(g_strdup(gsettings_prefix));
|
||||
|
||||
if (namespace_regexp)
|
||||
gnc_prefs_set_namespace_regexp(namespace_regexp);
|
||||
|
||||
if (args_remaining)
|
||||
file_to_load = args_remaining[0];
|
||||
}
|
||||
|
||||
static void
|
||||
inner_main_add_price_quotes(void *closure, int argc, char **argv)
|
||||
inner_main_add_price_quotes(void *data, int argc, char **argv)
|
||||
{
|
||||
const char* add_quotes_file = static_cast<const char*>(data);
|
||||
SCM mod, add_quotes, scm_book, scm_result = SCM_BOOL_F;
|
||||
QofSession *session = NULL;
|
||||
|
||||
@ -530,232 +233,17 @@ fail:
|
||||
gnc_shutdown(1);
|
||||
}
|
||||
|
||||
static void
|
||||
gnc_log_init()
|
||||
{
|
||||
if (log_to_filename != NULL)
|
||||
{
|
||||
#ifdef __MINGW64__
|
||||
char* filename = g_utf16_to_utf8(log_to_filename, -1, NULL, NULL, NULL);
|
||||
#else
|
||||
char* filename = log_to_filename;
|
||||
#endif
|
||||
qof_log_init_filename_special(filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* initialize logging to our file. */
|
||||
gchar *tracefilename;
|
||||
tracefilename = g_build_filename(g_get_tmp_dir(), "gnucash.trace",
|
||||
(gchar *)NULL);
|
||||
qof_log_init_filename(tracefilename);
|
||||
g_free(tracefilename);
|
||||
}
|
||||
|
||||
// set a reasonable default.
|
||||
qof_log_set_default(QOF_LOG_WARNING);
|
||||
|
||||
gnc_log_default();
|
||||
|
||||
if (gnc_prefs_is_debugging_enabled())
|
||||
{
|
||||
qof_log_set_level("", QOF_LOG_INFO);
|
||||
qof_log_set_level("qof", QOF_LOG_INFO);
|
||||
qof_log_set_level("gnc", QOF_LOG_INFO);
|
||||
}
|
||||
|
||||
{
|
||||
gchar *log_config_filename;
|
||||
log_config_filename = g_build_filename (gnc_userconfig_dir (),
|
||||
"log.conf", (char *)NULL);
|
||||
if (g_file_test(log_config_filename, G_FILE_TEST_EXISTS))
|
||||
qof_log_parse_log_config(log_config_filename);
|
||||
g_free(log_config_filename);
|
||||
}
|
||||
|
||||
if (log_flags != NULL)
|
||||
{
|
||||
int i = 0;
|
||||
for (; log_flags[i] != NULL; i++)
|
||||
{
|
||||
QofLogLevel level;
|
||||
gchar **parts = NULL;
|
||||
|
||||
gchar *log_opt = log_flags[i];
|
||||
parts = g_strsplit(log_opt, "=", 2);
|
||||
if (parts == NULL || parts[0] == NULL || parts[1] == NULL)
|
||||
{
|
||||
g_warning("string [%s] not parseable", log_opt);
|
||||
continue;
|
||||
}
|
||||
|
||||
level = qof_log_level_from_string(parts[1]);
|
||||
qof_log_set_level(parts[0], level);
|
||||
g_strfreev(parts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __MINGW32__
|
||||
/* 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
|
||||
* retrieve the Windows locale and set POSIX to match.
|
||||
*/
|
||||
static void
|
||||
set_win32_thread_locale()
|
||||
{
|
||||
WCHAR lpLocaleName[LOCALE_NAME_MAX_LENGTH];
|
||||
char *locale = NULL;
|
||||
|
||||
if (((locale = getenv ("LC_ALL")) != NULL && locale[0] != '\0') ||
|
||||
((locale = getenv ("LC_MESSAGES")) != NULL && locale[0] != '\0') ||
|
||||
((locale = getenv ("LANG")) != NULL && locale[0] != '\0'))
|
||||
{
|
||||
gunichar2* wlocale = NULL;
|
||||
int len = 0;
|
||||
len = strchr(locale, '.') - locale;
|
||||
locale[2] = '-';
|
||||
wlocale = g_utf8_to_utf16 (locale, len, NULL, NULL, NULL);
|
||||
if (IsValidLocaleName(wlocale))
|
||||
{
|
||||
LCID lcid = LocaleNameToLCID(wlocale, LOCALE_ALLOW_NEUTRAL_NAMES);
|
||||
SetThreadLocale(lcid);
|
||||
locale[2] = '_';
|
||||
setlocale (LC_ALL, locale);
|
||||
sys_locale = locale;
|
||||
g_free(wlocale);
|
||||
return;
|
||||
}
|
||||
g_free(locale);
|
||||
g_free(wlocale);
|
||||
}
|
||||
if (GetUserDefaultLocaleName(lpLocaleName, LOCALE_NAME_MAX_LENGTH))
|
||||
{
|
||||
sys_locale = g_utf16_to_utf8((gunichar2*)lpLocaleName,
|
||||
LOCALE_NAME_MAX_LENGTH,
|
||||
NULL, NULL, NULL);
|
||||
sys_locale[2] = '_';
|
||||
setlocale (LC_ALL, sys_locale);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Creates a console window on MSWindows to display stdout and stderr
|
||||
* when __MSWIN_CONSOLE__ is defined at the top of the file.
|
||||
*
|
||||
* Useful for displaying the diagnostics printed before logging is
|
||||
* started and if logging is redirected with --logto=stderr.
|
||||
*/
|
||||
static void
|
||||
redirect_stdout (void)
|
||||
{
|
||||
#if defined __MINGW32__ && __MSWIN_CONSOLE__
|
||||
static const WORD MAX_CONSOLE_LINES = 500;
|
||||
int hConHandle;
|
||||
long lStdHandle;
|
||||
CONSOLE_SCREEN_BUFFER_INFO coninfo;
|
||||
FILE *fp;
|
||||
|
||||
// allocate a console for this app
|
||||
AllocConsole();
|
||||
|
||||
// set the screen buffer to be big enough to let us scroll text
|
||||
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
|
||||
coninfo.dwSize.Y = MAX_CONSOLE_LINES;
|
||||
SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
|
||||
|
||||
// redirect unbuffered STDOUT to the console
|
||||
lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
|
||||
fp = _fdopen( hConHandle, "w" );
|
||||
*stdout = *fp;
|
||||
setvbuf( stdout, NULL, _IONBF, 0 );
|
||||
|
||||
// redirect unbuffered STDIN to the console
|
||||
lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE);
|
||||
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
|
||||
fp = _fdopen( hConHandle, "r" );
|
||||
*stdin = *fp;
|
||||
setvbuf( stdin, NULL, _IONBF, 0 );
|
||||
|
||||
// redirect unbuffered STDERR to the console
|
||||
lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE);
|
||||
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
|
||||
fp = _fdopen( hConHandle, "w" );
|
||||
*stderr = *fp;
|
||||
setvbuf( stderr, NULL, _IONBF, 0 );
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char ** argv)
|
||||
{
|
||||
gchar *localedir = gnc_path_get_localedir();
|
||||
#if !defined(G_THREADS_ENABLED) || defined(G_THREADS_IMPL_NONE)
|
||||
# error "No GLib thread implementation available!"
|
||||
#endif
|
||||
#ifdef ENABLE_BINRELOC
|
||||
{
|
||||
GError *binreloc_error = NULL;
|
||||
if (!gnc_gbr_init(&binreloc_error))
|
||||
{
|
||||
g_print("main: Error on gnc_gbr_init: %s\n", binreloc_error->message);
|
||||
g_error_free(binreloc_error);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
redirect_stdout ();
|
||||
Gnucash::Base application;
|
||||
|
||||
/* This should be called before gettext is initialized
|
||||
* The user may have configured a different language via
|
||||
* the environment file.
|
||||
*/
|
||||
#ifdef MAC_INTEGRATION
|
||||
set_mac_locale();
|
||||
#elif defined __MINGW32__
|
||||
set_win32_thread_locale();
|
||||
#endif
|
||||
gnc_environment_setup();
|
||||
#if ! defined MAC_INTEGRATION && ! defined __MINGW32__/* setlocale already done */
|
||||
sys_locale = g_strdup (setlocale (LC_ALL, ""));
|
||||
if (!sys_locale)
|
||||
{
|
||||
g_print ("The locale defined in the environment isn't supported. "
|
||||
"Falling back to the 'C' (US English) locale\n");
|
||||
g_setenv ("LC_ALL", "C", TRUE);
|
||||
setlocale (LC_ALL, "C");
|
||||
}
|
||||
#endif
|
||||
bindtextdomain(PROJECT_NAME, localedir);
|
||||
bindtextdomain("iso_4217", localedir); // For win32 to find currency name translations
|
||||
bind_textdomain_codeset("iso_4217", "UTF-8");
|
||||
textdomain(PROJECT_NAME);
|
||||
bind_textdomain_codeset(PROJECT_NAME, "UTF-8");
|
||||
g_free(localedir);
|
||||
application.parse_command_line (&argc, &argv);
|
||||
application.start ();
|
||||
|
||||
gnc_parse_command_line(&argc, &argv);
|
||||
gnc_print_unstable_message();
|
||||
|
||||
/* Make sure gnucash' user data directory is properly set up
|
||||
This must be done before any guile code is called as that would
|
||||
fail the migration message */
|
||||
userdata_migration_msg = gnc_filepath_init();
|
||||
if (userdata_migration_msg)
|
||||
g_print("\n\n%s\n", userdata_migration_msg);
|
||||
|
||||
gnc_log_init();
|
||||
gnc_engine_init (0, NULL);
|
||||
|
||||
/* Write some locale details to the log to simplify debugging */
|
||||
PINFO ("System locale returned %s", sys_locale ? sys_locale : "(null)");
|
||||
PINFO ("Effective locale set to %s.", setlocale (LC_ALL, NULL));
|
||||
g_free (sys_locale);
|
||||
sys_locale = NULL;
|
||||
|
||||
if (add_quotes_file)
|
||||
scm_boot_guile(argc, argv, inner_main_add_price_quotes, 0);
|
||||
auto quotes_file = application.get_quotes_file ();
|
||||
if (quotes_file)
|
||||
scm_boot_guile (argc, argv, inner_main_add_price_quotes, (void *)quotes_file);
|
||||
|
||||
exit(0); /* never reached */
|
||||
}
|
||||
|
@ -42,6 +42,8 @@
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#include "gnucash-base.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <dialog-new-user.h>
|
||||
#include <gfec.h>
|
||||
@ -97,83 +99,6 @@ static int is_development_version = FALSE;
|
||||
#define GNC_VCS ""
|
||||
#endif
|
||||
|
||||
/* Command-line option variables */
|
||||
static int gnucash_show_version = 0;
|
||||
static int debugging = 0;
|
||||
static int extra = 0;
|
||||
static gchar **log_flags = NULL;
|
||||
#ifdef __MINGW64__
|
||||
static wchar_t *log_to_filename = NULL;
|
||||
#else
|
||||
static char *log_to_filename = NULL;
|
||||
#endif
|
||||
static int nofile = 0;
|
||||
static const gchar *gsettings_prefix = NULL;
|
||||
static const char *add_quotes_file = NULL;
|
||||
static char *namespace_regexp = NULL;
|
||||
static const char *file_to_load = NULL;
|
||||
static gchar **args_remaining = NULL;
|
||||
static gchar *sys_locale = NULL;
|
||||
|
||||
static GOptionEntry options[] =
|
||||
{
|
||||
{
|
||||
"version", 'v', 0, G_OPTION_ARG_NONE, &gnucash_show_version,
|
||||
N_("Show GnuCash version"), NULL
|
||||
},
|
||||
|
||||
{
|
||||
"debug", '\0', 0, G_OPTION_ARG_NONE, &debugging,
|
||||
N_("Enable debugging mode: provide deep detail in the logs.\nThis is equivalent to: --log \"=info\" --log \"qof=info\" --log \"gnc=info\""), NULL
|
||||
},
|
||||
|
||||
{
|
||||
"extra", '\0', 0, G_OPTION_ARG_NONE, &extra,
|
||||
N_("Enable extra/development/debugging features."), NULL
|
||||
},
|
||||
|
||||
{
|
||||
"log", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &log_flags,
|
||||
N_("Log level overrides, of the form \"modulename={debug,info,warn,crit,error}\"\nExamples: \"--log qof=debug\" or \"--log gnc.backend.file.sx=info\"\nThis can be invoked multiple times."),
|
||||
NULL
|
||||
},
|
||||
|
||||
{
|
||||
"logto", '\0', 0, G_OPTION_ARG_STRING, &log_to_filename,
|
||||
N_("File to log into; defaults to \"/tmp/gnucash.trace\"; can be \"stderr\" or \"stdout\"."),
|
||||
NULL
|
||||
},
|
||||
|
||||
{
|
||||
"nofile", '\0', 0, G_OPTION_ARG_NONE, &nofile,
|
||||
N_("Do not load the last file opened"), NULL
|
||||
},
|
||||
{
|
||||
"gsettings-prefix", '\0', 0, G_OPTION_ARG_STRING, &gsettings_prefix,
|
||||
N_("Set the prefix for gsettings schemas for gsettings queries. This can be useful to have a different settings tree while debugging."),
|
||||
/* Translators: Argument description for autohelp; see
|
||||
http://developer.gnome.org/doc/API/2.0/glib/glib-Commandline-option-parser.html */
|
||||
N_("GSETTINGSPREFIX")
|
||||
},
|
||||
{
|
||||
"add-price-quotes", '\0', 0, G_OPTION_ARG_STRING, &add_quotes_file,
|
||||
N_("Add price quotes to given GnuCash datafile"),
|
||||
/* Translators: Argument description for autohelp; see
|
||||
http://developer.gnome.org/doc/API/2.0/glib/glib-Commandline-option-parser.html */
|
||||
N_("FILE")
|
||||
},
|
||||
{
|
||||
"namespace", '\0', 0, G_OPTION_ARG_STRING, &namespace_regexp,
|
||||
N_("Regular expression determining which namespace commodities will be retrieved"),
|
||||
/* Translators: Argument description for autohelp; see
|
||||
http://developer.gnome.org/doc/API/2.0/glib/glib-Commandline-option-parser.html */
|
||||
N_("REGEXP")
|
||||
},
|
||||
{
|
||||
G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &args_remaining, NULL, N_("[datafile]") },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static gchar *userdata_migration_msg = NULL;
|
||||
|
||||
static void
|
||||
@ -190,183 +115,6 @@ gnc_print_unstable_message(void)
|
||||
_("To find the last stable version, please refer to"), PACKAGE_URL);
|
||||
}
|
||||
|
||||
#ifdef MAC_INTEGRATION
|
||||
static void
|
||||
mac_set_currency_locale(NSLocale *locale, NSString *locale_str)
|
||||
{
|
||||
/* If the currency doesn't match the base locale, we need to find a locale that does match, because setlocale won't know what to do with just a currency identifier. */
|
||||
NSLocale *cur_locale = [[NSLocale alloc] initWithLocaleIdentifier: locale_str];
|
||||
if (![[locale objectForKey: NSLocaleCurrencyCode] isEqualToString:
|
||||
[cur_locale objectForKey: NSLocaleCurrencyCode]])
|
||||
{
|
||||
NSArray *all_locales = [NSLocale availableLocaleIdentifiers];
|
||||
NSEnumerator *locale_iter = [all_locales objectEnumerator];
|
||||
NSString *this_locale;
|
||||
NSString *currency = [locale objectForKey: NSLocaleCurrencyCode];
|
||||
NSString *money_locale = nil;
|
||||
while ((this_locale = (NSString*)[locale_iter nextObject]))
|
||||
{
|
||||
NSLocale *templocale = [[NSLocale alloc]
|
||||
initWithLocaleIdentifier: this_locale];
|
||||
if ([[templocale objectForKey: NSLocaleCurrencyCode]
|
||||
isEqualToString: currency])
|
||||
{
|
||||
money_locale = this_locale;
|
||||
[templocale release];
|
||||
break;
|
||||
}
|
||||
[templocale release];
|
||||
}
|
||||
if (money_locale)
|
||||
setlocale(LC_MONETARY, [money_locale UTF8String]);
|
||||
}
|
||||
[cur_locale release];
|
||||
}
|
||||
/* The locale that we got from AppKit isn't a supported POSIX one, so we need to
|
||||
* find something close. First see if we can find another locale for the
|
||||
* country; failing that, try the language. Ultimately fall back on en_US.
|
||||
*/
|
||||
static NSString*
|
||||
mac_find_close_country(NSString *locale_str, NSString *country_str,
|
||||
NSString *lang_str)
|
||||
{
|
||||
NSArray *all_locales = [NSLocale availableLocaleIdentifiers];
|
||||
NSEnumerator *locale_iter = [all_locales objectEnumerator];
|
||||
NSString *this_locale, *new_locale = nil;
|
||||
PWARN("Apple Locale is set to a value %s not supported"
|
||||
" by the C runtime", [locale_str UTF8String]);
|
||||
while ((this_locale = (NSString*)[locale_iter nextObject]))
|
||||
if ([[[NSLocale componentsFromLocaleIdentifier: this_locale]
|
||||
objectForKey: NSLocaleCountryCode]
|
||||
isEqualToString: country_str] &&
|
||||
setlocale (LC_ALL, [this_locale UTF8String]))
|
||||
{
|
||||
new_locale = this_locale;
|
||||
break;
|
||||
}
|
||||
if (!new_locale)
|
||||
while ((this_locale = (NSString*)[locale_iter nextObject]))
|
||||
if ([[[NSLocale componentsFromLocaleIdentifier: this_locale]
|
||||
objectForKey: NSLocaleLanguageCode]
|
||||
isEqualToString: lang_str] &&
|
||||
setlocale (LC_ALL, [this_locale UTF8String]))
|
||||
{
|
||||
new_locale = this_locale;
|
||||
break;
|
||||
}
|
||||
if (new_locale)
|
||||
locale_str = new_locale;
|
||||
else
|
||||
{
|
||||
locale_str = @"en_US";
|
||||
setlocale(LC_ALL, [locale_str UTF8String]);
|
||||
}
|
||||
PWARN("Using %s instead.", [locale_str UTF8String]);
|
||||
return locale_str;
|
||||
}
|
||||
|
||||
/* Language subgroups (e.g., US English) are reported in the form "ll-SS"
|
||||
* (e.g. again, "en-US"), not what gettext wants. We convert those to
|
||||
* old-style locales, which is easy for most cases. There are two where it
|
||||
* isn't, though: Simplified Chinese (zh-Hans) and traditional Chinese
|
||||
* (zh-Hant), which are normally assigned the locales zh_CN and zh_TW,
|
||||
* respectively. Those are handled specially.
|
||||
*/
|
||||
static NSString*
|
||||
mac_convert_complex_language(NSString* this_lang)
|
||||
{
|
||||
NSArray *elements = [this_lang componentsSeparatedByString: @"-"];
|
||||
if ([elements count] == 1)
|
||||
return this_lang;
|
||||
if ([[elements objectAtIndex: 0] isEqualToString: @"zh"]) {
|
||||
if ([[elements objectAtIndex: 1] isEqualToString: @"Hans"])
|
||||
this_lang = @"zh_CN";
|
||||
else
|
||||
this_lang = @"zh_TW";
|
||||
}
|
||||
else
|
||||
this_lang = [elements componentsJoinedByString: @"_"];
|
||||
return this_lang;
|
||||
}
|
||||
|
||||
static void
|
||||
mac_set_languages(NSArray* languages, NSString *lang_str)
|
||||
{
|
||||
/* Process the language list. */
|
||||
|
||||
const gchar *langs = NULL;
|
||||
NSEnumerator *lang_iter = [languages objectEnumerator];
|
||||
NSArray *new_languages = [NSArray array];
|
||||
NSString *this_lang = NULL;
|
||||
NSRange not_found = {NSNotFound, 0};
|
||||
while ((this_lang = [lang_iter nextObject])) {
|
||||
this_lang = [this_lang stringByTrimmingCharactersInSet:
|
||||
[NSCharacterSet characterSetWithCharactersInString: @"\""]];
|
||||
this_lang = mac_convert_complex_language(this_lang);
|
||||
new_languages = [new_languages arrayByAddingObject: this_lang];
|
||||
/* If it's an English language, add the "C" locale after it so that
|
||||
* any messages can default to it */
|
||||
if (!NSEqualRanges([this_lang rangeOfString: @"en"], not_found))
|
||||
new_languages = [new_languages arrayByAddingObject: @"C"];
|
||||
if (![new_languages containsObject: lang_str]) {
|
||||
NSArray *temp_array = [NSArray arrayWithObject: lang_str];
|
||||
new_languages = [temp_array arrayByAddingObjectsFromArray: new_languages];
|
||||
}
|
||||
langs = [[new_languages componentsJoinedByString:@":"] UTF8String];
|
||||
}
|
||||
if (langs && strlen(langs) > 0)
|
||||
{
|
||||
PWARN("Language list: %s", langs);
|
||||
g_setenv("LANGUAGE", langs, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
set_mac_locale()
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
|
||||
NSLocale *locale = [NSLocale currentLocale];
|
||||
NSString *lang_str, *country_str, *locale_str;
|
||||
NSArray *languages = [[defs arrayForKey: @"AppleLanguages"] retain];
|
||||
@try
|
||||
{
|
||||
lang_str = [locale objectForKey: NSLocaleLanguageCode];
|
||||
country_str = [locale objectForKey: NSLocaleCountryCode];
|
||||
locale_str = [[lang_str stringByAppendingString: @"_"]
|
||||
stringByAppendingString: country_str];
|
||||
}
|
||||
@catch (NSException *err)
|
||||
{
|
||||
PWARN("Locale detection raised error %s: %s. "
|
||||
"Check that your locale settings in "
|
||||
"System Preferences>Languages & Text are set correctly.",
|
||||
[[err name] UTF8String], [[err reason] UTF8String]);
|
||||
locale_str = @"_";
|
||||
}
|
||||
/* If we didn't get a valid current locale, the string will be just "_" */
|
||||
if ([locale_str isEqualToString: @"_"])
|
||||
locale_str = @"en_US";
|
||||
|
||||
lang_str = mac_convert_complex_language(lang_str);
|
||||
if (!setlocale(LC_ALL, [locale_str UTF8String]))
|
||||
locale_str = mac_find_close_country(locale_str, country_str, lang_str);
|
||||
if (g_getenv("LANG") == NULL)
|
||||
g_setenv("LANG", [locale_str UTF8String], TRUE);
|
||||
mac_set_currency_locale(locale, locale_str);
|
||||
/* Now call gnc_localeconv() to force creation of the app locale
|
||||
* before another call to setlocale messes it up. */
|
||||
gnc_localeconv ();
|
||||
/* Process the languages, including the one from the Apple locale. */
|
||||
if ([languages count] > 0)
|
||||
mac_set_languages(languages, lang_str);
|
||||
else
|
||||
g_setenv("LANGUAGE", [lang_str UTF8String], TRUE);
|
||||
[languages release];
|
||||
[pool drain];
|
||||
}
|
||||
#endif /* MAC_INTEGRATION */
|
||||
|
||||
static gboolean
|
||||
try_load_config_array(const gchar *fns[])
|
||||
{
|
||||
@ -441,52 +189,6 @@ load_user_config(void)
|
||||
try_load_config_array(stylesheet_files);
|
||||
}
|
||||
|
||||
/* Parse command line options, using GOption interface.
|
||||
* We can't let gtk_init_with_args do it because it fails
|
||||
* before parsing any arguments if the GUI can't be initialized.
|
||||
*/
|
||||
static void
|
||||
gnc_parse_command_line(int *argc, char ***argv)
|
||||
{
|
||||
GError *error = NULL;
|
||||
GOptionContext *context = g_option_context_new (_("- GnuCash, accounting for personal and small business finance"));
|
||||
|
||||
g_option_context_add_main_entries (context, options, PROJECT_NAME);
|
||||
g_option_context_add_group (context, gtk_get_option_group(FALSE));
|
||||
if (!g_option_context_parse (context, argc, argv, &error))
|
||||
{
|
||||
g_printerr (_("%s\nRun '%s --help' to see a full list of available command line options.\n"),
|
||||
error->message, *argv[0]);
|
||||
g_error_free (error);
|
||||
exit (1);
|
||||
}
|
||||
g_option_context_free (context);
|
||||
if (gnucash_show_version)
|
||||
{
|
||||
const char *format_string;
|
||||
if (is_development_version)
|
||||
format_string = _("GnuCash %s development version");
|
||||
else
|
||||
format_string = _("GnuCash %s");
|
||||
|
||||
g_print (format_string, gnc_version());
|
||||
g_print ("\n%s: %s\n", _("Build ID"), gnc_build_id());
|
||||
exit(0);
|
||||
}
|
||||
|
||||
gnc_prefs_set_debugging(debugging);
|
||||
gnc_prefs_set_extra(extra);
|
||||
|
||||
if (gsettings_prefix)
|
||||
gnc_gsettings_set_prefix(g_strdup(gsettings_prefix));
|
||||
|
||||
if (namespace_regexp)
|
||||
gnc_prefs_set_namespace_regexp(namespace_regexp);
|
||||
|
||||
if (args_remaining)
|
||||
file_to_load = args_remaining[0];
|
||||
}
|
||||
|
||||
static void
|
||||
load_gnucash_plugins()
|
||||
{
|
||||
@ -529,8 +231,9 @@ load_gnucash_modules()
|
||||
}
|
||||
|
||||
static void
|
||||
inner_main_add_price_quotes(void *closure, int argc, char **argv)
|
||||
inner_main_add_price_quotes(void *data, int argc, char **argv)
|
||||
{
|
||||
const char* add_quotes_file = static_cast<const char*>(data);
|
||||
SCM mod, add_quotes, scm_book, scm_result = SCM_BOOL_F;
|
||||
QofSession *session = NULL;
|
||||
|
||||
@ -589,7 +292,7 @@ fail:
|
||||
}
|
||||
|
||||
static char *
|
||||
get_file_to_load()
|
||||
get_file_to_load (const char* file_to_load)
|
||||
{
|
||||
if (file_to_load)
|
||||
return g_strdup(file_to_load);
|
||||
@ -600,9 +303,15 @@ get_file_to_load()
|
||||
|
||||
extern SCM scm_init_sw_gnome_module(void);
|
||||
|
||||
struct t_file_spec {
|
||||
int nofile;
|
||||
const char *file_to_load;
|
||||
};
|
||||
|
||||
static void
|
||||
inner_main (void *closure, int argc, char **argv)
|
||||
inner_main (void *data, int argc, char **argv)
|
||||
{
|
||||
auto user_file_spec = static_cast<t_file_spec*>(data);
|
||||
SCM main_mod;
|
||||
char* fn = NULL;
|
||||
|
||||
@ -648,7 +357,7 @@ inner_main (void *closure, int argc, char **argv)
|
||||
|
||||
gnc_hook_run(HOOK_STARTUP, NULL);
|
||||
|
||||
if (!nofile && (fn = get_file_to_load()) && *fn )
|
||||
if (!user_file_spec->nofile && (fn = get_file_to_load (user_file_spec->file_to_load)) && *fn )
|
||||
{
|
||||
gnc_update_splash_screen(_("Loading data..."), GNC_SPLASH_PERCENTAGE_UNKNOWN);
|
||||
gnc_file_open_file(gnc_get_splash_screen(), fn, /*open_readonly*/ FALSE);
|
||||
@ -687,236 +396,20 @@ inner_main (void *closure, int argc, char **argv)
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
gnc_log_init()
|
||||
{
|
||||
if (log_to_filename != NULL)
|
||||
{
|
||||
#ifdef __MINGW64__
|
||||
char* filename = g_utf16_to_utf8(log_to_filename, -1, NULL, NULL, NULL);
|
||||
#else
|
||||
char* filename = log_to_filename;
|
||||
#endif
|
||||
qof_log_init_filename_special(filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* initialize logging to our file. */
|
||||
gchar *tracefilename;
|
||||
tracefilename = g_build_filename(g_get_tmp_dir(), "gnucash.trace",
|
||||
(gchar *)NULL);
|
||||
qof_log_init_filename(tracefilename);
|
||||
g_free(tracefilename);
|
||||
}
|
||||
|
||||
// set a reasonable default.
|
||||
qof_log_set_default(QOF_LOG_WARNING);
|
||||
|
||||
gnc_log_default();
|
||||
|
||||
if (gnc_prefs_is_debugging_enabled())
|
||||
{
|
||||
qof_log_set_level("", QOF_LOG_INFO);
|
||||
qof_log_set_level("qof", QOF_LOG_INFO);
|
||||
qof_log_set_level("gnc", QOF_LOG_INFO);
|
||||
}
|
||||
|
||||
{
|
||||
gchar *log_config_filename;
|
||||
log_config_filename = g_build_filename (gnc_userconfig_dir (),
|
||||
"log.conf", (char *)NULL);
|
||||
if (g_file_test(log_config_filename, G_FILE_TEST_EXISTS))
|
||||
qof_log_parse_log_config(log_config_filename);
|
||||
g_free(log_config_filename);
|
||||
}
|
||||
|
||||
if (log_flags != NULL)
|
||||
{
|
||||
int i = 0;
|
||||
for (; log_flags[i] != NULL; i++)
|
||||
{
|
||||
QofLogLevel level;
|
||||
gchar **parts = NULL;
|
||||
|
||||
gchar *log_opt = log_flags[i];
|
||||
parts = g_strsplit(log_opt, "=", 2);
|
||||
if (parts == NULL || parts[0] == NULL || parts[1] == NULL)
|
||||
{
|
||||
g_warning("string [%s] not parseable", log_opt);
|
||||
continue;
|
||||
}
|
||||
|
||||
level = qof_log_level_from_string(parts[1]);
|
||||
qof_log_set_level(parts[0], level);
|
||||
g_strfreev(parts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __MINGW32__
|
||||
/* 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
|
||||
* retrieve the Windows locale and set POSIX to match.
|
||||
*/
|
||||
static void
|
||||
set_win32_thread_locale()
|
||||
{
|
||||
WCHAR lpLocaleName[LOCALE_NAME_MAX_LENGTH];
|
||||
char *locale = NULL;
|
||||
|
||||
if (((locale = getenv ("LC_ALL")) != NULL && locale[0] != '\0') ||
|
||||
((locale = getenv ("LC_MESSAGES")) != NULL && locale[0] != '\0') ||
|
||||
((locale = getenv ("LANG")) != NULL && locale[0] != '\0'))
|
||||
{
|
||||
gunichar2* wlocale = NULL;
|
||||
int len = 0;
|
||||
len = strchr(locale, '.') - locale;
|
||||
locale[2] = '-';
|
||||
wlocale = g_utf8_to_utf16 (locale, len, NULL, NULL, NULL);
|
||||
if (IsValidLocaleName(wlocale))
|
||||
{
|
||||
LCID lcid = LocaleNameToLCID(wlocale, LOCALE_ALLOW_NEUTRAL_NAMES);
|
||||
SetThreadLocale(lcid);
|
||||
locale[2] = '_';
|
||||
setlocale (LC_ALL, locale);
|
||||
sys_locale = locale;
|
||||
g_free(wlocale);
|
||||
return;
|
||||
}
|
||||
g_free(locale);
|
||||
g_free(wlocale);
|
||||
}
|
||||
if (GetUserDefaultLocaleName(lpLocaleName, LOCALE_NAME_MAX_LENGTH))
|
||||
{
|
||||
sys_locale = g_utf16_to_utf8((gunichar2*)lpLocaleName,
|
||||
LOCALE_NAME_MAX_LENGTH,
|
||||
NULL, NULL, NULL);
|
||||
sys_locale[2] = '_';
|
||||
setlocale (LC_ALL, sys_locale);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Creates a console window on MSWindows to display stdout and stderr
|
||||
* when __MSWIN_CONSOLE__ is defined at the top of the file.
|
||||
*
|
||||
* Useful for displaying the diagnostics printed before logging is
|
||||
* started and if logging is redirected with --logto=stderr.
|
||||
*/
|
||||
static void
|
||||
redirect_stdout (void)
|
||||
{
|
||||
#if defined __MINGW32__ && __MSWIN_CONSOLE__
|
||||
static const WORD MAX_CONSOLE_LINES = 500;
|
||||
int hConHandle;
|
||||
long lStdHandle;
|
||||
CONSOLE_SCREEN_BUFFER_INFO coninfo;
|
||||
FILE *fp;
|
||||
|
||||
// allocate a console for this app
|
||||
AllocConsole();
|
||||
|
||||
// set the screen buffer to be big enough to let us scroll text
|
||||
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
|
||||
coninfo.dwSize.Y = MAX_CONSOLE_LINES;
|
||||
SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
|
||||
|
||||
// redirect unbuffered STDOUT to the console
|
||||
lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
|
||||
fp = _fdopen( hConHandle, "w" );
|
||||
*stdout = *fp;
|
||||
setvbuf( stdout, NULL, _IONBF, 0 );
|
||||
|
||||
// redirect unbuffered STDIN to the console
|
||||
lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE);
|
||||
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
|
||||
fp = _fdopen( hConHandle, "r" );
|
||||
*stdin = *fp;
|
||||
setvbuf( stdin, NULL, _IONBF, 0 );
|
||||
|
||||
// redirect unbuffered STDERR to the console
|
||||
lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE);
|
||||
hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
|
||||
fp = _fdopen( hConHandle, "w" );
|
||||
*stderr = *fp;
|
||||
setvbuf( stderr, NULL, _IONBF, 0 );
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char ** argv)
|
||||
{
|
||||
gchar *localedir;
|
||||
#if !defined(G_THREADS_ENABLED) || defined(G_THREADS_IMPL_NONE)
|
||||
# error "No GLib thread implementation available!"
|
||||
#endif
|
||||
#ifdef ENABLE_BINRELOC
|
||||
{
|
||||
GError *binreloc_error = NULL;
|
||||
if (!gnc_gbr_init(&binreloc_error))
|
||||
{
|
||||
g_print("main: Error on gnc_gbr_init: %s\n", binreloc_error->message);
|
||||
g_error_free(binreloc_error);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
redirect_stdout ();
|
||||
Gnucash::Base application;
|
||||
|
||||
/* This should be called before gettext is initialized
|
||||
* The user may have configured a different language via
|
||||
* the environment file.
|
||||
*/
|
||||
#ifdef MAC_INTEGRATION
|
||||
set_mac_locale();
|
||||
#elif defined __MINGW32__
|
||||
set_win32_thread_locale();
|
||||
#endif
|
||||
gnc_environment_setup();
|
||||
#if ! defined MAC_INTEGRATION && ! defined __MINGW32__/* setlocale already done */
|
||||
sys_locale = g_strdup (setlocale (LC_ALL, ""));
|
||||
if (!sys_locale)
|
||||
{
|
||||
g_print ("The locale defined in the environment isn't supported. "
|
||||
"Falling back to the 'C' (US English) locale\n");
|
||||
g_setenv ("LC_ALL", "C", TRUE);
|
||||
setlocale (LC_ALL, "C");
|
||||
}
|
||||
#endif
|
||||
localedir = gnc_path_get_localedir();
|
||||
bindtextdomain(PROJECT_NAME, localedir);
|
||||
bindtextdomain("iso_4217", localedir); // For win32 to find currency name translations
|
||||
bind_textdomain_codeset("iso_4217", "UTF-8");
|
||||
textdomain(PROJECT_NAME);
|
||||
bind_textdomain_codeset(PROJECT_NAME, "UTF-8");
|
||||
g_free(localedir);
|
||||
|
||||
gnc_parse_command_line(&argc, &argv);
|
||||
gnc_print_unstable_message();
|
||||
|
||||
/* Make sure gnucash' user data directory is properly set up
|
||||
This must be done before any guile code is called as that would
|
||||
fail the migration message */
|
||||
userdata_migration_msg = gnc_filepath_init();
|
||||
if (userdata_migration_msg)
|
||||
g_print("\n\n%s\n", userdata_migration_msg);
|
||||
|
||||
gnc_log_init();
|
||||
gnc_engine_init (0, NULL);
|
||||
|
||||
/* Write some locale details to the log to simplify debugging */
|
||||
PINFO ("System locale returned %s", sys_locale ? sys_locale : "(null)");
|
||||
PINFO ("Effective locale set to %s.", setlocale (LC_ALL, NULL));
|
||||
g_free (sys_locale);
|
||||
sys_locale = NULL;
|
||||
application.parse_command_line (&argc, &argv);
|
||||
application.start();
|
||||
|
||||
/* If asked via a command line parameter, fetch quotes only */
|
||||
if (add_quotes_file)
|
||||
auto quotes_file = application.get_quotes_file ();
|
||||
if (quotes_file)
|
||||
{
|
||||
scm_boot_guile(argc, argv, inner_main_add_price_quotes, 0);
|
||||
exit(0); /* never reached */
|
||||
scm_boot_guile (argc, argv, inner_main_add_price_quotes, (void*) quotes_file);
|
||||
exit (0); /* never reached */
|
||||
}
|
||||
|
||||
/* We need to initialize gtk before looking up all modules */
|
||||
@ -934,6 +427,8 @@ main(int argc, char ** argv)
|
||||
gnc_module_system_init();
|
||||
|
||||
gnc_gui_init();
|
||||
scm_boot_guile(argc, argv, inner_main, 0);
|
||||
|
||||
auto user_file_spec = t_file_spec {application.get_no_file (), application.get_file_to_load ()};
|
||||
scm_boot_guile (argc, argv, inner_main, &user_file_spec);
|
||||
exit(0); /* never reached */
|
||||
}
|
||||
|
@ -210,6 +210,7 @@ gnucash/gnome-utils/print-session.c
|
||||
gnucash/gnome-utils/search-param.c
|
||||
gnucash/gnome-utils/tree-view-utils.c
|
||||
gnucash/gnome-utils/window-main-summarybar.c
|
||||
gnucash/gnucash-base.cpp
|
||||
gnucash/gnucash-cli.cpp
|
||||
gnucash/gnucash.cpp
|
||||
gnucash/gschemas/org.gnucash.dialogs.business.gschema.xml.in
|
||||
|
Loading…
Reference in New Issue
Block a user