summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--amount.cc7
-rw-r--r--balance.cc196
-rw-r--r--gnucash.cc41
-rw-r--r--ledger.cc10
-rw-r--r--ledger.h46
-rw-r--r--ledger.texi186
-rw-r--r--main.cc65
-rw-r--r--parse.cc21
8 files changed, 392 insertions, 180 deletions
diff --git a/amount.cc b/amount.cc
index b6c26270..bd3827d0 100644
--- a/amount.cc
+++ b/amount.cc
@@ -1,5 +1,4 @@
#include <sstream>
-#include <cassert>
#include <gmp.h> // GNU multi-precision library
#include <pcre.h> // Perl regular expression library
@@ -337,9 +336,6 @@ amount& gmp_amount::operator=(const char * num)
commodities_iterator item = commodities.find(symbol.c_str());
if (item == commodities.end()) {
quantity_comm = new commodity(symbol, prefix, separate, precision);
- std::pair<commodities_iterator, bool> insert_result =
- commodities.insert(commodities_entry(symbol, quantity_comm));
- assert(insert_result.second);
} else {
quantity_comm = (*item).second;
@@ -396,9 +392,6 @@ amount& gmp_amount::operator=(const char * num)
commodities_iterator item = commodities.find(symbol.c_str());
if (item == commodities.end()) {
price_comm = new commodity(symbol, prefix, separate, precision);
- std::pair<commodities_iterator, bool> insert_result =
- commodities.insert(commodities_entry(symbol, price_comm));
- assert(insert_result.second);
} else {
price_comm = (*item).second;
diff --git a/balance.cc b/balance.cc
index b3ecf593..fd09bae1 100644
--- a/balance.cc
+++ b/balance.cc
@@ -1,10 +1,7 @@
-#include <iostream>
-#include <vector>
+#include "ledger.h"
#include <pcre.h> // Perl regular expression library
-#include "ledger.h"
-
namespace ledger {
//////////////////////////////////////////////////////////////////////
@@ -12,10 +9,38 @@ namespace ledger {
// Balance report.
//
-void report_balances(std::ostream& out, std::vector<entry *>& ledger,
- bool show_children, bool show_empty)
+static inline bool matches(const std::list<pcre *>& regexps,
+ const std::string& str) {
+ for (std::list<pcre *>::const_iterator r = regexps.begin();
+ r != regexps.end();
+ r++) {
+ int ovec[3];
+ if (pcre_exec(*r, NULL, str.c_str(), str.length(), 0, 0, ovec, 3) >= 0)
+ return true;
+ }
+
+ return false;
+}
+
+void report_balances(int argc, char *argv[], std::ostream& out)
{
-#if 0
+ bool show_current = false;
+ bool show_cleared = false;
+ bool show_children = false;
+ bool show_empty = false;
+ bool no_subtotals = false;
+
+ int c;
+ while (-1 != (c = getopt(argc, argv, "cCsSn"))) {
+ switch (char(c)) {
+ case 'c': show_current = true; break;
+ case 'C': show_cleared = true; break;
+ case 's': show_children = true; break;
+ case 'S': show_empty = true; break;
+ case 'n': no_subtotals = true; break;
+ }
+ }
+
// Compile the list of specified regular expressions, which can be
// specified on the command line, or using an include/exclude file.
@@ -26,97 +51,118 @@ void report_balances(std::ostream& out, std::vector<entry *>& ledger,
int erroffset;
pcre * re = pcre_compile(argv[optind], PCRE_CASELESS,
&error, &erroffset, NULL);
- assert(re);
- regexps.push_back(re);
+ if (! re)
+ std::cerr << "Warning: Failed to compile regexp: " << argv[optind]
+ << std::endl;
+ else
+ regexps.push_back(re);
}
-#endif
- // The balance of all accounts must equal zero
- totals future_balance;
- totals current_balance;
- totals cleared_balance;
+ // Walk through all of the ledger entries, computing the account
+ // totals
+
+ std::map<account *, totals *> balances;
+
+ std::time_t now = std::time(NULL);
+
+ for (ledger_iterator i = ledger.begin(); i != ledger.end(); i++) {
+ for (std::list<transaction *>::iterator x = (*i)->xacts.begin();
+ x != (*i)->xacts.end();
+ x++) {
+ account * acct = (*x)->acct;
+ if (! regexps.empty() && ! matches(regexps, acct->name))
+ continue;
+
+ while (acct) {
+ 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;
+ }
+
+ if (show_current) {
+ if (difftime((*i)->date, now) < 0)
+ balance->credit((*x)->cost);
+ }
+ else if (show_cleared) {
+ if ((*i)->cleared)
+ balance->credit((*x)->cost);
+ }
+ else {
+ balance->credit((*x)->cost);
+ }
+
+ if (no_subtotals)
+ acct = NULL;
+ else
+ acct = acct->parent;
+ }
+ }
+ }
+
+ // Print out the balance report header
+
+ std::string which = "Future";
+ if (show_current)
+ which = "Current";
+ else if (show_cleared)
+ which = "Cleared";
std::cout.width(10);
- std::cout << std::right << "Future" << " ";
- std::cout.width(10);
- std::cout << std::right << "Current" << " ";
- std::cout.width(10);
- std::cout << std::right << "Cleared" << std::endl;
+ std::cout << std::right << which << std::endl;
- for (accounts_iterator i = accounts.begin();
- i != accounts.end();
- i++) {
- if (! show_empty && ! (*i).second->future)
+ // Walk through the accounts, given the balance report for each
+
+ totals total_balance;
+
+ for (accounts_iterator i = accounts.begin(); i != accounts.end(); i++) {
+ account * acct = (*i).second;
+
+ if (! regexps.empty() && ! matches(regexps, acct->name))
continue;
int depth = 0;
- account * acct = (*i).second;
- while (acct->parent) {
+ for (account * a = acct; a; a = a->parent)
depth++;
- acct = acct->parent;
- }
-#if 0
- if (! regexps.empty()) {
- bool matches = false;
- for (std::list<pcre *>::iterator r = regexps.begin();
- r != regexps.end();
- r++) {
- int ovector[30];
- if (pcre_exec(*r, NULL, (*i).first.c_str(), (*i).first.length(),
- 0, 0, ovector, 30) >= 0) {
- matches = true;
- break;
- }
- }
+ if (! show_children && depth)
+ continue;
- if (! matches)
- continue;
- }
- else
-#endif
- if (! show_children && depth) {
+ totals * balance = balances[acct];
+
+ if (! show_empty && ! *balance)
continue;
- }
std::cout.width(10);
- std::cout << (*i).second->future << " ";
- std::cout.width(10);
- std::cout << (*i).second->current << " ";
- std::cout.width(10);
- std::cout << (*i).second->cleared << " ";
+ std::cout << *balance << " ";
+
+ total_balance.credit(*balance);
if (depth) {
while (--depth >= 0)
std::cout << " ";
- std::cout << (*i).second->name << std::endl;
+ std::cout << acct->name << std::endl;
} else {
- std::cout << (*i).first << std::endl;
-
-#if 0
- if (regexps.empty()) {
-#endif
- future_balance.credit((*i).second->future);
- current_balance.credit((*i).second->current);
- cleared_balance.credit((*i).second->cleared);
-#if 0
- }
-#endif
+ std::cout << *acct << std::endl;
}
}
-#if 0
- if (regexps.empty()) {
-#endif
- std::cout.width(10);
- std::cout << std::right << future_balance << " ";
- std::cout.width(10);
- std::cout << std::right << current_balance << " ";
- std::cout.width(10);
- std::cout << std::right << cleared_balance << std::endl;
-#if 0
+ // Print the total of all the balances shown
+
+ std::cout.width(10);
+ std::cout << std::right << 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;
}
-#endif
}
} // namespace ledger
diff --git a/gnucash.cc b/gnucash.cc
index 70d35de5..dde18442 100644
--- a/gnucash.cc
+++ b/gnucash.cc
@@ -1,14 +1,12 @@
#include <sstream>
-#include <vector>
#include <cstring>
-#include <cassert>
+
+#include "ledger.h"
extern "C" {
#include <xmlparse.h> // expat XML parser
}
-#include "ledger.h"
-
namespace ledger {
static account * curr_account;
@@ -20,8 +18,6 @@ static amount * curr_value;
static std::string curr_quant;
static XML_Parser current_parser;
-static std::vector<entry *> * current_ledger;
-
enum {
NO_ACTION,
ACCOUNT_NAME,
@@ -116,7 +112,7 @@ static void endElement(void *userData, const char *name)
<< XML_GetCurrentLineNumber(current_parser) << std::endl;
curr_entry->print(std::cerr);
} else {
- current_ledger->push_back(curr_entry);
+ ledger.push_back(curr_entry);
}
curr_entry = NULL;
}
@@ -192,11 +188,18 @@ static void dataHandler(void *userData, const char *s, int len)
case XACT_ACCOUNT: {
accounts_iterator i = accounts.find(std::string(s, len));
- assert(i != accounts.end());
- curr_entry->xacts.back()->acct = (*i).second;
+ if (i == accounts.end()) {
+ std::cerr << "Could not find account " << std::string(s, len)
+ << " at line " << XML_GetCurrentLineNumber(current_parser)
+ << std::endl;
+ std::exit(1);
+ }
+
+ transaction * xact = curr_entry->xacts.back();
+ xact->acct = (*i).second;
std::string value = curr_quant + " " + (*i).second->comm->symbol;
- curr_entry->xacts.back()->cost = create_amount(value.c_str(), curr_value);
+ xact->cost = create_amount(value.c_str(), curr_value);
break;
}
@@ -215,27 +218,25 @@ static void dataHandler(void *userData, const char *s, int len)
}
}
-bool parse_gnucash(std::istream& in, std::vector<entry *>& ledger)
+bool parse_gnucash(std::istream& in)
{
char buf[BUFSIZ];
- XML_Parser parser = XML_ParserCreate(NULL);
- current_parser = parser;
-
- //XML_SetUserData(parser, &depth);
- XML_SetElementHandler(parser, startElement, endElement);
- XML_SetCharacterDataHandler(parser, dataHandler);
-
- current_ledger = &ledger;
-
curr_account = NULL;
curr_entry = NULL;
curr_comm = NULL;
action = NO_ACTION;
+ XML_Parser parser = XML_ParserCreate(NULL);
+ current_parser = parser;
+
+ XML_SetElementHandler(parser, startElement, endElement);
+ XML_SetCharacterDataHandler(parser, dataHandler);
+
while (! in.eof()) {
in.getline(buf, BUFSIZ - 1);
+
if (! XML_Parse(parser, buf, std::strlen(buf), in.eof())) {
std::cerr << XML_ErrorString(XML_GetErrorCode(parser))
<< " at line " << XML_GetCurrentLineNumber(parser)
diff --git a/ledger.cc b/ledger.cc
index 88a9d0b7..e2d7ed35 100644
--- a/ledger.cc
+++ b/ledger.cc
@@ -1,13 +1,11 @@
-#include <vector>
-
#include "ledger.h"
namespace ledger {
commodities_t commodities;
commodity * commodity_usd;
-
-accounts_t accounts;
+accounts_t accounts;
+ledger_t ledger;
void entry::print(std::ostream& out) const
{
@@ -105,10 +103,10 @@ amount * totals::value(const std::string& commodity)
return total;
}
-// Print out the entire ledger that was read in, but now sorted.
+// Print out the entire ledger that was read in, sorted by date.
// This can be used to "wash" ugly ledger files.
-void print_ledger(std::ostream& out, std::vector<entry *>& ledger)
+void print_ledger(int argc, char *argv[], std::ostream& out)
{
// Sort the list of entries by date, then print them in order.
diff --git a/ledger.h b/ledger.h
index 99287cc6..8cee10df 100644
--- a/ledger.h
+++ b/ledger.h
@@ -1,5 +1,5 @@
#ifndef _LEDGER_H
-#define _LEDGER_H "$Revision: 1.2 $"
+#define _LEDGER_H "$Revision: 1.3 $"
//////////////////////////////////////////////////////////////////////
//
@@ -11,9 +11,11 @@
#include <iostream>
#include <string>
+#include <vector>
#include <list>
#include <map>
#include <ctime>
+#include <cassert>
namespace ledger {
@@ -47,7 +49,7 @@ namespace ledger {
// commodity is converted into the first by computing which the price
// must have been in order to balance the transaction. Example:
//
-// 2004.06.18 c (BUY) Apple Computer
+// 2004.06.18 * (BUY) Apple Computer
// Assets:Brokerage $-200.00
// Assets:Brokerage 100 AAPL
//
@@ -64,7 +66,7 @@ namespace ledger {
// stock, and it will read this transaction as if it had been
// written:
//
-// 2004.06.18 c (BUY) Apple Computer
+// 2004.06.18 * (BUY) Apple Computer
// Assets:Brokerage $-200
// Assets:Brokerage 100 AAPL @ $2
//
@@ -72,7 +74,7 @@ namespace ledger {
// exchange for services rendered, use the regular single-commodity
// form of transaction:
//
-// 2004.07.11 c A kick-back for the broker
+// 2004.07.11 * A kick-back for the broker
// Assets:Brokerage -10 AAPL
// Expenses:Broker's Fees 10 AAPL
//
@@ -92,8 +94,7 @@ struct commodity
int precision;
commodity() : prefix(false), separate(true) {}
- commodity(const std::string& sym, bool pre, bool sep, int prec)
- : symbol(sym), prefix(pre), separate(sep), precision(prec) {}
+ commodity(const std::string& sym, bool pre, bool sep, int prec);
};
typedef std::map<const std::string, commodity *> commodities_t;
@@ -103,6 +104,15 @@ typedef std::pair<const std::string, commodity *> commodities_entry;
extern commodities_t commodities;
extern commodity * commodity_usd;
+inline commodity::commodity(const std::string& sym,
+ bool pre, bool sep, int prec)
+ : symbol(sym), prefix(pre), separate(sep), precision(prec) {
+ std::pair<commodities_iterator, bool> result =
+ commodities.insert(commodities_entry(sym, this));
+ assert(result.second);
+}
+
+
class amount
{
public:
@@ -181,6 +191,12 @@ struct cmp_entry_date {
}
};
+typedef std::vector<entry *> ledger_t;
+typedef ledger_t::iterator ledger_iterator;
+
+extern ledger_t ledger;
+
+
class totals
{
typedef std::map<const std::string, amount *> map_t;
@@ -217,6 +233,7 @@ operator<<(std::basic_ostream<char, Traits>& out, const totals& t) {
return out;
}
+
struct account
{
std::string name;
@@ -231,26 +248,9 @@ struct account
map children;
- // Balance totals, by commodity
- totals future;
- totals current;
- totals cleared;
-
account(const std::string& _name, struct account * _parent = NULL)
: name(_name), parent(_parent) {}
- void credit(const entry * ent, const amount * amt) {
- for (account * acct = this; acct; acct = acct->parent) {
- acct->future.credit(amt);
-
- if (difftime(ent->date, std::time(NULL)) < 0)
- acct->current.credit(amt);
-
- if (ent->cleared)
- acct->cleared.credit(amt);
- }
- }
-
operator std::string() const {
if (! parent)
return name;
diff --git a/ledger.texi b/ledger.texi
new file mode 100644
index 00000000..212ca041
--- /dev/null
+++ b/ledger.texi
@@ -0,0 +1,186 @@
+\input texinfo @c -*-texinfo-*-
+@comment $Id: ledger.texi,v 1.1 2003/09/30 00:09:43 johnw Exp $
+@comment %**start of header
+
+@setfilename ledger.info
+
+@settitle Ledger Accouting Tool
+@syncodeindex pg cp
+@comment %**end of header
+
+@dircategory Ledger Accouting Tool
+@direntry
+* ledger: (ledger)The Ledger Accouting Tool.
+@end direntry
+
+@titlepage
+@title Ledger Accouting Tool
+@author John Wiegley <@email{johnw@@newartisans.com}>
+@page
+@vskip 0pt plus 1filll
+@c @insertcopying
+@end titlepage
+
+@contents
+
+@ifnottex
+@node Top
+@top Ledger Accouting Tool
+
+@c @insertcopying
+@end ifnottex
+
+@chapter Introduction
+
+@code{ledger} is an accouting tool that has the chutzpah to exist. It
+provides not one bell or whistle for the money, and returns the user
+back to the days before user interfaces were even a twinkle on their
+father's CRT.
+
+What it does do is provide a double-entry accouting ledger with all of
+the flexibility and muscle of its modern day cousins---without any of
+the fat. Think of it as the bran muffin of accouting tools.
+
+To begin with, you need to start keeping a ledger. This is the basis
+of all accouting, and if you don't know how to do it, now is the time
+to learn. The little booklet that comes with your checkbook is a
+ledger, so we'll describe double-entry accouting in terms of that.
+
+A checkbook ledger records debits (subtractions, or withdrawals) and
+credits (additions, or deposits) with reference to a single account:
+your checking account. Where the money comes from, and where it goes
+to, are simply described in the memo field where you write the person
+or the company's name. The ultimate aim of keeping a checkbook ledger
+is so you know how much money is available to spend at all times.
+That is really the aim of all ledgers.
+
+What computers add is the ability to walk through all of those
+transactions and tell you things about your spending habits; let you
+devise budgets to get control over your spending; squirrel away money
+into virtual savings account without having to physically move the
+money around; etc. As you keep your checkbook ledger, you are
+recording a lot of information about your life and your habits, and
+sometimes that information can tell you things you aren't even aware
+of. That is the aim of all good accouting tools.
+
+The next step up from a checkbook ledger is a ledger that covers all
+of your accounts, not just your checking account. In this ledger, you
+write not only who the money goes to---in the case of a debit---but
+where the money is coming from. In the checkbook ledger, its assumed
+that all of the money is coming from your checking account. But in a
+general ledger, you have to write two-lines: The source and target.
+There must always be a debit from some account for any credit made to
+anyone else. This is what is meant by ``double-entry'' accouting.
+
+For example, let's say you have a checking account and a brokerage
+account, and that you can write checks from both of them. Rather than
+keeping two checkbooks, you decide to use one ledger for both. Once
+you get the hang of this, you'll be ready to use one ledger for all of
+your accouting needs, which gets you to the point of this
+introduction.
+
+So in your general ledger, you need to pay Pacific Bell Telephone for
+your monthly phone bill. The cost is $23.00. In a checkbook ledger,
+you would write out a line that credits your account with Pacific Bell
+by $23 as follows:
+
+@example
+9/29 100 Pacific Bell $23.00 $77.00
+@end example
+
+Very simple: You've written check #100 for $23 to Pacific Bell, which
+leaves your balance in checking at $77.
+
+But in a general ledger, you need to say where the money is coming
+from. A general ledger entry would look like this:
+
+@example
+9/29 100 Pacific Bell $23.00 $223.00
+ Checking $-23.00 $77.00
+@end example
+
+What does all of this mean? The first line shows a credit (or
+payment) to Pacific Bell to the tune of $23.00. Then, because there
+is no one ``balance'' in a general ledger, we've written in the total
+balance of your payments to the account ``Pacific Bell''. This was
+done by looking at the last entry for ``Pacific Bell'' in the general
+ledger, adding $23.00 to that amount, and writing in the total in the
+balance column.
+
+Secondly, the money is coming from your ``Checking'' account, which
+means a debit (or withdrawal) of $23.00, which will leave the ending
+balance in your ``Checking'' account at $77.00.
+
+The transaction itself must balance to $0: $23 goes to Pacific Bell,
+$23 comes from Checking: there is nothing left over to be accounted
+for. The money has in fact moved from one account to another. This
+is basis of double-entry accounting: That money never pops out of
+existence, it is always described as a transaction between
+accounts---as a flow from one place to another.
+
+Keeping a general ledger is the same as keeping two separate ledgers:
+One for Pacific Bell and one for Checking. In that case, each time
+you write a credit into one, you write a corresponding debit into the
+other. This makes it much easier to write in the running balance,
+since you don't have to go looking back for the last time an account
+was referenced, but it also means having a lot of ledger books if you
+deal with multiple accounts.
+
+Enter the beauty of a computerized accouting tool. The purpose of
+@code{ledger} is to make general ledger accouting simple by keeping
+track of the balances for you. Your only job is to enter credit/debit
+pairs and make sure they balance. If a transaction does not balance,
+@code{ledger} will display an error and ignore the
+transaction.@footnote{In some special cases, it will automatically
+balance the entry for you.}
+
+Your usage of @code{ledger} will have two parts: Keeping the ledger,
+and using the @code{ledger} tool to provide you with information
+summaries derived from your ledger's entries.
+
+@chapter Keeping a ledger
+
+The most important part of accounting is keeping a good ledger. If
+you have a good ledger, tools can be written to work whatever
+mathematically tricks you need to better understand your spending
+patterns. Without a good ledger, no tool, however smart, can help
+you.
+
+The @code{ledger} program aims at making ledger entry as simple as
+possible. Since it is a command-line tool, it does not provide a user
+interface for keeping a ledger. If you like, you may use
+@code{gnucash} to maintain your ledger, in which case the
+@code{ledger} program will read @code{gnucash}'s data files directly.
+In that case, read the @code{gnucash} manual now, and skip to the next
+chapter.
+
+If you are not using @code{gnucash}, but a text editor to maintain
+your ledger, read on. @code{ledger} has been designed to make data
+entry as simple as possible, by keeping the ledger format easy, and
+also by automagically determining as much information as possible
+based on the nature of your entries.
+
+For example, you do not need to tell @code{ledger} about the accounts
+you use. Any time @code{ledger} sees a debit or a credit to an
+account it knows nothing about, it will create it. If you use a
+commodity that is new to @code{ledger}, it will create that commodity,
+and determine its display characteristics (placement of the symbol
+before or after the amount, display precision, etc) based on how you
+used the commodity in the transaction.
+
+Here is the Pacific Bell example from above, given as a @code{ledger}
+transaction:
+
+@example
+9/29 (100) Pacific Bell
+ Expenses:Utilities:Telephone $23.00
+ Assets:Checking $-23.00
+@end example
+
+As you can see, it is very similar to what would be written on paper,
+minus the computed balance totals, and adding in account names that
+work better with @code{ledger}'s scheme of things.
+
+@chapter Using @code{ledger}
+
+@bye
diff --git a/main.cc b/main.cc
index 34cea2b0..29fdcaa2 100644
--- a/main.cc
+++ b/main.cc
@@ -1,44 +1,49 @@
#include <fstream>
-#include <vector>
-#include <cassert>
-
-#include <pcre.h> // Perl regular expression library
#include "ledger.h"
+#include <pcre.h> // Perl regular expression library
+
//////////////////////////////////////////////////////////////////////
//
// Command-line parser and top-level logic.
//
namespace ledger {
- extern bool parse_ledger(std::istream& in, std::vector<entry *>& ledger);
- extern bool parse_gnucash(std::istream& in, std::vector<entry *>& ledger);
- extern void report_balances(std::ostream& out, std::vector<entry *>& ledger,
- bool show_children, bool show_empty);
- extern void print_ledger(std::ostream& out, std::vector<entry *>& ledger);
+ extern bool parse_ledger(std::istream& in);
+ extern bool parse_gnucash(std::istream& in);
+
+ extern void report_balances(int argc, char *argv[], std::ostream& out);
+ extern void print_ledger(int argc, char *argv[], std::ostream& out);
}
using namespace ledger;
+void show_help(std::ostream& out)
+{
+ out << "usage: ledger [options] DATA_FILE COMMAND [ARGS]"
+ << std::endl
+ << "options:" << std::endl
+ << " -s show sub-accounts in balance totals" << std::endl
+ << " -S show empty accounts in balance totals" << std::endl
+ << "commands:" << std::endl
+ << " balance show balance totals" << std::endl
+ << " print print all ledger entries" << std::endl;
+}
+
int main(int argc, char *argv[])
{
- // Setup global defaults
+ // Global defaults
commodity_usd = new commodity("$", true, false, 2);
- commodities.insert(commodities_entry("$", commodity_usd));
commodities.insert(commodities_entry("USD", commodity_usd));
// Parse the command-line options
- bool show_children = false;
- bool show_empty = false;
-
int c;
- while (-1 != (c = getopt(argc, argv, "sS"))) {
+ while (-1 != (c = getopt(argc, argv, "+h"))) {
switch (char(c)) {
- case 's': show_children = true; break;
- case 'S': show_empty = true; break;
+ case 'h': show_help(std::cout); break;
}
}
@@ -51,36 +56,28 @@ int main(int argc, char *argv[])
<< "commands:" << std::endl
<< " balance show balance totals" << std::endl
<< " print print all ledger entries" << std::endl;
- std::exit(1);
+ return 1;
}
// Parse the ledger
std::ifstream file(argv[optind++]);
- std::vector<entry *> ledger;
- char buf[256];
- file.get(buf, 255);
+ char buf[32];
+ file.get(buf, 31);
file.seekg(0);
if (std::strncmp(buf, "<?xml version=\"1.0\"?>", 21) == 0)
- parse_gnucash(file, ledger);
+ parse_gnucash(file);
else
- parse_ledger(file, ledger);
-
- // Read the command word
-
- if (optind == argc) {
- std::cerr << "Command word missing" << std::endl;
- return 1;
- }
-
- const std::string command = argv[optind++];
+ parse_ledger(file);
// Process the command
+ const std::string command = argv[optind];
+
if (command == "balance")
- report_balances(std::cout, ledger, show_children, show_empty);
+ report_balances(argc - optind, &argv[optind], std::cout);
else if (command == "print")
- print_ledger(std::cout, ledger);
+ print_ledger(argc - optind, &argv[optind], std::cout);
}
diff --git a/parse.cc b/parse.cc
index 3f18b8f6..5e134dc2 100644
--- a/parse.cc
+++ b/parse.cc
@@ -1,14 +1,11 @@
-#include <iostream>
-#include <vector>
#include <cstring>
#include <ctime>
#include <cctype>
-#include <cassert>
-
-#include <pcre.h> // Perl regular expression library
#include "ledger.h"
+#include <pcre.h> // Perl regular expression library
+
namespace ledger {
//////////////////////////////////////////////////////////////////////
@@ -37,8 +34,7 @@ char * next_element(char * buf, bool variable = false)
static int linenum = 0;
-void finalize_entry(entry * curr, std::vector<entry *>& ledger)
-{
+inline void finalize_entry(entry * curr) {
if (curr) {
if (! curr->validate()) {
std::cerr << "Failed to balance the following transaction, "
@@ -50,7 +46,7 @@ void finalize_entry(entry * curr, std::vector<entry *>& ledger)
}
}
-bool parse_ledger(std::istream& in, std::vector<entry *>& ledger)
+bool parse_ledger(std::istream& in)
{
static std::time_t now = std::time(NULL);
static struct std::tm * now_tm = std::localtime(&now);
@@ -94,7 +90,7 @@ bool parse_ledger(std::istream& in, std::vector<entry *>& ledger)
}
if (curr)
- finalize_entry(curr, ledger);
+ finalize_entry(curr);
curr = new entry;
// Parse the date
@@ -165,11 +161,6 @@ bool parse_ledger(std::istream& in, std::vector<entry *>& ledger)
current = (*i).second;
}
}
-
- // Apply transaction to account (and all parent accounts)
-
- assert(current);
- current->credit(curr, xact->cost);
}
xact->acct = current;
@@ -181,7 +172,7 @@ bool parse_ledger(std::istream& in, std::vector<entry *>& ledger)
}
if (curr)
- finalize_entry(curr, ledger);
+ finalize_entry(curr);
return true;
}