summaryrefslogtreecommitdiff
path: root/reports.cc
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2004-05-13 18:37:12 -0400
committerjohnw <johnw@newartisans.com>2004-05-13 18:37:12 -0400
commit9ca1d24d30a485300b9486b18c068660da630cc1 (patch)
tree50d29d69278934be7e318a565d6223a6a0ab2c36 /reports.cc
parent31dafc3c163e4ceae42b1477c4eaeb4425b2b883 (diff)
downloadfork-ledger-9ca1d24d30a485300b9486b18c068660da630cc1.tar.gz
fork-ledger-9ca1d24d30a485300b9486b18c068660da630cc1.tar.bz2
fork-ledger-9ca1d24d30a485300b9486b18c068660da630cc1.zip
initial import into Darcs
Diffstat (limited to 'reports.cc')
-rw-r--r--reports.cc597
1 files changed, 433 insertions, 164 deletions
diff --git a/reports.cc b/reports.cc
index 6ec5c298..d4289a3e 100644
--- a/reports.cc
+++ b/reports.cc
@@ -1,13 +1,15 @@
#include "ledger.h"
-#define LEDGER_VERSION "1.3"
+#define LEDGER_VERSION "1.6"
-#include <fstream>
+#include <cstring>
#include <unistd.h>
namespace ledger {
static bool cleared_only = false;
+static bool uncleared_only = false;
+static bool cost_basis = false;
static bool show_virtual = true;
static bool get_quotes = false;
static bool show_children = false;
@@ -15,6 +17,12 @@ static bool show_sorted = false;
static bool show_empty = false;
static bool show_subtotals = true;
static bool full_names = false;
+static bool print_monthly = false;
+static bool gnuplot_safe = false;
+
+static amount * lower_limit = NULL;
+
+static mask * negonly_regexp = NULL;
static std::time_t begin_date;
static bool have_beginning = false;
@@ -65,28 +73,145 @@ static bool matches_date_range(entry * ent)
// Balance reporting code
//
-static void display_total(std::ostream& out, totals& balance,
- account * acct, bool top_level,
- int * headlines)
+static bool satisfies_limit(totals& balance)
{
- bool displayed = false;
+ bool satisfies = true;
+ bool invert = false;
- if (acct->checked == 1 &&
- (show_empty || ! acct->balance.is_zero())) {
- displayed = true;
+ assert(lower_limit);
- acct->balance.print(out, 20);
- if (show_subtotals && top_level)
- balance.credit(acct->balance);
+ if (balance.is_negative())
+ invert = true;
+ else
+ lower_limit->negate();
- if (acct->parent && ! full_names && ! top_level) {
- for (const account * a = acct; a; a = a->parent)
- out << " ";
- out << acct->name << std::endl;
+ balance.credit(lower_limit);
+ if (balance.is_negative())
+ satisfies = invert;
+ else
+ satisfies = ! invert;
+
+ lower_limit->negate();
+ balance.credit(lower_limit);
+
+ if (invert)
+ lower_limit->negate();
+
+ return satisfies;
+}
+
+static bool satisfies_limit(amount * balance)
+{
+ bool satisfies = true;
+ bool invert = false;
+
+ assert(lower_limit);
+
+ if (balance->is_negative())
+ invert = true;
+ else
+ lower_limit->negate();
+
+ balance->credit(lower_limit);
+ if (balance->is_negative())
+ satisfies = invert;
+ else
+ satisfies = ! invert;
+
+ lower_limit->negate();
+ balance->credit(lower_limit);
+
+ if (invert)
+ lower_limit->negate();
+
+ return satisfies;
+}
+
+static void adjust_total(account * acct)
+{
+ for (accounts_map_iterator i = acct->children.begin();
+ i != acct->children.end();
+ i++)
+ adjust_total((*i).second);
+
+ if (acct->checked == 1) {
+ if (! show_empty && acct->balance.is_zero())
+ acct->checked = 2;
+ else if (lower_limit && ! satisfies_limit(acct->balance))
+ acct->checked = 2;
+ else if (negonly_regexp && negonly_regexp->match(acct->as_str()) &&
+ ! acct->balance.is_negative())
+ acct->checked = 2;
+
+ if (acct->checked == 2) {
+ acct->balance.negate();
+ for (account * a = acct->parent; a; a = a->parent)
+ a->balance.credit(acct->balance);
+ }
+ }
+}
+
+static int acct_visible_children(account * acct)
+{
+ int count = 0;
+ for (accounts_map_iterator i = acct->children.begin();
+ i != acct->children.end();
+ i++) {
+ if ((*i).second->checked == 1) {
+ if ((*i).second->children.size() == 0)
+ count++;
+ else
+ count += acct_visible_children((*i).second);
+ }
+ }
+ return count;
+}
+
+static void display_total(std::ostream& out, totals& balance,
+ account * acct, int level, int * headlines)
+{
+ // If the number of visible children is exactly one, do not print
+ // the parent account, but just the one child (whose name will
+ // output with sufficiently qualification).
+
+ if (acct->checked == 1 && acct_visible_children(acct) != 1) {
+ if (acct->balance.is_zero()) {
+ out.width(20);
+ out << " ";
+ } else {
+ acct->balance.print(out, 20);
+ }
+
+ if (level == 0 || full_names || ! show_subtotals) {
+ if (show_subtotals) {
+ balance.credit(acct->balance);
+ (*headlines)++;
+ }
+
+ out << " " << acct->as_str() << std::endl;
} else {
- out << " " << acct->as_str() << std::endl;
- (*headlines)++;
+ for (int i = 0; i < level + 1; i++)
+ out << " ";
+
+ assert(acct->parent);
+ if (acct_visible_children(acct->parent) == 1) {
+ /* If the account has no other siblings, instead of printing:
+ Parent
+ Child
+ print:
+ Parent:Child */
+ const account * parent;
+ for (parent = acct->parent;
+ parent->parent && acct_visible_children(parent->parent) == 1;
+ parent = parent->parent) {}
+
+ out << acct->as_str(parent) << std::endl;
+ } else {
+ out << acct->name << std::endl;
+ }
}
+
+ level++;
}
// Display balances for all child accounts
@@ -94,10 +219,10 @@ static void display_total(std::ostream& out, totals& balance,
for (accounts_map_iterator i = acct->children.begin();
i != acct->children.end();
i++)
- display_total(out, balance, (*i).second, ! displayed, headlines);
+ display_total(out, balance, (*i).second, level, headlines);
}
-void report_balances(std::ostream& out, regexps_map& regexps)
+void report_balances(std::ostream& out, regexps_list& regexps)
{
// Walk through all of the ledger entries, computing the account
// totals
@@ -105,7 +230,8 @@ void report_balances(std::ostream& out, regexps_map& regexps)
for (entries_list_iterator i = main_ledger->entries.begin();
i != main_ledger->entries.end();
i++) {
- if ((cleared_only && ! (*i)->cleared) || ! matches_date_range(*i))
+ if ((cleared_only && ! (*i)->cleared) ||
+ (uncleared_only && (*i)->cleared) || ! matches_date_range(*i))
continue;
for (std::list<transaction *>::iterator x = (*i)->xacts.begin();
@@ -117,16 +243,17 @@ void report_balances(std::ostream& out, regexps_map& regexps)
for (account * acct = (*x)->acct;
acct;
acct = show_subtotals ? acct->parent : NULL) {
+ bool by_exclusion = false;
+ bool match = false;
+
if (acct->checked == 0) {
if (regexps.empty()) {
if (! (show_children || ! acct->parent))
acct->checked = 2;
else
acct->checked = 1;
- }
- else {
- bool by_exclusion = false;
- bool match = matches(regexps, acct->as_str(), &by_exclusion);
+ } else {
+ match = matches(regexps, acct->as_str(), &by_exclusion);
if (! match) {
acct->checked = 2;
}
@@ -144,9 +271,26 @@ void report_balances(std::ostream& out, regexps_map& regexps)
if (acct->checked == 1) {
amount * street = (*x)->cost->street(get_quotes);
+ if (cost_basis &&
+ street->commdty() == (*x)->cost->commdty() &&
+ (*x)->cost->has_price()) {
+ street = (*x)->cost->value();
+ }
acct->balance.credit(street);
delete street;
}
+ else if (show_subtotals) {
+ if (! regexps.empty() && ! match) {
+ for (account * a = acct->parent; a; a = a->parent) {
+ if (matches(regexps, a->as_str(), &by_exclusion) &&
+ ! by_exclusion) {
+ match = true;
+ break;
+ }
+ }
+ if (! match) break;
+ }
+ }
}
}
}
@@ -159,12 +303,14 @@ void report_balances(std::ostream& out, regexps_map& regexps)
for (accounts_map_iterator i = main_ledger->accounts.begin();
i != main_ledger->accounts.end();
- i++)
- display_total(out, balance, (*i).second, true, &headlines);
+ i++) {
+ adjust_total((*i).second);
+ display_total(out, balance, (*i).second, 0, &headlines);
+ }
// Print the total of all the balances shown
- if (show_subtotals && headlines > 1) {
+ if (show_subtotals && headlines > 1 && ! balance.is_zero()) {
out << "--------------------" << std::endl;
balance.print(out, 20);
out << std::endl;
@@ -188,98 +334,183 @@ static std::string truncated(const std::string& str, int width)
return buf;
}
-void print_register(const std::string& acct_name, std::ostream& out,
- regexps_map& regexps)
+enum periodicity_t {
+ PERIOD_NONE,
+ PERIOD_MONTHLY,
+ PERIOD_WEEKLY_SUN,
+ PERIOD_WEEKLY_MON
+};
+
+void print_register_transaction(std::ostream& out, entry *ent,
+ transaction *xact, totals& balance)
{
- mask acct_regex(acct_name);
+ char buf[32];
+ std::strftime(buf, 31, "%m/%d ", std::localtime(&ent->date));
+ out << buf;
- // Walk through all of the ledger entries, printing their register
- // formatted equivalent
+ out.width(25);
+ if (ent->desc.empty())
+ out << " ";
+ else
+ out << std::left << truncated(ent->desc, 25);
+ out << " ";
+
+ // Always display the street value, if prices have been
+ // specified
+
+ amount * street = xact->cost->street(get_quotes);
+ balance.credit(street);
+
+ // If there are two transactions, use the one which does not
+ // refer to this account. If there are more than two, print
+ // "<Splits...>", unless the -s option is being used (show
+ // children), in which case print all of the splits, like
+ // gnucash does.
+
+ transaction * xp;
+ if (ent->xacts.size() == 2) {
+ if (xact == ent->xacts.front())
+ xp = ent->xacts.back();
+ else
+ xp = ent->xacts.front();
+ } else {
+ xp = xact;
+ }
+ std::string xact_str = xp->acct_as_str();
- totals balance;
+ if (xp == xact && ! show_subtotals)
+ xact_str = "<Splits...>";
- for (entries_list_iterator i = main_ledger->entries.begin();
- i != main_ledger->entries.end();
- i++) {
- if ((cleared_only && ! (*i)->cleared) ||
- ! matches_date_range(*i) || ! (*i)->matches(regexps))
+ out.width(22);
+ out << std::left << truncated(xact_str, 22) << " ";
+
+ out.width(12);
+ out << std::right << street->as_str(true);
+ delete street;
+
+ balance.print(out, 12);
+
+ out << std::endl;
+
+ if (! show_children || xp != xact)
+ return;
+
+ for (std::list<transaction *>::iterator y = ent->xacts.begin();
+ y != ent->xacts.end();
+ y++) {
+ if (xact == *y)
continue;
- for (std::list<transaction *>::iterator x = (*i)->xacts.begin();
- x != (*i)->xacts.end();
- x++) {
- if (! acct_regex.match((*x)->acct->as_str()))
- continue;
+ out << " ";
- char buf[32];
- std::strftime(buf, 31, "%m/%d ", std::localtime(&(*i)->date));
- out << buf;
+ out.width(22);
+ out << std::left << truncated((*y)->acct_as_str(), 22) << " ";
- out.width(25);
- if ((*i)->desc.empty())
- out << " ";
- else
- out << std::left << truncated((*i)->desc, 25);
- out << " ";
+ out.width(12);
+ street = (*y)->cost->street(get_quotes);
+ out << std::right << street->as_str(true) << std::endl;
+ delete street;
+ }
+}
- // Always display the street value, if prices have been
- // specified
+void print_register_period(std::ostream& out, std::time_t date,
+ account *acct, amount& sum, totals& balance)
+{
+ char buf[32];
+ std::strftime(buf, 31, "%Y/%m/%d ", std::localtime(&date));
+ out << buf;
- amount * street = (*x)->cost->street(get_quotes);
- balance.credit(street);
+ if (! gnuplot_safe) {
+ out.width(20);
+ std::strftime(buf, 31, "%B", std::localtime(&date));
+ out << std::left << truncated(buf, 20);
+ out << " ";
- // If there are two transactions, use the one which does not
- // refer to this account. If there are more than two, print
- // "<Splits...>", unless the -s option is being used (show
- // children), in which case print all of the splits, like
- // gnucash does.
+ out.width(22);
+ out << std::left << truncated(acct->as_str(), 22) << " ";
+ } else {
+ commodity * cmdty = sum.commdty();
+ cmdty->symbol = "";
+ cmdty->separate = false;
+ cmdty->thousands = false;
+ cmdty->european = false;
+ }
- transaction * xact;
- if (! full_names && (*i)->xacts.size() == 2) {
- if (*x == (*i)->xacts.front())
- xact = (*i)->xacts.back();
- else
- xact = (*i)->xacts.front();
- } else {
- xact = *x;
- }
- std::string xact_str = xact->acct_as_str();
+ out.width(12);
+ out << std::right << sum.as_str();
- if (xact == *x && ! show_subtotals)
- xact_str = "<Splits...>";
+ if (! gnuplot_safe)
+ balance.print(out, 12);
- out.width(22);
- out << std::left << truncated(xact_str, 22) << " ";
+ out << std::endl;
+}
- out.width(12);
- out << std::right << street->as_str(true);
- delete street;
+void print_register(std::ostream& out, const std::string& acct_name,
+ regexps_list& regexps, periodicity_t period = PERIOD_NONE)
+{
+ mask acct_regex(acct_name);
- balance.print(out, 12);
+ // Walk through all of the ledger entries, printing their register
+ // formatted equivalent
- out << std::endl;
+ totals balance;
+ amount * period_sum = NULL; // jww (2004-04-27): should be 'totals' type
+ std::time_t last_date;
+ account * last_acct;
+ int last_mon = -1;
- if (! show_children || xact != *x)
- continue;
+ for (entries_list_iterator i = main_ledger->entries.begin();
+ i != main_ledger->entries.end();
+ i++) {
+ if ((cleared_only && ! (*i)->cleared) ||
+ (uncleared_only && (*i)->cleared) ||
+ ! matches_date_range(*i) || ! (*i)->matches(regexps))
+ continue;
- for (std::list<transaction *>::iterator y = (*i)->xacts.begin();
- y != (*i)->xacts.end();
- y++) {
- if (*x == *y)
- continue;
+ int entry_mon = std::localtime(&(*i)->date)->tm_mon;
- out << " ";
+ if (period_sum && period == PERIOD_MONTHLY &&
+ last_mon != -1 && entry_mon != last_mon) {
+ assert(last_acct);
+ print_register_period(out, last_date, last_acct,
+ *period_sum, balance);
+ delete period_sum;
+ period_sum = NULL;
+ }
- out.width(22);
- out << std::left << truncated((*y)->acct_as_str(), 22) << " ";
+ for (std::list<transaction *>::iterator x = (*i)->xacts.begin();
+ x != (*i)->xacts.end();
+ x++) {
+ if (! acct_regex.match((*x)->acct->as_str()) ||
+ (lower_limit && ! satisfies_limit((*x)->cost)))
+ continue;
+
+ if (period == PERIOD_NONE) {
+ print_register_transaction(out, *i, *x, balance);
+ } else {
+ amount * street = (*x)->cost->street(get_quotes);
+ balance.credit(street);
+
+ if (period_sum) {
+ period_sum->credit(street);
+ delete street;
+ } else {
+ period_sum = street;
+ }
- out.width(12);
- street = (*y)->cost->street(get_quotes);
- out << std::right << street->as_str(true) << std::endl;
- delete street;
+ last_acct = (*x)->acct;
+ last_date = (*i)->date;
+ last_mon = entry_mon;
}
}
}
+
+ if (period_sum) {
+ if (last_acct)
+ print_register_period(out, last_date, last_acct,
+ *period_sum, balance);
+ delete period_sum;
+ }
}
//////////////////////////////////////////////////////////////////////
@@ -289,14 +520,14 @@ void print_register(const std::string& acct_name, std::ostream& out,
// balances.
//
-static void equity_entry(account * acct, regexps_map& regexps,
+static void equity_entry(account * acct, regexps_list& regexps,
std::ostream& out)
{
if (! acct->balance.is_zero() &&
(regexps.empty() || matches(regexps, acct->as_str()))) {
entry opening(main_ledger);
- opening.date = std::time(NULL);
+ opening.date = have_ending ? end_date : std::time(NULL);
opening.cleared = true;
opening.desc = "Opening Balance";
@@ -330,7 +561,7 @@ static void equity_entry(account * acct, regexps_map& regexps,
equity_entry((*i).second, regexps, out);
}
-void equity_ledger(std::ostream& out, regexps_map& regexps)
+void equity_ledger(std::ostream& out, regexps_list& regexps)
{
// The account have their current totals already generated as a
// result of parsing. We just have to output those values.
@@ -347,9 +578,9 @@ void equity_ledger(std::ostream& out, regexps_map& regexps)
void add_new_entry(int index, int argc, char **argv)
{
- regexps_map regexps;
- entry added(main_ledger);
- entry * matching = NULL;
+ regexps_list regexps;
+ entry added(main_ledger);
+ entry * matching = NULL;
assert(index < argc);
@@ -423,14 +654,17 @@ void add_new_entry(int index, int argc, char **argv)
account * acct = NULL;
commodity * cmdty = NULL;
- for (std::list<transaction *>::iterator x = matching->xacts.begin();
- x != matching->xacts.end();
- x++) {
- if (acct_regex.match((*x)->acct->as_str())) {
- acct = (*x)->acct;
- cmdty = (*x)->cost->commdty();
- break;
- }
+
+ if (matching) {
+ for (std::list<transaction *>::iterator x = matching->xacts.begin();
+ x != matching->xacts.end();
+ x++) {
+ if (acct_regex.match((*x)->acct->as_str())) {
+ acct = (*x)->acct;
+ cmdty = (*x)->cost->commdty();
+ break;
+ }
+ }
}
if (acct)
@@ -466,7 +700,12 @@ void add_new_entry(int index, int argc, char **argv)
}
} else {
transaction * xact = new transaction();
- xact->acct = matching->xacts.back()->acct;
+ if (! matching) {
+ std::cerr << "Error: Could not figure out the account to draw from."
+ << std::endl;
+ std::exit(1);
+ }
+ xact->acct = matching->xacts.back()->acct;
xact->cost = NULL;
added.xacts.push_back(xact);
}
@@ -480,8 +719,8 @@ void add_new_entry(int index, int argc, char **argv)
// "wash" ugly ledger files. It's written here, instead of ledger.cc,
// in order to access the static globals above.
-void book::print(std::ostream& out, regexps_map& regexps,
- bool shortcut) const
+void book::print(std::ostream& out, regexps_list& regexps,
+ bool shortcut) const
{
for (entries_list_const_iterator i = entries.begin();
i != entries.end();
@@ -506,25 +745,33 @@ static void show_help(std::ostream& out)
<< " -b DATE specify a beginning date" << std::endl
<< " -e DATE specify an ending date" << std::endl
<< " -c do not show future entries (same as -e TODAY)" << std::endl
- << " -C also show cleared transactions" << std::endl
+ << " -C show only cleared transactions and balances" << std::endl
<< " -d DATE specify a date mask ('-d mon', for all mondays)" << std::endl
+ << " -E also show accounts with zero totals" << std::endl
<< " -f FILE specify pathname of ledger data file" << std::endl
<< " -F print each account's full name" << std::endl
<< " -h display this help text" << std::endl
<< " -i FILE read the list of inclusion regexps from FILE" << std::endl
- << " -n do not generate totals for parent accounts" << std::endl
+ << " -l AMT don't print balance totals whose abs value is <AMT" << std::endl
+ << " -M print register using monthly sub-totals" << std::endl
+ << " -G use with -M to produce gnuplot-friendly output" << std::endl
+ << " -n do not calculate parent account totals" << std::endl
+ << " -N REGEX accounts matching REGEXP only display if negative" << std::endl
<< " -p ARG set a price, or read prices from a file" << std::endl
<< " -P download price quotes from the Internet" << std::endl
<< " (works by running the command \"getquote SYMBOL\")" << std::endl
<< " -R do not factor in virtual transactions" << std::endl
<< " -s show sub-accounts in balance totals" << std::endl
- << " -S show empty accounts in balance totals" << std::endl
+ << " -S sort the output of \"print\" by date" << std::endl
+ << " -U show only uncleared transactions and balances" << std::endl
<< " -v display version information" << std::endl << std::endl
<< "commands:" << std::endl
<< " balance show balance totals" << std::endl
<< " register display a register for ACCOUNT" << std::endl
<< " print print all ledger entries" << std::endl
- << " equity generate equity ledger for all entries" << std::endl;
+ << " equity generate equity ledger for all entries" << std::endl
+ << " entry output a newly formed entry, based on arguments"
+ << std::endl;
}
//////////////////////////////////////////////////////////////////////
@@ -534,15 +781,18 @@ static void show_help(std::ostream& out)
int main(int argc, char * argv[])
{
- std::istream * file = NULL;
- std::string prices;
- regexps_map regexps;
- int index;
+ int index;
+ std::string prices;
+ std::string limit;
+ regexps_list regexps;
+
+ std::vector<std::string> files;
// Parse the command-line options
int c;
- while (-1 != (c = getopt(argc, argv, "+b:e:d:cChRV:f:i:p:PvsSEnF"))) {
+ while (-1 != (c = getopt(argc, argv,
+ "+b:e:d:cCUhBRV:f:i:p:PvsSEnFMGl:N:"))) {
switch (char(c)) {
case 'b':
have_beginning = true;
@@ -574,19 +824,27 @@ int main(int argc, char * argv[])
break;
case 'h': show_help(std::cout); break;
- case 'f': file = new std::ifstream(optarg); break;
-
+ case 'f': files.push_back(optarg); break;
case 'C': cleared_only = true; break;
+ case 'U': uncleared_only = true; break;
+ case 'B': cost_basis = true; break;
case 'R': show_virtual = false; break;
case 's': show_children = true; break;
case 'S': show_sorted = true; break;
case 'E': show_empty = true; break;
case 'n': show_subtotals = false; break;
case 'F': full_names = true; break;
+ case 'M': print_monthly = true; break;
+ case 'G': gnuplot_safe = true; break;
+
+ case 'N':
+ negonly_regexp = new mask(optarg);
+ break;
// -i path-to-file-of-regexps
case 'i':
- read_regexps(optarg, regexps);
+ if (access(optarg, R_OK) != -1)
+ read_regexps(optarg, regexps);
break;
// -p "COMMODITY=PRICE"
@@ -599,6 +857,10 @@ int main(int argc, char * argv[])
get_quotes = true;
break;
+ case 'l':
+ limit = optarg;
+ break;
+
case 'v':
std::cout
<< "Ledger Accouting Tool " LEDGER_VERSION << std::endl
@@ -620,20 +882,6 @@ int main(int argc, char * argv[])
index = optind;
- // A ledger data file must be specified
-
- if (! file) {
- const char * p = std::getenv("LEDGER");
- if (p)
- file = new std::ifstream(p);
-
- if (! file || ! *file) {
- std::cerr << ("Please specify ledger file using -f option "
- "or LEDGER environment variable.") << std::endl;
- return 1;
- }
- }
-
// Read the command word
const std::string command = argv[index++];
@@ -655,49 +903,64 @@ int main(int argc, char * argv[])
for (; index < argc; index++)
regexps.push_back(mask(argv[index]));
- // Parse the ledger
-
-#ifdef READ_GNUCASH
- char buf[32];
- file->get(buf, 31);
- file->seekg(0);
-
- if (std::strncmp(buf, "<?xml version=\"1.0\"?>", 21) == 0)
- main_ledger = parse_gnucash(*file, command == "equity");
- else
-#endif
- main_ledger = parse_ledger(*file, regexps, command == "equity");
+ // A ledger data file must be specified
- delete file;
+ int entry_count = 0;
+
+ main_ledger = new book;
+
+ if (files.empty()) {
+ if (char * p = std::getenv("LEDGER")) {
+ for (p = std::strtok(p, ":"); p; p = std::strtok(NULL, ":")) {
+ char * sep = std::strrchr(p, '=');
+ if (sep) *sep++ = '\0';
+ entry_count += parse_ledger_file(main_ledger, std::string(p),
+ regexps, command == "equity", sep);
+ }
+ }
+ } else {
+ for (std::vector<std::string>::iterator i = files.begin();
+ i != files.end(); i++) {
+ char buf[4096];
+ char * p = buf;
+ std::strcpy(p, (*i).c_str());
+ char * sep = std::strrchr(p, '=');
+ if (sep) *sep++ = '\0';
+ entry_count += parse_ledger_file(main_ledger, std::string(p),
+ regexps, command == "equity", sep);
+ }
+ }
- if (! main_ledger)
+ if (entry_count == 0) {
+ std::cerr << ("Please specify ledger file(s) using -f option "
+ "or LEDGER environment variable.") << std::endl;
return 1;
+ }
// Record any prices specified by the user
if (! prices.empty()) {
- if (access(prices.c_str(), R_OK) != -1) {
- std::ifstream pricedb(prices.c_str());
- while (! pricedb.eof()) {
- char buf[80];
- pricedb.getline(buf, 79);
- if (*buf && ! std::isspace(*buf))
- parse_price_setting(buf);
- }
- } else {
+ if (access(prices.c_str(), R_OK) != -1)
+ read_prices(prices);
+ else
parse_price_setting(prices);
- }
}
+ // Parse the lower limit, if specified
+
+ if (! limit.empty())
+ lower_limit = create_amount(limit);
+
// Process the command
if (command == "balance" || command == "bal") {
report_balances(std::cout, regexps);
}
else if (command == "register" || command == "reg") {
- if (show_sorted)
+ if (show_sorted || print_monthly)
main_ledger->sort(cmp_entry_date());
- print_register(argv[name_index], std::cout, regexps);
+ print_register(std::cout, argv[name_index], regexps,
+ print_monthly ? PERIOD_MONTHLY : PERIOD_NONE);
}
else if (command == "print") {
if (show_sorted)
@@ -721,6 +984,12 @@ int main(int argc, char * argv[])
// process is about to give back its heap to the OS.
delete main_ledger;
+
+ if (lower_limit)
+ delete lower_limit;
+
+ if (negonly_regexp)
+ delete negonly_regexp;
#endif
return 0;