summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--amount.cc464
-rw-r--r--balance.cc128
-rw-r--r--gnucash.cc16
-rw-r--r--ledger.cc23
-rw-r--r--ledger.h22
-rw-r--r--main.cc9
-rw-r--r--parse.cc46
7 files changed, 424 insertions, 284 deletions
diff --git a/amount.cc b/amount.cc
index bd3827d0..b3ce8b2e 100644
--- a/amount.cc
+++ b/amount.cc
@@ -47,46 +47,27 @@ class gmp_amount : public amount
return quantity_comm->symbol;
}
- virtual amount * copy() const {
- gmp_amount * new_amt = new gmp_amount();
- new_amt->priced = priced;
- mpz_set(new_amt->price, price);
- new_amt->price_comm = price_comm;
- mpz_set(new_amt->quantity, quantity);
- new_amt->quantity_comm = quantity_comm;
- return new_amt;
- }
-
- virtual amount * value() const {
- if (! priced) {
- return copy();
- } else {
- gmp_amount * new_amt = new gmp_amount();
- new_amt->priced = false;
- multiply(new_amt->quantity, quantity, price);
- new_amt->quantity_comm = price_comm;
- return new_amt;
- }
- }
+ virtual amount * copy() const;
+ virtual amount * value() const;
virtual operator bool() const;
virtual void credit(const amount * other) {
*this += *other;
}
+ virtual void negate() {
+ mpz_ui_sub(quantity, 0, quantity);
+ }
virtual void operator+=(const amount& other);
virtual void parse(const char * num) {
*this = num;
}
virtual amount& operator=(const char * num);
- virtual operator std::string() const;
-
- static const std::string to_str(const commodity * comm, const mpz_t val);
-
- static void parse(mpz_t out, char * num);
- static void round(mpz_t out, const mpz_t val, int prec);
- static void multiply(mpz_t out, const mpz_t l, const mpz_t r);
+ virtual std::string as_str(bool full_prec) const;
+ virtual operator std::string() const {
+ return as_str(false);
+ }
friend amount * create_amount(const char * value, const amount * price);
};
@@ -141,46 +122,129 @@ amount * create_amount(const char * value, const amount * price)
return a;
}
+static void round(mpz_t out, const mpz_t val, int prec)
+{
+ mpz_t divisor;
+ mpz_t quotient;
+ mpz_t remainder;
+
+ mpz_init(divisor);
+ mpz_init(quotient);
+ mpz_init(remainder);
+
+ mpz_ui_pow_ui(divisor, 10, MAX_PRECISION - prec);
+ mpz_tdiv_qr(quotient, remainder, val, divisor);
+
+ mpz_ui_pow_ui(divisor, 10, MAX_PRECISION - prec - 1);
+ mpz_mul_ui(divisor, divisor, 5);
+ if (mpz_cmp(remainder, divisor) >= 0) {
+ mpz_ui_pow_ui(divisor, 10, MAX_PRECISION - prec);
+ mpz_sub(remainder, divisor, remainder);
+ mpz_add(out, val, remainder);
+ } else {
+ mpz_sub(out, val, remainder);
+ }
+
+ mpz_clear(divisor);
+ mpz_clear(quotient);
+ mpz_clear(remainder);
+}
+
+static void multiply(mpz_t out, const mpz_t l, const mpz_t r)
+{
+ mpz_t divisor;
+
+ mpz_init(divisor);
+
+ mpz_mul(out, l, r);
+
+ // The number is at double-precision right now, so rounding at
+ // precision 0 effectively means rounding to the ordinary
+ // precision.
+ round(out, out, 0);
+
+ // after multiplying, truncate to the correct precision
+ mpz_ui_pow_ui(divisor, 10, MAX_PRECISION);
+ mpz_tdiv_q(out, out, divisor);
+
+ mpz_clear(divisor);
+}
+
+amount * gmp_amount::copy() const
+{
+ gmp_amount * new_amt = new gmp_amount();
+#if 0
+ // Don't copy the price
+ new_amt->priced = priced;
+ mpz_set(new_amt->price, price);
+ new_amt->price_comm = price_comm;
+#endif
+ mpz_set(new_amt->quantity, quantity);
+ new_amt->quantity_comm = quantity_comm;
+ return new_amt;
+}
+
+amount * gmp_amount::value() const
+{
+ if (! priced) {
+ return copy();
+ } else {
+ gmp_amount * new_amt = new gmp_amount();
+ new_amt->priced = false;
+ multiply(new_amt->quantity, quantity, price);
+ new_amt->quantity_comm = price_comm;
+ return new_amt;
+ }
+}
+
gmp_amount::operator bool() const
{
mpz_t copy;
mpz_init_set(copy, quantity);
assert(quantity_comm);
- gmp_amount::round(copy, copy, quantity_comm->precision);
+ if (quantity_comm->precision < MAX_PRECISION)
+ round(copy, copy, quantity_comm->precision);
bool zero = mpz_sgn(copy) == 0;
mpz_clear(copy);
return ! zero;
}
-const std::string gmp_amount::to_str(const commodity * comm, const mpz_t val)
+static std::string amount_to_str(const commodity * comm, const mpz_t val,
+ bool full_precision)
{
- mpz_t copy;
+ mpz_t temp;
mpz_t quotient;
mpz_t rquotient;
mpz_t remainder;
mpz_t divisor;
+
bool negative = false;
- mpz_init_set(copy, val);
+ mpz_init_set(temp, val);
mpz_init(quotient);
mpz_init(rquotient);
mpz_init(remainder);
mpz_init(divisor);
- gmp_amount::round(copy, copy, comm->precision);
+ if (! full_precision && comm->precision < MAX_PRECISION)
+ round(temp, temp, comm->precision);
mpz_ui_pow_ui(divisor, 10, MAX_PRECISION);
- mpz_tdiv_qr(quotient, remainder, copy, divisor);
+ mpz_tdiv_qr(quotient, remainder, temp, divisor);
if (mpz_sgn(quotient) < 0 || mpz_sgn(remainder) < 0)
negative = true;
mpz_abs(quotient, quotient);
mpz_abs(remainder, remainder);
- assert(MAX_PRECISION - comm->precision > 0);
- mpz_ui_pow_ui(divisor, 10, MAX_PRECISION - comm->precision);
- mpz_tdiv_qr(rquotient, remainder, remainder, divisor);
+ if (full_precision || comm->precision == MAX_PRECISION) {
+ mpz_set(rquotient, remainder);
+ } else {
+ assert(MAX_PRECISION - comm->precision > 0);
+ mpz_ui_pow_ui(divisor, 10, MAX_PRECISION - comm->precision);
+ mpz_tdiv_qr(rquotient, remainder, remainder, divisor);
+ }
std::ostringstream s;
@@ -192,10 +256,51 @@ const std::string gmp_amount::to_str(const commodity * comm, const mpz_t val)
if (negative)
s << "-";
- s << quotient;
- s << '.';
- s.width(comm->precision);
+ if (comm->thousands) {
+ // jww (2003-09-29): use a smarter starting value
+
+ bool printed = false;
+
+ for (int powers = 27; powers >= 0; powers -= 3) {
+ mpz_ui_pow_ui(divisor, 10, powers);
+ mpz_tdiv_q(temp, quotient, divisor);
+
+ if (mpz_sgn(temp) == 0)
+ continue;
+
+ mpz_ui_pow_ui(divisor, 10, 3);
+ mpz_tdiv_r(temp, temp, divisor);
+
+ if (printed) {
+ s.width(3);
+ s.fill('0');
+ }
+ s << temp;
+
+ if (powers > 0) {
+ if (comm->european)
+ s << ".";
+ else
+ s << ",";
+
+ printed = true;
+ }
+ }
+ } else {
+ s << quotient;
+ }
+
+ if (comm->european)
+ s << ',';
+ else
+ s << '.';
+
+ if (full_precision)
+ s.width(MAX_PRECISION);
+ else
+ s.width(comm->precision);
+
s.fill('0');
s << rquotient;
@@ -205,7 +310,7 @@ const std::string gmp_amount::to_str(const commodity * comm, const mpz_t val)
s << comm->symbol;
}
- mpz_clear(copy);
+ mpz_clear(temp);
mpz_clear(quotient);
mpz_clear(rquotient);
mpz_clear(remainder);
@@ -214,26 +319,30 @@ const std::string gmp_amount::to_str(const commodity * comm, const mpz_t val)
return s.str();
}
-gmp_amount::operator std::string() const
+std::string gmp_amount::as_str(bool full_prec) const
{
std::ostringstream s;
assert(quantity_comm);
- s << to_str(quantity_comm, quantity);
+ s << amount_to_str(quantity_comm, quantity, full_prec);
if (priced) {
assert(price_comm);
- s << " @ " << to_str(price_comm, price);
+ s << " @ " << amount_to_str(price_comm, price, full_prec);
}
return s.str();
}
-void gmp_amount::parse(mpz_t out, char * num)
+static void parse_number(mpz_t out, const char * num, commodity * comm)
{
if (char * p = std::strchr(num, '/')) {
mpz_t numer;
mpz_t val;
+ // The number was specified as a numerator over denominator, such
+ // as 5250/100. This gives us the precision, and avoids any
+ // nastiness having to do with international numbering formats.
+
std::string numer_str(num, p - num);
mpz_init_set_str(numer, numer_str.c_str(), 10);
mpz_init(val);
@@ -250,12 +359,20 @@ void gmp_amount::parse(mpz_t out, char * num)
else {
static char buf[256];
- // jww (2003-09-28): What if there is no decimal?
+ // The number is specified as the user desires, with the
+ // commodity telling us how to parse it.
std::memset(buf, '0', 255);
std::strncpy(buf, num, std::strlen(num));
- char * t = std::strchr(buf, '.');
+ if (comm->thousands)
+ while (char * t = std::strchr(buf, comm->european ? '.' : ','))
+ do { *t = *(t + 1); } while (*(t++ + 1));
+
+ char * t = std::strchr(buf, comm->european ? ',' : '.');
+ if (! t)
+ t = buf + std::strlen(num);
+
for (int prec = 0; prec < MAX_PRECISION; prec++) {
*t = *(t + 1);
t++;
@@ -266,6 +383,93 @@ void gmp_amount::parse(mpz_t out, char * num)
}
}
+static commodity * parse_amount(mpz_t out, const char * num,
+ int matched, int * ovector, int base)
+{
+ static char buf[256];
+
+ bool saw_commodity = false;
+
+ std::string symbol;
+ bool prefix, separate, thousands, european;
+ int precision, result;
+
+ if (ovector[base * 2] >= 0) {
+ // A prefix symbol was found
+ saw_commodity = true;
+ prefix = true;
+ separate = ovector[(base + 2) * 2] != ovector[(base + 2) * 2 + 1];
+ result = pcre_copy_substring(num, ovector, matched, base + 1, buf, 255);
+ assert(result >= 0);
+ symbol = buf;
+ }
+
+ // This is the value, and must be present
+ assert(ovector[(base + 3) * 2] >= 0);
+ result = pcre_copy_substring(num, ovector, matched, base + 3, buf, 255);
+ assert(result >= 0);
+
+ // Determine the precision used
+ if (char * p = std::strchr(buf, '.'))
+ precision = std::strlen(++p);
+ else if (char * p = std::strchr(buf, '/'))
+ precision = std::strlen(++p) - 1;
+ else
+ precision = 0;
+
+ // Where "thousands" markers used? Is it a european number?
+ if (char * p = std::strrchr(buf, ',')) {
+ if (std::strchr(p, '.'))
+ thousands = true;
+ else
+ european = true;
+ }
+
+ // Parse the actual quantity
+ std::string value_str = buf;
+
+ if (ovector[(base + 4) * 2] >= 0) {
+ // A suffix symbol was found
+ saw_commodity = true;
+ prefix = false;
+ separate = ovector[(base + 5) * 2] != ovector[(base + 5) * 2 + 1];
+ result = pcre_copy_substring(num, ovector, matched, base + 6, buf, 255);
+ assert(result >= 0);
+ symbol = buf;
+ }
+
+ commodity * comm = NULL;
+
+ if (! saw_commodity) {
+ std::cerr << "Error: No commodity specified: " << value_str
+ << std::endl;
+ std::exit(1);
+ } else {
+ commodities_iterator item = commodities.find(symbol.c_str());
+ if (item == commodities.end()) {
+ comm = new commodity(symbol, prefix, separate,
+ thousands, european, precision);
+ } else {
+ comm = (*item).second;
+#if 0
+ // If a finer precision was used than the commodity allows,
+ // increase the precision.
+ if (precision > comm->precision)
+ comm->precision = precision;
+#else
+ if (use_warnings && precision > comm->precision)
+ std::cerr << "Warning: Use of higher precision than expected: "
+ << value_str << std::endl;
+#endif
+ }
+ }
+ assert(comm);
+
+ parse_number(out, value_str.c_str(), comm);
+
+ return comm;
+}
+
amount& gmp_amount::operator=(const char * num)
{
// Compile the regular expression used for parsing amounts
@@ -274,133 +478,23 @@ amount& gmp_amount::operator=(const char * num)
const char *error;
int erroffset;
static const std::string amount_re =
- "(([^-0-9/.]+)(\\s*))?([-0-9/.]+)((\\s*)([^-0-9/.@]+))?";
+ "(([^-0-9/.,]+)(\\s*))?([-0-9/.,]+)((\\s*)([^-0-9/.,@]+))?";
const std::string regexp =
"^" + amount_re + "(\\s*@\\s*" + amount_re + ")?$";
re = pcre_compile(regexp.c_str(), 0, &error, &erroffset, NULL);
}
- bool saw_commodity;
- std::string symbol;
- bool prefix;
- bool separate;
- int precision;
-
- static char buf[256];
int ovector[60];
- int matched, result;
+ int matched;
matched = pcre_exec(re, NULL, num, std::strlen(num), 0, 0, ovector, 60);
if (matched > 0) {
- saw_commodity = false;
-
- if (ovector[1 * 2] >= 0) {
- // A prefix symbol was found
- saw_commodity = true;
- prefix = true;
- separate = ovector[3 * 2] != ovector[3 * 2 + 1];
- result = pcre_copy_substring(num, ovector, matched, 2, buf, 255);
- assert(result >= 0);
- symbol = buf;
- }
-
- // This is the value, and must be present
- assert(ovector[4 * 2] >= 0);
- result = pcre_copy_substring(num, ovector, matched, 4, buf, 255);
- assert(result >= 0);
-
- // Determine the precision used
- if (char * p = std::strchr(buf, '.'))
- precision = std::strlen(++p);
- else if (char * p = std::strchr(buf, '/'))
- precision = std::strlen(++p) - 1;
- else
- precision = 0;
-
- // Parse the actual quantity
- parse(quantity, buf);
-
- if (ovector[5 * 2] >= 0) {
- // A suffix symbol was found
- saw_commodity = true;
- prefix = false;
- separate = ovector[6 * 2] != ovector[6 * 2 + 1];
- result = pcre_copy_substring(num, ovector, matched, 7, buf, 255);
- assert(result >= 0);
- symbol = buf;
- }
-
- if (! saw_commodity) {
- quantity_comm = commodity_usd;
- } else {
- commodities_iterator item = commodities.find(symbol.c_str());
- if (item == commodities.end()) {
- quantity_comm = new commodity(symbol, prefix, separate, precision);
- } else {
- quantity_comm = (*item).second;
-
- // If a finer precision was used than the commodity allows,
- // increase the precision.
- if (precision > quantity_comm->precision)
- quantity_comm->precision = precision;
- }
- }
+ quantity_comm = parse_amount(quantity, num, matched, ovector, 1);
// If the following succeeded, then we have a price
if (ovector[8 * 2] >= 0) {
- saw_commodity = false;
-
- if (ovector[9 * 2] >= 0) {
- // A prefix symbol was found
- saw_commodity = true;
- prefix = true;
- separate = ovector[11 * 2] != ovector[11 * 2 + 1];
- result = pcre_copy_substring(num, ovector, matched, 10, buf, 255);
- assert(result >= 0);
- symbol = buf;
- }
-
- assert(ovector[12 * 2] >= 0);
- result = pcre_copy_substring(num, ovector, matched, 4, buf, 255);
- assert(result >= 0);
-
- // Determine the precision used
- if (char * p = std::strchr(buf, '.'))
- precision = std::strlen(++p);
- else if (char * p = std::strchr(buf, '/'))
- precision = std::strlen(++p) - 1;
- else
- precision = 0;
-
- // Parse the actual price
- parse(price, buf);
priced = true;
-
- if (ovector[13 * 2] >= 0) {
- // A suffix symbol was found
- saw_commodity = true;
- prefix = false;
- separate = ovector[14 * 2] != ovector[14 * 2 + 1];
- result = pcre_copy_substring(num, ovector, matched, 15, buf, 255);
- assert(result >= 0);
- symbol = buf;
- }
-
- if (! saw_commodity) {
- price_comm = commodity_usd;
- } else {
- commodities_iterator item = commodities.find(symbol.c_str());
- if (item == commodities.end()) {
- price_comm = new commodity(symbol, prefix, separate, precision);
- } else {
- price_comm = (*item).second;
-
- // If a finer precision was used than the commodity allows,
- // increase the precision.
- if (precision > price_comm->precision)
- price_comm->precision = precision;
- }
- }
+ price_comm = parse_amount(price, num, matched, ovector, 9);
}
} else {
std::cerr << "Failed to parse amount: " << num << std::endl;
@@ -415,52 +509,4 @@ void gmp_amount::operator+=(const amount& _other)
mpz_add(quantity, quantity, other.quantity);
}
-void gmp_amount::round(mpz_t out, const mpz_t val, int prec)
-{
- mpz_t divisor;
- mpz_t quotient;
- mpz_t remainder;
-
- mpz_init(divisor);
- mpz_init(quotient);
- mpz_init(remainder);
-
- mpz_ui_pow_ui(divisor, 10, MAX_PRECISION - prec);
- mpz_tdiv_qr(quotient, remainder, val, divisor);
-
- mpz_ui_pow_ui(divisor, 10, MAX_PRECISION - prec - 1);
- mpz_mul_ui(divisor, divisor, 5);
- if (mpz_cmp(remainder, divisor) >= 0) {
- mpz_ui_pow_ui(divisor, 10, MAX_PRECISION - prec);
- mpz_sub(remainder, divisor, remainder);
- mpz_add(out, val, remainder);
- } else {
- mpz_sub(out, val, remainder);
- }
-
- mpz_clear(divisor);
- mpz_clear(quotient);
- mpz_clear(remainder);
-}
-
-void gmp_amount::multiply(mpz_t out, const mpz_t l, const mpz_t r)
-{
- mpz_t divisor;
-
- mpz_init(divisor);
-
- mpz_mul(out, l, r);
-
- // The number is at double-precision right now, so rounding at
- // precision 0 effectively means rounding to the ordinary
- // precision.
- gmp_amount::round(out, out, 0);
-
- // after multiplying, truncate to the correct precision
- mpz_ui_pow_ui(divisor, 10, MAX_PRECISION);
- mpz_tdiv_q(out, out, divisor);
-
- mpz_clear(divisor);
-}
-
} // namespace ledger
diff --git a/balance.cc b/balance.cc
index fd09bae1..b67c5365 100644
--- a/balance.cc
+++ b/balance.cc
@@ -9,6 +9,12 @@ namespace ledger {
// Balance report.
//
+static bool show_current = false;
+static bool show_cleared = false;
+static bool show_children = false;
+static bool show_empty = false;
+static bool no_subtotals = false;
+
static inline bool matches(const std::list<pcre *>& regexps,
const std::string& str) {
for (std::list<pcre *>::const_iterator r = regexps.begin();
@@ -22,15 +28,37 @@ static inline bool matches(const std::list<pcre *>& regexps,
return false;
}
-void report_balances(int argc, char *argv[], std::ostream& out)
+void display_total(std::ostream& out, account * acct,
+ const std::map<account *, totals *>& balances)
{
- bool show_current = false;
- bool show_cleared = false;
- bool show_children = false;
- bool show_empty = false;
- bool no_subtotals = false;
+ std::map<account *, totals *>::const_iterator b = balances.find(acct);
+ if (b != balances.end()) {
+ totals * balance = (*b).second;
+
+ if (balance && (show_empty || *balance)) {
+ out << *balance;
+
+ if (acct->parent) {
+ for (account * a = acct; a; a = a->parent)
+ out << " ";
+ out << acct->name << std::endl;
+ } else {
+ out << " " << *acct << std::endl;
+ }
+ }
+ }
+
+ for (account::iterator i = acct->children.begin();
+ i != acct->children.end();
+ i++) {
+ display_total(out, (*i).second, balances);
+ }
+}
+void report_balances(int argc, char **argv, std::ostream& out)
+{
int c;
+ optind = 1;
while (-1 != (c = getopt(argc, argv, "cCsSn"))) {
switch (char(c)) {
case 'c': show_current = true; break;
@@ -62,16 +90,34 @@ void report_balances(int argc, char *argv[], std::ostream& out)
// totals
std::map<account *, totals *> balances;
-
std::time_t now = std::time(NULL);
+ totals total_balance;
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))
+
+ if (! regexps.empty()) {
+ if (show_children) {
+ bool match = false;
+ for (account * a = acct; a; a = a->parent) {
+ if (matches(regexps, a->name)) {
+ match = true;
+ break;
+ }
+ }
+ if (! match)
+ continue;
+ }
+ else if (! matches(regexps, acct->name)) {
+ continue;
+ }
+ }
+ else if (! show_children && acct->parent) {
continue;
+ }
while (acct) {
totals * balance = NULL;
@@ -84,16 +130,27 @@ void report_balances(int argc, char *argv[], std::ostream& out)
balance = (*t).second;
}
+ bool do_credit = false;
+
if (show_current) {
if (difftime((*i)->date, now) < 0)
- balance->credit((*x)->cost);
+ do_credit = true;
}
else if (show_cleared) {
if ((*i)->cleared)
- balance->credit((*x)->cost);
+ do_credit = true;
}
else {
+ do_credit = true;
+ }
+
+ if (do_credit) {
balance->credit((*x)->cost);
+
+ // If this is a top-level account, then update the total
+ // running balance as well.
+ if (! acct->parent)
+ total_balance.credit((*x)->cost);
}
if (no_subtotals)
@@ -104,6 +161,7 @@ void report_balances(int argc, char *argv[], std::ostream& out)
}
}
+#if 0
// Print out the balance report header
std::string which = "Future";
@@ -112,49 +170,23 @@ void report_balances(int argc, char *argv[], std::ostream& out)
else if (show_cleared)
which = "Cleared";
- std::cout.width(10);
- std::cout << std::right << which << std::endl;
-
- // 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;
+ out.width(20);
+ out << std::right << which << std::endl
+ << "--------------------" << std::endl;
+#endif
- if (! regexps.empty() && ! matches(regexps, acct->name))
- continue;
+ // Walk through all the top-level accounts, given the balance
+ // report for each, and then for each of their children.
- int depth = 0;
- for (account * a = acct; a; a = a->parent)
- depth++;
-
- if (! show_children && depth)
- continue;
-
- totals * balance = balances[acct];
-
- if (! show_empty && ! *balance)
- continue;
-
- std::cout.width(10);
- std::cout << *balance << " ";
-
- total_balance.credit(*balance);
-
- if (depth) {
- while (--depth >= 0)
- std::cout << " ";
- std::cout << acct->name << std::endl;
- } else {
- std::cout << *acct << std::endl;
- }
- }
+ for (accounts_iterator i = accounts.begin(); i != accounts.end(); i++)
+ if (! (*i).second->parent)
+ display_total(out, (*i).second, balances);
// Print the total of all the balances shown
- std::cout.width(10);
- std::cout << std::right << total_balance << std::endl;
+ if (! no_subtotals)
+ out << "--------------------" << std::endl
+ << total_balance << std::endl;
// Free up temporary variables created on the heap
diff --git a/gnucash.cc b/gnucash.cc
index dde18442..f4c48042 100644
--- a/gnucash.cc
+++ b/gnucash.cc
@@ -18,6 +18,8 @@ static amount * curr_value;
static std::string curr_quant;
static XML_Parser current_parser;
+accounts_t accounts_by_id;
+
enum {
NO_ACTION,
ACCOUNT_NAME,
@@ -96,7 +98,7 @@ static void endElement(void *userData, const char *name)
if (std::strcmp(name, "gnc:account") == 0) {
assert(curr_account);
accounts.insert(accounts_entry(curr_account->name, curr_account));
- accounts.insert(accounts_entry(curr_account_id, curr_account));
+ accounts_by_id.insert(accounts_entry(curr_account_id, curr_account));
curr_account = NULL;
}
else if (std::strcmp(name, "gnc:commodity") == 0) {
@@ -131,8 +133,8 @@ static void dataHandler(void *userData, const char *s, int len)
break;
case ACCOUNT_PARENT: {
- accounts_iterator i = accounts.find(std::string(s, len));
- assert(i != accounts.end());
+ accounts_iterator i = accounts_by_id.find(std::string(s, len));
+ assert(i != accounts_by_id.end());
curr_account->parent = (*i).second;
(*i).second->children.insert(account::pair(curr_account->name,
curr_account));
@@ -187,8 +189,8 @@ static void dataHandler(void *userData, const char *s, int len)
break;
case XACT_ACCOUNT: {
- accounts_iterator i = accounts.find(std::string(s, len));
- if (i == accounts.end()) {
+ accounts_iterator i = accounts_by_id.find(std::string(s, len));
+ if (i == accounts_by_id.end()) {
std::cerr << "Could not find account " << std::string(s, len)
<< " at line " << XML_GetCurrentLineNumber(current_parser)
<< std::endl;
@@ -246,6 +248,10 @@ bool parse_gnucash(std::istream& in)
}
XML_ParserFree(parser);
+ accounts_by_id.clear();
+ curr_account_id.clear();
+ curr_quant.clear();
+
return true;
}
diff --git a/ledger.cc b/ledger.cc
index e2d7ed35..301e109b 100644
--- a/ledger.cc
+++ b/ledger.cc
@@ -3,10 +3,11 @@
namespace ledger {
commodities_t commodities;
-commodity * commodity_usd;
accounts_t accounts;
ledger_t ledger;
+bool use_warnings = false;
+
void entry::print(std::ostream& out) const
{
char buf[32];
@@ -41,7 +42,7 @@ void entry::print(std::ostream& out) const
out << std::left << acct_name << " ";
out.width(10);
- out << std::right << *((*i)->cost);
+ out << std::right << (*i)->cost->as_str(true);
if (! (*i)->note.empty())
out << " ; " << (*i)->note;
@@ -61,8 +62,9 @@ bool entry::validate() const
}
if (balance) {
- std::cout << "Totals are:" << std::endl;
- balance.print(std::cout);
+ std::cerr << "Totals are:" << std::endl;
+ balance.print(std::cerr);
+ std::cerr << std::endl;
}
return ! balance; // must balance to 0.0
}
@@ -86,8 +88,17 @@ totals::operator bool() const
void totals::print(std::ostream& out) const
{
- for (const_iterator_t i = amounts.begin(); i != amounts.end(); i++)
- std::cout << (*i).first << " = " << *((*i).second);
+ bool first = true;
+ for (const_iterator_t i = amounts.begin(); i != amounts.end(); i++)
+ if (*((*i).second)) {
+ if (first)
+ first = false;
+ else
+ out << std::endl;
+
+ out.width(20);
+ out << std::right << *((*i).second);
+ }
}
amount * totals::value(const std::string& commodity)
diff --git a/ledger.h b/ledger.h
index 8cee10df..7e9a48f5 100644
--- a/ledger.h
+++ b/ledger.h
@@ -1,5 +1,5 @@
#ifndef _LEDGER_H
-#define _LEDGER_H "$Revision: 1.3 $"
+#define _LEDGER_H "$Revision: 1.4 $"
//////////////////////////////////////////////////////////////////////
//
@@ -90,11 +90,15 @@ struct commodity
bool prefix;
bool separate;
+ bool thousands;
+ bool european;
int precision;
- commodity() : prefix(false), separate(true) {}
- commodity(const std::string& sym, bool pre, bool sep, int prec);
+ commodity() : prefix(false), separate(true),
+ thousands(false), european(false) {}
+ commodity(const std::string& sym, bool pre, bool sep,
+ bool thou, bool euro, int prec);
};
typedef std::map<const std::string, commodity *> commodities_t;
@@ -102,11 +106,11 @@ typedef commodities_t::iterator commodities_iterator;
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) {
+inline commodity::commodity(const std::string& sym, bool pre, bool sep,
+ bool thou, bool euro, int prec)
+ : symbol(sym), prefix(pre), separate(sep),
+ thousands(thou), european(euro), precision(prec) {
std::pair<commodities_iterator, bool> result =
commodities.insert(commodities_entry(sym, this));
assert(result.second);
@@ -129,12 +133,14 @@ class amount
// Assignment
virtual void credit(const amount * other) = 0;
+ virtual void negate() = 0;
virtual void operator+=(const amount& other) = 0;
// String conversion routines
virtual void parse(const char * num) = 0;
virtual amount& operator=(const char * num) = 0;
+ virtual std::string as_str(bool full_prec = false) const = 0;
virtual operator std::string() const = 0;
};
@@ -272,6 +278,8 @@ typedef std::pair<const std::string, account *> accounts_entry;
extern accounts_t accounts;
+extern bool use_warnings;
+
} // namespace ledger
#endif // _LEDGER_H
diff --git a/main.cc b/main.cc
index 29fdcaa2..00d900d2 100644
--- a/main.cc
+++ b/main.cc
@@ -13,7 +13,7 @@ namespace 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 report_balances(int argc, char **argv, std::ostream& out);
extern void print_ledger(int argc, char *argv[], std::ostream& out);
}
@@ -35,15 +35,16 @@ int main(int argc, char *argv[])
{
// Global defaults
- commodity_usd = new commodity("$", true, false, 2);
- commodities.insert(commodities_entry("USD", commodity_usd));
+ commodity * usd = new commodity("$", true, false, true, false, 2);
+ commodities.insert(commodities_entry("USD", usd));
// Parse the command-line options
int c;
- while (-1 != (c = getopt(argc, argv, "+h"))) {
+ while (-1 != (c = getopt(argc, argv, "+hw"))) {
switch (char(c)) {
case 'h': show_help(std::cout); break;
+ case 'w': use_warnings = true; break;
}
}
diff --git a/parse.cc b/parse.cc
index 5e134dc2..749e3cbd 100644
--- a/parse.cc
+++ b/parse.cc
@@ -135,13 +135,49 @@ bool parse_ledger(std::istream& in)
else if (std::isspace(line[0])) {
transaction * xact = new transaction();
- xact->cost = create_amount(next_element(line, true));
+ char * p = line;
+ while (std::isspace(*p))
+ p++;
+
+ // The call to `next_element' will skip past the account name,
+ // and return a pointer to the beginning of the amount. Once
+ // we know where the amount is, we can strip off any
+ // transaction note, and parse it.
+
+ char * cost_str = next_element(p, true);
+ char * note_str;
+
+ // If there is no amount given, it is intended as an implicit
+ // amount; we must use the opposite of the value of the
+ // preceding transaction.
+ if (! cost_str || *cost_str == ';') {
+ if (cost_str) {
+ while (*cost_str == ';' || std::isspace(*cost_str))
+ cost_str++;
+ xact->note = cost_str;
+ }
+ xact->cost = curr->xacts.back()->cost->copy();
+ xact->cost->negate();
+ }
+ else {
+ note_str = std::strchr(cost_str, ';');
+ if (note_str) {
+ *note_str++ = '\0';
+ while (std::isspace(*note_str))
+ note_str++;
+ xact->note = note_str;
+ }
+
+ for (char * t = cost_str + (std::strlen(cost_str) - 1);
+ std::isspace(*t);
+ t--)
+ *t = '\0';
+
+ xact->cost = create_amount(cost_str);
+ }
- // jww (2003-09-28): Reverse parse the account name to find the
- // correct account. This means that each account needs to know
- // its children.
account * current = NULL;
- for (char * tok = std::strtok(line, ":");
+ for (char * tok = std::strtok(p, ":");
tok;
tok = std::strtok(NULL, ":")) {
if (! current) {