22 #include <glib/gstdio.h> 28 #ifdef __STRICT_ANSI__ 29 #undef __STRICT_ANSI__ 30 #define __STRICT_ANSI_UNSET__ 1 49 #include "gnc-pricedb-p.h" 52 #include "SX-book-p.h" 54 #include "TransactionP.hpp" 57 #ifdef __STRICT_ANSI_UNSET__ 58 #undef __STRICT_ANSI_UNSET__ 59 #define __STRICT_ANSI__ 1 63 # define g_fopen fopen 67 #include "gnc-xml-backend.hpp" 68 #include "sixtp-parsers.h" 69 #include "sixtp-utils.h" 72 #include "sixtp-dom-parsers.h" 74 #include "io-gncxml-gen.h" 76 static QofLogModule log_module = GNC_MOD_IO;
98 static std::vector<GncXmlDataType_t> backend_registry;
100 gnc_xml_register_backend(GncXmlDataType_t& xmlbe)
102 backend_registry.push_back(xmlbe);
105 #define GNC_V2_STRING "gnc-v2" 107 const gchar* gnc_v2_xml_version_string = GNC_V2_STRING;
109 gnc_v2_book_version_string;
112 static std::pair<FILE*, GThread*> try_gz_open (
const char* filename,
116 static bool is_gzipped_file (
const gchar* name);
119 clear_up_account_commodity (
120 gnc_commodity_table* tbl,
Account* act,
121 gnc_commodity * (*getter) (
const Account* account),
122 void (*setter) (
Account* account, gnc_commodity* comm),
123 int (*scu_getter) (
const Account* account),
124 void (*scu_setter) (
Account* account,
int scu))
127 gnc_commodity* com = getter (act);
131 old_scu = scu_getter (act);
149 PWARN (
"unable to find global commodity for %s adding new",
156 if (old_scu != 0 && scu_setter)
157 scu_setter (act, old_scu);
163 clear_up_transaction_commodity (
164 gnc_commodity_table* tbl, Transaction* trans,
165 gnc_commodity * (*getter) (
const Transaction* trans),
166 void (*setter) (Transaction* trans, gnc_commodity* comm))
169 gnc_commodity* com = getter (trans);
185 PWARN (
"unable to find global commodity for %s adding new",
192 setter (trans, gcom);
201 gnc_commodity_table*
table;
207 clear_up_account_commodity (
table, act,
212 clear_up_account_commodity (
table, act,
227 gnc_book_set_root_account (data->book, act);
234 root = gnc_book_get_root_account (data->book);
239 data->counter.accounts_loaded++;
240 sixtp_run_callback (data,
"account");
246 add_book_local (
sixtp_gdv2* data, QofBook* book)
248 data->counter.books_loaded++;
249 sixtp_run_callback (data,
"book");
255 add_commodity_local (
sixtp_gdv2* data, gnc_commodity* com)
257 gnc_commodity_table*
table;
263 data->counter.commodities_loaded++;
264 sixtp_run_callback (data,
"commodities");
270 add_transaction_local (
sixtp_gdv2* data, Transaction* trn)
272 gnc_commodity_table*
table;
277 clear_up_transaction_commodity (
table, trn,
285 data->counter.transactions_loaded++;
286 sixtp_run_callback (data,
"transaction");
291 add_schedXaction_local (
sixtp_gdv2* data, SchedXaction* sx)
294 sxes = gnc_book_get_schedxactions (data->book);
295 gnc_sxes_add_sx (sxes, sx);
296 data->counter.schedXactions_loaded++;
297 sixtp_run_callback (data,
"schedXactions");
302 add_template_transaction_local (
sixtp_gdv2* data,
303 gnc_template_xaction_data* txd)
314 for (n = txd->accts; n; n = n->next)
321 gnc_book_set_template_root (book, (
Account*)n->data);
335 for (n = txd->transactions; n; n = n->next)
338 add_transaction_local (data, (Transaction*)n->data);
345 add_pricedb_local (
sixtp_gdv2* data, GNCPriceDB* db)
352 counter (
const GncXmlDataType_t& data,
file_backend* be_data)
354 g_return_if_fail (data.version == GNC_FILE_BACKEND_VERS);
356 if (be_data->ok == TRUE)
359 if (!g_strcmp0 (be_data->tag, data.type_name))
366 gnc_counter_end_handler (gpointer data_for_children,
367 GSList* data_from_children, GSList* sibling_data,
368 gpointer parent_data, gpointer global_data,
369 gpointer* result,
const gchar* tag)
374 xmlNodePtr tree = (xmlNodePtr)data_for_children;
375 gxpf_data* gdata = (gxpf_data*)global_data;
387 g_return_val_if_fail (tree, FALSE);
393 type = (
char*)xmlGetProp (tree, BAD_CAST
"cd:type");
394 strval = dom_tree_to_text (tree);
395 if (!string_to_gint64 (strval, &val))
397 PERR (
"string_to_gint64 failed with input: %s",
398 strval ? strval :
"(null)");
401 else if (g_strcmp0 (type,
"transaction") == 0)
403 sixdata->counter.transactions_total = val;
405 else if (g_strcmp0 (type,
"account") == 0)
407 sixdata->counter.accounts_total = val;
409 else if (g_strcmp0 (type,
"book") == 0)
411 sixdata->counter.books_total = val;
413 else if (g_strcmp0 (type,
"commodity") == 0)
415 sixdata->counter.commodities_total = val;
417 else if (g_strcmp0 (type,
"schedxaction") == 0)
419 sixdata->counter.schedXactions_total = val;
421 else if (g_strcmp0 (type,
"budget") == 0)
423 sixdata->counter.budgets_total = val;
425 else if (g_strcmp0 (type,
"price") == 0)
427 sixdata->counter.prices_total = val;
435 for(
auto data : backend_registry)
436 counter(data, &be_data);
438 if (be_data.ok == FALSE)
440 PERR (
"Unknown type: %s", type ? type :
"(null)");
458 gnc_counter_sixtp_parser_create (
void)
460 return sixtp_dom_parser_new (gnc_counter_end_handler, NULL, NULL);
466 DEBUG (
"Transactions: Total: %d, Loaded: %d",
467 data->transactions_total, data->transactions_loaded);
468 DEBUG (
"Accounts: Total: %d, Loaded: %d",
469 data->accounts_total, data->accounts_loaded);
470 DEBUG (
"Books: Total: %d, Loaded: %d",
471 data->books_total, data->books_loaded);
472 DEBUG (
"Commodities: Total: %d, Loaded: %d",
473 data->commodities_total, data->commodities_loaded);
474 DEBUG (
"Scheduled Transactions: Total: %d, Loaded: %d",
475 data->schedXactions_total, data->schedXactions_loaded);
476 DEBUG (
"Budgets: Total: %d, Loaded: %d",
477 data->budgets_total, data->budgets_loaded);
481 file_rw_feedback (
sixtp_gdv2* gd,
const char* type)
484 int loaded, total, percentage;
486 g_assert (gd != NULL);
487 if (!gd->gui_display_fn)
490 counter = &gd->counter;
491 loaded = counter->transactions_loaded + counter->accounts_loaded +
492 counter->books_loaded + counter->commodities_loaded +
493 counter->schedXactions_loaded + counter->budgets_loaded +
494 counter->prices_loaded;
495 total = counter->transactions_total + counter->accounts_total +
496 counter->books_total + counter->commodities_total +
497 counter->schedXactions_total + counter->budgets_total +
498 counter->prices_total;
502 percentage = (loaded * 100) / total;
503 if (percentage > 100)
520 gd->gui_display_fn (NULL, percentage);
523 static const char* BOOK_TAG =
"gnc:book";
524 static const char* BOOK_ID_TAG =
"book:id";
525 static const char* BOOK_SLOTS_TAG =
"book:slots";
526 static const char* ACCOUNT_TAG =
"gnc:account";
527 static const char* PRICEDB_TAG =
"gnc:pricedb";
528 static const char* COMMODITY_TAG =
"gnc:commodity";
529 static const char* COUNT_DATA_TAG =
"gnc:count-data";
530 static const char* TRANSACTION_TAG =
"gnc:transaction";
531 static const char* SCHEDXACTION_TAG =
"gnc:schedxaction";
532 static const char* TEMPLATE_TRANSACTION_TAG =
"gnc:template-transactions";
533 static const char* BUDGET_TAG =
"gnc:budget";
536 add_item (
const GncXmlDataType_t& data,
struct file_backend* be_data)
538 g_return_if_fail (data.version == GNC_FILE_BACKEND_VERS);
543 if (!g_strcmp0 (be_data->tag, data.type_name))
546 (data.add_item)(be_data->gd, be_data->data);
553 book_callback (
const char* tag, gpointer globaldata, gpointer data)
557 if (g_strcmp0 (tag, ACCOUNT_TAG) == 0)
559 add_account_local (gd, (
Account*)data);
561 else if (g_strcmp0 (tag, PRICEDB_TAG) == 0)
563 add_pricedb_local (gd, (GNCPriceDB*)data);
565 else if (g_strcmp0 (tag, COMMODITY_TAG) == 0)
567 add_commodity_local (gd, (gnc_commodity*)data);
569 else if (g_strcmp0 (tag, TRANSACTION_TAG) == 0)
571 add_transaction_local (gd, (Transaction*)data);
573 else if (g_strcmp0 (tag, SCHEDXACTION_TAG) == 0)
575 add_schedXaction_local (gd, (SchedXaction*)data);
577 else if (g_strcmp0 (tag, TEMPLATE_TRANSACTION_TAG) == 0)
579 add_template_transaction_local (gd, (gnc_template_xaction_data*)data);
581 else if (g_strcmp0 (tag, BUDGET_TAG) == 0)
594 for (
auto data : backend_registry)
595 add_item(data, &be_data);
597 if (be_data.ok == FALSE)
599 PWARN (
"unexpected tag %s", tag);
606 generic_callback (
const char* tag, gpointer globaldata, gpointer data)
610 if (g_strcmp0 (tag, BOOK_TAG) == 0)
612 add_book_local (gd, (QofBook*)data);
613 book_callback (tag, globaldata, data);
618 book_callback (tag, globaldata, data);
624 add_parser(
const GncXmlDataType_t& data,
struct file_backend* be_data)
626 g_return_if_fail (data.version == GNC_FILE_BACKEND_VERS);
628 if (be_data->ok == FALSE)
631 if (data.create_parser)
632 if (!sixtp_add_some_sub_parsers(
633 be_data->parser, TRUE,
634 data.type_name, (data.create_parser)(),
640 scrub (
const GncXmlDataType_t& data,
struct file_backend* be_data)
642 g_return_if_fail (data.version == GNC_FILE_BACKEND_VERS);
645 (data.scrub)(be_data->book);
652 countCallbackFn countcallback,
657 if (gd == NULL)
return NULL;
660 gd->counter.accounts_loaded = 0;
661 gd->counter.accounts_total = 0;
662 gd->counter.books_loaded = 0;
663 gd->counter.books_total = 0;
664 gd->counter.commodities_loaded = 0;
665 gd->counter.commodities_total = 0;
666 gd->counter.transactions_loaded = 0;
667 gd->counter.transactions_total = 0;
668 gd->counter.prices_loaded = 0;
669 gd->counter.prices_total = 0;
670 gd->counter.schedXactions_loaded = 0;
671 gd->counter.schedXactions_total = 0;
672 gd->counter.budgets_loaded = 0;
673 gd->counter.budgets_total = 0;
674 gd->exporting = exporting;
675 gd->countCallback = countcallback;
676 gd->gui_display_fn = gui_display_fn;
681 qof_session_load_from_xml_file_v2_full (
683 sixtp_push_handler push_handler, gpointer push_user_data,
684 QofBookFileType type)
696 gd = gnc_sixtp_gdv2_new (book, FALSE, file_rw_feedback,
697 xml_be->get_percentage());
699 top_parser = sixtp_new ();
700 main_parser = sixtp_new ();
701 book_parser = sixtp_new ();
703 if (type == GNC_BOOK_XML2_FILE)
704 v2type = g_strdup (GNC_V2_STRING);
706 if (!sixtp_add_some_sub_parsers (
717 if (!sixtp_add_some_sub_parsers (
719 COUNT_DATA_TAG, gnc_counter_sixtp_parser_create (),
720 BOOK_TAG, book_parser,
725 PRICEDB_TAG, gnc_pricedb_sixtp_parser_create (),
726 COMMODITY_TAG, gnc_commodity_sixtp_parser_create (),
727 ACCOUNT_TAG, gnc_account_sixtp_parser_create (),
728 TRANSACTION_TAG, gnc_transaction_sixtp_parser_create (),
729 SCHEDXACTION_TAG, gnc_schedXaction_sixtp_parser_create (),
730 TEMPLATE_TRANSACTION_TAG, gnc_template_transaction_sixtp_parser_create (),
736 if (!sixtp_add_some_sub_parsers (
738 BOOK_ID_TAG, gnc_book_id_sixtp_parser_create (),
739 BOOK_SLOTS_TAG, gnc_book_slots_sixtp_parser_create (),
740 COUNT_DATA_TAG, gnc_counter_sixtp_parser_create (),
741 PRICEDB_TAG, gnc_pricedb_sixtp_parser_create (),
742 COMMODITY_TAG, gnc_commodity_sixtp_parser_create (),
743 ACCOUNT_TAG, gnc_account_sixtp_parser_create (),
744 BUDGET_TAG, gnc_budget_sixtp_parser_create (),
745 TRANSACTION_TAG, gnc_transaction_sixtp_parser_create (),
746 SCHEDXACTION_TAG, gnc_schedXaction_sixtp_parser_create (),
747 TEMPLATE_TRANSACTION_TAG, gnc_template_transaction_sixtp_parser_create (),
754 be_data.parser = book_parser;
755 for (
auto data : backend_registry)
756 add_parser(data, &be_data);
757 if (be_data.ok == FALSE)
762 xaccDisableDataScrubbing ();
766 gpointer parse_result = NULL;
769 gpdata.cb = generic_callback;
770 gpdata.parsedata = gd;
771 gpdata.bookdata = book;
773 retval = sixtp_parse_push (top_parser, push_handler, push_user_data,
774 NULL, &gpdata, &parse_result);
784 auto filename = xml_be->get_filename();
785 auto [file, thread] = try_gz_open (filename,
"r",
786 is_gzipped_file (filename), FALSE);
789 PWARN (
"Unable to open file %s", filename);
794 retval = gnc_xml_parse_fd (top_parser, file,
795 generic_callback, gd, book);
798 g_thread_join (thread);
804 sixtp_destroy (top_parser);
806 xaccEnableDataScrubbing ();
809 debug_print_counter_data (&gd->counter);
812 sixtp_destroy (top_parser);
815 xaccEnableDataScrubbing ();
821 memset (&be_data, 0,
sizeof (be_data));
823 for (
auto data : backend_registry)
824 scrub(data, &be_data);
827 root = gnc_book_get_root_account (book);
834 xaccAccountTreeScrubSplits (root);
840 gnc_account_foreach_descendant (root,
843 gnc_account_foreach_descendant (template_root,
847 if (qof_instance_get_editlevel(root) != 0)
849 if (qof_instance_get_editlevel(template_root) != 0)
863 qof_session_load_from_xml_file_v2 (
GncXmlBackend* xml_be, QofBook* book,
864 QofBookFileType type)
866 return qof_session_load_from_xml_file_v2_full (xml_be, book, NULL, NULL, type);
872 write_counts (FILE* out, ...)
876 gboolean success = TRUE;
879 type = va_arg (ap,
char*);
881 while (success && type)
883 int amount = va_arg (ap,
int);
887 #if GNUCASH_REALLY_BUILD_AN_XML_TREE_ON_OUTPUT 888 char *type_dup = g_strdup (type);
892 val = g_strdup_printf (
"%d", amount);
894 node = xmlNewNode (NULL, BAD_CAST COUNT_DATA_TAG);
900 xmlSetProp (node, BAD_CAST
"cd:type", checked_char_cast (type_dup));
901 xmlNodeAddContent (node, checked_char_cast (val));
905 xmlElemDump (out, NULL, node);
908 if (ferror (out) || fprintf (out,
"\n") < 0)
914 if (fprintf (out,
"<%s %s=\"%s\">%d</%s>\n",
915 COUNT_DATA_TAG,
"cd:type", type, amount, COUNT_DATA_TAG) < 0)
924 type = va_arg (ap,
char*);
932 compare_namespaces (gconstpointer a, gconstpointer b)
934 const gchar* sa = (
const gchar*) a;
935 const gchar* sb = (
const gchar*) b;
936 return (g_strcmp0 (sa, sb));
940 compare_commodity_ids (gconstpointer a, gconstpointer b)
942 const gnc_commodity* ca = (
const gnc_commodity*) a;
943 const gnc_commodity* cb = (
const gnc_commodity*) b;
948 static gboolean write_pricedb (FILE* out, QofBook* book,
sixtp_gdv2* gd);
949 static gboolean write_transactions (FILE* out, QofBook* book,
sixtp_gdv2* gd);
950 static gboolean write_template_transaction_data (FILE* out, QofBook* book,
952 static gboolean write_schedXactions (FILE* out, QofBook* book,
sixtp_gdv2* gd);
953 static void write_budget (
QofInstance* ent, gpointer data);
956 write_counts(
const GncXmlDataType_t& data,
struct file_backend* be_data)
958 g_return_if_fail (data.version == GNC_FILE_BACKEND_VERS);
961 write_counts (be_data->out, data.type_name,
962 (data.get_count) (be_data->book),
967 write_data(
const GncXmlDataType_t& data,
struct file_backend* be_data)
969 g_return_if_fail (data.version == GNC_FILE_BACKEND_VERS);
971 if (data.write && !ferror(be_data->out))
972 (data.write)(be_data->out, be_data->book);
976 write_book (FILE* out, QofBook* book,
sixtp_gdv2* gd)
983 if (fprintf (out,
"<%s version=\"%s\">\n", BOOK_TAG,
984 gnc_v2_book_version_string) < 0)
986 if (!write_book_parts (out, book))
992 if (!write_counts (out,
1001 g_list_length (gnc_book_get_schedxactions (book)->sx_list),
1008 for (
auto data : backend_registry)
1009 write_counts(data, &be_data);
1012 || !write_commodities (out, book, gd)
1013 || !write_pricedb (out, book, gd)
1014 || !write_accounts (out, book, gd)
1015 || !write_transactions (out, book, gd)
1016 || !write_template_transaction_data (out, book, gd)
1017 || !write_schedXactions (out, book, gd))
1022 write_budget, &be_data);
1026 for (
auto data : backend_registry)
1027 write_data(data, &be_data);
1031 if (fprintf (out,
"</%s>\n", BOOK_TAG) < 0)
1038 write_commodities (FILE* out, QofBook* book,
sixtp_gdv2* gd)
1040 gnc_commodity_table* tbl;
1043 gboolean success = TRUE;
1050 namespaces = g_list_sort (namespaces, compare_namespaces);
1053 for (lp = namespaces; success && lp; lp = lp->next)
1059 static_cast<const char*> (lp->data));
1060 comms = g_list_sort (comms, compare_commodity_ids);
1062 for (lp2 = comms; lp2; lp2 = lp2->next)
1064 comnode = gnc_commodity_dom_tree_create (static_cast<const gnc_commodity*>
1066 if (comnode == NULL)
1069 xmlElemDump (out, NULL, comnode);
1070 if (ferror (out) || fprintf (out,
"\n") < 0)
1076 xmlFreeNode (comnode);
1077 gd->counter.commodities_loaded++;
1078 sixtp_run_callback (gd,
"commodities");
1081 g_list_free (comms);
1084 if (namespaces) g_list_free (namespaces);
1090 write_pricedb (FILE* out, QofBook* book,
sixtp_gdv2* gd)
1094 xmlOutputBufferPtr outbuf;
1107 if (fprintf (out,
"<%s version=\"%s\">\n", parent->name,
1108 xmlGetProp (parent, BAD_CAST
"version")) < 0)
1113 outbuf = xmlOutputBufferCreateFile (out, NULL);
1116 xmlFreeNode (parent);
1120 for (node = parent->children; node; node = node->next)
1123 xmlOutputBufferWrite (outbuf, 2,
" ");
1124 xmlNodeDumpOutput (outbuf, NULL, node, 1, 1, NULL);
1126 xmlOutputBufferWrite (outbuf, 1,
"\n");
1129 gd->counter.prices_loaded += 1;
1130 sixtp_run_callback (gd,
"prices");
1133 xmlOutputBufferClose (outbuf);
1135 if (ferror (out) || fprintf (out,
"</%s>\n", parent->name) < 0)
1137 xmlFreeNode (parent);
1141 xmlFreeNode (parent);
1146 xml_add_trn_data (Transaction* t, gpointer data)
1148 struct file_backend* be_data =
static_cast<decltype (be_data)
> (data);
1151 node = gnc_transaction_dom_tree_create (t);
1153 xmlElemDump (be_data->out, NULL, node);
1156 if (ferror (be_data->out) || fprintf (be_data->out,
"\n") < 0)
1159 be_data->gd->counter.transactions_loaded++;
1160 sixtp_run_callback (be_data->gd,
"transaction");
1165 write_transactions (FILE* out, QofBook* book,
sixtp_gdv2* gd)
1174 (gpointer) &be_data);
1178 write_template_transaction_data (FILE* out, QofBook* book,
sixtp_gdv2* gd)
1189 if (fprintf (out,
"<%s>\n", TEMPLATE_TRANSACTION_TAG) < 0
1190 || !write_account_tree (out, ra, gd)
1192 || fprintf (out,
"</%s>\n", TEMPLATE_TRANSACTION_TAG) < 0)
1201 write_schedXactions (FILE* out, QofBook* book,
sixtp_gdv2* gd)
1203 GList* schedXactions;
1204 SchedXaction* tmpSX;
1207 schedXactions = gnc_book_get_schedxactions (book)->sx_list;
1209 if (schedXactions == NULL)
1214 tmpSX =
static_cast<decltype (tmpSX)
> (schedXactions->data);
1215 node = gnc_schedXaction_dom_tree_create (tmpSX);
1216 xmlElemDump (out, NULL, node);
1218 if (ferror (out) || fprintf (out,
"\n") < 0)
1220 gd->counter.schedXactions_loaded++;
1221 sixtp_run_callback (gd,
"schedXactions");
1223 while ((schedXactions = schedXactions->next));
1232 struct file_backend* file_be =
static_cast<decltype (file_be)
> (data);
1234 GncBudget* bgt = GNC_BUDGET (ent);
1236 if (ferror (file_be->out))
1239 node = gnc_budget_dom_tree_create (bgt);
1240 xmlElemDump (file_be->out, NULL, node);
1242 if (ferror (file_be->out) || fprintf (file_be->out,
"\n") < 0)
1245 file_be->gd->counter.budgets_loaded++;
1246 sixtp_run_callback (file_be->gd,
"budgets");
1250 gnc_xml2_write_namespace_decl (FILE* out,
const char* name_space)
1252 g_return_val_if_fail (name_space, FALSE);
1253 return fprintf (out,
"\n xmlns:%s=\"http://www.gnucash.org/XML/%s\"",
1254 name_space, name_space) >= 0;
1258 write_namespace (
const GncXmlDataType_t& data, FILE* out)
1260 g_return_if_fail (data.version == GNC_FILE_BACKEND_VERS);
1262 if (data.ns && !ferror(out))
1267 write_v2_header (FILE* out)
1269 if (fprintf (out,
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n") < 0
1270 || fprintf (out,
"<" GNC_V2_STRING) < 0
1272 || !gnc_xml2_write_namespace_decl (out,
"gnc")
1273 || !gnc_xml2_write_namespace_decl (out,
"act")
1274 || !gnc_xml2_write_namespace_decl (out,
"book")
1275 || !gnc_xml2_write_namespace_decl (out,
"cd")
1276 || !gnc_xml2_write_namespace_decl (out,
"cmdty")
1277 || !gnc_xml2_write_namespace_decl (out,
"price")
1278 || !gnc_xml2_write_namespace_decl (out,
"slot")
1279 || !gnc_xml2_write_namespace_decl (out,
"split")
1280 || !gnc_xml2_write_namespace_decl (out,
"sx")
1281 || !gnc_xml2_write_namespace_decl (out,
"trn")
1282 || !gnc_xml2_write_namespace_decl (out,
"ts")
1283 || !gnc_xml2_write_namespace_decl (out,
"fs")
1284 || !gnc_xml2_write_namespace_decl (out,
"bgt")
1285 || !gnc_xml2_write_namespace_decl (out,
"recurrence")
1286 || !gnc_xml2_write_namespace_decl (out,
"lot"))
1291 for (
auto data : backend_registry)
1292 write_namespace(data, out);
1294 if (ferror (out) || fprintf (out,
">\n") < 0)
1301 gnc_book_write_to_xml_filehandle_v2 (QofBook* book, FILE* out)
1305 gboolean success = TRUE;
1307 if (!out)
return FALSE;
1309 if (!write_v2_header (out)
1310 || !write_counts (out,
"book", 1, NULL))
1314 gd = gnc_sixtp_gdv2_new (book, FALSE, file_rw_feedback,
1315 qof_be->get_percentage());
1316 gd->counter.commodities_total =
1318 gd->counter.accounts_total = 1 +
1321 gd->counter.schedXactions_total =
1322 g_list_length (gnc_book_get_schedxactions (book)->sx_list);
1328 if (!write_book (out, book, gd)
1329 || fprintf (out,
"</" GNC_V2_STRING
">\n\n") < 0)
1340 gnc_book_write_accounts_to_xml_filehandle_v2 (
QofBackend* qof_be, QofBook* book,
1343 gnc_commodity_table*
table;
1347 gboolean success = TRUE;
1349 if (!out)
return FALSE;
1351 root = gnc_book_get_root_account (book);
1357 if (!write_v2_header (out)
1358 || !write_counts (out,
"commodity", ncom,
"account", nacc, NULL))
1361 gd = gnc_sixtp_gdv2_new (book, TRUE, file_rw_feedback,
1362 qof_be->get_percentage());
1363 gd->counter.commodities_total = ncom;
1364 gd->counter.accounts_total = nacc;
1366 if (!write_commodities (out, book, gd)
1367 || !write_accounts (out, book, gd)
1368 || fprintf (out,
"</" GNC_V2_STRING
">\n\n") < 0)
1375 static inline gzFile
1376 do_gzopen (
const char* filename,
const char* perms)
1380 char* new_perms =
nullptr;
1381 char* conv_name = g_win32_locale_filename_from_utf8 (filename);
1385 g_warning (
"Could not convert '%s' to system codepage",
1390 if (strchr (perms,
'b'))
1391 new_perms = g_strdup (perms);
1393 new_perms = g_strdup_printf (
"%cb%s", *perms, perms + 1);
1395 file = gzopen (conv_name, new_perms);
1400 return gzopen (filename, perms);
1404 constexpr uint32_t BUFLEN{4096};
1409 bool success =
true;
1410 gchar buffer[BUFLEN];
1414 auto bytes = read (params->fd, buffer, BUFLEN);
1417 if (gzwrite (file, buffer, bytes) <= 0)
1420 auto error = gzerror (file, &errnum);
1421 g_warning (
"Could not write the compressed file '%s'. The error is: '%s' (%d)",
1422 params->filename, error, errnum);
1426 else if (bytes == 0)
1432 g_warning (
"Could not read from pipe. The error is '%s' (errno %d)",
1433 g_strerror (errno) ? g_strerror (errno) :
"", errno);
1441 #define WRITE_FN _write 1443 #define WRITE_FN write 1449 bool success =
true;
1450 gchar buffer[BUFLEN];
1454 auto gzval = gzread (file, buffer, BUFLEN);
1457 if (WRITE_FN (params->fd, buffer, gzval) < 0)
1459 g_warning (
"Could not write to pipe. The error is '%s' (%d)",
1460 g_strerror (errno) ? g_strerror (errno) :
"", errno);
1464 else if (gzval == 0)
1471 const gchar* error = gzerror (file, &errnum);
1472 g_warning (
"Could not read from compressed file '%s'. The error is: '%s' (%d)",
1473 params->filename, error, errnum);
1486 bool success =
true;
1488 auto file = do_gzopen (params->filename, params->perms);
1492 g_warning (
"Child threads gzopen failed");
1494 goto cleanup_gz_thread_func;
1499 success = gz_thread_write (file, params);
1503 success = gz_thread_read (file, params);
1506 if ((gzval = gzclose (file)) != Z_OK)
1508 g_warning (
"Could not close the compressed file '%s' (errnum %d)",
1509 params->filename, gzval);
1513 cleanup_gz_thread_func:
1515 g_free (params->filename);
1516 g_free (params->perms);
1519 return GINT_TO_POINTER (success);
1522 static std::pair<FILE*, GThread*>
1523 try_gz_open (
const char* filename,
const char* perms, gboolean compress,
1526 if (strstr (filename,
".gz.") != NULL)
1530 return std::pair<FILE*, GThread*>(g_fopen (filename, perms),
1537 if (_pipe (filedes, 4096, _O_BINARY) < 0)
1546 if (pipe (filedes) < 0 ||
1547 fcntl(filedes[0], F_SETFD, FD_CLOEXEC) == -1 ||
1548 fcntl(filedes[1], F_SETFD, FD_CLOEXEC) == -1)
1551 g_warning (
"Pipe setup failed with errno %d. Opening uncompressed file.", errno);
1558 return std::pair<FILE*, GThread*>(g_fopen (filename, perms),
1563 params->fd = filedes[write ? 0 : 1];
1564 params->filename = g_strdup (filename);
1565 params->perms = g_strdup (perms);
1566 params->write = write;
1568 auto thread = g_thread_new (
"xml_thread", (GThreadFunc) gz_thread_func,
1571 FILE* file =
nullptr;
1575 g_warning (
"Could not create thread for (de)compression.");
1576 g_free (params->filename);
1577 g_free (params->perms);
1581 file = g_fopen (filename, perms);
1586 file = fdopen (filedes[1],
"w");
1588 file = fdopen (filedes[0],
"r");
1591 return std::pair<FILE*, GThread*>(file, thread);
1596 gnc_book_write_to_xml_file_v2 (QofBook* book,
const char* filename,
1599 bool success =
true;
1601 auto [file, thread] = try_gz_open (filename,
"w", compress, TRUE);
1606 if (!gnc_book_write_to_xml_filehandle_v2 (book, file))
1616 if (g_thread_join (thread) ==
nullptr)
1629 gnc_book_write_accounts_to_xml_file_v2 (
QofBackend* qof_be, QofBook* book,
1630 const char* filename)
1633 gboolean success = TRUE;
1635 out = g_fopen (filename,
"w");
1639 || !gnc_book_write_accounts_to_xml_filehandle_v2 (qof_be, book, out))
1643 if (out && fclose (out))
1658 is_gzipped_file (
const gchar* name)
1660 unsigned char buf[2];
1661 int fd = g_open (name, O_RDONLY, 0);
1668 if (read (fd, buf, 2) != 2)
1676 if (buf[0] == 037 && buf[1] == 0213)
1685 gnc_is_xml_data_file_v2 (
const gchar* name, gboolean* with_encoding)
1687 if (is_gzipped_file (name))
1690 char first_chunk[256];
1693 file = do_gzopen (name,
"r");
1696 return GNC_BOOK_NOT_OURS;
1698 num_read = gzread (file, first_chunk,
sizeof (first_chunk) - 1);
1702 return GNC_BOOK_NOT_OURS;
1704 return gnc_is_our_first_xml_chunk (first_chunk, with_encoding);
1707 return (gnc_is_our_xml_file (name, with_encoding));
1712 replace_character_references (gchar*
string)
1714 gchar* cursor, *semicolon, *tail;
1717 for (cursor = strstr (
string,
"&#");
1719 cursor = strstr (cursor,
"&#"))
1721 semicolon = strchr (cursor,
';');
1722 if (semicolon && *semicolon)
1727 if (* (cursor + 2) ==
'x')
1729 number = strtol (cursor + 3, &tail, 16);
1733 number = strtol (cursor + 2, &tail, 10);
1735 if (errno || tail != semicolon || number < 0 || number > 255)
1737 PWARN (
"Illegal character reference");
1742 *cursor = (gchar) number;
1744 if (* (semicolon + 1))
1747 tail = g_strdup (semicolon + 1);
1748 strcpy (cursor, tail);
1760 PWARN (
"Unclosed character reference");
1771 g_free (conv->utf8_string);
1777 conv_list_free (GList* conv_list)
1779 g_list_foreach (conv_list, (GFunc) conv_free, NULL);
1780 g_list_free (conv_list);
1791 GHashTable** unique, GHashTable** ambiguous,
1794 GList* iconv_list = NULL, *conv_list = NULL, *iter;
1797 GHashTable* processed = NULL;
1798 gint n_impossible = 0;
1799 GError* error = NULL;
1800 gboolean clean_return = FALSE;
1802 auto [file, thread] = try_gz_open (filename,
"r",
1803 is_gzipped_file (filename), FALSE);
1806 PWARN (
"Unable to open file %s", filename);
1807 goto cleanup_find_ambs;
1812 ascii->encoding = g_quark_from_string (
"ASCII");
1813 ascii->iconv = g_iconv_open (
"UTF-8",
"ASCII");
1814 if (ascii->iconv == (GIConv) - 1)
1816 PWARN (
"Unable to open ASCII ICONV conversion descriptor");
1817 goto cleanup_find_ambs;
1821 for (iter = encodings; iter; iter = iter->next)
1824 iconv_item->encoding = GPOINTER_TO_UINT (iter->data);
1825 if (iconv_item->encoding == ascii->encoding)
1830 enc = g_quark_to_string (iconv_item->encoding);
1831 iconv_item->iconv = g_iconv_open (
"UTF-8", enc);
1832 if (iconv_item->iconv == (GIConv) - 1)
1834 PWARN (
"Unable to open IConv conversion descriptor for '%s'", enc);
1835 g_free (iconv_item);
1836 goto cleanup_find_ambs;
1840 iconv_list = g_list_prepend (iconv_list, iconv_item);
1846 *unique = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
1847 (GDestroyNotify) conv_free);
1849 *ambiguous = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
1850 (GDestroyNotify) conv_list_free);
1853 processed = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1858 gchar line[256], *word, *utf8;
1859 gchar** word_array, **word_cursor;
1862 if (!fgets (line,
sizeof (line) - 1, file))
1870 goto cleanup_find_ambs;
1875 replace_character_references (line);
1876 word_array = g_strsplit_set (line,
"> <", 0);
1879 for (word_cursor = word_array; *word_cursor; word_cursor++)
1881 word = *word_cursor;
1885 utf8 = g_convert_with_iconv (word, -1, ascii->iconv,
1886 NULL, NULL, &error);
1893 g_error_free (error);
1896 if (g_hash_table_lookup_extended (processed, word, NULL, NULL))
1904 for (iter = iconv_list; iter; iter = iter->next)
1906 iconv_item =
static_cast<decltype (iconv_item)
> (iter->data);
1907 utf8 = g_convert_with_iconv (word, -1, iconv_item->iconv,
1908 NULL, NULL, &error);
1912 conv->encoding = iconv_item->encoding;
1913 conv->utf8_string = utf8;
1914 conv_list = g_list_prepend (conv_list, conv);
1918 g_error_free (error);
1927 *impossible = g_list_append (*impossible, g_strdup (word));
1932 else if (conv_list->next)
1936 g_hash_table_insert (*ambiguous, g_strdup (word), conv_list);
1940 conv_list_free (conv_list);
1949 g_hash_table_insert (*unique, g_strdup (word), conv);
1955 g_list_free (conv_list);
1958 g_hash_table_insert (processed, g_strdup (word), NULL);
1960 g_strfreev (word_array);
1963 clean_return = TRUE;
1969 for (iter = iconv_list; iter; iter = iter->next)
1974 g_free (iter->data);
1977 g_list_free (iconv_list);
1980 g_hash_table_destroy (processed);
1987 g_thread_join (thread);
1990 return (clean_return) ? n_impossible : -1;
1995 const char* filename;
2000 parse_with_subst_push_handler (xmlParserCtxtPtr xml_context,
2003 GIConv ascii = (GIConv) - 1;
2004 GString* output = NULL;
2005 GError* error = NULL;
2007 auto filename = push_data->filename;
2008 auto [file, thread] = try_gz_open (filename,
"r",
2009 is_gzipped_file (filename), FALSE);
2012 PWARN (
"Unable to open file %s", filename);
2013 goto cleanup_push_handler;
2016 ascii = g_iconv_open (
"UTF-8",
"ASCII");
2017 if (ascii == (GIConv) - 1)
2019 PWARN (
"Unable to open ASCII ICONV conversion descriptor");
2020 goto cleanup_push_handler;
2026 gchar line[256], *word, *repl, *utf8;
2028 gchar* start, *cursor;
2030 if (!fgets (line,
sizeof (line) - 1, file))
2038 goto cleanup_push_handler;
2042 replace_character_references (line);
2043 output = g_string_new (line);
2046 cursor = output->str;
2051 while (*cursor ==
'>' || *cursor ==
' ' || *cursor ==
'<' ||
2065 while (*cursor && *cursor !=
'>' && *cursor !=
' ' && *cursor !=
'<' &&
2072 utf8 = g_convert_with_iconv (start, len, ascii, NULL, NULL, &error);
2082 g_error_free (error);
2085 word = g_strndup (start, len);
2086 repl =
static_cast<decltype (repl)
> (g_hash_table_lookup (push_data->subst,
2092 output = g_string_insert (g_string_erase (output, pos, len),
2094 pos += strlen (repl);
2095 cursor = output->str + pos;
2100 goto cleanup_push_handler;
2105 if (xmlParseChunk (xml_context, output->str, output->len, 0) != 0)
2107 goto cleanup_push_handler;
2112 xmlParseChunk (xml_context,
"", 0, 1);
2114 cleanup_push_handler:
2117 g_string_free (output, TRUE);
2118 if (ascii != (GIConv) - 1)
2119 g_iconv_close (ascii);
2124 g_thread_join (thread);
2135 push_data->filename = xml_be->get_filename();
2136 push_data->subst = subst;
2138 success = qof_session_load_from_xml_file_v2_full (
2139 xml_be, book, (sixtp_push_handler) parse_with_subst_push_handler,
2140 push_data, GNC_BOOK_XML2_FILE);
2144 qof_instance_set_dirty (QOF_INSTANCE (book));
gnc_commodity * gnc_commodity_table_insert(gnc_commodity_table *table, gnc_commodity *comm)
Add a new commodity to the commodity table.
Account * gnc_account_get_parent(const Account *acc)
This routine returns a pointer to the parent of the specified account.
int xaccAccountTreeForEachTransaction(Account *acc, TransactionCallback proc, void *data)
Traverse all of the transactions in the given account group.
void xaccAccountScrubKvp(Account *account)
Removes empty "notes", "placeholder", and "hbci" KVP slots from Accounts.
gnc_commodity_table * gnc_commodity_table_get_table(QofBook *book)
Returns the commodity table associated with a book.
void xaccTransScrubCurrency(Transaction *trans)
The xaccTransScrubCurrency method fixes transactions without a common_currency by looking for the mos...
void gnc_account_append_child(Account *new_parent, Account *child)
This function will remove from the child account any pre-existing parent relationship, and will then add the account as a child of the new parent.
gint gnc_account_n_descendants(const Account *account)
Return the number of descendants of the specified account.
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
Retrieve the mnemonic for the specified commodity.
int xaccAccountGetCommoditySCUi(const Account *acc)
Return the 'internal' SCU setting.
void xaccAccountTreeScrubCommodities(Account *acc)
The xaccAccountTreeScrubCommodities will scrub the currency/commodity of all accounts & transactions ...
gnc_commodity * DxaccAccountGetCurrency(const Account *acc)
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account's account type.
void xaccAccountScrubCommodity(Account *account)
The xaccAccountScrubCommodity method fixed accounts without a commodity by using the old account curr...
void qof_backend_set_error(QofBackend *qof_be, QofBackendError err)
Set the error on the specified QofBackend.
void xaccLogDisable(void)
document me
#define DEBUG(format, args...)
Print a debugging message.
Account * gnc_book_get_template_root(const QofBook *book)
Returns the template group from the book.
const char * gnc_commodity_get_namespace(const gnc_commodity *cm)
Retrieve the namespace for the specified commodity.
couldn't write to the file
void xaccTransScrubPostedDate(Transaction *trans)
Changes Transaction date_posted timestamps from 00:00 local to 11:00 UTC.
#define PERR(format, args...)
Log a serious error.
GNCPriceDB * gnc_pricedb_get_db(QofBook *book)
Return the pricedb associated with the book.
void xaccTransSetCurrency(Transaction *trans, gnc_commodity *curr)
Set a new currency on a transaction.
#define PWARN(format, args...)
Log a warning.
convert single-entry accounts to clean double-entry
void qof_book_mark_session_saved(QofBook *book)
The qof_book_mark_saved() routine marks the book as having been saved (to a file, to a database)...
api for GnuCash version 2 XML-based file format
GList * gnc_commodity_table_get_namespaces(const gnc_commodity_table *table)
Return a list of all namespaces in the commodity table.
gboolean gnc_xml2_parse_with_subst(GncXmlBackend *xml_be, QofBook *book, GHashTable *subst)
Parse a file in push mode, but replace byte sequences in the file given a hash table of substitutions...
guint gnc_pricedb_get_num_prices(GNCPriceDB *db)
Return the number of prices in the database.
Anchor Scheduled Transaction info in a book.
void xaccTransCommitEdit(Transaction *trans)
The xaccTransCommitEdit() method indicates that the changes to the transaction and its splits are com...
void xaccTransBeginEdit(Transaction *trans)
The xaccTransBeginEdit() method must be called before any changes are made to a transaction or any of...
All type declarations for the whole Gnucash engine.
void xaccAccountSetCommoditySCU(Account *acc, int scu)
Set the SCU for the account.
CommodityList * gnc_commodity_table_get_commodities(const gnc_commodity_table *table, const char *name_space)
Return a list of all commodities in the commodity table that are in the given namespace.
API for the transaction logger.
guint gnc_book_count_transactions(QofBook *book)
gint gnc_xml2_find_ambiguous(const gchar *filename, GList *encodings, GHashTable **unique, GHashTable **ambiguous, GList **impossible)
Read a file as plain byte stream to find words that are not completely ASCII.
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account's commodity.
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Returns the valuation commodity of this transaction.
const char * gnc_commodity_get_unique_name(const gnc_commodity *cm)
Retrieve the 'unique' name for the specified commodity.
void(* QofBePercentageFunc)(const char *message, double percent)
DOCUMENT ME!
bool check_error()
Report if there is an error.
QofCollection * qof_book_get_collection(const QofBook *book, QofIdType entity_type)
Return The table of entities of the given type.
guint gnc_commodity_table_get_size(const gnc_commodity_table *tbl)
Returns the number of commodities in the commodity table.
guint qof_collection_count(const QofCollection *col)
return the number of entities in the collection.
void DxaccAccountSetCurrency(Account *acc, gnc_commodity *currency)
QofBackend * qof_book_get_backend(const QofBook *book)
Retrieve the backend used by this book.
void xaccAccountTreeScrubQuoteSources(Account *root, gnc_commodity_table *table)
This routine will migrate the information about price quote sources from the account data structures ...
API for Transactions and Splits (journal entries)
void xaccAccountCommitEdit(Account *acc)
ThexaccAccountCommitEdit() subroutine is the second phase of a two-phase-commit wrapper for account u...
The hidden root account of an account tree.
void gnc_commodity_destroy(gnc_commodity *cm)
Destroy a commodity.
void xaccLogEnable(void)
document me
void xaccAccountSetCommodity(Account *acc, gnc_commodity *com)
Set the account's commodity.