io-gncxml-v2.c

00001 /********************************************************************\
00002  * Copyright (C) 2000,2001 Gnumatic Inc.                            *
00003  *                                                                  *
00004  * This program is free software; you can redistribute it and/or    *
00005  * modify it under the terms of the GNU General Public License as   *
00006  * published by the Free Software Foundation; either version 2 of   *
00007  * the License, or (at your option) any later version.              *
00008  *                                                                  *
00009  * This program is distributed in the hope that it will be useful,  *
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00012  * GNU General Public License for more details.                     *
00013  *                                                                  *
00014  * You should have received a copy of the GNU General Public License*
00015  * along with this program; if not, contact:                        *
00016  *                                                                  *
00017  * Free Software Foundation           Voice:  +1-617-542-5942       *
00018  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00019  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00020 \********************************************************************/
00021 
00022 #include "config.h"
00023 
00024 #include <glib.h>
00025 #include <glib/gstdio.h>
00026 #include <fcntl.h>
00027 #include <string.h>
00028 #include <unistd.h>
00029 #include <zlib.h>
00030 #include <errno.h>
00031 
00032 #include "gnc-engine.h"
00033 #include "gnc-pricedb-p.h"
00034 #include "Scrub.h"
00035 #include "SX-book.h"
00036 #include "SX-book-p.h"
00037 #include "Transaction.h"
00038 #include "TransactionP.h"
00039 #include "TransLog.h"
00040 #include "sixtp-dom-parsers.h"
00041 #include "io-gncxml-v2.h"
00042 #include "io-gncxml-gen.h"
00043 
00044 #include "sixtp.h"
00045 #include "sixtp-parsers.h"
00046 #include "sixtp-utils.h"
00047 #include "gnc-xml.h"
00048 #include "io-utils.h"
00049 
00050 static QofLogModule log_module = GNC_MOD_IO;
00051 
00052 /* map pointers, e.g. of type FILE*, to GThreads */
00053 static GHashTable *threads = NULL;
00054 G_LOCK_DEFINE_STATIC(threads);
00055 
00056 typedef struct {
00057   gint fd;
00058   gchar *filename;
00059   gchar *perms;
00060   gboolean compress;
00061 } gz_thread_params_t;
00062 
00063 /* Callback structure */
00064 struct file_backend {
00065   gboolean        ok;
00066   gpointer        data;
00067   sixtp_gdv2    * gd;
00068   const char    * tag;
00069   sixtp         * parser;
00070   FILE          * out;
00071   QofBook       * book;
00072 };
00073 
00074 #define GNC_V2_STRING "gnc-v2"
00075 extern const gchar *gnc_v2_book_version_string;        /* see gnc-book-xml-v2 */
00076 
00077 void
00078 run_callback(sixtp_gdv2 *data, const char *type)
00079 {
00080     if(data->countCallback)
00081     {
00082         data->countCallback(data, type);
00083     }
00084 }
00085 
00086 static void
00087 clear_up_account_commodity(
00088     gnc_commodity_table *tbl, Account *act,
00089     gnc_commodity * (*getter) (const Account *account),
00090     void (*setter) (Account *account, gnc_commodity *comm),
00091     int (*scu_getter) (const Account *account),
00092     void (*scu_setter) (Account *account, int scu))
00093 {
00094     gnc_commodity *gcom;
00095     gnc_commodity *com = getter(act);
00096     int old_scu;
00097 
00098     if (scu_getter)
00099       old_scu = scu_getter(act);
00100     else
00101       old_scu = 0;
00102 
00103     if(!com)
00104     {
00105         return;
00106     }
00107     
00108     gcom = gnc_commodity_table_lookup(tbl, gnc_commodity_get_namespace(com),
00109                                       gnc_commodity_get_mnemonic(com));
00110 
00111     if (gcom == com)
00112     {
00113         return;
00114     }
00115     else if(!gcom)
00116     {
00117         PWARN("unable to find global commodity for %s adding new",
00118                   gnc_commodity_get_unique_name(com));
00119         gnc_commodity_table_insert(tbl, com);
00120     }
00121     else
00122     {
00123         setter(act, gcom);
00124         if (old_scu != 0 && scu_setter)
00125           scu_setter(act, old_scu);
00126         gnc_commodity_destroy(com);
00127     }
00128 }
00129 
00130 static void
00131 clear_up_transaction_commodity(
00132     gnc_commodity_table *tbl, Transaction *trans,
00133     gnc_commodity * (*getter) (const Transaction *trans),
00134     void (*setter) (Transaction *trans, gnc_commodity *comm))
00135 {
00136     gnc_commodity *gcom;
00137     gnc_commodity *com = getter(trans);
00138 
00139     if(!com)
00140     {
00141         return;
00142     }
00143     
00144     gcom = gnc_commodity_table_lookup(tbl, gnc_commodity_get_namespace(com),
00145                                       gnc_commodity_get_mnemonic(com));
00146 
00147     if(gcom == com)
00148     {
00149         return;
00150     }
00151     else if(!gcom)
00152     {
00153         PWARN("unable to find global commodity for %s adding new",
00154                   gnc_commodity_get_unique_name(com));
00155         gnc_commodity_table_insert(tbl, com);
00156     }
00157     else
00158     {
00159         xaccTransBeginEdit(trans);
00160         setter(trans, gcom);
00161         xaccTransCommitEdit(trans);
00162         gnc_commodity_destroy(com);
00163     }
00164 }
00165 
00166 static gboolean
00167 add_account_local(sixtp_gdv2 *data, Account *act)
00168 {
00169     gnc_commodity_table *table;
00170     Account *parent, *root;
00171     int type;
00172 
00173     table = gnc_book_get_commodity_table (data->book);
00174 
00175     clear_up_account_commodity(table, act,
00176                                        DxaccAccountGetCurrency,
00177                                        DxaccAccountSetCurrency,
00178                                        NULL, NULL);
00179 
00180     clear_up_account_commodity(table, act,
00181                                xaccAccountGetCommodity,
00182                                xaccAccountSetCommodity,
00183                                xaccAccountGetCommoditySCUi,
00184                                xaccAccountSetCommoditySCU);
00185 
00186     xaccAccountScrubCommodity (act);
00187     xaccAccountScrubKvp (act);
00188 
00189     /* Backwards compatability.  If there's no parent, see if this
00190      * account is of type ROOT.  If not, find or create a ROOT
00191      * account and make that the parent. */
00192     type = xaccAccountGetType(act);
00193     if (type == ACCT_TYPE_ROOT) {
00194       gnc_book_set_root_account(data->book, act);
00195     } else {
00196       parent = gnc_account_get_parent(act);
00197       if (parent == NULL) {
00198         root = gnc_book_get_root_account(data->book);
00199         gnc_account_append_child(root, act);
00200       }
00201     }
00202 
00203     data->counter.accounts_loaded++;
00204     run_callback(data, "account");
00205 
00206     return FALSE;
00207 }
00208 
00209 static gboolean
00210 add_book_local(sixtp_gdv2 *data, QofBook *book)
00211 {
00212     data->counter.books_loaded++;
00213     run_callback(data, "book");
00214 
00215     return FALSE;
00216 }
00217 
00218 static gboolean
00219 add_commodity_local(sixtp_gdv2 *data, gnc_commodity *com)
00220 {
00221     gnc_commodity_table *table;
00222 
00223     table = gnc_book_get_commodity_table (data->book);
00224 
00225     gnc_commodity_table_insert(table, com);
00226 
00227     data->counter.commodities_loaded++;
00228     run_callback(data, "commodities");
00229 
00230     return TRUE;
00231 }
00232 
00233 static gboolean
00234 add_transaction_local(sixtp_gdv2 *data, Transaction *trn)
00235 {
00236     gnc_commodity_table *table;
00237     
00238     table = gnc_book_get_commodity_table (data->book);
00239 
00240     xaccTransBeginEdit (trn);
00241     clear_up_transaction_commodity(table, trn,
00242                                    xaccTransGetCurrency,
00243                                    xaccTransSetCurrency);
00244 
00245     xaccTransScrubCurrency (trn);
00246     xaccTransCommitEdit (trn);
00247 
00248     data->counter.transactions_loaded++;
00249     run_callback(data, "transaction");
00250     return TRUE;
00251 }
00252 
00253 static gboolean
00254 add_schedXaction_local(sixtp_gdv2 *data, SchedXaction *sx)
00255 {
00256      SchedXactions *sxes;
00257      sxes = gnc_book_get_schedxactions(data->book);
00258      gnc_sxes_add_sx(sxes, sx);
00259      data->counter.schedXactions_loaded++;
00260      run_callback(data, "schedXactions");
00261      return TRUE;
00262 }
00263 
00264 static gboolean
00265 add_template_transaction_local( sixtp_gdv2 *data,
00266                                 gnc_template_xaction_data *txd )
00267 {
00268     GList *n;
00269     Account *tmpAcct;
00270     Account *acctRoot = NULL;
00271     QofBook *book;
00272 
00273     book = data->book;
00274 
00275     /* expect a struct of: */
00276     /* . template accounts. */
00277     /* . transactions in those accounts. */
00278     for ( n = txd->accts; n; n = n->next ) {
00279         if ( gnc_account_get_parent( (Account*)n->data ) == NULL ) {
00280             /* remove the gnc_book_init-created account of the same name */
00281             acctRoot = gnc_book_get_template_root(book);
00282             tmpAcct = gnc_account_lookup_by_name( acctRoot,
00283                                     xaccAccountGetName( (Account*)n->data ) );
00284             if ( tmpAcct != NULL ) {
00285 /* XXX hack alert FIXME .... Should this be 'Remove', or 'Destroy'?
00286  * If we just remove, then this seems to be a memory leak to me, since
00287  * it is never reparented.  Shouldn't it be a Destroy ???
00288  */
00289                 gnc_account_remove_child( acctRoot, tmpAcct );
00290             }
00291 
00292             gnc_account_append_child( acctRoot, (Account*)n->data );
00293         }
00294 
00295     }
00296 
00297     for ( n = txd->transactions; n; n = n->next ) {
00298         /* insert transactions into accounts */
00299         add_transaction_local( data, (Transaction*)n->data );
00300     }
00301 
00302     return TRUE;
00303 }
00304 
00305 static gboolean
00306 add_pricedb_local(sixtp_gdv2 *data, GNCPriceDB *db)
00307 {
00308     /* gnc_pricedb_print_contents(db, stdout); */
00309     return TRUE;
00310 }
00311 
00312 static void
00313 do_counter_cb (const char *type, gpointer data_p, gpointer be_data_p)
00314 {
00315   GncXmlDataType_t *data = data_p;
00316   struct file_backend *be_data = be_data_p;
00317 
00318   g_return_if_fail (type && data && be_data);
00319   g_return_if_fail (data->version == GNC_FILE_BACKEND_VERS);
00320 
00321   if (be_data->ok == TRUE)
00322     return;
00323 
00324   if (!safe_strcmp (be_data->tag, data->type_name))
00325     be_data->ok = TRUE;
00326 
00327   /* XXX: should we do anything with this counter? */
00328 }
00329 
00330 static gboolean
00331 gnc_counter_end_handler(gpointer data_for_children,
00332                         GSList* data_from_children, GSList* sibling_data,
00333                         gpointer parent_data, gpointer global_data,
00334                         gpointer *result, const gchar *tag)
00335 {
00336     char *strval;
00337     gint64 val;
00338     char *type;
00339     xmlNodePtr tree = (xmlNodePtr)data_for_children;
00340     gxpf_data *gdata = (gxpf_data*)global_data;
00341     sixtp_gdv2 *sixdata = (sixtp_gdv2*)gdata->parsedata;
00342     gboolean ret = TRUE;
00343     
00344     if (parent_data)
00345         return TRUE;
00346 
00347     /* OK.  For some messed up reason this is getting called again with a
00348        NULL tag.  So we ignore those cases */
00349     if (!tag)
00350         return TRUE;
00351     
00352     g_return_val_if_fail(tree, FALSE);
00353 
00354     /* Note: BADXML.
00355      *
00356      * This is invalid xml because the namespace isn't declared in the
00357      * tag itself. This should be changed to 'type' at some point. */
00358     type = (char*)xmlGetProp(tree, BAD_CAST "cd:type");
00359     strval = dom_tree_to_text(tree);
00360     if (!string_to_gint64(strval, &val))
00361     {
00362         PERR ("string_to_gint64 failed with input: %s",
00363                   strval ? strval : "(null)");
00364         ret = FALSE;
00365     }
00366     else if (safe_strcmp(type, "transaction") == 0)
00367     {
00368         sixdata->counter.transactions_total = val;
00369     }
00370     else if (safe_strcmp(type, "account") == 0)
00371     {
00372         sixdata->counter.accounts_total = val;
00373     }
00374     else if (safe_strcmp(type, "book") == 0)
00375     {
00376         sixdata->counter.books_total = val;
00377     }
00378     else if (safe_strcmp(type, "commodity") == 0)
00379     {
00380         sixdata->counter.commodities_total = val;
00381     }
00382     else if (safe_strcmp(type, "schedxaction") == 0)
00383     {
00384         sixdata->counter.schedXactions_total = val;
00385     }
00386     else if (safe_strcmp(type, "budget") == 0)
00387     {
00388         sixdata->counter.budgets_total = val;
00389     }
00390     else
00391     {
00392       struct file_backend be_data;
00393 
00394       be_data.ok = FALSE;
00395       be_data.tag = type;
00396 
00397       qof_object_foreach_backend (GNC_FILE_BACKEND, do_counter_cb, &be_data);
00398 
00399       if (be_data.ok == FALSE)
00400       {
00401         PERR("Unknown type: %s", type ? type : "(null)");
00402         /* Do *NOT* flag this as an error. Gnucash 1.8 writes invalid
00403          * xml by writing the 'cd:type' attribute without providing
00404          * the namespace in the gnc:count-data tag.  The parser is
00405          * entirely within its rights to refuse to read this bad
00406          * attribute. Gnucash will function correctly without the data
00407          * in this tag, so just let the error pass. */
00408         ret = TRUE;
00409       }
00410     }
00411 
00412     g_free (strval);
00413     xmlFree (type);
00414     xmlFreeNode(tree);
00415     return ret;
00416 }
00417 
00418 static sixtp*
00419 gnc_counter_sixtp_parser_create(void)
00420 {
00421      return sixtp_dom_parser_new(gnc_counter_end_handler, NULL, NULL);
00422 }
00423 
00424 static void
00425 debug_print_counter_data(load_counter *data)
00426 {
00427     DEBUG("Transactions: Total: %d, Loaded: %d",
00428           data->transactions_total, data->transactions_loaded);
00429     DEBUG("Accounts: Total: %d, Loaded: %d",
00430           data->accounts_total, data->accounts_loaded);
00431     DEBUG("Books: Total: %d, Loaded: %d",
00432           data->books_total, data->books_loaded);
00433     DEBUG("Commodities: Total: %d, Loaded: %d",
00434           data->commodities_total, data->commodities_loaded);
00435     DEBUG("Scheduled Transactions: Total: %d, Loaded: %d",
00436           data->schedXactions_total, data->schedXactions_loaded);
00437     DEBUG("Budgets: Total: %d, Loaded: %d",
00438           data->budgets_total, data->budgets_loaded);
00439 }
00440 
00441 static void
00442 file_rw_feedback (sixtp_gdv2 *gd, const char *type)
00443 {
00444     load_counter *counter;
00445     int loaded, total, percentage;
00446 
00447     g_assert(gd != NULL);
00448     if (!gd->gui_display_fn)
00449       return;
00450 
00451     counter = &gd->counter;
00452     loaded = counter->transactions_loaded + counter->accounts_loaded +
00453       counter->books_loaded + counter->commodities_loaded +
00454       counter->schedXactions_loaded + counter->budgets_loaded;
00455     total = counter->transactions_total + counter->accounts_total +
00456       counter->books_total + counter->commodities_total +
00457       counter->schedXactions_total + counter->budgets_total;
00458     if (total == 0)
00459       total = 1;
00460 
00461     percentage = (loaded * 100)/total;
00462     if (percentage > 100) {
00463       /* FIXME: Perhaps the below should be replaced by:
00464          print_counter_data(counter); */
00465 //      printf("Transactions: Total: %d, Loaded: %d\n",
00466 //             counter->transactions_total, counter->transactions_loaded);
00467 //      printf("Accounts: Total: %d, Loaded: %d\n",
00468 //             counter->accounts_total, counter->accounts_loaded);
00469 //      printf("Books: Total: %d, Loaded: %d\n",
00470 //             counter->books_total, counter->books_loaded);
00471 //      printf("Commodities: Total: %d, Loaded: %d\n",
00472 //             counter->commodities_total, counter->commodities_loaded);
00473 //      printf("Scheduled Transactions: Total: %d, Loaded: %d\n",
00474 //             counter->schedXactions_total, counter->schedXactions_loaded);
00475 //      printf("Budgets: Total: %d, Loaded: %d\n",
00476 //           counter->budgets_total, counter->budgets_loaded);
00477     }
00478     gd->gui_display_fn(NULL, percentage);
00479 }
00480 
00481 static const char *BOOK_TAG = "gnc:book";
00482 static const char *BOOK_ID_TAG = "book:id";
00483 static const char *BOOK_SLOTS_TAG = "book:slots";
00484 static const char *ACCOUNT_TAG = "gnc:account";
00485 static const char *PRICEDB_TAG = "gnc:pricedb";
00486 static const char *COMMODITY_TAG = "gnc:commodity";
00487 static const char *COUNT_DATA_TAG = "gnc:count-data";
00488 static const char *TRANSACTION_TAG = "gnc:transaction";
00489 static const char *SCHEDXACTION_TAG = "gnc:schedxaction";
00490 static const char *TEMPLATE_TRANSACTION_TAG = "gnc:template-transactions";
00491 static const char *BUDGET_TAG = "gnc:budget";
00492 
00493 static void
00494 add_item_cb (const char *type, gpointer data_p, gpointer be_data_p)
00495 {
00496   GncXmlDataType_t *data = data_p;
00497   struct file_backend *be_data = be_data_p;
00498 
00499   g_return_if_fail (type && data && be_data);
00500   g_return_if_fail (data->version == GNC_FILE_BACKEND_VERS);
00501 
00502   if (be_data->ok)
00503     return;
00504 
00505   if (!safe_strcmp (be_data->tag, data->type_name)) {
00506     if (data->add_item)
00507       (data->add_item)(be_data->gd, be_data->data);
00508 
00509     be_data->ok = TRUE;
00510   }
00511 }
00512 
00513 static gboolean
00514 book_callback(const char *tag, gpointer globaldata, gpointer data)
00515 {
00516     sixtp_gdv2 *gd = (sixtp_gdv2*)globaldata;
00517 
00518     if(safe_strcmp(tag, ACCOUNT_TAG) == 0)
00519     {
00520         add_account_local(gd, (Account*)data);
00521     }
00522     else if(safe_strcmp(tag, PRICEDB_TAG) == 0)
00523     {
00524         add_pricedb_local(gd, (GNCPriceDB*)data);
00525     }
00526     else if(safe_strcmp(tag, COMMODITY_TAG) == 0)
00527     {
00528         add_commodity_local(gd, (gnc_commodity*)data);
00529     }
00530     else if(safe_strcmp(tag, TRANSACTION_TAG) == 0)
00531     {
00532         add_transaction_local(gd, (Transaction*)data);
00533     }
00534     else if(safe_strcmp(tag, SCHEDXACTION_TAG) == 0)
00535     {
00536         add_schedXaction_local(gd, (SchedXaction*)data);
00537     }
00538     else if(safe_strcmp(tag, TEMPLATE_TRANSACTION_TAG) == 0)
00539     {
00540         add_template_transaction_local( gd, (gnc_template_xaction_data*)data );
00541     }
00542     else if(safe_strcmp(tag, BUDGET_TAG) == 0)
00543     {
00544         // Nothing needed here.
00545     }
00546     else
00547     {
00548       struct file_backend be_data;
00549 
00550       be_data.ok = FALSE;
00551       be_data.tag = tag;
00552       be_data.gd = gd;
00553       be_data.data = data;
00554 
00555       qof_object_foreach_backend (GNC_FILE_BACKEND, add_item_cb, &be_data);
00556 
00557       if (be_data.ok == FALSE)
00558       {
00559         PWARN ("unexpected tag %s", tag);
00560       }
00561     }
00562     return TRUE;
00563 }
00564 
00565 static gboolean
00566 generic_callback(const char *tag, gpointer globaldata, gpointer data)
00567 {
00568     sixtp_gdv2 *gd = (sixtp_gdv2*)globaldata;
00569 
00570     if(safe_strcmp(tag, BOOK_TAG) == 0)
00571     {
00572         add_book_local(gd, (QofBook*)data);
00573         book_callback(tag, globaldata, data);
00574     }
00575     else
00576     {
00577         // PWARN ("importing pre-book-style XML data file");
00578         book_callback(tag, globaldata, data);
00579     }
00580     return TRUE;
00581 }
00582 
00583 static void
00584 add_parser_cb (const char *type, gpointer data_p, gpointer be_data_p)
00585 {
00586   GncXmlDataType_t *data = data_p;
00587   struct file_backend *be_data = be_data_p;
00588 
00589   g_return_if_fail (type && data && be_data);
00590   g_return_if_fail (data->version == GNC_FILE_BACKEND_VERS);
00591 
00592   if (be_data->ok == FALSE)
00593     return;
00594 
00595   if (data->create_parser)
00596     if(!sixtp_add_some_sub_parsers(
00597            be_data->parser, TRUE,
00598            data->type_name, (data->create_parser)(),
00599            NULL, NULL))
00600       be_data->ok = FALSE;
00601 }
00602 
00603 static void
00604 scrub_cb (const char *type, gpointer data_p, gpointer be_data_p)
00605 {
00606   GncXmlDataType_t *data = data_p;
00607   struct file_backend *be_data = be_data_p;
00608 
00609   g_return_if_fail (type && data && be_data);
00610   g_return_if_fail (data->version == GNC_FILE_BACKEND_VERS);
00611 
00612   if (data->scrub)
00613     (data->scrub)(be_data->book);
00614 }
00615 
00616 static sixtp_gdv2 *
00617 gnc_sixtp_gdv2_new (
00618     QofBook *book,
00619     gboolean exporting,
00620     countCallbackFn countcallback,
00621     QofBePercentageFunc gui_display_fn)
00622 {
00623     sixtp_gdv2 *gd = g_new0(sixtp_gdv2, 1);
00624 
00625     if (gd == NULL) return NULL;
00626 
00627     gd->book = book;
00628     gd->counter.accounts_loaded = 0;
00629     gd->counter.accounts_total = 0;
00630     gd->counter.books_loaded = 0;
00631     gd->counter.books_total = 0;
00632     gd->counter.commodities_loaded = 0;
00633     gd->counter.commodities_total = 0;
00634     gd->counter.transactions_loaded = 0;
00635     gd->counter.transactions_total = 0;
00636     gd->counter.prices_loaded = 0;
00637     gd->counter.prices_total = 0;
00638     gd->counter.schedXactions_loaded = 0;
00639     gd->counter.schedXactions_total = 0;
00640     gd->counter.budgets_loaded = 0;
00641     gd->counter.budgets_total = 0;
00642     gd->exporting = exporting;
00643     gd->countCallback = countcallback;
00644     gd->gui_display_fn = gui_display_fn;
00645     return gd;
00646 }
00647 
00648 static gboolean
00649 qof_session_load_from_xml_file_v2_full(
00650     FileBackend *fbe, QofBook *book,
00651     sixtp_push_handler push_handler, gpointer push_user_data)
00652 {
00653     Account *root;
00654     QofBackend *be = &fbe->be;
00655     sixtp_gdv2 *gd;
00656     sixtp *top_parser;
00657     sixtp *main_parser;
00658     sixtp *book_parser;
00659     struct file_backend be_data;
00660     gboolean retval;
00661 
00662     gd = gnc_sixtp_gdv2_new(book, FALSE, file_rw_feedback, be->percentage);
00663 
00664     top_parser = sixtp_new();
00665     main_parser = sixtp_new();
00666     book_parser = sixtp_new();
00667 
00668     if(!sixtp_add_some_sub_parsers(
00669         top_parser, TRUE,
00670         GNC_V2_STRING, main_parser,
00671         NULL, NULL))
00672     {
00673         goto bail;
00674     }
00675 
00676     if(!sixtp_add_some_sub_parsers(
00677            main_parser, TRUE,
00678            COUNT_DATA_TAG, gnc_counter_sixtp_parser_create(),
00679            BOOK_TAG, book_parser,
00680 
00681            /* the following are present here only to support 
00682             * the older, pre-book format.  Basically, the top-level 
00683             * book is implicit. */
00684            PRICEDB_TAG, gnc_pricedb_sixtp_parser_create(),
00685            COMMODITY_TAG, gnc_commodity_sixtp_parser_create(),
00686            ACCOUNT_TAG, gnc_account_sixtp_parser_create(),
00687            TRANSACTION_TAG, gnc_transaction_sixtp_parser_create(),
00688            SCHEDXACTION_TAG, gnc_schedXaction_sixtp_parser_create(),
00689            TEMPLATE_TRANSACTION_TAG, gnc_template_transaction_sixtp_parser_create(),
00690            NULL, NULL))
00691     {
00692         goto bail;
00693     }
00694 
00695     if(!sixtp_add_some_sub_parsers(
00696            book_parser, TRUE,
00697            BOOK_ID_TAG, gnc_book_id_sixtp_parser_create(),
00698            BOOK_SLOTS_TAG, gnc_book_slots_sixtp_parser_create(),
00699            COUNT_DATA_TAG, gnc_counter_sixtp_parser_create(),
00700            PRICEDB_TAG, gnc_pricedb_sixtp_parser_create(),
00701            COMMODITY_TAG, gnc_commodity_sixtp_parser_create(),
00702            ACCOUNT_TAG, gnc_account_sixtp_parser_create(),
00703            BUDGET_TAG, gnc_budget_sixtp_parser_create(),
00704            TRANSACTION_TAG, gnc_transaction_sixtp_parser_create(),
00705            SCHEDXACTION_TAG, gnc_schedXaction_sixtp_parser_create(),
00706            TEMPLATE_TRANSACTION_TAG, gnc_template_transaction_sixtp_parser_create(),
00707            NULL, NULL))
00708     {
00709         goto bail;
00710     }
00711 
00712     be_data.ok = TRUE;
00713     be_data.parser = book_parser;
00714     qof_object_foreach_backend (GNC_FILE_BACKEND, add_parser_cb, &be_data);
00715     if (be_data.ok == FALSE)
00716       goto bail;
00717 
00718     /* stop logging while we load */
00719     xaccLogDisable ();
00720     xaccDisableDataScrubbing();
00721 
00722     if (push_handler) {
00723         gpointer parse_result = NULL;
00724         gxpf_data gpdata;
00725 
00726         gpdata.cb = generic_callback;
00727         gpdata.parsedata = gd;
00728         gpdata.bookdata = book;
00729 
00730         retval = sixtp_parse_push(top_parser, push_handler, push_user_data,
00731                                   NULL, &gpdata, &parse_result);
00732     } else {
00733         retval = gnc_xml_parse_file(top_parser, fbe->fullpath,
00734                                     generic_callback, gd, book);
00735     }
00736 
00737     if (!retval) {
00738         sixtp_destroy(top_parser);
00739         xaccLogEnable ();
00740         xaccEnableDataScrubbing();
00741         goto bail;
00742     }
00743     debug_print_counter_data(&gd->counter);
00744 
00745     /* destroy the parser */
00746     sixtp_destroy (top_parser);
00747     g_free(gd);
00748 
00749     xaccEnableDataScrubbing();
00750 
00751     /* Mark the book as saved */
00752     qof_book_mark_saved (book);
00753 
00754     /* Call individual scrub functions */
00755     memset(&be_data, 0, sizeof(be_data));
00756     be_data.book = book;
00757     qof_object_foreach_backend (GNC_FILE_BACKEND, scrub_cb, &be_data);
00758 
00759     /* fix price quote sources */
00760     root = gnc_book_get_root_account(book);
00761     xaccAccountTreeScrubQuoteSources (root, gnc_book_get_commodity_table(book));
00762 
00763     /* Fix account and transaction commodities */
00764     xaccAccountTreeScrubCommodities (root);
00765 
00766     /* Fix split amount/value */
00767     xaccAccountTreeScrubSplits (root);
00768 
00769     /* commit all groups, this completes the BeginEdit started when the
00770      * account_end_handler finished reading the account.
00771      */
00772     gnc_account_foreach_descendant(root,
00773                                    (AccountCb) xaccAccountCommitEdit,
00774                                    NULL);
00775 
00776     /* start logging again */
00777     xaccLogEnable ();
00778 
00779     return TRUE;
00780 
00781  bail:
00782     g_free(gd);
00783     return FALSE;
00784 }
00785 
00786 gboolean
00787 qof_session_load_from_xml_file_v2(FileBackend *fbe, QofBook *book)
00788 {
00789     return qof_session_load_from_xml_file_v2_full(fbe, book, NULL, NULL);
00790 }
00791 
00792 /***********************************************************************/
00793 
00794 static void
00795 write_counts(FILE* out, ...)
00796 {
00797     va_list ap;
00798     char *type;
00799 
00800     va_start(ap, out);
00801     type = va_arg(ap, char *);
00802 
00803     while(type)
00804     {
00805         int amount = va_arg(ap, int);
00806 
00807         if(amount != 0)
00808         {
00809 #if GNUCASH_REALLY_BUILD_AN_XML_TREE_ON_OUTPUT
00810             char *val;
00811             xmlNodePtr node;
00812 
00813             val = g_strdup_printf("%d", amount);
00814 
00815             node = xmlNewNode(NULL, BAD_CAST COUNT_DATA_TAG);
00816             /* Note: BADXML.
00817              *
00818              * This is invalid xml because the namespace isn't
00819              * declared in the tag itself. This should be changed to
00820              * 'type' at some point. */
00821             xmlSetProp(node, BAD_CAST "cd:type", BAD_CAST type);
00822             xmlNodeAddContent(node, BAD_CAST val);
00823 
00824             xmlElemDump(out, NULL, node);
00825             fprintf(out, "\n");
00826         
00827             g_free(val);
00828             xmlFreeNode(node);
00829 #else
00830             fprintf(out, "<%s %s=\"%s\">%d</%s>\n",
00831                     COUNT_DATA_TAG, "cd:type", type, amount, COUNT_DATA_TAG);
00832 #endif
00833         
00834         }
00835         
00836         type = va_arg(ap, char *);
00837     }
00838 
00839     va_end(ap);
00840 }
00841 
00842 static gint
00843 compare_namespaces(gconstpointer a, gconstpointer b)
00844 {
00845   const gchar *sa = (const gchar *) a;
00846   const gchar *sb = (const gchar *) b;
00847   return(safe_strcmp(sa, sb));
00848 }
00849 
00850 static gint
00851 compare_commodity_ids(gconstpointer a, gconstpointer b)
00852 {
00853   const gnc_commodity *ca = (const gnc_commodity *) a;
00854   const gnc_commodity *cb = (const gnc_commodity *) b;
00855   return(safe_strcmp(gnc_commodity_get_mnemonic(ca),
00856                      gnc_commodity_get_mnemonic(cb)));
00857 }
00858 
00859 static void write_pricedb (FILE *out, QofBook *book, sixtp_gdv2 *gd);
00860 static void write_transactions (FILE *out, QofBook *book, sixtp_gdv2 *gd);
00861 static void write_template_transaction_data (FILE *out, QofBook *book, sixtp_gdv2 *gd);
00862 static void write_schedXactions(FILE *out, QofBook *book, sixtp_gdv2 *gd);
00863 static void write_budget (QofInstance *ent, gpointer data);
00864 
00865 static void
00866 write_counts_cb (const char *type, gpointer data_p, gpointer be_data_p)
00867 {
00868   GncXmlDataType_t *data = data_p;
00869   struct file_backend *be_data = be_data_p;
00870 
00871   g_return_if_fail (type && data && be_data);
00872   g_return_if_fail (data->version == GNC_FILE_BACKEND_VERS);
00873 
00874   if (data->get_count)
00875     write_counts (be_data->out, data->type_name,
00876                   (data->get_count) (be_data->book),
00877                   NULL);
00878 }
00879 
00880 static void
00881 write_data_cb (const char *type, gpointer data_p, gpointer be_data_p)
00882 {
00883   GncXmlDataType_t *data = data_p;
00884   struct file_backend *be_data = be_data_p;
00885 
00886   g_return_if_fail (type && data && be_data);
00887   g_return_if_fail (data->version == GNC_FILE_BACKEND_VERS);
00888 
00889   if (data->write)
00890     (data->write)(be_data->out, be_data->book);
00891 }
00892 
00893 static void
00894 write_book(FILE *out, QofBook *book, sixtp_gdv2 *gd)
00895 {
00896     struct file_backend be_data;
00897 
00898 #ifdef IMPLEMENT_BOOK_DOM_TREES_LATER
00899     /* We can't just blast out the dom tree, because the dom tree
00900      * doesn't have the books, transactions, etc underneath it.
00901      * But that is just as well, since I think the performance
00902      * will be much better if we write out as we go along 
00903      */
00904     xmlNodePtr node;
00905 
00906     node = gnc_book_dom_tree_create(book);
00907 
00908     if(!node)
00909     {
00910         return;
00911     }
00912     
00913     xmlElemDump(out, NULL, node);
00914     if(fprintf(out, "\n") < 0)
00915         {
00916                 qof_backend_set_error(qof_book_get_backend(book), ERR_FILEIO_WRITE_ERROR);
00917                 return;
00918         }
00919 
00920     xmlFreeNode(node);
00921 #endif
00922 
00923     be_data.out = out;
00924     be_data.book = book;
00925     be_data.gd = gd;
00926     if(fprintf( out, "<%s version=\"%s\">\n", BOOK_TAG, gnc_v2_book_version_string) < 0)
00927         {
00928                 qof_backend_set_error(qof_book_get_backend(book), ERR_FILEIO_WRITE_ERROR);
00929                 return;
00930         }
00931     write_book_parts (out, book);
00932 
00933     /* gd->counter.{foo}_total fields should have all these totals
00934        already collected.  I don't know why we're re-calling all these
00935        functions.  */
00936     write_counts(out,
00937                  "commodity",
00938                  gnc_commodity_table_get_size(
00939                      gnc_book_get_commodity_table(book)),
00940                  "account",
00941                  1 + gnc_account_n_descendants(gnc_book_get_root_account(book)),
00942                  "transaction",
00943                  gnc_book_count_transactions(book),
00944                  "schedxaction",
00945                  g_list_length(gnc_book_get_schedxactions(book)->sx_list),
00946                  "budget", qof_collection_count(
00947                      qof_book_get_collection(book, GNC_ID_BUDGET)),
00948                  NULL);
00949 
00950     qof_object_foreach_backend (GNC_FILE_BACKEND, write_counts_cb, &be_data);
00951 
00952     write_commodities(out, book, gd);
00953     write_pricedb(out, book, gd);
00954     write_accounts(out, book, gd);
00955     write_transactions(out, book, gd);
00956     write_template_transaction_data(out, book, gd);
00957     write_schedXactions(out, book, gd);
00958 
00959     qof_collection_foreach(qof_book_get_collection(book, GNC_ID_BUDGET), 
00960         write_budget, &be_data);
00961 
00962     qof_object_foreach_backend (GNC_FILE_BACKEND, write_data_cb, &be_data);
00963 
00964     if(fprintf( out, "</%s>\n", BOOK_TAG ) < 0) {
00965                 qof_backend_set_error(qof_book_get_backend(book), ERR_FILEIO_WRITE_ERROR);
00966         }
00967 }
00968 
00969 void
00970 write_commodities(FILE *out, QofBook *book, sixtp_gdv2 *gd)
00971 {
00972     gnc_commodity_table *tbl;
00973     GList *namespaces;
00974     GList *lp;
00975 
00976     tbl = gnc_book_get_commodity_table(book);
00977 
00978     namespaces = gnc_commodity_table_get_namespaces(tbl);
00979     if(namespaces) 
00980     {
00981         namespaces = g_list_sort(namespaces, compare_namespaces);
00982     }
00983 
00984     for(lp = namespaces; lp; lp = lp->next) 
00985     {
00986         GList *comms, *lp2;
00987         xmlNodePtr comnode;
00988 
00989         comms = gnc_commodity_table_get_commodities(tbl, lp->data);
00990         comms = g_list_sort(comms, compare_commodity_ids);
00991 
00992         for(lp2 = comms; lp2; lp2 = lp2->next) {
00993             comnode = gnc_commodity_dom_tree_create(lp2->data);
00994             if (comnode == NULL)
00995               continue;
00996 
00997             xmlElemDump(out, NULL, comnode);
00998             fprintf(out, "\n");
00999 
01000             xmlFreeNode(comnode);
01001             gd->counter.commodities_loaded++;
01002             run_callback(gd, "commodities");
01003           }
01004 
01005         g_list_free (comms);
01006     }
01007 
01008     if (namespaces) g_list_free (namespaces);
01009 }
01010 
01011 static void
01012 write_pricedb(FILE *out, QofBook *book, sixtp_gdv2 *gd)
01013 {
01014     xmlNodePtr node;
01015 
01016     node = gnc_pricedb_dom_tree_create(gnc_book_get_pricedb(book));
01017 
01018     if(!node)
01019     {
01020         return;
01021     }
01022     
01023     xmlElemDump(out, NULL, node);
01024     fprintf(out, "\n");
01025 
01026     xmlFreeNode(node);
01027 }
01028 
01029 static int
01030 xml_add_trn_data(Transaction *t, gpointer data)
01031 {
01032     struct file_backend *be_data = data;
01033     xmlNodePtr node;
01034 
01035     node = gnc_transaction_dom_tree_create(t);
01036 
01037     xmlElemDump(be_data->out, NULL, node);
01038     fprintf(be_data->out, "\n");
01039 
01040     xmlFreeNode(node);
01041     be_data->gd->counter.transactions_loaded++;
01042     run_callback(be_data->gd, "transaction");
01043     return 0;
01044 }
01045 
01046 static void
01047 write_transactions(FILE *out, QofBook *book, sixtp_gdv2 *gd)
01048 {
01049     struct file_backend be_data;
01050 
01051     be_data.out = out;
01052     be_data.gd = gd;
01053     xaccAccountTreeForEachTransaction(gnc_book_get_root_account(book),
01054                                       xml_add_trn_data,
01055                                       (gpointer) &be_data);
01056 }
01057 
01058 static void
01059 write_template_transaction_data( FILE *out, QofBook *book, sixtp_gdv2 *gd )
01060 {
01061     Account *ra;
01062     struct file_backend be_data;
01063 
01064     be_data.out = out;
01065     be_data.gd = gd;
01066 
01067     ra = gnc_book_get_template_root(book);
01068     if ( gnc_account_n_descendants(ra) > 0 )
01069     {
01070         fprintf( out, "<%s>\n", TEMPLATE_TRANSACTION_TAG );
01071         write_account_tree( out, ra, gd );
01072         xaccAccountTreeForEachTransaction( ra, xml_add_trn_data, (gpointer)&be_data );
01073         fprintf( out, "</%s>\n", TEMPLATE_TRANSACTION_TAG );
01074     }
01075 }
01076 
01077 static void
01078 write_schedXactions( FILE *out, QofBook *book, sixtp_gdv2 *gd)
01079 {
01080      GList *schedXactions;
01081      SchedXaction *tmpSX;
01082      xmlNodePtr node;
01083      
01084      schedXactions = gnc_book_get_schedxactions(book)->sx_list;
01085 
01086      if ( schedXactions == NULL )
01087           return;
01088 
01089      do {
01090           tmpSX = schedXactions->data;
01091           node = gnc_schedXaction_dom_tree_create( tmpSX );
01092           xmlElemDump( out, NULL, node );
01093           fprintf( out, "\n" );
01094           xmlFreeNode( node );
01095           gd->counter.schedXactions_loaded++;
01096           run_callback(gd, "schedXactions");
01097      } while ( (schedXactions = schedXactions->next) );
01098 }
01099 
01100 static void
01101 write_budget (QofInstance *ent, gpointer data)
01102 {
01103     xmlNodePtr node;
01104     struct file_backend* be = data;
01105 
01106     GncBudget *bgt = GNC_BUDGET(ent);
01107     node = gnc_budget_dom_tree_create(bgt);
01108     xmlElemDump( be->out, NULL, node );
01109     fprintf( be->out, "\n" );
01110     xmlFreeNode( node );
01111     
01112     be->gd->counter.budgets_loaded++;
01113     run_callback(be->gd, "budgets");    
01114 }
01115 
01116 void
01117 gnc_xml2_write_namespace_decl (FILE *out, const char *namespace)
01118 {
01119   g_return_if_fail (namespace);
01120   fprintf(out, "\n     xmlns:%s=\"http://www.gnucash.org/XML/%s\"",
01121           namespace, namespace);
01122 }
01123 
01124 static void
01125 do_write_namespace_cb (const char *type, gpointer data_p, gpointer file_p)
01126 {
01127   GncXmlDataType_t *data = data_p;
01128   FILE *out = file_p;
01129 
01130   g_return_if_fail (type && data && out);
01131   g_re