GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
io-gncxml-v2.c
1 /********************************************************************\
2  * Copyright (C) 2000,2001 Gnumatic Inc. *
3  * *
4  * This program is free software; you can redistribute it and/or *
5  * modify it under the terms of the GNU General Public License as *
6  * published by the Free Software Foundation; either version 2 of *
7  * the License, or (at your option) any later version. *
8  * *
9  * This program is distributed in the hope that it will be useful, *
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12  * GNU General Public License for more details. *
13  * *
14  * You should have received a copy of the GNU General Public License*
15  * along with this program; if not, contact: *
16  * *
17  * Free Software Foundation Voice: +1-617-542-5942 *
18  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
19  * Boston, MA 02110-1301, USA [email protected] *
20 \********************************************************************/
21 
22 #include "config.h"
23 
24 #include <glib.h>
25 #include <glib/gstdio.h>
26 #include <fcntl.h>
27 #include <string.h>
28 #ifdef HAVE_UNISTD_H
29 # include <unistd.h>
30 #endif
31 #include <zlib.h>
32 #include <errno.h>
33 
34 #include "gnc-engine.h"
35 #include "gnc-pricedb-p.h"
36 #include "Scrub.h"
37 #include "SX-book.h"
38 #include "SX-book-p.h"
39 #include "Transaction.h"
40 #include "TransactionP.h"
41 #include "TransLog.h"
42 #include "sixtp-dom-parsers.h"
43 #include "io-gncxml-v2.h"
44 #include "io-gncxml-gen.h"
45 
46 #include "sixtp.h"
47 #include "sixtp-parsers.h"
48 #include "sixtp-utils.h"
49 #include "gnc-xml.h"
50 #include "io-utils.h"
51 #ifdef G_OS_WIN32
52 # include <io.h>
53 # define close _close
54 # define fdopen _fdopen
55 # define read _read
56 #endif
57 #include "platform.h"
58 #if COMPILER(MSVC)
59 # define g_fopen fopen
60 # define g_open _open
61 #endif
62 
63 /* Do not treat -Wstrict-aliasing warnings as errors because of problems of the
64  * G_LOCK* macros as declared by glib. See
65  * http://bugzilla.gnome.org/show_bug.cgi?id=316221 for additional information.
66  */
67 #if (__GNUC__ >= 4 && __GNUC_MINOR__ >= 2)
68 # pragma GCC diagnostic warning "-Wstrict-aliasing"
69 #endif
70 
71 static QofLogModule log_module = GNC_MOD_IO;
72 
73 /* map pointers, e.g. of type FILE*, to GThreads */
74 static GHashTable *threads = NULL;
75 G_LOCK_DEFINE_STATIC(threads);
76 
77 typedef struct
78 {
79  gint fd;
80  gchar *filename;
81  gchar *perms;
82  gboolean compress;
84 
85 /* Callback structure */
87 {
88  gboolean ok;
89  gpointer data;
90  sixtp_gdv2 * gd;
91  const char * tag;
92  sixtp * parser;
93  FILE * out;
94  QofBook * book;
95 };
96 
97 #define GNC_V2_STRING "gnc-v2"
98 /* non-static because they are used in sixtp.c */
99 const gchar *gnc_v2_xml_version_string = GNC_V2_STRING;
100 extern const gchar *gnc_v2_book_version_string; /* see gnc-book-xml-v2 */
101 
102 /* Forward declarations */
103 static FILE *try_gz_open (const char *filename, const char *perms, gboolean use_gzip,
104  gboolean compress);
105 static gboolean is_gzipped_file(const gchar *name);
106 static gboolean wait_for_gzip(FILE *file);
107 
108 void
109 run_callback(sixtp_gdv2 *data, const char *type)
110 {
111  if (data->countCallback)
112  {
113  data->countCallback(data, type);
114  }
115 }
116 
117 static void
118 clear_up_account_commodity(
119  gnc_commodity_table *tbl, Account *act,
120  gnc_commodity * (*getter) (const Account *account),
121  void (*setter) (Account *account, gnc_commodity *comm),
122  int (*scu_getter) (const Account *account),
123  void (*scu_setter) (Account *account, int scu))
124 {
125  gnc_commodity *gcom;
126  gnc_commodity *com = getter(act);
127  int old_scu;
128 
129  if (scu_getter)
130  old_scu = scu_getter(act);
131  else
132  old_scu = 0;
133 
134  if (!com)
135  {
136  return;
137  }
138 
139  gcom = gnc_commodity_table_lookup(tbl, gnc_commodity_get_namespace(com),
141 
142  if (gcom == com)
143  {
144  return;
145  }
146  else if (!gcom)
147  {
148  PWARN("unable to find global commodity for %s adding new",
150  gnc_commodity_table_insert(tbl, com);
151  }
152  else
153  {
154  setter(act, gcom);
155  if (old_scu != 0 && scu_setter)
156  scu_setter(act, old_scu);
158  }
159 }
160 
161 static void
162 clear_up_transaction_commodity(
163  gnc_commodity_table *tbl, Transaction *trans,
164  gnc_commodity * (*getter) (const Transaction *trans),
165  void (*setter) (Transaction *trans, gnc_commodity *comm))
166 {
167  gnc_commodity *gcom;
168  gnc_commodity *com = getter(trans);
169 
170  if (!com)
171  {
172  return;
173  }
174 
175  gcom = gnc_commodity_table_lookup(tbl, gnc_commodity_get_namespace(com),
177 
178  if (gcom == com)
179  {
180  return;
181  }
182  else if (!gcom)
183  {
184  PWARN("unable to find global commodity for %s adding new",
186  gnc_commodity_table_insert(tbl, com);
187  }
188  else
189  {
190  xaccTransBeginEdit(trans);
191  setter(trans, gcom);
192  xaccTransCommitEdit(trans);
194  }
195 }
196 
197 static gboolean
198 add_account_local(sixtp_gdv2 *data, Account *act)
199 {
201  Account *parent, *root;
202  int type;
203 
204  table = gnc_commodity_table_get_table (data->book);
205 
206  clear_up_account_commodity(table, act,
209  NULL, NULL);
210 
211  clear_up_account_commodity(table, act,
216 
218  xaccAccountScrubKvp (act);
219 
220  /* Backwards compatability. If there's no parent, see if this
221  * account is of type ROOT. If not, find or create a ROOT
222  * account and make that the parent. */
223  type = xaccAccountGetType(act);
224  if (type == ACCT_TYPE_ROOT)
225  {
226  gnc_book_set_root_account(data->book, act);
227  }
228  else
229  {
230  parent = gnc_account_get_parent(act);
231  if (parent == NULL)
232  {
233  root = gnc_book_get_root_account(data->book);
234  gnc_account_append_child(root, act);
235  }
236  }
237 
238  data->counter.accounts_loaded++;
239  run_callback(data, "account");
240 
241  return FALSE;
242 }
243 
244 static gboolean
245 add_book_local(sixtp_gdv2 *data, QofBook *book)
246 {
247  data->counter.books_loaded++;
248  run_callback(data, "book");
249 
250  return FALSE;
251 }
252 
253 static gboolean
254 add_commodity_local(sixtp_gdv2 *data, gnc_commodity *com)
255 {
256  gnc_commodity_table *table;
257 
258  table = gnc_commodity_table_get_table (data->book);
259 
260  gnc_commodity_table_insert(table, com);
261 
262  data->counter.commodities_loaded++;
263  run_callback(data, "commodities");
264 
265  return TRUE;
266 }
267 
268 static gboolean
269 add_transaction_local(sixtp_gdv2 *data, Transaction *trn)
270 {
271  gnc_commodity_table *table;
272 
273  table = gnc_commodity_table_get_table (data->book);
274 
275  xaccTransBeginEdit (trn);
276  clear_up_transaction_commodity(table, trn,
279 
281  xaccTransCommitEdit (trn);
282 
283  data->counter.transactions_loaded++;
284  run_callback(data, "transaction");
285  return TRUE;
286 }
287 
288 static gboolean
289 add_schedXaction_local(sixtp_gdv2 *data, SchedXaction *sx)
290 {
291  SchedXactions *sxes;
292  sxes = gnc_book_get_schedxactions(data->book);
293  gnc_sxes_add_sx(sxes, sx);
294  data->counter.schedXactions_loaded++;
295  run_callback(data, "schedXactions");
296  return TRUE;
297 }
298 
299 static gboolean
300 add_template_transaction_local( sixtp_gdv2 *data,
302 {
303  GList *n;
304  Account *acctRoot = NULL;
305  QofBook *book;
306 
307  book = data->book;
308 
309  /* expect a struct of: */
310  /* . template accounts. */
311  /* . transactions in those accounts. */
312  for ( n = txd->accts; n; n = n->next )
313  {
314  if ( gnc_account_get_parent( (Account*)n->data ) == NULL )
315  {
316  if ( xaccAccountGetType( (Account*)n->data ) == ACCT_TYPE_ROOT )
317  {
318  /* replace the gnc_book_init-created root account */
319  gnc_book_set_template_root(book, (Account *)n->data);
320  }
321  else
322  {
323  /* This is an old data file that doesn't have a template root
324  account and this is a top level account. Make it a child
325  of the template root account. */
326  acctRoot = gnc_book_get_template_root(book);
327  gnc_account_append_child( acctRoot, (Account*)n->data );
328  }
329  }
330 
331  }
332 
333  for ( n = txd->transactions; n; n = n->next )
334  {
335  /* insert transactions into accounts */
336  add_transaction_local( data, (Transaction*)n->data );
337  }
338 
339  return TRUE;
340 }
341 
342 static gboolean
343 add_pricedb_local(sixtp_gdv2 *data, GNCPriceDB *db)
344 {
345  /* gnc_pricedb_print_contents(db, stdout); */
346  return TRUE;
347 }
348 
349 static void
350 do_counter_cb (const char *type, gpointer data_p, gpointer be_data_p)
351 {
352  GncXmlDataType_t *data = data_p;
353  struct file_backend *be_data = be_data_p;
354 
355  g_return_if_fail (type && data && be_data);
356  g_return_if_fail (data->version == GNC_FILE_BACKEND_VERS);
357 
358  if (be_data->ok == TRUE)
359  return;
360 
361  if (!g_strcmp0 (be_data->tag, data->type_name))
362  be_data->ok = TRUE;
363 
364  /* XXX: should we do anything with this counter? */
365 }
366 
367 static gboolean
368 gnc_counter_end_handler(gpointer data_for_children,
369  GSList* data_from_children, GSList* sibling_data,
370  gpointer parent_data, gpointer global_data,
371  gpointer *result, const gchar *tag)
372 {
373  char *strval;
374  gint64 val;
375  char *type;
376  xmlNodePtr tree = (xmlNodePtr)data_for_children;
377  gxpf_data *gdata = (gxpf_data*)global_data;
378  sixtp_gdv2 *sixdata = (sixtp_gdv2*)gdata->parsedata;
379  gboolean ret = TRUE;
380 
381  if (parent_data)
382  return TRUE;
383 
384  /* OK. For some messed up reason this is getting called again with a
385  NULL tag. So we ignore those cases */
386  if (!tag)
387  return TRUE;
388 
389  g_return_val_if_fail(tree, FALSE);
390 
391  /* Note: BADXML.
392  *
393  * This is invalid xml because the namespace isn't declared in the
394  * tag itself. This should be changed to 'type' at some point. */
395  type = (char*)xmlGetProp(tree, BAD_CAST "cd:type");
396  strval = dom_tree_to_text(tree);
397  if (!string_to_gint64(strval, &val))
398  {
399  PERR ("string_to_gint64 failed with input: %s",
400  strval ? strval : "(null)");
401  ret = FALSE;
402  }
403  else if (g_strcmp0(type, "transaction") == 0)
404  {
405  sixdata->counter.transactions_total = val;
406  }
407  else if (g_strcmp0(type, "account") == 0)
408  {
409  sixdata->counter.accounts_total = val;
410  }
411  else if (g_strcmp0(type, "book") == 0)
412  {
413  sixdata->counter.books_total = val;
414  }
415  else if (g_strcmp0(type, "commodity") == 0)
416  {
417  sixdata->counter.commodities_total = val;
418  }
419  else if (g_strcmp0(type, "schedxaction") == 0)
420  {
421  sixdata->counter.schedXactions_total = val;
422  }
423  else if (g_strcmp0(type, "budget") == 0)
424  {
425  sixdata->counter.budgets_total = val;
426  }
427  else if (g_strcmp0(type, "price") == 0)
428  {
429  sixdata->counter.prices_total = val;
430  }
431  else
432  {
433  struct file_backend be_data;
434 
435  be_data.ok = FALSE;
436  be_data.tag = type;
437 
438  qof_object_foreach_backend (GNC_FILE_BACKEND, do_counter_cb, &be_data);
439 
440  if (be_data.ok == FALSE)
441  {
442  PERR("Unknown type: %s", type ? type : "(null)");
443  /* Do *NOT* flag this as an error. Gnucash 1.8 writes invalid
444  * xml by writing the 'cd:type' attribute without providing
445  * the namespace in the gnc:count-data tag. The parser is
446  * entirely within its rights to refuse to read this bad
447  * attribute. Gnucash will function correctly without the data
448  * in this tag, so just let the error pass. */
449  ret = TRUE;
450  }
451  }
452 
453  g_free (strval);
454  xmlFree (type);
455  xmlFreeNode(tree);
456  return ret;
457 }
458 
459 static sixtp*
460 gnc_counter_sixtp_parser_create(void)
461 {
462  return sixtp_dom_parser_new(gnc_counter_end_handler, NULL, NULL);
463 }
464 
465 static void
466 debug_print_counter_data(load_counter *data)
467 {
468  DEBUG("Transactions: Total: %d, Loaded: %d",
469  data->transactions_total, data->transactions_loaded);
470  DEBUG("Accounts: Total: %d, Loaded: %d",
471  data->accounts_total, data->accounts_loaded);
472  DEBUG("Books: Total: %d, Loaded: %d",
473  data->books_total, data->books_loaded);
474  DEBUG("Commodities: Total: %d, Loaded: %d",
475  data->commodities_total, data->commodities_loaded);
476  DEBUG("Scheduled Transactions: Total: %d, Loaded: %d",
477  data->schedXactions_total, data->schedXactions_loaded);
478  DEBUG("Budgets: Total: %d, Loaded: %d",
479  data->budgets_total, data->budgets_loaded);
480 }
481 
482 static void
483 file_rw_feedback (sixtp_gdv2 *gd, const char *type)
484 {
485  load_counter *counter;
486  int loaded, total, percentage;
487 
488  g_assert(gd != NULL);
489  if (!gd->gui_display_fn)
490  return;
491 
492  counter = &gd->counter;
493  loaded = counter->transactions_loaded + counter->accounts_loaded +
494  counter->books_loaded + counter->commodities_loaded +
495  counter->schedXactions_loaded + counter->budgets_loaded +
496  counter->prices_loaded;
497  total = counter->transactions_total + counter->accounts_total +
498  counter->books_total + counter->commodities_total +
499  counter->schedXactions_total + counter->budgets_total +
500  counter->prices_total;
501  if (total == 0)
502  total = 1;
503 
504  percentage = (loaded * 100) / total;
505  if (percentage > 100)
506  {
507  /* FIXME: Perhaps the below should be replaced by:
508  print_counter_data(counter); */
509 // printf("Transactions: Total: %d, Loaded: %d\n",
510 // counter->transactions_total, counter->transactions_loaded);
511 // printf("Accounts: Total: %d, Loaded: %d\n",
512 // counter->accounts_total, counter->accounts_loaded);
513 // printf("Books: Total: %d, Loaded: %d\n",
514 // counter->books_total, counter->books_loaded);
515 // printf("Commodities: Total: %d, Loaded: %d\n",
516 // counter->commodities_total, counter->commodities_loaded);
517 // printf("Scheduled Transactions: Total: %d, Loaded: %d\n",
518 // counter->schedXactions_total, counter->schedXactions_loaded);
519 // printf("Budgets: Total: %d, Loaded: %d\n",
520 // counter->budgets_total, counter->budgets_loaded);
521  }
522  gd->gui_display_fn(NULL, percentage);
523 }
524 
525 static const char *BOOK_TAG = "gnc:book";
526 static const char *BOOK_ID_TAG = "book:id";
527 static const char *BOOK_SLOTS_TAG = "book:slots";
528 static const char *ACCOUNT_TAG = "gnc:account";
529 static const char *PRICEDB_TAG = "gnc:pricedb";
530 static const char *COMMODITY_TAG = "gnc:commodity";
531 static const char *COUNT_DATA_TAG = "gnc:count-data";
532 static const char *TRANSACTION_TAG = "gnc:transaction";
533 static const char *SCHEDXACTION_TAG = "gnc:schedxaction";
534 static const char *TEMPLATE_TRANSACTION_TAG = "gnc:template-transactions";
535 static const char *BUDGET_TAG = "gnc:budget";
536 
537 static void
538 add_item_cb (const char *type, gpointer data_p, gpointer be_data_p)
539 {
540  GncXmlDataType_t *data = data_p;
541  struct file_backend *be_data = be_data_p;
542 
543  g_return_if_fail (type && data && be_data);
544  g_return_if_fail (data->version == GNC_FILE_BACKEND_VERS);
545 
546  if (be_data->ok)
547  return;
548 
549  if (!g_strcmp0 (be_data->tag, data->type_name))
550  {
551  if (data->add_item)
552  (data->add_item)(be_data->gd, be_data->data);
553 
554  be_data->ok = TRUE;
555  }
556 }
557 
558 static gboolean
559 book_callback(const char *tag, gpointer globaldata, gpointer data)
560 {
561  sixtp_gdv2 *gd = (sixtp_gdv2*)globaldata;
562 
563  if (g_strcmp0(tag, ACCOUNT_TAG) == 0)
564  {
565  add_account_local(gd, (Account*)data);
566  }
567  else if (g_strcmp0(tag, PRICEDB_TAG) == 0)
568  {
569  add_pricedb_local(gd, (GNCPriceDB*)data);
570  }
571  else if (g_strcmp0(tag, COMMODITY_TAG) == 0)
572  {
573  add_commodity_local(gd, (gnc_commodity*)data);
574  }
575  else if (g_strcmp0(tag, TRANSACTION_TAG) == 0)
576  {
577  add_transaction_local(gd, (Transaction*)data);
578  }
579  else if (g_strcmp0(tag, SCHEDXACTION_TAG) == 0)
580  {
581  add_schedXaction_local(gd, (SchedXaction*)data);
582  }
583  else if (g_strcmp0(tag, TEMPLATE_TRANSACTION_TAG) == 0)
584  {
585  add_template_transaction_local( gd, (gnc_template_xaction_data*)data );
586  }
587  else if (g_strcmp0(tag, BUDGET_TAG) == 0)
588  {
589  // Nothing needed here.
590  }
591  else
592  {
593  struct file_backend be_data;
594 
595  be_data.ok = FALSE;
596  be_data.tag = tag;
597  be_data.gd = gd;
598  be_data.data = data;
599 
600  qof_object_foreach_backend (GNC_FILE_BACKEND, add_item_cb, &be_data);
601 
602  if (be_data.ok == FALSE)
603  {
604  PWARN ("unexpected tag %s", tag);
605  }
606  }
607  return TRUE;
608 }
609 
610 static gboolean
611 generic_callback(const char *tag, gpointer globaldata, gpointer data)
612 {
613  sixtp_gdv2 *gd = (sixtp_gdv2*)globaldata;
614 
615  if (g_strcmp0(tag, BOOK_TAG) == 0)
616  {
617  add_book_local(gd, (QofBook*)data);
618  book_callback(tag, globaldata, data);
619  }
620  else
621  {
622  // PWARN ("importing pre-book-style XML data file");
623  book_callback(tag, globaldata, data);
624  }
625  return TRUE;
626 }
627 
628 static void
629 add_parser_cb (const char *type, gpointer data_p, gpointer be_data_p)
630 {
631  GncXmlDataType_t *data = data_p;
632  struct file_backend *be_data = be_data_p;
633 
634  g_return_if_fail (type && data && be_data);
635  g_return_if_fail (data->version == GNC_FILE_BACKEND_VERS);
636 
637  if (be_data->ok == FALSE)
638  return;
639 
640  if (data->create_parser)
641  if (!sixtp_add_some_sub_parsers(
642  be_data->parser, TRUE,
643  data->type_name, (data->create_parser)(),
644  NULL, NULL))
645  be_data->ok = FALSE;
646 }
647 
648 static void
649 scrub_cb (const char *type, gpointer data_p, gpointer be_data_p)
650 {
651  GncXmlDataType_t *data = data_p;
652  struct file_backend *be_data = be_data_p;
653 
654  g_return_if_fail (type && data && be_data);
655  g_return_if_fail (data->version == GNC_FILE_BACKEND_VERS);
656 
657  if (data->scrub)
658  (data->scrub)(be_data->book);
659 }
660 
661 static sixtp_gdv2 *
662 gnc_sixtp_gdv2_new (
663  QofBook *book,
664  gboolean exporting,
665  countCallbackFn countcallback,
666  QofBePercentageFunc gui_display_fn)
667 {
668  sixtp_gdv2 *gd = g_new0(sixtp_gdv2, 1);
669 
670  if (gd == NULL) return NULL;
671 
672  gd->book = book;
673  gd->counter.accounts_loaded = 0;
674  gd->counter.accounts_total = 0;
675  gd->counter.books_loaded = 0;
676  gd->counter.books_total = 0;
677  gd->counter.commodities_loaded = 0;
678  gd->counter.commodities_total = 0;
679  gd->counter.transactions_loaded = 0;
680  gd->counter.transactions_total = 0;
681  gd->counter.prices_loaded = 0;
682  gd->counter.prices_total = 0;
683  gd->counter.schedXactions_loaded = 0;
684  gd->counter.schedXactions_total = 0;
685  gd->counter.budgets_loaded = 0;
686  gd->counter.budgets_total = 0;
687  gd->exporting = exporting;
688  gd->countCallback = countcallback;
689  gd->gui_display_fn = gui_display_fn;
690  return gd;
691 }
692 
693 static gboolean
694 qof_session_load_from_xml_file_v2_full(
695  FileBackend *fbe, QofBook *book,
696  sixtp_push_handler push_handler, gpointer push_user_data,
697  QofBookFileType type)
698 {
699  Account *root;
700  QofBackend *be = &fbe->be;
701  sixtp_gdv2 *gd;
702  sixtp *top_parser;
703  sixtp *main_parser;
704  sixtp *book_parser;
705  struct file_backend be_data;
706  gboolean retval;
707  char *v2type = NULL;
708 
709  gd = gnc_sixtp_gdv2_new(book, FALSE, file_rw_feedback, be->percentage);
710 
711  top_parser = sixtp_new();
712  main_parser = sixtp_new();
713  book_parser = sixtp_new();
714 
715  if (type == GNC_BOOK_XML2_FILE)
716  v2type = g_strdup(GNC_V2_STRING);
717 
718  if (!sixtp_add_some_sub_parsers(
719  top_parser, TRUE,
720  v2type, main_parser,
721  NULL, NULL))
722  {
723  g_free(v2type);
724  goto bail;
725  }
726 
727  g_free(v2type);
728 
729  if (!sixtp_add_some_sub_parsers(
730  main_parser, TRUE,
731  COUNT_DATA_TAG, gnc_counter_sixtp_parser_create(),
732  BOOK_TAG, book_parser,
733 
734  /* the following are present here only to support
735  * the older, pre-book format. Basically, the top-level
736  * book is implicit. */
737  PRICEDB_TAG, gnc_pricedb_sixtp_parser_create(),
738  COMMODITY_TAG, gnc_commodity_sixtp_parser_create(),
739  ACCOUNT_TAG, gnc_account_sixtp_parser_create(),
740  TRANSACTION_TAG, gnc_transaction_sixtp_parser_create(),
741  SCHEDXACTION_TAG, gnc_schedXaction_sixtp_parser_create(),
742  TEMPLATE_TRANSACTION_TAG, gnc_template_transaction_sixtp_parser_create(),
743  NULL, NULL))
744  {
745  goto bail;
746  }
747 
748  if (!sixtp_add_some_sub_parsers(
749  book_parser, TRUE,
750  BOOK_ID_TAG, gnc_book_id_sixtp_parser_create(),
751  BOOK_SLOTS_TAG, gnc_book_slots_sixtp_parser_create(),
752  COUNT_DATA_TAG, gnc_counter_sixtp_parser_create(),
753  PRICEDB_TAG, gnc_pricedb_sixtp_parser_create(),
754  COMMODITY_TAG, gnc_commodity_sixtp_parser_create(),
755  ACCOUNT_TAG, gnc_account_sixtp_parser_create(),
756  BUDGET_TAG, gnc_budget_sixtp_parser_create(),
757  TRANSACTION_TAG, gnc_transaction_sixtp_parser_create(),
758  SCHEDXACTION_TAG, gnc_schedXaction_sixtp_parser_create(),
759  TEMPLATE_TRANSACTION_TAG, gnc_template_transaction_sixtp_parser_create(),
760  NULL, NULL))
761  {
762  goto bail;
763  }
764 
765  be_data.ok = TRUE;
766  be_data.parser = book_parser;
767  qof_object_foreach_backend (GNC_FILE_BACKEND, add_parser_cb, &be_data);
768  if (be_data.ok == FALSE)
769  goto bail;
770 
771  /* stop logging while we load */
772  xaccLogDisable ();
773  xaccDisableDataScrubbing();
774 
775  if (push_handler)
776  {
777  gpointer parse_result = NULL;
778  gxpf_data gpdata;
779 
780  gpdata.cb = generic_callback;
781  gpdata.parsedata = gd;
782  gpdata.bookdata = book;
783 
784  retval = sixtp_parse_push(top_parser, push_handler, push_user_data,
785  NULL, &gpdata, &parse_result);
786  }
787  else
788  {
789  /* Even though libxml2 knows how to decompress zipped files, we
790  * do it ourself since as of version 2.9.1 it has a bug that
791  * causes it to fail to decompress certain files. See
792  * https://bugzilla.gnome.org/show_bug.cgi?id=712528 for more
793  * info.
794  */
795  gchar *filename = fbe->fullpath;
796  FILE *file;
797  gboolean is_compressed = is_gzipped_file(filename);
798  file = try_gz_open(filename, "r", is_compressed, FALSE);
799  if (file == NULL)
800  {
801  PWARN("Unable to open file %s", filename);
802  retval = FALSE;
803  }
804  else
805  {
806  retval = gnc_xml_parse_fd(top_parser, file,
807  generic_callback, gd, book);
808  fclose(file);
809  if (is_compressed)
810  wait_for_gzip(file);
811  }
812  }
813 
814  if (!retval)
815  {
816  sixtp_destroy(top_parser);
817  xaccLogEnable ();
818  xaccEnableDataScrubbing();
819  goto bail;
820  }
821  debug_print_counter_data(&gd->counter);
822 
823  /* destroy the parser */
824  sixtp_destroy (top_parser);
825  g_free(gd);
826 
827  xaccEnableDataScrubbing();
828 
829  /* Mark the session as saved */
831 
832  /* Call individual scrub functions */
833  memset(&be_data, 0, sizeof(be_data));
834  be_data.book = book;
835  qof_object_foreach_backend (GNC_FILE_BACKEND, scrub_cb, &be_data);
836 
837  /* fix price quote sources */
838  root = gnc_book_get_root_account(book);
840 
841  /* Fix account and transaction commodities */
843 
844  /* Fix split amount/value */
845  xaccAccountTreeScrubSplits (root);
846 
847  /* commit all groups, this completes the BeginEdit started when the
848  * account_end_handler finished reading the account.
849  */
851  (AccountCb) xaccAccountCommitEdit,
852  NULL);
853 
854  /* start logging again */
855  xaccLogEnable ();
856 
857  return TRUE;
858 
859 bail:
860  g_free(gd);
861  return FALSE;
862 }
863 
864 gboolean
866  QofBookFileType type)
867 {
868  return qof_session_load_from_xml_file_v2_full(fbe, book, NULL, NULL, type);
869 }
870 
871 /***********************************************************************/
872 
873 static gboolean
874 write_counts(FILE* out, ...)
875 {
876  va_list ap;
877  char *type;
878  gboolean success = TRUE;
879 
880  va_start(ap, out);
881  type = g_strdup (va_arg(ap, char *));
882 
883  while (success && type)
884  {
885  int amount = va_arg(ap, int);
886 
887  if (amount != 0)
888  {
889 #if GNUCASH_REALLY_BUILD_AN_XML_TREE_ON_OUTPUT
890  char *val;
891  xmlNodePtr node;
892 
893  val = g_strdup_printf("%d", amount);
894 
895  node = xmlNewNode(NULL, BAD_CAST COUNT_DATA_TAG);
896  /* Note: BADXML.
897  *
898  * This is invalid xml because the namespace isn't
899  * declared in the tag itself. This should be changed to
900  * 'type' at some point. */
901  xmlSetProp(node, BAD_CAST "cd:type", checked_char_cast (type));
902  xmlNodeAddContent(node, checked_char_cast (val));
903  g_free(val);
904  g_free (type);
905 
906  xmlElemDump(out, NULL, node);
907  xmlFreeNode(node);
908 
909  if (ferror(out) || fprintf(out, "\n") < 0)
910  {
911  success = FALSE;
912  break;
913  }
914 #else
915  if (fprintf(out, "<%s %s=\"%s\">%d</%s>\n",
916  COUNT_DATA_TAG, "cd:type", type, amount, COUNT_DATA_TAG) < 0)
917  {
918  success = FALSE;
919  break;
920  }
921 #endif
922 
923  }
924 
925  type = va_arg(ap, char *);
926  }
927 
928  va_end(ap);
929  return success;
930 }
931 
932 static gint
933 compare_namespaces(gconstpointer a, gconstpointer b)
934 {
935  const gchar *sa = (const gchar *) a;
936  const gchar *sb = (const gchar *) b;
937  return(g_strcmp0(sa, sb));
938 }
939 
940 static gint
941 compare_commodity_ids(gconstpointer a, gconstpointer b)
942 {
943  const gnc_commodity *ca = (const gnc_commodity *) a;
944  const gnc_commodity *cb = (const gnc_commodity *) b;
945  return(g_strcmp0(gnc_commodity_get_mnemonic(ca),
947 }
948 
949 static gboolean write_pricedb (FILE *out, QofBook *book, sixtp_gdv2 *gd);
950 static gboolean write_transactions (FILE *out, QofBook *book, sixtp_gdv2 *gd);
951 static gboolean write_template_transaction_data (FILE *out, QofBook *book, sixtp_gdv2 *gd);
952 static gboolean write_schedXactions(FILE *out, QofBook *book, sixtp_gdv2 *gd);
953 static void write_budget (QofInstance *ent, gpointer data);
954 
955 static void
956 write_counts_cb (const char *type, gpointer data_p, gpointer be_data_p)
957 {
958  GncXmlDataType_t *data = data_p;
959  struct file_backend *be_data = be_data_p;
960 
961  g_return_if_fail (type && data && be_data);
962  g_return_if_fail (data->version == GNC_FILE_BACKEND_VERS);
963 
964  if (data->get_count)
965  write_counts (be_data->out, data->type_name,
966  (data->get_count) (be_data->book),
967  NULL);
968 }
969 
970 static void
971 write_data_cb (const char *type, gpointer data_p, gpointer be_data_p)
972 {
973  GncXmlDataType_t *data = data_p;
974  struct file_backend *be_data = be_data_p;
975 
976  g_return_if_fail (type && data && be_data);
977  g_return_if_fail (data->version == GNC_FILE_BACKEND_VERS);
978 
979  if (data->write && !ferror(be_data->out))
980  (data->write)(be_data->out, be_data->book);
981 }
982 
983 static gboolean
984 write_book(FILE *out, QofBook *book, sixtp_gdv2 *gd)
985 {
986  struct file_backend be_data;
987 
988 #ifdef IMPLEMENT_BOOK_DOM_TREES_LATER
989  /* We can't just blast out the dom tree, because the dom tree
990  * doesn't have the books, transactions, etc underneath it.
991  * But that is just as well, since I think the performance
992  * will be much better if we write out as we go along
993  */
994  xmlNodePtr node;
995 
996  node = gnc_book_dom_tree_create(book);
997 
998  if (!node)
999  {
1000  return FALSE;
1001  }
1002 
1003  xmlElemDump(out, NULL, node);
1004  xmlFreeNode(node);
1005 
1006  if (ferror(out) || fprintf(out, "\n") < 0)
1007  {
1008  return FALSE;
1009  }
1010 
1011 #endif
1012 
1013  be_data.out = out;
1014  be_data.book = book;
1015  be_data.gd = gd;
1016  if (fprintf( out, "<%s version=\"%s\">\n", BOOK_TAG, gnc_v2_book_version_string) < 0)
1017  return FALSE;
1018  if (!write_book_parts (out, book))
1019  return FALSE;
1020 
1021  /* gd->counter.{foo}_total fields should have all these totals
1022  already collected. I don't know why we're re-calling all these
1023  functions. */
1024  if (!write_counts(out,
1025  "commodity",
1028  "account",
1029  1 + gnc_account_n_descendants(gnc_book_get_root_account(book)),
1030  "transaction",
1032  "schedxaction",
1033  g_list_length(gnc_book_get_schedxactions(book)->sx_list),
1034  "budget", qof_collection_count(
1035  qof_book_get_collection(book, GNC_ID_BUDGET)),
1037  NULL))
1038  return FALSE;
1039 
1040  qof_object_foreach_backend (GNC_FILE_BACKEND, write_counts_cb, &be_data);
1041 
1042  if (ferror(out)
1043  || !write_commodities(out, book, gd)
1044  || !write_pricedb(out, book, gd)
1045  || !write_accounts(out, book, gd)
1046  || !write_transactions(out, book, gd)
1047  || !write_template_transaction_data(out, book, gd)
1048  || !write_schedXactions(out, book, gd))
1049 
1050  return FALSE;
1051 
1052  qof_collection_foreach(qof_book_get_collection(book, GNC_ID_BUDGET),
1053  write_budget, &be_data);
1054  if (ferror(out))
1055  return FALSE;
1056 
1057  qof_object_foreach_backend (GNC_FILE_BACKEND, write_data_cb, &be_data);
1058  if (ferror(out))
1059  return FALSE;
1060 
1061  if (fprintf( out, "</%s>\n", BOOK_TAG ) < 0)
1062  return FALSE;
1063 
1064  return TRUE;
1065 }
1066 
1067 gboolean
1068 write_commodities(FILE *out, QofBook *book, sixtp_gdv2 *gd)
1069 {
1070  gnc_commodity_table *tbl;
1071  GList *namespaces;
1072  GList *lp;
1073  gboolean success = TRUE;
1074 
1075  tbl = gnc_commodity_table_get_table(book);
1076 
1077  namespaces = gnc_commodity_table_get_namespaces(tbl);
1078  if (namespaces)
1079  {
1080  namespaces = g_list_sort(namespaces, compare_namespaces);
1081  }
1082 
1083  for (lp = namespaces; success && lp; lp = lp->next)
1084  {
1085  GList *comms, *lp2;
1086  xmlNodePtr comnode;
1087 
1088  comms = gnc_commodity_table_get_commodities(tbl, lp->data);
1089  comms = g_list_sort(comms, compare_commodity_ids);
1090 
1091  for (lp2 = comms; lp2; lp2 = lp2->next)
1092  {
1093  comnode = gnc_commodity_dom_tree_create(lp2->data);
1094  if (comnode == NULL)
1095  continue;
1096 
1097  xmlElemDump(out, NULL, comnode);
1098  if (ferror(out) || fprintf(out, "\n") < 0)
1099  {
1100  success = FALSE;
1101  break;
1102  }
1103 
1104  xmlFreeNode(comnode);
1105  gd->counter.commodities_loaded++;
1106  run_callback(gd, "commodities");
1107  }
1108 
1109  g_list_free (comms);
1110  }
1111 
1112  if (namespaces) g_list_free (namespaces);
1113 
1114  return success;
1115 }
1116 
1117 static gboolean
1118 write_pricedb(FILE *out, QofBook *book, sixtp_gdv2 *gd)
1119 {
1120  xmlNodePtr node;
1121  xmlNodePtr parent;
1122  xmlOutputBufferPtr outbuf;
1123 
1124  parent = gnc_pricedb_dom_tree_create(gnc_pricedb_get_db(book));
1125 
1126  if (!parent)
1127  {
1128  return TRUE;
1129  }
1130 
1131  /* Write out the parent pricedb tag then loop to write out each price.
1132  We do it this way instead of just calling xmlElemDump so that we can
1133  increment the progress bar as we go. */
1134 
1135  if (fprintf( out, "<%s version=\"%s\">\n", parent->name,
1136  xmlGetProp(parent, BAD_CAST "version")) < 0)
1137  return FALSE;
1138 
1139  /* We create our own output buffer so we can call xmlNodeDumpOutput to get
1140  the indendation correct. */
1141  outbuf = xmlOutputBufferCreateFile(out, NULL);
1142  if (outbuf == NULL)
1143  {
1144  xmlFreeNode(parent);
1145  return FALSE;
1146  }
1147 
1148  for (node = parent->children; node; node = node->next)
1149  {
1150  /* Write two spaces since xmlNodeDumpOutput doesn't indent the first line */
1151  xmlOutputBufferWrite(outbuf, 2, " ");
1152  xmlNodeDumpOutput(outbuf, NULL, node, 1, 1, NULL);
1153  /* It also doesn't terminate the last line */
1154  xmlOutputBufferWrite(outbuf, 1, "\n");
1155  if (ferror(out))
1156  break;
1157  gd->counter.prices_loaded += 1;
1158  run_callback(gd, "prices");
1159  }
1160 
1161  xmlOutputBufferClose(outbuf);
1162 
1163  if (ferror(out) || fprintf(out, "</%s>\n", parent->name) < 0)
1164  {
1165  xmlFreeNode(parent);
1166  return FALSE;
1167  }
1168 
1169  xmlFreeNode(parent);
1170  return TRUE;
1171 }
1172 
1173 static int
1174 xml_add_trn_data(Transaction *t, gpointer data)
1175 {
1176  struct file_backend *be_data = data;
1177  xmlNodePtr node;
1178 
1179  node = gnc_transaction_dom_tree_create(t);
1180 
1181  xmlElemDump(be_data->out, NULL, node);
1182  xmlFreeNode(node);
1183 
1184  if (ferror(be_data->out) || fprintf(be_data->out, "\n") < 0)
1185  return -1;
1186 
1187  be_data->gd->counter.transactions_loaded++;
1188  run_callback(be_data->gd, "transaction");
1189  return 0;
1190 }
1191 
1192 static gboolean
1193 write_transactions(FILE *out, QofBook *book, sixtp_gdv2 *gd)
1194 {
1195  struct file_backend be_data;
1196 
1197  be_data.out = out;
1198  be_data.gd = gd;
1199  return 0 ==
1200  xaccAccountTreeForEachTransaction(gnc_book_get_root_account(book),
1201  xml_add_trn_data,
1202  (gpointer) &be_data);
1203 }
1204 
1205 static gboolean
1206 write_template_transaction_data( FILE *out, QofBook *book, sixtp_gdv2 *gd )
1207 {
1208  Account *ra;
1209  struct file_backend be_data;
1210 
1211  be_data.out = out;
1212  be_data.gd = gd;
1213 
1214  ra = gnc_book_get_template_root(book);
1215  if ( gnc_account_n_descendants(ra) > 0 )
1216  {
1217  if (fprintf(out, "<%s>\n", TEMPLATE_TRANSACTION_TAG) < 0
1218  || !write_account_tree(out, ra, gd)
1219  || xaccAccountTreeForEachTransaction(ra, xml_add_trn_data, (gpointer)&be_data)
1220  || fprintf(out, "</%s>\n", TEMPLATE_TRANSACTION_TAG) < 0)
1221 
1222  return FALSE;
1223  }
1224 
1225  return TRUE;
1226 }
1227 
1228 static gboolean
1229 write_schedXactions( FILE *out, QofBook *book, sixtp_gdv2 *gd)
1230 {
1231  GList *schedXactions;
1232  SchedXaction *tmpSX;
1233  xmlNodePtr node;
1234 
1235  schedXactions = gnc_book_get_schedxactions(book)->sx_list;
1236 
1237  if (schedXactions == NULL)
1238  return TRUE;
1239 
1240  do
1241  {
1242  tmpSX = schedXactions->data;
1243  node = gnc_schedXaction_dom_tree_create( tmpSX );
1244  xmlElemDump( out, NULL, node );
1245  xmlFreeNode(node);
1246  if (ferror(out) || fprintf(out, "\n") < 0)
1247  return FALSE;
1248  gd->counter.schedXactions_loaded++;
1249  run_callback(gd, "schedXactions");
1250  }
1251  while ( (schedXactions = schedXactions->next) );
1252 
1253  return TRUE;
1254 }
1255 
1256 static void
1257 write_budget (QofInstance *ent, gpointer data)
1258 {
1259  xmlNodePtr node;
1260  struct file_backend* be = data;
1261 
1262  GncBudget *bgt = GNC_BUDGET(ent);
1263 
1264  if (ferror(be->out))
1265  return;
1266 
1267  node = gnc_budget_dom_tree_create(bgt);
1268  xmlElemDump( be->out, NULL, node );
1269  xmlFreeNode(node);
1270  if (ferror(be->out) || fprintf(be->out, "\n") < 0)
1271  return;
1272 
1273  be->gd->counter.budgets_loaded++;
1274  run_callback(be->gd, "budgets");
1275 }
1276 
1277 gboolean
1278 gnc_xml2_write_namespace_decl (FILE *out, const char *name_space)
1279 {
1280  g_return_val_if_fail(name_space, FALSE);
1281  return fprintf(out, "\n xmlns:%s=\"http://www.gnucash.org/XML/%s\"",
1282  name_space, name_space) >= 0;
1283 }
1284 
1285 static void
1286 do_write_namespace_cb (const char *type, gpointer data_p, gpointer file_p)
1287 {
1288  GncXmlDataType_t *data = data_p;
1289  FILE *out = file_p;
1290 
1291  g_return_if_fail (type && data && out);
1292  g_return_if_fail (data->version == GNC_FILE_BACKEND_VERS);
1293 
1294  if (data->ns && !ferror(out))
1295  (data->ns)(out);
1296 }
1297 
1298 static gboolean
1299 write_v2_header (FILE *out)
1300 {
1301  if (fprintf(out, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n") < 0
1302  || fprintf(out, "<" GNC_V2_STRING) < 0
1303 
1304  || !gnc_xml2_write_namespace_decl (out, "gnc")
1305  || !gnc_xml2_write_namespace_decl (out, "act")
1306  || !gnc_xml2_write_namespace_decl (out, "book")
1307  || !gnc_xml2_write_namespace_decl (out, "cd")
1308  || !gnc_xml2_write_namespace_decl (out, "cmdty")
1309  || !gnc_xml2_write_namespace_decl (out, "price")
1310  || !gnc_xml2_write_namespace_decl (out, "slot")
1311  || !gnc_xml2_write_namespace_decl (out, "split")
1312  || !gnc_xml2_write_namespace_decl (out, "sx")
1313  || !gnc_xml2_write_namespace_decl (out, "trn")
1314  || !gnc_xml2_write_namespace_decl (out, "ts")
1315  || !gnc_xml2_write_namespace_decl (out, "fs")
1316  || !gnc_xml2_write_namespace_decl (out, "bgt")
1317  || !gnc_xml2_write_namespace_decl (out, "recurrence")
1318  || !gnc_xml2_write_namespace_decl (out, "lot"))
1319 
1320  return FALSE;
1321 
1322  /* now cope with the plugins */
1323  qof_object_foreach_backend (GNC_FILE_BACKEND, do_write_namespace_cb, out);
1324 
1325  if (ferror(out) || fprintf(out, ">\n") < 0)
1326  return FALSE;
1327 
1328  return TRUE;
1329 }
1330 
1331 gboolean
1332 gnc_book_write_to_xml_filehandle_v2(QofBook *book, FILE *out)
1333 {
1334  QofBackend *be;
1335  sixtp_gdv2 *gd;
1336  gboolean success = TRUE;
1337 
1338  if (!out) return FALSE;
1339 
1340  if (!write_v2_header(out)
1341  || !write_counts(out, "book", 1, NULL))
1342  return FALSE;
1343 
1344  be = qof_book_get_backend(book);
1345  gd = gnc_sixtp_gdv2_new(book, FALSE, file_rw_feedback, be->percentage);
1346  gd->counter.commodities_total =
1348  gd->counter.accounts_total = 1 +
1349  gnc_account_n_descendants(gnc_book_get_root_account(book));
1350  gd->counter.transactions_total = gnc_book_count_transactions(book);
1351  gd->counter.schedXactions_total =
1352  g_list_length(gnc_book_get_schedxactions(book)->sx_list);
1353  gd->counter.budgets_total = qof_collection_count(
1354  qof_book_get_collection(book, GNC_ID_BUDGET));
1355  gd->counter.prices_total = gnc_pricedb_get_num_prices(gnc_pricedb_get_db(book));
1356 
1357  if (!write_book(out, book, gd)
1358  || fprintf(out, "</" GNC_V2_STRING ">\n\n") < 0)
1359  success = FALSE;
1360 
1361  g_free(gd);
1362  return success;
1363 }
1364 
1365 /*
1366  * This function is called by the "export" code.
1367  */
1368 gboolean
1370 {
1371  gnc_commodity_table *table;
1372  Account *root;
1373  int ncom, nacc;
1374  sixtp_gdv2 *gd;
1375  gboolean success = TRUE;
1376 
1377  if (!out) return FALSE;
1378 
1379  root = gnc_book_get_root_account(book);
1380  nacc = 1 + gnc_account_n_descendants(root);
1381 
1382  table = gnc_commodity_table_get_table(book);
1383  ncom = gnc_commodity_table_get_size(table);
1384 
1385  if (!write_v2_header(out)
1386  || !write_counts(out, "commodity", ncom, "account", nacc, NULL))
1387  return FALSE;
1388 
1389  gd = gnc_sixtp_gdv2_new(book, TRUE, file_rw_feedback, be->percentage);
1390  gd->counter.commodities_total = ncom;
1391  gd->counter.accounts_total = nacc;
1392 
1393  if (!write_commodities(out, book, gd)
1394  || !write_accounts(out, book, gd)
1395  || fprintf(out, "</" GNC_V2_STRING ">\n\n") < 0)
1396  success = FALSE;
1397 
1398  g_free(gd);
1399  return success;
1400 }
1401 
1402 #define BUFLEN 4096
1403 
1404 /* Compress or decompress function that is to be run in a separate thread.
1405  * Returns 1 on success or 0 otherwise, stuffed into a pointer type. */
1406 static gpointer
1407 gz_thread_func(gz_thread_params_t *params)
1408 {
1409  gchar buffer[BUFLEN];
1410  gssize bytes;
1411  gint gzval;
1412  gzFile file;
1413  gint success = 1;
1414 
1415 #ifdef G_OS_WIN32
1416  {
1417  gchar *conv_name = g_win32_locale_filename_from_utf8(params->filename);
1418  gchar *perms;
1419 
1420  if (!conv_name)
1421  {
1422  g_warning("Could not convert '%s' to system codepage",
1423  params->filename);
1424  success = 0;
1425  goto cleanup_gz_thread_func;
1426  }
1427 
1428  if (strchr(params->perms, 'b'))
1429  perms = g_strdup(params->perms);
1430  else
1431  perms = g_strdup_printf("%cb%s", *params->perms, params->perms + 1);
1432 
1433  file = gzopen(conv_name, perms);
1434  g_free(perms);
1435  g_free(conv_name);
1436  }
1437 #else /* !G_OS_WIN32 */
1438  file = gzopen(params->filename, params->perms);
1439 #endif /* G_OS_WIN32 */
1440 
1441  if (file == NULL)
1442  {
1443  g_warning("Child threads gzopen failed");
1444  success = 0;
1445  goto cleanup_gz_thread_func;
1446  }
1447 
1448  if (params->compress)
1449  {
1450  while (success)
1451  {
1452  bytes = read(params->fd, buffer, BUFLEN);
1453  if (bytes > 0)
1454  {
1455  if (gzwrite(file, buffer, bytes) <= 0)
1456  {
1457  gint errnum;
1458  const gchar *error = gzerror(file, &errnum);
1459  g_warning("Could not write the compressed file '%s'. The error is: '%s' (%d)",
1460  params->filename, error, errnum);
1461  success = 0;
1462  }
1463  }
1464  else if (bytes == 0)
1465  {
1466  break;
1467  }
1468  else
1469  {
1470  g_warning("Could not read from pipe. The error is '%s' (errno %d)",
1471  g_strerror(errno) ? g_strerror(errno) : "", errno);
1472  success = 0;
1473  }
1474  }
1475  }
1476  else
1477  {
1478  while (success)
1479  {
1480  gzval = gzread(file, buffer, BUFLEN);
1481  if (gzval > 0)
1482  {
1483  if (
1484 #if COMPILER(MSVC)
1485  _write
1486 #else
1487  write
1488 #endif
1489  (params->fd, buffer, gzval) < 0)
1490  {
1491  g_warning("Could not write to pipe. The error is '%s' (%d)",
1492  g_strerror(errno) ? g_strerror(errno) : "", errno);
1493  success = 0;
1494  }
1495  }
1496  else if (gzval == 0)
1497  {
1498  break;
1499  }
1500  else
1501  {
1502  gint errnum;
1503  const gchar *error = gzerror(file, &errnum);
1504  g_warning("Could not read from compressed file '%s'. The error is: '%s' (%d)",
1505  params->filename, error, errnum);
1506  success = 0;
1507  }
1508  }
1509  }
1510 
1511  if ((gzval = gzclose(file)) != Z_OK)
1512  {
1513  g_warning("Could not close the compressed file '%s' (errnum %d)",
1514  params->filename, gzval);
1515  success = 0;
1516  }
1517 
1518 cleanup_gz_thread_func:
1519  close(params->fd);
1520  g_free(params->filename);
1521  g_free(params->perms);
1522  g_free(params);
1523 
1524  return GINT_TO_POINTER(success);
1525 }
1526 
1527 static FILE *
1528 try_gz_open (const char *filename, const char *perms, gboolean use_gzip,
1529  gboolean compress)
1530 {
1531  if (strstr(filename, ".gz.") != NULL) /* its got a temp extension */
1532  use_gzip = TRUE;
1533 
1534  if (!use_gzip)
1535  return g_fopen(filename, perms);
1536 
1537  {
1538  int filedes[2];
1539  GThread *thread;
1540  GError *error = NULL;
1541  gz_thread_params_t *params;
1542  FILE *file;
1543 
1544 #ifdef G_OS_WIN32
1545  if (_pipe(filedes, 4096, _O_BINARY) < 0)
1546  {
1547 #else
1548  if (pipe(filedes) < 0)
1549  {
1550 #endif
1551  g_warning("Pipe call failed. Opening uncompressed file.");
1552  return g_fopen(filename, perms);
1553  }
1554 
1555  params = g_new(gz_thread_params_t, 1);
1556  params->fd = filedes[compress ? 0 : 1];
1557  params->filename = g_strdup(filename);
1558  params->perms = g_strdup(perms);
1559  params->compress = compress;
1560 
1561 #ifndef HAVE_GLIB_2_32
1562  thread = g_thread_create((GThreadFunc) gz_thread_func, params,
1563  TRUE, &error);
1564 #else
1565  thread = g_thread_new("xml_thread", (GThreadFunc) gz_thread_func,
1566  params);
1567 #endif
1568  if (!thread)
1569  {
1570  g_warning("Could not create thread for (de)compression: %s",
1571  error->message);
1572  g_error_free(error);
1573  g_free(params->filename);
1574  g_free(params->perms);
1575  g_free(params);
1576  close(filedes[0]);
1577  close(filedes[1]);
1578 
1579  return g_fopen(filename, perms);
1580  }
1581 
1582  if (compress)
1583  file = fdopen(filedes[1], "w");
1584  else
1585  file = fdopen(filedes[0], "r");
1586 
1587  G_LOCK(threads);
1588  if (!threads)
1589  threads = g_hash_table_new(g_direct_hash, g_direct_equal);
1590 
1591  g_hash_table_insert(threads, file, thread);
1592  G_UNLOCK(threads);
1593 
1594  return file;
1595  }
1596 }
1597 
1598 static gboolean
1599 wait_for_gzip(FILE *file)
1600 {
1601  gboolean retval = TRUE;
1602 
1603  G_LOCK(threads);
1604  if (threads)
1605  {
1606  GThread *thread = g_hash_table_lookup(threads, file);
1607  if (thread)
1608  {
1609  g_hash_table_remove(threads, file);
1610  retval = GPOINTER_TO_INT(g_thread_join(thread));
1611  }
1612  }
1613  G_UNLOCK(threads);
1614 
1615  return retval;
1616 }
1617 
1618 gboolean
1619 gnc_book_write_to_xml_file_v2(
1620  QofBook *book,
1621  const char *filename,
1622  gboolean compress)
1623 {
1624  FILE *out;
1625  gboolean success = TRUE;
1626 
1627  out = try_gz_open(filename, "w", compress, TRUE);
1628 
1629  /* Try to write as much as possible */
1630  if (!out
1631  || !gnc_book_write_to_xml_filehandle_v2(book, out)
1632  || !write_emacs_trailer(out))
1633  success = FALSE;
1634 
1635  /* Close the output stream */
1636  if (out && fclose(out))
1637  success = FALSE;
1638 
1639  /* Optionally wait for parallel compression threads */
1640  if (out && compress)
1641  if (!wait_for_gzip(out))
1642  success = FALSE;
1643 
1644  return success;
1645 }
1646 
1647 /*
1648  * Have to pass in the backend as this routine needs the temporary
1649  * backend for file export, not the real backend which could be
1650  * postgress or anything else.
1651  */
1652 gboolean
1653 gnc_book_write_accounts_to_xml_file_v2(
1654  QofBackend *be,
1655  QofBook *book,
1656  const char *filename)
1657 {
1658  FILE *out;
1659  gboolean success = TRUE;
1660 
1661  out = g_fopen(filename, "w");
1662 
1663  /* Try to write as much as possible */
1664  if (!out
1666  || !write_emacs_trailer(out))
1667  success = FALSE;
1668 
1669  /* Close the output stream */
1670  if (out && fclose(out))
1671  success = FALSE;
1672 
1673  if (!success
1674  && qof_backend_get_error(be) == ERR_BACKEND_NO_ERR)
1675  {
1676 
1677  /* Use a generic write error code */
1679  }
1680 
1681  return success;
1682 }
1683 
1684 /***********************************************************************/
1685 static gboolean
1686 is_gzipped_file(const gchar *name)
1687 {
1688  unsigned char buf[2];
1689  int fd = g_open(name, O_RDONLY, 0);
1690 
1691  if (fd == -1)
1692  {
1693  return FALSE;
1694  }
1695 
1696  if (read(fd, buf, 2) != 2)
1697  {
1698  close(fd);
1699  return FALSE;
1700  }
1701  close(fd);
1702 
1703  if (buf[0] == 037 && buf[1] == 0213)
1704  {
1705  return TRUE;
1706  }
1707 
1708  return FALSE;
1709 }
1710 
1711 QofBookFileType
1712 gnc_is_xml_data_file_v2(const gchar *name, gboolean *with_encoding)
1713 {
1714  if (is_gzipped_file(name))
1715  {
1716  gzFile file = NULL;
1717  char first_chunk[256];
1718  int num_read;
1719 
1720 #ifdef G_OS_WIN32
1721  {
1722  gchar *conv_name = g_win32_locale_filename_from_utf8(name);
1723  if (!conv_name)
1724  g_warning("Could not convert '%s' to system codepage", name);
1725  else
1726  {
1727  file = gzopen(conv_name, "rb");
1728  g_free(conv_name);
1729  }
1730  }
1731 #else
1732  file = gzopen(name, "r");
1733 #endif
1734  if (file == NULL)
1735  return GNC_BOOK_NOT_OURS;
1736 
1737  num_read = gzread(file, first_chunk, sizeof(first_chunk) - 1);
1738  gzclose(file);
1739 
1740  if (num_read < 1)
1741  return GNC_BOOK_NOT_OURS;
1742 
1743  return gnc_is_our_first_xml_chunk(first_chunk, with_encoding);
1744  }
1745 
1746  return (gnc_is_our_xml_file(name, with_encoding));
1747 }
1748 
1749 
1750 static void
1751 replace_character_references(gchar *string)
1752 {
1753  gchar *cursor, *semicolon, *tail;
1754  glong number;
1755 
1756  for (cursor = strstr(string, "&#");
1757  cursor && *cursor;
1758  cursor = strstr(cursor, "&#"))
1759  {
1760  semicolon = strchr(cursor, ';');
1761  if (semicolon && *semicolon)
1762  {
1763 
1764  /* parse number */
1765  errno = 0;
1766  if (*(cursor + 2) == 'x')
1767  {
1768  number = strtol(cursor + 3, &tail, 16);
1769  }
1770  else
1771  {
1772  number = strtol(cursor + 2, &tail, 10);
1773  }
1774  if (errno || tail != semicolon || number < 0 || number > 255)
1775  {
1776  PWARN("Illegal character reference");
1777  return;
1778  }
1779 
1780  /* overwrite '&' with the specified character */
1781  *cursor = (gchar) number;
1782  cursor++;
1783  if (*(semicolon + 1))
1784  {
1785  /* move text after semicolon the the left */
1786  tail = g_strdup(semicolon + 1);
1787  strcpy(cursor, tail);
1788  g_free(tail);
1789  }
1790  else
1791  {
1792  /* cut here */
1793  *cursor = '\0';
1794  }
1795 
1796  }
1797  else
1798  {
1799  PWARN("Unclosed character reference");
1800  return;
1801  }
1802  }
1803 }
1804 
1805 static void
1806 conv_free(conv_type *conv)
1807 {
1808  if (conv)
1809  {
1810  g_free(conv->utf8_string);
1811  g_free(conv);
1812  }
1813 }
1814 
1815 static void
1816 conv_list_free(GList *conv_list)
1817 {
1818  g_list_foreach(conv_list, (GFunc) conv_free, NULL);
1819  g_list_free(conv_list);
1820 }
1821 
1822 typedef struct
1823 {
1824  GQuark encoding;
1825  GIConv iconv;
1826 } iconv_item_type;
1827 
1828 gint
1829 gnc_xml2_find_ambiguous(const gchar *filename, GList *encodings,
1830  GHashTable **unique, GHashTable **ambiguous,
1831  GList **impossible)
1832 {
1833  FILE *file = NULL;
1834  GList *iconv_list = NULL, *conv_list = NULL, *iter;
1835  iconv_item_type *iconv_item = NULL, *ascii = NULL;
1836  const gchar *enc;
1837  GHashTable *processed = NULL;
1838  gint n_impossible = 0;
1839  GError *error = NULL;
1840  gboolean is_compressed;
1841  gboolean clean_return = FALSE;
1842 
1843  is_compressed = is_gzipped_file(filename);
1844  file = try_gz_open(filename, "r", is_compressed, FALSE);
1845  if (file == NULL)
1846  {
1847  PWARN("Unable to open file %s", filename);
1848  goto cleanup_find_ambs;
1849  }
1850 
1851  /* we need ascii */
1852  ascii = g_new(iconv_item_type, 1);
1853  ascii->encoding = g_quark_from_string("ASCII");
1854  ascii->iconv = g_iconv_open("UTF-8", "ASCII");
1855  if (ascii->iconv == (GIConv) - 1)
1856  {
1857  PWARN("Unable to open ASCII ICONV conversion descriptor");
1858  goto cleanup_find_ambs;
1859  }
1860 
1861  /* call iconv_open on encodings */
1862  for (iter = encodings; iter; iter = iter->next)
1863  {
1864  iconv_item = g_new(iconv_item_type, 1);
1865  iconv_item->encoding = GPOINTER_TO_UINT (iter->data);
1866  if (iconv_item->encoding == ascii->encoding)
1867  {
1868  continue;
1869  }
1870 
1871  enc = g_quark_to_string(iconv_item->encoding);
1872  iconv_item->iconv = g_iconv_open("UTF-8", enc);
1873  if (iconv_item->iconv == (GIConv) - 1)
1874  {
1875  PWARN("Unable to open IConv conversion descriptor for '%s'", enc);
1876  goto cleanup_find_ambs;
1877  }
1878  else
1879  {
1880  iconv_list = g_list_prepend(iconv_list, iconv_item);
1881  }
1882  }
1883 
1884  /* prepare data containers */
1885  if (unique)
1886  *unique = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
1887  (GDestroyNotify) conv_free);
1888  if (ambiguous)
1889  *ambiguous = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
1890  (GDestroyNotify) conv_list_free);
1891  if (impossible)
1892  *impossible = NULL;
1893  processed = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
1894 
1895  /* loop through lines */
1896  while (1)
1897  {
1898  gchar line[256], *word, *utf8;
1899  gchar **word_array, **word_cursor;
1900  conv_type *conv = NULL;
1901 
1902  if (!fgets(line, sizeof(line) - 1, file))
1903  {
1904  if (feof(file))
1905  {
1906  break;
1907  }
1908  else
1909  {
1910  goto cleanup_find_ambs;
1911  }
1912  }
1913 
1914  g_strchomp(line);
1915  replace_character_references(line);
1916  word_array = g_strsplit_set(line, "> <", 0);
1917 
1918  /* loop through words */
1919  for (word_cursor = word_array; *word_cursor; word_cursor++)
1920  {
1921  word = *word_cursor;
1922  if (!word)
1923  continue;
1924 
1925  utf8 = g_convert_with_iconv(word, -1, ascii->iconv,
1926  NULL, NULL, &error);
1927  if (utf8)
1928  {
1929  /* pure ascii */
1930  g_free(utf8);
1931  continue;
1932  }
1933  g_error_free(error);
1934  error = NULL;
1935 
1936  if (g_hash_table_lookup_extended(processed, word, NULL, NULL))
1937  {
1938  /* already processed */
1939  continue;
1940  }
1941 
1942  /* loop through encodings */
1943  conv_list = NULL;
1944  for (iter = iconv_list; iter; iter = iter->next)
1945  {
1946  iconv_item = iter->data;
1947  utf8 = g_convert_with_iconv(word, -1, iconv_item->iconv,
1948  NULL, NULL, &error);
1949  if (utf8)
1950  {
1951  conv = g_new(conv_type, 1);
1952  conv->encoding = iconv_item->encoding;
1953  conv->utf8_string = utf8;
1954  conv_list = g_list_prepend(conv_list, conv);
1955  }
1956  else
1957  {
1958  g_error_free(error);
1959  error = NULL;
1960  }
1961  }
1962 
1963  /* no successful conversion */
1964  if (!conv_list)
1965  {
1966  if (impossible)
1967  *impossible = g_list_append(*impossible, g_strdup(word));
1968  n_impossible++;
1969  }
1970 
1971  /* more than one successful conversion */
1972  else if (conv_list->next)
1973  {
1974  if (ambiguous)
1975  {
1976  g_hash_table_insert(*ambiguous, g_strdup(word), conv_list);
1977  }
1978  else
1979  {
1980  conv_list_free(conv_list);
1981  }
1982  }
1983 
1984  /* only one successful conversion */
1985  else
1986  {
1987  if (unique)
1988  {
1989  g_hash_table_insert(*unique, g_strdup(word), conv);
1990  }
1991  else
1992  {
1993  conv_free(conv);
1994  }
1995  g_list_free(conv_list);
1996  }
1997 
1998  g_hash_table_insert(processed, g_strdup(word), NULL);
1999  }
2000  g_strfreev(word_array);
2001  }
2002 
2003  clean_return = TRUE;
2004 
2005 cleanup_find_ambs:
2006 
2007  if (iconv_list)
2008  {
2009  for (iter = iconv_list; iter; iter = iter->next)
2010  {
2011  if (iter->data)
2012  {
2013  g_iconv_close(((iconv_item_type*) iter->data)->iconv);
2014  g_free(iter->data);
2015  }
2016  }
2017  g_list_free(iconv_list);
2018  }
2019  if (processed)
2020  g_hash_table_destroy(processed);
2021  if (ascii)
2022  g_free(ascii);
2023  if (file)
2024  {
2025  fclose(file);
2026  if (is_compressed)
2027  wait_for_gzip(file);
2028  }
2029 
2030  return (clean_return) ? n_impossible : -1;
2031 }
2032 
2033 typedef struct
2034 {
2035  gchar *filename;
2036  GHashTable *subst;
2037 } push_data_type;
2038 
2039 static void
2040 parse_with_subst_push_handler (xmlParserCtxtPtr xml_context,
2041  push_data_type *push_data)
2042 {
2043  const gchar *filename;
2044  FILE *file = NULL;
2045  GIConv ascii = (GIConv) - 1;
2046  GString *output = NULL;
2047  GError *error = NULL;
2048  gboolean is_compressed;
2049 
2050  filename = push_data->filename;
2051  is_compressed = is_gzipped_file(filename);
2052  file = try_gz_open(filename, "r", is_compressed, FALSE);
2053  if (file == NULL)
2054  {
2055  PWARN("Unable to open file %s", filename);
2056  goto cleanup_push_handler;
2057  }
2058 
2059  ascii = g_iconv_open("UTF-8", "ASCII");
2060  if (ascii == (GIConv) - 1)
2061  {
2062  PWARN("Unable to open ASCII ICONV conversion descriptor");
2063  goto cleanup_push_handler;
2064  }
2065 
2066  /* loop through lines */
2067  while (1)
2068  {
2069  gchar line[256], *word, *repl, *utf8;
2070  gint pos, len;
2071  gchar *start, *cursor;
2072 
2073  if (!fgets(line, sizeof(line) - 1, file))
2074  {
2075  if (feof(file))
2076  {
2077  break;
2078  }
2079  else
2080  {
2081  goto cleanup_push_handler;
2082  }
2083  }
2084 
2085  replace_character_references(line);
2086  output = g_string_new(line);
2087 
2088  /* loop through words */
2089  cursor = output->str;
2090  pos = 0;
2091  while (1)
2092  {
2093  /* ignore delimiters */
2094  while (*cursor == '>' || *cursor == ' ' || *cursor == '<' ||
2095  *cursor == '\n')
2096  {
2097  cursor++;
2098  pos += 1;
2099  }
2100 
2101  if (!*cursor)
2102  /* welcome to EOL */
2103  break;
2104 
2105  /* search for a delimiter */
2106  start = cursor;
2107  len = 0;
2108  while (*cursor && *cursor != '>' && *cursor != ' ' && *cursor != '<' &&
2109  *cursor != '\n')
2110  {
2111  cursor++;
2112  len++;
2113  }
2114 
2115  utf8 = g_convert_with_iconv(start, len, ascii, NULL, NULL, &error);
2116 
2117  if (utf8)
2118  {
2119  /* pure ascii */
2120  g_free(utf8);
2121  pos += len;
2122  }
2123  else
2124  {
2125  g_error_free(error);
2126  error = NULL;
2127 
2128  word = g_strndup(start, len);
2129  repl = g_hash_table_lookup(push_data->subst, word);
2130  g_free(word);
2131  if (repl)
2132  {
2133  /* there is a replacement */
2134  output = g_string_insert(g_string_erase(output, pos, len),
2135  pos, repl);
2136  pos += strlen(repl);
2137  cursor = output->str + pos;
2138  }
2139  else
2140  {
2141  /* there is no replacement, return immediately */
2142  goto cleanup_push_handler;
2143  }
2144  }
2145  }
2146 
2147  if (xmlParseChunk(xml_context, output->str, output->len, 0) != 0)
2148  {
2149  goto cleanup_push_handler;
2150  }
2151  }
2152 
2153  /* last chunk */
2154  xmlParseChunk(xml_context, "", 0, 1);
2155 
2156 cleanup_push_handler:
2157 
2158  if (output)
2159  g_string_free(output, TRUE);
2160  if (ascii != (GIConv) - 1)
2161  g_iconv_close(ascii);
2162  if (file)
2163  {
2164  fclose(file);
2165  if (is_compressed)
2166  wait_for_gzip(file);
2167  }
2168 }
2169 
2170 gboolean
2171 gnc_xml2_parse_with_subst (FileBackend *fbe, QofBook *book, GHashTable *subst)
2172 {
2173  push_data_type *push_data;
2174  gboolean success;
2175 
2176  push_data = g_new(push_data_type, 1);
2177  push_data->filename = fbe->fullpath;
2178  push_data->subst = subst;
2179 
2180  success = qof_session_load_from_xml_file_v2_full(
2181  fbe, book, (sixtp_push_handler) parse_with_subst_push_handler,
2182  push_data, GNC_BOOK_XML2_FILE);
2183 
2184  if (success)
2185  qof_instance_set_dirty (QOF_INSTANCE (book));
2186 
2187  return success;
2188 }
2189 
gnc_commodity * gnc_commodity_table_insert(gnc_commodity_table *table, gnc_commodity *comm)
Account * gnc_account_get_parent(const Account *acc)
Definition: Account.c:2623
int xaccAccountTreeForEachTransaction(Account *acc, TransactionCallback proc, void *data)
gnc_commodity_table * gnc_commodity_table_get_table(QofBook *book)
gboolean qof_session_load_from_xml_file_v2(FileBackend *, QofBook *, QofBookFileType)
Definition: io-gncxml-v2.c:865
Definition: sixtp.h:93
void xaccTransScrubCurrency(Transaction *trans)
Definition: Scrub.c:993
void qof_backend_set_error(QofBackend *be, QofBackendError err)
void gnc_account_append_child(Account *new_parent, Account *child)
Definition: Account.c:2525
gint gnc_account_n_descendants(const Account *account)
Definition: Account.c:2698
gboolean gnc_book_write_accounts_to_xml_filehandle_v2(QofBackend *be, QofBook *book, FILE *fh)
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
int xaccAccountGetCommoditySCUi(const Account *acc)
Definition: Account.c:2451
void xaccAccountTreeScrubCommodities(Account *acc)
Definition: Scrub.c:1174
#define GNC_FILE_BACKEND
Definition: io-gncxml-v2.h:99
gnc_commodity * DxaccAccountGetCurrency(const Account *acc)
Definition: Account.c:3128
void gnc_account_foreach_descendant(const Account *acc, AccountCb thunk, gpointer user_data)
Definition: Account.c:2958
GNCAccountType xaccAccountGetType(const Account *acc)
Definition: Account.c:3009
void xaccAccountScrubCommodity(Account *account)
Definition: Scrub.c:1110
gboolean gnc_xml2_parse_with_subst(FileBackend *fbe, QofBook *book, GHashTable *subst)
void xaccLogDisable(void)
Definition: TransLog.c:93
#define DEBUG(format, args...)
Definition: qoflog.h:255
Account * gnc_book_get_template_root(const QofBook *book)
Definition: SX-book.c:65
const char * gnc_commodity_get_namespace(const gnc_commodity *cm)
#define PERR(format, args...)
Definition: qoflog.h:237
void qof_collection_foreach(const QofCollection *, QofInstanceForeachCB, gpointer user_data)
GNCPriceDB * gnc_pricedb_get_db(QofBook *book)
Definition: gnc-pricedb.c:872
void xaccTransSetCurrency(Transaction *trans, gnc_commodity *curr)
Definition: Transaction.c:1354
#define PWARN(format, args...)
Definition: qoflog.h:243
convert single-entry accounts to clean double-entry
void qof_book_mark_session_saved(QofBook *book)
api for GnuCash version 2 XML-based file format
GList * gnc_commodity_table_get_namespaces(const gnc_commodity_table *table)
guint gnc_pricedb_get_num_prices(GNCPriceDB *db)
Definition: gnc-pricedb.c:894
Anchor Scheduled Transaction info in a book. See src/doc/books.txt for design overview.
void(* QofBePercentageFunc)(const char *message, double percent)
DOCUMENT ME!
Definition: qofbackend.h:150
QofBookFileType gnc_is_xml_data_file_v2(const gchar *name, gboolean *with_encoding)
QofBackendError qof_backend_get_error(QofBackend *be)
void xaccTransCommitEdit(Transaction *trans)
Definition: Transaction.c:1579
void xaccTransBeginEdit(Transaction *trans)
Definition: Transaction.c:1380
All type declarations for the whole Gnucash engine.
void xaccAccountSetCommoditySCU(Account *acc, int scu)
Definition: Account.c:2435
CommodityList * gnc_commodity_table_get_commodities(const gnc_commodity_table *table, const char *name_space)
API for the transaction logger.
guint gnc_book_count_transactions(QofBook *book)
Definition: Transaction.c:2483
gint gnc_xml2_find_ambiguous(const gchar *filename, GList *encodings, GHashTable **unique, GHashTable **ambiguous, GList **impossible)
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Definition: Account.c:3148
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Definition: Transaction.c:1348
gboolean gnc_xml2_write_namespace_decl(FILE *out, const char *name_space)
const char * gnc_commodity_get_unique_name(const gnc_commodity *cm)
QofCollection * qof_book_get_collection(const QofBook *, QofIdType)
guint gnc_commodity_table_get_size(const gnc_commodity_table *tbl)
guint qof_collection_count(const QofCollection *col)
void DxaccAccountSetCurrency(Account *acc, gnc_commodity *currency)
Definition: Account.c:2498
QofBackend * qof_book_get_backend(const QofBook *book)
Retrieve the backend used by this book.
void xaccAccountTreeScrubQuoteSources(Account *root, gnc_commodity_table *table)
Definition: Scrub.c:1231
API for Transactions and Splits (journal entries)
void xaccAccountCommitEdit(Account *acc)
Definition: Account.c:1321
void gnc_commodity_destroy(gnc_commodity *cm)
void xaccLogEnable(void)
Definition: TransLog.c:97
void run_callback(sixtp_gdv2 *data, const char *type)
Definition: io-gncxml-v2.c:109
const gchar * QofLogModule
Definition: qofid.h:89
void xaccAccountSetCommodity(Account *acc, gnc_commodity *com)
Definition: Account.c:2389