diff --git a/src/engine/FileIO.c b/src/engine/FileIO.c index 61a66bd777..b7915e28d9 100644 --- a/src/engine/FileIO.c +++ b/src/engine/FileIO.c @@ -1298,45 +1298,6 @@ readDMYDate( int fd, int token ) ********************** SAVE DATA *********************************** \********************************************************************/ -static void -xaccResetWriteFlags (AccountGroup *grp) -{ - int i, numAcc; - if (!grp) return; - - /* Zero out the write flag on all of the - * transactions. The write_flag is used to determine - * if a given transaction has already been written - * out to the file. This flag is necessary, since - * double-entry transactions appear in two accounts, - * while they should be written only once to the file. - * The write_flag is used ONLY by the routines in this - * module. - */ - numAcc = grp ->numAcc; - for( i=0; ichildren); - - /* zip over all accounts */ - s = acc->splits[0]; - n=0; - while (s) { - Transaction *trans; - trans = s->parent; - if (trans) trans -> write_flag = 0; - n++; - s = acc->splits[n]; - } - } - -} - /********************************************************************\ * xaccWriteAccountGroupFile * * writes account date to two files: the current file, and a * @@ -1453,16 +1414,16 @@ xaccWriteAccountGroup (int fd, AccountGroup *grp ) return 0; } - /* OK, now zero out the write flag on all of the + /* OK, now zero out the write flag on all of the * transactions. The write_flag is used to determine - * if a given transaction has already been written - * out to the file. This flag is necessary, since + * if a given transaction has already been written + * out to the file. This flag is necessary, since * double-entry transactions appear in two accounts, * while they should be written only once to the file. * The write_flag is used ONLY by the routines in this * module. */ - xaccResetWriteFlags (grp); + xaccGroupBeginStagedTransactionTraversals(grp); for( i=0; inumAcc; i++ ) { @@ -1517,6 +1478,21 @@ writeGroup (int fd, AccountGroup *grp ) * acc - the account data to save * * Return: -1 on failure * \********************************************************************/ + +static int +increment_int(Transaction *t, void *data) + { + int val = *((int *) data); + *((int *) data) = val + 1; + return 0; + } + +static int +_write_transaction_wrapper_(Transaction *t, void *data) + { + return (-1 == writeTransaction(*((int *) data), t)); + } + static int writeAccount( int fd, Account *acc ) { @@ -1602,23 +1578,17 @@ writeAccount( int fd, Account *acc ) /* figure out numTrans -- it will be less than the total * number of transactions in this account, because some * of the double entry transactions will already have been - * written. write_flag values are: + * written. + * marker flag values are: * 0 == uncounted, unwritten * 1 == counted, unwritten * 2 == written */ + + /* Use a marker of 1 for counting numUnwrittenTrans */ numUnwrittenTrans = 0; - i=0; - s = acc->splits[i]; - while (s) { - trans = s->parent; - if (0 == trans->write_flag) { - numUnwrittenTrans ++; - trans->write_flag = 1; - } - i++; - s = acc->splits[i]; - } + xaccAccountStagedTransactionTraversal(acc, 1, + increment_int, &numUnwrittenTrans); ntrans = numUnwrittenTrans; XACC_FLIP_INT (ntrans); @@ -1627,16 +1597,10 @@ writeAccount( int fd, Account *acc ) return -1; DEBUG ("writeAccount(): will write %d trans\n", numUnwrittenTrans); - i=0; - s = acc->splits[i]; - while (s) { - trans = s->parent; - if (1 == trans->write_flag) { - err = writeTransaction( fd, trans ); - if (-1 == err) return err; - } - i++; - s = acc->splits[i]; + + if(!xaccAccountStagedTransactionTraversal(acc, 2, + _write_transaction_wrapper_, &fd)) { + return -1; } if (acc->children) { @@ -1673,12 +1637,6 @@ writeTransaction( int fd, Transaction *trans ) Timespec ts; ENTER ("writeTransaction"); - /* If we've already written this transaction, don't write - * it again. That is, prevent double-entry transactions - * from being written twice - */ - if (2 == trans->write_flag) return 4; - trans->write_flag = 2; err = writeString( fd, xaccTransGetNum (trans) ); if( -1 == err ) return err; @@ -1892,14 +1850,4 @@ writeTSDate( int fd, Timespec *ts) return err; } -/********************************************************************/ -/* - Local Variables: - tab-width: 2 - indent-tabs-mode: nil - mode: c - c-indentation-style: gnu - eval: (c-set-offset 'block-open '-) - End: -*/ /*********************** END OF FILE *********************************/ diff --git a/src/engine/Group.c b/src/engine/Group.c index 00f1ed1ccc..102eb2a1c1 100644 --- a/src/engine/Group.c +++ b/src/engine/Group.c @@ -33,18 +33,11 @@ #include "GroupP.h" #include "TransactionP.h" #include "util.h" - -#ifndef FALSE -#define FALSE 0 -#endif - -#ifndef TRUE -#define TRUE 1 -#endif +#include "gnc-common.h" /********************************************************************\ * Because I can't use C++ for this project, doesn't mean that I * - * can't pretend too! These functions perform actions on the * + * can't pretend to! These functions perform actions on the * * AccountGroup data structure, in order to encapsulate the * * knowledge of the internals of the AccountGroup in one file. * \********************************************************************/ @@ -54,7 +47,7 @@ void xaccInitializeAccountGroup (AccountGroup *grp) { - grp->saved = TRUE; + grp->saved = GNC_T; grp->parent = NULL; grp->numAcc = 0; @@ -96,7 +89,7 @@ xaccFreeAccountGroup( AccountGroup *grp ) grp->numAcc = 0; grp->account = NULL; grp->balance = 0.0; - + _free(grp); } @@ -108,7 +101,7 @@ xaccAccountGroupMarkSaved (AccountGroup *grp) int i; if (!grp) return; - grp->saved = TRUE; + grp->saved = GNC_T; for (i=0; inumAcc; i++) { xaccAccountGroupMarkSaved (grp->account[i]->children); @@ -124,7 +117,7 @@ xaccAccountGroupNotSaved (AccountGroup *grp) int i; if (!grp) return 0; - if (FALSE == grp->saved) return 1; + if (GNC_F == grp->saved) return 1; for (i=0; inumAcc; i++) { not_saved = xaccAccountGroupNotSaved (grp->account[i]->children); @@ -345,7 +338,7 @@ xaccRemoveGroup (AccountGroup *grp) grp = acc -> parent; if (!grp) return; - grp->saved = FALSE; + grp->saved = GNC_F; } /********************************************************************\ @@ -378,7 +371,7 @@ xaccRemoveAccount (Account *acc) nacc --; arr[nacc] = NULL; grp->numAcc = nacc; - grp->saved = FALSE; + grp->saved = GNC_F; /* if this was the last account in a group, delete * the group as well (unless its a root group) */ @@ -432,7 +425,7 @@ xaccGroupInsertAccount( AccountGroup *grp, Account *acc ) if (grp == acc->parent) ralo = 0; xaccRemoveAccount (acc); } - grp->saved = FALSE; + grp->saved = GNC_F; /* set back-pointer to the accounts parent */ acc->parent = grp; @@ -793,4 +786,103 @@ xaccGroupGetDepth (AccountGroup *grp) return maxdepth; } +/********************************************************************\ +\********************************************************************/ + +void +xaccGroupBeginStagedTransactionTraversals (AccountGroup *grp) +{ + unsigned int numAcc; + unsigned int i; + + if (!grp) return; + + numAcc = grp->numAcc; + for(i = 0; i < numAcc; i++) { + unsigned int n = 0; + Account *acc; + Split *s = NULL; + acc = xaccGroupGetAccount(grp, i); + + if(!acc) return; + + /* recursively do sub-accounts */ + xaccGroupBeginStagedTransactionTraversals(acc->children); + + s = acc->splits[0]; + while (s) { + Transaction *trans = s->parent; + trans->marker = 0; + n++; + s = acc->splits[n]; + } + } +} + +int +xaccAccountStagedTransactionTraversal (Account *acc, + unsigned int stage, + int (*callback)(Transaction *t, void *cb_data), + void *cb_data) +{ + unsigned int n = 0; + Split *s = NULL; + + if(!acc) return; + + s = acc->splits[0]; + if (callback) { + int retval; + while (s) { + Transaction *trans = s->parent; + if (trans && (trans->marker < stage)) { + trans->marker = stage; + retval = callback(trans, cb_data); + if (retval) return retval; + } + n++; + s = acc->splits[n]; + } + } else { + while (s) { + Transaction *trans = s->parent; + if (trans && (trans->marker < stage)) { + trans->marker = stage; + } + n++; + s = acc->splits[n]; + } + } + return 0; +} + + +int +xaccGroupStagedTransactionTraversal(AccountGroup *grp, + unsigned int stage, + int (*callback)(Transaction *t, void *cb_data), + void *cb_data) +{ + unsigned int numAcc; + unsigned int i; + + if (!grp) return; + + numAcc = grp->numAcc; + for(i = 0; i < numAcc; i++) { + int retval; + int n = 0; + Account *acc; + acc = xaccGroupGetAccount(grp, i); + + /* recursively do sub-accounts */ + retval = xaccGroupStagedTransactionTraversal + (acc->children, stage, callback, cb_data); + if (retval) return retval; + retval = xaccAccountStagedTransactionTraversal (acc, stage, callback, cb_data); + if (retval) return retval; + } + return 0; +} + /****************** END OF FILE *************************************/ diff --git a/src/engine/Group.h b/src/engine/Group.h index 0ef0d76b6e..885114a3bb 100644 --- a/src/engine/Group.h +++ b/src/engine/Group.h @@ -27,6 +27,7 @@ #define __XACC_ACCOUNT_GROUP_H__ #include "config.h" +#include "gnc-common.h" #include "Account.h" @@ -189,4 +190,85 @@ char * xaccAccountGetNextChildCode (Account *acc, int num_digits); void xaccGroupAutoCode (AccountGroup *grp, int num_digits); void xaccGroupDepthAutoCode (AccountGroup *grp); +#ifndef SWIG + +/* + * The following functions provide support for "staged traversals" + * over all of the transactions in and account or group. The idea + * is to be able to perform a sequence of traversals ("stages"), + * and perform an operation on each transaction exactly once + * for that stage. + * Only transactions whose current "stage" is less than the + * stage of the current traversal will be affected, and they will be + * "brought up" to the current stage when they are processed. + * + * For example, you could perform a stage 1 traversal of all the + * transactions in an account, and then perform a stage 1 traversal of + * the transactions in a second account. Presuming the traversal of + * the first account didn't abort prematurely, any transactions shared + * by both accounts would be ignored during the traversal of the + * second account since they had been processed while traversing the + * first account. + * + * However, if you had traversed the second account using a stage + * of 2, then all the transactions in the second account would have + * been processed. + * + * Traversal can be aborted by having the callback function return a + * non-zero value. The traversal is aborted immediately, and the + * non-zero value is returned. Note that an aborted traversal can + * be restarted; no information is lost due to an abort. + * + * The initial impetus for this particular approach came from + * generalizing a mark/sweep practice that was already being used in + * FileIO.c. + * + * Note that currently, there is a hard limit of 256 stages, which + * can be changed by enlarging "marker" in the tranaction struct. + * */ + +/* xaccGroupBeginStagedTransactionTraversals() resets the traversal + * marker inside each of all the transactions in the group so that a + * new sequence of staged traversals can begin. + */ + +void xaccGroupBeginStagedTransactionTraversals(AccountGroup *grp); + +/* xaccGroupStagedTransactionTraversal() calls thunk on each + * transaction in the group whose current marker is less than the + * given `stage' and updates each transaction's marker to be `stage'. + * The traversal will stop if thunk() returns a non-zero value. + * xaccGroupStagedTransactionTraversal() function will return zero + * or the non-zero value returned by thunk(). This + * API does not handle handle recursive traversals. + * + * Currently the result of adding or removing transactions during a + * traversal is undefined, so don't do that. + */ + +int +xaccGroupStagedTransactionTraversal(AccountGroup *grp, + unsigned int stage, + int (*thunk)(Transaction *t, void *data), + void *data); + +/* xaccAccountStagedTransactionTraversal() calls thunk on each + * transaction in the account whose current marker is less than the + * given `stage' and updates each transaction's marker to be `stage'. + * The traversal will stop if thunk() returns a non-zero value. + * xaccAccountStagedTransactionTraversal() function will return zero + * or the non-zero value returned by thunk(). + * This API does not handle handle recursive traversals. + * + * Currently the result of adding or removing transactions during a + * traversal is undefined, so don't do that. + */ + +int xaccAccountStagedTransactionTraversal(Account *a, + unsigned int stage, + int (*thunk)(Transaction *t, void *data), + void *data); + +#endif + #endif /* __XACC_ACCOUNT_GROUP_H__ */ diff --git a/src/engine/Transaction.c b/src/engine/Transaction.c index 05e14a3e60..5521d4f2c4 100644 --- a/src/engine/Transaction.c +++ b/src/engine/Transaction.c @@ -343,7 +343,7 @@ xaccInitTransaction( Transaction * trans ) trans->date_posted.tv_sec = 0; trans->date_posted.tv_nsec = 0; - trans->write_flag = 0; + trans->marker = 0; trans->open = 0; trans->orig = NULL; } diff --git a/src/engine/TransactionP.h b/src/engine/TransactionP.h index 8089932f10..651b8f6468 100644 --- a/src/engine/TransactionP.h +++ b/src/engine/TransactionP.h @@ -148,7 +148,13 @@ struct _transaction Split **splits; /* list of splits, null terminated */ - char write_flag; /* used only during file IO */ + /* marker is used to track the progress of transaction traversals. + * 0 is never a legitimate marker value, so we can tell is we hit a + * new transaction in the middle of a traversal. All each new + * traversal cares about is whether or not the marker stored in a + * transaction is the same as or different than the one + * corresponding to the current traversal. */ + unsigned char marker; /* the "open" flag indicates if the transaction has been * opened for editing. */