summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2009-02-04 18:23:18 -0400
committerJohn Wiegley <johnw@newartisans.com>2009-02-04 19:55:08 -0400
commit73cf3b01fbd50c3a8a4fd96ff69643c28394d8fe (patch)
tree695fde78e03351750210715ea76ec686ff04fbfc /src
parentb9603a1512acdfeb5d304e5ae910c1da553b3337 (diff)
downloadfork-ledger-73cf3b01fbd50c3a8a4fd96ff69643c28394d8fe.tar.gz
fork-ledger-73cf3b01fbd50c3a8a4fd96ff69643c28394d8fe.tar.bz2
fork-ledger-73cf3b01fbd50c3a8a4fd96ff69643c28394d8fe.zip
Added structural support in main() for using a REPL.
Diffstat (limited to 'src')
-rw-r--r--src/main.cc261
-rw-r--r--src/option.cc35
-rw-r--r--src/option.h3
-rw-r--r--src/system.hh4
-rw-r--r--src/work.cc13
-rw-r--r--src/work.h3
6 files changed, 218 insertions, 101 deletions
diff --git a/src/main.cc b/src/main.cc
index b5b65da9..3cdc6cf4 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -33,43 +33,60 @@
#include "work.h" // This is where the meat of main() is, which
// was moved there for the sake of clarity
+using namespace ledger;
-int main(int argc, char * argv[], char * envp[])
-{
- using namespace ledger;
+namespace {
+ char * stripwhite (char * string)
+ {
+ if (! string)
+ return NULL;
- session_t * session = NULL;
- report_t * report = NULL;
- int status = 1;
- try {
- // The very first thing we do is handle some very special command-line
- // options, since they affect how the 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() initialize_memory_tracing();
+ register char *s, *t;
+
+ for (s = string; isspace (*s); s++)
+ ;
+
+ if (*s == 0)
+ return (s);
+
+ t = s + strlen (s) - 1;
+ while (t > s && isspace (*t))
+ t--;
+ *++t = '\0';
- INFO("Ledger starting");
+ return s;
+ }
+
+ strings_list split_arguments(char * line)
+ {
+ strings_list args;
- // Initialize global Boost/C++ environment
- std::ios::sync_with_stdio(false);
- filesystem::path::default_name_check(filesystem::portable_posix_name);
+ // jww (2009-02-04): This is too naive
+ for (char * p = std::strtok(line, " \t");
+ p;
+ p = std::strtok(NULL, " \t"))
+ args.push_back(p);
- // Create the session object, which maintains nearly all state relating to
- // this invocation of Ledger; and register all known journal parsers.
- session = new LEDGER_SESSION_T;
- set_session_context(session);
+ return args;
+ }
+ /**
+ * @return \c true if a command was actually executed; otherwise, it probably
+ * just resulted in setting some options.
+ */
+ bool execute_command(session_t& session,
+ ledger::strings_list args,
+ char ** envp = NULL)
+ {
// Create the report object, which maintains state relating to each
// command invocation. Because we're running from main(), the distinction
// between session and report doesn't really matter, but if a GUI were
// calling into Ledger it would have one session object per open document,
// with a separate report_t object for each report it generated.
- report = new report_t(*session);
- session->global_scope = report;
+ std::auto_ptr<report_t> manager(new report_t(session));
+ report_t& report(*manager.get());
+
+ session.global_scope = &report;
// Read the user's options, in the following order:
//
@@ -80,10 +97,13 @@ int main(int argc, char * argv[], char * envp[])
// Before processing command-line options, we must notify the session
// object that such options are beginning, since options like -f cause a
// complete override of files found anywhere else.
- read_environment_settings(*report, envp);
- session->read_init();
- session->now_at_command_line(true);
- strings_list args = read_command_line_arguments(*report, argc, argv);
+ if (envp) {
+ session.now_at_command_line(false);
+ read_environment_settings(report, envp);
+ session.read_init();
+ }
+ session.now_at_command_line(true);
+ args = read_command_arguments(report, args);
// Look for a precommand first, which is defined as any defined function
// whose name starts with "ledger_precmd_". The difference between a
@@ -98,56 +118,151 @@ int main(int argc, char * argv[], char * envp[])
//
// If such a command is found, create the output stream for the result and
// then invoke the command.
- string_iterator arg = args.begin();
- string verb = *arg++;
-
- if (function_t command = look_for_precommand(*report, verb)) {
- // 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());
- }
- else if (function_t command = look_for_command(*report, verb)) {
- // This is regular command verb, so parse the user's data.
- if (journal_t * journal = read_journal_files(*session, report->account)) {
- normalize_report_options(*report, verb); // jww (2009-02-02): a hack
-
- // 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());
-
- // Write out a binary cache of the journal data, if needful and
- // appropriate to do so.
- write_binary_cache(*session, journal);
+
+ if (args.empty()) {
+ read_journal_files(session, report.account);
+ return false;
+ } else {
+ string_iterator arg = args.begin();
+ string verb = *arg++;
+
+ if (function_t command = look_for_precommand(report, verb)) {
+ // 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());
}
- }
- else {
- throw_(std::logic_error, "Unrecognized command '" << verb << "'");
- }
+ else if (function_t command = look_for_command(report, verb)) {
+ // This is regular command verb, so parse the user's data if we
+ // haven't already at the beginning of the REPL.
+ if (! envp || read_journal_files(session, report.account)) {
+ normalize_report_options(report, verb); // jww (2009-02-02): a hack
+
+ // 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());
+ }
+ }
+ else {
+ throw_(std::logic_error, "Unrecognized command '" << verb << "'");
+ }
+
+ session.global_scope = NULL;
- // If we've reached this point, everything succeeded fine. Ledger uses
- // exceptions to notify of error conditions, so if you're using gdb, just
- // type "catch throw" to find the source point of any error.
- status = 0;
+ return true;
+ }
}
- catch (const std::exception& err) {
- std::cout.flush(); // first display anything that was pending
- // Display any pending error context information
- string context = error_context();
- if (! context.empty())
- std::cerr << context << std::endl;
+ int execute_command_wrapper(session_t& session,
+ ledger::strings_list args,
+ char ** envp = NULL)
+ {
+ int status = 1;
+
+ try {
+ if (! execute_command(session, args, envp))
+ return -1;
+
+ // If we've reached this point, everything succeeded fine. Ledger uses
+ // exceptions to notify of error conditions, so if you're using gdb,
+ // just type "catch throw" to find the source point of any error.
+ status = 0;
+ }
+ catch (const std::exception& err) {
+ std::cout.flush(); // first display anything that was pending
+
+ // Display any pending error context information
+ string context = error_context();
+ if (! context.empty())
+ std::cerr << context << std::endl;
- std::cerr << "Error: " << err.what() << std::endl;
- }
- catch (int _status) {
- status = _status; // used for a "quick" exit, and is used only
+ std::cerr << "Error: " << err.what() << std::endl;
+ }
+ catch (int _status) {
+ status = _status; // used for a "quick" exit, and is used only
// if help text (such as --help) was displayed
+ }
+ return status;
}
+}
+
+int main(int argc, char * argv[], char * envp[])
+{
+ session_t * session = NULL;
+
+ // The very first thing we do is handle some very special command-line
+ // options, since they affect how the 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() initialize_memory_tracing();
+
+ INFO("Ledger starting");
+
+ // Initialize global Boost/C++ environment
+ std::ios::sync_with_stdio(false);
+ filesystem::path::default_name_check(filesystem::portable_posix_name);
+
+ // Create the session object, which maintains nearly all state relating to
+ // this invocation of Ledger; and register all known journal parsers.
+ session = new LEDGER_SESSION_T;
+ set_session_context(session);
+
+ strings_list cmd_args;
+ for (int i = 1; i < argc; i++)
+ cmd_args.push_back(argv[i]);
+
+ int status = execute_command_wrapper(*session, cmd_args, envp);
+ if (status == -1) { // no command was given; enter the REPL
+ session->option_version(*session);
+
+#ifdef HAVE_LIBEDIT
+
+ rl_readline_name = "Ledger";
+#if 0
+ rl_attempted_completion_function = ledger_completion;
+#endif
- // Close the output stream, waiting on the pager process to exit if need be
- report->output_stream.close();
+ while (char * line = stripwhite(readline("==> "))) {
+ char * expansion;
+ int result;
+
+ result = history_expand(line, &expansion);
+
+ if (result < 0 || result == 2) {
+ throw_(std::logic_error,
+ "Failed to expand history reference '" << line << "'");
+ } else {
+ add_history(expansion);
+
+ strings_list line_argv = split_arguments(line);
+ execute_command_wrapper(*session, line_argv);
+ }
+ std::free(expansion);
+
+ std::free(line);
+ }
+
+#else // HAVE_LIBEDIT
+
+ while (! std::cin.eof()) {
+ std::cout << "--> ";
+ char line[1024];
+ std::cin.getline(line, 1023);
+
+ char * p = stripwhite(line);
+ if (*p)
+ execute_command_wrapper(*session, split_arguments(line));
+ }
+
+#endif // HAVE_LIBEDIT
+
+ status = 0; // report success
+ }
// If memory verification is being performed (which can be very slow), clean
// up everything by closing the session and deleting the session object, and
@@ -157,8 +272,6 @@ int main(int argc, char * argv[], char * envp[])
set_session_context(NULL);
if (session != NULL)
checked_delete(session);
- if (report != NULL)
- checked_delete(report);
INFO("Ledger ended (Boost/libstdc++ may still hold memory)");
shutdown_memory_tracing();
diff --git a/src/option.cc b/src/option.cc
index a0891c4c..7f8bdeb4 100644
--- a/src/option.cc
+++ b/src/option.cc
@@ -137,17 +137,20 @@ void process_environment(const char ** envp, const string& tag,
}
}
-void process_arguments(int, char ** argv, scope_t& scope,
- std::list<string>& args)
+strings_list process_arguments(strings_list args, scope_t& scope)
{
bool anywhere = true;
- for (char ** i = argv; *i; i++) {
+ strings_list remaining;
+
+ for (strings_list::iterator i = args.begin();
+ i != args.end();
+ i++) {
DEBUG("option.args", "Examining argument '" << *i << "'");
if (! anywhere || (*i)[0] != '-') {
DEBUG("option.args", " adding to list of real args");
- args.push_back(*i);
+ remaining.push_back(*i);
continue;
}
@@ -161,20 +164,24 @@ void process_arguments(int, char ** argv, scope_t& scope,
DEBUG("option.args", " it's an option string");
- char * name = *i + 2;
- char * value = NULL;
- if (char * p = std::strchr(name, '=')) {
- *p++ = '\0';
- value = p;
+ string opt_name;
+ const char * name = (*i).c_str() + 2;
+ const char * value = NULL;
+
+ if (const char * p = std::strchr(name, '=')) {
+ opt_name = string(name, p - name);
+ value = ++p;
DEBUG("option.args", " read option value from option: " << value);
+ } else {
+ opt_name = name;
}
- op_bool_tuple opt(find_option(scope, name));
+ op_bool_tuple opt(find_option(scope, opt_name));
if (! opt.get<0>())
throw_(option_error, "illegal option --" << name);
if (opt.get<1>() && value == NULL) {
- value = *++i;
+ value = (*++i).c_str();
DEBUG("option.args", " read option value from arg: " << value);
if (value == NULL)
throw_(option_error, "missing option argument for --" << name);
@@ -203,9 +210,9 @@ void process_arguments(int, char ** argv, scope_t& scope,
}
foreach (op_bool_char_tuple& o, option_queue) {
- char * value = NULL;
+ const char * value = NULL;
if (o.get<1>()) {
- value = *++i;
+ value = (*++i).c_str();
DEBUG("option.args", " read option value from arg: " << value);
if (value == NULL)
throw_(option_error,
@@ -216,6 +223,8 @@ void process_arguments(int, char ** argv, scope_t& scope,
}
}
}
+
+ return remaining;
}
} // namespace ledger
diff --git a/src/option.h b/src/option.h
index acba035c..14b85c50 100644
--- a/src/option.h
+++ b/src/option.h
@@ -56,8 +56,7 @@ void process_option(const string& name, scope_t& scope,
void process_environment(const char ** envp, const string& tag,
scope_t& scope);
-void process_arguments(int argc, char ** argv, scope_t& scope,
- std::list<string>& args);
+strings_list process_arguments(strings_list args, scope_t& scope);
DECLARE_EXCEPTION(option_error, std::runtime_error);
diff --git a/src/system.hh b/src/system.hh
index 30a1e8d0..f70ee57b 100644
--- a/src/system.hh
+++ b/src/system.hh
@@ -138,6 +138,10 @@ typedef std::ostream::pos_type ostream_pos_type;
#include <mpfr.h>
#include "sha1.h"
+#ifdef HAVE_LIBEDIT
+#include <editline/readline.h>
+#endif
+
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/any.hpp>
diff --git a/src/work.cc b/src/work.cc
index a54b1352..9bd75c9c 100644
--- a/src/work.cc
+++ b/src/work.cc
@@ -97,22 +97,15 @@ void read_environment_settings(report_t& report, char * envp[])
TRACE_FINISH(environment, 1);
}
-strings_list
-read_command_line_arguments(report_t& report, int argc, char * argv[])
+strings_list read_command_arguments(report_t& report, strings_list args)
{
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);
- }
+ strings_list remaining = process_arguments(args, report);
TRACE_FINISH(arguments, 1);
- return args;
+ return remaining;
}
void normalize_session_options(session_t& session)
diff --git a/src/work.h b/src/work.h
index 607e46eb..935db18f 100644
--- a/src/work.h
+++ b/src/work.h
@@ -45,8 +45,7 @@ typedef std::pair<string_iterator, string_iterator> string_iterator_pair;
void handle_debug_options(int argc, char * argv[]);
void read_environment_settings(report_t& report, char * envp[]);
-strings_list read_command_line_arguments(report_t& report,
- int argc, char * argv[]);
+strings_list read_command_arguments(report_t& report, strings_list args);
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, const string& account);