diff --git a/src/Cmd.cpp b/src/Cmd.cpp index fd59d3473..93b382c96 100644 --- a/src/Cmd.cpp +++ b/src/Cmd.cpp @@ -134,7 +134,7 @@ void Cmd::loadCommands () commands.push_back (context.stringtable.get (CMD_OVERDUE, "overdue")); commands.push_back (context.stringtable.get (CMD_PROJECTS, "projects")); commands.push_back (context.stringtable.get (CMD_START, "start")); - commands.push_back (context.stringtable.get (CMD_STATS, "statistics")); + commands.push_back (context.stringtable.get (CMD_STATS, "stats")); commands.push_back (context.stringtable.get (CMD_STOP, "stop")); commands.push_back (context.stringtable.get (CMD_SUMMARY, "summary")); commands.push_back (context.stringtable.get (CMD_TAGS, "tags")); diff --git a/src/Context.cpp b/src/Context.cpp index dd91e13f7..b2c511b22 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -46,7 +46,8 @@ Context::Context () , task () , tdb () , stringtable () -, command ("") +, program ("") +, cmd () { // Set up randomness. #ifdef HAVE_SRANDOM @@ -68,8 +69,9 @@ Context::Context (const Context& other) task = other.task; tdb = other.tdb; stringtable = other.stringtable; + program = other.program; args = other.args; - command = other.command; + cmd = other.cmd; messages = other.messages; footnotes = other.footnotes; } @@ -88,8 +90,9 @@ Context& Context::operator= (const Context& other) task = other.task; tdb = other.tdb; stringtable = other.stringtable; + program = other.program; args = other.args; - command = other.command; + cmd = other.cmd; messages = other.messages; footnotes = other.footnotes; } @@ -107,7 +110,10 @@ void Context::initialize (int argc, char** argv) { // Capture the args. for (int i = 0; i < argc; ++i) - args.push_back (argv[i]); + if (i == 0) + program = argv[i]; + else + args.push_back (argv[i]); // Load the configuration file from the home directory. If the file cannot // be found, offer to create a sample one. @@ -148,12 +154,11 @@ int Context::run () std::cout << "--- start 1.8.0 ---" << std::endl; try { - parse (); - - // TODO Dispatch to command handlers. - // TODO Auto shadow update. - // TODO Auto gc. + parse (); // Parse command line. // TODO tdb.load (Filter); + dispatch (); // Dispatch to command handlers. + // TODO Auto gc. + shadow (); // Auto shadow update. } catch (const std::string& error) @@ -181,6 +186,86 @@ int Context::run () return 0; } +//////////////////////////////////////////////////////////////////////////////// +void Context::dispatch () +{ +/* + // If argc == 1 and there is a default.command, use it. Otherwise use + // argc/argv. + std::string defaultCommand = context.config.get ("default.command"); + if (args.size () == 0 || defaultCommand != "") + { + // Stuff the command line. + args.clear (); + split (args, defaultCommand, ' '); + std::cout << "[task " << defaultCommand << "]" << std::endl; + } + + loadCustomReports (); + + std::string command; + T task; + parse (args, command, task); + + bool gcMod = false; // Change occurred by way of gc. + bool cmdMod = false; // Change occurred by way of command type. + std::string out; + + // Read-only commands with no side effects. + if (command == "export") { out = handleExport (tdb, task); } + else if (command == "projects") { out = handleProjects (tdb, task); } + else if (command == "tags") { out = handleTags (tdb, task); } + else if (command == "info") { out = handleInfo (tdb, task); } + else if (command == "stats") { out = handleReportStats (tdb, task); } + else if (command == "history") { out = handleReportHistory (tdb, task); } + else if (command == "ghistory") { out = handleReportGHistory (tdb, task); } + else if (command == "calendar") { out = handleReportCalendar (tdb, task); } + else if (command == "summary") { out = handleReportSummary (tdb, task); } + else if (command == "timesheet") { out = handleReportTimesheet (tdb, task); } + else if (command == "colors") { out = handleColor ( ); } + else if (command == "version") { out = handleVersion ( ); } + else if (command == "help") { out = longUsage ( ); } + + // Commands that cause updates. + else if (command == "" && task.getId ()) { cmdMod = true; out = handleModify (tdb, task); } + else if (command == "add") { cmdMod = true; out = handleAdd (tdb, task); } + else if (command == "append") { cmdMod = true; out = handleAppend (tdb, task); } + else if (command == "annotate") { cmdMod = true; out = handleAnnotate (tdb, task); } + else if (command == "done") { cmdMod = true; out = handleDone (tdb, task); } + else if (command == "undelete") { cmdMod = true; out = handleUndelete (tdb, task); } + else if (command == "delete") { cmdMod = true; out = handleDelete (tdb, task); } + else if (command == "start") { cmdMod = true; out = handleStart (tdb, task); } + else if (command == "stop") { cmdMod = true; out = handleStop (tdb, task); } + else if (command == "undo") { cmdMod = true; out = handleUndo (tdb, task); } + else if (command == "import") { cmdMod = true; out = handleImport (tdb, task); } + else if (command == "duplicate") { cmdMod = true; out = handleDuplicate (tdb, task); } + else if (command == "edit") { cmdMod = true; out = handleEdit (tdb, task); } + + // Command that display IDs and therefore need TDB::gc first. + else if (command == "completed") { if (gc) gcMod = tdb.gc (); out = handleCompleted (tdb, task); } + else if (command == "next") { if (gc) gcMod = tdb.gc (); out = handleReportNext (tdb, task); } + else if (command == "active") { if (gc) gcMod = tdb.gc (); out = handleReportActive (tdb, task); } + else if (command == "overdue") { if (gc) gcMod = tdb.gc (); out = handleReportOverdue (tdb, task); } + else if (isCustomReport (command)) { if (gc) gcMod = tdb.gc (); out = handleCustomReport (tdb, task, command); } + + // If the command is not recognized, display usage. + else { out = shortUsage (); } + + // Only update the shadow file if such an update was not suppressed (shadow), + // and if an actual change occurred (gcMod || cmdMod). + if (shadow && (gcMod || cmdMod)) + updateShadowFile (tdb); + + return out; +*/ +} + +//////////////////////////////////////////////////////////////////////////////// +void Context::shadow () +{ + throw std::string ("unimplemented Context::shadow"); +} + //////////////////////////////////////////////////////////////////////////////// void Context::loadCorrectConfigFile () { @@ -237,7 +322,6 @@ void Context::loadCorrectConfigFile () //////////////////////////////////////////////////////////////////////////////// void Context::parse () { - command = ""; std::string descCandidate = ""; bool terminated = false; bool foundSequence = false; @@ -249,15 +333,18 @@ void Context::parse () { // The '--' argument shuts off all parsing - everything is an argument. if (*arg == "--") +{ +std::cout << "# parse terminator '" << *arg << "'" << std::endl; terminated = true; +} // Sequence // Note: "add" doesn't require an ID - else if (command != "add" && - sequence.valid (*arg) && - ! foundSomethingAfterSequence) + else if (cmd.command != "add" && + ! foundSomethingAfterSequence && + sequence.valid (*arg)) { - std::cout << "# found sequence" << std::endl; +std::cout << "# parse sequence '" << *arg << "'" << std::endl; sequence.parse (*arg); foundSequence = true; } @@ -309,30 +396,25 @@ void Context::parse () if (foundSequence) foundSomethingAfterSequence = true; - std::cout << "# found subst" << std::endl; +std::cout << "# parse subst '" << *arg << "'" << std::endl; subst.parse (*arg); } -/* - // Command. - else if (command == "") + + // It might be a command if one has not already been found. + else if (cmd.command == "" && + cmd.valid (*arg)) { +std::cout << "# parse cmd '" << *arg << "'" << std::endl; + cmd.parse (*arg); + if (foundSequence) foundSomethingAfterSequence = true; - - std::string l = lowerCase (arg); - if (isCommand (l) && validCommand (l)) - command = l; - else - { - if (descCandidate.length ()) - descCandidate += " "; - descCandidate += arg; - } } -*/ + // Anything else is just considered description. else { +std::cout << "# parse description '" << *arg << "'" << std::endl; if (foundSequence) foundSomethingAfterSequence = true; @@ -345,6 +427,7 @@ void Context::parse () // terminated, therefore everything subsequently is a description. else { +std::cout << "# parse post-termination description '" << *arg << "'" << std::endl; if (foundSequence) foundSomethingAfterSequence = true; diff --git a/src/Context.h b/src/Context.h index 26fdc1cbf..70ce60d23 100644 --- a/src/Context.h +++ b/src/Context.h @@ -32,6 +32,7 @@ #include "Config.h" #include "Sequence.h" #include "Subst.h" +#include "Cmd.h" #include "T2.h" #include "TDB2.h" #include "StringTable.h" @@ -47,6 +48,8 @@ public: void initialize (int, char**); // all startup int run (); // task classic int interactive (); // task interactive (not implemented) + void dispatch (); // command handler dispatch + void shadow (); // shadow file update void message (const std::string&); // Message sink void footnote (const std::string&); // Footnote sink @@ -64,8 +67,9 @@ public: T2 task; TDB2 tdb; StringTable stringtable; + std::string program; std::vector args; - std::string command; + Cmd cmd; private: std::vector messages; diff --git a/src/Sequence.cpp b/src/Sequence.cpp index 00d2c2608..9d6d457f6 100644 --- a/src/Sequence.cpp +++ b/src/Sequence.cpp @@ -68,6 +68,12 @@ bool Sequence::valid (const std::string& input) const if (range.size () < 1 || range.size () > 2) return false; + + if (range.size () == 1 && !validId (range[0])) + return false; + + if (range.size () == 2 && !validId (range[1])) + return false; } return true; diff --git a/src/sandbox/Makefile b/src/sandbox/Makefile index c7fccdd62..0ea2b37d7 100644 --- a/src/sandbox/Makefile +++ b/src/sandbox/Makefile @@ -5,7 +5,7 @@ LIBS = OBJECTS = main.o ../Context.o ../TDB2.o ../T2.o ../Sequence.o ../Filter.o \ ../Att.o ../Keymap.o ../Record.o ../StringTable.o ../Location.o \ ../util.o ../text.o ../Date.o ../Config.o ../Subst.o ../Nibbler.o \ - ../parse.o ../Duration.o ../T.o + ../parse.o ../Duration.o ../T.o ../Cmd.o all: $(PROJECT) diff --git a/src/task.cpp b/src/task.cpp index 8433b2a9b..ee57af755 100644 --- a/src/task.cpp +++ b/src/task.cpp @@ -304,7 +304,7 @@ int main (int argc, char** argv) try { context.initialize (argc, argv); - if (context.args[0].find ("itask") != std::string::npos) + if (context.program.find ("itask") != std::string::npos) status = context.interactive (); else status = context.run (); diff --git a/src/tests/seq.t.cpp b/src/tests/seq.t.cpp index b2225c396..dd50d5eb1 100644 --- a/src/tests/seq.t.cpp +++ b/src/tests/seq.t.cpp @@ -48,6 +48,8 @@ int main (int argc, char** argv) t.notok (seq.valid ("1--2"), "not valid 1--2"); t.notok (seq.valid ("1-2-3"), "not valid 1-2-3"); t.notok (seq.valid ("-1-2"), "not valid -1-2"); + t.notok (seq.valid ("1-two"), "not valid 1-two"); + t.notok (seq.valid ("one-2"), "not valid one-2"); t.ok (seq.valid ("1"), "valid 1"); t.ok (seq.valid ("1,3"), "valid 1,3"); @@ -55,8 +57,6 @@ int main (int argc, char** argv) t.ok (seq.valid ("1,3-5,7"), "valid 1,3-5,7"); t.ok (seq.valid ("1-1000"), "valid 1-1000"); t.ok (seq.valid ("1-1001"), "valid 1-1001"); - t.ok (seq.valid ("1-two"), "valid 1-two"); - t.ok (seq.valid ("one-2"), "valid one-2"); t.ok (seq.valid ("1-5,3-7"), "valid 1-5,3-7"); // 1