diff options
Diffstat (limited to 'reports.cc')
-rw-r--r-- | reports.cc | 1225 |
1 files changed, 0 insertions, 1225 deletions
diff --git a/reports.cc b/reports.cc deleted file mode 100644 index 2708c1d0..00000000 --- a/reports.cc +++ /dev/null @@ -1,1225 +0,0 @@ -#include "ledger.h" - -#define LEDGER_VERSION "1.7" - -#include <cstring> -#include <unistd.h> - -namespace ledger { - -static bool cleared_only = false; -static bool uncleared_only = false; -static bool show_virtual = true; -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; -static bool print_monthly = false; -static bool gnuplot_safe = false; - -static bool cost_basis = false; -static bool use_history = false; -static bool net_gain = false; -static bool get_quotes = false; - long pricing_leeway = 24 * 3600; - std::string price_db; - -static amount * lower_limit = NULL; - -static mask * negonly_regexp = NULL; - -static std::time_t begin_date; -static bool have_beginning = false; - -static std::time_t end_date; -static bool have_ending = false; - -static struct std::tm date_mask; -static bool have_date_mask = false; - -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; -} - -static amount * resolve_amount(amount * amt, - std::time_t * when = NULL, - totals * balance = NULL, - bool add_base_value = false, - bool free_memory = false) -{ - amount * value; - bool alloced = true; - - if (! use_history) { - value = amt; - alloced = false; - } - else if (cost_basis) { - value = amt->value(); - } - else if (net_gain) { - value = amt->street(when ? when : (have_ending ? &end_date : NULL), - use_history, get_quotes); - amount * basis = amt->value(); - if (value->commdty() == basis->commdty()) { - basis->negate(); - value->credit(basis); - } else { - // If the commodities do not match, ignore this amount by - // returning a zeroed value. - delete basis; - basis = value->copy(); - basis->negate(); - value->credit(basis); - delete basis; - } - } - else { - value = amt->street(when ? when : (have_ending ? &end_date : NULL), - use_history, get_quotes); - } - - if (balance) { - if (add_base_value) - balance->credit(cost_basis ? value : amt); - else - balance->credit(value); - } - - if (free_memory && alloced) { - delete value; - value = NULL; - } - else if (! free_memory && ! alloced) { - value = value->copy(); - } - - return value; -} - -static inline void print_resolved_balance(std::ostream& out, - std::time_t * when, - totals& balance, - bool added_base_value = false) -{ - if (! added_base_value || ! use_history || cost_basis) { - balance.print(out, 12); - } else { - totals * street = balance.street(when ? when : (have_ending ? - &end_date : NULL), - use_history, get_quotes); - street->print(out, 12); - delete street; - } -} - -////////////////////////////////////////////////////////////////////// -// -// Balance reporting code -// - -static bool satisfies_limit(totals& 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 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 << " "; - for (int i = 0; i < level; 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 - - for (accounts_map_iterator i = acct->children.begin(); - i != acct->children.end(); - i++) - display_total(out, balance, (*i).second, level, headlines); -} - -void report_balances(std::ostream& out, regexps_list& regexps) -{ - // Walk through all of the ledger entries, computing the account - // totals - - 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)) - continue; - - for (std::list<transaction *>::iterator x = (*i)->xacts.begin(); - x != (*i)->xacts.end(); - x++) { - if (! show_virtual && (*x)->is_virtual) - continue; - - 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 { - match = matches(regexps, acct->as_str(), &by_exclusion); - if (! match) { - acct->checked = 2; - } - else if (by_exclusion) { - if (! (show_children || ! acct->parent)) - acct->checked = 2; - else - acct->checked = 1; - } - else { - acct->checked = 1; - } - } - } - - if (acct->checked == 1) { - resolve_amount((*x)->cost, NULL, &acct->balance, false, true); - } - 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; - } - } - } - } - } - - // Walk through all the top-level accounts, giving the balance - // report for each, and then for each of their children. - - totals balance; - int headlines = 0; - - for (accounts_map_iterator i = main_ledger->accounts.begin(); - i != main_ledger->accounts.end(); - 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 && ! balance.is_zero()) { - out << "--------------------" << std::endl; - balance.print(out, 20); - out << std::endl; - } -} - -////////////////////////////////////////////////////////////////////// -// -// Register printing code -// - -static std::string truncated(const std::string& str, int width) -{ - char buf[256]; - memset(buf, '\0', 255); - std::strncpy(buf, str.c_str(), width); - if (buf[width - 1]) - std::strcpy(&buf[width - 3], "..."); - else - buf[width] = '\0'; - return buf; -} - -enum periodicity_t { - PERIOD_NONE, - PERIOD_MONTHLY, - PERIOD_WEEKLY_SUN, - PERIOD_WEEKLY_MON -}; - -static totals * prev_balance = NULL; -static std::time_t prev_date; - -void print_register_transaction(std::ostream& out, entry * ent, - transaction * xact, totals& balance); - -static void report_change_in_asset_value(std::ostream& out, std::time_t date, - account * acct, totals& balance) -{ - totals * prev_street_balance = - prev_balance->street(&prev_date, use_history, get_quotes); - totals * curr_street_balance = - prev_balance->street(&date, use_history, get_quotes); - - delete prev_balance; - prev_balance = NULL; - - prev_street_balance->negate(); - curr_street_balance->credit(*prev_street_balance); - - if (! curr_street_balance->is_zero()) { - for (totals::const_iterator i = curr_street_balance->amounts.begin(); - i != curr_street_balance->amounts.end(); - i++) { - if ((*i).second->is_zero()) - continue; - - entry change(main_ledger); - - change.date = date; - change.cleared = true; - change.desc = "Assets revalued"; - - transaction * trans = new transaction(); - trans->acct = const_cast<account *>(acct); - trans->cost = (*i).second->copy(); - change.xacts.push_back(trans); - - transaction * trans2 = new transaction(); - trans2->acct = main_ledger->find_account("Equity:Asset Gain"); - trans2->cost = (*i).second->copy(); - trans2->cost->negate(); - change.xacts.push_back(trans2); - - balance.credit(trans2->cost); - - print_register_transaction(out, &change, trans, balance); - - delete prev_balance; - prev_balance = NULL; - } - } - - delete prev_street_balance; - delete curr_street_balance; -} - -void print_register_transaction(std::ostream& out, entry * ent, - transaction * xact, totals& balance) -{ - if (prev_balance) - report_change_in_asset_value(out, ent->date, xact->acct, balance); - - char buf[32]; - std::strftime(buf, 31, "%Y/%m/%d ", std::localtime(&ent->date)); - out << buf; - - out.width(20); - if (ent->desc.empty()) - out << " "; - else - out << std::left << truncated(ent->desc, 20); - out << " "; - - // Always display the street value, if prices have been - // specified - - amount * street = resolve_amount(xact->cost, &ent->date, &balance, true); - - // 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(); - - if (xp == xact && ! show_subtotals) - xact_str = "<Splits...>"; - - out.width(22); - out << std::left << truncated(xact_str, 22) << " "; - - out.width(12); - out << std::right << street->as_str(true); - delete street; - - print_resolved_balance(out, &ent->date, balance, true); - - out << std::endl; - - assert(! prev_balance); - prev_balance = new totals; - prev_balance->credit(balance); - prev_date = ent->date; - - if (! show_children || xp != xact) - return; - - for (std::list<transaction *>::iterator y = ent->xacts.begin(); - y != ent->xacts.end(); - y++) { - if (xact == *y) - continue; - - out << " "; - - out.width(22); - out << std::left << truncated((*y)->acct_as_str(), 22) << " "; - out.width(12); - - street = resolve_amount((*y)->cost, &ent->date); - out << std::right << street->as_str(true) << std::endl; - delete street; - } -} - -void print_register_period(std::ostream& out, std::time_t date, - account * acct, amount& sum, totals& balance) -{ - if (! gnuplot_safe && prev_balance) { - sum.negate(); - balance.credit(&sum); - report_change_in_asset_value(out, date, acct, balance); - sum.negate(); - balance.credit(&sum); - } - - char buf[32]; - std::strftime(buf, 31, "%Y/%m/%d ", std::localtime(&date)); - out << buf; - - if (! gnuplot_safe) { - out.width(20); - std::strftime(buf, 31, "%B", std::localtime(&date)); - out << std::left << truncated(buf, 20); - out << " "; - - 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; - } - - out.width(12); - out << std::right << sum.as_str(); - - if (! gnuplot_safe) { - print_resolved_balance(out, &date, balance, true); - - assert(! prev_balance); - prev_balance = new totals; - prev_balance->credit(balance); - prev_date = date; - } - - out << std::endl; -} - -void print_register(std::ostream& out, const std::string& acct_name, - regexps_list& regexps, periodicity_t period = PERIOD_NONE) -{ - mask acct_regex(acct_name); - - // Walk through all of the ledger entries, printing their register - // formatted equivalent - - 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; - - 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; - - int entry_mon = std::localtime(&(*i)->date)->tm_mon; - - 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; - } - - 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 = resolve_amount((*x)->cost, &(*i)->date, &balance, - true); - if (period_sum) { - period_sum->credit(street); - delete street; - } else { - period_sum = street; - } - - last_mon = entry_mon; - } - - last_date = (*i)->date; - last_acct = (*x)->acct; - } - } - - if (period_sum) { - if (last_acct) - print_register_period(out, last_date, last_acct, *period_sum, balance); - delete period_sum; - } - - if (! gnuplot_safe && prev_balance) { - report_change_in_asset_value(out, have_ending ? end_date : std::time(NULL), - last_acct, balance); - } -} - -////////////////////////////////////////////////////////////////////// -// -// Create an Equity file based on a ledger. This is used for -// archiving past years, and starting out a new year with compiled -// balances. -// - -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 = have_ending ? end_date : std::time(NULL); - opening.cleared = true; - opening.desc = "Opening Balance"; - - for (totals::const_iterator i = acct->balance.amounts.begin(); - i != acct->balance.amounts.end(); - i++) { - // Skip it, if there is a zero balance for the commodity - if ((*i).second->is_zero()) - continue; - - transaction * xact = new transaction(); - xact->acct = const_cast<account *>(acct); - xact->cost = (*i).second->copy(); - opening.xacts.push_back(xact); - - xact = new transaction(); - xact->acct = main_ledger->find_account("Equity:Opening Balances"); - xact->cost = (*i).second->copy(); - xact->cost->negate(); - opening.xacts.push_back(xact); - } - - opening.print(out); - } - - // Display balances for all child accounts - - for (accounts_map_iterator i = acct->children.begin(); - i != acct->children.end(); - i++) - equity_entry((*i).second, regexps, out); -} - -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. - // totals - - for (accounts_map_iterator i = main_ledger->accounts.begin(); - i != main_ledger->accounts.end(); - i++) - equity_entry((*i).second, regexps, out); -} - -////////////////////////////////////////////////////////////////////// -// -// Report on the price of any commodities matching REGEXPS. This can -// be used to see what something was worth at a specific time. -// - -void price_report(std::ostream& out, regexps_list& regexps) -{ - if (! have_ending) { - end_date = std::time(NULL); - have_ending = true; - } - - for (commodities_map_iterator i = main_ledger->commodities.begin(); - i != main_ledger->commodities.end(); - i++) - if (regexps.empty() || matches(regexps, (*i).first)) { - amount * price = (*i).second->price(have_ending ? &end_date : NULL, - use_history, get_quotes); - if (price && ! price->is_zero()) { - out.width(20); - out << std::right << price->as_str() << " " << (*i).first - << std::endl; - } - } -} - -////////////////////////////////////////////////////////////////////// -// -// Add a new entry, using hueristic logic to simplify the entry -// requirements -// - -void add_new_entry(int index, int argc, char **argv) -{ - regexps_list regexps; - entry added(main_ledger); - entry * matching = NULL; - - assert(index < argc); - - if (! parse_date(argv[index++], &added.date)) { - std::cerr << "Error: Bad entry date: " << argv[index - 1] - << std::endl; - std::exit(1); - } - - added.cleared = cleared_only; - - if (index == argc) { - std::cerr << "Error: Too few arguments to 'entry'." << std::endl; - std::exit(1); - } - - regexps.clear(); - regexps.push_back(mask(argv[index++])); - - for (entries_list_reverse_iterator i = main_ledger->entries.rbegin(); - i != main_ledger->entries.rend(); - i++) { - if ((*i)->matches(regexps)) { - matching = *i; - break; - } - } - - added.desc = matching ? matching->desc : regexps.front().pattern; - - if (index == argc) { - std::cerr << "Error: Too few arguments to 'entry'." << std::endl; - std::exit(1); - } - - if (argv[index][0] == '-' || std::isdigit(argv[index][0])) { - if (! matching) { - std::cerr << "Error: Missing account name for non-matching entry." - << std::endl; - std::exit(1); - } - - transaction * m_xact, * xact, * first; - - m_xact = matching->xacts.front(); - - first = xact = new transaction(); - xact->acct = m_xact->acct; - xact->cost = create_amount(argv[index++]); - xact->cost->set_commdty(m_xact->cost->commdty()); - - added.xacts.push_back(xact); - - m_xact = matching->xacts.back(); - - xact = new transaction(); - xact->acct = m_xact->acct; - xact->cost = first->cost->copy(); - xact->cost->negate(); - - added.xacts.push_back(xact); - - if ((index + 1) < argc && std::string(argv[index]) == "-from") - if (account * acct = main_ledger->re_find_account(argv[++index])) - added.xacts.back()->acct = acct; - } else { - while (index < argc && std::string(argv[index]) != "-from") { - transaction * xact = new transaction(); - - mask acct_regex(argv[index++]); - - account * acct = NULL; - commodity * cmdty = NULL; - - 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) - xact->acct = acct; - else - xact->acct = main_ledger->re_find_account(acct_regex.pattern); - - if (! xact->acct) { - std::cerr << "Error: Could not find account name '" - << acct_regex.pattern << "'." << std::endl; - std::exit(1); - } - - if (index == argc) { - std::cerr << "Error: Too few arguments to 'entry'." << std::endl; - std::exit(1); - } - - xact->cost = create_amount(argv[index++]); - if (! xact->cost->commdty()) - xact->cost->set_commdty(cmdty); - - added.xacts.push_back(xact); - } - - if ((index + 1) < argc && std::string(argv[index]) == "-from") { - if (account * acct = main_ledger->re_find_account(argv[++index])) { - transaction * xact = new transaction(); - xact->acct = acct; - xact->cost = NULL; - - added.xacts.push_back(xact); - } - } else { - transaction * xact = new transaction(); - 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); - } - } - - if (added.finalize()) - added.print(std::cout); -} - -// Print out the entire ledger that was read in. 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_list& regexps, - bool shortcut) const -{ - for (entries_list_const_iterator i = entries.begin(); - i != entries.end(); - i++) { - if (! matches_date_range(*i) || ! (*i)->matches(regexps)) - continue; - - (*i)->print(out, shortcut); - } -} - -} // namespace ledger - -using namespace ledger; - -static void show_help(std::ostream& out) -{ - std::cerr - << "usage: ledger [options] COMMAND [options] [REGEXPS]" << std::endl - << std::endl - << "Basic options:" << std::endl - << " -h display this help text" << std::endl - << " -v display version information" << std::endl - << " -f FILE specify pathname of ledger data file" << std::endl - << " -i FILE read list of inclusion regexps from FILE" << std::endl - << std::endl - << "Report filtering:" << 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 - << " -d DATE specify a date mask ('-d mon', for all mondays)" << std::endl - << " -C show only cleared transactions and balances" << std::endl - << " -U show only uncleared transactions and balances" << std::endl - << " -l AMT don't print balance totals whose abs value is <AMT" << std::endl - << " -N REGEX accounts matching REGEXP only display if negative" << std::endl - << " -R do not consider virtual transactions: real only" << std::endl - << std::endl - << "Customizing output:" << std::endl - << " -n do not calculate parent account totals" << std::endl - << " -s show sub-accounts in balance totals" << std::endl - << " -S sort the output of \"print\" by date" << std::endl - << " -E show accounts that total to zero" << std::endl - << " -F print each account's full name" << std::endl - << " -M print register using monthly sub-totals" << std::endl - << " -G use with -M to produce gnuplot-friendly output" << std::endl - << std::endl - << "Commodity prices:" << std::endl - << " -P FILE sets the price database, for reading/writing price info" << std::endl - << " -T report commodity totals, not their market value" << std::endl - << " -V report the market value of commodities" << std::endl - << " -B report cost basis of commodities" << std::endl - << " -Q download new price information (when needed) from the Internet" << std::endl - << " (works by running \"getquote SYMBOL\")" << std::endl - << " -L MINS with -Q, fetch quotes only if data is older than MINS" << std::endl - << " -p STR specifies a direct commodity conversion: COMM=AMOUNT" << 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 - << " entry output a newly formed entry, based on arguments" << std::endl - << " price show the last known price for matching commodities" << std::endl; -} - -////////////////////////////////////////////////////////////////////// -// -// Command-line parser and top-level logic. -// - -int main(int argc, char * argv[]) -{ - int index; - std::string prices; - std::string limit; - regexps_list regexps; - - std::vector<std::string> files; - - main_ledger = new book; - - // Initialize some variables based on environment variable settings - - if (char * p = std::getenv("PRICE_HIST")) - price_db = p; - - if (char * p = std::getenv("PRICE_EXP")) - pricing_leeway = std::atol(p) * 60; - - // Parse the command-line options - - int c; - while (-1 != (c = getopt(argc, argv, - "+ABb:Ccd:Ee:Ff:Ghi:L:l:MN:nP:p:QRSsTUVv"))) { - switch (char(c)) { - case 'b': - have_beginning = true; - if (! parse_date(optarg, &begin_date)) { - std::cerr << "Error: Bad begin date: " << optarg << std::endl; - return 1; - } - break; - - case 'e': - have_ending = true; - if (! parse_date(optarg, &end_date)) { - std::cerr << "Error: Bad end date: " << optarg << std::endl; - return 1; - } - break; - - 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': files.push_back(optarg); break; - case 'C': cleared_only = true; break; - case 'U': uncleared_only = 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': - if (access(optarg, R_OK) != -1) - read_regexps(optarg, regexps); - break; - - // -p "COMMODITY=PRICE" - case 'p': - parse_price_setting(optarg); - break; - - case 'P': - price_db = optarg; - break; - - case 'Q': - get_quotes = true; - break; - - case 'V': - use_history = true; - break; - - case 'B': - cost_basis = true; - use_history = true; - break; - - case 'A': - net_gain = true; - use_history = true; - break; - - case 'T': - cost_basis = false; - use_history = false; - break; - - case 'L': - pricing_leeway = std::atol(optarg) * 60; - break; - - case 'l': - lower_limit = create_amount(optarg); - break; - - case 'v': - std::cout - << "Ledger Accouting Tool " LEDGER_VERSION << std::endl - << " Copyright (c) 2003 John Wiegley <johnw@newartisans.com>" - << std::endl << std::endl - << "This program is made available under the terms of the BSD" - << std::endl - << "Public License. See the LICENSE file included with the" - << std::endl - << "distribution for details and disclaimer." << std::endl; - return 0; - } - } - - if (optind == argc) { - show_help(std::cout); - return 1; - } - - index = optind; - - // Read the command word - - const std::string command = argv[index++]; - - int name_index = index; - if (command == "register" || command == "reg") { - if (net_gain) { - std::cerr << ("Reporting the asset gain makes " - "no sense for the register report.") - << std::endl; - return 1; - } - - if (name_index == argc) { - std::cerr << ("Error: Must specify an account name " - "after the 'register' command.") << std::endl; - return 1; - } - index++; - } - - // Compile the list of specified regular expressions, which can be - // specified after the command, or using the '-i FILE' option - - if (command != "entry") - for (; index < argc; index++) - regexps.push_back(mask(argv[index])); - - // A ledger data file must be specified - - int entry_count = 0; - - 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 (use_history && ! cost_basis && ! price_db.empty()) - entry_count += parse_ledger_file(main_ledger, price_db, regexps, - command == "equity"); - - if (entry_count == 0) { - std::cerr << ("Please specify ledger file(s) using -f option " - "or LEDGER environment variable.") << std::endl; - return 1; - } - - // Process the command - - if (command == "balance" || command == "bal") { - report_balances(std::cout, regexps); - } - else if (command == "register" || command == "reg") { - if (show_sorted || print_monthly) - main_ledger->sort(cmp_entry_date()); - print_register(std::cout, argv[name_index], regexps, - print_monthly ? PERIOD_MONTHLY : PERIOD_NONE); - } - else if (command == "print") { - if (show_sorted) - main_ledger->sort(cmp_entry_date()); - main_ledger->print(std::cout, regexps, ! full_names); - } - else if (command == "equity") { - equity_ledger(std::cout, regexps); - } - else if (command == "price" || command == "prices") { - price_report(std::cout, regexps); - } - else if (command == "entry") { - add_new_entry(index, argc, argv); - } - else { - std::cerr << "Error: Unrecognized command '" << command << "'." - << std::endl; - return 1; - } - -#ifdef DEBUG - // Ordinarily, deleting the main ledger isn't necessary, since the - // 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; -} - -// reports.cc ends here. |