summaryrefslogtreecommitdiff
path: root/src/driver
diff options
context:
space:
mode:
Diffstat (limited to 'src/driver')
-rw-r--r--src/driver/fdstream.hpp215
-rw-r--r--src/driver/main.cc481
-rw-r--r--src/driver/option.cc222
-rw-r--r--src/driver/option.h53
-rw-r--r--src/driver/report.cc223
-rw-r--r--src/driver/report.h190
-rw-r--r--src/driver/session.cc313
-rw-r--r--src/driver/session.h196
8 files changed, 1893 insertions, 0 deletions
diff --git a/src/driver/fdstream.hpp b/src/driver/fdstream.hpp
new file mode 100644
index 00000000..ffcf5709
--- /dev/null
+++ b/src/driver/fdstream.hpp
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2003-2007, 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.
+ */
+
+/* The following code declares classes to read from and write to
+ * file descriptore or file handles.
+ *
+ * See
+ * http://www.josuttis.com/cppcode
+ * for details and the latest version.
+ *
+ * - open:
+ * - integrating BUFSIZ on some systems?
+ * - optimized reading of multiple characters
+ * - stream for reading AND writing
+ * - i18n
+ *
+ * (C) Copyright Nicolai M. Josuttis 2001.
+ * Permission to copy, use, modify, sell and distribute this software
+ * is granted provided this copyright notice appears in all copies.
+ * This software is provided "as is" without express or implied
+ * warranty, and with no claim as to its suitability for any purpose.
+ *
+ * Version: Jul 28, 2002
+ * History:
+ * Jul 28, 2002: bugfix memcpy() => memmove()
+ * fdinbuf::underflow(): cast for return statements
+ * Aug 05, 2001: first public version
+ */
+#ifndef BOOST_FDSTREAM_HPP
+#define BOOST_FDSTREAM_HPP
+
+#include <istream>
+#include <ostream>
+#include <streambuf>
+// for EOF:
+#include <cstdio>
+// for memmove():
+#include <cstring>
+
+
+// low-level read and write functions
+#ifdef _MSC_VER
+# include <io.h>
+#else
+# include <unistd.h>
+//extern "C" {
+// int write (int fd, const char* buf, int num);
+// int read (int fd, char* buf, int num);
+//}
+#endif
+
+
+// BEGIN namespace BOOST
+namespace boost {
+
+
+/************************************************************
+ * fdostream
+ * - a stream that writes on a file descriptor
+ ************************************************************/
+
+
+class fdoutbuf : public std::streambuf {
+ protected:
+ int fd; // file descriptor
+ public:
+ // constructor
+ fdoutbuf (int _fd) : fd(_fd) {
+ }
+ protected:
+ // write one character
+ virtual int_type overflow (int_type c) {
+ if (c != EOF) {
+ char z = c;
+ if (write (fd, &z, 1) != 1) {
+ return EOF;
+ }
+ }
+ return c;
+ }
+ // write multiple characters
+ virtual
+ std::streamsize xsputn (const char* s,
+ std::streamsize num) {
+ return write(fd,s,num);
+ }
+};
+
+class fdostream : public std::ostream {
+ protected:
+ fdoutbuf buf;
+ public:
+ fdostream (int fd) : std::ostream(0), buf(fd) {
+ rdbuf(&buf);
+ }
+};
+
+
+/************************************************************
+ * fdistream
+ * - a stream that reads on a file descriptor
+ ************************************************************/
+
+class fdinbuf : public std::streambuf {
+ protected:
+ int fd; // file descriptor
+ protected:
+ /* data buffer:
+ * - at most, pbSize characters in putback area plus
+ * - at most, bufSize characters in ordinary read buffer
+ */
+ static const int pbSize = 4; // size of putback area
+ static const int bufSize = 1024; // size of the data buffer
+ char buffer[bufSize+pbSize]; // data buffer
+
+ public:
+ /* constructor
+ * - initialize file descriptor
+ * - initialize empty data buffer
+ * - no putback area
+ * => force underflow()
+ */
+ fdinbuf (int _fd) : fd(_fd) {
+ setg (buffer+pbSize, // beginning of putback area
+ buffer+pbSize, // read position
+ buffer+pbSize); // end position
+ }
+
+ protected:
+ // insert new characters into the buffer
+ virtual int_type underflow () {
+#ifndef _MSC_VER
+ using std::memmove;
+#endif
+
+ // is read position before end of buffer?
+ if (gptr() < egptr()) {
+ return traits_type::to_int_type(*gptr());
+ }
+
+ /* process size of putback area
+ * - use number of characters read
+ * - but at most size of putback area
+ */
+ int numPutback;
+ numPutback = gptr() - eback();
+ if (numPutback > pbSize) {
+ numPutback = pbSize;
+ }
+
+ /* copy up to pbSize characters previously read into
+ * the putback area
+ */
+ memmove (buffer+(pbSize-numPutback), gptr()-numPutback,
+ numPutback);
+
+ // read at most bufSize new characters
+ int num;
+ num = read (fd, buffer+pbSize, bufSize);
+ if (num <= 0) {
+ // ERROR or EOF
+ return EOF;
+ }
+
+ // reset buffer pointers
+ setg (buffer+(pbSize-numPutback), // beginning of putback area
+ buffer+pbSize, // read position
+ buffer+pbSize+num); // end of buffer
+
+ // return next character
+ return traits_type::to_int_type(*gptr());
+ }
+};
+
+class fdistream : public std::istream {
+ protected:
+ fdinbuf buf;
+ public:
+ fdistream (int fd) : std::istream(0), buf(fd) {
+ rdbuf(&buf);
+ }
+};
+
+
+} // END namespace boost
+
+#endif /*BOOST_FDSTREAM_HPP*/
diff --git a/src/driver/main.cc b/src/driver/main.cc
new file mode 100644
index 00000000..469bb5ee
--- /dev/null
+++ b/src/driver/main.cc
@@ -0,0 +1,481 @@
+/*
+ * Copyright (c) 2003-2007, 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 "utils.h"
+#include "option.h"
+//#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
+//#include "gnucash.h"
+//#endif
+//#include "qif.h"
+//#include "ofx.h"
+#include "jbuilder.h"
+#include "compile.h"
+
+#include <ledger.h>
+
+#ifdef HAVE_UNIX_PIPES
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <fdstream.hpp>
+#endif
+
+static int read_and_report(ledger::report_t& report, int argc, char * argv[],
+ char * envp[])
+{
+ using namespace ledger;
+
+ session_t& session(report.session);
+
+ // Handle the command-line arguments
+
+ strings_list args;
+ process_arguments(argc - 1, argv + 1, false, report, args);
+
+ if (args.empty()) {
+#if 0
+ help(std::cerr);
+#endif
+ return 1;
+ }
+ strings_list::iterator arg = args.begin();
+
+ if (! session.cache_file)
+ session.use_cache = false;
+ else
+ session.use_cache = ! session.data_file.empty() && session.price_db;
+
+ DEBUG("ledger.session.cache", "1. use_cache = " << session.use_cache);
+
+ // Process the environment settings
+
+ TRACE_START(environment, 1, "Processed environment variables");
+ process_environment(const_cast<const char **>(envp), "LEDGER_", report);
+ TRACE_FINISH(environment, 1);
+
+ optional<path> home;
+ if (const char * home_var = std::getenv("HOME"))
+ home = home_var;
+
+ if (! session.init_file)
+ session.init_file = home ? *home / ".ledgerrc" : "./.ledgerrc";
+ if (! session.price_db)
+ session.price_db = home ? *home / ".pricedb" : "./.pricedb";
+
+ if (! session.cache_file)
+ session.cache_file = home ? *home / ".ledger-cache" : "./.ledger-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");
+
+ // Read the command word and create a command object based on it
+
+ string verb = *arg++;
+
+ xml::xpath_t::function_t command;
+
+#if 0
+ if (verb == "register" || verb == "reg" || verb == "r")
+ command = register_command();
+ else if (verb == "balance" || verb == "bal" || verb == "b")
+ command = balance_command();
+ else if (verb == "print" || verb == "p")
+ command = print_command();
+ else if (verb == "equity")
+ command = equity_command();
+ else if (verb == "entry")
+ command = entry_command();
+ else if (verb == "dump")
+ command = dump_command();
+ else if (verb == "output")
+ command = output_command();
+ else if (verb == "prices")
+ command = prices_command();
+ else if (verb == "pricesdb")
+ command = pricesdb_command();
+ else if (verb == "csv")
+ command = csv_command();
+ else if (verb == "emacs" || verb == "lisp")
+ command = emacs_command();
+ else
+#endif
+ if (verb == "xml")
+ command = bind(xml_command, _1);
+ else if (verb == "expr")
+ ;
+ else if (verb == "xpath")
+ ;
+ else if (verb == "parse") {
+ xml::xpath_t expr(*arg);
+ xml::document_t temp(xml::LEDGER_NODE);
+
+ xml::xpath_t::context_scope_t doc_scope(report, &temp);
+
+ IF_INFO() {
+ std::cout << "Value expression tree:" << std::endl;
+ expr.dump(std::cout);
+ std::cout << std::endl;
+
+ std::cout << "Value expression parsed was:" << std::endl;
+ expr.print(std::cout, doc_scope);
+ std::cout << std::endl << std::endl;
+
+ expr.compile(doc_scope);
+
+ std::cout << "Value expression after compiling:" << std::endl;
+ expr.dump(std::cout);
+ std::cout << std::endl;
+
+ std::cout << "Value expression is now:" << std::endl;
+ expr.print(std::cout, doc_scope);
+ std::cout << std::endl << std::endl;
+
+ std::cout << "Result of calculation: ";
+ }
+
+ std::cout << expr.calc(doc_scope).strip_annotations() << std::endl;
+
+ return 0;
+ }
+ else {
+ char buf[128];
+ std::strcpy(buf, "command_");
+ std::strcat(buf, verb.c_str());
+
+ if (xml::xpath_t::ptr_op_t def = report.lookup(buf))
+ command = def->as_function();
+
+ if (! command)
+ throw_(std::logic_error, string("Unrecognized command '") + verb + "'");
+ }
+
+ // Parse the initialization file, which can only be textual; then
+ // parse the journal data.
+
+ session.read_init();
+
+ INFO_START(journal, "Read journal file");
+
+ xml::document_t xml_document(xml::LEDGER_NODE);
+ journal_t * journal = session.create_journal();
+ xml::journal_builder_t builder(xml_document, journal);
+
+ if (! session.read_data(builder, journal, report.account))
+ throw_(parse_error, "Failed to locate any journal entries; "
+ "did you specify a valid file with -f?");
+
+ INFO_FINISH(journal);
+
+ TRACE_FINISH(entry_text, 1);
+ TRACE_FINISH(entry_date, 1);
+ TRACE_FINISH(entry_details, 1);
+ TRACE_FINISH(entry_xacts, 1);
+ TRACE_FINISH(entries, 1);
+ TRACE_FINISH(parsing_total, 1);
+
+ // Configure the output stream
+
+#ifdef HAVE_UNIX_PIPES
+ int status, pfd[2]; // Pipe file descriptors
+#endif
+ std::ostream * out = &std::cout;
+
+ if (report.output_file) {
+ out = new ofstream(*report.output_file);
+ }
+#ifdef HAVE_UNIX_PIPES
+ else if (report.pager) {
+ status = pipe(pfd);
+ if (status == -1)
+ throw_(std::logic_error, "Failed to create pipe");
+
+ status = fork();
+ if (status < 0) {
+ throw_(std::logic_error, "Failed to fork child process");
+ }
+ else if (status == 0) { // child
+ // Duplicate pipe's reading end into stdin
+ status = dup2(pfd[0], STDIN_FILENO);
+ if (status == -1)
+ perror("dup2");
+
+ // Close unuseful file descriptors: the pipe's writing and
+ // reading ends (the latter is not needed anymore, after the
+ // duplication).
+ close(pfd[1]);
+ close(pfd[0]);
+
+ // Find command name: its the substring starting right of the
+ // rightmost '/' character in the pager pathname. See manpage
+ // for strrchr.
+ execlp(report.pager->native_file_string().c_str(),
+ basename(*report.pager).c_str(), (char *)0);
+ perror("execl");
+ exit(1);
+ }
+ else { // parent
+ close(pfd[0]);
+ out = new boost::fdostream(pfd[1]);
+ }
+ }
+#endif
+
+ report.define("ostream", value_t(out));
+
+ // Are we handling the expr commands? Do so now.
+
+ xml::xpath_t::context_scope_t doc_scope(report, &xml_document);
+
+ if (verb == "expr") {
+ xml::xpath_t expr(*arg);
+
+ IF_INFO() {
+ *out << "Value expression tree:" << std::endl;
+ expr.dump(*out);
+ *out << std::endl;
+ *out << "Value expression parsed was:" << std::endl;
+ expr.print(*out, doc_scope);
+ *out << std::endl << std::endl;
+ *out << "Result of calculation: ";
+ }
+
+ *out << expr.calc(doc_scope).strip_annotations() << std::endl;
+
+ return 0;
+ }
+ else if (verb == "xpath") {
+ std::cout << "XPath parsed: ";
+
+ xml::xpath_t xpath(*arg);
+ xpath.print(*out, doc_scope);
+ *out << std::endl;
+
+ IF_INFO() {
+ *out << "Raw results:" << std::endl;
+
+ foreach (const value_t& value, xpath.find_all(doc_scope)) {
+ if (value.is_xml_node())
+ value.as_xml_node()->print(std::cout);
+ else
+ std::cout << value;
+ std::cout << std::endl;
+ }
+
+ *out << "Compiled results:" << std::endl;
+ }
+
+ xml::compile_node(xml_document, report);
+
+ foreach (const value_t& value, xpath.find_all(doc_scope)) {
+ if (value.is_xml_node())
+ value.as_xml_node()->print(std::cout);
+ else
+ std::cout << value;
+ std::cout << std::endl;
+ }
+ return 0;
+ }
+
+ // Apply transforms to the hierarchical document structure
+
+ INFO_START(transforms, "Applied transforms");
+ report.apply_transforms(doc_scope);
+ INFO_FINISH(transforms);
+
+ // Create an argument scope containing the report command's
+ // arguments, and then invoke the command.
+
+ xml::xpath_t::call_scope_t command_args(doc_scope);
+
+ for (strings_list::iterator i = arg; i != args.end(); i++)
+ command_args.push_back(value_t(*i, true));
+
+ INFO_START(command, "Did user command '" << verb << "'");
+
+ command(command_args);
+
+ INFO_FINISH(command);
+
+ // 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");
+
+#if 0
+ ofstream stream(*session.cache_file);
+ write_binary_journal(stream, journal);
+#endif
+
+ TRACE_FINISH(binary_cache, 1);
+ }
+
+ // If the user specified a pager, wait for it to exit now
+
+#ifdef HAVE_UNIX_PIPES
+ if (! report.output_file && report.pager) {
+ checked_delete(out);
+ close(pfd[1]);
+
+ // Wait for child to finish
+ wait(&status);
+ if (status & 0xffff != 0)
+ throw_(std::logic_error, "Something went wrong in the pager");
+ }
+#endif
+ else if (DO_VERIFY() && report.output_file) {
+ checked_delete(out);
+ }
+
+ return 0;
+}
+
+int main(int argc, char * argv[], char * envp[])
+{
+ int status = 1;
+
+ 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;
+ ledger::_trace_level = boost::lexical_cast<int>(argv[i + 1]);
+ i++;
+#endif
+ }
+ }
+
+ IF_VERIFY()
+ ledger::initialize_memory_tracing();
+
+ try {
+ std::ios::sync_with_stdio(false);
+
+ boost::filesystem::path::default_name_check
+ (boost::filesystem::portable_posix_name);
+
+ INFO("Ledger starting");
+
+ std::auto_ptr<ledger::session_t> session(new ledger::session_t);
+
+ ledger::set_session_context(session.get());
+
+#if 0
+ session->register_parser(new binary_parser_t);
+#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
+ session->register_parser(new xml::xml_parser_t);
+ session->register_parser(new gnucash_parser_t);
+#endif
+#ifdef HAVE_LIBOFX
+ session->register_parser(new ofx_parser_t);
+#endif
+ session->register_parser(new qif_parser_t);
+#endif
+ session->register_parser(new ledger::textual_parser_t);
+
+ std::auto_ptr<ledger::report_t> report(new ledger::report_t(*session.get()));
+
+ status = read_and_report(*report.get(), argc, argv, envp);
+
+ if (DO_VERIFY()) {
+ ledger::set_session_context();
+ } else {
+ report.release();
+ session.release();
+ }
+ }
+#if 0
+ catch (error * err) {
+ std::cout.flush();
+ // Push a null here since there's no file context
+ if (err->context.empty() ||
+ ! dynamic_cast<xact_context *>(err->context.front()))
+ err->context.push_front(new error_context(""));
+ err->reveal_context(std::cerr, "Error");
+ std::cerr << err->what() << std::endl;
+ checked_delete(err);
+ }
+ catch (fatal * err) {
+ std::cout.flush();
+ // Push a null here since there's no file context
+ if (err->context.empty() ||
+ ! dynamic_cast<xact_context *>(err->context.front()))
+ err->context.push_front(new error_context(""));
+ err->reveal_context(std::cerr, "Fatal");
+ std::cerr << err->what() << std::endl;
+ checked_delete(err);
+ }
+#endif
+ catch (const std::exception& err) {
+ std::cout.flush();
+ std::cerr << "Error: " << err.what() << std::endl;
+ }
+ catch (int _status) {
+ status = _status;
+ }
+
+ IF_VERIFY() {
+ INFO("Ledger ended (Boost/libstdc++ may still hold memory)");
+ ledger::shutdown_memory_tracing();
+ } else {
+ INFO("Ledger ended");
+ }
+
+ return status;
+}
+
+// main.cc ends here.
diff --git a/src/driver/option.cc b/src/driver/option.cc
new file mode 100644
index 00000000..0b7f7ef9
--- /dev/null
+++ b/src/driver/option.cc
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2003-2007, 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 "option.h"
+
+namespace ledger {
+
+namespace {
+ typedef tuple<xml::xpath_t::ptr_op_t, bool> op_bool_tuple;
+
+ op_bool_tuple find_option(xml::xpath_t::scope_t& scope, const string& name)
+ {
+ char buf[128];
+ std::strcpy(buf, "option_");
+ char * p = &buf[7];
+ for (const char * q = name.c_str(); *q; q++) {
+ if (*q == '-')
+ *p++ = '_';
+ else
+ *p++ = *q;
+ }
+ *p = '\0';
+
+ xml::xpath_t::ptr_op_t op = scope.lookup(buf);
+ if (op)
+ return op_bool_tuple(op, false);
+
+ *p++ = '_';
+ *p = '\0';
+
+ return op_bool_tuple(scope.lookup(buf), true);
+ }
+
+ op_bool_tuple find_option(xml::xpath_t::scope_t& scope, const char letter)
+ {
+ char buf[10];
+ std::strcpy(buf, "option_");
+ buf[7] = letter;
+ buf[8] = '\0';
+
+ xml::xpath_t::ptr_op_t op = scope.lookup(buf);
+ if (op)
+ return op_bool_tuple(op, false);
+
+ buf[8] = '_';
+ buf[9] = '\0';
+
+ return op_bool_tuple(scope.lookup(buf), true);
+ }
+
+ void process_option(const xml::xpath_t::function_t& opt,
+ xml::xpath_t::scope_t& scope, const char * arg)
+ {
+#if 0
+ try {
+#endif
+ xml::xpath_t::call_scope_t args(scope);
+ if (arg)
+ args.push_back(value_t(arg, true));
+
+ opt(args);
+#if 0
+ }
+ catch (error * err) {
+ err->context.push_back
+ (new error_context
+ (string("While parsing option '--") + opt->long_opt +
+ "'" + (opt->short_opt != '\0' ?
+ (string(" (-") + opt->short_opt + "):") : ":")));
+ throw err;
+ }
+#endif
+ }
+}
+
+void process_option(const string& name, xml::xpath_t::scope_t& scope,
+ const char * arg)
+{
+ op_bool_tuple opt(find_option(scope, name));
+ if (opt.get<0>())
+ process_option(opt.get<0>()->as_function(), scope, arg);
+}
+
+void process_environment(const char ** envp, const string& tag,
+ xml::xpath_t::scope_t& scope)
+{
+ const char * tag_p = tag.c_str();
+ unsigned int tag_len = tag.length();
+
+ for (const char ** p = envp; *p; p++)
+ if (! tag_p || std::strncmp(*p, tag_p, tag_len) == 0) {
+ char buf[128];
+ char * r = buf;
+ const char * q;
+ for (q = *p + tag_len;
+ *q && *q != '=' && r - buf < 128;
+ q++)
+ if (*q == '_')
+ *r++ = '-';
+ else
+ *r++ = std::tolower(*q);
+ *r = '\0';
+
+ if (*q == '=') {
+#if 0
+ try {
+#endif
+ process_option(string(buf), scope, q + 1);
+#if 0
+ }
+ catch (error * err) {
+ err->context.push_back
+ (new error_context
+ (string("While parsing environment variable option '") +
+ *p + "':"));
+ throw err;
+ }
+#endif
+ }
+ }
+}
+
+void process_arguments(int argc, char ** argv, const bool anywhere,
+ xml::xpath_t::scope_t& scope,
+ std::list<string>& args)
+{
+ for (char ** i = argv; *i; i++) {
+ if ((*i)[0] != '-') {
+ if (anywhere) {
+ args.push_back(*i);
+ continue;
+ } else {
+ for (; *i; i++)
+ args.push_back(*i);
+ break;
+ }
+ }
+
+ // --long-option or -s
+ if ((*i)[1] == '-') {
+ if ((*i)[2] == '\0')
+ break;
+
+ char * name = *i + 2;
+ char * value = NULL;
+ if (char * p = std::strchr(name, '=')) {
+ *p++ = '\0';
+ value = p;
+ }
+
+ op_bool_tuple opt(find_option(scope, name));
+ if (! opt.get<0>())
+ throw_(option_error, "illegal option --" << name);
+
+ if (opt.get<1>() && value == NULL) {
+ value = *++i;
+ if (value == NULL)
+ throw_(option_error, "missing option argument for --" << name);
+ }
+ process_option(opt.get<0>()->as_function(), scope, value);
+ }
+ else if ((*i)[1] == '\0') {
+ throw_(option_error, "illegal option -");
+ }
+ else {
+ typedef tuple<xml::xpath_t::ptr_op_t, bool, char> op_bool_char_tuple;
+
+ std::list<op_bool_char_tuple> option_queue;
+
+ int x = 1;
+ for (char c = (*i)[x]; c != '\0'; x++, c = (*i)[x]) {
+ op_bool_tuple opt(find_option(scope, c));
+ if (! opt.get<0>())
+ throw_(option_error, "illegal option -" << c);
+
+ option_queue.push_back
+ (op_bool_char_tuple(opt.get<0>(), opt.get<1>(), c));
+ }
+
+ foreach (op_bool_char_tuple& o, option_queue) {
+ char * value = NULL;
+ if (o.get<1>()) {
+ value = *++i;
+ if (value == NULL)
+ throw_(option_error,
+ "missing option argument for -" << o.get<2>());
+ }
+ process_option(o.get<0>()->as_function(), scope, value);
+ }
+ }
+ }
+}
+
+} // namespace ledger
diff --git a/src/driver/option.h b/src/driver/option.h
new file mode 100644
index 00000000..d26c8417
--- /dev/null
+++ b/src/driver/option.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2003-2007, 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.
+ */
+
+#ifndef _OPTION_H
+#define _OPTION_H
+
+#include "xpath.h"
+
+namespace ledger {
+
+void process_option(const string& name, xml::xpath_t::scope_t& scope,
+ const char * arg = NULL);
+
+void process_environment(const char ** envp, const string& tag,
+ xml::xpath_t::scope_t& scope);
+
+void process_arguments(int argc, char ** argv, const bool anywhere,
+ xml::xpath_t::scope_t& scope,
+ std::list<string>& args);
+
+DECLARE_EXCEPTION(option_error);
+
+} // namespace ledger
+
+#endif // _OPTION_H
diff --git a/src/driver/report.cc b/src/driver/report.cc
new file mode 100644
index 00000000..ee3382da
--- /dev/null
+++ b/src/driver/report.cc
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2003-2007, 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 "report.h"
+
+namespace ledger {
+
+report_t::~report_t()
+{
+ TRACE_DTOR(report_t);
+}
+
+void report_t::apply_transforms(xml::xpath_t::scope_t& scope)
+{
+ typedef tuple<shared_ptr<transform_t>, value_t> transform_details_tuple;
+
+ foreach (transform_details_tuple& transform_details, transforms) {
+ xml::xpath_t::call_scope_t call_args(scope);
+ call_args.set_args(transform_details.get<1>());
+ (*transform_details.get<0>())(call_args);
+ }
+}
+
+value_t report_t::abbrev(xml::xpath_t::call_scope_t& args)
+{
+ if (args.size() < 2)
+ throw_(std::logic_error, "usage: abbrev(STRING, WIDTH [, STYLE, ABBREV_LEN])");
+
+ string str = args[0].as_string();
+ long wid = args[1];
+
+ elision_style_t style = session.elision_style;
+ if (args.size() == 3)
+ style = (elision_style_t)args[2].as_long();
+
+ long abbrev_len = session.abbrev_length;
+ if (args.size() == 4)
+ abbrev_len = args[3].as_long();
+
+ return value_t(abbreviate(str, wid, style, true, (int)abbrev_len), true);
+}
+
+value_t report_t::ftime(xml::xpath_t::call_scope_t& args)
+{
+ if (args.size() < 1)
+ throw_(std::logic_error, "usage: ftime(DATE [, DATE_FORMAT])");
+
+ moment_t date = args[0].as_datetime();
+
+ string date_format;
+ if (args.size() == 2)
+ date_format = args[1].as_string();
+#if 0
+ // jww (2007-04-18): Need to setup an output facet here
+ else
+ date_format = moment_t::output_format;
+
+ return value_t(date.as_string(date_format), true);
+#else
+ return NULL_VALUE;
+#endif
+}
+
+#if 0
+optional<value_t>
+report_t::resolve(const string& name, xml::xpath_t::call_scope_t& args)
+{
+ const char * p = name.c_str();
+ switch (*p) {
+ case 'a':
+ if (name == "abbrev") {
+ return abbrev(args);
+ }
+ break;
+
+ case 'f':
+ if (name == "ftime") {
+ return ftime(args);
+ }
+ break;
+ }
+ return xml::xpath_t::scope_t::resolve(name, args);
+}
+#endif
+
+xml::xpath_t::ptr_op_t report_t::lookup(const string& name)
+{
+ const char * p = name.c_str();
+ switch (*p) {
+ case 'o':
+ if (std::strncmp(p, "option_", 7) == 0) {
+ p = p + 7;
+ switch (*p) {
+ case 'a':
+#if 0
+ if (std::strcmp(p, "accounts") == 0)
+ return MAKE_FUNCTOR(report_t::option_accounts);
+ else
+#endif
+ if (std::strcmp(p, "amount") == 0)
+ return MAKE_FUNCTOR(report_t::option_amount);
+ break;
+
+ case 'b':
+ if (std::strcmp(p, "bar") == 0)
+ return MAKE_FUNCTOR(report_t::option_bar);
+ break;
+
+#if 0
+ case 'c':
+ if (std::strcmp(p, "clean") == 0)
+ return MAKE_FUNCTOR(report_t::option_clean);
+ else if (std::strcmp(p, "compact") == 0)
+ return MAKE_FUNCTOR(report_t::option_compact);
+ break;
+#endif
+
+ case 'e':
+#if 0
+ if (std::strcmp(p, "entries") == 0)
+ return MAKE_FUNCTOR(report_t::option_entries);
+ else if (std::strcmp(p, "eval") == 0)
+ return MAKE_FUNCTOR(report_t::option_eval);
+ else if (std::strcmp(p, "exclude") == 0)
+ return MAKE_FUNCTOR(report_t::option_remove);
+#endif
+ break;
+
+ case 'f':
+#if 0
+ if (std::strcmp(p, "foo") == 0)
+ return MAKE_FUNCTOR(report_t::option_foo);
+ else
+#endif
+ if (std::strcmp(p, "format") == 0)
+ return MAKE_FUNCTOR(report_t::option_format);
+ break;
+
+ case 'i':
+#if 0
+ if (std::strcmp(p, "include") == 0)
+ return MAKE_FUNCTOR(report_t::option_select);
+#endif
+ break;
+
+ case 'l':
+#if 0
+ if (! *(p + 1) || std::strcmp(p, "limit") == 0)
+ return MAKE_FUNCTOR(report_t::option_limit);
+#endif
+ break;
+
+#if 0
+ case 'm':
+ if (std::strcmp(p, "merge") == 0)
+ return MAKE_FUNCTOR(report_t::option_merge);
+ break;
+#endif
+
+ case 'r':
+#if 0
+ if (std::strcmp(p, "remove") == 0)
+ return MAKE_FUNCTOR(report_t::option_remove);
+#endif
+ break;
+
+#if 0
+ case 's':
+ if (std::strcmp(p, "select") == 0)
+ return MAKE_FUNCTOR(report_t::option_select);
+ else if (std::strcmp(p, "split") == 0)
+ return MAKE_FUNCTOR(report_t::option_split);
+ break;
+#endif
+
+ case 't':
+ if (! *(p + 1))
+ return MAKE_FUNCTOR(report_t::option_amount);
+ else if (std::strcmp(p, "total") == 0)
+ return MAKE_FUNCTOR(report_t::option_total);
+ break;
+
+ case 'T':
+ if (! *(p + 1))
+ return MAKE_FUNCTOR(report_t::option_total);
+ break;
+ }
+ }
+ break;
+ }
+
+ return xml::xpath_t::symbol_scope_t::lookup(name);
+}
+
+} // namespace ledger
diff --git a/src/driver/report.h b/src/driver/report.h
new file mode 100644
index 00000000..b435341b
--- /dev/null
+++ b/src/driver/report.h
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2003-2007, 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.
+ */
+
+#ifndef _REPORT_H
+#define _REPORT_H
+
+#include "session.h"
+#include "transform.h"
+
+namespace ledger {
+
+typedef std::list<string> strings_list;
+
+class report_t : public xml::xpath_t::symbol_scope_t
+{
+public:
+ optional<path> output_file;
+ string format_string;
+ string amount_expr;
+ string total_expr;
+ string date_output_format;
+
+ unsigned long budget_flags;
+
+ string account;
+ optional<path> pager;
+
+ bool show_totals;
+ bool raw_mode;
+
+ session_t& session;
+ transform_t * last_transform;
+
+ std::list<tuple<shared_ptr<transform_t>, value_t> > transforms;
+
+ explicit report_t(session_t& _session)
+ : xml::xpath_t::symbol_scope_t(downcast<xml::xpath_t::scope_t>(_session)),
+ show_totals(false),
+ raw_mode(false),
+ session(_session),
+ last_transform(NULL)
+ {
+ TRACE_CTOR(report_t, "session_t&");
+#if 0
+ eval("t=total,TOT=0,T()=(TOT=TOT+t,TOT)");
+#endif
+ }
+
+ virtual ~report_t();
+
+ void apply_transforms(xml::xpath_t::scope_t& scope);
+
+ //
+ // Utility functions for value expressions
+ //
+
+ value_t ftime(xml::xpath_t::call_scope_t& args);
+ value_t abbrev(xml::xpath_t::call_scope_t& args);
+
+ //
+ // Config options
+ //
+
+ void eval(const string& expr) {
+#if 0
+ xml::xpath_t(expr).compile((xml::document_t *)NULL, this);
+#endif
+ }
+ value_t option_eval(xml::xpath_t::call_scope_t& args) {
+ eval(args[0].as_string());
+ return NULL_VALUE;
+ }
+
+ value_t option_amount(xml::xpath_t::call_scope_t& args) {
+ eval(string("t=") + args[0].as_string());
+ return NULL_VALUE;
+ }
+ value_t option_total(xml::xpath_t::call_scope_t& args) {
+ eval(string("T()=") + args[0].as_string());
+ return NULL_VALUE;
+ }
+
+ value_t option_format(xml::xpath_t::call_scope_t& args) {
+ format_string = args[0].as_string();
+ return NULL_VALUE;
+ }
+
+ value_t option_raw(xml::xpath_t::call_scope_t& args) {
+ raw_mode = true;
+ return NULL_VALUE;
+ }
+
+ value_t option_foo(xml::xpath_t::call_scope_t& args) {
+ std::cout << "This is foo" << std::endl;
+ return NULL_VALUE;
+ }
+ value_t option_bar(xml::xpath_t::call_scope_t& args) {
+ std::cout << "This is bar: " << args[0] << std::endl;
+ return NULL_VALUE;
+ }
+
+ //
+ // Transform options
+ //
+
+#if 0
+ value_t option_select(xml::xpath_t::call_scope_t& args) {
+ transforms.push_back(new select_transform(args[0].as_string()));
+ return NULL_VALUE;
+ }
+ value_t option_limit(xml::xpath_t::call_scope_t& args) {
+ string expr = (string("//xact[") +
+ args[0].as_string() + "]");
+ transforms.push_back(new select_transform(expr));
+ return NULL_VALUE;
+ }
+
+ value_t option_remove(xml::xpath_t::call_scope_t& args) {
+ transforms.push_back(new remove_transform(args[0].as_string()));
+ return NULL_VALUE;
+ }
+
+ value_t option_accounts(xml::xpath_t::call_scope_t& args) {
+ transforms.push_back(new accounts_transform);
+ return NULL_VALUE;
+ }
+ value_t option_compact(xml::xpath_t::call_scope_t& args) {
+ transforms.push_back(new compact_transform);
+ return NULL_VALUE;
+ }
+ value_t option_clean(xml::xpath_t::call_scope_t& args) {
+ transforms.push_back(new clean_transform);
+ return NULL_VALUE;
+ }
+ value_t option_entries(xml::xpath_t::call_scope_t& args) {
+ transforms.push_back(new entries_transform);
+ return NULL_VALUE;
+ }
+
+ value_t option_split(xml::xpath_t::call_scope_t& args) {
+ transforms.push_back(new split_transform);
+ return NULL_VALUE;
+ }
+ value_t option_merge(xml::xpath_t::call_scope_t& args) {
+ transforms.push_back(new merge_transform);
+ return NULL_VALUE;
+ }
+#endif
+
+ //
+ // Scope members
+ //
+
+ virtual xml::xpath_t::ptr_op_t lookup(const string& name);
+};
+
+string abbrev(const string& str, unsigned int width,
+ const bool is_account);
+
+} // namespace ledger
+
+#endif // _REPORT_H
diff --git a/src/driver/session.cc b/src/driver/session.cc
new file mode 100644
index 00000000..71f5f349
--- /dev/null
+++ b/src/driver/session.cc
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2003-2007, 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 "session.h"
+
+namespace ledger {
+
+session_t * session_t::current = NULL;
+
+#if 0
+boost::mutex session_t::session_mutex;
+#endif
+
+static void initialize();
+static void shutdown();
+
+void set_session_context(session_t * session)
+{
+#if 0
+ session_t::session_mutex.lock();
+#endif
+
+ if (session && ! session_t::current) {
+ initialize();
+ }
+ else if (! session && session_t::current) {
+ shutdown();
+#if 0
+ session_t::session_mutex.unlock();
+#endif
+ }
+
+ session_t::current = session;
+}
+
+void release_session_context()
+{
+#if 0
+ session_t::session_mutex.unlock();
+#endif
+}
+
+session_t::session_t()
+ : symbol_scope_t(),
+
+ register_format
+ ("%((//entry)%{date} %-.20{payee}"
+ "%((./xact)%32|%-22{abbrev(account, 22)} %12.67t %12.80T\n))"),
+ wide_register_format
+ ("%D %-.35P %-.38A %22.108t %!22.132T\n%/"
+ "%48|%-.38A %22.108t %!22.132T\n"),
+ print_format
+#if 1
+ ("%(/%(/%{date} %-.20{payee}\n%(: %-34{account} %12t\n)\n))"),
+#else
+ ("\n%d %Y%C%P\n %-34W %12o%n\n%/ %-34W %12o%n\n"),
+#endif
+ balance_format
+ ("%(/%(//%20t %{\" \" * rdepth}%{rname}\n))--------------------\n%20t\n"),
+ equity_format
+
+ ("%((/)%{ftime(now, date_format)} %-.20{\"Opening Balance\"}\n%((.//account[value != 0]) %-34{fullname} %12{value}\n)\n)"),
+ plot_amount_format
+ ("%D %(@S(@t))\n"),
+ plot_total_format
+ ("%D %(@S(@T))\n"),
+ write_hdr_format
+ ("%d %Y%C%P\n"),
+ write_xact_format
+ (" %-34W %12o%n\n"),
+ prices_format
+ ("%[%Y/%m/%d %H:%M:%S %Z] %-10A %12t %12T\n"),
+ pricesdb_format
+ ("P %[%Y/%m/%d %H:%M:%S] %A %t\n"),
+
+ pricing_leeway(24 * 3600),
+
+ download_quotes(false),
+ use_cache(false),
+ cache_dirty(false),
+
+ now(now),
+
+ elision_style(ABBREVIATE),
+ abbrev_length(2),
+
+ ansi_codes(false),
+ ansi_invert(false)
+{
+ TRACE_CTOR(session_t, "xml::xpath_t::scope_t&");
+}
+
+std::size_t session_t::read_journal(std::istream& in,
+ const path& pathname,
+ xml::builder_t& builder)
+{
+#if 0
+ if (! master)
+ master = journal->master;
+#endif
+
+ foreach (parser_t& parser, parsers)
+ if (parser.test(in))
+ return parser.parse(in, pathname, builder);
+
+ return 0;
+}
+
+std::size_t session_t::read_journal(const path& pathname,
+ xml::builder_t& builder)
+{
+#if 0
+ journal->sources.push_back(pathname);
+#endif
+
+ if (! exists(pathname))
+ throw_(std::logic_error, "Cannot read file" << pathname);
+
+ ifstream stream(pathname);
+ return read_journal(stream, pathname, builder);
+}
+
+void session_t::read_init()
+{
+ if (! init_file)
+ return;
+
+ if (! exists(*init_file))
+ throw_(std::logic_error, "Cannot read init file" << *init_file);
+
+ ifstream init(*init_file);
+
+ // jww (2006-09-15): Read initialization options here!
+}
+
+std::size_t session_t::read_data(xml::builder_t& builder,
+ journal_t * journal,
+ const string& master_account)
+{
+ if (data_file.empty())
+ throw_(parse_error, "No journal file was specified (please use -f)");
+
+ TRACE_START(parser, 1, "Parsing journal file");
+
+ std::size_t entry_count = 0;
+
+ DEBUG("ledger.cache", "3. use_cache = " << use_cache);
+
+ if (use_cache && cache_file) {
+ DEBUG("ledger.cache", "using_cache " << cache_file->string());
+ cache_dirty = true;
+ if (exists(*cache_file)) {
+ scoped_variable<optional<path> >
+ save_price_db(journal->price_db, price_db);
+
+ entry_count += read_journal(*cache_file, builder);
+ if (entry_count > 0)
+ cache_dirty = false;
+ }
+ }
+
+ if (entry_count == 0) {
+ account_t * acct = NULL;
+ if (! master_account.empty())
+ acct = journal->find_account(master_account);
+
+ journal->price_db = price_db;
+ if (journal->price_db && exists(*journal->price_db)) {
+ if (read_journal(*journal->price_db, builder)) {
+ throw_(parse_error, "Entries not allowed in price history file");
+ } else {
+ DEBUG("ledger.cache",
+ "read price database " << journal->price_db->string());
+ journal->sources.pop_back();
+ }
+ }
+
+ DEBUG("ledger.cache", "rejected cache, parsing " << data_file.string());
+ if (data_file == "-") {
+ use_cache = false;
+ journal->sources.push_back("<stdin>");
+ entry_count += read_journal(std::cin, "<stdin>", builder);
+ }
+ else if (exists(data_file)) {
+ entry_count += read_journal(data_file, builder);
+ if (journal->price_db)
+ journal->sources.push_back(*journal->price_db);
+ }
+ }
+
+ VERIFY(journal->valid());
+
+ TRACE_STOP(parser, 1);
+
+ return entry_count;
+}
+
+#if 0
+optional<value_t>
+session_t::resolve(const string& name, xml::xpath_t::scope_t& locals)
+{
+ const char * p = name.c_str();
+ switch (*p) {
+ case 'd':
+#if 0
+ if (name == "date_format") {
+ // jww (2007-04-18): What to do here?
+ return value_t(moment_t::output_format, true);
+ }
+#endif
+ break;
+
+ case 'n':
+ switch (*++p) {
+ case 'o':
+ if (name == "now")
+ return value_t(now);
+ break;
+ }
+ break;
+
+ case 'r':
+ if (name == "register_format")
+ return value_t(register_format, true);
+ break;
+ }
+ return xml::xpath_t::scope_t::resolve(name, locals);
+}
+#endif
+
+xml::xpath_t::ptr_op_t session_t::lookup(const string& name)
+{
+ const char * p = name.c_str();
+ switch (*p) {
+ case 'o':
+ if (std::strncmp(p, "option_", 7) == 0) {
+ p = p + 7;
+ switch (*p) {
+ case 'd':
+ if (std::strcmp(p, "debug_") == 0)
+ return MAKE_FUNCTOR(session_t::option_debug_);
+ break;
+
+ case 'f':
+ if ((*(p + 1) == '_' && ! *(p + 2)) ||
+ std::strcmp(p, "file_") == 0)
+ return MAKE_FUNCTOR(session_t::option_file_);
+ break;
+
+ case 't':
+ if (std::strcmp(p, "trace_") == 0)
+ return MAKE_FUNCTOR(session_t::option_trace_);
+ break;
+
+ case 'v':
+ if (! *(p + 1) || std::strcmp(p, "verbose") == 0)
+ return MAKE_FUNCTOR(session_t::option_verbose);
+ else if (std::strcmp(p, "verify") == 0)
+ return MAKE_FUNCTOR(session_t::option_verify);
+ break;
+ }
+ }
+ break;
+ }
+
+ return xml::xpath_t::symbol_scope_t::lookup(name);
+}
+
+// jww (2007-04-26): All of Ledger should be accessed through a
+// session_t object
+static void initialize()
+{
+ amount_t::initialize();
+ value_t::initialize();
+ xml::xpath_t::initialize();
+}
+
+static void shutdown()
+{
+ xml::xpath_t::shutdown();
+ value_t::shutdown();
+ amount_t::shutdown();
+}
+
+} // namespace ledger
diff --git a/src/driver/session.h b/src/driver/session.h
new file mode 100644
index 00000000..43d7d722
--- /dev/null
+++ b/src/driver/session.h
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2003-2007, 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.
+ */
+
+#ifndef _SESSION_H
+#define _SESSION_H
+
+#include "xpath.h"
+#include "journal.h"
+#include "parser.h"
+#include "abbrev.h"
+
+namespace ledger {
+
+class session_t : public xml::xpath_t::symbol_scope_t
+{
+ public:
+ static session_t * current;
+
+ path data_file;
+ optional<path> init_file;
+ optional<path> cache_file;
+ optional<path> price_db;
+
+ string register_format;
+ string wide_register_format;
+ string print_format;
+ string balance_format;
+ string equity_format;
+ string plot_amount_format;
+ string plot_total_format;
+ string write_hdr_format;
+ string write_xact_format;
+ string prices_format;
+ string pricesdb_format;
+
+ unsigned long pricing_leeway;
+
+ bool download_quotes;
+ bool use_cache;
+ bool cache_dirty;
+
+ moment_t now;
+
+ elision_style_t elision_style;
+
+ int abbrev_length;
+
+ bool ansi_codes;
+ bool ansi_invert;
+
+ ptr_list<journal_t> journals;
+ ptr_list<parser_t> parsers;
+
+ session_t();
+ virtual ~session_t() {
+ TRACE_DTOR(session_t);
+ }
+
+ journal_t * create_journal() {
+ journal_t * journal = new journal_t;
+ journals.push_back(journal);
+ return journal;
+ }
+ void close_journal(journal_t * journal) {
+ for (ptr_list<journal_t>::iterator i = journals.begin();
+ i != journals.end();
+ i++)
+ if (&*i == journal) {
+ journals.erase(i);
+ return;
+ }
+ assert(false);
+ checked_delete(journal);
+ }
+
+ std::size_t read_journal(std::istream& in,
+ const path& pathname,
+ xml::builder_t& builder);
+ std::size_t read_journal(const path& pathname,
+ xml::builder_t& builder);
+
+ void read_init();
+
+ std::size_t read_data(xml::builder_t& builder,
+ journal_t * journal,
+ const string& master_account = "");
+
+ void register_parser(parser_t * parser) {
+ parsers.push_back(parser);
+ }
+ void unregister_parser(parser_t * parser) {
+ for (ptr_list<parser_t>::iterator i = parsers.begin();
+ i != parsers.end();
+ i++)
+ if (&*i == parser) {
+ parsers.erase(i);
+ return;
+ }
+ assert(false);
+ checked_delete(parser);
+ }
+
+ //
+ // Scope members
+ //
+
+ virtual xml::xpath_t::ptr_op_t lookup(const string& name);
+
+ //
+ // Debug options
+ //
+
+ value_t option_trace_(xml::xpath_t::scope_t& locals) {
+ return NULL_VALUE;
+ }
+ value_t option_debug_(xml::xpath_t::scope_t& locals) {
+ return NULL_VALUE;
+ }
+
+ value_t option_verify(xml::xpath_t::scope_t&) {
+ return NULL_VALUE;
+ }
+ value_t option_verbose(xml::xpath_t::scope_t&) {
+#if defined(LOGGING_ON)
+ if (_log_level < LOG_INFO)
+ _log_level = LOG_INFO;
+#endif
+ return NULL_VALUE;
+ }
+
+ //
+ // Option handlers
+ //
+
+ value_t option_file_(xml::xpath_t::call_scope_t& args) {
+ assert(args.size() == 1);
+ data_file = args[0].as_string();
+ return NULL_VALUE;
+ }
+
+#if 0
+#if defined(USE_BOOST_PYTHON)
+ value_t option_import_(xml::xpath_t::call_scope_t& args) {
+ python_import(optarg);
+ return NULL_VALUE;
+ }
+ value_t option_import_stdin(xml::xpath_t::call_scope_t& args) {
+ python_eval(std::cin, PY_EVAL_MULTI);
+ return NULL_VALUE;
+ }
+#endif
+#endif
+};
+
+/**
+ * This sets the current session context, transferring all static
+ * globals to point at the data structures related to this session.
+ * Although Ledger itself is not thread-safe, by locking, switching
+ * session context, then unlocking after the operation is done,
+ * multiple threads can sequentially make use of the library. Thus, a
+ * session_t maintains all of the information relating to a single
+ * usage of the Ledger library.
+ */
+void set_session_context(session_t * session = NULL);
+
+} // namespace ledger
+
+#endif // _SESSION_H