summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am13
-rwxr-xr-xacprep2
-rw-r--r--amount.cc590
-rw-r--r--amount.h325
-rw-r--r--balance.cc14
-rw-r--r--balance.h4
-rw-r--r--binary.cc282
-rw-r--r--config.cc11
-rw-r--r--datetime.cc62
-rw-r--r--datetime.h22
-rw-r--r--format.cc6
-rw-r--r--gnucash.cc33
-rw-r--r--journal.cc21
-rw-r--r--journal.h4
-rw-r--r--main.cc2
-rw-r--r--ofx.cc5
-rw-r--r--option.cc14
-rw-r--r--qif.cc10
-rw-r--r--quotes.cc12
-rw-r--r--quotes.h4
-rw-r--r--reconcile.cc2
-rw-r--r--textual.cc92
-rw-r--r--valexpr.cc26
-rw-r--r--valexpr.h6
-rw-r--r--walk.cc14
-rw-r--r--xml.cc24
26 files changed, 1008 insertions, 592 deletions
diff --git a/Makefile.am b/Makefile.am
index f141beb2..2af4a5eb 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,17 +1,23 @@
lib_LTLIBRARIES = libamounts.la libledger.la
+
libamounts_la_CXXFLAGS =
libamounts_la_SOURCES = \
amount.cc \
balance.cc \
+ datetime.cc \
value.cc
if HAVE_BOOST_PYTHON
libamounts_la_CXXFLAGS += -DUSE_BOOST_PYTHON=1
endif
+if DEBUG
+libamounts_la_CXXFLAGS += -DDEBUG_LEVEL=4
+libamounts_la_SOURCES += debug.cc
+endif
+
libledger_la_CXXFLAGS =
libledger_la_SOURCES = \
binary.cc \
config.cc \
- datetime.cc \
derive.cc \
emacs.cc \
format.cc \
@@ -40,7 +46,6 @@ libledger_la_SOURCES += ofx.cc
endif
if DEBUG
libledger_la_CXXFLAGS += -DDEBUG_LEVEL=4
-libledger_la_SOURCES += debug.cc
endif
libledger_la_LDFLAGS = -version-info 2:6
@@ -49,13 +54,13 @@ pkginclude_HEADERS = \
\
amount.h \
balance.h \
+ datetime.h \
value.h \
+ debug.h \
util.h \
\
binary.h \
config.h \
- datetime.h \
- debug.h \
derive.h \
emacs.h \
error.h \
diff --git a/acprep b/acprep
index 631bf197..622f940b 100755
--- a/acprep
+++ b/acprep
@@ -17,7 +17,7 @@ fi
autoconf
INCDIRS="-I/sw/include -I/usr/local/include/boost-1_33 -I/usr/include/httpd/xml"
-#INCDIRS="$INCDIRS -I/sw/include/libofx"
+INCDIRS="$INCDIRS -I/sw/include/libofx"
INCDIRS="$INCDIRS -I/sw/include/python2.4"
INCDIRS="$INCDIRS -Wno-long-double"
LIBDIRS="-L/sw/lib -L/usr/local/lib -L/sw/lib/python2.4/config"
diff --git a/amount.cc b/amount.cc
index 3ffb341e..4c3fb03f 100644
--- a/amount.cc
+++ b/amount.cc
@@ -1,4 +1,5 @@
#include "amount.h"
+#include "datetime.h"
#include "util.h"
#include <list>
@@ -45,10 +46,13 @@ static mpz_t temp;
static mpz_t divisor;
static amount_t::bigint_t true_value;
-commodity_t::updater_t * commodity_t::updater = NULL;
-commodities_map commodity_t::commodities;
-commodity_t * commodity_t::null_commodity;
-commodity_t * commodity_t::default_commodity = NULL;
+base_commodities_map commodity_base_t::commodities;
+
+commodity_base_t::updater_t * commodity_base_t::updater = NULL;
+
+commodities_map commodity_t::commodities;
+commodity_t * commodity_t::null_commodity;
+commodity_t * commodity_t::default_commodity = NULL;
static struct _init_amounts {
_init_amounts() {
@@ -57,23 +61,26 @@ static struct _init_amounts {
mpz_set_ui(true_value.val, 1);
- commodity_t::updater = NULL;
- commodity_t::null_commodity = commodity_t::find_commodity("", true);
+ commodity_base_t::updater = NULL;
+ commodity_t::null_commodity = commodity_t::create("");
commodity_t::default_commodity = NULL;
+ commodity_t::null_commodity->add_flags(COMMODITY_STYLE_NOMARKET |
+ COMMODITY_STYLE_BUILTIN);
+
// Add time commodity conversions, so that timelog's may be parsed
// in terms of seconds, but reported as minutes or hours.
commodity_t * commodity;
- commodity = commodity_t::find_commodity("s", true);
- commodity->flags() |= COMMODITY_STYLE_NOMARKET | COMMODITY_STYLE_BUILTIN;
+ commodity = commodity_t::create("s");
+ commodity->add_flags(COMMODITY_STYLE_NOMARKET | COMMODITY_STYLE_BUILTIN);
parse_conversion("1.0m", "60s");
parse_conversion("1.0h", "60m");
#if 0
- commodity = commodity_t::find_commodity("b", true);
- commodity->flags() |= COMMODITY_STYLE_NOMARKET | COMMODITY_STYLE_BUILTIN;
+ commodity = commodity_t::create("b");
+ commodity->add_flags(COMMODITY_STYLE_NOMARKET | COMMODITY_STYLE_BUILTIN);
parse_conversion("1.00 Kb", "1024 b");
parse_conversion("1.00 Mb", "1024 Kb");
@@ -86,9 +93,9 @@ static struct _init_amounts {
mpz_clear(temp);
mpz_clear(divisor);
- if (commodity_t::updater) {
- delete commodity_t::updater;
- commodity_t::updater = NULL;
+ if (commodity_base_t::updater) {
+ delete commodity_base_t::updater;
+ commodity_base_t::updater = NULL;
}
for (commodities_map::iterator i = commodity_t::commodities.begin();
@@ -603,8 +610,7 @@ std::string amount_t::quantity_string() const
commodity_t& comm(commodity());
unsigned short precision;
- if (comm == *commodity_t::null_commodity ||
- comm.flags() & COMMODITY_STYLE_VARIABLE) {
+ if (! comm || comm.flags() & COMMODITY_STYLE_VARIABLE) {
mpz_ui_pow_ui(divisor, 10, quantity->prec);
mpz_tdiv_qr(quotient, remainder, MPZ(quantity), divisor);
precision = quantity->prec;
@@ -707,11 +713,10 @@ std::ostream& operator<<(std::ostream& _out, const amount_t& amt)
// Ensure the value is rounded to the commodity's precision before
// outputting it. NOTE: `rquotient' is used here as a temp variable!
- commodity_t& comm(base.commodity());
+ commodity_t& comm(base.commodity());
unsigned short precision;
- if (comm == *commodity_t::null_commodity ||
- comm.flags() & COMMODITY_STYLE_VARIABLE) {
+ if (! comm || comm.flags() & COMMODITY_STYLE_VARIABLE) {
mpz_ui_pow_ui(divisor, 10, base.quantity->prec);
mpz_tdiv_qr(quotient, remainder, MPZ(base.quantity), divisor);
precision = base.quantity->prec;
@@ -755,16 +760,8 @@ std::ostream& operator<<(std::ostream& _out, const amount_t& amt)
}
if (! (comm.flags() & COMMODITY_STYLE_SUFFIXED)) {
- if (comm.quote) {
- std::string::size_type idx = comm.symbol.find(" {", 0);
- if (idx != std::string::npos)
- out << "\"" << comm.symbol.substr(0, idx) << "\""
- << comm.symbol.substr(idx);
- else
- out << "\"" << comm.symbol << "\"";
- } else {
- out << comm.symbol;
- }
+ comm.write(out);
+
if (comm.flags() & COMMODITY_STYLE_SEPARATED)
out << " ";
}
@@ -828,22 +825,22 @@ std::ostream& operator<<(std::ostream& _out, const amount_t& amt)
if (comm.flags() & COMMODITY_STYLE_SUFFIXED) {
if (comm.flags() & COMMODITY_STYLE_SEPARATED)
out << " ";
- if (comm.quote) {
- std::string::size_type idx = comm.symbol.find(" {", 0);
- if (idx != std::string::npos)
- out << "\"" << comm.symbol.substr(0, idx) << "\""
- << comm.symbol.substr(idx);
- else
- out << "\"" << comm.symbol << "\"";
- } else {
- out << comm.symbol;
- }
+
+ comm.write(out);
}
mpz_clear(quotient);
mpz_clear(rquotient);
mpz_clear(remainder);
+ // If there are any annotations associated with this commodity,
+ // output them now.
+
+ if (comm.annotated) {
+ annotated_commodity_t& ann(static_cast<annotated_commodity_t&>(comm));
+ ann.write_annotations(out);
+ }
+
// Things are output to a string first, so that if anyone has
// specified a width or fill for _out, it will be applied to the
// entire amount string, and not just the first part.
@@ -862,7 +859,9 @@ void parse_quantity(std::istream& in, std::string& value)
value = buf;
}
-void parse_commodity(std::istream& in, std::string& symbol)
+void parse_commodity(std::istream& in, std::string& name,
+ std::string& symbol, std::string& price,
+ std::string& date, std::string& tag)
{
char buf[256];
char c = peek_next_nonws(in);
@@ -877,7 +876,84 @@ void parse_commodity(std::istream& in, std::string& symbol)
READ_INTO(in, buf, 255, c, ! std::isspace(c) && ! std::isdigit(c) &&
c != '-' && c != '.');
}
- symbol = buf;
+ name = symbol = buf;
+
+ bool added_name = false;
+
+ c = peek_next_nonws(in);
+
+ if (c == '{') {
+ in.get(c);
+ READ_INTO(in, buf, 255, c, c != '}');
+ if (c == '}')
+ in.get(c);
+ else
+ throw amount_error("Commodity price lacks closing brace");
+ price = buf;
+ if (! added_name) {
+ added_name = true;
+ if (commodity_t::needs_quotes(symbol)) {
+ name = "\"";
+ name += symbol;
+ name += "\"";
+ }
+ }
+ name += " {";
+ name += price;
+ name += "}";
+ c = peek_next_nonws(in);
+ }
+
+ if (c == '[') {
+ in.get(c);
+ READ_INTO(in, buf, 255, c, c != ']');
+ if (c == ']')
+ in.get(c);
+ else
+ throw amount_error("Commodity date lacks closing bracket");
+ date = buf;
+ if (! added_name) {
+ added_name = true;
+ if (commodity_t::needs_quotes(symbol)) {
+ name = "\"";
+ name += symbol;
+ name += "\"";
+ }
+ }
+ name += " [";
+ name += date;
+ name += "]";
+ c = peek_next_nonws(in);
+ }
+
+ if (c == '(') {
+ in.get(c);
+ READ_INTO(in, buf, 255, c, c != ')');
+ if (c == ')')
+ in.get(c);
+ else
+ throw amount_error("Commodity tag lacks closing parenthesis");
+ tag = buf;
+ if (! added_name) {
+ added_name = true;
+ if (commodity_t::needs_quotes(symbol)) {
+ name = "\"";
+ name += symbol;
+ name += "\"";
+ }
+ }
+ name += " (";
+ name += tag;
+ name += ")";
+ c = peek_next_nonws(in);
+ }
+
+ DEBUG_PRINT("amounts.commodities", "Parsed commodity: "
+ << "name " << name << " "
+ << "symbol " << symbol << std::endl
+ << " price " << price << " "
+ << " date " << date << " "
+ << " tag " << tag);
}
void amount_t::parse(std::istream& in, unsigned short flags)
@@ -887,9 +963,12 @@ void amount_t::parse(std::istream& in, unsigned short flags)
// [-]NUM[ ]SYM [@ AMOUNT]
// SYM[ ][-]NUM [@ AMOUNT]
+ std::string name;
std::string symbol;
std::string quant;
std::string price;
+ std::string date;
+ std::string tag;
unsigned int comm_flags = COMMODITY_STYLE_DEFAULTS;
bool negative = false;
@@ -908,13 +987,13 @@ void amount_t::parse(std::istream& in, unsigned short flags)
if (std::isspace(n))
comm_flags |= COMMODITY_STYLE_SEPARATED;
- parse_commodity(in, symbol);
+ parse_commodity(in, name, symbol, price, date, tag);
if (! symbol.empty())
comm_flags |= COMMODITY_STYLE_SUFFIXED;
}
} else {
- parse_commodity(in, symbol);
+ parse_commodity(in, name, symbol, price, date, tag);
if (std::isspace(in.peek()))
comm_flags |= COMMODITY_STYLE_SEPARATED;
@@ -930,37 +1009,19 @@ void amount_t::parse(std::istream& in, unsigned short flags)
// Create the commodity if has not already been seen, and update the
// precision if something greater was used for the quantity.
- bool newly_created = (commodity_t::commodities.find(symbol) ==
- commodity_t::commodities.end());
-
- 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);
+ bool newly_created = false;
- priced_commodity->price = new amount_t(buf);
- priced_commodity->base = commodity_;
+ commodity_ = commodity_t::find(name);
+ if (! commodity_) {
+ newly_created = true;
- commodity_ = priced_commodity;
+ if (! price.empty() || ! date.empty() || ! tag.empty()) {
+ commodity_ = annotated_commodity_t::create(symbol, price, date, tag);
+ } else {
+ assert(name == symbol);
+ commodity_ = commodity_t::create(symbol);
+ }
+ assert(commodity_);
}
// Determine the precision of the amount, based on the usage of
@@ -995,9 +1056,9 @@ void amount_t::parse(std::istream& in, unsigned short flags)
// Set the commodity's flags and precision accordingly
if (newly_created || ! (flags & AMOUNT_PARSE_NO_MIGRATE)) {
- commodity().flags() |= comm_flags;
+ commodity().add_flags(comm_flags);
if (quantity->prec > commodity().precision())
- commodity().precision() = quantity->prec;
+ commodity().set_precision(quantity->prec);
}
// Now we have the final number. Remove commas and periods, if
@@ -1054,12 +1115,12 @@ void parse_conversion(const std::string& larger_str,
larger *= smaller;
if (larger.commodity()) {
- larger.commodity().smaller() = new amount_t(smaller);
- larger.commodity().flags() = (smaller.commodity().flags() |
- COMMODITY_STYLE_NOMARKET);
+ larger.commodity().set_smaller(smaller);
+ larger.commodity().add_flags(smaller.commodity().flags() |
+ COMMODITY_STYLE_NOMARKET);
}
if (smaller.commodity())
- smaller.commodity().larger() = new amount_t(larger);
+ smaller.commodity().set_larger(larger);
}
@@ -1177,98 +1238,187 @@ void amount_t::write_quantity(std::ostream& out) const
bool amount_t::valid() const
{
if (quantity) {
-#if 0
- // jww (2006-02-24): It's OK for commodity_ to be null here, it
- // just means to use the null_commodity
- if (! commodity_)
- return false;
-#endif
-
if (quantity->ref == 0)
return false;
}
else if (commodity_) {
return false;
}
-
return true;
}
+void amount_t::annotate_commodity(const amount_t& price,
+ const std::time_t date,
+ const std::string& tag)
+{
+ const commodity_t * this_base;
+ annotated_commodity_t * this_ann = NULL;
+
+ if (commodity().annotated) {
+ this_ann = &static_cast<annotated_commodity_t&>(commodity());
+ this_base = this_ann->base;
+ } else {
+ this_base = &commodity();
+ }
+
+ DEBUG_PRINT("amounts.commodities", "Annotating commodity for amount "
+ << *this << std::endl
+ << " price " << price << " "
+ << " date " << date << " "
+ << " tag " << tag);
+
+ annotated_commodity_t * ann_comm =
+ annotated_commodity_t::find_or_create(*this_base, price,
+ date == 0 && this_ann ?
+ this_ann->date : date,
+ tag.empty() && this_ann ?
+ this_ann->tag : tag);
+ if (ann_comm)
+ set_commodity(*ann_comm);
-void commodity_t::set_symbol(const std::string& sym)
+ DEBUG_PRINT("amounts.commodities", " Annotated amount is " << *this);
+}
+
+void amount_t::reduce_commodity(const bool keep_price,
+ const bool keep_date,
+ const bool keep_tag)
{
- *(const_cast<std::string *>(&symbol)) = sym;
- quote = false;
- for (const char * p = symbol.c_str(); *p; p++)
- if (std::isspace(*p) || std::isdigit(*p) || *p == '-' || *p == '.') {
- if (std::isspace(*p) && *(p + 1) == '{')
- return;
- quote = true;
- return;
- }
+ if (! commodity().annotated)
+ return;
+ if (keep_price && keep_date && keep_tag)
+ return;
+
+ DEBUG_PRINT("amounts.commodities", "Reducing commodity for amount "
+ << *this << std::endl
+ << " keep price " << keep_price << " "
+ << " keep date " << keep_date << " "
+ << " keep tag " << keep_tag);
+
+ annotated_commodity_t&
+ ann_comm(static_cast<annotated_commodity_t&>(commodity()));
+
+ annotated_commodity_t * new_ann_comm =
+ annotated_commodity_t::find_or_create(*ann_comm.base,
+ keep_price ?
+ ann_comm.price : amount_t(),
+ keep_date ? ann_comm.date : 0,
+ keep_tag ? ann_comm.tag : "");
+ assert(new_ann_comm);
+ set_commodity(*new_ann_comm);
+
+ DEBUG_PRINT("amounts.commodities", " Reduced amount is " << *this);
}
-void commodity_t::add_price(const std::time_t date, const amount_t& price)
+
+void commodity_base_t::add_price(const std::time_t date, const amount_t& price)
{
- if (! history())
- history() = new history_t;
+ if (! history)
+ history = new history_t;
- history_map::iterator i = history()->prices.find(date);
- if (i != history()->prices.end()) {
+ history_map::iterator i = history->prices.find(date);
+ if (i != history->prices.end()) {
(*i).second = price;
} else {
std::pair<history_map::iterator, bool> result
- = history()->prices.insert(history_pair(date, price));
+ = history->prices.insert(history_pair(date, price));
assert(result.second);
}
}
-commodity_t * commodity_t::find_commodity(const std::string& symbol,
- bool auto_create)
+commodity_base_t * commodity_base_t::create(const std::string& symbol)
{
- commodities_map::const_iterator i = commodities.find(symbol);
- if (i != commodities.end())
- return (*i).second;
+ commodity_base_t * commodity = new commodity_base_t(symbol);
+
+ DEBUG_PRINT("amounts.commodities", "Creating base commodity " << symbol);
+
+ std::pair<base_commodities_map::iterator, bool> result
+ = commodities.insert(base_commodities_pair(symbol, commodity));
+ assert(result.second);
+
+ return commodity;
+}
+
+bool commodity_t::needs_quotes(const std::string& symbol)
+{
+ for (const char * p = symbol.c_str(); *p; p++)
+ if (std::isspace(*p) || std::isdigit(*p) || *p == '-' || *p == '.')
+ return true;
- if (auto_create) {
- commodity_t * commodity = new commodity_t(symbol);
- add_commodity(commodity);
+ return false;
+}
+
+commodity_t * commodity_t::create(const std::string& symbol)
+{
+ std::auto_ptr<commodity_t> commodity(new commodity_t);
- // Start out the new commodity with the default commodity's flags
- // and precision, if one has been defined.
- if (default_commodity)
- commodity->flags() =
- (default_commodity->flags() & ~(COMMODITY_STYLE_THOUSANDS |
- COMMODITY_STYLE_NOMARKET));
+ commodity->ptr = commodity_base_t::create(symbol);
- return commodity;
+ if (needs_quotes(symbol)) {
+ commodity->qualified_symbol = "\"";
+ commodity->qualified_symbol += symbol;
+ commodity->qualified_symbol += "\"";
+ } else {
+ commodity->qualified_symbol = symbol;
}
+ DEBUG_PRINT("amounts.commodities",
+ "Creating commodity " << commodity->qualified_symbol);
+
+ std::pair<commodities_map::iterator, bool> result
+ = commodities.insert(commodities_pair(symbol, commodity.get()));
+ if (! result.second)
+ return NULL;
+
+ // Start out the new commodity with the default commodity's flags
+ // and precision, if one has been defined.
+ if (default_commodity)
+ commodity->drop_flags(COMMODITY_STYLE_THOUSANDS |
+ COMMODITY_STYLE_NOMARKET);
+
+ return commodity.release();
+}
+
+commodity_t * commodity_t::find_or_create(const std::string& symbol)
+{
+ DEBUG_PRINT("amounts.commodities", "Find-or-create commodity " << symbol);
+
+ commodity_t * commodity = find(symbol);
+ if (! commodity)
+ return create(symbol);
+}
+
+commodity_t * commodity_t::find(const std::string& symbol)
+{
+ DEBUG_PRINT("amounts.commodities", "Find commodity " << symbol);
+
+ commodities_map::const_iterator i = commodities.find(symbol);
+ if (i != commodities.end())
+ return (*i).second;
return NULL;
}
-amount_t commodity_t::value(const std::time_t moment)
+amount_t commodity_base_t::value(const std::time_t moment)
{
std::time_t age = 0;
amount_t price;
- if (history()) {
- assert(history()->prices.size() > 0);
+ if (history) {
+ assert(history->prices.size() > 0);
if (moment == 0) {
- history_map::reverse_iterator r = history()->prices.rbegin();
+ history_map::reverse_iterator r = history->prices.rbegin();
age = (*r).first;
price = (*r).second;
} else {
- history_map::iterator i = history()->prices.lower_bound(moment);
- if (i == history()->prices.end()) {
- history_map::reverse_iterator r = history()->prices.rbegin();
+ history_map::iterator i = history->prices.lower_bound(moment);
+ if (i == history->prices.end()) {
+ history_map::reverse_iterator r = history->prices.rbegin();
age = (*r).first;
price = (*r).second;
} else {
age = (*i).first;
if (std::difftime(moment, age) != 0) {
- if (i != history()->prices.begin()) {
+ if (i != history->prices.begin()) {
--i;
age = (*i).first;
price = (*i).second;
@@ -1284,12 +1434,146 @@ amount_t commodity_t::value(const std::time_t moment)
if (updater)
(*updater)(*this, moment, age,
- (history() && history()->prices.size() > 0 ?
- (*history()->prices.rbegin()).first : 0), price);
+ (history && history->prices.size() > 0 ?
+ (*history->prices.rbegin()).first : 0), price);
return price;
}
+std::string annotated_commodity_t::date_format = "%Y/%m/%d";
+
+void
+annotated_commodity_t::write_annotations(std::ostream& out,
+ const amount_t& price,
+ const std::time_t date,
+ const std::string& tag)
+{
+ if (price)
+ out << " {" << price << '}';
+
+ if (date) {
+ char buf[128];
+ std::strftime(buf, 127, annotated_commodity_t::date_format.c_str(),
+ std::localtime(&date));
+ out << " [" << buf << ']';
+ }
+
+ if (! tag.empty())
+ out << " (" << tag << ')';
+}
+
+std::string
+annotated_commodity_t::make_qualified_name(const commodity_t& comm,
+ const amount_t& price,
+ const std::time_t date,
+ const std::string& tag)
+{
+ std::ostringstream name;
+
+ comm.write(name);
+ write_annotations(name, price, date, tag);
+
+ DEBUG_PRINT("amounts.commodities", "make_qualified_name for "
+ << comm.qualified_symbol << std::endl
+ << " price " << price << " "
+ << " date " << date << " "
+ << " tag " << tag);
+
+ DEBUG_PRINT("amounts.commodities", "qualified_name is " << name.str());
+
+ return name.str();
+}
+
+annotated_commodity_t *
+annotated_commodity_t::create(const commodity_t& comm,
+ const amount_t& price,
+ const std::time_t date,
+ const std::string& tag,
+ const std::string& entry_name)
+{
+ std::auto_ptr<annotated_commodity_t> commodity(new annotated_commodity_t);
+
+ // Set the annotated bits
+ commodity->price = price;
+ commodity->date = date;
+ commodity->tag = tag;
+ commodity->base = &comm;
+ commodity->ptr = comm.ptr;
+
+ commodity->qualified_symbol = comm.symbol();
+
+ std::string mapping_key;
+ if (entry_name.empty())
+ mapping_key = make_qualified_name(comm, price, date, tag);
+ else
+ mapping_key = entry_name;
+
+ DEBUG_PRINT("amounts.commodities", "Creating annotated commodity "
+ << "symbol " << commodity->symbol()
+ << " key " << mapping_key << std::endl
+ << " price " << price << " "
+ << " date " << date << " "
+ << " tag " << tag);
+
+ // Add the fully annotated name to the map, so that this symbol may
+ // quickly be found again.
+ std::pair<commodities_map::iterator, bool> result
+ = commodities.insert(commodities_pair(mapping_key, commodity.get()));
+ if (! result.second)
+ return NULL;
+
+ return commodity.release();
+}
+
+annotated_commodity_t *
+annotated_commodity_t::create(const std::string& symbol,
+ const amount_t& price,
+ const std::time_t date,
+ const std::string& tag)
+{
+ commodity_t * comm = commodity_t::find_or_create(symbol);
+ assert(comm);
+ return create(*comm, price, date, tag);
+}
+
+annotated_commodity_t *
+annotated_commodity_t::create(const std::string& symbol,
+ const std::string& price,
+ const std::string& date,
+ const std::string& tag)
+{
+ commodity_t * comm = commodity_t::find_or_create(symbol);
+ assert(comm);
+
+ amount_t real_price;
+ if (! price.empty())
+ real_price.parse(price);
+
+ std::time_t real_date = 0;
+ if (! date.empty())
+ parse_date(date.c_str(), &real_date);
+
+ return create(*comm, real_price, real_date, tag);
+}
+
+annotated_commodity_t *
+annotated_commodity_t::find_or_create(const commodity_t& comm,
+ const amount_t& price,
+ const std::time_t date,
+ const std::string& tag)
+{
+ std::string name = make_qualified_name(comm, price, date, tag);
+
+ commodity_t * base = commodity_t::find(name);
+ if (base) {
+ assert(base->annotated);
+ return static_cast<annotated_commodity_t *>(base);
+ }
+ base = commodity_t::find_or_create(comm.base_symbol());
+
+ return create(*base, price, date, tag, name);
+}
+
} // namespace ledger
#ifdef USE_BOOST_PYTHON
@@ -1314,12 +1598,12 @@ void py_parse_2(amount_t& amount, const std::string& str) {
amount.parse(str);
}
-struct commodity_updater_wrap : public commodity_t::updater_t
+struct commodity_updater_wrap : public commodity_base_t::updater_t
{
PyObject * self;
commodity_updater_wrap(PyObject * self_) : self(self_) {}
- virtual void operator()(commodity_t& commodity,
+ virtual void operator()(commodity_base_t& commodity,
const std::time_t moment,
const std::time_t date,
const std::time_t last,
@@ -1328,14 +1612,9 @@ struct commodity_updater_wrap : public commodity_t::updater_t
}
};
-commodity_t * py_find_commodity_1(const std::string& symbol)
-{
- return commodity_t::find_commodity(symbol);
-}
-
-commodity_t * py_find_commodity_2(const std::string& symbol, bool auto_create)
+commodity_t * py_find_commodity(const std::string& symbol)
{
- return commodity_t::find_commodity(symbol, auto_create);
+ return commodity_t::find(symbol);
}
#define EXC_TRANSLATOR(type) \
@@ -1410,7 +1689,8 @@ void export_amount()
.def("valid", &amount_t::valid)
;
- class_< commodity_t::updater_t, commodity_updater_wrap, boost::noncopyable >
+ class_< commodity_base_t::updater_t, commodity_updater_wrap,
+ boost::noncopyable >
("Updater")
;
@@ -1422,44 +1702,38 @@ void export_amount()
scope().attr("COMMODITY_STYLE_NOMARKET") = COMMODITY_STYLE_NOMARKET;
scope().attr("COMMODITY_STYLE_VARIABLE") = COMMODITY_STYLE_VARIABLE;
+#if 0
class_< commodity_t > ("Commodity")
- .def(init<std::string, optional<unsigned int, unsigned int> >())
-
- .add_property("symbol", &commodity_t::symbol,
- &commodity_t::set_symbol)
+ .add_property("symbol", &commodity_t::symbol)
- // jww (2006-02-28): Use getters and setters!
- .def_readwrite("name", &commodity_t::name_)
- .def_readwrite("note", &commodity_t::note_)
- .def_readwrite("precision", &commodity_t::precision_)
- .def_readwrite("flags", &commodity_t::flags_)
- .def_readwrite("ident", &commodity_t::ident)
- .def_readwrite("updater", &commodity_t::updater)
+ .add_property("name", &commodity_t::name)
+ .add_property("note", &commodity_t::note)
+ .add_property("precision", &commodity_t::precision)
+ .add_property("flags", &commodity_t::flags)
+#if 0
+ .add_property("updater", &commodity_t::updater)
+#endif
.add_property("smaller",
- make_getter(&commodity_t::smaller_,
+ make_getter(&commodity_t::smaller,
return_value_policy<reference_existing_object>()))
.add_property("larger",
- make_getter(&commodity_t::larger_,
+ make_getter(&commodity_t::larger,
return_value_policy<reference_existing_object>()))
.def(self_ns::str(self))
+ .def("find", py_find_commodity,
+ return_value_policy<reference_existing_object>())
+ .staticmethod("find")
+
.def("add_price", &commodity_t::add_price)
.def("remove_price", &commodity_t::remove_price)
.def("value", &commodity_t::value)
.def("valid", &commodity_t::valid)
;
-
- scope().attr("NullCommodity") = commodity_t::null_commodity;
-
- def("add_commodity", &commodity_t::add_commodity);
- def("remove_commodity", &commodity_t::remove_commodity);
- def("find_commodity", py_find_commodity_1,
- return_value_policy<reference_existing_object>());
- def("find_commodity", py_find_commodity_2,
- return_value_policy<reference_existing_object>());
+#endif
#define EXC_TRANSLATE(type) \
register_exception_translator<type>(&exc_translate_ ## type);
diff --git a/amount.h b/amount.h
index d47aa575..b209adfc 100644
--- a/amount.h
+++ b/amount.h
@@ -10,6 +10,8 @@
#include <cassert>
#include <exception>
+#include "debug.h"
+
namespace ledger {
class commodity_t;
@@ -70,10 +72,15 @@ class amount_t
void set_commodity(commodity_t& comm) {
commodity_ = &comm;
}
+ void annotate_commodity(const amount_t& price,
+ const std::time_t date = 0,
+ const std::string& tag = "");
+ void reduce_commodity(const bool keep_price = false,
+ const bool keep_date = false,
+ const bool keep_tag = false);
void clear_commodity() {
commodity_ = NULL;
}
- amount_t base() const;
amount_t price() const;
bool null() const {
@@ -298,147 +305,264 @@ inline std::istream& operator>>(std::istream& in, amount_t& amt) {
typedef std::map<const std::time_t, amount_t> history_map;
typedef std::pair<const std::time_t, amount_t> history_pair;
-typedef std::map<const std::string, commodity_t *> commodities_map;
-typedef std::pair<const std::string, commodity_t *> commodities_pair;
+class commodity_base_t;
-class commodity_t
+typedef std::map<const std::string, commodity_base_t *> base_commodities_map;
+typedef std::pair<const std::string, commodity_base_t *> base_commodities_pair;
+
+class commodity_base_t
{
public:
+ friend class commodity_t;
+ friend class annotated_commodity_t;
+
+ typedef unsigned long ident_t;
+
+ ident_t ident;
+ std::string name;
+ std::string note;
+ unsigned short precision;
+ unsigned short flags;
+ amount_t * smaller;
+ amount_t * larger;
+
+ commodity_base_t() : precision(0), flags(COMMODITY_STYLE_DEFAULTS),
+ history(NULL), smaller(NULL), larger(NULL) {}
+
+ commodity_base_t(const std::string& _symbol,
+ unsigned int _precision = 0,
+ unsigned int _flags = COMMODITY_STYLE_DEFAULTS)
+ : precision(_precision), flags(_flags), history(NULL),
+ smaller(NULL), larger(NULL), symbol(_symbol) {}
+
+ ~commodity_base_t() {
+ if (history) delete history;
+ if (smaller) delete smaller;
+ if (larger) delete larger;
+ }
+
+ static base_commodities_map commodities;
+ static commodity_base_t * create(const std::string& symbol);
+
+ std::string symbol;
+
+ struct history_t {
+ history_map prices;
+ std::time_t last_lookup;
+ };
+ history_t * history;
+
+ void add_price(const std::time_t date, const amount_t& price);
+ bool remove_price(const std::time_t date) {
+ if (history) {
+ history_map::size_type n = history->prices.erase(date);
+ return n > 0;
+ }
+ return false;
+ }
+
+ amount_t value(const std::time_t moment = std::time(NULL));
+
class updater_t {
public:
virtual ~updater_t() {}
- virtual void operator()(commodity_t& commodity,
+ virtual void operator()(commodity_base_t& commodity,
const std::time_t moment,
const std::time_t date,
const std::time_t last,
amount_t& price) = 0;
};
+ friend class updater_t;
- typedef unsigned long ident_t;
+ static updater_t * updater;
+};
- struct history_t {
- history_map prices;
- std::time_t last_lookup;
- };
+typedef std::map<const std::string, commodity_t *> commodities_map;
+typedef std::pair<const std::string, commodity_t *> commodities_pair;
+
+class commodity_t
+{
+ friend class annotated_commodity_t;
- history_t * history_;
+ public:
+ // This map remembers all commodities that have been defined.
- history_t *& history() {
- return base ? base->history() : history_;
- }
+ static commodities_map commodities;
+ static commodity_t * null_commodity;
+ static commodity_t * default_commodity;
- const std::string symbol;
- bool quote;
- std::string name_;
- std::string note_;
- unsigned short precision_;
- unsigned short flags_;
- ident_t ident;
- amount_t * smaller_;
- amount_t * larger_;
- commodity_t * base; // base commodity for AAPL {$10} is AAPL
- amount_t * price; // its price is therefore $10.00
+ static commodity_t * create(const std::string& symbol);
+ static commodity_t * find(const std::string& name);
+ static commodity_t * find_or_create(const std::string& symbol);
- std::string& name() {
- return base ? base->name() : name_;
- }
- std::string& note() {
- return base ? base->note() : note_;
+ static bool needs_quotes(const std::string& symbol);
+
+ static void make_alias(const std::string& symbol,
+ commodity_t * commodity);
+
+ // These are specific to each commodity reference
+
+ typedef unsigned long ident_t;
+
+ ident_t ident;
+ commodity_base_t * ptr;
+ std::string qualified_symbol;
+ bool annotated;
+
+ public:
+ explicit commodity_t() : ptr(NULL), annotated(false) {}
+
+ operator bool() const {
+ return this != null_commodity;
}
- unsigned short& precision() {
- return base ? base->precision() : precision_;
+ bool operator==(const commodity_t& comm) const {
+ return ptr == comm.ptr;
}
- unsigned short& flags() {
- return base ? base->flags() : flags_;
+ bool operator!=(const commodity_t& comm) const {
+ return ptr != comm.ptr;
}
- amount_t *& smaller() {
- return base ? base->smaller() : smaller_;
+
+ std::string base_symbol() const {
+ return ptr->symbol;
}
- amount_t *& larger() {
- return base ? base->larger() : larger_;
+ std::string symbol() const {
+ return qualified_symbol;
}
- // If set, this global function pointer is called to determine
- // whether prices have been updated in the meanwhile.
-
- static updater_t * updater;
-
- // This map remembers all commodities that have been defined.
+ void write(std::ostream& out) const {
+ out << symbol();
+ }
- static commodities_map commodities;
- static commodity_t * null_commodity;
- static commodity_t * default_commodity;
+ std::string name() const {
+ return ptr->name;
+ }
+ void set_name(const std::string& arg) {
+ ptr->name = arg;
+ }
- static bool add_commodity(commodity_t * commodity,
- const std::string symbol = "") {
- // The argument "symbol" is useful for creating a symbol alias to
- // an underlying commodity type; it is used by the Gnucash parser
- // to link "USD" to "$".
- std::pair<commodities_map::iterator, bool> result
- = commodities.insert(commodities_pair((symbol.empty() ?
- commodity->symbol : symbol),
- commodity));
- return result.second;
+ std::string note() const {
+ return ptr->note;
}
- static bool remove_commodity(commodity_t * commodity) {
- commodities_map::size_type n = commodities.erase(commodity->symbol);
- return n > 0;
+ void set_note(const std::string& arg) {
+ ptr->note = arg;
}
- static commodity_t * find_commodity(const std::string& symbol,
- bool auto_create = false);
- // Now the per-object constructor and methods
+ unsigned short precision() const {
+ return ptr->precision;
+ }
+ void set_precision(unsigned short arg) {
+ ptr->precision = arg;
+ }
- commodity_t(const std::string& _symbol = "",
- unsigned int _precision = 0,
- unsigned int _flags = COMMODITY_STYLE_DEFAULTS)
- : precision_(_precision), flags_(_flags), history_(NULL),
- smaller_(NULL), larger_(NULL), base(NULL), price(NULL) {
- set_symbol(_symbol);
+ unsigned short flags() const {
+ return ptr->flags;
}
- ~commodity_t() {
- if (history_) delete history_;
- if (smaller_) delete smaller_;
- if (larger_) delete larger_;
- if (price) delete price;
+ void set_flags(unsigned short arg) {
+ ptr->flags = arg;
+ }
+ void add_flags(unsigned short arg) {
+ ptr->flags |= arg;
+ }
+ void drop_flags(unsigned short arg) {
+ ptr->flags &= ~arg;
}
- operator bool() const {
- return this != null_commodity;
+ amount_t * smaller() const {
+ return ptr->smaller;
}
- bool operator==(const commodity_t& comm) const {
- return this == &comm;
+ void set_smaller(const amount_t& arg) {
+ if (ptr->smaller)
+ delete ptr->smaller;
+ ptr->smaller = new amount_t(arg);
}
- bool operator!=(const commodity_t& comm) const {
- return this != &comm;
+
+ amount_t * larger() const {
+ return ptr->larger;
+ }
+ void set_larger(const amount_t& arg) {
+ if (ptr->larger)
+ delete ptr->larger;
+ ptr->larger = new amount_t(arg);
}
- void set_symbol(const std::string& sym);
+ commodity_base_t::history_t * history() const {
+ return ptr->history;
+ }
- void add_price(const std::time_t date, const amount_t& price);
+ void add_price(const std::time_t date, const amount_t& price) {
+ return ptr->add_price(date, price);
+ }
bool remove_price(const std::time_t date) {
- if (history_) {
- history_map::size_type n = history_->prices.erase(date);
- return n > 0;
- }
- return false;
+ return ptr->remove_price(date);
+ }
+ amount_t value(const std::time_t moment = std::time(NULL)) const {
+ return ptr->value(moment);
}
-
- amount_t value(const std::time_t moment = std::time(NULL));
bool valid() const {
- if (symbol.empty() && this != null_commodity)
+ if (symbol().empty() && this != null_commodity)
return false;
- if (precision_ > 16)
+ if (precision() > 16)
return false;
return true;
}
};
+class annotated_commodity_t : public commodity_t
+{
+ public:
+ const commodity_t * base;
+
+ amount_t price;
+ std::time_t date;
+ std::string tag;
+
+ static std::string date_format;
+
+ static void write_annotations(std::ostream& out,
+ const amount_t& price,
+ const std::time_t date,
+ const std::string& tag);
+ static
+ std::string make_qualified_name(const commodity_t& comm,
+ const amount_t& price,
+ const std::time_t date,
+ const std::string& tag);
+ static
+ annotated_commodity_t * create(const commodity_t& comm,
+ const amount_t& price,
+ const std::time_t date,
+ const std::string& tag,
+ const std::string& entry_name = "");
+ static
+ annotated_commodity_t * create(const std::string& symbol,
+ const amount_t& price,
+ const std::time_t date,
+ const std::string& tag);
+ static
+ annotated_commodity_t * create(const std::string& symbol,
+ const std::string& price,
+ const std::string& date,
+ const std::string& tag);
+ static
+ annotated_commodity_t * find_or_create(const commodity_t& comm,
+ const amount_t& price,
+ const std::time_t date,
+ const std::string& tag);
+
+ explicit annotated_commodity_t() {
+ annotated = true;
+ }
+
+ void write_annotations(std::ostream& out) const {
+ annotated_commodity_t::write_annotations(out, price, date, tag);
+ }
+};
+
inline std::ostream& operator<<(std::ostream& out, const commodity_t& comm) {
- out << comm.symbol;
+ out << comm.symbol();
return out;
}
@@ -449,21 +573,12 @@ inline commodity_t& amount_t::commodity() const {
return *commodity_;
}
-inline amount_t amount_t::base() const {
- if (commodity_ && commodity_->price) {
- amount_t temp(*this);
- assert(commodity_->base);
- temp.set_commodity(*(commodity_->base));
- return temp;
- } else {
- return *this;
- }
-}
-
inline amount_t amount_t::price() const {
- if (commodity_ && commodity_->price) {
- amount_t temp(*commodity_->price);
+ if (commodity_ && commodity_->annotated) {
+ amount_t temp(((annotated_commodity_t *)commodity_)->price);
temp *= *this;
+ DEBUG_PRINT("amounts.commodities",
+ "Returning price of " << *this << " = " << temp);
return temp;
} else {
return 0L;
diff --git a/balance.cc b/balance.cc
index 0697cb6c..07d4afc8 100644
--- a/balance.cc
+++ b/balance.cc
@@ -48,7 +48,7 @@ balance_t balance_t::price() const
struct compare_amount_commodities {
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();
}
};
@@ -106,8 +106,7 @@ balance_t& balance_t::operator*=(const balance_t& bal)
return *this *= (*bal.amounts.begin()).second;
}
else {
- std::string msg;
- std::ostringstream errmsg(msg);
+ std::ostringstream errmsg;
errmsg << "It makes no sense to multiply two balances: "
<< *this << " * " << bal;
throw amount_error(errmsg.str());
@@ -120,8 +119,7 @@ balance_t& balance_t::operator/=(const balance_t& bal)
return (*this = 0L);
}
else if (! bal) {
- std::string msg;
- std::ostringstream errmsg(msg);
+ std::ostringstream errmsg;
errmsg << "Attempt to divide by zero: " << *this << " / " << bal;
throw amount_error(errmsg.str());
}
@@ -132,8 +130,7 @@ balance_t& balance_t::operator/=(const balance_t& bal)
return (*this = 1L);
}
else {
- std::string msg;
- std::ostringstream errmsg(msg);
+ std::ostringstream errmsg;
errmsg << "It makes no sense to divide two balances: "
<< *this << " / " << bal;
throw amount_error(errmsg.str());
@@ -149,8 +146,7 @@ balance_t::operator amount_t() const
return amount_t();
}
else {
- std::string msg;
- std::ostringstream errmsg(msg);
+ std::ostringstream errmsg;
errmsg << "Cannot convert a balance with "
<< "multiple commodities to an amount: " << *this;
throw amount_error(errmsg.str());
diff --git a/balance.h b/balance.h
index d258e622..766837c9 100644
--- a/balance.h
+++ b/balance.h
@@ -147,7 +147,7 @@ class balance_t
balance_t& operator*=(const amount_t& amt) {
// Multiplying by the null commodity causes all amounts to be
// increased by the same factor.
- if (amt.commodity().symbol.empty()) {
+ if (! amt.commodity()) {
for (amounts_map::iterator i = amounts.begin();
i != amounts.end();
i++)
@@ -172,7 +172,7 @@ class balance_t
balance_t& operator/=(const amount_t& amt) {
// Dividing by the null commodity causes all amounts to be
// increased by the same factor.
- if (amt.commodity().symbol.empty()) {
+ if (! amt.commodity()) {
for (amounts_map::iterator i = amounts.begin();
i != amounts.end();
i++)
diff --git a/binary.cc b/binary.cc
index 33dd2240..6ef4bc65 100644
--- a/binary.cc
+++ b/binary.cc
@@ -10,25 +10,29 @@
namespace ledger {
-static unsigned long binary_magic_number = 0xFFEED765;
+static unsigned long binary_magic_number = 0xFFEED765;
#ifdef DEBUG_ENABLED
-static unsigned long format_version = 0x00020601;
+static unsigned long format_version = 0x00020603;
#else
-static unsigned long format_version = 0x00020600;
+static unsigned long format_version = 0x00020602;
#endif
-static account_t ** accounts;
-static account_t ** accounts_next;
-static unsigned int account_index;
+static account_t ** accounts;
+static account_t ** accounts_next;
+static unsigned int account_index;
-static commodity_t ** commodities;
-static commodity_t ** commodities_next;
-static unsigned int commodity_index;
+static commodity_base_t ** base_commodities;
+static commodity_base_t ** base_commodities_next;
+static unsigned int base_commodity_index;
-extern char * bigints;
-extern char * bigints_next;
-extern unsigned int bigints_index;
-extern unsigned int bigints_count;
+static commodity_t ** commodities;
+static commodity_t ** commodities_next;
+static unsigned int commodity_index;
+
+extern char * bigints;
+extern char * bigints_next;
+extern unsigned int bigints_index;
+extern unsigned int bigints_count;
#if DEBUG_LEVEL >= ALPHA
#define read_binary_guard(in, id) { \
@@ -400,26 +404,24 @@ inline void read_binary_period_entry(char *& data, period_entry_t * entry,
entry->period.parse(stream);
}
-inline commodity_t * read_binary_commodity(char *& data)
+inline commodity_base_t * read_binary_commodity_base(char *& data)
{
- commodity_t * commodity = new commodity_t;
- *commodities_next++ = commodity;
+ commodity_base_t * commodity = new commodity_base_t;
+ *base_commodities_next++ = commodity;
- read_binary_string(data, *(const_cast<std::string *>(&commodity->symbol)));
- read_binary_number(data, commodity->quote);
- read_binary_string(data, commodity->name_);
- read_binary_string(data, commodity->note_);
- read_binary_number(data, commodity->precision_);
- read_binary_number(data, commodity->flags_);
- read_binary_long(data, commodity->ident);
+ read_binary_string(data, commodity->symbol);
+ read_binary_string(data, commodity->name);
+ read_binary_string(data, commodity->note);
+ read_binary_number(data, commodity->precision);
+ read_binary_number(data, commodity->flags);
return commodity;
}
-inline void read_binary_commodity_extra(char *& data,
- commodity_t::ident_t ident)
+inline void read_binary_commodity_base_extra(char *& data,
+ commodity_t::ident_t ident)
{
- commodity_t * commodity = commodities[ident];
+ commodity_base_t * commodity = base_commodities[ident];
for (unsigned long i = 0, count = read_binary_long<unsigned long>(data);
i < count;
@@ -432,12 +434,12 @@ inline void read_binary_commodity_extra(char *& data,
// Upon insertion, amt will be copied, which will cause the amount
// to be duplicated (and thus not lost when the journal's
// item_pool is deleted.
- if (! commodity->history_)
- commodity->history_ = new commodity_t::history_t;
- commodity->history_->prices.insert(history_pair(when, amt));
+ if (! commodity->history)
+ commodity->history = new commodity_base_t::history_t;
+ commodity->history->prices.insert(history_pair(when, amt));
}
- if (commodity->history_)
- read_binary_long(data, commodity->history_->last_lookup);
+ if (commodity->history)
+ read_binary_long(data, commodity->history->last_lookup);
unsigned char flag;
@@ -445,24 +447,47 @@ inline void read_binary_commodity_extra(char *& data,
if (flag) {
amount_t amt;
read_binary_amount(data, amt);
- commodity->smaller_ = new amount_t(amt);
+ commodity->smaller = new amount_t(amt);
}
flag = read_binary_number<unsigned char>(data);
if (flag) {
amount_t amt;
read_binary_amount(data, amt);
- commodity->larger_ = new amount_t(amt);
+ commodity->larger = new amount_t(amt);
}
+}
- flag = read_binary_number<unsigned char>(data);
- if (flag) {
- amount_t amt;
- read_binary_amount(data, amt);
- commodity->price = new amount_t(amt);
- commodity->base =
- commodities[read_binary_long<commodity_t::ident_t>(data) - 1];
- }
+inline commodity_t * read_binary_commodity(char *& data)
+{
+ commodity_t * commodity = new commodity_t;
+ *commodities_next++ = commodity;
+
+ commodity->ptr =
+ base_commodities[read_binary_long<commodity_base_t::ident_t>(data) - 1];
+
+ read_binary_string(data, commodity->qualified_symbol);
+ commodity->annotated = false;
+
+ return commodity;
+}
+
+inline commodity_t * read_binary_commodity_annotated(char *& data)
+{
+ annotated_commodity_t * commodity = new annotated_commodity_t;
+ *commodities_next++ = commodity;
+
+ commodity->ptr =
+ base_commodities[read_binary_long<commodity_base_t::ident_t>(data) - 1];
+
+ read_binary_string(data, commodity->qualified_symbol);
+ commodity->annotated = true;
+
+ read_binary_amount(data, commodity->price);
+ read_binary_long(data, commodity->date);
+ read_binary_string(data, commodity->tag);
+
+ return commodity;
}
inline
@@ -472,7 +497,6 @@ account_t * read_binary_account(char *& data, journal_t * journal,
account_t * acct = new account_t(NULL);
*accounts_next++ = acct;
- acct->ident = read_binary_long<account_t::ident_t>(data);
acct->journal = journal;
account_t::ident_t id;
@@ -512,8 +536,9 @@ unsigned int read_binary_journal(std::istream& in,
journal_t * journal,
account_t * master)
{
- account_index =
- commodity_index = 0;
+ account_index =
+ base_commodity_index =
+ commodity_index = 0;
// Read in the files that participated in this journal, so that they
// can be checked for changes on reading.
@@ -587,30 +612,59 @@ unsigned int read_binary_journal(std::istream& in,
bigints = bigints_next = (item_pool + sizeof(entry_t) * count +
sizeof(transaction_t) * xact_count);
- // Read in the commodities
+ // Read in the base commodities and then derived commodities
+
+ commodity_base_t::ident_t bc_count =
+ read_binary_long<commodity_base_t::ident_t>(data);
+ base_commodities = base_commodities_next = new commodity_base_t *[bc_count];
- commodity_t::ident_t c_count = read_binary_long<commodity_t::ident_t>(data);
+ for (commodity_base_t::ident_t i = 0; i < bc_count; i++) {
+ commodity_base_t * commodity = read_binary_commodity_base(data);
+
+ if (commodity->flags & COMMODITY_STYLE_BUILTIN)
+ commodity_base_t::commodities.erase(commodity->symbol);
+
+ std::pair<base_commodities_map::iterator, bool> result =
+ commodity_base_t::commodities.insert
+ (base_commodities_pair(commodity->symbol, commodity));
+ if (! result.second)
+ throw error(std::string("Failed to read base commodity from cache: ") +
+ commodity->symbol);
+ }
+
+ for (commodity_base_t::ident_t i = 0; i < bc_count; i++)
+ read_binary_commodity_base_extra(data, i);
+
+ commodity_t::ident_t c_count = read_binary_long<commodity_t::ident_t>(data);
commodities = commodities_next = new commodity_t *[c_count];
+
for (commodity_t::ident_t i = 0; i < c_count; i++) {
- commodity_t * commodity = read_binary_commodity(data);
- if (! (commodity->flags_ & COMMODITY_STYLE_BUILTIN)) {
- if (commodity->symbol == "") {
- commodity_t::commodities.erase(commodity->symbol);
+ commodity_t * commodity;
+ std::string mapping_key;
+
+ if (read_binary_number<char>(data) == 0) {
+ commodity = read_binary_commodity(data);
+ mapping_key = commodity->ptr->symbol;
+ } else {
+ read_binary_string(data, mapping_key);
+ commodity = read_binary_commodity_annotated(data);
+ }
+
+ if (commodity->flags() & COMMODITY_STYLE_BUILTIN) {
+ commodity_t::commodities.erase(mapping_key);
+ if (commodity->symbol() == "") {
delete commodity_t::null_commodity;
commodity_t::null_commodity = commodity;
}
-
- std::pair<commodities_map::iterator, bool> result =
- commodity_t::commodities.insert(commodities_pair(commodity->symbol,
- commodity));
- if (! result.second)
- throw error(std::string("Failed to read commodity from cache: ") +
- commodity->symbol);
}
- }
- for (commodity_t::ident_t i = 0; i < c_count; i++)
- read_binary_commodity_extra(data, i);
+ std::pair<commodities_map::iterator, bool> result =
+ commodity_t::commodities.insert(commodities_pair(mapping_key,
+ commodity));
+ if (! result.second)
+ throw error(std::string("Failed to read commodity from cache: ") +
+ commodity->ptr->symbol);
+ }
commodity_t::ident_t ident;
read_binary_long(data, ident);
@@ -747,7 +801,7 @@ inline void write_binary_string(std::ostream& out, const std::string& str)
void write_binary_amount(std::ostream& out, const amount_t& amt)
{
if (amt.commodity_)
- write_binary_long(out, amt.commodity().ident);
+ write_binary_long(out, amt.commodity_->ident);
else
write_binary_long<commodity_t::ident_t>(out, 0xffffffff);
@@ -762,7 +816,7 @@ void write_binary_mask(std::ostream& out, mask_t * mask)
void write_binary_value_expr(std::ostream& out, const value_expr_t * expr)
{
- if (expr == NULL) {
+ if (! expr) {
write_binary_number<unsigned char>(out, 0);
return;
}
@@ -816,7 +870,7 @@ void write_binary_transaction(std::ostream& out, transaction_t * xact,
write_binary_long(out, xact->_date_eff);
write_binary_long(out, xact->account->ident);
- if (xact->amount_expr != NULL) {
+ if (xact->amount_expr) {
write_binary_number<char>(out, 1);
write_binary_value_expr(out, xact->amount_expr);
} else {
@@ -830,7 +884,7 @@ void write_binary_transaction(std::ostream& out, transaction_t * xact,
if (xact->cost &&
(! (ignore_calculated && xact->flags & TRANSACTION_CALCULATED))) {
write_binary_number<char>(out, 1);
- if (xact->cost_expr != NULL) {
+ if (xact->cost_expr) {
write_binary_number<char>(out, 1);
write_binary_value_expr(out, xact->cost_expr);
} else {
@@ -898,54 +952,69 @@ void write_binary_period_entry(std::ostream& out, period_entry_t * entry)
write_binary_string(out, entry->period_string);
}
-void write_binary_commodity(std::ostream& out, commodity_t * commodity)
+void write_binary_commodity_base(std::ostream& out, commodity_base_t * commodity)
{
+ commodity->ident = ++base_commodity_index;
+
write_binary_string(out, commodity->symbol);
- write_binary_number(out, commodity->quote);
- write_binary_string(out, commodity->name_);
- write_binary_string(out, commodity->note_);
- write_binary_number(out, commodity->precision_);
- write_binary_number(out, commodity->flags_);
- commodity->ident = ++commodity_index;
- write_binary_long(out, commodity->ident);
+ write_binary_string(out, commodity->name);
+ write_binary_string(out, commodity->note);
+ write_binary_number(out, commodity->precision);
+ write_binary_number(out, commodity->flags);
}
-void write_binary_commodity_extra(std::ostream& out, commodity_t * commodity)
+void write_binary_commodity_base_extra(std::ostream& out, commodity_base_t * commodity)
{
- if (! commodity->history_) {
+ if (! commodity->history) {
write_binary_long<unsigned long>(out, 0);
} else {
- write_binary_long<unsigned long>(out, commodity->history_->prices.size());
- for (history_map::const_iterator i = commodity->history_->prices.begin();
- i != commodity->history_->prices.end();
+ write_binary_long<unsigned long>(out, commodity->history->prices.size());
+ for (history_map::const_iterator i = commodity->history->prices.begin();
+ i != commodity->history->prices.end();
i++) {
write_binary_long(out, (*i).first);
write_binary_amount(out, (*i).second);
}
- write_binary_long(out, commodity->history_->last_lookup);
+ write_binary_long(out, commodity->history->last_lookup);
}
- if (commodity->smaller_) {
+ if (commodity->smaller) {
write_binary_number<unsigned char>(out, 1);
- write_binary_amount(out, *commodity->smaller_);
+ write_binary_amount(out, *commodity->smaller);
} else {
write_binary_number<unsigned char>(out, 0);
}
- if (commodity->larger_) {
+ if (commodity->larger) {
write_binary_number<unsigned char>(out, 1);
- write_binary_amount(out, *commodity->larger_);
+ write_binary_amount(out, *commodity->larger);
} else {
write_binary_number<unsigned char>(out, 0);
}
+}
- if (commodity->price) {
- write_binary_number<unsigned char>(out, 1);
- write_binary_amount(out, *commodity->price);
- write_binary_long(out, commodity->base->ident);
- } else {
- write_binary_number<unsigned char>(out, 0);
- }
+void write_binary_commodity(std::ostream& out, commodity_t * commodity)
+{
+ commodity->ident = ++commodity_index;
+
+ write_binary_long(out, commodity->ptr->ident);
+ write_binary_string(out, commodity->qualified_symbol);
+}
+
+void write_binary_commodity_annotated(std::ostream& out,
+ commodity_t * commodity)
+{
+ commodity->ident = ++commodity_index;
+
+ write_binary_long(out, commodity->ptr->ident);
+ write_binary_string(out, commodity->qualified_symbol);
+
+ annotated_commodity_t * ann_comm =
+ static_cast<annotated_commodity_t *>(commodity);
+
+ write_binary_amount(out, ann_comm->price);
+ write_binary_long(out, ann_comm->date);
+ write_binary_string(out, ann_comm->tag);
}
static inline account_t::ident_t count_accounts(account_t * account)
@@ -964,7 +1033,6 @@ void write_binary_account(std::ostream& out, account_t * account)
{
account->ident = ++account_index;
- write_binary_long(out, account->ident);
if (account->parent)
write_binary_long(out, account->parent->ident);
else
@@ -983,8 +1051,9 @@ void write_binary_account(std::ostream& out, account_t * account)
void write_binary_journal(std::ostream& out, journal_t * journal)
{
- account_index =
- commodity_index = 0;
+ account_index =
+ base_commodity_index =
+ commodity_index = 0;
write_binary_number(out, binary_magic_number);
write_binary_number(out, format_version);
@@ -1041,17 +1110,34 @@ void write_binary_journal(std::ostream& out, journal_t * journal)
// Write out the commodities
write_binary_long<commodity_t::ident_t>
- (out, commodity_t::commodities.size());
+ (out, commodity_base_t::commodities.size());
- for (commodities_map::const_iterator i = commodity_t::commodities.begin();
- i != commodity_t::commodities.end();
+ for (base_commodities_map::const_iterator i =
+ commodity_base_t::commodities.begin();
+ i != commodity_base_t::commodities.end();
+ i++)
+ write_binary_commodity_base(out, (*i).second);
+
+ for (base_commodities_map::const_iterator i =
+ commodity_base_t::commodities.begin();
+ i != commodity_base_t::commodities.end();
i++)
- write_binary_commodity(out, (*i).second);
+ write_binary_commodity_base_extra(out, (*i).second);
+
+ write_binary_long<commodity_t::ident_t>
+ (out, commodity_t::commodities.size());
for (commodities_map::const_iterator i = commodity_t::commodities.begin();
i != commodity_t::commodities.end();
- i++)
- write_binary_commodity_extra(out, (*i).second);
+ i++) {
+ write_binary_number<char>(out, (*i).second->annotated ? 1 : 0);
+ if (! (*i).second->annotated) {
+ write_binary_commodity(out, (*i).second);
+ } else {
+ write_binary_string(out, (*i).first); // the mapping key
+ write_binary_commodity_annotated(out, (*i).second);
+ }
+ }
if (commodity_t::default_commodity)
write_binary_long(out, commodity_t::default_commodity->ident);
diff --git a/config.cc b/config.cc
index cc04ed8b..54600369 100644
--- a/config.cc
+++ b/config.cc
@@ -292,9 +292,9 @@ void config_t::process_options(const std::string& command,
// If downloading is to be supported, configure the updater
- if (! commodity_t::updater && download_quotes)
- commodity_t::updater = new quotes_by_script(price_db, pricing_leeway,
- cache_dirty);
+ if (! commodity_base_t::updater && download_quotes)
+ commodity_base_t::updater =
+ new quotes_by_script(price_db, pricing_leeway, cache_dirty);
if (! date_format.empty())
format_t::date_format = date_format;
@@ -702,8 +702,9 @@ OPT_BEGIN(account, "a:") {
config->account = optarg;
} OPT_END(account);
-OPT_BEGIN(debug, "") {
+OPT_BEGIN(debug, ":") {
config->debug_mode = true;
+ ::setenv("DEBUG_CLASS", optarg, 1);
} OPT_END(debug);
//////////////////////////////////////////////////////////////////////
@@ -1098,7 +1099,7 @@ option_t config_options[CONFIG_OPTIONS_SIZE] = {
{ "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 },
+ { "debug", '\0', true, opt_debug, false },
{ "deviation", 'D', false, opt_deviation, false },
{ "display", 'd', true, opt_display, false },
{ "dow", '\0', false, opt_dow, false },
diff --git a/datetime.cc b/datetime.cc
index 16c1e04e..cf26d0c9 100644
--- a/datetime.cc
+++ b/datetime.cc
@@ -3,13 +3,10 @@
#endif
#include "datetime.h"
-#include "error.h"
#include <ctime>
#include <cctype>
-namespace ledger {
-
std::time_t now = std::time(NULL);
int now_year = std::localtime(&now)->tm_year;
@@ -108,8 +105,7 @@ static void parse_inclusion_specifier(const std::string& word,
struct std::tm when;
if (! parse_date_mask(word.c_str(), &when))
- throw interval_expr_error(std::string("Could not parse date mask: ") +
- word);
+ throw datetime_error(std::string("Could not parse date mask: ") + word);
when.tm_hour = 0;
when.tm_min = 0;
@@ -316,61 +312,5 @@ bool parse_date(const char * date_str, std::time_t * result, const int year)
bool quick_parse_date(const char * date_str, std::time_t * result)
{
-#if 1
return parse_date(date_str, result, now_year + 1900);
-#else
- int year = -1, month = -1, day, num = 0;
-
- for (const char * p = date_str; *p; p++) {
- if (*p == '/' || *p == '-' || *p == '.') {
- if (year == -1)
- year = num;
- else
- month = num;
- num = 0;
- }
- else if (*p < '0' || *p > '9') {
- return false;
- }
- else {
- num *= 10;
- num += *p - '0';
- }
- }
-
- day = num;
-
- if (month == -1) {
- month = year;
- year = now_year + 1900;
- }
-
- if (base == -1 || year != base_year) {
- struct std::tm when;
- std::memset(&when, 0, sizeof(when));
-
- base_year = year == -1 ? now_year + 1900 : year;
- when.tm_year = year == -1 ? now_year : year - 1900;
- when.tm_mday = 1;
-
- base = std::mktime(&when);
- year = base_year;
- }
-
- *result = base;
-
- --month;
- while (--month >= 0) {
- *result += month_days[month] * 24 * 60 * 60;
- if (month == 1 && year % 4 == 0 && year != 2000) // february in leap years
- *result += 24 * 60 * 60;
- }
-
- if (--day)
- *result += day * 24 * 60 * 60;
-
- return true;
-#endif
}
-
-} // namespace ledger
diff --git a/datetime.h b/datetime.h
index 78054ec2..fb3751bf 100644
--- a/datetime.h
+++ b/datetime.h
@@ -1,13 +1,9 @@
#ifndef _DATETIME_H
#define _DATETIME_H
-#include "debug.h"
-
#include <ctime>
#include <sstream>
-namespace ledger {
-
struct interval_t
{
unsigned int years;
@@ -20,19 +16,12 @@ struct interval_t
std::time_t _begin = 0, std::time_t _end = 0)
: years(_years), months(_months), seconds(_seconds),
begin(_begin), end(_end) {
- DEBUG_PRINT("ledger.memory.ctors", "ctor interval_t");
}
interval_t(const std::string& desc)
: years(0), months(0), seconds(0), begin(0), end(0) {
- DEBUG_PRINT("ledger.memory.ctors", "ctor interval_t");
std::istringstream stream(desc);
parse(stream);
}
-#ifdef DEBUG_ENABLED
- ~interval_t() {
- DEBUG_PRINT("ledger.memory.dtors", "dtor interval_t");
- }
-#endif
operator bool() const {
return seconds > 0 || months > 0 || years > 0;
@@ -57,6 +46,15 @@ bool parse_date(const char * date_str, std::time_t * result,
const int year = -1);
bool quick_parse_date(const char * date_str, std::time_t * result);
-} // namespace ledger
+class datetime_error : public std::exception {
+ std::string reason;
+ public:
+ datetime_error(const std::string& _reason) throw() : reason(_reason) {}
+ virtual ~datetime_error() throw() {}
+
+ virtual const char* what() const throw() {
+ return reason.c_str();
+ }
+};
#endif // _DATETIME_H
diff --git a/format.cc b/format.cc
index d062a710..732408e0 100644
--- a/format.cc
+++ b/format.cc
@@ -163,7 +163,7 @@ element_t * format_t::parse_elements(const std::string& fmt)
current->type = element_t::VALUE_EXPR;
- assert(current->val_expr == NULL);
+ assert(! current->val_expr);
current->val_expr = new value_expr(std::string(b, p));
break;
}
@@ -346,7 +346,7 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
commodity_t& comm(unit_cost.commodity());
bool has_flag = comm.flags() & COMMODITY_STYLE_VARIABLE;
if (! has_flag)
- unit_cost.commodity().flags() |= COMMODITY_STYLE_VARIABLE;
+ comm.add_flags(COMMODITY_STYLE_VARIABLE);
std::ostringstream stream;
stream << details.xact->amount << " @ " << unit_cost;
@@ -354,7 +354,7 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
use_disp = true;
if (! has_flag)
- unit_cost.commodity().flags() &= ~COMMODITY_STYLE_VARIABLE;
+ comm.drop_flags(COMMODITY_STYLE_VARIABLE);
}
else if (details.entry) {
unsigned int xacts_count = 0;
diff --git a/gnucash.cc b/gnucash.cc
index 03ea4fc3..cf858ba3 100644
--- a/gnucash.cc
+++ b/gnucash.cc
@@ -84,7 +84,10 @@ static void startElement(void *userData, const char *name, const char **atts)
action = ACCOUNT_PARENT;
else if (std::strcmp(name, "gnc:commodity") == 0) {
assert(! curr_comm);
+#if 0
+ // jww (2006-03-02): !!!
curr_comm = new commodity_t("");
+#endif
}
else if (std::strcmp(name, "cmdty:id") == 0)
action = COMM_SYM;
@@ -133,7 +136,10 @@ static void endElement(void *userData, const char *name)
}
else if (std::strcmp(name, "gnc:commodity") == 0) {
assert(curr_comm);
+#if 0
+ // jww (2006-03-02): !!!
commodity_t::add_commodity(curr_comm);
+#endif
curr_comm = NULL;
}
else if (std::strcmp(name, "gnc:transaction") == 0) {
@@ -252,20 +258,25 @@ static void dataHandler(void *userData, const char *s, int len)
case COMM_SYM:
if (curr_comm) {
+#if 0
+ // jww (2006-03-02): !!!
curr_comm->set_symbol(std::string(s, len));
+#endif
}
else if (curr_account) {
std::string symbol(s, len);
- commodity_t * comm = commodity_t::find_commodity(symbol, true);
+ commodity_t * comm = commodity_t::find_or_create(symbol);
+ assert(comm);
if (symbol != "$" && symbol != "USD")
- comm->flags() |= COMMODITY_STYLE_SEPARATED;
+ comm->add_flags(COMMODITY_STYLE_SEPARATED);
account_comms.insert(account_comm_pair(curr_account, comm));
}
else if (curr_entry) {
std::string symbol(s, len);
- entry_comm = commodity_t::find_commodity(symbol, true);
+ entry_comm = commodity_t::find_or_create(symbol);
+ assert(entry_comm);
if (symbol != "$" && symbol != "USD")
- entry_comm->flags() |= COMMODITY_STYLE_SEPARATED;
+ entry_comm->add_flags(COMMODITY_STYLE_SEPARATED);
}
break;
@@ -274,7 +285,7 @@ static void dataHandler(void *userData, const char *s, int len)
break;
case COMM_PREC:
- curr_comm->precision() = len - 1;
+ curr_comm->set_precision(len - 1);
break;
case ENTRY_NUM:
@@ -308,7 +319,7 @@ static void dataHandler(void *userData, const char *s, int len)
curr_value.set_commodity(*entry_comm);
if (precision > entry_comm->precision())
- entry_comm->precision() = precision;
+ entry_comm->set_precision(precision);
break;
}
@@ -380,11 +391,13 @@ unsigned int gnucash_parser_t::parse(std::istream& in,
// GnuCash uses the USD commodity without defining it, which really
// means $.
- commodity_t * usd;
- usd = new commodity_t("$", 2, COMMODITY_STYLE_THOUSANDS);
- commodity_t::add_commodity(usd, "$");
- usd = new commodity_t("$", 2, COMMODITY_STYLE_THOUSANDS);
+ commodity_t * usd = commodity_t::find_or_create("$");
+ usd->set_precision(2);
+ usd->add_flags(COMMODITY_STYLE_THOUSANDS);
+#if 0
+ //jww (2006-03-02): !!! make an alias here
commodity_t::add_commodity(usd, "USD");
+#endif
offset = 2;
parser = current_parser = XML_ParserCreate(NULL);
diff --git a/journal.cc b/journal.cc
index 5b9765e1..4e03ae1a 100644
--- a/journal.cc
+++ b/journal.cc
@@ -105,9 +105,12 @@ bool entry_base_t::finalize()
balance += *p;
}
- if ((*x)->cost && (*x)->amount.commodity().price)
- balance += (*((*x)->amount.commodity().price) * (*x)->amount -
- *((*x)->cost));
+ if ((*x)->cost && (*x)->amount.commodity().annotated) {
+ annotated_commodity_t&
+ ann_comm(static_cast<annotated_commodity_t&>((*x)->amount.commodity()));
+ if (ann_comm.price)
+ balance += ann_comm.price * (*x)->amount - *((*x)->cost);
+ }
}
}
@@ -115,6 +118,9 @@ bool entry_base_t::finalize()
if (no_amounts)
return true;
+ // If there is only one transaction, balance against the basket
+ // account if one has been set.
+
if (journal && journal->basket && transactions.size() == 1) {
assert(balance.type < value_t::BALANCE);
transaction_t * nxact = new transaction_t(journal->basket);
@@ -149,8 +155,13 @@ bool entry_base_t::finalize()
continue;
assert((*x)->amount);
-
balance -= (*x)->amount;
+
+ entry_t * entry = dynamic_cast<entry_t *>(this);
+ (*x)->amount.annotate_commodity(abs(per_unit_cost),
+ entry ? entry->actual_date() : 0,
+ entry ? entry->code : "");
+
(*x)->cost = new amount_t(- (per_unit_cost * (*x)->amount));
balance += *(*x)->cost;
}
@@ -289,7 +300,7 @@ void auto_entry_t::extend_entry(entry_base_t& entry)
t != transactions.end();
t++) {
amount_t amt;
- if ((*t)->amount.commodity().symbol.empty())
+ if (! (*t)->amount.commodity())
amt = (*i)->amount * (*t)->amount;
else
amt = (*t)->amount;
diff --git a/journal.h b/journal.h
index 65e6d609..b1eae918 100644
--- a/journal.h
+++ b/journal.h
@@ -167,11 +167,9 @@ class entry_t : public entry_base_t
}
entry_t(const entry_t& e);
-#ifdef DEBUG_ENABLED
virtual ~entry_t() {
DEBUG_PRINT("ledger.memory.dtors", "dtor entry_t");
}
-#endif
std::time_t actual_date() const {
return _date;
@@ -247,11 +245,9 @@ class period_entry_t : public entry_base_t
DEBUG_PRINT("ledger.memory.ctors", "ctor period_entry_t");
}
-#ifdef DEBUG_ENABLED
virtual ~period_entry_t() {
DEBUG_PRINT("ledger.memory.dtors", "dtor period_entry_t");
}
-#endif
virtual bool valid() const {
return period;
diff --git a/main.cc b/main.cc
index fb43d6a8..2ba7571b 100644
--- a/main.cc
+++ b/main.cc
@@ -209,7 +209,7 @@ int parse_and_report(int argc, char * argv[], char * envp[])
// rightmost '/' character in the pager pathname. See manpage
// for strrchr.
arg0 = std::strrchr(config.pager.c_str(), '/');
- if (arg0 != NULL)
+ if (arg0)
arg0++;
else
arg0 = config.pager.c_str(); // No slashes in pager.
diff --git a/ofx.cc b/ofx.cc
index 97cb7302..4b6bdbae 100644
--- a/ofx.cc
+++ b/ofx.cc
@@ -69,8 +69,7 @@ int ofx_proc_transaction_cb(struct OfxTransactionData data,
assert(ac != ofx_account_currencies.end());
commodity_t * default_commodity = (*ac).second;
- std::string buf;
- std::ostringstream stream(buf);
+ std::ostringstream stream;
stream << - data.units;
// jww (2005-02-09): what if the amount contains fees?
@@ -84,7 +83,7 @@ int ofx_proc_transaction_cb(struct OfxTransactionData data,
}
if (data.unitprice_valid && data.unitprice != 1.0) {
- std::ostringstream cstream(buf);
+ std::ostringstream cstream;
stream << - data.unitprice;
xact->cost = new amount_t(stream.str() + " " + default_commodity->symbol);
}
diff --git a/option.cc b/option.cc
index 043d51dc..3eb78281 100644
--- a/option.cc
+++ b/option.cc
@@ -49,7 +49,7 @@ bool process_option(option_t * options, const std::string& name,
const char * arg)
{
option_t * opt = search_options(options, name.c_str());
- if (opt != NULL && ! opt->handled) {
+ if (opt && ! opt->handled) {
opt->handler(arg);
opt->handled = true;
return true;
@@ -89,12 +89,12 @@ void process_arguments(option_t * options, int argc, char ** argv,
}
opt = search_options(options, name);
- if (opt == NULL)
+ if (! opt)
throw option_error(std::string("illegal option --") + name);
- if (opt->wants_arg && value == NULL) {
+ if (opt->wants_arg && ! value) {
value = *++i;
- if (value == NULL)
+ if (! value)
throw option_error(std::string("missing option argument for --") +
name);
}
@@ -102,18 +102,18 @@ void process_arguments(option_t * options, int argc, char ** argv,
} else {
char c = (*i)[1];
opt = search_options(options, c);
- if (opt == NULL)
+ if (! opt)
throw option_error(std::string("illegal option -") + c);
if (opt->wants_arg) {
value = *++i;
- if (value == NULL)
+ if (! value)
throw option_error(std::string("missing option argument for -") + c);
}
}
assert(opt);
- assert(value == NULL || opt->wants_arg);
+ assert(! value || opt->wants_arg);
process_option(opt, value);
next:
diff --git a/qif.cc b/qif.cc
index 64153b27..5d4eb3a5 100644
--- a/qif.cc
+++ b/qif.cc
@@ -118,13 +118,15 @@ unsigned int qif_parser_t::parse(std::istream& in,
unsigned long flags = xact->amount.commodity().flags();
unsigned short prec = xact->amount.commodity().precision();
- if (! def_commodity)
- def_commodity = commodity_t::find_commodity("$", true);
+ if (! def_commodity) {
+ def_commodity = commodity_t::find_or_create("$");
+ assert(def_commodity);
+ }
xact->amount.set_commodity(*def_commodity);
- def_commodity->flags() |= flags;
+ def_commodity->add_flags(flags);
if (prec > def_commodity->precision())
- def_commodity->precision() = prec;
+ def_commodity->set_precision(prec);
if (c == '$') {
saw_splits = true;
diff --git a/quotes.cc b/quotes.cc
index 48b0db01..77a4d929 100644
--- a/quotes.cc
+++ b/quotes.cc
@@ -9,7 +9,7 @@
namespace ledger {
-void quotes_by_script::operator()(commodity_t& commodity,
+void quotes_by_script::operator()(commodity_base_t& commodity,
const std::time_t moment,
const std::time_t date,
const std::time_t last,
@@ -22,12 +22,12 @@ void quotes_by_script::operator()(commodity_t& commodity,
DEBUG_PRINT_TIME_(moment);
DEBUG_PRINT_TIME_(date);
DEBUG_PRINT_TIME_(last);
- if (commodity.history())
- DEBUG_PRINT_TIME_(commodity.history()->last_lookup);
+ if (commodity.history)
+ DEBUG_PRINT_TIME_(commodity.history->last_lookup);
DEBUG_PRINT_("pricing_leeway is " << pricing_leeway);
- if ((commodity.history() &&
- std::difftime(now, commodity.history()->last_lookup) < pricing_leeway) ||
+ if ((commodity.history &&
+ std::difftime(now, commodity.history->last_lookup) < pricing_leeway) ||
std::difftime(now, last) < pricing_leeway ||
(price && std::difftime(moment, date) > 0 &&
std::difftime(moment, date) <= pricing_leeway))
@@ -61,7 +61,7 @@ void quotes_by_script::operator()(commodity_t& commodity,
price.parse(buf);
commodity.add_price(now, price);
- commodity.history()->last_lookup = now;
+ commodity.history->last_lookup = now;
cache_dirty = true;
if (price && ! price_db.empty()) {
diff --git a/quotes.h b/quotes.h
index 53008aa0..72d0589e 100644
--- a/quotes.h
+++ b/quotes.h
@@ -5,7 +5,7 @@
namespace ledger {
-class quotes_by_script : public commodity_t::updater_t
+class quotes_by_script : public commodity_base_t::updater_t
{
std::string price_db;
unsigned long pricing_leeway;
@@ -18,7 +18,7 @@ class quotes_by_script : public commodity_t::updater_t
: price_db(_price_db), pricing_leeway(_pricing_leeway),
cache_dirty(_cache_dirty) {}
- virtual void operator()(commodity_t& commodity,
+ virtual void operator()(commodity_base_t& commodity,
const std::time_t moment,
const std::time_t date,
const std::time_t last,
diff --git a/reconcile.cc b/reconcile.cc
index ec893c0b..e2f11198 100644
--- a/reconcile.cc
+++ b/reconcile.cc
@@ -71,7 +71,7 @@ void reconcile_transactions::flush()
balance -= cleared_balance;
if (balance.type >= value_t::BALANCE)
throw error(std::string("Reconcile balance is not of the same commodity ('") +
- b_comm.symbol + "' != '" + cb_comm.symbol + "')");
+ b_comm.symbol() + "' != '" + cb_comm.symbol() + "')");
// If the amount to reconcile is the same as the pending balance,
// then assume an exact match and return the results right away.
diff --git a/textual.cc b/textual.cc
index 8c2c15cd..59a2075d 100644
--- a/textual.cc
+++ b/textual.cc
@@ -131,7 +131,7 @@ value_expr_t * parse_amount(const char * text, amount_t& amt,
{
char * altbuf = NULL;
- if (*text && *text != '(') {
+ if (*text && *text != '(' && *text != '-') {
bool in_quote = false;
bool seen_digit = false;
for (const char * p = text + 1; *p; p++)
@@ -295,47 +295,29 @@ transaction_t * parse_transaction(char * line, account_t * account,
AMOUNT_PARSE_NO_REDUCE, *xact);
if (xact->amount_expr)
xact->amount_expr->acquire();
+
if (price) {
xact->cost = new amount_t;
xact->cost_expr = parse_amount(price, *xact->cost,
AMOUNT_PARSE_NO_MIGRATE, *xact);
if (xact->cost_expr)
xact->cost_expr->acquire();
- if (per_unit) {
- if (xact->amount.commodity().base == NULL) {
- std::string symbol = xact->amount.commodity().symbol;
- symbol += " {";
- symbol += price;
- symbol += "}";
-
- commodity_t * priced_commodity =
- commodity_t::find_commodity(symbol, true);
-
- priced_commodity->price = new amount_t(*xact->cost);
- priced_commodity->base = &xact->amount.commodity();
-
- xact->amount.set_commodity(*priced_commodity);
- }
+ if (per_unit) {
+ if (! xact->amount.commodity().annotated)
+ xact->amount.annotate_commodity(*xact->cost,
+ xact->entry->actual_date(),
+ xact->entry->code);
*xact->cost *= xact->amount;
*xact->cost = xact->cost->round(xact->cost->commodity().precision());
}
- else if (xact->amount.commodity().base == NULL) {
+ else if (! xact->amount.commodity().annotated) {
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.annotate_commodity(cost, xact->entry->actual_date(),
+ xact->entry->code);
}
}
xact->amount.reduce();
@@ -348,7 +330,7 @@ bool parse_transactions(std::istream& in,
account_t * account,
entry_base_t& entry,
const std::string& kind,
- istream_pos_type& beg_pos)
+ unsigned long beg_pos)
{
static char line[MAX_LINE + 1];
bool added = false;
@@ -357,7 +339,7 @@ bool parse_transactions(std::istream& in,
in.getline(line, MAX_LINE);
if (in.eof())
break;
- beg_pos += istream_pos_type(std::strlen(line) + 1);
+ beg_pos += std::strlen(line) + 1;
linenum++;
if (line[0] == ' ' || line[0] == '\t' || line[0] == '\r') {
@@ -382,7 +364,7 @@ namespace {
}
entry_t * parse_entry(std::istream& in, char * line, account_t * master,
- textual_parser_t& parser, istream_pos_type& beg_pos)
+ textual_parser_t& parser, unsigned long beg_pos)
{
std::auto_ptr<entry_t> curr(new entry_t);
@@ -441,15 +423,15 @@ entry_t * parse_entry(std::istream& in, char * line, account_t * master,
TIMER_START(entry_xacts);
- istream_pos_type end_pos;
- unsigned long beg_line = linenum;
+ unsigned long end_pos;
+ unsigned long beg_line = linenum;
while (! in.eof() && (in.peek() == ' ' || in.peek() == '\t')) {
line[0] = '\0';
in.getline(line, MAX_LINE);
if (in.eof() && line[0] == '\0')
break;
- end_pos = beg_pos + istream_pos_type(std::strlen(line) + 1);
+ end_pos = beg_pos + std::strlen(line) + 1;
linenum++;
if (line[0] == ' ' || line[0] == '\t' || line[0] == '\r') {
@@ -581,16 +563,16 @@ unsigned int textual_parser_t::parse(std::istream& in,
src_idx = journal->sources.size() - 1;
linenum = 1;
- istream_pos_type beg_pos = in.tellg();
- istream_pos_type end_pos;
- unsigned long beg_line = linenum;
+ unsigned long beg_pos = in.tellg();
+ unsigned long end_pos;
+ unsigned long beg_line = linenum;
while (in.good() && ! in.eof()) {
try {
in.getline(line, MAX_LINE);
if (in.eof())
break;
- end_pos = beg_pos + istream_pos_type(std::strlen(line) + 1);
+ end_pos = beg_pos + std::strlen(line) + 1;
linenum++;
switch (line[0]) {
@@ -690,8 +672,8 @@ unsigned int textual_parser_t::parse(std::istream& in,
parse_symbol(symbol_and_price, symbol);
amount_t price(symbol_and_price);
- commodity_t * commodity = commodity_t::find_commodity(symbol, true);
- commodity->add_price(date, price);
+ if (commodity_t * commodity = commodity_t::find_or_create(symbol))
+ commodity->add_price(date, price);
break;
}
@@ -700,8 +682,8 @@ unsigned int textual_parser_t::parse(std::istream& in,
std::string symbol;
parse_symbol(p, symbol);
- commodity_t * commodity = commodity_t::find_commodity(symbol, true);
- commodity->flags() |= COMMODITY_STYLE_NOMARKET;
+ if (commodity_t * commodity = commodity_t::find_or_create(symbol))
+ commodity->add_flags(COMMODITY_STYLE_NOMARKET);
break;
}
@@ -774,11 +756,11 @@ unsigned int textual_parser_t::parse(std::istream& in,
char * p = next_element(line);
std::string word(line + 1);
if (word == "include") {
- push_var<std::string> save_path(path);
- push_var<unsigned int> save_src_idx(src_idx);
- push_var<istream_pos_type> save_beg_pos(beg_pos);
- push_var<istream_pos_type> save_end_pos(end_pos);
- push_var<unsigned int> save_linenum(linenum);
+ push_var<std::string> save_path(path);
+ push_var<unsigned int> save_src_idx(src_idx);
+ push_var<unsigned long> save_beg_pos(beg_pos);
+ push_var<unsigned long> save_end_pos(end_pos);
+ push_var<unsigned int> save_linenum(linenum);
path = p;
if (path[0] != '/' && path[0] != '\\') {
@@ -831,7 +813,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
default: {
unsigned int first_line = linenum;
- istream_pos_type pos = end_pos;
+ unsigned long pos = end_pos;
if (entry_t * entry =
parse_entry(in, line, account_stack.front(), *this, pos)) {
if (journal->add_entry(entry)) {
@@ -845,11 +827,10 @@ unsigned int textual_parser_t::parse(std::istream& in,
print_entry(std::cerr, *entry);
delete entry;
- std::string msgbuf;
- std::ostringstream msg(msgbuf);
- msg << "Entry above does not balance; remainder is: "
- << entry_balance;
- throw parse_error(path, first_line, msg.str());
+ std::ostringstream errmsg;
+ errmsg << "Entry above does not balance; remainder is: "
+ << entry_balance;
+ throw parse_error(path, first_line, errmsg.str());
}
} else {
throw parse_error(path, first_line, "Failed to parse entry");
@@ -935,10 +916,9 @@ void write_textual_journal(journal_t& journal, std::string path,
auto_entries_list::iterator al = journal.auto_entries.begin();
period_entries_list::iterator pl = journal.period_entries.begin();
- istream_pos_type pos = 0;
- istream_pos_type jump_to;
+ unsigned long pos = 0;
- format_t hdr_fmt(write_hdr_format);
+ format_t hdr_fmt(write_hdr_format);
std::ifstream in(found.c_str());
while (! in.eof()) {
diff --git a/valexpr.cc b/valexpr.cc
index a5cb481f..61574d3f 100644
--- a/valexpr.cc
+++ b/valexpr.cc
@@ -96,7 +96,7 @@ namespace {
value_expr_t * reduce_leaves(value_expr_t * expr, const details_t& details,
value_expr_t * context)
{
- if (expr == NULL)
+ if (! expr)
return NULL;
value_auto_ptr temp;
@@ -119,7 +119,7 @@ namespace {
value_expr_t * find_leaf(value_expr_t * context, int goal, int& found)
{
- if (context == NULL)
+ if (! context)
return NULL;
if (context->kind != value_expr_t::O_COM) {
@@ -164,7 +164,7 @@ void value_expr_t::compute(value_t& result, const details_t& details,
transaction_xdata_(*details.xact).dflags & TRANSACTION_COMPOSITE)
result = transaction_xdata_(*details.xact).composite_amount;
else
- result = base_amount(details.xact->amount);
+ result = details.xact->amount;
}
else if (details.account && account_has_xdata(*details.account)) {
result = account_xdata(*details.account).value;
@@ -210,7 +210,7 @@ void value_expr_t::compute(value_t& result, const details_t& details,
if (details.xact->cost)
result = *details.xact->cost;
else
- result = base_amount(details.xact->amount);
+ result = details.xact->amount;
}
}
else if (details.account && account_has_xdata(*details.account)) {
@@ -493,7 +493,7 @@ void value_expr_t::compute(value_t& result, const details_t& details,
case F_COMMODITY_MASK:
assert(mask);
if (details.xact)
- result = mask->match(details.xact->amount.commodity().symbol);
+ result = mask->match(details.xact->amount.commodity().symbol());
else
result = false;
break;
@@ -757,7 +757,7 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope)
// Define the value associated with the defined identifier
value_auto_ptr def(parse_boolean_expr(in, params.get()));
- if (def.get() == NULL)
+ if (! def.get())
throw value_expr_error(std::string("Definition failed for '") + buf + "'");
node.reset(new value_expr_t(value_expr_t::O_DEF));
@@ -773,7 +773,7 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope)
} else {
assert(scope);
value_expr_t * def = scope->lookup(buf);
- if (def == NULL) {
+ if (! def) {
if (buf[1] == '\0' &&
(buf[0] == 'c' || buf[0] == 'C' || buf[0] == 'p' ||
buf[0] == 'w' || buf[0] == 'W' || buf[0] == 'e'))
@@ -796,15 +796,14 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope)
}
in.get(c);
- if (args.get() != NULL) {
+ if (args.get()) {
count = count_leaves(args.get());
node->set_right(args.release());
}
}
if (count != def->left->constant_i) {
- std::string msg;
- std::ostringstream errmsg(msg);
+ std::ostringstream errmsg;
errmsg << "Wrong number of arguments to '" << buf
<< "': saw " << count
<< ", wanted " << def->left->constant_i;
@@ -905,7 +904,8 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope)
unexpected(c, '}');
node.reset(new value_expr_t(value_expr_t::CONSTANT_A));
- node->constant_a = new amount_t(buf);
+ node->constant_a = new amount_t();
+ node->constant_a->parse(buf, AMOUNT_PARSE_NO_MIGRATE);
break;
}
@@ -1447,10 +1447,10 @@ void dump_value_expr(std::ostream& out, const value_expr_t * node,
if (node->right)
dump_value_expr(out, node->right, depth + 1);
} else {
- assert(node->right == NULL);
+ assert(! node->right);
}
} else {
- assert(node->left == NULL);
+ assert(! node->left);
}
}
diff --git a/valexpr.h b/valexpr.h
index b91dd460..58eb4c99 100644
--- a/valexpr.h
+++ b/valexpr.h
@@ -349,7 +349,7 @@ public:
}
virtual ~value_expr() {
DEBUG_PRINT("ledger.memory.dtors", "dtor value_expr");
- if (parsed != NULL)
+ if (parsed)
parsed->release();
}
@@ -363,12 +363,12 @@ extern std::auto_ptr<value_calc> amount_expr;
extern std::auto_ptr<value_calc> total_expr;
inline void compute_amount(value_t& result, const details_t& details) {
- if (amount_expr.get() != NULL)
+ if (amount_expr.get())
amount_expr->compute(result, details);
}
inline void compute_total(value_t& result, const details_t& details) {
- if (total_expr.get() != NULL)
+ if (total_expr.get())
total_expr->compute(result, details);
}
diff --git a/walk.cc b/walk.cc
index c6ea3254..4bb6a38a 100644
--- a/walk.cc
+++ b/walk.cc
@@ -44,14 +44,8 @@ void add_transaction_to(const transaction_t& xact, value_t& value)
transaction_xdata_(xact).dflags & TRANSACTION_COMPOSITE) {
value += transaction_xdata_(xact).composite_amount;
}
- else if (xact.cost || xact.amount.commodity().price || value) {
- if (xact.amount.commodity().price) {
- amount_t price(*xact.amount.commodity().price);
- price *= xact.amount;
- value.add(base_amount(xact.amount), &price, xact.cost);
- } else {
- value.add(base_amount(xact.amount), NULL, xact.cost);
- }
+ else if (xact.cost || xact.amount.commodity().annotated || value) {
+ value.add(xact.amount, xact.cost);
}
else {
value = xact.amount;
@@ -500,7 +494,7 @@ void set_comm_as_payee::operator()(transaction_t& xact)
entry_t& entry = entry_temps.back();
entry._date = xact.date();
entry.code = xact.entry->code;
- entry.payee = xact.amount.commodity().symbol;
+ entry.payee = xact.amount.commodity().symbol();
xact_temps.push_back(xact);
transaction_t& temp = xact_temps.back();
@@ -829,7 +823,7 @@ void walk_commodities(commodities_map& commodities,
continue;
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())
for (history_map::iterator j = (*i).second->history()->prices.begin();
diff --git a/xml.cc b/xml.cc
index 24a664db..916d4217 100644
--- a/xml.cc
+++ b/xml.cc
@@ -117,24 +117,26 @@ static void endElement(void *userData, const char *name)
}
else if (std::strcmp(name, "symbol") == 0) {
assert(! curr_comm);
- curr_comm = commodity_t::find_commodity(data, true);
- curr_comm->flags() |= COMMODITY_STYLE_SUFFIXED;
+ curr_comm = commodity_t::find_or_create(data);
+ assert(curr_comm);
+ curr_comm->add_flags(COMMODITY_STYLE_SUFFIXED);
if (! comm_flags.empty()) {
for (std::string::size_type i = 0, l = comm_flags.length(); i < l; i++) {
switch (comm_flags[i]) {
- case 'P': curr_comm->flags() &= ~COMMODITY_STYLE_SUFFIXED; break;
- case 'S': curr_comm->flags() |= COMMODITY_STYLE_SEPARATED; break;
- case 'T': curr_comm->flags() |= COMMODITY_STYLE_THOUSANDS; break;
- case 'E': curr_comm->flags() |= COMMODITY_STYLE_EUROPEAN; break;
+ case 'P': curr_comm->drop_flags(COMMODITY_STYLE_SUFFIXED); break;
+ case 'S': curr_comm->add_flags(COMMODITY_STYLE_SEPARATED); break;
+ case 'T': curr_comm->add_flags(COMMODITY_STYLE_THOUSANDS); break;
+ case 'E': curr_comm->add_flags(COMMODITY_STYLE_EUROPEAN); break;
}
}
}
}
+#if 0
+ // jww (2006-03-02): !!!
else if (std::strcmp(name, "price") == 0) {
assert(curr_comm);
amount_t * price = new amount_t(data);
- std::string symbol;
- std::ostringstream symstr(symbol);
+ std::ostringstream symstr;
symstr << curr_comm->symbol << " {" << *price << "}";
commodity_t * priced_comm =
commodity_t::find_commodity(symstr.str(), true);
@@ -142,6 +144,7 @@ static void endElement(void *userData, const char *name)
priced_comm->base = curr_comm;
curr_comm = priced_comm;
}
+#endif
else if (std::strcmp(name, "quantity") == 0) {
curr_entry->transactions.back()->amount.parse(data);
if (curr_comm) {
@@ -149,7 +152,7 @@ static void endElement(void *userData, const char *name)
if (i != std::string::npos) {
int precision = data.length() - i - 1;
if (precision > curr_comm->precision())
- curr_comm->precision() = precision;
+ curr_comm->set_precision(precision);
}
curr_entry->transactions.back()->amount.set_commodity(*curr_comm);
curr_comm = NULL;
@@ -260,6 +263,8 @@ void xml_write_amount(std::ostream& out, const amount_t& amount,
if (c.flags() & COMMODITY_STYLE_EUROPEAN) out << 'E';
out << "\">\n";
for (int i = 0; i < depth + 4; i++) out << ' ';
+#if 0
+ // jww (2006-03-02): !!!
if (c.price) {
out << "<symbol>" << c.base->symbol << "</symbol>\n";
for (int i = 0; i < depth + 4; i++) out << ' ';
@@ -270,6 +275,7 @@ void xml_write_amount(std::ostream& out, const amount_t& amount,
} else {
out << "<symbol>" << c.symbol << "</symbol>\n";
}
+#endif
for (int i = 0; i < depth + 2; i++) out << ' ';
out << "</commodity>\n";