Checked in all major updates.

This commit is contained in:
John Wiegley 2006-02-27 23:52:31 +00:00
parent 4507573d4f
commit 7901598f1d
30 changed files with 1934 additions and 1050 deletions

View file

@ -7,6 +7,7 @@ libledger_la_SOURCES = \
config.cc \ config.cc \
datetime.cc \ datetime.cc \
derive.cc \ derive.cc \
emacs.cc \
format.cc \ format.cc \
journal.cc \ journal.cc \
mask.cc \ mask.cc \
@ -20,10 +21,6 @@ libledger_la_SOURCES = \
valexpr.cc \ valexpr.cc \
value.cc \ value.cc \
walk.cc walk.cc
if USE_EDITOR
libledger_la_CXXFLAGS += -DUSE_EDITOR=1
libledger_la_SOURCES += emacs.cc
endif
if HAVE_EXPAT if HAVE_EXPAT
libledger_la_CXXFLAGS += -DHAVE_EXPAT=1 libledger_la_CXXFLAGS += -DHAVE_EXPAT=1
libledger_la_SOURCES += gnucash.cc xml.cc libledger_la_SOURCES += gnucash.cc xml.cc
@ -77,9 +74,6 @@ bin_PROGRAMS = ledger
ledger_CXXFLAGS = ledger_CXXFLAGS =
ledger_SOURCES = main.cc ledger_SOURCES = main.cc
ledger_LDADD = $(LIBOBJS) libledger.la ledger_LDADD = $(LIBOBJS) libledger.la
if USE_EDITOR
ledger_CXXFLAGS += -DUSE_EDITOR=1
endif
if HAVE_EXPAT if HAVE_EXPAT
ledger_CXXFLAGS += -DHAVE_EXPAT=1 ledger_CXXFLAGS += -DHAVE_EXPAT=1
ledger_LDADD += -lexpat ledger_LDADD += -lexpat

96
NEWS
View file

@ -3,18 +3,87 @@
* 2.5 * 2.5
- Added a new "csv" command, for outputting results in CSV format. - There have a few changes to value expression syntax. The most
significant incompatibilities being:
* Equality is now ==, not =
* The U, A, and S functions now requires parens around the argument.
Whereas before At was acceptable, now it must be specified as
A(t).
* The P function now always requires two arguments. The old
one-argument version P(x) is now the same as P(x,m).
The following value expression features are new:
* A C-like comma operator is supported, where all but the last term
are ignored. The is significant for the next feature:
* Function definitions are now supported. Scoping is governed
by parentheses. For example:
(x=100, x+10) ; yields 110 as the result
(f(x)=x*2,f(100)) ; yields 200 as the result
* Identifier names may be any length. Along with this support comes
alternate, longer names for all of the current one-letter value
expression variables:
Old New
--- ---
m now
a amount
a amount
b cost
i price
d date
X cleared
Y pending
R real
L actual
n index
N count
l depth
O total
B cost_total
I price_total
v market
V market_total
g gain
G gain_total
U(x) abs(x)
S(x) quant(x), quantity(x)
comm(x), commodity(x)
setcomm(x,y), set_commodity(x,y)
A(x) mean(x), avg(x), average(x)
P(x,y) val(x,y), value(x,y)
min(x,y)
max(x,y)
- There are new "parse" and "expr" commands, whose argument is a
single value expression. Ledger will simply print out the result of
evaluating it. "parse" happens before parsing your ledger file,
while "expr" happens afterward. Although "expr" is slower as a
result, any commodities you use will be formatted based on patterns
of usage seen in your ledger file.
These commands can be used to test value expressions, or for doing
calculation of commoditized amounts from a script.
A new "--debug" will also dump the resulting parse tree, useful for
submitting bug reports.
- Added new min(x,y) and max(x,y) value expression functions.
- Added a new value expression regexp command: - Added a new value expression regexp command:
C// compare against transaction amount's commodity symbol C// compare against a transaction amount's commodity symbol
- Added new @min(x,y) and @max(x,y) value expression functions. - Value expression function may now be defined within your ledger file
(or initialization file) using the following syntax:
- A new configure option "--disable-emacs" will disable generation of @def foo(x)=x*1000
transaction and entry location info, which is used by ledger.el and
the "write" command. If you use neither of these, then disabling This line makes the function "foo" available to all subsequent value
them will cut textual parsing time in half, and binary loading time expressions, to all command-line options taking a value expression,
(and cache file size) by a third. and to the new "expr" command (see above).
- Added a new "csv" command, for outputting results in CSV format.
- Effective dates may now be specified for entries: - Effective dates may now be specified for entries:
@ -74,28 +143,27 @@
transactions belong to the same entry. transactions belong to the same entry.
- Individual transactions may now be cleared separately. The old - Individual transactions may now be cleared separately. The old
syntax, which is still supported, clears all transactions in the syntax, which is still supported, clears all transactions in an
entry: entry:
2004/05/27 * Book Store 2004/05/27 * Book Store
Expenses:Dining $20.00 Expenses:Dining $20.00
Liabilities:MasterCard Liabilities:MasterCard
The new (and optional) syntax allows clearing just the MasterCard The new syntax allows clearing of just the MasterCard transaction:
transaction:
2004/05/27 Book Store 2004/05/27 Book Store
Expenses:Dining $20.00 Expenses:Dining $20.00
* Liabilities:MasterCard * Liabilities:MasterCard
NOTE: This changes the output format of the "emacs" and "xml" NOTE: This changes the output format of both the "emacs" and "xml"
reports. ledger.el will use the new syntax unless the Lisp variable reports. ledger.el uses the new syntax unless the Lisp variable
`ledger-clear-whole-entries' is set to t. `ledger-clear-whole-entries' is set to t.
- Removed Python integration support. - Removed Python integration support.
- Did much internal restructuring to allow the use of libledger.so in - Did much internal restructuring to allow the use of libledger.so in
non-command-line environments. non-command-line environments (such as GUI tools).
* 2.4 * 2.4

180
amount.cc
View file

@ -66,14 +66,14 @@ static struct _init_amounts {
commodity_t * commodity; commodity_t * commodity;
commodity = commodity_t::find_commodity("s", true); commodity = commodity_t::find_commodity("s", true);
commodity->flags |= COMMODITY_STYLE_NOMARKET | COMMODITY_STYLE_BUILTIN; commodity->flags() |= COMMODITY_STYLE_NOMARKET | COMMODITY_STYLE_BUILTIN;
parse_conversion("1.0m", "60s"); parse_conversion("1.0m", "60s");
parse_conversion("1.0h", "60m"); parse_conversion("1.0h", "60m");
#if 0 #if 0
commodity = commodity_t::find_commodity("b", true); commodity = commodity_t::find_commodity("b", true);
commodity->flags |= COMMODITY_STYLE_NOMARKET | COMMODITY_STYLE_BUILTIN; commodity->flags() |= COMMODITY_STYLE_NOMARKET | COMMODITY_STYLE_BUILTIN;
parse_conversion("1.00 Kb", "1024 b"); parse_conversion("1.00 Kb", "1024 b");
parse_conversion("1.00 Mb", "1024 Kb"); parse_conversion("1.00 Mb", "1024 Kb");
@ -417,7 +417,7 @@ amount_t& amount_t::operator*=(const amount_t& amt)
mpz_mul(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity)); mpz_mul(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
quantity->prec += amt.quantity->prec; quantity->prec += amt.quantity->prec;
unsigned int comm_prec = commodity().precision; unsigned int comm_prec = commodity().precision();
if (quantity->prec > comm_prec + 6U) { if (quantity->prec > comm_prec + 6U) {
mpz_round(MPZ(quantity), MPZ(quantity), quantity->prec, comm_prec + 6U); mpz_round(MPZ(quantity), MPZ(quantity), quantity->prec, comm_prec + 6U);
quantity->prec = comm_prec + 6U; quantity->prec = comm_prec + 6U;
@ -442,7 +442,7 @@ amount_t& amount_t::operator/=(const amount_t& amt)
mpz_tdiv_q(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity)); mpz_tdiv_q(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
quantity->prec += 6; quantity->prec += 6;
unsigned int comm_prec = commodity().precision; unsigned int comm_prec = commodity().precision();
if (quantity->prec > comm_prec + 6U) { if (quantity->prec > comm_prec + 6U) {
mpz_round(MPZ(quantity), MPZ(quantity), quantity->prec, comm_prec + 6U); mpz_round(MPZ(quantity), MPZ(quantity), quantity->prec, comm_prec + 6U);
quantity->prec = comm_prec + 6U; quantity->prec = comm_prec + 6U;
@ -504,12 +504,12 @@ amount_t::operator bool() const
if (! quantity) if (! quantity)
return false; return false;
if (quantity->prec <= commodity().precision) { if (quantity->prec <= commodity().precision()) {
return mpz_sgn(MPZ(quantity)) != 0; return mpz_sgn(MPZ(quantity)) != 0;
} else { } else {
mpz_set(temp, MPZ(quantity)); mpz_set(temp, MPZ(quantity));
if (commodity_) if (commodity_)
mpz_ui_pow_ui(divisor, 10, quantity->prec - commodity().precision); mpz_ui_pow_ui(divisor, 10, quantity->prec - commodity().precision());
else else
mpz_ui_pow_ui(divisor, 10, quantity->prec); mpz_ui_pow_ui(divisor, 10, quantity->prec);
mpz_tdiv_q(temp, temp, divisor); mpz_tdiv_q(temp, temp, divisor);
@ -559,9 +559,9 @@ amount_t amount_t::value(const std::time_t moment) const
{ {
if (quantity) { if (quantity) {
commodity_t& comm = commodity(); commodity_t& comm = commodity();
if (! (comm.flags & COMMODITY_STYLE_NOMARKET)) if (! (comm.flags() & COMMODITY_STYLE_NOMARKET))
if (amount_t amt = comm.value(moment)) if (amount_t amt = comm.value(moment))
return (amt * *this).round(amt.commodity().precision); return (amt * *this).round(amt.commodity().precision());
} }
return *this; return *this;
} }
@ -604,24 +604,23 @@ std::string amount_t::quantity_string() const
unsigned short precision; unsigned short precision;
if (comm == *commodity_t::null_commodity || if (comm == *commodity_t::null_commodity ||
comm.flags & COMMODITY_STYLE_VARIABLE) { comm.flags() & COMMODITY_STYLE_VARIABLE) {
mpz_ui_pow_ui(divisor, 10, quantity->prec); mpz_ui_pow_ui(divisor, 10, quantity->prec);
mpz_tdiv_qr(quotient, remainder, MPZ(quantity), divisor); mpz_tdiv_qr(quotient, remainder, MPZ(quantity), divisor);
precision = quantity->prec; precision = quantity->prec;
} }
else if (comm.precision < quantity->prec) { else if (comm.precision() < quantity->prec) {
mpz_round(rquotient, MPZ(quantity), quantity->prec, mpz_round(rquotient, MPZ(quantity), quantity->prec, comm.precision());
comm.precision); mpz_ui_pow_ui(divisor, 10, comm.precision());
mpz_ui_pow_ui(divisor, 10, comm.precision);
mpz_tdiv_qr(quotient, remainder, rquotient, divisor); mpz_tdiv_qr(quotient, remainder, rquotient, divisor);
precision = comm.precision; precision = comm.precision();
} }
else if (comm.precision > quantity->prec) { else if (comm.precision() > quantity->prec) {
mpz_ui_pow_ui(divisor, 10, comm.precision - quantity->prec); mpz_ui_pow_ui(divisor, 10, comm.precision() - quantity->prec);
mpz_mul(rquotient, MPZ(quantity), divisor); mpz_mul(rquotient, MPZ(quantity), divisor);
mpz_ui_pow_ui(divisor, 10, comm.precision); mpz_ui_pow_ui(divisor, 10, comm.precision());
mpz_tdiv_qr(quotient, remainder, rquotient, divisor); mpz_tdiv_qr(quotient, remainder, rquotient, divisor);
precision = comm.precision; precision = comm.precision();
} }
else if (quantity->prec) { else if (quantity->prec) {
mpz_ui_pow_ui(divisor, 10, quantity->prec); mpz_ui_pow_ui(divisor, 10, quantity->prec);
@ -682,11 +681,11 @@ std::ostream& operator<<(std::ostream& _out, const amount_t& amt)
} }
amount_t base(amt); amount_t base(amt);
if (amt.commodity().larger) { if (amt.commodity().larger()) {
amount_t last(amt); amount_t last(amt);
while (last.commodity().larger) { while (last.commodity().larger()) {
last /= *last.commodity().larger; last /= *last.commodity().larger();
last.commodity_ = last.commodity().larger->commodity_; last.commodity_ = last.commodity().larger()->commodity_;
if (ledger::abs(last) < 1) if (ledger::abs(last) < 1)
break; break;
base = last; base = last;
@ -712,24 +711,24 @@ std::ostream& operator<<(std::ostream& _out, const amount_t& amt)
unsigned short precision; unsigned short precision;
if (comm == *commodity_t::null_commodity || if (comm == *commodity_t::null_commodity ||
comm.flags & COMMODITY_STYLE_VARIABLE) { comm.flags() & COMMODITY_STYLE_VARIABLE) {
mpz_ui_pow_ui(divisor, 10, base.quantity->prec); mpz_ui_pow_ui(divisor, 10, base.quantity->prec);
mpz_tdiv_qr(quotient, remainder, MPZ(base.quantity), divisor); mpz_tdiv_qr(quotient, remainder, MPZ(base.quantity), divisor);
precision = base.quantity->prec; precision = base.quantity->prec;
} }
else if (comm.precision < base.quantity->prec) { else if (comm.precision() < base.quantity->prec) {
mpz_round(rquotient, MPZ(base.quantity), base.quantity->prec, mpz_round(rquotient, MPZ(base.quantity), base.quantity->prec,
comm.precision); comm.precision());
mpz_ui_pow_ui(divisor, 10, comm.precision); mpz_ui_pow_ui(divisor, 10, comm.precision());
mpz_tdiv_qr(quotient, remainder, rquotient, divisor); mpz_tdiv_qr(quotient, remainder, rquotient, divisor);
precision = comm.precision; precision = comm.precision();
} }
else if (comm.precision > base.quantity->prec) { else if (comm.precision() > base.quantity->prec) {
mpz_ui_pow_ui(divisor, 10, comm.precision - base.quantity->prec); mpz_ui_pow_ui(divisor, 10, comm.precision() - base.quantity->prec);
mpz_mul(rquotient, MPZ(base.quantity), divisor); mpz_mul(rquotient, MPZ(base.quantity), divisor);
mpz_ui_pow_ui(divisor, 10, comm.precision); mpz_ui_pow_ui(divisor, 10, comm.precision());
mpz_tdiv_qr(quotient, remainder, rquotient, divisor); mpz_tdiv_qr(quotient, remainder, rquotient, divisor);
precision = comm.precision; precision = comm.precision();
} }
else if (base.quantity->prec) { else if (base.quantity->prec) {
mpz_ui_pow_ui(divisor, 10, base.quantity->prec); mpz_ui_pow_ui(divisor, 10, base.quantity->prec);
@ -755,12 +754,18 @@ std::ostream& operator<<(std::ostream& _out, const amount_t& amt)
return _out; return _out;
} }
if (! (comm.flags & COMMODITY_STYLE_SUFFIXED)) { if (! (comm.flags() & COMMODITY_STYLE_SUFFIXED)) {
if (comm.quote) if (comm.quote) {
out << "\"" << comm.symbol << "\""; std::string::size_type idx = comm.symbol.find(" {", 0);
if (idx != std::string::npos)
out << "\"" << comm.symbol.substr(0, idx) << "\""
<< comm.symbol.substr(idx);
else else
out << "\"" << comm.symbol << "\"";
} else {
out << comm.symbol; out << comm.symbol;
if (comm.flags & COMMODITY_STYLE_SEPARATED) }
if (comm.flags() & COMMODITY_STYLE_SEPARATED)
out << " "; out << " ";
} }
@ -770,7 +775,7 @@ std::ostream& operator<<(std::ostream& _out, const amount_t& amt)
if (mpz_sgn(quotient) == 0) { if (mpz_sgn(quotient) == 0) {
out << '0'; out << '0';
} }
else if (! (comm.flags & COMMODITY_STYLE_THOUSANDS)) { else if (! (comm.flags() & COMMODITY_STYLE_THOUSANDS)) {
char * p = mpz_get_str(NULL, 10, quotient); char * p = mpz_get_str(NULL, 10, quotient);
out << p; out << p;
std::free(p); std::free(p);
@ -799,7 +804,7 @@ std::ostream& operator<<(std::ostream& _out, const amount_t& amt)
i != strs.rend(); i != strs.rend();
i++) { i++) {
if (printed) { if (printed) {
out << (comm.flags & COMMODITY_STYLE_EUROPEAN ? '.' : ','); out << (comm.flags() & COMMODITY_STYLE_EUROPEAN ? '.' : ',');
out.width(3); out.width(3);
out.fill('0'); out.fill('0');
} }
@ -810,7 +815,7 @@ std::ostream& operator<<(std::ostream& _out, const amount_t& amt)
} }
if (precision) { if (precision) {
out << ((comm.flags & COMMODITY_STYLE_EUROPEAN) ? ',' : '.'); out << ((comm.flags() & COMMODITY_STYLE_EUROPEAN) ? ',' : '.');
out.width(precision); out.width(precision);
out.fill('0'); out.fill('0');
@ -820,14 +825,20 @@ std::ostream& operator<<(std::ostream& _out, const amount_t& amt)
std::free(p); std::free(p);
} }
if (comm.flags & COMMODITY_STYLE_SUFFIXED) { if (comm.flags() & COMMODITY_STYLE_SUFFIXED) {
if (comm.flags & COMMODITY_STYLE_SEPARATED) if (comm.flags() & COMMODITY_STYLE_SEPARATED)
out << " "; out << " ";
if (comm.quote) if (comm.quote) {
out << "\"" << comm.symbol << "\""; std::string::size_type idx = comm.symbol.find(" {", 0);
if (idx != std::string::npos)
out << "\"" << comm.symbol.substr(0, idx) << "\""
<< comm.symbol.substr(idx);
else else
out << "\"" << comm.symbol << "\"";
} else {
out << comm.symbol; out << comm.symbol;
} }
}
mpz_clear(quotient); mpz_clear(quotient);
mpz_clear(rquotient); mpz_clear(rquotient);
@ -878,6 +889,7 @@ void amount_t::parse(std::istream& in, unsigned short flags)
std::string symbol; std::string symbol;
std::string quant; std::string quant;
std::string price;
unsigned int comm_flags = COMMODITY_STYLE_DEFAULTS; unsigned int comm_flags = COMMODITY_STYLE_DEFAULTS;
bool negative = false; bool negative = false;
@ -923,6 +935,34 @@ void amount_t::parse(std::istream& in, unsigned short flags)
commodity_ = commodity_t::find_commodity(symbol, true); commodity_ = commodity_t::find_commodity(symbol, true);
// If a per-unit price is specified for this amount, record it by
// creating a specialized commodity at that price. This is a
// different from the whole transaction cost, which is associated
// with the transaction and not with the amount. For example, a
// sale of 10 AAPL shares for $100 (the cost) is a different thing
// from selling 10 AAPL {$10} (where $10 is the commodity price) for
// $50, which implies a capital loss of $50.
if (peek_next_nonws(in) == '{') {
char c;
char buf[256];
in.get(c);
READ_INTO(in, buf, 255, c, c != '}');
if (c == '}')
in.get(c);
else
throw amount_error("Commodity price lacks closing brace");
symbol = symbol + " {" + buf + "}";
commodity_t * priced_commodity =
commodity_t::find_commodity(symbol, true);
priced_commodity->price = new amount_t(buf);
priced_commodity->base = commodity_;
commodity_ = priced_commodity;
}
// Determine the precision of the amount, based on the usage of // Determine the precision of the amount, based on the usage of
// comma or period. // comma or period.
@ -940,12 +980,12 @@ void amount_t::parse(std::istream& in, unsigned short flags)
} }
else if (last_comma != std::string::npos && else if (last_comma != std::string::npos &&
(! commodity_t::default_commodity || (! commodity_t::default_commodity ||
commodity_t::default_commodity->flags & COMMODITY_STYLE_EUROPEAN)) { commodity_t::default_commodity->flags() & COMMODITY_STYLE_EUROPEAN)) {
comm_flags |= COMMODITY_STYLE_EUROPEAN; comm_flags |= COMMODITY_STYLE_EUROPEAN;
quantity->prec = quant.length() - last_comma - 1; quantity->prec = quant.length() - last_comma - 1;
} }
else if (last_period != std::string::npos && else if (last_period != std::string::npos &&
! (commodity().flags & COMMODITY_STYLE_EUROPEAN)) { ! (commodity().flags() & COMMODITY_STYLE_EUROPEAN)) {
quantity->prec = quant.length() - last_period - 1; quantity->prec = quant.length() - last_period - 1;
} }
else { else {
@ -955,9 +995,9 @@ void amount_t::parse(std::istream& in, unsigned short flags)
// Set the commodity's flags and precision accordingly // Set the commodity's flags and precision accordingly
if (newly_created || ! (flags & AMOUNT_PARSE_NO_MIGRATE)) { if (newly_created || ! (flags & AMOUNT_PARSE_NO_MIGRATE)) {
commodity().flags |= comm_flags; commodity().flags() |= comm_flags;
if (quantity->prec > commodity().precision) if (quantity->prec > commodity().precision())
commodity().precision = quantity->prec; commodity().precision() = quantity->prec;
} }
// Now we have the final number. Remove commas and periods, if // Now we have the final number. Remove commas and periods, if
@ -991,9 +1031,9 @@ void amount_t::parse(std::istream& in, unsigned short flags)
void amount_t::reduce() void amount_t::reduce()
{ {
while (commodity_ && commodity().smaller) { while (commodity_ && commodity().smaller()) {
*this *= *commodity().smaller; *this *= *commodity().smaller();
commodity_ = commodity().smaller->commodity_; commodity_ = commodity().smaller()->commodity_;
} }
} }
@ -1014,12 +1054,12 @@ void parse_conversion(const std::string& larger_str,
larger *= smaller; larger *= smaller;
if (larger.commodity()) { if (larger.commodity()) {
larger.commodity().smaller = new amount_t(smaller); larger.commodity().smaller() = new amount_t(smaller);
larger.commodity().flags = (smaller.commodity().flags | larger.commodity().flags() = (smaller.commodity().flags() |
COMMODITY_STYLE_NOMARKET); COMMODITY_STYLE_NOMARKET);
} }
if (smaller.commodity()) if (smaller.commodity())
smaller.commodity().larger = new amount_t(larger); smaller.commodity().larger() = new amount_t(larger);
} }
@ -1161,6 +1201,8 @@ void commodity_t::set_symbol(const std::string& sym)
quote = false; quote = false;
for (const char * p = symbol.c_str(); *p; p++) for (const char * p = symbol.c_str(); *p; p++)
if (std::isspace(*p) || std::isdigit(*p) || *p == '-' || *p == '.') { if (std::isspace(*p) || std::isdigit(*p) || *p == '-' || *p == '.') {
if (std::isspace(*p) && *(p + 1) == '{')
return;
quote = true; quote = true;
return; return;
} }
@ -1168,15 +1210,15 @@ void commodity_t::set_symbol(const std::string& sym)
void commodity_t::add_price(const std::time_t date, const amount_t& price) void commodity_t::add_price(const std::time_t date, const amount_t& price)
{ {
if (! history) if (! history())
history = new history_t; history() = new history_t;
history_map::iterator i = history->prices.find(date); history_map::iterator i = history()->prices.find(date);
if (i != history->prices.end()) { if (i != history()->prices.end()) {
(*i).second = price; (*i).second = price;
} else { } else {
std::pair<history_map::iterator, bool> result std::pair<history_map::iterator, bool> result
= history->prices.insert(history_pair(date, price)); = history()->prices.insert(history_pair(date, price));
assert(result.second); assert(result.second);
} }
} }
@ -1195,8 +1237,8 @@ commodity_t * commodity_t::find_commodity(const std::string& symbol,
// Start out the new commodity with the default commodity's flags // Start out the new commodity with the default commodity's flags
// and precision, if one has been defined. // and precision, if one has been defined.
if (default_commodity) if (default_commodity)
commodity->flags = commodity->flags() =
(default_commodity->flags & ~(COMMODITY_STYLE_THOUSANDS | (default_commodity->flags() & ~(COMMODITY_STYLE_THOUSANDS |
COMMODITY_STYLE_NOMARKET)); COMMODITY_STYLE_NOMARKET));
return commodity; return commodity;
@ -1210,23 +1252,23 @@ amount_t commodity_t::value(const std::time_t moment)
std::time_t age = 0; std::time_t age = 0;
amount_t price; amount_t price;
if (history) { if (history()) {
assert(history->prices.size() > 0); assert(history()->prices.size() > 0);
if (moment == 0) { if (moment == 0) {
history_map::reverse_iterator r = history->prices.rbegin(); history_map::reverse_iterator r = history()->prices.rbegin();
age = (*r).first; age = (*r).first;
price = (*r).second; price = (*r).second;
} else { } else {
history_map::iterator i = history->prices.lower_bound(moment); history_map::iterator i = history()->prices.lower_bound(moment);
if (i == history->prices.end()) { if (i == history()->prices.end()) {
history_map::reverse_iterator r = history->prices.rbegin(); history_map::reverse_iterator r = history()->prices.rbegin();
age = (*r).first; age = (*r).first;
price = (*r).second; price = (*r).second;
} else { } else {
age = (*i).first; age = (*i).first;
if (std::difftime(moment, age) != 0) { if (std::difftime(moment, age) != 0) {
if (i != history->prices.begin()) { if (i != history()->prices.begin()) {
--i; --i;
age = (*i).first; age = (*i).first;
price = (*i).second; price = (*i).second;
@ -1242,8 +1284,8 @@ amount_t commodity_t::value(const std::time_t moment)
if (updater) if (updater)
(*updater)(*this, moment, age, (*updater)(*this, moment, age,
(history && history->prices.size() > 0 ? (history() && history()->prices.size() > 0 ?
(*history->prices.rbegin()).first : 0), price); (*history()->prices.rbegin()).first : 0), price);
return price; return price;
} }

View file

@ -73,6 +73,7 @@ class amount_t
void clear_commodity() { void clear_commodity() {
commodity_ = NULL; commodity_ = NULL;
} }
amount_t base_amount() const;
bool null() const { bool null() const {
return ! quantity && ! commodity_; return ! quantity && ! commodity_;
@ -316,16 +317,42 @@ class commodity_t
std::time_t last_lookup; std::time_t last_lookup;
}; };
history_t * history_;
history_t *& history() {
return base ? base->history() : history_;
}
const std::string symbol; const std::string symbol;
bool quote; bool quote;
std::string name; std::string name_;
std::string note; std::string note_;
unsigned short precision; unsigned short precision_;
unsigned short flags; unsigned short flags_;
ident_t ident; ident_t ident;
history_t * history; amount_t * smaller_;
amount_t * smaller; amount_t * larger_;
amount_t * larger; commodity_t * base; // base commodity for AAPL {$10} is AAPL
amount_t * price; // its price is therefore $10.00
std::string& name() {
return base ? base->name() : name_;
}
std::string& note() {
return base ? base->note() : note_;
}
unsigned short& precision() {
return base ? base->precision() : precision_;
}
unsigned short& flags() {
return base ? base->flags() : flags_;
}
amount_t *& smaller() {
return base ? base->smaller() : smaller_;
}
amount_t *& larger() {
return base ? base->larger() : larger_;
}
// If set, this global function pointer is called to determine // If set, this global function pointer is called to determine
// whether prices have been updated in the meanwhile. // whether prices have been updated in the meanwhile.
@ -361,14 +388,15 @@ class commodity_t
commodity_t(const std::string& _symbol = "", commodity_t(const std::string& _symbol = "",
unsigned int _precision = 0, unsigned int _precision = 0,
unsigned int _flags = COMMODITY_STYLE_DEFAULTS) unsigned int _flags = COMMODITY_STYLE_DEFAULTS)
: precision(_precision), flags(_flags), history(NULL), : precision_(_precision), flags_(_flags), history_(NULL),
smaller(NULL), larger(NULL) { smaller_(NULL), larger_(NULL), base(NULL), price(NULL) {
set_symbol(_symbol); set_symbol(_symbol);
} }
~commodity_t() { ~commodity_t() {
if (history) delete history; if (history_) delete history_;
if (smaller) delete smaller; if (smaller_) delete smaller_;
if (larger) delete larger; if (larger_) delete larger_;
if (price) delete price;
} }
operator bool() const { operator bool() const {
@ -385,8 +413,8 @@ class commodity_t
void add_price(const std::time_t date, const amount_t& price); void add_price(const std::time_t date, const amount_t& price);
bool remove_price(const std::time_t date) { bool remove_price(const std::time_t date) {
if (history) { if (history_) {
history_map::size_type n = history->prices.erase(date); history_map::size_type n = history_->prices.erase(date);
return n > 0; return n > 0;
} }
return false; return false;
@ -398,7 +426,7 @@ class commodity_t
if (symbol.empty() && this != null_commodity) if (symbol.empty() && this != null_commodity)
return false; return false;
if (precision > 16) if (precision_ > 16)
return false; return false;
return true; return true;
@ -417,6 +445,17 @@ inline commodity_t& amount_t::commodity() const {
return *commodity_; return *commodity_;
} }
inline amount_t amount_t::base_amount() const {
if (commodity_ && commodity_->price) {
amount_t temp(*this);
assert(commodity_->base);
temp.set_commodity(*(commodity_->base));
return temp;
} else {
return *this;
}
}
class amount_error : public std::exception { class amount_error : public std::exception {
std::string reason; std::string reason;
public: public:

View file

@ -34,6 +34,22 @@ balance_t balance_t::value(const std::time_t moment) const
return temp; return temp;
} }
balance_t balance_t::factor_price() const
{
balance_t temp;
for (amounts_map::const_iterator i = amounts.begin();
i != amounts.end();
i++) {
if ((*i).second.commodity().price)
temp += *((*i).second.commodity().price) * (*i).second;
else
temp += (*i).second;
}
return temp;
}
struct compare_amount_commodities { struct compare_amount_commodities {
bool operator()(const amount_t * left, const amount_t * right) const { bool operator()(const amount_t * left, const amount_t * right) const {
return left->commodity().symbol < right->commodity().symbol; return left->commodity().symbol < right->commodity().symbol;

View file

@ -426,6 +426,7 @@ class balance_t
amount_t amount(const commodity_t& commodity) const; amount_t amount(const commodity_t& commodity) const;
balance_t value(const std::time_t moment) const; balance_t value(const std::time_t moment) const;
balance_t factor_price() const;
void write(std::ostream& out, void write(std::ostream& out,
const int first_width, const int first_width,
@ -443,7 +444,7 @@ class balance_t
i != amounts.end(); i != amounts.end();
i++) i++)
if ((*i).second.commodity()) if ((*i).second.commodity())
(*i).second = (*i).second.round((*i).second.commodity().precision); (*i).second = (*i).second.round((*i).second.commodity().precision());
} }
}; };

160
binary.cc
View file

@ -11,18 +11,10 @@
namespace ledger { namespace ledger {
static unsigned long binary_magic_number = 0xFFEED765; static unsigned long binary_magic_number = 0xFFEED765;
#ifdef USE_EDITOR
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
static unsigned long format_version = 0x00020589; static unsigned long format_version = 0x0002050b;
#else #else
static unsigned long format_version = 0x00020588; static unsigned long format_version = 0x0002050a;
#endif
#else
#ifdef DEBUG_ENABLED
static unsigned long format_version = 0x00020509;
#else
static unsigned long format_version = 0x00020508;
#endif
#endif #endif
static account_t ** accounts; static account_t ** accounts;
@ -272,8 +264,10 @@ inline void read_binary_value_expr(char *& data, value_expr_t *& expr)
expr = new value_expr_t(kind); expr = new value_expr_t(kind);
if (kind > value_expr_t::TERMINALS) {
read_binary_value_expr(data, expr->left); read_binary_value_expr(data, expr->left);
read_binary_value_expr(data, expr->right); if (expr->left) expr->left->acquire();
}
switch (expr->kind) { switch (expr->kind) {
case value_expr_t::CONSTANT_T: case value_expr_t::CONSTANT_T:
@ -283,15 +277,30 @@ inline void read_binary_value_expr(char *& data, value_expr_t *& expr)
read_binary_long(data, expr->constant_i); read_binary_long(data, expr->constant_i);
break; break;
case value_expr_t::CONSTANT_A: case value_expr_t::CONSTANT_A:
read_binary_amount(data, expr->constant_a); expr->constant_a = new amount_t();
read_binary_amount(data, *(expr->constant_a));
break; break;
case value_expr_t::F_FUNC: case value_expr_t::CONSTANT_V:
read_binary_string(data, expr->constant_s); assert(0);
break; break;
}
case value_expr_t::F_CODE_MASK:
case value_expr_t::F_PAYEE_MASK:
case value_expr_t::F_NOTE_MASK:
case value_expr_t::F_ACCOUNT_MASK:
case value_expr_t::F_SHORT_ACCOUNT_MASK:
case value_expr_t::F_COMMODITY_MASK:
if (read_binary_number<unsigned char>(data) == 1) if (read_binary_number<unsigned char>(data) == 1)
read_binary_mask(data, expr->mask); read_binary_mask(data, expr->mask);
break;
default:
if (kind > value_expr_t::TERMINALS) {
read_binary_value_expr(data, expr->right);
if (expr->right) expr->right->acquire();
}
break;
}
} }
@ -301,18 +310,22 @@ inline void read_binary_transaction(char *& data, transaction_t * xact)
read_binary_long(data, xact->_date_eff); read_binary_long(data, xact->_date_eff);
xact->account = accounts[read_binary_long<account_t::ident_t>(data) - 1]; xact->account = accounts[read_binary_long<account_t::ident_t>(data) - 1];
if (read_binary_number<char>(data) == 1) if (read_binary_number<char>(data) == 1) {
read_binary_value_expr(data, xact->amount_expr); read_binary_value_expr(data, xact->amount_expr);
else if (xact->amount_expr) xact->amount_expr->acquire();
} else {
read_binary_amount(data, xact->amount); read_binary_amount(data, xact->amount);
}
if (*data++ == 1) { if (*data++ == 1) {
xact->cost = new amount_t; xact->cost = new amount_t;
if (read_binary_number<char>(data) == 1) if (read_binary_number<char>(data) == 1) {
read_binary_value_expr(data, xact->cost_expr); read_binary_value_expr(data, xact->cost_expr);
else if (xact->cost_expr) xact->cost_expr->acquire();
} else {
read_binary_amount(data, *xact->cost); read_binary_amount(data, *xact->cost);
}
} else { } else {
xact->cost = NULL; xact->cost = NULL;
} }
@ -322,12 +335,10 @@ inline void read_binary_transaction(char *& data, transaction_t * xact)
xact->flags |= TRANSACTION_BULK_ALLOC; xact->flags |= TRANSACTION_BULK_ALLOC;
read_binary_string(data, &xact->note); read_binary_string(data, &xact->note);
#ifdef USE_EDITOR
xact->beg_pos = read_binary_long<unsigned long>(data); xact->beg_pos = read_binary_long<unsigned long>(data);
read_binary_long(data, xact->beg_line); read_binary_long(data, xact->beg_line);
xact->end_pos = read_binary_long<unsigned long>(data); xact->end_pos = read_binary_long<unsigned long>(data);
read_binary_long(data, xact->end_line); read_binary_long(data, xact->end_line);
#endif
xact->data = NULL; xact->data = NULL;
@ -340,13 +351,11 @@ inline void read_binary_transaction(char *& data, transaction_t * xact)
inline void read_binary_entry_base(char *& data, entry_base_t * entry, inline void read_binary_entry_base(char *& data, entry_base_t * entry,
transaction_t *& xact_pool, bool& finalize) transaction_t *& xact_pool, bool& finalize)
{ {
#ifdef USE_EDITOR
read_binary_long(data, entry->src_idx); read_binary_long(data, entry->src_idx);
entry->beg_pos = read_binary_long<unsigned long>(data); entry->beg_pos = read_binary_long<unsigned long>(data);
read_binary_long(data, entry->beg_line); read_binary_long(data, entry->beg_line);
entry->end_pos = read_binary_long<unsigned long>(data); entry->end_pos = read_binary_long<unsigned long>(data);
read_binary_long(data, entry->end_line); read_binary_long(data, entry->end_line);
#endif
bool ignore_calculated = read_binary_number<char>(data) == 1; bool ignore_calculated = read_binary_number<char>(data) == 1;
@ -376,8 +385,10 @@ inline void read_binary_auto_entry(char *& data, auto_entry_t * entry,
{ {
bool ignore; bool ignore;
read_binary_entry_base(data, entry, xact_pool, ignore); read_binary_entry_base(data, entry, xact_pool, ignore);
read_binary_string(data, &entry->predicate_string); value_expr_t * expr;
entry->predicate = new item_predicate<transaction_t>(entry->predicate_string); read_binary_value_expr(data, expr);
// the item_predicate constructor will acquire the reference
entry->predicate = new item_predicate<transaction_t>(expr);
} }
inline void read_binary_period_entry(char *& data, period_entry_t * entry, inline void read_binary_period_entry(char *& data, period_entry_t * entry,
@ -396,10 +407,10 @@ inline commodity_t * read_binary_commodity(char *& data)
read_binary_string(data, *(const_cast<std::string *>(&commodity->symbol))); read_binary_string(data, *(const_cast<std::string *>(&commodity->symbol)));
read_binary_number(data, commodity->quote); read_binary_number(data, commodity->quote);
read_binary_string(data, commodity->name); read_binary_string(data, commodity->name_);
read_binary_string(data, commodity->note); read_binary_string(data, commodity->note_);
read_binary_number(data, commodity->precision); read_binary_number(data, commodity->precision_);
read_binary_number(data, commodity->flags); read_binary_number(data, commodity->flags_);
read_binary_long(data, commodity->ident); read_binary_long(data, commodity->ident);
return commodity; return commodity;
@ -421,12 +432,12 @@ inline void read_binary_commodity_extra(char *& data,
// Upon insertion, amt will be copied, which will cause the amount // Upon insertion, amt will be copied, which will cause the amount
// to be duplicated (and thus not lost when the journal's // to be duplicated (and thus not lost when the journal's
// item_pool is deleted. // item_pool is deleted.
if (! commodity->history) if (! commodity->history_)
commodity->history = new commodity_t::history_t; commodity->history_ = new commodity_t::history_t;
commodity->history->prices.insert(history_pair(when, amt)); commodity->history_->prices.insert(history_pair(when, amt));
} }
if (commodity->history) if (commodity->history_)
read_binary_long(data, commodity->history->last_lookup); read_binary_long(data, commodity->history_->last_lookup);
unsigned char flag; unsigned char flag;
@ -434,14 +445,23 @@ inline void read_binary_commodity_extra(char *& data,
if (flag) { if (flag) {
amount_t amt; amount_t amt;
read_binary_amount(data, amt); read_binary_amount(data, amt);
commodity->smaller = new amount_t(amt); commodity->smaller_ = new amount_t(amt);
} }
flag = read_binary_number<unsigned char>(data); flag = read_binary_number<unsigned char>(data);
if (flag) { if (flag) {
amount_t amt; amount_t amt;
read_binary_amount(data, amt); read_binary_amount(data, amt);
commodity->larger = new amount_t(amt); commodity->larger_ = new amount_t(amt);
}
flag = read_binary_number<unsigned char>(data);
if (flag) {
amount_t amt;
read_binary_amount(data, amt);
commodity->price = new amount_t(amt);
commodity->base =
commodities[read_binary_long<commodity_t::ident_t>(data) - 1];
} }
} }
@ -570,7 +590,7 @@ unsigned int read_binary_journal(std::istream& in,
commodities = commodities_next = new commodity_t *[c_count]; commodities = commodities_next = new commodity_t *[c_count];
for (commodity_t::ident_t i = 0; i < c_count; i++) { for (commodity_t::ident_t i = 0; i < c_count; i++) {
commodity_t * commodity = read_binary_commodity(data); commodity_t * commodity = read_binary_commodity(data);
if (! (commodity->flags & COMMODITY_STYLE_BUILTIN)) { if (! (commodity->flags_ & COMMODITY_STYLE_BUILTIN)) {
if (commodity->symbol == "") { if (commodity->symbol == "") {
commodity_t::commodities.erase(commodity->symbol); commodity_t::commodities.erase(commodity->symbol);
delete commodity_t::null_commodity; delete commodity_t::null_commodity;
@ -737,7 +757,7 @@ void write_binary_mask(std::ostream& out, mask_t * mask)
write_binary_string(out, mask->pattern); write_binary_string(out, mask->pattern);
} }
void write_binary_value_expr(std::ostream& out, value_expr_t * expr) void write_binary_value_expr(std::ostream& out, const value_expr_t * expr)
{ {
if (expr == NULL) { if (expr == NULL) {
write_binary_number<unsigned char>(out, 0); write_binary_number<unsigned char>(out, 0);
@ -746,8 +766,9 @@ void write_binary_value_expr(std::ostream& out, value_expr_t * expr)
write_binary_number<unsigned char>(out, 1); write_binary_number<unsigned char>(out, 1);
write_binary_number(out, expr->kind); write_binary_number(out, expr->kind);
if (expr->kind > value_expr_t::TERMINALS)
write_binary_value_expr(out, expr->left); write_binary_value_expr(out, expr->left);
write_binary_value_expr(out, expr->right);
switch (expr->kind) { switch (expr->kind) {
case value_expr_t::CONSTANT_T: case value_expr_t::CONSTANT_T:
@ -757,19 +778,32 @@ void write_binary_value_expr(std::ostream& out, value_expr_t * expr)
write_binary_long(out, expr->constant_i); write_binary_long(out, expr->constant_i);
break; break;
case value_expr_t::CONSTANT_A: case value_expr_t::CONSTANT_A:
write_binary_amount(out, expr->constant_a); write_binary_amount(out, *(expr->constant_a));
break; break;
case value_expr_t::F_FUNC: case value_expr_t::CONSTANT_V:
write_binary_string(out, expr->constant_s); assert(0);
break; break;
}
case value_expr_t::F_CODE_MASK:
case value_expr_t::F_PAYEE_MASK:
case value_expr_t::F_NOTE_MASK:
case value_expr_t::F_ACCOUNT_MASK:
case value_expr_t::F_SHORT_ACCOUNT_MASK:
case value_expr_t::F_COMMODITY_MASK:
if (expr->mask) { if (expr->mask) {
write_binary_number<char>(out, 1); write_binary_number<char>(out, 1);
write_binary_mask(out, expr->mask); write_binary_mask(out, expr->mask);
} else { } else {
write_binary_number<char>(out, 0); write_binary_number<char>(out, 0);
} }
break;
default:
if (expr->kind > value_expr_t::TERMINALS)
write_binary_value_expr(out, expr->right);
break;
}
} }
void write_binary_transaction(std::ostream& out, transaction_t * xact, void write_binary_transaction(std::ostream& out, transaction_t * xact,
@ -808,23 +842,19 @@ void write_binary_transaction(std::ostream& out, transaction_t * xact,
write_binary_number(out, xact->flags); write_binary_number(out, xact->flags);
write_binary_string(out, xact->note); write_binary_string(out, xact->note);
#ifdef USE_EDITOR
write_binary_long(out, xact->beg_pos); write_binary_long(out, xact->beg_pos);
write_binary_long(out, xact->beg_line); write_binary_long(out, xact->beg_line);
write_binary_long(out, xact->end_pos); write_binary_long(out, xact->end_pos);
write_binary_long(out, xact->end_line); write_binary_long(out, xact->end_line);
#endif
} }
void write_binary_entry_base(std::ostream& out, entry_base_t * entry) void write_binary_entry_base(std::ostream& out, entry_base_t * entry)
{ {
#ifdef USE_EDITOR
write_binary_long(out, entry->src_idx); write_binary_long(out, entry->src_idx);
write_binary_long(out, entry->beg_pos); write_binary_long(out, entry->beg_pos);
write_binary_long(out, entry->beg_line); write_binary_long(out, entry->beg_line);
write_binary_long(out, entry->end_pos); write_binary_long(out, entry->end_pos);
write_binary_long(out, entry->end_line); write_binary_long(out, entry->end_line);
#endif
bool ignore_calculated = false; bool ignore_calculated = false;
for (transactions_list::const_iterator i = entry->transactions.begin(); for (transactions_list::const_iterator i = entry->transactions.begin();
@ -856,7 +886,7 @@ void write_binary_entry(std::ostream& out, entry_t * entry)
void write_binary_auto_entry(std::ostream& out, auto_entry_t * entry) void write_binary_auto_entry(std::ostream& out, auto_entry_t * entry)
{ {
write_binary_entry_base(out, entry); write_binary_entry_base(out, entry);
write_binary_string(out, entry->predicate_string); write_binary_value_expr(out, entry->predicate->predicate);
} }
void write_binary_period_entry(std::ostream& out, period_entry_t * entry) void write_binary_period_entry(std::ostream& out, period_entry_t * entry)
@ -869,39 +899,47 @@ void write_binary_commodity(std::ostream& out, commodity_t * commodity)
{ {
write_binary_string(out, commodity->symbol); write_binary_string(out, commodity->symbol);
write_binary_number(out, commodity->quote); write_binary_number(out, commodity->quote);
write_binary_string(out, commodity->name); write_binary_string(out, commodity->name_);
write_binary_string(out, commodity->note); write_binary_string(out, commodity->note_);
write_binary_number(out, commodity->precision); write_binary_number(out, commodity->precision_);
write_binary_number(out, commodity->flags); write_binary_number(out, commodity->flags_);
commodity->ident = ++commodity_index; commodity->ident = ++commodity_index;
write_binary_long(out, commodity->ident); write_binary_long(out, commodity->ident);
} }
void write_binary_commodity_extra(std::ostream& out, commodity_t * commodity) void write_binary_commodity_extra(std::ostream& out, commodity_t * commodity)
{ {
if (! commodity->history) { if (! commodity->history_) {
write_binary_long<unsigned long>(out, 0); write_binary_long<unsigned long>(out, 0);
} else { } else {
write_binary_long<unsigned long>(out, commodity->history->prices.size()); write_binary_long<unsigned long>(out, commodity->history_->prices.size());
for (history_map::const_iterator i = commodity->history->prices.begin(); for (history_map::const_iterator i = commodity->history_->prices.begin();
i != commodity->history->prices.end(); i != commodity->history_->prices.end();
i++) { i++) {
write_binary_long(out, (*i).first); write_binary_long(out, (*i).first);
write_binary_amount(out, (*i).second); write_binary_amount(out, (*i).second);
} }
write_binary_long(out, commodity->history->last_lookup); write_binary_long(out, commodity->history_->last_lookup);
} }
if (commodity->smaller) { if (commodity->smaller_) {
write_binary_number<unsigned char>(out, 1); write_binary_number<unsigned char>(out, 1);
write_binary_amount(out, *commodity->smaller); write_binary_amount(out, *commodity->smaller_);
} else { } else {
write_binary_number<unsigned char>(out, 0); write_binary_number<unsigned char>(out, 0);
} }
if (commodity->larger) { if (commodity->larger_) {
write_binary_number<unsigned char>(out, 1); write_binary_number<unsigned char>(out, 1);
write_binary_amount(out, *commodity->larger); write_binary_amount(out, *commodity->larger_);
} else {
write_binary_number<unsigned char>(out, 0);
}
if (commodity->price) {
write_binary_number<unsigned char>(out, 1);
write_binary_amount(out, *commodity->price);
write_binary_long(out, commodity->base->ident);
} else { } else {
write_binary_number<unsigned char>(out, 0); write_binary_number<unsigned char>(out, 0);
} }

186
config.cc
View file

@ -17,15 +17,43 @@
namespace ledger { namespace ledger {
std::list<option_t> config_options; namespace {
config_t * config = NULL;
static config_t * config = NULL; void xact_amount(value_t& result, const details_t& details, value_expr_t *)
{
if (transaction_has_xdata(*details.xact) &&
transaction_xdata_(*details.xact).dflags & TRANSACTION_COMPOSITE)
result = transaction_xdata_(*details.xact).composite_amount;
else
result = details.xact->amount;
}
void xact_running_total(value_t& result, const details_t& details,
value_expr_t *)
{
result = transaction_xdata_(*details.xact).total;
}
void account_amount(value_t& result, const details_t& details,
value_expr_t *)
{
if (account_has_xdata(*details.account))
result = account_xdata(*details.account).value;
}
void account_total(value_t& result, const details_t& details,
value_expr_t *)
{
if (account_has_xdata(*details.account))
result = account_xdata(*details.account).total;
}
}
void config_t::reset() void config_t::reset()
{ {
amount_expr = "a"; amount_expr = "a";
total_expr = "O"; total_expr = "O";
total_expr_template = "#";
pricing_leeway = 24 * 3600; pricing_leeway = 24 * 3600;
budget_flags = BUDGET_NO_BUDGET; budget_flags = BUDGET_NO_BUDGET;
balance_format = "%20T %2_%-a\n"; balance_format = "%20T %2_%-a\n";
@ -34,8 +62,8 @@ void config_t::reset()
wide_register_format = ("%D %-.35P %-.38A %22.108t %22.132T\n%/" wide_register_format = ("%D %-.35P %-.38A %22.108t %22.132T\n%/"
"%48|%-.38A %22.108t %22.132T\n"); "%48|%-.38A %22.108t %22.132T\n");
csv_register_format = "\"%D\",\"%P\",\"%A\",\"%t\",\"%T\"\n"; csv_register_format = "\"%D\",\"%P\",\"%A\",\"%t\",\"%T\"\n";
plot_amount_format = "%D %(St)\n"; plot_amount_format = "%D %(S(t))\n";
plot_total_format = "%D %(ST)\n"; plot_total_format = "%D %(S(T))\n";
print_format = "\n%d %Y%C%P\n %-34W %12o%n\n%/ %-34W %12o%n\n"; print_format = "\n%d %Y%C%P\n %-34W %12o%n\n%/ %-34W %12o%n\n";
write_hdr_format = "%d %Y%C%P\n"; write_hdr_format = "%d %Y%C%P\n";
write_xact_format = " %-34W %12o%n\n"; write_xact_format = " %-34W %12o%n\n";
@ -62,6 +90,7 @@ void config_t::reset()
show_revalued = false; show_revalued = false;
show_revalued_only = false; show_revalued_only = false;
download_quotes = false; download_quotes = false;
debug_mode = false;
use_cache = false; use_cache = false;
cache_dirty = false; cache_dirty = false;
@ -133,7 +162,7 @@ config_t::regexps_to_predicate(const std::string& command,
if (! show_related && ! show_all_related) { if (! show_related && ! show_all_related) {
if (! display_predicate.empty()) if (! display_predicate.empty())
display_predicate += "&"; display_predicate += "&";
else if (! show_empty) if (! show_empty)
display_predicate += "T&"; display_predicate += "T&";
if (add_predicate == 2) if (add_predicate == 2)
@ -143,6 +172,8 @@ config_t::regexps_to_predicate(const std::string& command,
display_predicate += ")/"; display_predicate += ")/";
} }
else if (! show_empty) { else if (! show_empty) {
if (! display_predicate.empty())
display_predicate += "&";
display_predicate += "T"; display_predicate += "T";
} }
} }
@ -178,6 +209,18 @@ void config_t::process_environment(char ** envp, const std::string& tag)
config = NULL; config = NULL;
} }
static std::string expand_value_expr(const std::string& tmpl,
const std::string& expr)
{
std::string xp = tmpl;
for (std::string::size_type i = xp.find('#');
i != std::string::npos;
i = xp.find('#'))
xp = (std::string(xp, 0, i) + "(" + expr + ")" +
std::string(xp, i + 1));
return xp;
}
void config_t::process_options(const std::string& command, void config_t::process_options(const std::string& command,
strings_list::iterator arg, strings_list::iterator arg,
strings_list::iterator args_end) strings_list::iterator args_end)
@ -244,32 +287,8 @@ void config_t::process_options(const std::string& command,
// Setup the values of %t and %T, used in format strings // Setup the values of %t and %T, used in format strings
try { ledger::amount_expr.reset(new value_expr(amount_expr));
ledger::amount_expr.reset(parse_value_expr(amount_expr)); ledger::total_expr.reset(new value_expr(total_expr));
}
catch (const value_expr_error& err) {
throw error(std::string("In amount expression '") + amount_expr +
"': " + err.what());
}
std::string expr = total_expr_template;
for (std::string::size_type i = expr.find('#');
i != std::string::npos;
i = expr.find('#'))
expr = (std::string(expr, 0, i) + "(" + total_expr + ")" +
std::string(expr, i + 1));
DEBUG_PRINT("ledger.config.total_expr",
"Total expression template = " << total_expr_template);
DEBUG_PRINT("ledger.config.total_expr",
"Total expression is now = " << expr);
try {
ledger::total_expr.reset(parse_value_expr(expr));
}
catch (const value_expr_error& err) {
throw error(std::string("In total expression '") + expr + "': " +
err.what());
}
// If downloading is to be supported, configure the updater // If downloading is to be supported, configure the updater
@ -683,6 +702,10 @@ OPT_BEGIN(account, "a:") {
config->account = optarg; config->account = optarg;
} OPT_END(account); } OPT_END(account);
OPT_BEGIN(debug, "") {
config->debug_mode = true;
} OPT_END(debug);
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// //
// Report filtering // Report filtering
@ -755,6 +778,10 @@ OPT_BEGIN(actual, "L") {
config->predicate += "L"; config->predicate += "L";
} OPT_END(actual); } OPT_END(actual);
OPT_BEGIN(lots, "") {
show_lots = true;
} OPT_END(lots);
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// //
// Output customization // Output customization
@ -1013,6 +1040,12 @@ OPT_BEGIN(basis, "B") {
config->total_expr = "B"; config->total_expr = "B";
} OPT_END(basis); } OPT_END(basis);
OPT_BEGIN(price, "I") {
show_lots = true; // don't show them, but use in calculations
config->amount_expr = "i";
config->total_expr = "I";
} OPT_END(price);
OPT_BEGIN(market, "V") { OPT_BEGIN(market, "V") {
config->show_revalued = true; config->show_revalued = true;
@ -1034,15 +1067,98 @@ OPT_BEGIN(gain, "G") {
} OPT_END(gain); } OPT_END(gain);
OPT_BEGIN(average, "A") { OPT_BEGIN(average, "A") {
config->total_expr_template = "A#"; config->total_expr = expand_value_expr("A(#)", config->total_expr);
} OPT_END(average); } OPT_END(average);
OPT_BEGIN(deviation, "D") { OPT_BEGIN(deviation, "D") {
config->total_expr_template = "t-A#"; config->total_expr = expand_value_expr("t-A(#)", config->total_expr);
} OPT_END(deviation); } OPT_END(deviation);
OPT_BEGIN(percentage, "%") { OPT_BEGIN(percentage, "%") {
config->total_expr_template = "^#&{100.0%}*(#/^#)"; config->total_expr = expand_value_expr("^#&{100.0%}*(#/^#)", config->total_expr);
} OPT_END(percentage); } OPT_END(percentage);
//////////////////////////////////////////////////////////////////////
option_t config_options[CONFIG_OPTIONS_SIZE] = {
{ "account", 'a', true, opt_account, false },
{ "actual", 'L', false, opt_actual, false },
{ "add-budget", '\0', false, opt_add_budget, false },
{ "amount", 't', true, opt_amount, false },
{ "amount-data", 'j', false, opt_amount_data, false },
{ "average", 'A', false, opt_average, false },
{ "balance-format", '\0', true, opt_balance_format, false },
{ "basis", 'B', false, opt_basis, false },
{ "begin", 'b', true, opt_begin, false },
{ "budget", '\0', false, opt_budget, false },
{ "by-payee", 'P', false, opt_by_payee, false },
{ "cache", '\0', true, opt_cache, false },
{ "cleared", 'C', false, opt_cleared, false },
{ "collapse", 'n', false, opt_collapse, false },
{ "comm-as-payee", 'x', false, opt_comm_as_payee, false },
{ "csv-register-format", '\0', true, opt_csv_register_format, false },
{ "current", 'c', false, opt_current, false },
{ "date-format", 'y', true, opt_date_format, false },
{ "debug", '\0', false, opt_debug, false },
{ "deviation", 'D', false, opt_deviation, false },
{ "display", 'd', true, opt_display, false },
{ "dow", '\0', false, opt_dow, false },
{ "download", 'Q', false, opt_download, false },
{ "effective", '\0', false, opt_effective, false },
{ "empty", 'E', false, opt_empty, false },
{ "end", 'e', true, opt_end, false },
{ "equity-format", '\0', true, opt_equity_format, false },
{ "file", 'f', true, opt_file, false },
{ "forecast", '\0', true, opt_forecast, false },
{ "format", 'F', true, opt_format, false },
{ "full-help", 'H', false, opt_full_help, false },
{ "gain", 'G', false, opt_gain, false },
{ "head", '\0', true, opt_head, false },
{ "help", 'h', false, opt_help, false },
{ "help-calc", '\0', false, opt_help_calc, false },
{ "help-comm", '\0', false, opt_help_comm, false },
{ "help-disp", '\0', false, opt_help_disp, false },
{ "init-file", 'i', true, opt_init_file, false },
{ "input-date-format", '\0', true, opt_input_date_format, false },
{ "limit", 'l', true, opt_limit, false },
{ "lots", '\0', false, opt_lots, false },
{ "market", 'V', false, opt_market, false },
{ "monthly", 'M', false, opt_monthly, false },
{ "no-cache", '\0', false, opt_no_cache, false },
{ "output", 'o', true, opt_output, false },
{ "pager", '\0', true, opt_pager, false },
{ "percentage", '%', false, opt_percentage, false },
{ "performance", 'g', false, opt_performance, false },
{ "period", 'p', true, opt_period, false },
{ "period-sort", '\0', true, opt_period_sort, false },
{ "plot-amount-format", '\0', true, opt_plot_amount_format, false },
{ "plot-total-format", '\0', true, opt_plot_total_format, false },
{ "price", 'I', false, opt_price, false },
{ "price-db", '\0', true, opt_price_db, false },
{ "price-exp", 'Z', true, opt_price_exp, false },
{ "prices-format", '\0', true, opt_prices_format, false },
{ "print-format", '\0', true, opt_print_format, false },
{ "quantity", 'O', false, opt_quantity, false },
{ "real", 'R', false, opt_real, false },
{ "reconcile", '\0', true, opt_reconcile, false },
{ "reconcile-date", '\0', true, opt_reconcile_date, false },
{ "register-format", '\0', true, opt_register_format, false },
{ "related", 'r', false, opt_related, false },
{ "sort", 'S', true, opt_sort, false },
{ "subtotal", 's', false, opt_subtotal, false },
{ "tail", '\0', true, opt_tail, false },
{ "total", 'T', true, opt_total, false },
{ "total-data", 'J', false, opt_total_data, false },
{ "totals", '\0', false, opt_totals, false },
{ "unbudgeted", '\0', false, opt_unbudgeted, false },
{ "uncleared", 'U', false, opt_uncleared, false },
{ "version", 'v', false, opt_version, false },
{ "weekly", 'W', false, opt_weekly, false },
{ "wide", 'w', false, opt_wide, false },
{ "wide-register-format", '\0', true, opt_wide_register_format, false },
{ "write-hdr-format", '\0', true, opt_write_hdr_format, false },
{ "write-xact-format", '\0', true, opt_write_xact_format, false },
{ "yearly", 'Y', false, opt_yearly, false },
};
} // namespace ledger } // namespace ledger

View file

@ -42,7 +42,6 @@ class config_t
std::string sort_string; std::string sort_string;
std::string amount_expr; std::string amount_expr;
std::string total_expr; std::string total_expr;
std::string total_expr_template;
std::string forecast_limit; std::string forecast_limit;
std::string reconcile_balance; std::string reconcile_balance;
std::string reconcile_date; std::string reconcile_date;
@ -66,6 +65,7 @@ class config_t
bool download_quotes; bool download_quotes;
bool use_cache; bool use_cache;
bool cache_dirty; bool cache_dirty;
bool debug_mode;
config_t() { config_t() {
reset(); reset();
@ -73,7 +73,6 @@ class config_t
config_t(const config_t&) { config_t(const config_t&) {
assert(0); assert(0);
} }
void reset(); void reset();
void regexps_to_predicate(const std::string& command, void regexps_to_predicate(const std::string& command,
@ -100,24 +99,15 @@ class config_t
std::list<item_handler<transaction_t> *>& ptrs); std::list<item_handler<transaction_t> *>& ptrs);
}; };
extern std::list<option_t> config_options; #define CONFIG_OPTIONS_SIZE 78
extern option_t config_options[CONFIG_OPTIONS_SIZE];
void option_help(std::ostream& out); void option_help(std::ostream& out);
struct declared_option_handler : public option_handler {
declared_option_handler(const std::string& label,
const std::string& opt_chars) {
add_option_handler(config_options, label, opt_chars, *this);
}
};
#define OPT_BEGIN(tag, chars) \ #define OPT_BEGIN(tag, chars) \
static struct opt_ ## tag ## _handler \ void opt_ ## tag(const char * optarg)
: public declared_option_handler { \
opt_ ## tag ## _handler() : declared_option_handler(#tag, chars) {} \
virtual void operator()(const char * optarg)
#define OPT_END(tag) } opt_ ## tag ## _handler_obj #define OPT_END(tag)
} // namespace ledger } // namespace ledger

View file

@ -200,15 +200,6 @@ AC_ARG_ENABLE(debug,
esac],[debug=false]) esac],[debug=false])
AM_CONDITIONAL(DEBUG, test x$debug = xtrue) AM_CONDITIONAL(DEBUG, test x$debug = xtrue)
AC_ARG_ENABLE(emacs,
[ --enable-emacs Turn on Emacs support],
[case "${enableval}" in
yes) emacs=true ;;
no) emacs=false ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-emacs) ;;
esac],[emacs=true])
AM_CONDITIONAL(USE_EDITOR, test x$emacs = xtrue)
# Checks for header files. # Checks for header files.
AC_STDC_HEADERS AC_STDC_HEADERS
AC_HAVE_HEADERS(sys/stat.h) AC_HAVE_HEADERS(sys/stat.h)

View file

@ -55,6 +55,9 @@ bool _debug_active(const char * const cls);
#define DEBUG(cls) (_debug_active(cls)) #define DEBUG(cls) (_debug_active(cls))
#define DEBUG_() DEBUG(_debug_cls) #define DEBUG_() DEBUG(_debug_cls)
#define DEBUG_IF(cls) if (_debug_active(cls))
#define DEBUG_IF_() if (_debug_active(_debug_cls))
#define DEBUG_PRINT(cls, x) \ #define DEBUG_PRINT(cls, x) \
if (_debug_stream && _debug_active(cls)) { \ if (_debug_stream && _debug_active(cls)) { \
*_debug_stream << x << std::endl; \ *_debug_stream << x << std::endl; \
@ -91,6 +94,8 @@ void operator delete[](void*, const std::nothrow_t&) throw();
#define DEBUG_CLASS(cls) #define DEBUG_CLASS(cls)
#define DEBUG(cls) 0 #define DEBUG(cls) 0
#define DEBUG_() 0 #define DEBUG_() 0
#define DEBUG_IF(cls)
#define DEBUG_IF_()
#define DEBUG_PRINT(cls, x) #define DEBUG_PRINT(cls, x)
#define DEBUG_PRINT_(x) #define DEBUG_PRINT_(x)
#define DEBUG_PRINT_TIME(cls, x) #define DEBUG_PRINT_TIME(cls, x)

100
derive.cc
View file

@ -23,7 +23,8 @@ entry_t * derive_new_entry(journal_t& journal,
mask_t regexp(*i++); mask_t regexp(*i++);
for (entries_list::reverse_iterator j = journal.entries.rbegin(); entries_list::reverse_iterator j;
for (j = journal.entries.rbegin();
j != journal.entries.rend(); j != journal.entries.rend();
j++) j++)
if (regexp.match((*j)->payee)) { if (regexp.match((*j)->payee)) {
@ -33,23 +34,42 @@ entry_t * derive_new_entry(journal_t& journal,
added->payee = matching ? matching->payee : regexp.pattern; added->payee = matching ? matching->payee : regexp.pattern;
if (i == end) { if (! matching) {
if (! matching) account_t * acct;
throw error("Could not find a matching payee"); if (i == end || ((*i)[0] == '-' || std::isdigit((*i)[0]))) {
acct = journal.find_account("Expenses");
}
else if (i != end) {
acct = journal.find_account_re(*i);
if (! acct)
acct = journal.find_account(*i);
assert(acct);
i++;
}
if (i == end)
added->add_transaction(new transaction_t(acct));
else
added->add_transaction(new transaction_t(acct, amount_t(*i++)));
if (journal.basket)
acct = journal.basket;
else
acct = journal.find_account("Equity");
added->add_transaction(new transaction_t(acct));
}
else if (i == end) {
// If no argument were given but the payee, assume the user wants // If no argument were given but the payee, assume the user wants
// to see the same transaction as last time. // to see the same transaction as last time.
added->code = matching->code; added->code = matching->code;
for (transactions_list::iterator j = matching->transactions.begin(); for (transactions_list::iterator k = matching->transactions.begin();
j != matching->transactions.end(); k != matching->transactions.end();
j++) k++)
added->add_transaction(new transaction_t(**j)); added->add_transaction(new transaction_t(**k));
} }
else if ((*i)[0] == '-' || std::isdigit((*i)[0])) { else if ((*i)[0] == '-' || std::isdigit((*i)[0])) {
if (! matching)
throw error("Could not determine the account to draw from");
transaction_t * m_xact, * xact, * first; transaction_t * m_xact, * xact, * first;
m_xact = matching->transactions.front(); m_xact = matching->transactions.front();
@ -68,7 +88,7 @@ entry_t * derive_new_entry(journal_t& journal,
account_t * acct = journal.find_account_re(*i); account_t * acct = journal.find_account_re(*i);
if (! acct) if (! acct)
acct = journal.find_account(*i); acct = journal.find_account(*i);
if (acct) assert(acct);
added->transactions.back()->account = acct; added->transactions.back()->account = acct;
} }
} }
@ -76,59 +96,57 @@ entry_t * derive_new_entry(journal_t& journal,
while (i != end) { while (i != end) {
std::string& re_pat(*i++); std::string& re_pat(*i++);
account_t * acct = NULL; account_t * acct = NULL;
commodity_t * cmdty = NULL; amount_t * amt = NULL;
if (matching) {
mask_t acct_regex(re_pat); mask_t acct_regex(re_pat);
for (transactions_list::const_iterator x for (; j != journal.entries.rend(); j++)
= matching->transactions.begin(); if (regexp.match((*j)->payee)) {
x != matching->transactions.end(); entry_t * entry = *j;
x++) { for (transactions_list::const_iterator x =
entry->transactions.begin();
x != entry->transactions.end();
x++)
if (acct_regex.match((*x)->account->fullname())) { if (acct_regex.match((*x)->account->fullname())) {
acct = (*x)->account; acct = (*x)->account;
cmdty = &(*x)->amount.commodity(); amt = &(*x)->amount;
break; matching = entry;
} goto found;
} }
} }
found:
if (! acct) if (! acct)
acct = journal.find_account_re(re_pat); acct = journal.find_account_re(re_pat);
if (! acct) if (! acct)
acct = journal.find_account(re_pat); acct = journal.find_account(re_pat);
transaction_t * xact;
if (i == end) { if (i == end) {
added->add_transaction(new transaction_t(acct)); if (amt)
goto done; xact = new transaction_t(acct, *amt);
} else
xact = new transaction_t(acct);
transaction_t * xact = new transaction_t(acct, amount_t(*i++)); } else {
xact = new transaction_t(acct, amount_t(*i++));
if (! xact->amount.commodity()) { if (! xact->amount.commodity()) {
if (cmdty) if (amt)
xact->amount.set_commodity(*cmdty); xact->amount.set_commodity(amt->commodity());
else if (commodity_t::default_commodity) else if (commodity_t::default_commodity)
xact->amount.set_commodity(*commodity_t::default_commodity); xact->amount.set_commodity(*commodity_t::default_commodity);
} }
}
added->add_transaction(xact); added->add_transaction(xact);
} }
account_t * draw_acct; assert(matching->transactions.back()->account);
if (matching) if (account_t * draw_acct = matching->transactions.back()->account)
draw_acct = matching->transactions.back()->account; added->add_transaction(new transaction_t(draw_acct));
else if (journal.basket)
draw_acct = journal.basket;
else
throw error("Could not determine the account to draw from");
transaction_t * xact = new transaction_t(draw_acct);
added->add_transaction(xact);
} }
done: done:
if (! added->finalize() || if (! run_hooks(journal.entry_finalize_hooks, *added) ||
! run_hooks(journal.entry_finalize_hooks, *added)) ! added->finalize())
throw error("Failed to finalize derived entry (check commodities)"); throw error("Failed to finalize derived entry (check commodities)");
return added.release(); return added.release();

View file

@ -162,13 +162,9 @@ element_t * format_t::parse_elements(const std::string& fmt)
throw format_error("Missing ')'"); throw format_error("Missing ')'");
current->type = element_t::VALUE_EXPR; current->type = element_t::VALUE_EXPR;
try {
current->val_expr = parse_value_expr(std::string(b, p)); assert(current->val_expr == NULL);
} current->val_expr = new value_expr(std::string(b, p));
catch (value_expr_error& err) {
throw value_expr_error(std::string("In format expression '") +
std::string(b, p) + "': " + err.what());
}
break; break;
} }
@ -293,22 +289,22 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
case element_t::AMOUNT: case element_t::AMOUNT:
case element_t::TOTAL: case element_t::TOTAL:
case element_t::VALUE_EXPR: { case element_t::VALUE_EXPR: {
value_expr_t * expr = NULL; value_calc * calc = NULL;
switch (elem->type) { switch (elem->type) {
case element_t::AMOUNT: expr = amount_expr.get(); break; case element_t::AMOUNT: calc = amount_expr.get(); break;
case element_t::TOTAL: expr = total_expr.get(); break; case element_t::TOTAL: calc = total_expr.get(); break;
case element_t::VALUE_EXPR: expr = elem->val_expr; break; case element_t::VALUE_EXPR: calc = elem->val_expr; break;
default: default:
assert(0); assert(0);
break; break;
} }
if (! expr) if (! calc)
break; break;
value_t value; value_t value;
balance_t * bal = NULL; balance_t * bal = NULL;
expr->compute(value, details); calc->compute(value, details);
switch (value.type) { switch (value.type) {
case value_t::BOOLEAN: case value_t::BOOLEAN:
@ -348,9 +344,9 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
amount_t unit_cost = *details.xact->cost / details.xact->amount; amount_t unit_cost = *details.xact->cost / details.xact->amount;
commodity_t& comm(unit_cost.commodity()); commodity_t& comm(unit_cost.commodity());
bool has_flag = comm.flags & COMMODITY_STYLE_VARIABLE; bool has_flag = comm.flags() & COMMODITY_STYLE_VARIABLE;
if (! has_flag) if (! has_flag)
unit_cost.commodity().flags |= COMMODITY_STYLE_VARIABLE; unit_cost.commodity().flags() |= COMMODITY_STYLE_VARIABLE;
std::ostringstream stream; std::ostringstream stream;
stream << details.xact->amount << " @ " << unit_cost; stream << details.xact->amount << " @ " << unit_cost;
@ -358,7 +354,7 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
use_disp = true; use_disp = true;
if (! has_flag) if (! has_flag)
unit_cost.commodity().flags &= ~COMMODITY_STYLE_VARIABLE; unit_cost.commodity().flags() &= ~COMMODITY_STYLE_VARIABLE;
} }
else if (details.entry) { else if (details.entry) {
unsigned int xacts_count = 0; unsigned int xacts_count = 0;
@ -388,7 +384,6 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
} }
break; break;
#ifdef USE_EDITOR
case element_t::SOURCE: case element_t::SOURCE:
if (details.entry && details.entry->journal) { if (details.entry && details.entry->journal) {
int idx = details.entry->src_idx; int idx = details.entry->src_idx;
@ -441,7 +436,6 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
if (details.xact) if (details.xact)
out << details.xact->end_line; out << details.xact->end_line;
break; break;
#endif
case element_t::DATE_STRING: { case element_t::DATE_STRING: {
std::time_t date = 0; std::time_t date = 0;

View file

@ -51,7 +51,7 @@ struct element_t
kind_t type; kind_t type;
std::string chars; std::string chars;
value_expr_t * val_expr; value_expr * val_expr;
struct element_t * next; struct element_t * next;

View file

@ -45,11 +45,9 @@ static std::istream * instreamp;
static unsigned int offset; static unsigned int offset;
static XML_Parser parser; static XML_Parser parser;
static std::string path; static std::string path;
#ifdef USE_EDITOR
static unsigned int src_idx; static unsigned int src_idx;
static istream_pos_type beg_pos; static istream_pos_type beg_pos;
static unsigned long beg_line; static unsigned long beg_line;
#endif
static transaction_t::state_t curr_state; static transaction_t::state_t curr_state;
@ -148,13 +146,11 @@ static void endElement(void *userData, const char *name)
have_error = "The above entry does not balance"; have_error = "The above entry does not balance";
delete curr_entry; delete curr_entry;
} else { } else {
#ifdef USE_EDITOR
curr_entry->src_idx = src_idx; curr_entry->src_idx = src_idx;
curr_entry->beg_pos = beg_pos; curr_entry->beg_pos = beg_pos;
curr_entry->beg_line = beg_line; curr_entry->beg_line = beg_line;
curr_entry->end_pos = instreamp->tellg(); curr_entry->end_pos = instreamp->tellg();
curr_entry->end_line = XML_GetCurrentLineNumber(parser) - offset; curr_entry->end_line = XML_GetCurrentLineNumber(parser) - offset;
#endif
count++; count++;
} }
@ -180,7 +176,7 @@ static void endElement(void *userData, const char *name)
if (default_commodity) { if (default_commodity) {
curr_quant.set_commodity(*default_commodity); curr_quant.set_commodity(*default_commodity);
value = curr_quant.round(default_commodity->precision); value = curr_quant.round(default_commodity->precision());
if (curr_value.commodity() == *default_commodity) if (curr_value.commodity() == *default_commodity)
curr_value = value; curr_value = value;
@ -193,12 +189,10 @@ static void endElement(void *userData, const char *name)
if (value != curr_value) if (value != curr_value)
xact->cost = new amount_t(curr_value); xact->cost = new amount_t(curr_value);
#ifdef USE_EDITOR
xact->beg_pos = beg_pos; xact->beg_pos = beg_pos;
xact->beg_line = beg_line; xact->beg_line = beg_line;
xact->end_pos = instreamp->tellg(); xact->end_pos = instreamp->tellg();
xact->end_line = XML_GetCurrentLineNumber(parser) - offset; xact->end_line = XML_GetCurrentLineNumber(parser) - offset;
#endif
// Clear the relevant variables for the next run // Clear the relevant variables for the next run
curr_state = transaction_t::UNCLEARED; curr_state = transaction_t::UNCLEARED;
@ -264,23 +258,23 @@ static void dataHandler(void *userData, const char *s, int len)
std::string symbol(s, len); std::string symbol(s, len);
commodity_t * comm = commodity_t::find_commodity(symbol, true); commodity_t * comm = commodity_t::find_commodity(symbol, true);
if (symbol != "$" && symbol != "USD") if (symbol != "$" && symbol != "USD")
comm->flags |= COMMODITY_STYLE_SEPARATED; comm->flags() |= COMMODITY_STYLE_SEPARATED;
account_comms.insert(account_comm_pair(curr_account, comm)); account_comms.insert(account_comm_pair(curr_account, comm));
} }
else if (curr_entry) { else if (curr_entry) {
std::string symbol(s, len); std::string symbol(s, len);
entry_comm = commodity_t::find_commodity(symbol, true); entry_comm = commodity_t::find_commodity(symbol, true);
if (symbol != "$" && symbol != "USD") if (symbol != "$" && symbol != "USD")
entry_comm->flags |= COMMODITY_STYLE_SEPARATED; entry_comm->flags() |= COMMODITY_STYLE_SEPARATED;
} }
break; break;
case COMM_NAME: case COMM_NAME:
curr_comm->name = std::string(s, len); curr_comm->name() = std::string(s, len);
break; break;
case COMM_PREC: case COMM_PREC:
curr_comm->precision = len - 1; curr_comm->precision() = len - 1;
break; break;
case ENTRY_NUM: case ENTRY_NUM:
@ -313,8 +307,8 @@ static void dataHandler(void *userData, const char *s, int len)
curr_value = convert_number(std::string(s, len), &precision); curr_value = convert_number(std::string(s, len), &precision);
curr_value.set_commodity(*entry_comm); curr_value.set_commodity(*entry_comm);
if (precision > entry_comm->precision) if (precision > entry_comm->precision())
entry_comm->precision = precision; entry_comm->precision() = precision;
break; break;
} }
@ -382,9 +376,7 @@ unsigned int gnucash_parser_t::parse(std::istream& in,
instreamp = &in; instreamp = &in;
path = original_file ? *original_file : "<gnucash>"; path = original_file ? *original_file : "<gnucash>";
#ifdef USE_EDITOR
src_idx = journal->sources.size() - 1; src_idx = journal->sources.size() - 1;
#endif
// GnuCash uses the USD commodity without defining it, which really // GnuCash uses the USD commodity without defining it, which really
// means $. // means $.
@ -401,10 +393,8 @@ unsigned int gnucash_parser_t::parse(std::istream& in,
XML_SetCharacterDataHandler(parser, dataHandler); XML_SetCharacterDataHandler(parser, dataHandler);
while (in.good() && ! in.eof()) { while (in.good() && ! in.eof()) {
#ifdef USE_EDITOR
beg_pos = in.tellg(); beg_pos = in.tellg();
beg_line = (XML_GetCurrentLineNumber(parser) - offset) + 1; beg_line = (XML_GetCurrentLineNumber(parser) - offset) + 1;
#endif
in.getline(buf, BUFSIZ - 1); in.getline(buf, BUFSIZ - 1);
std::strcat(buf, "\n"); std::strcat(buf, "\n");

View file

@ -13,6 +13,14 @@ const std::string version = PACKAGE_VERSION;
bool transaction_t::use_effective_date = false; bool transaction_t::use_effective_date = false;
transaction_t::~transaction_t()
{
DEBUG_PRINT("ledger.memory.dtors", "dtor transaction_t");
if (cost) delete cost;
if (amount_expr) amount_expr->release();
if (cost_expr) cost_expr->release();
}
std::time_t transaction_t::actual_date() const std::time_t transaction_t::actual_date() const
{ {
if (_date == 0 && entry) if (_date == 0 && entry)
@ -96,6 +104,10 @@ bool entry_base_t::finalize()
} else { } else {
balance += *p; balance += *p;
} }
if ((*x)->cost && (*x)->amount.commodity().price)
balance += (*((*x)->amount.commodity().price) * (*x)->amount -
*((*x)->cost));
} }
} }

View file

@ -44,12 +44,10 @@ class transaction_t
state_t state; state_t state;
unsigned short flags; unsigned short flags;
std::string note; std::string note;
#ifdef USE_EDITOR
istream_pos_type beg_pos; istream_pos_type beg_pos;
unsigned long beg_line; unsigned long beg_line;
istream_pos_type end_pos; istream_pos_type end_pos;
unsigned long end_line; unsigned long end_line;
#endif
mutable void * data; mutable void * data;
static bool use_effective_date; static bool use_effective_date;
@ -58,13 +56,9 @@ class transaction_t
: entry(NULL), _date(0), _date_eff(0), account(_account), : entry(NULL), _date(0), _date_eff(0), account(_account),
amount_expr(NULL), cost(NULL), cost_expr(NULL), amount_expr(NULL), cost(NULL), cost_expr(NULL),
state(UNCLEARED), flags(TRANSACTION_NORMAL), state(UNCLEARED), flags(TRANSACTION_NORMAL),
#ifdef USE_EDITOR beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) {
beg_pos(0), beg_line(0), end_pos(0), end_line(0),
#endif
data(NULL) {
DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t"); DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t");
} }
transaction_t(account_t * _account, transaction_t(account_t * _account,
const amount_t& _amount, const amount_t& _amount,
unsigned int _flags = TRANSACTION_NORMAL, unsigned int _flags = TRANSACTION_NORMAL,
@ -72,30 +66,18 @@ class transaction_t
: entry(NULL), _date(0), _date_eff(0), account(_account), : entry(NULL), _date(0), _date_eff(0), account(_account),
amount(_amount), amount_expr(NULL), cost(NULL), cost_expr(NULL), amount(_amount), amount_expr(NULL), cost(NULL), cost_expr(NULL),
state(UNCLEARED), flags(_flags), note(_note), state(UNCLEARED), flags(_flags), note(_note),
#ifdef USE_EDITOR beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) {
beg_pos(0), beg_line(0), end_pos(0), end_line(0),
#endif
data(NULL) {
DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t"); DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t");
} }
transaction_t(const transaction_t& xact) transaction_t(const transaction_t& xact)
: entry(xact.entry), _date(0), _date_eff(0), account(xact.account), : entry(xact.entry), _date(0), _date_eff(0), account(xact.account),
amount(xact.amount), amount_expr(NULL), amount(xact.amount), amount_expr(NULL),
cost(xact.cost ? new amount_t(*xact.cost) : NULL), cost_expr(NULL), cost(xact.cost ? new amount_t(*xact.cost) : NULL), cost_expr(NULL),
state(xact.state), flags(xact.flags), note(xact.note), state(xact.state), flags(xact.flags), note(xact.note),
#ifdef USE_EDITOR beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) {
beg_pos(0), beg_line(0), end_pos(0), end_line(0),
#endif
data(NULL) {
DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t"); DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t");
} }
~transaction_t();
~transaction_t() {
DEBUG_PRINT("ledger.memory.dtors", "dtor transaction_t");
if (cost)
delete cost;
}
std::time_t actual_date() const; std::time_t actual_date() const;
std::time_t effective_date() const; std::time_t effective_date() const;
@ -124,26 +106,20 @@ class entry_base_t
{ {
public: public:
journal_t * journal; journal_t * journal;
#ifdef USE_EDITOR
unsigned long src_idx; unsigned long src_idx;
istream_pos_type beg_pos; istream_pos_type beg_pos;
unsigned long beg_line; unsigned long beg_line;
istream_pos_type end_pos; istream_pos_type end_pos;
unsigned long end_line; unsigned long end_line;
#endif
transactions_list transactions; transactions_list transactions;
entry_base_t() : journal(NULL), entry_base_t() : journal(NULL),
#ifdef USE_EDITOR
beg_pos(0), beg_line(0), end_pos(0), end_line(0) beg_pos(0), beg_line(0), end_pos(0), end_line(0)
#endif
{ {
DEBUG_PRINT("ledger.memory.ctors", "ctor entry_base_t"); DEBUG_PRINT("ledger.memory.ctors", "ctor entry_base_t");
} }
entry_base_t(const entry_base_t& e) : journal(NULL), entry_base_t(const entry_base_t& e) : journal(NULL),
#ifdef USE_EDITOR
beg_pos(0), beg_line(0), end_pos(0), end_line(0) beg_pos(0), beg_line(0), end_pos(0), end_line(0)
#endif
{ {
DEBUG_PRINT("ledger.memory.ctors", "ctor entry_base_t"); DEBUG_PRINT("ledger.memory.ctors", "ctor entry_base_t");
for (transactions_list::const_iterator i = e.transactions.begin(); for (transactions_list::const_iterator i = e.transactions.begin();

34
main.cc
View file

@ -104,10 +104,10 @@ int parse_and_report(int argc, char * argv[], char * envp[])
command = "p"; command = "p";
else if (command == "output") else if (command == "output")
command = "w"; command = "w";
#ifdef USE_EDITOR
else if (command == "emacs") else if (command == "emacs")
command = "x"; command = "x";
#endif else if (command == "lisp")
command = "x";
else if (command == "xml") else if (command == "xml")
command = "X"; command = "X";
else if (command == "entry") else if (command == "entry")
@ -122,6 +122,20 @@ int parse_and_report(int argc, char * argv[], char * envp[])
config.register_format = config.csv_register_format; config.register_format = config.csv_register_format;
command = "r"; command = "r";
} }
else if (command == "parse") {
value_auto_ptr expr(ledger::parse_value_expr(*arg));
if (config.debug_mode) {
ledger::dump_value_expr(std::cout, expr.get());
std::cout << std::endl;
}
value_t result;
expr->compute(result, details_t());
std::cout << result << std::endl;
return 0;
}
else if (command == "expr") {
// this gets done below...
}
else else
throw error(std::string("Unrecognized command '") + command + "'"); throw error(std::string("Unrecognized command '") + command + "'");
@ -211,6 +225,20 @@ int parse_and_report(int argc, char * argv[], char * envp[])
} }
#endif #endif
// Are we handling the parse or expr commands? Do so now.
if (command == "expr") {
value_auto_ptr expr(ledger::parse_value_expr(*arg));
if (config.debug_mode) {
ledger::dump_value_expr(std::cout, expr.get());
std::cout << std::endl;
}
value_t result;
expr->compute(result, details_t());
std::cout << result << std::endl;
return 0;
}
// Compile the format strings // Compile the format strings
const std::string * format; const std::string * format;
@ -245,10 +273,8 @@ int parse_and_report(int argc, char * argv[], char * envp[])
formatter = new set_account_value; formatter = new set_account_value;
else if (command == "p" || command == "e") else if (command == "p" || command == "e")
formatter = new format_entries(*out, *format); formatter = new format_entries(*out, *format);
#ifdef USE_EDITOR
else if (command == "x") else if (command == "x")
formatter = new format_emacs_transactions(*out); formatter = new format_emacs_transactions(*out);
#endif
else if (command == "X") { else if (command == "X") {
#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE) #if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
formatter = new format_xml_entries(*out, config.show_totals); formatter = new format_xml_entries(*out, config.show_totals);

179
option.cc
View file

@ -1,4 +1,5 @@
#include "option.h" #include "option.h"
#include "config.h"
#include "debug.h" #include "debug.h"
#include <iostream> #include <iostream>
@ -6,149 +7,125 @@
#include "util.h" #include "util.h"
void add_option_handler(std::list<option_t>& options,
const std::string& label,
const std::string& opt_chars,
option_handler& option)
{
option_t opt;
char buf[128];
char * p = buf;
for (const char * q = label.c_str();
*q && p - buf < 128;
q++)
if (*q == '_')
*p++ = '-';
else
*p++ = *q;
*p = '\0';
opt.long_opt = buf;
if (! opt_chars.empty()) {
if (opt_chars[0] != ':')
opt.short_opt = opt_chars[0];
if (opt_chars[opt_chars.length() - 1] == ':')
opt.wants_arg = true;
}
opt.handler = &option;
options.push_front(opt);
}
namespace { namespace {
inline void process_option(const option_t& opt, inline void process_option(option_t * opt, const char * arg = NULL) {
const char * arg = NULL) { if (! opt->handled) {
if (! opt.handler->handled) { opt->handler(arg);
(*opt.handler)(arg); opt->handled = true;
opt.handler->handled = true;
} }
} }
option_t * search_options(option_t * array, const char * name)
{
int first = 0;
int last = CONFIG_OPTIONS_SIZE;
while (first <= last) {
int mid = (first + last) / 2; // compute mid point.
int result;
if ((result = (int)name[0] - (int)array[mid].long_opt[0]) == 0)
result = std::strcmp(name, array[mid].long_opt);
if (result > 0)
first = mid + 1; // repeat search in top half.
else if (result < 0)
last = mid - 1; // repeat search in bottom half.
else
return &array[mid];
}
return NULL;
}
option_t * search_options(option_t * array, const char letter)
{
for (int i = 0; i < CONFIG_OPTIONS_SIZE; i++)
if (letter == array[i].short_opt)
return &array[i];
return NULL;
}
} }
bool process_option(std::list<option_t>& options, bool process_option(option_t * options, const std::string& name,
const std::string& opt, const char * arg) const char * arg)
{ {
for (std::list<option_t>::iterator i = options.begin(); option_t * opt = search_options(options, name.c_str());
i != options.end(); if (opt != NULL && ! opt->handled) {
i++) opt->handler(arg);
if ((*i).long_opt == opt) { opt->handled = true;
if (! (*i).handler->handled) {
(*(*i).handler)(arg);
(*i).handler->handled = true;
return true; return true;
} }
break;
}
return false; return false;
} }
void process_arguments(std::list<option_t>& options, void process_arguments(option_t * options, int argc, char ** argv,
int argc, char ** argv, const bool anywhere, const bool anywhere, std::list<std::string>& args)
std::list<std::string>& args)
{ {
int index = 0; int index = 0;
for (char ** i = argv; index < argc; i++, index++) { for (char ** i = argv; *i; i++) {
if ((*i)[0] != '-') { if ((*i)[0] != '-') {
if (anywhere) { if (anywhere) {
args.push_back(*i); args.push_back(*i);
continue; continue;
} else { } else {
for (; index < argc; i++, index++) for (; *i; i++)
args.push_back(*i); args.push_back(*i);
break; break;
} }
} }
// --long-option // --long-option or -s
again: again:
option_t * opt = NULL;
char * value = NULL;
if ((*i)[1] == '-') { if ((*i)[1] == '-') {
if ((*i)[2] == '\0') if ((*i)[2] == '\0')
break; break;
for (std::list<option_t>::iterator j = options.begin(); char * name = *i + 2;
j != options.end(); if (char * p = std::strchr(name, '=')) {
j++) *p++ = '\0';
if ((*j).wants_arg) { value = p;
if (const char * p = std::strchr(*i + 2, '=')) {
if ((*j).long_opt == std::string(*i + 2, p - (*i + 2))) {
process_option(*j, p + 1);
goto next;
}
}
else if ((*j).long_opt == *i + 2) {
if (++index >= argc)
throw option_error(std::string("missing option argument for ") +
*i);
process_option(*j, argv[index]);
i++;
goto next;
}
}
else if ((*j).long_opt == *i + 2) {
process_option(*j);
goto next;
} }
throw option_error(std::string("illegal option ") + *i); opt = search_options(options, name);
} else { if (opt == NULL)
for (std::list<option_t>::iterator j = options.begin(); throw option_error(std::string("illegal option --") + name);
j != options.end();
j++) if (opt->wants_arg && value == NULL) {
if ((*i)[1] == (*j).short_opt) { value = *++i;
if ((*j).wants_arg) { if (value == NULL)
if (++index >= argc) throw option_error(std::string("missing option argument for --") +
throw option_error(std::string("missing argument for option ") + name);
*i);
process_option(*j, argv[index]);
i++;
goto next;
} else {
process_option(*j);
if ((*i)[2]) {
std::strcpy(*i + 1, *i + 2);
goto again;
} }
goto next; process_option(opt, value);
} else {
char c = (*i)[1];
opt = search_options(options, c);
if (opt == NULL)
throw option_error(std::string("illegal option -") + c);
if (opt->wants_arg) {
value = *++i;
if (value == NULL)
throw option_error(std::string("missing option argument for -") + c);
} }
} }
throw option_error(std::string("illegal option -- ") + (*i)[1]); assert(opt);
} assert(value == NULL || opt->wants_arg);
process_option(opt, value);
next: next:
; ;
} }
} }
void process_environment(std::list<option_t>& options, void process_environment(option_t * options, char ** envp,
char ** envp, const std::string& tag) const std::string& tag)
{ {
const char * tag_p = tag.c_str(); const char * tag_p = tag.c_str();
int tag_len = tag.length(); unsigned int tag_len = tag.length();
for (char ** p = envp; *p; p++) for (char ** p = envp; *p; p++)
if (! tag_p || std::strncmp(*p, tag_p, tag_len) == 0) { if (! tag_p || std::strncmp(*p, tag_p, tag_len) == 0) {

View file

@ -5,20 +5,14 @@
#include <string> #include <string>
#include <exception> #include <exception>
struct option_handler { typedef void (*handler_t)(const char * arg);
bool handled;
option_handler() : handled(false) {}
virtual ~option_handler() {}
virtual void operator()(const char * arg = NULL) = 0;
};
struct option_t { struct option_t {
const char * long_opt;
char short_opt; char short_opt;
std::string long_opt;
bool wants_arg; bool wants_arg;
option_handler * handler; handler_t handler;
bool handled;
option_t() : short_opt(0), wants_arg(false), handler(NULL) {}
}; };
class option_error : public std::exception { class option_error : public std::exception {
@ -32,14 +26,11 @@ class option_error : public std::exception {
} }
}; };
void add_option_handler(std::list<option_t>& options, const std::string& label, bool process_option(option_t * options, const std::string& opt,
const std::string& opt_chars, option_handler& option); const char * arg = NULL);
bool process_option(std::list<option_t>& options, void process_arguments(option_t * options, int argc, char ** argv,
const std::string& opt, const char * arg = NULL); const bool anywhere, std::list<std::string>& args);
void process_arguments(std::list<option_t>& options, void process_environment(option_t * options, char ** envp,
int argc, char ** argv, const bool anywhere, const std::string& tag);
std::list<std::string>& args);
void process_environment(std::list<option_t>& options,
char ** envp, const std::string& tag);
#endif // _OPTION_H #endif // _OPTION_H

18
qif.cc
View file

@ -63,7 +63,6 @@ unsigned int qif_parser_t::parse(std::istream& in,
src_idx = journal->sources.size() - 1; src_idx = journal->sources.size() - 1;
linenum = 1; linenum = 1;
#ifdef USE_EDITOR
istream_pos_type beg_pos = 0; istream_pos_type beg_pos = 0;
unsigned long beg_line = 0; unsigned long beg_line = 0;
@ -72,9 +71,6 @@ unsigned int qif_parser_t::parse(std::istream& in,
beg_pos = in.tellg(); \ beg_pos = in.tellg(); \
beg_line = linenum; \ beg_line = linenum; \
} }
#else
#define SET_BEG_POS_AND_LINE()
#endif
while (in.good() && ! in.eof()) { while (in.good() && ! in.eof()) {
char c; char c;
@ -119,16 +115,16 @@ unsigned int qif_parser_t::parse(std::istream& in,
get_line(in); get_line(in);
xact->amount.parse(line); xact->amount.parse(line);
unsigned long flags = xact->amount.commodity().flags; unsigned long flags = xact->amount.commodity().flags();
unsigned short prec = xact->amount.commodity().precision; unsigned short prec = xact->amount.commodity().precision();
if (! def_commodity) if (! def_commodity)
def_commodity = commodity_t::find_commodity("$", true); def_commodity = commodity_t::find_commodity("$", true);
xact->amount.set_commodity(*def_commodity); xact->amount.set_commodity(*def_commodity);
def_commodity->flags |= flags; def_commodity->flags() |= flags;
if (prec > def_commodity->precision) if (prec > def_commodity->precision())
def_commodity->precision = prec; def_commodity->precision() = prec;
if (c == '$') { if (c == '$') {
saw_splits = true; saw_splits = true;
@ -221,13 +217,11 @@ unsigned int qif_parser_t::parse(std::istream& in,
} }
if (journal->add_entry(entry.get())) { if (journal->add_entry(entry.get())) {
#ifdef USE_EDITOR
entry->src_idx = src_idx; entry->src_idx = src_idx;
entry->beg_pos = beg_pos; entry->beg_pos = beg_pos;
entry->beg_line = beg_line; entry->beg_line = beg_line;
entry->end_pos = in.tellg(); entry->end_pos = in.tellg();
entry->end_line = linenum; entry->end_line = linenum;
#endif
entry.release(); entry.release();
count++; count++;
} }
@ -240,9 +234,7 @@ unsigned int qif_parser_t::parse(std::istream& in,
saw_splits = false; saw_splits = false;
saw_category = false; saw_category = false;
total = NULL; total = NULL;
#ifdef USE_EDITOR
beg_line = 0; beg_line = 0;
#endif
break; break;
} }

View file

@ -22,12 +22,12 @@ void quotes_by_script::operator()(commodity_t& commodity,
DEBUG_PRINT_TIME_(moment); DEBUG_PRINT_TIME_(moment);
DEBUG_PRINT_TIME_(date); DEBUG_PRINT_TIME_(date);
DEBUG_PRINT_TIME_(last); DEBUG_PRINT_TIME_(last);
if (commodity.history) if (commodity.history())
DEBUG_PRINT_TIME_(commodity.history->last_lookup); DEBUG_PRINT_TIME_(commodity.history()->last_lookup);
DEBUG_PRINT_("pricing_leeway is " << pricing_leeway); DEBUG_PRINT_("pricing_leeway is " << pricing_leeway);
if ((commodity.history && if ((commodity.history() &&
std::difftime(now, commodity.history->last_lookup) < pricing_leeway) || std::difftime(now, commodity.history()->last_lookup) < pricing_leeway) ||
std::difftime(now, last) < pricing_leeway || std::difftime(now, last) < pricing_leeway ||
(price && std::difftime(moment, date) > 0 && (price && std::difftime(moment, date) > 0 &&
std::difftime(moment, date) <= pricing_leeway)) std::difftime(moment, date) <= pricing_leeway))
@ -61,7 +61,7 @@ void quotes_by_script::operator()(commodity_t& commodity,
price.parse(buf); price.parse(buf);
commodity.add_price(now, price); commodity.add_price(now, price);
commodity.history->last_lookup = now; commodity.history()->last_lookup = now;
cache_dirty = true; cache_dirty = true;
if (price && ! price_db.empty()) { if (price && ! price_db.empty()) {

View file

@ -290,13 +290,49 @@ transaction_t * parse_transaction(char * line, account_t * account)
if (amount) { if (amount) {
xact->amount_expr = parse_amount(amount, xact->amount, xact->amount_expr = parse_amount(amount, xact->amount,
AMOUNT_PARSE_NO_REDUCE, *xact); AMOUNT_PARSE_NO_REDUCE, *xact);
if (xact->amount_expr)
xact->amount_expr->acquire();
if (price) { if (price) {
xact->cost = new amount_t; xact->cost = new amount_t;
xact->cost_expr = parse_amount(price, *xact->cost, xact->cost_expr = parse_amount(price, *xact->cost,
AMOUNT_PARSE_NO_MIGRATE, *xact); AMOUNT_PARSE_NO_MIGRATE, *xact);
if (xact->cost_expr)
xact->cost_expr->acquire();
if (per_unit) { if (per_unit) {
if (xact->amount.commodity().base == NULL) {
std::string symbol = xact->amount.commodity().symbol;
symbol += " {";
symbol += price;
symbol += "}";
commodity_t * priced_commodity =
commodity_t::find_commodity(symbol, true);
priced_commodity->price = new amount_t(*xact->cost);
priced_commodity->base = &xact->amount.commodity();
xact->amount.set_commodity(*priced_commodity);
}
*xact->cost *= xact->amount; *xact->cost *= xact->amount;
*xact->cost = xact->cost->round(xact->cost->commodity().precision); *xact->cost = xact->cost->round(xact->cost->commodity().precision());
}
else if (xact->amount.commodity().base == NULL) {
amount_t cost(*xact->cost);
cost /= xact->amount;
cost = cost.round(cost.commodity().precision());
std::string symbol;
std::ostringstream symstr(symbol);
symstr << xact->amount.commodity().symbol << " {" << cost << "}";
commodity_t * priced_commodity =
commodity_t::find_commodity(symstr.str(), true);
priced_commodity->price = new amount_t(cost);
priced_commodity->base = &xact->amount.commodity();
xact->amount.set_commodity(*priced_commodity);
} }
} }
xact->amount.reduce(); xact->amount.reduce();
@ -318,10 +354,9 @@ bool parse_transactions(std::istream& in,
in.getline(line, MAX_LINE); in.getline(line, MAX_LINE);
if (in.eof()) if (in.eof())
break; break;
#ifdef USE_EDITOR
beg_pos += istream_pos_type(std::strlen(line) + 1); beg_pos += istream_pos_type(std::strlen(line) + 1);
#endif
linenum++; linenum++;
if (line[0] == ' ' || line[0] == '\t' || line[0] == '\r') { if (line[0] == ' ' || line[0] == '\t' || line[0] == '\r') {
char * p = skip_ws(line); char * p = skip_ws(line);
if (! *p || *p == '\r') if (! *p || *p == '\r')
@ -403,20 +438,17 @@ entry_t * parse_entry(std::istream& in, char * line, account_t * master,
TIMER_START(entry_xacts); TIMER_START(entry_xacts);
#ifdef USE_EDITOR
istream_pos_type end_pos; istream_pos_type end_pos;
unsigned long beg_line = linenum; unsigned long beg_line = linenum;
#endif
while (! in.eof() && (in.peek() == ' ' || in.peek() == '\t')) { while (! in.eof() && (in.peek() == ' ' || in.peek() == '\t')) {
line[0] = '\0'; line[0] = '\0';
in.getline(line, MAX_LINE); in.getline(line, MAX_LINE);
if (in.eof() && line[0] == '\0') if (in.eof() && line[0] == '\0')
break; break;
#ifdef USE_EDITOR
end_pos = beg_pos + istream_pos_type(std::strlen(line) + 1); end_pos = beg_pos + istream_pos_type(std::strlen(line) + 1);
#endif
linenum++; linenum++;
if (line[0] == ' ' || line[0] == '\t' || line[0] == '\r') { if (line[0] == ' ' || line[0] == '\t' || line[0] == '\r') {
char * p = skip_ws(line); char * p = skip_ws(line);
if (! *p || *p == '\r') if (! *p || *p == '\r')
@ -547,20 +579,17 @@ unsigned int textual_parser_t::parse(std::istream& in,
src_idx = journal->sources.size() - 1; src_idx = journal->sources.size() - 1;
linenum = 1; linenum = 1;
#ifdef USE_EDITOR
istream_pos_type beg_pos = in.tellg(); istream_pos_type beg_pos = in.tellg();
istream_pos_type end_pos; istream_pos_type end_pos;
unsigned long beg_line = linenum; unsigned long beg_line = linenum;
#endif
while (in.good() && ! in.eof()) { while (in.good() && ! in.eof()) {
try { try {
in.getline(line, MAX_LINE); in.getline(line, MAX_LINE);
if (in.eof()) if (in.eof())
break; break;
linenum++;
#ifdef USE_EDITOR
end_pos = beg_pos + istream_pos_type(std::strlen(line) + 1); end_pos = beg_pos + istream_pos_type(std::strlen(line) + 1);
#endif linenum++;
switch (line[0]) { switch (line[0]) {
case '\0': case '\0':
@ -674,7 +703,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
parse_symbol(p, symbol); parse_symbol(p, symbol);
commodity_t * commodity = commodity_t::find_commodity(symbol, true); commodity_t * commodity = commodity_t::find_commodity(symbol, true);
commodity->flags |= COMMODITY_STYLE_NOMARKET; commodity->flags() |= COMMODITY_STYLE_NOMARKET;
break; break;
} }
@ -732,13 +761,11 @@ unsigned int textual_parser_t::parse(std::istream& in,
if (pe->finalize()) { if (pe->finalize()) {
extend_entry_base(journal, *pe); extend_entry_base(journal, *pe);
journal->period_entries.push_back(pe); journal->period_entries.push_back(pe);
#ifdef USE_EDITOR
pe->src_idx = src_idx; pe->src_idx = src_idx;
pe->beg_pos = beg_pos; pe->beg_pos = beg_pos;
pe->beg_line = beg_line; pe->beg_line = beg_line;
pe->end_pos = end_pos; pe->end_pos = end_pos;
pe->end_line = linenum; pe->end_line = linenum;
#endif
} else { } else {
throw parse_error(path, linenum, "Period entry failed to balance"); throw parse_error(path, linenum, "Period entry failed to balance");
} }
@ -746,16 +773,15 @@ unsigned int textual_parser_t::parse(std::istream& in,
break; break;
} }
case '@':
case '!': { // directive case '!': { // directive
char * p = next_element(line); char * p = next_element(line);
std::string word(line + 1); std::string word(line + 1);
if (word == "include") { if (word == "include") {
push_var<std::string> save_path(path); push_var<std::string> save_path(path);
push_var<unsigned int> save_src_idx(src_idx); push_var<unsigned int> save_src_idx(src_idx);
#ifdef USE_EDITOR
push_var<istream_pos_type> save_beg_pos(beg_pos); push_var<istream_pos_type> save_beg_pos(beg_pos);
push_var<istream_pos_type> save_end_pos(end_pos); push_var<istream_pos_type> save_end_pos(end_pos);
#endif
push_var<unsigned int> save_linenum(linenum); push_var<unsigned int> save_linenum(linenum);
path = p; path = p;
@ -799,26 +825,25 @@ unsigned int textual_parser_t::parse(std::istream& in,
assert(result.second); assert(result.second);
} }
} }
else if (word == "def") {
if (! global_scope.get())
init_value_expr();
value_auto_ptr expr(parse_boolean_expr(p, global_scope.get()));
}
break; break;
} }
default: { default: {
unsigned int first_line = linenum; unsigned int first_line = linenum;
#ifdef USE_EDITOR
istream_pos_type pos = end_pos; istream_pos_type pos = end_pos;
#else
istream_pos_type pos;
#endif
if (entry_t * entry = if (entry_t * entry =
parse_entry(in, line, account_stack.front(), *this, pos)) { parse_entry(in, line, account_stack.front(), *this, pos)) {
if (journal->add_entry(entry)) { if (journal->add_entry(entry)) {
#ifdef USE_EDITOR
entry->src_idx = src_idx; entry->src_idx = src_idx;
entry->beg_pos = beg_pos; entry->beg_pos = beg_pos;
entry->beg_line = beg_line; entry->beg_line = beg_line;
entry->end_pos = end_pos; entry->end_pos = end_pos;
entry->end_line = linenum; entry->end_line = linenum;
#endif
count++; count++;
} else { } else {
print_entry(std::cerr, *entry); print_entry(std::cerr, *entry);
@ -833,9 +858,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
} else { } else {
throw parse_error(path, first_line, "Failed to parse entry"); throw parse_error(path, first_line, "Failed to parse entry");
} }
#ifdef USE_EDITOR
end_pos = pos; end_pos = pos;
#endif
break; break;
} }
} }
@ -854,9 +877,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
<< err.what() << std::endl;; << err.what() << std::endl;;
errors++; errors++;
} }
#ifdef USE_EDITOR
beg_pos = end_pos; beg_pos = end_pos;
#endif
} }
done: done:
@ -876,8 +897,6 @@ unsigned int textual_parser_t::parse(std::istream& in,
return count; return count;
} }
#ifdef USE_EDITOR
void write_textual_journal(journal_t& journal, std::string path, void write_textual_journal(journal_t& journal, std::string path,
item_handler<transaction_t>& formatter, item_handler<transaction_t>& formatter,
const std::string& write_hdr_format, const std::string& write_hdr_format,
@ -964,6 +983,4 @@ void write_textual_journal(journal_t& journal, std::string path,
} }
} }
#endif // USE_EDITOR
} // namespace ledger } // namespace ledger

1100
valexpr.cc

File diff suppressed because it is too large Load diff

288
valexpr.h
View file

@ -16,6 +16,7 @@ struct details_t
const transaction_t * xact; const transaction_t * xact;
const account_t * account; const account_t * account;
details_t() : entry(NULL), xact(NULL), account(NULL) {}
details_t(const entry_t& _entry) details_t(const entry_t& _entry)
: entry(&_entry), xact(NULL), account(NULL) { : entry(&_entry), xact(NULL), account(NULL) {
DEBUG_PRINT("ledger.memory.ctors", "ctor details_t"); DEBUG_PRINT("ledger.memory.ctors", "ctor details_t");
@ -32,6 +33,29 @@ struct details_t
#endif #endif
}; };
typedef void (*value_func_t)(value_t& result, const details_t& details,
value_expr_t * context);
class value_calc
{
public:
virtual ~value_calc() {}
virtual void compute(value_t& result, const details_t& details,
value_expr_t * context = NULL) = 0;
};
class value_func : public value_calc
{
value_func_t func;
public:
value_func(value_func_t _func) : func(_func) {}
virtual void compute(value_t& result, const details_t& details,
value_expr_t * context = NULL) {
func(result, details, context);
}
};
struct value_expr_t struct value_expr_t
{ {
enum kind_t { enum kind_t {
@ -39,10 +63,14 @@ struct value_expr_t
CONSTANT_I, CONSTANT_I,
CONSTANT_T, CONSTANT_T,
CONSTANT_A, CONSTANT_A,
CONSTANT_V,
CONSTANTS,
// Item details // Item details
AMOUNT, AMOUNT,
COST, COST,
PRICE,
DATE, DATE,
CLEARED, CLEARED,
PENDING, PENDING,
@ -55,6 +83,7 @@ struct value_expr_t
COUNT, COUNT,
TOTAL, TOTAL,
COST_TOTAL, COST_TOTAL,
PRICE_TOTAL,
// Relating to format_t // Relating to format_t
VALUE_EXPR, VALUE_EXPR,
@ -62,13 +91,12 @@ struct value_expr_t
// Functions // Functions
F_NOW, F_NOW,
F_PARENT,
F_ARITH_MEAN, F_ARITH_MEAN,
F_QUANTITY,
F_COMMODITY,
F_SET_COMMODITY,
F_VALUE, F_VALUE,
F_FUNC,
F_NEG,
F_ABS, F_ABS,
F_STRIP,
F_CODE_MASK, F_CODE_MASK,
F_PAYEE_MASK, F_PAYEE_MASK,
F_NOTE_MASK, F_NOTE_MASK,
@ -76,11 +104,18 @@ struct value_expr_t
F_SHORT_ACCOUNT_MASK, F_SHORT_ACCOUNT_MASK,
F_COMMODITY_MASK, F_COMMODITY_MASK,
TERMINALS,
F_PARENT,
// Binary operators // Binary operators
O_NEG,
O_ADD, O_ADD,
O_SUB, O_SUB,
O_MUL, O_MUL,
O_DIV, O_DIV,
O_PERC,
O_NEQ,
O_EQ, O_EQ,
O_LT, O_LT,
O_LTE, O_LTE,
@ -91,88 +126,244 @@ struct value_expr_t
O_OR, O_OR,
O_QUES, O_QUES,
O_COL, O_COL,
O_COM,
O_DEF,
O_REF,
O_ARG, O_ARG,
LAST LAST
}; };
kind_t kind; kind_t kind;
mutable short refc;
value_expr_t * left; value_expr_t * left;
value_expr_t * right;
union { union {
std::time_t constant_t; std::time_t constant_t;
long constant_i; long constant_i;
}; amount_t * constant_a;
std::string constant_s; value_t * constant_v;
amount_t constant_a;
mask_t * mask; mask_t * mask;
value_expr_t * right;
};
value_expr_t(const kind_t _kind) value_expr_t(const kind_t _kind)
: kind(_kind), left(NULL), right(NULL), mask(NULL) { : kind(_kind), refc(0), left(NULL), right(NULL) {
DEBUG_PRINT("ledger.memory.ctors", "ctor value_expr_t"); DEBUG_PRINT("ledger.memory.ctors", "ctor value_expr_t");
} }
~value_expr_t();
~value_expr_t() { void release() const {
DEBUG_PRINT("ledger.memory.dtors", "dtor value_expr_t"); DEBUG_PRINT("ledger.valexpr.memory",
if (mask) delete mask; "Releasing " << this << ", refc now " << refc - 1);
if (left) delete left; assert(refc > 0);
if (right) delete right; if (--refc == 0)
delete this;
}
value_expr_t * acquire() {
DEBUG_PRINT("ledger.valexpr.memory",
"Acquiring " << this << ", refc now " << refc + 1);
assert(refc >= 0);
refc++;
return this;
}
const value_expr_t * acquire() const {
DEBUG_PRINT("ledger.valexpr.memory",
"Acquiring " << this << ", refc now " << refc + 1);
refc++;
return this;
} }
void compute(value_t& result, const details_t& details) const; void set_left(value_expr_t * expr) {
assert(kind > TERMINALS);
if (left)
left->release();
left = expr ? expr->acquire() : NULL;
}
void set_right(value_expr_t * expr) {
assert(kind > TERMINALS);
if (right)
right->release();
right = expr ? expr->acquire() : NULL;
}
void compute(value_t& result, const details_t& details,
value_expr_t * context = NULL) const;
}; };
extern std::auto_ptr<value_expr_t> amount_expr; struct scope_t
extern std::auto_ptr<value_expr_t> total_expr; {
scope_t * parent;
typedef std::map<const std::string, value_expr_t *> symbol_map;
typedef std::pair<const std::string, value_expr_t *> symbol_pair;
symbol_map symbols;
scope_t(scope_t * _parent = NULL) : parent(_parent) {}
~scope_t() {
for (symbol_map::iterator i = symbols.begin();
i != symbols.end();
i++)
(*i).second->release();
}
void define(const std::string& name, value_expr_t * def) {
DEBUG_PRINT("ledger.valexpr.syms",
"Defining '" << name << "' = " << def);
std::pair<symbol_map::iterator, bool> result
= symbols.insert(symbol_pair(name, def->acquire()));
if (! result.second)
throw value_expr_error(std::string("Redefinition of '") +
name + "' in same scope");
}
value_expr_t * lookup(const std::string& name) {
symbol_map::const_iterator i = symbols.find(name);
if (i != symbols.end())
return (*i).second;
else if (parent)
return parent->lookup(name);
return NULL;
}
};
extern std::auto_ptr<scope_t> global_scope;
extern std::time_t terminus; extern std::time_t terminus;
extern bool initialized;
void init_value_expr();
bool compute_amount(value_expr_t * expr, amount_t& amt, transaction_t& xact); bool compute_amount(value_expr_t * expr, amount_t& amt, transaction_t& xact);
struct scope_t;
value_expr_t * parse_boolean_expr(std::istream& in, scope_t * scope);
inline value_expr_t * parse_boolean_expr(const char * p,
scope_t * scope = NULL) {
std::istringstream stream(p);
return parse_boolean_expr(stream, scope);
}
inline value_expr_t * parse_boolean_expr(const std::string& str,
scope_t * scope = NULL) {
return parse_boolean_expr(str.c_str(), scope);
}
value_expr_t * parse_value_expr(std::istream& in,
scope_t * scope = NULL,
const bool partial = false);
inline value_expr_t * parse_value_expr(const char * p,
scope_t * scope = NULL,
const bool partial = false) {
std::istringstream stream(p);
return parse_value_expr(stream, scope, partial);
}
inline value_expr_t * parse_value_expr(const std::string& str,
scope_t * scope = NULL,
const bool partial = false) {
return parse_value_expr(str.c_str(), scope);
}
void dump_value_expr(std::ostream& out, const value_expr_t * node,
const int depth = 0);
//////////////////////////////////////////////////////////////////////
//
// This class is used so that during the "in between" stages of value
// expression parsing -- while no one yet holds a reference to the
// value_expr_t object -- we can be assured of deletion should an
// exception happen to whip by.
struct value_auto_ptr {
value_expr_t * ptr;
value_auto_ptr() : ptr(NULL) {}
explicit value_auto_ptr(value_expr_t * _ptr) : ptr(_ptr) {}
~value_auto_ptr() {
if (ptr && ptr->refc == 0)
delete ptr;
}
value_expr_t& operator*() const throw() {
return *ptr;
}
value_expr_t * operator->() const throw() {
return ptr;
}
value_expr_t * get() const throw() { return ptr; }
value_expr_t * release() throw() {
value_expr_t * tmp = ptr;
ptr = 0;
return tmp;
}
void reset(value_expr_t * p = 0) throw() {
if (p != ptr) {
if (ptr && ptr->refc == 0)
delete ptr;
ptr = p;
}
}
};
//////////////////////////////////////////////////////////////////////
class value_expr : public value_calc
{
std::string expr;
value_expr_t * parsed;
public:
value_expr(const std::string& _expr) : expr(_expr) {
try {
parsed = parse_value_expr(expr);
parsed->acquire();
}
catch (const value_expr_error& err) {
throw error(std::string("In value expression '") +
expr + "': " + err.what());
}
}
value_expr(value_expr_t * _parsed) : parsed(_parsed->acquire()) {}
virtual ~value_expr() {
if (parsed != NULL)
parsed->release();
}
virtual void compute(value_t& result, const details_t& details,
value_expr_t * context = NULL) {
parsed->compute(result, details, context);
}
};
extern std::auto_ptr<value_calc> amount_expr;
extern std::auto_ptr<value_calc> total_expr;
inline void compute_amount(value_t& result, const details_t& details) { inline void compute_amount(value_t& result, const details_t& details) {
if (amount_expr.get()) if (amount_expr.get() != NULL)
amount_expr->compute(result, details); amount_expr->compute(result, details);
} }
inline void compute_total(value_t& result, const details_t& details) { inline void compute_total(value_t& result, const details_t& details) {
if (total_expr.get()) if (total_expr.get() != NULL)
total_expr->compute(result, details); total_expr->compute(result, details);
} }
value_expr_t * parse_value_expr(std::istream& in,
const bool partial = false);
inline value_expr_t * parse_value_expr(const char * p,
const bool partial = false) {
std::istringstream stream(p);
return parse_value_expr(stream, partial);
}
inline value_expr_t * parse_value_expr(const std::string& str,
const bool partial = false) {
return parse_value_expr(str.c_str());
}
#ifdef DEBUG_ENABLED
void dump_value_expr(std::ostream& out, const value_expr_t * node);
#endif
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
template <typename T> template <typename T>
class item_predicate class item_predicate
{ {
const value_expr_t * predicate;
bool allocated;
public: public:
item_predicate(const std::string& _predicate) const value_expr_t * predicate;
: predicate(NULL), allocated(false) {
item_predicate(const std::string& _predicate) : predicate(NULL) {
DEBUG_PRINT("ledger.memory.ctors", "ctor item_predicate<T>"); DEBUG_PRINT("ledger.memory.ctors", "ctor item_predicate<T>");
if (! _predicate.empty()) { if (! _predicate.empty()) {
try { try {
predicate = parse_value_expr(_predicate); predicate = parse_value_expr(_predicate)->acquire();
allocated = true;
} }
catch (value_expr_error& err) { catch (value_expr_error& err) {
throw value_expr_error(std::string("In predicate '") + throw value_expr_error(std::string("In predicate '") +
@ -181,14 +372,14 @@ class item_predicate
} }
} }
item_predicate(const value_expr_t * _predicate = NULL) item_predicate(const value_expr_t * _predicate = NULL)
: predicate(_predicate), allocated(false) { : predicate(_predicate->acquire()) {
DEBUG_PRINT("ledger.memory.ctors", "ctor item_predicate<T>"); DEBUG_PRINT("ledger.memory.ctors", "ctor item_predicate<T>");
} }
~item_predicate() { ~item_predicate() {
DEBUG_PRINT("ledger.memory.dtors", "dtor item_predicate<T>"); DEBUG_PRINT("ledger.memory.dtors", "dtor item_predicate<T>");
if (predicate && allocated) if (predicate)
delete predicate; predicate->release();
} }
bool operator()(const T& item) const { bool operator()(const T& item) const {
@ -196,9 +387,8 @@ class item_predicate
value_t result; value_t result;
predicate->compute(result, details_t(item)); predicate->compute(result, details_t(item));
return result; return result;
} else {
return true;
} }
return true;
} }
}; };

View file

@ -747,6 +747,38 @@ value_t value_t::cost() const
return value_t(); return value_t();
} }
value_t value_t::factor_price() const
{
switch (type) {
case BOOLEAN:
case INTEGER:
return *this;
case AMOUNT: {
commodity_t& comm = ((amount_t *) data)->commodity();
if (comm.price != NULL)
return value_t(*comm.price * *((amount_t *) data));
return *this;
}
case BALANCE:
return ((balance_t *) data)->factor_price();
case BALANCE_PAIR: {
balance_pair_t temp(((balance_pair_t *) data)->quantity.factor_price());
if (((balance_pair_t *) data)->cost)
temp.cost = new balance_t(((balance_pair_t *) data)->cost);
return temp;
}
default:
assert(0);
break;
}
assert(0);
return value_t();
}
value_t& value_t::add(const amount_t& amount, const amount_t * cost) value_t& value_t::add(const amount_t& amount, const amount_t * cost)
{ {
switch (type) { switch (type) {

View file

@ -266,6 +266,7 @@ class value_t
void abs(); void abs();
void cast(type_t cast_type); void cast(type_t cast_type);
value_t cost() const; value_t cost() const;
value_t factor_price() const;
value_t& add(const amount_t& amount, const amount_t * cost = NULL); value_t& add(const amount_t& amount, const amount_t * cost = NULL);
value_t value(const std::time_t moment) const { value_t value(const std::time_t moment) const {
@ -290,7 +291,7 @@ class value_t
case AMOUNT: { case AMOUNT: {
amount_t& amount = *((amount_t *) data); amount_t& amount = *((amount_t *) data);
if (amount.commodity()) if (amount.commodity())
amount = amount.round(amount.commodity().precision); amount = amount.round(amount.commodity().precision());
break; break;
} }
case BALANCE: case BALANCE:

30
walk.cc
View file

@ -7,6 +7,8 @@
namespace ledger { namespace ledger {
bool show_lots = false;
template <> template <>
bool compare_items<transaction_t>::operator()(const transaction_t * left, bool compare_items<transaction_t>::operator()(const transaction_t * left,
const transaction_t * right) const transaction_t * right)
@ -39,12 +41,20 @@ transaction_xdata_t& transaction_xdata(const transaction_t& xact)
void add_transaction_to(const transaction_t& xact, value_t& value) void add_transaction_to(const transaction_t& xact, value_t& value)
{ {
if (transaction_has_xdata(xact) && if (transaction_has_xdata(xact) &&
transaction_xdata_(xact).dflags & TRANSACTION_COMPOSITE) transaction_xdata_(xact).dflags & TRANSACTION_COMPOSITE) {
value += transaction_xdata_(xact).composite_amount; value += transaction_xdata_(xact).composite_amount;
else if (xact.cost || value) }
value.add(xact.amount, xact.cost); else if (xact.cost || value) {
else amount_t * cost = xact.cost;
value = xact.amount; if (cost && cost->commodity().price)
cost = new amount_t(cost->base_amount());
value.add(translate_amount(xact.amount), cost);
if (cost != xact.cost)
delete cost;
}
else {
value = translate_amount(xact.amount);
}
} }
void truncate_entries::flush() void truncate_entries::flush()
@ -790,7 +800,7 @@ void walk_accounts(account_t& account,
const std::string& sort_string) const std::string& sort_string)
{ {
if (! sort_string.empty()) { if (! sort_string.empty()) {
std::auto_ptr<value_expr_t> sort_order; value_auto_ptr sort_order;
try { try {
sort_order.reset(parse_value_expr(sort_string)); sort_order.reset(parse_value_expr(sort_string));
} }
@ -814,15 +824,15 @@ void walk_commodities(commodities_map& commodities,
for (commodities_map::iterator i = commodities.begin(); for (commodities_map::iterator i = commodities.begin();
i != commodities.end(); i != commodities.end();
i++) { i++) {
if ((*i).second->flags & COMMODITY_STYLE_NOMARKET) if ((*i).second->flags() & COMMODITY_STYLE_NOMARKET)
continue; continue;
entry_temps.push_back(entry_t()); entry_temps.push_back(entry_t());
acct_temps.push_back(account_t(NULL, (*i).second->symbol)); acct_temps.push_back(account_t(NULL, (*i).second->symbol));
if ((*i).second->history) if ((*i).second->history())
for (history_map::iterator j = (*i).second->history->prices.begin(); for (history_map::iterator j = (*i).second->history()->prices.begin();
j != (*i).second->history->prices.end(); j != (*i).second->history()->prices.end();
j++) { j++) {
entry_temps.back()._date = (*j).first; entry_temps.back()._date = (*j).first;

15
walk.h
View file

@ -120,6 +120,11 @@ inline const account_t * xact_account(const transaction_t& xact) {
return xact_account(const_cast<transaction_t&>(xact)); return xact_account(const_cast<transaction_t&>(xact));
} }
extern bool show_lots;
#define translate_amount(amt) \
((! show_lots && amt.commodity().price) ? amt.base_amount() : amt)
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
inline void walk_transactions(transactions_list::iterator begin, inline void walk_transactions(transactions_list::iterator begin,
@ -212,20 +217,19 @@ class sort_transactions : public item_handler<transaction_t>
transactions_deque transactions; transactions_deque transactions;
const value_expr_t * sort_order; const value_expr_t * sort_order;
bool allocated;
public: public:
sort_transactions(item_handler<transaction_t> * handler, sort_transactions(item_handler<transaction_t> * handler,
const value_expr_t * _sort_order) const value_expr_t * _sort_order)
: item_handler<transaction_t>(handler), : item_handler<transaction_t>(handler),
sort_order(_sort_order), allocated(false) {} sort_order(_sort_order) {}
sort_transactions(item_handler<transaction_t> * handler, sort_transactions(item_handler<transaction_t> * handler,
const std::string& _sort_order) const std::string& _sort_order)
: item_handler<transaction_t>(handler), allocated(false) { : item_handler<transaction_t>(handler) {
try { try {
sort_order = parse_value_expr(_sort_order); sort_order = parse_value_expr(_sort_order);
allocated = true; sort_order->acquire();
} }
catch (value_expr_error& err) { catch (value_expr_error& err) {
throw value_expr_error(std::string("In sort string '") + _sort_order + throw value_expr_error(std::string("In sort string '") + _sort_order +
@ -235,8 +239,7 @@ class sort_transactions : public item_handler<transaction_t>
virtual ~sort_transactions() { virtual ~sort_transactions() {
assert(sort_order); assert(sort_order);
if (allocated) sort_order->release();
delete sort_order;
} }
virtual void post_accumulated_xacts(); virtual void post_accumulated_xacts();

57
xml.cc
View file

@ -115,27 +115,41 @@ static void endElement(void *userData, const char *name)
else if (std::strcmp(name, "tr:generated") == 0) { else if (std::strcmp(name, "tr:generated") == 0) {
curr_entry->transactions.back()->flags |= TRANSACTION_AUTO; curr_entry->transactions.back()->flags |= TRANSACTION_AUTO;
} }
else if (std::strcmp(name, "commodity") == 0) { else if (std::strcmp(name, "symbol") == 0) {
assert(! curr_comm); assert(! curr_comm);
curr_comm = commodity_t::find_commodity(data, true); curr_comm = commodity_t::find_commodity(data, true);
curr_comm->flags |= COMMODITY_STYLE_SUFFIXED; curr_comm->flags() |= COMMODITY_STYLE_SUFFIXED;
if (! comm_flags.empty()) if (! comm_flags.empty()) {
for (std::string::size_type i = 0, l = comm_flags.length(); i < l; i++) for (std::string::size_type i = 0, l = comm_flags.length(); i < l; i++) {
switch (comm_flags[i]) { switch (comm_flags[i]) {
case 'P': curr_comm->flags &= ~COMMODITY_STYLE_SUFFIXED; break; case 'P': curr_comm->flags() &= ~COMMODITY_STYLE_SUFFIXED; break;
case 'S': curr_comm->flags |= COMMODITY_STYLE_SEPARATED; break; case 'S': curr_comm->flags() |= COMMODITY_STYLE_SEPARATED; break;
case 'T': curr_comm->flags |= COMMODITY_STYLE_THOUSANDS; break; case 'T': curr_comm->flags() |= COMMODITY_STYLE_THOUSANDS; break;
case 'E': curr_comm->flags |= COMMODITY_STYLE_EUROPEAN; break; case 'E': curr_comm->flags() |= COMMODITY_STYLE_EUROPEAN; break;
} }
} }
}
}
else if (std::strcmp(name, "price") == 0) {
assert(curr_comm);
amount_t * price = new amount_t(data);
std::string symbol;
std::ostringstream symstr(symbol);
symstr << curr_comm->symbol << " {" << *price << "}";
commodity_t * priced_comm =
commodity_t::find_commodity(symstr.str(), true);
priced_comm->price = price;
priced_comm->base = curr_comm;
curr_comm = priced_comm;
}
else if (std::strcmp(name, "quantity") == 0) { else if (std::strcmp(name, "quantity") == 0) {
curr_entry->transactions.back()->amount.parse(data); curr_entry->transactions.back()->amount.parse(data);
if (curr_comm) { if (curr_comm) {
std::string::size_type i = data.find('.'); std::string::size_type i = data.find('.');
if (i != std::string::npos) { if (i != std::string::npos) {
int precision = data.length() - i - 1; int precision = data.length() - i - 1;
if (precision > curr_comm->precision) if (precision > curr_comm->precision())
curr_comm->precision = precision; curr_comm->precision() = precision;
} }
curr_entry->transactions.back()->amount.set_commodity(*curr_comm); curr_entry->transactions.back()->amount.set_commodity(*curr_comm);
curr_comm = NULL; curr_comm = NULL;
@ -240,11 +254,24 @@ void xml_write_amount(std::ostream& out, const amount_t& amount,
commodity_t& c = amount.commodity(); commodity_t& c = amount.commodity();
for (int i = 0; i < depth + 2; i++) out << ' '; for (int i = 0; i < depth + 2; i++) out << ' ';
out << "<commodity flags=\""; out << "<commodity flags=\"";
if (! (c.flags & COMMODITY_STYLE_SUFFIXED)) out << 'P'; if (! (c.flags() & COMMODITY_STYLE_SUFFIXED)) out << 'P';
if (c.flags & COMMODITY_STYLE_SEPARATED) out << 'S'; if (c.flags() & COMMODITY_STYLE_SEPARATED) out << 'S';
if (c.flags & COMMODITY_STYLE_THOUSANDS) out << 'T'; if (c.flags() & COMMODITY_STYLE_THOUSANDS) out << 'T';
if (c.flags & COMMODITY_STYLE_EUROPEAN) out << 'E'; if (c.flags() & COMMODITY_STYLE_EUROPEAN) out << 'E';
out << "\">" << c.symbol << "</commodity>\n"; out << "\">\n";
for (int i = 0; i < depth + 4; i++) out << ' ';
if (c.price) {
out << "<symbol>" << c.base->symbol << "</symbol>\n";
for (int i = 0; i < depth + 4; i++) out << ' ';
out << "<price>\n";
xml_write_amount(out, *c.price, depth + 6);
for (int i = 0; i < depth + 4; i++) out << ' ';
out << "</price>\n";
} else {
out << "<symbol>" << c.symbol << "</symbol>\n";
}
for (int i = 0; i < depth + 2; i++) out << ' ';
out << "</commodity>\n";
for (int i = 0; i < depth + 2; i++) out << ' '; for (int i = 0; i < depth + 2; i++) out << ' ';
out << "<quantity>"; out << "<quantity>";