*** empty log message ***
This commit is contained in:
parent
31b68bbebc
commit
9800e3febc
9 changed files with 127 additions and 62 deletions
29
amount.cc
29
amount.cc
|
|
@ -596,10 +596,9 @@ bool amount_t::realzero() const
|
|||
amount_t amount_t::value(const std::time_t moment) const
|
||||
{
|
||||
if (quantity) {
|
||||
commodity_t& comm = commodity();
|
||||
if (! (comm.flags() & COMMODITY_STYLE_NOMARKET))
|
||||
if (amount_t amt = comm.value(moment))
|
||||
return (amt * *this).round();
|
||||
amount_t amt(commodity().value(moment));
|
||||
if (! amt.realzero())
|
||||
return (amt * *this).round();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
|
@ -973,6 +972,13 @@ void parse_annotations(std::istream& in, amount_t& price,
|
|||
|
||||
price.parse(buf, AMOUNT_PARSE_NO_MIGRATE);
|
||||
price.reduce();
|
||||
|
||||
// Since this price will maintain its own precision, make sure
|
||||
// it is at least as large as the base commodity, since the user
|
||||
// may have only specified {$1} or something similar.
|
||||
|
||||
if (price.quantity->prec < price.commodity().precision())
|
||||
price = price.round(); // no need to retain individual precision
|
||||
}
|
||||
else if (c == '[') {
|
||||
if (date)
|
||||
|
|
@ -1084,7 +1090,7 @@ void amount_t::parse(std::istream& in, unsigned char flags)
|
|||
}
|
||||
assert(commodity_);
|
||||
|
||||
if (price || date || ! tag.empty())
|
||||
if (! price.realzero() || date || ! tag.empty())
|
||||
commodity_ =
|
||||
annotated_commodity_t::find_or_create(*commodity_, price, date, tag);
|
||||
}
|
||||
|
|
@ -1435,6 +1441,19 @@ void commodity_base_t::add_price(const std::time_t date, const amount_t& price)
|
|||
}
|
||||
}
|
||||
|
||||
bool commodity_base_t::remove_price(const std::time_t date)
|
||||
{
|
||||
if (history) {
|
||||
history_map::size_type n = history->prices.erase(date);
|
||||
if (n > 0) {
|
||||
if (history->prices.empty())
|
||||
history = NULL;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
commodity_base_t * commodity_base_t::create(const std::string& symbol)
|
||||
{
|
||||
commodity_base_t * commodity = new commodity_base_t(symbol);
|
||||
|
|
|
|||
21
amount.h
21
amount.h
|
|
@ -283,6 +283,9 @@ class amount_t
|
|||
|
||||
friend void clean_commodity_history(char * item_pool,
|
||||
char * item_pool_end);
|
||||
|
||||
friend void parse_annotations(std::istream& in, amount_t& price,
|
||||
std::time_t& date, std::string& tag);
|
||||
};
|
||||
|
||||
unsigned int sizeof_bigint_t();
|
||||
|
|
@ -365,8 +368,9 @@ class commodity_base_t
|
|||
amount_t * smaller;
|
||||
amount_t * larger;
|
||||
|
||||
commodity_base_t() : precision(0), flags(COMMODITY_STYLE_DEFAULTS),
|
||||
history(NULL), smaller(NULL), larger(NULL) {}
|
||||
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,
|
||||
|
|
@ -388,18 +392,13 @@ class commodity_base_t
|
|||
struct history_t {
|
||||
history_map prices;
|
||||
std::time_t last_lookup;
|
||||
std::time_t bogus_time;
|
||||
history_t() : last_lookup(0), bogus_time(0) {}
|
||||
};
|
||||
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;
|
||||
}
|
||||
|
||||
void add_price(const std::time_t date, const amount_t& price);
|
||||
bool remove_price(const std::time_t date);
|
||||
amount_t value(const std::time_t moment = std::time(NULL));
|
||||
|
||||
class updater_t {
|
||||
|
|
|
|||
40
binary.cc
40
binary.cc
|
|
@ -12,9 +12,9 @@ namespace ledger {
|
|||
|
||||
static unsigned long binary_magic_number = 0xFFEED765;
|
||||
#ifdef DEBUG_ENABLED
|
||||
static unsigned long format_version = 0x00020607;
|
||||
static unsigned long format_version = 0x00020609;
|
||||
#else
|
||||
static unsigned long format_version = 0x00020606;
|
||||
static unsigned long format_version = 0x00020608;
|
||||
#endif
|
||||
|
||||
static account_t ** accounts;
|
||||
|
|
@ -415,6 +415,7 @@ inline void read_binary_commodity_base_extra(char *& data,
|
|||
{
|
||||
commodity_base_t * commodity = base_commodities[ident];
|
||||
|
||||
bool read_history = false;
|
||||
for (unsigned long i = 0, count = read_binary_long<unsigned long>(data);
|
||||
i < count;
|
||||
i++) {
|
||||
|
|
@ -429,8 +430,9 @@ inline void read_binary_commodity_base_extra(char *& data,
|
|||
if (! commodity->history)
|
||||
commodity->history = new commodity_base_t::history_t;
|
||||
commodity->history->prices.insert(history_pair(when, amt));
|
||||
read_history = true;
|
||||
}
|
||||
if (commodity->history)
|
||||
if (read_history)
|
||||
read_binary_long(data, commodity->history->last_lookup);
|
||||
|
||||
unsigned char flag;
|
||||
|
|
@ -629,8 +631,7 @@ unsigned int read_binary_journal(std::istream& in,
|
|||
// expression passed to an option, we'll just override the
|
||||
// flags, but keep the commodity pointer intact.
|
||||
if (c == commodity_base_t::commodities.end() ||
|
||||
(*c).second->history || (*c).second->smaller ||
|
||||
(*c).second->larger)
|
||||
(*c).second->smaller || (*c).second->larger)
|
||||
throw new error(std::string("Failed to read base commodity from cache: ") +
|
||||
commodity->symbol);
|
||||
|
||||
|
|
@ -638,7 +639,6 @@ unsigned int read_binary_journal(std::istream& in,
|
|||
(*c).second->note = commodity->note;
|
||||
(*c).second->precision = commodity->precision;
|
||||
(*c).second->flags = commodity->flags;
|
||||
(*c).second->history = commodity->history;
|
||||
(*c).second->smaller = commodity->smaller;
|
||||
(*c).second->larger = commodity->larger;
|
||||
|
||||
|
|
@ -646,9 +646,6 @@ unsigned int read_binary_journal(std::istream& in,
|
|||
}
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
|
|
@ -680,6 +677,9 @@ unsigned int read_binary_journal(std::istream& in,
|
|||
commodity->base->symbol);
|
||||
}
|
||||
|
||||
for (commodity_base_t::ident_t i = 0; i < bc_count; i++)
|
||||
read_binary_commodity_base_extra(data, i);
|
||||
|
||||
commodity_t::ident_t ident;
|
||||
read_binary_long(data, ident);
|
||||
if (ident == 0xffffffff || ident == 0)
|
||||
|
|
@ -971,8 +971,12 @@ void write_binary_commodity_base(std::ostream& out, commodity_base_t * commodity
|
|||
write_binary_number(out, commodity->flags);
|
||||
}
|
||||
|
||||
void write_binary_commodity_base_extra(std::ostream& out, commodity_base_t * commodity)
|
||||
void write_binary_commodity_base_extra(std::ostream& out,
|
||||
commodity_base_t * commodity)
|
||||
{
|
||||
if (commodity->history && commodity->history->bogus_time)
|
||||
commodity->remove_price(commodity->history->bogus_time);
|
||||
|
||||
if (! commodity->history) {
|
||||
write_binary_long<unsigned long>(out, 0);
|
||||
} else {
|
||||
|
|
@ -1127,12 +1131,6 @@ void write_binary_journal(std::ostream& out, journal_t * journal)
|
|||
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_base_extra(out, (*i).second);
|
||||
|
||||
write_binary_long<commodity_t::ident_t>
|
||||
(out, commodity_t::commodities.size());
|
||||
|
||||
|
|
@ -1155,6 +1153,16 @@ void write_binary_journal(std::ostream& out, journal_t * journal)
|
|||
}
|
||||
}
|
||||
|
||||
// Write out the history and smaller/larger convertible links after
|
||||
// both the base and the main commodities have been written, since
|
||||
// the amounts in both will refer to the mains.
|
||||
|
||||
for (base_commodities_map::const_iterator i =
|
||||
commodity_base_t::commodities.begin();
|
||||
i != commodity_base_t::commodities.end();
|
||||
i++)
|
||||
write_binary_commodity_base_extra(out, (*i).second);
|
||||
|
||||
if (commodity_t::default_commodity)
|
||||
write_binary_long(out, commodity_t::default_commodity->ident);
|
||||
else
|
||||
|
|
|
|||
50
config.cc
50
config.cc
|
|
@ -106,21 +106,10 @@ std::string expand_path(const std::string& path)
|
|||
return result;
|
||||
}
|
||||
|
||||
std::string resolve_path(const std::string& path)
|
||||
{
|
||||
std::string resolved;;
|
||||
inline std::string resolve_path(const std::string& path) {
|
||||
if (path[0] == '~')
|
||||
resolved = expand_path(path);
|
||||
else
|
||||
resolved = path;
|
||||
|
||||
#ifdef HAVE_REALPATH
|
||||
char buf[PATH_MAX];
|
||||
::realpath(resolved.c_str(), buf);
|
||||
return std::string(buf);
|
||||
#else
|
||||
return resolved;
|
||||
#endif
|
||||
return expand_path(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
void config_t::reset()
|
||||
|
|
@ -1256,6 +1245,38 @@ OPT_BEGIN(market, "V") {
|
|||
ledger::total_expr.reset(new value_expr("V"));
|
||||
} OPT_END(market);
|
||||
|
||||
namespace {
|
||||
void parse_price_setting(const char * optarg)
|
||||
{
|
||||
char * equals = std::strchr(optarg, '=');
|
||||
if (! equals)
|
||||
return;
|
||||
|
||||
while (std::isspace(*optarg))
|
||||
optarg++;
|
||||
while (equals > optarg && std::isspace(*(equals - 1)))
|
||||
equals--;
|
||||
|
||||
std::string symbol(optarg, 0, equals - optarg);
|
||||
amount_t price(equals + 1);
|
||||
|
||||
if (commodity_t * commodity = commodity_t::find_or_create(symbol)) {
|
||||
commodity->add_price(now, price);
|
||||
commodity->history()->bogus_time = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OPT_BEGIN(set_price, ":") {
|
||||
std::string arg(optarg);
|
||||
std::string::size_type beg = 0;
|
||||
for (std::string::size_type pos = arg.find(';');
|
||||
pos != std::string::npos;
|
||||
beg = pos + 1, pos = arg.find(';', beg))
|
||||
parse_price_setting(std::string(arg, beg, pos).c_str());
|
||||
parse_price_setting(std::string(arg, beg).c_str());
|
||||
} OPT_END(set_price);
|
||||
|
||||
OPT_BEGIN(performance, "g") {
|
||||
ledger::amount_expr.reset(new value_expr("P(a,m)-b"));
|
||||
ledger::total_expr.reset(new value_expr("P(O,m)-B"));
|
||||
|
|
@ -1362,6 +1383,7 @@ option_t config_options[CONFIG_OPTIONS_SIZE] = {
|
|||
{ "reconcile-date", '\0', true, opt_reconcile_date, false },
|
||||
{ "register-format", '\0', true, opt_register_format, false },
|
||||
{ "related", 'r', false, opt_related, false },
|
||||
{ "set-price", '\0', true, opt_set_price, false },
|
||||
{ "sort", 'S', true, opt_sort, false },
|
||||
{ "subtotal", 's', false, opt_subtotal, false },
|
||||
{ "tail", '\0', true, opt_tail, false },
|
||||
|
|
|
|||
2
config.h
2
config.h
|
|
@ -107,7 +107,7 @@ class config_t
|
|||
std::list<item_handler<transaction_t> *>& ptrs);
|
||||
};
|
||||
|
||||
#define CONFIG_OPTIONS_SIZE 89
|
||||
#define CONFIG_OPTIONS_SIZE 90
|
||||
extern option_t config_options[CONFIG_OPTIONS_SIZE];
|
||||
|
||||
void option_help(std::ostream& out);
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ bool entry_base_t::finalize()
|
|||
continue;
|
||||
|
||||
if (! empty_allowed)
|
||||
break;
|
||||
throw new error("Only one transaction with null amount allowed per entry");
|
||||
empty_allowed = false;
|
||||
|
||||
// If one transaction gives no value at all, its value will become
|
||||
|
|
|
|||
3
main.cc
3
main.cc
|
|
@ -389,7 +389,8 @@ int parse_and_report(config_t& config, int argc, char * argv[], char * envp[])
|
|||
|
||||
// Write out the binary cache, if need be
|
||||
|
||||
if (config.use_cache && config.cache_dirty && ! config.cache_file.empty()) {
|
||||
if (config.use_cache && config.cache_dirty &&
|
||||
! config.cache_file.empty()) {
|
||||
TRACE_PUSH(binary_cache, "Writing journal file");
|
||||
|
||||
std::ofstream stream(config.cache_file.c_str());
|
||||
|
|
|
|||
38
textual.cc
38
textual.cc
|
|
@ -630,20 +630,36 @@ unsigned int textual_parser_t::parse(std::istream& in,
|
|||
char * date_field = skip_ws(line + 1);
|
||||
char * time_field = next_element(date_field);
|
||||
if (! time_field) break;
|
||||
char * symbol_and_price = next_element(time_field);
|
||||
if (! symbol_and_price) break;
|
||||
|
||||
char date_buffer[64];
|
||||
std::strcpy(date_buffer, date_field);
|
||||
date_buffer[std::strlen(date_field)] = ' ';
|
||||
std::strcpy(&date_buffer[std::strlen(date_field) + 1], time_field);
|
||||
|
||||
std::time_t date;
|
||||
char * symbol_and_price;
|
||||
std::time_t date;
|
||||
struct std::tm when;
|
||||
if (strptime(date_buffer, "%Y/%m/%d %H:%M:%S", &when)) {
|
||||
date = std::mktime(&when);
|
||||
|
||||
if (std::isdigit(time_field[0])) {
|
||||
symbol_and_price = next_element(time_field);
|
||||
if (! symbol_and_price) break;
|
||||
|
||||
char date_buffer[64];
|
||||
std::strcpy(date_buffer, date_field);
|
||||
date_buffer[std::strlen(date_field)] = ' ';
|
||||
std::strcpy(&date_buffer[std::strlen(date_field) + 1], time_field);
|
||||
|
||||
if (strptime(date_buffer, "%Y/%m/%d %H:%M:%S", &when)) {
|
||||
date = std::mktime(&when);
|
||||
} else {
|
||||
throw new parse_error("Failed to parse date/time");
|
||||
}
|
||||
} else {
|
||||
throw new parse_error("Failed to parse date");
|
||||
symbol_and_price = time_field;
|
||||
|
||||
if (strptime(date_field, "%Y/%m/%d", &when)) {
|
||||
when.tm_hour = 0;
|
||||
when.tm_min = 0;
|
||||
when.tm_sec = 0;
|
||||
date = std::mktime(&when);
|
||||
} else {
|
||||
throw new parse_error("Failed to parse date");
|
||||
}
|
||||
}
|
||||
|
||||
std::string symbol;
|
||||
|
|
|
|||
4
walk.cc
4
walk.cc
|
|
@ -42,7 +42,7 @@ 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 || value) {
|
||||
else if (xact.cost || ! value.realzero()) {
|
||||
value.add(xact.amount, xact.cost);
|
||||
}
|
||||
else {
|
||||
|
|
@ -784,7 +784,7 @@ void sum_accounts(account_t& account)
|
|||
|
||||
value_t result;
|
||||
compute_amount(result, details_t(account));
|
||||
if (result)
|
||||
if (! result.realzero())
|
||||
xdata.total += result;
|
||||
xdata.total_count += xdata.count;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue