GnuCash c935c2f+
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 #if ! defined MAC_INTEGRATION && ! defined __MINGW32__/* setlocale already done */
183 sys_locale = g_strdup (setlocale (LC_ALL, ""));
184 if (!sys_locale)
185 {
186 std::cerr << "The locale defined in the environment isn't supported. "
187 << "Falling back to the 'C' (US English) locale\n";
188 g_setenv ("LC_ALL", "C", TRUE);
189 setlocale (LC_ALL, "C");
190 }
191 #endif
192
193 auto localedir = gnc_path_get_localedir ();
194 bindtextdomain(PROJECT_NAME, localedir);
195 bindtextdomain("iso_4217", localedir); // For win32 to find currency name translations
196 bind_textdomain_codeset("iso_4217", "UTF-8");
197 textdomain(PROJECT_NAME);
198 bind_textdomain_codeset(PROJECT_NAME, "UTF-8");
199
200 gnc_init_boost_locale (localedir);
201 std::cerr.imbue (gnc_get_boost_locale());
202 std::cout.imbue (gnc_get_boost_locale());
203 g_free(localedir);
204
205 // Now that gettext is properly initialized, set our help tagline.
206 m_tagline = _("- GnuCash, accounting for personal and small business finance");
207 m_opt_desc_display = std::make_unique<bpo::options_description>
208 ((bl::format (std::string{_("{1} [options] [datafile]")}) % m_app_name).str() + std::string(" ") + m_tagline);
209 add_common_program_options();
210}
211
212
213/* Parse command line options, using GOption interface.
214 * We can't let gtk_init_with_args do it because it fails
215 * before parsing any arguments if the GUI can't be initialized.
216 */
217void
218Gnucash::CoreApp::parse_command_line (int argc, char **argv)
219{
220 try
221 {
222 bpo::store (bpo::command_line_parser (argc, argv).
223 options (m_opt_desc_all).positional(m_pos_opt_desc).run(), m_opt_map);
224 bpo::notify (m_opt_map);
225 }
226 catch (std::exception &e)
227 {
228 std::cerr << e.what() << "\n\n";
229 std::cerr << *m_opt_desc_display.get() << std::endl;
230
231 exit(1);
232 }
233
234 if (m_show_paths)
235 {
236 std::cout << _("GnuCash Paths") << '\n';
237 for (const auto& ep : gnc_list_all_paths ())
238 {
239 std::cout << ep.env_name << ": " << ep.env_path;
240 if (ep.modifiable)
241 std::cout << ' ' << _("(user modifiable)");
242 std::cout << '\n';
243 }
244 exit (0);
245 }
246
247 if (m_show_version)
248 {
249 bl::format rel_fmt (std::string{_("GnuCash {1}")});
250 bl::format dev_fmt (std::string{_("GnuCash {1} development version")});
251
252 if (is_development_version)
253 std::cout << dev_fmt % gnc_version () << "\n";
254 else
255 std::cout << rel_fmt % gnc_version () << "\n";
256
257 std::cout << _("Build ID") << ": " << gnc_build_id () << "\n";
258 exit(0);
259 }
260
261 if (m_show_help)
262 {
263 std::cout << *m_opt_desc_display.get() << std::endl;
264 exit(0);
265 }
266
267 gnc_prefs_set_debugging (m_debug);
268 gnc_prefs_set_extra (m_extra);
269}
270
271/* Define command line options common to all gnucash binaries. */
272void
273Gnucash::CoreApp::add_common_program_options (void)
274{
275 bpo::options_description common_options(_("Common Options"));
276 common_options.add_options()
277 ("help,h", bpo::bool_switch (&m_show_help),
278 _("Show this help message"))
279 ("version,v", bpo::bool_switch (&m_show_version),
280 _("Show GnuCash version"))
281 ("debug", bpo::bool_switch (&m_debug),
282 _("Enable debugging mode: provide deep detail in the logs.\nThis is equivalent to: --log \"=info\" --log \"qof=info\" --log \"gnc=info\""))
283 ("extra", bpo::bool_switch(&m_extra),
284 _("Enable extra/development/debugging features."))
285 ("log", bpo::value (&m_log_flags),
286 _("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."))
287 ("paths", bpo::bool_switch(&m_show_paths),
288 _("Show paths"))
289 ("logto", bpo::value (&m_log_to_filename),
290 _("File to log into; defaults to \"/tmp/gnucash.trace\"; can be \"stderr\" or \"stdout\"."));
291
292 bpo::options_description hidden_options(_("Hidden Options"));
293 hidden_options.add_options()
294 ("input-file", bpo::value (&m_file_to_load),
295 _("[datafile]"));
296
297 m_pos_opt_desc.add("input-file", -1);
298
299 m_opt_desc_all.add (common_options);
300 m_opt_desc_all.add (hidden_options);
301
302 m_opt_desc_display->add (common_options);
303}
304
305void
306Gnucash::CoreApp::start (void)
307{
308 gnc_print_unstable_message();
309
310 /* Make sure gnucash' user data directory is properly set up
311 * This must be done before any guile code is called as that would
312 * fail the migration message */
313 userdata_migration_msg = gnc_filepath_init();
314 if (userdata_migration_msg)
315 g_print("\n\n%s\n", userdata_migration_msg);
316
317 gnc_log_init (m_log_flags, m_log_to_filename);
318 gnc_engine_init (0, NULL);
319
320 /* Write some locale details to the log to simplify debugging */
321 PINFO ("System locale returned %s", sys_locale ? sys_locale : "(null)");
322 PINFO ("Effective locale set to %s.", setlocale (LC_ALL, NULL));
323 g_free (sys_locale);
324 sys_locale = NULL;
325}
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