From 9a14d6322ca1b1651ba31f997c1f719c1d8612e3 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Sat, 11 Oct 2003 21:39:09 +0000 Subject: *** empty log message *** --- Makefile | 4 +- ledger.cc | 13 ---- ledger.h | 16 ++++- parse.cc | 58 ++++++++------- reports.cc | 235 ++++++++++++++++++++++++++----------------------------------- 5 files changed, 146 insertions(+), 180 deletions(-) diff --git a/Makefile b/Makefile index adb62b03..42cc7bf4 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ CODE = amount.cc ledger.cc parse.cc reports.cc OBJS = $(patsubst %.cc,%.o,$(CODE)) CFLAGS = -Wall -ansi -pedantic -DFLAGS = -O3 -fomit-frame-pointer -mcpu=pentium -#DFLAGS = -g -DDEBUG=1 +#DFLAGS = -O3 -fomit-frame-pointer -mcpu=pentium +DFLAGS = -g -DDEBUG=1 INCS = LIBS = -lgmpxx -lgmp -lpcre diff --git a/ledger.cc b/ledger.cc index d0006d0b..073665e3 100644 --- a/ledger.cc +++ b/ledger.cc @@ -212,19 +212,6 @@ const std::string account::as_str() const return full_name; } -// Print out the entire ledger that was read in, sorted by date. -// This can be used to "wash" ugly ledger files. - -void book::print(std::ostream& out, regexps_map& regexps, - bool shortcut) const -{ - for (entries_list_const_iterator i = entries.begin(); - i != entries.end(); - i++) - if ((*i)->matches(regexps)) - (*i)->print(out, shortcut); -} - mask::mask(const std::string& pat) : exclude(false) { const char * p = pat.c_str(); diff --git a/ledger.h b/ledger.h index 83ca9693..b7fe5e06 100644 --- a/ledger.h +++ b/ledger.h @@ -1,5 +1,5 @@ #ifndef _LEDGER_H -#define _LEDGER_H "$Revision: 1.24 $" +#define _LEDGER_H "$Revision: 1.25 $" ////////////////////////////////////////////////////////////////////// // @@ -294,6 +294,20 @@ inline commodity::commodity(const std::string& sym, bool pre, bool sep, assert(result.second); } +// Parsing routines + +extern book * parse_ledger(std::istream& in, regexps_map& regexps, + bool compute_balances); +#ifdef READ_GNUCASH +extern book * parse_gnucash(std::istream& in, bool compute_balances); +#endif + +extern bool parse_date_mask(const char * date_str, + struct std::tm * result); +extern bool parse_date(const char * date_str, std::time_t * result, + const int year = -1); +extern void parse_price_setting(const std::string& setting); + } // namespace ledger #endif // _LEDGER_H diff --git a/parse.cc b/parse.cc index dc073c4a..5cdfb9e3 100644 --- a/parse.cc +++ b/parse.cc @@ -49,40 +49,44 @@ static const char *formats[] = { NULL }; -bool parse_date(const char * date_str, std::time_t * result, - const int year = -1) +bool parse_date_mask(const char * date_str, struct std::tm * result) { - struct std::tm when; - - std::time_t now = std::time(NULL); - struct std::tm * now_tm = std::localtime(&now); - for (const char ** f = formats; *f; f++) { - memset(&when, INT_MAX, sizeof(struct std::tm)); - if (strptime(date_str, *f, &when)) { - when.tm_hour = 0; - when.tm_min = 0; - when.tm_sec = 0; - - if (when.tm_year == -1) - when.tm_year = year == -1 ? now_tm->tm_year : year - 1900; - - if (std::strcmp(*f, "%Y") == 0) { - when.tm_mon = 0; - when.tm_mday = 1; - } else { - if (when.tm_mon == -1) - when.tm_mon = now_tm->tm_mon; - if (when.tm_mday == -1) - when.tm_mday = now_tm->tm_mday; - } - *result = std::mktime(&when); + memset(result, INT_MAX, sizeof(struct std::tm)); + if (strptime(date_str, *f, result)) return true; - } } return false; } +bool parse_date(const char * date_str, std::time_t * result, const int year) +{ + struct std::tm when; + + if (! parse_date_mask(date_str, &when)) + return false; + + static std::time_t now = std::time(NULL); + static struct std::tm * now_tm = std::localtime(&now); + + when.tm_hour = 0; + when.tm_min = 0; + when.tm_sec = 0; + + if (when.tm_year == -1) + when.tm_year = ((year == -1) ? now_tm->tm_year : (year - 1900)); + + if (when.tm_mon == -1) + when.tm_mon = now_tm->tm_mon; + + if (when.tm_mday == -1) + when.tm_mday = now_tm->tm_mday; + + *result = std::mktime(&when); + + return true; +} + void parse_price_setting(const std::string& setting) { char buf[128]; diff --git a/reports.cc b/reports.cc index 9713b99b..b050d33f 100644 --- a/reports.cc +++ b/reports.cc @@ -1,37 +1,64 @@ #include "ledger.h" -#define LEDGER_VERSION "1.1" +#define LEDGER_VERSION "1.2" #include #include namespace ledger { -extern book * parse_ledger(std::istream& in, regexps_map& regexps, - bool compute_balances); -#ifdef READ_GNUCASH -extern book * parse_gnucash(std::istream& in, bool compute_balances); -#endif +static bool show_cleared = false; +static bool show_virtual = true; +static bool get_quotes = false; +static bool show_children = false; +static bool show_sorted = false; +static bool show_empty = false; +static bool show_subtotals = true; +static bool full_names = false; -extern bool parse_date(const char * date_str, std::time_t * result, - const int year = -1); -extern void parse_price_setting(const std::string& setting); +static std::time_t begin_date; +static bool have_beginning = false; +static std::time_t end_date; +static bool have_ending = false; -static bool show_cleared; -static bool show_virtual; -static bool get_quotes; -static bool show_children; -static bool show_sorted; -static bool show_empty; -static bool show_subtotals; -static bool full_names; +static struct std::tm date_mask; +static bool have_date_mask = false; -static std::time_t begin_date; -static bool have_beginning; -static std::time_t end_date; -static bool have_ending; +static bool matches_date_range(entry * ent) +{ + if (have_beginning && difftime(ent->date, begin_date) < 0) + return false; + + if (have_ending && difftime(ent->date, end_date) >= 0) + return false; + + if (have_date_mask) { + struct std::tm * then = std::localtime(&ent->date); + + if (date_mask.tm_mon != -1 && + date_mask.tm_mon != then->tm_mon) + return false; + if (date_mask.tm_mday != -1 && + date_mask.tm_mday != then->tm_mday) + return false; + +#if 0 + // jww (2003-10-10): This causes only certain days of the week to + // print, even when it was not included in the mask. + if (date_mask.tm_wday != -1 && + date_mask.tm_wday != then->tm_wday) + return false; +#endif + + if (date_mask.tm_year != -1 && + date_mask.tm_year != then->tm_year) + return false; + } + + return true; +} ////////////////////////////////////////////////////////////////////// // @@ -78,9 +105,7 @@ 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 ((show_cleared && ! (*i)->cleared) || - (have_beginning && difftime((*i)->date, begin_date) < 0) || - (have_ending && difftime((*i)->date, end_date) >= 0)) + if ((show_cleared && ! (*i)->cleared) || ! matches_date_range(*i)) continue; for (std::list::iterator x = (*i)->xacts.begin(); @@ -177,17 +202,15 @@ void print_register(const std::string& acct_name, std::ostream& out, for (entries_list_iterator i = main_ledger->entries.begin(); i != main_ledger->entries.end(); i++) { - if (! (*i)->matches(regexps)) + if ((! have_beginning && ! have_ending && ! have_date_mask && + ! show_cleared && (*i)->cleared) || + ! matches_date_range(*i) || ! (*i)->matches(regexps)) continue; for (std::list::iterator x = (*i)->xacts.begin(); x != (*i)->xacts.end(); x++) { - if ((! have_beginning && ! have_ending && - ! show_cleared && (*i)->cleared) || - (have_beginning && difftime((*i)->date, begin_date) < 0) || - (have_ending && difftime((*i)->date, end_date) >= 0) || - (! acct_regex.match((*x)->acct->as_str()))) + if (! acct_regex.match((*x)->acct->as_str())) continue; char buf[32]; @@ -334,6 +357,25 @@ void equity_ledger(std::ostream& out, regexps_map& regexps) equity_entry((*i).second, regexps, out); } +// Print out the entire ledger that was read in, sorted by date. +// This can be used to "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 +{ + for (entries_list_const_iterator i = entries.begin(); + i != entries.end(); + i++) { + if ((show_cleared && ! (*i)->cleared) || + ! matches_date_range(*i) || + ! (*i)->matches(regexps)) + continue; + + (*i)->print(out, shortcut); + } +} + } // namespace ledger using namespace ledger; @@ -344,35 +386,28 @@ static void show_help(std::ostream& out) << "usage: ledger [options] COMMAND [options] [REGEXPS]" << std::endl << std::endl << "ledger options:" << std::endl - << " -C also show cleared transactions" << std::endl - << " -d DATE specify an implicit date range (e.g., -d april)" - << std::endl << " -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 + << " -d DATE specify a date mask ('-d mon', for all mondays)" << 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 - << " -R do not factor any virtual transactions" << std::endl - << " -V FILE use virtual mappings listed in FILE" << std::endl << " -i FILE read the list of inclusion regexps from FILE" << std::endl - << " -p FILE read the list of prices from FILE" << std::endl + << " -n do not generate totals for parent accounts" << 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 - << " -v display version information" << std::endl - << " -w print out warnings where applicable" << std::endl - << 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 + << " -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 - << std::endl - << "`balance' options:" << std::endl - << " -F print each account's full name" << std::endl - << " -n do not generate totals for parent accounts" << std::endl - << " -s show sub-accounts in balance totals" << std::endl - << " -S show empty accounts in balance totals" << std::endl; + << " equity generate equity ledger for all entries" << std::endl; } ////////////////////////////////////////////////////////////////////// @@ -387,94 +422,40 @@ int main(int argc, char * argv[]) regexps_map regexps; int index; - have_beginning = false; - have_ending = false; - show_cleared = false; - show_virtual = true; - show_children = false; - show_sorted = false; - show_empty = false; - show_subtotals = true; - full_names = false; - // Parse the command-line options int c; while (-1 != (c = getopt(argc, argv, "+b:e:d:cChRV:f:i:p:PvsSEnF"))) { switch (char(c)) { case 'b': - case 'e': { - std::time_t when; - if (! parse_date(optarg, &when)) { - std::cerr << "Error: Bad date string: " << optarg << std::endl; + have_beginning = true; + if (! parse_date(optarg, &begin_date)) { + std::cerr << "Error: Bad begin date: " << optarg << std::endl; return 1; } - - if (c == 'b') { - begin_date = when; - have_beginning = true; - } else { - end_date = when; - have_ending = true; - } break; - } -#if 0 - case 'd': { - if (! parse_date(optarg, &begin_date)) { - std::cerr << "Error: Bad date string: " << optarg << std::endl; + case 'e': + have_ending = true; + if (! parse_date(optarg, &end_date)) { + std::cerr << "Error: Bad end date: " << optarg << std::endl; return 1; } - have_beginning = true; - - struct std::tm when, then; - std::memset(&then, 0, sizeof(struct std::tm)); - - std::time_t now = std::time(NULL); - struct std::tm * now_tm = std::localtime(&now); - - for (const char ** f = formats; *f; f++) { - memset(&when, INT_MAX, sizeof(struct std::tm)); - if (strptime(optarg, *f, &when)) { - then.tm_hour = 0; - then.tm_min = 0; - then.tm_sec = 0; - - if (when.tm_year != -1) - then.tm_year = when.tm_year + 1; - else - then.tm_year = now_tm->tm_year; - - if (std::strcmp(*f, "%Y") == 0) { - then.tm_mon = 0; - then.tm_mday = 1; - } else { - if (when.tm_mon != -1) - then.tm_mon = when.tm_mon + 1; - else - then.tm_mon = now_tm->tm_mon; - - if (when.tm_mday != -1) - then.tm_mday = when.tm_mday + 1; - else - then.tm_mday = now_tm->tm_mday; - } - - end_date = std::mktime(&then); - have_ending = true; - break; - } - } break; - } -#endif case 'c': end_date = std::time(NULL); have_ending = true; break; + case 'd': + have_date_mask = true; + if (! parse_date_mask(optarg, &date_mask)) { + std::cerr << "Error: Bad date mask: " << optarg << std::endl; + return 1; + } + break; + case 'h': show_help(std::cout); break; case 'f': file = new std::ifstream(optarg); break; @@ -522,26 +503,6 @@ int main(int argc, char * argv[]) index = optind; -#if 0 - if (have_beginning || have_ending) { - std::cout << "Reporting"; - - if (have_beginning) { - char buf[32]; - std::strftime(buf, 31, "%Y.%m.%d", std::localtime(&begin_date)); - std::cout << " from " << buf; - } - - if (have_ending) { - char buf[32]; - std::strftime(buf, 31, "%Y.%m.%d", std::localtime(&end_date)); - std::cout << " until " << buf; - } - - std::cout << std::endl; - } -#endif - // A ledger data file must be specified if (! file) { -- cgit v1.2.3