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

114
NEWS
View file

@ -3,18 +3,87 @@
* 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:
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
transaction and entry location info, which is used by ledger.el and
the "write" command. If you use neither of these, then disabling
them will cut textual parsing time in half, and binary loading time
(and cache file size) by a third.
@def foo(x)=x*1000
This line makes the function "foo" available to all subsequent value
expressions, to all command-line options taking a value expression,
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:
@ -74,28 +143,27 @@
transactions belong to the same entry.
- 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:
2004/05/27 * Book Store
Expenses:Dining $20.00
Liabilities:MasterCard
The new (and optional) syntax allows clearing just the MasterCard
transaction:
The new syntax allows clearing of just the MasterCard transaction:
2004/05/27 Book Store
Expenses:Dining $20.00
* Liabilities:MasterCard
NOTE: This changes the output format of the "emacs" and "xml"
reports. ledger.el will use the new syntax unless the Lisp variable
NOTE: This changes the output format of both the "emacs" and "xml"
reports. ledger.el uses the new syntax unless the Lisp variable
`ledger-clear-whole-entries' is set to t.
- Removed Python integration support.
- 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
@ -264,27 +332,27 @@ command-line driver.
-d now specifies the display predicate. To give a date mask similar
to 1.7, use the -p (period) option.
-P now generates the "by payee" report. To specify a price database
to use, use --price-db.
-G now generates a net gain report. To print totals in a format
consumable by gnuplot, use -J.
-l now specifies the calculation predicate. To emulate the old
usage of "-l \$100", use: -d "AT>100".
-N is gone. Instead of "-N REGEX", use: -d "/REGEX/?T>0:T".
-F now specifies the report format string. The old meaning of -F
now has little use.
-S now takes a value expression as the sorting criterion. To get
the old meaning of "-S", use "-S d".
-n now means "collapse entries in the register report". The get the
old meaning of -n in the balance report, use "-T a".
-p now specifies the reporting period. You can convert commodities
in a report using value expressions. For example, to display hours
at $10 per hour:
@ -329,7 +397,7 @@ command-line driver.
Note: This command is identical (and internally converted) to:
ledger -l "/expenses/|//john/" register
- To include entries from another file into a specific account, use:
!account ACCOUNT
!include FILE

188
amount.cc
View file

@ -66,14 +66,14 @@ static struct _init_amounts {
commodity_t * commodity;
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.0h", "60m");
#if 0
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 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));
quantity->prec += amt.quantity->prec;
unsigned int comm_prec = commodity().precision;
unsigned int comm_prec = commodity().precision();
if (quantity->prec > comm_prec + 6U) {
mpz_round(MPZ(quantity), MPZ(quantity), 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));
quantity->prec += 6;
unsigned int comm_prec = commodity().precision;
unsigned int comm_prec = commodity().precision();
if (quantity->prec > comm_prec + 6U) {
mpz_round(MPZ(quantity), MPZ(quantity), quantity->prec, comm_prec + 6U);
quantity->prec = comm_prec + 6U;
@ -504,12 +504,12 @@ amount_t::operator bool() const
if (! quantity)
return false;
if (quantity->prec <= commodity().precision) {
if (quantity->prec <= commodity().precision()) {
return mpz_sgn(MPZ(quantity)) != 0;
} else {
mpz_set(temp, MPZ(quantity));
if (commodity_)
mpz_ui_pow_ui(divisor, 10, quantity->prec - commodity().precision);
mpz_ui_pow_ui(divisor, 10, quantity->prec - commodity().precision());
else
mpz_ui_pow_ui(divisor, 10, quantity->prec);
mpz_tdiv_q(temp, temp, divisor);
@ -559,9 +559,9 @@ amount_t amount_t::value(const std::time_t moment) const
{
if (quantity) {
commodity_t& comm = commodity();
if (! (comm.flags & COMMODITY_STYLE_NOMARKET))
if (! (comm.flags() & COMMODITY_STYLE_NOMARKET))
if (amount_t amt = comm.value(moment))
return (amt * *this).round(amt.commodity().precision);
return (amt * *this).round(amt.commodity().precision());
}
return *this;
}
@ -604,24 +604,23 @@ std::string amount_t::quantity_string() const
unsigned short precision;
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_tdiv_qr(quotient, remainder, MPZ(quantity), divisor);
precision = quantity->prec;
}
else if (comm.precision < quantity->prec) {
mpz_round(rquotient, MPZ(quantity), quantity->prec,
comm.precision);
mpz_ui_pow_ui(divisor, 10, comm.precision);
else if (comm.precision() < quantity->prec) {
mpz_round(rquotient, MPZ(quantity), quantity->prec, comm.precision());
mpz_ui_pow_ui(divisor, 10, comm.precision());
mpz_tdiv_qr(quotient, remainder, rquotient, divisor);
precision = comm.precision;
precision = comm.precision();
}
else if (comm.precision > quantity->prec) {
mpz_ui_pow_ui(divisor, 10, comm.precision - quantity->prec);
else if (comm.precision() > quantity->prec) {
mpz_ui_pow_ui(divisor, 10, comm.precision() - quantity->prec);
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);
precision = comm.precision;
precision = comm.precision();
}
else if (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);
if (amt.commodity().larger) {
if (amt.commodity().larger()) {
amount_t last(amt);
while (last.commodity().larger) {
last /= *last.commodity().larger;
last.commodity_ = last.commodity().larger->commodity_;
while (last.commodity().larger()) {
last /= *last.commodity().larger();
last.commodity_ = last.commodity().larger()->commodity_;
if (ledger::abs(last) < 1)
break;
base = last;
@ -712,24 +711,24 @@ std::ostream& operator<<(std::ostream& _out, const amount_t& amt)
unsigned short precision;
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_tdiv_qr(quotient, remainder, MPZ(base.quantity), divisor);
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,
comm.precision);
mpz_ui_pow_ui(divisor, 10, comm.precision);
comm.precision());
mpz_ui_pow_ui(divisor, 10, comm.precision());
mpz_tdiv_qr(quotient, remainder, rquotient, divisor);
precision = comm.precision;
precision = comm.precision();
}
else if (comm.precision > base.quantity->prec) {
mpz_ui_pow_ui(divisor, 10, comm.precision - base.quantity->prec);
else if (comm.precision() > base.quantity->prec) {
mpz_ui_pow_ui(divisor, 10, comm.precision() - base.quantity->prec);
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);
precision = comm.precision;
precision = comm.precision();
}
else if (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;
}
if (! (comm.flags & COMMODITY_STYLE_SUFFIXED)) {
if (comm.quote)
out << "\"" << comm.symbol << "\"";
else
if (! (comm.flags() & COMMODITY_STYLE_SUFFIXED)) {
if (comm.quote) {
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
out << "\"" << comm.symbol << "\"";
} else {
out << comm.symbol;
if (comm.flags & COMMODITY_STYLE_SEPARATED)
}
if (comm.flags() & COMMODITY_STYLE_SEPARATED)
out << " ";
}
@ -770,7 +775,7 @@ std::ostream& operator<<(std::ostream& _out, const amount_t& amt)
if (mpz_sgn(quotient) == 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);
out << p;
std::free(p);
@ -799,7 +804,7 @@ std::ostream& operator<<(std::ostream& _out, const amount_t& amt)
i != strs.rend();
i++) {
if (printed) {
out << (comm.flags & COMMODITY_STYLE_EUROPEAN ? '.' : ',');
out << (comm.flags() & COMMODITY_STYLE_EUROPEAN ? '.' : ',');
out.width(3);
out.fill('0');
}
@ -810,7 +815,7 @@ std::ostream& operator<<(std::ostream& _out, const amount_t& amt)
}
if (precision) {
out << ((comm.flags & COMMODITY_STYLE_EUROPEAN) ? ',' : '.');
out << ((comm.flags() & COMMODITY_STYLE_EUROPEAN) ? ',' : '.');
out.width(precision);
out.fill('0');
@ -820,13 +825,19 @@ std::ostream& operator<<(std::ostream& _out, const amount_t& amt)
std::free(p);
}
if (comm.flags & COMMODITY_STYLE_SUFFIXED) {
if (comm.flags & COMMODITY_STYLE_SEPARATED)
if (comm.flags() & COMMODITY_STYLE_SUFFIXED) {
if (comm.flags() & COMMODITY_STYLE_SEPARATED)
out << " ";
if (comm.quote)
out << "\"" << comm.symbol << "\"";
else
if (comm.quote) {
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
out << "\"" << comm.symbol << "\"";
} else {
out << comm.symbol;
}
}
mpz_clear(quotient);
@ -878,6 +889,7 @@ void amount_t::parse(std::istream& in, unsigned short flags)
std::string symbol;
std::string quant;
std::string price;
unsigned int comm_flags = COMMODITY_STYLE_DEFAULTS;
bool negative = false;
@ -923,6 +935,34 @@ void amount_t::parse(std::istream& in, unsigned short flags)
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
// comma or period.
@ -940,12 +980,12 @@ void amount_t::parse(std::istream& in, unsigned short flags)
}
else if (last_comma != std::string::npos &&
(! 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;
quantity->prec = quant.length() - last_comma - 1;
}
else if (last_period != std::string::npos &&
! (commodity().flags & COMMODITY_STYLE_EUROPEAN)) {
! (commodity().flags() & COMMODITY_STYLE_EUROPEAN)) {
quantity->prec = quant.length() - last_period - 1;
}
else {
@ -955,9 +995,9 @@ void amount_t::parse(std::istream& in, unsigned short flags)
// Set the commodity's flags and precision accordingly
if (newly_created || ! (flags & AMOUNT_PARSE_NO_MIGRATE)) {
commodity().flags |= comm_flags;
if (quantity->prec > commodity().precision)
commodity().precision = quantity->prec;
commodity().flags() |= comm_flags;
if (quantity->prec > commodity().precision())
commodity().precision() = quantity->prec;
}
// 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()
{
while (commodity_ && commodity().smaller) {
*this *= *commodity().smaller;
commodity_ = commodity().smaller->commodity_;
while (commodity_ && commodity().smaller()) {
*this *= *commodity().smaller();
commodity_ = commodity().smaller()->commodity_;
}
}
@ -1014,12 +1054,12 @@ void parse_conversion(const std::string& larger_str,
larger *= smaller;
if (larger.commodity()) {
larger.commodity().smaller = new amount_t(smaller);
larger.commodity().flags = (smaller.commodity().flags |
COMMODITY_STYLE_NOMARKET);
larger.commodity().smaller() = new amount_t(smaller);
larger.commodity().flags() = (smaller.commodity().flags() |
COMMODITY_STYLE_NOMARKET);
}
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;
for (const char * p = symbol.c_str(); *p; p++)
if (std::isspace(*p) || std::isdigit(*p) || *p == '-' || *p == '.') {
if (std::isspace(*p) && *(p + 1) == '{')
return;
quote = true;
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)
{
if (! history)
history = new history_t;
if (! history())
history() = new history_t;
history_map::iterator i = history->prices.find(date);
if (i != history->prices.end()) {
history_map::iterator i = history()->prices.find(date);
if (i != history()->prices.end()) {
(*i).second = price;
} else {
std::pair<history_map::iterator, bool> result
= history->prices.insert(history_pair(date, price));
= history()->prices.insert(history_pair(date, price));
assert(result.second);
}
}
@ -1195,9 +1237,9 @@ commodity_t * commodity_t::find_commodity(const std::string& symbol,
// Start out the new commodity with the default commodity's flags
// and precision, if one has been defined.
if (default_commodity)
commodity->flags =
(default_commodity->flags & ~(COMMODITY_STYLE_THOUSANDS |
COMMODITY_STYLE_NOMARKET));
commodity->flags() =
(default_commodity->flags() & ~(COMMODITY_STYLE_THOUSANDS |
COMMODITY_STYLE_NOMARKET));
return commodity;
}
@ -1210,23 +1252,23 @@ amount_t commodity_t::value(const std::time_t moment)
std::time_t age = 0;
amount_t price;
if (history) {
assert(history->prices.size() > 0);
if (history()) {
assert(history()->prices.size() > 0);
if (moment == 0) {
history_map::reverse_iterator r = history->prices.rbegin();
history_map::reverse_iterator r = history()->prices.rbegin();
age = (*r).first;
price = (*r).second;
} else {
history_map::iterator i = history->prices.lower_bound(moment);
if (i == history->prices.end()) {
history_map::reverse_iterator r = history->prices.rbegin();
history_map::iterator i = history()->prices.lower_bound(moment);
if (i == history()->prices.end()) {
history_map::reverse_iterator r = history()->prices.rbegin();
age = (*r).first;
price = (*r).second;
} else {
age = (*i).first;
if (std::difftime(moment, age) != 0) {
if (i != history->prices.begin()) {
if (i != history()->prices.begin()) {
--i;
age = (*i).first;
price = (*i).second;
@ -1242,8 +1284,8 @@ amount_t commodity_t::value(const std::time_t moment)
if (updater)
(*updater)(*this, moment, age,
(history && history->prices.size() > 0 ?
(*history->prices.rbegin()).first : 0), price);
(history() && history()->prices.size() > 0 ?
(*history()->prices.rbegin()).first : 0), price);
return price;
}

View file

@ -73,6 +73,7 @@ class amount_t
void clear_commodity() {
commodity_ = NULL;
}
amount_t base_amount() const;
bool null() const {
return ! quantity && ! commodity_;
@ -316,21 +317,47 @@ class commodity_t
std::time_t last_lookup;
};
history_t * history_;
history_t *& history() {
return base ? base->history() : history_;
}
const std::string symbol;
bool quote;
std::string name;
std::string note;
unsigned short precision;
unsigned short flags;
std::string name_;
std::string note_;
unsigned short precision_;
unsigned short flags_;
ident_t ident;
history_t * history;
amount_t * smaller;
amount_t * larger;
amount_t * smaller_;
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
// whether prices have been updated in the meanwhile.
static updater_t * updater;
static updater_t * updater;
// This map remembers all commodities that have been defined.
@ -361,14 +388,15 @@ class commodity_t
commodity_t(const std::string& _symbol = "",
unsigned int _precision = 0,
unsigned int _flags = COMMODITY_STYLE_DEFAULTS)
: precision(_precision), flags(_flags), history(NULL),
smaller(NULL), larger(NULL) {
: precision_(_precision), flags_(_flags), history_(NULL),
smaller_(NULL), larger_(NULL), base(NULL), price(NULL) {
set_symbol(_symbol);
}
~commodity_t() {
if (history) delete history;
if (smaller) delete smaller;
if (larger) delete larger;
if (history_) delete history_;
if (smaller_) delete smaller_;
if (larger_) delete larger_;
if (price) delete price;
}
operator bool() const {
@ -385,8 +413,8 @@ class commodity_t
void add_price(const std::time_t date, const amount_t& price);
bool remove_price(const std::time_t date) {
if (history) {
history_map::size_type n = history->prices.erase(date);
if (history_) {
history_map::size_type n = history_->prices.erase(date);
return n > 0;
}
return false;
@ -398,7 +426,7 @@ class commodity_t
if (symbol.empty() && this != null_commodity)
return false;
if (precision > 16)
if (precision_ > 16)
return false;
return true;
@ -417,6 +445,17 @@ inline commodity_t& amount_t::commodity() const {
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 {
std::string reason;
public:

View file

@ -34,6 +34,22 @@ balance_t balance_t::value(const std::time_t moment) const
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 {
bool operator()(const amount_t * left, const amount_t * right) const {
return left->commodity().symbol < right->commodity().symbol;

View file

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

178
binary.cc
View file

@ -11,18 +11,10 @@
namespace ledger {
static unsigned long binary_magic_number = 0xFFEED765;
#ifdef USE_EDITOR
#ifdef DEBUG_ENABLED
static unsigned long format_version = 0x00020589;
static unsigned long format_version = 0x0002050b;
#else
static unsigned long format_version = 0x00020588;
#endif
#else
#ifdef DEBUG_ENABLED
static unsigned long format_version = 0x00020509;
#else
static unsigned long format_version = 0x00020508;
#endif
static unsigned long format_version = 0x0002050a;
#endif
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);
read_binary_value_expr(data, expr->left);
read_binary_value_expr(data, expr->right);
if (kind > value_expr_t::TERMINALS) {
read_binary_value_expr(data, expr->left);
if (expr->left) expr->left->acquire();
}
switch (expr->kind) {
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);
break;
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;
case value_expr_t::F_FUNC:
read_binary_string(data, expr->constant_s);
case value_expr_t::CONSTANT_V:
assert(0);
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)
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;
}
if (read_binary_number<unsigned char>(data) == 1)
read_binary_mask(data, expr->mask);
}
@ -301,18 +310,22 @@ inline void read_binary_transaction(char *& data, transaction_t * xact)
read_binary_long(data, xact->_date_eff);
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);
else
if (xact->amount_expr) xact->amount_expr->acquire();
} else {
read_binary_amount(data, xact->amount);
}
if (*data++ == 1) {
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);
else
if (xact->cost_expr) xact->cost_expr->acquire();
} else {
read_binary_amount(data, *xact->cost);
}
} else {
xact->cost = NULL;
}
@ -322,12 +335,10 @@ inline void read_binary_transaction(char *& data, transaction_t * xact)
xact->flags |= TRANSACTION_BULK_ALLOC;
read_binary_string(data, &xact->note);
#ifdef USE_EDITOR
xact->beg_pos = read_binary_long<unsigned long>(data);
read_binary_long(data, xact->beg_line);
xact->end_pos = read_binary_long<unsigned long>(data);
read_binary_long(data, xact->end_line);
#endif
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,
transaction_t *& xact_pool, bool& finalize)
{
#ifdef USE_EDITOR
read_binary_long(data, entry->src_idx);
entry->beg_pos = read_binary_long<unsigned long>(data);
read_binary_long(data, entry->beg_line);
entry->end_pos = read_binary_long<unsigned long>(data);
read_binary_long(data, entry->end_line);
#endif
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;
read_binary_entry_base(data, entry, xact_pool, ignore);
read_binary_string(data, &entry->predicate_string);
entry->predicate = new item_predicate<transaction_t>(entry->predicate_string);
value_expr_t * expr;
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,
@ -396,10 +407,10 @@ inline commodity_t * read_binary_commodity(char *& data)
read_binary_string(data, *(const_cast<std::string *>(&commodity->symbol)));
read_binary_number(data, commodity->quote);
read_binary_string(data, commodity->name);
read_binary_string(data, commodity->note);
read_binary_number(data, commodity->precision);
read_binary_number(data, commodity->flags);
read_binary_string(data, commodity->name_);
read_binary_string(data, commodity->note_);
read_binary_number(data, commodity->precision_);
read_binary_number(data, commodity->flags_);
read_binary_long(data, commodity->ident);
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
// to be duplicated (and thus not lost when the journal's
// item_pool is deleted.
if (! commodity->history)
commodity->history = new commodity_t::history_t;
commodity->history->prices.insert(history_pair(when, amt));
if (! commodity->history_)
commodity->history_ = new commodity_t::history_t;
commodity->history_->prices.insert(history_pair(when, amt));
}
if (commodity->history)
read_binary_long(data, commodity->history->last_lookup);
if (commodity->history_)
read_binary_long(data, commodity->history_->last_lookup);
unsigned char flag;
@ -434,14 +445,23 @@ inline void read_binary_commodity_extra(char *& data,
if (flag) {
amount_t 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);
if (flag) {
amount_t 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];
for (commodity_t::ident_t i = 0; i < c_count; i++) {
commodity_t * commodity = read_binary_commodity(data);
if (! (commodity->flags & COMMODITY_STYLE_BUILTIN)) {
if (! (commodity->flags_ & COMMODITY_STYLE_BUILTIN)) {
if (commodity->symbol == "") {
commodity_t::commodities.erase(commodity->symbol);
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);
}
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) {
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(out, expr->kind);
write_binary_value_expr(out, expr->left);
write_binary_value_expr(out, expr->right);
if (expr->kind > value_expr_t::TERMINALS)
write_binary_value_expr(out, expr->left);
switch (expr->kind) {
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);
break;
case value_expr_t::CONSTANT_A:
write_binary_amount(out, expr->constant_a);
write_binary_amount(out, *(expr->constant_a));
break;
case value_expr_t::F_FUNC:
write_binary_string(out, expr->constant_s);
case value_expr_t::CONSTANT_V:
assert(0);
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) {
write_binary_number<char>(out, 1);
write_binary_mask(out, expr->mask);
} else {
write_binary_number<char>(out, 0);
}
break;
default:
if (expr->kind > value_expr_t::TERMINALS)
write_binary_value_expr(out, expr->right);
break;
}
if (expr->mask) {
write_binary_number<char>(out, 1);
write_binary_mask(out, expr->mask);
} else {
write_binary_number<char>(out, 0);
}
}
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_string(out, xact->note);
#ifdef USE_EDITOR
write_binary_long(out, xact->beg_pos);
write_binary_long(out, xact->beg_line);
write_binary_long(out, xact->end_pos);
write_binary_long(out, xact->end_line);
#endif
}
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->beg_pos);
write_binary_long(out, entry->beg_line);
write_binary_long(out, entry->end_pos);
write_binary_long(out, entry->end_line);
#endif
bool ignore_calculated = false;
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)
{
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)
@ -869,39 +899,47 @@ void write_binary_commodity(std::ostream& out, commodity_t * commodity)
{
write_binary_string(out, commodity->symbol);
write_binary_number(out, commodity->quote);
write_binary_string(out, commodity->name);
write_binary_string(out, commodity->note);
write_binary_number(out, commodity->precision);
write_binary_number(out, commodity->flags);
write_binary_string(out, commodity->name_);
write_binary_string(out, commodity->note_);
write_binary_number(out, commodity->precision_);
write_binary_number(out, commodity->flags_);
commodity->ident = ++commodity_index;
write_binary_long(out, commodity->ident);
}
void write_binary_commodity_extra(std::ostream& out, commodity_t * commodity)
{
if (! commodity->history) {
if (! commodity->history_) {
write_binary_long<unsigned long>(out, 0);
} else {
write_binary_long<unsigned long>(out, commodity->history->prices.size());
for (history_map::const_iterator i = commodity->history->prices.begin();
i != commodity->history->prices.end();
write_binary_long<unsigned long>(out, commodity->history_->prices.size());
for (history_map::const_iterator i = commodity->history_->prices.begin();
i != commodity->history_->prices.end();
i++) {
write_binary_long(out, (*i).first);
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_amount(out, *commodity->smaller);
write_binary_amount(out, *commodity->smaller_);
} else {
write_binary_number<unsigned char>(out, 0);
}
if (commodity->larger) {
if (commodity->larger_) {
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 {
write_binary_number<unsigned char>(out, 0);
}

190
config.cc
View file

@ -17,15 +17,43 @@
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()
{
amount_expr = "a";
total_expr = "O";
total_expr_template = "#";
amount_expr = "a";
total_expr = "O";
pricing_leeway = 24 * 3600;
budget_flags = BUDGET_NO_BUDGET;
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%/"
"%48|%-.38A %22.108t %22.132T\n");
csv_register_format = "\"%D\",\"%P\",\"%A\",\"%t\",\"%T\"\n";
plot_amount_format = "%D %(St)\n";
plot_total_format = "%D %(ST)\n";
plot_amount_format = "%D %(S(t))\n";
plot_total_format = "%D %(S(T))\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_xact_format = " %-34W %12o%n\n";
@ -62,6 +90,7 @@ void config_t::reset()
show_revalued = false;
show_revalued_only = false;
download_quotes = false;
debug_mode = false;
use_cache = false;
cache_dirty = false;
@ -133,7 +162,7 @@ config_t::regexps_to_predicate(const std::string& command,
if (! show_related && ! show_all_related) {
if (! display_predicate.empty())
display_predicate += "&";
else if (! show_empty)
if (! show_empty)
display_predicate += "T&";
if (add_predicate == 2)
@ -143,6 +172,8 @@ config_t::regexps_to_predicate(const std::string& command,
display_predicate += ")/";
}
else if (! show_empty) {
if (! display_predicate.empty())
display_predicate += "&";
display_predicate += "T";
}
}
@ -178,6 +209,18 @@ void config_t::process_environment(char ** envp, const std::string& tag)
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,
strings_list::iterator arg,
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
try {
ledger::amount_expr.reset(parse_value_expr(amount_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());
}
ledger::amount_expr.reset(new value_expr(amount_expr));
ledger::total_expr.reset(new value_expr(total_expr));
// If downloading is to be supported, configure the updater
@ -683,6 +702,10 @@ OPT_BEGIN(account, "a:") {
config->account = optarg;
} OPT_END(account);
OPT_BEGIN(debug, "") {
config->debug_mode = true;
} OPT_END(debug);
//////////////////////////////////////////////////////////////////////
//
// Report filtering
@ -755,6 +778,10 @@ OPT_BEGIN(actual, "L") {
config->predicate += "L";
} OPT_END(actual);
OPT_BEGIN(lots, "") {
show_lots = true;
} OPT_END(lots);
//////////////////////////////////////////////////////////////////////
//
// Output customization
@ -1013,6 +1040,12 @@ OPT_BEGIN(basis, "B") {
config->total_expr = "B";
} 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") {
config->show_revalued = true;
@ -1034,15 +1067,98 @@ OPT_BEGIN(gain, "G") {
} OPT_END(gain);
OPT_BEGIN(average, "A") {
config->total_expr_template = "A#";
config->total_expr = expand_value_expr("A(#)", config->total_expr);
} OPT_END(average);
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_BEGIN(percentage, "%") {
config->total_expr_template = "^#&{100.0%}*(#/^#)";
config->total_expr = expand_value_expr("^#&{100.0%}*(#/^#)", config->total_expr);
} 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

View file

@ -42,7 +42,6 @@ class config_t
std::string sort_string;
std::string amount_expr;
std::string total_expr;
std::string total_expr_template;
std::string forecast_limit;
std::string reconcile_balance;
std::string reconcile_date;
@ -66,6 +65,7 @@ class config_t
bool download_quotes;
bool use_cache;
bool cache_dirty;
bool debug_mode;
config_t() {
reset();
@ -73,7 +73,6 @@ class config_t
config_t(const config_t&) {
assert(0);
}
void reset();
void regexps_to_predicate(const std::string& command,
@ -100,24 +99,15 @@ class config_t
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);
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) \
void opt_ ## tag(const char * optarg)
#define OPT_BEGIN(tag, chars) \
static struct opt_ ## tag ## _handler \
: 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

View file

@ -200,15 +200,6 @@ AC_ARG_ENABLE(debug,
esac],[debug=false])
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.
AC_STDC_HEADERS
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_() DEBUG(_debug_cls)
#define DEBUG_IF(cls) if (_debug_active(cls))
#define DEBUG_IF_() if (_debug_active(_debug_cls))
#define DEBUG_PRINT(cls, x) \
if (_debug_stream && _debug_active(cls)) { \
*_debug_stream << x << std::endl; \
@ -91,6 +94,8 @@ void operator delete[](void*, const std::nothrow_t&) throw();
#define DEBUG_CLASS(cls)
#define DEBUG(cls) 0
#define DEBUG_() 0
#define DEBUG_IF(cls)
#define DEBUG_IF_()
#define DEBUG_PRINT(cls, x)
#define DEBUG_PRINT_(x)
#define DEBUG_PRINT_TIME(cls, x)

120
derive.cc
View file

@ -23,7 +23,8 @@ entry_t * derive_new_entry(journal_t& journal,
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++)
if (regexp.match((*j)->payee)) {
@ -33,23 +34,42 @@ entry_t * derive_new_entry(journal_t& journal,
added->payee = matching ? matching->payee : regexp.pattern;
if (i == end) {
if (! matching)
throw error("Could not find a matching payee");
if (! matching) {
account_t * acct;
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
// to see the same transaction as last time.
added->code = matching->code;
for (transactions_list::iterator j = matching->transactions.begin();
j != matching->transactions.end();
j++)
added->add_transaction(new transaction_t(**j));
for (transactions_list::iterator k = matching->transactions.begin();
k != matching->transactions.end();
k++)
added->add_transaction(new transaction_t(**k));
}
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;
m_xact = matching->transactions.front();
@ -68,67 +88,65 @@ entry_t * derive_new_entry(journal_t& journal,
account_t * acct = journal.find_account_re(*i);
if (! acct)
acct = journal.find_account(*i);
if (acct)
added->transactions.back()->account = acct;
assert(acct);
added->transactions.back()->account = acct;
}
}
else {
while (i != end) {
std::string& re_pat(*i++);
account_t * acct = NULL;
commodity_t * cmdty = NULL;
std::string& re_pat(*i++);
account_t * acct = 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
= matching->transactions.begin();
x != matching->transactions.end();
x++) {
if (acct_regex.match((*x)->account->fullname())) {
acct = (*x)->account;
cmdty = &(*x)->amount.commodity();
break;
}
for (; j != journal.entries.rend(); j++)
if (regexp.match((*j)->payee)) {
entry_t * entry = *j;
for (transactions_list::const_iterator x =
entry->transactions.begin();
x != entry->transactions.end();
x++)
if (acct_regex.match((*x)->account->fullname())) {
acct = (*x)->account;
amt = &(*x)->amount;
matching = entry;
goto found;
}
}
}
found:
if (! acct)
acct = journal.find_account_re(re_pat);
if (! acct)
acct = journal.find_account(re_pat);
transaction_t * xact;
if (i == end) {
added->add_transaction(new transaction_t(acct));
goto done;
if (amt)
xact = new transaction_t(acct, *amt);
else
xact = new transaction_t(acct);
} else {
xact = new transaction_t(acct, amount_t(*i++));
if (! xact->amount.commodity()) {
if (amt)
xact->amount.set_commodity(amt->commodity());
else if (commodity_t::default_commodity)
xact->amount.set_commodity(*commodity_t::default_commodity);
}
}
transaction_t * xact = new transaction_t(acct, amount_t(*i++));
if (! xact->amount.commodity()) {
if (cmdty)
xact->amount.set_commodity(*cmdty);
else if (commodity_t::default_commodity)
xact->amount.set_commodity(*commodity_t::default_commodity);
}
added->add_transaction(xact);
}
account_t * draw_acct;
if (matching)
draw_acct = matching->transactions.back()->account;
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);
assert(matching->transactions.back()->account);
if (account_t * draw_acct = matching->transactions.back()->account)
added->add_transaction(new transaction_t(draw_acct));
}
done:
if (! added->finalize() ||
! run_hooks(journal.entry_finalize_hooks, *added))
if (! run_hooks(journal.entry_finalize_hooks, *added) ||
! added->finalize())
throw error("Failed to finalize derived entry (check commodities)");
return added.release();

View file

@ -162,13 +162,9 @@ element_t * format_t::parse_elements(const std::string& fmt)
throw format_error("Missing ')'");
current->type = element_t::VALUE_EXPR;
try {
current->val_expr = parse_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());
}
assert(current->val_expr == NULL);
current->val_expr = new value_expr(std::string(b, p));
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::TOTAL:
case element_t::VALUE_EXPR: {
value_expr_t * expr = NULL;
value_calc * calc = NULL;
switch (elem->type) {
case element_t::AMOUNT: expr = amount_expr.get(); break;
case element_t::TOTAL: expr = total_expr.get(); break;
case element_t::VALUE_EXPR: expr = elem->val_expr; break;
case element_t::AMOUNT: calc = amount_expr.get(); break;
case element_t::TOTAL: calc = total_expr.get(); break;
case element_t::VALUE_EXPR: calc = elem->val_expr; break;
default:
assert(0);
break;
}
if (! expr)
if (! calc)
break;
value_t value;
balance_t * bal = NULL;
expr->compute(value, details);
calc->compute(value, details);
switch (value.type) {
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;
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)
unit_cost.commodity().flags |= COMMODITY_STYLE_VARIABLE;
unit_cost.commodity().flags() |= COMMODITY_STYLE_VARIABLE;
std::ostringstream stream;
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;
if (! has_flag)
unit_cost.commodity().flags &= ~COMMODITY_STYLE_VARIABLE;
unit_cost.commodity().flags() &= ~COMMODITY_STYLE_VARIABLE;
}
else if (details.entry) {
unsigned int xacts_count = 0;
@ -388,7 +384,6 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
}
break;
#ifdef USE_EDITOR
case element_t::SOURCE:
if (details.entry && details.entry->journal) {
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)
out << details.xact->end_line;
break;
#endif
case element_t::DATE_STRING: {
std::time_t date = 0;
@ -617,7 +611,7 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
}
}
format_transactions::format_transactions(std::ostream& _output_stream,
format_transactions::format_transactions(std::ostream& _output_stream,
const std::string& format)
: output_stream(_output_stream), last_entry(NULL), last_xact(NULL)
{
@ -731,7 +725,7 @@ bool disp_subaccounts_p(const account_t& account,
return display;
}
bool display_account(const account_t& account,
bool display_account(const account_t& account,
const item_predicate<account_t>& disp_pred)
{
// Never display an account that has already been displayed.

View file

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

View file

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

View file

@ -13,6 +13,14 @@ const std::string version = PACKAGE_VERSION;
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
{
if (_date == 0 && entry)
@ -96,6 +104,10 @@ bool entry_base_t::finalize()
} else {
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;
unsigned short flags;
std::string note;
#ifdef USE_EDITOR
istream_pos_type beg_pos;
unsigned long beg_line;
istream_pos_type end_pos;
unsigned long end_line;
#endif
mutable void * data;
static bool use_effective_date;
@ -58,13 +56,9 @@ class transaction_t
: entry(NULL), _date(0), _date_eff(0), account(_account),
amount_expr(NULL), cost(NULL), cost_expr(NULL),
state(UNCLEARED), flags(TRANSACTION_NORMAL),
#ifdef USE_EDITOR
beg_pos(0), beg_line(0), end_pos(0), end_line(0),
#endif
data(NULL) {
beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) {
DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t");
}
transaction_t(account_t * _account,
const amount_t& _amount,
unsigned int _flags = TRANSACTION_NORMAL,
@ -72,30 +66,18 @@ class transaction_t
: entry(NULL), _date(0), _date_eff(0), account(_account),
amount(_amount), amount_expr(NULL), cost(NULL), cost_expr(NULL),
state(UNCLEARED), flags(_flags), note(_note),
#ifdef USE_EDITOR
beg_pos(0), beg_line(0), end_pos(0), end_line(0),
#endif
data(NULL) {
beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) {
DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t");
}
transaction_t(const transaction_t& xact)
: entry(xact.entry), _date(0), _date_eff(0), account(xact.account),
amount(xact.amount), amount_expr(NULL),
cost(xact.cost ? new amount_t(*xact.cost) : NULL), cost_expr(NULL),
state(xact.state), flags(xact.flags), note(xact.note),
#ifdef USE_EDITOR
beg_pos(0), beg_line(0), end_pos(0), end_line(0),
#endif
data(NULL) {
beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) {
DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t");
}
~transaction_t() {
DEBUG_PRINT("ledger.memory.dtors", "dtor transaction_t");
if (cost)
delete cost;
}
~transaction_t();
std::time_t actual_date() const;
std::time_t effective_date() const;
@ -124,26 +106,20 @@ class entry_base_t
{
public:
journal_t * journal;
#ifdef USE_EDITOR
unsigned long src_idx;
istream_pos_type beg_pos;
unsigned long beg_line;
istream_pos_type end_pos;
unsigned long end_line;
#endif
transactions_list transactions;
entry_base_t() : journal(NULL),
#ifdef USE_EDITOR
beg_pos(0), beg_line(0), end_pos(0), end_line(0)
#endif
{
DEBUG_PRINT("ledger.memory.ctors", "ctor entry_base_t");
}
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)
#endif
{
DEBUG_PRINT("ledger.memory.ctors", "ctor entry_base_t");
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";
else if (command == "output")
command = "w";
#ifdef USE_EDITOR
else if (command == "emacs")
command = "x";
#endif
else if (command == "lisp")
command = "x";
else if (command == "xml")
command = "X";
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;
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
throw error(std::string("Unrecognized command '") + command + "'");
@ -211,6 +225,20 @@ int parse_and_report(int argc, char * argv[], char * envp[])
}
#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
const std::string * format;
@ -245,10 +273,8 @@ int parse_and_report(int argc, char * argv[], char * envp[])
formatter = new set_account_value;
else if (command == "p" || command == "e")
formatter = new format_entries(*out, *format);
#ifdef USE_EDITOR
else if (command == "x")
formatter = new format_emacs_transactions(*out);
#endif
else if (command == "X") {
#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
formatter = new format_xml_entries(*out, config.show_totals);

187
option.cc
View file

@ -1,4 +1,5 @@
#include "option.h"
#include "config.h"
#include "debug.h"
#include <iostream>
@ -6,149 +7,125 @@
#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 {
inline void process_option(const option_t& opt,
const char * arg = NULL) {
if (! opt.handler->handled) {
(*opt.handler)(arg);
opt.handler->handled = true;
inline void process_option(option_t * opt, const char * arg = NULL) {
if (! opt->handled) {
opt->handler(arg);
opt->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,
const std::string& opt, const char * arg)
bool process_option(option_t * options, const std::string& name,
const char * arg)
{
for (std::list<option_t>::iterator i = options.begin();
i != options.end();
i++)
if ((*i).long_opt == opt) {
if (! (*i).handler->handled) {
(*(*i).handler)(arg);
(*i).handler->handled = true;
return true;
}
break;
}
option_t * opt = search_options(options, name.c_str());
if (opt != NULL && ! opt->handled) {
opt->handler(arg);
opt->handled = true;
return true;
}
return false;
}
void process_arguments(std::list<option_t>& options,
int argc, char ** argv, const bool anywhere,
std::list<std::string>& args)
void process_arguments(option_t * options, int argc, char ** argv,
const bool anywhere, std::list<std::string>& args)
{
int index = 0;
for (char ** i = argv; index < argc; i++, index++) {
for (char ** i = argv; *i; i++) {
if ((*i)[0] != '-') {
if (anywhere) {
args.push_back(*i);
continue;
} else {
for (; index < argc; i++, index++)
for (; *i; i++)
args.push_back(*i);
break;
}
}
// --long-option
// --long-option or -s
again:
option_t * opt = NULL;
char * value = NULL;
if ((*i)[1] == '-') {
if ((*i)[2] == '\0')
break;
for (std::list<option_t>::iterator j = options.begin();
j != options.end();
j++)
if ((*j).wants_arg) {
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;
}
char * name = *i + 2;
if (char * p = std::strchr(name, '=')) {
*p++ = '\0';
value = p;
}
throw option_error(std::string("illegal option ") + *i);
opt = search_options(options, name);
if (opt == NULL)
throw option_error(std::string("illegal option --") + name);
if (opt->wants_arg && value == NULL) {
value = *++i;
if (value == NULL)
throw option_error(std::string("missing option argument for --") +
name);
}
process_option(opt, value);
} else {
for (std::list<option_t>::iterator j = options.begin();
j != options.end();
j++)
if ((*i)[1] == (*j).short_opt) {
if ((*j).wants_arg) {
if (++index >= argc)
throw option_error(std::string("missing argument for option ") +
*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;
}
}
char c = (*i)[1];
opt = search_options(options, c);
if (opt == NULL)
throw option_error(std::string("illegal option -") + c);
throw option_error(std::string("illegal option -- ") + (*i)[1]);
if (opt->wants_arg) {
value = *++i;
if (value == NULL)
throw option_error(std::string("missing option argument for -") + c);
}
}
assert(opt);
assert(value == NULL || opt->wants_arg);
process_option(opt, value);
next:
;
}
}
void process_environment(std::list<option_t>& options,
char ** envp, const std::string& tag)
void process_environment(option_t * options, char ** envp,
const std::string& tag)
{
const char * tag_p = tag.c_str();
int tag_len = tag.length();
unsigned int tag_len = tag.length();
for (char ** p = envp; *p; p++)
if (! tag_p || std::strncmp(*p, tag_p, tag_len) == 0) {

View file

@ -5,20 +5,14 @@
#include <string>
#include <exception>
struct option_handler {
bool handled;
option_handler() : handled(false) {}
virtual ~option_handler() {}
virtual void operator()(const char * arg = NULL) = 0;
};
typedef void (*handler_t)(const char * arg);
struct option_t {
char short_opt;
std::string long_opt;
bool wants_arg;
option_handler * handler;
option_t() : short_opt(0), wants_arg(false), handler(NULL) {}
const char * long_opt;
char short_opt;
bool wants_arg;
handler_t handler;
bool handled;
};
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,
const std::string& opt_chars, option_handler& option);
bool process_option(std::list<option_t>& options,
const std::string& opt, const char * arg = NULL);
void process_arguments(std::list<option_t>& options,
int argc, char ** argv, const bool anywhere,
std::list<std::string>& args);
void process_environment(std::list<option_t>& options,
char ** envp, const std::string& tag);
bool process_option(option_t * options, const std::string& opt,
const char * arg = NULL);
void process_arguments(option_t * options, int argc, char ** argv,
const bool anywhere, std::list<std::string>& args);
void process_environment(option_t * options, char ** envp,
const std::string& tag);
#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;
linenum = 1;
#ifdef USE_EDITOR
istream_pos_type beg_pos = 0;
unsigned long beg_line = 0;
@ -72,9 +71,6 @@ unsigned int qif_parser_t::parse(std::istream& in,
beg_pos = in.tellg(); \
beg_line = linenum; \
}
#else
#define SET_BEG_POS_AND_LINE()
#endif
while (in.good() && ! in.eof()) {
char c;
@ -119,16 +115,16 @@ unsigned int qif_parser_t::parse(std::istream& in,
get_line(in);
xact->amount.parse(line);
unsigned long flags = xact->amount.commodity().flags;
unsigned short prec = xact->amount.commodity().precision;
unsigned long flags = xact->amount.commodity().flags();
unsigned short prec = xact->amount.commodity().precision();
if (! def_commodity)
def_commodity = commodity_t::find_commodity("$", true);
xact->amount.set_commodity(*def_commodity);
def_commodity->flags |= flags;
if (prec > def_commodity->precision)
def_commodity->precision = prec;
def_commodity->flags() |= flags;
if (prec > def_commodity->precision())
def_commodity->precision() = prec;
if (c == '$') {
saw_splits = true;
@ -221,13 +217,11 @@ unsigned int qif_parser_t::parse(std::istream& in,
}
if (journal->add_entry(entry.get())) {
#ifdef USE_EDITOR
entry->src_idx = src_idx;
entry->beg_pos = beg_pos;
entry->beg_line = beg_line;
entry->end_pos = in.tellg();
entry->end_line = linenum;
#endif
entry.release();
count++;
}
@ -240,9 +234,7 @@ unsigned int qif_parser_t::parse(std::istream& in,
saw_splits = false;
saw_category = false;
total = NULL;
#ifdef USE_EDITOR
beg_line = 0;
#endif
break;
}

View file

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

View file

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

1164
valexpr.cc

File diff suppressed because it is too large Load diff

292
valexpr.h
View file

@ -16,6 +16,7 @@ struct details_t
const transaction_t * xact;
const account_t * account;
details_t() : entry(NULL), xact(NULL), account(NULL) {}
details_t(const entry_t& _entry)
: entry(&_entry), xact(NULL), account(NULL) {
DEBUG_PRINT("ledger.memory.ctors", "ctor details_t");
@ -32,6 +33,29 @@ struct details_t
#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
{
enum kind_t {
@ -39,10 +63,14 @@ struct value_expr_t
CONSTANT_I,
CONSTANT_T,
CONSTANT_A,
CONSTANT_V,
CONSTANTS,
// Item details
AMOUNT,
COST,
PRICE,
DATE,
CLEARED,
PENDING,
@ -55,6 +83,7 @@ struct value_expr_t
COUNT,
TOTAL,
COST_TOTAL,
PRICE_TOTAL,
// Relating to format_t
VALUE_EXPR,
@ -62,13 +91,12 @@ struct value_expr_t
// Functions
F_NOW,
F_PARENT,
F_ARITH_MEAN,
F_QUANTITY,
F_COMMODITY,
F_SET_COMMODITY,
F_VALUE,
F_FUNC,
F_NEG,
F_ABS,
F_STRIP,
F_CODE_MASK,
F_PAYEE_MASK,
F_NOTE_MASK,
@ -76,11 +104,18 @@ struct value_expr_t
F_SHORT_ACCOUNT_MASK,
F_COMMODITY_MASK,
TERMINALS,
F_PARENT,
// Binary operators
O_NEG,
O_ADD,
O_SUB,
O_MUL,
O_DIV,
O_PERC,
O_NEQ,
O_EQ,
O_LT,
O_LTE,
@ -91,88 +126,244 @@ struct value_expr_t
O_OR,
O_QUES,
O_COL,
O_COM,
O_DEF,
O_REF,
O_ARG,
LAST
};
kind_t kind;
mutable short refc;
value_expr_t * left;
value_expr_t * right;
union {
std::time_t constant_t;
long constant_i;
std::time_t constant_t;
long constant_i;
amount_t * constant_a;
value_t * constant_v;
mask_t * mask;
value_expr_t * right;
};
std::string constant_s;
amount_t constant_a;
mask_t * mask;
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");
}
~value_expr_t();
~value_expr_t() {
DEBUG_PRINT("ledger.memory.dtors", "dtor value_expr_t");
if (mask) delete mask;
if (left) delete left;
if (right) delete right;
void release() const {
DEBUG_PRINT("ledger.valexpr.memory",
"Releasing " << this << ", refc now " << refc - 1);
assert(refc > 0);
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;
extern std::auto_ptr<value_expr_t> total_expr;
struct scope_t
{
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 bool initialized;
void init_value_expr();
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) {
if (amount_expr.get())
if (amount_expr.get() != NULL)
amount_expr->compute(result, 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);
}
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>
class item_predicate
{
const value_expr_t * predicate;
bool allocated;
public:
item_predicate(const std::string& _predicate)
: predicate(NULL), allocated(false) {
const value_expr_t * predicate;
item_predicate(const std::string& _predicate) : predicate(NULL) {
DEBUG_PRINT("ledger.memory.ctors", "ctor item_predicate<T>");
if (! _predicate.empty()) {
try {
predicate = parse_value_expr(_predicate);
allocated = true;
predicate = parse_value_expr(_predicate)->acquire();
}
catch (value_expr_error& err) {
throw value_expr_error(std::string("In predicate '") +
@ -181,14 +372,14 @@ class item_predicate
}
}
item_predicate(const value_expr_t * _predicate = NULL)
: predicate(_predicate), allocated(false) {
: predicate(_predicate->acquire()) {
DEBUG_PRINT("ledger.memory.ctors", "ctor item_predicate<T>");
}
~item_predicate() {
DEBUG_PRINT("ledger.memory.dtors", "dtor item_predicate<T>");
if (predicate && allocated)
delete predicate;
if (predicate)
predicate->release();
}
bool operator()(const T& item) const {
@ -196,9 +387,8 @@ class item_predicate
value_t result;
predicate->compute(result, details_t(item));
return result;
} else {
return true;
}
return true;
}
};

View file

@ -747,6 +747,38 @@ value_t value_t::cost() const
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)
{
switch (type) {

View file

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

30
walk.cc
View file

@ -7,6 +7,8 @@
namespace ledger {
bool show_lots = false;
template <>
bool compare_items<transaction_t>::operator()(const transaction_t * left,
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)
{
if (transaction_has_xdata(xact) &&
transaction_xdata_(xact).dflags & TRANSACTION_COMPOSITE)
transaction_xdata_(xact).dflags & TRANSACTION_COMPOSITE) {
value += transaction_xdata_(xact).composite_amount;
else if (xact.cost || value)
value.add(xact.amount, xact.cost);
else
value = xact.amount;
}
else if (xact.cost || value) {
amount_t * cost = xact.cost;
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()
@ -790,7 +800,7 @@ void walk_accounts(account_t& account,
const std::string& sort_string)
{
if (! sort_string.empty()) {
std::auto_ptr<value_expr_t> sort_order;
value_auto_ptr sort_order;
try {
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();
i != commodities.end();
i++) {
if ((*i).second->flags & COMMODITY_STYLE_NOMARKET)
if ((*i).second->flags() & COMMODITY_STYLE_NOMARKET)
continue;
entry_temps.push_back(entry_t());
acct_temps.push_back(account_t(NULL, (*i).second->symbol));
if ((*i).second->history)
for (history_map::iterator j = (*i).second->history->prices.begin();
j != (*i).second->history->prices.end();
if ((*i).second->history())
for (history_map::iterator j = (*i).second->history()->prices.begin();
j != (*i).second->history()->prices.end();
j++) {
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));
}
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,
@ -212,20 +217,19 @@ class sort_transactions : public item_handler<transaction_t>
transactions_deque transactions;
const value_expr_t * sort_order;
bool allocated;
public:
sort_transactions(item_handler<transaction_t> * handler,
const value_expr_t * _sort_order)
: item_handler<transaction_t>(handler),
sort_order(_sort_order), allocated(false) {}
sort_order(_sort_order) {}
sort_transactions(item_handler<transaction_t> * handler,
const std::string& _sort_order)
: item_handler<transaction_t>(handler), allocated(false) {
: item_handler<transaction_t>(handler) {
try {
sort_order = parse_value_expr(_sort_order);
allocated = true;
sort_order->acquire();
}
catch (value_expr_error& err) {
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() {
assert(sort_order);
if (allocated)
delete sort_order;
sort_order->release();
}
virtual void post_accumulated_xacts();

57
xml.cc
View file

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