mirror of
https://github.com/Gnucash/gnucash.git
synced 2024-11-25 18:30:23 -06:00
Remove abandoned C-language QIF implementation.
This commit is contained in:
parent
554001604a
commit
edd439a05e
@ -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)
|
||||
|
||||
|
||||
|
@ -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,12 @@ 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 +1089,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);
|
||||
|
@ -910,7 +910,9 @@
|
||||
;; 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)))
|
||||
(if (and (and
|
||||
(and far-acct-name near acct-name)
|
||||
(not (qif-xtn:mark (car xtns))))
|
||||
(string=? (qif-xtn:from-acct (car xtns)) far-acct-name))
|
||||
(begin
|
||||
(set! how
|
||||
|
@ -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)
|
@ -1,417 +0,0 @@
|
||||
/*
|
||||
* qif-context.c -- create/destroy QIF Contexts
|
||||
*
|
||||
* Written By: Derek Atkins <derek@ihtfp.com>
|
||||
* Copyright (c) 2003 Derek Atkins <warlord@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@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);
|
||||
}
|
@ -1,152 +0,0 @@
|
||||
/*
|
||||
* qif-defaults.c -- QIF Defaults -- default accounts...
|
||||
*
|
||||
* Created by: Derek Atkins <derek@ihtfp.com>
|
||||
* Copyright (c) 2003 Derek Atkins <warlord@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@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);
|
||||
}
|
||||
|
@ -1,44 +0,0 @@
|
||||
/*
|
||||
* qif-defaults.h -- QIF Defaults -- default accounts...
|
||||
*
|
||||
* Created by: Derek Atkins <derek@ihtfp.com>
|
||||
* Copyright (c) 2003 Derek Atkins <warlord@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@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 */
|
@ -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@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@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);
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
/* qif-import-p.h -- a QIF Importer module (private headers)
|
||||
*
|
||||
* Written By: Derek Atkins <derek@ihtfp.com>
|
||||
* Copyright (c) 2003 Derek Atkins <warlord@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@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 */
|
@ -1,100 +0,0 @@
|
||||
/* qif-import-p.h -- a QIF Importer module (private headers)
|
||||
*
|
||||
* Written By: Derek Atkins <derek@ihtfp.com>
|
||||
* Copyright (c) 2003 Derek Atkins <warlord@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@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 */
|
@ -1,159 +0,0 @@
|
||||
/*
|
||||
* qif-import.h -- a QIF Import module
|
||||
*
|
||||
* Written By: Derek Atkins <derek@ihtfp.com>
|
||||
* Copyright (c) 2003 Derek Atkins <warlord@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@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 */
|
@ -1,174 +0,0 @@
|
||||
/*
|
||||
* qif-objects-p.h -- Private header: QIF objects for the QIF importer
|
||||
*
|
||||
* Written By: Derek Atkins <derek@ihtfp.com>
|
||||
* Copyright (c) 2003 Derek Atkins <warlord@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@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 */
|
File diff suppressed because it is too large
Load Diff
@ -1,71 +0,0 @@
|
||||
/*
|
||||
* qif-objects.h -- QIF objects for the QIF importer
|
||||
*
|
||||
* Written By: Derek Atkins <derek@ihtfp.com>
|
||||
* Copyright (c) 2003 Derek Atkins <warlord@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@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 */
|
@ -1,935 +0,0 @@
|
||||
/*
|
||||
* qif-parse.c -- parse QIF
|
||||
*
|
||||
* Written by: Derek Atkins <derek@ihtfp.com>
|
||||
* Copyright (c) 2003 Derek Atkins <warlord@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@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;
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
/*
|
||||
* qif-parse.h -- routines for parsing pieces of a QIF file
|
||||
*
|
||||
* Written By: Derek Atkins <derek@ihtfp.com>
|
||||
* Copyright (c) 2003 Derek Atkins <warlord@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@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 */
|
@ -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)
|
@ -1,6 +0,0 @@
|
||||
!Type:Bank
|
||||
D2003/01/27
|
||||
T123.45
|
||||
PTest Payee
|
||||
LTest Category
|
||||
^
|
@ -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@gnu.org *
|
||||
* *
|
||||
\********************************************************************/
|
||||
|
||||
#include "qif-import.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
qif_context_new();
|
||||
return 0;
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
/*
|
||||
* test-qif.c -- Test the QIF Import routines.
|
||||
*
|
||||
* Created by: Derek Atkins <derek@ihtfp.com>
|
||||
* Copyright (c) 2003 Derek Atkins <warlord@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@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;
|
||||
}
|
||||
|
@ -347,9 +347,6 @@ 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
|
||||
@ -486,8 +483,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 +571,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
|
||||
|
Loading…
Reference in New Issue
Block a user