*** 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);
}
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());

View file

@ -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));
}
}
}

View file

@ -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);
}

View file

@ -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;

View file

@ -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())

View file

@ -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));

41
main.cc
View file

@ -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
}

View file

@ -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;
book * ledger = new book;
main_ledger = ledger;
do_compute = compute_balances;
linenum = 0;
ledger->compute_balances = compute_balances;
linenum = 0;
while (! in.eof()) {
switch (in.peek()) {
case -1: // end of file

View file

@ -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;
}