*** empty log message ***
This commit is contained in:
parent
68e6b8538e
commit
a40813d896
8 changed files with 137 additions and 119 deletions
31
Makefile
31
Makefile
|
|
@ -1,21 +1,26 @@
|
|||
define GNUCASH
|
||||
true
|
||||
endef
|
||||
|
||||
CODE = amount.cc ledger.cc parse.cc \
|
||||
balance.cc register.cc equity.cc main.cc
|
||||
ifdef GNUCASH
|
||||
CODE := $(CODE) gnucash.cc
|
||||
endif
|
||||
CODE = amount.cc \
|
||||
ledger.cc \
|
||||
parse.cc \
|
||||
balance.cc \
|
||||
register.cc \
|
||||
equity.cc \
|
||||
main.cc
|
||||
|
||||
OBJS = $(patsubst %.cc,%.o,$(CODE))
|
||||
|
||||
CFLAGS = -Wall -ansi -pedantic -DDEFAULT_COMMODITY="\"\$$\"" -DHUQUQULLAH=1
|
||||
#DFLAGS = -O3 -fomit-frame-pointer
|
||||
DFLAGS = -g # -pg
|
||||
CFLAGS = -Wall -ansi -pedantic -DDEFAULT_COMMODITY="\"\$$\""
|
||||
CFLAGS := $(CFLAGS) -DHUQUQULLAH=1
|
||||
|
||||
DFLAGS = -O3 -fomit-frame-pointer
|
||||
#DFLAGS = -g -O2 # -pg
|
||||
|
||||
INCS = -I/usr/include/xmltok
|
||||
|
||||
LIBS = -lgmpxx -lgmp -lpcre
|
||||
|
||||
ifdef GNUCASH
|
||||
CODE := $(CODE) gnucash.cc
|
||||
CFLAGS := $(CFLAGS) -DREAD_GNUCASH=1
|
||||
LIBS := $(LIBS) -lxmlparse
|
||||
endif
|
||||
|
||||
|
|
@ -28,7 +33,7 @@ ledger: $(OBJS)
|
|||
g++ $(CFLAGS) $(INCS) $(DFLAGS) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f libledger.so ledger *.o
|
||||
rm -f ledger *.o
|
||||
|
||||
rebuild: clean deps all
|
||||
|
||||
|
|
|
|||
|
|
@ -474,9 +474,12 @@ static commodity * parse_amount(mpz_t out, const char * num,
|
|||
static char buf[256];
|
||||
|
||||
bool saw_commodity = false;
|
||||
bool prefix = false;
|
||||
bool separate = true;
|
||||
bool thousands = true;
|
||||
bool european = false;
|
||||
|
||||
std::string symbol;
|
||||
bool prefix, separate, thousands, european;
|
||||
int precision, result;
|
||||
|
||||
if (ovector[base * 2] >= 0) {
|
||||
|
|
|
|||
60
balance.cc
60
balance.cc
|
|
@ -41,23 +41,18 @@ static bool account_matches(const account * acct,
|
|||
return match;
|
||||
}
|
||||
|
||||
static void display_total(std::ostream& out, totals& total_balance,
|
||||
static void display_total(std::ostream& out, totals& balance,
|
||||
const account * acct, bool top_level,
|
||||
const std::map<account *, totals *>& balances,
|
||||
const std::list<mask>& regexps)
|
||||
{
|
||||
bool displayed = false;
|
||||
|
||||
std::map<account *, totals *>::const_iterator b =
|
||||
balances.find(const_cast<account *>(acct));
|
||||
if (b != balances.end()) {
|
||||
totals * balance = (*b).second;
|
||||
if (balance && (show_empty || *balance)) {
|
||||
if (acct->display && (show_empty || acct->balance)) {
|
||||
displayed = true;
|
||||
|
||||
out << *balance;
|
||||
out << acct->balance;
|
||||
if (top_level)
|
||||
total_balance.credit(*balance);
|
||||
balance.credit(acct->balance);
|
||||
|
||||
if (acct->parent && ! no_subtotals && ! full_names) {
|
||||
for (const account * a = acct; a; a = a->parent)
|
||||
|
|
@ -67,15 +62,13 @@ static void display_total(std::ostream& out, totals& total_balance,
|
|||
out << " " << *acct << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Display balances for all child accounts
|
||||
|
||||
for (account::const_iterator i = acct->children.begin();
|
||||
i != acct->children.end();
|
||||
i++)
|
||||
display_total(out, total_balance, (*i).second, ! displayed,
|
||||
balances, regexps);
|
||||
display_total(out, balance, (*i).second, ! displayed, regexps);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -122,11 +115,14 @@ void report_balances(int argc, char **argv, std::ostream& out)
|
|||
// Walk through all of the ledger entries, computing the account
|
||||
// totals
|
||||
|
||||
std::map<account *, totals *> balances;
|
||||
|
||||
for (entries_iterator i = main_ledger.entries.begin();
|
||||
i != main_ledger.entries.end();
|
||||
i++) {
|
||||
if ((have_beginning && difftime((*i)->date, begin_date) < 0) ||
|
||||
(have_ending && difftime((*i)->date, end_date) >= 0) ||
|
||||
(show_cleared && ! (*i)->cleared))
|
||||
continue;
|
||||
|
||||
for (std::list<transaction *>::iterator x = (*i)->xacts.begin();
|
||||
x != (*i)->xacts.end();
|
||||
x++) {
|
||||
|
|
@ -140,27 +136,8 @@ void report_balances(int argc, char **argv, std::ostream& out)
|
|||
else if (! (true_match || show_children || ! acct->parent))
|
||||
continue;
|
||||
|
||||
totals * balance = NULL;
|
||||
|
||||
std::map<account *, totals *>::iterator t = balances.find(acct);
|
||||
if (t == balances.end()) {
|
||||
balance = new totals;
|
||||
balances.insert(std::pair<account *, totals *>(acct, balance));
|
||||
} else {
|
||||
balance = (*t).second;
|
||||
}
|
||||
|
||||
bool do_credit = true;
|
||||
if (have_beginning && difftime((*i)->date, begin_date) < 0)
|
||||
do_credit = false;
|
||||
else if (have_ending && difftime((*i)->date, end_date) > 0)
|
||||
do_credit = false;
|
||||
else if (show_cleared && ! (*i)->cleared)
|
||||
do_credit = false;
|
||||
if (! do_credit)
|
||||
continue;
|
||||
|
||||
balance->credit((*x)->cost->street());
|
||||
acct->display = true;
|
||||
acct->balance.credit((*x)->cost->street());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -168,25 +145,18 @@ void report_balances(int argc, char **argv, std::ostream& out)
|
|||
// Walk through all the top-level accounts, giving the balance
|
||||
// report for each, and then for each of their children.
|
||||
|
||||
totals total_balance;
|
||||
totals balance;
|
||||
|
||||
for (accounts_iterator i = main_ledger.accounts.begin();
|
||||
i != main_ledger.accounts.end();
|
||||
i++)
|
||||
display_total(out, total_balance, (*i).second, true, balances, regexps);
|
||||
display_total(out, balance, (*i).second, true, regexps);
|
||||
|
||||
// Print the total of all the balances shown
|
||||
|
||||
if (! no_subtotals)
|
||||
out << "--------------------" << std::endl
|
||||
<< total_balance << std::endl;
|
||||
|
||||
// Free up temporary variables created on the heap
|
||||
|
||||
for (std::map<account *, totals *>::iterator i = balances.begin();
|
||||
i != balances.end();
|
||||
i++)
|
||||
delete (*i).second;
|
||||
<< balance << std::endl;
|
||||
}
|
||||
|
||||
} // namespace ledger
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ static commodity * curr_comm;
|
|||
static amount * curr_value;
|
||||
static std::string curr_quant;
|
||||
static XML_Parser current_parser;
|
||||
|
||||
static bool do_compute;
|
||||
static accounts_t accounts_by_id;
|
||||
|
||||
static enum {
|
||||
|
|
@ -205,6 +205,8 @@ static void dataHandler(void *userData, const char *s, int len)
|
|||
|
||||
std::string value = curr_quant + " " + (*i).second->comm->symbol;
|
||||
xact->cost = create_amount(value.c_str(), curr_value);
|
||||
|
||||
if (do_compute)
|
||||
xact->acct->balance.credit(xact->cost);
|
||||
break;
|
||||
}
|
||||
|
|
@ -224,13 +226,14 @@ static void dataHandler(void *userData, const char *s, int len)
|
|||
}
|
||||
}
|
||||
|
||||
bool parse_gnucash(std::istream& in)
|
||||
bool parse_gnucash(std::istream& in, bool compute_balances)
|
||||
{
|
||||
char buf[BUFSIZ];
|
||||
|
||||
curr_account = NULL;
|
||||
curr_entry = NULL;
|
||||
curr_comm = NULL;
|
||||
do_compute = compute_balances;
|
||||
|
||||
action = NO_ACTION;
|
||||
|
||||
|
|
|
|||
17
ledger.cc
17
ledger.cc
|
|
@ -96,12 +96,16 @@ bool entry::matches(const std::list<mask>& regexps) const
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef DO_CLEANUP
|
||||
|
||||
totals::~totals()
|
||||
{
|
||||
for (iterator i = amounts.begin(); i != amounts.end(); i++)
|
||||
delete (*i).second;
|
||||
}
|
||||
|
||||
#endif // DO_CLEANUP
|
||||
|
||||
void totals::credit(const totals& other)
|
||||
{
|
||||
for (const_iterator i = other.amounts.begin();
|
||||
|
|
@ -236,9 +240,10 @@ bool matches(const std::list<mask>& regexps, const std::string& str,
|
|||
return match;
|
||||
}
|
||||
|
||||
#ifdef DO_CLEANUP
|
||||
|
||||
state::~state()
|
||||
{
|
||||
#if 0
|
||||
for (commodities_iterator i = commodities.begin();
|
||||
i != commodities.end();
|
||||
i++)
|
||||
|
|
@ -253,9 +258,10 @@ state::~state()
|
|||
i != entries.end();
|
||||
i++)
|
||||
delete *i;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // DO_CLEANUP
|
||||
|
||||
void state::record_price(const char * setting)
|
||||
{
|
||||
char buf[128];
|
||||
|
|
@ -275,6 +281,10 @@ void state::record_price(const char * setting)
|
|||
|
||||
account * state::find_account(const char * name, bool create)
|
||||
{
|
||||
accounts_iterator i = accounts_cache.find(name);
|
||||
if (i != accounts_cache.end())
|
||||
return (*i).second;
|
||||
|
||||
char * buf = new char[std::strlen(name) + 1];
|
||||
std::strcpy(buf, name);
|
||||
|
||||
|
|
@ -307,6 +317,9 @@ account * state::find_account(const char * name, bool create)
|
|||
|
||||
delete[] buf;
|
||||
|
||||
if (current)
|
||||
accounts_cache.insert(accounts_entry(name, current));
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
|
|
|
|||
19
ledger.h
19
ledger.h
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef _LEDGER_H
|
||||
#define _LEDGER_H "$Revision: 1.11 $"
|
||||
#define _LEDGER_H "$Revision: 1.12 $"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
|
@ -172,10 +172,12 @@ struct transaction
|
|||
|
||||
transaction() : acct(NULL), cost(NULL) {}
|
||||
|
||||
#ifdef DO_CLEANUP
|
||||
~transaction() {
|
||||
if (cost)
|
||||
delete cost;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
struct entry
|
||||
|
|
@ -189,6 +191,12 @@ struct entry
|
|||
std::list<transaction *> xacts;
|
||||
|
||||
entry() : cleared(false) {}
|
||||
|
||||
#ifdef DO_CLEANUP
|
||||
// If we're running as a command-line tool, it's cheaper to just
|
||||
// throw away the heap on exit, than spend time freeing things up
|
||||
// like a good citizen.
|
||||
|
||||
~entry() {
|
||||
for (std::list<transaction *>::iterator i = xacts.begin();
|
||||
i != xacts.end();
|
||||
|
|
@ -196,6 +204,7 @@ struct entry
|
|||
delete *i;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool matches(const std::list<mask>& regexps) const;
|
||||
void print(std::ostream& out) const;
|
||||
|
|
@ -221,7 +230,9 @@ struct totals
|
|||
|
||||
map amounts;
|
||||
|
||||
#ifdef DO_CLEANUP
|
||||
~totals();
|
||||
#endif
|
||||
|
||||
void credit(const amount * val) {
|
||||
std::pair<iterator, bool> result =
|
||||
|
|
@ -257,6 +268,7 @@ struct account
|
|||
std::string name;
|
||||
commodity * comm; // default commodity for this account
|
||||
totals balance;
|
||||
bool display;
|
||||
|
||||
typedef std::map<const std::string, struct account *> map;
|
||||
typedef map::iterator iterator;
|
||||
|
|
@ -266,7 +278,7 @@ struct account
|
|||
map children;
|
||||
|
||||
account(const std::string& _name, struct account * _parent = NULL)
|
||||
: parent(_parent), name(_name) {}
|
||||
: parent(_parent), name(_name), display(false) {}
|
||||
|
||||
const std::string as_str() const {
|
||||
if (! parent)
|
||||
|
|
@ -297,10 +309,13 @@ struct state
|
|||
{
|
||||
commodities_t commodities;
|
||||
accounts_t accounts;
|
||||
accounts_t accounts_cache; // maps full names to accounts
|
||||
entries_t entries;
|
||||
totals prices;
|
||||
|
||||
#ifdef DO_CLEANUP
|
||||
~state();
|
||||
#endif
|
||||
|
||||
void record_price(const char * setting);
|
||||
account * find_account(const char * name, bool create = true);
|
||||
|
|
|
|||
14
main.cc
14
main.cc
|
|
@ -3,8 +3,10 @@
|
|||
#include <fstream>
|
||||
|
||||
namespace ledger {
|
||||
extern bool parse_ledger(std::istream& in);
|
||||
extern bool parse_gnucash(std::istream& in);
|
||||
extern bool parse_ledger(std::istream& in, bool compute_balances);
|
||||
#ifdef READ_GNUCASH
|
||||
extern bool parse_gnucash(std::istream& in, bool compute_balances);
|
||||
#endif
|
||||
|
||||
extern void report_balances(int argc, char **argv, std::ostream& out);
|
||||
extern void print_register(int argc, char **argv, std::ostream& out);
|
||||
|
|
@ -188,16 +190,20 @@ int main(int argc, char *argv[])
|
|||
|
||||
// Parse the ledger
|
||||
|
||||
#ifdef READ_GNUCASH
|
||||
char buf[32];
|
||||
file->get(buf, 31);
|
||||
file->seekg(0);
|
||||
|
||||
if (std::strncmp(buf, "<?xml version=\"1.0\"?>", 21) == 0)
|
||||
parse_gnucash(*file);
|
||||
parse_gnucash(*file, command == "equity");
|
||||
else
|
||||
parse_ledger(*file);
|
||||
#endif
|
||||
parse_ledger(*file, command == "equity");
|
||||
|
||||
#ifdef DO_CLEANUP
|
||||
delete file;
|
||||
#endif
|
||||
|
||||
// Process the command
|
||||
|
||||
|
|
|
|||
51
parse.cc
51
parse.cc
|
|
@ -50,38 +50,33 @@ static inline void finalize_entry(entry * curr)
|
|||
// Ledger parser
|
||||
//
|
||||
|
||||
bool parse_ledger(std::istream& in)
|
||||
bool parse_ledger(std::istream& in, bool compute_balances)
|
||||
{
|
||||
static std::time_t now = std::time(NULL);
|
||||
static struct std::tm * now_tm = std::localtime(&now);
|
||||
static int current_year = now_tm->tm_year + 1900;
|
||||
std::time_t now = std::time(NULL);
|
||||
struct std::tm * now_tm = std::localtime(&now);
|
||||
int current_year = now_tm->tm_year + 1900;
|
||||
|
||||
static char line[1024];
|
||||
char line[1024];
|
||||
|
||||
static struct std::tm moment;
|
||||
struct std::tm moment;
|
||||
memset(&moment, 0, sizeof(struct std::tm));
|
||||
|
||||
entry * curr = NULL;
|
||||
|
||||
// Compile the regular expression used for parsing amounts
|
||||
static pcre * entry_re = NULL;
|
||||
if (! entry_re) {
|
||||
const char *error;
|
||||
int erroffset;
|
||||
static const std::string regexp =
|
||||
"^(([0-9]{4})[./])?([0-9]+)[./]([0-9]+)\\s+(\\*\\s+)?"
|
||||
"(\\(([^)]+)\\)\\s+)?(.+)";
|
||||
entry_re = pcre_compile(regexp.c_str(), 0, &error, &erroffset, NULL);
|
||||
}
|
||||
pcre * entry_re = pcre_compile(regexp.c_str(), 0,
|
||||
&error, &erroffset, NULL);
|
||||
|
||||
while (! in.eof()) {
|
||||
in.getline(line, 1023);
|
||||
linenum++;
|
||||
|
||||
if (in.eof()) {
|
||||
break;
|
||||
}
|
||||
else if (line[0] == '\n') {
|
||||
if (line[0] == '\n') {
|
||||
continue;
|
||||
}
|
||||
else if (std::isdigit(line[0])) {
|
||||
|
|
@ -96,28 +91,28 @@ bool parse_ledger(std::istream& in)
|
|||
continue;
|
||||
}
|
||||
|
||||
// If we haven't finished with the last entry yet, do so now
|
||||
|
||||
if (curr)
|
||||
finalize_entry(curr);
|
||||
|
||||
curr = new entry;
|
||||
|
||||
// Parse the date
|
||||
|
||||
int mday, mon, year = current_year;
|
||||
|
||||
int year = current_year;
|
||||
if (ovector[1 * 2] >= 0) {
|
||||
pcre_copy_substring(line, ovector, matched, 2, buf, 255);
|
||||
year = std::atoi(buf);
|
||||
}
|
||||
|
||||
if (ovector[3 * 2] >= 0) {
|
||||
assert(ovector[3 * 2] >= 0);
|
||||
pcre_copy_substring(line, ovector, matched, 3, buf, 255);
|
||||
mon = std::atoi(buf);
|
||||
}
|
||||
int mon = std::atoi(buf);
|
||||
|
||||
if (ovector[4 * 2] >= 0) {
|
||||
assert(ovector[4 * 2] >= 0);
|
||||
pcre_copy_substring(line, ovector, matched, 4, buf, 255);
|
||||
mday = std::atoi(buf);
|
||||
}
|
||||
int mday = std::atoi(buf);
|
||||
|
||||
moment.tm_mday = mday;
|
||||
moment.tm_mon = mon - 1;
|
||||
|
|
@ -125,6 +120,8 @@ bool parse_ledger(std::istream& in)
|
|||
|
||||
curr->date = std::mktime(&moment);
|
||||
|
||||
// Parse the remaining entry details
|
||||
|
||||
if (ovector[5 * 2] >= 0)
|
||||
curr->cleared = true;
|
||||
|
||||
|
|
@ -164,6 +161,7 @@ bool parse_ledger(std::istream& in)
|
|||
cost_str++;
|
||||
xact->note = cost_str;
|
||||
}
|
||||
|
||||
xact->cost = curr->xacts.front()->cost->copy();
|
||||
xact->cost->negate();
|
||||
}
|
||||
|
|
@ -198,6 +196,7 @@ bool parse_ledger(std::istream& in)
|
|||
#endif
|
||||
|
||||
xact->acct = main_ledger.find_account(p);
|
||||
if (compute_balances)
|
||||
xact->acct->balance.credit(xact->cost);
|
||||
|
||||
curr->xacts.push_back(xact);
|
||||
|
|
@ -215,9 +214,11 @@ bool parse_ledger(std::istream& in)
|
|||
temp = xact->cost->value();
|
||||
t->cost = temp->value(huquq);
|
||||
delete temp;
|
||||
t->acct->balance.credit(t->cost);
|
||||
curr->xacts.push_back(t);
|
||||
|
||||
if (compute_balances)
|
||||
t->acct->balance.credit(t->cost);
|
||||
|
||||
// Balance the above transaction by recording the inverse in
|
||||
// Expenses:Huququ'llah.
|
||||
t = new transaction();
|
||||
|
|
@ -226,8 +227,10 @@ bool parse_ledger(std::istream& in)
|
|||
t->cost = temp->value(huquq);
|
||||
delete temp;
|
||||
t->cost->negate();
|
||||
t->acct->balance.credit(t->cost);
|
||||
curr->xacts.push_back(t);
|
||||
|
||||
if (compute_balances)
|
||||
t->acct->balance.credit(t->cost);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue