GnuCash  5.6-150-g038405b370+
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 
53 namespace bl = boost::locale;
54 
55 /* This static indicates the debugging module that this .o belongs to. */
56 static 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
64 constexpr int is_development_version = TRUE;
65 #else
66 constexpr int is_development_version = FALSE;
67 #define GNC_VCS ""
68 #endif
69 
70 static gchar *userdata_migration_msg = NULL;
71 
72 static void
73 gnc_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 
85 void
86 Gnucash::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 
112 static void
113 gnc_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 
161 Gnucash::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  */
217 void
218 Gnucash::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. */
272 void
273 Gnucash::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 
305 void
306 Gnucash::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 }
void qof_log_set_level(QofLogModule log_module, QofLogLevel level)
Set the logging level of the given log_module.
Definition: qoflog.cpp:291
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
functions to query various version related strings that were set at build time.
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:396
void qof_log_init_filename(const gchar *log_filename)
Specify a filename for log output.
Definition: qoflog.cpp:207
code to set up the environment for proper gnucash functioning.
void gnc_engine_init(int argc, char **argv)
PROTOTYPES.
Definition: gnc-engine.cpp:116
char * gnc_filepath_init(void)
Initializes the gnucash user data directory.
void gnc_environment_setup(void)
Parse <prefix>/etc/gnucash/environment and set environment variables based on the contents of that fi...
All type declarations for the whole Gnucash engine.
Generic api to store and retrieve preferences.
const gchar * gnc_userconfig_dir(void)
Return the user&#39;s config directory for gnucash.
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
File path resolution utility functions.
void qof_log_parse_log_config(const char *filename)
Parse a log-configuration file.
Definition: qoflog.cpp:415