Checked in all major updates.
This commit is contained in:
parent
4507573d4f
commit
7901598f1d
30 changed files with 1934 additions and 1050 deletions
|
|
@ -7,6 +7,7 @@ libledger_la_SOURCES = \
|
||||||
config.cc \
|
config.cc \
|
||||||
datetime.cc \
|
datetime.cc \
|
||||||
derive.cc \
|
derive.cc \
|
||||||
|
emacs.cc \
|
||||||
format.cc \
|
format.cc \
|
||||||
journal.cc \
|
journal.cc \
|
||||||
mask.cc \
|
mask.cc \
|
||||||
|
|
@ -20,10 +21,6 @@ libledger_la_SOURCES = \
|
||||||
valexpr.cc \
|
valexpr.cc \
|
||||||
value.cc \
|
value.cc \
|
||||||
walk.cc
|
walk.cc
|
||||||
if USE_EDITOR
|
|
||||||
libledger_la_CXXFLAGS += -DUSE_EDITOR=1
|
|
||||||
libledger_la_SOURCES += emacs.cc
|
|
||||||
endif
|
|
||||||
if HAVE_EXPAT
|
if HAVE_EXPAT
|
||||||
libledger_la_CXXFLAGS += -DHAVE_EXPAT=1
|
libledger_la_CXXFLAGS += -DHAVE_EXPAT=1
|
||||||
libledger_la_SOURCES += gnucash.cc xml.cc
|
libledger_la_SOURCES += gnucash.cc xml.cc
|
||||||
|
|
@ -77,9 +74,6 @@ bin_PROGRAMS = ledger
|
||||||
ledger_CXXFLAGS =
|
ledger_CXXFLAGS =
|
||||||
ledger_SOURCES = main.cc
|
ledger_SOURCES = main.cc
|
||||||
ledger_LDADD = $(LIBOBJS) libledger.la
|
ledger_LDADD = $(LIBOBJS) libledger.la
|
||||||
if USE_EDITOR
|
|
||||||
ledger_CXXFLAGS += -DUSE_EDITOR=1
|
|
||||||
endif
|
|
||||||
if HAVE_EXPAT
|
if HAVE_EXPAT
|
||||||
ledger_CXXFLAGS += -DHAVE_EXPAT=1
|
ledger_CXXFLAGS += -DHAVE_EXPAT=1
|
||||||
ledger_LDADD += -lexpat
|
ledger_LDADD += -lexpat
|
||||||
|
|
|
||||||
114
NEWS
114
NEWS
|
|
@ -3,18 +3,87 @@
|
||||||
|
|
||||||
* 2.5
|
* 2.5
|
||||||
|
|
||||||
- Added a new "csv" command, for outputting results in CSV format.
|
- There have a few changes to value expression syntax. The most
|
||||||
|
significant incompatibilities being:
|
||||||
|
|
||||||
|
* Equality is now ==, not =
|
||||||
|
* The U, A, and S functions now requires parens around the argument.
|
||||||
|
Whereas before At was acceptable, now it must be specified as
|
||||||
|
A(t).
|
||||||
|
* The P function now always requires two arguments. The old
|
||||||
|
one-argument version P(x) is now the same as P(x,m).
|
||||||
|
|
||||||
|
The following value expression features are new:
|
||||||
|
|
||||||
|
* A C-like comma operator is supported, where all but the last term
|
||||||
|
are ignored. The is significant for the next feature:
|
||||||
|
* Function definitions are now supported. Scoping is governed
|
||||||
|
by parentheses. For example:
|
||||||
|
(x=100, x+10) ; yields 110 as the result
|
||||||
|
(f(x)=x*2,f(100)) ; yields 200 as the result
|
||||||
|
* Identifier names may be any length. Along with this support comes
|
||||||
|
alternate, longer names for all of the current one-letter value
|
||||||
|
expression variables:
|
||||||
|
|
||||||
|
Old New
|
||||||
|
--- ---
|
||||||
|
m now
|
||||||
|
a amount
|
||||||
|
a amount
|
||||||
|
b cost
|
||||||
|
i price
|
||||||
|
d date
|
||||||
|
X cleared
|
||||||
|
Y pending
|
||||||
|
R real
|
||||||
|
L actual
|
||||||
|
n index
|
||||||
|
N count
|
||||||
|
l depth
|
||||||
|
O total
|
||||||
|
B cost_total
|
||||||
|
I price_total
|
||||||
|
v market
|
||||||
|
V market_total
|
||||||
|
g gain
|
||||||
|
G gain_total
|
||||||
|
U(x) abs(x)
|
||||||
|
S(x) quant(x), quantity(x)
|
||||||
|
comm(x), commodity(x)
|
||||||
|
setcomm(x,y), set_commodity(x,y)
|
||||||
|
A(x) mean(x), avg(x), average(x)
|
||||||
|
P(x,y) val(x,y), value(x,y)
|
||||||
|
min(x,y)
|
||||||
|
max(x,y)
|
||||||
|
|
||||||
|
- There are new "parse" and "expr" commands, whose argument is a
|
||||||
|
single value expression. Ledger will simply print out the result of
|
||||||
|
evaluating it. "parse" happens before parsing your ledger file,
|
||||||
|
while "expr" happens afterward. Although "expr" is slower as a
|
||||||
|
result, any commodities you use will be formatted based on patterns
|
||||||
|
of usage seen in your ledger file.
|
||||||
|
|
||||||
|
These commands can be used to test value expressions, or for doing
|
||||||
|
calculation of commoditized amounts from a script.
|
||||||
|
|
||||||
|
A new "--debug" will also dump the resulting parse tree, useful for
|
||||||
|
submitting bug reports.
|
||||||
|
|
||||||
|
- Added new min(x,y) and max(x,y) value expression functions.
|
||||||
|
|
||||||
- Added a new value expression regexp command:
|
- Added a new value expression regexp command:
|
||||||
C// compare against transaction amount's commodity symbol
|
C// compare against a transaction amount's commodity symbol
|
||||||
|
|
||||||
- Added new @min(x,y) and @max(x,y) value expression functions.
|
- Value expression function may now be defined within your ledger file
|
||||||
|
(or initialization file) using the following syntax:
|
||||||
|
|
||||||
- A new configure option "--disable-emacs" will disable generation of
|
@def foo(x)=x*1000
|
||||||
transaction and entry location info, which is used by ledger.el and
|
|
||||||
the "write" command. If you use neither of these, then disabling
|
This line makes the function "foo" available to all subsequent value
|
||||||
them will cut textual parsing time in half, and binary loading time
|
expressions, to all command-line options taking a value expression,
|
||||||
(and cache file size) by a third.
|
and to the new "expr" command (see above).
|
||||||
|
|
||||||
|
- Added a new "csv" command, for outputting results in CSV format.
|
||||||
|
|
||||||
- Effective dates may now be specified for entries:
|
- Effective dates may now be specified for entries:
|
||||||
|
|
||||||
|
|
@ -74,28 +143,27 @@
|
||||||
transactions belong to the same entry.
|
transactions belong to the same entry.
|
||||||
|
|
||||||
- Individual transactions may now be cleared separately. The old
|
- Individual transactions may now be cleared separately. The old
|
||||||
syntax, which is still supported, clears all transactions in the
|
syntax, which is still supported, clears all transactions in an
|
||||||
entry:
|
entry:
|
||||||
|
|
||||||
2004/05/27 * Book Store
|
2004/05/27 * Book Store
|
||||||
Expenses:Dining $20.00
|
Expenses:Dining $20.00
|
||||||
Liabilities:MasterCard
|
Liabilities:MasterCard
|
||||||
|
|
||||||
The new (and optional) syntax allows clearing just the MasterCard
|
The new syntax allows clearing of just the MasterCard transaction:
|
||||||
transaction:
|
|
||||||
|
|
||||||
2004/05/27 Book Store
|
2004/05/27 Book Store
|
||||||
Expenses:Dining $20.00
|
Expenses:Dining $20.00
|
||||||
* Liabilities:MasterCard
|
* Liabilities:MasterCard
|
||||||
|
|
||||||
NOTE: This changes the output format of the "emacs" and "xml"
|
NOTE: This changes the output format of both the "emacs" and "xml"
|
||||||
reports. ledger.el will use the new syntax unless the Lisp variable
|
reports. ledger.el uses the new syntax unless the Lisp variable
|
||||||
`ledger-clear-whole-entries' is set to t.
|
`ledger-clear-whole-entries' is set to t.
|
||||||
|
|
||||||
- Removed Python integration support.
|
- Removed Python integration support.
|
||||||
|
|
||||||
- Did much internal restructuring to allow the use of libledger.so in
|
- Did much internal restructuring to allow the use of libledger.so in
|
||||||
non-command-line environments.
|
non-command-line environments (such as GUI tools).
|
||||||
|
|
||||||
* 2.4
|
* 2.4
|
||||||
|
|
||||||
|
|
@ -264,27 +332,27 @@ command-line driver.
|
||||||
|
|
||||||
-d now specifies the display predicate. To give a date mask similar
|
-d now specifies the display predicate. To give a date mask similar
|
||||||
to 1.7, use the -p (period) option.
|
to 1.7, use the -p (period) option.
|
||||||
|
|
||||||
-P now generates the "by payee" report. To specify a price database
|
-P now generates the "by payee" report. To specify a price database
|
||||||
to use, use --price-db.
|
to use, use --price-db.
|
||||||
|
|
||||||
-G now generates a net gain report. To print totals in a format
|
-G now generates a net gain report. To print totals in a format
|
||||||
consumable by gnuplot, use -J.
|
consumable by gnuplot, use -J.
|
||||||
|
|
||||||
-l now specifies the calculation predicate. To emulate the old
|
-l now specifies the calculation predicate. To emulate the old
|
||||||
usage of "-l \$100", use: -d "AT>100".
|
usage of "-l \$100", use: -d "AT>100".
|
||||||
|
|
||||||
-N is gone. Instead of "-N REGEX", use: -d "/REGEX/?T>0:T".
|
-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
|
-F now specifies the report format string. The old meaning of -F
|
||||||
now has little use.
|
now has little use.
|
||||||
|
|
||||||
-S now takes a value expression as the sorting criterion. To get
|
-S now takes a value expression as the sorting criterion. To get
|
||||||
the old meaning of "-S", use "-S d".
|
the old meaning of "-S", use "-S d".
|
||||||
|
|
||||||
-n now means "collapse entries in the register report". The get the
|
-n now means "collapse entries in the register report". The get the
|
||||||
old meaning of -n in the balance report, use "-T a".
|
old meaning of -n in the balance report, use "-T a".
|
||||||
|
|
||||||
-p now specifies the reporting period. You can convert commodities
|
-p now specifies the reporting period. You can convert commodities
|
||||||
in a report using value expressions. For example, to display hours
|
in a report using value expressions. For example, to display hours
|
||||||
at $10 per hour:
|
at $10 per hour:
|
||||||
|
|
@ -329,7 +397,7 @@ command-line driver.
|
||||||
|
|
||||||
Note: This command is identical (and internally converted) to:
|
Note: This command is identical (and internally converted) to:
|
||||||
ledger -l "/expenses/|//john/" register
|
ledger -l "/expenses/|//john/" register
|
||||||
|
|
||||||
- To include entries from another file into a specific account, use:
|
- To include entries from another file into a specific account, use:
|
||||||
!account ACCOUNT
|
!account ACCOUNT
|
||||||
!include FILE
|
!include FILE
|
||||||
|
|
|
||||||
188
amount.cc
188
amount.cc
|
|
@ -66,14 +66,14 @@ static struct _init_amounts {
|
||||||
commodity_t * commodity;
|
commodity_t * commodity;
|
||||||
|
|
||||||
commodity = commodity_t::find_commodity("s", true);
|
commodity = commodity_t::find_commodity("s", true);
|
||||||
commodity->flags |= COMMODITY_STYLE_NOMARKET | COMMODITY_STYLE_BUILTIN;
|
commodity->flags() |= COMMODITY_STYLE_NOMARKET | COMMODITY_STYLE_BUILTIN;
|
||||||
|
|
||||||
parse_conversion("1.0m", "60s");
|
parse_conversion("1.0m", "60s");
|
||||||
parse_conversion("1.0h", "60m");
|
parse_conversion("1.0h", "60m");
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
commodity = commodity_t::find_commodity("b", true);
|
commodity = commodity_t::find_commodity("b", true);
|
||||||
commodity->flags |= COMMODITY_STYLE_NOMARKET | COMMODITY_STYLE_BUILTIN;
|
commodity->flags() |= COMMODITY_STYLE_NOMARKET | COMMODITY_STYLE_BUILTIN;
|
||||||
|
|
||||||
parse_conversion("1.00 Kb", "1024 b");
|
parse_conversion("1.00 Kb", "1024 b");
|
||||||
parse_conversion("1.00 Mb", "1024 Kb");
|
parse_conversion("1.00 Mb", "1024 Kb");
|
||||||
|
|
@ -417,7 +417,7 @@ amount_t& amount_t::operator*=(const amount_t& amt)
|
||||||
mpz_mul(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
|
mpz_mul(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
|
||||||
quantity->prec += amt.quantity->prec;
|
quantity->prec += amt.quantity->prec;
|
||||||
|
|
||||||
unsigned int comm_prec = commodity().precision;
|
unsigned int comm_prec = commodity().precision();
|
||||||
if (quantity->prec > comm_prec + 6U) {
|
if (quantity->prec > comm_prec + 6U) {
|
||||||
mpz_round(MPZ(quantity), MPZ(quantity), quantity->prec, comm_prec + 6U);
|
mpz_round(MPZ(quantity), MPZ(quantity), quantity->prec, comm_prec + 6U);
|
||||||
quantity->prec = comm_prec + 6U;
|
quantity->prec = comm_prec + 6U;
|
||||||
|
|
@ -442,7 +442,7 @@ amount_t& amount_t::operator/=(const amount_t& amt)
|
||||||
mpz_tdiv_q(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
|
mpz_tdiv_q(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
|
||||||
quantity->prec += 6;
|
quantity->prec += 6;
|
||||||
|
|
||||||
unsigned int comm_prec = commodity().precision;
|
unsigned int comm_prec = commodity().precision();
|
||||||
if (quantity->prec > comm_prec + 6U) {
|
if (quantity->prec > comm_prec + 6U) {
|
||||||
mpz_round(MPZ(quantity), MPZ(quantity), quantity->prec, comm_prec + 6U);
|
mpz_round(MPZ(quantity), MPZ(quantity), quantity->prec, comm_prec + 6U);
|
||||||
quantity->prec = comm_prec + 6U;
|
quantity->prec = comm_prec + 6U;
|
||||||
|
|
@ -504,12 +504,12 @@ amount_t::operator bool() const
|
||||||
if (! quantity)
|
if (! quantity)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (quantity->prec <= commodity().precision) {
|
if (quantity->prec <= commodity().precision()) {
|
||||||
return mpz_sgn(MPZ(quantity)) != 0;
|
return mpz_sgn(MPZ(quantity)) != 0;
|
||||||
} else {
|
} else {
|
||||||
mpz_set(temp, MPZ(quantity));
|
mpz_set(temp, MPZ(quantity));
|
||||||
if (commodity_)
|
if (commodity_)
|
||||||
mpz_ui_pow_ui(divisor, 10, quantity->prec - commodity().precision);
|
mpz_ui_pow_ui(divisor, 10, quantity->prec - commodity().precision());
|
||||||
else
|
else
|
||||||
mpz_ui_pow_ui(divisor, 10, quantity->prec);
|
mpz_ui_pow_ui(divisor, 10, quantity->prec);
|
||||||
mpz_tdiv_q(temp, temp, divisor);
|
mpz_tdiv_q(temp, temp, divisor);
|
||||||
|
|
@ -559,9 +559,9 @@ amount_t amount_t::value(const std::time_t moment) const
|
||||||
{
|
{
|
||||||
if (quantity) {
|
if (quantity) {
|
||||||
commodity_t& comm = commodity();
|
commodity_t& comm = commodity();
|
||||||
if (! (comm.flags & COMMODITY_STYLE_NOMARKET))
|
if (! (comm.flags() & COMMODITY_STYLE_NOMARKET))
|
||||||
if (amount_t amt = comm.value(moment))
|
if (amount_t amt = comm.value(moment))
|
||||||
return (amt * *this).round(amt.commodity().precision);
|
return (amt * *this).round(amt.commodity().precision());
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
@ -604,24 +604,23 @@ std::string amount_t::quantity_string() const
|
||||||
unsigned short precision;
|
unsigned short precision;
|
||||||
|
|
||||||
if (comm == *commodity_t::null_commodity ||
|
if (comm == *commodity_t::null_commodity ||
|
||||||
comm.flags & COMMODITY_STYLE_VARIABLE) {
|
comm.flags() & COMMODITY_STYLE_VARIABLE) {
|
||||||
mpz_ui_pow_ui(divisor, 10, quantity->prec);
|
mpz_ui_pow_ui(divisor, 10, quantity->prec);
|
||||||
mpz_tdiv_qr(quotient, remainder, MPZ(quantity), divisor);
|
mpz_tdiv_qr(quotient, remainder, MPZ(quantity), divisor);
|
||||||
precision = quantity->prec;
|
precision = quantity->prec;
|
||||||
}
|
}
|
||||||
else if (comm.precision < quantity->prec) {
|
else if (comm.precision() < quantity->prec) {
|
||||||
mpz_round(rquotient, MPZ(quantity), quantity->prec,
|
mpz_round(rquotient, MPZ(quantity), quantity->prec, comm.precision());
|
||||||
comm.precision);
|
mpz_ui_pow_ui(divisor, 10, comm.precision());
|
||||||
mpz_ui_pow_ui(divisor, 10, comm.precision);
|
|
||||||
mpz_tdiv_qr(quotient, remainder, rquotient, divisor);
|
mpz_tdiv_qr(quotient, remainder, rquotient, divisor);
|
||||||
precision = comm.precision;
|
precision = comm.precision();
|
||||||
}
|
}
|
||||||
else if (comm.precision > quantity->prec) {
|
else if (comm.precision() > quantity->prec) {
|
||||||
mpz_ui_pow_ui(divisor, 10, comm.precision - quantity->prec);
|
mpz_ui_pow_ui(divisor, 10, comm.precision() - quantity->prec);
|
||||||
mpz_mul(rquotient, MPZ(quantity), divisor);
|
mpz_mul(rquotient, MPZ(quantity), divisor);
|
||||||
mpz_ui_pow_ui(divisor, 10, comm.precision);
|
mpz_ui_pow_ui(divisor, 10, comm.precision());
|
||||||
mpz_tdiv_qr(quotient, remainder, rquotient, divisor);
|
mpz_tdiv_qr(quotient, remainder, rquotient, divisor);
|
||||||
precision = comm.precision;
|
precision = comm.precision();
|
||||||
}
|
}
|
||||||
else if (quantity->prec) {
|
else if (quantity->prec) {
|
||||||
mpz_ui_pow_ui(divisor, 10, quantity->prec);
|
mpz_ui_pow_ui(divisor, 10, quantity->prec);
|
||||||
|
|
@ -682,11 +681,11 @@ std::ostream& operator<<(std::ostream& _out, const amount_t& amt)
|
||||||
}
|
}
|
||||||
|
|
||||||
amount_t base(amt);
|
amount_t base(amt);
|
||||||
if (amt.commodity().larger) {
|
if (amt.commodity().larger()) {
|
||||||
amount_t last(amt);
|
amount_t last(amt);
|
||||||
while (last.commodity().larger) {
|
while (last.commodity().larger()) {
|
||||||
last /= *last.commodity().larger;
|
last /= *last.commodity().larger();
|
||||||
last.commodity_ = last.commodity().larger->commodity_;
|
last.commodity_ = last.commodity().larger()->commodity_;
|
||||||
if (ledger::abs(last) < 1)
|
if (ledger::abs(last) < 1)
|
||||||
break;
|
break;
|
||||||
base = last;
|
base = last;
|
||||||
|
|
@ -712,24 +711,24 @@ std::ostream& operator<<(std::ostream& _out, const amount_t& amt)
|
||||||
unsigned short precision;
|
unsigned short precision;
|
||||||
|
|
||||||
if (comm == *commodity_t::null_commodity ||
|
if (comm == *commodity_t::null_commodity ||
|
||||||
comm.flags & COMMODITY_STYLE_VARIABLE) {
|
comm.flags() & COMMODITY_STYLE_VARIABLE) {
|
||||||
mpz_ui_pow_ui(divisor, 10, base.quantity->prec);
|
mpz_ui_pow_ui(divisor, 10, base.quantity->prec);
|
||||||
mpz_tdiv_qr(quotient, remainder, MPZ(base.quantity), divisor);
|
mpz_tdiv_qr(quotient, remainder, MPZ(base.quantity), divisor);
|
||||||
precision = base.quantity->prec;
|
precision = base.quantity->prec;
|
||||||
}
|
}
|
||||||
else if (comm.precision < base.quantity->prec) {
|
else if (comm.precision() < base.quantity->prec) {
|
||||||
mpz_round(rquotient, MPZ(base.quantity), base.quantity->prec,
|
mpz_round(rquotient, MPZ(base.quantity), base.quantity->prec,
|
||||||
comm.precision);
|
comm.precision());
|
||||||
mpz_ui_pow_ui(divisor, 10, comm.precision);
|
mpz_ui_pow_ui(divisor, 10, comm.precision());
|
||||||
mpz_tdiv_qr(quotient, remainder, rquotient, divisor);
|
mpz_tdiv_qr(quotient, remainder, rquotient, divisor);
|
||||||
precision = comm.precision;
|
precision = comm.precision();
|
||||||
}
|
}
|
||||||
else if (comm.precision > base.quantity->prec) {
|
else if (comm.precision() > base.quantity->prec) {
|
||||||
mpz_ui_pow_ui(divisor, 10, comm.precision - base.quantity->prec);
|
mpz_ui_pow_ui(divisor, 10, comm.precision() - base.quantity->prec);
|
||||||
mpz_mul(rquotient, MPZ(base.quantity), divisor);
|
mpz_mul(rquotient, MPZ(base.quantity), divisor);
|
||||||
mpz_ui_pow_ui(divisor, 10, comm.precision);
|
mpz_ui_pow_ui(divisor, 10, comm.precision());
|
||||||
mpz_tdiv_qr(quotient, remainder, rquotient, divisor);
|
mpz_tdiv_qr(quotient, remainder, rquotient, divisor);
|
||||||
precision = comm.precision;
|
precision = comm.precision();
|
||||||
}
|
}
|
||||||
else if (base.quantity->prec) {
|
else if (base.quantity->prec) {
|
||||||
mpz_ui_pow_ui(divisor, 10, base.quantity->prec);
|
mpz_ui_pow_ui(divisor, 10, base.quantity->prec);
|
||||||
|
|
@ -755,12 +754,18 @@ std::ostream& operator<<(std::ostream& _out, const amount_t& amt)
|
||||||
return _out;
|
return _out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! (comm.flags & COMMODITY_STYLE_SUFFIXED)) {
|
if (! (comm.flags() & COMMODITY_STYLE_SUFFIXED)) {
|
||||||
if (comm.quote)
|
if (comm.quote) {
|
||||||
out << "\"" << comm.symbol << "\"";
|
std::string::size_type idx = comm.symbol.find(" {", 0);
|
||||||
else
|
if (idx != std::string::npos)
|
||||||
|
out << "\"" << comm.symbol.substr(0, idx) << "\""
|
||||||
|
<< comm.symbol.substr(idx);
|
||||||
|
else
|
||||||
|
out << "\"" << comm.symbol << "\"";
|
||||||
|
} else {
|
||||||
out << comm.symbol;
|
out << comm.symbol;
|
||||||
if (comm.flags & COMMODITY_STYLE_SEPARATED)
|
}
|
||||||
|
if (comm.flags() & COMMODITY_STYLE_SEPARATED)
|
||||||
out << " ";
|
out << " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -770,7 +775,7 @@ std::ostream& operator<<(std::ostream& _out, const amount_t& amt)
|
||||||
if (mpz_sgn(quotient) == 0) {
|
if (mpz_sgn(quotient) == 0) {
|
||||||
out << '0';
|
out << '0';
|
||||||
}
|
}
|
||||||
else if (! (comm.flags & COMMODITY_STYLE_THOUSANDS)) {
|
else if (! (comm.flags() & COMMODITY_STYLE_THOUSANDS)) {
|
||||||
char * p = mpz_get_str(NULL, 10, quotient);
|
char * p = mpz_get_str(NULL, 10, quotient);
|
||||||
out << p;
|
out << p;
|
||||||
std::free(p);
|
std::free(p);
|
||||||
|
|
@ -799,7 +804,7 @@ std::ostream& operator<<(std::ostream& _out, const amount_t& amt)
|
||||||
i != strs.rend();
|
i != strs.rend();
|
||||||
i++) {
|
i++) {
|
||||||
if (printed) {
|
if (printed) {
|
||||||
out << (comm.flags & COMMODITY_STYLE_EUROPEAN ? '.' : ',');
|
out << (comm.flags() & COMMODITY_STYLE_EUROPEAN ? '.' : ',');
|
||||||
out.width(3);
|
out.width(3);
|
||||||
out.fill('0');
|
out.fill('0');
|
||||||
}
|
}
|
||||||
|
|
@ -810,7 +815,7 @@ std::ostream& operator<<(std::ostream& _out, const amount_t& amt)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (precision) {
|
if (precision) {
|
||||||
out << ((comm.flags & COMMODITY_STYLE_EUROPEAN) ? ',' : '.');
|
out << ((comm.flags() & COMMODITY_STYLE_EUROPEAN) ? ',' : '.');
|
||||||
|
|
||||||
out.width(precision);
|
out.width(precision);
|
||||||
out.fill('0');
|
out.fill('0');
|
||||||
|
|
@ -820,13 +825,19 @@ std::ostream& operator<<(std::ostream& _out, const amount_t& amt)
|
||||||
std::free(p);
|
std::free(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (comm.flags & COMMODITY_STYLE_SUFFIXED) {
|
if (comm.flags() & COMMODITY_STYLE_SUFFIXED) {
|
||||||
if (comm.flags & COMMODITY_STYLE_SEPARATED)
|
if (comm.flags() & COMMODITY_STYLE_SEPARATED)
|
||||||
out << " ";
|
out << " ";
|
||||||
if (comm.quote)
|
if (comm.quote) {
|
||||||
out << "\"" << comm.symbol << "\"";
|
std::string::size_type idx = comm.symbol.find(" {", 0);
|
||||||
else
|
if (idx != std::string::npos)
|
||||||
|
out << "\"" << comm.symbol.substr(0, idx) << "\""
|
||||||
|
<< comm.symbol.substr(idx);
|
||||||
|
else
|
||||||
|
out << "\"" << comm.symbol << "\"";
|
||||||
|
} else {
|
||||||
out << comm.symbol;
|
out << comm.symbol;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mpz_clear(quotient);
|
mpz_clear(quotient);
|
||||||
|
|
@ -878,6 +889,7 @@ void amount_t::parse(std::istream& in, unsigned short flags)
|
||||||
|
|
||||||
std::string symbol;
|
std::string symbol;
|
||||||
std::string quant;
|
std::string quant;
|
||||||
|
std::string price;
|
||||||
unsigned int comm_flags = COMMODITY_STYLE_DEFAULTS;
|
unsigned int comm_flags = COMMODITY_STYLE_DEFAULTS;
|
||||||
bool negative = false;
|
bool negative = false;
|
||||||
|
|
||||||
|
|
@ -923,6 +935,34 @@ void amount_t::parse(std::istream& in, unsigned short flags)
|
||||||
|
|
||||||
commodity_ = commodity_t::find_commodity(symbol, true);
|
commodity_ = commodity_t::find_commodity(symbol, true);
|
||||||
|
|
||||||
|
// If a per-unit price is specified for this amount, record it by
|
||||||
|
// creating a specialized commodity at that price. This is a
|
||||||
|
// different from the whole transaction cost, which is associated
|
||||||
|
// with the transaction and not with the amount. For example, a
|
||||||
|
// sale of 10 AAPL shares for $100 (the cost) is a different thing
|
||||||
|
// from selling 10 AAPL {$10} (where $10 is the commodity price) for
|
||||||
|
// $50, which implies a capital loss of $50.
|
||||||
|
|
||||||
|
if (peek_next_nonws(in) == '{') {
|
||||||
|
char c;
|
||||||
|
char buf[256];
|
||||||
|
in.get(c);
|
||||||
|
READ_INTO(in, buf, 255, c, c != '}');
|
||||||
|
if (c == '}')
|
||||||
|
in.get(c);
|
||||||
|
else
|
||||||
|
throw amount_error("Commodity price lacks closing brace");
|
||||||
|
|
||||||
|
symbol = symbol + " {" + buf + "}";
|
||||||
|
commodity_t * priced_commodity =
|
||||||
|
commodity_t::find_commodity(symbol, true);
|
||||||
|
|
||||||
|
priced_commodity->price = new amount_t(buf);
|
||||||
|
priced_commodity->base = commodity_;
|
||||||
|
|
||||||
|
commodity_ = priced_commodity;
|
||||||
|
}
|
||||||
|
|
||||||
// Determine the precision of the amount, based on the usage of
|
// Determine the precision of the amount, based on the usage of
|
||||||
// comma or period.
|
// comma or period.
|
||||||
|
|
||||||
|
|
@ -940,12 +980,12 @@ void amount_t::parse(std::istream& in, unsigned short flags)
|
||||||
}
|
}
|
||||||
else if (last_comma != std::string::npos &&
|
else if (last_comma != std::string::npos &&
|
||||||
(! commodity_t::default_commodity ||
|
(! commodity_t::default_commodity ||
|
||||||
commodity_t::default_commodity->flags & COMMODITY_STYLE_EUROPEAN)) {
|
commodity_t::default_commodity->flags() & COMMODITY_STYLE_EUROPEAN)) {
|
||||||
comm_flags |= COMMODITY_STYLE_EUROPEAN;
|
comm_flags |= COMMODITY_STYLE_EUROPEAN;
|
||||||
quantity->prec = quant.length() - last_comma - 1;
|
quantity->prec = quant.length() - last_comma - 1;
|
||||||
}
|
}
|
||||||
else if (last_period != std::string::npos &&
|
else if (last_period != std::string::npos &&
|
||||||
! (commodity().flags & COMMODITY_STYLE_EUROPEAN)) {
|
! (commodity().flags() & COMMODITY_STYLE_EUROPEAN)) {
|
||||||
quantity->prec = quant.length() - last_period - 1;
|
quantity->prec = quant.length() - last_period - 1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -955,9 +995,9 @@ void amount_t::parse(std::istream& in, unsigned short flags)
|
||||||
// Set the commodity's flags and precision accordingly
|
// Set the commodity's flags and precision accordingly
|
||||||
|
|
||||||
if (newly_created || ! (flags & AMOUNT_PARSE_NO_MIGRATE)) {
|
if (newly_created || ! (flags & AMOUNT_PARSE_NO_MIGRATE)) {
|
||||||
commodity().flags |= comm_flags;
|
commodity().flags() |= comm_flags;
|
||||||
if (quantity->prec > commodity().precision)
|
if (quantity->prec > commodity().precision())
|
||||||
commodity().precision = quantity->prec;
|
commodity().precision() = quantity->prec;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we have the final number. Remove commas and periods, if
|
// Now we have the final number. Remove commas and periods, if
|
||||||
|
|
@ -991,9 +1031,9 @@ void amount_t::parse(std::istream& in, unsigned short flags)
|
||||||
|
|
||||||
void amount_t::reduce()
|
void amount_t::reduce()
|
||||||
{
|
{
|
||||||
while (commodity_ && commodity().smaller) {
|
while (commodity_ && commodity().smaller()) {
|
||||||
*this *= *commodity().smaller;
|
*this *= *commodity().smaller();
|
||||||
commodity_ = commodity().smaller->commodity_;
|
commodity_ = commodity().smaller()->commodity_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1014,12 +1054,12 @@ void parse_conversion(const std::string& larger_str,
|
||||||
larger *= smaller;
|
larger *= smaller;
|
||||||
|
|
||||||
if (larger.commodity()) {
|
if (larger.commodity()) {
|
||||||
larger.commodity().smaller = new amount_t(smaller);
|
larger.commodity().smaller() = new amount_t(smaller);
|
||||||
larger.commodity().flags = (smaller.commodity().flags |
|
larger.commodity().flags() = (smaller.commodity().flags() |
|
||||||
COMMODITY_STYLE_NOMARKET);
|
COMMODITY_STYLE_NOMARKET);
|
||||||
}
|
}
|
||||||
if (smaller.commodity())
|
if (smaller.commodity())
|
||||||
smaller.commodity().larger = new amount_t(larger);
|
smaller.commodity().larger() = new amount_t(larger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1161,6 +1201,8 @@ void commodity_t::set_symbol(const std::string& sym)
|
||||||
quote = false;
|
quote = false;
|
||||||
for (const char * p = symbol.c_str(); *p; p++)
|
for (const char * p = symbol.c_str(); *p; p++)
|
||||||
if (std::isspace(*p) || std::isdigit(*p) || *p == '-' || *p == '.') {
|
if (std::isspace(*p) || std::isdigit(*p) || *p == '-' || *p == '.') {
|
||||||
|
if (std::isspace(*p) && *(p + 1) == '{')
|
||||||
|
return;
|
||||||
quote = true;
|
quote = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -1168,15 +1210,15 @@ void commodity_t::set_symbol(const std::string& sym)
|
||||||
|
|
||||||
void commodity_t::add_price(const std::time_t date, const amount_t& price)
|
void commodity_t::add_price(const std::time_t date, const amount_t& price)
|
||||||
{
|
{
|
||||||
if (! history)
|
if (! history())
|
||||||
history = new history_t;
|
history() = new history_t;
|
||||||
|
|
||||||
history_map::iterator i = history->prices.find(date);
|
history_map::iterator i = history()->prices.find(date);
|
||||||
if (i != history->prices.end()) {
|
if (i != history()->prices.end()) {
|
||||||
(*i).second = price;
|
(*i).second = price;
|
||||||
} else {
|
} else {
|
||||||
std::pair<history_map::iterator, bool> result
|
std::pair<history_map::iterator, bool> result
|
||||||
= history->prices.insert(history_pair(date, price));
|
= history()->prices.insert(history_pair(date, price));
|
||||||
assert(result.second);
|
assert(result.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1195,9 +1237,9 @@ commodity_t * commodity_t::find_commodity(const std::string& symbol,
|
||||||
// Start out the new commodity with the default commodity's flags
|
// Start out the new commodity with the default commodity's flags
|
||||||
// and precision, if one has been defined.
|
// and precision, if one has been defined.
|
||||||
if (default_commodity)
|
if (default_commodity)
|
||||||
commodity->flags =
|
commodity->flags() =
|
||||||
(default_commodity->flags & ~(COMMODITY_STYLE_THOUSANDS |
|
(default_commodity->flags() & ~(COMMODITY_STYLE_THOUSANDS |
|
||||||
COMMODITY_STYLE_NOMARKET));
|
COMMODITY_STYLE_NOMARKET));
|
||||||
|
|
||||||
return commodity;
|
return commodity;
|
||||||
}
|
}
|
||||||
|
|
@ -1210,23 +1252,23 @@ amount_t commodity_t::value(const std::time_t moment)
|
||||||
std::time_t age = 0;
|
std::time_t age = 0;
|
||||||
amount_t price;
|
amount_t price;
|
||||||
|
|
||||||
if (history) {
|
if (history()) {
|
||||||
assert(history->prices.size() > 0);
|
assert(history()->prices.size() > 0);
|
||||||
|
|
||||||
if (moment == 0) {
|
if (moment == 0) {
|
||||||
history_map::reverse_iterator r = history->prices.rbegin();
|
history_map::reverse_iterator r = history()->prices.rbegin();
|
||||||
age = (*r).first;
|
age = (*r).first;
|
||||||
price = (*r).second;
|
price = (*r).second;
|
||||||
} else {
|
} else {
|
||||||
history_map::iterator i = history->prices.lower_bound(moment);
|
history_map::iterator i = history()->prices.lower_bound(moment);
|
||||||
if (i == history->prices.end()) {
|
if (i == history()->prices.end()) {
|
||||||
history_map::reverse_iterator r = history->prices.rbegin();
|
history_map::reverse_iterator r = history()->prices.rbegin();
|
||||||
age = (*r).first;
|
age = (*r).first;
|
||||||
price = (*r).second;
|
price = (*r).second;
|
||||||
} else {
|
} else {
|
||||||
age = (*i).first;
|
age = (*i).first;
|
||||||
if (std::difftime(moment, age) != 0) {
|
if (std::difftime(moment, age) != 0) {
|
||||||
if (i != history->prices.begin()) {
|
if (i != history()->prices.begin()) {
|
||||||
--i;
|
--i;
|
||||||
age = (*i).first;
|
age = (*i).first;
|
||||||
price = (*i).second;
|
price = (*i).second;
|
||||||
|
|
@ -1242,8 +1284,8 @@ amount_t commodity_t::value(const std::time_t moment)
|
||||||
|
|
||||||
if (updater)
|
if (updater)
|
||||||
(*updater)(*this, moment, age,
|
(*updater)(*this, moment, age,
|
||||||
(history && history->prices.size() > 0 ?
|
(history() && history()->prices.size() > 0 ?
|
||||||
(*history->prices.rbegin()).first : 0), price);
|
(*history()->prices.rbegin()).first : 0), price);
|
||||||
|
|
||||||
return price;
|
return price;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
71
amount.h
71
amount.h
|
|
@ -73,6 +73,7 @@ class amount_t
|
||||||
void clear_commodity() {
|
void clear_commodity() {
|
||||||
commodity_ = NULL;
|
commodity_ = NULL;
|
||||||
}
|
}
|
||||||
|
amount_t base_amount() const;
|
||||||
|
|
||||||
bool null() const {
|
bool null() const {
|
||||||
return ! quantity && ! commodity_;
|
return ! quantity && ! commodity_;
|
||||||
|
|
@ -316,21 +317,47 @@ class commodity_t
|
||||||
std::time_t last_lookup;
|
std::time_t last_lookup;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
history_t * history_;
|
||||||
|
|
||||||
|
history_t *& history() {
|
||||||
|
return base ? base->history() : history_;
|
||||||
|
}
|
||||||
|
|
||||||
const std::string symbol;
|
const std::string symbol;
|
||||||
bool quote;
|
bool quote;
|
||||||
std::string name;
|
std::string name_;
|
||||||
std::string note;
|
std::string note_;
|
||||||
unsigned short precision;
|
unsigned short precision_;
|
||||||
unsigned short flags;
|
unsigned short flags_;
|
||||||
ident_t ident;
|
ident_t ident;
|
||||||
history_t * history;
|
amount_t * smaller_;
|
||||||
amount_t * smaller;
|
amount_t * larger_;
|
||||||
amount_t * larger;
|
commodity_t * base; // base commodity for AAPL {$10} is AAPL
|
||||||
|
amount_t * price; // its price is therefore $10.00
|
||||||
|
|
||||||
|
std::string& name() {
|
||||||
|
return base ? base->name() : name_;
|
||||||
|
}
|
||||||
|
std::string& note() {
|
||||||
|
return base ? base->note() : note_;
|
||||||
|
}
|
||||||
|
unsigned short& precision() {
|
||||||
|
return base ? base->precision() : precision_;
|
||||||
|
}
|
||||||
|
unsigned short& flags() {
|
||||||
|
return base ? base->flags() : flags_;
|
||||||
|
}
|
||||||
|
amount_t *& smaller() {
|
||||||
|
return base ? base->smaller() : smaller_;
|
||||||
|
}
|
||||||
|
amount_t *& larger() {
|
||||||
|
return base ? base->larger() : larger_;
|
||||||
|
}
|
||||||
|
|
||||||
// If set, this global function pointer is called to determine
|
// If set, this global function pointer is called to determine
|
||||||
// whether prices have been updated in the meanwhile.
|
// whether prices have been updated in the meanwhile.
|
||||||
|
|
||||||
static updater_t * updater;
|
static updater_t * updater;
|
||||||
|
|
||||||
// This map remembers all commodities that have been defined.
|
// This map remembers all commodities that have been defined.
|
||||||
|
|
||||||
|
|
@ -361,14 +388,15 @@ class commodity_t
|
||||||
commodity_t(const std::string& _symbol = "",
|
commodity_t(const std::string& _symbol = "",
|
||||||
unsigned int _precision = 0,
|
unsigned int _precision = 0,
|
||||||
unsigned int _flags = COMMODITY_STYLE_DEFAULTS)
|
unsigned int _flags = COMMODITY_STYLE_DEFAULTS)
|
||||||
: precision(_precision), flags(_flags), history(NULL),
|
: precision_(_precision), flags_(_flags), history_(NULL),
|
||||||
smaller(NULL), larger(NULL) {
|
smaller_(NULL), larger_(NULL), base(NULL), price(NULL) {
|
||||||
set_symbol(_symbol);
|
set_symbol(_symbol);
|
||||||
}
|
}
|
||||||
~commodity_t() {
|
~commodity_t() {
|
||||||
if (history) delete history;
|
if (history_) delete history_;
|
||||||
if (smaller) delete smaller;
|
if (smaller_) delete smaller_;
|
||||||
if (larger) delete larger;
|
if (larger_) delete larger_;
|
||||||
|
if (price) delete price;
|
||||||
}
|
}
|
||||||
|
|
||||||
operator bool() const {
|
operator bool() const {
|
||||||
|
|
@ -385,8 +413,8 @@ class commodity_t
|
||||||
|
|
||||||
void add_price(const std::time_t date, const amount_t& price);
|
void add_price(const std::time_t date, const amount_t& price);
|
||||||
bool remove_price(const std::time_t date) {
|
bool remove_price(const std::time_t date) {
|
||||||
if (history) {
|
if (history_) {
|
||||||
history_map::size_type n = history->prices.erase(date);
|
history_map::size_type n = history_->prices.erase(date);
|
||||||
return n > 0;
|
return n > 0;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -398,7 +426,7 @@ class commodity_t
|
||||||
if (symbol.empty() && this != null_commodity)
|
if (symbol.empty() && this != null_commodity)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (precision > 16)
|
if (precision_ > 16)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -417,6 +445,17 @@ inline commodity_t& amount_t::commodity() const {
|
||||||
return *commodity_;
|
return *commodity_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline amount_t amount_t::base_amount() const {
|
||||||
|
if (commodity_ && commodity_->price) {
|
||||||
|
amount_t temp(*this);
|
||||||
|
assert(commodity_->base);
|
||||||
|
temp.set_commodity(*(commodity_->base));
|
||||||
|
return temp;
|
||||||
|
} else {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class amount_error : public std::exception {
|
class amount_error : public std::exception {
|
||||||
std::string reason;
|
std::string reason;
|
||||||
public:
|
public:
|
||||||
|
|
|
||||||
16
balance.cc
16
balance.cc
|
|
@ -34,6 +34,22 @@ balance_t balance_t::value(const std::time_t moment) const
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
balance_t balance_t::factor_price() const
|
||||||
|
{
|
||||||
|
balance_t temp;
|
||||||
|
|
||||||
|
for (amounts_map::const_iterator i = amounts.begin();
|
||||||
|
i != amounts.end();
|
||||||
|
i++) {
|
||||||
|
if ((*i).second.commodity().price)
|
||||||
|
temp += *((*i).second.commodity().price) * (*i).second;
|
||||||
|
else
|
||||||
|
temp += (*i).second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
struct compare_amount_commodities {
|
struct compare_amount_commodities {
|
||||||
bool operator()(const amount_t * left, const amount_t * right) const {
|
bool operator()(const amount_t * left, const amount_t * right) const {
|
||||||
return left->commodity().symbol < right->commodity().symbol;
|
return left->commodity().symbol < right->commodity().symbol;
|
||||||
|
|
|
||||||
|
|
@ -426,6 +426,7 @@ class balance_t
|
||||||
|
|
||||||
amount_t amount(const commodity_t& commodity) const;
|
amount_t amount(const commodity_t& commodity) const;
|
||||||
balance_t value(const std::time_t moment) const;
|
balance_t value(const std::time_t moment) const;
|
||||||
|
balance_t factor_price() const;
|
||||||
|
|
||||||
void write(std::ostream& out,
|
void write(std::ostream& out,
|
||||||
const int first_width,
|
const int first_width,
|
||||||
|
|
@ -443,7 +444,7 @@ class balance_t
|
||||||
i != amounts.end();
|
i != amounts.end();
|
||||||
i++)
|
i++)
|
||||||
if ((*i).second.commodity())
|
if ((*i).second.commodity())
|
||||||
(*i).second = (*i).second.round((*i).second.commodity().precision);
|
(*i).second = (*i).second.round((*i).second.commodity().precision());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
178
binary.cc
178
binary.cc
|
|
@ -11,18 +11,10 @@
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
static unsigned long binary_magic_number = 0xFFEED765;
|
static unsigned long binary_magic_number = 0xFFEED765;
|
||||||
#ifdef USE_EDITOR
|
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
static unsigned long format_version = 0x00020589;
|
static unsigned long format_version = 0x0002050b;
|
||||||
#else
|
#else
|
||||||
static unsigned long format_version = 0x00020588;
|
static unsigned long format_version = 0x0002050a;
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#ifdef DEBUG_ENABLED
|
|
||||||
static unsigned long format_version = 0x00020509;
|
|
||||||
#else
|
|
||||||
static unsigned long format_version = 0x00020508;
|
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static account_t ** accounts;
|
static account_t ** accounts;
|
||||||
|
|
@ -272,8 +264,10 @@ inline void read_binary_value_expr(char *& data, value_expr_t *& expr)
|
||||||
|
|
||||||
expr = new value_expr_t(kind);
|
expr = new value_expr_t(kind);
|
||||||
|
|
||||||
read_binary_value_expr(data, expr->left);
|
if (kind > value_expr_t::TERMINALS) {
|
||||||
read_binary_value_expr(data, expr->right);
|
read_binary_value_expr(data, expr->left);
|
||||||
|
if (expr->left) expr->left->acquire();
|
||||||
|
}
|
||||||
|
|
||||||
switch (expr->kind) {
|
switch (expr->kind) {
|
||||||
case value_expr_t::CONSTANT_T:
|
case value_expr_t::CONSTANT_T:
|
||||||
|
|
@ -283,15 +277,30 @@ inline void read_binary_value_expr(char *& data, value_expr_t *& expr)
|
||||||
read_binary_long(data, expr->constant_i);
|
read_binary_long(data, expr->constant_i);
|
||||||
break;
|
break;
|
||||||
case value_expr_t::CONSTANT_A:
|
case value_expr_t::CONSTANT_A:
|
||||||
read_binary_amount(data, expr->constant_a);
|
expr->constant_a = new amount_t();
|
||||||
|
read_binary_amount(data, *(expr->constant_a));
|
||||||
break;
|
break;
|
||||||
case value_expr_t::F_FUNC:
|
case value_expr_t::CONSTANT_V:
|
||||||
read_binary_string(data, expr->constant_s);
|
assert(0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
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;
|
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);
|
read_binary_long(data, xact->_date_eff);
|
||||||
xact->account = accounts[read_binary_long<account_t::ident_t>(data) - 1];
|
xact->account = accounts[read_binary_long<account_t::ident_t>(data) - 1];
|
||||||
|
|
||||||
if (read_binary_number<char>(data) == 1)
|
if (read_binary_number<char>(data) == 1) {
|
||||||
read_binary_value_expr(data, xact->amount_expr);
|
read_binary_value_expr(data, xact->amount_expr);
|
||||||
else
|
if (xact->amount_expr) xact->amount_expr->acquire();
|
||||||
|
} else {
|
||||||
read_binary_amount(data, xact->amount);
|
read_binary_amount(data, xact->amount);
|
||||||
|
}
|
||||||
|
|
||||||
if (*data++ == 1) {
|
if (*data++ == 1) {
|
||||||
xact->cost = new amount_t;
|
xact->cost = new amount_t;
|
||||||
|
|
||||||
if (read_binary_number<char>(data) == 1)
|
if (read_binary_number<char>(data) == 1) {
|
||||||
read_binary_value_expr(data, xact->cost_expr);
|
read_binary_value_expr(data, xact->cost_expr);
|
||||||
else
|
if (xact->cost_expr) xact->cost_expr->acquire();
|
||||||
|
} else {
|
||||||
read_binary_amount(data, *xact->cost);
|
read_binary_amount(data, *xact->cost);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
xact->cost = NULL;
|
xact->cost = NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -322,12 +335,10 @@ inline void read_binary_transaction(char *& data, transaction_t * xact)
|
||||||
xact->flags |= TRANSACTION_BULK_ALLOC;
|
xact->flags |= TRANSACTION_BULK_ALLOC;
|
||||||
read_binary_string(data, &xact->note);
|
read_binary_string(data, &xact->note);
|
||||||
|
|
||||||
#ifdef USE_EDITOR
|
|
||||||
xact->beg_pos = read_binary_long<unsigned long>(data);
|
xact->beg_pos = read_binary_long<unsigned long>(data);
|
||||||
read_binary_long(data, xact->beg_line);
|
read_binary_long(data, xact->beg_line);
|
||||||
xact->end_pos = read_binary_long<unsigned long>(data);
|
xact->end_pos = read_binary_long<unsigned long>(data);
|
||||||
read_binary_long(data, xact->end_line);
|
read_binary_long(data, xact->end_line);
|
||||||
#endif
|
|
||||||
|
|
||||||
xact->data = NULL;
|
xact->data = NULL;
|
||||||
|
|
||||||
|
|
@ -340,13 +351,11 @@ inline void read_binary_transaction(char *& data, transaction_t * xact)
|
||||||
inline void read_binary_entry_base(char *& data, entry_base_t * entry,
|
inline void read_binary_entry_base(char *& data, entry_base_t * entry,
|
||||||
transaction_t *& xact_pool, bool& finalize)
|
transaction_t *& xact_pool, bool& finalize)
|
||||||
{
|
{
|
||||||
#ifdef USE_EDITOR
|
|
||||||
read_binary_long(data, entry->src_idx);
|
read_binary_long(data, entry->src_idx);
|
||||||
entry->beg_pos = read_binary_long<unsigned long>(data);
|
entry->beg_pos = read_binary_long<unsigned long>(data);
|
||||||
read_binary_long(data, entry->beg_line);
|
read_binary_long(data, entry->beg_line);
|
||||||
entry->end_pos = read_binary_long<unsigned long>(data);
|
entry->end_pos = read_binary_long<unsigned long>(data);
|
||||||
read_binary_long(data, entry->end_line);
|
read_binary_long(data, entry->end_line);
|
||||||
#endif
|
|
||||||
|
|
||||||
bool ignore_calculated = read_binary_number<char>(data) == 1;
|
bool ignore_calculated = read_binary_number<char>(data) == 1;
|
||||||
|
|
||||||
|
|
@ -376,8 +385,10 @@ inline void read_binary_auto_entry(char *& data, auto_entry_t * entry,
|
||||||
{
|
{
|
||||||
bool ignore;
|
bool ignore;
|
||||||
read_binary_entry_base(data, entry, xact_pool, ignore);
|
read_binary_entry_base(data, entry, xact_pool, ignore);
|
||||||
read_binary_string(data, &entry->predicate_string);
|
value_expr_t * expr;
|
||||||
entry->predicate = new item_predicate<transaction_t>(entry->predicate_string);
|
read_binary_value_expr(data, expr);
|
||||||
|
// the item_predicate constructor will acquire the reference
|
||||||
|
entry->predicate = new item_predicate<transaction_t>(expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void read_binary_period_entry(char *& data, period_entry_t * entry,
|
inline void read_binary_period_entry(char *& data, period_entry_t * entry,
|
||||||
|
|
@ -396,10 +407,10 @@ inline commodity_t * read_binary_commodity(char *& data)
|
||||||
|
|
||||||
read_binary_string(data, *(const_cast<std::string *>(&commodity->symbol)));
|
read_binary_string(data, *(const_cast<std::string *>(&commodity->symbol)));
|
||||||
read_binary_number(data, commodity->quote);
|
read_binary_number(data, commodity->quote);
|
||||||
read_binary_string(data, commodity->name);
|
read_binary_string(data, commodity->name_);
|
||||||
read_binary_string(data, commodity->note);
|
read_binary_string(data, commodity->note_);
|
||||||
read_binary_number(data, commodity->precision);
|
read_binary_number(data, commodity->precision_);
|
||||||
read_binary_number(data, commodity->flags);
|
read_binary_number(data, commodity->flags_);
|
||||||
read_binary_long(data, commodity->ident);
|
read_binary_long(data, commodity->ident);
|
||||||
|
|
||||||
return commodity;
|
return commodity;
|
||||||
|
|
@ -421,12 +432,12 @@ inline void read_binary_commodity_extra(char *& data,
|
||||||
// Upon insertion, amt will be copied, which will cause the amount
|
// Upon insertion, amt will be copied, which will cause the amount
|
||||||
// to be duplicated (and thus not lost when the journal's
|
// to be duplicated (and thus not lost when the journal's
|
||||||
// item_pool is deleted.
|
// item_pool is deleted.
|
||||||
if (! commodity->history)
|
if (! commodity->history_)
|
||||||
commodity->history = new commodity_t::history_t;
|
commodity->history_ = new commodity_t::history_t;
|
||||||
commodity->history->prices.insert(history_pair(when, amt));
|
commodity->history_->prices.insert(history_pair(when, amt));
|
||||||
}
|
}
|
||||||
if (commodity->history)
|
if (commodity->history_)
|
||||||
read_binary_long(data, commodity->history->last_lookup);
|
read_binary_long(data, commodity->history_->last_lookup);
|
||||||
|
|
||||||
unsigned char flag;
|
unsigned char flag;
|
||||||
|
|
||||||
|
|
@ -434,14 +445,23 @@ inline void read_binary_commodity_extra(char *& data,
|
||||||
if (flag) {
|
if (flag) {
|
||||||
amount_t amt;
|
amount_t amt;
|
||||||
read_binary_amount(data, amt);
|
read_binary_amount(data, amt);
|
||||||
commodity->smaller = new amount_t(amt);
|
commodity->smaller_ = new amount_t(amt);
|
||||||
}
|
}
|
||||||
|
|
||||||
flag = read_binary_number<unsigned char>(data);
|
flag = read_binary_number<unsigned char>(data);
|
||||||
if (flag) {
|
if (flag) {
|
||||||
amount_t amt;
|
amount_t amt;
|
||||||
read_binary_amount(data, amt);
|
read_binary_amount(data, amt);
|
||||||
commodity->larger = new amount_t(amt);
|
commodity->larger_ = new amount_t(amt);
|
||||||
|
}
|
||||||
|
|
||||||
|
flag = read_binary_number<unsigned char>(data);
|
||||||
|
if (flag) {
|
||||||
|
amount_t amt;
|
||||||
|
read_binary_amount(data, amt);
|
||||||
|
commodity->price = new amount_t(amt);
|
||||||
|
commodity->base =
|
||||||
|
commodities[read_binary_long<commodity_t::ident_t>(data) - 1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -570,7 +590,7 @@ unsigned int read_binary_journal(std::istream& in,
|
||||||
commodities = commodities_next = new commodity_t *[c_count];
|
commodities = commodities_next = new commodity_t *[c_count];
|
||||||
for (commodity_t::ident_t i = 0; i < c_count; i++) {
|
for (commodity_t::ident_t i = 0; i < c_count; i++) {
|
||||||
commodity_t * commodity = read_binary_commodity(data);
|
commodity_t * commodity = read_binary_commodity(data);
|
||||||
if (! (commodity->flags & COMMODITY_STYLE_BUILTIN)) {
|
if (! (commodity->flags_ & COMMODITY_STYLE_BUILTIN)) {
|
||||||
if (commodity->symbol == "") {
|
if (commodity->symbol == "") {
|
||||||
commodity_t::commodities.erase(commodity->symbol);
|
commodity_t::commodities.erase(commodity->symbol);
|
||||||
delete commodity_t::null_commodity;
|
delete commodity_t::null_commodity;
|
||||||
|
|
@ -737,7 +757,7 @@ void write_binary_mask(std::ostream& out, mask_t * mask)
|
||||||
write_binary_string(out, mask->pattern);
|
write_binary_string(out, mask->pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_binary_value_expr(std::ostream& out, value_expr_t * expr)
|
void write_binary_value_expr(std::ostream& out, const value_expr_t * expr)
|
||||||
{
|
{
|
||||||
if (expr == NULL) {
|
if (expr == NULL) {
|
||||||
write_binary_number<unsigned char>(out, 0);
|
write_binary_number<unsigned char>(out, 0);
|
||||||
|
|
@ -746,8 +766,9 @@ void write_binary_value_expr(std::ostream& out, value_expr_t * expr)
|
||||||
write_binary_number<unsigned char>(out, 1);
|
write_binary_number<unsigned char>(out, 1);
|
||||||
|
|
||||||
write_binary_number(out, expr->kind);
|
write_binary_number(out, expr->kind);
|
||||||
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) {
|
switch (expr->kind) {
|
||||||
case value_expr_t::CONSTANT_T:
|
case value_expr_t::CONSTANT_T:
|
||||||
|
|
@ -757,19 +778,32 @@ void write_binary_value_expr(std::ostream& out, value_expr_t * expr)
|
||||||
write_binary_long(out, expr->constant_i);
|
write_binary_long(out, expr->constant_i);
|
||||||
break;
|
break;
|
||||||
case value_expr_t::CONSTANT_A:
|
case value_expr_t::CONSTANT_A:
|
||||||
write_binary_amount(out, expr->constant_a);
|
write_binary_amount(out, *(expr->constant_a));
|
||||||
break;
|
break;
|
||||||
case value_expr_t::F_FUNC:
|
case value_expr_t::CONSTANT_V:
|
||||||
write_binary_string(out, expr->constant_s);
|
assert(0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
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;
|
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,
|
void write_binary_transaction(std::ostream& out, transaction_t * xact,
|
||||||
|
|
@ -808,23 +842,19 @@ void write_binary_transaction(std::ostream& out, transaction_t * xact,
|
||||||
write_binary_number(out, xact->flags);
|
write_binary_number(out, xact->flags);
|
||||||
write_binary_string(out, xact->note);
|
write_binary_string(out, xact->note);
|
||||||
|
|
||||||
#ifdef USE_EDITOR
|
|
||||||
write_binary_long(out, xact->beg_pos);
|
write_binary_long(out, xact->beg_pos);
|
||||||
write_binary_long(out, xact->beg_line);
|
write_binary_long(out, xact->beg_line);
|
||||||
write_binary_long(out, xact->end_pos);
|
write_binary_long(out, xact->end_pos);
|
||||||
write_binary_long(out, xact->end_line);
|
write_binary_long(out, xact->end_line);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_binary_entry_base(std::ostream& out, entry_base_t * entry)
|
void write_binary_entry_base(std::ostream& out, entry_base_t * entry)
|
||||||
{
|
{
|
||||||
#ifdef USE_EDITOR
|
|
||||||
write_binary_long(out, entry->src_idx);
|
write_binary_long(out, entry->src_idx);
|
||||||
write_binary_long(out, entry->beg_pos);
|
write_binary_long(out, entry->beg_pos);
|
||||||
write_binary_long(out, entry->beg_line);
|
write_binary_long(out, entry->beg_line);
|
||||||
write_binary_long(out, entry->end_pos);
|
write_binary_long(out, entry->end_pos);
|
||||||
write_binary_long(out, entry->end_line);
|
write_binary_long(out, entry->end_line);
|
||||||
#endif
|
|
||||||
|
|
||||||
bool ignore_calculated = false;
|
bool ignore_calculated = false;
|
||||||
for (transactions_list::const_iterator i = entry->transactions.begin();
|
for (transactions_list::const_iterator i = entry->transactions.begin();
|
||||||
|
|
@ -856,7 +886,7 @@ void write_binary_entry(std::ostream& out, entry_t * entry)
|
||||||
void write_binary_auto_entry(std::ostream& out, auto_entry_t * entry)
|
void write_binary_auto_entry(std::ostream& out, auto_entry_t * entry)
|
||||||
{
|
{
|
||||||
write_binary_entry_base(out, entry);
|
write_binary_entry_base(out, entry);
|
||||||
write_binary_string(out, entry->predicate_string);
|
write_binary_value_expr(out, entry->predicate->predicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_binary_period_entry(std::ostream& out, period_entry_t * entry)
|
void write_binary_period_entry(std::ostream& out, period_entry_t * entry)
|
||||||
|
|
@ -869,39 +899,47 @@ void write_binary_commodity(std::ostream& out, commodity_t * commodity)
|
||||||
{
|
{
|
||||||
write_binary_string(out, commodity->symbol);
|
write_binary_string(out, commodity->symbol);
|
||||||
write_binary_number(out, commodity->quote);
|
write_binary_number(out, commodity->quote);
|
||||||
write_binary_string(out, commodity->name);
|
write_binary_string(out, commodity->name_);
|
||||||
write_binary_string(out, commodity->note);
|
write_binary_string(out, commodity->note_);
|
||||||
write_binary_number(out, commodity->precision);
|
write_binary_number(out, commodity->precision_);
|
||||||
write_binary_number(out, commodity->flags);
|
write_binary_number(out, commodity->flags_);
|
||||||
commodity->ident = ++commodity_index;
|
commodity->ident = ++commodity_index;
|
||||||
write_binary_long(out, commodity->ident);
|
write_binary_long(out, commodity->ident);
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_binary_commodity_extra(std::ostream& out, commodity_t * commodity)
|
void write_binary_commodity_extra(std::ostream& out, commodity_t * commodity)
|
||||||
{
|
{
|
||||||
if (! commodity->history) {
|
if (! commodity->history_) {
|
||||||
write_binary_long<unsigned long>(out, 0);
|
write_binary_long<unsigned long>(out, 0);
|
||||||
} else {
|
} else {
|
||||||
write_binary_long<unsigned long>(out, commodity->history->prices.size());
|
write_binary_long<unsigned long>(out, commodity->history_->prices.size());
|
||||||
for (history_map::const_iterator i = commodity->history->prices.begin();
|
for (history_map::const_iterator i = commodity->history_->prices.begin();
|
||||||
i != commodity->history->prices.end();
|
i != commodity->history_->prices.end();
|
||||||
i++) {
|
i++) {
|
||||||
write_binary_long(out, (*i).first);
|
write_binary_long(out, (*i).first);
|
||||||
write_binary_amount(out, (*i).second);
|
write_binary_amount(out, (*i).second);
|
||||||
}
|
}
|
||||||
write_binary_long(out, commodity->history->last_lookup);
|
write_binary_long(out, commodity->history_->last_lookup);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (commodity->smaller) {
|
if (commodity->smaller_) {
|
||||||
write_binary_number<unsigned char>(out, 1);
|
write_binary_number<unsigned char>(out, 1);
|
||||||
write_binary_amount(out, *commodity->smaller);
|
write_binary_amount(out, *commodity->smaller_);
|
||||||
} else {
|
} else {
|
||||||
write_binary_number<unsigned char>(out, 0);
|
write_binary_number<unsigned char>(out, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (commodity->larger) {
|
if (commodity->larger_) {
|
||||||
write_binary_number<unsigned char>(out, 1);
|
write_binary_number<unsigned char>(out, 1);
|
||||||
write_binary_amount(out, *commodity->larger);
|
write_binary_amount(out, *commodity->larger_);
|
||||||
|
} else {
|
||||||
|
write_binary_number<unsigned char>(out, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commodity->price) {
|
||||||
|
write_binary_number<unsigned char>(out, 1);
|
||||||
|
write_binary_amount(out, *commodity->price);
|
||||||
|
write_binary_long(out, commodity->base->ident);
|
||||||
} else {
|
} else {
|
||||||
write_binary_number<unsigned char>(out, 0);
|
write_binary_number<unsigned char>(out, 0);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
190
config.cc
190
config.cc
|
|
@ -17,15 +17,43 @@
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
std::list<option_t> config_options;
|
namespace {
|
||||||
|
config_t * config = NULL;
|
||||||
|
|
||||||
static config_t * config = NULL;
|
void xact_amount(value_t& result, const details_t& details, value_expr_t *)
|
||||||
|
{
|
||||||
|
if (transaction_has_xdata(*details.xact) &&
|
||||||
|
transaction_xdata_(*details.xact).dflags & TRANSACTION_COMPOSITE)
|
||||||
|
result = transaction_xdata_(*details.xact).composite_amount;
|
||||||
|
else
|
||||||
|
result = details.xact->amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void xact_running_total(value_t& result, const details_t& details,
|
||||||
|
value_expr_t *)
|
||||||
|
{
|
||||||
|
result = transaction_xdata_(*details.xact).total;
|
||||||
|
}
|
||||||
|
|
||||||
|
void account_amount(value_t& result, const details_t& details,
|
||||||
|
value_expr_t *)
|
||||||
|
{
|
||||||
|
if (account_has_xdata(*details.account))
|
||||||
|
result = account_xdata(*details.account).value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void account_total(value_t& result, const details_t& details,
|
||||||
|
value_expr_t *)
|
||||||
|
{
|
||||||
|
if (account_has_xdata(*details.account))
|
||||||
|
result = account_xdata(*details.account).total;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void config_t::reset()
|
void config_t::reset()
|
||||||
{
|
{
|
||||||
amount_expr = "a";
|
amount_expr = "a";
|
||||||
total_expr = "O";
|
total_expr = "O";
|
||||||
total_expr_template = "#";
|
|
||||||
pricing_leeway = 24 * 3600;
|
pricing_leeway = 24 * 3600;
|
||||||
budget_flags = BUDGET_NO_BUDGET;
|
budget_flags = BUDGET_NO_BUDGET;
|
||||||
balance_format = "%20T %2_%-a\n";
|
balance_format = "%20T %2_%-a\n";
|
||||||
|
|
@ -34,8 +62,8 @@ void config_t::reset()
|
||||||
wide_register_format = ("%D %-.35P %-.38A %22.108t %22.132T\n%/"
|
wide_register_format = ("%D %-.35P %-.38A %22.108t %22.132T\n%/"
|
||||||
"%48|%-.38A %22.108t %22.132T\n");
|
"%48|%-.38A %22.108t %22.132T\n");
|
||||||
csv_register_format = "\"%D\",\"%P\",\"%A\",\"%t\",\"%T\"\n";
|
csv_register_format = "\"%D\",\"%P\",\"%A\",\"%t\",\"%T\"\n";
|
||||||
plot_amount_format = "%D %(St)\n";
|
plot_amount_format = "%D %(S(t))\n";
|
||||||
plot_total_format = "%D %(ST)\n";
|
plot_total_format = "%D %(S(T))\n";
|
||||||
print_format = "\n%d %Y%C%P\n %-34W %12o%n\n%/ %-34W %12o%n\n";
|
print_format = "\n%d %Y%C%P\n %-34W %12o%n\n%/ %-34W %12o%n\n";
|
||||||
write_hdr_format = "%d %Y%C%P\n";
|
write_hdr_format = "%d %Y%C%P\n";
|
||||||
write_xact_format = " %-34W %12o%n\n";
|
write_xact_format = " %-34W %12o%n\n";
|
||||||
|
|
@ -62,6 +90,7 @@ void config_t::reset()
|
||||||
show_revalued = false;
|
show_revalued = false;
|
||||||
show_revalued_only = false;
|
show_revalued_only = false;
|
||||||
download_quotes = false;
|
download_quotes = false;
|
||||||
|
debug_mode = false;
|
||||||
|
|
||||||
use_cache = false;
|
use_cache = false;
|
||||||
cache_dirty = false;
|
cache_dirty = false;
|
||||||
|
|
@ -133,7 +162,7 @@ config_t::regexps_to_predicate(const std::string& command,
|
||||||
if (! show_related && ! show_all_related) {
|
if (! show_related && ! show_all_related) {
|
||||||
if (! display_predicate.empty())
|
if (! display_predicate.empty())
|
||||||
display_predicate += "&";
|
display_predicate += "&";
|
||||||
else if (! show_empty)
|
if (! show_empty)
|
||||||
display_predicate += "T&";
|
display_predicate += "T&";
|
||||||
|
|
||||||
if (add_predicate == 2)
|
if (add_predicate == 2)
|
||||||
|
|
@ -143,6 +172,8 @@ config_t::regexps_to_predicate(const std::string& command,
|
||||||
display_predicate += ")/";
|
display_predicate += ")/";
|
||||||
}
|
}
|
||||||
else if (! show_empty) {
|
else if (! show_empty) {
|
||||||
|
if (! display_predicate.empty())
|
||||||
|
display_predicate += "&";
|
||||||
display_predicate += "T";
|
display_predicate += "T";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -178,6 +209,18 @@ void config_t::process_environment(char ** envp, const std::string& tag)
|
||||||
config = NULL;
|
config = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::string expand_value_expr(const std::string& tmpl,
|
||||||
|
const std::string& expr)
|
||||||
|
{
|
||||||
|
std::string xp = tmpl;
|
||||||
|
for (std::string::size_type i = xp.find('#');
|
||||||
|
i != std::string::npos;
|
||||||
|
i = xp.find('#'))
|
||||||
|
xp = (std::string(xp, 0, i) + "(" + expr + ")" +
|
||||||
|
std::string(xp, i + 1));
|
||||||
|
return xp;
|
||||||
|
}
|
||||||
|
|
||||||
void config_t::process_options(const std::string& command,
|
void config_t::process_options(const std::string& command,
|
||||||
strings_list::iterator arg,
|
strings_list::iterator arg,
|
||||||
strings_list::iterator args_end)
|
strings_list::iterator args_end)
|
||||||
|
|
@ -244,32 +287,8 @@ void config_t::process_options(const std::string& command,
|
||||||
|
|
||||||
// Setup the values of %t and %T, used in format strings
|
// Setup the values of %t and %T, used in format strings
|
||||||
|
|
||||||
try {
|
ledger::amount_expr.reset(new value_expr(amount_expr));
|
||||||
ledger::amount_expr.reset(parse_value_expr(amount_expr));
|
ledger::total_expr.reset(new value_expr(total_expr));
|
||||||
}
|
|
||||||
catch (const value_expr_error& err) {
|
|
||||||
throw error(std::string("In amount expression '") + amount_expr +
|
|
||||||
"': " + err.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string expr = total_expr_template;
|
|
||||||
for (std::string::size_type i = expr.find('#');
|
|
||||||
i != std::string::npos;
|
|
||||||
i = expr.find('#'))
|
|
||||||
expr = (std::string(expr, 0, i) + "(" + total_expr + ")" +
|
|
||||||
std::string(expr, i + 1));
|
|
||||||
|
|
||||||
DEBUG_PRINT("ledger.config.total_expr",
|
|
||||||
"Total expression template = " << total_expr_template);
|
|
||||||
DEBUG_PRINT("ledger.config.total_expr",
|
|
||||||
"Total expression is now = " << expr);
|
|
||||||
try {
|
|
||||||
ledger::total_expr.reset(parse_value_expr(expr));
|
|
||||||
}
|
|
||||||
catch (const value_expr_error& err) {
|
|
||||||
throw error(std::string("In total expression '") + expr + "': " +
|
|
||||||
err.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
// If downloading is to be supported, configure the updater
|
// If downloading is to be supported, configure the updater
|
||||||
|
|
||||||
|
|
@ -683,6 +702,10 @@ OPT_BEGIN(account, "a:") {
|
||||||
config->account = optarg;
|
config->account = optarg;
|
||||||
} OPT_END(account);
|
} OPT_END(account);
|
||||||
|
|
||||||
|
OPT_BEGIN(debug, "") {
|
||||||
|
config->debug_mode = true;
|
||||||
|
} OPT_END(debug);
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Report filtering
|
// Report filtering
|
||||||
|
|
@ -755,6 +778,10 @@ OPT_BEGIN(actual, "L") {
|
||||||
config->predicate += "L";
|
config->predicate += "L";
|
||||||
} OPT_END(actual);
|
} OPT_END(actual);
|
||||||
|
|
||||||
|
OPT_BEGIN(lots, "") {
|
||||||
|
show_lots = true;
|
||||||
|
} OPT_END(lots);
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Output customization
|
// Output customization
|
||||||
|
|
@ -1013,6 +1040,12 @@ OPT_BEGIN(basis, "B") {
|
||||||
config->total_expr = "B";
|
config->total_expr = "B";
|
||||||
} OPT_END(basis);
|
} OPT_END(basis);
|
||||||
|
|
||||||
|
OPT_BEGIN(price, "I") {
|
||||||
|
show_lots = true; // don't show them, but use in calculations
|
||||||
|
config->amount_expr = "i";
|
||||||
|
config->total_expr = "I";
|
||||||
|
} OPT_END(price);
|
||||||
|
|
||||||
OPT_BEGIN(market, "V") {
|
OPT_BEGIN(market, "V") {
|
||||||
config->show_revalued = true;
|
config->show_revalued = true;
|
||||||
|
|
||||||
|
|
@ -1034,15 +1067,98 @@ OPT_BEGIN(gain, "G") {
|
||||||
} OPT_END(gain);
|
} OPT_END(gain);
|
||||||
|
|
||||||
OPT_BEGIN(average, "A") {
|
OPT_BEGIN(average, "A") {
|
||||||
config->total_expr_template = "A#";
|
config->total_expr = expand_value_expr("A(#)", config->total_expr);
|
||||||
} OPT_END(average);
|
} OPT_END(average);
|
||||||
|
|
||||||
OPT_BEGIN(deviation, "D") {
|
OPT_BEGIN(deviation, "D") {
|
||||||
config->total_expr_template = "t-A#";
|
config->total_expr = expand_value_expr("t-A(#)", config->total_expr);
|
||||||
} OPT_END(deviation);
|
} OPT_END(deviation);
|
||||||
|
|
||||||
OPT_BEGIN(percentage, "%") {
|
OPT_BEGIN(percentage, "%") {
|
||||||
config->total_expr_template = "^#&{100.0%}*(#/^#)";
|
config->total_expr = expand_value_expr("^#&{100.0%}*(#/^#)", config->total_expr);
|
||||||
} OPT_END(percentage);
|
} OPT_END(percentage);
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
option_t config_options[CONFIG_OPTIONS_SIZE] = {
|
||||||
|
{ "account", 'a', true, opt_account, false },
|
||||||
|
{ "actual", 'L', false, opt_actual, false },
|
||||||
|
{ "add-budget", '\0', false, opt_add_budget, false },
|
||||||
|
{ "amount", 't', true, opt_amount, false },
|
||||||
|
{ "amount-data", 'j', false, opt_amount_data, false },
|
||||||
|
{ "average", 'A', false, opt_average, false },
|
||||||
|
{ "balance-format", '\0', true, opt_balance_format, false },
|
||||||
|
{ "basis", 'B', false, opt_basis, false },
|
||||||
|
{ "begin", 'b', true, opt_begin, false },
|
||||||
|
{ "budget", '\0', false, opt_budget, false },
|
||||||
|
{ "by-payee", 'P', false, opt_by_payee, false },
|
||||||
|
{ "cache", '\0', true, opt_cache, false },
|
||||||
|
{ "cleared", 'C', false, opt_cleared, false },
|
||||||
|
{ "collapse", 'n', false, opt_collapse, false },
|
||||||
|
{ "comm-as-payee", 'x', false, opt_comm_as_payee, false },
|
||||||
|
{ "csv-register-format", '\0', true, opt_csv_register_format, false },
|
||||||
|
{ "current", 'c', false, opt_current, false },
|
||||||
|
{ "date-format", 'y', true, opt_date_format, false },
|
||||||
|
{ "debug", '\0', false, opt_debug, false },
|
||||||
|
{ "deviation", 'D', false, opt_deviation, false },
|
||||||
|
{ "display", 'd', true, opt_display, false },
|
||||||
|
{ "dow", '\0', false, opt_dow, false },
|
||||||
|
{ "download", 'Q', false, opt_download, false },
|
||||||
|
{ "effective", '\0', false, opt_effective, false },
|
||||||
|
{ "empty", 'E', false, opt_empty, false },
|
||||||
|
{ "end", 'e', true, opt_end, false },
|
||||||
|
{ "equity-format", '\0', true, opt_equity_format, false },
|
||||||
|
{ "file", 'f', true, opt_file, false },
|
||||||
|
{ "forecast", '\0', true, opt_forecast, false },
|
||||||
|
{ "format", 'F', true, opt_format, false },
|
||||||
|
{ "full-help", 'H', false, opt_full_help, false },
|
||||||
|
{ "gain", 'G', false, opt_gain, false },
|
||||||
|
{ "head", '\0', true, opt_head, false },
|
||||||
|
{ "help", 'h', false, opt_help, false },
|
||||||
|
{ "help-calc", '\0', false, opt_help_calc, false },
|
||||||
|
{ "help-comm", '\0', false, opt_help_comm, false },
|
||||||
|
{ "help-disp", '\0', false, opt_help_disp, false },
|
||||||
|
{ "init-file", 'i', true, opt_init_file, false },
|
||||||
|
{ "input-date-format", '\0', true, opt_input_date_format, false },
|
||||||
|
{ "limit", 'l', true, opt_limit, false },
|
||||||
|
{ "lots", '\0', false, opt_lots, false },
|
||||||
|
{ "market", 'V', false, opt_market, false },
|
||||||
|
{ "monthly", 'M', false, opt_monthly, false },
|
||||||
|
{ "no-cache", '\0', false, opt_no_cache, false },
|
||||||
|
{ "output", 'o', true, opt_output, false },
|
||||||
|
{ "pager", '\0', true, opt_pager, false },
|
||||||
|
{ "percentage", '%', false, opt_percentage, false },
|
||||||
|
{ "performance", 'g', false, opt_performance, false },
|
||||||
|
{ "period", 'p', true, opt_period, false },
|
||||||
|
{ "period-sort", '\0', true, opt_period_sort, false },
|
||||||
|
{ "plot-amount-format", '\0', true, opt_plot_amount_format, false },
|
||||||
|
{ "plot-total-format", '\0', true, opt_plot_total_format, false },
|
||||||
|
{ "price", 'I', false, opt_price, false },
|
||||||
|
{ "price-db", '\0', true, opt_price_db, false },
|
||||||
|
{ "price-exp", 'Z', true, opt_price_exp, false },
|
||||||
|
{ "prices-format", '\0', true, opt_prices_format, false },
|
||||||
|
{ "print-format", '\0', true, opt_print_format, false },
|
||||||
|
{ "quantity", 'O', false, opt_quantity, false },
|
||||||
|
{ "real", 'R', false, opt_real, false },
|
||||||
|
{ "reconcile", '\0', true, opt_reconcile, false },
|
||||||
|
{ "reconcile-date", '\0', true, opt_reconcile_date, false },
|
||||||
|
{ "register-format", '\0', true, opt_register_format, false },
|
||||||
|
{ "related", 'r', false, opt_related, false },
|
||||||
|
{ "sort", 'S', true, opt_sort, false },
|
||||||
|
{ "subtotal", 's', false, opt_subtotal, false },
|
||||||
|
{ "tail", '\0', true, opt_tail, false },
|
||||||
|
{ "total", 'T', true, opt_total, false },
|
||||||
|
{ "total-data", 'J', false, opt_total_data, false },
|
||||||
|
{ "totals", '\0', false, opt_totals, false },
|
||||||
|
{ "unbudgeted", '\0', false, opt_unbudgeted, false },
|
||||||
|
{ "uncleared", 'U', false, opt_uncleared, false },
|
||||||
|
{ "version", 'v', false, opt_version, false },
|
||||||
|
{ "weekly", 'W', false, opt_weekly, false },
|
||||||
|
{ "wide", 'w', false, opt_wide, false },
|
||||||
|
{ "wide-register-format", '\0', true, opt_wide_register_format, false },
|
||||||
|
{ "write-hdr-format", '\0', true, opt_write_hdr_format, false },
|
||||||
|
{ "write-xact-format", '\0', true, opt_write_xact_format, false },
|
||||||
|
{ "yearly", 'Y', false, opt_yearly, false },
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace ledger
|
} // namespace ledger
|
||||||
|
|
|
||||||
22
config.h
22
config.h
|
|
@ -42,7 +42,6 @@ class config_t
|
||||||
std::string sort_string;
|
std::string sort_string;
|
||||||
std::string amount_expr;
|
std::string amount_expr;
|
||||||
std::string total_expr;
|
std::string total_expr;
|
||||||
std::string total_expr_template;
|
|
||||||
std::string forecast_limit;
|
std::string forecast_limit;
|
||||||
std::string reconcile_balance;
|
std::string reconcile_balance;
|
||||||
std::string reconcile_date;
|
std::string reconcile_date;
|
||||||
|
|
@ -66,6 +65,7 @@ class config_t
|
||||||
bool download_quotes;
|
bool download_quotes;
|
||||||
bool use_cache;
|
bool use_cache;
|
||||||
bool cache_dirty;
|
bool cache_dirty;
|
||||||
|
bool debug_mode;
|
||||||
|
|
||||||
config_t() {
|
config_t() {
|
||||||
reset();
|
reset();
|
||||||
|
|
@ -73,7 +73,6 @@ class config_t
|
||||||
config_t(const config_t&) {
|
config_t(const config_t&) {
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
void regexps_to_predicate(const std::string& command,
|
void regexps_to_predicate(const std::string& command,
|
||||||
|
|
@ -100,24 +99,15 @@ class config_t
|
||||||
std::list<item_handler<transaction_t> *>& ptrs);
|
std::list<item_handler<transaction_t> *>& ptrs);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern std::list<option_t> config_options;
|
#define CONFIG_OPTIONS_SIZE 78
|
||||||
|
extern option_t config_options[CONFIG_OPTIONS_SIZE];
|
||||||
|
|
||||||
void option_help(std::ostream& out);
|
void option_help(std::ostream& out);
|
||||||
|
|
||||||
struct declared_option_handler : public option_handler {
|
#define OPT_BEGIN(tag, chars) \
|
||||||
declared_option_handler(const std::string& label,
|
void opt_ ## tag(const char * optarg)
|
||||||
const std::string& opt_chars) {
|
|
||||||
add_option_handler(config_options, label, opt_chars, *this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#define OPT_BEGIN(tag, chars) \
|
#define OPT_END(tag)
|
||||||
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
|
|
||||||
|
|
||||||
} // namespace ledger
|
} // namespace ledger
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -200,15 +200,6 @@ AC_ARG_ENABLE(debug,
|
||||||
esac],[debug=false])
|
esac],[debug=false])
|
||||||
AM_CONDITIONAL(DEBUG, test x$debug = xtrue)
|
AM_CONDITIONAL(DEBUG, test x$debug = xtrue)
|
||||||
|
|
||||||
AC_ARG_ENABLE(emacs,
|
|
||||||
[ --enable-emacs Turn on Emacs support],
|
|
||||||
[case "${enableval}" in
|
|
||||||
yes) emacs=true ;;
|
|
||||||
no) emacs=false ;;
|
|
||||||
*) AC_MSG_ERROR(bad value ${enableval} for --enable-emacs) ;;
|
|
||||||
esac],[emacs=true])
|
|
||||||
AM_CONDITIONAL(USE_EDITOR, test x$emacs = xtrue)
|
|
||||||
|
|
||||||
# Checks for header files.
|
# Checks for header files.
|
||||||
AC_STDC_HEADERS
|
AC_STDC_HEADERS
|
||||||
AC_HAVE_HEADERS(sys/stat.h)
|
AC_HAVE_HEADERS(sys/stat.h)
|
||||||
|
|
|
||||||
5
debug.h
5
debug.h
|
|
@ -55,6 +55,9 @@ bool _debug_active(const char * const cls);
|
||||||
#define DEBUG(cls) (_debug_active(cls))
|
#define DEBUG(cls) (_debug_active(cls))
|
||||||
#define DEBUG_() DEBUG(_debug_cls)
|
#define DEBUG_() DEBUG(_debug_cls)
|
||||||
|
|
||||||
|
#define DEBUG_IF(cls) if (_debug_active(cls))
|
||||||
|
#define DEBUG_IF_() if (_debug_active(_debug_cls))
|
||||||
|
|
||||||
#define DEBUG_PRINT(cls, x) \
|
#define DEBUG_PRINT(cls, x) \
|
||||||
if (_debug_stream && _debug_active(cls)) { \
|
if (_debug_stream && _debug_active(cls)) { \
|
||||||
*_debug_stream << x << std::endl; \
|
*_debug_stream << x << std::endl; \
|
||||||
|
|
@ -91,6 +94,8 @@ void operator delete[](void*, const std::nothrow_t&) throw();
|
||||||
#define DEBUG_CLASS(cls)
|
#define DEBUG_CLASS(cls)
|
||||||
#define DEBUG(cls) 0
|
#define DEBUG(cls) 0
|
||||||
#define DEBUG_() 0
|
#define DEBUG_() 0
|
||||||
|
#define DEBUG_IF(cls)
|
||||||
|
#define DEBUG_IF_()
|
||||||
#define DEBUG_PRINT(cls, x)
|
#define DEBUG_PRINT(cls, x)
|
||||||
#define DEBUG_PRINT_(x)
|
#define DEBUG_PRINT_(x)
|
||||||
#define DEBUG_PRINT_TIME(cls, x)
|
#define DEBUG_PRINT_TIME(cls, x)
|
||||||
|
|
|
||||||
120
derive.cc
120
derive.cc
|
|
@ -23,7 +23,8 @@ entry_t * derive_new_entry(journal_t& journal,
|
||||||
|
|
||||||
mask_t regexp(*i++);
|
mask_t regexp(*i++);
|
||||||
|
|
||||||
for (entries_list::reverse_iterator j = journal.entries.rbegin();
|
entries_list::reverse_iterator j;
|
||||||
|
for (j = journal.entries.rbegin();
|
||||||
j != journal.entries.rend();
|
j != journal.entries.rend();
|
||||||
j++)
|
j++)
|
||||||
if (regexp.match((*j)->payee)) {
|
if (regexp.match((*j)->payee)) {
|
||||||
|
|
@ -33,23 +34,42 @@ entry_t * derive_new_entry(journal_t& journal,
|
||||||
|
|
||||||
added->payee = matching ? matching->payee : regexp.pattern;
|
added->payee = matching ? matching->payee : regexp.pattern;
|
||||||
|
|
||||||
if (i == end) {
|
if (! matching) {
|
||||||
if (! matching)
|
account_t * acct;
|
||||||
throw error("Could not find a matching payee");
|
if (i == end || ((*i)[0] == '-' || std::isdigit((*i)[0]))) {
|
||||||
|
acct = journal.find_account("Expenses");
|
||||||
|
}
|
||||||
|
else if (i != end) {
|
||||||
|
acct = journal.find_account_re(*i);
|
||||||
|
if (! acct)
|
||||||
|
acct = journal.find_account(*i);
|
||||||
|
assert(acct);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == end)
|
||||||
|
added->add_transaction(new transaction_t(acct));
|
||||||
|
else
|
||||||
|
added->add_transaction(new transaction_t(acct, amount_t(*i++)));
|
||||||
|
|
||||||
|
if (journal.basket)
|
||||||
|
acct = journal.basket;
|
||||||
|
else
|
||||||
|
acct = journal.find_account("Equity");
|
||||||
|
|
||||||
|
added->add_transaction(new transaction_t(acct));
|
||||||
|
}
|
||||||
|
else if (i == end) {
|
||||||
// If no argument were given but the payee, assume the user wants
|
// If no argument were given but the payee, assume the user wants
|
||||||
// to see the same transaction as last time.
|
// to see the same transaction as last time.
|
||||||
added->code = matching->code;
|
added->code = matching->code;
|
||||||
|
|
||||||
for (transactions_list::iterator j = matching->transactions.begin();
|
for (transactions_list::iterator k = matching->transactions.begin();
|
||||||
j != matching->transactions.end();
|
k != matching->transactions.end();
|
||||||
j++)
|
k++)
|
||||||
added->add_transaction(new transaction_t(**j));
|
added->add_transaction(new transaction_t(**k));
|
||||||
}
|
}
|
||||||
else if ((*i)[0] == '-' || std::isdigit((*i)[0])) {
|
else if ((*i)[0] == '-' || std::isdigit((*i)[0])) {
|
||||||
if (! matching)
|
|
||||||
throw error("Could not determine the account to draw from");
|
|
||||||
|
|
||||||
transaction_t * m_xact, * xact, * first;
|
transaction_t * m_xact, * xact, * first;
|
||||||
m_xact = matching->transactions.front();
|
m_xact = matching->transactions.front();
|
||||||
|
|
||||||
|
|
@ -68,67 +88,65 @@ entry_t * derive_new_entry(journal_t& journal,
|
||||||
account_t * acct = journal.find_account_re(*i);
|
account_t * acct = journal.find_account_re(*i);
|
||||||
if (! acct)
|
if (! acct)
|
||||||
acct = journal.find_account(*i);
|
acct = journal.find_account(*i);
|
||||||
if (acct)
|
assert(acct);
|
||||||
added->transactions.back()->account = acct;
|
added->transactions.back()->account = acct;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
while (i != end) {
|
while (i != end) {
|
||||||
std::string& re_pat(*i++);
|
std::string& re_pat(*i++);
|
||||||
account_t * acct = NULL;
|
account_t * acct = NULL;
|
||||||
commodity_t * cmdty = NULL;
|
amount_t * amt = NULL;
|
||||||
|
|
||||||
if (matching) {
|
mask_t acct_regex(re_pat);
|
||||||
mask_t acct_regex(re_pat);
|
|
||||||
|
|
||||||
for (transactions_list::const_iterator x
|
for (; j != journal.entries.rend(); j++)
|
||||||
= matching->transactions.begin();
|
if (regexp.match((*j)->payee)) {
|
||||||
x != matching->transactions.end();
|
entry_t * entry = *j;
|
||||||
x++) {
|
for (transactions_list::const_iterator x =
|
||||||
if (acct_regex.match((*x)->account->fullname())) {
|
entry->transactions.begin();
|
||||||
acct = (*x)->account;
|
x != entry->transactions.end();
|
||||||
cmdty = &(*x)->amount.commodity();
|
x++)
|
||||||
break;
|
if (acct_regex.match((*x)->account->fullname())) {
|
||||||
}
|
acct = (*x)->account;
|
||||||
|
amt = &(*x)->amount;
|
||||||
|
matching = entry;
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
found:
|
||||||
if (! acct)
|
if (! acct)
|
||||||
acct = journal.find_account_re(re_pat);
|
acct = journal.find_account_re(re_pat);
|
||||||
if (! acct)
|
if (! acct)
|
||||||
acct = journal.find_account(re_pat);
|
acct = journal.find_account(re_pat);
|
||||||
|
|
||||||
|
transaction_t * xact;
|
||||||
if (i == end) {
|
if (i == end) {
|
||||||
added->add_transaction(new transaction_t(acct));
|
if (amt)
|
||||||
goto done;
|
xact = new transaction_t(acct, *amt);
|
||||||
|
else
|
||||||
|
xact = new transaction_t(acct);
|
||||||
|
} 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);
|
added->add_transaction(xact);
|
||||||
}
|
}
|
||||||
|
|
||||||
account_t * draw_acct;
|
assert(matching->transactions.back()->account);
|
||||||
if (matching)
|
if (account_t * draw_acct = matching->transactions.back()->account)
|
||||||
draw_acct = matching->transactions.back()->account;
|
added->add_transaction(new transaction_t(draw_acct));
|
||||||
else if (journal.basket)
|
|
||||||
draw_acct = journal.basket;
|
|
||||||
else
|
|
||||||
throw error("Could not determine the account to draw from");
|
|
||||||
|
|
||||||
transaction_t * xact = new transaction_t(draw_acct);
|
|
||||||
added->add_transaction(xact);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
if (! added->finalize() ||
|
if (! run_hooks(journal.entry_finalize_hooks, *added) ||
|
||||||
! run_hooks(journal.entry_finalize_hooks, *added))
|
! added->finalize())
|
||||||
throw error("Failed to finalize derived entry (check commodities)");
|
throw error("Failed to finalize derived entry (check commodities)");
|
||||||
|
|
||||||
return added.release();
|
return added.release();
|
||||||
|
|
|
||||||
34
format.cc
34
format.cc
|
|
@ -162,13 +162,9 @@ element_t * format_t::parse_elements(const std::string& fmt)
|
||||||
throw format_error("Missing ')'");
|
throw format_error("Missing ')'");
|
||||||
|
|
||||||
current->type = element_t::VALUE_EXPR;
|
current->type = element_t::VALUE_EXPR;
|
||||||
try {
|
|
||||||
current->val_expr = parse_value_expr(std::string(b, p));
|
assert(current->val_expr == NULL);
|
||||||
}
|
current->val_expr = new value_expr(std::string(b, p));
|
||||||
catch (value_expr_error& err) {
|
|
||||||
throw value_expr_error(std::string("In format expression '") +
|
|
||||||
std::string(b, p) + "': " + err.what());
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -293,22 +289,22 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
|
||||||
case element_t::AMOUNT:
|
case element_t::AMOUNT:
|
||||||
case element_t::TOTAL:
|
case element_t::TOTAL:
|
||||||
case element_t::VALUE_EXPR: {
|
case element_t::VALUE_EXPR: {
|
||||||
value_expr_t * expr = NULL;
|
value_calc * calc = NULL;
|
||||||
switch (elem->type) {
|
switch (elem->type) {
|
||||||
case element_t::AMOUNT: expr = amount_expr.get(); break;
|
case element_t::AMOUNT: calc = amount_expr.get(); break;
|
||||||
case element_t::TOTAL: expr = total_expr.get(); break;
|
case element_t::TOTAL: calc = total_expr.get(); break;
|
||||||
case element_t::VALUE_EXPR: expr = elem->val_expr; break;
|
case element_t::VALUE_EXPR: calc = elem->val_expr; break;
|
||||||
default:
|
default:
|
||||||
assert(0);
|
assert(0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (! expr)
|
if (! calc)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
value_t value;
|
value_t value;
|
||||||
balance_t * bal = NULL;
|
balance_t * bal = NULL;
|
||||||
|
|
||||||
expr->compute(value, details);
|
calc->compute(value, details);
|
||||||
|
|
||||||
switch (value.type) {
|
switch (value.type) {
|
||||||
case value_t::BOOLEAN:
|
case value_t::BOOLEAN:
|
||||||
|
|
@ -348,9 +344,9 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
|
||||||
amount_t unit_cost = *details.xact->cost / details.xact->amount;
|
amount_t unit_cost = *details.xact->cost / details.xact->amount;
|
||||||
|
|
||||||
commodity_t& comm(unit_cost.commodity());
|
commodity_t& comm(unit_cost.commodity());
|
||||||
bool has_flag = comm.flags & COMMODITY_STYLE_VARIABLE;
|
bool has_flag = comm.flags() & COMMODITY_STYLE_VARIABLE;
|
||||||
if (! has_flag)
|
if (! has_flag)
|
||||||
unit_cost.commodity().flags |= COMMODITY_STYLE_VARIABLE;
|
unit_cost.commodity().flags() |= COMMODITY_STYLE_VARIABLE;
|
||||||
|
|
||||||
std::ostringstream stream;
|
std::ostringstream stream;
|
||||||
stream << details.xact->amount << " @ " << unit_cost;
|
stream << details.xact->amount << " @ " << unit_cost;
|
||||||
|
|
@ -358,7 +354,7 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
|
||||||
use_disp = true;
|
use_disp = true;
|
||||||
|
|
||||||
if (! has_flag)
|
if (! has_flag)
|
||||||
unit_cost.commodity().flags &= ~COMMODITY_STYLE_VARIABLE;
|
unit_cost.commodity().flags() &= ~COMMODITY_STYLE_VARIABLE;
|
||||||
}
|
}
|
||||||
else if (details.entry) {
|
else if (details.entry) {
|
||||||
unsigned int xacts_count = 0;
|
unsigned int xacts_count = 0;
|
||||||
|
|
@ -388,7 +384,6 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
#ifdef USE_EDITOR
|
|
||||||
case element_t::SOURCE:
|
case element_t::SOURCE:
|
||||||
if (details.entry && details.entry->journal) {
|
if (details.entry && details.entry->journal) {
|
||||||
int idx = details.entry->src_idx;
|
int idx = details.entry->src_idx;
|
||||||
|
|
@ -441,7 +436,6 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
|
||||||
if (details.xact)
|
if (details.xact)
|
||||||
out << details.xact->end_line;
|
out << details.xact->end_line;
|
||||||
break;
|
break;
|
||||||
#endif
|
|
||||||
|
|
||||||
case element_t::DATE_STRING: {
|
case element_t::DATE_STRING: {
|
||||||
std::time_t date = 0;
|
std::time_t date = 0;
|
||||||
|
|
@ -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)
|
const std::string& format)
|
||||||
: output_stream(_output_stream), last_entry(NULL), last_xact(NULL)
|
: output_stream(_output_stream), last_entry(NULL), last_xact(NULL)
|
||||||
{
|
{
|
||||||
|
|
@ -731,7 +725,7 @@ bool disp_subaccounts_p(const account_t& account,
|
||||||
return display;
|
return display;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool display_account(const account_t& account,
|
bool display_account(const account_t& account,
|
||||||
const item_predicate<account_t>& disp_pred)
|
const item_predicate<account_t>& disp_pred)
|
||||||
{
|
{
|
||||||
// Never display an account that has already been displayed.
|
// Never display an account that has already been displayed.
|
||||||
|
|
|
||||||
2
format.h
2
format.h
|
|
@ -51,7 +51,7 @@ struct element_t
|
||||||
|
|
||||||
kind_t type;
|
kind_t type;
|
||||||
std::string chars;
|
std::string chars;
|
||||||
value_expr_t * val_expr;
|
value_expr * val_expr;
|
||||||
|
|
||||||
struct element_t * next;
|
struct element_t * next;
|
||||||
|
|
||||||
|
|
|
||||||
24
gnucash.cc
24
gnucash.cc
|
|
@ -45,11 +45,9 @@ static std::istream * instreamp;
|
||||||
static unsigned int offset;
|
static unsigned int offset;
|
||||||
static XML_Parser parser;
|
static XML_Parser parser;
|
||||||
static std::string path;
|
static std::string path;
|
||||||
#ifdef USE_EDITOR
|
|
||||||
static unsigned int src_idx;
|
static unsigned int src_idx;
|
||||||
static istream_pos_type beg_pos;
|
static istream_pos_type beg_pos;
|
||||||
static unsigned long beg_line;
|
static unsigned long beg_line;
|
||||||
#endif
|
|
||||||
|
|
||||||
static transaction_t::state_t curr_state;
|
static transaction_t::state_t curr_state;
|
||||||
|
|
||||||
|
|
@ -148,13 +146,11 @@ static void endElement(void *userData, const char *name)
|
||||||
have_error = "The above entry does not balance";
|
have_error = "The above entry does not balance";
|
||||||
delete curr_entry;
|
delete curr_entry;
|
||||||
} else {
|
} else {
|
||||||
#ifdef USE_EDITOR
|
|
||||||
curr_entry->src_idx = src_idx;
|
curr_entry->src_idx = src_idx;
|
||||||
curr_entry->beg_pos = beg_pos;
|
curr_entry->beg_pos = beg_pos;
|
||||||
curr_entry->beg_line = beg_line;
|
curr_entry->beg_line = beg_line;
|
||||||
curr_entry->end_pos = instreamp->tellg();
|
curr_entry->end_pos = instreamp->tellg();
|
||||||
curr_entry->end_line = XML_GetCurrentLineNumber(parser) - offset;
|
curr_entry->end_line = XML_GetCurrentLineNumber(parser) - offset;
|
||||||
#endif
|
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,7 +176,7 @@ static void endElement(void *userData, const char *name)
|
||||||
|
|
||||||
if (default_commodity) {
|
if (default_commodity) {
|
||||||
curr_quant.set_commodity(*default_commodity);
|
curr_quant.set_commodity(*default_commodity);
|
||||||
value = curr_quant.round(default_commodity->precision);
|
value = curr_quant.round(default_commodity->precision());
|
||||||
|
|
||||||
if (curr_value.commodity() == *default_commodity)
|
if (curr_value.commodity() == *default_commodity)
|
||||||
curr_value = value;
|
curr_value = value;
|
||||||
|
|
@ -193,12 +189,10 @@ static void endElement(void *userData, const char *name)
|
||||||
if (value != curr_value)
|
if (value != curr_value)
|
||||||
xact->cost = new amount_t(curr_value);
|
xact->cost = new amount_t(curr_value);
|
||||||
|
|
||||||
#ifdef USE_EDITOR
|
|
||||||
xact->beg_pos = beg_pos;
|
xact->beg_pos = beg_pos;
|
||||||
xact->beg_line = beg_line;
|
xact->beg_line = beg_line;
|
||||||
xact->end_pos = instreamp->tellg();
|
xact->end_pos = instreamp->tellg();
|
||||||
xact->end_line = XML_GetCurrentLineNumber(parser) - offset;
|
xact->end_line = XML_GetCurrentLineNumber(parser) - offset;
|
||||||
#endif
|
|
||||||
|
|
||||||
// Clear the relevant variables for the next run
|
// Clear the relevant variables for the next run
|
||||||
curr_state = transaction_t::UNCLEARED;
|
curr_state = transaction_t::UNCLEARED;
|
||||||
|
|
@ -264,23 +258,23 @@ static void dataHandler(void *userData, const char *s, int len)
|
||||||
std::string symbol(s, len);
|
std::string symbol(s, len);
|
||||||
commodity_t * comm = commodity_t::find_commodity(symbol, true);
|
commodity_t * comm = commodity_t::find_commodity(symbol, true);
|
||||||
if (symbol != "$" && symbol != "USD")
|
if (symbol != "$" && symbol != "USD")
|
||||||
comm->flags |= COMMODITY_STYLE_SEPARATED;
|
comm->flags() |= COMMODITY_STYLE_SEPARATED;
|
||||||
account_comms.insert(account_comm_pair(curr_account, comm));
|
account_comms.insert(account_comm_pair(curr_account, comm));
|
||||||
}
|
}
|
||||||
else if (curr_entry) {
|
else if (curr_entry) {
|
||||||
std::string symbol(s, len);
|
std::string symbol(s, len);
|
||||||
entry_comm = commodity_t::find_commodity(symbol, true);
|
entry_comm = commodity_t::find_commodity(symbol, true);
|
||||||
if (symbol != "$" && symbol != "USD")
|
if (symbol != "$" && symbol != "USD")
|
||||||
entry_comm->flags |= COMMODITY_STYLE_SEPARATED;
|
entry_comm->flags() |= COMMODITY_STYLE_SEPARATED;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COMM_NAME:
|
case COMM_NAME:
|
||||||
curr_comm->name = std::string(s, len);
|
curr_comm->name() = std::string(s, len);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COMM_PREC:
|
case COMM_PREC:
|
||||||
curr_comm->precision = len - 1;
|
curr_comm->precision() = len - 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ENTRY_NUM:
|
case ENTRY_NUM:
|
||||||
|
|
@ -313,8 +307,8 @@ static void dataHandler(void *userData, const char *s, int len)
|
||||||
curr_value = convert_number(std::string(s, len), &precision);
|
curr_value = convert_number(std::string(s, len), &precision);
|
||||||
curr_value.set_commodity(*entry_comm);
|
curr_value.set_commodity(*entry_comm);
|
||||||
|
|
||||||
if (precision > entry_comm->precision)
|
if (precision > entry_comm->precision())
|
||||||
entry_comm->precision = precision;
|
entry_comm->precision() = precision;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -382,9 +376,7 @@ unsigned int gnucash_parser_t::parse(std::istream& in,
|
||||||
|
|
||||||
instreamp = ∈
|
instreamp = ∈
|
||||||
path = original_file ? *original_file : "<gnucash>";
|
path = original_file ? *original_file : "<gnucash>";
|
||||||
#ifdef USE_EDITOR
|
|
||||||
src_idx = journal->sources.size() - 1;
|
src_idx = journal->sources.size() - 1;
|
||||||
#endif
|
|
||||||
|
|
||||||
// GnuCash uses the USD commodity without defining it, which really
|
// GnuCash uses the USD commodity without defining it, which really
|
||||||
// means $.
|
// means $.
|
||||||
|
|
@ -401,10 +393,8 @@ unsigned int gnucash_parser_t::parse(std::istream& in,
|
||||||
XML_SetCharacterDataHandler(parser, dataHandler);
|
XML_SetCharacterDataHandler(parser, dataHandler);
|
||||||
|
|
||||||
while (in.good() && ! in.eof()) {
|
while (in.good() && ! in.eof()) {
|
||||||
#ifdef USE_EDITOR
|
|
||||||
beg_pos = in.tellg();
|
beg_pos = in.tellg();
|
||||||
beg_line = (XML_GetCurrentLineNumber(parser) - offset) + 1;
|
beg_line = (XML_GetCurrentLineNumber(parser) - offset) + 1;
|
||||||
#endif
|
|
||||||
|
|
||||||
in.getline(buf, BUFSIZ - 1);
|
in.getline(buf, BUFSIZ - 1);
|
||||||
std::strcat(buf, "\n");
|
std::strcat(buf, "\n");
|
||||||
|
|
|
||||||
12
journal.cc
12
journal.cc
|
|
@ -13,6 +13,14 @@ const std::string version = PACKAGE_VERSION;
|
||||||
|
|
||||||
bool transaction_t::use_effective_date = false;
|
bool transaction_t::use_effective_date = false;
|
||||||
|
|
||||||
|
transaction_t::~transaction_t()
|
||||||
|
{
|
||||||
|
DEBUG_PRINT("ledger.memory.dtors", "dtor transaction_t");
|
||||||
|
if (cost) delete cost;
|
||||||
|
if (amount_expr) amount_expr->release();
|
||||||
|
if (cost_expr) cost_expr->release();
|
||||||
|
}
|
||||||
|
|
||||||
std::time_t transaction_t::actual_date() const
|
std::time_t transaction_t::actual_date() const
|
||||||
{
|
{
|
||||||
if (_date == 0 && entry)
|
if (_date == 0 && entry)
|
||||||
|
|
@ -96,6 +104,10 @@ bool entry_base_t::finalize()
|
||||||
} else {
|
} else {
|
||||||
balance += *p;
|
balance += *p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((*x)->cost && (*x)->amount.commodity().price)
|
||||||
|
balance += (*((*x)->amount.commodity().price) * (*x)->amount -
|
||||||
|
*((*x)->cost));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
32
journal.h
32
journal.h
|
|
@ -44,12 +44,10 @@ class transaction_t
|
||||||
state_t state;
|
state_t state;
|
||||||
unsigned short flags;
|
unsigned short flags;
|
||||||
std::string note;
|
std::string note;
|
||||||
#ifdef USE_EDITOR
|
|
||||||
istream_pos_type beg_pos;
|
istream_pos_type beg_pos;
|
||||||
unsigned long beg_line;
|
unsigned long beg_line;
|
||||||
istream_pos_type end_pos;
|
istream_pos_type end_pos;
|
||||||
unsigned long end_line;
|
unsigned long end_line;
|
||||||
#endif
|
|
||||||
mutable void * data;
|
mutable void * data;
|
||||||
|
|
||||||
static bool use_effective_date;
|
static bool use_effective_date;
|
||||||
|
|
@ -58,13 +56,9 @@ class transaction_t
|
||||||
: entry(NULL), _date(0), _date_eff(0), account(_account),
|
: entry(NULL), _date(0), _date_eff(0), account(_account),
|
||||||
amount_expr(NULL), cost(NULL), cost_expr(NULL),
|
amount_expr(NULL), cost(NULL), cost_expr(NULL),
|
||||||
state(UNCLEARED), flags(TRANSACTION_NORMAL),
|
state(UNCLEARED), flags(TRANSACTION_NORMAL),
|
||||||
#ifdef USE_EDITOR
|
beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) {
|
||||||
beg_pos(0), beg_line(0), end_pos(0), end_line(0),
|
|
||||||
#endif
|
|
||||||
data(NULL) {
|
|
||||||
DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t");
|
DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t");
|
||||||
}
|
}
|
||||||
|
|
||||||
transaction_t(account_t * _account,
|
transaction_t(account_t * _account,
|
||||||
const amount_t& _amount,
|
const amount_t& _amount,
|
||||||
unsigned int _flags = TRANSACTION_NORMAL,
|
unsigned int _flags = TRANSACTION_NORMAL,
|
||||||
|
|
@ -72,30 +66,18 @@ class transaction_t
|
||||||
: entry(NULL), _date(0), _date_eff(0), account(_account),
|
: entry(NULL), _date(0), _date_eff(0), account(_account),
|
||||||
amount(_amount), amount_expr(NULL), cost(NULL), cost_expr(NULL),
|
amount(_amount), amount_expr(NULL), cost(NULL), cost_expr(NULL),
|
||||||
state(UNCLEARED), flags(_flags), note(_note),
|
state(UNCLEARED), flags(_flags), note(_note),
|
||||||
#ifdef USE_EDITOR
|
beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) {
|
||||||
beg_pos(0), beg_line(0), end_pos(0), end_line(0),
|
|
||||||
#endif
|
|
||||||
data(NULL) {
|
|
||||||
DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t");
|
DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t");
|
||||||
}
|
}
|
||||||
|
|
||||||
transaction_t(const transaction_t& xact)
|
transaction_t(const transaction_t& xact)
|
||||||
: entry(xact.entry), _date(0), _date_eff(0), account(xact.account),
|
: entry(xact.entry), _date(0), _date_eff(0), account(xact.account),
|
||||||
amount(xact.amount), amount_expr(NULL),
|
amount(xact.amount), amount_expr(NULL),
|
||||||
cost(xact.cost ? new amount_t(*xact.cost) : NULL), cost_expr(NULL),
|
cost(xact.cost ? new amount_t(*xact.cost) : NULL), cost_expr(NULL),
|
||||||
state(xact.state), flags(xact.flags), note(xact.note),
|
state(xact.state), flags(xact.flags), note(xact.note),
|
||||||
#ifdef USE_EDITOR
|
beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL) {
|
||||||
beg_pos(0), beg_line(0), end_pos(0), end_line(0),
|
|
||||||
#endif
|
|
||||||
data(NULL) {
|
|
||||||
DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t");
|
DEBUG_PRINT("ledger.memory.ctors", "ctor transaction_t");
|
||||||
}
|
}
|
||||||
|
~transaction_t();
|
||||||
~transaction_t() {
|
|
||||||
DEBUG_PRINT("ledger.memory.dtors", "dtor transaction_t");
|
|
||||||
if (cost)
|
|
||||||
delete cost;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::time_t actual_date() const;
|
std::time_t actual_date() const;
|
||||||
std::time_t effective_date() const;
|
std::time_t effective_date() const;
|
||||||
|
|
@ -124,26 +106,20 @@ class entry_base_t
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
journal_t * journal;
|
journal_t * journal;
|
||||||
#ifdef USE_EDITOR
|
|
||||||
unsigned long src_idx;
|
unsigned long src_idx;
|
||||||
istream_pos_type beg_pos;
|
istream_pos_type beg_pos;
|
||||||
unsigned long beg_line;
|
unsigned long beg_line;
|
||||||
istream_pos_type end_pos;
|
istream_pos_type end_pos;
|
||||||
unsigned long end_line;
|
unsigned long end_line;
|
||||||
#endif
|
|
||||||
transactions_list transactions;
|
transactions_list transactions;
|
||||||
|
|
||||||
entry_base_t() : journal(NULL),
|
entry_base_t() : journal(NULL),
|
||||||
#ifdef USE_EDITOR
|
|
||||||
beg_pos(0), beg_line(0), end_pos(0), end_line(0)
|
beg_pos(0), beg_line(0), end_pos(0), end_line(0)
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
DEBUG_PRINT("ledger.memory.ctors", "ctor entry_base_t");
|
DEBUG_PRINT("ledger.memory.ctors", "ctor entry_base_t");
|
||||||
}
|
}
|
||||||
entry_base_t(const entry_base_t& e) : journal(NULL),
|
entry_base_t(const entry_base_t& e) : journal(NULL),
|
||||||
#ifdef USE_EDITOR
|
|
||||||
beg_pos(0), beg_line(0), end_pos(0), end_line(0)
|
beg_pos(0), beg_line(0), end_pos(0), end_line(0)
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
DEBUG_PRINT("ledger.memory.ctors", "ctor entry_base_t");
|
DEBUG_PRINT("ledger.memory.ctors", "ctor entry_base_t");
|
||||||
for (transactions_list::const_iterator i = e.transactions.begin();
|
for (transactions_list::const_iterator i = e.transactions.begin();
|
||||||
|
|
|
||||||
34
main.cc
34
main.cc
|
|
@ -104,10 +104,10 @@ int parse_and_report(int argc, char * argv[], char * envp[])
|
||||||
command = "p";
|
command = "p";
|
||||||
else if (command == "output")
|
else if (command == "output")
|
||||||
command = "w";
|
command = "w";
|
||||||
#ifdef USE_EDITOR
|
|
||||||
else if (command == "emacs")
|
else if (command == "emacs")
|
||||||
command = "x";
|
command = "x";
|
||||||
#endif
|
else if (command == "lisp")
|
||||||
|
command = "x";
|
||||||
else if (command == "xml")
|
else if (command == "xml")
|
||||||
command = "X";
|
command = "X";
|
||||||
else if (command == "entry")
|
else if (command == "entry")
|
||||||
|
|
@ -122,6 +122,20 @@ int parse_and_report(int argc, char * argv[], char * envp[])
|
||||||
config.register_format = config.csv_register_format;
|
config.register_format = config.csv_register_format;
|
||||||
command = "r";
|
command = "r";
|
||||||
}
|
}
|
||||||
|
else if (command == "parse") {
|
||||||
|
value_auto_ptr expr(ledger::parse_value_expr(*arg));
|
||||||
|
if (config.debug_mode) {
|
||||||
|
ledger::dump_value_expr(std::cout, expr.get());
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
value_t result;
|
||||||
|
expr->compute(result, details_t());
|
||||||
|
std::cout << result << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (command == "expr") {
|
||||||
|
// this gets done below...
|
||||||
|
}
|
||||||
else
|
else
|
||||||
throw error(std::string("Unrecognized command '") + command + "'");
|
throw error(std::string("Unrecognized command '") + command + "'");
|
||||||
|
|
||||||
|
|
@ -211,6 +225,20 @@ int parse_and_report(int argc, char * argv[], char * envp[])
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Are we handling the parse or expr commands? Do so now.
|
||||||
|
|
||||||
|
if (command == "expr") {
|
||||||
|
value_auto_ptr expr(ledger::parse_value_expr(*arg));
|
||||||
|
if (config.debug_mode) {
|
||||||
|
ledger::dump_value_expr(std::cout, expr.get());
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
value_t result;
|
||||||
|
expr->compute(result, details_t());
|
||||||
|
std::cout << result << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Compile the format strings
|
// Compile the format strings
|
||||||
|
|
||||||
const std::string * format;
|
const std::string * format;
|
||||||
|
|
@ -245,10 +273,8 @@ int parse_and_report(int argc, char * argv[], char * envp[])
|
||||||
formatter = new set_account_value;
|
formatter = new set_account_value;
|
||||||
else if (command == "p" || command == "e")
|
else if (command == "p" || command == "e")
|
||||||
formatter = new format_entries(*out, *format);
|
formatter = new format_entries(*out, *format);
|
||||||
#ifdef USE_EDITOR
|
|
||||||
else if (command == "x")
|
else if (command == "x")
|
||||||
formatter = new format_emacs_transactions(*out);
|
formatter = new format_emacs_transactions(*out);
|
||||||
#endif
|
|
||||||
else if (command == "X") {
|
else if (command == "X") {
|
||||||
#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
|
#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
|
||||||
formatter = new format_xml_entries(*out, config.show_totals);
|
formatter = new format_xml_entries(*out, config.show_totals);
|
||||||
|
|
|
||||||
187
option.cc
187
option.cc
|
|
@ -1,4 +1,5 @@
|
||||||
#include "option.h"
|
#include "option.h"
|
||||||
|
#include "config.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
@ -6,149 +7,125 @@
|
||||||
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
void add_option_handler(std::list<option_t>& options,
|
|
||||||
const std::string& label,
|
|
||||||
const std::string& opt_chars,
|
|
||||||
option_handler& option)
|
|
||||||
{
|
|
||||||
option_t opt;
|
|
||||||
|
|
||||||
char buf[128];
|
|
||||||
char * p = buf;
|
|
||||||
for (const char * q = label.c_str();
|
|
||||||
*q && p - buf < 128;
|
|
||||||
q++)
|
|
||||||
if (*q == '_')
|
|
||||||
*p++ = '-';
|
|
||||||
else
|
|
||||||
*p++ = *q;
|
|
||||||
*p = '\0';
|
|
||||||
opt.long_opt = buf;
|
|
||||||
|
|
||||||
if (! opt_chars.empty()) {
|
|
||||||
if (opt_chars[0] != ':')
|
|
||||||
opt.short_opt = opt_chars[0];
|
|
||||||
|
|
||||||
if (opt_chars[opt_chars.length() - 1] == ':')
|
|
||||||
opt.wants_arg = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
opt.handler = &option;
|
|
||||||
|
|
||||||
options.push_front(opt);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
inline void process_option(const option_t& opt,
|
inline void process_option(option_t * opt, const char * arg = NULL) {
|
||||||
const char * arg = NULL) {
|
if (! opt->handled) {
|
||||||
if (! opt.handler->handled) {
|
opt->handler(arg);
|
||||||
(*opt.handler)(arg);
|
opt->handled = true;
|
||||||
opt.handler->handled = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
option_t * search_options(option_t * array, const char * name)
|
||||||
|
{
|
||||||
|
int first = 0;
|
||||||
|
int last = CONFIG_OPTIONS_SIZE;
|
||||||
|
while (first <= last) {
|
||||||
|
int mid = (first + last) / 2; // compute mid point.
|
||||||
|
|
||||||
|
int result;
|
||||||
|
if ((result = (int)name[0] - (int)array[mid].long_opt[0]) == 0)
|
||||||
|
result = std::strcmp(name, array[mid].long_opt);
|
||||||
|
|
||||||
|
if (result > 0)
|
||||||
|
first = mid + 1; // repeat search in top half.
|
||||||
|
else if (result < 0)
|
||||||
|
last = mid - 1; // repeat search in bottom half.
|
||||||
|
else
|
||||||
|
return &array[mid];
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
option_t * search_options(option_t * array, const char letter)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < CONFIG_OPTIONS_SIZE; i++)
|
||||||
|
if (letter == array[i].short_opt)
|
||||||
|
return &array[i];
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool process_option(std::list<option_t>& options,
|
bool process_option(option_t * options, const std::string& name,
|
||||||
const std::string& opt, const char * arg)
|
const char * arg)
|
||||||
{
|
{
|
||||||
for (std::list<option_t>::iterator i = options.begin();
|
option_t * opt = search_options(options, name.c_str());
|
||||||
i != options.end();
|
if (opt != NULL && ! opt->handled) {
|
||||||
i++)
|
opt->handler(arg);
|
||||||
if ((*i).long_opt == opt) {
|
opt->handled = true;
|
||||||
if (! (*i).handler->handled) {
|
return true;
|
||||||
(*(*i).handler)(arg);
|
}
|
||||||
(*i).handler->handled = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void process_arguments(std::list<option_t>& options,
|
void process_arguments(option_t * options, int argc, char ** argv,
|
||||||
int argc, char ** argv, const bool anywhere,
|
const bool anywhere, std::list<std::string>& args)
|
||||||
std::list<std::string>& args)
|
|
||||||
{
|
{
|
||||||
int index = 0;
|
int index = 0;
|
||||||
for (char ** i = argv; index < argc; i++, index++) {
|
for (char ** i = argv; *i; i++) {
|
||||||
if ((*i)[0] != '-') {
|
if ((*i)[0] != '-') {
|
||||||
if (anywhere) {
|
if (anywhere) {
|
||||||
args.push_back(*i);
|
args.push_back(*i);
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
for (; index < argc; i++, index++)
|
for (; *i; i++)
|
||||||
args.push_back(*i);
|
args.push_back(*i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --long-option
|
// --long-option or -s
|
||||||
again:
|
again:
|
||||||
|
option_t * opt = NULL;
|
||||||
|
char * value = NULL;
|
||||||
|
|
||||||
if ((*i)[1] == '-') {
|
if ((*i)[1] == '-') {
|
||||||
if ((*i)[2] == '\0')
|
if ((*i)[2] == '\0')
|
||||||
break;
|
break;
|
||||||
|
|
||||||
for (std::list<option_t>::iterator j = options.begin();
|
char * name = *i + 2;
|
||||||
j != options.end();
|
if (char * p = std::strchr(name, '=')) {
|
||||||
j++)
|
*p++ = '\0';
|
||||||
if ((*j).wants_arg) {
|
value = p;
|
||||||
if (const char * p = std::strchr(*i + 2, '=')) {
|
}
|
||||||
if ((*j).long_opt == std::string(*i + 2, p - (*i + 2))) {
|
|
||||||
process_option(*j, p + 1);
|
|
||||||
goto next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ((*j).long_opt == *i + 2) {
|
|
||||||
if (++index >= argc)
|
|
||||||
throw option_error(std::string("missing option argument for ") +
|
|
||||||
*i);
|
|
||||||
process_option(*j, argv[index]);
|
|
||||||
i++;
|
|
||||||
goto next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ((*j).long_opt == *i + 2) {
|
|
||||||
process_option(*j);
|
|
||||||
goto next;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw option_error(std::string("illegal option ") + *i);
|
opt = search_options(options, name);
|
||||||
|
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 {
|
} else {
|
||||||
for (std::list<option_t>::iterator j = options.begin();
|
char c = (*i)[1];
|
||||||
j != options.end();
|
opt = search_options(options, c);
|
||||||
j++)
|
if (opt == NULL)
|
||||||
if ((*i)[1] == (*j).short_opt) {
|
throw option_error(std::string("illegal option -") + c);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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:
|
next:
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void process_environment(std::list<option_t>& options,
|
void process_environment(option_t * options, char ** envp,
|
||||||
char ** envp, const std::string& tag)
|
const std::string& tag)
|
||||||
{
|
{
|
||||||
const char * tag_p = tag.c_str();
|
const char * tag_p = tag.c_str();
|
||||||
int tag_len = tag.length();
|
unsigned int tag_len = tag.length();
|
||||||
|
|
||||||
for (char ** p = envp; *p; p++)
|
for (char ** p = envp; *p; p++)
|
||||||
if (! tag_p || std::strncmp(*p, tag_p, tag_len) == 0) {
|
if (! tag_p || std::strncmp(*p, tag_p, tag_len) == 0) {
|
||||||
|
|
|
||||||
33
option.h
33
option.h
|
|
@ -5,20 +5,14 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
|
||||||
struct option_handler {
|
typedef void (*handler_t)(const char * arg);
|
||||||
bool handled;
|
|
||||||
option_handler() : handled(false) {}
|
|
||||||
virtual ~option_handler() {}
|
|
||||||
virtual void operator()(const char * arg = NULL) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct option_t {
|
struct option_t {
|
||||||
char short_opt;
|
const char * long_opt;
|
||||||
std::string long_opt;
|
char short_opt;
|
||||||
bool wants_arg;
|
bool wants_arg;
|
||||||
option_handler * handler;
|
handler_t handler;
|
||||||
|
bool handled;
|
||||||
option_t() : short_opt(0), wants_arg(false), handler(NULL) {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class option_error : public std::exception {
|
class option_error : public std::exception {
|
||||||
|
|
@ -32,14 +26,11 @@ class option_error : public std::exception {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void add_option_handler(std::list<option_t>& options, const std::string& label,
|
bool process_option(option_t * options, const std::string& opt,
|
||||||
const std::string& opt_chars, option_handler& option);
|
const char * arg = NULL);
|
||||||
bool process_option(std::list<option_t>& options,
|
void process_arguments(option_t * options, int argc, char ** argv,
|
||||||
const std::string& opt, const char * arg = NULL);
|
const bool anywhere, std::list<std::string>& args);
|
||||||
void process_arguments(std::list<option_t>& options,
|
void process_environment(option_t * options, char ** envp,
|
||||||
int argc, char ** argv, const bool anywhere,
|
const std::string& tag);
|
||||||
std::list<std::string>& args);
|
|
||||||
void process_environment(std::list<option_t>& options,
|
|
||||||
char ** envp, const std::string& tag);
|
|
||||||
|
|
||||||
#endif // _OPTION_H
|
#endif // _OPTION_H
|
||||||
|
|
|
||||||
18
qif.cc
18
qif.cc
|
|
@ -63,7 +63,6 @@ unsigned int qif_parser_t::parse(std::istream& in,
|
||||||
src_idx = journal->sources.size() - 1;
|
src_idx = journal->sources.size() - 1;
|
||||||
linenum = 1;
|
linenum = 1;
|
||||||
|
|
||||||
#ifdef USE_EDITOR
|
|
||||||
istream_pos_type beg_pos = 0;
|
istream_pos_type beg_pos = 0;
|
||||||
unsigned long beg_line = 0;
|
unsigned long beg_line = 0;
|
||||||
|
|
||||||
|
|
@ -72,9 +71,6 @@ unsigned int qif_parser_t::parse(std::istream& in,
|
||||||
beg_pos = in.tellg(); \
|
beg_pos = in.tellg(); \
|
||||||
beg_line = linenum; \
|
beg_line = linenum; \
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
#define SET_BEG_POS_AND_LINE()
|
|
||||||
#endif
|
|
||||||
|
|
||||||
while (in.good() && ! in.eof()) {
|
while (in.good() && ! in.eof()) {
|
||||||
char c;
|
char c;
|
||||||
|
|
@ -119,16 +115,16 @@ unsigned int qif_parser_t::parse(std::istream& in,
|
||||||
get_line(in);
|
get_line(in);
|
||||||
xact->amount.parse(line);
|
xact->amount.parse(line);
|
||||||
|
|
||||||
unsigned long flags = xact->amount.commodity().flags;
|
unsigned long flags = xact->amount.commodity().flags();
|
||||||
unsigned short prec = xact->amount.commodity().precision;
|
unsigned short prec = xact->amount.commodity().precision();
|
||||||
|
|
||||||
if (! def_commodity)
|
if (! def_commodity)
|
||||||
def_commodity = commodity_t::find_commodity("$", true);
|
def_commodity = commodity_t::find_commodity("$", true);
|
||||||
xact->amount.set_commodity(*def_commodity);
|
xact->amount.set_commodity(*def_commodity);
|
||||||
|
|
||||||
def_commodity->flags |= flags;
|
def_commodity->flags() |= flags;
|
||||||
if (prec > def_commodity->precision)
|
if (prec > def_commodity->precision())
|
||||||
def_commodity->precision = prec;
|
def_commodity->precision() = prec;
|
||||||
|
|
||||||
if (c == '$') {
|
if (c == '$') {
|
||||||
saw_splits = true;
|
saw_splits = true;
|
||||||
|
|
@ -221,13 +217,11 @@ unsigned int qif_parser_t::parse(std::istream& in,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (journal->add_entry(entry.get())) {
|
if (journal->add_entry(entry.get())) {
|
||||||
#ifdef USE_EDITOR
|
|
||||||
entry->src_idx = src_idx;
|
entry->src_idx = src_idx;
|
||||||
entry->beg_pos = beg_pos;
|
entry->beg_pos = beg_pos;
|
||||||
entry->beg_line = beg_line;
|
entry->beg_line = beg_line;
|
||||||
entry->end_pos = in.tellg();
|
entry->end_pos = in.tellg();
|
||||||
entry->end_line = linenum;
|
entry->end_line = linenum;
|
||||||
#endif
|
|
||||||
entry.release();
|
entry.release();
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
@ -240,9 +234,7 @@ unsigned int qif_parser_t::parse(std::istream& in,
|
||||||
saw_splits = false;
|
saw_splits = false;
|
||||||
saw_category = false;
|
saw_category = false;
|
||||||
total = NULL;
|
total = NULL;
|
||||||
#ifdef USE_EDITOR
|
|
||||||
beg_line = 0;
|
beg_line = 0;
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
10
quotes.cc
10
quotes.cc
|
|
@ -22,12 +22,12 @@ void quotes_by_script::operator()(commodity_t& commodity,
|
||||||
DEBUG_PRINT_TIME_(moment);
|
DEBUG_PRINT_TIME_(moment);
|
||||||
DEBUG_PRINT_TIME_(date);
|
DEBUG_PRINT_TIME_(date);
|
||||||
DEBUG_PRINT_TIME_(last);
|
DEBUG_PRINT_TIME_(last);
|
||||||
if (commodity.history)
|
if (commodity.history())
|
||||||
DEBUG_PRINT_TIME_(commodity.history->last_lookup);
|
DEBUG_PRINT_TIME_(commodity.history()->last_lookup);
|
||||||
DEBUG_PRINT_("pricing_leeway is " << pricing_leeway);
|
DEBUG_PRINT_("pricing_leeway is " << pricing_leeway);
|
||||||
|
|
||||||
if ((commodity.history &&
|
if ((commodity.history() &&
|
||||||
std::difftime(now, commodity.history->last_lookup) < pricing_leeway) ||
|
std::difftime(now, commodity.history()->last_lookup) < pricing_leeway) ||
|
||||||
std::difftime(now, last) < pricing_leeway ||
|
std::difftime(now, last) < pricing_leeway ||
|
||||||
(price && std::difftime(moment, date) > 0 &&
|
(price && std::difftime(moment, date) > 0 &&
|
||||||
std::difftime(moment, date) <= pricing_leeway))
|
std::difftime(moment, date) <= pricing_leeway))
|
||||||
|
|
@ -61,7 +61,7 @@ void quotes_by_script::operator()(commodity_t& commodity,
|
||||||
price.parse(buf);
|
price.parse(buf);
|
||||||
commodity.add_price(now, price);
|
commodity.add_price(now, price);
|
||||||
|
|
||||||
commodity.history->last_lookup = now;
|
commodity.history()->last_lookup = now;
|
||||||
cache_dirty = true;
|
cache_dirty = true;
|
||||||
|
|
||||||
if (price && ! price_db.empty()) {
|
if (price && ! price_db.empty()) {
|
||||||
|
|
|
||||||
81
textual.cc
81
textual.cc
|
|
@ -290,13 +290,49 @@ transaction_t * parse_transaction(char * line, account_t * account)
|
||||||
if (amount) {
|
if (amount) {
|
||||||
xact->amount_expr = parse_amount(amount, xact->amount,
|
xact->amount_expr = parse_amount(amount, xact->amount,
|
||||||
AMOUNT_PARSE_NO_REDUCE, *xact);
|
AMOUNT_PARSE_NO_REDUCE, *xact);
|
||||||
|
if (xact->amount_expr)
|
||||||
|
xact->amount_expr->acquire();
|
||||||
if (price) {
|
if (price) {
|
||||||
xact->cost = new amount_t;
|
xact->cost = new amount_t;
|
||||||
xact->cost_expr = parse_amount(price, *xact->cost,
|
xact->cost_expr = parse_amount(price, *xact->cost,
|
||||||
AMOUNT_PARSE_NO_MIGRATE, *xact);
|
AMOUNT_PARSE_NO_MIGRATE, *xact);
|
||||||
|
if (xact->cost_expr)
|
||||||
|
xact->cost_expr->acquire();
|
||||||
if (per_unit) {
|
if (per_unit) {
|
||||||
|
if (xact->amount.commodity().base == NULL) {
|
||||||
|
std::string symbol = xact->amount.commodity().symbol;
|
||||||
|
symbol += " {";
|
||||||
|
symbol += price;
|
||||||
|
symbol += "}";
|
||||||
|
|
||||||
|
commodity_t * priced_commodity =
|
||||||
|
commodity_t::find_commodity(symbol, true);
|
||||||
|
|
||||||
|
priced_commodity->price = new amount_t(*xact->cost);
|
||||||
|
priced_commodity->base = &xact->amount.commodity();
|
||||||
|
|
||||||
|
xact->amount.set_commodity(*priced_commodity);
|
||||||
|
}
|
||||||
|
|
||||||
*xact->cost *= xact->amount;
|
*xact->cost *= xact->amount;
|
||||||
*xact->cost = xact->cost->round(xact->cost->commodity().precision);
|
*xact->cost = xact->cost->round(xact->cost->commodity().precision());
|
||||||
|
}
|
||||||
|
else if (xact->amount.commodity().base == NULL) {
|
||||||
|
amount_t cost(*xact->cost);
|
||||||
|
cost /= xact->amount;
|
||||||
|
cost = cost.round(cost.commodity().precision());
|
||||||
|
|
||||||
|
std::string symbol;
|
||||||
|
std::ostringstream symstr(symbol);
|
||||||
|
symstr << xact->amount.commodity().symbol << " {" << cost << "}";
|
||||||
|
|
||||||
|
commodity_t * priced_commodity =
|
||||||
|
commodity_t::find_commodity(symstr.str(), true);
|
||||||
|
|
||||||
|
priced_commodity->price = new amount_t(cost);
|
||||||
|
priced_commodity->base = &xact->amount.commodity();
|
||||||
|
|
||||||
|
xact->amount.set_commodity(*priced_commodity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
xact->amount.reduce();
|
xact->amount.reduce();
|
||||||
|
|
@ -318,10 +354,9 @@ bool parse_transactions(std::istream& in,
|
||||||
in.getline(line, MAX_LINE);
|
in.getline(line, MAX_LINE);
|
||||||
if (in.eof())
|
if (in.eof())
|
||||||
break;
|
break;
|
||||||
#ifdef USE_EDITOR
|
|
||||||
beg_pos += istream_pos_type(std::strlen(line) + 1);
|
beg_pos += istream_pos_type(std::strlen(line) + 1);
|
||||||
#endif
|
|
||||||
linenum++;
|
linenum++;
|
||||||
|
|
||||||
if (line[0] == ' ' || line[0] == '\t' || line[0] == '\r') {
|
if (line[0] == ' ' || line[0] == '\t' || line[0] == '\r') {
|
||||||
char * p = skip_ws(line);
|
char * p = skip_ws(line);
|
||||||
if (! *p || *p == '\r')
|
if (! *p || *p == '\r')
|
||||||
|
|
@ -403,20 +438,17 @@ entry_t * parse_entry(std::istream& in, char * line, account_t * master,
|
||||||
|
|
||||||
TIMER_START(entry_xacts);
|
TIMER_START(entry_xacts);
|
||||||
|
|
||||||
#ifdef USE_EDITOR
|
|
||||||
istream_pos_type end_pos;
|
istream_pos_type end_pos;
|
||||||
unsigned long beg_line = linenum;
|
unsigned long beg_line = linenum;
|
||||||
#endif
|
|
||||||
while (! in.eof() && (in.peek() == ' ' || in.peek() == '\t')) {
|
while (! in.eof() && (in.peek() == ' ' || in.peek() == '\t')) {
|
||||||
line[0] = '\0';
|
line[0] = '\0';
|
||||||
in.getline(line, MAX_LINE);
|
in.getline(line, MAX_LINE);
|
||||||
if (in.eof() && line[0] == '\0')
|
if (in.eof() && line[0] == '\0')
|
||||||
break;
|
break;
|
||||||
#ifdef USE_EDITOR
|
|
||||||
end_pos = beg_pos + istream_pos_type(std::strlen(line) + 1);
|
end_pos = beg_pos + istream_pos_type(std::strlen(line) + 1);
|
||||||
#endif
|
|
||||||
|
|
||||||
linenum++;
|
linenum++;
|
||||||
|
|
||||||
if (line[0] == ' ' || line[0] == '\t' || line[0] == '\r') {
|
if (line[0] == ' ' || line[0] == '\t' || line[0] == '\r') {
|
||||||
char * p = skip_ws(line);
|
char * p = skip_ws(line);
|
||||||
if (! *p || *p == '\r')
|
if (! *p || *p == '\r')
|
||||||
|
|
@ -547,20 +579,17 @@ unsigned int textual_parser_t::parse(std::istream& in,
|
||||||
src_idx = journal->sources.size() - 1;
|
src_idx = journal->sources.size() - 1;
|
||||||
linenum = 1;
|
linenum = 1;
|
||||||
|
|
||||||
#ifdef USE_EDITOR
|
|
||||||
istream_pos_type beg_pos = in.tellg();
|
istream_pos_type beg_pos = in.tellg();
|
||||||
istream_pos_type end_pos;
|
istream_pos_type end_pos;
|
||||||
unsigned long beg_line = linenum;
|
unsigned long beg_line = linenum;
|
||||||
#endif
|
|
||||||
while (in.good() && ! in.eof()) {
|
while (in.good() && ! in.eof()) {
|
||||||
try {
|
try {
|
||||||
in.getline(line, MAX_LINE);
|
in.getline(line, MAX_LINE);
|
||||||
if (in.eof())
|
if (in.eof())
|
||||||
break;
|
break;
|
||||||
linenum++;
|
|
||||||
#ifdef USE_EDITOR
|
|
||||||
end_pos = beg_pos + istream_pos_type(std::strlen(line) + 1);
|
end_pos = beg_pos + istream_pos_type(std::strlen(line) + 1);
|
||||||
#endif
|
linenum++;
|
||||||
|
|
||||||
switch (line[0]) {
|
switch (line[0]) {
|
||||||
case '\0':
|
case '\0':
|
||||||
|
|
@ -674,7 +703,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
|
||||||
parse_symbol(p, symbol);
|
parse_symbol(p, symbol);
|
||||||
|
|
||||||
commodity_t * commodity = commodity_t::find_commodity(symbol, true);
|
commodity_t * commodity = commodity_t::find_commodity(symbol, true);
|
||||||
commodity->flags |= COMMODITY_STYLE_NOMARKET;
|
commodity->flags() |= COMMODITY_STYLE_NOMARKET;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -732,13 +761,11 @@ unsigned int textual_parser_t::parse(std::istream& in,
|
||||||
if (pe->finalize()) {
|
if (pe->finalize()) {
|
||||||
extend_entry_base(journal, *pe);
|
extend_entry_base(journal, *pe);
|
||||||
journal->period_entries.push_back(pe);
|
journal->period_entries.push_back(pe);
|
||||||
#ifdef USE_EDITOR
|
|
||||||
pe->src_idx = src_idx;
|
pe->src_idx = src_idx;
|
||||||
pe->beg_pos = beg_pos;
|
pe->beg_pos = beg_pos;
|
||||||
pe->beg_line = beg_line;
|
pe->beg_line = beg_line;
|
||||||
pe->end_pos = end_pos;
|
pe->end_pos = end_pos;
|
||||||
pe->end_line = linenum;
|
pe->end_line = linenum;
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
throw parse_error(path, linenum, "Period entry failed to balance");
|
throw parse_error(path, linenum, "Period entry failed to balance");
|
||||||
}
|
}
|
||||||
|
|
@ -746,16 +773,15 @@ unsigned int textual_parser_t::parse(std::istream& in,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case '@':
|
||||||
case '!': { // directive
|
case '!': { // directive
|
||||||
char * p = next_element(line);
|
char * p = next_element(line);
|
||||||
std::string word(line + 1);
|
std::string word(line + 1);
|
||||||
if (word == "include") {
|
if (word == "include") {
|
||||||
push_var<std::string> save_path(path);
|
push_var<std::string> save_path(path);
|
||||||
push_var<unsigned int> save_src_idx(src_idx);
|
push_var<unsigned int> save_src_idx(src_idx);
|
||||||
#ifdef USE_EDITOR
|
|
||||||
push_var<istream_pos_type> save_beg_pos(beg_pos);
|
push_var<istream_pos_type> save_beg_pos(beg_pos);
|
||||||
push_var<istream_pos_type> save_end_pos(end_pos);
|
push_var<istream_pos_type> save_end_pos(end_pos);
|
||||||
#endif
|
|
||||||
push_var<unsigned int> save_linenum(linenum);
|
push_var<unsigned int> save_linenum(linenum);
|
||||||
|
|
||||||
path = p;
|
path = p;
|
||||||
|
|
@ -799,26 +825,25 @@ unsigned int textual_parser_t::parse(std::istream& in,
|
||||||
assert(result.second);
|
assert(result.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (word == "def") {
|
||||||
|
if (! global_scope.get())
|
||||||
|
init_value_expr();
|
||||||
|
value_auto_ptr expr(parse_boolean_expr(p, global_scope.get()));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
unsigned int first_line = linenum;
|
unsigned int first_line = linenum;
|
||||||
#ifdef USE_EDITOR
|
|
||||||
istream_pos_type pos = end_pos;
|
istream_pos_type pos = end_pos;
|
||||||
#else
|
|
||||||
istream_pos_type pos;
|
|
||||||
#endif
|
|
||||||
if (entry_t * entry =
|
if (entry_t * entry =
|
||||||
parse_entry(in, line, account_stack.front(), *this, pos)) {
|
parse_entry(in, line, account_stack.front(), *this, pos)) {
|
||||||
if (journal->add_entry(entry)) {
|
if (journal->add_entry(entry)) {
|
||||||
#ifdef USE_EDITOR
|
|
||||||
entry->src_idx = src_idx;
|
entry->src_idx = src_idx;
|
||||||
entry->beg_pos = beg_pos;
|
entry->beg_pos = beg_pos;
|
||||||
entry->beg_line = beg_line;
|
entry->beg_line = beg_line;
|
||||||
entry->end_pos = end_pos;
|
entry->end_pos = end_pos;
|
||||||
entry->end_line = linenum;
|
entry->end_line = linenum;
|
||||||
#endif
|
|
||||||
count++;
|
count++;
|
||||||
} else {
|
} else {
|
||||||
print_entry(std::cerr, *entry);
|
print_entry(std::cerr, *entry);
|
||||||
|
|
@ -833,9 +858,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
|
||||||
} else {
|
} else {
|
||||||
throw parse_error(path, first_line, "Failed to parse entry");
|
throw parse_error(path, first_line, "Failed to parse entry");
|
||||||
}
|
}
|
||||||
#ifdef USE_EDITOR
|
|
||||||
end_pos = pos;
|
end_pos = pos;
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -854,9 +877,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
|
||||||
<< err.what() << std::endl;;
|
<< err.what() << std::endl;;
|
||||||
errors++;
|
errors++;
|
||||||
}
|
}
|
||||||
#ifdef USE_EDITOR
|
|
||||||
beg_pos = end_pos;
|
beg_pos = end_pos;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
|
|
@ -876,8 +897,6 @@ unsigned int textual_parser_t::parse(std::istream& in,
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_EDITOR
|
|
||||||
|
|
||||||
void write_textual_journal(journal_t& journal, std::string path,
|
void write_textual_journal(journal_t& journal, std::string path,
|
||||||
item_handler<transaction_t>& formatter,
|
item_handler<transaction_t>& formatter,
|
||||||
const std::string& write_hdr_format,
|
const std::string& write_hdr_format,
|
||||||
|
|
@ -964,6 +983,4 @@ void write_textual_journal(journal_t& journal, std::string path,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // USE_EDITOR
|
|
||||||
|
|
||||||
} // namespace ledger
|
} // namespace ledger
|
||||||
|
|
|
||||||
1164
valexpr.cc
1164
valexpr.cc
File diff suppressed because it is too large
Load diff
292
valexpr.h
292
valexpr.h
|
|
@ -16,6 +16,7 @@ struct details_t
|
||||||
const transaction_t * xact;
|
const transaction_t * xact;
|
||||||
const account_t * account;
|
const account_t * account;
|
||||||
|
|
||||||
|
details_t() : entry(NULL), xact(NULL), account(NULL) {}
|
||||||
details_t(const entry_t& _entry)
|
details_t(const entry_t& _entry)
|
||||||
: entry(&_entry), xact(NULL), account(NULL) {
|
: entry(&_entry), xact(NULL), account(NULL) {
|
||||||
DEBUG_PRINT("ledger.memory.ctors", "ctor details_t");
|
DEBUG_PRINT("ledger.memory.ctors", "ctor details_t");
|
||||||
|
|
@ -32,6 +33,29 @@ struct details_t
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef void (*value_func_t)(value_t& result, const details_t& details,
|
||||||
|
value_expr_t * context);
|
||||||
|
|
||||||
|
class value_calc
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~value_calc() {}
|
||||||
|
virtual void compute(value_t& result, const details_t& details,
|
||||||
|
value_expr_t * context = NULL) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class value_func : public value_calc
|
||||||
|
{
|
||||||
|
value_func_t func;
|
||||||
|
public:
|
||||||
|
value_func(value_func_t _func) : func(_func) {}
|
||||||
|
|
||||||
|
virtual void compute(value_t& result, const details_t& details,
|
||||||
|
value_expr_t * context = NULL) {
|
||||||
|
func(result, details, context);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct value_expr_t
|
struct value_expr_t
|
||||||
{
|
{
|
||||||
enum kind_t {
|
enum kind_t {
|
||||||
|
|
@ -39,10 +63,14 @@ struct value_expr_t
|
||||||
CONSTANT_I,
|
CONSTANT_I,
|
||||||
CONSTANT_T,
|
CONSTANT_T,
|
||||||
CONSTANT_A,
|
CONSTANT_A,
|
||||||
|
CONSTANT_V,
|
||||||
|
|
||||||
|
CONSTANTS,
|
||||||
|
|
||||||
// Item details
|
// Item details
|
||||||
AMOUNT,
|
AMOUNT,
|
||||||
COST,
|
COST,
|
||||||
|
PRICE,
|
||||||
DATE,
|
DATE,
|
||||||
CLEARED,
|
CLEARED,
|
||||||
PENDING,
|
PENDING,
|
||||||
|
|
@ -55,6 +83,7 @@ struct value_expr_t
|
||||||
COUNT,
|
COUNT,
|
||||||
TOTAL,
|
TOTAL,
|
||||||
COST_TOTAL,
|
COST_TOTAL,
|
||||||
|
PRICE_TOTAL,
|
||||||
|
|
||||||
// Relating to format_t
|
// Relating to format_t
|
||||||
VALUE_EXPR,
|
VALUE_EXPR,
|
||||||
|
|
@ -62,13 +91,12 @@ struct value_expr_t
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
F_NOW,
|
F_NOW,
|
||||||
F_PARENT,
|
|
||||||
F_ARITH_MEAN,
|
F_ARITH_MEAN,
|
||||||
|
F_QUANTITY,
|
||||||
|
F_COMMODITY,
|
||||||
|
F_SET_COMMODITY,
|
||||||
F_VALUE,
|
F_VALUE,
|
||||||
F_FUNC,
|
|
||||||
F_NEG,
|
|
||||||
F_ABS,
|
F_ABS,
|
||||||
F_STRIP,
|
|
||||||
F_CODE_MASK,
|
F_CODE_MASK,
|
||||||
F_PAYEE_MASK,
|
F_PAYEE_MASK,
|
||||||
F_NOTE_MASK,
|
F_NOTE_MASK,
|
||||||
|
|
@ -76,11 +104,18 @@ struct value_expr_t
|
||||||
F_SHORT_ACCOUNT_MASK,
|
F_SHORT_ACCOUNT_MASK,
|
||||||
F_COMMODITY_MASK,
|
F_COMMODITY_MASK,
|
||||||
|
|
||||||
|
TERMINALS,
|
||||||
|
|
||||||
|
F_PARENT,
|
||||||
|
|
||||||
// Binary operators
|
// Binary operators
|
||||||
|
O_NEG,
|
||||||
O_ADD,
|
O_ADD,
|
||||||
O_SUB,
|
O_SUB,
|
||||||
O_MUL,
|
O_MUL,
|
||||||
O_DIV,
|
O_DIV,
|
||||||
|
O_PERC,
|
||||||
|
O_NEQ,
|
||||||
O_EQ,
|
O_EQ,
|
||||||
O_LT,
|
O_LT,
|
||||||
O_LTE,
|
O_LTE,
|
||||||
|
|
@ -91,88 +126,244 @@ struct value_expr_t
|
||||||
O_OR,
|
O_OR,
|
||||||
O_QUES,
|
O_QUES,
|
||||||
O_COL,
|
O_COL,
|
||||||
|
O_COM,
|
||||||
|
O_DEF,
|
||||||
|
O_REF,
|
||||||
O_ARG,
|
O_ARG,
|
||||||
|
|
||||||
LAST
|
LAST
|
||||||
};
|
};
|
||||||
|
|
||||||
kind_t kind;
|
kind_t kind;
|
||||||
|
mutable short refc;
|
||||||
value_expr_t * left;
|
value_expr_t * left;
|
||||||
value_expr_t * right;
|
|
||||||
|
|
||||||
union {
|
union {
|
||||||
std::time_t constant_t;
|
std::time_t constant_t;
|
||||||
long constant_i;
|
long constant_i;
|
||||||
|
amount_t * constant_a;
|
||||||
|
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)
|
value_expr_t(const kind_t _kind)
|
||||||
: kind(_kind), left(NULL), right(NULL), mask(NULL) {
|
: kind(_kind), refc(0), left(NULL), right(NULL) {
|
||||||
DEBUG_PRINT("ledger.memory.ctors", "ctor value_expr_t");
|
DEBUG_PRINT("ledger.memory.ctors", "ctor value_expr_t");
|
||||||
}
|
}
|
||||||
|
~value_expr_t();
|
||||||
|
|
||||||
~value_expr_t() {
|
void release() const {
|
||||||
DEBUG_PRINT("ledger.memory.dtors", "dtor value_expr_t");
|
DEBUG_PRINT("ledger.valexpr.memory",
|
||||||
if (mask) delete mask;
|
"Releasing " << this << ", refc now " << refc - 1);
|
||||||
if (left) delete left;
|
assert(refc > 0);
|
||||||
if (right) delete right;
|
if (--refc == 0)
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
value_expr_t * acquire() {
|
||||||
|
DEBUG_PRINT("ledger.valexpr.memory",
|
||||||
|
"Acquiring " << this << ", refc now " << refc + 1);
|
||||||
|
assert(refc >= 0);
|
||||||
|
refc++;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
const value_expr_t * acquire() const {
|
||||||
|
DEBUG_PRINT("ledger.valexpr.memory",
|
||||||
|
"Acquiring " << this << ", refc now " << refc + 1);
|
||||||
|
refc++;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void compute(value_t& result, const details_t& details) const;
|
void set_left(value_expr_t * expr) {
|
||||||
|
assert(kind > TERMINALS);
|
||||||
|
if (left)
|
||||||
|
left->release();
|
||||||
|
left = expr ? expr->acquire() : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_right(value_expr_t * expr) {
|
||||||
|
assert(kind > TERMINALS);
|
||||||
|
if (right)
|
||||||
|
right->release();
|
||||||
|
right = expr ? expr->acquire() : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void compute(value_t& result, const details_t& details,
|
||||||
|
value_expr_t * context = NULL) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern std::auto_ptr<value_expr_t> amount_expr;
|
struct scope_t
|
||||||
extern std::auto_ptr<value_expr_t> total_expr;
|
{
|
||||||
|
scope_t * parent;
|
||||||
|
|
||||||
|
typedef std::map<const std::string, value_expr_t *> symbol_map;
|
||||||
|
typedef std::pair<const std::string, value_expr_t *> symbol_pair;
|
||||||
|
|
||||||
|
symbol_map symbols;
|
||||||
|
|
||||||
|
scope_t(scope_t * _parent = NULL) : parent(_parent) {}
|
||||||
|
~scope_t() {
|
||||||
|
for (symbol_map::iterator i = symbols.begin();
|
||||||
|
i != symbols.end();
|
||||||
|
i++)
|
||||||
|
(*i).second->release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void define(const std::string& name, value_expr_t * def) {
|
||||||
|
DEBUG_PRINT("ledger.valexpr.syms",
|
||||||
|
"Defining '" << name << "' = " << def);
|
||||||
|
std::pair<symbol_map::iterator, bool> result
|
||||||
|
= symbols.insert(symbol_pair(name, def->acquire()));
|
||||||
|
if (! result.second)
|
||||||
|
throw value_expr_error(std::string("Redefinition of '") +
|
||||||
|
name + "' in same scope");
|
||||||
|
}
|
||||||
|
value_expr_t * lookup(const std::string& name) {
|
||||||
|
symbol_map::const_iterator i = symbols.find(name);
|
||||||
|
if (i != symbols.end())
|
||||||
|
return (*i).second;
|
||||||
|
else if (parent)
|
||||||
|
return parent->lookup(name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
extern std::auto_ptr<scope_t> global_scope;
|
||||||
|
|
||||||
extern std::time_t terminus;
|
extern std::time_t terminus;
|
||||||
|
extern bool initialized;
|
||||||
|
|
||||||
|
void init_value_expr();
|
||||||
|
|
||||||
bool compute_amount(value_expr_t * expr, amount_t& amt, transaction_t& xact);
|
bool compute_amount(value_expr_t * expr, amount_t& amt, transaction_t& xact);
|
||||||
|
|
||||||
|
struct scope_t;
|
||||||
|
value_expr_t * parse_boolean_expr(std::istream& in, scope_t * scope);
|
||||||
|
|
||||||
|
inline value_expr_t * parse_boolean_expr(const char * p,
|
||||||
|
scope_t * scope = NULL) {
|
||||||
|
std::istringstream stream(p);
|
||||||
|
return parse_boolean_expr(stream, scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline value_expr_t * parse_boolean_expr(const std::string& str,
|
||||||
|
scope_t * scope = NULL) {
|
||||||
|
return parse_boolean_expr(str.c_str(), scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
value_expr_t * parse_value_expr(std::istream& in,
|
||||||
|
scope_t * scope = NULL,
|
||||||
|
const bool partial = false);
|
||||||
|
|
||||||
|
inline value_expr_t * parse_value_expr(const char * p,
|
||||||
|
scope_t * scope = NULL,
|
||||||
|
const bool partial = false) {
|
||||||
|
std::istringstream stream(p);
|
||||||
|
return parse_value_expr(stream, scope, partial);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline value_expr_t * parse_value_expr(const std::string& str,
|
||||||
|
scope_t * scope = NULL,
|
||||||
|
const bool partial = false) {
|
||||||
|
return parse_value_expr(str.c_str(), scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_value_expr(std::ostream& out, const value_expr_t * node,
|
||||||
|
const int depth = 0);
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// This class is used so that during the "in between" stages of value
|
||||||
|
// expression parsing -- while no one yet holds a reference to the
|
||||||
|
// value_expr_t object -- we can be assured of deletion should an
|
||||||
|
// exception happen to whip by.
|
||||||
|
|
||||||
|
struct value_auto_ptr {
|
||||||
|
value_expr_t * ptr;
|
||||||
|
value_auto_ptr() : ptr(NULL) {}
|
||||||
|
explicit value_auto_ptr(value_expr_t * _ptr) : ptr(_ptr) {}
|
||||||
|
~value_auto_ptr() {
|
||||||
|
if (ptr && ptr->refc == 0)
|
||||||
|
delete ptr;
|
||||||
|
}
|
||||||
|
value_expr_t& operator*() const throw() {
|
||||||
|
return *ptr;
|
||||||
|
}
|
||||||
|
value_expr_t * operator->() const throw() {
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
value_expr_t * get() const throw() { return ptr; }
|
||||||
|
value_expr_t * release() throw() {
|
||||||
|
value_expr_t * tmp = ptr;
|
||||||
|
ptr = 0;
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
void reset(value_expr_t * p = 0) throw() {
|
||||||
|
if (p != ptr) {
|
||||||
|
if (ptr && ptr->refc == 0)
|
||||||
|
delete ptr;
|
||||||
|
ptr = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class value_expr : public value_calc
|
||||||
|
{
|
||||||
|
std::string expr;
|
||||||
|
value_expr_t * parsed;
|
||||||
|
|
||||||
|
public:
|
||||||
|
value_expr(const std::string& _expr) : expr(_expr) {
|
||||||
|
try {
|
||||||
|
parsed = parse_value_expr(expr);
|
||||||
|
parsed->acquire();
|
||||||
|
}
|
||||||
|
catch (const value_expr_error& err) {
|
||||||
|
throw error(std::string("In value expression '") +
|
||||||
|
expr + "': " + err.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value_expr(value_expr_t * _parsed) : parsed(_parsed->acquire()) {}
|
||||||
|
|
||||||
|
virtual ~value_expr() {
|
||||||
|
if (parsed != NULL)
|
||||||
|
parsed->release();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void compute(value_t& result, const details_t& details,
|
||||||
|
value_expr_t * context = NULL) {
|
||||||
|
parsed->compute(result, details, context);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
extern std::auto_ptr<value_calc> amount_expr;
|
||||||
|
extern std::auto_ptr<value_calc> total_expr;
|
||||||
|
|
||||||
inline void compute_amount(value_t& result, const details_t& details) {
|
inline void compute_amount(value_t& result, const details_t& details) {
|
||||||
if (amount_expr.get())
|
if (amount_expr.get() != NULL)
|
||||||
amount_expr->compute(result, details);
|
amount_expr->compute(result, details);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void compute_total(value_t& result, const details_t& details) {
|
inline void compute_total(value_t& result, const details_t& details) {
|
||||||
if (total_expr.get())
|
if (total_expr.get() != NULL)
|
||||||
total_expr->compute(result, details);
|
total_expr->compute(result, details);
|
||||||
}
|
}
|
||||||
|
|
||||||
value_expr_t * parse_value_expr(std::istream& in,
|
|
||||||
const bool partial = false);
|
|
||||||
|
|
||||||
inline value_expr_t * parse_value_expr(const char * p,
|
|
||||||
const bool partial = false) {
|
|
||||||
std::istringstream stream(p);
|
|
||||||
return parse_value_expr(stream, partial);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline value_expr_t * parse_value_expr(const std::string& str,
|
|
||||||
const bool partial = false) {
|
|
||||||
return parse_value_expr(str.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG_ENABLED
|
|
||||||
void dump_value_expr(std::ostream& out, const value_expr_t * node);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class item_predicate
|
class item_predicate
|
||||||
{
|
{
|
||||||
const value_expr_t * predicate;
|
|
||||||
bool allocated;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
item_predicate(const std::string& _predicate)
|
const value_expr_t * predicate;
|
||||||
: predicate(NULL), allocated(false) {
|
|
||||||
|
item_predicate(const std::string& _predicate) : predicate(NULL) {
|
||||||
DEBUG_PRINT("ledger.memory.ctors", "ctor item_predicate<T>");
|
DEBUG_PRINT("ledger.memory.ctors", "ctor item_predicate<T>");
|
||||||
if (! _predicate.empty()) {
|
if (! _predicate.empty()) {
|
||||||
try {
|
try {
|
||||||
predicate = parse_value_expr(_predicate);
|
predicate = parse_value_expr(_predicate)->acquire();
|
||||||
allocated = true;
|
|
||||||
}
|
}
|
||||||
catch (value_expr_error& err) {
|
catch (value_expr_error& err) {
|
||||||
throw value_expr_error(std::string("In predicate '") +
|
throw value_expr_error(std::string("In predicate '") +
|
||||||
|
|
@ -181,14 +372,14 @@ class item_predicate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
item_predicate(const value_expr_t * _predicate = NULL)
|
item_predicate(const value_expr_t * _predicate = NULL)
|
||||||
: predicate(_predicate), allocated(false) {
|
: predicate(_predicate->acquire()) {
|
||||||
DEBUG_PRINT("ledger.memory.ctors", "ctor item_predicate<T>");
|
DEBUG_PRINT("ledger.memory.ctors", "ctor item_predicate<T>");
|
||||||
}
|
}
|
||||||
|
|
||||||
~item_predicate() {
|
~item_predicate() {
|
||||||
DEBUG_PRINT("ledger.memory.dtors", "dtor item_predicate<T>");
|
DEBUG_PRINT("ledger.memory.dtors", "dtor item_predicate<T>");
|
||||||
if (predicate && allocated)
|
if (predicate)
|
||||||
delete predicate;
|
predicate->release();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator()(const T& item) const {
|
bool operator()(const T& item) const {
|
||||||
|
|
@ -196,9 +387,8 @@ class item_predicate
|
||||||
value_t result;
|
value_t result;
|
||||||
predicate->compute(result, details_t(item));
|
predicate->compute(result, details_t(item));
|
||||||
return result;
|
return result;
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
32
value.cc
32
value.cc
|
|
@ -747,6 +747,38 @@ value_t value_t::cost() const
|
||||||
return value_t();
|
return value_t();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
value_t value_t::factor_price() const
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case BOOLEAN:
|
||||||
|
case INTEGER:
|
||||||
|
return *this;
|
||||||
|
|
||||||
|
case AMOUNT: {
|
||||||
|
commodity_t& comm = ((amount_t *) data)->commodity();
|
||||||
|
if (comm.price != NULL)
|
||||||
|
return value_t(*comm.price * *((amount_t *) data));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
case BALANCE:
|
||||||
|
return ((balance_t *) data)->factor_price();
|
||||||
|
|
||||||
|
case BALANCE_PAIR: {
|
||||||
|
balance_pair_t temp(((balance_pair_t *) data)->quantity.factor_price());
|
||||||
|
if (((balance_pair_t *) data)->cost)
|
||||||
|
temp.cost = new balance_t(((balance_pair_t *) data)->cost);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
assert(0);
|
||||||
|
return value_t();
|
||||||
|
}
|
||||||
|
|
||||||
value_t& value_t::add(const amount_t& amount, const amount_t * cost)
|
value_t& value_t::add(const amount_t& amount, const amount_t * cost)
|
||||||
{
|
{
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
|
|
||||||
3
value.h
3
value.h
|
|
@ -266,6 +266,7 @@ class value_t
|
||||||
void abs();
|
void abs();
|
||||||
void cast(type_t cast_type);
|
void cast(type_t cast_type);
|
||||||
value_t cost() const;
|
value_t cost() const;
|
||||||
|
value_t factor_price() const;
|
||||||
value_t& add(const amount_t& amount, const amount_t * cost = NULL);
|
value_t& add(const amount_t& amount, const amount_t * cost = NULL);
|
||||||
|
|
||||||
value_t value(const std::time_t moment) const {
|
value_t value(const std::time_t moment) const {
|
||||||
|
|
@ -290,7 +291,7 @@ class value_t
|
||||||
case AMOUNT: {
|
case AMOUNT: {
|
||||||
amount_t& amount = *((amount_t *) data);
|
amount_t& amount = *((amount_t *) data);
|
||||||
if (amount.commodity())
|
if (amount.commodity())
|
||||||
amount = amount.round(amount.commodity().precision);
|
amount = amount.round(amount.commodity().precision());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case BALANCE:
|
case BALANCE:
|
||||||
|
|
|
||||||
30
walk.cc
30
walk.cc
|
|
@ -7,6 +7,8 @@
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
|
bool show_lots = false;
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
bool compare_items<transaction_t>::operator()(const transaction_t * left,
|
bool compare_items<transaction_t>::operator()(const transaction_t * left,
|
||||||
const transaction_t * right)
|
const transaction_t * right)
|
||||||
|
|
@ -39,12 +41,20 @@ transaction_xdata_t& transaction_xdata(const transaction_t& xact)
|
||||||
void add_transaction_to(const transaction_t& xact, value_t& value)
|
void add_transaction_to(const transaction_t& xact, value_t& value)
|
||||||
{
|
{
|
||||||
if (transaction_has_xdata(xact) &&
|
if (transaction_has_xdata(xact) &&
|
||||||
transaction_xdata_(xact).dflags & TRANSACTION_COMPOSITE)
|
transaction_xdata_(xact).dflags & TRANSACTION_COMPOSITE) {
|
||||||
value += transaction_xdata_(xact).composite_amount;
|
value += transaction_xdata_(xact).composite_amount;
|
||||||
else if (xact.cost || value)
|
}
|
||||||
value.add(xact.amount, xact.cost);
|
else if (xact.cost || value) {
|
||||||
else
|
amount_t * cost = xact.cost;
|
||||||
value = xact.amount;
|
if (cost && cost->commodity().price)
|
||||||
|
cost = new amount_t(cost->base_amount());
|
||||||
|
value.add(translate_amount(xact.amount), cost);
|
||||||
|
if (cost != xact.cost)
|
||||||
|
delete cost;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
value = translate_amount(xact.amount);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void truncate_entries::flush()
|
void truncate_entries::flush()
|
||||||
|
|
@ -790,7 +800,7 @@ void walk_accounts(account_t& account,
|
||||||
const std::string& sort_string)
|
const std::string& sort_string)
|
||||||
{
|
{
|
||||||
if (! sort_string.empty()) {
|
if (! sort_string.empty()) {
|
||||||
std::auto_ptr<value_expr_t> sort_order;
|
value_auto_ptr sort_order;
|
||||||
try {
|
try {
|
||||||
sort_order.reset(parse_value_expr(sort_string));
|
sort_order.reset(parse_value_expr(sort_string));
|
||||||
}
|
}
|
||||||
|
|
@ -814,15 +824,15 @@ void walk_commodities(commodities_map& commodities,
|
||||||
for (commodities_map::iterator i = commodities.begin();
|
for (commodities_map::iterator i = commodities.begin();
|
||||||
i != commodities.end();
|
i != commodities.end();
|
||||||
i++) {
|
i++) {
|
||||||
if ((*i).second->flags & COMMODITY_STYLE_NOMARKET)
|
if ((*i).second->flags() & COMMODITY_STYLE_NOMARKET)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
entry_temps.push_back(entry_t());
|
entry_temps.push_back(entry_t());
|
||||||
acct_temps.push_back(account_t(NULL, (*i).second->symbol));
|
acct_temps.push_back(account_t(NULL, (*i).second->symbol));
|
||||||
|
|
||||||
if ((*i).second->history)
|
if ((*i).second->history())
|
||||||
for (history_map::iterator j = (*i).second->history->prices.begin();
|
for (history_map::iterator j = (*i).second->history()->prices.begin();
|
||||||
j != (*i).second->history->prices.end();
|
j != (*i).second->history()->prices.end();
|
||||||
j++) {
|
j++) {
|
||||||
entry_temps.back()._date = (*j).first;
|
entry_temps.back()._date = (*j).first;
|
||||||
|
|
||||||
|
|
|
||||||
15
walk.h
15
walk.h
|
|
@ -120,6 +120,11 @@ inline const account_t * xact_account(const transaction_t& xact) {
|
||||||
return xact_account(const_cast<transaction_t&>(xact));
|
return xact_account(const_cast<transaction_t&>(xact));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern bool show_lots;
|
||||||
|
|
||||||
|
#define translate_amount(amt) \
|
||||||
|
((! show_lots && amt.commodity().price) ? amt.base_amount() : amt)
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
inline void walk_transactions(transactions_list::iterator begin,
|
inline void walk_transactions(transactions_list::iterator begin,
|
||||||
|
|
@ -212,20 +217,19 @@ class sort_transactions : public item_handler<transaction_t>
|
||||||
|
|
||||||
transactions_deque transactions;
|
transactions_deque transactions;
|
||||||
const value_expr_t * sort_order;
|
const value_expr_t * sort_order;
|
||||||
bool allocated;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
sort_transactions(item_handler<transaction_t> * handler,
|
sort_transactions(item_handler<transaction_t> * handler,
|
||||||
const value_expr_t * _sort_order)
|
const value_expr_t * _sort_order)
|
||||||
: item_handler<transaction_t>(handler),
|
: item_handler<transaction_t>(handler),
|
||||||
sort_order(_sort_order), allocated(false) {}
|
sort_order(_sort_order) {}
|
||||||
|
|
||||||
sort_transactions(item_handler<transaction_t> * handler,
|
sort_transactions(item_handler<transaction_t> * handler,
|
||||||
const std::string& _sort_order)
|
const std::string& _sort_order)
|
||||||
: item_handler<transaction_t>(handler), allocated(false) {
|
: item_handler<transaction_t>(handler) {
|
||||||
try {
|
try {
|
||||||
sort_order = parse_value_expr(_sort_order);
|
sort_order = parse_value_expr(_sort_order);
|
||||||
allocated = true;
|
sort_order->acquire();
|
||||||
}
|
}
|
||||||
catch (value_expr_error& err) {
|
catch (value_expr_error& err) {
|
||||||
throw value_expr_error(std::string("In sort string '") + _sort_order +
|
throw value_expr_error(std::string("In sort string '") + _sort_order +
|
||||||
|
|
@ -235,8 +239,7 @@ class sort_transactions : public item_handler<transaction_t>
|
||||||
|
|
||||||
virtual ~sort_transactions() {
|
virtual ~sort_transactions() {
|
||||||
assert(sort_order);
|
assert(sort_order);
|
||||||
if (allocated)
|
sort_order->release();
|
||||||
delete sort_order;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void post_accumulated_xacts();
|
virtual void post_accumulated_xacts();
|
||||||
|
|
|
||||||
57
xml.cc
57
xml.cc
|
|
@ -115,18 +115,32 @@ static void endElement(void *userData, const char *name)
|
||||||
else if (std::strcmp(name, "tr:generated") == 0) {
|
else if (std::strcmp(name, "tr:generated") == 0) {
|
||||||
curr_entry->transactions.back()->flags |= TRANSACTION_AUTO;
|
curr_entry->transactions.back()->flags |= TRANSACTION_AUTO;
|
||||||
}
|
}
|
||||||
else if (std::strcmp(name, "commodity") == 0) {
|
else if (std::strcmp(name, "symbol") == 0) {
|
||||||
assert(! curr_comm);
|
assert(! curr_comm);
|
||||||
curr_comm = commodity_t::find_commodity(data, true);
|
curr_comm = commodity_t::find_commodity(data, true);
|
||||||
curr_comm->flags |= COMMODITY_STYLE_SUFFIXED;
|
curr_comm->flags() |= COMMODITY_STYLE_SUFFIXED;
|
||||||
if (! comm_flags.empty())
|
if (! comm_flags.empty()) {
|
||||||
for (std::string::size_type i = 0, l = comm_flags.length(); i < l; i++)
|
for (std::string::size_type i = 0, l = comm_flags.length(); i < l; i++) {
|
||||||
switch (comm_flags[i]) {
|
switch (comm_flags[i]) {
|
||||||
case 'P': curr_comm->flags &= ~COMMODITY_STYLE_SUFFIXED; break;
|
case 'P': curr_comm->flags() &= ~COMMODITY_STYLE_SUFFIXED; break;
|
||||||
case 'S': curr_comm->flags |= COMMODITY_STYLE_SEPARATED; break;
|
case 'S': curr_comm->flags() |= COMMODITY_STYLE_SEPARATED; break;
|
||||||
case 'T': curr_comm->flags |= COMMODITY_STYLE_THOUSANDS; break;
|
case 'T': curr_comm->flags() |= COMMODITY_STYLE_THOUSANDS; break;
|
||||||
case 'E': curr_comm->flags |= COMMODITY_STYLE_EUROPEAN; break;
|
case 'E': curr_comm->flags() |= COMMODITY_STYLE_EUROPEAN; break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (std::strcmp(name, "price") == 0) {
|
||||||
|
assert(curr_comm);
|
||||||
|
amount_t * price = new amount_t(data);
|
||||||
|
std::string symbol;
|
||||||
|
std::ostringstream symstr(symbol);
|
||||||
|
symstr << curr_comm->symbol << " {" << *price << "}";
|
||||||
|
commodity_t * priced_comm =
|
||||||
|
commodity_t::find_commodity(symstr.str(), true);
|
||||||
|
priced_comm->price = price;
|
||||||
|
priced_comm->base = curr_comm;
|
||||||
|
curr_comm = priced_comm;
|
||||||
}
|
}
|
||||||
else if (std::strcmp(name, "quantity") == 0) {
|
else if (std::strcmp(name, "quantity") == 0) {
|
||||||
curr_entry->transactions.back()->amount.parse(data);
|
curr_entry->transactions.back()->amount.parse(data);
|
||||||
|
|
@ -134,8 +148,8 @@ static void endElement(void *userData, const char *name)
|
||||||
std::string::size_type i = data.find('.');
|
std::string::size_type i = data.find('.');
|
||||||
if (i != std::string::npos) {
|
if (i != std::string::npos) {
|
||||||
int precision = data.length() - i - 1;
|
int precision = data.length() - i - 1;
|
||||||
if (precision > curr_comm->precision)
|
if (precision > curr_comm->precision())
|
||||||
curr_comm->precision = precision;
|
curr_comm->precision() = precision;
|
||||||
}
|
}
|
||||||
curr_entry->transactions.back()->amount.set_commodity(*curr_comm);
|
curr_entry->transactions.back()->amount.set_commodity(*curr_comm);
|
||||||
curr_comm = NULL;
|
curr_comm = NULL;
|
||||||
|
|
@ -240,11 +254,24 @@ void xml_write_amount(std::ostream& out, const amount_t& amount,
|
||||||
commodity_t& c = amount.commodity();
|
commodity_t& c = amount.commodity();
|
||||||
for (int i = 0; i < depth + 2; i++) out << ' ';
|
for (int i = 0; i < depth + 2; i++) out << ' ';
|
||||||
out << "<commodity flags=\"";
|
out << "<commodity flags=\"";
|
||||||
if (! (c.flags & COMMODITY_STYLE_SUFFIXED)) out << 'P';
|
if (! (c.flags() & COMMODITY_STYLE_SUFFIXED)) out << 'P';
|
||||||
if (c.flags & COMMODITY_STYLE_SEPARATED) out << 'S';
|
if (c.flags() & COMMODITY_STYLE_SEPARATED) out << 'S';
|
||||||
if (c.flags & COMMODITY_STYLE_THOUSANDS) out << 'T';
|
if (c.flags() & COMMODITY_STYLE_THOUSANDS) out << 'T';
|
||||||
if (c.flags & COMMODITY_STYLE_EUROPEAN) out << 'E';
|
if (c.flags() & COMMODITY_STYLE_EUROPEAN) out << 'E';
|
||||||
out << "\">" << c.symbol << "</commodity>\n";
|
out << "\">\n";
|
||||||
|
for (int i = 0; i < depth + 4; i++) out << ' ';
|
||||||
|
if (c.price) {
|
||||||
|
out << "<symbol>" << c.base->symbol << "</symbol>\n";
|
||||||
|
for (int i = 0; i < depth + 4; i++) out << ' ';
|
||||||
|
out << "<price>\n";
|
||||||
|
xml_write_amount(out, *c.price, depth + 6);
|
||||||
|
for (int i = 0; i < depth + 4; i++) out << ' ';
|
||||||
|
out << "</price>\n";
|
||||||
|
} else {
|
||||||
|
out << "<symbol>" << c.symbol << "</symbol>\n";
|
||||||
|
}
|
||||||
|
for (int i = 0; i < depth + 2; i++) out << ' ';
|
||||||
|
out << "</commodity>\n";
|
||||||
|
|
||||||
for (int i = 0; i < depth + 2; i++) out << ' ';
|
for (int i = 0; i < depth + 2; i++) out << ' ';
|
||||||
out << "<quantity>";
|
out << "<quantity>";
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue