diff --git a/Makefile b/Makefile index b6056bc6..d498004c 100644 --- a/Makefile +++ b/Makefile @@ -64,7 +64,7 @@ clean: test-pre-clean install: installdirs $(INSTALL_PROGRAM) todo.sh $(DESTDIR)$(bindir)/todo.sh - $(INSTALL_DATA) todo_completion $(DESTDIR)$(datarootdir)/todo + $(INSTALL_DATA) todo_completion $(DESTDIR)$(datarootdir)/todo.sh [ -e $(DESTDIR)$(sysconfdir)/todo/config ] || \ sed "s/^\(export[ \t]*TODO_DIR=\).*/\1~\/.todo/" todo.cfg > $(DESTDIR)$(sysconfdir)/todo/config diff --git a/README.md b/README.md index 925afc8f..594cb9e4 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ make test *NOTE:* Makefile defaults to several default paths for installed files. Adjust to your system: - `INSTALL_DIR`: PATH for executables (default /usr/local/bin) -- `CONFIG_DIR`: PATH for todo.txt config +- `CONFIG_DIR`: PATH for the todo.txt configuration template - `BASH_COMPLETION`: PATH for autocompletion scripts (default to /etc/bash_completion.d) ```shell @@ -58,6 +58,11 @@ make install CONFIG_DIR=/etc INSTALL_DIR=/usr/bin BASH_COMPLETION=/usr/share/bas https://aur.archlinux.org/packages/todotxt/ +## Configuration + +No configuration is required; however, most users tweak the default settings (e.g. relocating the todo.txt directory to a subdirectory of the user's home directory, or onto a cloud drive (via the `TODO_DIR` variable)), modify the colors, add additional highlighting of projects, contexts, dates, and so on. A configuration template with a commented-out list of all available options is included. +It is recommended to _copy_ that template into one of the locations listed by `todo.sh help` on `-d CONFIG_FILE`, even if it is installed in the global configuration location (`/etc/todo/config`). + ## Usage ```shell todo.sh [-fhpantvV] [-d todo_config] action [task_number] [task_description] diff --git a/tests/actions-test-lib.sh b/tests/actions-test-lib.sh index 51d4eefc..42185f24 100644 --- a/tests/actions-test-lib.sh +++ b/tests/actions-test-lib.sh @@ -1,20 +1,26 @@ #!/bin/bash -make_action() +make_dummy_action() { - unset TODO_ACTIONS_DIR - [ -d .todo.actions.d ] || mkdir .todo.actions.d - cat > ".todo.actions.d/$1" < "$1" < ".todo.actions.d/$1/$1" < todo.txt +test_todo_session 'replace handling prepended priority on add' <>> todo.sh -t add "new task" +1 2009-02-13 new task +TODO: 1 added. + +>>> todo.sh replace 1 '(B) this also has a priority now' +1 2009-02-13 new task +TODO: Replaced task with: +1 (B) 2009-02-13 this also has a priority now +EOF + cat /dev/null > todo.txt test_todo_session 'replace handling priority and prepended date on add' <>> todo.sh -t add "new task" @@ -156,6 +168,18 @@ TODO: Replaced task with: 1 (A) 2009-02-13 this is just a new one EOF +cat /dev/null > todo.txt +test_todo_session 'replace handling prepended priority and date on add' <>> todo.sh -t add "new task" +1 2009-02-13 new task +TODO: 1 added. + +>>> todo.sh replace 1 '(C) 2010-07-04 this also has a priority and new date' +1 2009-02-13 new task +TODO: Replaced task with: +1 (C) 2010-07-04 this also has a priority and new date +EOF + echo '(A) 2009-02-13 this is just a new one' > todo.txt test_todo_session 'replace with prepended date replaces existing date' <>> todo.sh replace 1 2010-07-04 this also has a new date @@ -164,6 +188,14 @@ TODO: Replaced task with: 1 (A) 2010-07-04 this also has a new date EOF +echo '(A) 2009-02-13 this is just a new one' > todo.txt +test_todo_session 'replace with prepended priority replaces existing priority' <>> todo.sh replace 1 '(B) this also has a new priority' +1 (A) 2009-02-13 this is just a new one +TODO: Replaced task with: +1 (B) 2009-02-13 this also has a new priority +EOF + echo '2009-02-13 this is just a new one' > todo.txt test_todo_session 'replace with prepended priority and date replaces existing date' <>> todo.sh replace 1 '(B) 2010-07-04 this also has a new date' @@ -172,4 +204,13 @@ TODO: Replaced task with: 1 (B) 2010-07-04 this also has a new date EOF + +echo '(A) 2009-02-13 this is just a new one' > todo.txt +test_todo_session 'replace with prepended priority and date replaces existing priority and date' <>> todo.sh replace 1 '(B) 2010-07-04 this also has a new prio+date' +1 (A) 2009-02-13 this is just a new one +TODO: Replaced task with: +1 (B) 2010-07-04 this also has a new prio+date +EOF + test_done diff --git a/tests/t1200-pri.sh b/tests/t1200-pri.sh index fd8bd10c..54b610fd 100755 --- a/tests/t1200-pri.sh +++ b/tests/t1200-pri.sh @@ -90,6 +90,7 @@ TODO: 2 re-prioritized from (C) to (A). TODO: 3 of 3 tasks shown >>> todo.sh pri 2 a +=== 1 2 (A) notice the sunflowers TODO: 2 already prioritized (A). diff --git a/tests/t1250-listpri.sh b/tests/t1250-listpri.sh index ff374e2f..26303cbc 100755 --- a/tests/t1250-listpri.sh +++ b/tests/t1250-listpri.sh @@ -96,6 +96,26 @@ TODO: 0 of 5 tasks shown -- TODO: 1 of 5 tasks shown EOF +test_todo_session 'listpri filtering concatenation of priorities and -ranges' <>> todo.sh -p listpri CX +3 (C) notice the sunflowers +2 (X) clean the house from A-Z +4 (X) listen to music +-- +TODO: 3 of 5 tasks shown + +>>> todo.sh -p listpri ABR-Y +1 (B) smell the uppercase Roses +flowers @outside +2 (X) clean the house from A-Z +4 (X) listen to music +-- +TODO: 3 of 5 tasks shown + +>>> todo.sh -p listpri A- +2 (X) clean the house from A-Z +-- +TODO: 1 of 5 tasks shown +EOF cat > todo.txt <>> todo.sh -a do 3 +=== 1 TODO: 3 is already marked done. EOF diff --git a/tests/t1700-depri.sh b/tests/t1700-depri.sh index 7ec7e7f9..f459ef10 100755 --- a/tests/t1700-depri.sh +++ b/tests/t1700-depri.sh @@ -82,6 +82,7 @@ test_todo_session 'depriority of unprioritized task' <>> todo.sh depri 3 2 +=== 1 TODO: 3 is not prioritized. 2 notice the sunflowers TODO: 2 deprioritized. diff --git a/tests/t1800-del.sh b/tests/t1800-del.sh index 6d96e89c..356596fa 100755 --- a/tests/t1800-del.sh +++ b/tests/t1800-del.sh @@ -60,8 +60,9 @@ test_todo_session 'del with confirmation' <>> printf n | todo.sh del 1 -Delete '(B) smell the uppercase Roses +flowers @outside'? (y/n)$SPACE +\\ TODO: No tasks were deleted. +=== 1 >>> todo.sh -p list 2 (A) notice the sunflowers @@ -71,15 +72,17 @@ TODO: No tasks were deleted. TODO: 3 of 3 tasks shown >>> printf x | todo.sh del 1 -Delete '(B) smell the uppercase Roses +flowers @outside'? (y/n)$SPACE +\\ TODO: No tasks were deleted. +=== 1 >>> echo | todo.sh del 1 -Delete '(B) smell the uppercase Roses +flowers @outside'? (y/n)$SPACE +\\ TODO: No tasks were deleted. +=== 1 >>> printf y | todo.sh del 1 -Delete '(B) smell the uppercase Roses +flowers @outside'? (y/n)$SPACE +\\ 1 (B) smell the uppercase Roses +flowers @outside TODO: 1 deleted. diff --git a/tests/t1850-move.sh b/tests/t1850-move.sh index 9fbdc0b6..2a9e6ea3 100755 --- a/tests/t1850-move.sh +++ b/tests/t1850-move.sh @@ -4,8 +4,6 @@ test_description='basic move functionality ' . ./test-lib.sh -SPACE=' ' - cat > todo.txt <>> printf y | todo.sh move 1 done.txt 2>&1 | sed -e "s#'[^']\{1,\}/\([^/']\{1,\}\)'#'\1'#g" -e 's#from .\{1,\}/\([^/]\{1,\}\) to .\{1,\}/\([^/]\{1,\}\)?#from \1 to \2?#g' -Move '(B) smell the uppercase Roses +flowers @outside' from todo.txt to done.txt? (y/n)$SPACE +\\ 1 (B) smell the uppercase Roses +flowers @outside TODO: 1 moved from 'todo.txt' to 'done.txt'. diff --git a/tests/t1910-deduplicate.sh b/tests/t1910-deduplicate.sh index 3617807d..67217864 100755 --- a/tests/t1910-deduplicate.sh +++ b/tests/t1910-deduplicate.sh @@ -32,6 +32,7 @@ EOF test_todo_session 'deduplicate without duplicates' <>> todo.sh deduplicate +=== 1 TODO: No duplicate tasks found EOF diff --git a/tests/t2120-shorthelp.sh b/tests/t2120-shorthelp.sh index f0123b72..82275db5 100755 --- a/tests/t2120-shorthelp.sh +++ b/tests/t2120-shorthelp.sh @@ -47,7 +47,7 @@ echo 'export TODO_ACTIONS_DIR=$HOME/custom.actions' >> custom.cfg export TODOTXT_GLOBAL_CFG_FILE=global.cfg test_todo_session '-h and fatal error without config' <>> todo.sh -h | sed '/^ \\{0,2\\}[A-Z]/!d' +>>> todo.sh -h 2>&1 | sed '/^ \\{0,2\\}[A-Z]/!d' Usage: todo.sh [-fhpantvV] [-d todo_config] action [task_number] [task_description] Actions: Actions can be added and overridden using scripts in the actions @@ -58,7 +58,7 @@ EOF # Config option comes too late; "Add-on Actions" is *not* mentioned here. test_todo_session '-h and fatal error with trailing custom config' <>> todo.sh -h -d custom.cfg | sed '/^ \\{0,2\\}[A-Z]/!d' +>>> todo.sh -h -d custom.cfg 2>&1 | sed '/^ \\{0,2\\}[A-Z]/!d' Usage: todo.sh [-fhpantvV] [-d todo_config] action [task_number] [task_description] Actions: Actions can be added and overridden using scripts in the actions @@ -69,7 +69,7 @@ EOF # Config option processed; "Add-on Actions" is mentioned here. test_todo_session '-h output with preceding custom config' <>> todo.sh -d custom.cfg -h | sed '/^ \\{0,2\\}[A-Z]/!d' +>>> todo.sh -d custom.cfg -h 2>&1 | sed '/^ \\{0,2\\}[A-Z]/!d' Usage: todo.sh [-fhpantvV] [-d todo_config] action [task_number] [task_description] Actions: Actions can be added and overridden using scripts in the actions diff --git a/tests/t8000-actions.sh b/tests/t8000-actions.sh index 763c4e92..1c480c67 100755 --- a/tests/t8000-actions.sh +++ b/tests/t8000-actions.sh @@ -44,4 +44,50 @@ custom action bad === 42 EOF +make_action +ln -s /actionsdir/doesnotexist/badlink .todo.actions.d/badlink +# On Cygwin, the Windows ACL may still grant execution rights. In this case, we +# skip the test. +if [ -x .todo.actions.d/badlink ]; then + SKIP_TESTS="${SKIP_TESTS}${SKIP_TESTS+ }t8000.6 t8000.7" +fi +test_todo_session 'broken symlink' <>> todo.sh badlink 2>&1 | sed "s#'[^']*\(\\.todo\\.actions\\.d/[^']\{1,\}\)'#'\1'#g" +Fatal Error: Broken link to custom action: '.todo.actions.d/badlink' + +>>> todo.sh do 2>/dev/null +=== 1 +EOF + +make_action +mkdir .todo.actions.d/badfolderlink +ln -s /actionsdir/doesnotexist/badfolderlink .todo.actions.d/badfolderlink/badfolderlink +# On Cygwin, the Windows ACL may still grant execution rights. In this case, we +# skip the test. +if [ -x .todo.actions.d/badfolderlink/badfolderlink ]; then + SKIP_TESTS="${SKIP_TESTS}${SKIP_TESTS+ }t8000.8 t8000.9" +fi +test_todo_session 'broken symlink in folder' <>> todo.sh badfolderlink 2>&1 | sed "s#'[^']*\(\\.todo\\.actions\\.d/[^']\{1,\}\)'#'\1'#g" +Fatal Error: Broken link to custom action: '.todo.actions.d/badfolderlink/badfolderlink' + +>>> todo.sh do 2>/dev/null +=== 1 +EOF + +make_action +ln -s /actionsdir/doesnotexist/do .todo.actions.d/do +# On Cygwin, the Windows ACL may still grant execution rights. In this case, we +# skip the test. +if [ -x .todo.actions.d/do ]; then + SKIP_TESTS="${SKIP_TESTS}${SKIP_TESTS+ }t8000.10 t8000.11" +fi +test_todo_session 'broken symlink overrides built-in action' <>> todo.sh do 2>&1 | sed "s#'[^']*\(\\.todo\\.actions\\.d/[^']\{1,\}\)'#'\1'#g" +Fatal Error: Broken link to custom action: '.todo.actions.d/do' + +>>> todo.sh do 2>/dev/null +=== 1 +EOF + test_done diff --git a/tests/t8010-listaddons.sh b/tests/t8010-listaddons.sh index b4e89aa9..ee0495a9 100755 --- a/tests/t8010-listaddons.sh +++ b/tests/t8010-listaddons.sh @@ -28,12 +28,7 @@ ls quux EOF -chmod -x .todo.actions.d/foo -# On Cygwin, clearing the executable flag may have no effect, as the Windows ACL -# may still grant execution rights. In this case, we skip the test. -if [ -x .todo.actions.d/foo ]; then - SKIP_TESTS="${SKIP_TESTS}${SKIP_TESTS+ }t8010.4" -fi +invalidate_action .todo.actions.d/foo t8010.4 test_todo_session 'nonexecutable action' <>> todo.sh listaddons bar @@ -66,13 +61,7 @@ norris quux EOF -# nthorne: shamelessly stolen from above.. -chmod -x .todo.actions.d/norris/norris -# On Cygwin, clearing the executable flag may have no effect, as the Windows ACL -# may still grant execution rights. In this case, we skip the test. -if [ -x .todo.actions.d/norris/norris ]; then - SKIP_TESTS="${SKIP_TESTS}${SKIP_TESTS+ }t8010.8" -fi +invalidate_action .todo.actions.d/norris/norris t8010.8 test_todo_session 'nonexecutable action in subfolder' <>> todo.sh listaddons bar diff --git a/todo.cfg b/todo.cfg index 0ab7d9bb..3ccd2181 100644 --- a/todo.cfg +++ b/todo.cfg @@ -76,6 +76,13 @@ export REPORT_FILE="$TODO_DIR/report.txt" # === BEHAVIOR === +## verbosity +# +# By default, additional information and confirmation of actions (like +# "TODO: 1 added") are printed. You can suppress this via 0 or add extra +# verbosity via 2. +# export TODOTXT_VERBOSE=1 + ## customize list output # # TODOTXT_SORT_COMMAND will filter after line numbers are diff --git a/todo.sh b/todo.sh index f6844219..7c43c1eb 100755 --- a/todo.sh +++ b/todo.sh @@ -254,7 +254,7 @@ actionsHelp() listpri [PRIORITIES] [TERM...] lsp [PRIORITIES] [TERM...] Displays all tasks prioritized PRIORITIES. - PRIORITIES can be a single one (A) or a range (A-C). + PRIORITIES can be a [concatenation of] single (A) or range (A-C). If no PRIORITIES specified, lists all prioritized tasks. If TERM specified, lists only prioritized tasks that contain TERM(s). Hides all tasks that contain TERM(s) preceded by a minus sign @@ -349,14 +349,14 @@ dieWithHelp() case "$1" in help) help;; shorthelp) shorthelp;; - esac + esac >&2 shift die "$@" } die() { - echo "$*" + echo >&2 "$*" exit 1 } @@ -364,12 +364,11 @@ confirm() { [ $TODOTXT_FORCE = 0 ] || return 0 - printf %s "${1:?}? (y/n) " local readArgs=(-e -r) [ -n "${BASH_VERSINFO:-}" ] && [ \( ${BASH_VERSINFO[0]} -eq 4 -a ${BASH_VERSINFO[1]} -ge 1 \) -o ${BASH_VERSINFO[0]} -gt 4 ] && readArgs+=(-N 1) # Bash 4.1+ supports -N nchars local answer - read "${readArgs[@]}" answer + read -p "${1:?}? (y/n) " "${readArgs[@]}" answer echo [ "$answer" = "y" ] } @@ -451,28 +450,38 @@ replaceOrPrepend() getTodo "$item" if [[ -z "$1" && $TODOTXT_FORCE = 0 ]]; then - echo -n "$querytext" - read -r -i "$todo" -e input + read -p "$querytext" -r -i "$todo" -e input else input=$* fi # Retrieve existing priority and prepended date local -r priAndDateExpr='^\((.) \)\{0,1\}\([0-9]\{2,4\}-[0-9]\{2\}-[0-9]\{2\} \)\{0,1\}' - priority=$(sed -e "$item!d" -e "${item}s/${priAndDateExpr}.*/\\1/" "$TODO_FILE") - prepdate=$(sed -e "$item!d" -e "${item}s/${priAndDateExpr}.*/\\2/" "$TODO_FILE") - - if [ "$prepdate" ] && [ "$action" = "replace" ] && [ "$(echo "$input"|sed -e "s/${priAndDateExpr}.*/\\1\\2/")" ]; then + originalPriority=$(sed -e "$item!d" -e "${item}s/${priAndDateExpr}.*/\\1/" "$TODO_FILE") + priority="$originalPriority" + originalPrepdate=$(sed -e "$item!d" -e "${item}s/${priAndDateExpr}.*/\\2/" "$TODO_FILE") + prepdate="$originalPrepdate" + if [ "$action" = "replace" ]; then + replacementPrepdate="$(echo "$input"|sed -e "s/${priAndDateExpr}.*/\\2/")" + if [ "$replacementPrepdate" ]; then # If the replaced text starts with a [priority +] date, it will replace # the existing date, too. - prepdate= + prepdate="$replacementPrepdate" + fi + replacementPriority="$(echo "$input"|sed -e "s/${priAndDateExpr}.*/\\1/")" + if [ "$replacementPriority" ]; then + # If the replaced text starts with a priority, it will replace + # the existing priority, too. + priority="$replacementPriority" + fi + input="$(echo "$input"|sed -e "s/${priAndDateExpr}//")" fi # Temporarily remove any existing priority and prepended date, perform the # change (replace/prepend) and re-insert the existing priority and prepended # date again. cleaninput "for sed" - sed -i.bak -e "$item s/^${priority}${prepdate}//" -e "$item s|^.*|${priority}${prepdate}${input}${backref}|" "$TODO_FILE" + sed -i.bak -e "$item s/^${originalPriority}${originalPrepdate}//" -e "$item s|^.*|${priority}${prepdate}${input}${backref}|" "$TODO_FILE" if [ "$TODOTXT_VERBOSE" -gt 0 ]; then getNewtodo "$item" case "$action" in @@ -825,11 +834,6 @@ _addto() { fi } -shellquote() -{ - typeset -r qq=\'; printf %s\\n "'${1//\'/${qq}\\${qq}${qq}}'"; -} - filtercommand() { filter=${1:-} @@ -844,13 +848,13 @@ filtercommand() then ## First character isn't a dash: hide lines that don't match ## this $search_term - filter="${filter:-}${filter:+ | }grep -i $(shellquote "$search_term")" + printf -v filter '%sgrep -i %q' "${filter:-}${filter:+ | }" "$search_term" else ## First character is a dash: hide lines that match this ## $search_term # ## Remove the first character (-) before adding to our filter command - filter="${filter:-}${filter:+ | }grep -v -i $(shellquote "${search_term:1}")" + printf -v filter '%sgrep -v -i %q' "${filter:-}${filter:+ | }" "${search_term:1}" fi done @@ -1037,7 +1041,18 @@ listWordsWithSigil() | sort -u } -export -f cleaninput getPrefix getTodo getNewtodo shellquote filtercommand _list listWordsWithSigil getPadding _format die +hasCustomAction() +{ + [ -d "${1:?}" ] || return 1 + [ -x "$1/${2:?}" ] && return 0 + if [ -h "$1/$2" ] && [ ! -e "$1/$2" ] + then + dieWithHelp "$2" "Fatal Error: Broken link to custom action: '$1/$2'" + fi + return 1 +} + +export -f cleaninput getPrefix getTodo getNewtodo filtercommand _list listWordsWithSigil getPadding _format die # == HANDLE ACTION == action=$( printf "%s\n" "$ACTION" | tr '[:upper:]' '[:lower:]' ) @@ -1052,11 +1067,11 @@ then shift ## Reset action to new first argument action=$( printf "%s\n" "$1" | tr '[:upper:]' '[:lower:]' ) -elif [ -d "$TODO_ACTIONS_DIR/$action" ] && [ -x "$TODO_ACTIONS_DIR/$action/$action" ] +elif hasCustomAction "$TODO_ACTIONS_DIR/$action" "$action" then "$TODO_ACTIONS_DIR/$action/$action" "$@" exit $? -elif [ -d "$TODO_ACTIONS_DIR" ] && [ -x "$TODO_ACTIONS_DIR/$action" ] +elif hasCustomAction "$TODO_ACTIONS_DIR" "$action" then "$TODO_ACTIONS_DIR/$action" "$@" exit $? @@ -1066,8 +1081,7 @@ fi case $action in "add" | "a") if [[ -z "$2" && $TODOTXT_FORCE = 0 ]]; then - echo -n "Add: " - read -e -r input + read -p "Add: " -e -r input else [ -z "$2" ] && die "usage: $TODO_SH add \"TODO ITEM\"" shift @@ -1078,8 +1092,7 @@ case $action in "addm") if [[ -z "$2" && $TODOTXT_FORCE = 0 ]]; then - echo -n "Add: " - read -e -r input + read -p "Add: " -e -r input else [ -z "$2" ] && die "usage: $TODO_SH addm \"TODO ITEM\"" shift @@ -1119,8 +1132,7 @@ case $action in getTodo "$item" if [[ -z "$1" && $TODOTXT_FORCE = 0 ]]; then - echo -n "Append: " - read -e -r input + read -p "Append: " -e -r input else input=$* fi @@ -1171,7 +1183,7 @@ case $action in echo "TODO: $item deleted." fi else - echo "TODO: No tasks were deleted." + die "TODO: No tasks were deleted." fi else sed -i.bak \ @@ -1201,6 +1213,7 @@ case $action in # Split multiple depri's, if comma separated change to whitespace separated # Loop the 'depri' function for each item + status=0 for item in ${*//,/ }; do getTodo "$item" @@ -1212,9 +1225,11 @@ case $action in echo "TODO: $item deprioritized." fi else - echo "TODO: $item is not prioritized." + echo >&2 "TODO: $item is not prioritized." + status=1 fi done + exit $status ;; "do" | "done" ) @@ -1225,6 +1240,7 @@ case $action in # Split multiple do's, if comma separated change to whitespace separated # Loop the 'do' function for each item + status=0 for item in ${*//,/ }; do getTodo "$item" @@ -1240,15 +1256,17 @@ case $action in echo "TODO: $item marked as done." fi else - echo "TODO: $item is already marked done." + echo >&2 "TODO: $item is already marked done." + status=1 fi done if [ $TODOTXT_AUTO_ARCHIVE = 1 ]; then # Recursively invoke the script to allow overriding of the archive # action. - "$TODO_FULL_SH" archive + "$TODO_FULL_SH" archive || status=$? fi + exit $status ;; "help" ) @@ -1328,8 +1346,8 @@ case $action in "listpri" | "lsp" ) shift ## was "listpri", new $1 is priority to list or first TERM - pri=$(printf "%s\n" "$1" | tr '[:lower:]' '[:upper:]' | grep -e '^[A-Z]$' -e '^[A-Z]-[A-Z]$') && shift || pri="A-Z" - post_filter_command="${post_filter_command:-}${post_filter_command:+ | }grep '^ *[0-9]\+ ([${pri}]) '" + pri=$(printf "%s\n" "$1" | grep '^\([A-Za-z]\|[A-Za-z]-[A-Za-z]\|[A-Z][A-Z-]*[A-Z]\)$') && shift || pri="A-Z" + post_filter_command="${post_filter_command:-}${post_filter_command:+ | }grep '^ *[0-9]\+ ([${pri^^}]) '" _list "$TODO_FILE" "$@" ;; @@ -1364,7 +1382,7 @@ case $action in echo "TODO: $item moved from '$src' to '$dest'." fi else - echo "TODO: No tasks moved." + die "TODO: No tasks moved." fi ;; @@ -1375,6 +1393,7 @@ case $action in "pri" | "p" ) shift + status=0 while [ "$#" -gt 0 ] ; do item=$1 newpri=$( printf "%s\n" "$2" | tr '[:lower:]' '[:upper:]' ) @@ -1406,10 +1425,12 @@ note: PRIORITY must be anywhere from A to Z." fi fi if [ "$oldpri" = "$newpri" ]; then - echo "TODO: $item already prioritized ($newpri)." + echo >&2 "TODO: $item already prioritized ($newpri)." + status=1 fi shift; shift done + exit $status ;; "replace" ) @@ -1477,7 +1498,7 @@ note: PRIORITY must be anywhere from A to Z." newTaskNum=$( sed -e '/./!d' "$TODO_FILE" | sed -n '$ =' ) deduplicateNum=$(( originalTaskNum - newTaskNum )) if [ $deduplicateNum -eq 0 ]; then - echo "TODO: No duplicate tasks found" + die "TODO: No duplicate tasks found" else echo "TODO: $deduplicateNum duplicate task(s) removed" fi