*** empty log message ***

This commit is contained in:
John Wiegley 2003-10-01 07:11:57 +00:00
parent 68e6b8538e
commit a40813d896
8 changed files with 137 additions and 119 deletions

View file

@ -1,22 +1,27 @@
define GNUCASH CODE = amount.cc \
true ledger.cc \
endef parse.cc \
balance.cc \
CODE = amount.cc ledger.cc parse.cc \ register.cc \
balance.cc register.cc equity.cc main.cc equity.cc \
ifdef GNUCASH main.cc
CODE := $(CODE) gnucash.cc
endif
OBJS = $(patsubst %.cc,%.o,$(CODE)) OBJS = $(patsubst %.cc,%.o,$(CODE))
CFLAGS = -Wall -ansi -pedantic -DDEFAULT_COMMODITY="\"\$$\"" -DHUQUQULLAH=1 CFLAGS = -Wall -ansi -pedantic -DDEFAULT_COMMODITY="\"\$$\""
#DFLAGS = -O3 -fomit-frame-pointer CFLAGS := $(CFLAGS) -DHUQUQULLAH=1
DFLAGS = -g # -pg
DFLAGS = -O3 -fomit-frame-pointer
#DFLAGS = -g -O2 # -pg
INCS = -I/usr/include/xmltok INCS = -I/usr/include/xmltok
LIBS = -lgmpxx -lgmp -lpcre LIBS = -lgmpxx -lgmp -lpcre
ifdef GNUCASH ifdef GNUCASH
LIBS := $(LIBS) -lxmlparse CODE := $(CODE) gnucash.cc
CFLAGS := $(CFLAGS) -DREAD_GNUCASH=1
LIBS := $(LIBS) -lxmlparse
endif endif
all: make.deps ledger all: make.deps ledger
@ -28,7 +33,7 @@ ledger: $(OBJS)
g++ $(CFLAGS) $(INCS) $(DFLAGS) -c -o $@ $< g++ $(CFLAGS) $(INCS) $(DFLAGS) -c -o $@ $<
clean: clean:
rm -f libledger.so ledger *.o rm -f ledger *.o
rebuild: clean deps all rebuild: clean deps all

View file

@ -474,9 +474,12 @@ static commodity * parse_amount(mpz_t out, const char * num,
static char buf[256]; static char buf[256];
bool saw_commodity = false; bool saw_commodity = false;
bool prefix = false;
bool separate = true;
bool thousands = true;
bool european = false;
std::string symbol; std::string symbol;
bool prefix, separate, thousands, european;
int precision, result; int precision, result;
if (ovector[base * 2] >= 0) { if (ovector[base * 2] >= 0) {

View file

@ -4,17 +4,17 @@
namespace ledger { namespace ledger {
extern bool show_cleared; extern bool show_cleared;
extern std::time_t begin_date; extern std::time_t begin_date;
extern bool have_beginning; extern bool have_beginning;
extern std::time_t end_date; extern std::time_t end_date;
extern bool have_ending; extern bool have_ending;
static bool show_children; static bool show_children;
static bool show_empty; static bool show_empty;
static bool no_subtotals; static bool no_subtotals;
static bool full_names; static bool full_names;
static bool account_matches(const account * acct, static bool account_matches(const account * acct,
const std::list<mask>& regexps, const std::list<mask>& regexps,
@ -41,31 +41,25 @@ static bool account_matches(const account * acct,
return match; 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 account * acct, bool top_level,
const std::map<account *, totals *>& balances,
const std::list<mask>& regexps) const std::list<mask>& regexps)
{ {
bool displayed = false; bool displayed = false;
std::map<account *, totals *>::const_iterator b = if (acct->display && (show_empty || acct->balance)) {
balances.find(const_cast<account *>(acct)); displayed = true;
if (b != balances.end()) {
totals * balance = (*b).second;
if (balance && (show_empty || *balance)) {
displayed = true;
out << *balance; out << acct->balance;
if (top_level) if (top_level)
total_balance.credit(*balance); balance.credit(acct->balance);
if (acct->parent && ! no_subtotals && ! full_names) { if (acct->parent && ! no_subtotals && ! full_names) {
for (const account * a = acct; a; a = a->parent) for (const account * a = acct; a; a = a->parent)
out << " "; out << " ";
out << acct->name << std::endl; out << acct->name << std::endl;
} else { } else {
out << " " << *acct << std::endl; out << " " << *acct << std::endl;
}
} }
} }
@ -74,8 +68,7 @@ static void display_total(std::ostream& out, totals& total_balance,
for (account::const_iterator i = acct->children.begin(); for (account::const_iterator i = acct->children.begin();
i != acct->children.end(); i != acct->children.end();
i++) i++)
display_total(out, total_balance, (*i).second, ! displayed, display_total(out, balance, (*i).second, ! displayed, regexps);
balances, 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 // Walk through all of the ledger entries, computing the account
// totals // totals
std::map<account *, totals *> balances;
for (entries_iterator i = main_ledger.entries.begin(); for (entries_iterator i = main_ledger.entries.begin();
i != main_ledger.entries.end(); i != main_ledger.entries.end();
i++) { 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(); for (std::list<transaction *>::iterator x = (*i)->xacts.begin();
x != (*i)->xacts.end(); x != (*i)->xacts.end();
x++) { x++) {
@ -140,27 +136,8 @@ void report_balances(int argc, char **argv, std::ostream& out)
else if (! (true_match || show_children || ! acct->parent)) else if (! (true_match || show_children || ! acct->parent))
continue; continue;
totals * balance = NULL; acct->display = true;
acct->balance.credit((*x)->cost->street());
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());
} }
} }
} }
@ -168,25 +145,18 @@ void report_balances(int argc, char **argv, std::ostream& out)
// Walk through all the top-level accounts, giving the balance // Walk through all the top-level accounts, giving the balance
// report for each, and then for each of their children. // report for each, and then for each of their children.
totals total_balance; totals balance;
for (accounts_iterator i = main_ledger.accounts.begin(); for (accounts_iterator i = main_ledger.accounts.begin();
i != main_ledger.accounts.end(); i != main_ledger.accounts.end();
i++) 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 // Print the total of all the balances shown
if (! no_subtotals) if (! no_subtotals)
out << "--------------------" << std::endl out << "--------------------" << std::endl
<< total_balance << std::endl; << 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;
} }
} // namespace ledger } // namespace ledger

View file

@ -17,8 +17,8 @@ static commodity * curr_comm;
static amount * curr_value; static amount * curr_value;
static std::string curr_quant; static std::string curr_quant;
static XML_Parser current_parser; static XML_Parser current_parser;
static bool do_compute;
static accounts_t accounts_by_id; static accounts_t accounts_by_id;
static enum { static enum {
NO_ACTION, NO_ACTION,
@ -205,7 +205,9 @@ static void dataHandler(void *userData, const char *s, int len)
std::string value = curr_quant + " " + (*i).second->comm->symbol; std::string value = curr_quant + " " + (*i).second->comm->symbol;
xact->cost = create_amount(value.c_str(), curr_value); xact->cost = create_amount(value.c_str(), curr_value);
xact->acct->balance.credit(xact->cost);
if (do_compute)
xact->acct->balance.credit(xact->cost);
break; 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]; char buf[BUFSIZ];
curr_account = NULL; curr_account = NULL;
curr_entry = NULL; curr_entry = NULL;
curr_comm = NULL; curr_comm = NULL;
do_compute = compute_balances;
action = NO_ACTION; action = NO_ACTION;

View file

@ -96,12 +96,16 @@ bool entry::matches(const std::list<mask>& regexps) const
} }
} }
#ifdef DO_CLEANUP
totals::~totals() totals::~totals()
{ {
for (iterator i = amounts.begin(); i != amounts.end(); i++) for (iterator i = amounts.begin(); i != amounts.end(); i++)
delete (*i).second; delete (*i).second;
} }
#endif // DO_CLEANUP
void totals::credit(const totals& other) void totals::credit(const totals& other)
{ {
for (const_iterator i = other.amounts.begin(); for (const_iterator i = other.amounts.begin();
@ -236,9 +240,10 @@ bool matches(const std::list<mask>& regexps, const std::string& str,
return match; return match;
} }
#ifdef DO_CLEANUP
state::~state() state::~state()
{ {
#if 0
for (commodities_iterator i = commodities.begin(); for (commodities_iterator i = commodities.begin();
i != commodities.end(); i != commodities.end();
i++) i++)
@ -253,9 +258,10 @@ state::~state()
i != entries.end(); i != entries.end();
i++) i++)
delete *i; delete *i;
#endif
} }
#endif // DO_CLEANUP
void state::record_price(const char * setting) void state::record_price(const char * setting)
{ {
char buf[128]; char buf[128];
@ -275,6 +281,10 @@ void state::record_price(const char * setting)
account * state::find_account(const char * name, bool create) 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]; char * buf = new char[std::strlen(name) + 1];
std::strcpy(buf, name); std::strcpy(buf, name);
@ -307,6 +317,9 @@ account * state::find_account(const char * name, bool create)
delete[] buf; delete[] buf;
if (current)
accounts_cache.insert(accounts_entry(name, current));
return current; return current;
} }

View file

@ -1,5 +1,5 @@
#ifndef _LEDGER_H #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) {} transaction() : acct(NULL), cost(NULL) {}
#ifdef DO_CLEANUP
~transaction() { ~transaction() {
if (cost) if (cost)
delete cost; delete cost;
} }
#endif
}; };
struct entry struct entry
@ -189,6 +191,12 @@ struct entry
std::list<transaction *> xacts; std::list<transaction *> xacts;
entry() : cleared(false) {} 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() { ~entry() {
for (std::list<transaction *>::iterator i = xacts.begin(); for (std::list<transaction *>::iterator i = xacts.begin();
i != xacts.end(); i != xacts.end();
@ -196,6 +204,7 @@ struct entry
delete *i; delete *i;
} }
} }
#endif
bool matches(const std::list<mask>& regexps) const; bool matches(const std::list<mask>& regexps) const;
void print(std::ostream& out) const; void print(std::ostream& out) const;
@ -221,7 +230,9 @@ struct totals
map amounts; map amounts;
#ifdef DO_CLEANUP
~totals(); ~totals();
#endif
void credit(const amount * val) { void credit(const amount * val) {
std::pair<iterator, bool> result = std::pair<iterator, bool> result =
@ -257,6 +268,7 @@ struct account
std::string name; std::string name;
commodity * comm; // default commodity for this account commodity * comm; // default commodity for this account
totals balance; totals balance;
bool display;
typedef std::map<const std::string, struct account *> map; typedef std::map<const std::string, struct account *> map;
typedef map::iterator iterator; typedef map::iterator iterator;
@ -266,7 +278,7 @@ struct account
map children; map children;
account(const std::string& _name, struct account * _parent = NULL) 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 { const std::string as_str() const {
if (! parent) if (! parent)
@ -297,10 +309,13 @@ struct state
{ {
commodities_t commodities; commodities_t commodities;
accounts_t accounts; accounts_t accounts;
accounts_t accounts_cache; // maps full names to accounts
entries_t entries; entries_t entries;
totals prices; totals prices;
#ifdef DO_CLEANUP
~state(); ~state();
#endif
void record_price(const char * setting); void record_price(const char * setting);
account * find_account(const char * name, bool create = true); account * find_account(const char * name, bool create = true);

14
main.cc
View file

@ -3,8 +3,10 @@
#include <fstream> #include <fstream>
namespace ledger { namespace ledger {
extern bool parse_ledger(std::istream& in); extern bool parse_ledger(std::istream& in, bool compute_balances);
extern bool parse_gnucash(std::istream& in); #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 report_balances(int argc, char **argv, std::ostream& out);
extern void print_register(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 // Parse the ledger
#ifdef READ_GNUCASH
char buf[32]; char buf[32];
file->get(buf, 31); file->get(buf, 31);
file->seekg(0); file->seekg(0);
if (std::strncmp(buf, "<?xml version=\"1.0\"?>", 21) == 0) if (std::strncmp(buf, "<?xml version=\"1.0\"?>", 21) == 0)
parse_gnucash(*file); parse_gnucash(*file, command == "equity");
else else
parse_ledger(*file); #endif
parse_ledger(*file, command == "equity");
#ifdef DO_CLEANUP
delete file; delete file;
#endif
// Process the command // Process the command

View file

@ -50,38 +50,33 @@ static inline void finalize_entry(entry * curr)
// Ledger parser // 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); std::time_t now = std::time(NULL);
static struct std::tm * now_tm = std::localtime(&now); struct std::tm * now_tm = std::localtime(&now);
static int current_year = now_tm->tm_year + 1900; 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)); memset(&moment, 0, sizeof(struct std::tm));
entry * curr = NULL; entry * curr = NULL;
// Compile the regular expression used for parsing amounts // Compile the regular expression used for parsing amounts
static pcre * entry_re = NULL; const char *error;
if (! entry_re) { int erroffset;
const char *error; static const std::string regexp =
int erroffset; "^(([0-9]{4})[./])?([0-9]+)[./]([0-9]+)\\s+(\\*\\s+)?"
static const std::string regexp = "(\\(([^)]+)\\)\\s+)?(.+)";
"^(([0-9]{4})[./])?([0-9]+)[./]([0-9]+)\\s+(\\*\\s+)?" pcre * entry_re = pcre_compile(regexp.c_str(), 0,
"(\\(([^)]+)\\)\\s+)?(.+)"; &error, &erroffset, NULL);
entry_re = pcre_compile(regexp.c_str(), 0, &error, &erroffset, NULL);
}
while (! in.eof()) { while (! in.eof()) {
in.getline(line, 1023); in.getline(line, 1023);
linenum++; linenum++;
if (in.eof()) { if (line[0] == '\n') {
break;
}
else if (line[0] == '\n') {
continue; continue;
} }
else if (std::isdigit(line[0])) { else if (std::isdigit(line[0])) {
@ -96,28 +91,28 @@ bool parse_ledger(std::istream& in)
continue; continue;
} }
// If we haven't finished with the last entry yet, do so now
if (curr) if (curr)
finalize_entry(curr); finalize_entry(curr);
curr = new entry; curr = new entry;
// Parse the date // Parse the date
int mday, mon, year = current_year; int year = current_year;
if (ovector[1 * 2] >= 0) { if (ovector[1 * 2] >= 0) {
pcre_copy_substring(line, ovector, matched, 2, buf, 255); pcre_copy_substring(line, ovector, matched, 2, buf, 255);
year = std::atoi(buf); year = std::atoi(buf);
} }
if (ovector[3 * 2] >= 0) { assert(ovector[3 * 2] >= 0);
pcre_copy_substring(line, ovector, matched, 3, buf, 255); 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); pcre_copy_substring(line, ovector, matched, 4, buf, 255);
mday = std::atoi(buf); int mday = std::atoi(buf);
}
moment.tm_mday = mday; moment.tm_mday = mday;
moment.tm_mon = mon - 1; moment.tm_mon = mon - 1;
@ -125,6 +120,8 @@ bool parse_ledger(std::istream& in)
curr->date = std::mktime(&moment); curr->date = std::mktime(&moment);
// Parse the remaining entry details
if (ovector[5 * 2] >= 0) if (ovector[5 * 2] >= 0)
curr->cleared = true; curr->cleared = true;
@ -164,6 +161,7 @@ bool parse_ledger(std::istream& in)
cost_str++; cost_str++;
xact->note = cost_str; xact->note = cost_str;
} }
xact->cost = curr->xacts.front()->cost->copy(); xact->cost = curr->xacts.front()->cost->copy();
xact->cost->negate(); xact->cost->negate();
} }
@ -198,7 +196,8 @@ bool parse_ledger(std::istream& in)
#endif #endif
xact->acct = main_ledger.find_account(p); xact->acct = main_ledger.find_account(p);
xact->acct->balance.credit(xact->cost); if (compute_balances)
xact->acct->balance.credit(xact->cost);
curr->xacts.push_back(xact); curr->xacts.push_back(xact);
@ -215,9 +214,11 @@ bool parse_ledger(std::istream& in)
temp = xact->cost->value(); temp = xact->cost->value();
t->cost = temp->value(huquq); t->cost = temp->value(huquq);
delete temp; delete temp;
t->acct->balance.credit(t->cost);
curr->xacts.push_back(t); curr->xacts.push_back(t);
if (compute_balances)
t->acct->balance.credit(t->cost);
// Balance the above transaction by recording the inverse in // Balance the above transaction by recording the inverse in
// Expenses:Huququ'llah. // Expenses:Huququ'llah.
t = new transaction(); t = new transaction();
@ -226,8 +227,10 @@ bool parse_ledger(std::istream& in)
t->cost = temp->value(huquq); t->cost = temp->value(huquq);
delete temp; delete temp;
t->cost->negate(); t->cost->negate();
t->acct->balance.credit(t->cost);
curr->xacts.push_back(t); curr->xacts.push_back(t);
if (compute_balances)
t->acct->balance.credit(t->cost);
} }
#endif #endif
} }