Inspired by Omari Norman, I've rewritten main.cc so it's easy to approach.
This commit is contained in:
parent
75daee6a5d
commit
9d267fa133
12 changed files with 555 additions and 375 deletions
|
|
@ -151,6 +151,7 @@ pkginclude_HEADERS = \
|
|||
src/reconcile.h \
|
||||
src/quotes.h \
|
||||
\
|
||||
src/work.h \
|
||||
src/ledger.h \
|
||||
\
|
||||
python/pyledger.h \
|
||||
|
|
@ -194,7 +195,7 @@ ledger_CPPFLAGS = $(lib_cppflags)
|
|||
if HAVE_BOOST_PYTHON
|
||||
ledger_CPPFLAGS += -I$(srcdir)/python
|
||||
endif
|
||||
ledger_SOURCES = src/main.cc
|
||||
ledger_SOURCES = src/main.cc src/work.cc
|
||||
ledger_LDADD = $(LIBOBJS) $(lib_LTLIBRARIES)
|
||||
if HAVE_BOOST_PYTHON
|
||||
ledger_LDADD += libledger_python.la
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ expr_t::ptr_op_t account_t::lookup(const string& name)
|
|||
break;
|
||||
}
|
||||
|
||||
return session_t::current->current_report->lookup(name);
|
||||
return session_t::current->report->lookup(name);
|
||||
}
|
||||
|
||||
bool account_t::valid() const
|
||||
|
|
@ -240,7 +240,7 @@ void account_t::calculate_sums()
|
|||
}
|
||||
|
||||
call_scope_t args(*this);
|
||||
value_t amount(session_t::current->current_report->get_amount_expr(args));
|
||||
value_t amount(session_t::current->report->get_amount_expr(args));
|
||||
if (! amount.is_null()) {
|
||||
add_or_set_value(xd.total, amount);
|
||||
xd.total_count += xd.count;
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ expr_t::ptr_op_t item_t::lookup(const string& name)
|
|||
break;
|
||||
}
|
||||
|
||||
return session_t::current->current_report->lookup(name);
|
||||
return session_t::current->report->lookup(name);
|
||||
}
|
||||
|
||||
bool item_t::valid() const
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@
|
|||
#define _LEDGER_H
|
||||
|
||||
#include <utils.h>
|
||||
#include <option.h>
|
||||
|
||||
#include <value.h>
|
||||
|
||||
|
|
@ -67,4 +68,11 @@
|
|||
#include <reconcile.h>
|
||||
#include <quotes.h>
|
||||
|
||||
#if defined(HAVE_BOOST_PYTHON)
|
||||
#include <pyinterp.h>
|
||||
#define LEDGER_SESSION_T python_interpreter_t
|
||||
#else
|
||||
#define LEDGER_SESSION_T session_t
|
||||
#endif
|
||||
|
||||
#endif // _LEDGER_H
|
||||
|
|
|
|||
396
src/main.cc
396
src/main.cc
|
|
@ -29,335 +29,129 @@
|
|||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "session.h"
|
||||
#include "report.h"
|
||||
#include "option.h"
|
||||
#include "help.h"
|
||||
#include "pyinterp.h"
|
||||
#include <ledger.h>
|
||||
|
||||
#include "textual.h"
|
||||
#include "qif.h"
|
||||
#include "xml.h"
|
||||
#include "gnucash.h"
|
||||
#ifdef HAVE_LIBOFX
|
||||
#include "ofx.h"
|
||||
#endif
|
||||
|
||||
namespace ledger {
|
||||
int read_and_report(ledger::report_t& report,
|
||||
int argc, char * argv[], char * envp[])
|
||||
{
|
||||
using namespace ledger;
|
||||
|
||||
session_t& session(report.session);
|
||||
|
||||
// Setup global defaults
|
||||
|
||||
optional<path> home;
|
||||
if (const char * home_var = std::getenv("HOME"))
|
||||
home = home_var;
|
||||
|
||||
session.init_file = home ? *home / ".ledgerrc" : "./.ledgerrc";
|
||||
session.price_db = home ? *home / ".pricedb" : "./.pricedb";
|
||||
session.cache_file = home ? *home / ".ledger-cache" : "./.ledger-cache";
|
||||
|
||||
// Process the environment settings
|
||||
|
||||
TRACE_START(environment, 1, "Processed environment variables");
|
||||
|
||||
process_environment(const_cast<const char **>(envp), "LEDGER_", report);
|
||||
|
||||
#if 1
|
||||
// These are here for backwards compatability, but are deprecated.
|
||||
|
||||
if (const char * p = std::getenv("LEDGER"))
|
||||
process_option("file", report, p);
|
||||
if (const char * p = std::getenv("LEDGER_INIT"))
|
||||
process_option("init-file", report, p);
|
||||
if (const char * p = std::getenv("PRICE_HIST"))
|
||||
process_option("price-db", report, p);
|
||||
if (const char * p = std::getenv("PRICE_EXP"))
|
||||
process_option("price-exp", report, p);
|
||||
#endif
|
||||
|
||||
TRACE_FINISH(environment, 1);
|
||||
|
||||
// Read the initialization file
|
||||
|
||||
TRACE_START(init, 1, "Read initialization file");
|
||||
|
||||
session.read_init();
|
||||
|
||||
TRACE_FINISH(init, 1);
|
||||
|
||||
// Handle the command-line arguments
|
||||
|
||||
TRACE_START(arguments, 1, "Processed command-line arguments");
|
||||
|
||||
strings_list args;
|
||||
process_arguments(argc - 1, argv + 1, report, args);
|
||||
|
||||
if (args.empty()) {
|
||||
ledger::help(std::cout);
|
||||
return 1;
|
||||
}
|
||||
strings_list::iterator arg = args.begin();
|
||||
|
||||
if (! session.cache_file)
|
||||
session.use_cache = false;
|
||||
else if (session.use_cache)
|
||||
session.use_cache = ! session.data_file.empty() && session.price_db;
|
||||
|
||||
DEBUG("ledger.session.cache", "1. use_cache = " << session.use_cache);
|
||||
|
||||
if (session.data_file == *session.cache_file)
|
||||
session.use_cache = false;
|
||||
|
||||
DEBUG("ledger.session.cache", "2. use_cache = " << session.use_cache);
|
||||
|
||||
INFO("Initialization file is " << session.init_file->string());
|
||||
INFO("Price database is " << session.price_db->string());
|
||||
INFO("Binary cache is " << session.cache_file->string());
|
||||
INFO("Journal file is " << session.data_file.string());
|
||||
|
||||
if (! session.use_cache)
|
||||
INFO("Binary cache mechanism will not be used");
|
||||
|
||||
TRACE_FINISH(arguments, 1);
|
||||
|
||||
// Read the command word and see if it's any of the debugging commands
|
||||
// that Ledger supports.
|
||||
|
||||
string verb = *arg++;
|
||||
|
||||
function_t command;
|
||||
if (expr_t::ptr_op_t def = report.lookup(string("ledger_precmd_") + verb))
|
||||
command = def->as_function();
|
||||
|
||||
// Parse the initialization file, which can only be textual; then
|
||||
// parse the journal data. But only do this if there was no
|
||||
// "pre-command", which are always executed without doing any
|
||||
// parsing.
|
||||
|
||||
if (! command) {
|
||||
INFO_START(journal, "Read journal file");
|
||||
|
||||
journal_t& journal(*session.create_journal());
|
||||
|
||||
std::size_t count = session.read_data(journal, report.account);
|
||||
if (count == 0)
|
||||
throw_(parse_error, "Failed to locate any journal entries; "
|
||||
"did you specify a valid file with -f?");
|
||||
|
||||
INFO_FINISH(journal);
|
||||
|
||||
INFO("Found " << count << " entries");
|
||||
|
||||
TRACE_FINISH(entry_text, 1);
|
||||
TRACE_FINISH(entry_details, 1);
|
||||
TRACE_FINISH(entry_xacts, 1);
|
||||
TRACE_FINISH(entries, 1);
|
||||
TRACE_FINISH(session_parser, 1);
|
||||
TRACE_FINISH(parsing_total, 1);
|
||||
|
||||
// Lookup the command object corresponding to the command verb.
|
||||
|
||||
if (expr_t::ptr_op_t def = report.lookup(string("ledger_cmd_") + verb))
|
||||
command = def->as_function();
|
||||
}
|
||||
|
||||
if (! command)
|
||||
throw_(std::logic_error, string("Unrecognized command '") + verb + "'");
|
||||
|
||||
#if 1
|
||||
// Patch up some of the reporting options based on what kind of
|
||||
// command it was.
|
||||
|
||||
// jww (2008-08-14): This code really needs to be rationalized away
|
||||
// for 3.0.
|
||||
|
||||
if (verb == "print" || verb == "entry" || verb == "dump") {
|
||||
report.show_related = true;
|
||||
report.show_all_related = true;
|
||||
}
|
||||
else if (verb == "equity") {
|
||||
report.show_subtotal = true;
|
||||
}
|
||||
else if (report.show_related) {
|
||||
if (verb[0] == 'r') {
|
||||
report.show_inverted = true;
|
||||
} else {
|
||||
report.show_subtotal = true;
|
||||
report.show_all_related = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (verb[0] != 'b' && verb[0] != 'r')
|
||||
amount_t::keep_base = true;
|
||||
|
||||
// Setup the default value for the display predicate
|
||||
|
||||
if (report.display_predicate.empty()) {
|
||||
if (verb[0] == 'b') {
|
||||
if (! report.show_empty)
|
||||
report.display_predicate = "total";
|
||||
if (! report.show_subtotal) {
|
||||
if (! report.display_predicate.empty())
|
||||
report.display_predicate += "&";
|
||||
report.display_predicate += "depth<=1";
|
||||
}
|
||||
}
|
||||
else if (verb == "equity") {
|
||||
report.display_predicate = "amount_expr"; // jww (2008-08-14): ???
|
||||
}
|
||||
else if (verb[0] == 'r' && ! report.show_empty) {
|
||||
report.display_predicate = "amount";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Now setup the various formatting strings
|
||||
|
||||
// jww (2008-08-14): I hear a song, and it's sound is "HaAaaCcK"
|
||||
|
||||
#if 0
|
||||
if (! date_output_format.empty())
|
||||
date_t::output_format = date_output_format;
|
||||
#endif
|
||||
|
||||
amount_t::keep_price = report.keep_price;
|
||||
amount_t::keep_date = report.keep_date;
|
||||
amount_t::keep_tag = report.keep_tag;
|
||||
|
||||
if (! report.report_period.empty() && ! report.sort_all)
|
||||
report.entry_sort = true;
|
||||
|
||||
// Setup the output stream, which might involve invoking the pager
|
||||
|
||||
report.output_stream.initialize(report.output_file, report.pager_path);
|
||||
|
||||
// Create an argument scope containing the report command's
|
||||
// arguments, and then invoke the command.
|
||||
|
||||
call_scope_t command_args(report);
|
||||
|
||||
for (strings_list::iterator i = arg; i != args.end(); i++)
|
||||
command_args.push_back(string_value(*i));
|
||||
|
||||
INFO_START(command, "Did user command '" << verb << "'");
|
||||
|
||||
command(command_args);
|
||||
|
||||
INFO_FINISH(command);
|
||||
|
||||
#if 0
|
||||
// Write out the binary cache, if need be
|
||||
|
||||
if (session.use_cache && session.cache_dirty && session.cache_file) {
|
||||
TRACE_START(binary_cache, 1, "Wrote binary journal file");
|
||||
|
||||
ofstream stream(*session.cache_file);
|
||||
journal.write(stream);
|
||||
|
||||
TRACE_FINISH(binary_cache, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#include "work.h" // this is where the top-level code is
|
||||
|
||||
int main(int argc, char * argv[], char * envp[])
|
||||
{
|
||||
int status = 1;
|
||||
using namespace ledger;
|
||||
|
||||
for (int i = 1; i < argc; i++)
|
||||
if (argv[i][0] == '-') {
|
||||
if (std::strcmp(argv[i], "--verify") == 0) {
|
||||
#if defined(VERIFY_ON)
|
||||
ledger::verify_enabled = true;
|
||||
#endif
|
||||
}
|
||||
else if (std::strcmp(argv[i], "--verbose") == 0 ||
|
||||
std::strcmp(argv[i], "-v") == 0) {
|
||||
#if defined(LOGGING_ON)
|
||||
ledger::_log_level = ledger::LOG_INFO;
|
||||
#endif
|
||||
}
|
||||
else if (i + 1 < argc && std::strcmp(argv[i], "--debug") == 0) {
|
||||
#if defined(DEBUG_ON)
|
||||
ledger::_log_level = ledger::LOG_DEBUG;
|
||||
ledger::_log_category = argv[i + 1];
|
||||
i++;
|
||||
#endif
|
||||
}
|
||||
else if (i + 1 < argc && std::strcmp(argv[i], "--trace") == 0) {
|
||||
#if defined(TRACING_ON)
|
||||
ledger::_log_level = ledger::LOG_TRACE;
|
||||
try {
|
||||
ledger::_trace_level = boost::lexical_cast<int>(argv[i + 1]);
|
||||
}
|
||||
catch (const boost::bad_lexical_cast& e) {
|
||||
std::cerr << "Argument to --trace must be an integer."
|
||||
<< std::endl;
|
||||
return 1;
|
||||
}
|
||||
i++;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
// The very first thing we do is handle some very special command-line
|
||||
// options, since they affect how the whole environment is setup:
|
||||
//
|
||||
// --verify ; turns on memory tracing
|
||||
// --verbose ; turns on logging
|
||||
// --debug CATEGORY ; turns on debug logging
|
||||
// --trace LEVEL ; turns on trace logging
|
||||
handle_debug_options(argc, argv);
|
||||
|
||||
IF_VERIFY()
|
||||
ledger::initialize_memory_tracing();
|
||||
initialize_memory_tracing();
|
||||
|
||||
// Initialize the global C++ environment
|
||||
std::ios::sync_with_stdio(false);
|
||||
filesystem::path::default_name_check(filesystem::portable_posix_name);
|
||||
|
||||
// Initialization of Ledger can now begin. The whole series of operations
|
||||
// is contained in a try block, so we can report errors in a nicer fashion.
|
||||
INFO("Ledger starting");
|
||||
|
||||
session_t * session = NULL;
|
||||
int status = 1;
|
||||
try {
|
||||
std::ios::sync_with_stdio(false);
|
||||
// Create the session object, which maintains nearly all state relating to
|
||||
// this invocation of Ledger.
|
||||
session = new LEDGER_SESSION_T;
|
||||
set_session_context(session);
|
||||
|
||||
boost::filesystem::path::default_name_check
|
||||
(boost::filesystem::portable_posix_name);
|
||||
// Register all known journal parsers. The order of these is important.
|
||||
register_journal_parsers(*session);
|
||||
|
||||
INFO("Ledger starting");
|
||||
// Create the report object, which maintains state relating to each
|
||||
// command invocation. Because we're running this from main() the
|
||||
// distinction between session and report doesn't matter, but if a GUI
|
||||
// were calling into Ledger, it would have one session object, with a
|
||||
// separate report object for each report it generated.
|
||||
session->report.reset(new report_t(*session));
|
||||
report_t& report(*session->report.get());
|
||||
|
||||
#if defined(HAVE_BOOST_PYTHON)
|
||||
std::auto_ptr<ledger::session_t> session(new ledger::python_interpreter_t);
|
||||
#else
|
||||
std::auto_ptr<ledger::session_t> session(new ledger::session_t);
|
||||
#endif
|
||||
// Read user option settings, first in the environment, then from the
|
||||
// user's initialization file and then from the command-line. The first
|
||||
// non-option argument thereafter is the "command verb".
|
||||
read_environment_settings(report, envp);
|
||||
session->read_init();
|
||||
|
||||
ledger::set_session_context(session.get());
|
||||
// Notify the session object that all option handlers invoked beyond this
|
||||
// point came from the command-line
|
||||
session->now_at_command_line(true);
|
||||
|
||||
#if 0
|
||||
session->register_parser(new ledger::journal_t::binary_parser_t);
|
||||
#endif
|
||||
session->register_parser(new ledger::xml_parser_t);
|
||||
session->register_parser(new ledger::gnucash_parser_t);
|
||||
#ifdef HAVE_LIBOFX
|
||||
session->register_parser(new ledger::ofx_parser_t);
|
||||
#endif
|
||||
session->register_parser(new ledger::qif_parser_t);
|
||||
session->register_parser(new ledger::textual_parser_t);
|
||||
strings_list args = read_command_line_arguments(report, argc, argv);
|
||||
string_iterator arg = args.begin();
|
||||
string verb = *arg++;
|
||||
|
||||
session->current_report.reset(new ledger::report_t(*session.get()));
|
||||
// Look for a precommand first, which is defined as any defined function
|
||||
// whose name starts with "ledger_precmd_". The difference between a
|
||||
// precommand and a regular command is that precommands ignore the journal
|
||||
// data file completely, nor is the user's init file read.
|
||||
//
|
||||
// Here are some examples:
|
||||
//
|
||||
// parse STRING ; show how a value expression is parsed
|
||||
// eval STRING ; simply evaluate a value expression
|
||||
// format STRING ; show how a format string is parsed
|
||||
//
|
||||
// If such a command is found, create the output stream for the result and
|
||||
// then invoke the command.
|
||||
if (function_t command = look_for_precommand(report, verb)) {
|
||||
create_output_stream(report);
|
||||
invoke_command_verb(report, command, arg, args.end());
|
||||
}
|
||||
else if (function_t command = look_for_command(report, verb)) {
|
||||
// Parse the user's journal files.
|
||||
if (journal_t * journal = read_journal_files(*session)) {
|
||||
normalize_report_options(report, verb); // this is a total hack
|
||||
|
||||
status = read_and_report(*session->current_report.get(), argc, argv, envp);
|
||||
// Create the output stream (it might be a file, the console or a
|
||||
// PAGER subprocess) and invoke the report command.
|
||||
create_output_stream(report);
|
||||
invoke_command_verb(report, command, arg, args.end());
|
||||
|
||||
if (! DO_VERIFY())
|
||||
session.release(); // don't free anything! just let it leak
|
||||
// Write out a binary cache of the journal data, if needed and
|
||||
// appropriate to do so
|
||||
write_binary_cache(*session, journal);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw_(std::logic_error, "Unrecognized command '" << verb << "'");
|
||||
}
|
||||
|
||||
// If we got here, everything succeeded just fine. Ledger uses exceptions
|
||||
// to notify of any error conditions, so if you're using gdb, type "catch
|
||||
// throw" to find the source of any errors.
|
||||
status = 0;
|
||||
}
|
||||
catch (const std::exception& err) {
|
||||
std::cout.flush();
|
||||
std::cerr << ledger::error_context()
|
||||
<< "Error: " << err.what() << std::endl;
|
||||
std::cerr << error_context() << "Error: " << err.what() << std::endl;
|
||||
}
|
||||
catch (int _status) {
|
||||
status = _status;
|
||||
}
|
||||
|
||||
// If memory verification is being performed (which can be very slow), clean
|
||||
// up everything by closing the session and deleting the session object, and
|
||||
// then shutting down the memory tracing subsystem. Otherwise, let it all
|
||||
// leak because we're about to exit anyway.
|
||||
IF_VERIFY() {
|
||||
set_session_context(NULL);
|
||||
if (session != NULL)
|
||||
checked_delete(session);
|
||||
|
||||
INFO("Ledger ended (Boost/libstdc++ may still hold memory)");
|
||||
ledger::set_session_context();
|
||||
ledger::shutdown_memory_tracing();
|
||||
shutdown_memory_tracing();
|
||||
} else {
|
||||
// Don't free anything, just let it all leak.
|
||||
INFO("Ledger ended");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@
|
|||
#include "account.h"
|
||||
#include "session.h"
|
||||
|
||||
#if defined(HAVE_LIBOFX)
|
||||
|
||||
#include <libofx.h>
|
||||
|
||||
namespace ledger {
|
||||
|
|
@ -261,3 +263,5 @@ std::size_t ofx_parser_t::parse(std::istream& in,
|
|||
}
|
||||
|
||||
} // namespace ledger
|
||||
|
||||
#endif HAVE_LIBOFX
|
||||
|
|
|
|||
|
|
@ -488,38 +488,7 @@ namespace {
|
|||
out << std::endl << "--- Expression tree ---" << std::endl;
|
||||
expr.dump(out);
|
||||
|
||||
out << std::endl << "--- Calculated value ---" << std::endl;
|
||||
expr.calc(args).print(out);
|
||||
out << std::endl;
|
||||
|
||||
return 0L;
|
||||
}
|
||||
|
||||
value_t compile_command(call_scope_t& args)
|
||||
{
|
||||
var_t<string> arg(args, 0);
|
||||
|
||||
if (! arg) {
|
||||
throw std::logic_error("Usage: compile TEXT");
|
||||
return 1L;
|
||||
}
|
||||
|
||||
report_t& report(find_scope<report_t>(args));
|
||||
std::ostream& out(report.output_stream);
|
||||
|
||||
out << "--- Input text ---" << std::endl;
|
||||
out << *arg << std::endl;
|
||||
|
||||
out << std::endl << "--- Text as parsed ---" << std::endl;
|
||||
expr_t expr(*arg);
|
||||
expr.print(out);
|
||||
out << std::endl;
|
||||
|
||||
out << std::endl << "--- Expression tree ---" << std::endl;
|
||||
expr.dump(out);
|
||||
|
||||
expr.compile(args);
|
||||
|
||||
out << std::endl << "--- Compiled tree ---" << std::endl;
|
||||
expr.dump(out);
|
||||
|
||||
|
|
@ -680,11 +649,6 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
|
|||
return WRAP_FUNCTOR(period_command);
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
if (std::strcmp(p, "compile") == 0)
|
||||
return WRAP_FUNCTOR(compile_command);
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
if (std::strcmp(p, "eval") == 0)
|
||||
return WRAP_FUNCTOR(eval_command);
|
||||
|
|
|
|||
|
|
@ -735,6 +735,8 @@ public:
|
|||
}
|
||||
|
||||
value_t option_price_db_(call_scope_t& args) { // :
|
||||
// jww (2009-01-31): This, and several of the other option handlers,
|
||||
// should be in the session object.
|
||||
session.price_db = args[0].as_string();
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,7 +70,12 @@ void release_session_context()
|
|||
}
|
||||
|
||||
session_t::session_t()
|
||||
: register_format
|
||||
: next_data_file_from_command_line(false),
|
||||
saw_data_file_from_command_line(false),
|
||||
next_price_db_from_command_line(false),
|
||||
saw_price_db_from_command_line(false),
|
||||
|
||||
register_format
|
||||
("%-.9(date) %-.20(payee) %-.23(account(23)) %!12(print_balance(amount_expr, 12, 67)) "
|
||||
"%!12(print_balance(display_total, 12, 80, true))\n%/"
|
||||
"%31|%-.23(account(23)) %!12(print_balance(amount_expr, 12, 67)) "
|
||||
|
|
@ -116,6 +121,14 @@ session_t::session_t()
|
|||
master(new account_t(NULL, ""))
|
||||
{
|
||||
TRACE_CTOR(session_t, "");
|
||||
|
||||
optional<path> home;
|
||||
if (const char * home_var = std::getenv("HOME"))
|
||||
home = home_var;
|
||||
|
||||
init_file = home ? *home / ".ledgerrc" : "./.ledgerrc";
|
||||
price_db = home ? *home / ".pricedb" : "./.pricedb";
|
||||
cache_file = home ? *home / ".ledger-cache" : "./.ledger-cache";
|
||||
}
|
||||
|
||||
session_t::~session_t()
|
||||
|
|
@ -159,6 +172,8 @@ void session_t::read_init()
|
|||
if (! exists(*init_file))
|
||||
throw_(std::logic_error, "Cannot read init file" << *init_file);
|
||||
|
||||
TRACE_START(init, 1, "Read initialization file");
|
||||
|
||||
ifstream init(*init_file);
|
||||
|
||||
journal_t temp;
|
||||
|
|
@ -167,12 +182,14 @@ void session_t::read_init()
|
|||
temp.period_entries.size() > 0)
|
||||
throw_(parse_error, "Entries found in initialization file '" <<
|
||||
init_file << "'");
|
||||
|
||||
TRACE_FINISH(init, 1);
|
||||
}
|
||||
|
||||
std::size_t session_t::read_data(journal_t& journal,
|
||||
const string& master_account)
|
||||
{
|
||||
if (data_file.empty())
|
||||
if (data_files.empty())
|
||||
throw_(parse_error, "No journal file was specified (please use -f)");
|
||||
|
||||
TRACE_START(session_parser, 1, "Parsed journal file");
|
||||
|
|
@ -210,33 +227,39 @@ std::size_t session_t::read_data(journal_t& journal,
|
|||
}
|
||||
}
|
||||
|
||||
DEBUG("ledger.cache", "rejected cache, parsing " << data_file.string());
|
||||
if (data_file == "-") {
|
||||
use_cache = false;
|
||||
journal.sources.push_back("/dev/stdin");
|
||||
|
||||
// To avoid problems with stdin and pipes, etc., we read the entire
|
||||
// file in beforehand into a memory buffer, and then parcel it out
|
||||
// from there.
|
||||
std::ostringstream buffer;
|
||||
foreach (const path& pathname, data_files) {
|
||||
DEBUG("ledger.cache", "rejected cache, parsing " << pathname.string());
|
||||
if (pathname == "-") {
|
||||
use_cache = false;
|
||||
journal.sources.push_back("/dev/stdin");
|
||||
|
||||
while (std::cin.good() && ! std::cin.eof()) {
|
||||
static char line[8192];
|
||||
std::cin.read(line, 8192);
|
||||
std::streamsize count = std::cin.gcount();
|
||||
buffer.write(line, count);
|
||||
// To avoid problems with stdin and pipes, etc., we read the entire
|
||||
// file in beforehand into a memory buffer, and then parcel it out
|
||||
// from there.
|
||||
std::ostringstream buffer;
|
||||
|
||||
while (std::cin.good() && ! std::cin.eof()) {
|
||||
static char line[8192];
|
||||
std::cin.read(line, 8192);
|
||||
std::streamsize count = std::cin.gcount();
|
||||
buffer.write(line, count);
|
||||
}
|
||||
buffer.flush();
|
||||
|
||||
std::istringstream buf_in(buffer.str());
|
||||
|
||||
entry_count += read_journal(journal, buf_in, "/dev/stdin", acct);
|
||||
}
|
||||
else if (exists(pathname)) {
|
||||
entry_count += read_journal(journal, pathname, acct);
|
||||
if (journal.price_db)
|
||||
journal.sources.push_back(*journal.price_db);
|
||||
clean_accounts();
|
||||
}
|
||||
else {
|
||||
throw_(parse_error, "Could not open journal file '" << pathname << "'");
|
||||
}
|
||||
buffer.flush();
|
||||
|
||||
std::istringstream buf_in(buffer.str());
|
||||
|
||||
entry_count += read_journal(journal, buf_in, "/dev/stdin", acct);
|
||||
}
|
||||
else if (exists(data_file)) {
|
||||
entry_count += read_journal(journal, data_file, acct);
|
||||
if (journal.price_db)
|
||||
journal.sources.push_back(*journal.price_db);
|
||||
clean_accounts();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -71,12 +71,16 @@ class session_t : public noncopyable, public scope_t
|
|||
public:
|
||||
static session_t * current;
|
||||
|
||||
scoped_ptr<report_t> current_report;
|
||||
scoped_ptr<report_t> report;
|
||||
|
||||
path data_file;
|
||||
std::list<path> data_files;
|
||||
bool next_data_file_from_command_line;
|
||||
bool saw_data_file_from_command_line;
|
||||
optional<path> init_file;
|
||||
optional<path> cache_file;
|
||||
optional<path> price_db;
|
||||
optional<path> price_db;
|
||||
bool next_price_db_from_command_line;
|
||||
bool saw_price_db_from_command_line;
|
||||
|
||||
string register_format;
|
||||
string wide_register_format;
|
||||
|
|
@ -114,6 +118,11 @@ public:
|
|||
session_t();
|
||||
virtual ~session_t();
|
||||
|
||||
void now_at_command_line(const bool truth) {
|
||||
next_data_file_from_command_line = truth;
|
||||
next_price_db_from_command_line = truth;
|
||||
}
|
||||
|
||||
journal_t * create_journal() {
|
||||
journal_t * journal = new journal_t;
|
||||
journals.push_back(journal);
|
||||
|
|
@ -243,11 +252,13 @@ See LICENSE file included with the distribution for details and disclaimer.\n";
|
|||
|
||||
value_t option_file_(call_scope_t& args) { // f
|
||||
assert(args.size() == 1);
|
||||
|
||||
// jww (2008-08-13): Add support for multiple files
|
||||
data_file = args[0].as_string();
|
||||
use_cache = false;
|
||||
|
||||
if (next_data_file_from_command_line &&
|
||||
! saw_data_file_from_command_line) {
|
||||
data_files.clear();
|
||||
use_cache = false;
|
||||
saw_data_file_from_command_line = true;
|
||||
}
|
||||
data_files.push_back(args[0].as_string());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
308
src/work.cc
Normal file
308
src/work.cc
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
/*
|
||||
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of New Artisans LLC nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <ledger.h>
|
||||
|
||||
#include "work.h"
|
||||
|
||||
namespace ledger {
|
||||
|
||||
void handle_debug_options(int argc, char * argv[])
|
||||
{
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (argv[i][0] == '-') {
|
||||
if (std::strcmp(argv[i], "--verify") == 0) {
|
||||
#if defined(VERIFY_ON)
|
||||
verify_enabled = true; // global in utils.h
|
||||
#endif
|
||||
}
|
||||
else if (std::strcmp(argv[i], "--verbose") == 0 ||
|
||||
std::strcmp(argv[i], "-v") == 0) {
|
||||
#if defined(LOGGING_ON)
|
||||
_log_level = LOG_INFO; // global in utils.h
|
||||
#endif
|
||||
}
|
||||
else if (i + 1 < argc && std::strcmp(argv[i], "--debug") == 0) {
|
||||
#if defined(DEBUG_ON)
|
||||
_log_level = LOG_DEBUG; // global in utils.h
|
||||
_log_category = argv[i + 1]; // global in utils.h
|
||||
i++;
|
||||
#endif
|
||||
}
|
||||
else if (i + 1 < argc && std::strcmp(argv[i], "--trace") == 0) {
|
||||
#if defined(TRACING_ON)
|
||||
_log_level = LOG_TRACE; // global in utils.h
|
||||
try {
|
||||
// global in utils.h
|
||||
_trace_level = boost::lexical_cast<int>(argv[i + 1]);
|
||||
}
|
||||
catch (const boost::bad_lexical_cast& e) {
|
||||
std::cerr << "Argument to --trace must be an integer."
|
||||
<< std::endl;
|
||||
throw int(1);
|
||||
}
|
||||
i++;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void register_journal_parsers(session_t& session)
|
||||
{
|
||||
#if 0
|
||||
session.register_parser(new journal_t::binary_parser_t);
|
||||
#endif
|
||||
session.register_parser(new xml_parser_t);
|
||||
session.register_parser(new gnucash_parser_t);
|
||||
#ifdef HAVE_LIBOFX
|
||||
session.register_parser(new ofx_parser_t);
|
||||
#endif
|
||||
session.register_parser(new qif_parser_t);
|
||||
session.register_parser(new textual_parser_t);
|
||||
}
|
||||
|
||||
void read_environment_settings(report_t& report, char * envp[])
|
||||
{
|
||||
TRACE_START(environment, 1, "Processed environment variables");
|
||||
|
||||
process_environment(const_cast<const char **>(envp), "LEDGER_",
|
||||
report);
|
||||
|
||||
#if 1
|
||||
// These are here for backwards compatability, but are deprecated.
|
||||
|
||||
if (const char * p = std::getenv("LEDGER"))
|
||||
process_option("file", report, p);
|
||||
if (const char * p = std::getenv("LEDGER_INIT"))
|
||||
process_option("init-file", report, p);
|
||||
if (const char * p = std::getenv("PRICE_HIST"))
|
||||
process_option("price-db", report, p);
|
||||
if (const char * p = std::getenv("PRICE_EXP"))
|
||||
process_option("price-exp", report, p);
|
||||
#endif
|
||||
|
||||
TRACE_FINISH(environment, 1);
|
||||
}
|
||||
|
||||
strings_list
|
||||
read_command_line_arguments(report_t& report, int argc, char * argv[])
|
||||
{
|
||||
TRACE_START(arguments, 1, "Processed command-line arguments");
|
||||
|
||||
strings_list args;
|
||||
process_arguments(argc - 1, argv + 1, report, args);
|
||||
|
||||
if (args.empty()) {
|
||||
help(std::cout);
|
||||
throw int(1);
|
||||
}
|
||||
|
||||
TRACE_FINISH(arguments, 1);
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
void normalize_session_options(session_t& session)
|
||||
{
|
||||
if (! session.cache_file)
|
||||
session.use_cache = false;
|
||||
|
||||
DEBUG("ledger.session.cache", "1. use_cache = " << session.use_cache);
|
||||
|
||||
if (std::find(session.data_files.begin(),
|
||||
session.data_files.end(), *session.cache_file) !=
|
||||
session.data_files.end())
|
||||
session.use_cache = false;
|
||||
|
||||
DEBUG("ledger.session.cache", "2. use_cache = " << session.use_cache);
|
||||
|
||||
INFO("Initialization file is " << session.init_file->string());
|
||||
INFO("Price database is " << session.price_db->string());
|
||||
INFO("Binary cache is " << session.cache_file->string());
|
||||
|
||||
foreach (const path& pathname, session.data_files)
|
||||
INFO("Journal file is " << pathname.string());
|
||||
|
||||
if (! session.use_cache)
|
||||
INFO("Binary cache mechanism will not be used");
|
||||
}
|
||||
|
||||
function_t look_for_precommand(report_t& report, const string& verb)
|
||||
{
|
||||
if (expr_t::ptr_op_t def = report.lookup(string("ledger_precmd_") + verb))
|
||||
return def->as_function();
|
||||
else
|
||||
return function_t();
|
||||
}
|
||||
|
||||
journal_t * read_journal_files(session_t& session)
|
||||
{
|
||||
INFO_START(journal, "Read journal file");
|
||||
|
||||
journal_t * journal(session.create_journal());
|
||||
|
||||
std::size_t count = session.read_data(*journal, session.report->account);
|
||||
if (count == 0)
|
||||
throw_(parse_error, "Failed to locate any journal entries; "
|
||||
"did you specify a valid file with -f?");
|
||||
|
||||
INFO_FINISH(journal);
|
||||
|
||||
INFO("Found " << count << " entries");
|
||||
|
||||
TRACE_FINISH(entry_text, 1);
|
||||
TRACE_FINISH(entry_details, 1);
|
||||
TRACE_FINISH(entry_xacts, 1);
|
||||
TRACE_FINISH(entries, 1);
|
||||
TRACE_FINISH(session_parser, 1);
|
||||
TRACE_FINISH(parsing_total, 1);
|
||||
|
||||
return journal;
|
||||
}
|
||||
|
||||
function_t look_for_command(report_t& report, const string& verb)
|
||||
{
|
||||
if (expr_t::ptr_op_t def = report.lookup(string("ledger_cmd_") + verb))
|
||||
return def->as_function();
|
||||
else
|
||||
return function_t();
|
||||
}
|
||||
|
||||
void normalize_report_options(report_t& report, const string& verb)
|
||||
{
|
||||
#if 1
|
||||
// Patch up some of the reporting options based on what kind of
|
||||
// command it was.
|
||||
|
||||
// jww (2008-08-14): This code really needs to be rationalized away
|
||||
// for 3.0.
|
||||
|
||||
if (verb == "print" || verb == "entry" || verb == "dump") {
|
||||
report.show_related = true;
|
||||
report.show_all_related = true;
|
||||
}
|
||||
else if (verb == "equity") {
|
||||
report.show_subtotal = true;
|
||||
}
|
||||
else if (report.show_related) {
|
||||
if (verb[0] == 'r') {
|
||||
report.show_inverted = true;
|
||||
} else {
|
||||
report.show_subtotal = true;
|
||||
report.show_all_related = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (verb[0] != 'b' && verb[0] != 'r')
|
||||
amount_t::keep_base = true;
|
||||
|
||||
// Setup the default value for the display predicate
|
||||
|
||||
if (report.display_predicate.empty()) {
|
||||
if (verb[0] == 'b') {
|
||||
if (! report.show_empty)
|
||||
report.display_predicate = "total";
|
||||
if (! report.show_subtotal) {
|
||||
if (! report.display_predicate.empty())
|
||||
report.display_predicate += "&";
|
||||
report.display_predicate += "depth<=1";
|
||||
}
|
||||
}
|
||||
else if (verb == "equity") {
|
||||
report.display_predicate = "amount_expr"; // jww (2008-08-14): ???
|
||||
}
|
||||
else if (verb[0] == 'r' && ! report.show_empty) {
|
||||
report.display_predicate = "amount";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Now setup the various formatting strings
|
||||
|
||||
// jww (2008-08-14): I hear a song, and it's sound is "HaAaaCcK"
|
||||
|
||||
#if 0
|
||||
if (! date_output_format.empty())
|
||||
date_t::output_format = date_output_format;
|
||||
#endif
|
||||
|
||||
amount_t::keep_price = report.keep_price;
|
||||
amount_t::keep_date = report.keep_date;
|
||||
amount_t::keep_tag = report.keep_tag;
|
||||
|
||||
if (! report.report_period.empty() && ! report.sort_all)
|
||||
report.entry_sort = true;
|
||||
}
|
||||
|
||||
void create_output_stream(report_t& report)
|
||||
{
|
||||
report.output_stream.initialize(report.output_file, report.pager_path);
|
||||
}
|
||||
|
||||
void invoke_command_verb(report_t& report,
|
||||
function_t& command,
|
||||
string_iterator args_begin,
|
||||
string_iterator args_end)
|
||||
{
|
||||
// Create an argument scope containing the report command's
|
||||
// arguments, and then invoke the command.
|
||||
|
||||
call_scope_t command_args(report);
|
||||
|
||||
for (string_iterator i = args_begin; i != args_end; i++)
|
||||
command_args.push_back(string_value(*i));
|
||||
|
||||
INFO_START(command, "Finished executing command");
|
||||
|
||||
command(command_args);
|
||||
|
||||
INFO_FINISH(command);
|
||||
}
|
||||
|
||||
void write_binary_cache(session_t& session, journal_t * journal)
|
||||
{
|
||||
// Write out the binary cache, if need be
|
||||
|
||||
if (session.use_cache && session.cache_dirty && session.cache_file) {
|
||||
TRACE_START(binary_cache, 1, "Wrote binary journal file");
|
||||
|
||||
ofstream stream(*session.cache_file);
|
||||
#if 0
|
||||
// jww (2009-01-31): NYI
|
||||
journal->write(stream);
|
||||
#endif
|
||||
|
||||
TRACE_FINISH(binary_cache, 1);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ledger
|
||||
65
src/work.h
Normal file
65
src/work.h
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (c) 2003-2009, John Wiegley. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* - Neither the name of New Artisans LLC nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file work.h
|
||||
* @author John Wiegley
|
||||
*
|
||||
* @brief Contains the top-level functions used by main.cc
|
||||
*/
|
||||
#ifndef _WORK_H
|
||||
#define _WORK_H
|
||||
|
||||
namespace ledger {
|
||||
|
||||
typedef strings_list::iterator string_iterator;
|
||||
typedef std::pair<string_iterator, string_iterator> string_iterator_pair;
|
||||
|
||||
void handle_debug_options(int argc, char * argv[]);
|
||||
void register_journal_parsers(session_t& session);
|
||||
void read_environment_settings(report_t& report, char * envp[]);
|
||||
strings_list read_command_line_arguments(report_t& report,
|
||||
int argc, char * argv[]);
|
||||
void normalize_session_options(session_t& session);
|
||||
function_t look_for_precommand(report_t& report, const string& verb);
|
||||
journal_t * read_journal_files(session_t& session);
|
||||
function_t look_for_command(report_t& report, const string& verb);
|
||||
void normalize_report_options(report_t& report, const string& verb);
|
||||
void create_output_stream(report_t& report);
|
||||
void invoke_command_verb(report_t& report,
|
||||
function_t& command,
|
||||
string_iterator args_begin,
|
||||
string_iterator args_end);
|
||||
void write_binary_cache(session_t& session, journal_t * journal);
|
||||
|
||||
} // namespace ledger
|
||||
|
||||
#endif // _WORK_H
|
||||
Loading…
Add table
Reference in a new issue