22 #include <glib/gstdio.h> 28 #ifdef __STRICT_ANSI__ 29 #undef __STRICT_ANSI__ 30 #define __STRICT_ANSI_UNSET__ 1 50 #include "gnc-pricedb-p.h" 53 #include "SX-book-p.h" 55 #include "TransactionP.hpp" 58 #ifdef __STRICT_ANSI_UNSET__ 59 #undef __STRICT_ANSI_UNSET__ 60 #define __STRICT_ANSI__ 1 64 # define g_fopen fopen 68 #include "gnc-xml-backend.hpp" 69 #include "sixtp-parsers.h" 70 #include "sixtp-utils.h" 73 #include "sixtp-dom-parsers.h" 75 #include "io-gncxml-gen.h" 77 static QofLogModule log_module = GNC_MOD_IO;
99 static std::vector<GncXmlDataType_t> backend_registry;
101 gnc_xml_register_backend(GncXmlDataType_t& xmlbe)
103 backend_registry.push_back(xmlbe);
106 #define GNC_V2_STRING "gnc-v2" 108 const gchar* gnc_v2_xml_version_string = GNC_V2_STRING;
110 gnc_v2_book_version_string;
113 static std::pair<FILE*, GThread*> try_gz_open (
const char* filename,
117 static bool is_gzipped_file (
const gchar* name);
120 clear_up_account_commodity (
121 gnc_commodity_table* tbl,
Account* act,
122 gnc_commodity * (*getter) (
const Account* account),
123 void (*setter) (
Account* account, gnc_commodity* comm),
124 int (*scu_getter) (
const Account* account),
125 void (*scu_setter) (
Account* account,
int scu))
128 gnc_commodity* com = getter (act);
132 old_scu = scu_getter (act);
150 PWARN (
"unable to find global commodity for %s adding new",
157 if (old_scu != 0 && scu_setter)
158 scu_setter (act, old_scu);
164 clear_up_transaction_commodity (
165 gnc_commodity_table* tbl, Transaction* trans,
166 gnc_commodity * (*getter) (
const Transaction* trans),
167 void (*setter) (Transaction* trans, gnc_commodity* comm))
170 gnc_commodity* com = getter (trans);
186 PWARN (
"unable to find global commodity for %s adding new",
193 setter (trans, gcom);
202 gnc_commodity_table*
table;
208 clear_up_account_commodity (
table, act,
213 clear_up_account_commodity (
table, act,
228 gnc_book_set_root_account (data->book, act);
235 root = gnc_book_get_root_account (data->book);
240 data->counter.accounts_loaded++;
241 sixtp_run_callback (data,
"account");
247 add_book_local (
sixtp_gdv2* data, QofBook* book)
249 data->counter.books_loaded++;
250 sixtp_run_callback (data,
"book");
256 add_commodity_local (
sixtp_gdv2* data, gnc_commodity* com)
258 gnc_commodity_table*
table;
264 data->counter.commodities_loaded++;
265 sixtp_run_callback (data,
"commodities");
271 add_transaction_local (
sixtp_gdv2* data, Transaction* trn)
273 gnc_commodity_table*
table;
278 clear_up_transaction_commodity (
table, trn,
286 data->counter.transactions_loaded++;
287 sixtp_run_callback (data,
"transaction");
292 add_schedXaction_local (
sixtp_gdv2* data, SchedXaction* sx)
295 sxes = gnc_book_get_schedxactions (data->book);
296 gnc_sxes_add_sx (sxes, sx);
297 data->counter.schedXactions_loaded++;
298 sixtp_run_callback (data,
"schedXactions");
303 add_template_transaction_local (
sixtp_gdv2* data,
304 gnc_template_xaction_data* txd)
315 for (n = txd->accts; n; n = n->next)
322 gnc_book_set_template_root (book, (
Account*)n->data);
336 for (n = txd->transactions; n; n = n->next)
339 add_transaction_local (data, (Transaction*)n->data);
346 add_pricedb_local (
sixtp_gdv2* data, GNCPriceDB* db)
353 counter (
const GncXmlDataType_t& data,
file_backend* be_data)
355 g_return_if_fail (data.version == GNC_FILE_BACKEND_VERS);
357 if (be_data->ok == TRUE)
360 if (!g_strcmp0 (be_data->tag, data.type_name))
367 gnc_counter_end_handler (gpointer data_for_children,
368 GSList* data_from_children, GSList* sibling_data,
369 gpointer parent_data, gpointer global_data,
370 gpointer* result,
const gchar* tag)
375 xmlNodePtr tree = (xmlNodePtr)data_for_children;
376 gxpf_data* gdata = (gxpf_data*)global_data;
388 g_return_val_if_fail (tree, FALSE);
394 type = (
char*)xmlGetProp (tree, BAD_CAST
"cd:type");
395 strval = dom_tree_to_text (tree);
396 if (!string_to_gint64 (strval, &val))
398 PERR (
"string_to_gint64 failed with input: %s",
399 strval ? strval :
"(null)");
402 else if (g_strcmp0 (type,
"transaction") == 0)
404 sixdata->counter.transactions_total = val;
406 else if (g_strcmp0 (type,
"account") == 0)
408 sixdata->counter.accounts_total = val;
410 else if (g_strcmp0 (type,
"book") == 0)
412 sixdata->counter.books_total = val;
414 else if (g_strcmp0 (type,
"commodity") == 0)
416 sixdata->counter.commodities_total = val;
418 else if (g_strcmp0 (type,
"schedxaction") == 0)
420 sixdata->counter.schedXactions_total = val;
422 else if (g_strcmp0 (type,
"budget") == 0)
424 sixdata->counter.budgets_total = val;
426 else if (g_strcmp0 (type,
"price") == 0)
428 sixdata->counter.prices_total = val;
436 for(
auto data : backend_registry)
437 counter(data, &be_data);
439 if (be_data.ok == FALSE)
441 PERR (
"Unknown type: %s", type ? type :
"(null)");
459 gnc_counter_sixtp_parser_create (
void)
461 return sixtp_dom_parser_new (gnc_counter_end_handler, NULL, NULL);
467 DEBUG (
"Transactions: Total: %d, Loaded: %d",
468 data->transactions_total, data->transactions_loaded);
469 DEBUG (
"Accounts: Total: %d, Loaded: %d",
470 data->accounts_total, data->accounts_loaded);
471 DEBUG (
"Books: Total: %d, Loaded: %d",
472 data->books_total, data->books_loaded);
473 DEBUG (
"Commodities: Total: %d, Loaded: %d",
474 data->commodities_total, data->commodities_loaded);
475 DEBUG (
"Scheduled Transactions: Total: %d, Loaded: %d",
476 data->schedXactions_total, data->schedXactions_loaded);
477 DEBUG (
"Budgets: Total: %d, Loaded: %d",
478 data->budgets_total, data->budgets_loaded);
482 file_rw_feedback (
sixtp_gdv2* gd,
const char* type)
485 int loaded, total, percentage;
487 g_assert (gd != NULL);
488 if (!gd->gui_display_fn)
491 counter = &gd->counter;
492 loaded = counter->transactions_loaded + counter->accounts_loaded +
493 counter->books_loaded + counter->commodities_loaded +
494 counter->schedXactions_loaded + counter->budgets_loaded +
495 counter->prices_loaded;
496 total = counter->transactions_total + counter->accounts_total +
497 counter->books_total + counter->commodities_total +
498 counter->schedXactions_total + counter->budgets_total +
499 counter->prices_total;
503 percentage = (loaded * 100) / total;
504 if (percentage > 100)
521 gd->gui_display_fn (NULL, percentage);
524 static const char* BOOK_TAG =
"gnc:book";
525 static const char* BOOK_ID_TAG =
"book:id";
526 static const char* BOOK_SLOTS_TAG =
"book:slots";
527 static const char* ACCOUNT_TAG =
"gnc:account";
528 static const char* PRICEDB_TAG =
"gnc:pricedb";
529 static const char* COMMODITY_TAG =
"gnc:commodity";
530 static const char* COUNT_DATA_TAG =
"gnc:count-data";
531 static const char* TRANSACTION_TAG =
"gnc:transaction";
532 static const char* SCHEDXACTION_TAG =
"gnc:schedxaction";
533 static const char* TEMPLATE_TRANSACTION_TAG =
"gnc:template-transactions";
534 static const char* BUDGET_TAG =
"gnc:budget";
537 add_item (
const GncXmlDataType_t& data,
struct file_backend* be_data)
539 g_return_if_fail (data.version == GNC_FILE_BACKEND_VERS);
544 if (!g_strcmp0 (be_data->tag, data.type_name))
547 (data.add_item)(be_data->gd, be_data->data);
554 book_callback (
const char* tag, gpointer globaldata, gpointer data)
558 if (g_strcmp0 (tag, ACCOUNT_TAG) == 0)
560 add_account_local (gd, (
Account*)data);
562 else if (g_strcmp0 (tag, PRICEDB_TAG) == 0)
564 add_pricedb_local (gd, (GNCPriceDB*)data);
566 else if (g_strcmp0 (tag, COMMODITY_TAG) == 0)
568 add_commodity_local (gd, (gnc_commodity*)data);
570 else if (g_strcmp0 (tag, TRANSACTION_TAG) == 0)
572 add_transaction_local (gd, (Transaction*)data);
574 else if (g_strcmp0 (tag, SCHEDXACTION_TAG) == 0)
576 add_schedXaction_local (gd, (SchedXaction*)data);
578 else if (g_strcmp0 (tag, TEMPLATE_TRANSACTION_TAG) == 0)
580 add_template_transaction_local (gd, (gnc_template_xaction_data*)data);
582 else if (g_strcmp0 (tag, BUDGET_TAG) == 0)
595 for (
auto data : backend_registry)
596 add_item(data, &be_data);
598 if (be_data.ok == FALSE)
600 PWARN (
"unexpected tag %s", tag);
607 generic_callback (
const char* tag, gpointer globaldata, gpointer data)
611 if (g_strcmp0 (tag, BOOK_TAG) == 0)
613 add_book_local (gd, (QofBook*)data);
614 book_callback (tag, globaldata, data);
619 book_callback (tag, globaldata, data);
625 add_parser(
const GncXmlDataType_t& data,
struct file_backend* be_data)
627 g_return_if_fail (data.version == GNC_FILE_BACKEND_VERS);
629 if (be_data->ok == FALSE)
632 if (data.create_parser)
633 if (!sixtp_add_some_sub_parsers(
634 be_data->parser, TRUE,
635 data.type_name, (data.create_parser)(),
641 scrub (
const GncXmlDataType_t& data,
struct file_backend* be_data)
643 g_return_if_fail (data.version == GNC_FILE_BACKEND_VERS);
646 (data.scrub)(be_data->book);
653 countCallbackFn countcallback,
658 if (gd == NULL)
return NULL;
661 gd->counter.accounts_loaded = 0;
662 gd->counter.accounts_total = 0;
663 gd->counter.books_loaded = 0;
664 gd->counter.books_total = 0;
665 gd->counter.commodities_loaded = 0;
666 gd->counter.commodities_total = 0;
667 gd->counter.transactions_loaded = 0;
668 gd->counter.transactions_total = 0;
669 gd->counter.prices_loaded = 0;
670 gd->counter.prices_total = 0;
671 gd->counter.schedXactions_loaded = 0;
672 gd->counter.schedXactions_total = 0;
673 gd->counter.budgets_loaded = 0;
674 gd->counter.budgets_total = 0;
675 gd->exporting = exporting;
676 gd->countCallback = countcallback;
677 gd->gui_display_fn = gui_display_fn;
682 qof_session_load_from_xml_file_v2_full (
684 sixtp_push_handler push_handler, gpointer push_user_data,
685 QofBookFileType type)
697 gd = gnc_sixtp_gdv2_new (book, FALSE, file_rw_feedback,
698 xml_be->get_percentage());
700 top_parser = sixtp_new ();
701 main_parser = sixtp_new ();
702 book_parser = sixtp_new ();
704 if (type == GNC_BOOK_XML2_FILE)
705 v2type = g_strdup (GNC_V2_STRING);
707 if (!sixtp_add_some_sub_parsers (
718 if (!sixtp_add_some_sub_parsers (
720 COUNT_DATA_TAG, gnc_counter_sixtp_parser_create (),
721 BOOK_TAG, book_parser,
726 PRICEDB_TAG, gnc_pricedb_sixtp_parser_create (),
727 COMMODITY_TAG, gnc_commodity_sixtp_parser_create (),
728 ACCOUNT_TAG, gnc_account_sixtp_parser_create (),
729 TRANSACTION_TAG, gnc_transaction_sixtp_parser_create (),
730 SCHEDXACTION_TAG, gnc_schedXaction_sixtp_parser_create (),
731 TEMPLATE_TRANSACTION_TAG, gnc_template_transaction_sixtp_parser_create (),
737 if (!sixtp_add_some_sub_parsers (
739 BOOK_ID_TAG, gnc_book_id_sixtp_parser_create (),
740 BOOK_SLOTS_TAG, gnc_book_slots_sixtp_parser_create (),
741 COUNT_DATA_TAG, gnc_counter_sixtp_parser_create (),
742 PRICEDB_TAG, gnc_pricedb_sixtp_parser_create (),
743 COMMODITY_TAG, gnc_commodity_sixtp_parser_create (),
744 ACCOUNT_TAG, gnc_account_sixtp_parser_create (),
745 BUDGET_TAG, gnc_budget_sixtp_parser_create (),
746 TRANSACTION_TAG, gnc_transaction_sixtp_parser_create (),
747 SCHEDXACTION_TAG, gnc_schedXaction_sixtp_parser_create (),
748 TEMPLATE_TRANSACTION_TAG, gnc_template_transaction_sixtp_parser_create (),
755 be_data.parser = book_parser;
756 for (
auto data : backend_registry)
757 add_parser(data, &be_data);
758 if (be_data.ok == FALSE)
763 xaccDisableDataScrubbing ();
767 gpointer parse_result = NULL;
770 gpdata.cb = generic_callback;
771 gpdata.parsedata = gd;
772 gpdata.bookdata = book;
774 retval = sixtp_parse_push (top_parser, push_handler, push_user_data,
775 NULL, &gpdata, &parse_result);
785 auto filename = xml_be->get_filename();
786 auto [file, thread] = try_gz_open (filename,
"r",
787 is_gzipped_file (filename), FALSE);
790 PWARN (
"Unable to open file %s", filename);
795 retval = gnc_xml_parse_fd (top_parser, file,
796 generic_callback, gd, book);
799 g_thread_join (thread);
805 sixtp_destroy (top_parser);
807 xaccEnableDataScrubbing ();
810 debug_print_counter_data (&gd->counter);
813 sixtp_destroy (top_parser);
816 xaccEnableDataScrubbing ();
822 memset (&be_data, 0,
sizeof (be_data));
824 for (
auto data : backend_registry)
825 scrub(data, &be_data);
828 root = gnc_book_get_root_account (book);
835 xaccAccountTreeScrubSplits (root);
841 gnc_account_foreach_descendant (root,
844 gnc_account_foreach_descendant (template_root,
848 if (qof_instance_get_editlevel(root) != 0)
850 if (qof_instance_get_editlevel(template_root) != 0)
864 qof_session_load_from_xml_file_v2 (
GncXmlBackend* xml_be, QofBook* book,
865 QofBookFileType type)
867 return qof_session_load_from_xml_file_v2_full (xml_be, book, NULL, NULL, type);
873 write_counts (FILE* out, ...)
877 gboolean success = TRUE;
880 type = va_arg (ap,
char*);
882 while (success && type)
884 int amount = va_arg (ap,
int);
888 #if GNUCASH_REALLY_BUILD_AN_XML_TREE_ON_OUTPUT 889 char *type_dup = g_strdup (type);
893 val = g_strdup_printf (
"%d", amount);
895 node = xmlNewNode (NULL, BAD_CAST COUNT_DATA_TAG);
901 xmlSetProp (node, BAD_CAST
"cd:type", checked_char_cast (type_dup));
902 xmlNodeAddContent (node, checked_char_cast (val));
906 xmlElemDump (out, NULL, node);
909 if (ferror (out) || fprintf (out,
"\n") < 0)
915 if (fprintf (out,
"<%s %s=\"%s\">%d</%s>\n",
916 COUNT_DATA_TAG,
"cd:type", type, amount, COUNT_DATA_TAG) < 0)
925 type = va_arg (ap,
char*);
933 compare_namespaces (gconstpointer a, gconstpointer b)
935 const gchar* sa = (
const gchar*) a;
936 const gchar* sb = (
const gchar*) b;
937 return (g_strcmp0 (sa, sb));
941 compare_commodity_ids (gconstpointer a, gconstpointer b)
943 const gnc_commodity* ca = (
const gnc_commodity*) a;
944 const gnc_commodity* cb = (
const gnc_commodity*) b;
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,
953 static gboolean write_schedXactions (FILE* out, QofBook* book,
sixtp_gdv2* gd);
954 static void write_budget (
QofInstance* ent, gpointer data);
957 write_counts(
const GncXmlDataType_t& data,
struct file_backend* be_data)
959 g_return_if_fail (data.version == GNC_FILE_BACKEND_VERS);
962 write_counts (be_data->out, data.type_name,
963 (data.get_count) (be_data->book),
968 write_data(
const GncXmlDataType_t& data,
struct file_backend* be_data)
970 g_return_if_fail (data.version == GNC_FILE_BACKEND_VERS);
972 if (data.write && !ferror(be_data->out))
973 (data.write)(be_data->out, be_data->book);
977 write_book (FILE* out, QofBook* book,
sixtp_gdv2* gd)
984 if (fprintf (out,
"<%s version=\"%s\">\n", BOOK_TAG,
985 gnc_v2_book_version_string) < 0)
987 if (!write_book_parts (out, book))
993 if (!write_counts (out,
1002 g_list_length (gnc_book_get_schedxactions (book)->sx_list),
1009 for (
auto data : backend_registry)
1010 write_counts(data, &be_data);
1013 || !write_commodities (out, book, gd)
1014 || !write_pricedb (out, book, gd)
1015 || !write_accounts (out, book, gd)
1016 || !write_transactions (out, book, gd)
1017 || !write_template_transaction_data (out, book, gd)
1018 || !write_schedXactions (out, book, gd))
1023 write_budget, &be_data);
1027 for (
auto data : backend_registry)
1028 write_data(data, &be_data);
1032 if (fprintf (out,
"</%s>\n", BOOK_TAG) < 0)
1039 write_commodities (FILE* out, QofBook* book,
sixtp_gdv2* gd)
1041 gnc_commodity_table* tbl;
1044 gboolean success = TRUE;
1051 namespaces = g_list_sort (namespaces, compare_namespaces);
1054 for (lp = namespaces; success && lp; lp = lp->next)
1060 static_cast<const char*> (lp->data));
1061 comms = g_list_sort (comms, compare_commodity_ids);
1063 for (lp2 = comms; lp2; lp2 = lp2->next)
1065 comnode = gnc_commodity_dom_tree_create (static_cast<const gnc_commodity*>
1067 if (comnode == NULL)
1070 xmlElemDump (out, NULL, comnode);
1071 if (ferror (out) || fprintf (out,
"\n") < 0)
1077 xmlFreeNode (comnode);
1078 gd->counter.commodities_loaded++;
1079 sixtp_run_callback (gd,
"commodities");
1082 g_list_free (comms);
1085 if (namespaces) g_list_free (namespaces);
1091 write_pricedb (FILE* out, QofBook* book,
sixtp_gdv2* gd)
1095 xmlOutputBufferPtr outbuf;
1108 if (fprintf (out,
"<%s version=\"%s\">\n", parent->name,
1109 xmlGetProp (parent, BAD_CAST
"version")) < 0)
1114 outbuf = xmlOutputBufferCreateFile (out, NULL);
1117 xmlFreeNode (parent);
1121 for (node = parent->children; node; node = node->next)
1124 xmlOutputBufferWrite (outbuf, 2,
" ");
1125 xmlNodeDumpOutput (outbuf, NULL, node, 1, 1, NULL);
1127 xmlOutputBufferWrite (outbuf, 1,
"\n");
1130 gd->counter.prices_loaded += 1;
1131 sixtp_run_callback (gd,
"prices");
1134 xmlOutputBufferClose (outbuf);
1136 if (ferror (out) || fprintf (out,
"</%s>\n", parent->name) < 0)
1138 xmlFreeNode (parent);
1142 xmlFreeNode (parent);
1147 xml_add_trn_data (Transaction* t, gpointer data)
1149 struct file_backend* be_data =
static_cast<decltype (be_data)
> (data);
1152 node = gnc_transaction_dom_tree_create (t);
1154 xmlElemDump (be_data->out, NULL, node);
1157 if (ferror (be_data->out) || fprintf (be_data->out,
"\n") < 0)
1160 be_data->gd->counter.transactions_loaded++;
1161 sixtp_run_callback (be_data->gd,
"transaction");
1166 write_transactions (FILE* out, QofBook* book,
sixtp_gdv2* gd)
1175 (gpointer) &be_data);
1179 write_template_transaction_data (FILE* out, QofBook* book,
sixtp_gdv2* gd)
1190 if (fprintf (out,
"<%s>\n", TEMPLATE_TRANSACTION_TAG) < 0
1191 || !write_account_tree (out, ra, gd)
1193 || fprintf (out,
"</%s>\n", TEMPLATE_TRANSACTION_TAG) < 0)
1202 write_schedXactions (FILE* out, QofBook* book,
sixtp_gdv2* gd)
1204 GList* schedXactions;
1205 SchedXaction* tmpSX;
1208 schedXactions = gnc_book_get_schedxactions (book)->sx_list;
1210 if (schedXactions == NULL)
1215 tmpSX =
static_cast<decltype (tmpSX)
> (schedXactions->data);
1216 node = gnc_schedXaction_dom_tree_create (tmpSX);
1217 xmlElemDump (out, NULL, node);
1219 if (ferror (out) || fprintf (out,
"\n") < 0)
1221 gd->counter.schedXactions_loaded++;
1222 sixtp_run_callback (gd,
"schedXactions");
1224 while ((schedXactions = schedXactions->next));
1233 struct file_backend* file_be =
static_cast<decltype (file_be)
> (data);
1235 GncBudget* bgt = GNC_BUDGET (ent);
1237 if (ferror (file_be->out))
1240 node = gnc_budget_dom_tree_create (bgt);
1241 xmlElemDump (file_be->out, NULL, node);
1243 if (ferror (file_be->out) || fprintf (file_be->out,
"\n") < 0)
1246 file_be->gd->counter.budgets_loaded++;
1247 sixtp_run_callback (file_be->gd,
"budgets");
1251 gnc_xml2_write_namespace_decl (FILE* out,
const char* name_space)
1253 g_return_val_if_fail (name_space, FALSE);
1254 return fprintf (out,
"\n xmlns:%s=\"http://www.gnucash.org/XML/%s\"",
1255 name_space, name_space) >= 0;
1259 write_namespace (
const GncXmlDataType_t& data, FILE* out)
1261 g_return_if_fail (data.version == GNC_FILE_BACKEND_VERS);
1263 if (data.ns && !ferror(out))
1268 write_v2_header (FILE* out)
1270 if (fprintf (out,
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n") < 0
1271 || fprintf (out,
"<" GNC_V2_STRING) < 0
1273 || !gnc_xml2_write_namespace_decl (out,
"gnc")
1274 || !gnc_xml2_write_namespace_decl (out,
"act")
1275 || !gnc_xml2_write_namespace_decl (out,
"book")
1276 || !gnc_xml2_write_namespace_decl (out,
"cd")
1277 || !gnc_xml2_write_namespace_decl (out,
"cmdty")
1278 || !gnc_xml2_write_namespace_decl (out,
"price")
1279 || !gnc_xml2_write_namespace_decl (out,
"slot")
1280 || !gnc_xml2_write_namespace_decl (out,
"split")
1281 || !gnc_xml2_write_namespace_decl (out,
"sx")
1282 || !gnc_xml2_write_namespace_decl (out,
"trn")
1283 || !gnc_xml2_write_namespace_decl (out,
"ts")
1284 || !gnc_xml2_write_namespace_decl (out,
"fs")
1285 || !gnc_xml2_write_namespace_decl (out,
"bgt")
1286 || !gnc_xml2_write_namespace_decl (out,
"recurrence")
1287 || !gnc_xml2_write_namespace_decl (out,
"lot"))
1292 for (
auto data : backend_registry)
1293 write_namespace(data, out);
1295 if (ferror (out) || fprintf (out,
">\n") < 0)
1302 gnc_book_write_to_xml_filehandle_v2 (QofBook* book, FILE* out)
1306 gboolean success = TRUE;
1308 if (!out)
return FALSE;
1310 if (!write_v2_header (out)
1311 || !write_counts (out,
"book", 1, NULL))
1315 gd = gnc_sixtp_gdv2_new (book, FALSE, file_rw_feedback,
1316 qof_be->get_percentage());
1317 gd->counter.commodities_total =
1319 gd->counter.accounts_total = 1 +
1322 gd->counter.schedXactions_total =
1323 g_list_length (gnc_book_get_schedxactions (book)->sx_list);
1329 if (!write_book (out, book, gd)
1330 || fprintf (out,
"</" GNC_V2_STRING
">\n\n") < 0)
1341 gnc_book_write_accounts_to_xml_filehandle_v2 (
QofBackend* qof_be, QofBook* book,
1344 gnc_commodity_table*
table;
1348 gboolean success = TRUE;
1350 if (!out)
return FALSE;
1352 root = gnc_book_get_root_account (book);
1358 if (!write_v2_header (out)
1359 || !write_counts (out,
"commodity", ncom,
"account", nacc, NULL))
1362 gd = gnc_sixtp_gdv2_new (book, TRUE, file_rw_feedback,
1363 qof_be->get_percentage());
1364 gd->counter.commodities_total = ncom;
1365 gd->counter.accounts_total = nacc;
1367 if (!write_commodities (out, book, gd)
1368 || !write_accounts (out, book, gd)
1369 || fprintf (out,
"</" GNC_V2_STRING
">\n\n") < 0)
1376 static inline gzFile
1377 do_gzopen (
const char* filename,
const char* perms)
1381 char* new_perms =
nullptr;
1382 char* conv_name = g_win32_locale_filename_from_utf8 (filename);
1386 g_warning (
"Could not convert '%s' to system codepage",
1391 if (strchr (perms,
'b'))
1392 new_perms = g_strdup (perms);
1394 new_perms = g_strdup_printf (
"%cb%s", *perms, perms + 1);
1396 file = gzopen (conv_name, new_perms);
1401 return gzopen (filename, perms);
1405 constexpr uint32_t BUFLEN{4096};
1410 bool success =
true;
1411 gchar buffer[BUFLEN];
1415 auto bytes = read (params->fd, buffer, BUFLEN);
1418 if (gzwrite (file, buffer, bytes) <= 0)
1421 auto error = gzerror (file, &errnum);
1422 g_warning (
"Could not write the compressed file '%s'. The error is: '%s' (%d)",
1423 params->filename, error, errnum);
1427 else if (bytes == 0)
1433 g_warning (
"Could not read from pipe. The error is '%s' (errno %d)",
1434 g_strerror (errno) ? g_strerror (errno) :
"", errno);
1442 #define WRITE_FN _write 1444 #define WRITE_FN write 1450 bool success =
true;
1451 gchar buffer[BUFLEN];
1455 auto gzval = gzread (file, buffer, BUFLEN);
1458 if (WRITE_FN (params->fd, buffer, gzval) < 0)
1460 g_warning (
"Could not write to pipe. The error is '%s' (%d)",
1461 g_strerror (errno) ? g_strerror (errno) :
"", errno);
1465 else if (gzval == 0)
1472 const gchar* error = gzerror (file, &errnum);
1473 g_warning (
"Could not read from compressed file '%s'. The error is: '%s' (%d)",
1474 params->filename, error, errnum);
1487 bool success =
true;
1489 auto file = do_gzopen (params->filename, params->perms);
1493 g_warning (
"Child threads gzopen failed");
1495 goto cleanup_gz_thread_func;
1500 success = gz_thread_write (file, params);
1504 success = gz_thread_read (file, params);
1507 if ((gzval = gzclose (file)) != Z_OK)
1509 g_warning (
"Could not close the compressed file '%s' (errnum %d)",
1510 params->filename, gzval);
1514 cleanup_gz_thread_func:
1516 g_free (params->filename);
1517 g_free (params->perms);
1520 return GINT_TO_POINTER (success);
1523 static std::pair<FILE*, GThread*>
1524 try_gz_open (
const char* filename,
const char* perms, gboolean compress,
1527 if (strstr (filename,
".gz.") != NULL)
1531 return std::pair<FILE*, GThread*>(g_fopen (filename, perms),
1538 if (_pipe (filedes, 4096, _O_BINARY) < 0)
1547 if (pipe (filedes) < 0 ||
1548 fcntl(filedes[0], F_SETFD, FD_CLOEXEC) == -1 ||
1549 fcntl(filedes[1], F_SETFD, FD_CLOEXEC) == -1)
1552 g_warning (
"Pipe setup failed with errno %d. Opening uncompressed file.", errno);
1559 return std::pair<FILE*, GThread*>(g_fopen (filename, perms),
1564 params->fd = filedes[write ? 0 : 1];
1565 params->filename = g_strdup (filename);
1566 params->perms = g_strdup (perms);
1567 params->write = write;
1569 auto thread = g_thread_new (
"xml_thread", (GThreadFunc) gz_thread_func,
1572 FILE* file =
nullptr;
1576 g_warning (
"Could not create thread for (de)compression.");
1577 g_free (params->filename);
1578 g_free (params->perms);
1582 file = g_fopen (filename, perms);
1587 file = fdopen (filedes[1],
"w");
1589 file = fdopen (filedes[0],
"r");
1592 return std::pair<FILE*, GThread*>(file, thread);
1597 gnc_book_write_to_xml_file_v2 (QofBook* book,
const char* filename,
1600 bool success =
true;
1602 auto [file, thread] = try_gz_open (filename,
"w", compress, TRUE);
1607 if (!gnc_book_write_to_xml_filehandle_v2 (book, file))
1617 if (g_thread_join (thread) ==
nullptr)
1630 gnc_book_write_accounts_to_xml_file_v2 (
QofBackend* qof_be, QofBook* book,
1631 const char* filename)
1634 gboolean success = TRUE;
1636 out = g_fopen (filename,
"w");
1640 || !gnc_book_write_accounts_to_xml_filehandle_v2 (qof_be, book, out))
1644 if (out && fclose (out))
1659 is_gzipped_file (
const gchar* name)
1661 unsigned char buf[2];
1662 int fd = g_open (name, O_RDONLY, 0);
1669 if (read (fd, buf, 2) != 2)
1677 if (buf[0] == 037 && buf[1] == 0213)
1686 gnc_is_xml_data_file_v2 (
const gchar* name, gboolean* with_encoding)
1688 if (is_gzipped_file (name))
1691 char first_chunk[256];
1694 file = do_gzopen (name,
"r");
1697 return GNC_BOOK_NOT_OURS;
1699 num_read = gzread (file, first_chunk,
sizeof (first_chunk) - 1);
1703 return GNC_BOOK_NOT_OURS;
1705 return gnc_is_our_first_xml_chunk (first_chunk, with_encoding);
1708 return (gnc_is_our_xml_file (name, with_encoding));
1713 replace_character_references (gchar*
string)
1715 gchar* cursor, *semicolon, *tail;
1718 for (cursor = strstr (
string,
"&#");
1720 cursor = strstr (cursor,
"&#"))
1722 semicolon = strchr (cursor,
';');
1723 if (semicolon && *semicolon)
1728 if (* (cursor + 2) ==
'x')
1730 number = strtol (cursor + 3, &tail, 16);
1734 number = strtol (cursor + 2, &tail, 10);
1736 if (errno || tail != semicolon || number < 0 || number > 255)
1738 PWARN (
"Illegal character reference");
1743 *cursor = (gchar) number;
1745 if (* (semicolon + 1))
1748 tail = g_strdup (semicolon + 1);
1749 strcpy (cursor, tail);
1761 PWARN (
"Unclosed character reference");
1772 g_free (conv->utf8_string);
1778 conv_list_free (GList* conv_list)
1780 g_list_foreach (conv_list, (GFunc) conv_free, NULL);
1781 g_list_free (conv_list);
1792 GHashTable** unique, GHashTable** ambiguous,
1795 GList* iconv_list = NULL, *conv_list = NULL, *iter;
1798 GHashTable* processed = NULL;
1799 gint n_impossible = 0;
1800 GError* error = NULL;
1801 gboolean clean_return = FALSE;
1803 auto [file, thread] = try_gz_open (filename,
"r",
1804 is_gzipped_file (filename), FALSE);
1807 PWARN (
"Unable to open file %s", filename);
1808 goto cleanup_find_ambs;
1813 ascii->encoding = g_quark_from_string (
"ASCII");
1814 ascii->iconv = g_iconv_open (
"UTF-8",
"ASCII");
1815 if (ascii->iconv == (GIConv) - 1)
1817 PWARN (
"Unable to open ASCII ICONV conversion descriptor");
1818 goto cleanup_find_ambs;
1822 for (iter = encodings; iter; iter = iter->next)
1825 iconv_item->encoding = GPOINTER_TO_UINT (iter->data);
1826 if (iconv_item->encoding == ascii->encoding)
1831 enc = g_quark_to_string (iconv_item->encoding);
1832 iconv_item->iconv = g_iconv_open (
"UTF-8", enc);
1833 if (iconv_item->iconv == (GIConv) - 1)
1835 PWARN (
"Unable to open IConv conversion descriptor for '%s'", enc);
1836 g_free (iconv_item);
1837 goto cleanup_find_ambs;
1841 iconv_list = g_list_prepend (iconv_list, iconv_item);
1847 *unique = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
1848 (GDestroyNotify) conv_free);
1850 *ambiguous = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
1851 (GDestroyNotify) conv_list_free);
1854 processed = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1859 gchar line[256], *word, *utf8;
1860 gchar** word_array, **word_cursor;
1863 if (!fgets (line,
sizeof (line) - 1, file))
1871 goto cleanup_find_ambs;
1876 replace_character_references (line);
1877 word_array = g_strsplit_set (line,
"> <", 0);
1880 for (word_cursor = word_array; *word_cursor; word_cursor++)
1882 word = *word_cursor;
1886 utf8 = g_convert_with_iconv (word, -1, ascii->iconv,
1887 NULL, NULL, &error);
1894 g_error_free (error);
1897 if (g_hash_table_lookup_extended (processed, word, NULL, NULL))
1905 for (iter = iconv_list; iter; iter = iter->next)
1907 iconv_item =
static_cast<decltype (iconv_item)
> (iter->data);
1908 utf8 = g_convert_with_iconv (word, -1, iconv_item->iconv,
1909 NULL, NULL, &error);
1913 conv->encoding = iconv_item->encoding;
1914 conv->utf8_string = utf8;
1915 conv_list = g_list_prepend (conv_list, conv);
1919 g_error_free (error);
1928 *impossible = g_list_append (*impossible, g_strdup (word));
1933 else if (conv_list->next)
1937 g_hash_table_insert (*ambiguous, g_strdup (word), conv_list);
1941 conv_list_free (conv_list);
1950 g_hash_table_insert (*unique, g_strdup (word), conv);
1956 g_list_free (conv_list);
1959 g_hash_table_insert (processed, g_strdup (word), NULL);
1961 g_strfreev (word_array);
1964 clean_return = TRUE;
1970 for (iter = iconv_list; iter; iter = iter->next)
1975 g_free (iter->data);
1978 g_list_free (iconv_list);
1981 g_hash_table_destroy (processed);
1988 g_thread_join (thread);
1991 return (clean_return) ? n_impossible : -1;
1996 const char* filename;
2001 parse_with_subst_push_handler (xmlParserCtxtPtr xml_context,
2004 GIConv ascii = (GIConv) - 1;
2005 GString* output = NULL;
2006 GError* error = NULL;
2008 auto filename = push_data->filename;
2009 auto [file, thread] = try_gz_open (filename,
"r",
2010 is_gzipped_file (filename), FALSE);
2013 PWARN (
"Unable to open file %s", filename);
2014 goto cleanup_push_handler;
2017 ascii = g_iconv_open (
"UTF-8",
"ASCII");
2018 if (ascii == (GIConv) - 1)
2020 PWARN (
"Unable to open ASCII ICONV conversion descriptor");
2021 goto cleanup_push_handler;
2027 gchar line[256], *word, *repl, *utf8;
2029 gchar* start, *cursor;
2031 if (!fgets (line,
sizeof (line) - 1, file))
2039 goto cleanup_push_handler;
2043 replace_character_references (line);
2044 output = g_string_new (line);
2047 cursor = output->str;
2052 while (*cursor ==
'>' || *cursor ==
' ' || *cursor ==
'<' ||
2066 while (*cursor && *cursor !=
'>' && *cursor !=
' ' && *cursor !=
'<' &&
2073 utf8 = g_convert_with_iconv (start, len, ascii, NULL, NULL, &error);
2083 g_error_free (error);
2086 word = g_strndup (start, len);
2087 repl =
static_cast<decltype (repl)
> (g_hash_table_lookup (push_data->subst,
2093 output = g_string_insert (g_string_erase (output, pos, len),
2095 pos += strlen (repl);
2096 cursor = output->str + pos;
2101 goto cleanup_push_handler;
2106 if (xmlParseChunk (xml_context, output->str, output->len, 0) != 0)
2108 goto cleanup_push_handler;
2113 xmlParseChunk (xml_context,
"", 0, 1);
2115 cleanup_push_handler:
2118 g_string_free (output, TRUE);
2119 if (ascii != (GIConv) - 1)
2120 g_iconv_close (ascii);
2125 g_thread_join (thread);
2136 push_data->filename = xml_be->get_filename();
2137 push_data->subst = subst;
2139 success = qof_session_load_from_xml_file_v2_full (
2140 xml_be, book, (sixtp_push_handler) parse_with_subst_push_handler,
2141 push_data, GNC_BOOK_XML2_FILE);
2145 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.