Added --ansi and --ansi-invert options.

This commit is contained in:
John Wiegley 2006-03-13 08:39:12 +00:00
parent fd525ec382
commit 82d0ee869b
10 changed files with 126 additions and 47 deletions

View file

@ -47,7 +47,7 @@ endif
if DEBUG if DEBUG
libledger_la_CXXFLAGS += -DDEBUG_LEVEL=4 libledger_la_CXXFLAGS += -DDEBUG_LEVEL=4
endif endif
libledger_la_LDFLAGS = -release 2.6 libledger_la_LDFLAGS = -release 3.0
pkginclude_HEADERS = \ pkginclude_HEADERS = \
acconf.h \ acconf.h \

7
NEWS
View file

@ -1,11 +1,16 @@
Ledger NEWS Ledger NEWS
* 2.6 * 3.0
- Error reporting has been greatly improving, now showing full - Error reporting has been greatly improving, now showing full
contextual information for most error messages. contextual information for most error messages.
- Added new --ansi reporting option, which shows negative values as
red using ANSI terminal codes; --ansi-invert makes non-negative
values red (which makes more sense for income and budget reports,
for example).
- Added a new --only predicate, which occurs during transaction - Added a new --only predicate, which occurs during transaction
processing between --limit and --display. Here is a summary of how processing between --limit and --display. Here is a summary of how
the three supported predicates are used: the three supported predicates are used:

View file

@ -1344,7 +1344,7 @@ void amount_t::annotate_commodity(const amount_t& price,
if (commodity().annotated) { if (commodity().annotated) {
this_ann = &static_cast<annotated_commodity_t&>(commodity()); this_ann = &static_cast<annotated_commodity_t&>(commodity());
this_base = this_ann->base; this_base = this_ann->ptr;
} else { } else {
this_base = &commodity(); this_base = &commodity();
} }
@ -1392,7 +1392,7 @@ amount_t amount_t::strip_annotations(const bool _keep_price,
(_keep_tag && ! ann_comm.tag.empty())) (_keep_tag && ! ann_comm.tag.empty()))
{ {
new_comm = annotated_commodity_t::find_or_create new_comm = annotated_commodity_t::find_or_create
(*ann_comm.base, _keep_price ? ann_comm.price : amount_t(), (*ann_comm.ptr, _keep_price ? ann_comm.price : amount_t(),
_keep_date ? ann_comm.date : 0, _keep_tag ? ann_comm.tag : ""); _keep_date ? ann_comm.date : 0, _keep_tag ? ann_comm.tag : "");
} else { } else {
new_comm = commodity_t::find_or_create(ann_comm.base_symbol()); new_comm = commodity_t::find_or_create(ann_comm.base_symbol());
@ -1465,7 +1465,7 @@ commodity_t * commodity_t::create(const std::string& symbol)
{ {
std::auto_ptr<commodity_t> commodity(new commodity_t); std::auto_ptr<commodity_t> commodity(new commodity_t);
commodity->ptr = commodity_base_t::create(symbol); commodity->base = commodity_base_t::create(symbol);
if (needs_quotes(symbol)) { if (needs_quotes(symbol)) {
commodity->qualified_symbol = "\""; commodity->qualified_symbol = "\"";
@ -1607,10 +1607,10 @@ annotated_commodity_t::create(const commodity_t& comm,
commodity->date = date; commodity->date = date;
commodity->tag = tag; commodity->tag = tag;
commodity->base = &comm; commodity->ptr = &comm;
assert(commodity->base);
commodity->ptr = comm.ptr;
assert(commodity->ptr); assert(commodity->ptr);
commodity->base = comm.base;
assert(commodity->base);
commodity->qualified_symbol = comm.symbol(); commodity->qualified_symbol = comm.symbol();

View file

@ -444,25 +444,25 @@ class commodity_t
typedef unsigned long ident_t; typedef unsigned long ident_t;
ident_t ident; ident_t ident;
commodity_base_t * ptr; commodity_base_t * base;
std::string qualified_symbol; std::string qualified_symbol;
bool annotated; bool annotated;
public: public:
explicit commodity_t() : ptr(NULL), annotated(false) {} explicit commodity_t() : base(NULL), annotated(false) {}
operator bool() const { operator bool() const {
return this != null_commodity; return this != null_commodity;
} }
bool operator==(const commodity_t& comm) const { bool operator==(const commodity_t& comm) const {
return ptr == comm.ptr; return base == comm.base;
} }
bool operator!=(const commodity_t& comm) const { bool operator!=(const commodity_t& comm) const {
return ptr != comm.ptr; return base != comm.base;
} }
std::string base_symbol() const { std::string base_symbol() const {
return ptr->symbol; return base->symbol;
} }
std::string symbol() const { std::string symbol() const {
return qualified_symbol; return qualified_symbol;
@ -473,69 +473,69 @@ class commodity_t
} }
std::string name() const { std::string name() const {
return ptr->name; return base->name;
} }
void set_name(const std::string& arg) { void set_name(const std::string& arg) {
ptr->name = arg; base->name = arg;
} }
std::string note() const { std::string note() const {
return ptr->note; return base->note;
} }
void set_note(const std::string& arg) { void set_note(const std::string& arg) {
ptr->note = arg; base->note = arg;
} }
unsigned char precision() const { unsigned char precision() const {
return ptr->precision; return base->precision;
} }
void set_precision(unsigned char arg) { void set_precision(unsigned char arg) {
ptr->precision = arg; base->precision = arg;
} }
unsigned char flags() const { unsigned char flags() const {
return ptr->flags; return base->flags;
} }
void set_flags(unsigned char arg) { void set_flags(unsigned char arg) {
ptr->flags = arg; base->flags = arg;
} }
void add_flags(unsigned char arg) { void add_flags(unsigned char arg) {
ptr->flags |= arg; base->flags |= arg;
} }
void drop_flags(unsigned char arg) { void drop_flags(unsigned char arg) {
ptr->flags &= ~arg; base->flags &= ~arg;
} }
amount_t * smaller() const { amount_t * smaller() const {
return ptr->smaller; return base->smaller;
} }
void set_smaller(const amount_t& arg) { void set_smaller(const amount_t& arg) {
if (ptr->smaller) if (base->smaller)
delete ptr->smaller; delete base->smaller;
ptr->smaller = new amount_t(arg); base->smaller = new amount_t(arg);
} }
amount_t * larger() const { amount_t * larger() const {
return ptr->larger; return base->larger;
} }
void set_larger(const amount_t& arg) { void set_larger(const amount_t& arg) {
if (ptr->larger) if (base->larger)
delete ptr->larger; delete base->larger;
ptr->larger = new amount_t(arg); base->larger = new amount_t(arg);
} }
commodity_base_t::history_t * history() const { commodity_base_t::history_t * history() const {
return ptr->history; return base->history;
} }
void add_price(const std::time_t date, const amount_t& price) { void add_price(const std::time_t date, const amount_t& price) {
return ptr->add_price(date, price); return base->add_price(date, price);
} }
bool remove_price(const std::time_t date) { bool remove_price(const std::time_t date) {
return ptr->remove_price(date); return base->remove_price(date);
} }
amount_t value(const std::time_t moment = std::time(NULL)) const { amount_t value(const std::time_t moment = std::time(NULL)) const {
return ptr->value(moment); return base->value(moment);
} }
bool valid() const; bool valid() const;
@ -544,7 +544,7 @@ class commodity_t
class annotated_commodity_t : public commodity_t class annotated_commodity_t : public commodity_t
{ {
public: public:
const commodity_t * base; const commodity_t * ptr;
amount_t price; amount_t price;
std::time_t date; std::time_t date;

View file

@ -455,7 +455,7 @@ inline commodity_t * read_binary_commodity(char *& data)
commodity_t * commodity = new commodity_t; commodity_t * commodity = new commodity_t;
*commodities_next++ = commodity; *commodities_next++ = commodity;
commodity->ptr = commodity->base =
base_commodities[read_binary_long<commodity_base_t::ident_t>(data) - 1]; base_commodities[read_binary_long<commodity_base_t::ident_t>(data) - 1];
read_binary_string(data, commodity->qualified_symbol); read_binary_string(data, commodity->qualified_symbol);
@ -469,13 +469,13 @@ inline commodity_t * read_binary_commodity_annotated(char *& data)
annotated_commodity_t * commodity = new annotated_commodity_t; annotated_commodity_t * commodity = new annotated_commodity_t;
*commodities_next++ = commodity; *commodities_next++ = commodity;
commodity->ptr = commodity->base =
base_commodities[read_binary_long<commodity_base_t::ident_t>(data) - 1]; base_commodities[read_binary_long<commodity_base_t::ident_t>(data) - 1];
read_binary_string(data, commodity->qualified_symbol); read_binary_string(data, commodity->qualified_symbol);
commodity->annotated = true; commodity->annotated = true;
commodity->base = commodity->ptr =
commodities[read_binary_long<commodity_t::ident_t>(data) - 1]; commodities[read_binary_long<commodity_t::ident_t>(data) - 1];
read_binary_amount(data, commodity->price); read_binary_amount(data, commodity->price);
read_binary_long(data, commodity->date); read_binary_long(data, commodity->date);
@ -658,7 +658,7 @@ unsigned int read_binary_journal(std::istream& in,
if (read_binary_number<char>(data) == 0) { if (read_binary_number<char>(data) == 0) {
commodity = read_binary_commodity(data); commodity = read_binary_commodity(data);
mapping_key = commodity->ptr->symbol; mapping_key = commodity->base->symbol;
} else { } else {
read_binary_string(data, mapping_key); read_binary_string(data, mapping_key);
commodity = read_binary_commodity_annotated(data); commodity = read_binary_commodity_annotated(data);
@ -677,7 +677,7 @@ unsigned int read_binary_journal(std::istream& in,
commodity)); commodity));
if (! result.second && commodity->annotated) if (! result.second && commodity->annotated)
throw new error(std::string("Failed to read commodity from cache: ") + throw new error(std::string("Failed to read commodity from cache: ") +
commodity->ptr->symbol); commodity->base->symbol);
} }
commodity_t::ident_t ident; commodity_t::ident_t ident;
@ -1005,7 +1005,7 @@ void write_binary_commodity(std::ostream& out, commodity_t * commodity)
{ {
commodity->ident = ++commodity_index; commodity->ident = ++commodity_index;
write_binary_long(out, commodity->ptr->ident); write_binary_long(out, commodity->base->ident);
write_binary_string(out, commodity->qualified_symbol); write_binary_string(out, commodity->qualified_symbol);
} }
@ -1014,7 +1014,7 @@ void write_binary_commodity_annotated(std::ostream& out,
{ {
commodity->ident = ++commodity_index; commodity->ident = ++commodity_index;
write_binary_long(out, commodity->ptr->ident); write_binary_long(out, commodity->base->ident);
write_binary_string(out, commodity->qualified_symbol); write_binary_string(out, commodity->qualified_symbol);
annotated_commodity_t * ann_comm = annotated_commodity_t * ann_comm =

View file

@ -1115,6 +1115,16 @@ OPT_BEGIN(total_data, "J") {
config->format_string = config->plot_total_format; config->format_string = config->plot_total_format;
} OPT_END(total_data); } OPT_END(total_data);
OPT_BEGIN(ansi, "") {
format_t::ansi_codes = true;
format_t::ansi_invert = false;
} OPT_END(ansi);
OPT_BEGIN(ansi_invert, "") {
format_t::ansi_codes =
format_t::ansi_invert = true;
} OPT_END(ansi);
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// //
// Commodity reporting // Commodity reporting
@ -1192,6 +1202,8 @@ option_t config_options[CONFIG_OPTIONS_SIZE] = {
{ "add-budget", '\0', false, opt_add_budget, false }, { "add-budget", '\0', false, opt_add_budget, false },
{ "amount", 't', true, opt_amount, false }, { "amount", 't', true, opt_amount, false },
{ "amount-data", 'j', false, opt_amount_data, false }, { "amount-data", 'j', false, opt_amount_data, false },
{ "ansi", '\0', false, opt_ansi, false },
{ "ansi-invert", '\0', false, opt_ansi_invert, false },
{ "average", 'A', false, opt_average, false }, { "average", 'A', false, opt_average, false },
{ "balance-format", '\0', true, opt_balance_format, false }, { "balance-format", '\0', true, opt_balance_format, false },
{ "basis", 'B', false, opt_basis, false }, { "basis", 'B', false, opt_basis, false },

View file

@ -107,7 +107,7 @@ class config_t
std::list<item_handler<transaction_t> *>& ptrs); std::list<item_handler<transaction_t> *>& ptrs);
}; };
#define CONFIG_OPTIONS_SIZE 86 #define CONFIG_OPTIONS_SIZE 88
extern option_t config_options[CONFIG_OPTIONS_SIZE]; extern option_t config_options[CONFIG_OPTIONS_SIZE];
void option_help(std::ostream& out); void option_help(std::ostream& out);

View file

@ -2,8 +2,8 @@
# Process this file with autoconf to produce a configure script. # Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59) AC_PREREQ(2.59)
AC_INIT(ledger, 2.6, johnw@newartisans.com) AC_INIT(ledger, 3.0, johnw@newartisans.com)
AM_INIT_AUTOMAKE(ledger, 2.6) AM_INIT_AUTOMAKE(ledger, 3.0)
AC_CONFIG_SRCDIR([main.cc]) AC_CONFIG_SRCDIR([main.cc])
AC_CONFIG_HEADER([acconf.h]) AC_CONFIG_HEADER([acconf.h])

View file

@ -7,6 +7,9 @@
namespace ledger { namespace ledger {
bool format_t::ansi_codes = false;
bool format_t::ansi_invert = false;
std::string truncated(const std::string& str, unsigned int width, std::string truncated(const std::string& str, unsigned int width,
const int style) const int style)
{ {
@ -264,6 +267,26 @@ static bool entry_state(const entry_t * entry, transaction_t::state_t * state)
return ! hetero; return ! hetero;
} }
namespace {
void mark_red(std::ostream& out, const element_t * elem) {
out.setf(std::ios::left);
out.width(0);
out << "\e[31m";
if (elem->align_left)
out << std::left;
else
out << std::right;
if (elem->min_width > 0)
out.width(elem->min_width);
}
void mark_plain(std::ostream& out) {
out << "\e[0m";
}
}
void format_t::format(std::ostream& out_str, const details_t& details) const void format_t::format(std::ostream& out_str, const details_t& details) const
{ {
for (const element_t * elem = elements; elem; elem = elem->next) { for (const element_t * elem = elements; elem; elem = elem->next) {
@ -322,15 +345,37 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
case value_t::BOOLEAN: case value_t::BOOLEAN:
out << (*((bool *) value.data) ? "true" : "false"); out << (*((bool *) value.data) ? "true" : "false");
break; break;
case value_t::INTEGER: case value_t::INTEGER:
if (ansi_codes) {
if (ansi_invert) {
if (*((long *) value.data) > 0)
mark_red(out, elem);
} else {
if (*((long *) value.data) < 0)
mark_red(out, elem);
}
}
out << *((long *) value.data); out << *((long *) value.data);
break; break;
case value_t::DATETIME: case value_t::DATETIME:
out << *((datetime_t *) value.data); out << *((datetime_t *) value.data);
break; break;
case value_t::AMOUNT: case value_t::AMOUNT:
if (ansi_codes) {
if (ansi_invert) {
if (*((amount_t *) value.data) > 0)
mark_red(out, elem);
} else {
if (*((amount_t *) value.data) < 0)
mark_red(out, elem);
}
}
out << *((amount_t *) value.data); out << *((amount_t *) value.data);
break; break;
case value_t::BALANCE: case value_t::BALANCE:
bal = (balance_t *) value.data; bal = (balance_t *) value.data;
// fall through... // fall through...
@ -339,14 +384,28 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
if (! bal) if (! bal)
bal = &((balance_pair_t *) value.data)->quantity; bal = &((balance_pair_t *) value.data)->quantity;
if (ansi_codes) {
if (ansi_invert) {
if (*bal > 0)
mark_red(out, elem);
} else {
if (*bal < 0)
mark_red(out, elem);
}
}
bal->write(out, elem->min_width, bal->write(out, elem->min_width,
(elem->max_width > 0 ? elem->max_width : elem->min_width)); (elem->max_width > 0 ?
elem->max_width : elem->min_width));
ignore_max_width = true; ignore_max_width = true;
break; break;
default: default:
assert(0); assert(0);
break; break;
} }
if (ansi_codes)
mark_plain(out);
break; break;
} }

View file

@ -72,6 +72,9 @@ struct format_t
std::string format_string; std::string format_string;
element_t * elements; element_t * elements;
static bool ansi_codes;
static bool ansi_invert;
format_t() : elements(NULL) { format_t() : elements(NULL) {
DEBUG_PRINT("ledger.memory.ctors", "ctor format_t"); DEBUG_PRINT("ledger.memory.ctors", "ctor format_t");
} }