mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-06-26 10:54:26 +02:00
Bug 879 - Description/Annotation ending with Slash Causes Problems
- Backslashes actually. The escaping mechanism in the low-level parser was eating leading \ characters when it should not. Very hard bug to find, trivial to fix. - Added unit tests to several components while narrowing this down.
This commit is contained in:
parent
7a2bf28005
commit
ec96d929a0
10 changed files with 135 additions and 41 deletions
|
@ -228,6 +228,8 @@
|
|||
+ Fixed bug #860, which prevented lower-case priority values from being
|
||||
accepted (thanks to Michelle Crane).
|
||||
+ Fixed bug #862, which suppressed feedback from the 'denotate' command.
|
||||
+ Fixed bug #879, which mis-parsed escaped characters in the data file (thanks
|
||||
to Michelle Crane).
|
||||
+ Fixed bug #892, which caused a segfault due to misuse of
|
||||
std::map::operator[] (thanks to Dmitriy Samborskiy).
|
||||
+ Fixed bug #897, which adds the UUID field to the 'completed' report
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#define L10N // Localization complete.
|
||||
|
||||
#include <fstream>
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#define L10N // Localization complete.
|
||||
|
||||
#include <text.h>
|
||||
|
@ -410,10 +409,10 @@ std::string json::encode (const std::string& input)
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TODO Pointers might speed this up.
|
||||
std::string json::decode (const std::string& input)
|
||||
{
|
||||
std::string output;
|
||||
|
||||
for (unsigned int i = 0; i < input.length (); ++i)
|
||||
{
|
||||
if (input[i] == '\\')
|
||||
|
@ -437,7 +436,7 @@ std::string json::decode (const std::string& input)
|
|||
i += 3;
|
||||
break;
|
||||
|
||||
// If it is an unrecognized seqeence, do nothing.
|
||||
// If it is an unrecognized sequence, do nothing.
|
||||
default:
|
||||
output += '\\';
|
||||
output += input[i];
|
||||
|
|
|
@ -234,6 +234,7 @@ bool Nibbler::getQuoted (
|
|||
{
|
||||
bool inquote = false;
|
||||
bool inescape = false;
|
||||
char previous = 0;
|
||||
char current = 0;
|
||||
result = "";
|
||||
|
||||
|
@ -250,6 +251,7 @@ bool Nibbler::getQuoted (
|
|||
if (current == '\\' && !inescape)
|
||||
{
|
||||
inescape = true;
|
||||
previous = current;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -270,6 +272,12 @@ bool Nibbler::getQuoted (
|
|||
}
|
||||
else
|
||||
{
|
||||
if (previous)
|
||||
{
|
||||
result += previous;
|
||||
previous = 0;
|
||||
}
|
||||
|
||||
result += current;
|
||||
inescape = false;
|
||||
}
|
||||
|
|
|
@ -309,7 +309,7 @@ void Task::parse (const std::string& input)
|
|||
nl.skip (':') &&
|
||||
nl.getQuoted ('"', value))
|
||||
{
|
||||
// Experimental legacy value translation.
|
||||
// Experimental legacy value translation of 'recur:m' --> 'recur:mo'.
|
||||
if (name == "recur" &&
|
||||
digitsOnly (value.substr (0, value.length () - 1)) &&
|
||||
value[value.length () - 1] == 'm')
|
||||
|
|
|
@ -492,11 +492,9 @@ unsigned burndown_size (unsigned ntasks)
|
|||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Encode values prior to serialization.
|
||||
// \t -> &tab;
|
||||
// " -> &dquot;
|
||||
// [ -> &open;
|
||||
// ] -> &close;
|
||||
// \ -> \\ (extra chars to disambiguate multi-line comment)
|
||||
const std::string encode (const std::string& value)
|
||||
{
|
||||
std::string modified = value;
|
||||
|
@ -510,9 +508,8 @@ const std::string encode (const std::string& value)
|
|||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Decode values after parse.
|
||||
// \t <- &tab;
|
||||
// " <- " or &dquot;
|
||||
// ' <- &squot;
|
||||
// " <- &dquot;
|
||||
// ' <- &squot; or "
|
||||
// , <- ,
|
||||
// [ <- &open;
|
||||
// ] <- &close;
|
||||
|
|
67
test/bug.879.t
Executable file
67
test/bug.879.t
Executable file
|
@ -0,0 +1,67 @@
|
|||
#! /usr/bin/perl
|
||||
################################################################################
|
||||
## taskwarrior - a command line task list manager.
|
||||
##
|
||||
## Copyright 2006-2012, Paul Beckingham, Federico Hernandez.
|
||||
##
|
||||
## Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
## of this software and associated documentation files (the "Software"), to deal
|
||||
## in the Software without restriction, including without limitation the rights
|
||||
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
## copies of the Software, and to permit persons to whom the Software is
|
||||
## furnished to do so, subject to the following conditions:
|
||||
##
|
||||
## The above copyright notice and this permission notice shall be included
|
||||
## in all copies or substantial portions of the Software.
|
||||
##
|
||||
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
## THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
## SOFTWARE.
|
||||
##
|
||||
## http://www.opensource.org/licenses/mit-license.php
|
||||
##
|
||||
################################################################################
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Test::More tests => 8;
|
||||
|
||||
# Create the rc file.
|
||||
if (open my $fh, '>', 'bug.rc')
|
||||
{
|
||||
print $fh "data.location=.\n";
|
||||
close $fh;
|
||||
ok (-r 'bug.rc', 'Created bug.rc');
|
||||
}
|
||||
|
||||
# Bug 879: Backslash at end of description/annotation causes problems.
|
||||
qx{../src/task rc:bug.rc add one\\\\};
|
||||
my $output = qx{../src/task rc:bug.rc list};
|
||||
like ($output, qr/one\\/, 'Backslash preserved in description');
|
||||
|
||||
qx{../src/task rc:bug.rc 1 annotate foo\\\\};
|
||||
$output = qx{../src/task rc:bug.rc list};
|
||||
like ($output, qr/one\\/, 'Backslash preserved in description');
|
||||
like ($output, qr/foo\\/, 'Backslash preserved in annotation 1');
|
||||
|
||||
qx{../src/task rc:bug.rc 1 annotate bar\\\\};
|
||||
$output = qx{../src/task rc:bug.rc list};
|
||||
like ($output, qr/one\\/, 'Backslash preserved in description');
|
||||
like ($output, qr/foo\\/, 'Backslash preserved in annotation 1');
|
||||
like ($output, qr/bar\\/, 'Backslash preserved in annotation 2');
|
||||
|
||||
# Cleanup.
|
||||
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' &&
|
||||
! -r 'bug.rc', 'Cleanup');
|
||||
|
||||
exit 0;
|
||||
|
|
@ -104,7 +104,7 @@ const char *negative_tests[] =
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
int main (int argc, char** argv)
|
||||
{
|
||||
UnitTest t (NUM_POSITIVE_TESTS + NUM_NEGATIVE_TESTS + 14);
|
||||
UnitTest t (NUM_POSITIVE_TESTS + NUM_NEGATIVE_TESTS + 22);
|
||||
|
||||
// Positive tests.
|
||||
for (int i = 0; i < NUM_POSITIVE_TESTS; ++i)
|
||||
|
@ -161,6 +161,17 @@ int main (int argc, char** argv)
|
|||
|
||||
t.is (json::encode ("1€2"), "1€2", "json::encode € -> €");
|
||||
t.is (json::decode ("1\\u20ac2"), "1€2", "json::decode \\u20ac -> €");
|
||||
|
||||
std::string encoded = json::encode ("one\\");
|
||||
t.is (encoded, "one\\\\", "json::encode one\\\\ -> one\\\\\\\\");
|
||||
t.is ((int)encoded.length (), 5, "json::encode one\\\\ -> length 5");
|
||||
t.is (encoded[0], 'o', "json::encode one\\\\[0] -> o");
|
||||
t.is (encoded[1], 'n', "json::encode one\\\\[1] -> n");
|
||||
t.is (encoded[2], 'e', "json::encode one\\\\[2] -> e");
|
||||
t.is (encoded[3], '\\', "json::encode one\\\\[3] -> \\");
|
||||
t.is (encoded[4], '\\', "json::encode one\\\\[4] -> \\");
|
||||
|
||||
t.is (json::decode (encoded), "one\\", "json::decode one\\\\\\\\ -> one\\\\");
|
||||
}
|
||||
|
||||
catch (std::string& e) {t.diag (e);}
|
||||
|
|
|
@ -39,15 +39,15 @@ int main (int argc, char** argv)
|
|||
{
|
||||
#ifdef NIBBLER_FEATURE_DATE
|
||||
#ifdef NIBBLER_FEATURE_REGEX
|
||||
UnitTest t (292);
|
||||
UnitTest t (296);
|
||||
#else
|
||||
UnitTest t (268);
|
||||
UnitTest t (272);
|
||||
#endif
|
||||
#else
|
||||
#ifdef NIBBLER_FEATURE_REGEX
|
||||
UnitTest t (242);
|
||||
UnitTest t (246);
|
||||
#else
|
||||
UnitTest t (218);
|
||||
UnitTest t (222);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
@ -210,36 +210,44 @@ int main (int argc, char** argv)
|
|||
|
||||
n = Nibbler ("'\"'");
|
||||
t.ok (n.getQuoted ('\'', s), " ''\"'' : getQuoted (''') -> true");
|
||||
t.is (s, "\"", " ''\"'' : getQuoted (''') -> '\"'"); // 81
|
||||
t.is (s, "\"", " ''\"'' : getQuoted (''') -> '\"'");
|
||||
|
||||
n = Nibbler ("'x'");
|
||||
t.ok (n.getQuoted ('\'', s), " ''x'' : getQuoted (''') -> true");
|
||||
t.is (s, "x", " ''x'' : getQuoted (''') -> ''"); // 83
|
||||
t.is (s, "x", " ''x'' : getQuoted (''') -> ''");
|
||||
|
||||
n = Nibbler ("'x");
|
||||
t.notok (n.getQuoted ('\'', s), " ''x' : getQuoted (''') -> false");
|
||||
|
||||
n = Nibbler ("x");
|
||||
t.notok (n.getQuoted ('\'', s), " 'x' : getQuoted (''') -> false");
|
||||
t.notok (n.getQuoted ('\'', s), " 'x' : getQuoted (''') -> false"); // 90
|
||||
|
||||
n = Nibbler ("\"one\\\"two\"");
|
||||
t.notok (n.getQuoted ('\'', s), "\"one\\\"two\" : getQuoted (''') -> false"); // 86
|
||||
t.notok (n.getQuoted ('\'', s), "\"one\\\"two\" : getQuoted (''') -> false");
|
||||
|
||||
n = Nibbler ("\"one\\\"two\"");
|
||||
t.ok (n.getQuoted ('"', s, false), "\"one\\\"two\" : getQuoted ('\"', false, false) -> true"); // 87
|
||||
t.is (s, "one\"two", "getQuoted ('\"', false) -> one\"two"); // 88
|
||||
t.ok (n.getQuoted ('"', s, false), "\"one\\\"two\" : getQuoted ('\"', false, false) -> true");
|
||||
t.is (s, "one\\\"two", "getQuoted ('\"', false) -> one\\\"two");
|
||||
|
||||
n = Nibbler ("\"one\\\"two\"");
|
||||
t.ok (n.getQuoted ('"', s, true), "\"one\\\"two\" : getQuoted ('\"', false, true) -> true"); // 89
|
||||
t.is (s, "\"one\"two\"", "getQuoted ('\"', true) -> \"one\"two\""); // 90
|
||||
t.ok (n.getQuoted ('"', s, true), "\"one\\\"two\" : getQuoted ('\"', false, true) -> true");
|
||||
t.is (s, "\"one\\\"two\"", "getQuoted ('\"', true) -> \"one\\\"two\"");
|
||||
|
||||
n = Nibbler ("\"one\\\"two\"");
|
||||
t.ok (n.getQuoted ('"', s, false), "\"one\\\"two\" : getQuoted ('\"', true, false) -> true"); // 91
|
||||
t.is (s, "one\"two", "getQuoted ('\"', false) -> one\"two"); // 92
|
||||
t.ok (n.getQuoted ('"', s, false), "\"one\\\"two\" : getQuoted ('\"', true, false) -> true");
|
||||
t.is (s, "one\\\"two", "getQuoted ('\"', false) -> one\\\"two");
|
||||
|
||||
n = Nibbler ("\"one\\\"two\"");
|
||||
t.ok (n.getQuoted ('"', s, true), "\"one\\\"two\" : getQuoted ('\"', true, true) -> true"); // 93
|
||||
t.is (s, "\"one\"two\"", "getQuoted ('\"', true) -> \"one\"two\""); // 94
|
||||
t.ok (n.getQuoted ('"', s, true), "\"one\\\"two\" : getQuoted ('\"', s, true) -> true");
|
||||
t.is (s, "\"one\\\"two\"", "getQuoted ('\"', s, true) -> \"one\\\"two\"");
|
||||
|
||||
n = Nibbler ("\"one\\\\\"");
|
||||
t.ok (n.getQuoted ('\"', s, true), "\"one\\\" : getQuoted ('\"', s, true) -> true");
|
||||
t.is (s, "\"one\\\\\"", "getQuoted ('\"', s, true) -> \"one\\\\\"");
|
||||
|
||||
n = Nibbler ("\"one\\\\\"");
|
||||
t.ok (n.getQuoted ('\"', s, false), "one\\ : getQuoted ('\"', s, false) -> true");
|
||||
t.is (s, "one\\\\", "getQuoted ('\"', s, false) -> \"one\\\\\"");
|
||||
|
||||
// bool getDigit (int&);
|
||||
t.diag ("Nibbler::getDigit");
|
||||
|
|
|
@ -129,6 +129,9 @@ int main (int argc, char** argv)
|
|||
t.is (structured[3], " one.four", "indentTree 'one.four' -> ' one.four'");
|
||||
t.is (structured[4], "two", "indentTree 'two' -> 'two'");
|
||||
|
||||
// TODO const std::string encode (const std::string& value);
|
||||
// TODO const std::string decode (const std::string& value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue