*** empty log message ***

This commit is contained in:
John Wiegley 2003-10-05 00:22:17 +00:00
parent 2c10922614
commit 260217c8ab
9 changed files with 164 additions and 142 deletions

View file

@ -45,17 +45,13 @@ class gmp_amount : public amount
mpz_clear(quantity); mpz_clear(quantity);
} }
virtual commodity * comm() const { virtual commodity * commdty() const {
return quantity_comm; return quantity_comm;
} }
virtual const std::string& comm_symbol() const {
assert(quantity_comm);
return quantity_comm->symbol;
}
virtual amount * copy() const; virtual amount * copy() const;
virtual amount * value(amount *) const; virtual amount * value(amount *) const;
virtual amount * street() const; virtual amount * street(bool get_quotes) const;
virtual bool has_price() const { virtual bool has_price() const {
return priced; return priced;
} }
@ -199,54 +195,54 @@ amount * gmp_amount::value(amount * pr) const
} }
} }
amount * gmp_amount::street() const static bool get_commodity_price(commodity * comm)
{ {
amount * cost = NULL; using namespace std;
const amount * amt = this;
extern bool get_quotes; char buf[256];
buf[0] = '\0';
for (int cycles = 0; cycles < 10; cycles++) { if (FILE * fp = popen((std::string("getquote ") +
totals::iterator pi = comm->symbol).c_str(), "r")) {
main_ledger->prices.amounts.find(amt->comm_symbol()); if (feof(fp) || ! fgets(buf , 255, fp)) {
if (pi == main_ledger->prices.amounts.end()) { fclose(fp);
using namespace std; return false;
}
fclose(fp);
}
if (! get_quotes) if (buf[0]) {
break; char * p = strchr(buf, '\n');
if (p) *p = '\0';
char buf[256]; comm->price = create_amount(buf);
buf[0] = '\0'; return true;
}
return false;
}
if (FILE * fp = popen((std::string("getquote ") + amount * gmp_amount::street(bool get_quotes) const
amt->comm_symbol()).c_str(), "r")) { {
if (feof(fp) || ! fgets(buf , 255, fp)) { amount * amt = copy();
fclose(fp);
break;
}
fclose(fp);
}
if (buf[0]) { int max = 10;
char * p = strchr(buf, '\n');
if (p) *p = '\0';
main_ledger->record_price((amt->comm_symbol() + "=" + buf).c_str()); while (--max >= 0) {
continue; if (! amt->commdty()->price) {
} if (get_quotes)
break; get_commodity_price(amt->commdty());
} else { if (! amt->commdty()->price)
amount * temp = cost;
amt = cost = amt->value((*pi).second);
bool same = temp && temp->comm() == cost->comm();
if (temp)
delete temp;
if (same)
break; break;
} }
amount * old = amt;
amt = amt->value(amt->commdty()->price);
delete old;
if (amt->commdty() == old->commdty())
break;
} }
return cost ? cost : copy(); return amt;
} }
void gmp_amount::set_value(const amount * val) void gmp_amount::set_value(const amount * val)
@ -551,7 +547,6 @@ static commodity * parse_amount(mpz_t out, const char * num,
} }
commodity * comm = NULL; commodity * comm = NULL;
if (saw_commodity) { if (saw_commodity) {
commodities_map_iterator item = commodities_map_iterator item =
main_ledger->commodities.find(symbol.c_str()); main_ledger->commodities.find(symbol.c_str());

View file

@ -10,6 +10,7 @@ extern bool show_children;
extern bool show_empty; extern bool show_empty;
extern bool show_subtotals; extern bool show_subtotals;
extern bool full_names; extern bool full_names;
extern bool get_quotes;
extern std::time_t begin_date; extern std::time_t begin_date;
extern bool have_beginning; extern bool have_beginning;
@ -100,7 +101,7 @@ void report_balances(std::ostream& out, regexps_map& regexps)
} }
if (acct->checked == 1) if (acct->checked == 1)
acct->balance.credit((*x)->cost->street()); acct->balance.credit((*x)->cost->street(get_quotes));
} }
} }
} }

View file

@ -2,6 +2,8 @@
namespace ledger { namespace ledger {
extern bool get_quotes;
static void equity_entry(account * acct, regexps_map& regexps, static void equity_entry(account * acct, regexps_map& regexps,
std::ostream& out) std::ostream& out)
{ {
@ -23,12 +25,12 @@ static void equity_entry(account * acct, regexps_map& regexps,
xact = new transaction(); xact = new transaction();
xact->acct = const_cast<account *>(acct); xact->acct = const_cast<account *>(acct);
xact->cost = (*i).second->street(); xact->cost = (*i).second->street(get_quotes);
opening.xacts.push_back(xact); opening.xacts.push_back(xact);
xact = new transaction(); xact = new transaction();
xact->acct = main_ledger->find_account("Equity:Opening Balances"); xact->acct = main_ledger->find_account("Equity:Opening Balances");
xact->cost = (*i).second->street(); xact->cost = (*i).second->street(get_quotes);
xact->cost->negate(); xact->cost->negate();
opening.xacts.push_back(xact); opening.xacts.push_back(xact);
} }

View file

@ -18,6 +18,7 @@ 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 accounts_map accounts_by_id; static accounts_map accounts_by_id;
static bool do_compute;
static enum { static enum {
NO_ACTION, NO_ACTION,
@ -197,7 +198,7 @@ static void dataHandler(void *userData, const char *s, int len)
std::string value = curr_quant + " " + xact->acct->comm->symbol; std::string value = curr_quant + " " + xact->acct->comm->symbol;
if (curr_value->comm() == xact->acct->comm) { if (curr_value->commdty() == xact->acct->comm) {
// assert: value must be equal to curr_value. // assert: value must be equal to curr_value.
delete curr_value; delete curr_value;
curr_value = NULL; curr_value = NULL;
@ -207,7 +208,7 @@ static void dataHandler(void *userData, const char *s, int len)
if (curr_value) if (curr_value)
delete curr_value; delete curr_value;
if (main_ledger->compute_balances) if (do_compute)
xact->acct->balance.credit(xact->cost); xact->acct->balance.credit(xact->cost);
break; break;
} }
@ -227,15 +228,14 @@ static void dataHandler(void *userData, const char *s, int len)
} }
} }
state * parse_gnucash(std::istream& in, bool compute_balances) book * parse_gnucash(std::istream& in, bool compute_balances)
{ {
char buf[BUFSIZ]; char buf[BUFSIZ];
state * ledger = new state; book * ledger = new book;
main_ledger = ledger;
ledger->compute_balances = compute_balances;
main_ledger = ledger;
do_compute = compute_balances;
action = NO_ACTION; action = NO_ACTION;
curr_account = NULL; curr_account = NULL;
curr_entry = NULL; curr_entry = NULL;

View file

@ -5,7 +5,7 @@
namespace ledger { namespace ledger {
bool use_warnings = false; bool use_warnings = false;
state * main_ledger; book * main_ledger;
const std::string transaction::acct_as_str() const const std::string transaction::acct_as_str() const
{ {
@ -67,9 +67,9 @@ void entry::print(std::ostream& out, bool shortcut) const
out << std::endl; out << std::endl;
if (shortcut && if (shortcut && (xacts.size() != 2 ||
(xacts.size() != 2 || (xacts.front()->cost->commdty() !=
xacts.front()->cost->comm() != xacts.back()->cost->comm())) xacts.back()->cost->commdty())))
shortcut = false; shortcut = false;
for (std::list<transaction *>::const_iterator x = xacts.begin(); for (std::list<transaction *>::const_iterator x = xacts.begin();
@ -172,7 +172,7 @@ void totals::print(std::ostream& out, int width) const
// Print out the entire ledger that was read in, sorted by date. // Print out the entire ledger that was read in, sorted by date.
// This can be used to "wash" ugly ledger files. // This can be used to "wash" ugly ledger files.
void state::print(std::ostream& out, regexps_map& regexps, void book::print(std::ostream& out, regexps_map& regexps,
bool shortcut) const bool shortcut) const
{ {
for (entries_list_const_iterator i = entries.begin(); for (entries_list_const_iterator i = entries.begin();
@ -251,7 +251,7 @@ bool matches(const regexps_map& regexps, const std::string& str,
return match; return match;
} }
state::~state() book::~book()
{ {
for (commodities_map_iterator i = commodities.begin(); for (commodities_map_iterator i = commodities.begin();
i != commodities.end(); i != commodities.end();
@ -269,24 +269,7 @@ state::~state()
delete *i; delete *i;
} }
void state::record_price(const std::string& setting) account * book::find_account(const std::string& name, bool create)
{
char buf[128];
std::strcpy(buf, setting.c_str());
assert(setting.length() < 128);
char * c = buf;
char * p = std::strchr(buf, '=');
if (! p) {
std::cerr << "Warning: Invalid price setting: " << setting << std::endl;
} else {
*p++ = '\0';
prices.amounts.insert(totals::pair(c, create_amount(p)));
}
}
account * state::find_account(const std::string& name, bool create)
{ {
accounts_map_iterator i = accounts_cache.find(name); accounts_map_iterator i = accounts_cache.find(name);
if (i != accounts_cache.end()) if (i != accounts_cache.end())

View file

@ -1,5 +1,5 @@
#ifndef _LEDGER_H #ifndef _LEDGER_H
#define _LEDGER_H "$Revision: 1.19 $" #define _LEDGER_H "$Revision: 1.20 $"
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// //
@ -26,19 +26,22 @@
namespace ledger { namespace ledger {
struct amount;
struct commodity struct commodity
{ {
std::string name; std::string name;
std::string symbol; std::string symbol;
bool prefix; mutable amount * price; // the current price
bool separate;
bool thousands;
bool european;
int precision; bool prefix;
bool separate;
bool thousands;
bool european;
commodity() : prefix(false), separate(true), int precision;
commodity() : price(NULL), prefix(false), separate(true),
thousands(false), european(false) {} thousands(false), european(false) {}
commodity(const std::string& sym, bool pre = false, bool sep = true, commodity(const std::string& sym, bool pre = false, bool sep = true,
bool thou = true, bool euro = false, int prec = 2); bool thou = true, bool euro = false, int prec = 2);
@ -54,11 +57,10 @@ class amount
public: public:
virtual ~amount() {} virtual ~amount() {}
virtual commodity * comm() const = 0; virtual commodity * commdty() const = 0;
virtual const std::string& comm_symbol() const = 0;
virtual amount * copy() const = 0; virtual amount * copy() const = 0;
virtual amount * value(amount * pr = NULL) const = 0; virtual amount * value(amount * pr = NULL) const = 0;
virtual amount * street() const = 0; virtual amount * street(bool get_quotes) const = 0;
virtual bool has_price() const = 0; virtual bool has_price() const = 0;
virtual void set_value(const amount * pr) = 0; virtual void set_value(const amount * pr) = 0;
@ -88,6 +90,10 @@ struct mask
pcre * regexp; pcre * regexp;
mask(const std::string& pattern); mask(const std::string& pattern);
~mask() {
pcre_free(regexp);
}
}; };
typedef std::list<mask> regexps_map; typedef std::list<mask> regexps_map;
@ -169,10 +175,10 @@ typedef entries_list::const_iterator entries_list_const_iterator;
struct totals struct totals
{ {
typedef std::map<const std::string, amount *> map; typedef std::map<commodity *, amount *> map;
typedef map::iterator iterator; typedef map::iterator iterator;
typedef map::const_iterator const_iterator; typedef map::const_iterator const_iterator;
typedef std::pair<const std::string, amount *> pair; typedef std::pair<commodity *, amount *> pair;
map amounts; map amounts;
@ -180,9 +186,9 @@ struct totals
void credit(const amount * val) { void credit(const amount * val) {
std::pair<iterator, bool> result = std::pair<iterator, bool> result =
amounts.insert(pair(val->comm_symbol(), val->copy())); amounts.insert(pair(val->commdty(), val->copy()));
if (! result.second) if (! result.second)
amounts[val->comm_symbol()]->credit(val); amounts[val->commdty()]->credit(val);
} }
void credit(const totals& other); void credit(const totals& other);
@ -191,7 +197,7 @@ struct totals
void print(std::ostream& out, int width) const; void print(std::ostream& out, int width) const;
// Returns an allocated entity // Returns an allocated entity
amount * sum(const std::string& comm) { amount * sum(commodity * comm) {
return amounts[comm]; return amounts[comm];
} }
}; };
@ -231,13 +237,12 @@ struct account
}; };
struct state struct book
{ {
commodities_map commodities; commodities_map commodities;
accounts_map accounts; accounts_map accounts;
accounts_map accounts_cache; // maps full names to accounts accounts_map accounts_cache; // maps full names to accounts
entries_list entries; entries_list entries;
totals prices;
int current_year; int current_year;
typedef std::map<std::list<mask> *, typedef std::map<std::list<mask> *,
@ -248,12 +253,9 @@ struct state
typedef virtual_map::const_iterator virtual_map_iterator; typedef virtual_map::const_iterator virtual_map_iterator;
bool compute_balances;
virtual_map virtual_mapping; virtual_map virtual_mapping;
~state(); ~book();
void record_price(const std::string& setting);
template<typename Compare> template<typename Compare>
void sort(Compare comp) { void sort(Compare comp) {
@ -264,12 +266,12 @@ struct state
account * find_account(const std::string& name, bool create = true); account * find_account(const std::string& name, bool create = true);
}; };
extern state * main_ledger; extern book * main_ledger;
extern bool use_warnings; extern bool use_warnings;
inline commodity::commodity(const std::string& sym, bool pre, bool sep, inline commodity::commodity(const std::string& sym, bool pre, bool sep,
bool thou, bool euro, int prec) bool thou, bool euro, int prec)
: symbol(sym), prefix(pre), separate(sep), : symbol(sym), price(NULL), prefix(pre), separate(sep),
thousands(thou), european(euro), precision(prec) { thousands(thou), european(euro), precision(prec) {
std::pair<commodities_map_iterator, bool> result = std::pair<commodities_map_iterator, bool> result =
main_ledger->commodities.insert(commodities_map_pair(sym, this)); main_ledger->commodities.insert(commodities_map_pair(sym, this));

41
main.cc
View file

@ -5,14 +5,15 @@
#include <fstream> #include <fstream>
namespace ledger { namespace ledger {
extern state * parse_ledger(std::istream& in, regexps_map& regexps, extern book * parse_ledger(std::istream& in, regexps_map& regexps,
bool compute_balances); bool compute_balances);
#ifdef READ_GNUCASH #ifdef READ_GNUCASH
extern state * parse_gnucash(std::istream& in, bool compute_balances); extern book * parse_gnucash(std::istream& in, bool compute_balances);
#endif #endif
extern bool parse_date(const char * date_str, std::time_t * result, extern bool parse_date(const char * date_str, std::time_t * result,
const int year = -1); const int year = -1);
extern void parse_price_setting(const std::string& setting);
extern void report_balances(std::ostream& out, regexps_map& regexps); extern void report_balances(std::ostream& out, regexps_map& regexps);
extern void print_register(const std::string& acct_name, std::ostream& out, extern void print_register(const std::string& acct_name, std::ostream& out,
@ -80,6 +81,7 @@ static void show_help(std::ostream& out)
int main(int argc, char * argv[]) int main(int argc, char * argv[])
{ {
std::istream * file = NULL; std::istream * file = NULL;
std::string prices;
regexps_map regexps; regexps_map regexps;
@ -189,18 +191,7 @@ int main(int argc, char * argv[])
// -p "COMMODITY=PRICE" // -p "COMMODITY=PRICE"
// -p path-to-price-database // -p path-to-price-database
case 'p': case 'p':
if (access(optarg, R_OK) != -1) { prices = optarg;
std::ifstream pricedb(optarg);
while (! pricedb.eof()) {
char buf[80];
pricedb.getline(buf, 79);
if (*buf && ! std::isspace(*buf))
main_ledger->record_price(buf);
}
} else {
main_ledger->record_price(optarg);
}
break; break;
case 'P': case 'P':
@ -297,6 +288,22 @@ int main(int argc, char * argv[])
for (; optind < argc; optind++) for (; optind < argc; optind++)
regexps.push_back(mask(argv[optind])); regexps.push_back(mask(argv[optind]));
// Record any prices specified by the user
if (! prices.empty()) {
if (access(prices.c_str(), R_OK) != -1) {
std::ifstream pricedb(prices.c_str());
while (! pricedb.eof()) {
char buf[80];
pricedb.getline(buf, 79);
if (*buf && ! std::isspace(*buf))
parse_price_setting(buf);
}
} else {
parse_price_setting(prices);
}
}
// Process the command // Process the command
if (command == "balance") { if (command == "balance") {
@ -313,8 +320,10 @@ int main(int argc, char * argv[])
equity_ledger(std::cout, regexps); equity_ledger(std::cout, regexps);
} }
#if 0 #ifdef DEBUG
// Deleting the main ledger just isn't necessary at this point. // Ordinarily, deleting the main ledger just isn't necessary at
// this point.
delete main_ledger; delete main_ledger;
#endif #endif
} }

View file

@ -84,11 +84,40 @@ bool parse_date(const char * date_str, std::time_t * result,
return false; return false;
} }
void parse_price_setting(const std::string& setting)
{
char buf[128];
std::strcpy(buf, setting.c_str());
assert(setting.length() < 128);
char * c = buf;
char * p = std::strchr(buf, '=');
if (! p) {
std::cerr << "Warning: Invalid price setting: " << setting << std::endl;
} else {
*p++ = '\0';
commodity * comm = NULL;
commodities_map_iterator item = main_ledger->commodities.find(c);
if (item == main_ledger->commodities.end()) {
comm = new commodity(c);
} else {
comm = (*item).second;
}
assert(comm);
comm->price = create_amount(p);
}
}
#define MAX_LINE 1024 #define MAX_LINE 1024
static int linenum; static int linenum;
static bool do_compute;
transaction * parse_transaction(std::istream& in, state * ledger) transaction * parse_transaction(std::istream& in, book * ledger)
{ {
transaction * xact = new transaction(); transaction * xact = new transaction();
@ -148,13 +177,13 @@ transaction * parse_transaction(std::istream& in, state * ledger)
xact->acct = ledger->find_account(p); xact->acct = ledger->find_account(p);
if (ledger->compute_balances && xact->cost) if (do_compute && xact->cost)
xact->acct->balance.credit(xact->cost); xact->acct->balance.credit(xact->cost);
return xact; return xact;
} }
entry * parse_entry(std::istream& in, state * ledger) entry * parse_entry(std::istream& in, book * ledger)
{ {
entry * curr = new entry; entry * curr = new entry;
@ -233,12 +262,12 @@ entry * parse_entry(std::istream& in, state * ledger)
continue; continue;
if (! (*x)->cost->has_price() && if (! (*x)->cost->has_price() &&
! (*x)->cost->comm()->prefix && ! (*x)->cost->commdty()->prefix &&
(*x)->cost->comm()->separate) { (*x)->cost->commdty()->separate) {
for (totals::iterator i = balance.amounts.begin(); for (totals::iterator i = balance.amounts.begin();
i != balance.amounts.end(); i != balance.amounts.end();
i++) { i++) {
if ((*i).second->comm() != (*x)->cost->comm()) { if ((*i).second->commdty() != (*x)->cost->commdty()) {
(*x)->cost->set_value((*i).second); (*x)->cost->set_value((*i).second);
break; break;
} }
@ -276,7 +305,7 @@ entry * parse_entry(std::istream& in, state * ledger)
(*x)->cost = (*i).second->value(); (*x)->cost = (*i).second->value();
(*x)->cost->negate(); (*x)->cost->negate();
if (ledger->compute_balances) if (do_compute)
(*x)->acct->balance.credit((*x)->cost); (*x)->acct->balance.credit((*x)->cost);
} }
@ -284,7 +313,7 @@ entry * parse_entry(std::istream& in, state * ledger)
// transactions and create new virtual transactions for all that // transactions and create new virtual transactions for all that
// apply. // apply.
for (state::virtual_map_iterator m = ledger->virtual_mapping.begin(); for (book::virtual_map_iterator m = ledger->virtual_mapping.begin();
m != ledger->virtual_mapping.end(); m != ledger->virtual_mapping.end();
m++) { m++) {
std::list<transaction *> xacts; std::list<transaction *> xacts;
@ -301,7 +330,7 @@ entry * parse_entry(std::istream& in, state * ledger)
i++) { i++) {
transaction * t; transaction * t;
if ((*i)->cost->comm()) { if ((*i)->cost->commdty()) {
t = new transaction((*i)->acct, (*i)->cost); t = new transaction((*i)->acct, (*i)->cost);
} else { } else {
amount * temp = (*x)->cost->value(); amount * temp = (*x)->cost->value();
@ -359,7 +388,7 @@ entry * parse_entry(std::istream& in, state * ledger)
x++) { x++) {
curr->xacts.push_back(*x); curr->xacts.push_back(*x);
if (ledger->compute_balances) if (do_compute)
(*x)->acct->balance.credit((*x)->cost); (*x)->acct->balance.credit((*x)->cost);
} }
} }
@ -379,7 +408,7 @@ entry * parse_entry(std::istream& in, state * ledger)
return curr; return curr;
} }
void parse_automated_transactions(std::istream& in, state * ledger) void parse_automated_transactions(std::istream& in, book * ledger)
{ {
static char line[MAX_LINE + 1]; static char line[MAX_LINE + 1];
@ -415,7 +444,7 @@ void parse_automated_transactions(std::istream& in, state * ledger)
} }
if (masks && xacts) if (masks && xacts)
ledger->virtual_mapping.insert(state::virtual_map_pair(masks, xacts)); ledger->virtual_mapping.insert(book::virtual_map_pair(masks, xacts));
else if (masks) else if (masks)
delete masks; delete masks;
else if (xacts) else if (xacts)
@ -427,18 +456,18 @@ void parse_automated_transactions(std::istream& in, state * ledger)
// Ledger parser // Ledger parser
// //
state * parse_ledger(std::istream& in, regexps_map& regexps, book * parse_ledger(std::istream& in, regexps_map& regexps,
bool compute_balances) bool compute_balances)
{ {
static char line[MAX_LINE + 1]; static char line[MAX_LINE + 1];
char c; char c;
state * ledger = new state; book * ledger = new book;
main_ledger = ledger; main_ledger = ledger;
do_compute = compute_balances;
linenum = 0;
ledger->compute_balances = compute_balances;
linenum = 0;
while (! in.eof()) { while (! in.eof()) {
switch (in.peek()) { switch (in.peek()) {
case -1: // end of file case -1: // end of file

View file

@ -4,12 +4,13 @@
namespace ledger { namespace ledger {
extern bool show_cleared; extern bool show_cleared;
extern bool get_quotes;
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 std::string truncated(const std::string& str, int width) static std::string truncated(const std::string& str, int width)
{ {
@ -83,7 +84,7 @@ void print_register(const std::string& acct_name, std::ostream& out,
// Always display the street value, if prices have been // Always display the street value, if prices have been
// specified // specified
amount * street = (*x)->cost->street(); amount * street = (*x)->cost->street(get_quotes);
balance.credit(street); balance.credit(street);
// If there are two transactions, use the one which does not // If there are two transactions, use the one which does not
@ -126,7 +127,7 @@ void print_register(const std::string& acct_name, std::ostream& out,
out << std::left << truncated((*y)->acct_as_str(), 22) << " "; out << std::left << truncated((*y)->acct_as_str(), 22) << " ";
out.width(12); out.width(12);
street = (*y)->cost->street(); street = (*y)->cost->street(get_quotes);
out << std::right << street->as_str(true) << std::endl; out << std::right << street->as_str(true) << std::endl;
delete street; delete street;
} }