diff options
Diffstat (limited to 'format.cc')
-rw-r--r-- | format.cc | 1045 |
1 files changed, 162 insertions, 883 deletions
@@ -1,985 +1,264 @@ +#ifdef USE_PCH +#include "pch.h" +#else #include "format.h" #include "error.h" #include "util.h" +#ifdef USE_BOOST_PYTHON +#include "py_eval.h" +#endif #include <cstdlib> +#endif namespace ledger { -format_t::elision_style_t format_t::elision_style = ABBREVIATE; -int format_t::abbrev_length = 2; - -bool format_t::ansi_codes = false; -bool format_t::ansi_invert = false; - -std::string format_t::truncate(const std::string& str, unsigned int width, - const bool is_account) -{ - const int len = str.length(); - if (len <= width) - return str; - - assert(width < 4095); - - char buf[4096]; - - switch (elision_style) { - case TRUNCATE_LEADING: - // This method truncates at the beginning. - std::strncpy(buf, str.c_str() + (len - width), width); - buf[0] = '.'; - buf[1] = '.'; - break; - - case TRUNCATE_MIDDLE: - // This method truncates in the middle. - std::strncpy(buf, str.c_str(), width / 2); - std::strncpy(buf + width / 2, - str.c_str() + (len - (width / 2 + width % 2)), - width / 2 + width % 2); - buf[width / 2 - 1] = '.'; - buf[width / 2] = '.'; - break; - - case ABBREVIATE: - if (is_account) { - std::list<std::string> parts; - std::string::size_type beg = 0; - for (std::string::size_type pos = str.find(':'); - pos != std::string::npos; - beg = pos + 1, pos = str.find(':', beg)) - parts.push_back(std::string(str, beg, pos - beg)); - parts.push_back(std::string(str, beg)); - - std::string result; - int newlen = len; - for (std::list<std::string>::iterator i = parts.begin(); - i != parts.end(); - i++) { - // Don't contract the last element - std::list<std::string>::iterator x = i; - if (++x == parts.end()) { - result += *i; - break; - } - - if (newlen > width) { - result += std::string(*i, 0, abbrev_length); - result += ":"; - newlen -= (*i).length() - abbrev_length; - } else { - result += *i; - result += ":"; - } - } - - if (newlen > width) { - // Even abbreviated its too big to show the last account, so - // abbreviate all but the last and truncate at the beginning. - std::strncpy(buf, result.c_str() + (result.length() - width), width); - buf[0] = '.'; - buf[1] = '.'; - } else { - std::strcpy(buf, result.c_str()); - } - break; - } - // fall through... - - case TRUNCATE_TRAILING: - // This method truncates at the end (the default). - std::strncpy(buf, str.c_str(), width - 2); - buf[width - 2] = '.'; - buf[width - 1] = '.'; - break; - } - buf[width] = '\0'; - - return buf; -} - -std::string partial_account_name(const account_t& account) -{ - std::string name; - - for (const account_t * acct = &account; - acct && acct->parent; - acct = acct->parent) { - if (account_has_xdata(*acct) && - account_xdata_(*acct).dflags & ACCOUNT_DISPLAYED) - break; - - if (name.empty()) - name = acct->name; - else - name = acct->name + ":" + name; - } - - return name; -} - -element_t * format_t::parse_elements(const std::string& fmt) +void format_t::parse(const std::string& fmt) { - std::auto_ptr<element_t> result; - element_t * current = NULL; char buf[1024]; char * q = buf; + if (elements.size() > 0) + clear_elements(); + format_string = fmt; + for (const char * p = fmt.c_str(); *p; p++) { if (*p != '%' && *p != '\\') { *q++ = *p; continue; } - - if (! result.get()) { - result.reset(new element_t); - current = result.get(); - } else { - current->next = new element_t; - current = current->next; + else if (*p == '\\') { + p++; + switch (*p) { + case 'b': *q++ = '\b'; break; + case 'f': *q++ = '\f'; break; + case 'n': *q++ = '\n'; break; + case 'r': *q++ = '\r'; break; + case 't': *q++ = '\t'; break; + case 'v': *q++ = '\v'; break; + default: + *q++ = *p; + break; + } + continue; + } + else { + assert(*p == '%'); + if (*(p + 1) == '%') { + p++; // %% is the same as \% + *q++ = *p; + continue; + } } + current = new element_t; + elements.push_back(current); + if (q != buf) { - current->type = element_t::STRING; - current->chars = std::string(buf, q); + current->kind = element_t::TEXT; + current->chars = new std::string(buf, q); q = buf; - current->next = new element_t; - current = current->next; - } - - if (*p == '\\') { - p++; - current->type = element_t::STRING; - switch (*p) { - case 'b': current->chars = "\b"; break; - case 'f': current->chars = "\f"; break; - case 'n': current->chars = "\n"; break; - case 'r': current->chars = "\r"; break; - case 't': current->chars = "\t"; break; - case 'v': current->chars = "\v"; break; - } - continue; + current = new element_t; + elements.push_back(current); } ++p; - while (*p == '!' || *p == '-') { - switch (*p) { - case '-': - current->flags |= ELEMENT_ALIGN_LEFT; - break; - case '!': - current->flags |= ELEMENT_HIGHLIGHT; - break; - } + if (*p == '-') { + current->align_left = true; ++p; } - int num = 0; - while (*p && std::isdigit(*p)) { - num *= 10; - num += *p++ - '0'; + if (*p && std::isdigit(*p)) { + int num = *p++ - '0'; + while (*p && std::isdigit(*p)) { + num *= 10; + num += *p++ - '0'; + } + current->min_width = num; } - current->min_width = num; if (*p == '.') { ++p; - num = 0; + int num = 0; while (*p && std::isdigit(*p)) { num *= 10; num += *p++ - '0'; } + current->max_width = num; - if (current->min_width == 0) + if (current->min_width == -1) current->min_width = current->max_width; } + if (current->max_width != -1 && current->min_width != -1 && + current->max_width < current->min_width) + throw new format_error("Maximum width is less than the minimum width"); + switch (*p) { - case '%': - current->type = element_t::STRING; - current->chars = "%"; + case '|': + current->kind = element_t::COLUMN; break; + case '{': case '(': { + char open = *p; + char close = *p == '{' ? '}' : ')'; ++p; const char * b = p; int depth = 1; while (*p) { - if (*p == ')' && --depth == 0) + if (*p == close && --depth == 0) break; - else if (*p == '(') + else if (*p == open) ++depth; p++; } - if (*p != ')') - throw new format_error("Missing ')'"); - - current->type = element_t::VALUE_EXPR; + if (*p != close) + throw new format_error(std::string("Missing '") + close + "'"); - assert(! current->val_expr); - current->val_expr = std::string(b, p); - break; - } - - case '[': { - ++p; - const char * b = p; - int depth = 1; - while (*p) { - if (*p == ']' && --depth == 0) - break; - else if (*p == '[') - ++depth; - p++; + if (open == '{') { + assert(! current->xpath); + current->kind = element_t::XPATH; + current->xpath = new xml::xpath_t(std::string(b, p)); + } else { + assert(! current->format); + current->kind = element_t::GROUP; + current->format = new format_t(std::string(b, p)); } - if (*p != ']') - throw new format_error("Missing ']'"); - - current->type = element_t::DATE_STRING; - current->chars = std::string(b, p); break; } - case 'x': - switch (*++p) { - case 'B': current->type = element_t::XACT_BEG_POS; break; - case 'b': current->type = element_t::XACT_BEG_LINE; break; - case 'E': current->type = element_t::XACT_END_POS; break; - case 'e': current->type = element_t::XACT_END_LINE; break; - case '\0': - goto END; - } - break; - - case 'd': - current->type = element_t::COMPLETE_DATE_STRING; - current->chars = datetime_t::output_format; - break; - case 'D': - current->type = element_t::DATE_STRING; - current->chars = datetime_t::output_format; + default: + assert(! current->xpath); + current->kind = element_t::XPATH; + current->xpath = new xml::xpath_t(std::string(p, p + 1)); break; - - case 'S': current->type = element_t::SOURCE; break; - case 'B': current->type = element_t::ENTRY_BEG_POS; break; - case 'b': current->type = element_t::ENTRY_BEG_LINE; break; - case 'E': current->type = element_t::ENTRY_END_POS; break; - case 'e': current->type = element_t::ENTRY_END_LINE; break; - case 'X': current->type = element_t::CLEARED; break; - case 'Y': current->type = element_t::ENTRY_CLEARED; break; - case 'C': current->type = element_t::CODE; break; - case 'P': current->type = element_t::PAYEE; break; - case 'W': current->type = element_t::OPT_ACCOUNT; break; - case 'a': current->type = element_t::ACCOUNT_NAME; break; - case 'A': current->type = element_t::ACCOUNT_FULLNAME; break; - case 't': current->type = element_t::AMOUNT; break; - case 'o': current->type = element_t::OPT_AMOUNT; break; - case 'T': current->type = element_t::TOTAL; break; - case 'N': current->type = element_t::NOTE; break; - case 'n': current->type = element_t::OPT_NOTE; break; - case '|': current->type = element_t::SPACER; break; - case '_': current->type = element_t::DEPTH_SPACER; break; } } END: if (q != buf) { - if (! result.get()) { - result.reset(new element_t); - current = result.get(); - } else { - current->next = new element_t; - current = current->next; - } - current->type = element_t::STRING; - current->chars = std::string(buf, q); - } + current = new element_t; + elements.push_back(current); - return result.release(); -} - -namespace { - inline void mark_red(std::ostream& out, const element_t * elem) { - out.setf(std::ios::left); - out.width(0); - out << "\e[31m"; - - if (elem->flags & ELEMENT_ALIGN_LEFT) - out << std::left; - else - out << std::right; - - if (elem->min_width > 0) - out.width(elem->min_width); + current->kind = element_t::TEXT; + current->chars = new std::string(buf, q); } +} - inline void mark_plain(std::ostream& out) { - out << "\e[0m"; +void format_t::compile(xml::node_t * context) +{ + for (std::list<element_t *>::iterator i = elements.begin(); + i != elements.end(); + i++) + switch ((*i)->kind) { + case element_t::XPATH: + assert((*i)->xpath); + (*i)->xpath->compile(context); + break; + case element_t::GROUP: + assert((*i)->format); + (*i)->format->compile(context); + break; } } -void format_t::format(std::ostream& out_str, const details_t& details) const +int format_t::element_formatter_t::operator() + (std::ostream& out_str, element_t * elem, xml::node_t * context, + int column) const { - for (const element_t * elem = elements; elem; elem = elem->next) { - std::ostringstream out; - std::string name; - bool ignore_max_width = false; - - if (elem->flags & ELEMENT_ALIGN_LEFT) - out << std::left; - else - out << std::right; - - if (elem->min_width > 0) - out.width(elem->min_width); - - switch (elem->type) { - case element_t::STRING: - out << elem->chars; - break; - - case element_t::AMOUNT: - case element_t::TOTAL: - case element_t::VALUE_EXPR: { - value_expr calc; - switch (elem->type) { - case element_t::AMOUNT: calc = amount_expr; break; - case element_t::TOTAL: calc = total_expr; break; - case element_t::VALUE_EXPR: calc = elem->val_expr; break; - default: - assert(0); - break; - } - if (! calc) - break; - - value_t value; - 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 << (*((bool *) value.data) ? "true" : "false"); - break; - - case value_t::INTEGER: - if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) { - if (ansi_invert) { - if (*((long *) value.data) > 0) { - mark_red(out, elem); - highlighted = true; - } - } else { - if (*((long *) value.data) < 0) { - mark_red(out, elem); - highlighted = true; - } - } - } - out << *((long *) value.data); - break; - - case value_t::DATETIME: - out << *((datetime_t *) value.data); - break; - - case value_t::AMOUNT: - if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) { - if (ansi_invert) { - if (*((amount_t *) value.data) > 0) { - mark_red(out, elem); - highlighted = true; - } - } else { - if (*((amount_t *) value.data) < 0) { - mark_red(out, elem); - highlighted = true; - } - } - } - out << *((amount_t *) value.data); - break; - - case value_t::BALANCE: - bal = (balance_t *) value.data; - // fall through... - - case value_t::BALANCE_PAIR: - if (! bal) - bal = &((balance_pair_t *) value.data)->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->write(out, elem->min_width, - (elem->max_width > 0 ? - elem->max_width : elem->min_width)); - - ignore_max_width = true; - break; - default: - assert(0); - break; - } - - if (highlighted) - mark_plain(out); - break; + if (elem->kind == element_t::COLUMN) { + if (elem->max_width != -1 && elem->max_width < column) { + out_str << '\n'; + column = 0; } - case element_t::OPT_AMOUNT: - if (details.xact) { - std::string disp; - bool use_disp = false; - - if (details.xact->cost && details.xact->amount) { - std::ostringstream stream; - if (! details.xact->amount_expr.expr.empty()) - stream << details.xact->amount_expr.expr; - else - stream << details.xact->amount.strip_annotations(); - - if (! details.xact->cost_expr.empty()) - stream << details.xact->cost_expr; - 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; - transaction_t * first = NULL; - transaction_t * last = NULL; - - for (transactions_list::const_iterator i - = details.entry->transactions.begin(); - i != details.entry->transactions.end(); - i++) - if (transaction_has_xdata(**i) && - transaction_xdata_(**i).dflags & TRANSACTION_TO_DISPLAY) { - xacts_count++; - if (! first) - first = *i; - last = *i; - } - - use_disp = (xacts_count == 2 && details.xact == last && - first->amount == - last->amount); - } - - if (! use_disp) { - if (! details.xact->amount_expr.expr.empty()) - out << details.xact->amount_expr.expr; - 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; - for (strings_list::iterator i = details.entry->journal->sources.begin(); - i != details.entry->journal->sources.end(); - i++) - if (! idx--) { - out << *i; - 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: { - datetime_t date; - if (details.xact) - date = details.xact->date(); - else if (details.entry) - date = details.entry->date(); - - char buf[256]; - std::strftime(buf, 255, elem->chars.c_str(), date.localtime()); - out << (elem->max_width == 0 ? buf : truncate(buf, elem->max_width)); - break; - } - - case element_t::COMPLETE_DATE_STRING: { - datetime_t actual_date; - datetime_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]; - std::strftime(abuf, 255, elem->chars.c_str(), actual_date.localtime()); - - if (effective_date && effective_date != actual_date) { - char buf[512]; - char ebuf[256]; - std::strftime(ebuf, 255, elem->chars.c_str(), - effective_date.localtime()); - - 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 transaction_t::CLEARED: - out << "* "; - break; - case transaction_t::PENDING: - out << "! "; - break; - } - } - break; - - case element_t::ENTRY_CLEARED: - if (details.entry) { - transaction_t::state_t state; - if (details.entry->get_state(&state)) - switch (state) { - case transaction_t::CLEARED: - out << "* "; - break; - case transaction_t::PENDING: - out << "! "; - break; - } - } - break; - - case element_t::CODE: { - std::string temp; - if (details.entry && ! details.entry->code.empty()) { - temp += "("; - temp += details.entry->code; - temp += ") "; - } - out << temp; - break; + if (elem->min_width != -1 && elem->min_width > column) { + out_str << std::string(elem->min_width - column, ' '); + column = elem->min_width; } - - case element_t::PAYEE: - if (details.entry) - out << (elem->max_width == 0 ? - details.entry->payee : truncate(details.entry->payee, - elem->max_width)); - break; - - case element_t::OPT_NOTE: - if (details.xact && ! details.xact->note.empty()) - 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) { - transaction_t::state_t state; - if (! details.entry->get_state(&state)) - switch (details.xact->state) { - case transaction_t::CLEARED: - name = "* "; - break; - case transaction_t::PENDING: - name = "! "; - 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->flags & TRANSACTION_VIRTUAL) { - if (elem->max_width > 2) - name = truncate(name, elem->max_width - 2, true); - - if (details.xact->flags & TRANSACTION_BALANCE) - name = "[" + name + "]"; - else - name = "(" + name + ")"; - } - else if (elem->max_width > 0) - name = truncate(name, elem->max_width, true); - - out << name; - } else { - out << " "; - } - break; - - case element_t::SPACER: - 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; - - default: - assert(0); - break; - } - - std::string temp = out.str(); - if (! ignore_max_width && - elem->max_width > 0 && elem->max_width < temp.length()) - temp.erase(elem->max_width); - out_str << temp; + return column; } -} -format_transactions::format_transactions(std::ostream& _output_stream, - const std::string& format) - : output_stream(_output_stream), last_entry(NULL), last_xact(NULL) -{ - const char * f = format.c_str(); - if (const char * p = std::strstr(f, "%/")) { - first_line_format.reset(std::string(f, 0, p - f)); - next_lines_format.reset(std::string(p + 2)); - } else { - first_line_format.reset(format); - next_lines_format.reset(format); - } -} + std::ostringstream out; -void format_transactions::operator()(transaction_t& xact) -{ - if (! transaction_has_xdata(xact) || - ! (transaction_xdata_(xact).dflags & TRANSACTION_DISPLAYED)) { - if (last_entry != xact.entry) { - first_line_format.format(output_stream, details_t(xact)); - last_entry = xact.entry; - } - else if (last_xact && last_xact->date() != xact.date()) { - first_line_format.format(output_stream, details_t(xact)); - } - else { - next_lines_format.format(output_stream, details_t(xact)); - } + if (elem->align_left) + out << std::left; + else + out << std::right; - transaction_xdata(xact).dflags |= TRANSACTION_DISPLAYED; - last_xact = &xact; - } -} + if (elem->min_width > 0) + out.width(elem->min_width); -void format_entries::format_last_entry() -{ - bool first = true; - for (transactions_list::const_iterator i = last_entry->transactions.begin(); - i != last_entry->transactions.end(); - i++) { - if (transaction_has_xdata(**i) && - transaction_xdata_(**i).dflags & TRANSACTION_TO_DISPLAY) { - if (first) { - first_line_format.format(output_stream, details_t(**i)); - first = false; - } else { - next_lines_format.format(output_stream, details_t(**i)); - } - transaction_xdata_(**i).dflags |= TRANSACTION_DISPLAYED; - } - } -} - -void format_entries::operator()(transaction_t& xact) -{ - transaction_xdata(xact).dflags |= TRANSACTION_TO_DISPLAY; + int start_column = column; - if (last_entry && xact.entry != last_entry) - format_last_entry(); + if (elem->kind == element_t::XPATH) + elem->xpath->calc(context).strip_annotations() + .write(out, elem->min_width, elem->max_width); + else if (elem->kind == element_t::GROUP) + column = elem->format->format(out, context, column); + else if (elem->kind == element_t::TEXT) + out << *elem->chars; + else + assert(0); - last_entry = xact.entry; -} + std::string temp = out.str(); + for (std::string::const_iterator i = temp.begin(); + i != temp.end(); + i++) + if (*i == '\n' || *i == '\r') + column = 0; + else + column++; -void print_entry(std::ostream& out, const entry_base_t& entry_base, - const std::string& prefix) -{ - std::string print_format; + int virtual_width = column - start_column; - if (const entry_t * entry = dynamic_cast<const entry_t *>(&entry_base)) { - print_format = (prefix + "%D %X%C%P\n" + - prefix + " %-34A %12o\n%/" + - prefix + " %-34A %12o\n"); + if (elem->min_width != -1 && virtual_width < elem->min_width) { + out_str << temp << std::string(' ', elem->min_width - virtual_width); } - else if (const auto_entry_t * entry = - dynamic_cast<const auto_entry_t *>(&entry_base)) { - out << "= " << entry->predicate_string << '\n'; - print_format = prefix + " %-34A %12o\n"; - } - else if (const period_entry_t * entry = - dynamic_cast<const period_entry_t *>(&entry_base)) { - out << "~ " << entry->period_string << '\n'; - print_format = prefix + " %-34A %12o\n"; + else if (elem->max_width != -1 && virtual_width > elem->max_width) { + temp.erase(temp.length() - (virtual_width - elem->max_width)); + out_str << temp; } else { - assert(0); + out_str << temp; } - format_entries formatter(out, print_format); - walk_transactions(const_cast<transactions_list&>(entry_base.transactions), - formatter); - formatter.flush(); - - clear_transaction_xdata cleaner; - walk_transactions(const_cast<transactions_list&>(entry_base.transactions), - cleaner); + return column; } -bool disp_subaccounts_p(const account_t& account, - const item_predicate<account_t>& disp_pred, - const account_t *& to_show) +int format_t::format(std::ostream& out, xml::node_t * context, + int column, const element_formatter_t& formatter) const { - bool display = false; - unsigned int counted = 0; - bool matches = disp_pred(account); - value_t acct_total; - bool computed = false; - value_t result; - - to_show = NULL; - - for (accounts_map::const_iterator i = account.accounts.begin(); - i != account.accounts.end(); - i++) { - if (! disp_pred(*(*i).second)) - continue; - - compute_total(result, details_t(*(*i).second)); - if (! computed) { - compute_total(acct_total, details_t(account)); - computed = true; - } + for (std::list<element_t *>::const_iterator i = elements.begin(); + i != elements.end(); + i++) + column = formatter(out, *i, context, column); - if ((result != acct_total) || counted > 0) { - display = matches; - break; - } - to_show = (*i).second; - counted++; - } - - return display; -} - -bool display_account(const account_t& account, - const item_predicate<account_t>& disp_pred) -{ - // Never display an account that has already been displayed. - if (account_has_xdata(account) && - account_xdata_(account).dflags & ACCOUNT_DISPLAYED) - return false; - - // At this point, one of two possibilities exists: the account is a - // leaf which matches the predicate restrictions; or it is a parent - // and two or more children must be subtotaled; or it is a parent - // and its child has been hidden by the predicate. So first, - // determine if it is a parent that must be displayed regardless of - // the predicate. - - const account_t * account_to_show = NULL; - if (disp_subaccounts_p(account, disp_pred, account_to_show)) - return true; - - return ! account_to_show && disp_pred(account); + return column; } -void format_account::operator()(account_t& account) -{ - if (display_account(account, disp_pred)) { - if (! account.parent) { - account_xdata(account).dflags |= ACCOUNT_TO_DISPLAY; - } else { - format.format(output_stream, details_t(account)); - account_xdata(account).dflags |= ACCOUNT_DISPLAYED; - } - } -} +} // namespace ledger -format_equity::format_equity(std::ostream& _output_stream, - const std::string& _format, - const std::string& display_predicate) - : output_stream(_output_stream), disp_pred(display_predicate) -{ - const char * f = _format.c_str(); - if (const char * p = std::strstr(f, "%/")) { - first_line_format.reset(std::string(f, 0, p - f)); - next_lines_format.reset(std::string(p + 2)); - } else { - first_line_format.reset(_format); - next_lines_format.reset(_format); - } +#ifdef USE_BOOST_PYTHON - entry_t header_entry; - header_entry.payee = "Opening Balances"; - header_entry._date = datetime_t::now; - first_line_format.format(output_stream, details_t(header_entry)); -} +#ifndef USE_PCH +#include <boost/python.hpp> +#endif -void format_equity::flush() -{ - account_xdata_t xdata; - xdata.value = total; - xdata.value.negate(); - account_t summary(NULL, "Equity:Opening Balances"); - summary.data = &xdata; - - if (total.type >= value_t::BALANCE) { - balance_t * bal; - if (total.type == value_t::BALANCE) - bal = (balance_t *) total.data; - else if (total.type == value_t::BALANCE_PAIR) - bal = &((balance_pair_t *) total.data)->quantity; - else - assert(0); - - for (amounts_map::const_iterator i = bal->amounts.begin(); - i != bal->amounts.end(); - i++) { - xdata.value = (*i).second; - xdata.value.negate(); - next_lines_format.format(output_stream, details_t(summary)); - } - } else { - next_lines_format.format(output_stream, details_t(summary)); - } - output_stream.flush(); -} +using namespace boost::python; +using namespace ledger; -void format_equity::operator()(account_t& account) +void export_format() { - if (display_account(account, disp_pred)) { - if (account_has_xdata(account)) { - value_t val = account_xdata_(account).value; - - if (val.type >= value_t::BALANCE) { - balance_t * bal; - if (val.type == value_t::BALANCE) - bal = (balance_t *) val.data; - else if (val.type == value_t::BALANCE_PAIR) - bal = &((balance_pair_t *) val.data)->quantity; - else - assert(0); - - for (amounts_map::const_iterator i = bal->amounts.begin(); - i != bal->amounts.end(); - i++) { - account_xdata_(account).value = (*i).second; - next_lines_format.format(output_stream, details_t(account)); - } - account_xdata_(account).value = val; - } else { - next_lines_format.format(output_stream, details_t(account)); - } - total += val; - } - account_xdata(account).dflags |= ACCOUNT_DISPLAYED; - } + class_< format_t > ("Format") + .def(init<std::string>()) + .def("parse", &format_t::parse) + .def("format", &format_t::format) + ; } -} // namespace ledger +#endif // USE_BOOST_PYTHON |