summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config.cc12
-rw-r--r--datetime.cc4
-rw-r--r--datetime.h3
-rw-r--r--ledger.h4
-rw-r--r--main.cc113
-rw-r--r--valexpr.cc119
-rw-r--r--valexpr.h12
-rw-r--r--walk.h2
8 files changed, 169 insertions, 100 deletions
diff --git a/config.cc b/config.cc
index 99c4d4b4..2df1b6b1 100644
--- a/config.cc
+++ b/config.cc
@@ -26,7 +26,7 @@ config_t::config_t()
price_db += "/.pricedb";
value_expr = "a";
- total_expr = "T";
+ total_expr = "O";
pricing_leeway = 24 * 3600;
show_subtotals = true;
show_expanded = false;
@@ -301,7 +301,7 @@ OPT_BEGIN(download, "Q") {
OPT_BEGIN(quantity, "O") {
config->value_expr = "a";
- config->total_expr = "T";
+ config->total_expr = "O";
} OPT_END(quantity);
OPT_BEGIN(basis, "B") {
@@ -326,23 +326,23 @@ OPT_BEGIN(gain, "G") {
OPT_BEGIN(average, "A") {
config->value_expr = "a";
- config->total_expr = "MT";
+ config->total_expr = "MO";
} OPT_END(average);
OPT_BEGIN(deviation, "D") {
config->value_expr = "a";
- config->total_expr = "DMT";
+ config->total_expr = "DMO";
} OPT_END(deviation);
OPT_BEGIN(trend, "X") {
config->value_expr = "a";
- config->total_expr = "MDMT";
+ config->total_expr = "MDMO";
} OPT_END(trend);
OPT_BEGIN(weighted_trend, "Z") {
config->value_expr = "a";
config->total_expr
- = "MD(MT/(1+(((t-d)/(30*86400))<0?0:((t-d)/(30*86400)))))";
+ = "MD(MO/(1+(((t-d)/(30*86400))<0?0:((t-d)/(30*86400)))))";
} OPT_END(weighted_trend);
} // namespace ledger
diff --git a/datetime.cc b/datetime.cc
index 586722b5..4f6cf0d3 100644
--- a/datetime.cc
+++ b/datetime.cc
@@ -4,8 +4,8 @@
namespace ledger {
-static std::time_t now = std::time(NULL);
- struct std::tm * now_tm = std::localtime(&now);
+std::time_t now = std::time(NULL);
+struct std::tm * now_tm = std::localtime(&now);
static std::time_t base = -1;
static int base_year = -1;
diff --git a/datetime.h b/datetime.h
index 1acf2609..d546dbaa 100644
--- a/datetime.h
+++ b/datetime.h
@@ -3,6 +3,8 @@
#include "ledger.h"
+#include <ctime>
+
namespace ledger {
struct interval_t
@@ -20,6 +22,7 @@ struct interval_t
static interval_t * parse(std::istream& in);
};
+extern std::time_t now;
extern struct std::tm * now_tm;
bool parse_date_mask(const char * date_str, struct std::tm * result);
diff --git a/ledger.h b/ledger.h
index 6c11fa5a..02418981 100644
--- a/ledger.h
+++ b/ledger.h
@@ -125,6 +125,7 @@ class account_t
mutable balance_pair_t value;
mutable balance_pair_t total;
+ mutable unsigned int count; // transactions counted toward total
mutable ident_t ident;
mutable unsigned short dflags;
mutable std::string _fullname;
@@ -135,7 +136,8 @@ class account_t
const std::string& _name = "",
const std::string& _note = "")
: parent(_parent), name(_name), note(_note),
- depth(parent ? parent->depth + 1 : 0), dflags(0) {}
+ depth(parent ? parent->depth + 1 : 0),
+ count(0), dflags(0) {}
~account_t();
diff --git a/main.cc b/main.cc
index bf82d12a..d7f71b7f 100644
--- a/main.cc
+++ b/main.cc
@@ -66,6 +66,66 @@ namespace std {
#endif // NO_CLEANUP
+static void
+regexps_to_predicate(std::list<std::string>::const_iterator begin,
+ std::list<std::string>::const_iterator end,
+ config_t * config, const bool account_regexp = false,
+ const bool add_account_short_masks = false)
+{
+ std::vector<std::string> regexps(2);
+
+ // Treat the remaining command-line arguments as regular
+ // expressions, used for refining report results.
+
+ for (std::list<std::string>::const_iterator i = begin;
+ i != end;
+ i++)
+ if ((*i)[0] == '-') {
+ if (! regexps[1].empty())
+ regexps[1] += "|";
+ regexps[1] += (*i).substr(1);
+ }
+ else if ((*i)[0] == '+') {
+ if (! regexps[0].empty())
+ regexps[0] += "|";
+ regexps[0] += (*i).substr(1);
+ }
+ else {
+ if (! regexps[0].empty())
+ regexps[0] += "|";
+ regexps[0] += *i;
+ }
+
+ for (std::vector<std::string>::const_iterator i = regexps.begin();
+ i != regexps.end();
+ i++)
+ if (! (*i).empty()) {
+ if (! config->predicate.empty())
+ config->predicate += "&";
+
+ if (i != regexps.begin()) {
+ config->predicate += "!";
+ }
+ else if (add_account_short_masks &&
+ (*i).find(':') == std::string::npos) {
+ if (! config->display_predicate.empty())
+ config->display_predicate += "&";
+ else if (! config->show_empty)
+ config->display_predicate += "T&";
+
+ config->display_predicate += "///(?:";
+ config->display_predicate += *i;
+ config->display_predicate += ")/";
+ }
+
+ if (! account_regexp)
+ config->predicate += "/";
+ config->predicate += "/(?:";
+ config->predicate += *i;
+ config->predicate += ")/";
+ }
+}
+
int main(int argc, char * argv[], char * envp[])
{
std::auto_ptr<journal_t> journal(new journal_t);
@@ -123,25 +183,16 @@ int main(int argc, char * argv[], char * envp[])
throw error("Entries not allowed in initialization file");
if (use_cache && ! config->cache_file.empty()) {
- journal->sources.clear(); // remove init_file
entry_count += parse_journal_file(config->cache_file, journal.get());
- journal->sources.pop_front(); // remove cache_file
-
- strings_list exceptions;
- std::set_difference(journal->sources.begin(), journal->sources.end(),
- config->files.begin(), config->files.end(),
- std::back_insert_iterator<strings_list>(exceptions));
-
- if (entry_count == 0 || exceptions.size() > 0) {
+ if (entry_count == 0) {
journal.reset(new journal_t);
- entry_count = 0;
cache_dirty = true;
} else {
cache_dirty = false;
}
}
- if (entry_count == 0)
+ if (entry_count == 0) {
for (strings_list::iterator i = config->files.begin();
i != config->files.end();
i++)
@@ -153,9 +204,10 @@ int main(int argc, char * argv[], char * envp[])
entry_count += parse_journal_file(*i, journal.get());
}
- if (! config->price_db.empty())
- if (parse_journal_file(config->price_db, journal.get()))
- throw error("Entries not allowed in price history file");
+ if (! config->price_db.empty())
+ if (parse_journal_file(config->price_db, journal.get()))
+ throw error("Entries not allowed in price history file");
+ }
for (strings_list::iterator i = config->price_settings.begin();
i != config->price_settings.end();
@@ -219,21 +271,11 @@ int main(int argc, char * argv[], char * envp[])
if (*i == "--")
break;
- const std::string pred = regexps_to_predicate(arg, i);
- if (! pred.empty()) {
- if (! config->predicate.empty())
- config->predicate += "&";
- config->predicate += pred;
- }
-
- if (i != args.end()) {
- const std::string pred = regexps_to_predicate(i, args.end(), false);
- if (! pred.empty()) {
- if (! config->predicate.empty())
- config->predicate += "&";
- config->predicate += pred;
- }
- }
+ regexps_to_predicate(arg, i, config, true,
+ command == "b" && ! config->show_expanded &&
+ config->display_predicate.empty());
+ if (i != args.end())
+ regexps_to_predicate(i, args.end(), config);
}
// Compile the predicates
@@ -242,18 +284,23 @@ int main(int argc, char * argv[], char * envp[])
if (command == "b") {
if (! config->show_empty)
config->display_predicate = "T";
-
- if (! config->show_expanded && config->predicate.empty()) {
+ if (! config->show_expanded) {
if (! config->display_predicate.empty())
config->display_predicate += "&";
- config->display_predicate += "!n";
+ config->display_predicate += "!l";
}
}
else if (command == "E") {
- config->display_predicate = "a";
+ config->display_predicate = "t";
}
}
+#ifdef DEBUG_ENABLED
+ DEBUG_PRINT("ledger.main.predicates", "predicate: " << config->predicate);
+ DEBUG_PRINT("ledger.main.predicates",
+ "disp-pred: " << config->display_predicate);
+#endif
+
// Compile the sorting criteria
std::auto_ptr<value_expr_t> sort_order;
diff --git a/valexpr.cc b/valexpr.cc
index 5dfac952..6801cf7e 100644
--- a/valexpr.cc
+++ b/valexpr.cc
@@ -1,4 +1,5 @@
#include "valexpr.h"
+#include "format.h"
#include "error.h"
#include "datetime.h"
#include "debug.h"
@@ -119,15 +120,32 @@ void value_expr_t::compute(balance_t& result, const details_t& details) const
result = details.account->total.cost;
break;
+ case VALUE_EXPR:
+#ifdef NO_CLEANUP
+ assert(format_t::value_expr);
+#else
+ assert(format_t::value_expr.get());
+#endif
+ format_t::value_expr->compute(result, details);
+ break;
+ case TOTAL_EXPR:
+#ifdef NO_CLEANUP
+ assert(format_t::total_expr);
+#else
+ assert(format_t::total_expr.get());
+#endif
+ format_t::total_expr->compute(result, details);
+ break;
+
case DATE:
if (details.entry)
result = (unsigned int) details.entry->date;
else
- result = (unsigned int) std::time(NULL);
+ result = (unsigned int) now;
break;
case TODAY:
- result = (unsigned int) std::time(NULL);
+ result = (unsigned int) now;
break;
case CLEARED:
@@ -169,6 +187,11 @@ void value_expr_t::compute(balance_t& result, const details_t& details) const
case INDEX:
if (details.xact)
result = details.xact->index + 1;
+ break;
+
+ case DEPTH:
+ if (details.xact)
+ result = details.xact->account->depth - 1;
else if (details.account)
result = details.account->depth - 1;
break;
@@ -179,6 +202,11 @@ void value_expr_t::compute(balance_t& result, const details_t& details) const
left->compute(result, details);
result /= amount_t(details.xact->index + 1);
}
+ else if (details.account && details.account->count) {
+ assert(left);
+ left->compute(result, details);
+ result /= amount_t(details.account->count);
+ }
break;
case F_NEG:
@@ -214,6 +242,12 @@ void value_expr_t::compute(balance_t& result, const details_t& details) const
result = mask->match(details.account->fullname());
break;
+ case F_SHORT_ACCOUNT_MASK:
+ assert(mask);
+ if (details.account)
+ result = mask->match(details.account->name);
+ break;
+
case F_VALUE: {
assert(left);
left->compute(result, details);
@@ -225,18 +259,18 @@ void value_expr_t::compute(balance_t& result, const details_t& details) const
if (details.entry)
moment = details.entry->date;
else
- moment = std::time(NULL);
+ moment = now;
break;
case TODAY:
- moment = std::time(NULL);
+ moment = now;
break;
default:
throw compute_error("Invalid date passed to P(value,date)");
}
} else {
- moment = std::time(NULL);
+ moment = now;
}
result = result.value(moment);
break;
@@ -359,16 +393,21 @@ value_expr_t * parse_value_term(std::istream& in)
case 'a': node = new value_expr_t(value_expr_t::AMOUNT); break;
case 'c': node = new value_expr_t(value_expr_t::COST); break;
case 'd': node = new value_expr_t(value_expr_t::DATE); break;
- case 't': node = new value_expr_t(value_expr_t::TODAY); break;
+ case 'N': node = new value_expr_t(value_expr_t::TODAY); break;
case 'X': node = new value_expr_t(value_expr_t::CLEARED); break;
case 'R': node = new value_expr_t(value_expr_t::REAL); break;
case 'n': node = new value_expr_t(value_expr_t::INDEX); break;
+ case 'l': node = new value_expr_t(value_expr_t::DEPTH); break;
case 'B': node = new value_expr_t(value_expr_t::BALANCE); break;
- case 'T': node = new value_expr_t(value_expr_t::TOTAL); break;
+ case 'O': node = new value_expr_t(value_expr_t::TOTAL); break;
case 'C': node = new value_expr_t(value_expr_t::COST_TOTAL); break;
+ // Relating to format_t
+ case 't': node = new value_expr_t(value_expr_t::VALUE_EXPR); break;
+ case 'T': node = new value_expr_t(value_expr_t::TOTAL_EXPR); break;
+
// Compound terms
- case 'v': node = parse_value_expr("P(a,d)"); break;
+ case 'v': node = parse_value_expr("P(t,d)"); break;
case 'V': node = parse_value_term("P(T,d)"); break;
case 'g': node = parse_value_expr("v-c"); break;
case 'G': node = parse_value_expr("V-C"); break;
@@ -423,13 +462,20 @@ value_expr_t * parse_value_term(std::istream& in)
// Other
case '/': {
- bool payee_mask = false;
+ bool payee_mask = false;
+ bool short_account_mask = false;
c = peek_next_nonws(in);
if (c == '/') {
- payee_mask = true;
in.get(c);
c = in.peek();
+ if (c == '/') {
+ in.get(c);
+ c = in.peek();
+ short_account_mask = true;
+ } else {
+ payee_mask = true;
+ }
}
static char buf[4096];
@@ -438,8 +484,10 @@ value_expr_t * parse_value_term(std::istream& in)
throw value_expr_error("Missing closing '/'");
in.get(c);
- node = new value_expr_t(payee_mask ? value_expr_t::F_PAYEE_MASK :
- value_expr_t::F_ACCOUNT_MASK);
+ node = new value_expr_t(short_account_mask ?
+ value_expr_t::F_SHORT_ACCOUNT_MASK :
+ (payee_mask ? value_expr_t::F_PAYEE_MASK :
+ value_expr_t::F_ACCOUNT_MASK));
node->mask = new mask_t(buf);
break;
}
@@ -664,47 +712,6 @@ value_expr_t * parse_value_expr(std::istream& in)
return node;
}
-std::string regexps_to_predicate(std::list<std::string>::const_iterator begin,
- std::list<std::string>::const_iterator end,
- const bool account_regexp)
-{
- std::vector<std::string> regexps(2);
- std::string pred;
-
- // Treat the remaining command-line arguments as regular
- // expressions, used for refining report results.
-
- for (std::list<std::string>::const_iterator i = begin;
- i != end;
- i++)
- if ((*i)[0] == '-') {
- if (! regexps[1].empty())
- regexps[1] += "|";
- regexps[1] += (*i).substr(1);
- } else {
- if (! regexps[0].empty())
- regexps[0] += "|";
- regexps[0] += *i;
- }
-
- for (std::vector<std::string>::const_iterator i = regexps.begin();
- i != regexps.end();
- i++)
- if (! (*i).empty()) {
- if (! pred.empty())
- pred += "&";
- if (i != regexps.begin())
- pred += "!";
- if (! account_regexp)
- pred += "/";
- pred += "/(?:";
- pred += *i;
- pred += ")/";
- }
-
- return pred;
-}
-
#ifdef DEBUG_ENABLED
void dump_value_expr(std::ostream& out, const value_expr_t * node)
@@ -724,6 +731,7 @@ void dump_value_expr(std::ostream& out, const value_expr_t * node)
case value_expr_t::CLEARED: out << "CLEARED"; break;
case value_expr_t::REAL: out << "REAL"; break;
case value_expr_t::INDEX: out << "INDEX"; break;
+ case value_expr_t::DEPTH: out << "DEPTH"; break;
case value_expr_t::BALANCE: out << "BALANCE"; break;
case value_expr_t::COST_BALANCE: out << "COST_BALANCE"; break;
case value_expr_t::TOTAL: out << "TOTAL"; break;
@@ -763,6 +771,11 @@ void dump_value_expr(std::ostream& out, const value_expr_t * node)
out << "A_MASK(" << node->mask->pattern << ")";
break;
+ case value_expr_t::F_SHORT_ACCOUNT_MASK:
+ assert(node->mask);
+ out << "A_SMASK(" << node->mask->pattern << ")";
+ break;
+
case value_expr_t::F_VALUE:
out << "VALUE(";
dump_value_expr(out, node->left);
diff --git a/valexpr.h b/valexpr.h
index 81b20495..d6d590bc 100644
--- a/valexpr.h
+++ b/valexpr.h
@@ -52,7 +52,8 @@ struct value_expr_t
TODAY,
CLEARED,
REAL,
- INDEX, // for accounts, this is the DEPTH
+ INDEX,
+ DEPTH,
// Item totals
BALANCE,
@@ -60,6 +61,10 @@ struct value_expr_t
TOTAL,
COST_TOTAL,
+ // Relating to format_t
+ VALUE_EXPR,
+ TOTAL_EXPR,
+
// Functions
F_ARITH_MEAN,
F_VALUE,
@@ -68,6 +73,7 @@ struct value_expr_t
F_STRIP,
F_PAYEE_MASK,
F_ACCOUNT_MASK,
+ F_SHORT_ACCOUNT_MASK,
// Binary operators
O_ADD,
@@ -174,10 +180,6 @@ class item_predicate
}
};
-std::string regexps_to_predicate(std::list<std::string>::const_iterator begin,
- std::list<std::string>::const_iterator end,
- const bool account_regexp = true);
-
} // namespace report
#endif // _REPORT_H
diff --git a/walk.h b/walk.h
index 42a20ed1..07e592ce 100644
--- a/walk.h
+++ b/walk.h
@@ -117,6 +117,7 @@ class add_to_account_value : public item_handler<transaction_t>
public:
virtual void operator()(transaction_t * xact) {
xact->account->value += *xact;
+ xact->account->count++;
}
};
@@ -368,6 +369,7 @@ inline void sum_accounts(account_t * account) {
i++) {
sum_accounts((*i).second);
account->total += (*i).second->total;
+ account->count += (*i).second->count;
}
account->total += account->value;
}