diff --git a/AUTHORS b/AUTHORS index 55267243a..0d57c48ab 100644 --- a/AUTHORS +++ b/AUTHORS @@ -20,6 +20,7 @@ Contributing Authors: P.C. Shyamshankar Johan Friis Steven de Brouwer + John Florian With thanks to: Eugene Kramer diff --git a/i18n/strings.en-US b/i18n/strings.en-US index 33f959d91..53c65764f 100644 --- a/i18n/strings.en-US +++ b/i18n/strings.en-US @@ -57,6 +57,7 @@ 226 undelete 227 undo 228 version +229 shell # 3xx Attributes - must be sequential 300 project diff --git a/src/Cmd.cpp b/src/Cmd.cpp index 4e34dd7ed..554b2b5e8 100644 --- a/src/Cmd.cpp +++ b/src/Cmd.cpp @@ -32,6 +32,7 @@ #include "util.h" #include "text.h" #include "i18n.h" +#include "main.h" extern Context context; @@ -124,6 +125,9 @@ void Cmd::load () commands.push_back (context.stringtable.get (CMD_INFO, "info")); commands.push_back (context.stringtable.get (CMD_NEXT, "next")); commands.push_back (context.stringtable.get (CMD_PROJECTS, "projects")); +#ifdef FEATURE_SHELL + commands.push_back (context.stringtable.get (CMD_SHELL, "shell")); +#endif commands.push_back (context.stringtable.get (CMD_START, "start")); commands.push_back (context.stringtable.get (CMD_STATS, "stats")); commands.push_back (context.stringtable.get (CMD_STOP, "stop")); @@ -207,6 +211,7 @@ bool Cmd::isReadOnlyCommand () command == context.stringtable.get (CMD_INFO, "info") || command == context.stringtable.get (CMD_NEXT, "next") || command == context.stringtable.get (CMD_PROJECTS, "projects") || + command == context.stringtable.get (CMD_SHELL, "shell") || command == context.stringtable.get (CMD_STATS, "stats") || command == context.stringtable.get (CMD_SUMMARY, "summary") || command == context.stringtable.get (CMD_TAGS, "tags") || diff --git a/src/Context.cpp b/src/Context.cpp index 0168e0f9f..0fd1a9908 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -67,13 +67,6 @@ Context::~Context () //////////////////////////////////////////////////////////////////////////////// void Context::initialize (int argc, char** argv) { - // Set up randomness. -#ifdef HAVE_SRANDOM - srandom (time (NULL)); -#else - srand (time (NULL)); -#endif - // Capture the args. for (int i = 0; i < argc; ++i) if (i == 0) @@ -87,6 +80,13 @@ void Context::initialize (int argc, char** argv) //////////////////////////////////////////////////////////////////////////////// void Context::initialize () { + // Set up randomness. +#ifdef HAVE_SRANDOM + srandom (time (NULL)); +#else + srand (time (NULL)); +#endif + // Load the configuration file from the home directory. If the file cannot // be found, offer to create a sample one. loadCorrectConfigFile (); @@ -205,6 +205,9 @@ std::string Context::dispatch () else if (cmd.command == "import") { out = handleImport (); } else if (cmd.command == "duplicate") { out = handleDuplicate (); } else if (cmd.command == "edit") { out = handleEdit (); } +#ifdef FEATURE_SHELL + else if (cmd.command == "shell") { handleShell (); } +#endif else if (cmd.command == "" && sequence.size ()) { out = handleModify (); } @@ -533,6 +536,28 @@ void Context::parse ( } } +//////////////////////////////////////////////////////////////////////////////// +void Context::clear () +{ +// Config config; + filter.clear (); +// Keymap keymap; + sequence.clear (); + subst.clear (); +// task.clear (); + task = Task (); + tdb.clear (); +// stringtable.clear (); + program = ""; + args.clear (); + cmd.command = ""; + tagAdditions.clear (); + tagRemovals.clear (); + + clearMessages (); + inShadow = false; +} + //////////////////////////////////////////////////////////////////////////////// // Add all the attributes in the task to the filter. All except uuid. void Context::autoFilter (Task& t, Filter& f) @@ -633,6 +658,7 @@ void Context::clearMessages () headers.clear (); messages.clear (); footnotes.clear (); + debugMessages.clear (); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/Context.h b/src/Context.h index 4e35f5259..621df030f 100644 --- a/src/Context.h +++ b/src/Context.h @@ -60,6 +60,7 @@ public: void parse (); void parse (std::vector &, Cmd&, Task&, Sequence&, Subst&, Filter&); + void clear (); private: void loadCorrectConfigFile (); diff --git a/src/Subst.cpp b/src/Subst.cpp index 4a966b084..2dd160185 100644 --- a/src/Subst.cpp +++ b/src/Subst.cpp @@ -168,3 +168,11 @@ void Subst::apply ( } //////////////////////////////////////////////////////////////////////////////// +void Subst::clear () +{ + mFrom = ""; + mTo = ""; + mGlobal = false; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/Subst.h b/src/Subst.h index 445bddf17..ef0f0eb53 100644 --- a/src/Subst.h +++ b/src/Subst.h @@ -42,6 +42,7 @@ public: bool valid (const std::string&) const; void parse (const std::string&); void apply (std::string&, std::vector &) const; + void clear (); public: std::string mFrom; diff --git a/src/TDB.cpp b/src/TDB.cpp index 923d6d3c6..ec4772276 100644 --- a/src/TDB.cpp +++ b/src/TDB.cpp @@ -85,7 +85,6 @@ TDB::~TDB () //////////////////////////////////////////////////////////////////////////////// void TDB::clear () { - mPending.clear (); mLocations.clear (); mLock = true; @@ -93,6 +92,10 @@ void TDB::clear () unlock (); mAllOpenAndLocked = false; + mId = 1; + mPending.clear (); + mNew.clear (); + mModified.clear (); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/command.cpp b/src/command.cpp index 99a410ff0..05cb3fd25 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -38,6 +38,7 @@ #include "text.h" #include "util.h" #include "main.h" +#include "../auto.h" #ifdef HAVE_LIBNCURSES #include @@ -394,6 +395,9 @@ std::string handleVersion () "defaultwidth displayweeknumber due echo.command locale locking " "monthsperline nag next project shadow.command shadow.file shadow.notify " "weekstart editor import.synonym.id import.synonym.uuid " +#ifdef FEATURE_SHELL + "shell.prompt " +#endif "import.synonym.status import.synonym.tags import.synonym.entry " "import.synonym.start import.synonym.due import.synonym.recur " "import.synonym.end import.synonym.project import.synonym.priority " @@ -956,6 +960,79 @@ std::string handleDuplicate () return out.str (); } +//////////////////////////////////////////////////////////////////////////////// +#ifdef FEATURE_SHELL +void handleShell () +{ + // Display some kind of welcome message. + std::cout << ((context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false)) + ? Text::colorize (Text::bold, Text::nocolor, PACKAGE) + : PACKAGE) + << " shell" + << std::endl + << std::endl + << "Enter any task command (such as 'list'), or hit 'Enter'." + << std::endl + << "There is no need to include the 'task' command itself." + << std::endl + << std::endl; + + // Preserve any special override arguments, and reapply them for each + // shell command. + std::vector special; + foreach (arg, context.args) + if (arg->substr (0, 3) == "rc." || + arg->substr (0, 3) == "rc:") + special.push_back (*arg); + + std::string quit = "quit"; // TODO i18n + std::string command; + bool keepGoing = true; + + do + { + std::cout << context.config.get ("shell.prompt", "task>") << " "; + + command = ""; + std::getline (std::cin, command); + command = lowerCase (trim (command)); + + if (command.length () > 0 && + command.length () <= quit.length () && + command == quit.substr (0, command.length ())) + { + keepGoing = false; + } + else + { + try + { + context.clear (); + + std::vector args; + split (args, command, ' '); + foreach (arg, special) context.args.push_back (*arg); + foreach (arg, args) context.args.push_back (*arg); + + context.initialize (); + context.run (); + } + + catch (std::string& error) + { + std::cout << error << std::endl; + } + + catch (...) + { + std::cerr << context.stringtable.get (100, "Unknown error.") << std::endl; + } + } + } + while (keepGoing); +} +#endif + //////////////////////////////////////////////////////////////////////////////// std::string handleColor () { diff --git a/src/i18n.h b/src/i18n.h index 6e1687aa7..266998274 100644 --- a/src/i18n.h +++ b/src/i18n.h @@ -93,6 +93,7 @@ #define CMD_UNDELETE 226 #define CMD_UNDO 227 #define CMD_VERSION 228 +#define CMD_SHELL 229 // 3xx Attributes #define ATT_PROJECT 300 diff --git a/src/main.h b/src/main.h index 4e6acc338..deb4ec7a0 100644 --- a/src/main.h +++ b/src/main.h @@ -27,6 +27,7 @@ #define FEATURE_TDB_OPT 1 // TDB Optimization reduces file I/O. #define FEATURE_NEW_ID 1 // Echoes back new id. +#define FEATURE_SHELL 1 // Interactive shell. #include #include @@ -72,6 +73,9 @@ std::string handleUndo (); std::string handleColor (); std::string handleAnnotate (); std::string handleDuplicate (); +#ifdef FEATURE_SHELL +void handleShell (); +#endif int deltaAppend (Task&); int deltaDescription (Task&); int deltaTags (Task&); diff --git a/src/report.cpp b/src/report.cpp index 2aebc16cf..20592718e 100644 --- a/src/report.cpp +++ b/src/report.cpp @@ -99,6 +99,12 @@ std::string shortUsage () table.addCell (row, 1, "task edit ID"); table.addCell (row, 2, "Launches an editor to let you modify all aspects of a task directly, therefore it is to be used carefully."); +#ifdef FEATURE_SHELL + row = table.addRow (); + table.addCell (row, 1, "task shell"); + table.addCell (row, 2, "Launches an interactive shell."); +#endif + row = table.addRow (); table.addCell (row, 1, "task duplicate ID [tags] [attrs] [desc...]"); table.addCell (row, 2, "Duplicates the specified task, and allows modifications."); diff --git a/src/util.cpp b/src/util.cpp index 96ddbc7d9..e6af2c775 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -64,7 +64,6 @@ bool confirm (const std::string& question) std::getline (std::cin, answer); answer = lowerCase (trim (answer)); - if (answer == "\n") std::cout << "newline\n"; // TODO i18n } while (answer != "y" && // TODO i18n answer != "ye" && // TODO i18n