diff options
author | John Wiegley <johnw@newartisans.com> | 2003-10-05 00:22:17 +0000 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2003-10-05 00:22:17 +0000 |
commit | 260217c8ab48c6000021222e0d1c989dce80b869 (patch) | |
tree | 74a76b2ab36ef99bf56be92b848544394e14a579 | |
parent | 2c109226140a99a07af079c70e54fb396bb3484e (diff) | |
download | fork-ledger-260217c8ab48c6000021222e0d1c989dce80b869.tar.gz fork-ledger-260217c8ab48c6000021222e0d1c989dce80b869.tar.bz2 fork-ledger-260217c8ab48c6000021222e0d1c989dce80b869.zip |
*** empty log message ***
-rw-r--r-- | amount.cc | 83 | ||||
-rw-r--r-- | balance.cc | 3 | ||||
-rw-r--r-- | equity.cc | 6 | ||||
-rw-r--r-- | gnucash.cc | 14 | ||||
-rw-r--r-- | ledger.cc | 31 | ||||
-rw-r--r-- | ledger.h | 52 | ||||
-rw-r--r-- | main.cc | 41 | ||||
-rw-r--r-- | parse.cc | 65 | ||||
-rw-r--r-- | register.cc | 11 |
9 files changed, 164 insertions, 142 deletions
@@ -45,17 +45,13 @@ class gmp_amount : public amount mpz_clear(quantity); } - virtual commodity * comm() const { + virtual commodity * commdty() const { return quantity_comm; } - virtual const std::string& comm_symbol() const { - assert(quantity_comm); - return quantity_comm->symbol; - } virtual amount * copy() const; virtual amount * value(amount *) const; - virtual amount * street() const; + virtual amount * street(bool get_quotes) const; virtual bool has_price() const { 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; - const amount * amt = this; + using namespace std; - extern bool get_quotes; + char buf[256]; + buf[0] = '\0'; - for (int cycles = 0; cycles < 10; cycles++) { - totals::iterator pi = - main_ledger->prices.amounts.find(amt->comm_symbol()); - if (pi == main_ledger->prices.amounts.end()) { - using namespace std; + if (FILE * fp = popen((std::string("getquote ") + + comm->symbol).c_str(), "r")) { + if (feof(fp) || ! fgets(buf , 255, fp)) { + fclose(fp); + return false; + } + fclose(fp); + } - if (! get_quotes) - break; + if (buf[0]) { + char * p = strchr(buf, '\n'); + if (p) *p = '\0'; - char buf[256]; - buf[0] = '\0'; + comm->price = create_amount(buf); + return true; + } + return false; +} - if (FILE * fp = popen((std::string("getquote ") + - amt->comm_symbol()).c_str(), "r")) { - if (feof(fp) || ! fgets(buf , 255, fp)) { - fclose(fp); - break; - } - fclose(fp); - } +amount * gmp_amount::street(bool get_quotes) const +{ + amount * amt = copy(); - if (buf[0]) { - char * p = strchr(buf, '\n'); - if (p) *p = '\0'; + int max = 10; - main_ledger->record_price((amt->comm_symbol() + "=" + buf).c_str()); - continue; - } - break; - } else { - amount * temp = cost; - amt = cost = amt->value((*pi).second); - - bool same = temp && temp->comm() == cost->comm(); - if (temp) - delete temp; - if (same) + while (--max >= 0) { + if (! amt->commdty()->price) { + if (get_quotes) + get_commodity_price(amt->commdty()); + if (! amt->commdty()->price) 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) @@ -551,7 +547,6 @@ static commodity * parse_amount(mpz_t out, const char * num, } commodity * comm = NULL; - if (saw_commodity) { commodities_map_iterator item = main_ledger->commodities.find(symbol.c_str()); @@ -10,6 +10,7 @@ extern bool show_children; extern bool show_empty; extern bool show_subtotals; extern bool full_names; +extern bool get_quotes; extern std::time_t begin_date; extern bool have_beginning; @@ -100,7 +101,7 @@ void report_balances(std::ostream& out, regexps_map& regexps) } if (acct->checked == 1) - acct->balance.credit((*x)->cost->street()); + acct->balance.credit((*x)->cost->street(get_quotes)); } } } @@ -2,6 +2,8 @@ namespace ledger { +extern bool get_quotes; + static void equity_entry(account * acct, regexps_map& regexps, std::ostream& out) { @@ -23,12 +25,12 @@ static void equity_entry(account * acct, regexps_map& regexps, xact = new transaction(); xact->acct = const_cast<account *>(acct); - xact->cost = (*i).second->street(); + xact->cost = (*i).second->street(get_quotes); opening.xacts.push_back(xact); xact = new transaction(); xact->acct = main_ledger->find_account("Equity:Opening Balances"); - xact->cost = (*i).second->street(); + xact->cost = (*i).second->street(get_quotes); xact->cost->negate(); opening.xacts.push_back(xact); } @@ -18,6 +18,7 @@ static amount * curr_value; static std::string curr_quant; static XML_Parser current_parser; static accounts_map accounts_by_id; +static bool do_compute; static enum { 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; - if (curr_value->comm() == xact->acct->comm) { + if (curr_value->commdty() == xact->acct->comm) { // assert: value must be equal to curr_value. delete curr_value; curr_value = NULL; @@ -207,7 +208,7 @@ static void dataHandler(void *userData, const char *s, int len) if (curr_value) delete curr_value; - if (main_ledger->compute_balances) + if (do_compute) xact->acct->balance.credit(xact->cost); 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]; - state * ledger = new state; - - main_ledger = ledger; - ledger->compute_balances = compute_balances; + book * ledger = new book; + main_ledger = ledger; + do_compute = compute_balances; action = NO_ACTION; curr_account = NULL; curr_entry = NULL; @@ -5,7 +5,7 @@ namespace ledger { bool use_warnings = false; -state * main_ledger; +book * main_ledger; const std::string transaction::acct_as_str() const { @@ -67,9 +67,9 @@ void entry::print(std::ostream& out, bool shortcut) const out << std::endl; - if (shortcut && - (xacts.size() != 2 || - xacts.front()->cost->comm() != xacts.back()->cost->comm())) + if (shortcut && (xacts.size() != 2 || + (xacts.front()->cost->commdty() != + xacts.back()->cost->commdty()))) shortcut = false; 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. // 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 { for (entries_list_const_iterator i = entries.begin(); @@ -251,7 +251,7 @@ bool matches(const regexps_map& regexps, const std::string& str, return match; } -state::~state() +book::~book() { for (commodities_map_iterator i = commodities.begin(); i != commodities.end(); @@ -269,24 +269,7 @@ state::~state() delete *i; } -void state::record_price(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'; - prices.amounts.insert(totals::pair(c, create_amount(p))); - } -} - -account * state::find_account(const std::string& name, bool create) +account * book::find_account(const std::string& name, bool create) { accounts_map_iterator i = accounts_cache.find(name); if (i != accounts_cache.end()) @@ -1,5 +1,5 @@ #ifndef _LEDGER_H -#define _LEDGER_H "$Revision: 1.19 $" +#define _LEDGER_H "$Revision: 1.20 $" ////////////////////////////////////////////////////////////////////// // @@ -26,19 +26,22 @@ namespace ledger { +struct amount; struct commodity { std::string name; std::string symbol; - bool prefix; - bool separate; - bool thousands; - bool european; + mutable amount * price; // the current price - 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) {} commodity(const std::string& sym, bool pre = false, bool sep = true, bool thou = true, bool euro = false, int prec = 2); @@ -54,11 +57,10 @@ class amount public: virtual ~amount() {} - virtual commodity * comm() const = 0; - virtual const std::string& comm_symbol() const = 0; + virtual commodity * commdty() const = 0; virtual amount * copy() 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 void set_value(const amount * pr) = 0; @@ -88,6 +90,10 @@ struct mask pcre * regexp; mask(const std::string& pattern); + + ~mask() { + pcre_free(regexp); + } }; typedef std::list<mask> regexps_map; @@ -169,10 +175,10 @@ typedef entries_list::const_iterator entries_list_const_iterator; struct totals { - typedef std::map<const std::string, amount *> map; - typedef map::iterator iterator; - typedef map::const_iterator const_iterator; - typedef std::pair<const std::string, amount *> pair; + typedef std::map<commodity *, amount *> map; + typedef map::iterator iterator; + typedef map::const_iterator const_iterator; + typedef std::pair<commodity *, amount *> pair; map amounts; @@ -180,9 +186,9 @@ struct totals void credit(const amount * val) { std::pair<iterator, bool> result = - amounts.insert(pair(val->comm_symbol(), val->copy())); + amounts.insert(pair(val->commdty(), val->copy())); if (! result.second) - amounts[val->comm_symbol()]->credit(val); + amounts[val->commdty()]->credit(val); } void credit(const totals& other); @@ -191,7 +197,7 @@ struct totals void print(std::ostream& out, int width) const; // Returns an allocated entity - amount * sum(const std::string& comm) { + amount * sum(commodity * comm) { return amounts[comm]; } }; @@ -231,13 +237,12 @@ struct account }; -struct state +struct book { commodities_map commodities; accounts_map accounts; accounts_map accounts_cache; // maps full names to accounts entries_list entries; - totals prices; int current_year; typedef std::map<std::list<mask> *, @@ -248,12 +253,9 @@ struct state typedef virtual_map::const_iterator virtual_map_iterator; - bool compute_balances; virtual_map virtual_mapping; - ~state(); - - void record_price(const std::string& setting); + ~book(); template<typename Compare> void sort(Compare comp) { @@ -264,12 +266,12 @@ struct state account * find_account(const std::string& name, bool create = true); }; -extern state * main_ledger; +extern book * main_ledger; extern bool use_warnings; inline commodity::commodity(const std::string& sym, bool pre, bool sep, 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) { std::pair<commodities_map_iterator, bool> result = main_ledger->commodities.insert(commodities_map_pair(sym, this)); @@ -5,14 +5,15 @@ #include <fstream> 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); #ifdef READ_GNUCASH - extern state * parse_gnucash(std::istream& in, bool compute_balances); + extern book * parse_gnucash(std::istream& in, bool compute_balances); #endif extern bool parse_date(const char * date_str, std::time_t * result, 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 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[]) { std::istream * file = NULL; + std::string prices; regexps_map regexps; @@ -189,18 +191,7 @@ int main(int argc, char * argv[]) // -p "COMMODITY=PRICE" // -p path-to-price-database case 'p': - if (access(optarg, R_OK) != -1) { - 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); - } + prices = optarg; break; case 'P': @@ -297,6 +288,22 @@ int main(int argc, char * argv[]) for (; optind < argc; 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 if (command == "balance") { @@ -313,8 +320,10 @@ int main(int argc, char * argv[]) equity_ledger(std::cout, regexps); } -#if 0 - // Deleting the main ledger just isn't necessary at this point. +#ifdef DEBUG + // Ordinarily, deleting the main ledger just isn't necessary at + // this point. + delete main_ledger; #endif } @@ -84,11 +84,40 @@ bool parse_date(const char * date_str, std::time_t * result, 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 -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(); @@ -148,13 +177,13 @@ transaction * parse_transaction(std::istream& in, state * ledger) xact->acct = ledger->find_account(p); - if (ledger->compute_balances && xact->cost) + if (do_compute && xact->cost) xact->acct->balance.credit(xact->cost); return xact; } -entry * parse_entry(std::istream& in, state * ledger) +entry * parse_entry(std::istream& in, book * ledger) { entry * curr = new entry; @@ -233,12 +262,12 @@ entry * parse_entry(std::istream& in, state * ledger) continue; if (! (*x)->cost->has_price() && - ! (*x)->cost->comm()->prefix && - (*x)->cost->comm()->separate) { + ! (*x)->cost->commdty()->prefix && + (*x)->cost->commdty()->separate) { for (totals::iterator i = balance.amounts.begin(); i != balance.amounts.end(); i++) { - if ((*i).second->comm() != (*x)->cost->comm()) { + if ((*i).second->commdty() != (*x)->cost->commdty()) { (*x)->cost->set_value((*i).second); break; } @@ -276,7 +305,7 @@ entry * parse_entry(std::istream& in, state * ledger) (*x)->cost = (*i).second->value(); (*x)->cost->negate(); - if (ledger->compute_balances) + if (do_compute) (*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 // 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++) { std::list<transaction *> xacts; @@ -301,7 +330,7 @@ entry * parse_entry(std::istream& in, state * ledger) i++) { transaction * t; - if ((*i)->cost->comm()) { + if ((*i)->cost->commdty()) { t = new transaction((*i)->acct, (*i)->cost); } else { amount * temp = (*x)->cost->value(); @@ -359,7 +388,7 @@ entry * parse_entry(std::istream& in, state * ledger) x++) { curr->xacts.push_back(*x); - if (ledger->compute_balances) + if (do_compute) (*x)->acct->balance.credit((*x)->cost); } } @@ -379,7 +408,7 @@ entry * parse_entry(std::istream& in, state * ledger) 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]; @@ -415,7 +444,7 @@ void parse_automated_transactions(std::istream& in, state * ledger) } 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) delete masks; else if (xacts) @@ -427,18 +456,18 @@ void parse_automated_transactions(std::istream& in, state * ledger) // Ledger parser // -state * parse_ledger(std::istream& in, regexps_map& regexps, +book * parse_ledger(std::istream& in, regexps_map& regexps, bool compute_balances) { static char line[MAX_LINE + 1]; char c; - state * ledger = new state; - main_ledger = ledger; + book * ledger = new book; - ledger->compute_balances = compute_balances; + main_ledger = ledger; + do_compute = compute_balances; + linenum = 0; - linenum = 0; while (! in.eof()) { switch (in.peek()) { case -1: // end of file diff --git a/register.cc b/register.cc index 38579ce0..db9b4849 100644 --- a/register.cc +++ b/register.cc @@ -4,12 +4,13 @@ namespace ledger { -extern bool show_cleared; +extern bool show_cleared; +extern bool get_quotes; extern std::time_t begin_date; -extern bool have_beginning; +extern bool have_beginning; extern std::time_t end_date; -extern bool have_ending; +extern bool have_ending; 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 // specified - amount * street = (*x)->cost->street(); + amount * street = (*x)->cost->street(get_quotes); balance.credit(street); // 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.width(12); - street = (*y)->cost->street(); + street = (*y)->cost->street(get_quotes); out << std::right << street->as_str(true) << std::endl; delete street; } |