mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@505 57a11ea4-9604-0410-9ed3-97b8803252fd
945 lines
30 KiB
C
945 lines
30 KiB
C
/********************************************************************\
|
|
* QIFIO.c -- read from and writing Quicken Export format files *
|
|
* for xacc (X-Accountant) *
|
|
* Copyright (C) 1997 Robin D. Clark *
|
|
* Copyright (C) 1997 Linas Vepstas *
|
|
* *
|
|
* 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, write to the Free Software *
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
|
|
* *
|
|
* Author: Linas Vepstas *
|
|
* Internet: linas@linas.org *
|
|
* *
|
|
* NOTE: This software is *very alpha*, and is likely to core *
|
|
* dump on unexpected file formats, or otheriwse mangle and *
|
|
* loose data. It sort-of works for the one QIF file its been *
|
|
* tested on ... The contents of this file are not well designed, *
|
|
* it is just a quick hack ... a lot more qork is required. *
|
|
* *
|
|
* NOTE: the readxxxx/writexxxx functions changed the current *
|
|
* position in the file, and so the order which these *
|
|
* functions are called in important *
|
|
* *
|
|
\********************************************************************/
|
|
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <Xm/Xm.h>
|
|
|
|
#include "config.h"
|
|
|
|
#include "Account.h"
|
|
#include "Data.h"
|
|
#include "messages.h"
|
|
#include "util.h"
|
|
|
|
#define PERMS 0666
|
|
#define WFLAGS (O_WRONLY | O_CREAT | O_TRUNC)
|
|
#define RFLAGS O_RDONLY
|
|
|
|
extern Widget toplevel;
|
|
|
|
/********************************************************************\
|
|
* xaccReadQIFLine *
|
|
* reads in one line of ASCII, until cr-nl *
|
|
* *
|
|
* Args: fd -- file descriptor *
|
|
* Return: pointer to static char buff containing ASCII *
|
|
\********************************************************************/
|
|
|
|
#define XACC_LINE_BUFF_SIZE 1000
|
|
char * xaccReadQIFLine( int fd )
|
|
{
|
|
static char linebuff [XACC_LINE_BUFF_SIZE+1];
|
|
int n;
|
|
int err;
|
|
|
|
/* read chars from file until newline */
|
|
n = 0;
|
|
while (XACC_LINE_BUFF_SIZE > n) {
|
|
err = read( fd, &linebuff[n], sizeof (char) );
|
|
if( sizeof(char) != err )
|
|
{
|
|
return NULL;
|
|
}
|
|
if( '\n' == linebuff[n] ) { n++; break; }
|
|
n++;
|
|
}
|
|
linebuff[n] = 0x0;
|
|
|
|
/* if newline not found, bust out */
|
|
if (XACC_LINE_BUFF_SIZE <= n) return NULL;
|
|
return linebuff;
|
|
}
|
|
|
|
/********************************************************************\
|
|
* xaccReadQIFDiscard *
|
|
* reads in lines of ASCII, discarding until end of transaction *
|
|
* *
|
|
* Args: fd -- file descriptor *
|
|
* Return: first line of new transaction *
|
|
\********************************************************************/
|
|
|
|
#define NSTRNCMP(x,y) (0==strncmp((x),(y),strlen(y)))
|
|
|
|
char * xaccReadQIFDiscard( int fd )
|
|
{
|
|
char * qifline;
|
|
|
|
qifline = xaccReadQIFLine (fd);
|
|
if (!qifline) return NULL;
|
|
if ('!' == qifline [0]) return qifline;
|
|
|
|
while (qifline) {
|
|
if (NSTRNCMP(qifline, "^^")) {
|
|
qifline = xaccReadQIFLine (fd);
|
|
return qifline;
|
|
} else
|
|
if (NSTRNCMP(qifline, "^\n")) {
|
|
qifline = xaccReadQIFLine (fd);
|
|
return qifline;
|
|
} else
|
|
if (NSTRNCMP(qifline, "^\r")) {
|
|
qifline = xaccReadQIFLine (fd);
|
|
return qifline;
|
|
} else
|
|
if ('!' == qifline [0]) return qifline;
|
|
qifline = xaccReadQIFLine (fd);
|
|
}
|
|
return qifline;
|
|
}
|
|
|
|
/********************************************************************\
|
|
* xaccReadQIFCategory *
|
|
* reads in category account name, description, etc. *
|
|
* *
|
|
* implementation resembles xaccReadQIFCategory *
|
|
* *
|
|
* Args: fd -- file descriptor *
|
|
* Args: acc -- account structure to fill in *
|
|
* Return: first new line after end of transaction *
|
|
\********************************************************************/
|
|
|
|
#define XACC_PREP_STRING(str) { \
|
|
char * tok; \
|
|
int len; \
|
|
tok = strchr (&qifline[1], '\n'); \
|
|
if (tok) *tok = 0x0; \
|
|
tok = strchr (&qifline[1], '\r'); \
|
|
if (tok) *tok = 0x0; \
|
|
len = strlen (&qifline[1]); \
|
|
(str) = (char *)XtMalloc (len+1); \
|
|
strncpy ((str), &qifline[1], len); \
|
|
(str)[len] = 0x0; \
|
|
}
|
|
|
|
#define XACC_PREP_NULL_STRING(str) { \
|
|
if (!(str)) { (str) = (char *)XtMalloc (sizeof(char)); (str)[0] = 0x0; } \
|
|
}
|
|
|
|
char * xaccReadQIFCategory (int fd, Account * acc)
|
|
{
|
|
char * qifline;
|
|
|
|
if (!acc) return NULL;
|
|
|
|
acc -> flags = 0x0; /* flags is byte */
|
|
acc -> type = -1; /* type is byte */
|
|
acc -> accountName = 0x0; /* string */
|
|
acc -> description = 0x0; /* string */
|
|
acc -> notes = 0x0; /* string */
|
|
|
|
qifline = xaccReadQIFLine (fd);
|
|
if (!qifline) return NULL;
|
|
if ('!' == qifline [0]) return qifline;
|
|
|
|
/* scan for account name, description, type */
|
|
while (qifline) {
|
|
|
|
/* N == Name */
|
|
if ('N' == qifline [0]) {
|
|
XACC_PREP_STRING (acc->accountName);
|
|
} else
|
|
|
|
/* D == Description */
|
|
if ('D' == qifline [0]) {
|
|
XACC_PREP_STRING (acc->description);
|
|
} else
|
|
|
|
/* T == Taxable -- this income is taxable */
|
|
if ('T' == qifline [0]) {
|
|
} else
|
|
|
|
/* E == Expense Category */
|
|
if ('E' == qifline [0]) {
|
|
acc->type = EXPENSE;
|
|
} else
|
|
|
|
/* I == Income Category */
|
|
if ('I' == qifline [0]) {
|
|
acc->type = INCOME;
|
|
} else
|
|
|
|
/* R == Tax Rate Indicator; -- some number ... */
|
|
if ('R' == qifline [0]) {
|
|
} else
|
|
|
|
/* check for end-of-transaction marker */
|
|
if (NSTRNCMP(qifline, "^^")) {
|
|
break;
|
|
} else
|
|
if (NSTRNCMP(qifline, "^\n")) {
|
|
break;
|
|
} else
|
|
if (NSTRNCMP(qifline, "^\r")) {
|
|
break;
|
|
} else
|
|
if ('!' == qifline [0]) break;
|
|
|
|
qifline = xaccReadQIFLine (fd);
|
|
}
|
|
|
|
XACC_PREP_NULL_STRING (acc->accountName);
|
|
XACC_PREP_NULL_STRING (acc->description);
|
|
XACC_PREP_NULL_STRING (acc->notes);
|
|
|
|
return qifline;
|
|
}
|
|
|
|
/********************************************************************\
|
|
* xaccReadQIFAccount *
|
|
* reads in account name, description, etc. *
|
|
* *
|
|
* Args: fd -- file descriptor *
|
|
* Args: acc -- account structure to fill in *
|
|
* Return: first new line after end of transaction *
|
|
\********************************************************************/
|
|
|
|
char * xaccReadQIFAccount (int fd, Account * acc)
|
|
{
|
|
char * qifline;
|
|
|
|
if (!acc) return NULL;
|
|
|
|
acc -> flags = 0x0; /* flags is byte */
|
|
acc -> type = -1; /* type is byte */
|
|
acc -> accountName = 0x0; /* string */
|
|
acc -> description = 0x0; /* string */
|
|
acc -> notes = 0x0; /* string */
|
|
|
|
qifline = xaccReadQIFLine (fd);
|
|
if (!qifline) return NULL;
|
|
if ('!' == qifline [0]) return qifline;
|
|
|
|
/* scan for account name, description, type */
|
|
while (qifline) {
|
|
if ('N' == qifline [0]) {
|
|
XACC_PREP_STRING (acc->accountName);
|
|
} else
|
|
if ('D' == qifline [0]) {
|
|
XACC_PREP_STRING (acc->description);
|
|
} else
|
|
if ('T' == qifline [0]) {
|
|
|
|
if (NSTRNCMP (&qifline[1], "Bank")) {
|
|
acc -> type = BANK;
|
|
} else
|
|
if (NSTRNCMP (&qifline[1], "Cash")) {
|
|
acc -> type = CASH;
|
|
} else
|
|
if (NSTRNCMP (&qifline[1], "CCard")) {
|
|
acc -> type = CREDIT;
|
|
} else
|
|
if (NSTRNCMP (&qifline[1], "Invst")) {
|
|
acc -> type = STOCK;
|
|
} else
|
|
if (NSTRNCMP (&qifline[1], "Oth A")) {
|
|
acc -> type = ASSET;
|
|
} else
|
|
if (NSTRNCMP (&qifline[1], "Oth L")) {
|
|
acc -> type = LIABILITY;
|
|
} else
|
|
{
|
|
printf ("QIF Parse: Unsupported account type %s \n", &qifline[1]);
|
|
acc -> type = -1; /* hack alert -- */
|
|
}
|
|
} else
|
|
|
|
/* check for end-of-transaction marker */
|
|
if (NSTRNCMP (qifline, "^^")) {
|
|
break;
|
|
} else
|
|
if (NSTRNCMP (qifline, "^\n")) {
|
|
break;
|
|
} else
|
|
if (NSTRNCMP (qifline, "^\r")) {
|
|
break;
|
|
} else
|
|
if ('!' == qifline [0]) break;
|
|
qifline = xaccReadQIFLine (fd);
|
|
}
|
|
|
|
XACC_PREP_NULL_STRING (acc->accountName);
|
|
XACC_PREP_NULL_STRING (acc->description);
|
|
XACC_PREP_NULL_STRING (acc->notes);
|
|
|
|
return qifline;
|
|
}
|
|
|
|
/********************************************************************\
|
|
* read a sequence of accounts or categories, inserting them into
|
|
* the indicated group
|
|
\********************************************************************/
|
|
|
|
char * xaccReadQIFAccList (int fd, AccountGroup *grp, int cat)
|
|
{
|
|
char * qifline;
|
|
Account *acc;
|
|
char *str, *tok;
|
|
|
|
if (!grp) return 0x0;
|
|
do {
|
|
acc = xaccMallocAccount();
|
|
if (cat) {
|
|
qifline = xaccReadQIFCategory (fd, acc);
|
|
} else {
|
|
qifline = xaccReadQIFAccount (fd, acc);
|
|
}
|
|
if ('!' == qifline [0]) break;
|
|
|
|
if (-1 == acc->type) { /* free up malloced data if unknown account type */
|
|
xaccFreeAccount(acc);
|
|
continue;
|
|
}
|
|
if (!qifline) { /* free up malloced data if the read bombed. */
|
|
xaccFreeAccount(acc);
|
|
continue;
|
|
}
|
|
|
|
/* check to see if this is a sub-account.
|
|
* Sub-accounts will have a colon in the name */
|
|
str = acc->accountName;
|
|
tok = strchr (str, ':');
|
|
if (tok) {
|
|
Account *parent;
|
|
|
|
/* find the parent account, and parent to it */
|
|
*tok = 0x0;
|
|
parent = xaccGetAccountFromName (grp, str);
|
|
*tok = ':';
|
|
|
|
if (parent) {
|
|
|
|
/* trim off the parent account name ... */
|
|
/* tok += sizeof(char); leave behind the colon ... */
|
|
str = XtNewString (tok);
|
|
XtFree (acc->accountName);
|
|
acc->accountName = str;
|
|
|
|
xaccInsertSubAccount( parent, acc );
|
|
} else {
|
|
/* we should never get here if the qif file is OK */
|
|
insertAccount( grp, acc );
|
|
}
|
|
} else {
|
|
insertAccount( grp, acc );
|
|
}
|
|
|
|
} while (qifline);
|
|
|
|
|
|
return qifline;
|
|
}
|
|
|
|
/********************************************************************\
|
|
* xaccParseQIFDate *
|
|
* parses date of the form MM/DD/YY *
|
|
* *
|
|
* Args: date -- pointer to Date structure *
|
|
* Args: str -- pointer to string rep of date *
|
|
* Return: void *
|
|
\********************************************************************/
|
|
|
|
void xaccParseQIFDate (Date * dat, char * str)
|
|
{
|
|
char * tok;
|
|
|
|
if (!str) return;
|
|
tok = strchr (str, '/');
|
|
if (!tok) return;
|
|
*tok = 0x0;
|
|
dat->month = atoi (str);
|
|
|
|
str = tok+sizeof(char);
|
|
tok = strchr (str, '/');
|
|
if (!tok) return;
|
|
*tok = 0x0;
|
|
dat->day = atoi (str);
|
|
|
|
str = tok+sizeof(char);
|
|
tok = strchr (str, '\r');
|
|
if (!tok) {
|
|
tok = strchr (str, '\n');
|
|
if (!tok) return;
|
|
}
|
|
*tok = 0x0;
|
|
dat->year = atoi (str);
|
|
dat->year += 1900;
|
|
}
|
|
|
|
/********************************************************************\
|
|
\********************************************************************/
|
|
|
|
Account *
|
|
xaccGetXferQIFAccount (Account *acc, char *qifline)
|
|
{
|
|
Account *xfer_acc;
|
|
AccountGroup *rootgrp;
|
|
char * tmp;
|
|
|
|
/* remove square brackets from name, remove carriage return ... */
|
|
qifline = &qifline[1];
|
|
if ('[' == qifline[0]) {
|
|
qifline = &qifline[1];
|
|
tmp = strchr (qifline, ']');
|
|
if (tmp) *tmp = 0x0;
|
|
}
|
|
tmp = strchr (qifline, '\r');
|
|
if(tmp) *tmp = 0x0;
|
|
tmp = strchr (qifline, '\n');
|
|
if(tmp) *tmp = 0x0;
|
|
|
|
/* see if the account exists */
|
|
rootgrp = xaccGetAccountRoot (acc);
|
|
xfer_acc = xaccGetAccountFromName (rootgrp, qifline);
|
|
|
|
/* if not, create it */
|
|
if (!xfer_acc) {
|
|
xfer_acc = xaccMallocAccount ();
|
|
xfer_acc->accountName = XtNewString (qifline);
|
|
xfer_acc->description = XtNewString ("");
|
|
xfer_acc->notes = XtNewString ("");
|
|
|
|
/* by default, assume its an expense-type category */
|
|
xfer_acc->type = EXPENSE;
|
|
insertAccount (rootgrp, xfer_acc);
|
|
}
|
|
|
|
return xfer_acc;
|
|
}
|
|
|
|
/********************************************************************\
|
|
\********************************************************************/
|
|
|
|
Account *
|
|
xaccGetSecurityQIFAccount (Account *acc, char *qifline)
|
|
{
|
|
Account *xfer_acc;
|
|
AccountGroup *rootgrp;
|
|
char * tmp;
|
|
|
|
/* remove square brackets from name, remove carriage return ... */
|
|
qifline = &qifline[1];
|
|
if ('[' == qifline[0]) {
|
|
qifline = &qifline[1];
|
|
tmp = strchr (qifline, ']');
|
|
if (tmp) *tmp = 0x0;
|
|
}
|
|
tmp = strchr (qifline, '\r');
|
|
if(tmp) *tmp = 0x0;
|
|
tmp = strchr (qifline, '\n');
|
|
if(tmp) *tmp = 0x0;
|
|
|
|
/* see if the account exists */
|
|
rootgrp = xaccGetAccountRoot (acc);
|
|
xfer_acc = xaccGetAccountFromName (rootgrp, qifline);
|
|
|
|
/* if not, create it */
|
|
if (!xfer_acc) {
|
|
xfer_acc = xaccMallocAccount ();
|
|
xfer_acc->accountName = XtNewString (qifline);
|
|
xfer_acc->description = XtNewString ("");
|
|
xfer_acc->notes = XtNewString ("");
|
|
xfer_acc->type = STOCK;
|
|
xaccInsertSubAccount (acc, xfer_acc);
|
|
}
|
|
|
|
return xfer_acc;
|
|
}
|
|
|
|
/********************************************************************\
|
|
* xaccReadQIFTransaction *
|
|
* reads in transaction *
|
|
* *
|
|
* NB: this code will have to different, depending on the *
|
|
* type of transaction (bank, stock, etc. *
|
|
* *
|
|
* Args: fd -- file descriptor *
|
|
* Args: acc -- account structure to fill in *
|
|
* Return: first new line after end of transaction *
|
|
\********************************************************************/
|
|
|
|
#define XACC_ACTION(q,x) \
|
|
if (!strncmp (&qifline[1], q, strlen(q))) { \
|
|
trans->credit_split.action = XtNewString(x); \
|
|
} else
|
|
|
|
|
|
char * xaccReadQIFTransaction (int fd, Account *acc)
|
|
{
|
|
Transaction *trans;
|
|
Split *split = NULL;
|
|
char * qifline;
|
|
int isneg = 0;
|
|
int got_share_quantity = 0;
|
|
int share_xfer = 0;
|
|
int is_security = 0;
|
|
Account *sub_acc = 0x0;
|
|
Account *xfer_acc = 0x0;
|
|
double adjust = 0.0;
|
|
|
|
if (!acc) return NULL;
|
|
|
|
qifline = xaccReadQIFLine (fd);
|
|
if (!qifline) return NULL;
|
|
if ('!' == qifline [0]) return qifline;
|
|
|
|
trans = xaccMallocTransaction ();
|
|
|
|
/* scan for transaction date, description, type */
|
|
while (qifline) {
|
|
/* D == date */
|
|
if ('D' == qifline [0]) {
|
|
xaccParseQIFDate (&(trans->date), &qifline[1]);
|
|
} else
|
|
|
|
/* E == memo for split */
|
|
if ('E' == qifline [0]) {
|
|
if (split) {
|
|
XACC_PREP_STRING (split->memo);
|
|
}
|
|
} else
|
|
|
|
/* I == share price */
|
|
if ('I' == qifline [0]) {
|
|
trans -> credit_split.share_price = xaccParseUSAmount (&qifline[1]);
|
|
} else
|
|
|
|
/* L == name of acount from which transfer occured */
|
|
if ('L' == qifline [0]) {
|
|
/* locate the transfer account */
|
|
xfer_acc = xaccGetXferQIFAccount (acc, qifline);
|
|
} else
|
|
|
|
/* M == memo field */
|
|
if ('M' == qifline [0]) {
|
|
XACC_PREP_STRING (trans->credit_split.memo);
|
|
} else
|
|
|
|
/* N == check numbers for Banks, but Action for portfolios */
|
|
if ('N' == qifline [0]) {
|
|
|
|
if (!strncmp (qifline, "NSell", 5)) isneg = 1;
|
|
if (!strncmp (qifline, "NSell", 5)) share_xfer = 1;
|
|
if (!strncmp (qifline, "NBuy", 4)) share_xfer = 1;
|
|
|
|
/* if a recognized action, convert to our cannonical names */
|
|
XACC_ACTION ("Buy", "Buy")
|
|
XACC_ACTION ("Sell", "Sell")
|
|
XACC_ACTION ("Div", "Div")
|
|
XACC_ACTION ("CGLong", "LTCG")
|
|
XACC_ACTION ("CGShort", "STCG")
|
|
XACC_ACTION ("IntInc", "Int")
|
|
XACC_ACTION ("DEP", "Deposit")
|
|
XACC_ACTION ("XIn", "Deposit")
|
|
XACC_ACTION ("XOut", "Withdraw")
|
|
{
|
|
XACC_PREP_STRING (trans->num);
|
|
}
|
|
} else
|
|
|
|
/* O == adjustments */
|
|
/* hack alert -- sometimes adjustments are quite large.
|
|
* I have no clue why, and what to do about it. For what
|
|
* its worth, I can prove that Quicken version 3.0 makes
|
|
* math errors ... */
|
|
if ('O' == qifline [0]) {
|
|
double pute;
|
|
adjust = xaccParseUSAmount (&qifline[1]);
|
|
pute = (trans->credit_split.damount) * (trans->credit_split.share_price);
|
|
if (isneg) pute = -pute;
|
|
|
|
printf ("QIF Warning: Adjustment of %.2f to amount %.2f not handled \n", adjust, pute);
|
|
} else
|
|
|
|
/* P == Payee, for Bank accounts */
|
|
if ('P' == qifline [0]) {
|
|
XACC_PREP_STRING (trans->description);
|
|
} else
|
|
|
|
/* Q == number of shares */
|
|
if ('Q' == qifline [0]) {
|
|
trans -> credit_split.damount = xaccParseUSAmount (&qifline[1]);
|
|
if (isneg) trans -> credit_split.damount = - (trans->credit_split.damount);
|
|
got_share_quantity = 1;
|
|
} else
|
|
|
|
/* S == split */
|
|
if ('S' == qifline [0]) {
|
|
split = xaccMallocSplit();
|
|
|
|
split -> memo = 0x0; /* string */
|
|
split -> damount = 0.0; /* amount is double */
|
|
split -> share_price= 1.0; /* share_price is double */
|
|
split -> reconciled = NREC; /* reconciled is byte */
|
|
split -> parent = trans; /* parent transaction */
|
|
split -> acc = (struct _account *) xaccGetXferQIFAccount (acc, qifline);
|
|
|
|
xaccTransAppendSplit (trans, split);
|
|
|
|
/* hack alert -- we should insert this split into
|
|
* the split account, and remove the L field */
|
|
} else
|
|
|
|
/* T == total */
|
|
if ('T' == qifline [0]) {
|
|
/* ignore T for stock transactions, since T is a dollar amount */
|
|
if (0 == got_share_quantity) {
|
|
trans -> credit_split.damount = xaccParseUSAmount (&qifline[1]);
|
|
if (isneg) trans -> credit_split.damount = - (trans->credit_split.damount);
|
|
}
|
|
} else
|
|
|
|
/* Y == Name of Security */
|
|
if ('Y' == qifline [0]) {
|
|
XACC_PREP_STRING (trans->description);
|
|
|
|
is_security = 1;
|
|
if (share_xfer) {
|
|
/* locate or create the sub-account account */
|
|
sub_acc = xaccGetSecurityQIFAccount (acc, qifline);
|
|
}
|
|
|
|
} else
|
|
|
|
/* $ == dollar amount */
|
|
if ('$' == qifline [0]) {
|
|
/* for splits, $ records the part of the total for each split */
|
|
if (split) {
|
|
split -> damount = xaccParseUSAmount (&qifline[1]);
|
|
} else {
|
|
/* Currently, it appears that the $ amount is a redundant
|
|
* number that we can safely ignore. To get fancy,
|
|
* we use it to double-check the above work, since it
|
|
* appears to always appear the last entry in the
|
|
* transaction. Round things out to pennies, to
|
|
* handle round-off errors.
|
|
*/
|
|
double parse, pute;
|
|
int got, wanted;
|
|
parse = xaccParseUSAmount (&qifline[1]);
|
|
pute = (trans->credit_split.damount) * (trans->credit_split.share_price);
|
|
if (isneg) pute = -pute;
|
|
|
|
wanted = (int) (100.0 * parse + 0.5);
|
|
got = (int) (100.0 * (pute+adjust) + 0.5);
|
|
if (wanted != got) {
|
|
printf ("QIF Parse Error: wanted %f got %f \n", parse, pute);
|
|
}
|
|
}
|
|
} else
|
|
|
|
/* check for end-of-transaction marker */
|
|
if (NSTRNCMP (qifline, "^^")) {
|
|
break;
|
|
} else
|
|
if (NSTRNCMP (qifline, "^\n")) {
|
|
break;
|
|
} else
|
|
if (NSTRNCMP (qifline, "^\r")) {
|
|
break;
|
|
} else
|
|
if ('!' == qifline [0]) break;
|
|
qifline = xaccReadQIFLine (fd);
|
|
|
|
}
|
|
|
|
/* at this point, we should see an end-of-transaction marker
|
|
* if we see something else, assume the worst, free the last
|
|
* transaction, and return */
|
|
if ('!' == qifline[0]) {
|
|
xaccFreeTransaction (trans);
|
|
return qifline;
|
|
}
|
|
|
|
XACC_PREP_NULL_STRING (trans->num);
|
|
XACC_PREP_NULL_STRING (trans->description);
|
|
XACC_PREP_NULL_STRING (trans->credit_split.memo);
|
|
XACC_PREP_NULL_STRING (trans->credit_split.action);
|
|
|
|
|
|
/* fundamentally differnt handling for securities and non-securities */
|
|
if (is_security) {
|
|
|
|
/* if the transaction is a sale/purchase of a security,
|
|
* then it is a defacto transfer between the brokerage account
|
|
* and the stock account. */
|
|
if (share_xfer) {
|
|
if (!split) {
|
|
split = xaccMallocSplit ();
|
|
xaccTransAppendSplit (trans, split);
|
|
}
|
|
|
|
/* Insert the transaction into the main brokerage
|
|
* account as a debit, unless an alternate account
|
|
* was specified. */
|
|
if (xfer_acc) {
|
|
split->acc = (struct _account *) xfer_acc;
|
|
xaccInsertSplit (xfer_acc, split);
|
|
} else {
|
|
split->acc = (struct _account *) acc;
|
|
xaccInsertSplit (acc, split);
|
|
}
|
|
|
|
/* normally, the security account is pointed at by
|
|
* sub_acc; the security account is credited.
|
|
* But, just in case its missing, avoid a core dump */
|
|
if (sub_acc) {
|
|
trans->credit_split.acc = (struct _account *) sub_acc;
|
|
xaccInsertSplit (sub_acc, split);
|
|
}
|
|
} else {
|
|
|
|
/* else, we are here if its not a share transfer.
|
|
* It is probably dividend or other income */
|
|
|
|
/* if a transfer account is specified, the transfer
|
|
* account gets the dividend credit; otherwise, the
|
|
* main account gets it */
|
|
if (xfer_acc) {
|
|
trans->credit_split.acc = (struct _account *) xfer_acc;
|
|
xaccInsertSplit (xfer_acc, &(trans->credit_split));
|
|
} else {
|
|
trans->credit_split.acc = (struct _account *) acc;
|
|
xaccInsertSplit (acc, &(trans->credit_split));
|
|
}
|
|
}
|
|
|
|
} else {
|
|
/* if we are here, its not a security, but an ordinary account */
|
|
/* if a transfer account was specified, it is the debited account */
|
|
if (xfer_acc) {
|
|
if (!split) {
|
|
split = xaccMallocSplit ();
|
|
xaccTransAppendSplit (trans, split);
|
|
}
|
|
split->acc = (struct _account *) xfer_acc;
|
|
xaccInsertSplit (xfer_acc, split);
|
|
}
|
|
|
|
/* the transaction itself appears as a credit */
|
|
trans->credit_split.acc = (struct _account *) acc;
|
|
xaccInsertSplit (acc, &(trans->credit_split));
|
|
}
|
|
|
|
return qifline;
|
|
}
|
|
|
|
/********************************************************************\
|
|
* read a sequence of transactions, inserting them into
|
|
* the indicated account
|
|
\********************************************************************/
|
|
|
|
char * xaccReadQIFTransList (int fd, Account *acc)
|
|
{
|
|
char * qifline;
|
|
|
|
if (!acc) return 0x0;
|
|
qifline = xaccReadQIFTransaction (fd, acc);
|
|
while (qifline) {
|
|
if ('!' == qifline[0]) break;
|
|
qifline = xaccReadQIFTransaction (fd, acc);
|
|
}
|
|
return qifline;
|
|
}
|
|
|
|
/********************************************************************\
|
|
********************** LOAD DATA ***********************************
|
|
\********************************************************************/
|
|
|
|
/********************************************************************\
|
|
* xaccReadQIFData *
|
|
* reads in the data from file datafile *
|
|
* *
|
|
* Args: datafile - the file to load the data from *
|
|
* Return: the struct with the program data in it *
|
|
\********************************************************************/
|
|
|
|
#define STRSTR(x,y) ((NSTRNCMP(x,y)) || (NSTRNCMP((&(x)[1]), y)))
|
|
|
|
AccountGroup *
|
|
xaccReadQIFData( char *datafile )
|
|
{
|
|
int fd;
|
|
int skip = 0;
|
|
char * qifline;
|
|
AccountGroup *grp;
|
|
char buf[BUFSIZE];
|
|
|
|
fd = open( datafile, RFLAGS, 0 );
|
|
if( fd == -1 )
|
|
{
|
|
sprintf (buf, FILE_NOT_FOUND_MSG, datafile);
|
|
errorBox (toplevel, buf);
|
|
return NULL;
|
|
}
|
|
|
|
/* read the first line of the file */
|
|
qifline = xaccReadQIFLine (fd);
|
|
if( NULL == qifline )
|
|
{
|
|
sprintf (buf, FILE_EMPTY_MSG, datafile);
|
|
errorBox (toplevel, buf);
|
|
close(fd);
|
|
return NULL;
|
|
}
|
|
|
|
grp = xaccMallocAccountGroup();
|
|
|
|
while (qifline) {
|
|
if (STRSTR (qifline, "Type:Bank")) {
|
|
Account *acc = xaccMallocAccount();
|
|
DEBUG ("got bank\n");
|
|
|
|
acc->type = BANK;
|
|
acc->accountName = XtNewString ("Quicken Bank Account");
|
|
acc->description = XtNewString ("");
|
|
acc->notes = XtNewString ("");
|
|
|
|
insertAccount( grp, acc );
|
|
qifline = xaccReadQIFTransList (fd, acc);
|
|
continue;
|
|
} else
|
|
|
|
if (STRSTR (qifline, "Type:Cat")) {
|
|
DEBUG ("got category\n");
|
|
qifline = xaccReadQIFAccList (fd, grp, 1);
|
|
continue;
|
|
} else
|
|
|
|
if (STRSTR (qifline, "Type:Class")) {
|
|
DEBUG ("got class\n");
|
|
qifline = xaccReadQIFDiscard (fd);
|
|
continue;
|
|
} else
|
|
|
|
if (STRSTR (qifline, "Type:Invst")) {
|
|
Account *acc = xaccMallocAccount();
|
|
DEBUG ("got Invst\n");
|
|
|
|
acc->type = BANK;
|
|
acc->accountName = XtNewString ("Quicken Investment Account");
|
|
acc->description = XtNewString ("");
|
|
acc->notes = XtNewString ("");
|
|
|
|
insertAccount( grp, acc );
|
|
qifline = xaccReadQIFTransList (fd, acc);
|
|
continue;
|
|
} else
|
|
|
|
if (STRSTR (qifline, "Type:Memorized")) {
|
|
DEBUG ("got memorized\n");
|
|
qifline = xaccReadQIFDiscard (fd);
|
|
continue;
|
|
} else
|
|
|
|
if (STRSTR (qifline, "Option:AutoSwitch")) {
|
|
DEBUG ("got autoswitch on\n");
|
|
skip = 1;
|
|
qifline = xaccReadQIFDiscard (fd);
|
|
continue;
|
|
} else
|
|
|
|
if (STRSTR (qifline, "Clear:AutoSwitch")) {
|
|
DEBUG ("got autoswitch clear\n");
|
|
skip = 0;
|
|
qifline = xaccReadQIFDiscard (fd);
|
|
continue;
|
|
} else
|
|
|
|
if (STRSTR (qifline, "Account")) {
|
|
if (skip) {
|
|
/* loop and read all of the account names and descriptions */
|
|
/* no actual dollar data is expected to be read here ... */
|
|
while (qifline) {
|
|
qifline = xaccReadQIFAccList (fd, grp, 0);
|
|
if (!qifline) break;
|
|
if ('!' == qifline[0]) break;
|
|
}
|
|
} else {
|
|
/* read account name, followed by dollar data ... */
|
|
Account *preexisting;
|
|
Account *acc = xaccMallocAccount();
|
|
DEBUG ("got account\n");
|
|
qifline = xaccReadQIFAccount (fd, acc);
|
|
if (!qifline) { /* free up malloced data if the read bombed. */
|
|
xaccFreeAccount(acc);
|
|
continue;
|
|
}
|
|
if (-1 == acc->type) { /* free up malloced data if unknown account type */
|
|
xaccFreeAccount(acc);
|
|
continue;
|
|
}
|
|
|
|
/* check to see if we already know this account;
|
|
* if we do, use it, otherwise create it */
|
|
preexisting = xaccGetAccountFromName (grp, acc->accountName);
|
|
if (preexisting) {
|
|
xaccFreeAccount (acc);
|
|
acc = preexisting;
|
|
} else {
|
|
insertAccount( grp, acc );
|
|
}
|
|
|
|
/* spin until start of transaction records */
|
|
/* in theory, in a perfect file, the transaction data follows immediately */
|
|
while (qifline) {
|
|
if ('!' == qifline[0]) break;
|
|
qifline = xaccReadQIFDiscard (fd);
|
|
}
|
|
|
|
/* read transactions */
|
|
if (qifline) qifline = xaccReadQIFTransList (fd, acc);
|
|
}
|
|
continue;
|
|
} else
|
|
|
|
if ('!' == qifline[0]) {
|
|
DEBUG ("unknown or unexpected stanza:");
|
|
DEBUG (qifline);
|
|
qifline = xaccReadQIFDiscard (fd);
|
|
continue;
|
|
} else {
|
|
|
|
qifline = xaccReadQIFDiscard (fd);
|
|
}
|
|
}
|
|
|
|
close(fd);
|
|
return grp;
|
|
}
|
|
|
|
/* ========================== END OF FILE ======================= */
|