summaryrefslogtreecommitdiff
path: root/format.cc
diff options
context:
space:
mode:
Diffstat (limited to 'format.cc')
-rw-r--r--format.cc259
1 files changed, 158 insertions, 101 deletions
diff --git a/format.cc b/format.cc
index 1c7bf12d..6b5a4a9b 100644
--- a/format.cc
+++ b/format.cc
@@ -1,4 +1,5 @@
#include "format.h"
+#include "error.h"
namespace ledger {
@@ -24,20 +25,36 @@ std::string maximal_account_name(const item_t * item,
return name;
}
-std::string format_t::report_line(const item_t * item,
- const item_t * displayed_parent) const
+node_t * format_t::value_expr = NULL;
+node_t * format_t::total_expr = NULL;
+
+element_t * format_t::parse_elements(const std::string& fmt)
{
- std::string result;
+ element_t * result = NULL;
+ element_t * current = NULL;
+ std::string str;
- for (const char * p = format_string.c_str(); *p; p++) {
+ for (const char * p = fmt.c_str(); *p; p++) {
if (*p == '%') {
- bool leftalign = false;
- int width = 0;
- int strict_width = 0;
+ if (! result) {
+ current = result = new element_t;
+ } else {
+ current->next = new element_t;
+ current = current->next;
+ }
+
+ if (! str.empty()) {
+ current->type = element_t::STRING;
+ current->chars = str;
+ str = "";
+
+ current->next = new element_t;
+ current = current->next;
+ }
++p;
if (*p == '-') {
- leftalign = true;
+ current->align_left = true;
++p;
}
@@ -45,7 +62,7 @@ std::string format_t::report_line(const item_t * item,
while (*p && std::isdigit(*p))
num += *p++;
if (! num.empty())
- width = std::atol(num.c_str());
+ current->min_width = std::atol(num.c_str());
if (*p == '.') {
++p;
@@ -53,132 +70,172 @@ std::string format_t::report_line(const item_t * item,
while (*p && std::isdigit(*p))
num += *p++;
if (! num.empty()) {
- strict_width = std::atol(num.c_str());
- if (width == 0)
- width = strict_width;
+ current->max_width = std::atol(num.c_str());
+ if (current->min_width == 0)
+ current->min_width = current->max_width;
}
}
- std::ostringstream out;
-
- if (leftalign)
- out << std::left;
- else
- out << std::right;
-
- if (width > 0)
- out.width(width);
-
switch (*p) {
case '%':
- out << "%";
+ current->type = element_t::STRING;
+ current->chars = "%";
break;
- case '(': {
+ case '(':
++p;
num = "";
while (*p && *p != ')')
num += *p++;
- assert(*p == ')');
+ if (*p != ')')
+ throw format_error("Missing ')'");
- node_t * style = parse_expr(num, NULL);
- balance_t value = style->compute(item);
- value.write(out, width, strict_width > 0 ? strict_width : width);
- delete style;
+ current->type = element_t::VALUE_EXPR;
+ current->val_expr = parse_expr(num);
break;
- }
- case '[': {
+ case '[':
++p;
num = "";
while (*p && *p != ']')
num += *p++;
- assert(*p == ']');
-
- if (item->date != -1) {
- char buf[256];
- std::strftime(buf, 255, num.c_str(), std::gmtime(&item->date));
- out << (strict_width == 0 ? buf : truncated(buf, strict_width));
- } else {
- out << " ";
- }
+ if (*p != ']')
+ throw format_error("Missing ']'");
+
+ current->type = element_t::DATE_STRING;
+ current->chars = num;
break;
- }
- case 'd': {
- if (item->date != -1) {
- char buf[32];
- std::strftime(buf, 31, "%Y/%m/%d", std::gmtime(&item->date));
- out << (strict_width == 0 ? buf : truncated(buf, strict_width));
- } else {
- out << " ";
- }
+ case 'd':
+ current->type = element_t::DATE_STRING;
+ current->chars = "%Y/%m/%d";
break;
+
+ case 'p': current->type = element_t::PAYEE; break;
+ case 'n': current->type = element_t::ACCOUNT_NAME; break;
+ case 'N': current->type = element_t::ACCOUNT_FULLNAME; break;
+ case 't': current->type = element_t::VALUE; break;
+ case 'T': current->type = element_t::TOTAL; break;
+ case '_': current->type = element_t::SPACER; break;
}
+ } else {
+ str += *p;
+ }
+ }
- case 'p':
- out << (strict_width == 0 ?
- item->payee : truncated(item->payee, strict_width));
- break;
+ if (! str.empty()) {
+ if (! result) {
+ current = result = new element_t;
+ } else {
+ current->next = new element_t;
+ current = current->next;
+ }
+ current->type = element_t::STRING;
+ current->chars = str;
+ }
- case 'n':
- if (item->account) {
- std::string name = maximal_account_name(item, displayed_parent);
- out << (strict_width == 0 ? name : truncated(name, strict_width));
- } else {
- out << " ";
- }
- break;
+ return result;
+}
- case 'N':
- if (item->account)
- out << (strict_width == 0 ?
- item->account->fullname() :
- truncated(item->account->fullname(), strict_width));
- else
- out << " ";
- break;
+void format_t::format_elements(std::ostream& out, const item_t * item,
+ const item_t * displayed_parent) const
+{
+ std::string result;
- case 't':
- if (value_style) {
- balance_t value = compute_value(item);
- value.write(out, width, strict_width > 0 ? strict_width : width);
- }
- break;
+ for (const element_t * elem = elements;
+ elem;
+ elem = elem->next) {
+ if (elem->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::VALUE_EXPR: {
+ balance_t value = elem->val_expr->compute(item);
+ value.write(out, elem->min_width,
+ elem->max_width > 0 ? elem->max_width : elem->min_width);
+ break;
+ }
- case 'T':
- if (total_style) {
- balance_t value = compute_total(item);
- value.write(out, width, strict_width > 0 ? strict_width : width);
- }
- break;
+ case element_t::DATE_STRING:
+ if (item->date != -1) {
+ char buf[256];
+ std::strftime(buf, 255, elem->chars.c_str(), std::gmtime(&item->date));
+ out << (elem->max_width == 0 ? buf : truncated(buf, elem->max_width));
+ } else {
+ out << " ";
+ }
+ break;
+
+ case element_t::PAYEE:
+ out << (elem->max_width == 0 ?
+ item->payee : truncated(item->payee, elem->max_width));
+ break;
+
+ case element_t::ACCOUNT_NAME:
+ if (item->account) {
+ std::string name = maximal_account_name(item, displayed_parent);
+ out << (elem->max_width == 0 ? name : truncated(name, elem->max_width));
+ } else {
+ out << " ";
+ }
+ break;
- case '_': {
- int depth = 0;
- for (const item_t * i = item; i->parent; i = i->parent)
- depth++;
+ case element_t::ACCOUNT_FULLNAME:
+ if (item->account)
+ out << (elem->max_width == 0 ?
+ item->account->fullname() :
+ truncated(item->account->fullname(), elem->max_width));
+ else
+ out << " ";
+ break;
+
+ case element_t::VALUE: {
+ balance_t value = compute_value(item);
+ value.write(out, elem->min_width,
+ elem->max_width > 0 ? elem->max_width : elem->min_width);
+ break;
+ }
- for (const item_t * i = item->parent;
- i && i->account && i != displayed_parent;
- i = i->parent)
- depth--;
+ case element_t::TOTAL: {
+ balance_t value = compute_total(item);
+ value.write(out, elem->min_width,
+ elem->max_width > 0 ? elem->max_width : elem->min_width);
+ break;
+ }
- while (--depth >= 0) {
- if (width > 0 || strict_width > 0)
- out.width(width > strict_width ? width : strict_width);
- out << " ";
- }
- break;
- }
+ case element_t::SPACER: {
+ int depth = 0;
+ for (const item_t * i = item; i->parent; i = i->parent)
+ depth++;
+
+ for (const item_t * i = item->parent;
+ i && i->account && i != displayed_parent;
+ i = i->parent)
+ depth--;
+
+ while (--depth >= 0) {
+ 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;
+ }
- result += out.str();
- } else {
- result += *p;
+ default:
+ assert(0);
+ break;
}
}
-
- return result;
}
} // namespace ledger