GnuCash fec5643+
Loading...
Searching...
No Matches
gnucash-core-app.cpp
1/*
2 * gnucash-core-app.cpp -- Basic application object for gnucash binaries
3 *
4 * Copyright (C) 2020 Geert Janssens <geert@kobaltwit.be>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, contact:
18 *
19 * Free Software Foundation Voice: +1-617-542-5942
20 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
21 * Boston, MA 02110-1301, USA gnu@gnu.org
22 */
23#include <config.h>
24
25#include <libguile.h>
26#include <guile-mappings.h>
27#ifdef __MINGW32__
28#include <Windows.h>
29#include <fcntl.h>
30#endif
31
32#include "gnucash-core-app.hpp"
33
34#include <glib/gi18n.h>
35#include <binreloc.h>
36#include <gnc-engine.h>
37#include <gfec.h>
38#include <gnc-environment.h>
39#include <gnc-filepath-utils.h>
40#include <gnc-locale-utils.h>
41#include <gnc-path.h>
42#include <gnc-prefs.h>
43#include <gnc-version.h>
44#include "gnucash-locale-platform.h"
45
46#include <boost/algorithm/string.hpp>
47#include <boost/locale.hpp>
48#include <iostream>
49#include <string>
50#include <vector>
51#include <gnc-report.h>
52
53namespace bl = boost::locale;
54
55/* This static indicates the debugging module that this .o belongs to. */
56static QofLogModule log_module = GNC_MOD_GUI;
57
58#include <libintl.h>
59#include <locale.h>
60#include <gnc-locale-utils.hpp>
61
62/* GNC_VCS is defined whenever we're building from a git work tree */
63#ifdef GNC_VCS
64constexpr int is_development_version = TRUE;
65#else
66constexpr int is_development_version = FALSE;
67#define GNC_VCS ""
68#endif
69
70static gchar *userdata_migration_msg = NULL;
71
72static void
73gnc_print_unstable_message(void)
74{
75 if (!is_development_version) return;
76
77 std::cerr << _("This is a development version. It may or may not work.") << "\n"
78 << _("Report bugs and other problems to gnucash-devel@gnucash.org") << "\n"
79 /* Translators: {1} will be replaced with an URL*/
80 << bl::format (std::string{_("You can also lookup and file bug reports at {1}")}) % PACKAGE_BUGREPORT << "\n"
81 /* Translators: {1} will be replaced with an URL*/
82 << bl::format (std::string{_("To find the last stable version, please refer to {1}")}) % PACKAGE_URL << "\n";
83}
84
85void
86Gnucash::gnc_load_scm_config (MessageCb update_message_cb)
87{
88 static auto is_system_config_loaded = false;
89 if (!is_system_config_loaded)
90 {
91 /* Translators: Guile is the programming language of the reports */
92 auto msg = _("Loading system wide Guile extensions…");
93 update_message_cb (msg);
94 auto system_config_dir = gnc_path_get_pkgsysconfdir ();
95 auto system_config = g_build_filename (system_config_dir, "config", nullptr);
96 is_system_config_loaded = gfec_try_load (system_config);
97 g_free (system_config_dir);
98 g_free (system_config);
99 }
100
101 static auto is_user_config_loaded = false;
102 if (!is_user_config_loaded)
103 {
104 auto msg = _("Loading user specific Guile extensions…");
105 update_message_cb (msg);
106 auto config_filename = g_build_filename (gnc_userconfig_dir (), "config-user.scm", nullptr);
107 is_user_config_loaded = gfec_try_load (config_filename);
108 g_free (config_filename);
109 }
110}
111
112static void
113gnc_log_init (const std::vector <std::string> log_flags,
114 const boost::optional <std::string> &log_to_filename)
115{
116 if (log_to_filename && !log_to_filename->empty())
117 {
118 auto utf8_filename = log_to_filename->c_str();
119 qof_log_init_filename_special (utf8_filename);
120 }
121 else
122 {
123 /* initialize logging to our file. */
124 auto tracefilename = g_build_filename (g_get_tmp_dir(), "gnucash.trace",
125 (gchar *)NULL);
126 qof_log_init_filename (tracefilename);
127 g_free (tracefilename);
128 }
129
130 if (gnc_prefs_is_debugging_enabled())
131 {
132 qof_log_set_level ("", QOF_LOG_INFO);
133 qof_log_set_level ("qof", QOF_LOG_INFO);
134 qof_log_set_level ("gnc", QOF_LOG_INFO);
135 }
136
137 auto log_config_filename = g_build_filename (gnc_userconfig_dir (),
138 "log.conf", (char *)NULL);
139 if (g_file_test (log_config_filename, G_FILE_TEST_EXISTS))
140 qof_log_parse_log_config (log_config_filename);
141 g_free (log_config_filename);
142
143 for (auto log_flag : log_flags)
144 {
145 if (log_flag.empty () ||
146 log_flag[0] == '=' ||
147 log_flag[log_flag.length () - 1] == '=')
148 {
149 g_warning ("string [%s] not parseable", log_flag.c_str());
150 continue;
151 }
152
153 std::vector<std::string> split_flag;
154 boost::split (split_flag, log_flag, [](char c){return c == '=';});
155
156 auto level = qof_log_level_from_string (split_flag[1].c_str());
157 qof_log_set_level (split_flag[0].c_str(), level);
158 }
159}
160
161Gnucash::CoreApp::CoreApp (const char* app_name) : m_app_name {app_name}
162{
163 #ifdef ENABLE_BINRELOC
164 {
165 GError *binreloc_error = NULL;
166 if (!gnc_gbr_init(&binreloc_error))
167 {
168 std::cerr << "main: Error on gnc_gbr_init: " << binreloc_error->message << "\n";
169 g_error_free(binreloc_error);
170 }
171 }
172 #endif
173
174 /* This should be called before gettext is initialized
175 * The user may have configured a different language via
176 * the environment file.
177 */
179 #if defined MAC_INTEGRATION || defined __MINGW32__
180 sys_locale = set_platform_locale();
181 #endif
182 #ifdef __MINGW32__
183 set_platform_ctype_to_acp();
184 #endif
185 #if ! defined MAC_INTEGRATION && ! defined __MINGW32__/* setlocale already done */
186 sys_locale = g_strdup (setlocale (LC_ALL, ""));
187 if (!sys_locale)
188 {
189 std::cerr << "The locale defined in the environment isn't supported. "
190 << "Falling back to the 'C' (US English) locale\n";
191 g_setenv ("LC_ALL", "C", TRUE);
192 setlocale (LC_ALL, "C");
193 }
194 #endif
195
196 auto localedir = gnc_path_get_localedir ();
197 bindtextdomain(PROJECT_NAME, localedir);
198 bindtextdomain("iso_4217", localedir); // For win32 to find currency name translations
199 bind_textdomain_codeset("iso_4217", "UTF-8");
200 textdomain(PROJECT_NAME);
201 bind_textdomain_codeset(PROJECT_NAME, "UTF-8");
202
203 gnc_init_boost_locale (localedir);
204 std::cerr.imbue (gnc_get_boost_locale());
205 std::cout.imbue (gnc_get_boost_locale());
206 g_free(localedir);
207
208 // Now that gettext is properly initialized, set our help tagline.
209 m_tagline = _("- GnuCash, accounting for personal and small business finance");
210 m_opt_desc_display = std::make_unique<bpo::options_description>
211 ((bl::format (std::string{_("{1} [options] [datafile]")}) % m_app_name).str() + std::string(" ") + m_tagline);
212 add_common_program_options();
213}
214
215
216/* Parse command line options, using GOption interface.
217 * We can't let gtk_init_with_args do it because it fails
218 * before parsing any arguments if the GUI can't be initialized.
219 */
220void
221Gnucash::CoreApp::parse_command_line (int argc, char **argv)
222{
223 try
224 {
225 bpo::store (bpo::command_line_parser (argc, argv).
226 options (m_opt_desc_all).positional(m_pos_opt_desc).run(), m_opt_map);
227 bpo::notify (m_opt_map);
228 }
229 catch (std::exception &e)
230 {
231 std::cerr << e.what() << "\n\n";
232 std::cerr << *m_opt_desc_display.get() << std::endl;
233
234 exit(1);
235 }
236
237 if (m_show_paths)
238 {
239 std::cout << _("GnuCash Paths") << '\n';
240 for (const auto& ep : gnc_list_all_paths ())
241 {
242 std::cout << ep.env_name << ": " << ep.env_path;
243 if (ep.modifiable)
244 std::cout << ' ' << _("(user modifiable)");
245 std::cout << '\n';
246 }
247 exit (0);
248 }
249
250 if (m_show_version)
251 {
252 bl::format rel_fmt (std::string{_("GnuCash {1}")});
253 bl::format dev_fmt (std::string{_("GnuCash {1} development version")});
254
255 if (is_development_version)
256 std::cout << dev_fmt % gnc_version () << "\n";
257 else
258 std::cout << rel_fmt % gnc_version () << "\n";
259
260 std::cout << _("Build ID") << ": " << gnc_build_id () << "\n";
261 exit(0);
262 }
263
264 if (m_show_help)
265 {
266 std::cout << *m_opt_desc_display.get() << std::endl;
267 exit(0);
268 }
269
270 gnc_prefs_set_debugging (m_debug);
271 gnc_prefs_set_extra (m_extra);
272}
273
274/* Define command line options common to all gnucash binaries. */
275void
276Gnucash::CoreApp::add_common_program_options (void)
277{
278 bpo::options_description common_options(_("Common Options"));
279 common_options.add_options()
280 ("help,h", bpo::bool_switch (&m_show_help),
281 _("Show this help message"))
282 ("version,v", bpo::bool_switch (&m_show_version),
283 _("Show GnuCash version"))
284 ("debug", bpo::bool_switch (&m_debug),
285 _("Enable debugging mode: provide deep detail in the logs.\nThis is equivalent to: --log \"=info\" --log \"qof=info\" --log \"gnc=info\""))
286 ("extra", bpo::bool_switch(&m_extra),
287 _("Enable extra/development/debugging features."))
288 ("log", bpo::value (&m_log_flags),
289 _("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."))
290 ("paths", bpo::bool_switch(&m_show_paths),
291 _("Show paths"))
292 ("logto", bpo::value (&m_log_to_filename),
293 _("File to log into; defaults to \"/tmp/gnucash.trace\"; can be \"stderr\" or \"stdout\"."));
294
295 bpo::options_description hidden_options(_("Hidden Options"));
296 hidden_options.add_options()
297 ("input-file", bpo::value (&m_file_to_load),
298 _("[datafile]"));
299
300 m_pos_opt_desc.add("input-file", -1);
301
302 m_opt_desc_all.add (common_options);
303 m_opt_desc_all.add (hidden_options);
304
305 m_opt_desc_display->add (common_options);
306}
307
308void
309Gnucash::CoreApp::start (void)
310{
311 gnc_print_unstable_message();
312
313 /* Make sure gnucash' user data directory is properly set up
314 * This must be done before any guile code is called as that would
315 * fail the migration message */
316 userdata_migration_msg = gnc_filepath_init();
317 if (userdata_migration_msg)
318 g_print("\n\n%s\n", userdata_migration_msg);
319
320 gnc_log_init (m_log_flags, m_log_to_filename);
321 gnc_engine_init (0, NULL);
322
323 /* Write some locale details to the log to simplify debugging */
324 PINFO ("System locale returned %s", sys_locale ? sys_locale : "(null)");
325 PINFO ("Effective locale set to %s.", setlocale (LC_ALL, NULL));
326 g_free (sys_locale);
327 sys_locale = NULL;
328}
All type declarations for the whole Gnucash engine.
code to set up the environment for proper gnucash functioning.
File path resolution utility functions.
Generic api to store and retrieve preferences.
functions to query various version related strings that were set at build time.
void gnc_engine_init(int argc, char **argv)
PROTOTYPES.
void gnc_environment_setup(void)
Parse <prefix>/etc/gnucash/environment and set environment variables based on the contents of that fi...
const char * gnc_version(void)
Parse <prefix>/etc/gnucash/environment and set environment variables based on the contents of that fi...
Definition gnc-version.c:35
#define PINFO(format, args...)
Print an informational note.
Definition qoflog.h:256
void qof_log_set_level(QofLogModule log_module, QofLogLevel level)
Set the logging level of the given log_module.
Definition qoflog.cpp:297
void qof_log_init_filename(const gchar *log_filename)
Specify a filename for log output.
Definition qoflog.cpp:207
void qof_log_parse_log_config(const char *filename)
Parse a log-configuration file.
Definition qoflog.cpp:421
void qof_log_init_filename_special(const char *log_to_filename)
If log_to_filename is "stderr" or "stdout" (exactly, case-insensitive), then those special files are ...
Definition qoflog.cpp:402