diff options
-rw-r--r-- | format.cc | 413 | ||||
-rw-r--r-- | main.cc | 110 | ||||
-rw-r--r-- | mask.cc | 29 | ||||
-rw-r--r-- | mask.h | 5 | ||||
-rw-r--r-- | op.cc | 42 | ||||
-rw-r--r-- | report.cc | 123 | ||||
-rw-r--r-- | report.h | 23 | ||||
-rw-r--r-- | xact.cc | 6 |
8 files changed, 158 insertions, 593 deletions
@@ -272,430 +272,33 @@ void format_t::format(std::ostream& out_str, scope_t& scope) try { elem->expr.compile(scope); + value_t value; if (elem->expr.is_function()) { call_scope_t args(scope); args.push_back(long(elem->max_width)); - elem->expr.get_function()(args).dump(out, elem->min_width); + value = elem->expr.get_function()(args); } else { - elem->expr.calc(scope).dump(out, elem->min_width); + value = elem->expr.calc(scope); } + value.strip_annotations().dump(out, elem->min_width); } catch (const calc_error&) { out << (string("%") + elem->chars); } break; -#if 0 - case element_t::ACCOUNT_FULLNAME: - scope.resolve("account").dump(out, elem->min_width); - break; - case element_t::ACCOUNT_NAME: - scope.resolve("account_base").dump(out, elem->min_width); - break; - - case element_t::AMOUNT: - out << "a"; - //out << scope.resolve("amount"); - break; - case element_t::TOTAL: - out << "T"; - //out << scope.resolve("total"); - break; - - case element_t::VALUE_EXPR: { - expr_t * calc; - switch (elem->type) { - case element_t::AMOUNT: - assert(value_expr::amount_expr.get()); - calc = value_expr::amount_expr.get(); - break; - case element_t::TOTAL: - assert(value_expr::total_expr.get()); - calc = value_expr::total_expr.get(); - break; - case element_t::VALUE_EXPR: - calc = const_cast<value_expr *>(&elem->val_expr); - break; - default: - assert(false); - break; - } - if (! calc) - break; - - value_t value; - const balance_t * bal = NULL; - - calc->compute(value, details); - - if (! amount_t::keep_price || - ! amount_t::keep_date || - ! amount_t::keep_tag) { - switch (value.type()) { - case value_t::AMOUNT: - case value_t::BALANCE: - case value_t::BALANCE_PAIR: - value = value.strip_annotations(); - break; - default: - break; - } - } - - bool highlighted = false; - - switch (value.type()) { - case value_t::BOOLEAN: - out << (value.as_boolean() ? "true" : "false"); - break; - - case value_t::INTEGER: - if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) { - if (ansi_invert) { - if (value.as_long() > 0) { - mark_red(out, elem); - highlighted = true; - } - } else { - if (value.as_long() < 0) { - mark_red(out, elem); - highlighted = true; - } - } - } - out << value.as_long(); - break; - - case value_t::DATETIME: - out << value.as_datetime(); - break; - - case value_t::AMOUNT: - if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) { - if (ansi_invert) { - if (value.as_amount().sign() > 0) { - mark_red(out, elem); - highlighted = true; - } - } else { - if (value.as_amount().sign() < 0) { - mark_red(out, elem); - highlighted = true; - } - } - } - out << value.as_amount(); - break; - - case value_t::BALANCE: - bal = &(value.as_balance()); - // fall through... - - case value_t::BALANCE_PAIR: - if (! bal) - bal = &(value.as_balance_pair().quantity()); - - if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) { - if (ansi_invert) { - if (*bal > 0) { - mark_red(out, elem); - highlighted = true; - } - } else { - if (*bal < 0) { - mark_red(out, elem); - highlighted = true; - } - } - } - bal->print(out, elem->min_width, - (elem->max_width > 0 ? - elem->max_width : elem->min_width)); - - ignore_max_width = true; - break; - default: - assert(false); - break; - } - - if (highlighted) - mark_plain(out); - break; - } - - case element_t::OPT_AMOUNT: - if (details.xact) { - string disp; - bool use_disp = false; - - if (details.xact->cost && details.xact->amount) { - std::ostringstream stream; - if (! details.xact->amount_expr.expr_str.empty()) - stream << details.xact->amount_expr.expr_str; - else - stream << details.xact->amount.strip_annotations(); - - if (details.xact->cost_expr) - stream << details.xact->cost_expr->expr_str; - else - stream << " @ " << amount_t(*details.xact->cost / - details.xact->amount).unround(); - disp = stream.str(); - use_disp = true; - } - else if (details.entry) { - unsigned int xacts_count = 0; - xact_t * first = NULL; - xact_t * last = NULL; - - foreach (const transaction_t * xact, details.entry->xacts) { - if (xact_has_xdata(*xact) && - xact_xdata_(*xact).dflags & XACT_TO_DISPLAY) { - xacts_count++; - if (! first) - first = xact; - last = xact; - } - } - - use_disp = (xacts_count == 2 && details.xact == last && - first->amount == - last->amount); - } - - if (! use_disp) { - if (! details.xact->amount_expr.expr_str.empty()) - out << details.xact->amount_expr.expr_str; - else - out << details.xact->amount.strip_annotations(); - } else { - out << disp; - } - } - break; - - case element_t::SOURCE: - if (details.entry && details.entry->journal) { - int idx = details.entry->src_idx; - foreach (const path& path, details.entry->journal->sources) - if (! idx--) { - out << path; - break; - } - } - break; - - case element_t::ENTRY_BEG_POS: - if (details.entry) - out << (unsigned long)details.entry->beg_pos; - break; - - case element_t::ENTRY_BEG_LINE: - if (details.entry) - out << details.entry->beg_line; - break; - - case element_t::ENTRY_END_POS: - if (details.entry) - out << (unsigned long)details.entry->end_pos; - break; - - case element_t::ENTRY_END_LINE: - if (details.entry) - out << details.entry->end_line; - break; - - case element_t::XACT_BEG_POS: - if (details.xact) - out << (unsigned long)details.xact->beg_pos; - break; - - case element_t::XACT_BEG_LINE: - if (details.xact) - out << details.xact->beg_line; - break; - - case element_t::XACT_END_POS: - if (details.xact) - out << (unsigned long)details.xact->end_pos; - break; - - case element_t::XACT_END_LINE: - if (details.xact) - out << details.xact->end_line; - break; - - case element_t::DATE_STRING: - out << format_date(scope.resolve("date").as_date()); - break; - - case element_t::COMPLETE_DATE_STRING: { - date_t actual_date; - date_t effective_date; - if (details.xact) { - actual_date = details.xact->actual_date(); - effective_date = details.xact->effective_date(); - } - else if (details.entry) { - actual_date = details.entry->actual_date(); - effective_date = details.entry->effective_date(); - } - - char abuf[256]; -#if 0 - // jww (2008-04-20): This needs to be rewritten - std::strftime(abuf, 255, elem->chars.c_str(), actual_date.localtime()); -#else - abuf[0] = '\0'; -#endif - - if (is_valid(effective_date) && effective_date != actual_date) { - char buf[512]; - char ebuf[256]; -#if 0 - // jww (2008-04-20): This needs to be rewritten - std::strftime(ebuf, 255, elem->chars.c_str(), - effective_date.localtime()); -#else - ebuf[0] = '\0'; -#endif - - std::strcpy(buf, abuf); - std::strcat(buf, "="); - std::strcat(buf, ebuf); - - out << (elem->max_width == 0 ? buf : truncate(buf, elem->max_width)); - } else { - out << (elem->max_width == 0 ? abuf : truncate(abuf, elem->max_width)); - } - break; - } - - case element_t::CLEARED: - if (details.xact) { - switch (details.xact->state) { - case xact_t::CLEARED: - out << "* "; - break; - case xact_t::PENDING: - out << "! "; - break; - case xact_t::UNCLEARED: - break; - } - } - break; - - case element_t::ENTRY_CLEARED: - if (details.entry) { - xact_t::state_t state; - if (details.entry->get_state(&state)) - switch (state) { - case xact_t::CLEARED: - out << "* "; - break; - case xact_t::PENDING: - out << "! "; - break; - case xact_t::UNCLEARED: - break; - } - } - break; - - case element_t::CODE: { - string temp; - if (details.entry && details.entry->code) { - temp += "("; - temp += *details.entry->code; - temp += ") "; - } - out << temp; - break; - } - - case element_t::PAYEE: - scope.resolve("payee").dump(out, elem->min_width); - break; - - case element_t::OPT_NOTE: - if (details.xact && details.xact->note) - out << " ; "; - // fall through... - - case element_t::NOTE: - if (details.xact) - out << (elem->max_width == 0 ? - details.xact->note : truncate(*details.xact->note, - elem->max_width)); - break; - - case element_t::OPT_ACCOUNT: - if (details.entry && details.xact) { - xact_t::state_t state; - if (! details.entry->get_state(&state)) - switch (details.xact->state) { - case xact_t::CLEARED: - name = "* "; - break; - case xact_t::PENDING: - name = "! "; - break; - case xact_t::UNCLEARED: - break; - } - } - // fall through... - - case element_t::ACCOUNT_NAME: - case element_t::ACCOUNT_FULLNAME: - if (details.account) { - name += (elem->type == element_t::ACCOUNT_FULLNAME ? - details.account->fullname() : - partial_account_name(*details.account)); - - if (details.xact && details.xact->has_flags(XACT_VIRTUAL)) { - if (elem->max_width > 2) - name = truncate(name, elem->max_width - 2, true); - - if (details.xact->has_flags(XACT_BALANCE)) - name = string("[") + name + "]"; - else - name = string("(") + name + ")"; - } - else if (elem->max_width > 0) - name = truncate(name, elem->max_width, true); - - out << name; - } else { - out << " "; - } - break; - - case element_t::DEPTH_SPACER: - for (const account_t * acct = details.account; - acct; - acct = acct->parent) - if (account_has_xdata(*acct) && - account_xdata_(*acct).dflags & ACCOUNT_DISPLAYED) { - if (elem->min_width > 0 || elem->max_width > 0) - out.width(elem->min_width > elem->max_width ? - elem->min_width : elem->max_width); - out << " "; - } - break; -#endif - default: assert(false); break; } string temp = out.str(); + if (! ignore_max_width && elem->max_width > 0 && elem->max_width < temp.length()) - truncate(temp, elem->max_width); - out_str << temp; + out_str << truncate(temp, elem->max_width); + else + out_str << temp; } } @@ -52,6 +52,102 @@ #endif namespace ledger { + string args_to_predicate(value_t::sequence_t::const_iterator begin, + value_t::sequence_t::const_iterator end) + { + string acct_value_expr; + string payee_value_expr; + string note_value_expr; + + string * value_expr; + + enum regexp_kind_t { + ACCOUNT_REGEXP, + PAYEE_REGEXP, + NOTE_REGEXP + } + kind = ACCOUNT_REGEXP; + + value_expr = &acct_value_expr; + + for ( ; begin != end; begin++) { + const string& arg((*begin).as_string()); + + if (arg == "--") { + kind = PAYEE_REGEXP; + value_expr = &payee_value_expr; + } + else if (arg == "/") { + kind = NOTE_REGEXP; + value_expr = ¬e_value_expr; + } + else { + if (! value_expr->empty()) + *value_expr += "|"; + + switch (kind) { + case ACCOUNT_REGEXP: + *value_expr += "account =~ "; + break; + case PAYEE_REGEXP: + *value_expr += "payee =~ "; + break; + case NOTE_REGEXP: + *value_expr += "note =~ "; + break; + } + + const char * p = arg.c_str(); + if (*p == '-') { + *value_expr += "!"; + p++; + } + + *value_expr += "/"; + while (*p) { + if (*p == '/') + *value_expr += "\\"; + *value_expr += *p; + p++; + } + *value_expr += "/"; + } + } + + string final_value_expr; + + if (! acct_value_expr.empty()) { + if (! payee_value_expr.empty() || + ! note_value_expr.empty()) + final_value_expr = string("(") + acct_value_expr + ")"; + else + final_value_expr = acct_value_expr; + } + + if (! payee_value_expr.empty()) { + if (! acct_value_expr.empty()) + final_value_expr += string("&(") + payee_value_expr + ")"; + else if (! note_value_expr.empty()) + final_value_expr = string("(") + payee_value_expr + ")"; + else + final_value_expr = payee_value_expr; + } + + if (! note_value_expr.empty()) { + if (! acct_value_expr.empty() || + ! payee_value_expr.empty()) + final_value_expr += string("&(") + note_value_expr + ")"; + else if (acct_value_expr.empty() && + payee_value_expr.empty()) + final_value_expr = note_value_expr; + } + + DEBUG("report.predicate", + "Regexp predicate expression = " << final_value_expr); + + return final_value_expr; + } + template <class Formatter = format_xacts> class xacts_report { @@ -65,9 +161,19 @@ namespace ledger { { ptr_t<std::ostream> ostream(args, 0); var_t<string> format(args, format_name); + report_t& report(find_scope<report_t>(args)); + + if (! report.format_string.empty()) + *format = report.format_string; + + if (! report.predicate.empty()) + report.predicate = string("(") + report.predicate + ")&"; + report.predicate += + args_to_predicate(++args.value().as_sequence().begin(), + args.value().as_sequence().end()); - find_scope<report_t>(args).xacts_report - (xact_handler_ptr(new Formatter(*ostream, *format))); + report.xacts_report(xact_handler_ptr(new Formatter(*ostream, + *format))); return true; } }; @@ -34,7 +34,7 @@ namespace ledger { -mask_t::mask_t(const string& pat) : exclude(false), expr() +mask_t::mask_t(const string& pat) : expr() { TRACE_CTOR(mask_t, "const string&"); *this = pat; @@ -42,40 +42,17 @@ mask_t::mask_t(const string& pat) : exclude(false), expr() mask_t& mask_t::operator=(const string& pat) { - exclude = false; - - const char * p = pat.c_str(); - - if (*p == '-') { - exclude = true; - p++; - while (std::isspace(*p)) - p++; - } - else if (*p == '+') { - p++; - while (std::isspace(*p)) - p++; - } - - expr.assign(p); - + expr.assign(pat.c_str(), regex::perl | regex::icase); return *this; } void mask_t::read(const char *& data) { - binary::read_number(data, exclude); - - string pattern; - binary::read_string(data, pattern); - - *this = pattern; + *this = binary::read_string(data); } void mask_t::write(std::ostream& out) const { - binary::write_number(out, exclude); binary::write_string(out, expr.str()); } @@ -41,12 +41,11 @@ class mask_t mask_t(); public: - bool exclude; boost::regex expr; explicit mask_t(const string& pattern); - mask_t(const mask_t& m) : exclude(m.exclude), expr(m.expr) { + mask_t(const mask_t& m) : expr(m.expr) { TRACE_CTOR(mask_t, "copy"); } ~mask_t() throw() { @@ -56,7 +55,7 @@ public: mask_t& operator=(const string& other); bool match(const string& str) const { - return boost::regex_match(str, expr) && ! exclude; + return boost::regex_search(str, expr); } void read(const char *& data); @@ -428,48 +428,6 @@ void expr_t::op_t::compute(value_t& result, break; } - case F_CODE_MASK: - if (details.entry && details.entry->code) - result = as_mask().match(*details.entry->code); - else - result = false; - break; - - case F_PAYEE_MASK: - if (details.entry) - result = as_mask().match(details.entry->payee); - else - result = false; - break; - - case F_NOTE_MASK: - if (details.xact && details.xact->note) - result = as_mask().match(*details.xact->note); - else - result = false; - break; - - case F_ACCOUNT_MASK: - if (details.account) - result = as_mask().match(details.account->fullname()); - else - result = false; - break; - - case F_SHORT_ACCOUNT_MASK: - if (details.account) - result = as_mask().match(details.account->name); - else - result = false; - break; - - case F_COMMODITY_MASK: - if (details.xact) - result = as_mask().match(details.xact->amount.commodity().base_symbol()); - else - result = false; - break; - case O_ARG: { long arg_index = 0; assert(left()->kind == INDEX); @@ -35,96 +35,6 @@ namespace ledger { #if 0 -void -report_t::regexps_to_predicate(const std::string& command, - std::list<std::string>::const_iterator begin, - std::list<std::string>::const_iterator end, - const bool account_regexp, - const bool add_account_short_masks, - const bool logical_and) -{ - std::string regexps[2]; - - assert(begin != end); - - // 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 (int i = 0; i < 2; i++) { - if (regexps[i].empty()) - continue; - - if (! predicate.empty()) - predicate += logical_and ? "&" : "|"; - - int add_predicate = 0; // 1 adds /.../, 2 adds ///.../ - if (i == 1) { - predicate += "!"; - } - else if (add_account_short_masks) { - if (regexps[i].find(':') != std::string::npos || - regexps[i].find('.') != std::string::npos || - regexps[i].find('*') != std::string::npos || - regexps[i].find('+') != std::string::npos || - regexps[i].find('[') != std::string::npos || - regexps[i].find('(') != std::string::npos) { - show_subtotal = true; - add_predicate = 1; - } else { - add_predicate = 2; - } - } - else { - add_predicate = 1; - } - - if (i != 1 && command == "b" && account_regexp) { - if (! show_related && ! show_all_related) { - if (! display_predicate.empty()) - display_predicate += "&"; - if (! show_empty) - display_predicate += "T&"; - - if (add_predicate == 2) - display_predicate += "//"; - display_predicate += "/(?:"; - display_predicate += regexps[i]; - display_predicate += ")/"; - } - else if (! show_empty) { - if (! display_predicate.empty()) - display_predicate += "&"; - display_predicate += "T"; - } - } - - if (! account_regexp) - predicate += "/"; - predicate += "/(?:"; - predicate += regexps[i]; - predicate += ")/"; - } -} - void report_t::process_options(const std::string& command, strings_list::iterator arg, strings_list::iterator args_end) @@ -342,8 +252,11 @@ report_t::chain_xact_handlers(xact_handler_ptr base_handler, // This filter_xacts will only pass through xacts // matching the `predicate'. - if (! predicate.empty()) + if (! predicate.empty()) { + DEBUG("report.predicate", + "Report predicate expression = " << predicate); handler.reset(new filter_xacts(handler, predicate)); + } #if 0 // budget_xacts takes a set of xacts from a data @@ -493,31 +406,30 @@ expr_t::ptr_op_t report_t::lookup(const string& name) if (std::strncmp(p, "opt_", 4) == 0) { p = p + 4; switch (*p) { +#if 0 case 'a': if (std::strcmp(p, "amount") == 0) return MAKE_FUNCTOR(report_t::option_amount); break; +#endif case 'b': - if (std::strcmp(p, "bar") == 0) - return MAKE_FUNCTOR(report_t::option_bar); + if (std::strcmp(p, "bar_") == 0) + return MAKE_FUNCTOR(report_t::option_bar_); break; case 'f': - if (std::strcmp(p, "format") == 0) - return MAKE_FUNCTOR(report_t::option_format); - else if (name.find("fmt_") == 0) { - switch (name[4]) { - case 't': - return MAKE_FUNCTOR(report_t::get_amount_expr); -#if 0 - case 'T': - return MAKE_FUNCTOR(report_t::get_total_expr); -#endif - } - } + if (std::strcmp(p, "F_") == 0 || + std::strcmp(p, "format_") == 0) + return MAKE_FUNCTOR(report_t::option_format_); break; + case 'l': + if (std::strcmp(p, "l_") || std::strcmp(p, "limit_")) + return MAKE_FUNCTOR(report_t::option_limit_); + break; + +#if 0 case 't': if (! *(p + 1)) return MAKE_FUNCTOR(report_t::option_amount); @@ -529,6 +441,7 @@ expr_t::ptr_op_t report_t::lookup(const string& name) if (! *(p + 1)) return MAKE_FUNCTOR(report_t::option_total); break; +#endif } } break; @@ -198,6 +198,7 @@ public: // Config options // +#if 0 void eval(const string& expr) { expr_t(expr).calc(*this); } @@ -206,22 +207,23 @@ public: return NULL_VALUE; } - value_t option_amount(call_scope_t& args) { + value_t option_amount_(call_scope_t& args) { eval(string("t=") + args[0].as_string()); return NULL_VALUE; } - value_t option_total(call_scope_t& args) { + value_t option_total_(call_scope_t& args) { eval(string("T()=") + args[0].as_string()); return NULL_VALUE; } - value_t option_format(call_scope_t& args) { - format_string = args[0].as_string(); + value_t option_raw(call_scope_t&) { + raw_mode = true; return NULL_VALUE; } +#endif - value_t option_raw(call_scope_t&) { - raw_mode = true; + value_t option_format_(call_scope_t& args) { + format_string = args[0].as_string(); return NULL_VALUE; } @@ -229,11 +231,18 @@ public: std::cout << "This is foo" << std::endl; return NULL_VALUE; } - value_t option_bar(call_scope_t& args) { + value_t option_bar_(call_scope_t& args) { std::cout << "This is bar: " << args[0] << std::endl; return args[0]; } + value_t option_limit_(call_scope_t& args) { + if (! predicate.empty()) + predicate += "&"; + predicate += args[0].as_string(); + return true; + } + // // Formatting functions // @@ -105,12 +105,12 @@ namespace { { xact_t& xact(downcast<xact_t>(*scope.parent)); - var_t<long> width(scope, 0); + var_t<long> max_width(scope, 0); string name = xact.reported_account()->fullname(); - if (width && *width > 2) - name = format_t::truncate(name, *width - 2, true); + if (max_width && *max_width > 2) + name = format_t::truncate(name, *max_width - 2, true); if (xact.has_flags(XACT_VIRTUAL)) { if (xact.must_balance()) |