*** empty log message ***

This commit is contained in:
John Wiegley 2006-03-15 11:54:18 +00:00
parent 30f79b0761
commit 2964dd15b2
9 changed files with 174 additions and 48 deletions

23
NEWS
View file

@ -21,10 +21,22 @@
C 1.00 Gb = 1024 Mb
C 1.00 Tb = 1024 Gb
- Added --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 --ansi reporting option, which shows negative values in the
running total column of the register report as red, using ANSI
terminal codes; --ansi-invert makes non-negative values red (which
makes more sense for the income and budget reports).
The --ansi functionality is triggered by the format modifier "!",
for example the register reports uses the following for the total
(last) column:
%!12.80T
At the moment neither the balance report nor any of the other
reports make use of the ! modifier, and so will not change color
even if --ansi is used. However, you can modify these report format
strings yourself in ~/.ledgerrc if you wish to see red coloring of
negative sums in other places.
- Added --only predicate, which occurs during transaction processing
between --limit and --display. Here is a summary of how the three
@ -183,6 +195,9 @@
- Added a new "csv" command, for outputting results in CSV format.
- Ledger now expands ~ in file pathnames specified in environment
variables, initialization files and journal files.
- Effective dates may now be specified for entries:
2004/10/03=2004/09/30 Credit card company

113
config.cc
View file

@ -15,6 +15,14 @@
#include <unistd.h>
#endif
#ifdef HAVE_REALPATH
extern "C" char *realpath(const char *, char resolved_path[]);
#endif
#if defined(HAVE_GETPWUID) || defined(HAVE_GETPWNAM)
#include <pwd.h>
#endif
namespace ledger {
namespace {
@ -50,6 +58,71 @@ namespace {
}
}
std::string expand_path(const std::string& path)
{
if (path.length() == 0 || path[0] != '~')
return path;
const char * pfx = NULL;
std::string::size_type pos = path.find_first_of('/');
if (path.length() == 1 || pos == 1) {
pfx = std::getenv("HOME");
#ifdef HAVE_GETPWUID
if (! pfx) {
// Punt. We're trying to expand ~/, but HOME isn't set
struct passwd * pw = getpwuid(getuid());
if (pw)
pfx = pw->pw_dir;
}
#endif
}
#ifdef HAVE_GETPWNAM
else {
std::string user(path, 1, pos == std::string::npos ?
std::string::npos : pos - 1);
struct passwd * pw = getpwnam(user.c_str());
if (pw)
pfx = pw->pw_dir;
}
#endif
// if we failed to find an expansion, return the path unchanged.
if (! pfx)
return path;
std::string result(pfx);
if (pos == std::string::npos)
return result;
if (result.length() == 0 || result[result.length() - 1] != '/')
result += '/';
result += path.substr(pos + 1);
return result;
}
std::string resolve_path(const std::string& path)
{
std::string resolved;;
if (path[0] == '~')
resolved = expand_path(path);
else
resolved = path;
#ifdef HAVE_REALPATH
char buf[PATH_MAX];
::realpath(resolved.c_str(), buf);
return std::string(buf);
#else
return resolved;
#endif
}
void config_t::reset()
{
ledger::amount_expr.reset(new value_expr("a"));
@ -58,10 +131,10 @@ void config_t::reset()
pricing_leeway = 24 * 3600;
budget_flags = BUDGET_NO_BUDGET;
balance_format = "%20T %2_%-a\n";
register_format = ("%D %-.20P %-.22A %12.67t %12.80T\n%/"
"%32|%-.22A %12.67t %12.80T\n");
wide_register_format = ("%D %-.35P %-.38A %22.108t %22.132T\n%/"
"%48|%-.38A %22.108t %22.132T\n");
register_format = ("%D %-.20P %-.22A %12.67t %!12.80T\n%/"
"%32|%-.22A %12.67t %!12.80T\n");
wide_register_format = ("%D %-.35P %-.38A %22.108t %!22.132T\n%/"
"%48|%-.38A %22.108t %!22.132T\n");
csv_register_format = "\"%D\",\"%P\",\"%A\",\"%t\",\"%T\"\n";
plot_amount_format = "%D %(S(t))\n";
plot_total_format = "%D %(S(T))\n";
@ -727,19 +800,29 @@ OPT_BEGIN(version, "v") {
} OPT_END(version);
OPT_BEGIN(init_file, "i:") {
config->init_file = optarg;
std::string path = resolve_path(optarg);
if (access(path.c_str(), R_OK) != -1)
config->init_file = path;
else
throw new error(std::string("The init file '") + path +
"' does not exist or is not readable");
} OPT_END(init_file);
OPT_BEGIN(file, "f:") {
if (std::string(optarg) == "-" || access(optarg, R_OK) != -1)
if (std::string(optarg) == "-") {
config->data_file = optarg;
else
throw new error(std::string("The ledger file '") + optarg +
"' does not exist or is not readable");
} else {
std::string path = resolve_path(optarg);
if (access(path.c_str(), R_OK) != -1)
config->data_file = path;
else
throw new error(std::string("The ledger file '") + path +
"' does not exist or is not readable");
}
} OPT_END(file);
OPT_BEGIN(cache, ":") {
config->cache_file = optarg;
config->cache_file = resolve_path(optarg);
} OPT_END(cache);
OPT_BEGIN(no_cache, "") {
@ -747,8 +830,14 @@ OPT_BEGIN(no_cache, "") {
} OPT_END(no_cache);
OPT_BEGIN(output, "o:") {
if (std::string(optarg) != "-")
config->output_file = optarg;
if (std::string(optarg) != "-") {
std::string path = resolve_path(optarg);
if (access(path.c_str(), W_OK) != -1)
config->output_file = path;
else
throw new error(std::string("The output file '") + path +
"' is not writable");
}
} OPT_END(output);
OPT_BEGIN(account, "a:") {

View file

@ -117,6 +117,8 @@ void option_help(std::ostream& out);
#define OPT_END(tag)
std::string resolve_path(const std::string& path);
//////////////////////////////////////////////////////////////////////
void trace(const std::string& cat, const std::string& str);

View file

@ -274,7 +274,7 @@ AC_STRUCT_TM
# Checks for library functions.
#AC_FUNC_ERROR_AT_LINE
AC_HEADER_STDC
AC_CHECK_FUNCS([access mktime realpath stat strftime strptime])
AC_CHECK_FUNCS([access mktime realpath stat strftime strptime getpwuid getpwnam])
AC_CONFIG_FILES([Makefile])
AC_OUTPUT

View file

@ -118,8 +118,15 @@ element_t * format_t::parse_elements(const std::string& fmt)
}
++p;
if (*p == '-') {
current->align_left = true;
while (*p == '!' || *p == '-') {
switch (*p) {
case '-':
current->flags |= ELEMENT_ALIGN_LEFT;
break;
case '!':
current->flags |= ELEMENT_HIGHLIGHT;
break;
}
++p;
}
@ -273,7 +280,7 @@ namespace {
out.width(0);
out << "\e[31m";
if (elem->align_left)
if (elem->flags & ELEMENT_ALIGN_LEFT)
out << std::left;
else
out << std::right;
@ -294,7 +301,7 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
std::string name;
bool ignore_max_width = false;
if (elem->align_left)
if (elem->flags & ELEMENT_ALIGN_LEFT)
out << std::left;
else
out << std::right;
@ -347,7 +354,7 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
break;
case value_t::INTEGER:
if (ansi_codes) {
if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) {
if (ansi_invert) {
if (*((long *) value.data) > 0)
mark_red(out, elem);
@ -364,7 +371,7 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
break;
case value_t::AMOUNT:
if (ansi_codes) {
if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) {
if (ansi_invert) {
if (*((amount_t *) value.data) > 0)
mark_red(out, elem);
@ -384,7 +391,7 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
if (! bal)
bal = &((balance_pair_t *) value.data)->quantity;
if (ansi_codes) {
if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT) {
if (ansi_invert) {
if (*bal > 0)
mark_red(out, elem);
@ -404,7 +411,7 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
break;
}
if (ansi_codes)
if (ansi_codes && elem->flags & ELEMENT_HIGHLIGHT)
mark_plain(out);
break;
}

View file

@ -13,6 +13,9 @@ std::string truncated(const std::string& str, unsigned int width,
std::string partial_account_name(const account_t& account,
const unsigned int start_depth);
#define ELEMENT_ALIGN_LEFT 0x01
#define ELEMENT_HIGHLIGHT 0x02
struct element_t
{
enum kind_t {
@ -45,18 +48,18 @@ struct element_t
DEPTH_SPACER
};
bool align_left;
unsigned int min_width;
unsigned int max_width;
kind_t type;
std::string chars;
value_expr * val_expr;
kind_t type;
unsigned char flags;
std::string chars;
unsigned char min_width;
unsigned char max_width;
value_expr * val_expr;
struct element_t * next;
element_t() : align_left(false), min_width(0), max_width(0),
type(STRING), val_expr(NULL), next(NULL) {
element_t() : type(STRING), flags(false),
min_width(0), max_width(0),
val_expr(NULL), next(NULL) {
DEBUG_PRINT("ledger.memory.ctors", "ctor element_t");
}

View file

@ -21,7 +21,7 @@
#include <cstdio>
#include <cstdlib>
#if defined(__GNUG__) && __GNUG__ < 3
#ifdef HAVE_REALPATH
extern "C" char *realpath(const char *, char resolved_path[]);
#endif
@ -737,13 +737,15 @@ unsigned int textual_parser_t::parse(std::istream& in,
push_var<unsigned int> save_linenum(linenum);
path = p;
if (path[0] != '/' && path[0] != '\\') {
if (path[0] != '/' && path[0] != '\\' && path[0] != '~') {
std::string::size_type pos = save_path.prev.rfind('/');
if (pos == std::string::npos)
pos = save_path.prev.rfind('\\');
if (pos != std::string::npos)
path = std::string(save_path.prev, 0, pos + 1) + path;
}
path = resolve_path(path);
DEBUG_PRINT("ledger.textual.include", "line " << linenum << ": " <<
"Including path '" << path << "'");
@ -783,7 +785,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
else if (word == "def") {
if (! global_scope.get())
init_value_expr();
value_auto_ptr expr(parse_boolean_expr(p, global_scope.get()));
parse_value_definition(p);
}
break;
}
@ -856,10 +858,11 @@ void write_textual_journal(journal_t& journal, std::string path,
{
unsigned long index = 0;
std::string found;
char buf1[PATH_MAX];
char buf2[PATH_MAX];
#ifdef HAVE_REALPATH
char buf1[PATH_MAX];
char buf2[PATH_MAX];
::realpath(path.c_str(), buf1);
for (strings_list::iterator i = journal.sources.begin();
i != journal.sources.end();

View file

@ -1375,10 +1375,11 @@ void init_value_expr()
node->left->constant_i = 2;
node->set_right(new value_expr_t(value_expr_t::F_VALUE));
globals->define("P", node);
value_auto_ptr val(parse_boolean_expr("value=P(t,m)", globals));
value_auto_ptr tval(parse_boolean_expr("total_value=P(T,m)", globals));
value_auto_ptr valof(parse_boolean_expr("valueof(x)=P(x,m)", globals));
value_auto_ptr dvalof(parse_boolean_expr("datedvalueof(x,y)=P(x,y)", globals));
parse_value_definition("value=P(t,m)", globals);
parse_value_definition("total_value=P(T,m)", globals);
parse_value_definition("valueof(x)=P(x,m)", globals);
parse_value_definition("datedvalueof(x,y)=P(x,y)", globals);
node = new value_expr_t(value_expr_t::O_DEF);
node->set_left(new value_expr_t(value_expr_t::CONSTANT_I));
@ -1416,9 +1417,9 @@ void init_value_expr()
node->set_right(new value_expr_t(value_expr_t::F_DAY));
globals->define("dayof", node);
value_auto_ptr year(parse_boolean_expr("year=yearof(d)", globals));
value_auto_ptr month(parse_boolean_expr("month=monthof(d)", globals));
value_auto_ptr day(parse_boolean_expr("day=dayof(d)", globals));
parse_value_definition("year=yearof(d)", globals);
parse_value_definition("month=monthof(d)", globals);
parse_value_definition("day=dayof(d)", globals);
// Macros
node = parse_value_expr("P(a,d)");
@ -1437,8 +1438,8 @@ void init_value_expr()
globals->define("G", node);
globals->define("total_gain", node);
value_auto_ptr minx(parse_boolean_expr("min(x,y)=x<y?x:y", globals));
value_auto_ptr maxx(parse_boolean_expr("max(x,y)=x>y?x:y", globals));
parse_value_definition("min(x,y)=x<y?x:y", globals);
parse_value_definition("max(x,y)=x>y?x:y", globals);
}
value_expr_t * parse_value_expr(std::istream& in, scope_t * scope,

View file

@ -460,6 +460,12 @@ inline value_t compute_total(const details_t& details = details_t()) {
return total_expr->compute(details);
}
inline void parse_value_definition(const std::string& str,
scope_t * scope = NULL) {
value_auto_ptr expr
(parse_boolean_expr(str, scope ? scope : global_scope.get()));
}
//////////////////////////////////////////////////////////////////////
template <typename T>