gnucash maint: Multiple changes pushed

John Ralls jralls at code.gnucash.org
Fri Jun 15 18:21:26 EDT 2018


Updated	 via  https://github.com/Gnucash/gnucash/commit/7ce8c9d3 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d6de324b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/1f8f6817 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/9f1bfddc (commit)
	from  https://github.com/Gnucash/gnucash/commit/edd439a0 (commit)



commit 7ce8c9d3363ddb388933b8f5e41ddf8607d07f8f
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Jun 15 15:21:14 2018 -0700

    Bug 796595 - QIF Import Select Account button to add a new account...
    
    is labled gnc-account-new but should be New.
    
    Made it "New Account".

diff --git a/gnucash/gtkbuilder/dialog-account-picker.glade b/gnucash/gtkbuilder/dialog-account-picker.glade
index 0bd5aa9..6144047 100644
--- a/gnucash/gtkbuilder/dialog-account-picker.glade
+++ b/gnucash/gtkbuilder/dialog-account-picker.glade
@@ -154,7 +154,7 @@
             <property name="layout_style">end</property>
             <child>
               <object class="GtkButton" id="newbutton">
-                <property name="label">gnc-account-new</property>
+                <property name="label" translatable="yes">New Account</property>
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="can_default">True</property>

commit d6de324b3283af318e44ca16bd85db6eaf3f75b1
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Jun 15 14:22:43 2018 -0700

    Bug 795272 - QIF importer causes application crash if action is invalid.
    
    Fixes the crash and also pauses after loading if there are load errors.
    The load "protection" catches exceptions other than bad-date so that it's
    real protection.
    Check for unbalanced transactions (i.e. with only one account) and don't
    try to match if there is; report the error in the error log space in the
    assistant.
    Don't proceed to finding duplicates if the new account tree hasn't been
    created, there aren't any.

diff --git a/gnucash/import-export/qif-imp/assistant-qif-import.c b/gnucash/import-export/qif-imp/assistant-qif-import.c
index 21f3e74..1ab3788 100644
--- a/gnucash/import-export/qif-imp/assistant-qif-import.c
+++ b/gnucash/import-export/qif-imp/assistant-qif-import.c
@@ -59,6 +59,7 @@
 #include "gnc-prefs.h"
 #include "gnc-ui.h"
 #include "guile-mappings.h"
+#include <gfec.h>
 
 #include "swig-runtime.h"
 
@@ -1067,6 +1068,11 @@ gnc_ui_qif_import_commodity_update(QIFImportWindow * wind)
     }
 }
 
+static void
+_gfec_error_handler(const char *message)
+{
+    PERR("qif-import:qif-to-gnc-undo encountered an error: %s", message);
+}
 
 /****************************************************************
  * gnc_ui_qif_import_convert_undo
@@ -1082,7 +1088,7 @@ gnc_ui_qif_import_convert_undo(QIFImportWindow * wind)
     gnc_set_busy_cursor(NULL, TRUE);
 
     /* Undo the conversion. */
-    scm_call_1(undo, wind->imported_account_tree);
+    gfec_apply(undo, wind->imported_account_tree, _gfec_error_handler);
 
     /* There's no imported account tree any more. */
     scm_gc_unprotect_object(wind->imported_account_tree);
@@ -1916,6 +1922,7 @@ gnc_ui_qif_import_load_progress_start_cb(GtkButton * button,
 
                 wind->ask_date_format = TRUE;
             }
+            wind->load_stop = TRUE;
         }
         else
         {
@@ -1937,22 +1944,27 @@ gnc_ui_qif_import_load_progress_start_cb(GtkButton * button,
     gtk_widget_set_sensitive(wind->load_pause, FALSE);
     gtk_widget_set_sensitive(wind->load_start, FALSE);
 
-    if (wind->load_stop == FALSE)
-    {
-        /* The file was loaded successfully. */
-        gnc_progress_dialog_set_sub(wind->load_progress, _("Loading completed"));
-        gnc_progress_dialog_set_value(wind->load_progress, 1);
+    /* The file was loaded successfully. */
+    gnc_progress_dialog_set_sub(wind->load_progress, _("Loading completed"));
+    gnc_progress_dialog_set_value(wind->load_progress, 1);
 
-        scm_gc_unprotect_object(wind->imported_files);
-        wind->imported_files = imported_files;
-        scm_gc_protect_object(wind->imported_files);
+    scm_gc_unprotect_object(wind->imported_files);
+    wind->imported_files = imported_files;
+    scm_gc_protect_object(wind->imported_files);
 
-        gtk_widget_set_sensitive(wind->load_pause, FALSE);
-        wind->busy = FALSE;
+    gtk_widget_set_sensitive(wind->load_pause, FALSE);
+    wind->busy = FALSE;
+
+    if (wind->load_stop == FALSE)
+    {
 
         /* Auto step to next page */
         gtk_assistant_set_current_page (assistant, num + 1);
     }
+    else
+    {
+        wind->load_stop = FALSE;
+    }
 }
 
 
@@ -2934,58 +2946,59 @@ gnc_ui_qif_import_convert_progress_start_cb(GtkButton * button,
         wind->busy = FALSE;
         wind->load_stop = TRUE;
     }
+    if (wind->load_stop == FALSE)
+    {
+        /* Save the imported account tree. */
+        scm_gc_unprotect_object(wind->imported_account_tree);
+        wind->imported_account_tree = retval;
+        scm_gc_protect_object(wind->imported_account_tree);
 
-    /* Save the imported account tree. */
-    scm_gc_unprotect_object(wind->imported_account_tree);
-    wind->imported_account_tree = retval;
-    scm_gc_protect_object(wind->imported_account_tree);
-
-    /*
-     * Detect potentially duplicated transactions.
-     */
+        /*
+         * Detect potentially duplicated transactions.
+         */
 
-    /* This step will fill the remainder of the bar. */
-    gnc_progress_dialog_push(wind->convert_progress, 1);
-    retval = scm_call_3(find_duplicates,
-                        scm_c_eval_string("(gnc-get-current-root-account)"),
-                        wind->imported_account_tree, progress);
-    gnc_progress_dialog_pop(wind->convert_progress);
+        /* This step will fill the remainder of the bar. */
+        gnc_progress_dialog_push(wind->convert_progress, 1);
+        retval = scm_call_3(find_duplicates,
+                            scm_c_eval_string("(gnc-get-current-root-account)"),
+                            wind->imported_account_tree, progress);
+        gnc_progress_dialog_pop(wind->convert_progress);
 
-    /* Save the results. */
-    scm_gc_unprotect_object(wind->match_transactions);
-    wind->match_transactions = retval;
-    scm_gc_protect_object(wind->match_transactions);
+        /* Save the results. */
+        scm_gc_unprotect_object(wind->match_transactions);
+        wind->match_transactions = retval;
+        scm_gc_protect_object(wind->match_transactions);
 
-    if (retval == SCM_BOOL_T)
-    {
-        /* Canceled by the user. */
-        gtk_widget_set_sensitive(wind->convert_pause, FALSE);
-        gnc_progress_dialog_set_sub(wind->convert_progress, _("Canceling"));
-        wind->busy = FALSE;
-        wind->load_stop = TRUE;
-    }
-    else if (retval == SCM_BOOL_F)
-    {
-        /* An error occurred during duplicate checking. */
+        if (retval == SCM_BOOL_T)
+        {
+            /* Canceled by the user. */
+            gtk_widget_set_sensitive(wind->convert_pause, FALSE);
+            gnc_progress_dialog_set_sub(wind->convert_progress, _("Canceling"));
+            wind->busy = FALSE;
+            wind->load_stop = TRUE;
+        }
+        else if (retval == SCM_BOOL_F)
+        {
+            /* An error occurred during duplicate checking. */
 
-        /* Remove any converted data. */
-        gnc_progress_dialog_set_sub(wind->convert_progress, _("Cleaning up"));
-        gnc_ui_qif_import_convert_undo(wind);
+            /* Remove any converted data. */
+            gnc_progress_dialog_set_sub(wind->convert_progress, _("Cleaning up"));
+            gnc_ui_qif_import_convert_undo(wind);
 
-        /* Inform the user. */
-        gnc_progress_dialog_append_log(wind->convert_progress,
-                                       _( "A bug was detected while detecting duplicates."));
-        gnc_progress_dialog_set_sub(wind->convert_progress, _("Failed"));
-        gnc_progress_dialog_reset_value(wind->convert_progress);
-        gnc_error_dialog (GTK_WINDOW (assistant), "%s",
-                          _( "A bug was detected while detecting duplicates."));
-        /* FIXME: How should we request that the user report this problem? */
+            /* Inform the user. */
+            gnc_progress_dialog_append_log(wind->convert_progress,
+                                           _( "A bug was detected while detecting duplicates."));
+            gnc_progress_dialog_set_sub(wind->convert_progress, _("Failed"));
+            gnc_progress_dialog_reset_value(wind->convert_progress);
+            gnc_error_dialog (GTK_WINDOW (assistant), "%s",
+                              _( "A bug was detected while detecting duplicates."));
+            /* FIXME: How should we request that the user report this problem? */
 
-        gtk_widget_set_sensitive(wind->convert_pause, FALSE);
-        wind->busy = FALSE;
-        wind->load_stop = TRUE;
+            gtk_widget_set_sensitive(wind->convert_pause, FALSE);
+            wind->busy = FALSE;
+            wind->load_stop = TRUE;
+        }
     }
-
     /* Enable the Assistant Forward Button */
     gtk_assistant_set_page_complete (assistant, page, TRUE);
 
diff --git a/gnucash/import-export/qif-imp/qif-to-gnc.scm b/gnucash/import-export/qif-imp/qif-to-gnc.scm
index 5f7e3f1..f5d0794 100644
--- a/gnucash/import-export/qif-imp/qif-to-gnc.scm
+++ b/gnucash/import-export/qif-imp/qif-to-gnc.scm
@@ -255,7 +255,11 @@
                                              (length (qif-file:xtns b))))))
            (work-to-do 0)
            (work-done 0))
-
+      ;; Log any errors
+      (define (errorproc message)
+        (if (string? message)
+              (qif-import:log progress-dialog "qif-import:qif-to-gnc"
+                            message)))
       ;; This procedure handles progress reporting, pause, and cancel.
       (define (update-progress)
         (set! work-done (+ 1 work-done))
@@ -379,7 +383,7 @@
             (update-progress)
 
             (if (not (qif-xtn:mark xtn))
-                (qif-import:mark-matching-xtns xtn rest))
+                (qif-import:mark-matching-xtns xtn rest errorproc))
             (if (not (null? (cdr rest)))
                 (xloop (car rest) (cdr rest)))))
 
@@ -431,7 +435,7 @@
     (lambda ()
       (catch 'cancel
              (lambda ()
-               (catch 'bad-date private-convert (lambda (key . args) key)))
+               (catch #t private-convert (lambda (key . args) key)))
              (lambda (key . args) #t)))))
 
 
@@ -810,7 +814,7 @@
 ;;  mark them so they won't be imported.
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(define (qif-import:mark-matching-xtns xtn candidate-xtns)
+(define (qif-import:mark-matching-xtns xtn candidate-xtns errorproc)
   (let splitloop ((splits-left (qif-xtn:splits xtn)))
 
     ;; splits-left starts out as all the splits of this transaction.
@@ -822,7 +826,7 @@
                  (qif-split:category-is-account? (car splits-left)))
             (set! splits-left
                   (qif-import:mark-some-splits
-                   splits-left xtn candidate-xtns))
+                   splits-left xtn candidate-xtns errorproc))
             (set! splits-left (cdr splits-left))))
 
     (if (not (null? splits-left))
@@ -835,7 +839,7 @@
 ;; don't get imported.
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(define (qif-import:mark-some-splits splits xtn candidate-xtns)
+(define (qif-import:mark-some-splits splits xtn candidate-xtns errorproc)
   (let* ((n- (lambda (n) (gnc-numeric-neg n)))
          (nsub (lambda (a b) (gnc-numeric-sub a b 0 GNC-DENOM-LCD)))
          (n+ (lambda (a b) (gnc-numeric-add a b 0 GNC-DENOM-LCD)))
@@ -910,17 +914,20 @@
     ;; this is the grind loop.  Go over every unmarked transaction in
     ;; the candidate-xtns list.
     (let xtn-loop ((xtns candidate-xtns))
-      (if (and (not (qif-xtn:mark (car xtns)))
-               (string=? (qif-xtn:from-acct (car xtns)) far-acct-name))
-          (begin
-            (set! how
-                  (qif-import:xtn-has-matches? (car xtns) near-acct-name
-                                               date amount group-amount))
-            (if how
-                (begin
-                  (qif-import:merge-and-mark-xtns xtn same-acct-splits
-                                                  (car xtns) how)
-                  (set! done #t)))))
+      (if (not (and far-acct-name near-acct-name))
+          (if errorproc
+              (errorproc "Transaction with no or only one associated account."))
+          (if (and (not (qif-xtn:mark (car xtns))))
+              (string=? (qif-xtn:from-acct (car xtns)) far-acct-name)
+              (begin
+                (set! how
+                      (qif-import:xtn-has-matches? (car xtns) near-acct-name
+                                                   date amount group-amount))
+                (if how
+                    (begin
+                      (qif-import:merge-and-mark-xtns xtn same-acct-splits
+                                                      (car xtns) how)
+                      (set! done #t))))))
       ;; iterate with the next transaction
       (if (and (not done)
                (not (null? (cdr xtns))))

commit 1f8f681732bd52fb7d858b9eeb3269c973565712
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Jun 15 11:32:37 2018 -0700

    Remove abandoned C-Language QIF implementation.

diff --git a/gnucash/import-export/CMakeLists.txt b/gnucash/import-export/CMakeLists.txt
index 3b52ed8..cca8359 100644
--- a/gnucash/import-export/CMakeLists.txt
+++ b/gnucash/import-export/CMakeLists.txt
@@ -10,7 +10,6 @@ add_subdirectory(csv-imp)
 add_subdirectory(customer-import)
 add_subdirectory(log-replay)
 add_subdirectory(ofx)
-add_subdirectory(qif)
 add_subdirectory(qif-imp)
 
 
diff --git a/gnucash/import-export/qif/CMakeLists.txt b/gnucash/import-export/qif/CMakeLists.txt
deleted file mode 100644
index 608e14c..0000000
--- a/gnucash/import-export/qif/CMakeLists.txt
+++ /dev/null
@@ -1,43 +0,0 @@
-
-#Tests for this directory are not run.
-add_subdirectory(test)
-
-set(qif_SOURCES
-  qif-context.c
-  qif-defaults.c
-  qif-file.c
-  qif-objects.c
-  qif-parse.c
-)
-
-# Add dependency on config.h
-set_source_files_properties (${qif_SOURCES} PROPERTIES OBJECT_DEPENDS ${CONFIG_H})
-
-set(qif_noinst_HEADERS
-  qif-file.h
-  qif-defaults.h
-  qif-import-p.h
-  qif-import.h
-  qif-objects.h
-  qif-objects-p.h
-  qif-parse.h
-)
-
-add_library(gncmod-qif ${qif_noinst_HEADERS} ${qif_SOURCES})
-
-target_link_libraries(gncmod-qif gncmod-generic-import gncmod-engine ${GLIB2_LDFLAGS})
-
-target_compile_definitions(gncmod-qif PRIVATE -DG_LOG_DOMAIN=\"gnc.import.qif\")
-
-if (APPLE)
-  set_target_properties (gncmod-qif PROPERTIES INSTALL_NAME_DIR "${CMAKE_INSTALL_FULL_LIBDIR}/gnucash")
-endif()
-
-install(TARGETS gncmod-qif
-  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/gnucash
-  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/gnucash
-  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
-# No headers to install.
-
-set_local_dist(qif_DIST_local CMakeLists.txt ${qif_SOURCES} ${qif_noinst_HEADERS})
-set(qif_DIST ${qif_DIST_local} ${test_qif_DIST} PARENT_SCOPE)
diff --git a/gnucash/import-export/qif/qif-context.c b/gnucash/import-export/qif/qif-context.c
deleted file mode 100644
index 17fa472..0000000
--- a/gnucash/import-export/qif/qif-context.c
+++ /dev/null
@@ -1,417 +0,0 @@
-/*
- * qif-context.c -- create/destroy QIF Contexts
- *
- * Written By:	Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <glib.h>
-
-#include "qif-import-p.h"
-#include "qif-objects-p.h"
-
-static void qif_object_map_get_helper(gpointer key, gpointer value, gpointer listp);
-
-QifContext
-qif_context_new(void)
-{
-    QifContext ctx = g_new0(struct _QifContext, 1);
-
-    ctx->object_lists = g_hash_table_new(g_str_hash, g_str_equal);
-    ctx->object_maps = g_hash_table_new(g_str_hash, g_str_equal);
-
-    return ctx;
-}
-
-void
-qif_context_destroy(QifContext ctx)
-{
-    GList *node, *temp;
-    QifContext fctx;
-
-    if (!ctx) return;
-
-    /* First, try to destroy all the children contexts */
-    for (node = ctx->files; node; node = temp)
-    {
-        fctx = node->data;
-        temp = node->next;
-        qif_context_destroy(fctx);
-    }
-
-    /* ok, at this point we're actually destroying this context. */
-
-    /* force the end of record */
-    if (ctx->handler && ctx->handler->end)
-        ctx->handler->end(ctx);
-
-    /* destroy the state objects */
-    qif_object_list_destroy(ctx);
-    qif_object_map_destroy(ctx);
-
-    /* Remove us from our parent context */
-    if (ctx->parent)
-        ctx->parent->files = g_list_remove(ctx->parent->files, ctx);
-
-    g_free(ctx->filename);
-
-    g_assert(ctx->files == NULL);
-    g_free(ctx);
-}
-
-static GList *
-qif_context_get_foo_helper(QifContext ctx, GFunc get_helper)
-{
-    GHashTable *ht;
-    GList *node, *list = NULL;
-    QifContext fctx;
-
-    g_return_val_if_fail(ctx, NULL);
-    g_return_val_if_fail(ctx->parsed, NULL);
-    g_return_val_if_fail(get_helper, NULL);
-
-    ht = g_hash_table_new(g_direct_hash, g_direct_equal);
-
-    for (node = ctx->files; node; node = node->next)
-    {
-        fctx = node->data;
-        qif_object_list_foreach(fctx, QIF_O_TXN, get_helper, ht);
-    }
-
-    g_hash_table_foreach(ht, qif_object_map_get_helper, &list);
-    g_hash_table_destroy(ht);
-
-    return list;
-}
-
-static void
-qif_get_accts_helper(gpointer obj, gpointer htp)
-{
-    QifTxn txn = obj;
-    QifSplit split;
-    GHashTable *ht = htp;
-    GList *node;
-
-    if (txn->from_acct)
-        g_hash_table_insert(ht, txn->from_acct, txn->from_acct);
-
-    /* The default_split is using the from_acct, so we can ignore it */
-
-    for (node = txn->splits; node; node = node->next)
-    {
-        split = node->data;
-        if (split->cat.obj && split->cat_is_acct)
-            g_hash_table_insert(ht, split->cat.acct, split->cat.acct);
-    }
-}
-
-GList *
-qif_context_get_accounts(QifContext ctx)
-{
-    return qif_context_get_foo_helper(ctx, qif_get_accts_helper);
-}
-
-static void
-qif_get_cats_helper(gpointer obj, gpointer htp)
-{
-    QifTxn txn = obj;
-    QifSplit split;
-    GHashTable *ht = htp;
-    GList *node;
-
-    /* default_split uses from_acct, so no categories */
-
-    for (node = txn->splits; node; node = node->next)
-    {
-        split = node->data;
-        if (split->cat.obj && !split->cat_is_acct)
-            g_hash_table_insert(ht, split->cat.cat, split->cat.cat);
-    }
-}
-
-GList *
-qif_context_get_categories(QifContext ctx)
-{
-    return qif_context_get_foo_helper(ctx, qif_get_cats_helper);
-}
-
-/*****************************************************************************/
-
-/*
- * Insert and remove a QifObject from the Object Maps in this Qif Context
- */
-
-gint
-qif_object_map_count(QifContext ctx, const char *type)
-{
-    GHashTable *ht;
-
-    g_return_val_if_fail(ctx, 0);
-    g_return_val_if_fail(ctx->object_maps, 0);
-    g_return_val_if_fail(type, 0);
-
-    ht = g_hash_table_lookup(ctx->object_maps, type);
-    if (!ht)
-        return 0;
-
-    return g_hash_table_size(ht);
-}
-
-void
-qif_object_map_foreach(QifContext ctx, const char *type, GHFunc func, gpointer arg)
-{
-    GHashTable *ht;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(ctx->object_maps);
-    g_return_if_fail(type);
-
-    ht = g_hash_table_lookup(ctx->object_maps, type);
-    if (ht)
-        g_hash_table_foreach(ht, func, arg);
-}
-
-void
-qif_object_map_insert(QifContext ctx, const char *key, QifObject obj)
-{
-    GHashTable *ht;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(ctx->object_maps);
-    g_return_if_fail(key);
-    g_return_if_fail(obj);
-    g_return_if_fail(obj->type);
-
-    ht = g_hash_table_lookup(ctx->object_maps, obj->type);
-    if (!ht)
-    {
-        ht = g_hash_table_new(g_str_hash, g_str_equal);
-        g_assert(ht);
-        g_hash_table_insert(ctx->object_maps, (gpointer)obj->type, ht);
-    }
-
-    g_hash_table_insert(ht, (gpointer)key, obj);
-}
-
-void
-qif_object_map_remove(QifContext ctx, const char *type, const char *key)
-{
-    GHashTable *ht;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(ctx->object_maps);
-    g_return_if_fail(type);
-    g_return_if_fail(key);
-
-    ht = g_hash_table_lookup(ctx->object_maps, type);
-    if (!ht) return;
-
-    g_hash_table_remove(ht, key);
-}
-
-QifObject
-qif_object_map_lookup(QifContext ctx, const char *type, const char *key)
-{
-    GHashTable *ht;
-
-    g_return_val_if_fail(ctx, NULL);
-    g_return_val_if_fail(ctx->object_maps, NULL);
-    g_return_val_if_fail(type, NULL);
-    g_return_val_if_fail(key, NULL);
-
-    ht = g_hash_table_lookup(ctx->object_maps, type);
-    if (!ht) return NULL;
-
-    return g_hash_table_lookup(ht, key);
-}
-
-/* This GList _SHOULD_ be freed by the caller */
-
-static void
-qif_object_map_get_helper(gpointer key, gpointer value, gpointer arg)
-{
-    GList **listp = arg;
-    g_return_if_fail(listp);
-
-    *listp = g_list_prepend(*listp, value);
-}
-
-GList *
-qif_object_map_get(QifContext ctx, const char *type)
-{
-    GHashTable *ht;
-    GList *list = NULL;
-
-    g_return_val_if_fail(ctx, NULL);
-    g_return_val_if_fail(ctx->object_maps, NULL);
-    g_return_val_if_fail(type, NULL);
-
-    ht = g_hash_table_lookup(ctx->object_maps, type);
-    if (!ht)
-        return NULL;
-
-    g_hash_table_foreach(ht, qif_object_map_get_helper, &list);
-
-    return list;
-}
-
-static gboolean
-qif_object_map_remove_each(gpointer key, gpointer value, gpointer arg)
-{
-    QifObject obj = value;
-    obj->destroy(obj);
-    return TRUE;
-}
-
-static gboolean
-qif_object_map_remove_all(gpointer key, gpointer value, gpointer arg)
-{
-    GHashTable *ht = value;
-
-    g_hash_table_foreach_remove(ht, qif_object_map_remove_each, NULL);
-    g_hash_table_destroy(ht);
-    return TRUE;
-}
-
-void qif_object_map_destroy(QifContext ctx)
-{
-    g_return_if_fail(ctx);
-    g_return_if_fail(ctx->object_maps);
-
-    g_hash_table_foreach_remove(ctx->object_maps, qif_object_map_remove_all, NULL);
-    g_hash_table_destroy(ctx->object_maps);
-}
-
-/*****************************************************************************/
-
-/*
- * Insert and remove a QifObject from the Object Lists in this Qif Context
- */
-
-void
-qif_object_list_reverse(QifContext ctx, const char *type)
-{
-    GList *list;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(ctx->object_lists);
-    g_return_if_fail(type);
-
-    list = qif_object_list_get(ctx, type);
-    list = g_list_reverse(list);
-    g_hash_table_insert(ctx->object_lists, (gpointer)type, list);
-}
-
-gint
-qif_object_list_count(QifContext ctx, const char *type)
-{
-    GList *list;
-
-    g_return_val_if_fail(ctx, 0);
-    g_return_val_if_fail(ctx->object_lists, 0);
-    g_return_val_if_fail(type, 0);
-
-    list = g_hash_table_lookup(ctx->object_lists, type);
-    return g_list_length(list);
-}
-
-void
-qif_object_list_foreach(QifContext ctx, const char *type, GFunc func, gpointer arg)
-{
-    GList *list;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(ctx->object_lists);
-    g_return_if_fail(type);
-
-    list = qif_object_list_get(ctx, type);
-    g_list_foreach(list, func, arg);
-}
-
-void
-qif_object_list_insert(QifContext ctx, QifObject obj)
-{
-    GList *list;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(ctx->object_lists);
-    g_return_if_fail(obj);
-    g_return_if_fail(obj->type && *obj->type);
-
-    list = g_hash_table_lookup(ctx->object_lists, obj->type);
-    list = g_list_prepend(list, obj);
-    g_hash_table_insert(ctx->object_lists, (gpointer)obj->type, list);
-}
-
-void
-qif_object_list_remove(QifContext ctx, QifObject obj)
-{
-    GList *list;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(ctx->object_lists);
-    g_return_if_fail(obj);
-    g_return_if_fail(obj->type && *obj->type);
-
-    list = g_hash_table_lookup(ctx->object_lists, obj->type);
-    list = g_list_remove(list, obj);
-    g_hash_table_insert(ctx->object_lists, (gpointer)obj->type, list);
-}
-
-GList *
-qif_object_list_get(QifContext ctx, const char *type)
-{
-    g_return_val_if_fail(ctx, NULL);
-    g_return_val_if_fail(ctx->object_lists, NULL);
-    g_return_val_if_fail(type, NULL);
-
-    return g_hash_table_lookup(ctx->object_lists, type);
-}
-
-static gboolean
-qif_object_list_remove_all(gpointer key, gpointer value, gpointer arg)
-{
-    GList *list = value;
-    GList *node;
-    QifObject obj;
-
-    for (node = list; node; node = node->next)
-    {
-        obj = node->data;
-        obj->destroy(obj);
-    }
-
-    g_list_free(list);
-    return TRUE;
-}
-
-void
-qif_object_list_destroy(QifContext ctx)
-{
-    g_return_if_fail(ctx);
-    g_return_if_fail(ctx->object_lists);
-
-    g_hash_table_foreach_remove(ctx->object_lists, qif_object_list_remove_all, NULL);
-    g_hash_table_destroy(ctx->object_lists);
-}
diff --git a/gnucash/import-export/qif/qif-defaults.c b/gnucash/import-export/qif/qif-defaults.c
deleted file mode 100644
index e29c141..0000000
--- a/gnucash/import-export/qif/qif-defaults.c
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * qif-defaults.c -- QIF Defaults -- default accounts...
- *
- * Created by:	Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <glib.h>
-#include <glib/gi18n.h>
-
-#include "gnc-helpers.h"
-#include "qif-import-p.h"
-#include "qif-objects-p.h"
-#include "qif-defaults.h"
-
-
-static GList *stock_list = NULL;
-static GList *ext_stock_list = NULL;
-static GList *income_list = NULL;
-static GList *expense_list = NULL;
-static GList *equity_list = NULL;
-
-#define RETURN_ACCT(c,n,l) { if (stock_list == NULL) acct_type_init(); \
-	return find_or_make_acct(c, n, l); \
-}
-
-static void
-acct_type_init(void)
-{
-    stock_list = qif_parse_acct_type("__stock__", -1);
-    ext_stock_list = qif_parse_acct_type("__extstock__", -1);
-    income_list = qif_parse_acct_type("__income__", -1);
-    expense_list = qif_parse_acct_type("__expense__", -1);
-    equity_list = qif_parse_acct_type("__equity__", -1);
-}
-
-QifAccount qif_default_equity_acct(QifContext ctx)
-{
-    char *name = g_strdup(_("Retained Earnings"));
-    RETURN_ACCT(ctx, name, equity_list);
-}
-
-QifAccount qif_default_margin_interest_acct(QifContext ctx)
-{
-    char *name = g_strdup_printf("%s%s%s", _("Margin Interest"),
-                                 gnc_get_account_separator_string(),
-                                 ctx->current_acct->name);
-    RETURN_ACCT(ctx, name, expense_list);
-}
-
-QifAccount qif_default_commission_acct(QifContext ctx)
-{
-    char *name = g_strdup_printf("%s%s%s", _("Commissions"),
-                                 gnc_get_account_separator_string(),
-                                 ctx->current_acct->name);
-    RETURN_ACCT(ctx, name, expense_list);
-}
-
-QifAccount qif_default_stock_acct(QifContext ctx, const char *security)
-{
-    char *name = g_strdup_printf("%s%s%s", ctx->current_acct->name,
-                                 gnc_get_account_separator_string(),
-                                 security);
-    RETURN_ACCT(ctx, name, stock_list);
-}
-
-QifAccount qif_default_cglong_acct(QifContext ctx, const char *security)
-{
-    char *name = g_strdup_printf("%s%s%s%s%s", _("Cap. gain (long)"),
-                                 gnc_get_account_separator_string(),
-                                 ctx->current_acct->name,
-                                 gnc_get_account_separator_string(),
-                                 security);
-    RETURN_ACCT(ctx, name, income_list);
-}
-
-QifAccount qif_default_cgmid_acct(QifContext ctx, const char *security)
-{
-    char *name = g_strdup_printf("%s%s%s%s%s", _("Cap. gain (mid)"),
-                                 gnc_get_account_separator_string(),
-                                 ctx->current_acct->name,
-                                 gnc_get_account_separator_string(),
-                                 security);
-    RETURN_ACCT(ctx, name, income_list);
-}
-
-QifAccount qif_default_cgshort_acct(QifContext ctx, const char *security)
-{
-    char *name = g_strdup_printf("%s%s%s%s%s", _("Cap. gain (short)"),
-                                 gnc_get_account_separator_string(),
-                                 ctx->current_acct->name,
-                                 gnc_get_account_separator_string(),
-                                 security);
-    RETURN_ACCT(ctx, name, income_list);
-}
-
-QifAccount qif_default_dividend_acct(QifContext ctx, const char *security)
-{
-    char *name = g_strdup_printf("%s%s%s%s%s", _("Dividends"),
-                                 gnc_get_account_separator_string(),
-                                 ctx->current_acct->name,
-                                 gnc_get_account_separator_string(),
-                                 security);
-    RETURN_ACCT(ctx, name, income_list);
-}
-
-QifAccount qif_default_interest_acct(QifContext ctx, const char *security)
-{
-    char *name = g_strdup_printf("%s%s%s%s%s", _("Interest"),
-                                 gnc_get_account_separator_string(),
-                                 ctx->current_acct->name,
-                                 gnc_get_account_separator_string(),
-                                 security);
-    RETURN_ACCT(ctx, name, income_list);
-}
-
-QifAccount qif_default_capital_return_acct(QifContext ctx, const char *security)
-{
-    char *name = g_strdup_printf("%s%s%s%s%s", _("Cap Return"),
-                                 gnc_get_account_separator_string(),
-                                 ctx->current_acct->name,
-                                 gnc_get_account_separator_string(),
-                                 security);
-    RETURN_ACCT(ctx, name, income_list);
-}
-
-QifAccount qif_default_equity_holding(QifContext ctx, const char *security)
-{
-    return qif_default_equity_acct(ctx);
-}
-
diff --git a/gnucash/import-export/qif/qif-defaults.h b/gnucash/import-export/qif/qif-defaults.h
deleted file mode 100644
index 30c6562..0000000
--- a/gnucash/import-export/qif/qif-defaults.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * qif-defaults.h -- QIF Defaults -- default accounts...
- *
- * Created by:	Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifndef QIF_DEFAULTS_H
-#define QIF_DEFAULTS_H
-
-#include "qif-objects.h"
-#include "qif-import.h"
-
-QifAccount qif_default_equity_acct(QifContext ctx);
-QifAccount qif_default_equity_holding(QifContext ctx, const char *security);
-
-QifAccount qif_default_margin_interest_acct(QifContext ctx);
-QifAccount qif_default_commission_acct(QifContext ctx);
-QifAccount qif_default_stock_acct(QifContext ctx, const char *security);
-QifAccount qif_default_cglong_acct(QifContext ctx, const char *security);
-QifAccount qif_default_cgmid_acct(QifContext ctx, const char *security);
-QifAccount qif_default_cgshort_acct(QifContext ctx, const char *security);
-QifAccount qif_default_dividend_acct(QifContext ctx, const char *security);
-QifAccount qif_default_interest_acct(QifContext ctx, const char *security);
-QifAccount qif_default_capital_return_acct(QifContext ctx, const char *security);
-
-#endif /* QIF_DEFAULTS_H */
diff --git a/gnucash/import-export/qif/qif-file.c b/gnucash/import-export/qif/qif-file.c
deleted file mode 100644
index 775f0da..0000000
--- a/gnucash/import-export/qif/qif-file.c
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * qif-file.c -- parse a QIF File into its pieces
- *
- * Written by:  Derek Atkins  <derek@@ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <glib.h>
-#include <glib/gstdio.h>
-#include <string.h>
-
-#include "gnc-engine.h"
-
-#include "qif-import-p.h"
-#include "qif-objects-p.h"
-
-static QofLogModule log_module = GNC_MOD_IMPORT;
-
-
-static QifLine
-qif_make_line(const char* buf, gint lineno)
-{
-    QifLine line;
-    g_return_val_if_fail(buf && *buf, NULL);
-
-    line = g_new0(struct _QifLine, 1);
-    line->type = *buf;
-    line->lineno = lineno;
-    line->line = g_strdup(buf + 1);
-
-    return line;
-}
-
-void
-qif_record_destroy(GList *record)
-{
-    GList *node;
-    QifLine line;
-
-    for (node = record; node; node = node->next)
-    {
-        line = node->data;
-        g_free(line->line);
-        g_free(line);
-    }
-
-    g_list_free(record);
-}
-
-/* This returns a record, which is a bunch of QifLines, ending
- * with a line with just a '^'.  If it finds a line that begins
- * with a !, then destroy the current record state, set the "found_bangtype",
- * and return NULL.
- */
-static GList *
-qif_make_record(QifContext ctx, char *buf, size_t bufsiz, gboolean *found_bangtype)
-{
-    GList *record = NULL;
-    QifLine line;
-
-    g_return_val_if_fail(ctx, NULL);
-    g_return_val_if_fail(buf, NULL);
-    g_return_val_if_fail(found_bangtype, NULL);
-
-    *found_bangtype = FALSE;
-
-    while (fgets(buf, bufsiz, ctx->fp) != NULL)
-    {
-
-        /* increment the line number */
-        ctx->lineno++;
-
-        /* strip start/end whitespace */
-        g_strstrip(buf);
-
-        /* if there is nothing left in the string, ignore it */
-        if (strlen(buf) == 0)
-            continue;
-
-        /* If this is a bangline, then set the flag, clear our state, and return NULL */
-        if (*buf == '!')
-        {
-            *found_bangtype = TRUE;
-            break;
-        }
-
-        /* See if this is an End of Record marker */
-        if (*buf == '^')
-        {
-            /* Yep.  If we've got a record then break and return ... */
-            if (record)
-                break;
-            /* ... otherwise just continue reading (i.e. ignore empty records) */
-            else
-                continue;
-        }
-
-        /* otherwise, add the line to the list */
-        line = qif_make_line(buf, ctx->lineno);
-        if (line)
-            record = g_list_prepend(record, line);
-
-        /* and continue... */
-    }
-
-    /* If we found a bangtype, destroy anything we've collected */
-    if (*found_bangtype)
-    {
-        if (record)
-            PERR("error loading file: incomplete record at line %d", ctx->lineno);
-
-        qif_record_destroy(record);
-        record = NULL;
-    }
-
-    return g_list_reverse(record);
-}
-
-/* read a qif file and parse it, line by line
- * return QIF_E_OK on success or some other QIF Error.
- */
-static QifError
-qif_read_file(QifContext ctx, FILE *f)
-{
-    char buf[BUFSIZ];
-    GList *record;
-    gboolean found_bang;
-    QifError err = QIF_E_OK;
-
-    g_return_val_if_fail(ctx, QIF_E_BADARGS);
-    g_return_val_if_fail(f, QIF_E_BADARGS);
-
-    ctx->fp = f;
-    ctx->lineno = -1;
-
-    do
-    {
-        found_bang = FALSE;
-        record = qif_make_record(ctx, buf, sizeof(buf), &found_bang);
-
-        /* If we got a record, process it */
-        if (record)
-        {
-            if (!ctx->handler || !ctx->handler->parse_record)
-            {
-                PERR("Trying to process QIF record without a handler at %d", ctx->lineno);
-            }
-            else
-            {
-                err = ctx->handler->parse_record(ctx, record);
-            }
-
-            /* Now destroy it; we don't need it anymore */
-            qif_record_destroy(record);
-        }
-
-        /* if we found a bangtype, process that */
-        if (found_bang)
-        {
-            g_assert(*buf == '!');
-
-            /* First, process the end of the last handler.  This could possibly
-             * merge items into the context or perform some other operation
-             */
-            if (ctx->handler && ctx->handler->end)
-            {
-                err = ctx->handler->end(ctx);
-                if (err != QIF_E_OK)
-                    break;
-            }
-
-            /* Now process the bangtype (stored in buf) to set the new handler */
-            qif_parse_bangtype(ctx, buf);
-        }
-
-    }
-    while ((record || found_bang) && err == QIF_E_OK);
-
-    /* Make sure to run any end processor */
-    if (err == QIF_E_OK && ctx->handler && ctx->handler->end)
-        err = ctx->handler->end(ctx);
-
-    if (err == QIF_E_OK)
-        qif_object_list_reverse(ctx, QIF_O_TXN);
-
-    return err;
-}
-
-static QifError
-qif_import_file(QifContext ctx, const char *filename)
-{
-    QifError err;
-    FILE *fp;
-
-    g_return_val_if_fail(ctx, QIF_E_BADARGS);
-    g_return_val_if_fail(filename, QIF_E_BADARGS);
-    g_return_val_if_fail(*filename, QIF_E_BADARGS);
-
-    /* Open the file */
-    fp = g_fopen(filename, "r");
-    if (fp == NULL)
-        return QIF_E_NOFILE;
-
-    ctx->filename = g_strdup(filename);
-
-    /* read the file */
-    err = qif_read_file(ctx, fp);
-
-    /* close the file */
-    fclose(fp);
-
-    return err;
-}
-
-
-QifContext
-qif_file_new(QifContext ctx, const char *filename)
-{
-    QifContext fctx;
-
-    g_return_val_if_fail(ctx, NULL);
-    g_return_val_if_fail(filename, NULL);
-
-    fctx = qif_context_new();
-
-    /* we should assume that we've got a bank account... just in case.. */
-    qif_parse_bangtype(fctx, "!type:bank");
-
-    /* Open the file */
-    if (qif_import_file(fctx, filename) != QIF_E_OK)
-    {
-        qif_context_destroy(fctx);
-        fctx = NULL;
-    }
-
-    /* Return the new context */
-    if (fctx)
-    {
-        ctx->files = g_list_prepend(ctx->files, fctx);
-        fctx->parent = ctx;
-
-        /* Make sure the file gets merged into the parent */
-        ctx->parsed = FALSE;
-    }
-
-    return fctx;
-}
-
-QifError
-qif_file_parse(QifContext ctx, gpointer ui_args)
-{
-    g_return_val_if_fail(ctx, QIF_E_BADARGS);
-    g_return_val_if_fail(!qif_file_needs_account(ctx), QIF_E_BADSTATE);
-
-    qif_parse_all(ctx, ui_args);
-    ctx->parsed = TRUE;
-
-    return QIF_E_OK;
-}
-
-gboolean
-qif_file_needs_account(QifContext ctx)
-{
-    g_return_val_if_fail(ctx, FALSE);
-
-    return ((ctx->parse_flags & QIF_F_TXN_NEEDS_ACCT) ||
-            (ctx->parse_flags & QIF_F_ITXN_NEEDS_ACCT));
-}
-
-const char *
-qif_file_filename(QifContext ctx)
-{
-    g_return_val_if_fail(ctx, NULL);
-    return ctx->filename;
-}
-
-static void
-set_txn_acct(gpointer obj, gpointer arg)
-{
-    QifTxn txn = obj;
-    QifAccount acct = arg;
-
-    if (!txn->from_acct)
-        txn->from_acct = acct;
-}
-
-void
-qif_file_set_default_account(QifContext ctx, const char *acct_name)
-{
-    QifAccount acct;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(acct_name);
-
-    if (! qif_file_needs_account(ctx)) return;
-
-    acct = find_or_make_acct(ctx, g_strdup(acct_name),
-                             qif_parse_acct_type_guess(ctx->parse_type));
-
-    qif_object_list_foreach(ctx, QIF_O_TXN, set_txn_acct, acct);
-
-    qif_clear_flag(ctx->parse_flags, QIF_F_TXN_NEEDS_ACCT);
-    qif_clear_flag(ctx->parse_flags, QIF_F_ITXN_NEEDS_ACCT);
-}
diff --git a/gnucash/import-export/qif/qif-file.h b/gnucash/import-export/qif/qif-file.h
deleted file mode 100644
index c4b85f1..0000000
--- a/gnucash/import-export/qif/qif-file.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* qif-import-p.h -- a QIF Importer module (private headers)
- *
- * Written By:	Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifndef QIF_FILE_H
-#define QIF_FILE_H
-
-struct _QifLine
-{
-    char		type;
-    gint		lineno;
-    char *	line;
-};
-
-void qif_record_destroy(GList *record);
-
-#endif /* QIF_FILE_H */
diff --git a/gnucash/import-export/qif/qif-import-p.h b/gnucash/import-export/qif/qif-import-p.h
deleted file mode 100644
index 96c6314..0000000
--- a/gnucash/import-export/qif/qif-import-p.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/* qif-import-p.h -- a QIF Importer module (private headers)
- *
- * Written By:	Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifndef QIF_IMPORT_P_H
-#define QIF_IMPORT_P_H
-
-#include "qif-import.h"
-#include "qif-objects.h"
-#include "qif-parse.h"
-#include "qif-file.h"
-
-#include <stdio.h>
-
-struct _QifHandler
-{
-    void		(*init)(QifContext ctx);
-    QifError	(*parse_record)(QifContext ctx, GList *record);
-    QifError	(*end)(QifContext ctx);
-};
-
-struct _QifContext
-{
-    /* The parent context */
-    QifContext	parent;
-
-    /* file information */
-    char *	filename;
-    FILE *	fp;
-    gint		lineno;
-
-    /* This describes what we are parsing right now */
-    QifType	parse_type;
-    QifHandler	handler;
-    gpointer	parse_state;
-
-    /* A bunch of flags for the current handler */
-    gint		parse_flags;
-    gboolean	parsed;
-
-    /* The current and "opening balance" account */
-    QifAccount	current_acct;
-    QifAccount	opening_bal_acct;
-
-    /* HashTable of Maps of data objects */
-    GHashTable *	object_maps;
-
-    /* HashTable of Lists of data objects */
-    GHashTable *	object_lists;
-
-    /* List of files */
-    GList *files;
-};
-
-/* Object Maps */
-gint qif_object_map_count(QifContext ctx, const char *type);
-void qif_object_map_foreach(QifContext ctx, const char *type,
-                            GHFunc func, gpointer arg);
-void qif_object_map_insert(QifContext ctx, const char *key, QifObject obj);
-void qif_object_map_remove(QifContext ctx, const char *type, const char *key);
-QifObject qif_object_map_lookup(QifContext ctx, const char *type, const char *key);
-void qif_object_map_destroy(QifContext ctx);
-/* GList _SHOULD_ be freed by the caller */
-GList * qif_object_map_get(QifContext ctx, const char *type);
-
-/* Object Lists */
-void qif_object_list_reverse(QifContext ctx, const char *type);
-gint qif_object_list_count(QifContext ctx, const char *type);
-void qif_object_list_foreach(QifContext ctx, const char *type,
-                             GFunc func, gpointer arg);
-void qif_object_list_insert(QifContext ctx, QifObject obj);
-void qif_object_list_remove(QifContext ctx, QifObject obj);
-void qif_object_list_destroy(QifContext ctx);
-/* GList should NOT be freed by the caller */
-GList *qif_object_list_get(QifContext ctx, const char *type);
-
-/* Set and clear flags in bit-flags */
-#define qif_set_flag(i,f) (i |= f)
-#define qif_clear_flag(i,f) (i &= ~f)
-
-#endif /* QIF_IMPORT_P_H */
diff --git a/gnucash/import-export/qif/qif-import.h b/gnucash/import-export/qif/qif-import.h
deleted file mode 100644
index 9602029..0000000
--- a/gnucash/import-export/qif/qif-import.h
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * qif-import.h -- a QIF Import module
- *
- * Written By:	Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifndef QIF_IMPORT_H
-#define QIF_IMPORT_H
-
-#include <stdio.h>
-#include "qof.h"
-
-typedef enum
-{
-    QIF_TYPE_BANK = 1,
-    QIF_TYPE_CASH,
-    QIF_TYPE_CCARD,
-    QIF_TYPE_INVST,
-    QIF_TYPE_PORT,
-    QIF_TYPE_OTH_A,
-    QIF_TYPE_OTH_L,
-    QIF_TYPE_CLASS,
-    QIF_TYPE_CAT,
-    QIF_TYPE_SECURITY,
-    QIF_ACCOUNT,
-    QIF_AUTOSWITCH,
-    QIF_CLEAR_AUTOSWITCH
-} QifType;
-
-/* Make sure this patches */
-#define QIF_TYPE_MAX QIF_CLEAR_AUTOSWITCH
-
-typedef struct _QifHandler *QifHandler;
-typedef struct _QifContext *QifContext;
-typedef struct _QifLine *QifLine;
-
-/* Qif Flags */
-#define QIF_F_IGNORE_ACCOUNTS	(1 << 0)
-#define QIF_F_TXN_NEEDS_ACCT	(1 << 1)
-#define QIF_F_ITXN_NEEDS_ACCT	(1 << 2)
-
-/* Qif Reconciled Flag */
-typedef enum
-{
-    QIF_R_NO = 0,
-    QIF_R_CLEARED,
-    QIF_R_RECONCILED,
-    QIF_R_BUDGETED,
-} QifRecnFlag;
-
-/* Qif Errors */
-
-typedef enum
-{
-    QIF_E_OK = 0,
-    QIF_E_INTERNAL,
-    QIF_E_BADSTATE,
-    QIF_E_BADARGS,
-    QIF_E_NOFILE,
-} QifError;
-
-
-/* Qif (investment?) Actions */
-typedef enum
-{
-    QIF_A_NONE = 0,
-    QIF_A_BUY,
-    QIF_A_BUYX,
-    QIF_A_CGLONG,
-    QIF_A_CGLONGX,
-    QIF_A_CGMID,
-    QIF_A_CGMIDX,
-    QIF_A_CGSHORT,
-    QIF_A_CGSHORTX,
-    QIF_A_DIV,
-    QIF_A_DIVX,
-    QIF_A_EXERCISE,
-    QIF_A_EXERCISEX,
-    QIF_A_EXPIRE,
-    QIF_A_GRANT,
-    QIF_A_INTINC,
-    QIF_A_INTINCX,
-    QIF_A_MARGINT,
-    QIF_A_MARGINTX,
-    QIF_A_MISCEXP,
-    QIF_A_MISCEXPX,
-    QIF_A_MISCINC,
-    QIF_A_MISCINCX,
-    QIF_A_REINVDIV,
-    QIF_A_REINVINT,
-    QIF_A_REINVLG,
-    QIF_A_REINVMD,
-    QIF_A_REINVSG,
-    QIF_A_REINVSH,
-    QIF_A_REMINDER,
-    QIF_A_RTRNCAP,
-    QIF_A_RTRNCAPX,
-    QIF_A_SELL,
-    QIF_A_SELLX,
-    QIF_A_SHRSIN,
-    QIF_A_SHRSOUT,
-    QIF_A_STKSPLIT,
-    QIF_A_VEST,
-    QIF_A_XIN,
-    QIF_A_XOUT,
-} QifAction;
-
-/* Public API Functions */
-
-/* Create a QIF Import Context */
-QifContext qif_context_new(void);
-void qif_context_destroy(QifContext ctx);
-
-/* Open and read a QIF File.  You must pass in the parent
- * context; it will return the child (file) context
- */
-QifContext qif_file_new(QifContext ctx, const char* filename);
-
-/* Does a qif-file need a default QIF account? */
-gboolean qif_file_needs_account(QifContext ctx);
-
-/* Return the filename of the QIF file */
-const char * qif_file_filename(QifContext ctx);
-
-/* Provide a default QIF Account for the QIF File */
-void qif_file_set_default_account(QifContext ctx, const char *acct_name);
-
-/* Parse the QIF File */
-QifError qif_file_parse(QifContext ctx, gpointer ui_arg);
-
-/* Merge all the qif-files from the children and into the context */
-void qif_parse_merge_files(QifContext ctx);
-
-/* Obtain the list of USED QifAccounts and QifCategories.  Finds all
- * references from the transactions in the QifContext.  The returned
- * GList must be freed by the caller.
- */
-GList *qif_context_get_accounts(QifContext ctx);
-GList *qif_context_get_categories(QifContext ctx);
-
-#endif /* QIF_IMPORT_H */
diff --git a/gnucash/import-export/qif/qif-objects-p.h b/gnucash/import-export/qif/qif-objects-p.h
deleted file mode 100644
index dbd3aa4..0000000
--- a/gnucash/import-export/qif/qif-objects-p.h
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * qif-objects-p.h -- Private header: QIF objects for the QIF importer
- *
- * Written By:	Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifndef QIF_OBJECTS_P_H
-#define QIF_OBJECTS_P_H
-
-#include "qof.h"
-
-#include "qif-import.h"
-#include "qif-objects.h"
-
-struct _QifAccount
-{
-    struct _QifObject obj;
-
-    char *	name;
-    char *	desc;
-
-    char *	limitstr;
-    gnc_numeric	limit;
-
-    char *	budgetstr;
-    gnc_numeric	budget;
-
-    GList *	type_list;
-
-};
-
-struct _QifCategory
-{
-    struct _QifObject obj;
-
-    char *	name;
-    char *	desc;
-    char *	taxclass;
-
-    gboolean	taxable;
-    gboolean	expense;
-    gboolean	income;
-
-    char *	budgetstr;
-    gnc_numeric	budget;
-
-};
-
-struct _QifClass
-{
-    struct _QifObject obj;
-
-    char *	name;
-    char *	desc;
-    char *	taxdesig;
-
-};
-
-struct _QifSecurity
-{
-    struct _QifObject obj;
-
-    char *	name;
-    char *	symbol;
-    char *	type;
-
-};
-
-struct _QifTxn
-{
-    struct _QifObject obj;
-
-    QifType	txn_type;
-
-    char *	datestr;
-    Timespec	date;
-
-    char *	payee;
-    char *	address;
-    char *	num;
-
-    QifRecnFlag	cleared;
-
-    /* Investment info */
-    QifInvstTxn	invst_info;
-
-    /* The default_split is the default (forward) part of the QIF transaction */
-    QifSplit	default_split;
-
-    /* The current_split (if any) defines the current "qif split" we are handling */
-    QifSplit	current_split;
-
-    /* The "from" account */
-    QifAccount	from_acct;
-
-    /* The list of splits for this txn */
-    GList *	splits;
-
-};
-
-struct _QifSplit
-{
-    char *	memo;
-
-    char *	amountstr;
-    gnc_numeric	amount;
-    gnc_numeric	value;
-
-    char *	catstr;
-
-    /* parsed category/account info */
-
-    union
-    {
-        QifObject	obj;
-        QifCategory	cat;
-        QifAccount	acct;
-    } cat;
-    gboolean	cat_is_acct;
-    QifClass	cat_class;
-
-};
-
-struct _QifInvstTxn
-{
-    QifAction	action;
-
-    gnc_numeric	amount;
-    gnc_numeric	d_amount;
-    gnc_numeric	price;
-    gnc_numeric	shares;
-    gnc_numeric	commission;
-
-    char *	amountstr;
-    char *	d_amountstr;
-    char *	pricestr;
-    char *	sharesstr;
-    char *	commissionstr;
-
-    char *	security;
-    char *	catstr;
-
-    union
-    {
-        QifObject	obj;
-        QifCategory	cat;
-        QifAccount	acct;
-    } far_cat;
-    gboolean	far_cat_is_acct;
-};
-
-/* to be run after parsing all the dates and amounts */
-void qif_txn_setup_splits(QifTxn txn);
-void qif_invst_txn_setup_splits(QifContext ctx, QifTxn txn);
-
-#endif /* QIF_OBJECTS_P_H */
diff --git a/gnucash/import-export/qif/qif-objects.c b/gnucash/import-export/qif/qif-objects.c
deleted file mode 100644
index 0fad928..0000000
--- a/gnucash/import-export/qif/qif-objects.c
+++ /dev/null
@@ -1,1468 +0,0 @@
-/*
- * qif-objects.c -- Objects for the QIF Importer
- *
- * Written by:	Derek Atkins  <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <glib.h>
-#include <string.h>
-#include "Account.h"
-
-#include "gnc-engine.h"
-
-#include "qif-import-p.h"
-#include "qif-objects-p.h"
-#include "qif-defaults.h"
-
-static QofLogModule log_module = GNC_MOD_IMPORT;
-
-/* create a new object of type t, with type-string type and
- * destroy function dest.  Requires 'obj' to be set.
- */
-#define qif_object_new(t,typ,dest) ({ \
-	obj = (QifObject) g_new0(t, 1); \
-	obj->type = typ; \
-	obj->destroy = dest; \
-	obj; \
-})
-
-/* Save the string from this "line".  Also:
- * - make sure we're not over-writing anything.
- * - make sure the 'line' object no longer references the string.
- */
-#define qif_save_str(var) { \
-	if (var) { \
-		PERR("duplicate found at line %d: %s", line->lineno, line->line); \
-		g_free(var); \
-	} \
-	(var) = line->line; \
-	line->line = NULL; \
-}
-
-/* QIF Account */
-static void
-qif_account_destroy(QifObject obj)
-{
-    QifAccount acct = (QifAccount) obj;
-
-    g_free(acct->name);
-    g_free(acct->desc);
-    g_free(acct->limitstr);
-    g_free(acct->budgetstr);
-
-    g_free(acct);
-};
-
-static QifAccount
-qif_account_new(void)
-{
-    QifObject obj;
-    QifAccount acct;
-
-    obj = qif_object_new(struct _QifAccount, QIF_O_ACCOUNT, qif_account_destroy);
-
-    acct = (QifAccount)obj;
-    acct->type_list = qif_parse_acct_type("bank", -1);
-
-    acct->limit = gnc_numeric_zero();
-    acct->budget = gnc_numeric_zero();
-    return acct;
-}
-
-/*
- * Merge acct into ctx.  If this account already exists in ctx then
- * merge in any new values from acct into the ctx version and return
- * the existing acct.  If the account does not already exist, then
- * insert it into the ctx and return it.
- */
-QifAccount
-qif_account_merge(QifContext ctx, QifAccount acct)
-{
-    QifAccount acct2 =
-        (QifAccount)qif_object_map_lookup(ctx, acct->obj.type, acct->name);
-
-    if (!acct2)
-    {
-        qif_object_map_insert(ctx, acct->obj.type, (QifObject)acct);
-        return acct;
-    }
-
-    /* obviously the name is the same, so don't worry about that */
-
-    if (!acct2->desc && acct->desc)
-        acct2->desc = g_strdup(acct->desc);
-
-    if (!acct2->type_list && acct->type_list)
-        acct2->type_list = acct->type_list;
-
-    if (!acct2->limitstr && acct->limitstr)
-    {
-        acct2->limitstr = g_strdup(acct->limitstr);
-        acct2->limit = acct->limit;
-    }
-
-    if (!acct2->budgetstr && acct->budgetstr)
-    {
-        acct2->budgetstr = g_strdup(acct->budgetstr);
-        acct2->budget = acct->budget;
-    }
-
-    return acct2;
-}
-
-static QifError
-qif_account_parse(QifContext ctx, GList *record)
-{
-    QifAccount acct, temp;
-    QifLine line;
-
-    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
-    g_return_val_if_fail(record, QIF_E_BADSTATE);
-
-    acct = qif_account_new();
-
-    for (; record; record = record->next)
-    {
-        line = record->data;
-
-        switch (line->type)
-        {
-        case 'N':			/* N : account name */
-            qif_save_str(acct->name);
-            break;
-        case 'D': 			/* D : account description */
-            qif_save_str(acct->desc);
-            break;
-        case 'T':			/* T : account type */
-            acct->type_list = qif_parse_acct_type(line->line, line->lineno);
-            break;
-        case 'L':			/* L : account limit */
-            qif_save_str(acct->limitstr);
-            break;
-        case 'B':			/* B : account budget */
-            qif_save_str(acct->budgetstr);
-            break;
-        default:
-            PERR("Unknown QIF account data at line %d: %s", line->lineno, line->line);
-        }
-    }
-
-    /* Merge the account into the context */
-    temp = qif_account_merge(ctx, acct);
-    if (! (ctx->parse_flags & QIF_F_IGNORE_ACCOUNTS))
-        ctx->current_acct = temp;
-    if (temp != acct)
-        qif_account_destroy((QifObject)acct);
-
-    return QIF_E_OK;
-}
-
-/* QIF Category */
-static void
-qif_cat_destroy(QifObject obj)
-{
-    QifCategory cat = (QifCategory) obj;
-
-    g_free(cat->name);
-    g_free(cat->desc);
-    g_free(cat->taxclass);
-    g_free(cat->budgetstr);
-
-    g_free(cat);
-}
-
-static QifCategory
-qif_cat_new(void)
-{
-    QifObject obj;
-    QifCategory cat;
-
-    obj = qif_object_new(struct _QifCategory, QIF_O_CATEGORY, qif_cat_destroy);
-    cat = (QifCategory)obj;
-    cat->budget = gnc_numeric_zero();
-
-    return cat;
-}
-
-/*
- * Merge cat into ctx.  If this category already exists in ctx then
- * merge in any new values from cat into the ctx version and return
- * the existing cat.  If the category does not already exist, then
- * insert it into the ctx and return it.
- */
-QifCategory
-qif_cat_merge(QifContext ctx, QifCategory cat)
-{
-    QifCategory cat2 =
-        (QifCategory)qif_object_map_lookup(ctx, cat->obj.type, cat->name);
-
-    if (!cat2)
-    {
-        qif_object_map_insert(ctx, cat->obj.type, (QifObject)cat);
-        return cat;
-    }
-
-    /* obviously the name is the same, so don't worry about that */
-
-    if (!cat2->desc && cat->desc)
-        cat2->desc = g_strdup(cat->desc);
-
-    if (!cat2->taxclass && cat->taxclass)
-        cat2->taxclass = g_strdup(cat->taxclass);
-
-    cat2->taxable = (cat2->taxable || cat->taxable);
-    cat2->expense = (cat2->expense || cat->expense);
-    cat2->income = (cat2->income || cat->income);
-
-    if (!cat2->budgetstr && cat->budgetstr)
-    {
-        cat2->budgetstr = g_strdup(cat->budgetstr);
-        cat2->budget = cat->budget;
-    }
-
-    return cat2;
-}
-
-static QifError
-qif_cat_parse(QifContext ctx, GList *record)
-{
-    QifCategory cat;
-    QifLine line;
-
-    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
-    g_return_val_if_fail(record, QIF_E_BADSTATE);
-
-    cat = qif_cat_new();
-
-    for (; record; record = record->next)
-    {
-        line = record->data;
-
-        switch (line->type)
-        {
-        case 'N':			/* N : category name */
-            qif_save_str(cat->name);
-            break;
-        case 'D':			/* D : category description */
-            qif_save_str(cat->desc);
-            break;
-        case 'T':			/* T : category is taxable? */
-            cat->taxable = TRUE;
-            break;
-        case 'E':			/* E : category is expense? */
-            cat->expense = TRUE;
-            break;
-        case 'I':			/* I : category is income? */
-            cat->income = TRUE;
-            break;
-        case 'R':			/* R : category taxclass XXX */
-            /* XXX: a number? */
-            qif_save_str(cat->taxclass);
-            break;
-        case 'B':			/* B : category budget */
-            qif_save_str(cat->budgetstr);
-            break;
-        default:
-            PERR("Unknown QIF category data at line %d: %s", line->lineno, line->line);
-        }
-    }
-
-    if (qif_cat_merge(ctx, cat) != cat)
-        qif_cat_destroy((QifObject)cat);
-
-    return QIF_E_OK;
-}
-
-/* QIF Class */
-static void
-qif_class_destroy(QifObject obj)
-{
-    QifClass qclass = (QifClass) obj;
-
-    g_free(qclass->name);
-    g_free(qclass->desc);
-    g_free(qclass->taxdesig);
-
-    g_free(qclass);
-}
-
-static QifClass
-qif_class_new()
-{
-    QifObject obj;
-
-    obj = qif_object_new(struct _QifClass, QIF_O_CLASS, qif_class_destroy);
-    return (QifClass)obj;
-}
-
-/*
- * Merge qclass into ctx.  If this class already exists in ctx then
- * merge in any new values from qclass into the ctx version and return
- * the existing qclass.  If the class does not already exist, then
- * insert it into the ctx and return it.
- */
-QifClass
-qif_class_merge(QifContext ctx, QifClass qclass)
-{
-    QifClass qclass2 =
-        (QifClass)qif_object_map_lookup(ctx, qclass->obj.type, qclass->name);
-
-    if (!qclass2)
-    {
-        qif_object_map_insert(ctx, qclass->obj.type, (QifObject)qclass);
-        return qclass;
-    }
-
-    /* obviously the name is the same, so don't worry about that */
-
-    if (!qclass2->desc && qclass->desc)
-        qclass2->desc = g_strdup(qclass->desc);
-
-    if (!qclass2->taxdesig && qclass->taxdesig)
-        qclass2->taxdesig = g_strdup(qclass->taxdesig);
-
-    return qclass2;
-}
-
-static QifError
-qif_class_parse(QifContext ctx, GList *record)
-{
-    QifClass qclass;
-    QifLine line;
-
-    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
-    g_return_val_if_fail(record, QIF_E_BADSTATE);
-
-    qclass = qif_class_new();
-
-    for (; record; record = record->next)
-    {
-        line = record->data;
-
-        switch (line->type)
-        {
-        case 'N':			/* N : class name */
-            qif_save_str(qclass->name);
-            break;
-        case 'D':			/* D : class description */
-            qif_save_str(qclass->desc);
-            break;
-        case 'R':			/* R : Tax designator */
-            qif_save_str(qclass->taxdesig);
-            break;
-        default:
-            PERR("Unknown QIF class data at line %d: %s", line->lineno, line->line);
-        }
-    }
-
-    if (qif_class_merge(ctx, qclass) != qclass)
-        qif_class_destroy((QifObject)qclass);
-
-    return QIF_E_OK;
-}
-
-/* QIF Security Symbol */
-static void
-qif_security_destroy(QifObject obj)
-{
-    QifSecurity security = (QifSecurity) obj;
-
-    g_free(security->name);
-    g_free(security->symbol);
-    g_free(security->type);
-
-    g_free(security);
-}
-
-static QifSecurity
-qif_security_new()
-{
-    QifObject obj;
-
-    obj = qif_object_new(struct _QifSecurity, QIF_O_SECURITY, qif_security_destroy);
-    return (QifSecurity)obj;
-}
-
-/*
- * Merge security into ctx.  If this security already exists in ctx then
- * merge in any new values from security into the ctx version and return
- * the existing security.  If the security does not already exist, then
- * insert it into the ctx and return it.
- */
-QifSecurity
-qif_security_merge(QifContext ctx, QifSecurity security)
-{
-    QifSecurity security2 =
-        (QifSecurity)qif_object_map_lookup(ctx, security->obj.type, security->name);
-
-    if (!security2)
-    {
-        qif_object_map_insert(ctx, security->obj.type, (QifObject)security);
-        return security;
-    }
-
-    /* obviously the name is the same, so don't worry about that */
-
-    if (!security2->symbol && security->symbol)
-        security2->symbol = g_strdup(security->symbol);
-
-    if (!security2->type && security->type)
-        security2->type = g_strdup(security->type);
-
-    return security2;
-}
-
-static QifError
-qif_security_parse(QifContext ctx, GList *record)
-{
-    QifSecurity security;
-    QifLine line;
-
-    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
-    g_return_val_if_fail(record, QIF_E_BADSTATE);
-
-    security = qif_security_new();
-
-    for (; record; record = record->next)
-    {
-        line = record->data;
-
-        switch (line->type)
-        {
-        case 'N':			/* N : security name */
-            qif_save_str(security->name);
-            break;
-        case 'S':			/* S : security symbol */
-            qif_save_str(security->symbol);
-            break;
-        case 'T':			/* T : security type */
-            qif_save_str(security->type);
-            break;
-        default:
-            PERR("Unknown QIF security data at line %d: %s", line->lineno, line->line);
-        }
-    }
-
-    if (qif_security_merge(ctx, security) != security)
-        qif_security_destroy((QifObject)security);
-
-    return QIF_E_OK;
-}
-
-/********************* TXN *********************/
-
-static QifSplit
-qif_split_new()
-{
-    QifSplit split = g_new0(struct _QifSplit, 1);
-
-    /* Initialize to 'zero' (even though they are not valid) */
-    split->amount = gnc_numeric_zero();
-    split->value = gnc_numeric_zero();
-
-    return split;
-}
-
-static void
-qif_split_destroy(QifSplit split)
-{
-    if (!split) return;
-
-    g_free(split->memo);
-    g_free(split->catstr);
-    g_free(split->amountstr);
-
-    g_free(split);
-}
-
-static QifSplit
-qif_split_copy(QifSplit split)
-{
-    QifSplit s = qif_split_new();
-
-    memcpy(s, split, sizeof(*s));
-    if (s->memo) s->memo = g_strdup(s->memo);
-    if (s->amountstr) s->amountstr = g_strdup(s->amountstr);
-    if (s->catstr) s->memo = g_strdup(s->catstr);
-
-    return s;
-}
-
-/* Forward declarations */
-static void qif_txn_invst_destroy(QifInvstTxn);
-
-/* QIF Transaction */
-
-static void
-qif_split_parse_category(QifContext ctx, QifSplit split)
-{
-    char *cat = NULL;
-    char *cat_class = NULL;
-    char *miscx_cat = NULL;
-    char *miscx_class = NULL;
-
-    gboolean miscx_is_acct;
-
-    static GList *types = NULL;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(split);
-    g_return_if_fail(split->cat.cat == NULL && split->cat_class == NULL);
-
-    if (qif_parse_split_category(split->catstr,
-                                 &cat, &split->cat_is_acct, &cat_class,
-                                 &miscx_cat, &miscx_is_acct, &miscx_class))
-    {
-        g_assert(cat);
-
-        if (split->cat_is_acct)
-        {
-            if (types == NULL)
-                types = qif_parse_acct_type("__any_bank__", -1);
-
-            split->cat.acct = find_or_make_acct(ctx, cat, types);
-
-        }
-        else
-            split->cat.cat = find_or_make_cat(ctx, cat);
-
-        if (cat_class)
-            split->cat_class = find_or_make_class(ctx, cat_class);
-
-        /* miscx isn't used in a normal transaction, so just ignore it */
-        if (miscx_cat)
-            g_free(miscx_cat);
-        if (miscx_class)
-            g_free(miscx_class);
-
-    }
-    else
-        PERR("Problem parsing split category: %s", split->catstr);
-}
-
-static void
-qif_txn_destroy(QifObject obj)
-{
-    QifTxn txn = (QifTxn) obj;
-    GList *node;
-    QifSplit split;
-
-    g_free(txn->datestr);
-    g_free(txn->payee);
-    g_free(txn->address);
-    g_free(txn->num);
-
-    if (txn->invst_info)
-        qif_txn_invst_destroy(txn->invst_info);
-
-    for (node = txn->splits; node; node = node->next)
-    {
-        split = node->data;
-        if (split == txn->default_split)
-            txn->default_split = NULL;
-        if (split == txn->current_split)
-            txn->current_split = NULL;
-
-        qif_split_destroy(split);
-    }
-
-    g_list_free(txn->splits);
-    qif_split_destroy(txn->default_split);
-    qif_split_destroy(txn->current_split);
-
-    g_free(txn);
-}
-
-static QifTxn
-qif_txn_new(void)
-{
-    QifObject obj;
-    QifTxn txn;
-
-    obj = qif_object_new(struct _QifTxn, "qif-txn", qif_txn_destroy);
-    txn = (QifTxn) obj;
-    txn->default_split = qif_split_new();
-
-    return txn;
-}
-
-static void
-qif_txn_init(QifContext ctx)
-{
-    qif_clear_flag(ctx->parse_flags, QIF_F_IGNORE_ACCOUNTS);
-    ctx->parse_state = NULL;
-}
-
-static gboolean
-qif_is_bad_numeric_string(const char* line)
-{
-    return (strncmp(line, "...", 3) == 0);
-}
-
-/*
- * this is called for the first transaction after each !Type: tag.
- *
- * if the first transaction after a !Type: tag has a payee of "Opening
- * Balance" or "Initial Balance", we have to massage the transaction a
- * little.  The meaning of an OB transaction is "transfer from Equity
- * to the account specified in the L line."  Idiomatically, ms-money
- * and some others use this transaction instead of an Account record
- * to specify "this" account (the from-account for all following
- * transactions), so we have to allow for that.
- *
- * Even if the payee isn't "Opening Balance", we if we have no default
- * from-account by this time we need to set one.  In that case we set
- * the default account based on the file name.
- *
- * If we DO know the account already, and this is a transfer to it,
- * it's also an opening balance regardless of the payee.
- *
- * In the end make sure that the context 'current account' is set.
- */
-static void
-qif_process_opening_balance_txn(QifContext ctx, QifTxn txn)
-{
-    QifSplit split = txn->default_split;
-    QifAccount cur_acct = NULL;	/* We know that ctx->current_acct is NULL */
-
-    g_return_if_fail(txn->invst_info == NULL);
-
-    if ((!cur_acct && txn->payee &&
-            (!strcasecmp(txn->payee, "Opening Balance") ||
-             !strcasecmp(txn->payee, "Initial Balance")) && split->cat_is_acct) ||
-            (cur_acct &&
-             ((split->cat_is_acct && !strcasecmp(split->cat.acct->name, cur_acct->name))
-              ||
-              (!split->cat_is_acct && !strcasecmp(split->cat.cat->name, cur_acct->name))))
-       )
-    {
-
-        /* This is an explicit "Opening Balance" transactions.  We need to
-         * change the "from account" to point to the equity account that
-         * the opening balance comes from...
-         */
-        if (split->cat_is_acct)
-            cur_acct = split->cat.acct;
-        else
-        {
-            g_assert(split->cat.cat);
-            cur_acct = find_or_make_acct(ctx, g_strdup(split->cat.cat->name),
-                                         qif_parse_acct_type_guess(txn->txn_type));
-            split->cat_is_acct = TRUE;
-        }
-        split->cat.acct = qif_default_equity_acct(ctx);
-    }
-
-    /*
-     * If we found an opening balance account then set up the context.
-     * If we didn't actually succeed in finding an account then
-     * set a flag so we can go back later and look for it.
-     */
-
-    if (cur_acct)
-    {
-        ctx->opening_bal_acct = cur_acct;
-        ctx->current_acct = cur_acct;
-    }
-    else
-        qif_set_flag(ctx->parse_flags, QIF_F_TXN_NEEDS_ACCT);
-}
-
-/* process all the splits in the transaction -- if this is a "split
- * transaction" then make sure the sum of all the amounts (including
- * the default split) does NOT equal zero -- if it does then we want
- * to reverse all the splits.  The "amount" should be the 'T' amount
- * from the txn.
- */
-static void
-qif_txn_fix_amounts(QifTxn txn, gnc_numeric amount)
-{
-    gnc_numeric sum = amount;
-    QifSplit split;
-    GList *node;
-
-    g_return_if_fail(txn);
-
-    /* No current_split, so this is NOT a split transaction. */
-    if (!txn->current_split) return;
-
-    /* Then add in every split in the split-list */
-    for (node = txn->splits; node; node = node->next)
-    {
-        split = node->data;
-        sum = gnc_numeric_add(sum, split->amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
-    }
-
-    /* if the sum is not zero then reverse all the amounts in the split list */
-    if (!gnc_numeric_zero_p(sum))
-        for (node = txn->splits; node; node = node->next)
-        {
-            split = node->data;
-            split->amount = gnc_numeric_neg(split->amount);
-        }
-}
-
-static QifError
-qif_txn_parse(QifContext ctx, GList *record)
-{
-    QifTxn txn;
-    QifLine line;
-    GList *node;
-    QifSplit split;
-
-    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
-    g_return_val_if_fail(record, QIF_E_BADSTATE);
-
-    txn = qif_txn_new();
-    txn->txn_type = ctx->parse_type;
-
-    for (; record; record = record->next)
-    {
-        line = record->data;
-
-        switch (line->type)
-        {
-        case 'D':			/* D : transaction date */
-            qif_save_str(txn->datestr);
-            break;
-        case 'P':			/* P : payee */
-            qif_save_str(txn->payee);
-            break;
-        case 'A':			/* A : address */
-            /* multiple 'A' lines are appended together with newlines */
-            if (txn->address)
-            {
-                char *tmp = txn->address;
-                txn->address = g_strconcat(tmp, "\n", line->line, NULL);
-                g_free(tmp);
-            }
-            else
-                qif_save_str(txn->address);
-            break;
-        case 'N':			/* N : check/transaction number */
-            qif_save_str(txn->num);
-            break;
-        case 'C':			/* C : transaction cleared flag */
-            txn->cleared = qif_parse_cleared(line);
-            break;
-        case 'L':			/* L : default split category */
-            if (!txn->current_split) qif_save_str(txn->default_split->catstr);
-            break;
-        case 'M':			/* M : default split memo */
-            if (!txn->current_split) qif_save_str(txn->default_split->memo);
-            break;
-        case 'T':			/* T : total transaction amount */
-            if (!txn->current_split && !qif_is_bad_numeric_string(line->line))
-                qif_save_str(txn->default_split->amountstr);
-            break;
-        case 'S':			/* S : split category */
-            /* This implies a quicken-style "split transaction", so we're mostly
-             * going to ignore the default_split except for internal verification.
-             */
-            txn->current_split = qif_split_new();
-            txn->splits = g_list_prepend(txn->splits, txn->current_split);
-            qif_save_str(txn->current_split->catstr);
-            break;
-        case 'E':			/* E : split memo */
-            if (txn->current_split)
-                qif_save_str(txn->current_split->memo);
-            break;
-        case '$':			/* split amount */
-            if (txn->current_split && !qif_is_bad_numeric_string(line->line))
-                qif_save_str(txn->current_split->amountstr);
-            break;
-        default:
-            PERR("Unknown QIF transaction data at line %d: %s", line->lineno, line->line);
-        }
-    }
-
-    /* If we have no date string then there is no reason to do anything else */
-    if (txn->datestr)
-    {
-        /* We delay processing the date and amount strings until later.. */
-
-        /* parse the category on each split */
-        for (node = txn->splits; node; node = node->next)
-        {
-            split = node->data;
-            if (split->catstr)
-                qif_split_parse_category(ctx, split);
-        }
-        /* ... including the default split */
-        if (txn->default_split->catstr)
-            qif_split_parse_category(ctx, txn->default_split);
-
-        /* if we don't have an account, then deal with the opening balance */
-        if (!ctx->current_acct)
-            qif_process_opening_balance_txn(ctx, txn);
-
-        /* Set the transaction's from account */
-        txn->from_acct = ctx->current_acct;
-
-        /* And add it to the process list */
-        ctx->parse_state = g_list_prepend(ctx->parse_state, txn);
-
-    }
-    else
-        /* no date?  Ignore this txn */
-        qif_txn_destroy((QifObject)txn);
-
-    return QIF_E_OK;
-}
-
-/* after we parse the amounts, fix up the transaction splits */
-void
-qif_txn_setup_splits(QifTxn txn)
-{
-    QifSplit split, this_split;
-    GList *node;
-    gnc_numeric total;
-
-    if (txn->splits)
-    {
-        /* We have a bunch of "far" splits -- maybe fix up the totals.. */
-        qif_txn_fix_amounts(txn, txn->default_split->amount);
-
-        /* Re-Compute the total for the "near" (default) split */
-        total = gnc_numeric_zero();
-        for (node = txn->splits; node; node = node->next)
-        {
-            split = node->data;
-            split->value = split->amount;
-            total = gnc_numeric_add(total, split->amount, 0, GNC_HOW_DENOM_LCD);
-        }
-
-        /* And re-set the default-split amount */
-        txn->default_split->amount = gnc_numeric_neg(total);
-
-    }
-    else
-    {
-        /* not a split txn.  Compute the "far" split by copying the "near"
-         *  split and then moving the 'near' split to the far split.
-         */
-
-        /* First make a copy of this transaction and move the copy to the 'near' */
-        split = txn->default_split;
-        this_split = qif_split_copy(split);
-        txn->default_split = this_split;
-
-        /* then adjust the 'far' txn */
-        split->amount = gnc_numeric_neg(split->amount);
-        split->value = split->amount;
-        txn->splits = g_list_prepend(txn->splits, split);
-    }
-
-    /* Set the default-split value from the default-split amount */
-    txn->default_split->value = txn->default_split->amount;
-}
-
-/* This is called when we're done processing an account.  We want
- * to merge the transactions in the "parse_state" into the Qif Context
- */
-static QifError
-qif_txn_end_acct(QifContext ctx)
-{
-    GList *node;
-    QifTxn txn;
-    gboolean txn_needs_acct;
-
-    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
-
-    /* Return now if there is nothing to do. */
-    if (!ctx->parse_state) return QIF_E_OK;
-
-    /* Walk through the list of transactions.  First check if it
-     * needs a from-account; then add it to the context.
-     */
-
-    txn_needs_acct = (ctx->parse_flags & QIF_F_TXN_NEEDS_ACCT);
-
-    /* Invert the list so we're working in the right order */
-    ctx->parse_state = g_list_reverse(ctx->parse_state);
-
-    for (node = ctx->parse_state; node; node = node->next)
-    {
-        txn = node->data;
-
-        /* If we need a from account, then set it.. */
-        if (txn_needs_acct && ctx->opening_bal_acct && !txn->from_acct)
-            txn->from_acct = ctx->opening_bal_acct;
-
-        /* merge the txn into the context (prepends to the list) */
-        qif_object_list_insert(ctx, (QifObject)txn);
-    }
-
-    if (txn_needs_acct && ctx->opening_bal_acct)
-        qif_clear_flag(ctx->parse_flags, QIF_F_TXN_NEEDS_ACCT);
-
-    /* clean up our state */
-    g_list_free(ctx->parse_state);
-    ctx->parse_state = NULL;
-
-    return QIF_E_OK;
-}
-
-/* Extra info in an Investment Transaction */
-static QifInvstTxn
-qif_invst_txn_new(void)
-{
-    QifInvstTxn itxn = g_new0(struct _QifInvstTxn, 1);
-
-    itxn->amount = gnc_numeric_zero();
-    itxn->d_amount = gnc_numeric_zero();
-    itxn->price = gnc_numeric_zero();
-    itxn->shares = gnc_numeric_zero();
-    itxn->commission = gnc_numeric_zero();
-
-    return itxn;
-}
-
-static void
-qif_txn_invst_destroy(QifInvstTxn itxn)
-{
-    if (!itxn) return;
-
-    g_free(itxn->amountstr);
-    g_free(itxn->d_amountstr);
-    g_free(itxn->pricestr);
-    g_free(itxn->sharesstr);
-    g_free(itxn->commissionstr);
-    g_free(itxn->security);
-
-    g_free(itxn->catstr);
-
-    g_free(itxn);
-}
-
-static QifError
-qif_txn_invst_parse(QifContext ctx, GList *record)
-{
-    QifTxn txn;
-    QifInvstTxn itxn;
-    QifLine line;
-
-    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
-    g_return_val_if_fail(record, QIF_E_BADSTATE);
-
-    txn = qif_txn_new();
-    txn->txn_type = ctx->parse_type;
-    itxn = qif_invst_txn_new();
-    txn->invst_info = itxn;
-
-    for (; record; record = record->next)
-    {
-        line = record->data;
-
-        switch (line->type)
-        {
-        case 'D':			/* D : transaction date */
-            qif_save_str(txn->datestr);
-            break;
-        case 'P':			/* P : txn payee */
-            qif_save_str(txn->payee);
-            break;
-        case 'N':			/* N : action */
-            itxn->action = qif_parse_action(line);
-            break;
-        case 'C':			/* C : cleared flag */
-            txn->cleared = qif_parse_cleared(line);
-            break;
-        case 'M':			/* M : memo */
-            if (!txn->current_split)
-                qif_save_str(txn->default_split->memo);
-            break;
-        case 'T':			/* T : total amount */
-            if (!qif_is_bad_numeric_string(line->line))
-                qif_save_str(itxn->amountstr);
-            break;
-        case '$':			/* $ : transfer amount */
-            if (!qif_is_bad_numeric_string(line->line))
-                qif_save_str(itxn->d_amountstr);
-            break;
-        case 'I':			/* I : share price */
-            qif_save_str(itxn->pricestr);
-            break;
-        case 'Q':			/* Q : number of shares */
-            qif_save_str(itxn->sharesstr);
-            break;
-        case 'Y':			/* Y : name of security */
-            qif_save_str(itxn->security);
-            break;
-        case 'O':			/* O : commission */
-            qif_save_str(itxn->commissionstr);
-            break;
-        case 'L':			/* L : category */
-            qif_save_str(itxn->catstr);
-            break;
-        default:
-            PERR("Unknown QIF Investment transaction data at line %d: %s",
-                 line->lineno, line->line);
-        }
-    }
-
-    /* If we have no date string then there is no reason to do anything else */
-    if (txn->datestr && itxn->action != QIF_A_NONE)
-    {
-
-        /* Make sure we've got a security name */
-        if (!itxn->security)
-            itxn->security = g_strdup("");	/* XXX */
-
-        /* if we don't have a from account, then mark the fact that
-         * we'll need one later.
-         */
-        if (ctx->current_acct)
-            txn->from_acct = ctx->current_acct;
-        else
-            qif_set_flag(ctx->parse_flags, QIF_F_ITXN_NEEDS_ACCT);
-
-        /* Add this transaction to the parse state for later processing */
-        ctx->parse_state = g_list_prepend(ctx->parse_state, txn);
-
-    }
-    else
-    {
-        /* no date?  Just destroy it */
-        qif_txn_destroy((QifObject)txn);
-    }
-
-    return QIF_E_OK;
-}
-
-
-void
-qif_invst_txn_setup_splits(QifContext ctx, QifTxn txn)
-{
-    QifInvstTxn itxn;
-    QifSplit near_split, far_split, comm_split;
-    QifAccount from_acct;
-
-    char *cat = NULL;
-    char *cat_class = NULL;
-    gboolean cat_is_acct = FALSE;
-    char *miscx = NULL;
-    char *miscx_class = NULL;
-    gboolean miscx_is_acct = FALSE;
-
-    /* Cached account-type lists */
-    static GList *bank_list = NULL;
-
-    gnc_numeric split_value;
-
-    g_return_if_fail(ctx);
-    g_return_if_fail(txn);
-    g_return_if_fail(txn->invst_info);
-
-    itxn = txn->invst_info;
-
-    /* Compute the share value, because we'll probably need it */
-    split_value = gnc_numeric_mul(itxn->shares, itxn->price, 0, GNC_HOW_DENOM_REDUCE);
-
-    /* Make sure that "amount" is a valid "transaction amount" */
-    if (!itxn->amountstr && itxn->d_amountstr)
-        itxn->amount = itxn->d_amount;
-
-    /* near and far splits..  for simplicity */
-    near_split = txn->default_split;
-    far_split = qif_split_new();
-    from_acct = txn->from_acct;
-
-    /* Parse the category string */
-    if (!qif_parse_split_category(itxn->catstr,
-                                  &cat, &cat_is_acct, &cat_class,
-                                  &miscx, &miscx_is_acct, &miscx_class))
-        PERR("Failure parsing category: %s", itxn->catstr);
-
-    /* Make sure we've got a cached list */
-    if (bank_list == NULL)
-        bank_list = qif_parse_acct_type("__any_bank__", -1);
-
-    /* find the NEAR account */
-
-    switch (itxn->action)
-    {
-    case QIF_A_BUY:
-    case QIF_A_BUYX:
-    case QIF_A_REINVDIV:
-    case QIF_A_REINVINT:
-    case QIF_A_REINVLG:
-    case QIF_A_REINVMD:
-    case QIF_A_REINVSG:
-    case QIF_A_REINVSH:
-    case QIF_A_SELL:
-    case QIF_A_SELLX:
-    case QIF_A_SHRSIN:
-    case QIF_A_SHRSOUT:
-    case QIF_A_STKSPLIT:
-        txn->from_acct = qif_default_stock_acct(ctx, itxn->security);
-        break;
-
-    case QIF_A_CGLONG:
-    case QIF_A_CGMID:
-    case QIF_A_CGSHORT:
-    case QIF_A_DIV:
-    case QIF_A_INTINC:
-    case QIF_A_MARGINT:
-    case QIF_A_MISCEXP:
-    case QIF_A_MISCINC:
-    case QIF_A_RTRNCAP:
-    case QIF_A_XIN:
-    case QIF_A_XOUT:
-        txn->from_acct = from_acct;
-        break;
-
-    case QIF_A_CGLONGX:
-    case QIF_A_CGMIDX:
-    case QIF_A_CGSHORTX:
-    case QIF_A_DIVX:
-    case QIF_A_INTINCX:
-    case QIF_A_MARGINTX:
-    case QIF_A_RTRNCAPX:
-        txn->from_acct = find_or_make_acct(ctx, cat, bank_list);
-        cat = NULL;
-        break;
-
-    case QIF_A_MISCEXPX:
-    case QIF_A_MISCINCX:
-        txn->from_acct = find_or_make_acct(ctx, miscx, bank_list);
-        miscx = NULL;
-        break;
-
-    default:
-        PERR("Unhandled Action: %d", itxn->action);
-        break;
-    }
-
-    /* find the FAR account */
-
-    itxn->far_cat_is_acct = TRUE;
-    switch (itxn->action)
-    {
-    case QIF_A_BUY:
-    case QIF_A_SELL:
-        itxn->far_cat.acct = from_acct;
-        break;
-
-    case QIF_A_BUYX:
-    case QIF_A_MISCEXP:
-    case QIF_A_MISCEXPX:
-    case QIF_A_MISCINC:
-    case QIF_A_MISCINCX:
-    case QIF_A_SELLX:
-    case QIF_A_XIN:
-    case QIF_A_XOUT:
-        itxn->far_cat.cat = find_or_make_cat(ctx, cat);
-        itxn->far_cat_is_acct = FALSE;
-        cat = NULL;
-        break;
-
-    case QIF_A_CGLONG:
-    case QIF_A_CGLONGX:
-    case QIF_A_REINVLG:
-        itxn->far_cat.acct = qif_default_cglong_acct(ctx, itxn->security);
-        break;
-
-    case QIF_A_CGMID:
-    case QIF_A_CGMIDX:
-    case QIF_A_REINVMD:
-        itxn->far_cat.acct = qif_default_cgmid_acct(ctx, itxn->security);
-        break;
-
-    case QIF_A_CGSHORT:
-    case QIF_A_CGSHORTX:
-    case QIF_A_REINVSG:
-    case QIF_A_REINVSH:
-        itxn->far_cat.acct = qif_default_cgshort_acct(ctx, itxn->security);
-        break;
-
-    case QIF_A_DIV:
-    case QIF_A_DIVX:
-    case QIF_A_REINVDIV:
-        itxn->far_cat.acct = qif_default_dividend_acct(ctx, itxn->security);
-        break;
-
-    case QIF_A_INTINC:
-    case QIF_A_INTINCX:
-    case QIF_A_REINVINT:
-        itxn->far_cat.acct = qif_default_interest_acct(ctx, itxn->security);
-        break;
-
-    case QIF_A_MARGINT:
-    case QIF_A_MARGINTX:
-        itxn->far_cat.acct = qif_default_margin_interest_acct(ctx);
-        break;
-
-    case QIF_A_RTRNCAP:
-    case QIF_A_RTRNCAPX:
-        itxn->far_cat.acct = qif_default_capital_return_acct(ctx, itxn->security);
-        break;
-
-    case QIF_A_SHRSIN:
-    case QIF_A_SHRSOUT:
-        itxn->far_cat.acct = qif_default_equity_holding(ctx, itxn->security);
-        break;
-
-    case QIF_A_STKSPLIT:
-        itxn->far_cat.acct = qif_default_stock_acct(ctx, itxn->security);
-        break;
-
-    default:
-        break;
-    }
-
-    /* If we don't have a far acct (or far category) then reset the flag */
-    if (!itxn->far_cat.obj)
-        itxn->far_cat_is_acct = FALSE;
-
-    /* And now fill in the "near" and "far" splits.  In particular we need
-     *
-     *	NEAR:	txn->from_acct, near_split->amount, value
-     *	FAR:	cat, far_split->amount, value
-     */
-    switch (itxn->action)
-    {
-    case QIF_A_BUY:
-    case QIF_A_BUYX:
-    case QIF_A_REINVDIV:
-    case QIF_A_REINVINT:
-    case QIF_A_REINVLG:
-    case QIF_A_REINVMD:
-    case QIF_A_REINVSG:
-    case QIF_A_REINVSH:
-    case QIF_A_SHRSIN:
-        near_split->amount = itxn->shares;
-        near_split->value = split_value;
-        far_split->amount = far_split->value = gnc_numeric_neg(itxn->amount);
-        break;
-
-    case QIF_A_SELL:
-    case QIF_A_SELLX:
-    case QIF_A_SHRSOUT:
-        near_split->amount = gnc_numeric_neg(itxn->shares);
-        near_split->value = gnc_numeric_neg(split_value);
-        far_split->amount = far_split->value = itxn->amount;
-        break;
-
-    case QIF_A_CGLONG:
-    case QIF_A_CGLONGX:
-    case QIF_A_CGMID:
-    case QIF_A_CGMIDX:
-    case QIF_A_CGSHORT:
-    case QIF_A_CGSHORTX:
-    case QIF_A_DIV:
-    case QIF_A_DIVX:
-    case QIF_A_INTINC:
-    case QIF_A_INTINCX:
-    case QIF_A_MISCINC:
-    case QIF_A_MISCINCX:
-    case QIF_A_RTRNCAP:
-    case QIF_A_RTRNCAPX:
-    case QIF_A_XIN:
-        near_split->amount = near_split->value = itxn->amount;
-        far_split->amount = far_split->value = gnc_numeric_neg(itxn->amount);
-        break;
-
-    case QIF_A_MARGINT:
-    case QIF_A_MARGINTX:
-    case QIF_A_MISCEXP:
-    case QIF_A_MISCEXPX:
-    case QIF_A_XOUT:
-        near_split->amount = near_split->value = gnc_numeric_neg(itxn->amount);
-        far_split->amount = far_split->value = itxn->amount;
-        break;
-
-    case QIF_A_STKSPLIT:
-        /* QIF just specifies the split ratio, not the number of shares
-         * in and out, so we have to fetch the number of shares from the
-         * security account..  FEH!
-         */
-
-        near_split->value = gnc_numeric_neg(split_value);
-        far_split->value = split_value;
-
-        /* XXX: FIXME: compute in-shares/out-shares based on ratio here:
-         *
-         * splitratio = num-shares / 10;
-         * in_shares = gnc_account_get_balance(near_acct);
-         * out_shares = in_shares * splitratio;
-         *
-         * near_split->amount = out_shares;
-         * far_split->amount = gnc_numeric_neg(in_shares);
-         *
-         * We know (later) that near_split == txn->default_split and
-         * far_split == txn->splits->data, so we'll just special-case this
-         * kind of txn when we convert to GNC later.
-         */
-
-        break;
-
-    default:
-        break;
-    }
-
-    /* Just make sure to set that it's an account, not a category */
-    far_split->cat.obj = itxn->far_cat.obj;
-    if (itxn->far_cat_is_acct)
-        far_split->cat_is_acct = TRUE;
-
-    /* make the commission split if we need it, then add it to the split-list  */
-    if (itxn->commissionstr)
-    {
-        comm_split = qif_split_new();
-        comm_split->cat.acct = qif_default_commission_acct(ctx);
-        comm_split->cat_is_acct = TRUE;
-        comm_split->amount = itxn->commission;
-        comm_split->value = itxn->commission;
-
-        txn->splits = g_list_prepend(txn->splits, comm_split);
-    }
-
-    /* Push the "far split" into the txn split-list */
-    txn->splits = g_list_prepend(txn->splits, far_split);
-
-    /* Free parsed strings.. */
-    g_free(cat);
-    g_free(cat_class);
-    g_free(miscx);
-    g_free(miscx_class);
-}
-
-
-/* Other handlers */
-static void
-qif_autoswitch_set(QifContext ctx)
-{
-    qif_set_flag(ctx->parse_flags, QIF_F_IGNORE_ACCOUNTS);
-}
-
-static void
-qif_autoswitch_clear(QifContext ctx)
-{
-    qif_clear_flag(ctx->parse_flags, QIF_F_IGNORE_ACCOUNTS);
-}
-
-/********************************************************************************
- * find or make ...
- */
-
-QifAccount
-find_or_make_acct(QifContext ctx, char *name, GList *types)
-{
-    QifAccount res;
-
-    res = (QifAccount)qif_object_map_lookup(ctx, QIF_O_ACCOUNT, name);
-    if (res)
-        g_free(name);
-    else
-    {
-        res = qif_account_new();
-        res->name = name;
-        res->type_list = types;
-
-        qif_object_map_insert(ctx, name, (QifObject)res);
-    }
-
-    return res;
-}
-
-QifCategory
-find_or_make_cat(QifContext ctx, char *name)
-{
-    QifCategory res;
-
-    res = (QifCategory)qif_object_map_lookup(ctx, QIF_O_CATEGORY, name);
-    if (res)
-        g_free(name);
-    else
-    {
-        res = qif_cat_new();
-
-        res->name = name;
-
-        qif_object_map_insert(ctx, name, (QifObject)res);
-    }
-
-    return res;
-}
-
-QifClass
-find_or_make_class(QifContext ctx, char *name)
-{
-    QifClass res;
-
-    res = (QifClass)qif_object_map_lookup(ctx, QIF_O_CLASS, name);
-    if (res)
-        g_free(name);
-    else
-    {
-        res = qif_class_new();
-        res->name = name;
-        qif_object_map_insert(ctx, name, (QifObject)res);
-    }
-    return res;
-}
-
-/*****************************************************************************/
-
-/*
- * initialize handlers
- */
-void
-qif_object_init(void)
-{
-    int i;
-    static struct
-    {
-        QifType		type;
-        struct _QifHandler	handler;
-    } handlers[] =
-    {
-        { QIF_TYPE_BANK, { qif_txn_init, qif_txn_parse, qif_txn_end_acct } },
-        { QIF_TYPE_CASH, { qif_txn_init, qif_txn_parse, qif_txn_end_acct } },
-        { QIF_TYPE_CCARD, { qif_txn_init, qif_txn_parse, qif_txn_end_acct } },
-        { QIF_TYPE_INVST, { qif_txn_init, qif_txn_invst_parse, qif_txn_end_acct } },
-        { QIF_TYPE_PORT, { qif_txn_init, qif_txn_invst_parse, qif_txn_end_acct } },
-        { QIF_TYPE_OTH_A, { qif_txn_init, qif_txn_parse, qif_txn_end_acct } },
-        { QIF_TYPE_OTH_L, { qif_txn_init, qif_txn_parse, qif_txn_end_acct } },
-        { QIF_TYPE_CLASS, { NULL, qif_class_parse, NULL } },
-        { QIF_TYPE_CAT, { NULL, qif_cat_parse, NULL } },
-        { QIF_TYPE_SECURITY, { NULL, qif_security_parse, NULL } },
-        { QIF_ACCOUNT, { NULL, qif_account_parse, NULL } },
-        { QIF_AUTOSWITCH, { qif_autoswitch_set, NULL, NULL } },
-        { QIF_CLEAR_AUTOSWITCH, { qif_autoswitch_clear, NULL, NULL } },
-        { 0, {NULL, NULL, NULL} }
-    };
-
-    for (i = 0; handlers[i].type > 0; i++)
-    {
-        if (handlers[i].type <= 0)
-        {
-            PERR("Invalid type?!?  (%d @ %d)", handlers[i].type, i);
-        }
-        else
-            qif_register_handler(handlers[i].type, &(handlers[i].handler));
-    }
-}
diff --git a/gnucash/import-export/qif/qif-objects.h b/gnucash/import-export/qif/qif-objects.h
deleted file mode 100644
index 2d28e59..0000000
--- a/gnucash/import-export/qif/qif-objects.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * qif-objects.h -- QIF objects for the QIF importer
- *
- * Written By:	Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifndef QIF_OBJECTS_H
-#define QIF_OBJECTS_H
-
-typedef struct _QifObject *QifObject;
-typedef struct _QifData *QifData;
-
-struct _QifObject
-{
-    const char*	type;
-    void		(*destroy)(QifObject);
-
-    /* QIF Objects contain data beyond this point.. */
-};
-
-#define QIF_O_ACCOUNT	"qif-acct"
-typedef struct _QifAccount *QifAccount;
-
-#define QIF_O_CATEGORY	"qif-cat"
-typedef struct _QifCategory *QifCategory;
-
-#define QIF_O_CLASS	"qif-class"
-typedef struct _QifClass *QifClass;
-
-#define QIF_O_SECURITY	"qif-security"
-typedef struct _QifSecurity *QifSecurity;
-
-#define QIF_O_TXN	"qif-txn"
-typedef struct _QifTxn *QifTxn;
-typedef struct _QifSplit *QifSplit;
-typedef struct _QifInvstTxn *QifInvstTxn;
-
-void qif_object_init(void);
-
-QifAccount find_or_make_acct(QifContext ctx, char *name, GList *types);
-QifCategory find_or_make_cat(QifContext ctx, char *name);
-QifClass find_or_make_class(QifContext ctx, char *name);
-
-/* merge the object into the context.  Returns the object that's in
- * the context, which is either the supplied object or the
- * already-existing object.
- */
-QifAccount qif_account_merge(QifContext ctx, QifAccount acct);
-QifCategory qif_cat_merge(QifContext ctx, QifCategory cat);
-QifClass qif_class_merge(QifContext ctx, QifClass qclass);
-QifSecurity qif_security_merge(QifContext ctx, QifSecurity security);
-
-#endif /* QIF_OBJECTS_H */
diff --git a/gnucash/import-export/qif/qif-parse.c b/gnucash/import-export/qif/qif-parse.c
deleted file mode 100644
index f291c86..0000000
--- a/gnucash/import-export/qif/qif-parse.c
+++ /dev/null
@@ -1,935 +0,0 @@
-/*
- * qif-parse.c -- parse QIF
- *
- * Written by:        Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <glib.h>
-#include <glib/gi18n.h>
-#include <string.h>
-
-/* For regex */
-#include <sys/types.h>
-#include <regex.h>
-
-#include <stdarg.h>
-
-#include "gnc-engine.h"
-#include "gnc-ui-util.h"
-
-#include "qif-import-p.h"
-#include "qif-objects-p.h"
-
-#include "import-parse.h"
-
-static QofLogModule log_module = GNC_MOD_IMPORT;
-
-/* An array of handlers for the various bang-types */
-static QifHandler qif_handlers[QIF_TYPE_MAX+1] = { NULL };
-
-/* Parser Regular Expressions */
-static gboolean qifp_regex_compiled = FALSE;
-static regex_t category_regex;
-
-/* A Hash Table of bang-types */
-static GHashTable *qif_bangtype_map = NULL;
-
-/* A Hash Table of action strings */
-static GHashTable *qif_action_map = NULL;
-
-/* A Hash Table of account types */
-static GHashTable *qif_atype_map = NULL;
-
-/************************************************************************/
-
-/* Register a handler */
-void
-qif_register_handler(QifType type, QifHandler handler)
-{
-    if (type <= 0 || type > QIF_TYPE_MAX)
-    {
-        PERR("Invalid type: %d", type);
-        return;
-    }
-    qif_handlers[type] = handler;
-}
-
-static void
-compile_regex()
-{
-    regcomp(&category_regex,
-            "^ *(\\[)?([^]/|]*)(]?)(/([^|]*))?(\\|(\\[)?([^]/]*)(]?)(/(.*))?)? *$",
-            REG_EXTENDED);
-
-    qifp_regex_compiled = TRUE;
-}
-
-#define QIF_ADD_TYPE(ts,t) \
-        g_hash_table_insert(qif_bangtype_map, ts, GINT_TO_POINTER(t)); \
-        g_hash_table_insert(qif_bangtype_map, _(ts), GINT_TO_POINTER(t));
-
-static void
-build_bangtype_map()
-{
-    g_return_if_fail(!qif_bangtype_map);
-
-    qif_bangtype_map = g_hash_table_new(g_str_hash, g_str_equal);
-    g_assert(qif_bangtype_map);
-
-    /* Translators FIXME: It is unclear whether these strings should
-       really be translated, and if yes, into which translation. */
-    QIF_ADD_TYPE(N_("type:bank"), QIF_TYPE_BANK);
-    QIF_ADD_TYPE(N_("type:cash"), QIF_TYPE_CASH);
-    QIF_ADD_TYPE(N_("type:ccard"), QIF_TYPE_CCARD);
-    QIF_ADD_TYPE(N_("type:invst"), QIF_TYPE_INVST);
-    QIF_ADD_TYPE(N_("type:port"), QIF_TYPE_PORT);
-    QIF_ADD_TYPE(N_("type:oth a"), QIF_TYPE_OTH_A);
-    QIF_ADD_TYPE(N_("type:oth l"), QIF_TYPE_OTH_L);
-    QIF_ADD_TYPE(N_("type:class"), QIF_TYPE_CLASS);
-    QIF_ADD_TYPE(N_("type:cat"), QIF_TYPE_CAT);
-    QIF_ADD_TYPE(N_("type:security"), QIF_TYPE_SECURITY);
-    QIF_ADD_TYPE(N_("account"), QIF_ACCOUNT);
-    QIF_ADD_TYPE(N_("option:autoswitch"), QIF_AUTOSWITCH);
-    QIF_ADD_TYPE(N_("clear:autoswitch"), QIF_CLEAR_AUTOSWITCH);
-}
-#undef QIF_ADD_TYPE
-
-#define QIF_ADD_ACT(ts,t) \
-        g_hash_table_insert(qif_action_map, ts, GINT_TO_POINTER(t));
-
-static void
-build_action_map()
-{
-    g_return_if_fail(!qif_action_map);
-
-    qif_action_map = g_hash_table_new(g_str_hash, g_str_equal);
-    g_assert(qif_action_map);
-
-    QIF_ADD_ACT("buy", QIF_A_BUY);
-    QIF_ADD_ACT("cvrshrt", QIF_A_BUY);
-    QIF_ADD_ACT("kauf", QIF_A_BUY);
-    QIF_ADD_ACT("buyx", QIF_A_BUYX);
-    QIF_ADD_ACT("cvrshrtx", QIF_A_BUYX);
-    QIF_ADD_ACT("kaufx", QIF_A_BUYX);
-    QIF_ADD_ACT("cglong", QIF_A_CGLONG);
-    QIF_ADD_ACT("kapgew", QIF_A_CGLONG); /* Kapitalgewinnsteuer */
-    QIF_ADD_ACT("cglongx", QIF_A_CGLONG);
-    QIF_ADD_ACT("kapgewx", QIF_A_CGLONG);
-    QIF_ADD_ACT("cgmid", QIF_A_CGMID);
-    QIF_ADD_ACT("cgmidx", QIF_A_CGMIDX);
-    QIF_ADD_ACT("cgshort", QIF_A_CGSHORT);
-    QIF_ADD_ACT("k.gewsp", QIF_A_CGSHORT);
-    QIF_ADD_ACT("cgshortx", QIF_A_CGSHORTX);
-    QIF_ADD_ACT("k.gewspx", QIF_A_CGSHORTX);
-    QIF_ADD_ACT("div", QIF_A_DIV); /* dividende */
-    QIF_ADD_ACT("divx", QIF_A_DIVX);
-    //QIF_ADD_ACT("exercise", QIF_A_EXERCISE);
-    //QIF_ADD_ACT("exercisex", QIF_A_EXERCISEX);
-    //QIF_ADD_ACT("expire", QIF_A_EXPIRE);
-    //QIF_ADD_ACT("grant", QIF_A_GRANT);
-    QIF_ADD_ACT("int", QIF_A_INTINC);
-    QIF_ADD_ACT("intinc", QIF_A_INTINC);
-    QIF_ADD_ACT("aktzu", QIF_A_INTINC); /* zinsen */
-    QIF_ADD_ACT("intx", QIF_A_INTINCX);
-    QIF_ADD_ACT("intincx", QIF_A_INTINCX);
-    QIF_ADD_ACT("margint", QIF_A_MARGINT);
-    QIF_ADD_ACT("margintx", QIF_A_MARGINTX);
-    QIF_ADD_ACT("miscexp", QIF_A_MISCEXP);
-    QIF_ADD_ACT("miscexpx", QIF_A_MISCEXPX);
-    QIF_ADD_ACT("miscinc", QIF_A_MISCINC);
-    QIF_ADD_ACT("cash", QIF_A_MISCINC);
-    QIF_ADD_ACT("miscincx", QIF_A_MISCINCX);
-    QIF_ADD_ACT("reinvdiv", QIF_A_REINVDIV);
-    QIF_ADD_ACT("reinvint", QIF_A_REINVINT);
-    QIF_ADD_ACT("reinvzin", QIF_A_REINVINT);
-    QIF_ADD_ACT("reinvlg", QIF_A_REINVLG);
-    QIF_ADD_ACT("reinvkur", QIF_A_REINVLG);
-    QIF_ADD_ACT("reinvmd", QIF_A_REINVMD);
-    QIF_ADD_ACT("reinvsg", QIF_A_REINVSG);
-    QIF_ADD_ACT("reinvksp", QIF_A_REINVSG);
-    QIF_ADD_ACT("reinvsh", QIF_A_REINVSH);
-    QIF_ADD_ACT("reminder", QIF_A_REMINDER);
-    QIF_ADD_ACT("erinnerg", QIF_A_REMINDER);
-    QIF_ADD_ACT("rtrncap", QIF_A_RTRNCAP);
-    QIF_ADD_ACT("rtrncapx", QIF_A_RTRNCAPX);
-    QIF_ADD_ACT("sell", QIF_A_SELL);
-    QIF_ADD_ACT("shtsell", QIF_A_SELL);
-    QIF_ADD_ACT("verkauf", QIF_A_SELL); /* verkaufen */
-    QIF_ADD_ACT("sellx", QIF_A_SELLX);
-    QIF_ADD_ACT("shtsellx", QIF_A_SELLX);
-    QIF_ADD_ACT("verkaufx", QIF_A_SELLX); /* verkaufen */
-    QIF_ADD_ACT("shrsin", QIF_A_SHRSIN);
-    QIF_ADD_ACT("aktzu", QIF_A_SHRSIN);
-    QIF_ADD_ACT("shrsout", QIF_A_SHRSOUT);
-    QIF_ADD_ACT("aktab", QIF_A_SHRSOUT);
-    QIF_ADD_ACT("stksplit", QIF_A_STKSPLIT);
-    QIF_ADD_ACT("aktsplit", QIF_A_STKSPLIT);
-    //QIF_ADD_ACT("vest", QIF_A_VEST);
-    QIF_ADD_ACT("xin", QIF_A_XIN);
-    QIF_ADD_ACT("contribx", QIF_A_XIN);
-    QIF_ADD_ACT("xout", QIF_A_XOUT);
-    QIF_ADD_ACT("withdrwx", QIF_A_XOUT);
-}
-#undef QIF_ADD_ACT
-
-static GList *
-make_list(int count, ...)
-{
-    GList *result = NULL;
-    GNCAccountType type;
-    va_list ap;
-
-    va_start (ap, count);
-    while (count--)
-    {
-        type = va_arg (ap, GNCAccountType);
-        result = g_list_prepend (result, GINT_TO_POINTER(type));
-    }
-    va_end (ap);
-
-
-    return g_list_reverse(result);
-}
-
-#define QIF_ADD_ATYPE(a,t) g_hash_table_insert(qif_atype_map, a, t);
-static void
-build_atype_map()
-{
-    g_return_if_fail(!qif_atype_map);
-
-    qif_atype_map = g_hash_table_new(g_str_hash, g_str_equal);
-    g_assert(qif_atype_map);
-
-    QIF_ADD_ATYPE("bank", make_list(1, ACCT_TYPE_BANK));
-    QIF_ADD_ATYPE("port", make_list(1, ACCT_TYPE_BANK));
-    QIF_ADD_ATYPE("cash", make_list(1, ACCT_TYPE_CASH));
-    QIF_ADD_ATYPE("ccard", make_list(1, ACCT_TYPE_CREDIT));
-    QIF_ADD_ATYPE("invst", make_list(3, ACCT_TYPE_BANK, ACCT_TYPE_STOCK,
-                                     ACCT_TYPE_MUTUAL));
-    QIF_ADD_ATYPE("oth a", make_list(3, ACCT_TYPE_ASSET, ACCT_TYPE_BANK,
-                                     ACCT_TYPE_CASH));
-    QIF_ADD_ATYPE("oth l", make_list(2, ACCT_TYPE_LIABILITY, ACCT_TYPE_CREDIT));
-    QIF_ADD_ATYPE("mutual", make_list(3, ACCT_TYPE_BANK, ACCT_TYPE_MUTUAL,
-                                      ACCT_TYPE_STOCK));
-
-    /* Internal types */
-    QIF_ADD_ATYPE("__any_bank__", make_list(5, ACCT_TYPE_BANK, ACCT_TYPE_CREDIT,
-                                            ACCT_TYPE_CASH, ACCT_TYPE_ASSET,
-                                            ACCT_TYPE_LIABILITY));
-    QIF_ADD_ATYPE("__all__", make_list(7, ACCT_TYPE_BANK, ACCT_TYPE_CREDIT,
-                                       ACCT_TYPE_CASH, ACCT_TYPE_ASSET,
-                                       ACCT_TYPE_LIABILITY, ACCT_TYPE_STOCK,
-                                       ACCT_TYPE_MUTUAL));
-    QIF_ADD_ATYPE("__stock__", make_list(2, ACCT_TYPE_STOCK, ACCT_TYPE_MUTUAL));
-    QIF_ADD_ATYPE("__income__", make_list(1, ACCT_TYPE_INCOME));
-    QIF_ADD_ATYPE("__expense__", make_list(1, ACCT_TYPE_EXPENSE));
-    QIF_ADD_ATYPE("__equity__", make_list(1, ACCT_TYPE_EQUITY));
-}
-#undef QIF_ADD_ATYPE
-
-/************************************************************************/
-
-/*
- * We've got a !Type line.  Parse the line into the appropriate
- * type and then initialize the handler.
- */
-void
-qif_parse_bangtype(QifContext ctx, const char *line)
-{
-    QifType type;
-    char *bangtype;
-    gpointer result;
-
-    g_return_if_fail(line && *line == '!');
-
-    if (!qif_bangtype_map)
-        build_bangtype_map();
-
-    /* Make a local copy so we can manipulate it.
-     * - strip off leading/trailing whitespace
-     * - make it all lower case
-     */
-    bangtype = g_utf8_strdown(line + 1, -1);
-    g_strstrip(bangtype);
-
-    /* In some cases we get "!Type Bank" -- change the space to a colon */
-    if (!strncmp(bangtype, "type ", 5))
-        bangtype[5] = ':';
-
-    /* Lookup the bangtype in the map and then destroy the local copy */
-    result = g_hash_table_lookup(qif_bangtype_map, bangtype);
-    g_free(bangtype);
-
-    if (!result)
-    {
-        PWARN("Unknown bang-type at line %d: %s.  Ignored", ctx->lineno, line);
-        return;
-    }
-    type = GPOINTER_TO_INT(result);
-
-    /* Set the current context parse type and handler */
-    ctx->parse_type = type;
-    ctx->handler = qif_handlers[type];
-
-    /* now initialize this new parse type (if there's an init function) */
-    if (ctx->handler && ctx->handler->init)
-        ctx->handler->init(ctx);
-}
-
-/* returns TRUE if successful, FALSE if there is a problem */
-gboolean
-qif_parse_split_category(const char* str,
-                         char** cat, gboolean *cat_is_acct, char** cat_class,
-                         char** miscx_cat, gboolean *miscx_cat_is_acct,
-                         char **miscx_class)
-{
-    /* This is a pretty f**ked up string.  Basically it looks like:
-     *  ([)cat-or-acct(])(/(class))(|([)cat-of-acct(])(/ext))
-     *
-     * where data in parens is "optional" (depending on the context).
-     *
-     * examples from reality:
-     *
-     * category
-     * category:subcategory
-     * category/class
-     * category:subcat/class
-     * [account]
-     * [account]/class
-     *
-     * cat/cat-class|miscx-cat/miscx-class
-     */
-
-    regmatch_t pmatch[12];
-
-    g_return_val_if_fail(cat && cat_is_acct && cat_class &&
-                         miscx_cat && miscx_cat_is_acct && miscx_class, FALSE);
-
-
-    if (!qifp_regex_compiled)
-        compile_regex();
-
-    if (regexec(&category_regex, str, 12, pmatch, 0) != 0)
-    {
-        PERR("category match failed");
-        return FALSE;
-    }
-
-    /*
-     * what the substrings mean:
-     * 1 the opening [ for a transfer
-     * 2 the category
-     * 3 the closing ]
-     * 4 the class /
-     * 5 the class
-     * 6 the miscx expression (whole thing)
-     * 7 the opening [
-     * 8 the miscx category
-     * 9 the closing ]
-     * 10 the class /
-     * 11 the class
-     */
-
-    if (pmatch[2].rm_so == -1)
-    {
-        PERR("no category match found!");
-        return FALSE;
-    }
-
-    /* catgory name */
-    *cat = g_strndup(str + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so);
-    /* category is account? */
-    *cat_is_acct = (pmatch[1].rm_so != -1 && pmatch[3].rm_so != -1);
-    /* category class */
-    *cat_class = (pmatch[4].rm_so != -1 ?
-                  g_strndup(str + pmatch[5].rm_so, pmatch[5].rm_eo - pmatch[5].rm_so) :
-                  NULL);
-
-    /* miscx category name */
-    *miscx_cat = (pmatch[6].rm_so != -1 ?
-                  g_strndup(str + pmatch[8].rm_so, pmatch[8].rm_eo - pmatch[8].rm_so) :
-                  NULL);
-    /* miscx cat is acct */
-    *miscx_cat_is_acct  = (pmatch[7].rm_so != -1 && pmatch[9].rm_so != -1);
-    /* miscx class */
-    *miscx_class = (pmatch[10].rm_so != -1 ?
-                    g_strndup(str + pmatch[11].rm_so,
-                              pmatch[11].rm_eo - pmatch[11].rm_so) : NULL);
-
-    return TRUE;
-}
-
-/*
- * qif_parse_cleared -- parse the 'C'leared field of a QIF Transaction.
- * returns the QIF reconciled flag.
- *
- * * means cleared, x or X means reconciled, and ! or ? mean some
- * budget related stuff I don't understand.
- */
-QifRecnFlag
-qif_parse_cleared(QifLine line)
-{
-    g_return_val_if_fail(line, QIF_R_NO);
-    g_return_val_if_fail(line->line, QIF_R_NO);
-
-    switch (*line->line)
-    {
-    case '*':
-        return QIF_R_CLEARED;
-    case 'x':
-    case 'X':
-        return QIF_R_RECONCILED;
-    case '?':
-    case '!':
-        return QIF_R_BUDGETED;
-    default:
-        PERR("Unknown QIF Cleared flag at line %d: %s", line->lineno, line->line);
-        return QIF_R_NO;
-    }
-}
-
-QifAction qif_parse_action(QifLine line)
-{
-    QifAction qaction;
-    gpointer result;
-    char *action;
-
-    g_return_val_if_fail(line, QIF_A_NONE);
-    g_return_val_if_fail(line->line, QIF_A_NONE);
-
-    if (!qif_action_map)
-        build_action_map();
-
-    /* Duplicate the action and force it to lower case and strip any spaces */
-    action = g_utf8_strdown(line->line, -1);
-    g_strstrip(action);
-
-    result = g_hash_table_lookup(qif_action_map, action);
-    g_free(action);
-
-    if (!result)
-    {
-        /* XXX: pop up a dialog? */
-        PWARN("Unknown Action at line %d: %s.  Some transactions may be discarded",
-              line->lineno, line->line);
-        return QIF_A_NONE;
-    }
-
-    qaction = GPOINTER_TO_INT(result);
-    return qaction;
-}
-
-GList * qif_parse_acct_type(const char *str, gint lineno)
-{
-    GList *result;
-    char *type;
-
-    if (!qif_atype_map)
-        build_atype_map();
-
-    /* Duplicate the type and force it to lower case and strip any spaces */
-    type = g_utf8_strdown(str, -1);
-    g_strstrip(type);
-
-    result = g_hash_table_lookup(qif_atype_map, type);
-    g_free(type);
-
-    if (!result)
-    {
-        PWARN("Unknown account type at line %d: %s. ", lineno, str);
-        result = g_hash_table_lookup(qif_atype_map, "bank");
-        g_return_val_if_fail(result, NULL);
-    }
-
-    return result;
-}
-
-GList * qif_parse_acct_type_guess(QifType type)
-{
-    const char *atype = NULL;
-
-    switch (type)
-    {
-    case QIF_TYPE_BANK:
-        atype = "bank";
-        break;
-    case QIF_TYPE_CASH:
-        atype = "cash";
-        break;
-    case QIF_TYPE_CCARD:
-        atype = "ccard";
-        break;
-    case QIF_TYPE_INVST:
-        atype = "invst";
-        break;
-    case QIF_TYPE_PORT:
-        atype = "port";
-        break;
-    case QIF_TYPE_OTH_A:
-        atype = "oth a";
-        break;
-    case QIF_TYPE_OTH_L:
-        atype = "oth l";
-        break;
-    default:
-        return NULL;
-    }
-
-    return qif_parse_acct_type(atype, -1);
-}
-
-/***********************************************************************
- * Parsing numbers and dates...
- */
-
-typedef struct _parse_helper
-{
-    QifContext                ctx;
-
-    GncImportFormat        budget;
-    GncImportFormat        limit;
-    GncImportFormat        amount;
-    GncImportFormat        d_amount;
-    GncImportFormat        price;
-    GncImportFormat        shares;
-    GncImportFormat        commission;
-    GncImportFormat        date;
-} *parse_helper_t;
-
-#define QIF_PARSE_CHECK_NUMBER(str,help) { \
-        if (str) (help) = gnc_import_test_numeric((str), (help)); \
-}
-#define QIF_PARSE_PARSE_NUMBER(str,fmt,val) { \
-        if (str) gnc_import_parse_numeric((str), (fmt), (val)); \
-}
-
-static void
-qif_parse_check_account(gpointer key, gpointer val, gpointer data)
-{
-    parse_helper_t helper = data;
-    QifAccount acct = val;
-
-    QIF_PARSE_CHECK_NUMBER(acct->limitstr, helper->limit);
-    QIF_PARSE_CHECK_NUMBER(acct->budgetstr, helper->budget);
-}
-
-static void
-qif_parse_parse_account(gpointer key, gpointer val, gpointer data)
-{
-    parse_helper_t helper = data;
-    QifAccount acct = val;
-
-    QIF_PARSE_PARSE_NUMBER(acct->limitstr, helper->limit, &acct->limit);
-    QIF_PARSE_PARSE_NUMBER(acct->budgetstr, helper->budget, &acct->budget);
-}
-
-static void
-qif_parse_check_category(gpointer key, gpointer val, gpointer data)
-{
-    parse_helper_t helper = data;
-    QifCategory cat = val;
-
-    QIF_PARSE_CHECK_NUMBER(cat->budgetstr, helper->budget);
-}
-
-static void
-qif_parse_parse_category(gpointer key, gpointer val, gpointer data)
-{
-    parse_helper_t helper = data;
-    QifCategory cat = val;
-
-    QIF_PARSE_PARSE_NUMBER(cat->budgetstr, helper->budget, &cat->budget);
-}
-
-static void
-qif_parse_check_txn(gpointer val, gpointer data)
-{
-    parse_helper_t helper = data;
-    QifTxn txn = val;
-    QifSplit split;
-    QifInvstTxn itxn;
-    GList *node;
-
-    /* Check the date */
-    helper->date = gnc_import_test_date(txn->datestr, helper->date);
-
-    /* If this is an investment transaction, then all the info is in
-     * the invst_info.  Otherwise it's all in the splits.
-     */
-    itxn = txn->invst_info;
-    if (itxn)
-    {
-        QIF_PARSE_CHECK_NUMBER(itxn->amountstr, helper->amount);
-        QIF_PARSE_CHECK_NUMBER(itxn->d_amountstr, helper->d_amount);
-        QIF_PARSE_CHECK_NUMBER(itxn->pricestr, helper->price);
-        QIF_PARSE_CHECK_NUMBER(itxn->sharesstr, helper->shares);
-        QIF_PARSE_CHECK_NUMBER(itxn->commissionstr, helper->commission);
-
-    }
-    else
-    {
-        split = txn->default_split;
-        node = txn->splits;
-        do
-        {
-            QIF_PARSE_CHECK_NUMBER(split->amountstr, helper->amount);
-
-            if (node)
-            {
-                split = node->data;
-                node = node->next;
-            }
-            else
-                split = NULL;
-        }
-        while (split);
-    }
-}
-
-static void
-qif_parse_parse_txn(gpointer val, gpointer data)
-{
-    parse_helper_t helper = data;
-    QifTxn txn = val;
-    QifSplit split;
-    QifInvstTxn itxn;
-    GList *node;
-
-    /* Parse the date */
-    gnc_import_parse_date(txn->datestr, helper->date, &txn->date);
-
-    /* If this is an investment transaction, then all the info is in
-     * the invst_info.  Otherwise it's all in the splits.
-     */
-    itxn = txn->invst_info;
-    if (itxn)
-    {
-        QIF_PARSE_PARSE_NUMBER(itxn->amountstr, helper->amount, &itxn->amount);
-        QIF_PARSE_PARSE_NUMBER(itxn->d_amountstr, helper->d_amount, &itxn->d_amount);
-        QIF_PARSE_PARSE_NUMBER(itxn->pricestr, helper->price, &itxn->price);
-        QIF_PARSE_PARSE_NUMBER(itxn->sharesstr, helper->shares, &itxn->shares);
-        QIF_PARSE_PARSE_NUMBER(itxn->commissionstr, helper->commission,
-                               &itxn->commission);
-
-        qif_invst_txn_setup_splits(helper->ctx, txn);
-
-    }
-    else
-    {
-        split = txn->default_split;
-        node = txn->splits;
-        do
-        {
-            QIF_PARSE_PARSE_NUMBER(split->amountstr, helper->amount, &split->amount);
-
-            if (node)
-            {
-                split = node->data;
-                node = node->next;
-            }
-            else
-                split = NULL;
-        }
-        while (split);
-
-        qif_txn_setup_splits(txn);
-    }
-}
-
-void
-qif_parse_all(QifContext ctx, gpointer arg)
-{
-    struct _parse_helper helper;
-
-    helper.ctx = ctx;
-
-    /* PARSE ACCOUNTS */
-
-    /* First, figure out the formats */
-    helper.limit = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
-    helper.budget = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
-    qif_object_map_foreach(ctx, QIF_O_ACCOUNT, qif_parse_check_account, &helper);
-
-    /* Make sure it's not ambiguous */
-    if (helper.limit & (helper.limit - 1)) helper.limit = GNCIF_NUM_PERIOD;
-    if (helper.budget & (helper.budget - 1)) helper.budget = GNCIF_NUM_PERIOD;
-
-    /* Now convert the numbers */
-    qif_object_map_foreach(ctx, QIF_O_ACCOUNT, qif_parse_parse_account, &helper);
-
-    /* PARSE CATEGORIES */
-
-    helper.budget = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
-    qif_object_map_foreach(ctx, QIF_O_CATEGORY, qif_parse_check_category, &helper);
-
-    /* make sure it's not ambiguous */
-    if (helper.budget & (helper.budget - 1)) helper.budget = GNCIF_NUM_PERIOD;
-
-    /* Now convert the numbers */
-    qif_object_map_foreach(ctx, QIF_O_CATEGORY, qif_parse_parse_category, &helper);
-
-    /* PARSE TRANSACTIONS */
-    helper.amount = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
-    helper.d_amount = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
-    helper.price = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
-    helper.shares = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
-    helper.commission = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
-    helper.date = GNCIF_DATE_MDY | GNCIF_DATE_DMY | GNCIF_DATE_YMD | GNCIF_DATE_YDM;
-
-    qif_object_list_foreach(ctx, QIF_O_TXN, qif_parse_check_txn, &helper);
-
-    /* check/fix ambiguities */
-    if (helper.amount & (helper.amount - 1)) helper.amount = GNCIF_NUM_PERIOD;
-    if (helper.d_amount & (helper.d_amount - 1)) helper.d_amount = GNCIF_NUM_PERIOD;
-    if (helper.price & (helper.price - 1)) helper.price = GNCIF_NUM_PERIOD;
-    if (helper.shares & (helper.shares - 1)) helper.shares = GNCIF_NUM_PERIOD;
-    if (helper.commission & (helper.commission - 1))
-        helper.commission = GNCIF_NUM_PERIOD;
-
-    if (helper.date & (helper.date - 1))
-    {
-        helper.date = gnc_import_choose_fmt(_("The Date format is ambiguous.  "
-                                              "Please choose the correct format."),
-                                            helper.date, arg);
-    }
-
-    /* now parse it.. */
-    qif_object_list_foreach(ctx, QIF_O_TXN, qif_parse_parse_txn, &helper);
-}
-
-typedef struct
-{
-    QifContext        ctx;
-    GList *        list;
-    const char*        type;
-} qif_merge_t;
-
-static void
-qif_merge_accts(gpointer key, gpointer value, gpointer data)
-{
-    qif_merge_t *merge = data;
-    QifAccount acct = value;
-
-    /* Merge into the context.  Remember items moved into the parent */
-    if (qif_account_merge(merge->ctx, acct) == acct)
-        merge->list = g_list_prepend(merge->list, acct->name);
-}
-
-static void
-qif_merge_cats(gpointer key, gpointer value, gpointer data)
-{
-    qif_merge_t *merge = data;
-    QifCategory cat = value;
-
-    /* Merge into the context.  Remember items moved into the parent */
-    if (qif_cat_merge(merge->ctx, cat) == cat)
-        merge->list = g_list_prepend(merge->list, cat->name);
-}
-
-static void
-qif_merge_classes(gpointer key, gpointer value, gpointer data)
-{
-    qif_merge_t *merge = data;
-    QifClass qclass = value;
-
-    /* Merge into the context.  Remember items moved into the parent */
-    if (qif_class_merge(merge->ctx, qclass) == qclass)
-        merge->list = g_list_prepend(merge->list, qclass->name);
-}
-
-static void
-qif_merge_secs(gpointer key, gpointer value, gpointer data)
-{
-    qif_merge_t *merge = data;
-    QifSecurity sec = value;
-
-    /* Merge into the context.  Remember items moved into the parent */
-    if (qif_security_merge(merge->ctx, sec) == sec)
-        merge->list = g_list_prepend(merge->list, sec->name);
-}
-
-static void
-qif_merge_del(gpointer obj, gpointer data)
-{
-    qif_merge_t *merge = data;
-    const char *name = obj;
-
-    qif_object_map_remove(merge->ctx, merge->type, name);
-}
-
-static void
-qif_massage_split(QifSplit split, QifContext ctx)
-{
-    const char *type = QIF_O_CATEGORY;
-    char *name;
-
-    if (split->cat.obj)
-    {
-        if (split->cat_is_acct)
-        {
-            type = QIF_O_ACCOUNT;
-            name = split->cat.acct->name;
-        }
-        else
-            name = split->cat.cat->name;
-
-        split->cat.obj = qif_object_map_lookup(ctx, type, name);
-    }
-
-    if (split->cat_class)
-    {
-        split->cat_class = (QifClass) qif_object_map_lookup(ctx, QIF_O_CLASS,
-                           split->cat_class->name);
-    }
-}
-
-static void
-qif_massage_itxn(QifInvstTxn itxn, QifContext ctx)
-{
-    const char *type = QIF_O_CATEGORY;
-    char *name;
-
-    if (itxn->far_cat.obj)
-    {
-        if (itxn->far_cat_is_acct)
-        {
-            type = QIF_O_ACCOUNT;
-            name = itxn->far_cat.acct->name;
-        }
-        else
-            name = itxn->far_cat.cat->name;
-
-        itxn->far_cat.obj = qif_object_map_lookup(ctx, type, name);
-    }
-}
-
-static void
-qif_massage_txn(gpointer obj, gpointer data)
-{
-    QifTxn txn = obj;
-    QifContext ctx = data;
-    QifSplit split;
-    GList *node;
-
-    if (txn->from_acct)
-        txn->from_acct = (QifAccount) qif_object_map_lookup(ctx, QIF_O_ACCOUNT,
-                         txn->from_acct->name);
-
-    if (txn->invst_info)
-        qif_massage_itxn(txn->invst_info, ctx);
-
-    if (txn->default_split)
-        qif_massage_split(txn->default_split, ctx);
-
-    for (node = txn->splits; node; node = node->next)
-    {
-        split = node->data;
-        qif_massage_split(split, ctx);
-    }
-}
-
-void
-qif_parse_merge_files(QifContext ctx)
-{
-    GList *node;
-    GList *accts = NULL;
-    GList *cats = NULL;
-    GList *classes = NULL;
-    GList *securities = NULL;
-    QifContext fctx;
-
-    qif_merge_t merge;
-
-    g_return_if_fail(ctx);
-
-    /* Make sure each of the "file" contexts have been parsed.
-     * note that we don't care about OUR context -- we can run this
-     * process multiple times safely.
-     */
-    for (node = ctx->files; node; node = node->next)
-    {
-        fctx = node->data;
-        g_return_if_fail(fctx->parsed);
-    }
-
-
-    /* Iterate over each file.  Merge the Accounts, Categories, Classes,
-     * Securities, and Transactions into the top-level context.  Be sure
-     * to re-point all Transaction/Split category/class/account pointers
-     * to the new top-level item.  Then be sure to remove the
-     * "duplicated" items so we don't double-free (as we don't refcount,
-     * either).
-     */
-    for (node = ctx->files; node; node = node->next)
-    {
-        fctx = node->data;
-
-        /* Merge accts, categories, classes, and securities */
-
-        merge.ctx = ctx;
-        merge.list = NULL;
-        qif_object_map_foreach(fctx, QIF_O_ACCOUNT, qif_merge_accts, &merge);
-        accts = merge.list;
-
-        merge.list = NULL;
-        qif_object_map_foreach(fctx, QIF_O_CATEGORY, qif_merge_cats, &merge);
-        cats = merge.list;
-
-        merge.list = NULL;
-        qif_object_map_foreach(fctx, QIF_O_CLASS, qif_merge_classes, &merge);
-        classes = merge.list;
-
-        merge.list = NULL;
-        qif_object_map_foreach(fctx, QIF_O_SECURITY, qif_merge_secs, &merge);
-        securities = merge.list;
-
-
-        /* repoint the transactions to the merged context data */
-        qif_object_list_foreach(fctx, QIF_O_TXN, qif_massage_txn, ctx);
-
-
-        /* then remove from the file context objects referenced in the top context */
-        merge.ctx = fctx;
-        merge.type = QIF_O_ACCOUNT;
-        g_list_foreach(accts, qif_merge_del, &merge);
-        g_list_free(accts);
-
-        merge.type = QIF_O_CATEGORY;
-        g_list_foreach(cats, qif_merge_del, &merge);
-        g_list_free(cats);
-
-        merge.type = QIF_O_CLASS;
-        g_list_foreach(classes, qif_merge_del, &merge);
-        g_list_free(classes);
-
-        merge.type = QIF_O_SECURITY;
-        g_list_foreach(securities, qif_merge_del, &merge);
-        g_list_free(securities);
-
-    }
-
-    /* We've been parsed */
-    ctx->parsed = TRUE;
-}
diff --git a/gnucash/import-export/qif/qif-parse.h b/gnucash/import-export/qif/qif-parse.h
deleted file mode 100644
index 11d439c..0000000
--- a/gnucash/import-export/qif/qif-parse.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * qif-parse.h -- routines for parsing pieces of a QIF file
- *
- * Written By:	Derek Atkins  <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#ifndef QIF_PARSE_H
-#define QIF_PARSE_H
-
-#include "qif-import.h"
-
-void qif_register_handler(QifType type, QifHandler handler);
-void qif_parse_bangtype(QifContext ctx, const char *line);
-
-gboolean
-qif_parse_split_category(const char* str,
-                         char** cat, gboolean *cat_is_acct, char** cat_class,
-                         char** miscx_cat, gboolean *miscx_cat_is_acct,
-                         char **miscx_class);
-
-gboolean qif_parse_numeric(QifLine line, gnc_numeric *num);
-QifRecnFlag qif_parse_cleared(QifLine line);
-QifAction qif_parse_action(QifLine line);
-
-/* The caller should never destroy this list */
-GList * qif_parse_acct_type(const char *str, gint lineno);
-GList * qif_parse_acct_type_guess(QifType type);
-
-/* Parse all objects */
-void qif_parse_all(QifContext ctx, gpointer ui_args);
-
-#endif /* QIF_PARSE_H */
diff --git a/gnucash/import-export/qif/test/CMakeLists.txt b/gnucash/import-export/qif/test/CMakeLists.txt
deleted file mode 100644
index d874ca3..0000000
--- a/gnucash/import-export/qif/test/CMakeLists.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-
-set(QIF_TEST_INCLUDE_DIRS
-  ${CMAKE_BINARY_DIR}/common
-  ${CMAKE_SOURCE_DIR}/gnucash/import-export/qif
-  ${CMAKE_SOURCE_DIR}/libgnucash/engine
-  ${CMAKE_SOURCE_DIR}/common/test-core
-  ${GLIB2_INCLUDE_DIRS}
-)
-set(QIF_TEST_LIBS gncmod-qif test-core)
-
-if (FALSE)
-  # Tests for this directory are not run.
-  gnc_add_test(test-link-qif test-link.c QIF_TEST_INCLUDE_DIRS QIF_TEST_LIBS)
-  gnc_add_test(test-qif test-qif.c QIF_TEST_INCLUDE_DIRS QIF_TEST_LIBS
-    GNC_TEST_FILES=${CMAKE_CURRENT_SOURCE_DIR}/test-files)
-endif()
-
-set_dist_list(test_qif_DIST CMakeLists.txt test-link.c test-qif.c test-files/test-1-bank-txn.qif)
diff --git a/gnucash/import-export/qif/test/test-files/test-1-bank-txn.qif b/gnucash/import-export/qif/test/test-files/test-1-bank-txn.qif
deleted file mode 100644
index 59592d0..0000000
--- a/gnucash/import-export/qif/test/test-files/test-1-bank-txn.qif
+++ /dev/null
@@ -1,6 +0,0 @@
-!Type:Bank
-D2003/01/27
-T123.45
-PTest Payee
-LTest Category
-^
diff --git a/gnucash/import-export/qif/test/test-link.c b/gnucash/import-export/qif/test/test-link.c
deleted file mode 100644
index fd55d42..0000000
--- a/gnucash/import-export/qif/test/test-link.c
+++ /dev/null
@@ -1,28 +0,0 @@
-/********************************************************************\
- * This program is free software; you can redistribute it and/or    *
- * modify it under the terms of the GNU General Public License as   *
- * published by the Free Software Foundation; either version 2 of   *
- * the License, or (at your option) any later version.              *
- *                                                                  *
- * This program is distributed in the hope that it will be useful,  *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
- * GNU General Public License for more details.                     *
- *                                                                  *
- * You should have received a copy of the GNU General Public License*
- * along with this program; if not, contact:                        *
- *                                                                  *
- * Free Software Foundation           Voice:  +1-617-542-5942       *
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
- * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
- *                                                                  *
-\********************************************************************/
-
-#include "qif-import.h"
-
-int
-main(int argc, char *argv[])
-{
-    qif_context_new();
-    return 0;
-}
diff --git a/gnucash/import-export/qif/test/test-qif.c b/gnucash/import-export/qif/test/test-qif.c
deleted file mode 100644
index c331bef..0000000
--- a/gnucash/import-export/qif/test/test-qif.c
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * test-qif.c -- Test the QIF Import routines.
- *
- * Created by:	Derek Atkins <derek at ihtfp.com>
- * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, contact:
- *
- * Free Software Foundation           Voice:  +1-617-542-5942
- * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
- * Boston, MA  02110-1301,  USA       gnu at gnu.org
- */
-
-#include <glib.h>
-#include <libguile.h>
-
-#include "gnc-module.h"
-#include "qif-import.h"
-#include "qif-import-p.h"	/* Let's test some internal stuff, too */
-
-#include "test-stuff.h"
-
-/* XXX */
-extern void qif_object_init(void);
-
-static QifContext
-test_qif_load_file(QifContext ctx, const char *filename,
-                   gint txn_count, gint acct_count, gboolean needs_acct)
-{
-    QifContext file;
-
-    printf("qif loading \"%s\"...\n", filename);
-    file = qif_file_new(ctx, filename);
-    do_test(file != NULL, "failed to read file");
-    if (!file) return NULL;
-
-    do_test(qif_object_list_count(file, QIF_O_TXN) == txn_count,
-            "Transaction count didn't match");
-    do_test(qif_object_map_count(file, QIF_O_ACCOUNT) == acct_count,
-            "Account count didn't match");
-    do_test(qif_file_needs_account(file) == needs_acct,
-            "Needs account flad didn't match");
-
-    return file;
-}
-
-static void
-test_qif(void)
-{
-    QifContext ctx, file;
-    char *filename;
-    const char *location = g_getenv("GNC_TEST_FILES");
-    int i;
-
-    ctx = qif_context_new();
-    do_test(ctx != NULL, "failed to create the qif context");
-    if (!ctx) return;
-
-    if (!location)
-        location = "test-files";
-
-    for (i = 0; i < 1; i++)
-    {
-        filename = g_strdup_printf("%s/%s", location, "test-1-bank-txn.qif");
-        file = test_qif_load_file(ctx, filename, 1, 0, TRUE);
-        g_free(filename);
-        if (!file) continue;
-
-        if (qif_file_needs_account(file))
-            qif_file_set_default_account(file, "test-1-bank-txn");
-
-        do_test(qif_file_needs_account(file) == FALSE,
-                "'Needs account' flag not cleared properly");
-
-        do_test(qif_file_parse(file, NULL) == QIF_E_OK,
-                "file failed to parse.");
-    }
-
-    qif_context_destroy(ctx);
-
-    success("QIF test successful");
-}
-
-static void
-main_helper(void *closure, int argc, char **argv)
-{
-    qif_object_init();		/* XXX:FIXME */
-    test_qif();
-    print_test_results();
-    exit(get_rv());
-}
-
-int
-main(int argc, char **argv)
-{
-    scm_boot_guile(argc, argv, main_helper, NULL);
-    return 0;
-}
-
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 426b3f2..ede339f 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -347,9 +347,7 @@ gnucash/import-export/ofx/gnc-ofx-import.c
 gnucash/import-export/ofx/gnc-ofx-kvp.c
 gnucash/import-export/ofx/gnc-plugin-ofx.c
 gnucash/import-export/ofx/gschemas/org.gnucash.dialogs.import.ofx.gschema.xml.in
-gnucash/import-export/qif/qif-context.c
-gnucash/import-export/qif/qif-file.c
-gnucash/import-export/qif/qif-objects.c
+gnucash/import-export/qif-imp/.#assistant-qif-import.c
 gnucash/import-export/qif-imp/assistant-qif-import.c
 gnucash/import-export/qif-imp/dialog-account-picker.c
 gnucash/import-export/qif-imp/gncmod-qif-import.c
@@ -486,8 +484,7 @@ gnucash/report/standard-reports/general-journal.scm
 gnucash/report/standard-reports/general-ledger.scm
 gnucash/report/standard-reports/income-gst-statement.scm
 gnucash/report/standard-reports/income-statement.scm
-gnucash/report/standard-reports/net-barchart.scm
-gnucash/report/standard-reports/net-linechart.scm
+gnucash/report/standard-reports/net-charts.scm
 gnucash/report/standard-reports/portfolio.scm
 gnucash/report/standard-reports/price-scatter.scm
 gnucash/report/standard-reports/register.scm
@@ -575,7 +572,6 @@ libgnucash/backend/sql/gnc-sql-result.cpp
 libgnucash/backend/sql/gnc-tax-table-sql.cpp
 libgnucash/backend/sql/gnc-transaction-sql.cpp
 libgnucash/backend/sql/gnc-vendor-sql.cpp
-libgnucash/backend/xml/.#gnc-invoice-xml-v2.cpp
 libgnucash/backend/xml/gnc-account-xml-v2.cpp
 libgnucash/backend/xml/gnc-address-xml-v2.cpp
 libgnucash/backend/xml/gnc-backend-xml.cpp

commit 9f1bfddc27965f50295f507b062d63b8612d1c9b
Author: John Ralls <jralls at ceridwen.us>
Date:   Fri Jun 15 11:30:09 2018 -0700

    Revert "Remove abandoned C-language QIF implementation."
    
    Because it included some extraneous changes.
    This reverts commit edd439a05e1468b1f55d0df7c05441b67aac8787.

diff --git a/gnucash/import-export/CMakeLists.txt b/gnucash/import-export/CMakeLists.txt
index cca8359..3b52ed8 100644
--- a/gnucash/import-export/CMakeLists.txt
+++ b/gnucash/import-export/CMakeLists.txt
@@ -10,6 +10,7 @@ add_subdirectory(csv-imp)
 add_subdirectory(customer-import)
 add_subdirectory(log-replay)
 add_subdirectory(ofx)
+add_subdirectory(qif)
 add_subdirectory(qif-imp)
 
 
diff --git a/gnucash/import-export/qif-imp/assistant-qif-import.c b/gnucash/import-export/qif-imp/assistant-qif-import.c
index e424182..21f3e74 100644
--- a/gnucash/import-export/qif-imp/assistant-qif-import.c
+++ b/gnucash/import-export/qif-imp/assistant-qif-import.c
@@ -59,7 +59,6 @@
 #include "gnc-prefs.h"
 #include "gnc-ui.h"
 #include "guile-mappings.h"
-#include <gfec.h>
 
 #include "swig-runtime.h"
 
@@ -1068,12 +1067,6 @@ gnc_ui_qif_import_commodity_update(QIFImportWindow * wind)
     }
 }
 
-static void
-_gfec_error_handler(const char *message)
-{
-    PERR("qif-import:qif-to-gnc-undo encountered an error: %s", message);
-}
-
 
 /****************************************************************
  * gnc_ui_qif_import_convert_undo
@@ -1089,7 +1082,7 @@ gnc_ui_qif_import_convert_undo(QIFImportWindow * wind)
     gnc_set_busy_cursor(NULL, TRUE);
 
     /* Undo the conversion. */
-    gfec_apply(undo, wind->imported_account_tree, _gfec_error_handler);
+    scm_call_1(undo, wind->imported_account_tree);
 
     /* There's no imported account tree any more. */
     scm_gc_unprotect_object(wind->imported_account_tree);
diff --git a/gnucash/import-export/qif-imp/qif-to-gnc.scm b/gnucash/import-export/qif-imp/qif-to-gnc.scm
index 442cb47..5f7e3f1 100644
--- a/gnucash/import-export/qif-imp/qif-to-gnc.scm
+++ b/gnucash/import-export/qif-imp/qif-to-gnc.scm
@@ -910,9 +910,7 @@
     ;; this is the grind loop.  Go over every unmarked transaction in
     ;; the candidate-xtns list.
     (let xtn-loop ((xtns candidate-xtns))
-      (if (and (and
-                (and far-acct-name near acct-name)
-                (not (qif-xtn:mark (car xtns))))
+      (if (and (not (qif-xtn:mark (car xtns)))
                (string=? (qif-xtn:from-acct (car xtns)) far-acct-name))
           (begin
             (set! how
diff --git a/gnucash/import-export/qif/CMakeLists.txt b/gnucash/import-export/qif/CMakeLists.txt
new file mode 100644
index 0000000..608e14c
--- /dev/null
+++ b/gnucash/import-export/qif/CMakeLists.txt
@@ -0,0 +1,43 @@
+
+#Tests for this directory are not run.
+add_subdirectory(test)
+
+set(qif_SOURCES
+  qif-context.c
+  qif-defaults.c
+  qif-file.c
+  qif-objects.c
+  qif-parse.c
+)
+
+# Add dependency on config.h
+set_source_files_properties (${qif_SOURCES} PROPERTIES OBJECT_DEPENDS ${CONFIG_H})
+
+set(qif_noinst_HEADERS
+  qif-file.h
+  qif-defaults.h
+  qif-import-p.h
+  qif-import.h
+  qif-objects.h
+  qif-objects-p.h
+  qif-parse.h
+)
+
+add_library(gncmod-qif ${qif_noinst_HEADERS} ${qif_SOURCES})
+
+target_link_libraries(gncmod-qif gncmod-generic-import gncmod-engine ${GLIB2_LDFLAGS})
+
+target_compile_definitions(gncmod-qif PRIVATE -DG_LOG_DOMAIN=\"gnc.import.qif\")
+
+if (APPLE)
+  set_target_properties (gncmod-qif PROPERTIES INSTALL_NAME_DIR "${CMAKE_INSTALL_FULL_LIBDIR}/gnucash")
+endif()
+
+install(TARGETS gncmod-qif
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/gnucash
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/gnucash
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+# No headers to install.
+
+set_local_dist(qif_DIST_local CMakeLists.txt ${qif_SOURCES} ${qif_noinst_HEADERS})
+set(qif_DIST ${qif_DIST_local} ${test_qif_DIST} PARENT_SCOPE)
diff --git a/gnucash/import-export/qif/qif-context.c b/gnucash/import-export/qif/qif-context.c
new file mode 100644
index 0000000..17fa472
--- /dev/null
+++ b/gnucash/import-export/qif/qif-context.c
@@ -0,0 +1,417 @@
+/*
+ * qif-context.c -- create/destroy QIF Contexts
+ *
+ * Written By:	Derek Atkins <derek at ihtfp.com>
+ * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+
+#include "qif-import-p.h"
+#include "qif-objects-p.h"
+
+static void qif_object_map_get_helper(gpointer key, gpointer value, gpointer listp);
+
+QifContext
+qif_context_new(void)
+{
+    QifContext ctx = g_new0(struct _QifContext, 1);
+
+    ctx->object_lists = g_hash_table_new(g_str_hash, g_str_equal);
+    ctx->object_maps = g_hash_table_new(g_str_hash, g_str_equal);
+
+    return ctx;
+}
+
+void
+qif_context_destroy(QifContext ctx)
+{
+    GList *node, *temp;
+    QifContext fctx;
+
+    if (!ctx) return;
+
+    /* First, try to destroy all the children contexts */
+    for (node = ctx->files; node; node = temp)
+    {
+        fctx = node->data;
+        temp = node->next;
+        qif_context_destroy(fctx);
+    }
+
+    /* ok, at this point we're actually destroying this context. */
+
+    /* force the end of record */
+    if (ctx->handler && ctx->handler->end)
+        ctx->handler->end(ctx);
+
+    /* destroy the state objects */
+    qif_object_list_destroy(ctx);
+    qif_object_map_destroy(ctx);
+
+    /* Remove us from our parent context */
+    if (ctx->parent)
+        ctx->parent->files = g_list_remove(ctx->parent->files, ctx);
+
+    g_free(ctx->filename);
+
+    g_assert(ctx->files == NULL);
+    g_free(ctx);
+}
+
+static GList *
+qif_context_get_foo_helper(QifContext ctx, GFunc get_helper)
+{
+    GHashTable *ht;
+    GList *node, *list = NULL;
+    QifContext fctx;
+
+    g_return_val_if_fail(ctx, NULL);
+    g_return_val_if_fail(ctx->parsed, NULL);
+    g_return_val_if_fail(get_helper, NULL);
+
+    ht = g_hash_table_new(g_direct_hash, g_direct_equal);
+
+    for (node = ctx->files; node; node = node->next)
+    {
+        fctx = node->data;
+        qif_object_list_foreach(fctx, QIF_O_TXN, get_helper, ht);
+    }
+
+    g_hash_table_foreach(ht, qif_object_map_get_helper, &list);
+    g_hash_table_destroy(ht);
+
+    return list;
+}
+
+static void
+qif_get_accts_helper(gpointer obj, gpointer htp)
+{
+    QifTxn txn = obj;
+    QifSplit split;
+    GHashTable *ht = htp;
+    GList *node;
+
+    if (txn->from_acct)
+        g_hash_table_insert(ht, txn->from_acct, txn->from_acct);
+
+    /* The default_split is using the from_acct, so we can ignore it */
+
+    for (node = txn->splits; node; node = node->next)
+    {
+        split = node->data;
+        if (split->cat.obj && split->cat_is_acct)
+            g_hash_table_insert(ht, split->cat.acct, split->cat.acct);
+    }
+}
+
+GList *
+qif_context_get_accounts(QifContext ctx)
+{
+    return qif_context_get_foo_helper(ctx, qif_get_accts_helper);
+}
+
+static void
+qif_get_cats_helper(gpointer obj, gpointer htp)
+{
+    QifTxn txn = obj;
+    QifSplit split;
+    GHashTable *ht = htp;
+    GList *node;
+
+    /* default_split uses from_acct, so no categories */
+
+    for (node = txn->splits; node; node = node->next)
+    {
+        split = node->data;
+        if (split->cat.obj && !split->cat_is_acct)
+            g_hash_table_insert(ht, split->cat.cat, split->cat.cat);
+    }
+}
+
+GList *
+qif_context_get_categories(QifContext ctx)
+{
+    return qif_context_get_foo_helper(ctx, qif_get_cats_helper);
+}
+
+/*****************************************************************************/
+
+/*
+ * Insert and remove a QifObject from the Object Maps in this Qif Context
+ */
+
+gint
+qif_object_map_count(QifContext ctx, const char *type)
+{
+    GHashTable *ht;
+
+    g_return_val_if_fail(ctx, 0);
+    g_return_val_if_fail(ctx->object_maps, 0);
+    g_return_val_if_fail(type, 0);
+
+    ht = g_hash_table_lookup(ctx->object_maps, type);
+    if (!ht)
+        return 0;
+
+    return g_hash_table_size(ht);
+}
+
+void
+qif_object_map_foreach(QifContext ctx, const char *type, GHFunc func, gpointer arg)
+{
+    GHashTable *ht;
+
+    g_return_if_fail(ctx);
+    g_return_if_fail(ctx->object_maps);
+    g_return_if_fail(type);
+
+    ht = g_hash_table_lookup(ctx->object_maps, type);
+    if (ht)
+        g_hash_table_foreach(ht, func, arg);
+}
+
+void
+qif_object_map_insert(QifContext ctx, const char *key, QifObject obj)
+{
+    GHashTable *ht;
+
+    g_return_if_fail(ctx);
+    g_return_if_fail(ctx->object_maps);
+    g_return_if_fail(key);
+    g_return_if_fail(obj);
+    g_return_if_fail(obj->type);
+
+    ht = g_hash_table_lookup(ctx->object_maps, obj->type);
+    if (!ht)
+    {
+        ht = g_hash_table_new(g_str_hash, g_str_equal);
+        g_assert(ht);
+        g_hash_table_insert(ctx->object_maps, (gpointer)obj->type, ht);
+    }
+
+    g_hash_table_insert(ht, (gpointer)key, obj);
+}
+
+void
+qif_object_map_remove(QifContext ctx, const char *type, const char *key)
+{
+    GHashTable *ht;
+
+    g_return_if_fail(ctx);
+    g_return_if_fail(ctx->object_maps);
+    g_return_if_fail(type);
+    g_return_if_fail(key);
+
+    ht = g_hash_table_lookup(ctx->object_maps, type);
+    if (!ht) return;
+
+    g_hash_table_remove(ht, key);
+}
+
+QifObject
+qif_object_map_lookup(QifContext ctx, const char *type, const char *key)
+{
+    GHashTable *ht;
+
+    g_return_val_if_fail(ctx, NULL);
+    g_return_val_if_fail(ctx->object_maps, NULL);
+    g_return_val_if_fail(type, NULL);
+    g_return_val_if_fail(key, NULL);
+
+    ht = g_hash_table_lookup(ctx->object_maps, type);
+    if (!ht) return NULL;
+
+    return g_hash_table_lookup(ht, key);
+}
+
+/* This GList _SHOULD_ be freed by the caller */
+
+static void
+qif_object_map_get_helper(gpointer key, gpointer value, gpointer arg)
+{
+    GList **listp = arg;
+    g_return_if_fail(listp);
+
+    *listp = g_list_prepend(*listp, value);
+}
+
+GList *
+qif_object_map_get(QifContext ctx, const char *type)
+{
+    GHashTable *ht;
+    GList *list = NULL;
+
+    g_return_val_if_fail(ctx, NULL);
+    g_return_val_if_fail(ctx->object_maps, NULL);
+    g_return_val_if_fail(type, NULL);
+
+    ht = g_hash_table_lookup(ctx->object_maps, type);
+    if (!ht)
+        return NULL;
+
+    g_hash_table_foreach(ht, qif_object_map_get_helper, &list);
+
+    return list;
+}
+
+static gboolean
+qif_object_map_remove_each(gpointer key, gpointer value, gpointer arg)
+{
+    QifObject obj = value;
+    obj->destroy(obj);
+    return TRUE;
+}
+
+static gboolean
+qif_object_map_remove_all(gpointer key, gpointer value, gpointer arg)
+{
+    GHashTable *ht = value;
+
+    g_hash_table_foreach_remove(ht, qif_object_map_remove_each, NULL);
+    g_hash_table_destroy(ht);
+    return TRUE;
+}
+
+void qif_object_map_destroy(QifContext ctx)
+{
+    g_return_if_fail(ctx);
+    g_return_if_fail(ctx->object_maps);
+
+    g_hash_table_foreach_remove(ctx->object_maps, qif_object_map_remove_all, NULL);
+    g_hash_table_destroy(ctx->object_maps);
+}
+
+/*****************************************************************************/
+
+/*
+ * Insert and remove a QifObject from the Object Lists in this Qif Context
+ */
+
+void
+qif_object_list_reverse(QifContext ctx, const char *type)
+{
+    GList *list;
+
+    g_return_if_fail(ctx);
+    g_return_if_fail(ctx->object_lists);
+    g_return_if_fail(type);
+
+    list = qif_object_list_get(ctx, type);
+    list = g_list_reverse(list);
+    g_hash_table_insert(ctx->object_lists, (gpointer)type, list);
+}
+
+gint
+qif_object_list_count(QifContext ctx, const char *type)
+{
+    GList *list;
+
+    g_return_val_if_fail(ctx, 0);
+    g_return_val_if_fail(ctx->object_lists, 0);
+    g_return_val_if_fail(type, 0);
+
+    list = g_hash_table_lookup(ctx->object_lists, type);
+    return g_list_length(list);
+}
+
+void
+qif_object_list_foreach(QifContext ctx, const char *type, GFunc func, gpointer arg)
+{
+    GList *list;
+
+    g_return_if_fail(ctx);
+    g_return_if_fail(ctx->object_lists);
+    g_return_if_fail(type);
+
+    list = qif_object_list_get(ctx, type);
+    g_list_foreach(list, func, arg);
+}
+
+void
+qif_object_list_insert(QifContext ctx, QifObject obj)
+{
+    GList *list;
+
+    g_return_if_fail(ctx);
+    g_return_if_fail(ctx->object_lists);
+    g_return_if_fail(obj);
+    g_return_if_fail(obj->type && *obj->type);
+
+    list = g_hash_table_lookup(ctx->object_lists, obj->type);
+    list = g_list_prepend(list, obj);
+    g_hash_table_insert(ctx->object_lists, (gpointer)obj->type, list);
+}
+
+void
+qif_object_list_remove(QifContext ctx, QifObject obj)
+{
+    GList *list;
+
+    g_return_if_fail(ctx);
+    g_return_if_fail(ctx->object_lists);
+    g_return_if_fail(obj);
+    g_return_if_fail(obj->type && *obj->type);
+
+    list = g_hash_table_lookup(ctx->object_lists, obj->type);
+    list = g_list_remove(list, obj);
+    g_hash_table_insert(ctx->object_lists, (gpointer)obj->type, list);
+}
+
+GList *
+qif_object_list_get(QifContext ctx, const char *type)
+{
+    g_return_val_if_fail(ctx, NULL);
+    g_return_val_if_fail(ctx->object_lists, NULL);
+    g_return_val_if_fail(type, NULL);
+
+    return g_hash_table_lookup(ctx->object_lists, type);
+}
+
+static gboolean
+qif_object_list_remove_all(gpointer key, gpointer value, gpointer arg)
+{
+    GList *list = value;
+    GList *node;
+    QifObject obj;
+
+    for (node = list; node; node = node->next)
+    {
+        obj = node->data;
+        obj->destroy(obj);
+    }
+
+    g_list_free(list);
+    return TRUE;
+}
+
+void
+qif_object_list_destroy(QifContext ctx)
+{
+    g_return_if_fail(ctx);
+    g_return_if_fail(ctx->object_lists);
+
+    g_hash_table_foreach_remove(ctx->object_lists, qif_object_list_remove_all, NULL);
+    g_hash_table_destroy(ctx->object_lists);
+}
diff --git a/gnucash/import-export/qif/qif-defaults.c b/gnucash/import-export/qif/qif-defaults.c
new file mode 100644
index 0000000..e29c141
--- /dev/null
+++ b/gnucash/import-export/qif/qif-defaults.c
@@ -0,0 +1,152 @@
+/*
+ * qif-defaults.c -- QIF Defaults -- default accounts...
+ *
+ * Created by:	Derek Atkins <derek at ihtfp.com>
+ * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "gnc-helpers.h"
+#include "qif-import-p.h"
+#include "qif-objects-p.h"
+#include "qif-defaults.h"
+
+
+static GList *stock_list = NULL;
+static GList *ext_stock_list = NULL;
+static GList *income_list = NULL;
+static GList *expense_list = NULL;
+static GList *equity_list = NULL;
+
+#define RETURN_ACCT(c,n,l) { if (stock_list == NULL) acct_type_init(); \
+	return find_or_make_acct(c, n, l); \
+}
+
+static void
+acct_type_init(void)
+{
+    stock_list = qif_parse_acct_type("__stock__", -1);
+    ext_stock_list = qif_parse_acct_type("__extstock__", -1);
+    income_list = qif_parse_acct_type("__income__", -1);
+    expense_list = qif_parse_acct_type("__expense__", -1);
+    equity_list = qif_parse_acct_type("__equity__", -1);
+}
+
+QifAccount qif_default_equity_acct(QifContext ctx)
+{
+    char *name = g_strdup(_("Retained Earnings"));
+    RETURN_ACCT(ctx, name, equity_list);
+}
+
+QifAccount qif_default_margin_interest_acct(QifContext ctx)
+{
+    char *name = g_strdup_printf("%s%s%s", _("Margin Interest"),
+                                 gnc_get_account_separator_string(),
+                                 ctx->current_acct->name);
+    RETURN_ACCT(ctx, name, expense_list);
+}
+
+QifAccount qif_default_commission_acct(QifContext ctx)
+{
+    char *name = g_strdup_printf("%s%s%s", _("Commissions"),
+                                 gnc_get_account_separator_string(),
+                                 ctx->current_acct->name);
+    RETURN_ACCT(ctx, name, expense_list);
+}
+
+QifAccount qif_default_stock_acct(QifContext ctx, const char *security)
+{
+    char *name = g_strdup_printf("%s%s%s", ctx->current_acct->name,
+                                 gnc_get_account_separator_string(),
+                                 security);
+    RETURN_ACCT(ctx, name, stock_list);
+}
+
+QifAccount qif_default_cglong_acct(QifContext ctx, const char *security)
+{
+    char *name = g_strdup_printf("%s%s%s%s%s", _("Cap. gain (long)"),
+                                 gnc_get_account_separator_string(),
+                                 ctx->current_acct->name,
+                                 gnc_get_account_separator_string(),
+                                 security);
+    RETURN_ACCT(ctx, name, income_list);
+}
+
+QifAccount qif_default_cgmid_acct(QifContext ctx, const char *security)
+{
+    char *name = g_strdup_printf("%s%s%s%s%s", _("Cap. gain (mid)"),
+                                 gnc_get_account_separator_string(),
+                                 ctx->current_acct->name,
+                                 gnc_get_account_separator_string(),
+                                 security);
+    RETURN_ACCT(ctx, name, income_list);
+}
+
+QifAccount qif_default_cgshort_acct(QifContext ctx, const char *security)
+{
+    char *name = g_strdup_printf("%s%s%s%s%s", _("Cap. gain (short)"),
+                                 gnc_get_account_separator_string(),
+                                 ctx->current_acct->name,
+                                 gnc_get_account_separator_string(),
+                                 security);
+    RETURN_ACCT(ctx, name, income_list);
+}
+
+QifAccount qif_default_dividend_acct(QifContext ctx, const char *security)
+{
+    char *name = g_strdup_printf("%s%s%s%s%s", _("Dividends"),
+                                 gnc_get_account_separator_string(),
+                                 ctx->current_acct->name,
+                                 gnc_get_account_separator_string(),
+                                 security);
+    RETURN_ACCT(ctx, name, income_list);
+}
+
+QifAccount qif_default_interest_acct(QifContext ctx, const char *security)
+{
+    char *name = g_strdup_printf("%s%s%s%s%s", _("Interest"),
+                                 gnc_get_account_separator_string(),
+                                 ctx->current_acct->name,
+                                 gnc_get_account_separator_string(),
+                                 security);
+    RETURN_ACCT(ctx, name, income_list);
+}
+
+QifAccount qif_default_capital_return_acct(QifContext ctx, const char *security)
+{
+    char *name = g_strdup_printf("%s%s%s%s%s", _("Cap Return"),
+                                 gnc_get_account_separator_string(),
+                                 ctx->current_acct->name,
+                                 gnc_get_account_separator_string(),
+                                 security);
+    RETURN_ACCT(ctx, name, income_list);
+}
+
+QifAccount qif_default_equity_holding(QifContext ctx, const char *security)
+{
+    return qif_default_equity_acct(ctx);
+}
+
diff --git a/gnucash/import-export/qif/qif-defaults.h b/gnucash/import-export/qif/qif-defaults.h
new file mode 100644
index 0000000..30c6562
--- /dev/null
+++ b/gnucash/import-export/qif/qif-defaults.h
@@ -0,0 +1,44 @@
+/*
+ * qif-defaults.h -- QIF Defaults -- default accounts...
+ *
+ * Created by:	Derek Atkins <derek at ihtfp.com>
+ * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+#ifndef QIF_DEFAULTS_H
+#define QIF_DEFAULTS_H
+
+#include "qif-objects.h"
+#include "qif-import.h"
+
+QifAccount qif_default_equity_acct(QifContext ctx);
+QifAccount qif_default_equity_holding(QifContext ctx, const char *security);
+
+QifAccount qif_default_margin_interest_acct(QifContext ctx);
+QifAccount qif_default_commission_acct(QifContext ctx);
+QifAccount qif_default_stock_acct(QifContext ctx, const char *security);
+QifAccount qif_default_cglong_acct(QifContext ctx, const char *security);
+QifAccount qif_default_cgmid_acct(QifContext ctx, const char *security);
+QifAccount qif_default_cgshort_acct(QifContext ctx, const char *security);
+QifAccount qif_default_dividend_acct(QifContext ctx, const char *security);
+QifAccount qif_default_interest_acct(QifContext ctx, const char *security);
+QifAccount qif_default_capital_return_acct(QifContext ctx, const char *security);
+
+#endif /* QIF_DEFAULTS_H */
diff --git a/gnucash/import-export/qif/qif-file.c b/gnucash/import-export/qif/qif-file.c
new file mode 100644
index 0000000..775f0da
--- /dev/null
+++ b/gnucash/import-export/qif/qif-file.c
@@ -0,0 +1,326 @@
+/*
+ * qif-file.c -- parse a QIF File into its pieces
+ *
+ * Written by:  Derek Atkins  <derek@@ihtfp.com>
+ * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <string.h>
+
+#include "gnc-engine.h"
+
+#include "qif-import-p.h"
+#include "qif-objects-p.h"
+
+static QofLogModule log_module = GNC_MOD_IMPORT;
+
+
+static QifLine
+qif_make_line(const char* buf, gint lineno)
+{
+    QifLine line;
+    g_return_val_if_fail(buf && *buf, NULL);
+
+    line = g_new0(struct _QifLine, 1);
+    line->type = *buf;
+    line->lineno = lineno;
+    line->line = g_strdup(buf + 1);
+
+    return line;
+}
+
+void
+qif_record_destroy(GList *record)
+{
+    GList *node;
+    QifLine line;
+
+    for (node = record; node; node = node->next)
+    {
+        line = node->data;
+        g_free(line->line);
+        g_free(line);
+    }
+
+    g_list_free(record);
+}
+
+/* This returns a record, which is a bunch of QifLines, ending
+ * with a line with just a '^'.  If it finds a line that begins
+ * with a !, then destroy the current record state, set the "found_bangtype",
+ * and return NULL.
+ */
+static GList *
+qif_make_record(QifContext ctx, char *buf, size_t bufsiz, gboolean *found_bangtype)
+{
+    GList *record = NULL;
+    QifLine line;
+
+    g_return_val_if_fail(ctx, NULL);
+    g_return_val_if_fail(buf, NULL);
+    g_return_val_if_fail(found_bangtype, NULL);
+
+    *found_bangtype = FALSE;
+
+    while (fgets(buf, bufsiz, ctx->fp) != NULL)
+    {
+
+        /* increment the line number */
+        ctx->lineno++;
+
+        /* strip start/end whitespace */
+        g_strstrip(buf);
+
+        /* if there is nothing left in the string, ignore it */
+        if (strlen(buf) == 0)
+            continue;
+
+        /* If this is a bangline, then set the flag, clear our state, and return NULL */
+        if (*buf == '!')
+        {
+            *found_bangtype = TRUE;
+            break;
+        }
+
+        /* See if this is an End of Record marker */
+        if (*buf == '^')
+        {
+            /* Yep.  If we've got a record then break and return ... */
+            if (record)
+                break;
+            /* ... otherwise just continue reading (i.e. ignore empty records) */
+            else
+                continue;
+        }
+
+        /* otherwise, add the line to the list */
+        line = qif_make_line(buf, ctx->lineno);
+        if (line)
+            record = g_list_prepend(record, line);
+
+        /* and continue... */
+    }
+
+    /* If we found a bangtype, destroy anything we've collected */
+    if (*found_bangtype)
+    {
+        if (record)
+            PERR("error loading file: incomplete record at line %d", ctx->lineno);
+
+        qif_record_destroy(record);
+        record = NULL;
+    }
+
+    return g_list_reverse(record);
+}
+
+/* read a qif file and parse it, line by line
+ * return QIF_E_OK on success or some other QIF Error.
+ */
+static QifError
+qif_read_file(QifContext ctx, FILE *f)
+{
+    char buf[BUFSIZ];
+    GList *record;
+    gboolean found_bang;
+    QifError err = QIF_E_OK;
+
+    g_return_val_if_fail(ctx, QIF_E_BADARGS);
+    g_return_val_if_fail(f, QIF_E_BADARGS);
+
+    ctx->fp = f;
+    ctx->lineno = -1;
+
+    do
+    {
+        found_bang = FALSE;
+        record = qif_make_record(ctx, buf, sizeof(buf), &found_bang);
+
+        /* If we got a record, process it */
+        if (record)
+        {
+            if (!ctx->handler || !ctx->handler->parse_record)
+            {
+                PERR("Trying to process QIF record without a handler at %d", ctx->lineno);
+            }
+            else
+            {
+                err = ctx->handler->parse_record(ctx, record);
+            }
+
+            /* Now destroy it; we don't need it anymore */
+            qif_record_destroy(record);
+        }
+
+        /* if we found a bangtype, process that */
+        if (found_bang)
+        {
+            g_assert(*buf == '!');
+
+            /* First, process the end of the last handler.  This could possibly
+             * merge items into the context or perform some other operation
+             */
+            if (ctx->handler && ctx->handler->end)
+            {
+                err = ctx->handler->end(ctx);
+                if (err != QIF_E_OK)
+                    break;
+            }
+
+            /* Now process the bangtype (stored in buf) to set the new handler */
+            qif_parse_bangtype(ctx, buf);
+        }
+
+    }
+    while ((record || found_bang) && err == QIF_E_OK);
+
+    /* Make sure to run any end processor */
+    if (err == QIF_E_OK && ctx->handler && ctx->handler->end)
+        err = ctx->handler->end(ctx);
+
+    if (err == QIF_E_OK)
+        qif_object_list_reverse(ctx, QIF_O_TXN);
+
+    return err;
+}
+
+static QifError
+qif_import_file(QifContext ctx, const char *filename)
+{
+    QifError err;
+    FILE *fp;
+
+    g_return_val_if_fail(ctx, QIF_E_BADARGS);
+    g_return_val_if_fail(filename, QIF_E_BADARGS);
+    g_return_val_if_fail(*filename, QIF_E_BADARGS);
+
+    /* Open the file */
+    fp = g_fopen(filename, "r");
+    if (fp == NULL)
+        return QIF_E_NOFILE;
+
+    ctx->filename = g_strdup(filename);
+
+    /* read the file */
+    err = qif_read_file(ctx, fp);
+
+    /* close the file */
+    fclose(fp);
+
+    return err;
+}
+
+
+QifContext
+qif_file_new(QifContext ctx, const char *filename)
+{
+    QifContext fctx;
+
+    g_return_val_if_fail(ctx, NULL);
+    g_return_val_if_fail(filename, NULL);
+
+    fctx = qif_context_new();
+
+    /* we should assume that we've got a bank account... just in case.. */
+    qif_parse_bangtype(fctx, "!type:bank");
+
+    /* Open the file */
+    if (qif_import_file(fctx, filename) != QIF_E_OK)
+    {
+        qif_context_destroy(fctx);
+        fctx = NULL;
+    }
+
+    /* Return the new context */
+    if (fctx)
+    {
+        ctx->files = g_list_prepend(ctx->files, fctx);
+        fctx->parent = ctx;
+
+        /* Make sure the file gets merged into the parent */
+        ctx->parsed = FALSE;
+    }
+
+    return fctx;
+}
+
+QifError
+qif_file_parse(QifContext ctx, gpointer ui_args)
+{
+    g_return_val_if_fail(ctx, QIF_E_BADARGS);
+    g_return_val_if_fail(!qif_file_needs_account(ctx), QIF_E_BADSTATE);
+
+    qif_parse_all(ctx, ui_args);
+    ctx->parsed = TRUE;
+
+    return QIF_E_OK;
+}
+
+gboolean
+qif_file_needs_account(QifContext ctx)
+{
+    g_return_val_if_fail(ctx, FALSE);
+
+    return ((ctx->parse_flags & QIF_F_TXN_NEEDS_ACCT) ||
+            (ctx->parse_flags & QIF_F_ITXN_NEEDS_ACCT));
+}
+
+const char *
+qif_file_filename(QifContext ctx)
+{
+    g_return_val_if_fail(ctx, NULL);
+    return ctx->filename;
+}
+
+static void
+set_txn_acct(gpointer obj, gpointer arg)
+{
+    QifTxn txn = obj;
+    QifAccount acct = arg;
+
+    if (!txn->from_acct)
+        txn->from_acct = acct;
+}
+
+void
+qif_file_set_default_account(QifContext ctx, const char *acct_name)
+{
+    QifAccount acct;
+
+    g_return_if_fail(ctx);
+    g_return_if_fail(acct_name);
+
+    if (! qif_file_needs_account(ctx)) return;
+
+    acct = find_or_make_acct(ctx, g_strdup(acct_name),
+                             qif_parse_acct_type_guess(ctx->parse_type));
+
+    qif_object_list_foreach(ctx, QIF_O_TXN, set_txn_acct, acct);
+
+    qif_clear_flag(ctx->parse_flags, QIF_F_TXN_NEEDS_ACCT);
+    qif_clear_flag(ctx->parse_flags, QIF_F_ITXN_NEEDS_ACCT);
+}
diff --git a/gnucash/import-export/qif/qif-file.h b/gnucash/import-export/qif/qif-file.h
new file mode 100644
index 0000000..c4b85f1
--- /dev/null
+++ b/gnucash/import-export/qif/qif-file.h
@@ -0,0 +1,36 @@
+/* qif-import-p.h -- a QIF Importer module (private headers)
+ *
+ * Written By:	Derek Atkins <derek at ihtfp.com>
+ * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+#ifndef QIF_FILE_H
+#define QIF_FILE_H
+
+struct _QifLine
+{
+    char		type;
+    gint		lineno;
+    char *	line;
+};
+
+void qif_record_destroy(GList *record);
+
+#endif /* QIF_FILE_H */
diff --git a/gnucash/import-export/qif/qif-import-p.h b/gnucash/import-export/qif/qif-import-p.h
new file mode 100644
index 0000000..96c6314
--- /dev/null
+++ b/gnucash/import-export/qif/qif-import-p.h
@@ -0,0 +1,100 @@
+/* qif-import-p.h -- a QIF Importer module (private headers)
+ *
+ * Written By:	Derek Atkins <derek at ihtfp.com>
+ * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+#ifndef QIF_IMPORT_P_H
+#define QIF_IMPORT_P_H
+
+#include "qif-import.h"
+#include "qif-objects.h"
+#include "qif-parse.h"
+#include "qif-file.h"
+
+#include <stdio.h>
+
+struct _QifHandler
+{
+    void		(*init)(QifContext ctx);
+    QifError	(*parse_record)(QifContext ctx, GList *record);
+    QifError	(*end)(QifContext ctx);
+};
+
+struct _QifContext
+{
+    /* The parent context */
+    QifContext	parent;
+
+    /* file information */
+    char *	filename;
+    FILE *	fp;
+    gint		lineno;
+
+    /* This describes what we are parsing right now */
+    QifType	parse_type;
+    QifHandler	handler;
+    gpointer	parse_state;
+
+    /* A bunch of flags for the current handler */
+    gint		parse_flags;
+    gboolean	parsed;
+
+    /* The current and "opening balance" account */
+    QifAccount	current_acct;
+    QifAccount	opening_bal_acct;
+
+    /* HashTable of Maps of data objects */
+    GHashTable *	object_maps;
+
+    /* HashTable of Lists of data objects */
+    GHashTable *	object_lists;
+
+    /* List of files */
+    GList *files;
+};
+
+/* Object Maps */
+gint qif_object_map_count(QifContext ctx, const char *type);
+void qif_object_map_foreach(QifContext ctx, const char *type,
+                            GHFunc func, gpointer arg);
+void qif_object_map_insert(QifContext ctx, const char *key, QifObject obj);
+void qif_object_map_remove(QifContext ctx, const char *type, const char *key);
+QifObject qif_object_map_lookup(QifContext ctx, const char *type, const char *key);
+void qif_object_map_destroy(QifContext ctx);
+/* GList _SHOULD_ be freed by the caller */
+GList * qif_object_map_get(QifContext ctx, const char *type);
+
+/* Object Lists */
+void qif_object_list_reverse(QifContext ctx, const char *type);
+gint qif_object_list_count(QifContext ctx, const char *type);
+void qif_object_list_foreach(QifContext ctx, const char *type,
+                             GFunc func, gpointer arg);
+void qif_object_list_insert(QifContext ctx, QifObject obj);
+void qif_object_list_remove(QifContext ctx, QifObject obj);
+void qif_object_list_destroy(QifContext ctx);
+/* GList should NOT be freed by the caller */
+GList *qif_object_list_get(QifContext ctx, const char *type);
+
+/* Set and clear flags in bit-flags */
+#define qif_set_flag(i,f) (i |= f)
+#define qif_clear_flag(i,f) (i &= ~f)
+
+#endif /* QIF_IMPORT_P_H */
diff --git a/gnucash/import-export/qif/qif-import.h b/gnucash/import-export/qif/qif-import.h
new file mode 100644
index 0000000..9602029
--- /dev/null
+++ b/gnucash/import-export/qif/qif-import.h
@@ -0,0 +1,159 @@
+/*
+ * qif-import.h -- a QIF Import module
+ *
+ * Written By:	Derek Atkins <derek at ihtfp.com>
+ * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+#ifndef QIF_IMPORT_H
+#define QIF_IMPORT_H
+
+#include <stdio.h>
+#include "qof.h"
+
+typedef enum
+{
+    QIF_TYPE_BANK = 1,
+    QIF_TYPE_CASH,
+    QIF_TYPE_CCARD,
+    QIF_TYPE_INVST,
+    QIF_TYPE_PORT,
+    QIF_TYPE_OTH_A,
+    QIF_TYPE_OTH_L,
+    QIF_TYPE_CLASS,
+    QIF_TYPE_CAT,
+    QIF_TYPE_SECURITY,
+    QIF_ACCOUNT,
+    QIF_AUTOSWITCH,
+    QIF_CLEAR_AUTOSWITCH
+} QifType;
+
+/* Make sure this patches */
+#define QIF_TYPE_MAX QIF_CLEAR_AUTOSWITCH
+
+typedef struct _QifHandler *QifHandler;
+typedef struct _QifContext *QifContext;
+typedef struct _QifLine *QifLine;
+
+/* Qif Flags */
+#define QIF_F_IGNORE_ACCOUNTS	(1 << 0)
+#define QIF_F_TXN_NEEDS_ACCT	(1 << 1)
+#define QIF_F_ITXN_NEEDS_ACCT	(1 << 2)
+
+/* Qif Reconciled Flag */
+typedef enum
+{
+    QIF_R_NO = 0,
+    QIF_R_CLEARED,
+    QIF_R_RECONCILED,
+    QIF_R_BUDGETED,
+} QifRecnFlag;
+
+/* Qif Errors */
+
+typedef enum
+{
+    QIF_E_OK = 0,
+    QIF_E_INTERNAL,
+    QIF_E_BADSTATE,
+    QIF_E_BADARGS,
+    QIF_E_NOFILE,
+} QifError;
+
+
+/* Qif (investment?) Actions */
+typedef enum
+{
+    QIF_A_NONE = 0,
+    QIF_A_BUY,
+    QIF_A_BUYX,
+    QIF_A_CGLONG,
+    QIF_A_CGLONGX,
+    QIF_A_CGMID,
+    QIF_A_CGMIDX,
+    QIF_A_CGSHORT,
+    QIF_A_CGSHORTX,
+    QIF_A_DIV,
+    QIF_A_DIVX,
+    QIF_A_EXERCISE,
+    QIF_A_EXERCISEX,
+    QIF_A_EXPIRE,
+    QIF_A_GRANT,
+    QIF_A_INTINC,
+    QIF_A_INTINCX,
+    QIF_A_MARGINT,
+    QIF_A_MARGINTX,
+    QIF_A_MISCEXP,
+    QIF_A_MISCEXPX,
+    QIF_A_MISCINC,
+    QIF_A_MISCINCX,
+    QIF_A_REINVDIV,
+    QIF_A_REINVINT,
+    QIF_A_REINVLG,
+    QIF_A_REINVMD,
+    QIF_A_REINVSG,
+    QIF_A_REINVSH,
+    QIF_A_REMINDER,
+    QIF_A_RTRNCAP,
+    QIF_A_RTRNCAPX,
+    QIF_A_SELL,
+    QIF_A_SELLX,
+    QIF_A_SHRSIN,
+    QIF_A_SHRSOUT,
+    QIF_A_STKSPLIT,
+    QIF_A_VEST,
+    QIF_A_XIN,
+    QIF_A_XOUT,
+} QifAction;
+
+/* Public API Functions */
+
+/* Create a QIF Import Context */
+QifContext qif_context_new(void);
+void qif_context_destroy(QifContext ctx);
+
+/* Open and read a QIF File.  You must pass in the parent
+ * context; it will return the child (file) context
+ */
+QifContext qif_file_new(QifContext ctx, const char* filename);
+
+/* Does a qif-file need a default QIF account? */
+gboolean qif_file_needs_account(QifContext ctx);
+
+/* Return the filename of the QIF file */
+const char * qif_file_filename(QifContext ctx);
+
+/* Provide a default QIF Account for the QIF File */
+void qif_file_set_default_account(QifContext ctx, const char *acct_name);
+
+/* Parse the QIF File */
+QifError qif_file_parse(QifContext ctx, gpointer ui_arg);
+
+/* Merge all the qif-files from the children and into the context */
+void qif_parse_merge_files(QifContext ctx);
+
+/* Obtain the list of USED QifAccounts and QifCategories.  Finds all
+ * references from the transactions in the QifContext.  The returned
+ * GList must be freed by the caller.
+ */
+GList *qif_context_get_accounts(QifContext ctx);
+GList *qif_context_get_categories(QifContext ctx);
+
+#endif /* QIF_IMPORT_H */
diff --git a/gnucash/import-export/qif/qif-objects-p.h b/gnucash/import-export/qif/qif-objects-p.h
new file mode 100644
index 0000000..dbd3aa4
--- /dev/null
+++ b/gnucash/import-export/qif/qif-objects-p.h
@@ -0,0 +1,174 @@
+/*
+ * qif-objects-p.h -- Private header: QIF objects for the QIF importer
+ *
+ * Written By:	Derek Atkins <derek at ihtfp.com>
+ * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+#ifndef QIF_OBJECTS_P_H
+#define QIF_OBJECTS_P_H
+
+#include "qof.h"
+
+#include "qif-import.h"
+#include "qif-objects.h"
+
+struct _QifAccount
+{
+    struct _QifObject obj;
+
+    char *	name;
+    char *	desc;
+
+    char *	limitstr;
+    gnc_numeric	limit;
+
+    char *	budgetstr;
+    gnc_numeric	budget;
+
+    GList *	type_list;
+
+};
+
+struct _QifCategory
+{
+    struct _QifObject obj;
+
+    char *	name;
+    char *	desc;
+    char *	taxclass;
+
+    gboolean	taxable;
+    gboolean	expense;
+    gboolean	income;
+
+    char *	budgetstr;
+    gnc_numeric	budget;
+
+};
+
+struct _QifClass
+{
+    struct _QifObject obj;
+
+    char *	name;
+    char *	desc;
+    char *	taxdesig;
+
+};
+
+struct _QifSecurity
+{
+    struct _QifObject obj;
+
+    char *	name;
+    char *	symbol;
+    char *	type;
+
+};
+
+struct _QifTxn
+{
+    struct _QifObject obj;
+
+    QifType	txn_type;
+
+    char *	datestr;
+    Timespec	date;
+
+    char *	payee;
+    char *	address;
+    char *	num;
+
+    QifRecnFlag	cleared;
+
+    /* Investment info */
+    QifInvstTxn	invst_info;
+
+    /* The default_split is the default (forward) part of the QIF transaction */
+    QifSplit	default_split;
+
+    /* The current_split (if any) defines the current "qif split" we are handling */
+    QifSplit	current_split;
+
+    /* The "from" account */
+    QifAccount	from_acct;
+
+    /* The list of splits for this txn */
+    GList *	splits;
+
+};
+
+struct _QifSplit
+{
+    char *	memo;
+
+    char *	amountstr;
+    gnc_numeric	amount;
+    gnc_numeric	value;
+
+    char *	catstr;
+
+    /* parsed category/account info */
+
+    union
+    {
+        QifObject	obj;
+        QifCategory	cat;
+        QifAccount	acct;
+    } cat;
+    gboolean	cat_is_acct;
+    QifClass	cat_class;
+
+};
+
+struct _QifInvstTxn
+{
+    QifAction	action;
+
+    gnc_numeric	amount;
+    gnc_numeric	d_amount;
+    gnc_numeric	price;
+    gnc_numeric	shares;
+    gnc_numeric	commission;
+
+    char *	amountstr;
+    char *	d_amountstr;
+    char *	pricestr;
+    char *	sharesstr;
+    char *	commissionstr;
+
+    char *	security;
+    char *	catstr;
+
+    union
+    {
+        QifObject	obj;
+        QifCategory	cat;
+        QifAccount	acct;
+    } far_cat;
+    gboolean	far_cat_is_acct;
+};
+
+/* to be run after parsing all the dates and amounts */
+void qif_txn_setup_splits(QifTxn txn);
+void qif_invst_txn_setup_splits(QifContext ctx, QifTxn txn);
+
+#endif /* QIF_OBJECTS_P_H */
diff --git a/gnucash/import-export/qif/qif-objects.c b/gnucash/import-export/qif/qif-objects.c
new file mode 100644
index 0000000..0fad928
--- /dev/null
+++ b/gnucash/import-export/qif/qif-objects.c
@@ -0,0 +1,1468 @@
+/*
+ * qif-objects.c -- Objects for the QIF Importer
+ *
+ * Written by:	Derek Atkins  <derek at ihtfp.com>
+ * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <string.h>
+#include "Account.h"
+
+#include "gnc-engine.h"
+
+#include "qif-import-p.h"
+#include "qif-objects-p.h"
+#include "qif-defaults.h"
+
+static QofLogModule log_module = GNC_MOD_IMPORT;
+
+/* create a new object of type t, with type-string type and
+ * destroy function dest.  Requires 'obj' to be set.
+ */
+#define qif_object_new(t,typ,dest) ({ \
+	obj = (QifObject) g_new0(t, 1); \
+	obj->type = typ; \
+	obj->destroy = dest; \
+	obj; \
+})
+
+/* Save the string from this "line".  Also:
+ * - make sure we're not over-writing anything.
+ * - make sure the 'line' object no longer references the string.
+ */
+#define qif_save_str(var) { \
+	if (var) { \
+		PERR("duplicate found at line %d: %s", line->lineno, line->line); \
+		g_free(var); \
+	} \
+	(var) = line->line; \
+	line->line = NULL; \
+}
+
+/* QIF Account */
+static void
+qif_account_destroy(QifObject obj)
+{
+    QifAccount acct = (QifAccount) obj;
+
+    g_free(acct->name);
+    g_free(acct->desc);
+    g_free(acct->limitstr);
+    g_free(acct->budgetstr);
+
+    g_free(acct);
+};
+
+static QifAccount
+qif_account_new(void)
+{
+    QifObject obj;
+    QifAccount acct;
+
+    obj = qif_object_new(struct _QifAccount, QIF_O_ACCOUNT, qif_account_destroy);
+
+    acct = (QifAccount)obj;
+    acct->type_list = qif_parse_acct_type("bank", -1);
+
+    acct->limit = gnc_numeric_zero();
+    acct->budget = gnc_numeric_zero();
+    return acct;
+}
+
+/*
+ * Merge acct into ctx.  If this account already exists in ctx then
+ * merge in any new values from acct into the ctx version and return
+ * the existing acct.  If the account does not already exist, then
+ * insert it into the ctx and return it.
+ */
+QifAccount
+qif_account_merge(QifContext ctx, QifAccount acct)
+{
+    QifAccount acct2 =
+        (QifAccount)qif_object_map_lookup(ctx, acct->obj.type, acct->name);
+
+    if (!acct2)
+    {
+        qif_object_map_insert(ctx, acct->obj.type, (QifObject)acct);
+        return acct;
+    }
+
+    /* obviously the name is the same, so don't worry about that */
+
+    if (!acct2->desc && acct->desc)
+        acct2->desc = g_strdup(acct->desc);
+
+    if (!acct2->type_list && acct->type_list)
+        acct2->type_list = acct->type_list;
+
+    if (!acct2->limitstr && acct->limitstr)
+    {
+        acct2->limitstr = g_strdup(acct->limitstr);
+        acct2->limit = acct->limit;
+    }
+
+    if (!acct2->budgetstr && acct->budgetstr)
+    {
+        acct2->budgetstr = g_strdup(acct->budgetstr);
+        acct2->budget = acct->budget;
+    }
+
+    return acct2;
+}
+
+static QifError
+qif_account_parse(QifContext ctx, GList *record)
+{
+    QifAccount acct, temp;
+    QifLine line;
+
+    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
+    g_return_val_if_fail(record, QIF_E_BADSTATE);
+
+    acct = qif_account_new();
+
+    for (; record; record = record->next)
+    {
+        line = record->data;
+
+        switch (line->type)
+        {
+        case 'N':			/* N : account name */
+            qif_save_str(acct->name);
+            break;
+        case 'D': 			/* D : account description */
+            qif_save_str(acct->desc);
+            break;
+        case 'T':			/* T : account type */
+            acct->type_list = qif_parse_acct_type(line->line, line->lineno);
+            break;
+        case 'L':			/* L : account limit */
+            qif_save_str(acct->limitstr);
+            break;
+        case 'B':			/* B : account budget */
+            qif_save_str(acct->budgetstr);
+            break;
+        default:
+            PERR("Unknown QIF account data at line %d: %s", line->lineno, line->line);
+        }
+    }
+
+    /* Merge the account into the context */
+    temp = qif_account_merge(ctx, acct);
+    if (! (ctx->parse_flags & QIF_F_IGNORE_ACCOUNTS))
+        ctx->current_acct = temp;
+    if (temp != acct)
+        qif_account_destroy((QifObject)acct);
+
+    return QIF_E_OK;
+}
+
+/* QIF Category */
+static void
+qif_cat_destroy(QifObject obj)
+{
+    QifCategory cat = (QifCategory) obj;
+
+    g_free(cat->name);
+    g_free(cat->desc);
+    g_free(cat->taxclass);
+    g_free(cat->budgetstr);
+
+    g_free(cat);
+}
+
+static QifCategory
+qif_cat_new(void)
+{
+    QifObject obj;
+    QifCategory cat;
+
+    obj = qif_object_new(struct _QifCategory, QIF_O_CATEGORY, qif_cat_destroy);
+    cat = (QifCategory)obj;
+    cat->budget = gnc_numeric_zero();
+
+    return cat;
+}
+
+/*
+ * Merge cat into ctx.  If this category already exists in ctx then
+ * merge in any new values from cat into the ctx version and return
+ * the existing cat.  If the category does not already exist, then
+ * insert it into the ctx and return it.
+ */
+QifCategory
+qif_cat_merge(QifContext ctx, QifCategory cat)
+{
+    QifCategory cat2 =
+        (QifCategory)qif_object_map_lookup(ctx, cat->obj.type, cat->name);
+
+    if (!cat2)
+    {
+        qif_object_map_insert(ctx, cat->obj.type, (QifObject)cat);
+        return cat;
+    }
+
+    /* obviously the name is the same, so don't worry about that */
+
+    if (!cat2->desc && cat->desc)
+        cat2->desc = g_strdup(cat->desc);
+
+    if (!cat2->taxclass && cat->taxclass)
+        cat2->taxclass = g_strdup(cat->taxclass);
+
+    cat2->taxable = (cat2->taxable || cat->taxable);
+    cat2->expense = (cat2->expense || cat->expense);
+    cat2->income = (cat2->income || cat->income);
+
+    if (!cat2->budgetstr && cat->budgetstr)
+    {
+        cat2->budgetstr = g_strdup(cat->budgetstr);
+        cat2->budget = cat->budget;
+    }
+
+    return cat2;
+}
+
+static QifError
+qif_cat_parse(QifContext ctx, GList *record)
+{
+    QifCategory cat;
+    QifLine line;
+
+    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
+    g_return_val_if_fail(record, QIF_E_BADSTATE);
+
+    cat = qif_cat_new();
+
+    for (; record; record = record->next)
+    {
+        line = record->data;
+
+        switch (line->type)
+        {
+        case 'N':			/* N : category name */
+            qif_save_str(cat->name);
+            break;
+        case 'D':			/* D : category description */
+            qif_save_str(cat->desc);
+            break;
+        case 'T':			/* T : category is taxable? */
+            cat->taxable = TRUE;
+            break;
+        case 'E':			/* E : category is expense? */
+            cat->expense = TRUE;
+            break;
+        case 'I':			/* I : category is income? */
+            cat->income = TRUE;
+            break;
+        case 'R':			/* R : category taxclass XXX */
+            /* XXX: a number? */
+            qif_save_str(cat->taxclass);
+            break;
+        case 'B':			/* B : category budget */
+            qif_save_str(cat->budgetstr);
+            break;
+        default:
+            PERR("Unknown QIF category data at line %d: %s", line->lineno, line->line);
+        }
+    }
+
+    if (qif_cat_merge(ctx, cat) != cat)
+        qif_cat_destroy((QifObject)cat);
+
+    return QIF_E_OK;
+}
+
+/* QIF Class */
+static void
+qif_class_destroy(QifObject obj)
+{
+    QifClass qclass = (QifClass) obj;
+
+    g_free(qclass->name);
+    g_free(qclass->desc);
+    g_free(qclass->taxdesig);
+
+    g_free(qclass);
+}
+
+static QifClass
+qif_class_new()
+{
+    QifObject obj;
+
+    obj = qif_object_new(struct _QifClass, QIF_O_CLASS, qif_class_destroy);
+    return (QifClass)obj;
+}
+
+/*
+ * Merge qclass into ctx.  If this class already exists in ctx then
+ * merge in any new values from qclass into the ctx version and return
+ * the existing qclass.  If the class does not already exist, then
+ * insert it into the ctx and return it.
+ */
+QifClass
+qif_class_merge(QifContext ctx, QifClass qclass)
+{
+    QifClass qclass2 =
+        (QifClass)qif_object_map_lookup(ctx, qclass->obj.type, qclass->name);
+
+    if (!qclass2)
+    {
+        qif_object_map_insert(ctx, qclass->obj.type, (QifObject)qclass);
+        return qclass;
+    }
+
+    /* obviously the name is the same, so don't worry about that */
+
+    if (!qclass2->desc && qclass->desc)
+        qclass2->desc = g_strdup(qclass->desc);
+
+    if (!qclass2->taxdesig && qclass->taxdesig)
+        qclass2->taxdesig = g_strdup(qclass->taxdesig);
+
+    return qclass2;
+}
+
+static QifError
+qif_class_parse(QifContext ctx, GList *record)
+{
+    QifClass qclass;
+    QifLine line;
+
+    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
+    g_return_val_if_fail(record, QIF_E_BADSTATE);
+
+    qclass = qif_class_new();
+
+    for (; record; record = record->next)
+    {
+        line = record->data;
+
+        switch (line->type)
+        {
+        case 'N':			/* N : class name */
+            qif_save_str(qclass->name);
+            break;
+        case 'D':			/* D : class description */
+            qif_save_str(qclass->desc);
+            break;
+        case 'R':			/* R : Tax designator */
+            qif_save_str(qclass->taxdesig);
+            break;
+        default:
+            PERR("Unknown QIF class data at line %d: %s", line->lineno, line->line);
+        }
+    }
+
+    if (qif_class_merge(ctx, qclass) != qclass)
+        qif_class_destroy((QifObject)qclass);
+
+    return QIF_E_OK;
+}
+
+/* QIF Security Symbol */
+static void
+qif_security_destroy(QifObject obj)
+{
+    QifSecurity security = (QifSecurity) obj;
+
+    g_free(security->name);
+    g_free(security->symbol);
+    g_free(security->type);
+
+    g_free(security);
+}
+
+static QifSecurity
+qif_security_new()
+{
+    QifObject obj;
+
+    obj = qif_object_new(struct _QifSecurity, QIF_O_SECURITY, qif_security_destroy);
+    return (QifSecurity)obj;
+}
+
+/*
+ * Merge security into ctx.  If this security already exists in ctx then
+ * merge in any new values from security into the ctx version and return
+ * the existing security.  If the security does not already exist, then
+ * insert it into the ctx and return it.
+ */
+QifSecurity
+qif_security_merge(QifContext ctx, QifSecurity security)
+{
+    QifSecurity security2 =
+        (QifSecurity)qif_object_map_lookup(ctx, security->obj.type, security->name);
+
+    if (!security2)
+    {
+        qif_object_map_insert(ctx, security->obj.type, (QifObject)security);
+        return security;
+    }
+
+    /* obviously the name is the same, so don't worry about that */
+
+    if (!security2->symbol && security->symbol)
+        security2->symbol = g_strdup(security->symbol);
+
+    if (!security2->type && security->type)
+        security2->type = g_strdup(security->type);
+
+    return security2;
+}
+
+static QifError
+qif_security_parse(QifContext ctx, GList *record)
+{
+    QifSecurity security;
+    QifLine line;
+
+    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
+    g_return_val_if_fail(record, QIF_E_BADSTATE);
+
+    security = qif_security_new();
+
+    for (; record; record = record->next)
+    {
+        line = record->data;
+
+        switch (line->type)
+        {
+        case 'N':			/* N : security name */
+            qif_save_str(security->name);
+            break;
+        case 'S':			/* S : security symbol */
+            qif_save_str(security->symbol);
+            break;
+        case 'T':			/* T : security type */
+            qif_save_str(security->type);
+            break;
+        default:
+            PERR("Unknown QIF security data at line %d: %s", line->lineno, line->line);
+        }
+    }
+
+    if (qif_security_merge(ctx, security) != security)
+        qif_security_destroy((QifObject)security);
+
+    return QIF_E_OK;
+}
+
+/********************* TXN *********************/
+
+static QifSplit
+qif_split_new()
+{
+    QifSplit split = g_new0(struct _QifSplit, 1);
+
+    /* Initialize to 'zero' (even though they are not valid) */
+    split->amount = gnc_numeric_zero();
+    split->value = gnc_numeric_zero();
+
+    return split;
+}
+
+static void
+qif_split_destroy(QifSplit split)
+{
+    if (!split) return;
+
+    g_free(split->memo);
+    g_free(split->catstr);
+    g_free(split->amountstr);
+
+    g_free(split);
+}
+
+static QifSplit
+qif_split_copy(QifSplit split)
+{
+    QifSplit s = qif_split_new();
+
+    memcpy(s, split, sizeof(*s));
+    if (s->memo) s->memo = g_strdup(s->memo);
+    if (s->amountstr) s->amountstr = g_strdup(s->amountstr);
+    if (s->catstr) s->memo = g_strdup(s->catstr);
+
+    return s;
+}
+
+/* Forward declarations */
+static void qif_txn_invst_destroy(QifInvstTxn);
+
+/* QIF Transaction */
+
+static void
+qif_split_parse_category(QifContext ctx, QifSplit split)
+{
+    char *cat = NULL;
+    char *cat_class = NULL;
+    char *miscx_cat = NULL;
+    char *miscx_class = NULL;
+
+    gboolean miscx_is_acct;
+
+    static GList *types = NULL;
+
+    g_return_if_fail(ctx);
+    g_return_if_fail(split);
+    g_return_if_fail(split->cat.cat == NULL && split->cat_class == NULL);
+
+    if (qif_parse_split_category(split->catstr,
+                                 &cat, &split->cat_is_acct, &cat_class,
+                                 &miscx_cat, &miscx_is_acct, &miscx_class))
+    {
+        g_assert(cat);
+
+        if (split->cat_is_acct)
+        {
+            if (types == NULL)
+                types = qif_parse_acct_type("__any_bank__", -1);
+
+            split->cat.acct = find_or_make_acct(ctx, cat, types);
+
+        }
+        else
+            split->cat.cat = find_or_make_cat(ctx, cat);
+
+        if (cat_class)
+            split->cat_class = find_or_make_class(ctx, cat_class);
+
+        /* miscx isn't used in a normal transaction, so just ignore it */
+        if (miscx_cat)
+            g_free(miscx_cat);
+        if (miscx_class)
+            g_free(miscx_class);
+
+    }
+    else
+        PERR("Problem parsing split category: %s", split->catstr);
+}
+
+static void
+qif_txn_destroy(QifObject obj)
+{
+    QifTxn txn = (QifTxn) obj;
+    GList *node;
+    QifSplit split;
+
+    g_free(txn->datestr);
+    g_free(txn->payee);
+    g_free(txn->address);
+    g_free(txn->num);
+
+    if (txn->invst_info)
+        qif_txn_invst_destroy(txn->invst_info);
+
+    for (node = txn->splits; node; node = node->next)
+    {
+        split = node->data;
+        if (split == txn->default_split)
+            txn->default_split = NULL;
+        if (split == txn->current_split)
+            txn->current_split = NULL;
+
+        qif_split_destroy(split);
+    }
+
+    g_list_free(txn->splits);
+    qif_split_destroy(txn->default_split);
+    qif_split_destroy(txn->current_split);
+
+    g_free(txn);
+}
+
+static QifTxn
+qif_txn_new(void)
+{
+    QifObject obj;
+    QifTxn txn;
+
+    obj = qif_object_new(struct _QifTxn, "qif-txn", qif_txn_destroy);
+    txn = (QifTxn) obj;
+    txn->default_split = qif_split_new();
+
+    return txn;
+}
+
+static void
+qif_txn_init(QifContext ctx)
+{
+    qif_clear_flag(ctx->parse_flags, QIF_F_IGNORE_ACCOUNTS);
+    ctx->parse_state = NULL;
+}
+
+static gboolean
+qif_is_bad_numeric_string(const char* line)
+{
+    return (strncmp(line, "...", 3) == 0);
+}
+
+/*
+ * this is called for the first transaction after each !Type: tag.
+ *
+ * if the first transaction after a !Type: tag has a payee of "Opening
+ * Balance" or "Initial Balance", we have to massage the transaction a
+ * little.  The meaning of an OB transaction is "transfer from Equity
+ * to the account specified in the L line."  Idiomatically, ms-money
+ * and some others use this transaction instead of an Account record
+ * to specify "this" account (the from-account for all following
+ * transactions), so we have to allow for that.
+ *
+ * Even if the payee isn't "Opening Balance", we if we have no default
+ * from-account by this time we need to set one.  In that case we set
+ * the default account based on the file name.
+ *
+ * If we DO know the account already, and this is a transfer to it,
+ * it's also an opening balance regardless of the payee.
+ *
+ * In the end make sure that the context 'current account' is set.
+ */
+static void
+qif_process_opening_balance_txn(QifContext ctx, QifTxn txn)
+{
+    QifSplit split = txn->default_split;
+    QifAccount cur_acct = NULL;	/* We know that ctx->current_acct is NULL */
+
+    g_return_if_fail(txn->invst_info == NULL);
+
+    if ((!cur_acct && txn->payee &&
+            (!strcasecmp(txn->payee, "Opening Balance") ||
+             !strcasecmp(txn->payee, "Initial Balance")) && split->cat_is_acct) ||
+            (cur_acct &&
+             ((split->cat_is_acct && !strcasecmp(split->cat.acct->name, cur_acct->name))
+              ||
+              (!split->cat_is_acct && !strcasecmp(split->cat.cat->name, cur_acct->name))))
+       )
+    {
+
+        /* This is an explicit "Opening Balance" transactions.  We need to
+         * change the "from account" to point to the equity account that
+         * the opening balance comes from...
+         */
+        if (split->cat_is_acct)
+            cur_acct = split->cat.acct;
+        else
+        {
+            g_assert(split->cat.cat);
+            cur_acct = find_or_make_acct(ctx, g_strdup(split->cat.cat->name),
+                                         qif_parse_acct_type_guess(txn->txn_type));
+            split->cat_is_acct = TRUE;
+        }
+        split->cat.acct = qif_default_equity_acct(ctx);
+    }
+
+    /*
+     * If we found an opening balance account then set up the context.
+     * If we didn't actually succeed in finding an account then
+     * set a flag so we can go back later and look for it.
+     */
+
+    if (cur_acct)
+    {
+        ctx->opening_bal_acct = cur_acct;
+        ctx->current_acct = cur_acct;
+    }
+    else
+        qif_set_flag(ctx->parse_flags, QIF_F_TXN_NEEDS_ACCT);
+}
+
+/* process all the splits in the transaction -- if this is a "split
+ * transaction" then make sure the sum of all the amounts (including
+ * the default split) does NOT equal zero -- if it does then we want
+ * to reverse all the splits.  The "amount" should be the 'T' amount
+ * from the txn.
+ */
+static void
+qif_txn_fix_amounts(QifTxn txn, gnc_numeric amount)
+{
+    gnc_numeric sum = amount;
+    QifSplit split;
+    GList *node;
+
+    g_return_if_fail(txn);
+
+    /* No current_split, so this is NOT a split transaction. */
+    if (!txn->current_split) return;
+
+    /* Then add in every split in the split-list */
+    for (node = txn->splits; node; node = node->next)
+    {
+        split = node->data;
+        sum = gnc_numeric_add(sum, split->amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
+    }
+
+    /* if the sum is not zero then reverse all the amounts in the split list */
+    if (!gnc_numeric_zero_p(sum))
+        for (node = txn->splits; node; node = node->next)
+        {
+            split = node->data;
+            split->amount = gnc_numeric_neg(split->amount);
+        }
+}
+
+static QifError
+qif_txn_parse(QifContext ctx, GList *record)
+{
+    QifTxn txn;
+    QifLine line;
+    GList *node;
+    QifSplit split;
+
+    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
+    g_return_val_if_fail(record, QIF_E_BADSTATE);
+
+    txn = qif_txn_new();
+    txn->txn_type = ctx->parse_type;
+
+    for (; record; record = record->next)
+    {
+        line = record->data;
+
+        switch (line->type)
+        {
+        case 'D':			/* D : transaction date */
+            qif_save_str(txn->datestr);
+            break;
+        case 'P':			/* P : payee */
+            qif_save_str(txn->payee);
+            break;
+        case 'A':			/* A : address */
+            /* multiple 'A' lines are appended together with newlines */
+            if (txn->address)
+            {
+                char *tmp = txn->address;
+                txn->address = g_strconcat(tmp, "\n", line->line, NULL);
+                g_free(tmp);
+            }
+            else
+                qif_save_str(txn->address);
+            break;
+        case 'N':			/* N : check/transaction number */
+            qif_save_str(txn->num);
+            break;
+        case 'C':			/* C : transaction cleared flag */
+            txn->cleared = qif_parse_cleared(line);
+            break;
+        case 'L':			/* L : default split category */
+            if (!txn->current_split) qif_save_str(txn->default_split->catstr);
+            break;
+        case 'M':			/* M : default split memo */
+            if (!txn->current_split) qif_save_str(txn->default_split->memo);
+            break;
+        case 'T':			/* T : total transaction amount */
+            if (!txn->current_split && !qif_is_bad_numeric_string(line->line))
+                qif_save_str(txn->default_split->amountstr);
+            break;
+        case 'S':			/* S : split category */
+            /* This implies a quicken-style "split transaction", so we're mostly
+             * going to ignore the default_split except for internal verification.
+             */
+            txn->current_split = qif_split_new();
+            txn->splits = g_list_prepend(txn->splits, txn->current_split);
+            qif_save_str(txn->current_split->catstr);
+            break;
+        case 'E':			/* E : split memo */
+            if (txn->current_split)
+                qif_save_str(txn->current_split->memo);
+            break;
+        case '$':			/* split amount */
+            if (txn->current_split && !qif_is_bad_numeric_string(line->line))
+                qif_save_str(txn->current_split->amountstr);
+            break;
+        default:
+            PERR("Unknown QIF transaction data at line %d: %s", line->lineno, line->line);
+        }
+    }
+
+    /* If we have no date string then there is no reason to do anything else */
+    if (txn->datestr)
+    {
+        /* We delay processing the date and amount strings until later.. */
+
+        /* parse the category on each split */
+        for (node = txn->splits; node; node = node->next)
+        {
+            split = node->data;
+            if (split->catstr)
+                qif_split_parse_category(ctx, split);
+        }
+        /* ... including the default split */
+        if (txn->default_split->catstr)
+            qif_split_parse_category(ctx, txn->default_split);
+
+        /* if we don't have an account, then deal with the opening balance */
+        if (!ctx->current_acct)
+            qif_process_opening_balance_txn(ctx, txn);
+
+        /* Set the transaction's from account */
+        txn->from_acct = ctx->current_acct;
+
+        /* And add it to the process list */
+        ctx->parse_state = g_list_prepend(ctx->parse_state, txn);
+
+    }
+    else
+        /* no date?  Ignore this txn */
+        qif_txn_destroy((QifObject)txn);
+
+    return QIF_E_OK;
+}
+
+/* after we parse the amounts, fix up the transaction splits */
+void
+qif_txn_setup_splits(QifTxn txn)
+{
+    QifSplit split, this_split;
+    GList *node;
+    gnc_numeric total;
+
+    if (txn->splits)
+    {
+        /* We have a bunch of "far" splits -- maybe fix up the totals.. */
+        qif_txn_fix_amounts(txn, txn->default_split->amount);
+
+        /* Re-Compute the total for the "near" (default) split */
+        total = gnc_numeric_zero();
+        for (node = txn->splits; node; node = node->next)
+        {
+            split = node->data;
+            split->value = split->amount;
+            total = gnc_numeric_add(total, split->amount, 0, GNC_HOW_DENOM_LCD);
+        }
+
+        /* And re-set the default-split amount */
+        txn->default_split->amount = gnc_numeric_neg(total);
+
+    }
+    else
+    {
+        /* not a split txn.  Compute the "far" split by copying the "near"
+         *  split and then moving the 'near' split to the far split.
+         */
+
+        /* First make a copy of this transaction and move the copy to the 'near' */
+        split = txn->default_split;
+        this_split = qif_split_copy(split);
+        txn->default_split = this_split;
+
+        /* then adjust the 'far' txn */
+        split->amount = gnc_numeric_neg(split->amount);
+        split->value = split->amount;
+        txn->splits = g_list_prepend(txn->splits, split);
+    }
+
+    /* Set the default-split value from the default-split amount */
+    txn->default_split->value = txn->default_split->amount;
+}
+
+/* This is called when we're done processing an account.  We want
+ * to merge the transactions in the "parse_state" into the Qif Context
+ */
+static QifError
+qif_txn_end_acct(QifContext ctx)
+{
+    GList *node;
+    QifTxn txn;
+    gboolean txn_needs_acct;
+
+    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
+
+    /* Return now if there is nothing to do. */
+    if (!ctx->parse_state) return QIF_E_OK;
+
+    /* Walk through the list of transactions.  First check if it
+     * needs a from-account; then add it to the context.
+     */
+
+    txn_needs_acct = (ctx->parse_flags & QIF_F_TXN_NEEDS_ACCT);
+
+    /* Invert the list so we're working in the right order */
+    ctx->parse_state = g_list_reverse(ctx->parse_state);
+
+    for (node = ctx->parse_state; node; node = node->next)
+    {
+        txn = node->data;
+
+        /* If we need a from account, then set it.. */
+        if (txn_needs_acct && ctx->opening_bal_acct && !txn->from_acct)
+            txn->from_acct = ctx->opening_bal_acct;
+
+        /* merge the txn into the context (prepends to the list) */
+        qif_object_list_insert(ctx, (QifObject)txn);
+    }
+
+    if (txn_needs_acct && ctx->opening_bal_acct)
+        qif_clear_flag(ctx->parse_flags, QIF_F_TXN_NEEDS_ACCT);
+
+    /* clean up our state */
+    g_list_free(ctx->parse_state);
+    ctx->parse_state = NULL;
+
+    return QIF_E_OK;
+}
+
+/* Extra info in an Investment Transaction */
+static QifInvstTxn
+qif_invst_txn_new(void)
+{
+    QifInvstTxn itxn = g_new0(struct _QifInvstTxn, 1);
+
+    itxn->amount = gnc_numeric_zero();
+    itxn->d_amount = gnc_numeric_zero();
+    itxn->price = gnc_numeric_zero();
+    itxn->shares = gnc_numeric_zero();
+    itxn->commission = gnc_numeric_zero();
+
+    return itxn;
+}
+
+static void
+qif_txn_invst_destroy(QifInvstTxn itxn)
+{
+    if (!itxn) return;
+
+    g_free(itxn->amountstr);
+    g_free(itxn->d_amountstr);
+    g_free(itxn->pricestr);
+    g_free(itxn->sharesstr);
+    g_free(itxn->commissionstr);
+    g_free(itxn->security);
+
+    g_free(itxn->catstr);
+
+    g_free(itxn);
+}
+
+static QifError
+qif_txn_invst_parse(QifContext ctx, GList *record)
+{
+    QifTxn txn;
+    QifInvstTxn itxn;
+    QifLine line;
+
+    g_return_val_if_fail(ctx, QIF_E_INTERNAL);
+    g_return_val_if_fail(record, QIF_E_BADSTATE);
+
+    txn = qif_txn_new();
+    txn->txn_type = ctx->parse_type;
+    itxn = qif_invst_txn_new();
+    txn->invst_info = itxn;
+
+    for (; record; record = record->next)
+    {
+        line = record->data;
+
+        switch (line->type)
+        {
+        case 'D':			/* D : transaction date */
+            qif_save_str(txn->datestr);
+            break;
+        case 'P':			/* P : txn payee */
+            qif_save_str(txn->payee);
+            break;
+        case 'N':			/* N : action */
+            itxn->action = qif_parse_action(line);
+            break;
+        case 'C':			/* C : cleared flag */
+            txn->cleared = qif_parse_cleared(line);
+            break;
+        case 'M':			/* M : memo */
+            if (!txn->current_split)
+                qif_save_str(txn->default_split->memo);
+            break;
+        case 'T':			/* T : total amount */
+            if (!qif_is_bad_numeric_string(line->line))
+                qif_save_str(itxn->amountstr);
+            break;
+        case '$':			/* $ : transfer amount */
+            if (!qif_is_bad_numeric_string(line->line))
+                qif_save_str(itxn->d_amountstr);
+            break;
+        case 'I':			/* I : share price */
+            qif_save_str(itxn->pricestr);
+            break;
+        case 'Q':			/* Q : number of shares */
+            qif_save_str(itxn->sharesstr);
+            break;
+        case 'Y':			/* Y : name of security */
+            qif_save_str(itxn->security);
+            break;
+        case 'O':			/* O : commission */
+            qif_save_str(itxn->commissionstr);
+            break;
+        case 'L':			/* L : category */
+            qif_save_str(itxn->catstr);
+            break;
+        default:
+            PERR("Unknown QIF Investment transaction data at line %d: %s",
+                 line->lineno, line->line);
+        }
+    }
+
+    /* If we have no date string then there is no reason to do anything else */
+    if (txn->datestr && itxn->action != QIF_A_NONE)
+    {
+
+        /* Make sure we've got a security name */
+        if (!itxn->security)
+            itxn->security = g_strdup("");	/* XXX */
+
+        /* if we don't have a from account, then mark the fact that
+         * we'll need one later.
+         */
+        if (ctx->current_acct)
+            txn->from_acct = ctx->current_acct;
+        else
+            qif_set_flag(ctx->parse_flags, QIF_F_ITXN_NEEDS_ACCT);
+
+        /* Add this transaction to the parse state for later processing */
+        ctx->parse_state = g_list_prepend(ctx->parse_state, txn);
+
+    }
+    else
+    {
+        /* no date?  Just destroy it */
+        qif_txn_destroy((QifObject)txn);
+    }
+
+    return QIF_E_OK;
+}
+
+
+void
+qif_invst_txn_setup_splits(QifContext ctx, QifTxn txn)
+{
+    QifInvstTxn itxn;
+    QifSplit near_split, far_split, comm_split;
+    QifAccount from_acct;
+
+    char *cat = NULL;
+    char *cat_class = NULL;
+    gboolean cat_is_acct = FALSE;
+    char *miscx = NULL;
+    char *miscx_class = NULL;
+    gboolean miscx_is_acct = FALSE;
+
+    /* Cached account-type lists */
+    static GList *bank_list = NULL;
+
+    gnc_numeric split_value;
+
+    g_return_if_fail(ctx);
+    g_return_if_fail(txn);
+    g_return_if_fail(txn->invst_info);
+
+    itxn = txn->invst_info;
+
+    /* Compute the share value, because we'll probably need it */
+    split_value = gnc_numeric_mul(itxn->shares, itxn->price, 0, GNC_HOW_DENOM_REDUCE);
+
+    /* Make sure that "amount" is a valid "transaction amount" */
+    if (!itxn->amountstr && itxn->d_amountstr)
+        itxn->amount = itxn->d_amount;
+
+    /* near and far splits..  for simplicity */
+    near_split = txn->default_split;
+    far_split = qif_split_new();
+    from_acct = txn->from_acct;
+
+    /* Parse the category string */
+    if (!qif_parse_split_category(itxn->catstr,
+                                  &cat, &cat_is_acct, &cat_class,
+                                  &miscx, &miscx_is_acct, &miscx_class))
+        PERR("Failure parsing category: %s", itxn->catstr);
+
+    /* Make sure we've got a cached list */
+    if (bank_list == NULL)
+        bank_list = qif_parse_acct_type("__any_bank__", -1);
+
+    /* find the NEAR account */
+
+    switch (itxn->action)
+    {
+    case QIF_A_BUY:
+    case QIF_A_BUYX:
+    case QIF_A_REINVDIV:
+    case QIF_A_REINVINT:
+    case QIF_A_REINVLG:
+    case QIF_A_REINVMD:
+    case QIF_A_REINVSG:
+    case QIF_A_REINVSH:
+    case QIF_A_SELL:
+    case QIF_A_SELLX:
+    case QIF_A_SHRSIN:
+    case QIF_A_SHRSOUT:
+    case QIF_A_STKSPLIT:
+        txn->from_acct = qif_default_stock_acct(ctx, itxn->security);
+        break;
+
+    case QIF_A_CGLONG:
+    case QIF_A_CGMID:
+    case QIF_A_CGSHORT:
+    case QIF_A_DIV:
+    case QIF_A_INTINC:
+    case QIF_A_MARGINT:
+    case QIF_A_MISCEXP:
+    case QIF_A_MISCINC:
+    case QIF_A_RTRNCAP:
+    case QIF_A_XIN:
+    case QIF_A_XOUT:
+        txn->from_acct = from_acct;
+        break;
+
+    case QIF_A_CGLONGX:
+    case QIF_A_CGMIDX:
+    case QIF_A_CGSHORTX:
+    case QIF_A_DIVX:
+    case QIF_A_INTINCX:
+    case QIF_A_MARGINTX:
+    case QIF_A_RTRNCAPX:
+        txn->from_acct = find_or_make_acct(ctx, cat, bank_list);
+        cat = NULL;
+        break;
+
+    case QIF_A_MISCEXPX:
+    case QIF_A_MISCINCX:
+        txn->from_acct = find_or_make_acct(ctx, miscx, bank_list);
+        miscx = NULL;
+        break;
+
+    default:
+        PERR("Unhandled Action: %d", itxn->action);
+        break;
+    }
+
+    /* find the FAR account */
+
+    itxn->far_cat_is_acct = TRUE;
+    switch (itxn->action)
+    {
+    case QIF_A_BUY:
+    case QIF_A_SELL:
+        itxn->far_cat.acct = from_acct;
+        break;
+
+    case QIF_A_BUYX:
+    case QIF_A_MISCEXP:
+    case QIF_A_MISCEXPX:
+    case QIF_A_MISCINC:
+    case QIF_A_MISCINCX:
+    case QIF_A_SELLX:
+    case QIF_A_XIN:
+    case QIF_A_XOUT:
+        itxn->far_cat.cat = find_or_make_cat(ctx, cat);
+        itxn->far_cat_is_acct = FALSE;
+        cat = NULL;
+        break;
+
+    case QIF_A_CGLONG:
+    case QIF_A_CGLONGX:
+    case QIF_A_REINVLG:
+        itxn->far_cat.acct = qif_default_cglong_acct(ctx, itxn->security);
+        break;
+
+    case QIF_A_CGMID:
+    case QIF_A_CGMIDX:
+    case QIF_A_REINVMD:
+        itxn->far_cat.acct = qif_default_cgmid_acct(ctx, itxn->security);
+        break;
+
+    case QIF_A_CGSHORT:
+    case QIF_A_CGSHORTX:
+    case QIF_A_REINVSG:
+    case QIF_A_REINVSH:
+        itxn->far_cat.acct = qif_default_cgshort_acct(ctx, itxn->security);
+        break;
+
+    case QIF_A_DIV:
+    case QIF_A_DIVX:
+    case QIF_A_REINVDIV:
+        itxn->far_cat.acct = qif_default_dividend_acct(ctx, itxn->security);
+        break;
+
+    case QIF_A_INTINC:
+    case QIF_A_INTINCX:
+    case QIF_A_REINVINT:
+        itxn->far_cat.acct = qif_default_interest_acct(ctx, itxn->security);
+        break;
+
+    case QIF_A_MARGINT:
+    case QIF_A_MARGINTX:
+        itxn->far_cat.acct = qif_default_margin_interest_acct(ctx);
+        break;
+
+    case QIF_A_RTRNCAP:
+    case QIF_A_RTRNCAPX:
+        itxn->far_cat.acct = qif_default_capital_return_acct(ctx, itxn->security);
+        break;
+
+    case QIF_A_SHRSIN:
+    case QIF_A_SHRSOUT:
+        itxn->far_cat.acct = qif_default_equity_holding(ctx, itxn->security);
+        break;
+
+    case QIF_A_STKSPLIT:
+        itxn->far_cat.acct = qif_default_stock_acct(ctx, itxn->security);
+        break;
+
+    default:
+        break;
+    }
+
+    /* If we don't have a far acct (or far category) then reset the flag */
+    if (!itxn->far_cat.obj)
+        itxn->far_cat_is_acct = FALSE;
+
+    /* And now fill in the "near" and "far" splits.  In particular we need
+     *
+     *	NEAR:	txn->from_acct, near_split->amount, value
+     *	FAR:	cat, far_split->amount, value
+     */
+    switch (itxn->action)
+    {
+    case QIF_A_BUY:
+    case QIF_A_BUYX:
+    case QIF_A_REINVDIV:
+    case QIF_A_REINVINT:
+    case QIF_A_REINVLG:
+    case QIF_A_REINVMD:
+    case QIF_A_REINVSG:
+    case QIF_A_REINVSH:
+    case QIF_A_SHRSIN:
+        near_split->amount = itxn->shares;
+        near_split->value = split_value;
+        far_split->amount = far_split->value = gnc_numeric_neg(itxn->amount);
+        break;
+
+    case QIF_A_SELL:
+    case QIF_A_SELLX:
+    case QIF_A_SHRSOUT:
+        near_split->amount = gnc_numeric_neg(itxn->shares);
+        near_split->value = gnc_numeric_neg(split_value);
+        far_split->amount = far_split->value = itxn->amount;
+        break;
+
+    case QIF_A_CGLONG:
+    case QIF_A_CGLONGX:
+    case QIF_A_CGMID:
+    case QIF_A_CGMIDX:
+    case QIF_A_CGSHORT:
+    case QIF_A_CGSHORTX:
+    case QIF_A_DIV:
+    case QIF_A_DIVX:
+    case QIF_A_INTINC:
+    case QIF_A_INTINCX:
+    case QIF_A_MISCINC:
+    case QIF_A_MISCINCX:
+    case QIF_A_RTRNCAP:
+    case QIF_A_RTRNCAPX:
+    case QIF_A_XIN:
+        near_split->amount = near_split->value = itxn->amount;
+        far_split->amount = far_split->value = gnc_numeric_neg(itxn->amount);
+        break;
+
+    case QIF_A_MARGINT:
+    case QIF_A_MARGINTX:
+    case QIF_A_MISCEXP:
+    case QIF_A_MISCEXPX:
+    case QIF_A_XOUT:
+        near_split->amount = near_split->value = gnc_numeric_neg(itxn->amount);
+        far_split->amount = far_split->value = itxn->amount;
+        break;
+
+    case QIF_A_STKSPLIT:
+        /* QIF just specifies the split ratio, not the number of shares
+         * in and out, so we have to fetch the number of shares from the
+         * security account..  FEH!
+         */
+
+        near_split->value = gnc_numeric_neg(split_value);
+        far_split->value = split_value;
+
+        /* XXX: FIXME: compute in-shares/out-shares based on ratio here:
+         *
+         * splitratio = num-shares / 10;
+         * in_shares = gnc_account_get_balance(near_acct);
+         * out_shares = in_shares * splitratio;
+         *
+         * near_split->amount = out_shares;
+         * far_split->amount = gnc_numeric_neg(in_shares);
+         *
+         * We know (later) that near_split == txn->default_split and
+         * far_split == txn->splits->data, so we'll just special-case this
+         * kind of txn when we convert to GNC later.
+         */
+
+        break;
+
+    default:
+        break;
+    }
+
+    /* Just make sure to set that it's an account, not a category */
+    far_split->cat.obj = itxn->far_cat.obj;
+    if (itxn->far_cat_is_acct)
+        far_split->cat_is_acct = TRUE;
+
+    /* make the commission split if we need it, then add it to the split-list  */
+    if (itxn->commissionstr)
+    {
+        comm_split = qif_split_new();
+        comm_split->cat.acct = qif_default_commission_acct(ctx);
+        comm_split->cat_is_acct = TRUE;
+        comm_split->amount = itxn->commission;
+        comm_split->value = itxn->commission;
+
+        txn->splits = g_list_prepend(txn->splits, comm_split);
+    }
+
+    /* Push the "far split" into the txn split-list */
+    txn->splits = g_list_prepend(txn->splits, far_split);
+
+    /* Free parsed strings.. */
+    g_free(cat);
+    g_free(cat_class);
+    g_free(miscx);
+    g_free(miscx_class);
+}
+
+
+/* Other handlers */
+static void
+qif_autoswitch_set(QifContext ctx)
+{
+    qif_set_flag(ctx->parse_flags, QIF_F_IGNORE_ACCOUNTS);
+}
+
+static void
+qif_autoswitch_clear(QifContext ctx)
+{
+    qif_clear_flag(ctx->parse_flags, QIF_F_IGNORE_ACCOUNTS);
+}
+
+/********************************************************************************
+ * find or make ...
+ */
+
+QifAccount
+find_or_make_acct(QifContext ctx, char *name, GList *types)
+{
+    QifAccount res;
+
+    res = (QifAccount)qif_object_map_lookup(ctx, QIF_O_ACCOUNT, name);
+    if (res)
+        g_free(name);
+    else
+    {
+        res = qif_account_new();
+        res->name = name;
+        res->type_list = types;
+
+        qif_object_map_insert(ctx, name, (QifObject)res);
+    }
+
+    return res;
+}
+
+QifCategory
+find_or_make_cat(QifContext ctx, char *name)
+{
+    QifCategory res;
+
+    res = (QifCategory)qif_object_map_lookup(ctx, QIF_O_CATEGORY, name);
+    if (res)
+        g_free(name);
+    else
+    {
+        res = qif_cat_new();
+
+        res->name = name;
+
+        qif_object_map_insert(ctx, name, (QifObject)res);
+    }
+
+    return res;
+}
+
+QifClass
+find_or_make_class(QifContext ctx, char *name)
+{
+    QifClass res;
+
+    res = (QifClass)qif_object_map_lookup(ctx, QIF_O_CLASS, name);
+    if (res)
+        g_free(name);
+    else
+    {
+        res = qif_class_new();
+        res->name = name;
+        qif_object_map_insert(ctx, name, (QifObject)res);
+    }
+    return res;
+}
+
+/*****************************************************************************/
+
+/*
+ * initialize handlers
+ */
+void
+qif_object_init(void)
+{
+    int i;
+    static struct
+    {
+        QifType		type;
+        struct _QifHandler	handler;
+    } handlers[] =
+    {
+        { QIF_TYPE_BANK, { qif_txn_init, qif_txn_parse, qif_txn_end_acct } },
+        { QIF_TYPE_CASH, { qif_txn_init, qif_txn_parse, qif_txn_end_acct } },
+        { QIF_TYPE_CCARD, { qif_txn_init, qif_txn_parse, qif_txn_end_acct } },
+        { QIF_TYPE_INVST, { qif_txn_init, qif_txn_invst_parse, qif_txn_end_acct } },
+        { QIF_TYPE_PORT, { qif_txn_init, qif_txn_invst_parse, qif_txn_end_acct } },
+        { QIF_TYPE_OTH_A, { qif_txn_init, qif_txn_parse, qif_txn_end_acct } },
+        { QIF_TYPE_OTH_L, { qif_txn_init, qif_txn_parse, qif_txn_end_acct } },
+        { QIF_TYPE_CLASS, { NULL, qif_class_parse, NULL } },
+        { QIF_TYPE_CAT, { NULL, qif_cat_parse, NULL } },
+        { QIF_TYPE_SECURITY, { NULL, qif_security_parse, NULL } },
+        { QIF_ACCOUNT, { NULL, qif_account_parse, NULL } },
+        { QIF_AUTOSWITCH, { qif_autoswitch_set, NULL, NULL } },
+        { QIF_CLEAR_AUTOSWITCH, { qif_autoswitch_clear, NULL, NULL } },
+        { 0, {NULL, NULL, NULL} }
+    };
+
+    for (i = 0; handlers[i].type > 0; i++)
+    {
+        if (handlers[i].type <= 0)
+        {
+            PERR("Invalid type?!?  (%d @ %d)", handlers[i].type, i);
+        }
+        else
+            qif_register_handler(handlers[i].type, &(handlers[i].handler));
+    }
+}
diff --git a/gnucash/import-export/qif/qif-objects.h b/gnucash/import-export/qif/qif-objects.h
new file mode 100644
index 0000000..2d28e59
--- /dev/null
+++ b/gnucash/import-export/qif/qif-objects.h
@@ -0,0 +1,71 @@
+/*
+ * qif-objects.h -- QIF objects for the QIF importer
+ *
+ * Written By:	Derek Atkins <derek at ihtfp.com>
+ * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+#ifndef QIF_OBJECTS_H
+#define QIF_OBJECTS_H
+
+typedef struct _QifObject *QifObject;
+typedef struct _QifData *QifData;
+
+struct _QifObject
+{
+    const char*	type;
+    void		(*destroy)(QifObject);
+
+    /* QIF Objects contain data beyond this point.. */
+};
+
+#define QIF_O_ACCOUNT	"qif-acct"
+typedef struct _QifAccount *QifAccount;
+
+#define QIF_O_CATEGORY	"qif-cat"
+typedef struct _QifCategory *QifCategory;
+
+#define QIF_O_CLASS	"qif-class"
+typedef struct _QifClass *QifClass;
+
+#define QIF_O_SECURITY	"qif-security"
+typedef struct _QifSecurity *QifSecurity;
+
+#define QIF_O_TXN	"qif-txn"
+typedef struct _QifTxn *QifTxn;
+typedef struct _QifSplit *QifSplit;
+typedef struct _QifInvstTxn *QifInvstTxn;
+
+void qif_object_init(void);
+
+QifAccount find_or_make_acct(QifContext ctx, char *name, GList *types);
+QifCategory find_or_make_cat(QifContext ctx, char *name);
+QifClass find_or_make_class(QifContext ctx, char *name);
+
+/* merge the object into the context.  Returns the object that's in
+ * the context, which is either the supplied object or the
+ * already-existing object.
+ */
+QifAccount qif_account_merge(QifContext ctx, QifAccount acct);
+QifCategory qif_cat_merge(QifContext ctx, QifCategory cat);
+QifClass qif_class_merge(QifContext ctx, QifClass qclass);
+QifSecurity qif_security_merge(QifContext ctx, QifSecurity security);
+
+#endif /* QIF_OBJECTS_H */
diff --git a/gnucash/import-export/qif/qif-parse.c b/gnucash/import-export/qif/qif-parse.c
new file mode 100644
index 0000000..f291c86
--- /dev/null
+++ b/gnucash/import-export/qif/qif-parse.c
@@ -0,0 +1,935 @@
+/*
+ * qif-parse.c -- parse QIF
+ *
+ * Written by:        Derek Atkins <derek at ihtfp.com>
+ * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <string.h>
+
+/* For regex */
+#include <sys/types.h>
+#include <regex.h>
+
+#include <stdarg.h>
+
+#include "gnc-engine.h"
+#include "gnc-ui-util.h"
+
+#include "qif-import-p.h"
+#include "qif-objects-p.h"
+
+#include "import-parse.h"
+
+static QofLogModule log_module = GNC_MOD_IMPORT;
+
+/* An array of handlers for the various bang-types */
+static QifHandler qif_handlers[QIF_TYPE_MAX+1] = { NULL };
+
+/* Parser Regular Expressions */
+static gboolean qifp_regex_compiled = FALSE;
+static regex_t category_regex;
+
+/* A Hash Table of bang-types */
+static GHashTable *qif_bangtype_map = NULL;
+
+/* A Hash Table of action strings */
+static GHashTable *qif_action_map = NULL;
+
+/* A Hash Table of account types */
+static GHashTable *qif_atype_map = NULL;
+
+/************************************************************************/
+
+/* Register a handler */
+void
+qif_register_handler(QifType type, QifHandler handler)
+{
+    if (type <= 0 || type > QIF_TYPE_MAX)
+    {
+        PERR("Invalid type: %d", type);
+        return;
+    }
+    qif_handlers[type] = handler;
+}
+
+static void
+compile_regex()
+{
+    regcomp(&category_regex,
+            "^ *(\\[)?([^]/|]*)(]?)(/([^|]*))?(\\|(\\[)?([^]/]*)(]?)(/(.*))?)? *$",
+            REG_EXTENDED);
+
+    qifp_regex_compiled = TRUE;
+}
+
+#define QIF_ADD_TYPE(ts,t) \
+        g_hash_table_insert(qif_bangtype_map, ts, GINT_TO_POINTER(t)); \
+        g_hash_table_insert(qif_bangtype_map, _(ts), GINT_TO_POINTER(t));
+
+static void
+build_bangtype_map()
+{
+    g_return_if_fail(!qif_bangtype_map);
+
+    qif_bangtype_map = g_hash_table_new(g_str_hash, g_str_equal);
+    g_assert(qif_bangtype_map);
+
+    /* Translators FIXME: It is unclear whether these strings should
+       really be translated, and if yes, into which translation. */
+    QIF_ADD_TYPE(N_("type:bank"), QIF_TYPE_BANK);
+    QIF_ADD_TYPE(N_("type:cash"), QIF_TYPE_CASH);
+    QIF_ADD_TYPE(N_("type:ccard"), QIF_TYPE_CCARD);
+    QIF_ADD_TYPE(N_("type:invst"), QIF_TYPE_INVST);
+    QIF_ADD_TYPE(N_("type:port"), QIF_TYPE_PORT);
+    QIF_ADD_TYPE(N_("type:oth a"), QIF_TYPE_OTH_A);
+    QIF_ADD_TYPE(N_("type:oth l"), QIF_TYPE_OTH_L);
+    QIF_ADD_TYPE(N_("type:class"), QIF_TYPE_CLASS);
+    QIF_ADD_TYPE(N_("type:cat"), QIF_TYPE_CAT);
+    QIF_ADD_TYPE(N_("type:security"), QIF_TYPE_SECURITY);
+    QIF_ADD_TYPE(N_("account"), QIF_ACCOUNT);
+    QIF_ADD_TYPE(N_("option:autoswitch"), QIF_AUTOSWITCH);
+    QIF_ADD_TYPE(N_("clear:autoswitch"), QIF_CLEAR_AUTOSWITCH);
+}
+#undef QIF_ADD_TYPE
+
+#define QIF_ADD_ACT(ts,t) \
+        g_hash_table_insert(qif_action_map, ts, GINT_TO_POINTER(t));
+
+static void
+build_action_map()
+{
+    g_return_if_fail(!qif_action_map);
+
+    qif_action_map = g_hash_table_new(g_str_hash, g_str_equal);
+    g_assert(qif_action_map);
+
+    QIF_ADD_ACT("buy", QIF_A_BUY);
+    QIF_ADD_ACT("cvrshrt", QIF_A_BUY);
+    QIF_ADD_ACT("kauf", QIF_A_BUY);
+    QIF_ADD_ACT("buyx", QIF_A_BUYX);
+    QIF_ADD_ACT("cvrshrtx", QIF_A_BUYX);
+    QIF_ADD_ACT("kaufx", QIF_A_BUYX);
+    QIF_ADD_ACT("cglong", QIF_A_CGLONG);
+    QIF_ADD_ACT("kapgew", QIF_A_CGLONG); /* Kapitalgewinnsteuer */
+    QIF_ADD_ACT("cglongx", QIF_A_CGLONG);
+    QIF_ADD_ACT("kapgewx", QIF_A_CGLONG);
+    QIF_ADD_ACT("cgmid", QIF_A_CGMID);
+    QIF_ADD_ACT("cgmidx", QIF_A_CGMIDX);
+    QIF_ADD_ACT("cgshort", QIF_A_CGSHORT);
+    QIF_ADD_ACT("k.gewsp", QIF_A_CGSHORT);
+    QIF_ADD_ACT("cgshortx", QIF_A_CGSHORTX);
+    QIF_ADD_ACT("k.gewspx", QIF_A_CGSHORTX);
+    QIF_ADD_ACT("div", QIF_A_DIV); /* dividende */
+    QIF_ADD_ACT("divx", QIF_A_DIVX);
+    //QIF_ADD_ACT("exercise", QIF_A_EXERCISE);
+    //QIF_ADD_ACT("exercisex", QIF_A_EXERCISEX);
+    //QIF_ADD_ACT("expire", QIF_A_EXPIRE);
+    //QIF_ADD_ACT("grant", QIF_A_GRANT);
+    QIF_ADD_ACT("int", QIF_A_INTINC);
+    QIF_ADD_ACT("intinc", QIF_A_INTINC);
+    QIF_ADD_ACT("aktzu", QIF_A_INTINC); /* zinsen */
+    QIF_ADD_ACT("intx", QIF_A_INTINCX);
+    QIF_ADD_ACT("intincx", QIF_A_INTINCX);
+    QIF_ADD_ACT("margint", QIF_A_MARGINT);
+    QIF_ADD_ACT("margintx", QIF_A_MARGINTX);
+    QIF_ADD_ACT("miscexp", QIF_A_MISCEXP);
+    QIF_ADD_ACT("miscexpx", QIF_A_MISCEXPX);
+    QIF_ADD_ACT("miscinc", QIF_A_MISCINC);
+    QIF_ADD_ACT("cash", QIF_A_MISCINC);
+    QIF_ADD_ACT("miscincx", QIF_A_MISCINCX);
+    QIF_ADD_ACT("reinvdiv", QIF_A_REINVDIV);
+    QIF_ADD_ACT("reinvint", QIF_A_REINVINT);
+    QIF_ADD_ACT("reinvzin", QIF_A_REINVINT);
+    QIF_ADD_ACT("reinvlg", QIF_A_REINVLG);
+    QIF_ADD_ACT("reinvkur", QIF_A_REINVLG);
+    QIF_ADD_ACT("reinvmd", QIF_A_REINVMD);
+    QIF_ADD_ACT("reinvsg", QIF_A_REINVSG);
+    QIF_ADD_ACT("reinvksp", QIF_A_REINVSG);
+    QIF_ADD_ACT("reinvsh", QIF_A_REINVSH);
+    QIF_ADD_ACT("reminder", QIF_A_REMINDER);
+    QIF_ADD_ACT("erinnerg", QIF_A_REMINDER);
+    QIF_ADD_ACT("rtrncap", QIF_A_RTRNCAP);
+    QIF_ADD_ACT("rtrncapx", QIF_A_RTRNCAPX);
+    QIF_ADD_ACT("sell", QIF_A_SELL);
+    QIF_ADD_ACT("shtsell", QIF_A_SELL);
+    QIF_ADD_ACT("verkauf", QIF_A_SELL); /* verkaufen */
+    QIF_ADD_ACT("sellx", QIF_A_SELLX);
+    QIF_ADD_ACT("shtsellx", QIF_A_SELLX);
+    QIF_ADD_ACT("verkaufx", QIF_A_SELLX); /* verkaufen */
+    QIF_ADD_ACT("shrsin", QIF_A_SHRSIN);
+    QIF_ADD_ACT("aktzu", QIF_A_SHRSIN);
+    QIF_ADD_ACT("shrsout", QIF_A_SHRSOUT);
+    QIF_ADD_ACT("aktab", QIF_A_SHRSOUT);
+    QIF_ADD_ACT("stksplit", QIF_A_STKSPLIT);
+    QIF_ADD_ACT("aktsplit", QIF_A_STKSPLIT);
+    //QIF_ADD_ACT("vest", QIF_A_VEST);
+    QIF_ADD_ACT("xin", QIF_A_XIN);
+    QIF_ADD_ACT("contribx", QIF_A_XIN);
+    QIF_ADD_ACT("xout", QIF_A_XOUT);
+    QIF_ADD_ACT("withdrwx", QIF_A_XOUT);
+}
+#undef QIF_ADD_ACT
+
+static GList *
+make_list(int count, ...)
+{
+    GList *result = NULL;
+    GNCAccountType type;
+    va_list ap;
+
+    va_start (ap, count);
+    while (count--)
+    {
+        type = va_arg (ap, GNCAccountType);
+        result = g_list_prepend (result, GINT_TO_POINTER(type));
+    }
+    va_end (ap);
+
+
+    return g_list_reverse(result);
+}
+
+#define QIF_ADD_ATYPE(a,t) g_hash_table_insert(qif_atype_map, a, t);
+static void
+build_atype_map()
+{
+    g_return_if_fail(!qif_atype_map);
+
+    qif_atype_map = g_hash_table_new(g_str_hash, g_str_equal);
+    g_assert(qif_atype_map);
+
+    QIF_ADD_ATYPE("bank", make_list(1, ACCT_TYPE_BANK));
+    QIF_ADD_ATYPE("port", make_list(1, ACCT_TYPE_BANK));
+    QIF_ADD_ATYPE("cash", make_list(1, ACCT_TYPE_CASH));
+    QIF_ADD_ATYPE("ccard", make_list(1, ACCT_TYPE_CREDIT));
+    QIF_ADD_ATYPE("invst", make_list(3, ACCT_TYPE_BANK, ACCT_TYPE_STOCK,
+                                     ACCT_TYPE_MUTUAL));
+    QIF_ADD_ATYPE("oth a", make_list(3, ACCT_TYPE_ASSET, ACCT_TYPE_BANK,
+                                     ACCT_TYPE_CASH));
+    QIF_ADD_ATYPE("oth l", make_list(2, ACCT_TYPE_LIABILITY, ACCT_TYPE_CREDIT));
+    QIF_ADD_ATYPE("mutual", make_list(3, ACCT_TYPE_BANK, ACCT_TYPE_MUTUAL,
+                                      ACCT_TYPE_STOCK));
+
+    /* Internal types */
+    QIF_ADD_ATYPE("__any_bank__", make_list(5, ACCT_TYPE_BANK, ACCT_TYPE_CREDIT,
+                                            ACCT_TYPE_CASH, ACCT_TYPE_ASSET,
+                                            ACCT_TYPE_LIABILITY));
+    QIF_ADD_ATYPE("__all__", make_list(7, ACCT_TYPE_BANK, ACCT_TYPE_CREDIT,
+                                       ACCT_TYPE_CASH, ACCT_TYPE_ASSET,
+                                       ACCT_TYPE_LIABILITY, ACCT_TYPE_STOCK,
+                                       ACCT_TYPE_MUTUAL));
+    QIF_ADD_ATYPE("__stock__", make_list(2, ACCT_TYPE_STOCK, ACCT_TYPE_MUTUAL));
+    QIF_ADD_ATYPE("__income__", make_list(1, ACCT_TYPE_INCOME));
+    QIF_ADD_ATYPE("__expense__", make_list(1, ACCT_TYPE_EXPENSE));
+    QIF_ADD_ATYPE("__equity__", make_list(1, ACCT_TYPE_EQUITY));
+}
+#undef QIF_ADD_ATYPE
+
+/************************************************************************/
+
+/*
+ * We've got a !Type line.  Parse the line into the appropriate
+ * type and then initialize the handler.
+ */
+void
+qif_parse_bangtype(QifContext ctx, const char *line)
+{
+    QifType type;
+    char *bangtype;
+    gpointer result;
+
+    g_return_if_fail(line && *line == '!');
+
+    if (!qif_bangtype_map)
+        build_bangtype_map();
+
+    /* Make a local copy so we can manipulate it.
+     * - strip off leading/trailing whitespace
+     * - make it all lower case
+     */
+    bangtype = g_utf8_strdown(line + 1, -1);
+    g_strstrip(bangtype);
+
+    /* In some cases we get "!Type Bank" -- change the space to a colon */
+    if (!strncmp(bangtype, "type ", 5))
+        bangtype[5] = ':';
+
+    /* Lookup the bangtype in the map and then destroy the local copy */
+    result = g_hash_table_lookup(qif_bangtype_map, bangtype);
+    g_free(bangtype);
+
+    if (!result)
+    {
+        PWARN("Unknown bang-type at line %d: %s.  Ignored", ctx->lineno, line);
+        return;
+    }
+    type = GPOINTER_TO_INT(result);
+
+    /* Set the current context parse type and handler */
+    ctx->parse_type = type;
+    ctx->handler = qif_handlers[type];
+
+    /* now initialize this new parse type (if there's an init function) */
+    if (ctx->handler && ctx->handler->init)
+        ctx->handler->init(ctx);
+}
+
+/* returns TRUE if successful, FALSE if there is a problem */
+gboolean
+qif_parse_split_category(const char* str,
+                         char** cat, gboolean *cat_is_acct, char** cat_class,
+                         char** miscx_cat, gboolean *miscx_cat_is_acct,
+                         char **miscx_class)
+{
+    /* This is a pretty f**ked up string.  Basically it looks like:
+     *  ([)cat-or-acct(])(/(class))(|([)cat-of-acct(])(/ext))
+     *
+     * where data in parens is "optional" (depending on the context).
+     *
+     * examples from reality:
+     *
+     * category
+     * category:subcategory
+     * category/class
+     * category:subcat/class
+     * [account]
+     * [account]/class
+     *
+     * cat/cat-class|miscx-cat/miscx-class
+     */
+
+    regmatch_t pmatch[12];
+
+    g_return_val_if_fail(cat && cat_is_acct && cat_class &&
+                         miscx_cat && miscx_cat_is_acct && miscx_class, FALSE);
+
+
+    if (!qifp_regex_compiled)
+        compile_regex();
+
+    if (regexec(&category_regex, str, 12, pmatch, 0) != 0)
+    {
+        PERR("category match failed");
+        return FALSE;
+    }
+
+    /*
+     * what the substrings mean:
+     * 1 the opening [ for a transfer
+     * 2 the category
+     * 3 the closing ]
+     * 4 the class /
+     * 5 the class
+     * 6 the miscx expression (whole thing)
+     * 7 the opening [
+     * 8 the miscx category
+     * 9 the closing ]
+     * 10 the class /
+     * 11 the class
+     */
+
+    if (pmatch[2].rm_so == -1)
+    {
+        PERR("no category match found!");
+        return FALSE;
+    }
+
+    /* catgory name */
+    *cat = g_strndup(str + pmatch[2].rm_so, pmatch[2].rm_eo - pmatch[2].rm_so);
+    /* category is account? */
+    *cat_is_acct = (pmatch[1].rm_so != -1 && pmatch[3].rm_so != -1);
+    /* category class */
+    *cat_class = (pmatch[4].rm_so != -1 ?
+                  g_strndup(str + pmatch[5].rm_so, pmatch[5].rm_eo - pmatch[5].rm_so) :
+                  NULL);
+
+    /* miscx category name */
+    *miscx_cat = (pmatch[6].rm_so != -1 ?
+                  g_strndup(str + pmatch[8].rm_so, pmatch[8].rm_eo - pmatch[8].rm_so) :
+                  NULL);
+    /* miscx cat is acct */
+    *miscx_cat_is_acct  = (pmatch[7].rm_so != -1 && pmatch[9].rm_so != -1);
+    /* miscx class */
+    *miscx_class = (pmatch[10].rm_so != -1 ?
+                    g_strndup(str + pmatch[11].rm_so,
+                              pmatch[11].rm_eo - pmatch[11].rm_so) : NULL);
+
+    return TRUE;
+}
+
+/*
+ * qif_parse_cleared -- parse the 'C'leared field of a QIF Transaction.
+ * returns the QIF reconciled flag.
+ *
+ * * means cleared, x or X means reconciled, and ! or ? mean some
+ * budget related stuff I don't understand.
+ */
+QifRecnFlag
+qif_parse_cleared(QifLine line)
+{
+    g_return_val_if_fail(line, QIF_R_NO);
+    g_return_val_if_fail(line->line, QIF_R_NO);
+
+    switch (*line->line)
+    {
+    case '*':
+        return QIF_R_CLEARED;
+    case 'x':
+    case 'X':
+        return QIF_R_RECONCILED;
+    case '?':
+    case '!':
+        return QIF_R_BUDGETED;
+    default:
+        PERR("Unknown QIF Cleared flag at line %d: %s", line->lineno, line->line);
+        return QIF_R_NO;
+    }
+}
+
+QifAction qif_parse_action(QifLine line)
+{
+    QifAction qaction;
+    gpointer result;
+    char *action;
+
+    g_return_val_if_fail(line, QIF_A_NONE);
+    g_return_val_if_fail(line->line, QIF_A_NONE);
+
+    if (!qif_action_map)
+        build_action_map();
+
+    /* Duplicate the action and force it to lower case and strip any spaces */
+    action = g_utf8_strdown(line->line, -1);
+    g_strstrip(action);
+
+    result = g_hash_table_lookup(qif_action_map, action);
+    g_free(action);
+
+    if (!result)
+    {
+        /* XXX: pop up a dialog? */
+        PWARN("Unknown Action at line %d: %s.  Some transactions may be discarded",
+              line->lineno, line->line);
+        return QIF_A_NONE;
+    }
+
+    qaction = GPOINTER_TO_INT(result);
+    return qaction;
+}
+
+GList * qif_parse_acct_type(const char *str, gint lineno)
+{
+    GList *result;
+    char *type;
+
+    if (!qif_atype_map)
+        build_atype_map();
+
+    /* Duplicate the type and force it to lower case and strip any spaces */
+    type = g_utf8_strdown(str, -1);
+    g_strstrip(type);
+
+    result = g_hash_table_lookup(qif_atype_map, type);
+    g_free(type);
+
+    if (!result)
+    {
+        PWARN("Unknown account type at line %d: %s. ", lineno, str);
+        result = g_hash_table_lookup(qif_atype_map, "bank");
+        g_return_val_if_fail(result, NULL);
+    }
+
+    return result;
+}
+
+GList * qif_parse_acct_type_guess(QifType type)
+{
+    const char *atype = NULL;
+
+    switch (type)
+    {
+    case QIF_TYPE_BANK:
+        atype = "bank";
+        break;
+    case QIF_TYPE_CASH:
+        atype = "cash";
+        break;
+    case QIF_TYPE_CCARD:
+        atype = "ccard";
+        break;
+    case QIF_TYPE_INVST:
+        atype = "invst";
+        break;
+    case QIF_TYPE_PORT:
+        atype = "port";
+        break;
+    case QIF_TYPE_OTH_A:
+        atype = "oth a";
+        break;
+    case QIF_TYPE_OTH_L:
+        atype = "oth l";
+        break;
+    default:
+        return NULL;
+    }
+
+    return qif_parse_acct_type(atype, -1);
+}
+
+/***********************************************************************
+ * Parsing numbers and dates...
+ */
+
+typedef struct _parse_helper
+{
+    QifContext                ctx;
+
+    GncImportFormat        budget;
+    GncImportFormat        limit;
+    GncImportFormat        amount;
+    GncImportFormat        d_amount;
+    GncImportFormat        price;
+    GncImportFormat        shares;
+    GncImportFormat        commission;
+    GncImportFormat        date;
+} *parse_helper_t;
+
+#define QIF_PARSE_CHECK_NUMBER(str,help) { \
+        if (str) (help) = gnc_import_test_numeric((str), (help)); \
+}
+#define QIF_PARSE_PARSE_NUMBER(str,fmt,val) { \
+        if (str) gnc_import_parse_numeric((str), (fmt), (val)); \
+}
+
+static void
+qif_parse_check_account(gpointer key, gpointer val, gpointer data)
+{
+    parse_helper_t helper = data;
+    QifAccount acct = val;
+
+    QIF_PARSE_CHECK_NUMBER(acct->limitstr, helper->limit);
+    QIF_PARSE_CHECK_NUMBER(acct->budgetstr, helper->budget);
+}
+
+static void
+qif_parse_parse_account(gpointer key, gpointer val, gpointer data)
+{
+    parse_helper_t helper = data;
+    QifAccount acct = val;
+
+    QIF_PARSE_PARSE_NUMBER(acct->limitstr, helper->limit, &acct->limit);
+    QIF_PARSE_PARSE_NUMBER(acct->budgetstr, helper->budget, &acct->budget);
+}
+
+static void
+qif_parse_check_category(gpointer key, gpointer val, gpointer data)
+{
+    parse_helper_t helper = data;
+    QifCategory cat = val;
+
+    QIF_PARSE_CHECK_NUMBER(cat->budgetstr, helper->budget);
+}
+
+static void
+qif_parse_parse_category(gpointer key, gpointer val, gpointer data)
+{
+    parse_helper_t helper = data;
+    QifCategory cat = val;
+
+    QIF_PARSE_PARSE_NUMBER(cat->budgetstr, helper->budget, &cat->budget);
+}
+
+static void
+qif_parse_check_txn(gpointer val, gpointer data)
+{
+    parse_helper_t helper = data;
+    QifTxn txn = val;
+    QifSplit split;
+    QifInvstTxn itxn;
+    GList *node;
+
+    /* Check the date */
+    helper->date = gnc_import_test_date(txn->datestr, helper->date);
+
+    /* If this is an investment transaction, then all the info is in
+     * the invst_info.  Otherwise it's all in the splits.
+     */
+    itxn = txn->invst_info;
+    if (itxn)
+    {
+        QIF_PARSE_CHECK_NUMBER(itxn->amountstr, helper->amount);
+        QIF_PARSE_CHECK_NUMBER(itxn->d_amountstr, helper->d_amount);
+        QIF_PARSE_CHECK_NUMBER(itxn->pricestr, helper->price);
+        QIF_PARSE_CHECK_NUMBER(itxn->sharesstr, helper->shares);
+        QIF_PARSE_CHECK_NUMBER(itxn->commissionstr, helper->commission);
+
+    }
+    else
+    {
+        split = txn->default_split;
+        node = txn->splits;
+        do
+        {
+            QIF_PARSE_CHECK_NUMBER(split->amountstr, helper->amount);
+
+            if (node)
+            {
+                split = node->data;
+                node = node->next;
+            }
+            else
+                split = NULL;
+        }
+        while (split);
+    }
+}
+
+static void
+qif_parse_parse_txn(gpointer val, gpointer data)
+{
+    parse_helper_t helper = data;
+    QifTxn txn = val;
+    QifSplit split;
+    QifInvstTxn itxn;
+    GList *node;
+
+    /* Parse the date */
+    gnc_import_parse_date(txn->datestr, helper->date, &txn->date);
+
+    /* If this is an investment transaction, then all the info is in
+     * the invst_info.  Otherwise it's all in the splits.
+     */
+    itxn = txn->invst_info;
+    if (itxn)
+    {
+        QIF_PARSE_PARSE_NUMBER(itxn->amountstr, helper->amount, &itxn->amount);
+        QIF_PARSE_PARSE_NUMBER(itxn->d_amountstr, helper->d_amount, &itxn->d_amount);
+        QIF_PARSE_PARSE_NUMBER(itxn->pricestr, helper->price, &itxn->price);
+        QIF_PARSE_PARSE_NUMBER(itxn->sharesstr, helper->shares, &itxn->shares);
+        QIF_PARSE_PARSE_NUMBER(itxn->commissionstr, helper->commission,
+                               &itxn->commission);
+
+        qif_invst_txn_setup_splits(helper->ctx, txn);
+
+    }
+    else
+    {
+        split = txn->default_split;
+        node = txn->splits;
+        do
+        {
+            QIF_PARSE_PARSE_NUMBER(split->amountstr, helper->amount, &split->amount);
+
+            if (node)
+            {
+                split = node->data;
+                node = node->next;
+            }
+            else
+                split = NULL;
+        }
+        while (split);
+
+        qif_txn_setup_splits(txn);
+    }
+}
+
+void
+qif_parse_all(QifContext ctx, gpointer arg)
+{
+    struct _parse_helper helper;
+
+    helper.ctx = ctx;
+
+    /* PARSE ACCOUNTS */
+
+    /* First, figure out the formats */
+    helper.limit = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
+    helper.budget = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
+    qif_object_map_foreach(ctx, QIF_O_ACCOUNT, qif_parse_check_account, &helper);
+
+    /* Make sure it's not ambiguous */
+    if (helper.limit & (helper.limit - 1)) helper.limit = GNCIF_NUM_PERIOD;
+    if (helper.budget & (helper.budget - 1)) helper.budget = GNCIF_NUM_PERIOD;
+
+    /* Now convert the numbers */
+    qif_object_map_foreach(ctx, QIF_O_ACCOUNT, qif_parse_parse_account, &helper);
+
+    /* PARSE CATEGORIES */
+
+    helper.budget = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
+    qif_object_map_foreach(ctx, QIF_O_CATEGORY, qif_parse_check_category, &helper);
+
+    /* make sure it's not ambiguous */
+    if (helper.budget & (helper.budget - 1)) helper.budget = GNCIF_NUM_PERIOD;
+
+    /* Now convert the numbers */
+    qif_object_map_foreach(ctx, QIF_O_CATEGORY, qif_parse_parse_category, &helper);
+
+    /* PARSE TRANSACTIONS */
+    helper.amount = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
+    helper.d_amount = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
+    helper.price = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
+    helper.shares = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
+    helper.commission = GNCIF_NUM_PERIOD | GNCIF_NUM_COMMA;
+    helper.date = GNCIF_DATE_MDY | GNCIF_DATE_DMY | GNCIF_DATE_YMD | GNCIF_DATE_YDM;
+
+    qif_object_list_foreach(ctx, QIF_O_TXN, qif_parse_check_txn, &helper);
+
+    /* check/fix ambiguities */
+    if (helper.amount & (helper.amount - 1)) helper.amount = GNCIF_NUM_PERIOD;
+    if (helper.d_amount & (helper.d_amount - 1)) helper.d_amount = GNCIF_NUM_PERIOD;
+    if (helper.price & (helper.price - 1)) helper.price = GNCIF_NUM_PERIOD;
+    if (helper.shares & (helper.shares - 1)) helper.shares = GNCIF_NUM_PERIOD;
+    if (helper.commission & (helper.commission - 1))
+        helper.commission = GNCIF_NUM_PERIOD;
+
+    if (helper.date & (helper.date - 1))
+    {
+        helper.date = gnc_import_choose_fmt(_("The Date format is ambiguous.  "
+                                              "Please choose the correct format."),
+                                            helper.date, arg);
+    }
+
+    /* now parse it.. */
+    qif_object_list_foreach(ctx, QIF_O_TXN, qif_parse_parse_txn, &helper);
+}
+
+typedef struct
+{
+    QifContext        ctx;
+    GList *        list;
+    const char*        type;
+} qif_merge_t;
+
+static void
+qif_merge_accts(gpointer key, gpointer value, gpointer data)
+{
+    qif_merge_t *merge = data;
+    QifAccount acct = value;
+
+    /* Merge into the context.  Remember items moved into the parent */
+    if (qif_account_merge(merge->ctx, acct) == acct)
+        merge->list = g_list_prepend(merge->list, acct->name);
+}
+
+static void
+qif_merge_cats(gpointer key, gpointer value, gpointer data)
+{
+    qif_merge_t *merge = data;
+    QifCategory cat = value;
+
+    /* Merge into the context.  Remember items moved into the parent */
+    if (qif_cat_merge(merge->ctx, cat) == cat)
+        merge->list = g_list_prepend(merge->list, cat->name);
+}
+
+static void
+qif_merge_classes(gpointer key, gpointer value, gpointer data)
+{
+    qif_merge_t *merge = data;
+    QifClass qclass = value;
+
+    /* Merge into the context.  Remember items moved into the parent */
+    if (qif_class_merge(merge->ctx, qclass) == qclass)
+        merge->list = g_list_prepend(merge->list, qclass->name);
+}
+
+static void
+qif_merge_secs(gpointer key, gpointer value, gpointer data)
+{
+    qif_merge_t *merge = data;
+    QifSecurity sec = value;
+
+    /* Merge into the context.  Remember items moved into the parent */
+    if (qif_security_merge(merge->ctx, sec) == sec)
+        merge->list = g_list_prepend(merge->list, sec->name);
+}
+
+static void
+qif_merge_del(gpointer obj, gpointer data)
+{
+    qif_merge_t *merge = data;
+    const char *name = obj;
+
+    qif_object_map_remove(merge->ctx, merge->type, name);
+}
+
+static void
+qif_massage_split(QifSplit split, QifContext ctx)
+{
+    const char *type = QIF_O_CATEGORY;
+    char *name;
+
+    if (split->cat.obj)
+    {
+        if (split->cat_is_acct)
+        {
+            type = QIF_O_ACCOUNT;
+            name = split->cat.acct->name;
+        }
+        else
+            name = split->cat.cat->name;
+
+        split->cat.obj = qif_object_map_lookup(ctx, type, name);
+    }
+
+    if (split->cat_class)
+    {
+        split->cat_class = (QifClass) qif_object_map_lookup(ctx, QIF_O_CLASS,
+                           split->cat_class->name);
+    }
+}
+
+static void
+qif_massage_itxn(QifInvstTxn itxn, QifContext ctx)
+{
+    const char *type = QIF_O_CATEGORY;
+    char *name;
+
+    if (itxn->far_cat.obj)
+    {
+        if (itxn->far_cat_is_acct)
+        {
+            type = QIF_O_ACCOUNT;
+            name = itxn->far_cat.acct->name;
+        }
+        else
+            name = itxn->far_cat.cat->name;
+
+        itxn->far_cat.obj = qif_object_map_lookup(ctx, type, name);
+    }
+}
+
+static void
+qif_massage_txn(gpointer obj, gpointer data)
+{
+    QifTxn txn = obj;
+    QifContext ctx = data;
+    QifSplit split;
+    GList *node;
+
+    if (txn->from_acct)
+        txn->from_acct = (QifAccount) qif_object_map_lookup(ctx, QIF_O_ACCOUNT,
+                         txn->from_acct->name);
+
+    if (txn->invst_info)
+        qif_massage_itxn(txn->invst_info, ctx);
+
+    if (txn->default_split)
+        qif_massage_split(txn->default_split, ctx);
+
+    for (node = txn->splits; node; node = node->next)
+    {
+        split = node->data;
+        qif_massage_split(split, ctx);
+    }
+}
+
+void
+qif_parse_merge_files(QifContext ctx)
+{
+    GList *node;
+    GList *accts = NULL;
+    GList *cats = NULL;
+    GList *classes = NULL;
+    GList *securities = NULL;
+    QifContext fctx;
+
+    qif_merge_t merge;
+
+    g_return_if_fail(ctx);
+
+    /* Make sure each of the "file" contexts have been parsed.
+     * note that we don't care about OUR context -- we can run this
+     * process multiple times safely.
+     */
+    for (node = ctx->files; node; node = node->next)
+    {
+        fctx = node->data;
+        g_return_if_fail(fctx->parsed);
+    }
+
+
+    /* Iterate over each file.  Merge the Accounts, Categories, Classes,
+     * Securities, and Transactions into the top-level context.  Be sure
+     * to re-point all Transaction/Split category/class/account pointers
+     * to the new top-level item.  Then be sure to remove the
+     * "duplicated" items so we don't double-free (as we don't refcount,
+     * either).
+     */
+    for (node = ctx->files; node; node = node->next)
+    {
+        fctx = node->data;
+
+        /* Merge accts, categories, classes, and securities */
+
+        merge.ctx = ctx;
+        merge.list = NULL;
+        qif_object_map_foreach(fctx, QIF_O_ACCOUNT, qif_merge_accts, &merge);
+        accts = merge.list;
+
+        merge.list = NULL;
+        qif_object_map_foreach(fctx, QIF_O_CATEGORY, qif_merge_cats, &merge);
+        cats = merge.list;
+
+        merge.list = NULL;
+        qif_object_map_foreach(fctx, QIF_O_CLASS, qif_merge_classes, &merge);
+        classes = merge.list;
+
+        merge.list = NULL;
+        qif_object_map_foreach(fctx, QIF_O_SECURITY, qif_merge_secs, &merge);
+        securities = merge.list;
+
+
+        /* repoint the transactions to the merged context data */
+        qif_object_list_foreach(fctx, QIF_O_TXN, qif_massage_txn, ctx);
+
+
+        /* then remove from the file context objects referenced in the top context */
+        merge.ctx = fctx;
+        merge.type = QIF_O_ACCOUNT;
+        g_list_foreach(accts, qif_merge_del, &merge);
+        g_list_free(accts);
+
+        merge.type = QIF_O_CATEGORY;
+        g_list_foreach(cats, qif_merge_del, &merge);
+        g_list_free(cats);
+
+        merge.type = QIF_O_CLASS;
+        g_list_foreach(classes, qif_merge_del, &merge);
+        g_list_free(classes);
+
+        merge.type = QIF_O_SECURITY;
+        g_list_foreach(securities, qif_merge_del, &merge);
+        g_list_free(securities);
+
+    }
+
+    /* We've been parsed */
+    ctx->parsed = TRUE;
+}
diff --git a/gnucash/import-export/qif/qif-parse.h b/gnucash/import-export/qif/qif-parse.h
new file mode 100644
index 0000000..11d439c
--- /dev/null
+++ b/gnucash/import-export/qif/qif-parse.h
@@ -0,0 +1,50 @@
+/*
+ * qif-parse.h -- routines for parsing pieces of a QIF file
+ *
+ * Written By:	Derek Atkins  <derek at ihtfp.com>
+ * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+#ifndef QIF_PARSE_H
+#define QIF_PARSE_H
+
+#include "qif-import.h"
+
+void qif_register_handler(QifType type, QifHandler handler);
+void qif_parse_bangtype(QifContext ctx, const char *line);
+
+gboolean
+qif_parse_split_category(const char* str,
+                         char** cat, gboolean *cat_is_acct, char** cat_class,
+                         char** miscx_cat, gboolean *miscx_cat_is_acct,
+                         char **miscx_class);
+
+gboolean qif_parse_numeric(QifLine line, gnc_numeric *num);
+QifRecnFlag qif_parse_cleared(QifLine line);
+QifAction qif_parse_action(QifLine line);
+
+/* The caller should never destroy this list */
+GList * qif_parse_acct_type(const char *str, gint lineno);
+GList * qif_parse_acct_type_guess(QifType type);
+
+/* Parse all objects */
+void qif_parse_all(QifContext ctx, gpointer ui_args);
+
+#endif /* QIF_PARSE_H */
diff --git a/gnucash/import-export/qif/test/CMakeLists.txt b/gnucash/import-export/qif/test/CMakeLists.txt
new file mode 100644
index 0000000..d874ca3
--- /dev/null
+++ b/gnucash/import-export/qif/test/CMakeLists.txt
@@ -0,0 +1,18 @@
+
+set(QIF_TEST_INCLUDE_DIRS
+  ${CMAKE_BINARY_DIR}/common
+  ${CMAKE_SOURCE_DIR}/gnucash/import-export/qif
+  ${CMAKE_SOURCE_DIR}/libgnucash/engine
+  ${CMAKE_SOURCE_DIR}/common/test-core
+  ${GLIB2_INCLUDE_DIRS}
+)
+set(QIF_TEST_LIBS gncmod-qif test-core)
+
+if (FALSE)
+  # Tests for this directory are not run.
+  gnc_add_test(test-link-qif test-link.c QIF_TEST_INCLUDE_DIRS QIF_TEST_LIBS)
+  gnc_add_test(test-qif test-qif.c QIF_TEST_INCLUDE_DIRS QIF_TEST_LIBS
+    GNC_TEST_FILES=${CMAKE_CURRENT_SOURCE_DIR}/test-files)
+endif()
+
+set_dist_list(test_qif_DIST CMakeLists.txt test-link.c test-qif.c test-files/test-1-bank-txn.qif)
diff --git a/gnucash/import-export/qif/test/test-files/test-1-bank-txn.qif b/gnucash/import-export/qif/test/test-files/test-1-bank-txn.qif
new file mode 100644
index 0000000..59592d0
--- /dev/null
+++ b/gnucash/import-export/qif/test/test-files/test-1-bank-txn.qif
@@ -0,0 +1,6 @@
+!Type:Bank
+D2003/01/27
+T123.45
+PTest Payee
+LTest Category
+^
diff --git a/gnucash/import-export/qif/test/test-link.c b/gnucash/import-export/qif/test/test-link.c
new file mode 100644
index 0000000..fd55d42
--- /dev/null
+++ b/gnucash/import-export/qif/test/test-link.c
@@ -0,0 +1,28 @@
+/********************************************************************\
+ * This program is free software; you can redistribute it and/or    *
+ * modify it under the terms of the GNU General Public License as   *
+ * published by the Free Software Foundation; either version 2 of   *
+ * the License, or (at your option) any later version.              *
+ *                                                                  *
+ * This program is distributed in the hope that it will be useful,  *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
+ * GNU General Public License for more details.                     *
+ *                                                                  *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact:                        *
+ *                                                                  *
+ * Free Software Foundation           Voice:  +1-617-542-5942       *
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org                   *
+ *                                                                  *
+\********************************************************************/
+
+#include "qif-import.h"
+
+int
+main(int argc, char *argv[])
+{
+    qif_context_new();
+    return 0;
+}
diff --git a/gnucash/import-export/qif/test/test-qif.c b/gnucash/import-export/qif/test/test-qif.c
new file mode 100644
index 0000000..c331bef
--- /dev/null
+++ b/gnucash/import-export/qif/test/test-qif.c
@@ -0,0 +1,110 @@
+/*
+ * test-qif.c -- Test the QIF Import routines.
+ *
+ * Created by:	Derek Atkins <derek at ihtfp.com>
+ * Copyright (c) 2003 Derek Atkins <warlord at MIT.EDU>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+#include <glib.h>
+#include <libguile.h>
+
+#include "gnc-module.h"
+#include "qif-import.h"
+#include "qif-import-p.h"	/* Let's test some internal stuff, too */
+
+#include "test-stuff.h"
+
+/* XXX */
+extern void qif_object_init(void);
+
+static QifContext
+test_qif_load_file(QifContext ctx, const char *filename,
+                   gint txn_count, gint acct_count, gboolean needs_acct)
+{
+    QifContext file;
+
+    printf("qif loading \"%s\"...\n", filename);
+    file = qif_file_new(ctx, filename);
+    do_test(file != NULL, "failed to read file");
+    if (!file) return NULL;
+
+    do_test(qif_object_list_count(file, QIF_O_TXN) == txn_count,
+            "Transaction count didn't match");
+    do_test(qif_object_map_count(file, QIF_O_ACCOUNT) == acct_count,
+            "Account count didn't match");
+    do_test(qif_file_needs_account(file) == needs_acct,
+            "Needs account flad didn't match");
+
+    return file;
+}
+
+static void
+test_qif(void)
+{
+    QifContext ctx, file;
+    char *filename;
+    const char *location = g_getenv("GNC_TEST_FILES");
+    int i;
+
+    ctx = qif_context_new();
+    do_test(ctx != NULL, "failed to create the qif context");
+    if (!ctx) return;
+
+    if (!location)
+        location = "test-files";
+
+    for (i = 0; i < 1; i++)
+    {
+        filename = g_strdup_printf("%s/%s", location, "test-1-bank-txn.qif");
+        file = test_qif_load_file(ctx, filename, 1, 0, TRUE);
+        g_free(filename);
+        if (!file) continue;
+
+        if (qif_file_needs_account(file))
+            qif_file_set_default_account(file, "test-1-bank-txn");
+
+        do_test(qif_file_needs_account(file) == FALSE,
+                "'Needs account' flag not cleared properly");
+
+        do_test(qif_file_parse(file, NULL) == QIF_E_OK,
+                "file failed to parse.");
+    }
+
+    qif_context_destroy(ctx);
+
+    success("QIF test successful");
+}
+
+static void
+main_helper(void *closure, int argc, char **argv)
+{
+    qif_object_init();		/* XXX:FIXME */
+    test_qif();
+    print_test_results();
+    exit(get_rv());
+}
+
+int
+main(int argc, char **argv)
+{
+    scm_boot_guile(argc, argv, main_helper, NULL);
+    return 0;
+}
+
diff --git a/po/POTFILES.in b/po/POTFILES.in
index b8cc820..426b3f2 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -347,6 +347,9 @@ gnucash/import-export/ofx/gnc-ofx-import.c
 gnucash/import-export/ofx/gnc-ofx-kvp.c
 gnucash/import-export/ofx/gnc-plugin-ofx.c
 gnucash/import-export/ofx/gschemas/org.gnucash.dialogs.import.ofx.gschema.xml.in
+gnucash/import-export/qif/qif-context.c
+gnucash/import-export/qif/qif-file.c
+gnucash/import-export/qif/qif-objects.c
 gnucash/import-export/qif-imp/assistant-qif-import.c
 gnucash/import-export/qif-imp/dialog-account-picker.c
 gnucash/import-export/qif-imp/gncmod-qif-import.c
@@ -483,7 +486,8 @@ gnucash/report/standard-reports/general-journal.scm
 gnucash/report/standard-reports/general-ledger.scm
 gnucash/report/standard-reports/income-gst-statement.scm
 gnucash/report/standard-reports/income-statement.scm
-gnucash/report/standard-reports/net-charts.scm
+gnucash/report/standard-reports/net-barchart.scm
+gnucash/report/standard-reports/net-linechart.scm
 gnucash/report/standard-reports/portfolio.scm
 gnucash/report/standard-reports/price-scatter.scm
 gnucash/report/standard-reports/register.scm
@@ -571,6 +575,7 @@ libgnucash/backend/sql/gnc-sql-result.cpp
 libgnucash/backend/sql/gnc-tax-table-sql.cpp
 libgnucash/backend/sql/gnc-transaction-sql.cpp
 libgnucash/backend/sql/gnc-vendor-sql.cpp
+libgnucash/backend/xml/.#gnc-invoice-xml-v2.cpp
 libgnucash/backend/xml/gnc-account-xml-v2.cpp
 libgnucash/backend/xml/gnc-address-xml-v2.cpp
 libgnucash/backend/xml/gnc-backend-xml.cpp



Summary of changes:
 gnucash/gtkbuilder/dialog-account-picker.glade     |   2 +-
 .../import-export/qif-imp/assistant-qif-import.c   | 116 +++++++++++----------
 gnucash/import-export/qif-imp/qif-to-gnc.scm       |  43 ++++----
 po/POTFILES.in                                     |   1 +
 4 files changed, 87 insertions(+), 75 deletions(-)



More information about the gnucash-changes mailing list