Compare commits

..

9 Commits

Author SHA1 Message Date
Ingo Karkat
ef9c48d7ef Perform cleaninput() filtering without tr.
Again, Bash's global pattern substitution can be used. This only depends on the "extquote" shell option, which is set by default, but we set this just to be sure.
2011-05-31 17:03:30 +02:00
Ingo Karkat
3134e8f51a BUG: Multiple whitespace was condensed, CR/LF not turned to space.
Missing quoting inside cleaninput() condensed multiple subsequent spaces into a single space, even when the user took pains to preserve whitespace by properly quoting the task in the shell.
The 'tr' command completely removed CR and LF characters instead of replacing them with a single space.
Add test cases for the whitespace handling to the add, append, prepend and replace actions.
2011-05-31 16:41:21 +02:00
Ingo Karkat
0e3c8eb29b Expose cleaninput() for use in addons.
Despite its hard-coded use of $input, this can be useful in addons, too.
(I, for example, use this in a "todo.sh subst" addon that delegates to the "todo.sh replace" action after performing a sed-style replacement on the passed pattern and replacement.)
2011-05-31 15:46:14 +02:00
Ingo Karkat
e5d13150a9 Perform cleaninput() escaping without sed.
No need for invoking sed; Bash's global pattern substitution can be used.
2011-05-31 15:28:46 +02:00
Ingo Karkat
267e771ba8 Escape the substitution separator in cleaninput().
Improving on the previous fix and commit, no search for an unused substitution separator is actually needed; sed supports escaping of the substitution separator. Revert the $inputSep abstraction in favor of the former, hard-coded "|" character.
2011-05-31 15:10:55 +02:00
Ingo Karkat
a97144d0e8 FIX: cleaninput() for sed-replacement needs more escaping and unique separator.
Commit 8e4364f5e1 removed the deletion of the "|" character from cleaninput. That was okay for the cleaninput() use in _addto(), but not in those cases that used $input for replacement via sed.

Added corresponding tests for replace, append and prepend actions similar to what was added for the add action in the above commit.

To really fix the problem (and not just remove all "|" characters from the text), a separator character must be found that is not part of $input, and this must be used in the sed expression. As cleaninput() already modifies the global $input variable, another $inputSep global variable is used to pass back this information.

In addition, backslashes must be escaped in $input, or replacements like \1 wreak havoc.
2011-05-11 16:12:03 +02:00
Ingo Karkat
41dba2d636 Configure cleaninput() sed-escaping via argument instead of magic words.
Encoding the actions that require the additional sed-escaping inside cleaninput() is cumbersome and error-prone. Instead, let the calling action tell cleaninput() that it wants this.
2011-05-11 15:48:22 +02:00
Ingo Karkat
4643dce698 Remove no-op passing of $input to cleaninput().
The function actually uses and modifies the global variable directly.
2011-05-11 15:32:31 +02:00
Ingo Karkat
631b2fbcc3 Made replace test sessions independent from each other.
When the tests depend on the todo.txt contents from the previous sessions, errors will cascade and make it more difficult to pinpoint the problem.
2011-05-11 15:07:33 +02:00
8 changed files with 193 additions and 105 deletions

View File

@@ -77,4 +77,37 @@ TODO: 5 added.
TODO: 5 of 5 tasks shown TODO: 5 of 5 tasks shown
EOF EOF
#
# Advanced add
#
cat /dev/null > todo.txt
test_todo_session 'add with spaces' <<EOF
>>> todo.sh add "notice the three spaces"
1 notice the three spaces
TODO: 1 added.
>>> todo.sh add notice how the spaces get lost
2 notice how the spaces get lost
TODO: 2 added.
>>> todo.sh list
2 notice how the spaces get lost
1 notice the three spaces
--
TODO: 2 of 2 tasks shown
EOF
cat /dev/null > todo.txt
test_todo_session 'add with CR' <<EOF
>>> todo.sh add "smell the
Carriage Return"
1 smell the Carriage Return
TODO: 1 added.
>>> todo.sh list
1 smell the Carriage Return
--
TODO: 1 of 1 tasks shown
EOF

View File

@@ -45,6 +45,12 @@ grow some corn
thrash some hay thrash some hay
chase the chickens chase the chickens
EOF EOF
test_todo_session 'replace error' << EOF
>>> todo.sh replace 10 "hej!"
=== 1
TODO: No task 10.
EOF
test_todo_session 'replace in multi-item file' <<EOF test_todo_session 'replace in multi-item file' <<EOF
>>> todo.sh replace 1 smell the cheese >>> todo.sh replace 1 smell the cheese
1 smell the cows 1 smell the cows
@@ -62,33 +68,59 @@ TODO: Replaced task with:
4 collect the eggs 4 collect the eggs
EOF EOF
echo '(A) collect the eggs' > todo.txt
test_todo_session 'replace with priority' <<EOF test_todo_session 'replace with priority' <<EOF
>>> todo.sh pri 4 a >>> todo.sh replace 1 "collect the bread"
4 (A) collect the eggs 1 (A) collect the eggs
TODO: 4 prioritized (A).
>>> todo.sh replace 4 "collect the bread"
4 (A) collect the eggs
TODO: Replaced task with: TODO: Replaced task with:
4 (A) collect the bread 1 (A) collect the bread
>>> todo.sh replace 4 collect the eggs >>> todo.sh replace 1 collect the eggs
4 (A) collect the bread 1 (A) collect the bread
TODO: Replaced task with: TODO: Replaced task with:
4 (A) collect the eggs 1 (A) collect the eggs
EOF EOF
echo 'jump on hay' > todo.txt
test_todo_session 'replace with &' << EOF test_todo_session 'replace with &' << EOF
>>> todo.sh replace 3 "thrash the hay & thresh the wheat" >>> todo.sh replace 1 "thrash the hay & thrash the wheat"
3 jump on hay 1 jump on hay
TODO: Replaced task with: TODO: Replaced task with:
3 thrash the hay & thresh the wheat 1 thrash the hay & thrash the wheat
EOF EOF
test_todo_session 'replace error' << EOF echo 'jump on hay' > todo.txt
>>> todo.sh replace 10 "hej!" test_todo_session 'replace with spaces' <<EOF
=== 1 >>> todo.sh replace 1 "notice the three spaces"
TODO: No task 10. 1 jump on hay
TODO: Replaced task with:
1 notice the three spaces
EOF
cat > todo.txt <<EOF
smell the cows
grow some corn
thrash some hay
chase the chickens
EOF
test_todo_session 'replace with symbols' <<EOF
>>> todo.sh replace 1 "~@#$%^&*()-_=+[{]}|;:',<.>/?"
1 smell the cows
TODO: Replaced task with:
1 ~@#$%^&*()-_=+[{]}|;:',<.>/?
>>> todo.sh replace 2 '\`!\\"'
2 grow some corn
TODO: Replaced task with:
2 \`!\\"
>>> todo.sh list
4 chase the chickens
3 thrash some hay
2 \`!\\"
1 ~@#$%^&*()-_=+[{]}|;:',<.>/?
--
TODO: 4 of 4 tasks shown
EOF EOF
cat /dev/null > todo.txt cat /dev/null > todo.txt
@@ -124,6 +156,7 @@ TODO: Replaced task with:
1 (A) 2009-02-13 this is just a new one 1 (A) 2009-02-13 this is just a new one
EOF EOF
echo '(A) 2009-02-13 this is just a new one' > todo.txt
test_todo_session 'replace with prepended date replaces existing date' <<EOF test_todo_session 'replace with prepended date replaces existing date' <<EOF
>>> todo.sh replace 1 2010-07-04 this also has a new date >>> todo.sh replace 1 2010-07-04 this also has a new date
1 (A) 2009-02-13 this is just a new one 1 (A) 2009-02-13 this is just a new one

View File

@@ -53,28 +53,9 @@ TODO: 2 prioritized (C).
-- --
TODO: 3 of 3 tasks shown TODO: 3 of 3 tasks shown
>>> todo.sh add "smell the coffee +wakeup"
4 smell the coffee +wakeup
TODO: 4 added.
>>> todo.sh -p list
1 (B) smell the uppercase Roses +flowers @outside
2 (C) notice the sunflowers
4 smell the coffee +wakeup
3 stop
--
TODO: 4 of 4 tasks shown
EOF
cat > todo.txt <<EOF
(B) smell the uppercase Roses +flowers @outside
(C) notice the sunflowers
stop
EOF
test_todo_session 'reprioritize' <<EOF
>>> todo.sh pri 2 A >>> todo.sh pri 2 A
2 (A) notice the sunflowers 2 (A) notice the sunflowers
TODO: 2 re-prioritized from (C) to (A). TODO: 2 prioritized (A).
>>> todo.sh -p list >>> todo.sh -p list
2 (A) notice the sunflowers 2 (A) notice the sunflowers
@@ -85,12 +66,25 @@ TODO: 3 of 3 tasks shown
>>> todo.sh pri 2 a >>> todo.sh pri 2 a
2 (A) notice the sunflowers 2 (A) notice the sunflowers
TODO: 2 already prioritized (A). TODO: 2 prioritized (A).
>>> todo.sh -p listpri >>> todo.sh -p listpri
2 (A) notice the sunflowers 2 (A) notice the sunflowers
1 (B) smell the uppercase Roses +flowers @outside 1 (B) smell the uppercase Roses +flowers @outside
-- --
TODO: 2 of 3 tasks shown TODO: 2 of 3 tasks shown
>>> todo.sh add "smell the coffee +wakeup"
4 smell the coffee +wakeup
TODO: 4 added.
>>> todo.sh -p list
2 (A) notice the sunflowers
1 (B) smell the uppercase Roses +flowers @outside
4 smell the coffee +wakeup
3 stop
--
TODO: 4 of 4 tasks shown
EOF EOF
test_done test_done

View File

@@ -57,6 +57,34 @@ test_todo_session 'prepend with &' <<EOF
3 no running & jumping now stop 3 no running & jumping now stop
EOF EOF
echo 'jump on hay' > todo.txt
test_todo_session 'prepend with spaces' <<EOF
>>> todo.sh prepend 1 "notice the three spaces and"
1 notice the three spaces and jump on hay
EOF
cat > todo.txt <<EOF
smell the cows
grow some corn
thrash some hay
chase the chickens
EOF
test_todo_session 'prepend with symbols' <<EOF
>>> todo.sh prepend 1 "~@#$%^&*()-_=+[{]}|;:',<.>/?"
1 ~@#$%^&*()-_=+[{]}|;:',<.>/? smell the cows
>>> todo.sh prepend 2 '\`!\\"'
2 \`!\\" grow some corn
>>> todo.sh list
4 chase the chickens
3 thrash some hay
2 \`!\\" grow some corn
1 ~@#$%^&*()-_=+[{]}|;:',<.>/? smell the cows
--
TODO: 4 of 4 tasks shown
EOF
cat /dev/null > todo.txt cat /dev/null > todo.txt
test_todo_session 'prepend handling prepended date on add' <<EOF test_todo_session 'prepend handling prepended date on add' <<EOF
>>> todo.sh -t add "new task" >>> todo.sh -t add "new task"

View File

@@ -81,6 +81,6 @@ test_todo_session 'fail multiple do attempts' <<EOF
TODO: 3 marked as done. TODO: 3 marked as done.
>>> todo.sh -a do 3 >>> todo.sh -a do 3
TODO: 3 is already marked done. 3 is already marked done
EOF EOF
test_done test_done

View File

@@ -17,6 +17,12 @@ test_todo_session 'append usage' <<EOF
usage: todo.sh append ITEM# "TEXT TO APPEND" usage: todo.sh append ITEM# "TEXT TO APPEND"
EOF EOF
test_todo_session 'append error' << EOF
>>> todo.sh append 10 "hej!"
=== 1
TODO: No task 10.
EOF
test_todo_session 'basic append' <<EOF test_todo_session 'basic append' <<EOF
>>> todo.sh append 1 "smell the roses" >>> todo.sh append 1 "smell the roses"
1 notice the daisies smell the roses 1 notice the daisies smell the roses
@@ -37,11 +43,32 @@ test_todo_session 'basic append with &' <<EOF
TODO: 1 of 1 tasks shown TODO: 1 of 1 tasks shown
EOF EOF
echo 'jump on hay' > todo.txt
test_todo_session 'append with spaces' <<EOF
>>> todo.sh append 1 "and notice the three spaces"
1 jump on hay and notice the three spaces
EOF
test_todo_session 'append error' << EOF cat > todo.txt <<EOF
>>> todo.sh append 10 "hej!" smell the cows
=== 1 grow some corn
TODO: No task 10. thrash some hay
chase the chickens
EOF
test_todo_session 'append with symbols' <<EOF
>>> todo.sh append 1 "~@#$%^&*()-_=+[{]}|;:',<.>/?"
1 smell the cows ~@#$%^&*()-_=+[{]}|;:',<.>/?
>>> todo.sh append 2 '\`!\\"'
2 grow some corn \`!\\"
>>> todo.sh list
4 chase the chickens
2 grow some corn \`!\\"
1 smell the cows ~@#$%^&*()-_=+[{]}|;:',<.>/?
3 thrash some hay
--
TODO: 4 of 4 tasks shown
EOF EOF
cat > todo.txt <<EOF cat > todo.txt <<EOF

View File

@@ -68,30 +68,4 @@ TODO: 2 deprioritized.
TODO: 3 of 3 tasks shown TODO: 3 of 3 tasks shown
EOF EOF
cat > todo.txt <<EOF
(B) smell the uppercase Roses +flowers @outside
(A) notice the sunflowers
stop
EOF
test_todo_session 'depriority of unprioritized task' <<EOF
>>> todo.sh -p list
2 (A) notice the sunflowers
1 (B) smell the uppercase Roses +flowers @outside
3 stop
--
TODO: 3 of 3 tasks shown
>>> todo.sh depri 3 2
TODO: 3 is not prioritized.
2 notice the sunflowers
TODO: 2 deprioritized.
>>> todo.sh -p list
1 (B) smell the uppercase Roses +flowers @outside
2 notice the sunflowers
3 stop
--
TODO: 3 of 3 tasks shown
EOF
test_done test_done

71
todo.sh
View File

@@ -1,7 +1,7 @@
#! /bin/bash #! /bin/bash
# === HEAVY LIFTING === # === HEAVY LIFTING ===
shopt -s extglob shopt -s extglob extquote
# NOTE: Todo.sh requires the .todo/config configuration file to run. # NOTE: Todo.sh requires the .todo/config configuration file to run.
# Place the .todo/config file in your home directory or use the -d option for a custom location. # Place the .todo/config file in your home directory or use the -d option for a custom location.
@@ -267,16 +267,17 @@ cleanup()
cleaninput() cleaninput()
{ {
# Cleanup the input # Replace CR and LF with space; tasks always comprise a single line.
# Replace newlines with spaces Always input=${input//$'\r'/ }
input=`echo $input | tr -d '\r\n'` input=${input//$'\n'/ }
action_regexp="^\(append\|app\|prepend\|prep\|replace\)$" if [ "$1" = "for sed" ]; then
# This action uses sed with "|" as the substitution separator, and & as
# Check which action we are being used in as this affects what cleaning we do # the matched string; these must be escaped.
if [ `echo $action | grep -c $action_regexp` -eq 1 ]; then # Backslashes must be escaped, too, and before the other stuff.
# These actions use sed and & as the matched string so escape it input=${input//\\/\\\\}
input=`echo $input | sed 's/\&/\\\&/g'` input=${input//|/\\|}
input=${input//&/\\&}
fi fi
} }
@@ -321,11 +322,11 @@ replaceOrPrepend()
else else
input=$* input=$*
fi fi
cleaninput $input cleaninput "for sed"
# Retrieve existing priority and prepended date # Retrieve existing priority and prepended date
priority=$(sed -e "$item!d" -e $item's/^\((.) \)\{0,1\}\([0-9]\{2,4\}-[0-9]\{2\}-[0-9]\{2\} \)\{0,1\}.*/\1/' "$TODO_FILE") priority=$(sed -e "$item!d" -e $item's/^\(([A-Z]) \)\{0,1\}\([0-9]\{2,4\}-[0-9]\{2\}-[0-9]\{2\} \)\{0,1\}.*/\1/' "$TODO_FILE")
prepdate=$(sed -e "$item!d" -e $item's/^\((.) \)\{0,1\}\([0-9]\{2,4\}-[0-9]\{2\}-[0-9]\{2\} \)\{0,1\}.*/\2/' "$TODO_FILE") prepdate=$(sed -e "$item!d" -e $item's/^\(([A-Z]) \)\{0,1\}\([0-9]\{2,4\}-[0-9]\{2\}-[0-9]\{2\} \)\{0,1\}.*/\2/' "$TODO_FILE")
if [ "$prepdate" -a "$action" = "replace" ] && [ "$(echo "$input"|sed -e 's/^\([0-9]\{2,4\}-[0-9]\{2\}-[0-9]\{2\}\)\{0,1\}.*/\1/')" ]; then if [ "$prepdate" -a "$action" = "replace" ] && [ "$(echo "$input"|sed -e 's/^\([0-9]\{2,4\}-[0-9]\{2\}-[0-9]\{2\}\)\{0,1\}.*/\1/')" ]; then
# If the replaced text starts with a date, it will replace the existing # If the replaced text starts with a date, it will replace the existing
@@ -615,7 +616,7 @@ fi
_addto() { _addto() {
file="$1" file="$1"
input="$2" input="$2"
cleaninput $input cleaninput
if [[ $TODOTXT_DATE_ON_ADD = 1 ]]; then if [[ $TODOTXT_DATE_ON_ADD = 1 ]]; then
now=`date '+%Y-%m-%d'` now=`date '+%Y-%m-%d'`
@@ -750,7 +751,7 @@ _list() {
fi fi
} }
export -f _list die export -f cleaninput _list die
# == HANDLE ACTION == # == HANDLE ACTION ==
action=$( printf "%s\n" "$ACTION" | tr 'A-Z' 'a-z' ) action=$( printf "%s\n" "$ACTION" | tr 'A-Z' 'a-z' )
@@ -842,7 +843,7 @@ case $action in
[$SENTENCE_DELIMITERS]*) appendspace=;; [$SENTENCE_DELIMITERS]*) appendspace=;;
*) appendspace=" ";; *) appendspace=" ";;
esac esac
cleaninput $input cleaninput "for sed"
if sed -i.bak $item" s|^.*|&${appendspace}${input}|" "$TODO_FILE"; then if sed -i.bak $item" s|^.*|&${appendspace}${input}|" "$TODO_FILE"; then
if [ $TODOTXT_VERBOSE -gt 0 ]; then if [ $TODOTXT_VERBOSE -gt 0 ]; then
@@ -921,7 +922,10 @@ case $action in
todo=$(sed "$item!d" "$TODO_FILE") todo=$(sed "$item!d" "$TODO_FILE")
[ -z "$todo" ] && die "TODO: No task $item." [ -z "$todo" ] && die "TODO: No task $item."
if sed "$item!d" "$TODO_FILE" | grep "^(.) " > /dev/null; then sed -e $item"s/^(.) //" "$TODO_FILE" > /dev/null 2>&1
if [ "$?" -eq 0 ]; then
#it's all good, continue
sed -i.bak -e $item"s/^(.) //" "$TODO_FILE" sed -i.bak -e $item"s/^(.) //" "$TODO_FILE"
if [ $TODOTXT_VERBOSE -gt 0 ]; then if [ $TODOTXT_VERBOSE -gt 0 ]; then
NEWTODO=$(sed "$item!d" "$TODO_FILE") NEWTODO=$(sed "$item!d" "$TODO_FILE")
@@ -929,7 +933,7 @@ case $action in
echo "TODO: $item deprioritized." echo "TODO: $item deprioritized."
fi fi
else else
echo "TODO: $item is not prioritized." die "$errmsg"
fi fi
done done
;; ;;
@@ -961,7 +965,7 @@ case $action in
echo "TODO: $item marked as done." echo "TODO: $item marked as done."
fi fi
else else
echo "TODO: $item is already marked done." echo "$item is already marked done"
fi fi
done done
@@ -1020,7 +1024,7 @@ case $action in
} }
else else
## No priority specified; show all priority tasks ## No priority specified; show all priority tasks
pri="[A-Z]" pri="[[:upper:]]"
fi fi
pri="($pri)" pri="($pri)"
@@ -1086,23 +1090,18 @@ note: PRIORITY must be anywhere from A to Z."
[[ "$item" = +([0-9]) ]] || die "$errmsg" [[ "$item" = +([0-9]) ]] || die "$errmsg"
[[ "$newpri" = @([A-Z]) ]] || die "$errmsg" [[ "$newpri" = @([A-Z]) ]] || die "$errmsg"
oldpri=$(sed -ne $item's/^(\(.\)) .*/\1/p' "$TODO_FILE") sed -e $item"s/^(.) //" -e $item"s/^/($newpri) /" "$TODO_FILE" > /dev/null 2>&1
if [ "$oldpri" != "$newpri" ]; then
if [ "$?" -eq 0 ]; then
#it's all good, continue
sed -i.bak -e $item"s/^(.) //" -e $item"s/^/($newpri) /" "$TODO_FILE" sed -i.bak -e $item"s/^(.) //" -e $item"s/^/($newpri) /" "$TODO_FILE"
fi if [ $TODOTXT_VERBOSE -gt 0 ]; then
if [ $TODOTXT_VERBOSE -gt 0 ]; then NEWTODO=$(sed "$item!d" "$TODO_FILE")
NEWTODO=$(sed "$item!d" "$TODO_FILE") echo "$item $NEWTODO"
echo "$item $NEWTODO" echo "TODO: $item prioritized ($newpri)."
if [ "$oldpri" != "$newpri" ]; then fi
if [ "$oldpri" ]; then else
echo "TODO: $item re-prioritized from ($oldpri) to ($newpri)." die "$errmsg"
else
echo "TODO: $item prioritized ($newpri)."
fi
fi
fi
if [ "$oldpri" = "$newpri" ]; then
echo "TODO: $item already prioritized ($newpri)."
fi fi
;; ;;