diff --git a/src/commands/Command.cpp b/src/commands/Command.cpp index 562e5ea7b..daaf2b336 100644 --- a/src/commands/Command.cpp +++ b/src/commands/Command.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -326,6 +327,8 @@ void Command::filter (std::vector & output) } else { + safety (); + context.timer_filter.stop (); const std::vector & pending = context.tdb2.pending.get_tasks (); const std::vector & completed = context.tdb2.completed.get_tasks (); @@ -406,8 +409,11 @@ void Command::modify_task ( const A3& arguments, std::string& description) { + // Coalesce arguments together into sets to be processed as a batch. + A3 grouped_arguments = group_arguments (arguments); + std::vector ::const_iterator arg; - for (arg = arguments.begin (); arg != arguments.end (); ++arg) + for (arg = grouped_arguments.begin (); arg != grouped_arguments.end (); ++arg) { // Attributes are essentially name:value pairs, and correspond directly // to stored attributes. @@ -516,3 +522,39 @@ void Command::modify_task ( } //////////////////////////////////////////////////////////////////////////////// +// Disaster avoidance mechanism. +void Command::safety () +{ + if (! _read_only) + { + A3 write_filter = context.a3.extract_filter (); + if (!write_filter.size ()) // Potential disaster. + { + // If user is willing to be asked, this can be avoided. + if (context.config.getBoolean ("confirmation") && + confirm (STRING_TASK_SAFETY_VALVE)) + return; + + // No. + throw std::string (STRING_TASK_SAFETY_FAIL); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +A3 Command::group_arguments (const A3& input) +{ + A3 result; + + std::vector ::const_iterator arg; + for (arg = input.begin (); arg != input.end (); ++arg) + { + // TODO Create a grouped set of args. + + result.push_back (*arg); + } + + return result; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/commands/Command.h b/src/commands/Command.h index ed7abe7a4..d8c2acf24 100644 --- a/src/commands/Command.h +++ b/src/commands/Command.h @@ -63,6 +63,10 @@ protected: void modify_task_annotate (Task&, const A3&); void modify_task (Task&, const A3&, std::string&); + void safety (); + + A3 group_arguments (const A3&); + protected: std::string _keyword; std::string _usage; diff --git a/src/en-US.h b/src/en-US.h index a9312cd26..148937431 100644 --- a/src/en-US.h +++ b/src/en-US.h @@ -433,6 +433,8 @@ #define STRING_TASK_VALID_RECUR "The recurrence value '{1}' is not valid." #define STRING_TASK_VALID_WAIT_RECUR "You cannot create a task that is both waiting and recurring." #define STRING_TASK_VALID_PRIORITY "Priority values may be 'H', 'M' or 'L', not '{1}'." +#define STRING_TASK_SAFETY_VALVE "This command has no filter, and will modify all tasks. Are you sure?" +#define STRING_TASK_SAFETY_FAIL "Command prevented from running." // Taskmod #define STRING_TASKMOD_BAD_INIT "Taskmod::getUuid(): Task object not initialized." diff --git a/test/bug.annotate.t b/test/bug.annotate.t index 3b541d963..a5c4505f4 100755 --- a/test/bug.annotate.t +++ b/test/bug.annotate.t @@ -28,43 +28,36 @@ use strict; use warnings; -use Test::More tests => 9; +use Test::More tests => 5; # Create the rc file. -if (open my $fh, '>', 'bug_annotate.rc') +if (open my $fh, '>', 'bug.rc') { print $fh "data.location=.\n"; close $fh; - ok (-r 'bug_annotate.rc', 'Created bug_annotate.rc'); + ok (-r 'bug.rc', 'Created bug.rc'); } # Attempt a blank annotation. -qx{../src/task rc:bug_annotate.rc add foo}; -my $output = qx{../src/task rc:bug_annotate.rc 1 annotate}; +qx{../src/task rc:bug.rc add foo}; +my $output = qx{../src/task rc:bug.rc 1 annotate}; like ($output, qr/Additional text must be provided/, 'failed on blank annotation'); # Attempt an annotation without ID -$output = qx{../src/task rc:bug_annotate.rc annotate bar}; -like ($output, qr/ID needed to apply an annotation./, 'failed on annotation without ID'); +$output = qx{echo "-- n" | ../src/task rc:bug.rc annotate bar}; +like ($output, qr/Command prevented from running/, 'Filter-less write command inhibited'); + +$output = qx{echo "-- y" | ../src/task rc:bug.rc annotate bar}; +unlike ($output, qr/Command prevented from running/, 'Filter-less write command permitted'); # Cleanup. -unlink 'pending.data'; -ok (!-r 'pending.data', 'Removed pending.data'); - -unlink 'completed.data'; -ok (!-r 'completed.data', 'Removed completed.data'); - -unlink 'undo.data'; -ok (!-r 'undo.data', 'Removed undo.data'); - -unlink 'backlog.data'; -ok (!-r 'backlog.data', 'Removed backlog.data'); - -unlink 'synch.key'; -ok (!-r 'synch.key', 'Removed synch.key'); - -unlink 'bug_annotate.rc'; -ok (!-r 'bug_annotate.rc', 'Removed bug_annotate.rc'); +unlink qw(pending.data completed.data undo.data backlog.data synch.key bug.rc); +ok (! -r 'pending.data' && + ! -r 'completed.data' && + ! -r 'undo.data' && + ! -r 'backlog.data' && + ! -r 'synch_key.data' && + ! -r 'bug.rc', 'Cleanup'); exit 0;