summaryrefslogtreecommitdiff
path: root/ledger.h
diff options
context:
space:
mode:
Diffstat (limited to 'ledger.h')
-rw-r--r--ledger.h595
1 files changed, 334 insertions, 261 deletions
diff --git a/ledger.h b/ledger.h
index 6a98eb62..5aa47c9f 100644
--- a/ledger.h
+++ b/ledger.h
@@ -1,5 +1,5 @@
#ifndef _LEDGER_H
-#define _LEDGER_H "$Revision: 1.34 $"
+#define _LEDGER_H
//////////////////////////////////////////////////////////////////////
//
@@ -7,15 +7,16 @@
//
// A command-line tool for general double-entry accounting.
//
-// Copyright (c) 2003 John Wiegley <johnw@newartisans.com>
+// Copyright (c) 2003,2004 John Wiegley <johnw@newartisans.com>
//
-#include <iostream>
-#include <string>
-#include <vector>
-#include <list>
#include <map>
+#include <list>
+#include <string>
#include <ctime>
+#include <cctype>
+#include <iostream>
+#include <sstream>
#ifdef DEBUG
#include <cassert>
@@ -26,333 +27,405 @@
#define assert(x)
#endif
-#include <pcre.h> // Perl regular expression library
-
namespace ledger {
-class amount;
-class commodity
-{
- commodity(const commodity&);
-
- typedef std::map<const std::time_t, amount *> price_map;
- typedef std::pair<const std::time_t, amount *> price_map_pair;
-
- public:
- std::string name;
- std::string symbol;
+extern const std::string version;
- mutable bool sought;
+class commodity_t;
+class amount_t;
+class transaction_t;
+class entry_t;
+class account_t;
+class ledger_t;
- bool prefix;
- bool separate;
- bool thousands;
- bool european;
- int precision;
+class amount_t
+{
+ typedef void * base_type;
- protected:
- mutable price_map history; // the price history
- mutable amount * conversion; // fixed conversion (ignore history)
+ void _init();
+ void _copy(const amount_t& amt);
+ void _clear();
public:
- explicit commodity() : sought(false), prefix(false), separate(true),
- thousands(false), european(false), conversion(NULL) {}
-
- explicit commodity(const std::string& sym, bool pre = false,
- bool sep = true, bool thou = true,
- bool euro = false, int prec = 2);
- ~commodity();
+ base_type quantity; // amount, to MAX_PRECISION
+ commodity_t * commodity;
- void set_price(amount * price, std::time_t * when = NULL);
- amount * price(std::time_t * when = NULL,
- bool use_history = false, bool download = false) const;
-};
+ static commodity_t * null_commodity;
-typedef std::map<const std::string, commodity *> commodities_map;
-typedef commodities_map::iterator commodities_map_iterator;
-typedef std::pair<const std::string, commodity *> commodities_map_pair;
+ bool valid() const {
+ if (quantity)
+ return commodity != NULL;
+ else
+ return commodity == NULL;
+ }
+ // constructors
+ amount_t(commodity_t * _commodity = NULL)
+ : quantity(NULL), commodity(_commodity) {}
-class amount
-{
- public:
- virtual ~amount() {}
+ amount_t(const amount_t& amt) : quantity(NULL) {
+ if (amt.quantity)
+ _copy(amt);
+ else
+ commodity = amt.commodity;
+ }
+ amount_t(const std::string& value) {
+ _init();
+ std::istringstream str(value);
+ str >> *this;
+ }
+ amount_t(const int value) : quantity(NULL), commodity(NULL) {
+ if (value != 0) {
+ std::string str;
+ std::ostringstream strstr(str);
+ strstr << value;
+ parse(strstr.str());
+ }
+ }
+ amount_t(const unsigned int value) : quantity(NULL), commodity(NULL) {
+ if (value != 0) {
+ std::string str;
+ std::ostringstream strstr(str);
+ strstr << value;
+ parse(strstr.str());
+ }
+ }
+ amount_t(const double value) : quantity(NULL), commodity(NULL) {
+ if (value != 0.0) {
+ std::string str;
+ std::ostringstream strstr(str);
+ strstr << value;
+ parse(strstr.str());
+ }
+ }
- virtual commodity * commdty() const = 0;
- virtual void set_commdty(commodity *) = 0;
+ // destructor
+ ~amount_t() {
+ if (quantity)
+ _clear();
+ }
- virtual amount * copy() const = 0;
- virtual amount * value(const amount * pr = NULL) const = 0;
- virtual void set_value(const amount * pr) = 0;
- virtual amount * street(std::time_t * when = NULL,
- bool use_history = false,
- bool download = false) const = 0;
+ // assignment operator
+ amount_t& operator=(const amount_t& amt);
+ amount_t& operator=(const std::string& value);
+ amount_t& operator=(const int value);
+ amount_t& operator=(const unsigned int value);
+ amount_t& operator=(const double value);
+
+ // general methods
+ amount_t round(int precision = -1) const;
+
+ // in-place arithmetic
+ amount_t& operator*=(const amount_t& amt);
+ amount_t& operator/=(const amount_t& amt);
+ amount_t& operator%=(const amount_t& amt);
+ amount_t& operator+=(const amount_t& amt);
+ amount_t& operator-=(const amount_t& amt);
+
+ // simple arithmetic
+ amount_t operator*(const amount_t& amt) const {
+ amount_t temp = *this;
+ temp *= amt;
+ return temp;
+ }
+ amount_t operator/(const amount_t& amt) const {
+ amount_t temp = *this;
+ temp /= amt;
+ return temp;
+ }
+ amount_t operator%(const amount_t& amt) const {
+ amount_t temp = *this;
+ temp %= amt;
+ return temp;
+ }
+ amount_t operator+(const amount_t& amt) const {
+ amount_t temp = *this;
+ temp += amt;
+ return temp;
+ }
+ amount_t operator-(const amount_t& amt) const {
+ amount_t temp = *this;
+ temp -= amt;
+ return temp;
+ }
- virtual bool has_price() const = 0;
- virtual amount * per_item_price() const = 0;
+ // unary negation
+ amount_t& negate();
+ amount_t negated() const {
+ amount_t temp = *this;
+ temp.negate();
+ return temp;
+ }
+ amount_t operator-() const {
+ return negated();
+ }
- // Comparison
+ // test for non-zero (use ! for zero)
+ operator bool() const;
+
+ // comparisons to zero
+ bool operator<(const int num) const;
+ bool operator<=(const int num) const;
+ bool operator>(const int num) const;
+ bool operator>=(const int num) const;
+
+ // comparisons between amounts
+ bool operator<(const amount_t& amt) const;
+ bool operator<=(const amount_t& amt) const;
+ bool operator>(const amount_t& amt) const;
+ bool operator>=(const amount_t& amt) const;
+ bool operator==(const amount_t& amt) const;
+ bool operator!=(const amount_t& amt) const {
+ if (commodity != amt.commodity)
+ return true;
+ return ! (*this == amt);
+ }
- virtual bool is_zero() const = 0;
- virtual bool is_negative() const = 0;
- virtual int compare(const amount * other) const = 0;
+ amount_t value(const std::time_t moment) const;
- // Assignment
+ operator std::string() const;
- virtual void credit(const amount * other) = 0;
- virtual void negate() = 0;
+ void parse(std::istream& in, ledger_t * ledger = NULL);
+ void parse(const std::string& str, ledger_t * ledger = NULL) {
+ std::istringstream stream(str);
+ parse(stream, ledger);
+ }
- // String conversion routines
+ void write_quantity(std::ostream& out) const;
+ void read_quantity(std::istream& in);
- virtual void parse(const std::string& num) = 0;
- virtual const std::string as_str(bool full_prec = false) const = 0;
+ friend std::istream& operator>>(std::istream& in, amount_t& amt);
};
-extern amount * create_amount(const std::string& value,
- const amount * cost = NULL);
+void parse_quantity(std::istream& in, std::string& value);
+void parse_commodity(std::istream& in, std::string& symbol);
-class mask
-{
- public:
- bool exclude;
- std::string pattern;
- pcre * regexp;
+inline amount_t abs(const amount_t& amt) {
+ return amt < 0 ? amt.negated() : amt;
+}
- explicit mask(const std::string& pattern);
+inline std::istream& operator>>(std::istream& in, amount_t& amt) {
+ amt.parse(in);
+ return in;
+}
- mask(const mask&);
+std::ostream& operator<<(std::ostream& out, const amount_t& amt);
- ~mask() {
- pcre_free(regexp);
- }
- bool match(const std::string& str) const;
-};
+#define COMMODITY_STYLE_DEFAULTS 0x00
+#define COMMODITY_STYLE_SUFFIXED 0x01
+#define COMMODITY_STYLE_SEPARATED 0x02
+#define COMMODITY_STYLE_EUROPEAN 0x04
+#define COMMODITY_STYLE_THOUSANDS 0x08
+#define COMMODITY_STYLE_CONSULTED 0x10
+#define COMMODITY_STYLE_NOMARKET 0x20
-typedef std::list<mask> regexps_list;
-typedef std::list<mask>::iterator regexps_list_iterator;
-typedef std::list<mask>::const_iterator regexps_list_const_iterator;
+typedef std::map<const std::time_t, amount_t> history_map;
+typedef std::pair<const std::time_t, amount_t> history_pair;
-class account;
-class transaction
+class commodity_t
{
- transaction(const transaction&);
-
public:
- account * acct;
- amount * cost;
-
- std::string note;
-
- bool is_virtual;
- bool must_balance;
- bool specified;
-
- explicit transaction(account * _acct = NULL, amount * _cost = NULL)
- : acct(_acct), cost(_cost),
- is_virtual(false), must_balance(true), specified(false) {}
-
- ~transaction() {
- if (cost)
- delete cost;
+ std::string symbol;
+ std::string name;
+ std::string note;
+ unsigned int precision;
+ unsigned int flags;
+ history_map history;
+ amount_t conversion;
+ unsigned long ident;
+
+ static void (*updater)(commodity_t * commodity,
+ const std::time_t date,
+ const amount_t& price,
+ const std::time_t moment);
+
+ commodity_t(const std::string& _symbol = "",
+ unsigned int _precision = 2,
+ unsigned int _flags = COMMODITY_STYLE_DEFAULTS)
+ : symbol(_symbol), precision(_precision), flags(_flags) {}
+
+ void add_price(const std::time_t date, const amount_t& price) {
+ history.insert(history_pair(date, price));
+ }
+ bool remove_price(const std::time_t date) {
+ history_map::size_type n = history.erase(date);
+ return n > 0;
}
- const std::string acct_as_str() const;
+ void set_conversion(const amount_t& price) {
+ conversion = price;
+ }
- void print(std::ostream& out, bool display_quantity = true,
- bool display_price = true) const;
+ amount_t value(const std::time_t moment = std::time(NULL));
};
-class book;
-class entry
-{
- entry(const entry&);
+#define TRANSACTION_NORMAL 0x0
+#define TRANSACTION_VIRTUAL 0x1
+#define TRANSACTION_BALANCE 0x2
+class transaction_t
+{
public:
- book * ledger;
-
- std::time_t date;
- std::string code;
- std::string desc;
-
- bool cleared;
-
- std::list<transaction *> xacts;
+ entry_t * entry;
+ account_t * account;
+ amount_t amount;
+ amount_t cost;
+ unsigned int flags;
+ std::string note;
+
+ transaction_t(entry_t * _entry, account_t * _account)
+ : entry(_entry), account(_account), flags(TRANSACTION_NORMAL) {}
+
+ transaction_t(entry_t * _entry,
+ account_t * _account,
+ const amount_t& _amount,
+ const amount_t& _cost,
+ unsigned int _flags = TRANSACTION_NORMAL,
+ const std::string& _note = "")
+ : entry(_entry), account(_account), amount(_amount),
+ cost(_cost), flags(_flags), note(_note) {}
+};
- explicit entry(book * l) : ledger(l), cleared(false) {}
- // 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.
+typedef std::list<transaction_t *> transactions_list;
- ~entry() {
- for (std::list<transaction *>::iterator i = xacts.begin();
- i != xacts.end();
- i++) {
+class entry_t
+{
+ public:
+ enum entry_state_t {
+ UNCLEARED, CLEARED, PENDING
+ };
+
+ std::time_t date;
+ enum entry_state_t state;
+ std::string code;
+ std::string payee;
+ transactions_list transactions;
+
+ ~entry_t() {
+ for (transactions_list::iterator i = transactions.begin();
+ i != transactions.end();
+ i++)
delete *i;
- }
}
- bool matches(const regexps_list& regexps) const;
- bool validate(bool show_unaccounted = false) const;
- bool finalize(bool do_compute = false);
-
- void print(std::ostream& out, bool shortcut = true) const;
-};
-
-struct cmp_entry_date {
- bool operator()(const entry * left, const entry * right) {
- return std::difftime(left->date, right->date) < 0;
+ void add_transaction(transaction_t * xact) {
+ transactions.push_back(xact);
+ }
+ bool remove_transaction(transaction_t * xact) {
+ transactions.remove(xact);
+ return true;
}
};
-typedef std::vector<entry *> entries_list;
-typedef entries_list::iterator entries_list_iterator;
-typedef entries_list::reverse_iterator entries_list_reverse_iterator;
-typedef entries_list::const_iterator entries_list_const_iterator;
+typedef std::map<const std::string, account_t *> accounts_map;
+typedef std::pair<const std::string, account_t *> accounts_pair;
-class totals
-{
- totals(const totals&);
+inline std::ostream& operator<<(std::ostream& out, const account_t& acct);
+class account_t
+{
public:
- typedef std::map<commodity *, amount *> map;
- typedef map::iterator iterator;
- typedef map::const_iterator const_iterator;
- typedef std::pair<commodity *, amount *> pair;
-
- map amounts;
-
- totals() {}
- ~totals();
-
- void credit(const amount * val);
- void credit(const totals& other);
+ const account_t * parent;
+ std::string name;
+ std::string note;
+ accounts_map accounts;
+ mutable accounts_map accounts_cache;
+ transactions_list transactions;
+ unsigned long ident;
+ static unsigned long next_ident;
+
+ account_t(const account_t * _parent, const std::string& _name = "",
+ const std::string& _note = "")
+ : parent(_parent), name(_name), note(_note) {}
+
+ ~account_t();
+
+ std::string fullname() const;
+
+ void add_account(account_t * acct) {
+ acct->ident = next_ident++;
+ accounts.insert(accounts_pair(acct->name, acct));
+ }
+ bool remove_account(account_t * acct) {
+ accounts_map::size_type n = accounts.erase(acct->name);
+ return n > 0;
+ }
- void negate();
+ account_t * find_account(const std::string& name, bool auto_create = true);
- bool is_zero() const;
- bool is_negative() const;
+ operator std::string() const {
+ return fullname();
+ }
- void print(std::ostream& out, int width) const;
+ // These functions should only be called from ledger_t::add_entry
+ // and ledger_t::remove_entry; or from the various parsers.
+ void add_transaction(transaction_t * xact) {
+ transactions.push_back(xact);
+ }
+ bool remove_transaction(transaction_t * xact);
- totals * value() const;
- totals * street(std::time_t * when = NULL,
- bool use_history = false,
- bool download = false) const;
+ friend class ledger_t;
};
+inline std::ostream& operator<<(std::ostream& out, const account_t& acct) {
+ out << acct.fullname();
+ return out;
+}
-typedef std::map<const std::string, account *> accounts_map;
-typedef accounts_map::iterator accounts_map_iterator;
-typedef std::pair<const std::string, account *> accounts_map_pair;
-
-class account
-{
- account(const account&);
-
- public:
- account * parent;
-
- std::string name;
-#ifdef READ_GNUCASH
- commodity * comm; // default commodity for this account
-#endif
- totals balance; // optional, parse-time computed balance
- int checked; // 'balance' uses this for speed's sake
- accounts_map children;
-
- mutable std::string full_name;
-
- explicit account() : parent(NULL), checked(0) {}
-
- explicit account(const std::string& _name,
- struct account * _parent = NULL)
- : parent(_parent), name(_name), checked(0) {}
-
- ~account();
- const std::string as_str(const account * stop = NULL) const;
-};
+typedef std::map<const std::string, commodity_t *> commodities_map;
+typedef std::pair<const std::string, commodity_t *> commodities_pair;
+typedef std::list<entry_t *> entries_list;
-class book
+class ledger_t
{
- book(const book&);
-
public:
- typedef std::map<regexps_list *,
- std::list<transaction *> *> virtual_map;
-
- typedef std::pair<regexps_list *,
- std::list<transaction *> *> virtual_map_pair;
-
- typedef virtual_map::const_iterator virtual_map_iterator;
-
- commodities_map commodities;
- accounts_map accounts;
- accounts_map accounts_cache; // maps full names to accounts
- virtual_map virtual_mapping;
- entries_list entries;
- int current_year;
-
- book() {}
- ~book();
-
- template<typename Compare>
- void sort(Compare comp) {
- std::sort(entries.begin(), entries.end(), comp);
+ account_t * master;
+ commodities_map commodities;
+ entries_list entries;
+ std::list<std::string> sources;
+
+ ledger_t() {
+ master = new account_t(NULL, "");
+ master->ident = 0;
+ account_t::next_ident = 1;
}
- void print(std::ostream& out, regexps_list& regexps,
- bool shortcut) const;
- account * re_find_account(const std::string& regex);
- account * find_account(const std::string& name, bool create = true);
-};
-
-extern book * main_ledger;
+ ~ledger_t();
-inline commodity::commodity(const std::string& sym, bool pre, bool sep,
- bool thou, bool euro, int prec)
- : symbol(sym), sought(false), prefix(pre), separate(sep),
- thousands(thou), european(euro), precision(prec), conversion(NULL) {
-#ifdef DEBUG
- std::pair<commodities_map_iterator, bool> result =
-#endif
- main_ledger->commodities.insert(commodities_map_pair(sym, this));
-#ifdef DEBUG
- assert(result.second);
-#endif
-}
-
-// Parsing routines
+ void add_account(account_t * acct) {
+ master->add_account(acct);
+ }
+ bool remove_account(account_t * acct) {
+ return master->remove_account(acct);
+ }
-extern int parse_ledger(book * ledger, std::istream& in,
- regexps_list& regexps,
- bool compute_balances = false,
- const char * acct_prefix = NULL);
-#ifdef READ_GNUCASH
-extern book * parse_gnucash(std::istream& in, bool compute_balances = false);
-#endif
+ account_t * find_account(const std::string& name, bool auto_create = true) {
+ return master->find_account(name, auto_create);
+ }
-extern int parse_ledger_file(book * ledger, const std::string& file,
- regexps_list& regexps,
- bool compute_balances = false,
- const char * acct_prefix = NULL);
+ void add_commodity(commodity_t * commodity, const std::string symbol = "") {
+ commodities.insert(commodities_pair(symbol.empty() ?
+ commodity->symbol : symbol, commodity));
+ }
+ bool remove_commodity(commodity_t * commodity) {
+ commodities_map::size_type n = commodities.erase(commodity->symbol);
+ return n > 0;
+ }
-extern bool parse_date_mask(const char * date_str,
- struct std::tm * result);
-extern bool parse_date(const char * date_str, std::time_t * result,
- const int year = -1);
+ commodity_t * find_commodity(const std::string& symbol,
+ bool auto_create = false);
-extern void record_regexp(const std::string& pattern, regexps_list& regexps);
-extern void read_regexps(const std::string& path, regexps_list& regexps);
-extern bool matches(const regexps_list& regexps, const std::string& str,
- bool * by_exclusion = NULL);
+ bool add_entry(entry_t * entry);
+ bool remove_entry(entry_t * entry);
+};
-extern void parse_price_setting(const std::string& setting);
+int parse_ledger_file(char * p, ledger_t * book);
} // namespace ledger