mirror of
https://github.com/Gnucash/gnucash.git
synced 2026-08-05 18:56:55 -05:00
* src/import-export/qif/...
more work on the new qif importer. structure it into a parent context that contains a "list of files" (each file has its own context). Add some tests to actually test the code. It actually loads a basic qif file, now. Still need to perform merging (internal and into GNC) as well as the qif-to-gnc maps. git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@8914 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
@@ -14,26 +14,33 @@
|
||||
#include "qif-import-p.h"
|
||||
|
||||
QifContext
|
||||
qif_context_new(QifContext parent)
|
||||
qif_context_new(void)
|
||||
{
|
||||
QifContext ctx = g_new0(struct _QifContext, 1);
|
||||
|
||||
if (parent)
|
||||
ctx->parent = parent;
|
||||
|
||||
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);
|
||||
|
||||
/* we should assume that we've got a bank account... just in case.. */
|
||||
qif_parse_bangtype(ctx, "!type:bank");
|
||||
|
||||
/* Return the new context */
|
||||
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);
|
||||
@@ -42,6 +49,13 @@ qif_context_destroy(QifContext ctx)
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -51,6 +65,22 @@ qif_context_destroy(QifContext ctx)
|
||||
* 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)
|
||||
{
|
||||
@@ -171,8 +201,8 @@ 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_lists, qif_object_map_remove_all, NULL);
|
||||
g_hash_table_destroy(ctx->object_lists);
|
||||
g_hash_table_foreach_remove(ctx->object_maps, qif_object_map_remove_all, NULL);
|
||||
g_hash_table_destroy(ctx->object_maps);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
@@ -181,6 +211,19 @@ void qif_object_map_destroy(QifContext ctx)
|
||||
* Insert and remove a QifObject from the Object Lists in this Qif Context
|
||||
*/
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "gnc-engine-util.h"
|
||||
|
||||
#include "qif-import-p.h"
|
||||
#include "qif-objects-p.h"
|
||||
|
||||
static short module = MOD_IMPORT;
|
||||
|
||||
@@ -118,7 +119,7 @@ qif_make_record(QifContext ctx, char *buf, size_t bufsiz, gboolean *found_bangty
|
||||
/* read a qif file and parse it, line by line
|
||||
* return FALSE on fatal error, TRUE otherwise
|
||||
*/
|
||||
QifError
|
||||
static QifError
|
||||
qif_read_file(QifContext ctx, FILE *f)
|
||||
{
|
||||
char buf[BUFSIZ];
|
||||
@@ -126,8 +127,8 @@ qif_read_file(QifContext ctx, FILE *f)
|
||||
gboolean found_bang;
|
||||
QifError err = QIF_E_OK;
|
||||
|
||||
g_return_val_if_fail(ctx, FALSE);
|
||||
g_return_val_if_fail(f, FALSE);
|
||||
g_return_val_if_fail(ctx, QIF_E_BADARGS);
|
||||
g_return_val_if_fail(f, QIF_E_BADARGS);
|
||||
|
||||
ctx->fp = f;
|
||||
ctx->lineno = -1;
|
||||
@@ -173,3 +174,114 @@ qif_read_file(QifContext ctx, FILE *f)
|
||||
|
||||
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 = 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;
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,31 +25,34 @@ struct _QifContext {
|
||||
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;
|
||||
|
||||
/* The current and last seen account */
|
||||
/* The current and "opening balance" account */
|
||||
QifAccount current_acct;
|
||||
QifAccount last_seen_acct;
|
||||
QifAccount opening_bal_acct;
|
||||
|
||||
/* Current parse state */
|
||||
QifObject parse_state;
|
||||
/* HashTable of Maps of data objects */
|
||||
GHashTable * object_maps;
|
||||
|
||||
/* HashTable of Lists of data objects */
|
||||
GHashTable * object_lists;
|
||||
|
||||
/* HashTable of Maps of data objects */
|
||||
GHashTable * object_maps;
|
||||
/* 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);
|
||||
@@ -60,6 +63,7 @@ void qif_object_map_destroy(QifContext ctx);
|
||||
GList * qif_object_map_get(QifContext ctx, const char *type);
|
||||
|
||||
/* Object Lists */
|
||||
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);
|
||||
@@ -68,4 +72,8 @@ 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 */
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "gnc-numeric.h"
|
||||
|
||||
typedef enum {
|
||||
QIF_TYPE_BANK,
|
||||
QIF_TYPE_BANK = 1,
|
||||
QIF_TYPE_CASH,
|
||||
QIF_TYPE_CCARD,
|
||||
QIF_TYPE_INVST,
|
||||
@@ -37,6 +37,7 @@ 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 {
|
||||
@@ -52,6 +53,8 @@ typedef enum {
|
||||
QIF_E_OK = 0,
|
||||
QIF_E_INTERNAL,
|
||||
QIF_E_BADSTATE,
|
||||
QIF_E_BADARGS,
|
||||
QIF_E_NOFILE,
|
||||
} QifError;
|
||||
|
||||
|
||||
@@ -101,13 +104,25 @@ typedef enum {
|
||||
|
||||
/* Public API Functions */
|
||||
|
||||
QifContext qif_context_new(QifContext parent);
|
||||
/* Create a QIF Import Context */
|
||||
QifContext qif_context_new(void);
|
||||
void qif_context_destroy(QifContext ctx);
|
||||
|
||||
/* Reads the file into the qif context */
|
||||
QifError qif_read_file(QifContext ctx, FILE *f);
|
||||
/* 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);
|
||||
|
||||
/* Parse all objects */
|
||||
void qif_parse_all(QifContext ctx, gpointer arg);
|
||||
/* 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);
|
||||
|
||||
#endif /* QIF_IMPORT_H */
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#ifndef QIF_OBJECTS_P_H
|
||||
#define QIF_OBJECTS_P_H
|
||||
|
||||
#include "gnc-date.h"
|
||||
|
||||
#include "qif-import.h"
|
||||
#include "qif-objects.h"
|
||||
#include "gnc-numeric.h"
|
||||
@@ -130,6 +132,7 @@ struct _QifInvstTxn {
|
||||
char * commissionstr;
|
||||
|
||||
char * security;
|
||||
char * catstr;
|
||||
|
||||
union {
|
||||
QifObject obj;
|
||||
@@ -139,8 +142,8 @@ struct _QifInvstTxn {
|
||||
gboolean far_cat_is_acct;
|
||||
};
|
||||
|
||||
|
||||
void qif_txn_post_parse_amounts(QifTxn txn);
|
||||
/* 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 */
|
||||
|
||||
+163
-150
@@ -31,10 +31,6 @@ static short module = MOD_IMPORT;
|
||||
obj; \
|
||||
})
|
||||
|
||||
/* Set and clear flags in bit-flags */
|
||||
#define qif_set_flag(i,f) (i |= f)
|
||||
#define qif_clear_flag(i,f) (i &= ~f)
|
||||
|
||||
/* 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.
|
||||
@@ -133,7 +129,6 @@ qif_account_parse(QifContext ctx, GList *record)
|
||||
switch (line->type) {
|
||||
case 'N': /* N : account name */
|
||||
qif_save_str(acct->name);
|
||||
ctx->last_seen_acct = acct;
|
||||
break;
|
||||
case 'D': /* D : account description */
|
||||
qif_save_str(acct->desc);
|
||||
@@ -578,9 +573,6 @@ qif_txn_new(void)
|
||||
static void
|
||||
qif_txn_init(QifContext ctx)
|
||||
{
|
||||
if (ctx->parse_flags & QIF_F_IGNORE_ACCOUNTS)
|
||||
ctx->current_acct = ctx->last_seen_acct;
|
||||
|
||||
qif_clear_flag(ctx->parse_flags, QIF_F_IGNORE_ACCOUNTS);
|
||||
ctx->parse_state = NULL;
|
||||
}
|
||||
@@ -615,7 +607,7 @@ static void
|
||||
qif_process_opening_balance_txn(QifContext ctx, QifTxn txn)
|
||||
{
|
||||
QifSplit split = txn->default_split;
|
||||
QifAccount cur_acct = ctx->current_acct;
|
||||
QifAccount cur_acct = NULL; /* We know that ctx->current_acct is NULL */
|
||||
|
||||
g_return_if_fail(txn->invst_info == NULL);
|
||||
|
||||
@@ -643,8 +635,17 @@ qif_process_opening_balance_txn(QifContext ctx, QifTxn txn)
|
||||
split->cat.acct = qif_default_equity_acct(ctx);
|
||||
}
|
||||
|
||||
if (!ctx->current_acct)
|
||||
/*
|
||||
* 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
|
||||
@@ -763,22 +764,15 @@ qif_txn_parse(QifContext ctx, GList *record)
|
||||
if (txn->default_split->catstr)
|
||||
qif_split_parse_category(ctx, txn->default_split);
|
||||
|
||||
/* if this is the first transaction, then deal with the opening balance */
|
||||
if (!ctx->parse_state || !ctx->current_acct) {
|
||||
/* if we don't have an account, then deal with the opening balance */
|
||||
if (!ctx->current_acct)
|
||||
qif_process_opening_balance_txn(ctx, txn);
|
||||
|
||||
/* 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 (!ctx->current_acct)
|
||||
qif_set_flag(ctx->parse_flags, QIF_F_TXN_NEEDS_ACCT);
|
||||
}
|
||||
|
||||
/* Set the transaction's from account */
|
||||
txn->from_acct = ctx->current_acct;
|
||||
|
||||
/* And add it to the process list */
|
||||
ctx->parse_state = (gpointer)g_list_prepend((GList *)ctx->parse_state, txn);
|
||||
ctx->parse_state = g_list_prepend(ctx->parse_state, txn);
|
||||
|
||||
} else
|
||||
/* no date? Ignore this txn */
|
||||
@@ -787,9 +781,9 @@ qif_txn_parse(QifContext ctx, GList *record)
|
||||
return QIF_E_OK;
|
||||
}
|
||||
|
||||
/* after we parse the amounts, fix up the transaction */
|
||||
/* after we parse the amounts, fix up the transaction splits */
|
||||
void
|
||||
qif_txn_post_parse_amounts(QifTxn txn)
|
||||
qif_txn_setup_splits(QifTxn txn)
|
||||
{
|
||||
QifSplit split, this_split;
|
||||
GList *node;
|
||||
@@ -838,6 +832,7 @@ qif_txn_end_acct(QifContext ctx)
|
||||
{
|
||||
GList *node;
|
||||
QifTxn txn;
|
||||
gboolean txn_needs_acct;
|
||||
|
||||
g_return_val_if_fail(ctx, QIF_E_INTERNAL);
|
||||
|
||||
@@ -848,21 +843,24 @@ qif_txn_end_acct(QifContext ctx)
|
||||
* needs a from-account; then add it to the context.
|
||||
*/
|
||||
|
||||
for (node = (GList*)ctx->parse_state; node; node = node->next) {
|
||||
txn_needs_acct = (ctx->parse_flags & QIF_F_TXN_NEEDS_ACCT);
|
||||
|
||||
for (node = ctx->parse_state; node; node = node->next) {
|
||||
txn = node->data;
|
||||
|
||||
/* If we need a from account, then set it.. */
|
||||
if (ctx->parse_flags & QIF_F_TXN_NEEDS_ACCT && !txn->from_acct)
|
||||
txn->from_acct = ctx->current_acct;
|
||||
if (txn_needs_acct && ctx->opening_bal_acct && !txn->from_acct)
|
||||
txn->from_acct = ctx->opening_bal_acct;
|
||||
|
||||
/* merge the txn into the context */
|
||||
qif_object_list_insert(ctx, (QifObject)txn);
|
||||
}
|
||||
|
||||
qif_clear_flag(ctx->parse_flags, QIF_F_TXN_NEEDS_ACCT);
|
||||
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((GList*)ctx->parse_state);
|
||||
g_list_free(ctx->parse_state);
|
||||
ctx->parse_state = NULL;
|
||||
|
||||
return QIF_E_OK;
|
||||
@@ -895,6 +893,8 @@ qif_txn_invst_destroy(QifInvstTxn itxn)
|
||||
g_free(itxn->commissionstr);
|
||||
g_free(itxn->security);
|
||||
|
||||
g_free(itxn->catstr);
|
||||
|
||||
g_free(itxn);
|
||||
}
|
||||
|
||||
@@ -905,18 +905,6 @@ qif_txn_invst_parse(QifContext ctx, GList *record)
|
||||
QifInvstTxn itxn;
|
||||
QifLine line;
|
||||
|
||||
char *cat = NULL;
|
||||
char *cat_class = NULL;
|
||||
gboolean cat_is_acct = FALSE;
|
||||
char *miscx = NULL;
|
||||
char *miscx_class = NULL;
|
||||
gboolean miscx_is_acct = FALSE;
|
||||
|
||||
gboolean invalid_action = FALSE;
|
||||
|
||||
/* Cached account-type lists */
|
||||
static GList *bank_list = NULL;
|
||||
|
||||
g_return_val_if_fail(ctx, QIF_E_INTERNAL);
|
||||
g_return_val_if_fail(record, QIF_E_BADSTATE);
|
||||
|
||||
@@ -966,10 +954,7 @@ qif_txn_invst_parse(QifContext ctx, GList *record)
|
||||
qif_save_str(itxn->commissionstr);
|
||||
break;
|
||||
case 'L': /* L : category */
|
||||
if (!qif_parse_split_category(line->line,
|
||||
&cat, &cat_is_acct, &cat_class,
|
||||
&miscx, &miscx_is_acct, &miscx_class))
|
||||
PERR("Failure parsing category at line %d: %s", line->lineno, line->line);
|
||||
qif_save_str(itxn->catstr);
|
||||
break;
|
||||
default:
|
||||
PERR("Unknown QIF Investment transaction data at line %d: %s",
|
||||
@@ -980,122 +965,26 @@ qif_txn_invst_parse(QifContext ctx, GList *record)
|
||||
/* 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 cached list */
|
||||
if (bank_list == NULL)
|
||||
bank_list = qif_parse_acct_type("__any_bank__", -1);
|
||||
|
||||
/* Make sure we've got a security name */
|
||||
if (!itxn->security)
|
||||
itxn->security = g_strdup(""); /* XXX */
|
||||
|
||||
/* 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:
|
||||
/* 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;
|
||||
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);
|
||||
invalid_action = TRUE;
|
||||
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 = ctx->current_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 dont have a far acct (or far category) then reset the flag */
|
||||
if (!itxn->far_cat.obj)
|
||||
itxn->far_cat_is_acct = FALSE;
|
||||
|
||||
/* If this is invalid then destroy it */
|
||||
if (invalid_action)
|
||||
qif_txn_destroy((QifObject)txn);
|
||||
else
|
||||
/* Add this transaction to the parse state for later processing */
|
||||
ctx->parse_state = (gpointer)g_list_prepend((GList*)ctx->parse_state, txn);
|
||||
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);
|
||||
}
|
||||
|
||||
/* Free parsed strings.. */
|
||||
g_free(cat);
|
||||
g_free(cat_class);
|
||||
g_free(miscx);
|
||||
g_free(miscx_class);
|
||||
|
||||
return QIF_E_OK;
|
||||
}
|
||||
|
||||
@@ -1105,6 +994,17 @@ 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;
|
||||
|
||||
@@ -1124,6 +1024,108 @@ qif_invst_txn_setup_splits(QifContext ctx, QifTxn txn)
|
||||
/* 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 dont 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
|
||||
*
|
||||
@@ -1206,6 +1208,12 @@ qif_invst_txn_setup_splits(QifContext ctx, QifTxn txn)
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -1288,6 +1296,7 @@ find_or_make_class(QifContext ctx, char *name)
|
||||
void
|
||||
qif_object_init(void)
|
||||
{
|
||||
int i;
|
||||
static struct {
|
||||
QifType type;
|
||||
struct _QifHandler handler;
|
||||
@@ -1305,9 +1314,13 @@ qif_object_init(void)
|
||||
{ QIF_ACCOUNT, { NULL, qif_account_parse, NULL } },
|
||||
{ QIF_AUTOSWITCH, { qif_autoswitch_set, NULL, NULL } },
|
||||
{ QIF_CLEAR_AUTOSWITCH, { qif_autoswitch_clear, NULL, NULL } },
|
||||
{ -1, {NULL} },
|
||||
{ 0, {NULL, NULL, NULL} }
|
||||
};
|
||||
|
||||
(void)handlers; /* XXX */
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,11 +30,11 @@
|
||||
static short module = MOD_IMPORT;
|
||||
|
||||
/* An array of handlers for the various bang-types */
|
||||
static QifHandler qif_handlers[QIF_TYPE_MAX] = { NULL };
|
||||
static QifHandler qif_handlers[QIF_TYPE_MAX+1] = { NULL };
|
||||
|
||||
/* Parser Regular Expressions */
|
||||
static gboolean qifp_regex_compiled = FALSE;
|
||||
static regex_t category_regex;
|
||||
static gboolean regex_compiled = FALSE;
|
||||
|
||||
/* A Hash Table of bang-types */
|
||||
static GHashTable *qif_bangtype_map = NULL;
|
||||
@@ -51,6 +51,10 @@ static GHashTable *qif_atype_map = NULL;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -61,7 +65,7 @@ compile_regex()
|
||||
"^ *(\\[)?([^]/\\|]*)(]?)(/([^\\|]*))?(\\|(\\[)?([^]/]*)(]?)(/(.*))?)? *$",
|
||||
REG_EXTENDED);
|
||||
|
||||
regex_compiled = TRUE;
|
||||
qifp_regex_compiled = TRUE;
|
||||
}
|
||||
|
||||
#define QIF_ADD_TYPE(ts,t) \
|
||||
@@ -187,8 +191,8 @@ build_atype_map()
|
||||
{
|
||||
g_return_if_fail(!qif_atype_map);
|
||||
|
||||
qif_action_map = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
g_assert(qif_action_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, BANK));
|
||||
QIF_ADD_ATYPE("port", make_list(1, BANK));
|
||||
@@ -233,12 +237,12 @@ qif_parse_bangtype(QifContext ctx, const char *line)
|
||||
* - strip off leading/trailing whitespace
|
||||
* - make it all lower case
|
||||
*/
|
||||
bangtype = g_strdup(line);
|
||||
bangtype = g_strdup(line+1);
|
||||
g_strstrip(bangtype);
|
||||
g_strdown(bangtype);
|
||||
|
||||
/* In some cases we get "!Type Bank" -- change the space to a colon */
|
||||
if (!strncmp(bangtype, "!type ", 6))
|
||||
if (!strncmp(bangtype, "type ", 5))
|
||||
bangtype[5] = ':';
|
||||
|
||||
/* Lookup the bangtype in the map and then destroy the local copy */
|
||||
@@ -290,7 +294,7 @@ qif_parse_split_category(const char* str,
|
||||
miscx_cat && miscx_cat_is_acct && miscx_class, FALSE);
|
||||
|
||||
|
||||
if (!regex_compiled)
|
||||
if (!qifp_regex_compiled)
|
||||
compile_regex();
|
||||
|
||||
if (regexec(&category_regex, str, 12, pmatch, 0) != 0) {
|
||||
@@ -582,7 +586,7 @@ qif_parse_parse_txn(gpointer val, gpointer data)
|
||||
split = NULL;
|
||||
} while (split);
|
||||
|
||||
qif_txn_post_parse_amounts(txn);
|
||||
qif_txn_setup_splits(txn);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,4 +27,7 @@ QifAction qif_parse_action(QifLine line);
|
||||
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 */
|
||||
|
||||
@@ -5,3 +5,5 @@
|
||||
*.lo
|
||||
Makefile
|
||||
Makefile.in
|
||||
test-link
|
||||
test-qif
|
||||
|
||||
@@ -1 +1,53 @@
|
||||
TESTS=
|
||||
AM_CFLAGS = \
|
||||
-I${top_srcdir}/src \
|
||||
-I${top_srcdir}/src/gnc-module \
|
||||
-I${top_srcdir}/src/test-core \
|
||||
-I${top_srcdir}/src/engine \
|
||||
-I${top_srcdir}/src/app-utils \
|
||||
-I${top_srcdir}/src/import-export \
|
||||
-I${top_srcdir}/src/import-export/qif \
|
||||
${GUILE_INCS} \
|
||||
${GLIB_CFLAGS}
|
||||
|
||||
LDADD = \
|
||||
${top_builddir}/src/gnc-module/libgncmodule.la \
|
||||
${top_builddir}/src/test-core/libgncmod-test.la \
|
||||
../../libgncmod-generic-import.la \
|
||||
../libgncmod-qif.la \
|
||||
${GLIB_LIBS}
|
||||
|
||||
TESTS = \
|
||||
test-link \
|
||||
test-qif
|
||||
|
||||
GNC_TEST_DEPS := @GNC_TEST_SRFI_LOAD_CMD@ \
|
||||
--gnc-module-dir ${top_builddir}/src/gnc-module \
|
||||
--gnc-module-dir ${top_builddir}/src/engine \
|
||||
--gnc-module-dir ${top_builddir}/src/app-utils \
|
||||
--gnc-module-dir ${top_builddir}/src/import-export \
|
||||
--gnc-module-dir ${top_builddir}/src/import-export/qif \
|
||||
--gnc-module-dir ${top_builddir}/src/calculation \
|
||||
--gnc-module-dir ${top_builddir}/src/gnome-utils \
|
||||
--gnc-module-dir ${top_srcdir}/src/gnc-module \
|
||||
--gnc-module-dir ${top_srcdir}/src/engine \
|
||||
--gnc-module-dir ${top_srcdir}/src/app-utils \
|
||||
--gnc-module-dir ${top_srcdir}/src/gnome-utils \
|
||||
--gnc-module-dir ${top_builddir}/src/gnome-utils \
|
||||
--gnc-module-dir ${top_builddir}/src/network-utils \
|
||||
--gnc-module-dir ${top_builddir}/src/gnome \
|
||||
--library-dir ${G_WRAP_LIB_DIR} \
|
||||
--guile-load-dir ${G_WRAP_MODULE_DIR} \
|
||||
--guile-load-dir ${top_srcdir}/src/scm \
|
||||
--guile-load-dir ${top_srcdir}/src/import-export \
|
||||
--guile-load-dir ${top_srcdir}/src/import-export/qif
|
||||
|
||||
TESTS_ENVIRONMENT := \
|
||||
GNC_TEST_FILES=${srcdir}/test-files \
|
||||
$(shell ${top_srcdir}/src/gnc-test-env --no-exports ${GNC_TEST_DEPS})
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
test-link \
|
||||
test-qif
|
||||
|
||||
EXTRA_DIST = \
|
||||
test-files/test-1-bank-txn.qif
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
!Type:Bank
|
||||
D2003/01/27
|
||||
T123.45
|
||||
PTest Payee
|
||||
LTest Category
|
||||
^
|
||||
@@ -0,0 +1,8 @@
|
||||
#include "qif-import.h"
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
qif_context_new();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* test-qif.c -- Test the QIF Import routines.
|
||||
*
|
||||
* Created by: Derek Atkins <derek@ihtfp.com>
|
||||
*/
|
||||
|
||||
#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 = 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)
|
||||
{
|
||||
gnc_module_load("gnucash/import-export", 0);
|
||||
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user