From d508ed9dee31d5021b5567265719ab9a7dd7ae32 Mon Sep 17 00:00:00 2001 From: "David A. Harding" Date: Thu, 12 Mar 2009 01:02:10 -0400 Subject: [PATCH 01/20] Modularized Listing Sed Still more to do tomorrow. --- todo.sh | 131 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 98 insertions(+), 33 deletions(-) diff --git a/todo.sh b/todo.sh index 67c3c75..3911c51 100755 --- a/todo.sh +++ b/todo.sh @@ -339,6 +339,61 @@ fi # === HEAVY LIFTING === shopt -s extglob +_list-zero_padding() { + local LINES=$( wc -l $FILE_TO_NUMBER | sed 's/ .*//' ) + + local i=0 + for i in $( seq 1 6 ) + do + if [ $(( $LINES / 10 ** $i )) -gt 0 ] + then + : + else + break + fi + done + + echo $i +} + + +_list-number_lines() { + local FILE_TO_NUMBER="$1" + local PADDING=$( _list-zero_padding "$FILE_TO_NUMBER" ) + + sed = "$FILE_TO_NUMBER" \ + | sed "N; s/^/ /; s/ *\(.\{$PADDING,\}\)\n/\1 /" \ + | sed ''' + s/^ /00000/; + s/^ /0000/; + s/^ /000/; + s/^ /00/; + s/^ /0/; + ''' +} + +_list-sort_alphabetically() { + sort -f -k2 +} + +_list-add_priority_color() { + local FILE_TO_NUMBER="$1" + local PADDING=$( _list-zero_padding "$FILE_TO_NUMBER" ) + + sed '/^[0-9]\{'$PADDING'\} x /! { + s/\(.*(A).*\)/'$PRI_A'\1 '$DEFAULT'/g; + s/\(.*(B).*\)/'$PRI_B'\1 '$DEFAULT'/g; + s/\(.*(C).*\)/'$PRI_C'\1 '$DEFAULT'/g; + s/\(.*([D-Z]).*\)/'$PRI_X'\1 '$DEFAULT'/g; + }' +} + +_list-hide_priority_projects_contexts() { + sed 's/'${HIDE_PRIORITY_SUBSTITUTION:-^}'//g' \ + | sed 's/'${HIDE_PROJECTS_SUBSTITUTION:-^}'//g' \ + | sed 's/'${HIDE_CONTEXTS_SUBSTITUTION:-^}'//g' +} + # == HANDLE ACTION == action=$( printf "%s\n" "$1" | tr 'A-Z' 'a-z' ) @@ -508,35 +563,19 @@ case $action in item=$2 if [ -z "$item" ]; then echo -e "$( \ - sed = "$TODO_FILE" \ - | sed 'N; s/^/ /; s/ *\(.\{2,\}\)\n/\1 /' \ - | sed 's/^ /0/' \ - | sort -f -k2 \ - | sed '/^[0-9][0-9] x /! { - s/\(.*(A).*\)/'$PRI_A'\1 '$DEFAULT'/g; - s/\(.*(B).*\)/'$PRI_B'\1 '$DEFAULT'/g; - s/\(.*(C).*\)/'$PRI_C'\1 '$DEFAULT'/g; - s/\(.*([D-Z]).*\)/'$PRI_X'\1 '$DEFAULT'/g; - }' \ - | sed 's/'${HIDE_PRIORITY_SUBSTITUTION:-^}'//g' \ - | sed 's/'${HIDE_PROJECTS_SUBSTITUTION:-^}'//g' \ - | sed 's/'${HIDE_CONTEXTS_SUBSTITUTION:-^}'//g' \ + _list-number_lines "$TODO_FILE" \ + | _list-sort_alphabetically \ + | _list-add_priority_color "$TODO_FILE" \ + | _list-hide_priority_projects_contexts \ )" echo "--" NUMTASKS=$(wc -l "$TODO_FILE" | sed 's/^[[:space:]]*\([0-9]*\).*/\1/') echo "TODO: $NUMTASKS tasks in $TODO_FILE." else command=$( - sed = "$TODO_FILE" \ - | sed 'N; s/^/ /; s/ *\(.\{2,\}\)\n/\1 /' \ - | sed 's/^ /0/' \ - | sort -f -k2 \ - | sed '/^[0-9][0-9] x /! { - s/\(.*(A).*\)/'$PRI_A'\1 '$DEFAULT'/g; - s/\(.*(B).*\)/'$PRI_B'\1 '$DEFAULT'/g; - s/\(.*(C).*\)/'$PRI_C'\1 '$DEFAULT'/g; - s/\(.*([D-Z]).*\)/'$PRI_X'\1 '$DEFAULT'/g; - }' \ + _list-number_lines "$TODO_FILE" \ + | _list-sort_alphabetically \ + | _list-add_priority_color "$TODO_FILE" \ | grep -i $item ) shift @@ -547,10 +586,8 @@ case $action in done command=$( \ echo "$command" \ - | sort -f -k2 \ - | sed 's/'${HIDE_PRIORITY_SUBSTITUTION:-^}'//g' \ - | sed 's/'${HIDE_PROJECTS_SUBSTITUTION:-^}'//g' \ - | sed 's/'${HIDE_CONTEXTS_SUBSTITUTION:-^}'//g' \ + | _list-sort_alphabetically \ + | _list-hide_priority_projects_contexts \ ) echo -e "$command" fi @@ -561,9 +598,18 @@ case $action in cat "$TODO_FILE" "$DONE_FILE" > "$TMP_FILE" if [ -z "$item" ]; then - echo -e "`sed = "$TMP_FILE" | sed 'N; s/^/ /; s/ *\(.\{3,\}\)\n/\1 /' | sed 's/^ /00/' | sed 's/^ /0/' | sort -f -k2 | sed '/^[0-9][0-9][0-9] x /!s/\(.*(A).*\)/'$PRI_A'\1'$DEFAULT'/g' | sed '/^[0-9][0-9][0-9] x /!s/\(.*(B).*\)/'$PRI_B'\1'$DEFAULT'/g' | sed '/^[0-9][0-9][0-9] x /!s/\(.*(C).*\)/'$PRI_C'\1'$DEFAULT'/g' | sed '/^[0-9][0-9][0-9] x /!s/\(.*([A-Z]).*\)/'$PRI_X'\1'$DEFAULT'/'`" + echo -e "$( + _list-number_lines "$TMP_FILE" \ + | _list-sort_alphabetically \ + | _list-add_priority_color "$TMP_FILE" \ + )" else - command=`sed = "$TMP_FILE" | sed 'N; s/^/ /; s/ *\(.\{3,\}\)\n/\1 /' | sed 's/^ /00/' | sed 's/^ /0/' | sort -f -k2 | sed '/^[0-9][0-9][0-9] x /!s/\(.*(A).*\)/'$PRI_A'\1'$DEFAULT'/g' | sed '/^[0-9][0-9][0-9] x /!s/\(.*(B).*\)/'$PRI_B'\1'$DEFAULT'/g' | sed '/^[0-9][0-9][0-9] x /!s/\(.*(C).*\)/'$PRI_C'\1'$DEFAULT'/g' | sed '/^[0-9][0-9][0-9] x /!s/\(.*([A-Z]).*\)/'$PRI_X'\1'$DEFAULT'/' | grep -i $item ` + command=$( + _list-number_lines "$TMP_FILE" \ + | _list-sort_alphabetically \ + | _list-add_priority_color "$TMP_FILE" \ + | grep -i $item + ) shift shift for i in $* @@ -586,14 +632,23 @@ case $action in fi if [ -f "$src" ]; then if [ -z "$item" ]; then - echo -e "`sed = "$src" | sed 'N; s/^/ /; s/ *\(.\{2,\}\)\n/\1 /' | sed 's/^ /0/' | sort -f -k2 | sed '/^[0-9][0-9] x /!s/\(.*(A).*\)/'$PRI_A'\1'$DEFAULT'/g' | sed '/^[0-9][0-9] x /!s/\(.*(B).*\)/'$PRI_B'\1'$DEFAULT'/g' | sed '/^[0-9][0-9] x /!s/\(.*(C).*\)/'$PRI_C'\1'$DEFAULT'/g' | sed '/^[0-9][0-9] x /!s/\(.*([A-Z]).*\)/'$PRI_X'\1'$DEFAULT'/'`" + echo -e "$( + _list-number_lines "$src" \ + | _list-sort_alphabetically \ + | _list-add_priority_color "$src" + )" if [ $TODOTXT_VERBOSE = 1 ]; then echo "--" NUMTASKS=$( sed '/./!d' "$src" | wc -l | sed 's/^[[:space:]]*\([0-9]*\).*/\1/') echo "TODO: $NUMTASKS lines in $src." fi else - command=`sed = "$src" | sed 'N; s/^/ /; s/ *\(.\{2,\}\)\n/\1 /' | sed 's/^ /0/' | sort -f -k2 | sed '/^[0-9][0-9] x /!s/\(.*(A).*\)/'$PRI_A'\1'$DEFAULT'/g' | sed '/^[0-9][0-9] x /!s/\(.*(B).*\)/'$PRI_B'\1'$DEFAULT'/g' | sed '/^[0-9][0-9] x /!s/\(.*(C).*\)/'$PRI_C'\1'$DEFAULT'/g' | sed '/^[0-9][0-9] x /!s/\(.*([A-Z]).*\)/'$PRI_X'\1'$DEFAULT'/' | grep -i $item ` + command=$( + _list-number_lines "$src" \ + | _list-sort_alphabetically \ + | _list-add_priority_color "$src" \ + | grep -i $item + ) shift shift for i in $* @@ -623,7 +678,12 @@ case $action in note: PRIORITY must a single letter from A to Z." if [ -z "$pri" ]; then - echo -e "`sed = "$TODO_FILE" | sed 'N; s/^/ /; s/ *\(.\{2,\}\)\n/\1 /' | sed 's/^ /0/' | sort -f -k2 | sed 's/^ /0/' | sort -f -k2 | sed '/^[0-9][0-9] x /!s/\(.*(A).*\)/'$PRI_A'\1'$DEFAULT'/g' | sed '/^[0-9][0-9] x /!s/\(.*(B).*\)/'$PRI_B'\1'$DEFAULT'/g' | sed '/^[0-9][0-9] x /!s/\(.*(C).*\)/'$PRI_C'\1'$DEFAULT'/g' | sed '/^[0-9][0-9] x /!s/\(.*([A-Z]).*\)/'$PRI_X'\1'$DEFAULT'/'`" | grep \([A-Z]\) + echo -e "$( + _list-number_lines "$TODO_FILE" \ + | _list-sort_alphabetically \ + | _list-add_priority_color "$TODO_FILE" \ + | grep \([A-Z]\) + )" if [ $TODOTXT_VERBOSE = 1 ]; then echo "--" NUMTASKS=$(grep \([A-Z]\) "$TODO_FILE" | wc -l | sed 's/^[[:space:]]*\([0-9]*\).*/\1/') @@ -632,7 +692,12 @@ note: PRIORITY must a single letter from A to Z." else [[ "$pri" = +([A-Z]) ]] || die "$errmsg" - echo -e "`sed = "$TODO_FILE" | sed 'N; s/^/ /; s/ *\(.\{2,\}\)\n/\1 /' | sed 's/^ /0/' | sort -f -k2 | sed 's/^ /0/' | sort -f -k2 | sed '/^[0-9][0-9] x /!s/\(.*(A).*\)/'$PRI_A'\1'$DEFAULT'/g' | sed '/^[0-9][0-9] x /!s/\(.*(B).*\)/'$PRI_B'\1'$DEFAULT'/g' | sed '/^[0-9][0-9] x /!s/\(.*(C).*\)/'$PRI_C'\1'$DEFAULT'/g' | sed '/^[0-9][0-9] x /!s/\(.*([A-Z]).*\)/'$PRI_X'\1'$DEFAULT'/'`" | grep \($pri\) + echo -e "$( + _list-number_lines "$TODO_FILE" \ + | _list-sort_alphabetically \ + | _list-add_priority_color "$TODO_FILE" \ + | grep \($pri\) + )" if [ $TODOTXT_VERBOSE = 1 ]; then echo "--" NUMTASKS=$(grep \($pri\) "$TODO_FILE" | wc -l | sed 's/^[[:space:]]*\([0-9]*\).*/\1/') From f55f5e8b5ff77dac98c52322b6433299d5dcf575 Mon Sep 17 00:00:00 2001 From: "David A. Harding" Date: Thu, 12 Mar 2009 11:52:28 -0400 Subject: [PATCH 02/20] Make ls-Family Actions Use Listfile Backend Essentially, ls) shift exec "$TODO_SH" listfile "$TODO_FILE" "$@" ;; lsa) shift cat "$TODO_FILE" "$DONE_FILE" > "$TMP_FILE" exec $TODO_SH listfile "$TMP_FILE" "$@" ;; lsp) shift ## was "listpri" shift ## was priority exec $TODO_SH listfile "$TODO_FILE" "$pri" "$@" ;;; Also adds the following features: 1. Numbers are padded with up to five zeros (but only the minimum necessary), letting you list up to 999,999 tasks with the same formatting. 2. All ls-family commands hide context, priority, and project when the user sets those hide options. 3. Quoted arguments are passed on to grep as whole arguments, enabling the following: $ todo.sh ls buy a | head -n2 34 Buy a portable gas can 22 Buy door $ todo.sh ls "buy a" 34 Buy a portable gas can 4. listfile can take an absolute path. Any filename starting with a "/" will be treated as an absolute path; any other filename will be treated as relative to $TODO_DIR. Since a leading "/" would be striped by the operating system anyway under the old code, this is fully backward compatible. Contains the following regressions: 1. The ls verbose line count messages are more generic. 2. There is no verbose line count line for lspri. 3. I don't think listfile's absolute path feature will work on Windows. If it doesn't, either this patch needs to be thrown away, listall needs to be rewritten, or (my preference) $TMP_FILE needs to set as relative to $TODO_DIR. --- todo.sh | 308 +++++++++++++++++++++++--------------------------------- 1 file changed, 126 insertions(+), 182 deletions(-) diff --git a/todo.sh b/todo.sh index 3911c51..d6885bc 100755 --- a/todo.sh +++ b/todo.sh @@ -225,7 +225,7 @@ do unset HIDE_CONTEXTS_SUBSTITUTION else ## One or odd value -- hide context names - HIDE_CONTEXTS_SUBSTITUTION='[[:space:]]@[^[:space:]]\{1,\}' + export HIDE_CONTEXTS_SUBSTITUTION='[[:space:]]@[^[:space:]]\{1,\}' fi ;; '+' ) @@ -241,7 +241,7 @@ do unset HIDE_PROJECTS_SUBSTITUTION else ## One or odd value -- hide project names - HIDE_PROJECTS_SUBSTITUTION='[[:space:]][+][^[:space:]]\{1,\}' + export HIDE_PROJECTS_SUBSTITUTION='[[:space:]][+][^[:space:]]\{1,\}' fi ;; a ) @@ -275,7 +275,7 @@ do unset HIDE_PRIORITY_SUBSTITUTION else ## One or odd value -- hide priority labels - HIDE_PRIORITY_SUBSTITUTION="([A-Z])[[:space:]]" + export HIDE_PRIORITY_SUBSTITUTION="([A-Z])[[:space:]]" fi ;; t ) @@ -323,7 +323,7 @@ export TODO_SH [ -d "$TODO_DIR" ] || die "Fatal Error: $TODO_DIR is not a directory" cd "$TODO_DIR" || die "Fatal Error: Unable to cd to $TODO_DIR" -echo '' > "$TMP_FILE" || die "Fatal Error: Unable to write in $TODO_DIR" +[ -w "$TMP_FILE" ] || echo -n > $TMP_FILE || die "Fatal Error: Unable to write to $TMP_FILE" [ -f "$TODO_FILE" ] || cp /dev/null "$TODO_FILE" [ -f "$DONE_FILE" ] || cp /dev/null "$DONE_FILE" [ -f "$REPORT_FILE" ] || cp /dev/null "$REPORT_FILE" @@ -339,61 +339,6 @@ fi # === HEAVY LIFTING === shopt -s extglob -_list-zero_padding() { - local LINES=$( wc -l $FILE_TO_NUMBER | sed 's/ .*//' ) - - local i=0 - for i in $( seq 1 6 ) - do - if [ $(( $LINES / 10 ** $i )) -gt 0 ] - then - : - else - break - fi - done - - echo $i -} - - -_list-number_lines() { - local FILE_TO_NUMBER="$1" - local PADDING=$( _list-zero_padding "$FILE_TO_NUMBER" ) - - sed = "$FILE_TO_NUMBER" \ - | sed "N; s/^/ /; s/ *\(.\{$PADDING,\}\)\n/\1 /" \ - | sed ''' - s/^ /00000/; - s/^ /0000/; - s/^ /000/; - s/^ /00/; - s/^ /0/; - ''' -} - -_list-sort_alphabetically() { - sort -f -k2 -} - -_list-add_priority_color() { - local FILE_TO_NUMBER="$1" - local PADDING=$( _list-zero_padding "$FILE_TO_NUMBER" ) - - sed '/^[0-9]\{'$PADDING'\} x /! { - s/\(.*(A).*\)/'$PRI_A'\1 '$DEFAULT'/g; - s/\(.*(B).*\)/'$PRI_B'\1 '$DEFAULT'/g; - s/\(.*(C).*\)/'$PRI_C'\1 '$DEFAULT'/g; - s/\(.*([D-Z]).*\)/'$PRI_X'\1 '$DEFAULT'/g; - }' -} - -_list-hide_priority_projects_contexts() { - sed 's/'${HIDE_PRIORITY_SUBSTITUTION:-^}'//g' \ - | sed 's/'${HIDE_PROJECTS_SUBSTITUTION:-^}'//g' \ - | sed 's/'${HIDE_CONTEXTS_SUBSTITUTION:-^}'//g' -} - # == HANDLE ACTION == action=$( printf "%s\n" "$1" | tr 'A-Z' 'a-z' ) @@ -560,108 +505,124 @@ case $action in "list" | "ls" ) - item=$2 - if [ -z "$item" ]; then - echo -e "$( \ - _list-number_lines "$TODO_FILE" \ - | _list-sort_alphabetically \ - | _list-add_priority_color "$TODO_FILE" \ - | _list-hide_priority_projects_contexts \ - )" - echo "--" - NUMTASKS=$(wc -l "$TODO_FILE" | sed 's/^[[:space:]]*\([0-9]*\).*/\1/') - echo "TODO: $NUMTASKS tasks in $TODO_FILE." - else - command=$( - _list-number_lines "$TODO_FILE" \ - | _list-sort_alphabetically \ - | _list-add_priority_color "$TODO_FILE" \ - | grep -i $item - ) - shift - shift - for i in $* - do - command=`echo "$command" | grep -i $i ` - done - command=$( \ - echo "$command" \ - | _list-sort_alphabetically \ - | _list-hide_priority_projects_contexts \ - ) - echo -e "$command" - fi - cleanup ;; + shift + exec "$TODO_SH" listfile "$TODO_FILE" "$@" + ;; "listall" | "lsa" ) - item=$2 cat "$TODO_FILE" "$DONE_FILE" > "$TMP_FILE" - - if [ -z "$item" ]; then - echo -e "$( - _list-number_lines "$TMP_FILE" \ - | _list-sort_alphabetically \ - | _list-add_priority_color "$TMP_FILE" \ - )" - else - command=$( - _list-number_lines "$TMP_FILE" \ - | _list-sort_alphabetically \ - | _list-add_priority_color "$TMP_FILE" \ - | grep -i $item - ) - shift - shift - for i in $* - do - command=`echo "$command" | grep -i $i ` - done - command=`echo "$command" | sort -f -k2` - echo -e "$command" - fi - cleanup ;; - + shift + exec $TODO_SH listfile "$TMP_FILE" "$@" + ;; "listfile" | "lf" ) - src="$TODO_DIR/$2" - - if [ -z "$3" ]; then - item="" + ## If the file starts with a "/" use absolute path. Otherwise, make + ## it relative to $TODO_DIR + if [ "${2:0:1}" == / ] + then + ## Absolute path + src="$2" else - item=$3 + ## Relative path + src="$TODO_DIR/$2" fi - if [ -f "$src" ]; then - if [ -z "$item" ]; then - echo -e "$( - _list-number_lines "$src" \ - | _list-sort_alphabetically \ - | _list-add_priority_color "$src" - )" - if [ $TODOTXT_VERBOSE = 1 ]; then - echo "--" - NUMTASKS=$( sed '/./!d' "$src" | wc -l | sed 's/^[[:space:]]*\([0-9]*\).*/\1/') - echo "TODO: $NUMTASKS lines in $src." - fi - else - command=$( - _list-number_lines "$src" \ - | _list-sort_alphabetically \ - | _list-add_priority_color "$src" \ - | grep -i $item - ) - shift - shift - for i in $* - do - command=`echo "$command" | grep -i $i ` - done - command=`echo "$command" | sort -f -k2` - echo -e "$command" - fi - else + + ## Ensure the file exits before proceeding + if [ ! -f $src ] + then echo "TODO: File $src does not exist." fi - cleanup ;; + + ## Figure out how much padding we need to use + ## We need one level of padding for each power of 10 $LINES uses + LINES=$( wc -l "$src" | sed 's/ .*//' ) + i=0 + ## 6 places will pad tasks numbered less than 99,999, thus correctly + ## displaying up to 999,999 tasks. Anyone who's done a million tasks + ## should send me an email with your secret. + for i in $( seq 1 6 ) + do + if [ ! $(( $LINES / 10 ** $i )) -gt 0 ] + then + break + fi + done + PADDING=$i + unset i LINES + + item="${3:-}" + if [ -z "$item" ]; then + ## No search pattern specified, operate on whole file + echo -e "$( + sed = "$src" \ + | sed "N; s/^/ /; s/ *\(.\{$PADDING,\}\)\n/\1 /" \ + | sed ''' + s/^ /00000/; + s/^ /0000/; + s/^ /000/; + s/^ /00/; + s/^ /0/; + ''' \ + | sort -f -k2 \ + | sed ''' + /^[0-9]\{'$PADDING'\} x /! { + s/\(.*(A).*\)/'$PRI_A'\1 '$DEFAULT'/g; + s/\(.*(B).*\)/'$PRI_B'\1 '$DEFAULT'/g; + s/\(.*(C).*\)/'$PRI_C'\1 '$DEFAULT'/g; + s/\(.*([D-Z]).*\)/'$PRI_X'\1 '$DEFAULT'/g; + } + s/'${HIDE_PRIORITY_SUBSTITUTION:-^}'//g + s/'${HIDE_PROJECTS_SUBSTITUTION:-^}'//g + s/'${HIDE_CONTEXTS_SUBSTITUTION:-^}'//g + ''' \ + )" + if [ $TODOTXT_VERBOSE = 1 ]; then + echo "--" + NUMTASKS=$( sed '/./!d' "$src" | wc -l | sed 's/^[[:space:]]*\([0-9]*\).*/\1/') + echo "TODO: $NUMTASKS lines shown from $src" + fi + else + ## Search pattern or patterns specified; parse the file and then + ## grep for each pattern afterwords. + command=$( + sed = "$src" \ + | sed "N; s/^/ /; s/ *\(.\{$PADDING,\}\)\n/\1 /" \ + | sed ''' + s/^ /00000/; + s/^ /0000/; + s/^ /000/; + s/^ /00/; + s/^ /0/; + ''' \ + | sort -f -k2 \ + | sed ''' + /^[0-9]\{'$PADDING'\} x /! { + s/\(.*(A).*\)/'$PRI_A'\1 '$DEFAULT'/g; + s/\(.*(B).*\)/'$PRI_B'\1 '$DEFAULT'/g; + s/\(.*(C).*\)/'$PRI_C'\1 '$DEFAULT'/g; + s/\(.*([D-Z]).*\)/'$PRI_X'\1 '$DEFAULT'/g; + } + ''' \ + | grep -i "$item" + ) + shift ## was "listfile" + shift ## was first search pattern + for i in "$@" + do + command=`echo "$command" | grep -i "$i" ` + done + command=$( echo "$command" \ + | sort -f -k2 \ + | sed ''' + s/'${HIDE_PRIORITY_SUBSTITUTION:-^}'//g + s/'${HIDE_PROJECTS_SUBSTITUTION:-^}'//g + s/'${HIDE_CONTEXTS_SUBSTITUTION:-^}'//g + ''' \ + ) + echo -e "$command" + fi + cleanup + ;; "listcon" | "lsc" ) gawk '{for(i = 1; i <= NF; i++) print $i}' "$TODO_FILE" | grep '@' | sort | uniq @@ -673,38 +634,21 @@ case $action in "listpri" | "lsp" ) - pri=$( printf "%s\n" "$2" | tr 'a-z' 'A-Z' ) - errmsg="usage: $0 listpri PRIORITY -note: PRIORITY must a single letter from A to Z." - - if [ -z "$pri" ]; then - echo -e "$( - _list-number_lines "$TODO_FILE" \ - | _list-sort_alphabetically \ - | _list-add_priority_color "$TODO_FILE" \ - | grep \([A-Z]\) - )" - if [ $TODOTXT_VERBOSE = 1 ]; then - echo "--" - NUMTASKS=$(grep \([A-Z]\) "$TODO_FILE" | wc -l | sed 's/^[[:space:]]*\([0-9]*\).*/\1/') - echo "TODO: $NUMTASKS prioritized tasks in $TODO_FILE." - fi + if [ "${2:-}" ] + then + pri=$( printf "%s\n" "$2" | tr 'a-z' 'A-Z' | grep '^[A-Z]$' ) || { + die "usage: $0 listpri PRIORITY + note: PRIORITY must a single letter from A to Z." + } else - [[ "$pri" = +([A-Z]) ]] || die "$errmsg" - - echo -e "$( - _list-number_lines "$TODO_FILE" \ - | _list-sort_alphabetically \ - | _list-add_priority_color "$TODO_FILE" \ - | grep \($pri\) - )" - if [ $TODOTXT_VERBOSE = 1 ]; then - echo "--" - NUMTASKS=$(grep \($pri\) "$TODO_FILE" | wc -l | sed 's/^[[:space:]]*\([0-9]*\).*/\1/') - echo "TODO: $NUMTASKS tasks prioritized $pri in $TODO_FILE." - fi + pri="[A-Z]" fi - cleanup;; + pri="($pri)" + + shift ## was "listpri" + shift ## was priority + exec $TODO_SH listfile "$TODO_FILE" "$pri" "$@" + ;; "move" | "mv" ) # replace moved line with a blank line when TODOTXT_PRESERVE_LINE_NUMBERS is 1 From ee59233c36928c1e26bb08a7d0b7570c4277ff42 Mon Sep 17 00:00:00 2001 From: "David A. Harding" Date: Thu, 12 Mar 2009 22:23:59 -0400 Subject: [PATCH 03/20] Replaced Grep Loop, Fixed Sed Bug, Some Small Changes Implemented several suggestions by Jacobo de Vera: > 1. [lines 539-551]: [...] replace the for loop with simply this: > PADDING=${#LINES} > 2. [line 558]: As the script now supports a 6 digit number of tasks, > the first substitution should add 5 spaces instead of 2 > 3. [lines 606-613]: The first search item is processed before the for > loop, and the loop does the same for the rest. Wouldn't making this > more general make the code more readable? The changes for suggestion #3 let me add a new feature: when VERBOSE is enabled, the summary line prints more info -- and it prints it on every run: $ todo.sh ls "buy a" 34 Buy a portable gas can @errands +safe -- TODO: 1 of 49 tasks shown from /home/harding/var/git/todo/todo.txt Also, generalizing and centralizing the code added a small but measurable speed increase. No new known regressions were introduced. --- todo.sh | 146 ++++++++++++++++++++++---------------------------------- 1 file changed, 58 insertions(+), 88 deletions(-) diff --git a/todo.sh b/todo.sh index d6885bc..cf5219a 100755 --- a/todo.sh +++ b/todo.sh @@ -516,15 +516,17 @@ case $action in ;; "listfile" | "lf" ) + shift ## was "listfile", new $1 is filename + ## If the file starts with a "/" use absolute path. Otherwise, make ## it relative to $TODO_DIR - if [ "${2:0:1}" == / ] + if [ "${1:0:1}" == / ] then - ## Absolute path - src="$2" + ## Absolute path + src="$1" else - ## Relative path - src="$TODO_DIR/$2" + ## Relative path + src="$TODO_DIR/$1" fi ## Ensure the file exits before proceeding @@ -533,94 +535,62 @@ case $action in echo "TODO: File $src does not exist." fi + ## Get our search arguments, if any + shift ## was file name, new $1 is first search term + for search_term in "$@" + do + filter_command="${filter_command:-} ${filter_command:+|} \ + grep -i \"$search_term\" " + done + ## Figure out how much padding we need to use ## We need one level of padding for each power of 10 $LINES uses LINES=$( wc -l "$src" | sed 's/ .*//' ) - i=0 - ## 6 places will pad tasks numbered less than 99,999, thus correctly - ## displaying up to 999,999 tasks. Anyone who's done a million tasks - ## should send me an email with your secret. - for i in $( seq 1 6 ) - do - if [ ! $(( $LINES / 10 ** $i )) -gt 0 ] - then - break - fi - done - PADDING=$i - unset i LINES + PADDING=${#LINES} - item="${3:-}" - if [ -z "$item" ]; then - ## No search pattern specified, operate on whole file - echo -e "$( - sed = "$src" \ - | sed "N; s/^/ /; s/ *\(.\{$PADDING,\}\)\n/\1 /" \ - | sed ''' - s/^ /00000/; - s/^ /0000/; - s/^ /000/; - s/^ /00/; - s/^ /0/; - ''' \ - | sort -f -k2 \ - | sed ''' - /^[0-9]\{'$PADDING'\} x /! { - s/\(.*(A).*\)/'$PRI_A'\1 '$DEFAULT'/g; - s/\(.*(B).*\)/'$PRI_B'\1 '$DEFAULT'/g; - s/\(.*(C).*\)/'$PRI_C'\1 '$DEFAULT'/g; - s/\(.*([D-Z]).*\)/'$PRI_X'\1 '$DEFAULT'/g; - } - s/'${HIDE_PRIORITY_SUBSTITUTION:-^}'//g - s/'${HIDE_PROJECTS_SUBSTITUTION:-^}'//g - s/'${HIDE_CONTEXTS_SUBSTITUTION:-^}'//g - ''' \ - )" - if [ $TODOTXT_VERBOSE = 1 ]; then - echo "--" - NUMTASKS=$( sed '/./!d' "$src" | wc -l | sed 's/^[[:space:]]*\([0-9]*\).*/\1/') - echo "TODO: $NUMTASKS lines shown from $src" - fi - else - ## Search pattern or patterns specified; parse the file and then - ## grep for each pattern afterwords. - command=$( - sed = "$src" \ - | sed "N; s/^/ /; s/ *\(.\{$PADDING,\}\)\n/\1 /" \ - | sed ''' - s/^ /00000/; - s/^ /0000/; - s/^ /000/; - s/^ /00/; - s/^ /0/; - ''' \ - | sort -f -k2 \ - | sed ''' - /^[0-9]\{'$PADDING'\} x /! { - s/\(.*(A).*\)/'$PRI_A'\1 '$DEFAULT'/g; - s/\(.*(B).*\)/'$PRI_B'\1 '$DEFAULT'/g; - s/\(.*(C).*\)/'$PRI_C'\1 '$DEFAULT'/g; - s/\(.*([D-Z]).*\)/'$PRI_X'\1 '$DEFAULT'/g; - } - ''' \ - | grep -i "$item" - ) - shift ## was "listfile" - shift ## was first search pattern - for i in "$@" - do - command=`echo "$command" | grep -i "$i" ` - done - command=$( echo "$command" \ - | sort -f -k2 \ - | sed ''' - s/'${HIDE_PRIORITY_SUBSTITUTION:-^}'//g - s/'${HIDE_PROJECTS_SUBSTITUTION:-^}'//g - s/'${HIDE_CONTEXTS_SUBSTITUTION:-^}'//g - ''' \ - ) - echo -e "$command" + ## Search pattern or patterns specified; parse the file and then + ## grep for each pattern afterwords. + + command=$( + sed = "$src" \ + | sed "N; s/^/ /; s/ *\(.\{$PADDING,\}\)\n/\1 /" \ + | sed ''' + s/^ /00000/; + s/^ /0000/; + s/^ /000/; + s/^ /00/; + s/^ /0/; + ''' \ + | sort -f -k2 \ + | sed ''' + /^[0-9]\{'$PADDING'\} x /! { + s/\(.*(A).*\)/'$PRI_A'\1 '$DEFAULT'/g; + s/\(.*(B).*\)/'$PRI_B'\1 '$DEFAULT'/g; + s/\(.*(C).*\)/'$PRI_C'\1 '$DEFAULT'/g; + s/\(.*([D-Z]).*\)/'$PRI_X'\1 '$DEFAULT'/g; + } + ''' \ + | eval ${filter_command:-cat} \ + | sort -f -k2 \ + | sed ''' + s/'${HIDE_PRIORITY_SUBSTITUTION:-^}'//g + s/'${HIDE_PROJECTS_SUBSTITUTION:-^}'//g + s/'${HIDE_CONTEXTS_SUBSTITUTION:-^}'//g + ''' \ + ) + echo -e "$command" + + if [ $TODOTXT_VERBOSE == 1 ]; then + echo "--" + NUMTASKS=$( + sed '/^$/d' "$src" \ + | eval ${filter_command:-cat} \ + | wc -l \ + | sed 's/^[[:space:]]*\([0-9]*\).*/\1/' \ + ) + echo "TODO: $NUMTASKS of $LINES tasks shown from $src" fi + cleanup ;; From 448cecb91db28a9e59bc952ae2b78342147e5da3 Mon Sep 17 00:00:00 2001 From: "David A. Harding" Date: Fri, 13 Mar 2009 11:00:34 -0400 Subject: [PATCH 04/20] Simplify and Reused Code to Print ls Summary Line --- todo.sh | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/todo.sh b/todo.sh index cf5219a..13ec09c 100755 --- a/todo.sh +++ b/todo.sh @@ -581,13 +581,8 @@ case $action in echo -e "$command" if [ $TODOTXT_VERBOSE == 1 ]; then + NUMTASKS=$( echo -e "$command" | wc -l | sed 's/ .*//' ) echo "--" - NUMTASKS=$( - sed '/^$/d' "$src" \ - | eval ${filter_command:-cat} \ - | wc -l \ - | sed 's/^[[:space:]]*\([0-9]*\).*/\1/' \ - ) echo "TODO: $NUMTASKS of $LINES tasks shown from $src" fi From a03a3bf66bdf82020b6e02787195f2574379409a Mon Sep 17 00:00:00 2001 From: "David A. Harding" Date: Fri, 13 Mar 2009 11:59:03 -0400 Subject: [PATCH 05/20] Fixed Windows Regression, New _list Function Commit f55f5e8b5ff77d introduced a known regression on Windows that prevented users from using configuration files starting with C:\. The following logic fixes this: + ## If the file starts with a "/" use absolute path. Otherwise, + ## try to find it in either $TODO_DIR or using a relative path + if [ "${1:0:1}" == / ] + then + ## Absolute path + src="$FILE" + elif [ -f "$TODO_DIR/$FILE" ] + then + ## Path relative to todo.sh directory + src="$TODO_DIR/$1" + elif [ -f "$FILE" ] + then + ## Path relative to current working directory + src="$FILE" + else + echo "TODO: File $FILE does not exist." + exit 1 + fi New _list function takes a filename and a list of search expressions. We no longer use exec to call ourselves recursively. --- todo.sh | 173 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 95 insertions(+), 78 deletions(-) diff --git a/todo.sh b/todo.sh index 13ec09c..26da8cc 100755 --- a/todo.sh +++ b/todo.sh @@ -321,7 +321,7 @@ export TODO_SH [ -z "$1" ] && usage [ -d "$TODO_DIR" ] || die "Fatal Error: $TODO_DIR is not a directory" -cd "$TODO_DIR" || die "Fatal Error: Unable to cd to $TODO_DIR" +( cd "$TODO_DIR" ) || die "Fatal Error: Unable to cd to $TODO_DIR" [ -w "$TMP_FILE" ] || echo -n > $TMP_FILE || die "Fatal Error: Unable to write to $TMP_FILE" [ -f "$TODO_FILE" ] || cp /dev/null "$TODO_FILE" @@ -339,6 +339,79 @@ fi # === HEAVY LIFTING === shopt -s extglob +_list() { + local FILE="$1" + ## If the file starts with a "/" use absolute path. Otherwise, + ## try to find it in either $TODO_DIR or using a relative path + if [ "${1:0:1}" == / ] + then + ## Absolute path + src="$FILE" + elif [ -f "$TODO_DIR/$FILE" ] + then + ## Path relative to todo.sh directory + src="$TODO_DIR/$1" + elif [ -f "$FILE" ] + then + ## Path relative to current working directory + src="$FILE" + else + echo "TODO: File $FILE does not exist." + exit 1 + fi + + ## Get our search arguments, if any + shift ## was file name, new $1 is first search term + for search_term in "$@" + do + filter_command="${filter_command:-} ${filter_command:+|} \ + grep -i \"$search_term\" " + done + + ## Figure out how much padding we need to use + ## We need one level of padding for each power of 10 $LINES uses + LINES=$( wc -l "$src" | sed 's/ .*//' ) + PADDING=${#LINES} + + ## Search pattern or patterns specified; parse the file and then + ## grep for each pattern afterwords. + + command=$( + sed = "$src" \ + | sed "N; s/^/ /; s/ *\(.\{$PADDING,\}\)\n/\1 /" \ + | sed ''' + s/^ /00000/; + s/^ /0000/; + s/^ /000/; + s/^ /00/; + s/^ /0/; + ''' \ + | sort -f -k2 \ + | sed ''' + /^[0-9]\{'$PADDING'\} x /! { + s/\(.*(A).*\)/'$PRI_A'\1 '$DEFAULT'/g; + s/\(.*(B).*\)/'$PRI_B'\1 '$DEFAULT'/g; + s/\(.*(C).*\)/'$PRI_C'\1 '$DEFAULT'/g; + s/\(.*([D-Z]).*\)/'$PRI_X'\1 '$DEFAULT'/g; + } + ''' \ + | eval ${filter_command:-cat} \ + | sort -f -k2 \ + | sed ''' + s/'${HIDE_PRIORITY_SUBSTITUTION:-^}'//g + s/'${HIDE_PROJECTS_SUBSTITUTION:-^}'//g + s/'${HIDE_CONTEXTS_SUBSTITUTION:-^}'//g + ''' \ + ) + echo -e "$command" + + if [ $TODOTXT_VERBOSE == 1 ]; then + NUMTASKS=$( echo -e "$command" | wc -l | sed 's/ .*//' ) + echo "--" + echo "TODO: $NUMTASKS of $LINES tasks shown from $src" + fi +} + # == HANDLE ACTION == action=$( printf "%s\n" "$1" | tr 'A-Z' 'a-z' ) @@ -505,86 +578,27 @@ case $action in "list" | "ls" ) - shift - exec "$TODO_SH" listfile "$TODO_FILE" "$@" + shift ## Was ls; new $1 is first search term + _list "$TODO_FILE" "$@" + + cleanup ;; "listall" | "lsa" ) + shift ## Was lsa; new $1 is first search term + cat "$TODO_FILE" "$DONE_FILE" > "$TMP_FILE" - shift - exec $TODO_SH listfile "$TMP_FILE" "$@" + _list "$TMP_FILE" "$@" + + cleanup ;; "listfile" | "lf" ) - shift ## was "listfile", new $1 is filename + shift ## Was listfile, next $1 is file name + FILE="$1" + shift ## Was filename; next $1 is first search term - ## If the file starts with a "/" use absolute path. Otherwise, make - ## it relative to $TODO_DIR - if [ "${1:0:1}" == / ] - then - ## Absolute path - src="$1" - else - ## Relative path - src="$TODO_DIR/$1" - fi - - ## Ensure the file exits before proceeding - if [ ! -f $src ] - then - echo "TODO: File $src does not exist." - fi - - ## Get our search arguments, if any - shift ## was file name, new $1 is first search term - for search_term in "$@" - do - filter_command="${filter_command:-} ${filter_command:+|} \ - grep -i \"$search_term\" " - done - - ## Figure out how much padding we need to use - ## We need one level of padding for each power of 10 $LINES uses - LINES=$( wc -l "$src" | sed 's/ .*//' ) - PADDING=${#LINES} - - ## Search pattern or patterns specified; parse the file and then - ## grep for each pattern afterwords. - - command=$( - sed = "$src" \ - | sed "N; s/^/ /; s/ *\(.\{$PADDING,\}\)\n/\1 /" \ - | sed ''' - s/^ /00000/; - s/^ /0000/; - s/^ /000/; - s/^ /00/; - s/^ /0/; - ''' \ - | sort -f -k2 \ - | sed ''' - /^[0-9]\{'$PADDING'\} x /! { - s/\(.*(A).*\)/'$PRI_A'\1 '$DEFAULT'/g; - s/\(.*(B).*\)/'$PRI_B'\1 '$DEFAULT'/g; - s/\(.*(C).*\)/'$PRI_C'\1 '$DEFAULT'/g; - s/\(.*([D-Z]).*\)/'$PRI_X'\1 '$DEFAULT'/g; - } - ''' \ - | eval ${filter_command:-cat} \ - | sort -f -k2 \ - | sed ''' - s/'${HIDE_PRIORITY_SUBSTITUTION:-^}'//g - s/'${HIDE_PROJECTS_SUBSTITUTION:-^}'//g - s/'${HIDE_CONTEXTS_SUBSTITUTION:-^}'//g - ''' \ - ) - echo -e "$command" - - if [ $TODOTXT_VERBOSE == 1 ]; then - NUMTASKS=$( echo -e "$command" | wc -l | sed 's/ .*//' ) - echo "--" - echo "TODO: $NUMTASKS of $LINES tasks shown from $src" - fi + _list "$FILE" "$@" cleanup ;; @@ -599,20 +613,23 @@ case $action in "listpri" | "lsp" ) - if [ "${2:-}" ] + shift ## was "listpri" + + if [ "${1:-}" ] then - pri=$( printf "%s\n" "$2" | tr 'a-z' 'A-Z' | grep '^[A-Z]$' ) || { + ## A priority was specified + pri=$( printf "%s\n" "$1" | tr 'a-z' 'A-Z' | grep '^[A-Z]$' ) || { die "usage: $0 listpri PRIORITY note: PRIORITY must a single letter from A to Z." } else + ## No priority specified; show all priority tasks pri="[A-Z]" fi pri="($pri)" - - shift ## was "listpri" shift ## was priority - exec $TODO_SH listfile "$TODO_FILE" "$pri" "$@" + + _list "$TODO_FILE" "$pri" "$@" ;; "move" | "mv" ) From d6f00ca42ff178ea8a645eb24abb7d8ab147ef65 Mon Sep 17 00:00:00 2001 From: "David A. Harding" Date: Fri, 13 Mar 2009 12:14:08 -0400 Subject: [PATCH 06/20] Minor formatting and comments changes --- todo.sh | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/todo.sh b/todo.sh index 26da8cc..dd186b1 100755 --- a/todo.sh +++ b/todo.sh @@ -353,11 +353,11 @@ _list() { src="$TODO_DIR/$1" elif [ -f "$FILE" ] then - ## Path relative to current working directory - src="$FILE" + ## Path relative to current working directory + src="$FILE" else echo "TODO: File $FILE does not exist." - exit 1 + exit 1 fi ## Get our search arguments, if any @@ -373,11 +373,10 @@ _list() { LINES=$( wc -l "$src" | sed 's/ .*//' ) PADDING=${#LINES} - ## Search pattern or patterns specified; parse the file and then - ## grep for each pattern afterwords. - + ## Number, sort, and mangle the file, then run the filter command, + ## then mangle the file some more command=$( - sed = "$src" \ + sed = "$src" \ | sed "N; s/^/ /; s/ *\(.\{$PADDING,\}\)\n/\1 /" \ | sed ''' s/^ /00000/; @@ -386,7 +385,7 @@ _list() { s/^ /00/; s/^ /0/; ''' \ - | sort -f -k2 \ + | sort -f -k2 \ | sed ''' /^[0-9]\{'$PADDING'\} x /! { s/\(.*(A).*\)/'$PRI_A'\1 '$DEFAULT'/g; @@ -394,7 +393,7 @@ _list() { s/\(.*(C).*\)/'$PRI_C'\1 '$DEFAULT'/g; s/\(.*([D-Z]).*\)/'$PRI_X'\1 '$DEFAULT'/g; } - ''' \ + ''' \ | eval ${filter_command:-cat} \ | sort -f -k2 \ | sed ''' @@ -617,14 +616,14 @@ case $action in if [ "${1:-}" ] then - ## A priority was specified - pri=$( printf "%s\n" "$1" | tr 'a-z' 'A-Z' | grep '^[A-Z]$' ) || { - die "usage: $0 listpri PRIORITY - note: PRIORITY must a single letter from A to Z." - } + ## A priority was specified + pri=$( printf "%s\n" "$1" | tr 'a-z' 'A-Z' | grep '^[A-Z]$' ) || { + die "usage: $0 listpri PRIORITY + note: PRIORITY must a single letter from A to Z." + } else - ## No priority specified; show all priority tasks - pri="[A-Z]" + ## No priority specified; show all priority tasks + pri="[A-Z]" fi pri="($pri)" shift ## was priority From ed8e8e24d94f1fa7fe7a299abfaac9ec766c3a43 Mon Sep 17 00:00:00 2001 From: Ed Blackman Date: Fri, 13 Mar 2009 16:34:25 -0400 Subject: [PATCH 07/20] Consistent spacing for \ continuation in _list --- todo.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/todo.sh b/todo.sh index dd186b1..10416b5 100755 --- a/todo.sh +++ b/todo.sh @@ -394,13 +394,13 @@ _list() { s/\(.*([D-Z]).*\)/'$PRI_X'\1 '$DEFAULT'/g; } ''' \ - | eval ${filter_command:-cat} \ - | sort -f -k2 \ + | eval ${filter_command:-cat} \ + | sort -f -k2 \ | sed ''' s/'${HIDE_PRIORITY_SUBSTITUTION:-^}'//g s/'${HIDE_PROJECTS_SUBSTITUTION:-^}'//g s/'${HIDE_CONTEXTS_SUBSTITUTION:-^}'//g - ''' \ + ''' \ ) echo -e "$command" From 37a7bb0e8a92a918836a13f52282108cb97d74c0 Mon Sep 17 00:00:00 2001 From: Ed Blackman Date: Fri, 13 Mar 2009 16:40:55 -0400 Subject: [PATCH 08/20] Use relative rather than absolute filename in status message --- todo.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/todo.sh b/todo.sh index 10416b5..21a679c 100755 --- a/todo.sh +++ b/todo.sh @@ -407,7 +407,7 @@ _list() { if [ $TODOTXT_VERBOSE == 1 ]; then NUMTASKS=$( echo -e "$command" | wc -l | sed 's/ .*//' ) echo "--" - echo "TODO: $NUMTASKS of $LINES tasks shown from $src" + echo "TODO: $NUMTASKS of $LINES tasks shown from $FILE" fi } From cf3c5312bff5d57a8077d6c4a60a89370c794d96 Mon Sep 17 00:00:00 2001 From: Ed Blackman Date: Fri, 13 Mar 2009 17:02:31 -0400 Subject: [PATCH 09/20] Export _list for call by extensions --- todo.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/todo.sh b/todo.sh index 21a679c..a8025b4 100755 --- a/todo.sh +++ b/todo.sh @@ -411,6 +411,8 @@ _list() { fi } +export -f _list + # == HANDLE ACTION == action=$( printf "%s\n" "$1" | tr 'A-Z' 'a-z' ) From 9ab77253db7ef4fb0678658cfe61956daa772e0d Mon Sep 17 00:00:00 2001 From: Ed Blackman Date: Fri, 13 Mar 2009 18:17:16 -0400 Subject: [PATCH 10/20] Implement pre and post filters in _list --- todo.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/todo.sh b/todo.sh index a8025b4..b9298f0 100755 --- a/todo.sh +++ b/todo.sh @@ -362,12 +362,19 @@ _list() { ## Get our search arguments, if any shift ## was file name, new $1 is first search term + + filter_command="${prefilter_command:-}" + for search_term in "$@" do filter_command="${filter_command:-} ${filter_command:+|} \ grep -i \"$search_term\" " done + [ -n "$postfilter_command" ] && { + filter_command="${filter_command:-}${filter_command:+ | }${postfilter_command:-}" + } + ## Figure out how much padding we need to use ## We need one level of padding for each power of 10 $LINES uses LINES=$( wc -l "$src" | sed 's/ .*//' ) From 6be78ca5fafe971d98cd100fef8e71402b33378c Mon Sep 17 00:00:00 2001 From: "David A. Harding" Date: Fri, 13 Mar 2009 22:21:48 -0400 Subject: [PATCH 11/20] Added Exclusion Syntax by Jacobo de Vera -keyword or -"key phrase" should exclude those terms from the output. --- todo.sh | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/todo.sh b/todo.sh index b9298f0..a38e713 100755 --- a/todo.sh +++ b/todo.sh @@ -363,16 +363,31 @@ _list() { ## Get our search arguments, if any shift ## was file name, new $1 is first search term - filter_command="${prefilter_command:-}" + ## Prefix the filter_command with the pre_filter_command + filter_command="${pre_filter_command:-}" for search_term in "$@" do - filter_command="${filter_command:-} ${filter_command:+|} \ - grep -i \"$search_term\" " + ## See if the first character of $search_term is a dash + if [ ${search_term:0:1} != '-' ] + then + ## First character isn't a dash: hide lines that don't match + ## this $search_term + filter_command="${filter_command:-} ${filter_command:+|} \ + grep -i \"$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_command="${filter_command:-} ${filter_command:+|} \ + grep -v -i \"${search_term:1}\" " + fi done - [ -n "$postfilter_command" ] && { - filter_command="${filter_command:-}${filter_command:+ | }${postfilter_command:-}" + ## If post_filter_command is set, append it to the filter_command + [ -n "$post_hilter_command" ] && { + filter_command="${filter_command:-}${filter_command:+ | }${post_filter_command:-}" } ## Figure out how much padding we need to use From ab786075068efaeae587cf2dc5559e55a0e5bc79 Mon Sep 17 00:00:00 2001 From: Ed Blackman Date: Sat, 14 Mar 2009 01:15:52 -0400 Subject: [PATCH 12/20] Use sed line counting to replace 'wc -l' --- todo.sh | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/todo.sh b/todo.sh index a38e713..b6da6a3 100755 --- a/todo.sh +++ b/todo.sh @@ -392,7 +392,7 @@ _list() { ## Figure out how much padding we need to use ## We need one level of padding for each power of 10 $LINES uses - LINES=$( wc -l "$src" | sed 's/ .*//' ) + LINES=$( sed -ne '$ =' < "$src" ) PADDING=${#LINES} ## Number, sort, and mangle the file, then run the filter command, @@ -427,9 +427,10 @@ _list() { echo -e "$command" if [ $TODOTXT_VERBOSE == 1 ]; then - NUMTASKS=$( echo -e "$command" | wc -l | sed 's/ .*//' ) + NUMTASKS=$( echo -e "$command" | sed -ne '/^$/d' -e '$ =' ) + echo "--" - echo "TODO: $NUMTASKS of $LINES tasks shown from $FILE" + echo "TODO: ${NUMTASKS:-0} of $LINES tasks shown from $FILE" fi } @@ -471,7 +472,7 @@ case $action in input="$now $input" fi echo "$input" >> "$TODO_FILE" - TASKNUM=$(wc -l "$TODO_FILE" | sed 's/^[[:space:]]*\([0-9]*\).*/\1/') + TASKNUM=$(sed -ne "$ =" < "$TODO_FILE") [[ $TODOTXT_VERBOSE = 1 ]] && echo "TODO: '$input' added on line $TASKNUM." cleanup;; @@ -485,7 +486,7 @@ case $action in if [ -f "$dest" ]; then echo "$input" >> "$dest" - TASKNUM=$(wc -l "$dest" | sed 's/^[[:space:]]*\([0-9]*\).*/\1/') + TASKNUM=$(sed -ne "$ =" < "$dest") [[ $TODOTXT_VERBOSE = 1 ]] && echo "TODO: '$input' added to $dest on line $TASKNUM." else echo "TODO: Destination file $dest does not exist." @@ -781,15 +782,15 @@ note: PRIORITY must be anywhere from A to Z." sed '/^x /!d' "$TODO_FILE" >> "$DONE_FILE" sed -i.bak '/^x /d' "$TODO_FILE" - NUMLINES=$(wc -l "$TODO_FILE" | sed 's/^[[:space:]]*\([0-9]*\).*/\1/') - if [ $NUMLINES = "0" ]; then + NUMLINES=$( sed -ne '$ =' < "$TODO_FILE" ) + if [ ${NUMLINES:-0} = "0" ]; then echo "datetime todos dones" >> "$REPORT_FILE" fi #now report - TOTAL=$(cat "$TODO_FILE" | wc -l | sed 's/^[ \t]*//') - TDONE=$(cat "$DONE_FILE" | wc -l | sed 's/^[ \t]*//') - TECHO=$(echo $(date +%Y-%m-%d-%T); echo ' '; echo $TOTAL; echo ' '; - echo $TDONE) + TOTAL=$( sed -ne '$ =' < "$TODO_FILE" ) + TDONE=$( sed -ne '$ =' < "$DONE_FILE" ) + TECHO=$(echo $(date +%Y-%m-%d-%T); echo ' '; echo ${TOTAL:-0}; echo ' '; + echo ${TDONE:-0}) echo $TECHO >> "$REPORT_FILE" [[ $TODOTXT_VERBOSE = 1 ]] && echo "TODO: Report file updated." cat "$REPORT_FILE" From 12bbf8fe67c764fb3a310db70781188bcb53573b Mon Sep 17 00:00:00 2001 From: Ed Blackman Date: Sat, 14 Mar 2009 01:17:46 -0400 Subject: [PATCH 13/20] Fix spacing (tabs to spaces) --- todo.sh | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/todo.sh b/todo.sh index b6da6a3..c633485 100755 --- a/todo.sh +++ b/todo.sh @@ -368,21 +368,21 @@ _list() { for search_term in "$@" do - ## See if the first character of $search_term is a dash - if [ ${search_term:0:1} != '-' ] - then - ## First character isn't a dash: hide lines that don't match - ## this $search_term - filter_command="${filter_command:-} ${filter_command:+|} \ - grep -i \"$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_command="${filter_command:-} ${filter_command:+|} \ - grep -v -i \"${search_term:1}\" " - fi + ## See if the first character of $search_term is a dash + if [ ${search_term:0:1} != '-' ] + then + ## First character isn't a dash: hide lines that don't match + ## this $search_term + filter_command="${filter_command:-} ${filter_command:+|} \ + grep -i \"$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_command="${filter_command:-} ${filter_command:+|} \ + grep -v -i \"${search_term:1}\" " + fi done ## If post_filter_command is set, append it to the filter_command From 02980ae7eebe297ddf3696eb4719a8b893496ef4 Mon Sep 17 00:00:00 2001 From: Ed Blackman Date: Sat, 14 Mar 2009 01:18:42 -0400 Subject: [PATCH 14/20] Fix "post_hilter_command" typo --- todo.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/todo.sh b/todo.sh index c633485..0e8f1f7 100755 --- a/todo.sh +++ b/todo.sh @@ -386,7 +386,7 @@ _list() { done ## If post_filter_command is set, append it to the filter_command - [ -n "$post_hilter_command" ] && { + [ -n "$post_filter_command" ] && { filter_command="${filter_command:-}${filter_command:+ | }${post_filter_command:-}" } From f8b2646b92ce031ffda976154327d09e888dcf98 Mon Sep 17 00:00:00 2001 From: "David A. Harding" Date: Sat, 14 Mar 2009 09:35:10 -0400 Subject: [PATCH 15/20] Don't Echo Empty Lines & Streamline Sed wc -l Syntax --- todo.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/todo.sh b/todo.sh index 0e8f1f7..1042b5f 100755 --- a/todo.sh +++ b/todo.sh @@ -392,7 +392,7 @@ _list() { ## Figure out how much padding we need to use ## We need one level of padding for each power of 10 $LINES uses - LINES=$( sed -ne '$ =' < "$src" ) + LINES=$( sed -n '$ =' "$src" ) PADDING=${#LINES} ## Number, sort, and mangle the file, then run the filter command, @@ -424,10 +424,10 @@ _list() { s/'${HIDE_CONTEXTS_SUBSTITUTION:-^}'//g ''' \ ) - echo -e "$command" + echo -ne "$command${command:+\n}" if [ $TODOTXT_VERBOSE == 1 ]; then - NUMTASKS=$( echo -e "$command" | sed -ne '/^$/d' -e '$ =' ) + NUMTASKS=$( echo -ne "$command" | sed -n '$ =' ) echo "--" echo "TODO: ${NUMTASKS:-0} of $LINES tasks shown from $FILE" @@ -472,7 +472,7 @@ case $action in input="$now $input" fi echo "$input" >> "$TODO_FILE" - TASKNUM=$(sed -ne "$ =" < "$TODO_FILE") + TASKNUM=$(sed -n '$ =' "$TODO_FILE") [[ $TODOTXT_VERBOSE = 1 ]] && echo "TODO: '$input' added on line $TASKNUM." cleanup;; @@ -486,7 +486,7 @@ case $action in if [ -f "$dest" ]; then echo "$input" >> "$dest" - TASKNUM=$(sed -ne "$ =" < "$dest") + TASKNUM=$(sed -n '$ =' "$dest") [[ $TODOTXT_VERBOSE = 1 ]] && echo "TODO: '$input' added to $dest on line $TASKNUM." else echo "TODO: Destination file $dest does not exist." @@ -782,13 +782,13 @@ note: PRIORITY must be anywhere from A to Z." sed '/^x /!d' "$TODO_FILE" >> "$DONE_FILE" sed -i.bak '/^x /d' "$TODO_FILE" - NUMLINES=$( sed -ne '$ =' < "$TODO_FILE" ) + NUMLINES=$( sed -n '$ =' "$TODO_FILE" ) if [ ${NUMLINES:-0} = "0" ]; then echo "datetime todos dones" >> "$REPORT_FILE" fi #now report - TOTAL=$( sed -ne '$ =' < "$TODO_FILE" ) - TDONE=$( sed -ne '$ =' < "$DONE_FILE" ) + TOTAL=$( sed -n '$ =' "$TODO_FILE" ) + TDONE=$( sed -n '$ =' "$DONE_FILE" ) TECHO=$(echo $(date +%Y-%m-%d-%T); echo ' '; echo ${TOTAL:-0}; echo ' '; echo ${TDONE:-0}) echo $TECHO >> "$REPORT_FILE" From 7e04849a4fe8cc8eabe169b89e20d4f1677caa5a Mon Sep 17 00:00:00 2001 From: Ed Blackman Date: Sat, 14 Mar 2009 13:39:27 -0400 Subject: [PATCH 16/20] Move filter before priority text changes, so filter doesn't match invisible text --- todo.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/todo.sh b/todo.sh index 1042b5f..b739181 100755 --- a/todo.sh +++ b/todo.sh @@ -408,6 +408,7 @@ _list() { s/^ /0/; ''' \ | sort -f -k2 \ + | eval ${filter_command:-cat} \ | sed ''' /^[0-9]\{'$PADDING'\} x /! { s/\(.*(A).*\)/'$PRI_A'\1 '$DEFAULT'/g; @@ -416,7 +417,6 @@ _list() { s/\(.*([D-Z]).*\)/'$PRI_X'\1 '$DEFAULT'/g; } ''' \ - | eval ${filter_command:-cat} \ | sort -f -k2 \ | sed ''' s/'${HIDE_PRIORITY_SUBSTITUTION:-^}'//g From 07bb979d4315e9abc22323ce7258c8a42dfaed9e Mon Sep 17 00:00:00 2001 From: "David A. Harding" Date: Sat, 14 Mar 2009 15:06:53 -0400 Subject: [PATCH 17/20] Move Filter Command Up & Remove Extra Sort Command --- todo.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/todo.sh b/todo.sh index b739181..d9761e6 100755 --- a/todo.sh +++ b/todo.sh @@ -400,6 +400,7 @@ _list() { command=$( sed = "$src" \ | sed "N; s/^/ /; s/ *\(.\{$PADDING,\}\)\n/\1 /" \ + | eval ${filter_command:-cat} \ | sed ''' s/^ /00000/; s/^ /0000/; @@ -408,7 +409,6 @@ _list() { s/^ /0/; ''' \ | sort -f -k2 \ - | eval ${filter_command:-cat} \ | sed ''' /^[0-9]\{'$PADDING'\} x /! { s/\(.*(A).*\)/'$PRI_A'\1 '$DEFAULT'/g; @@ -417,7 +417,6 @@ _list() { s/\(.*([D-Z]).*\)/'$PRI_X'\1 '$DEFAULT'/g; } ''' \ - | sort -f -k2 \ | sed ''' s/'${HIDE_PRIORITY_SUBSTITUTION:-^}'//g s/'${HIDE_PROJECTS_SUBSTITUTION:-^}'//g From 39ee9ab045ec699367c9e112cb6a96de93cabe3a Mon Sep 17 00:00:00 2001 From: "David A. Harding" Date: Tue, 17 Mar 2009 14:07:48 -0400 Subject: [PATCH 18/20] Added -vv For Debugging Output --- todo.sh | 59 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/todo.sh b/todo.sh index d9761e6..505ea7f 100755 --- a/todo.sh +++ b/todo.sh @@ -150,6 +150,8 @@ help() when it's added. -v Verbose mode turns on confirmation messages + -vv + Extra verbose mode prints some debugging information -V Displays version, license and credits @@ -197,13 +199,13 @@ archive() { #defragment blank lines sed -i.bak -e '/./!d' "$TODO_FILE" - [[ $TODOTXT_VERBOSE = 1 ]] && grep "^x " "$TODO_FILE" + [ $TODOTXT_VERBOSE -gt 0 ] && grep "^x " "$TODO_FILE" grep "^x " "$TODO_FILE" >> "$DONE_FILE" sed -i.bak '/^x /d' "$TODO_FILE" cp "$TODO_FILE" "$TMP_FILE" sed -n 'G; s/\n/&&/; /^\([ -~]*\n\).*\n\1/d; s/\n//; h; P' "$TMP_FILE" > "$TODO_FILE" - #[[ $TODOTXT_VERBOSE = 1 ]] && echo "TODO: Duplicate tasks have been removed." - [[ $TODOTXT_VERBOSE = 1 ]] && echo "TODO: $TODO_FILE archived." + #[[ $TODOTXT_VERBOSE -gt 0 ]] && echo "TODO: Duplicate tasks have been removed." + [ $TODOTXT_VERBOSE -gt 0 ] && echo "TODO: $TODO_FILE archived." cleanup } @@ -282,7 +284,7 @@ do TODOTXT_DATE_ON_ADD=1 ;; v ) - TODOTXT_VERBOSE=1 + : $(( TODOTXT_VERBOSE++ )) ;; V ) version @@ -425,12 +427,16 @@ _list() { ) echo -ne "$command${command:+\n}" - if [ $TODOTXT_VERBOSE == 1 ]; then + if [ $TODOTXT_VERBOSE -gt 0 ]; then NUMTASKS=$( echo -ne "$command" | sed -n '$ =' ) echo "--" echo "TODO: ${NUMTASKS:-0} of $LINES tasks shown from $FILE" fi + if [ $TODOTXT_VERBOSE -gt 1 ] + then + echo "TODO DEBUG: Filter Command was: ${filter_command:-cat}" + fi } export -f _list @@ -472,7 +478,7 @@ case $action in fi echo "$input" >> "$TODO_FILE" TASKNUM=$(sed -n '$ =' "$TODO_FILE") - [[ $TODOTXT_VERBOSE = 1 ]] && echo "TODO: '$input' added on line $TASKNUM." + [ $TODOTXT_VERBOSE -gt 0 ] && echo "TODO: '$input' added on line $TASKNUM." cleanup;; "addto" ) @@ -486,7 +492,7 @@ case $action in if [ -f "$dest" ]; then echo "$input" >> "$dest" TASKNUM=$(sed -n '$ =' "$dest") - [[ $TODOTXT_VERBOSE = 1 ]] && echo "TODO: '$input' added to $dest on line $TASKNUM." + [ $TODOTXT_VERBOSE -gt 0 ] && echo "TODO: '$input' added to $dest on line $TASKNUM." else echo "TODO: Destination file $dest does not exist." fi @@ -508,7 +514,7 @@ case $action in fi if sed -i.bak $item" s|^.*|& $input|" "$TODO_FILE"; then newtodo=$(sed "$item!d" "$TODO_FILE") - [[ $TODOTXT_VERBOSE = 1 ]] && echo "$item: $newtodo" + [ $TODOTXT_VERBOSE -gt 0 ] && echo "$item: $newtodo" else echo "TODO: Error appending task $item." fi @@ -543,7 +549,7 @@ case $action in # leave blank line behind (preserves line numbers) sed -i.bak -e $2"s/^.*//" "$TODO_FILE" fi - [[ $TODOTXT_VERBOSE = 1 ]] && echo "TODO: '$DELETEME' deleted." + [ $TODOTXT_VERBOSE -gt 0 ] && echo "TODO: '$DELETEME' deleted." cleanup else echo "TODO: No tasks were deleted." @@ -553,7 +559,7 @@ case $action in fi else sed -i.bak -e $item"s/$3/ /g" "$TODO_FILE" - [[ $TODOTXT_VERBOSE = 1 ]] && echo "TODO: $3 removed from $item." + [ $TODOTXT_VERBOSE -gt 0 ] && echo "TODO: $3 removed from $item." fi ;; "depri" | "dp" ) @@ -570,8 +576,8 @@ case $action in #it's all good, continue sed -i.bak -e $2"s/^(.*) //" "$TODO_FILE" NEWTODO=$(sed "$2!d" "$TODO_FILE") - [[ $TODOTXT_VERBOSE = 1 ]] && echo -e "`echo "$item: $NEWTODO"`" - [[ $TODOTXT_VERBOSE = 1 ]] && echo "TODO: $item deprioritized." + [ $TODOTXT_VERBOSE -gt 0 ] && echo -e "`echo "$item: $NEWTODO"`" + [ $TODOTXT_VERBOSE -gt 0 ] && echo "TODO: $item deprioritized." cleanup else die "$errmsg" @@ -591,8 +597,8 @@ case $action in sed -i.bak $item"s/^(.*) //" "$TODO_FILE" sed -i.bak $item"s|^|&x $now |" "$TODO_FILE" newtodo=$(sed "$item!d" "$TODO_FILE") - [[ $TODOTXT_VERBOSE = 1 ]] && echo "$item: $newtodo" - [[ $TODOTXT_VERBOSE = 1 ]] && echo "TODO: $item marked as done." + [ $TODOTXT_VERBOSE -gt 0 ] && echo "$item: $newtodo" + [ $TODOTXT_VERBOSE -gt 0 ] && echo "TODO: $item marked as done." if [ $TODOTXT_AUTO_ARCHIVE = 1 ]; then archive @@ -636,23 +642,22 @@ case $action in "listpri" | "lsp" ) - shift ## was "listpri" + shift ## was "listpri", new $1 is priority to list if [ "${1:-}" ] then ## A priority was specified pri=$( printf "%s\n" "$1" | tr 'a-z' 'A-Z' | grep '^[A-Z]$' ) || { - die "usage: $0 listpri PRIORITY - note: PRIORITY must a single letter from A to Z." + die "usage: $0 listpri PRIORITY + note: PRIORITY must a single letter from A to Z." } else ## No priority specified; show all priority tasks pri="[A-Z]" fi pri="($pri)" - shift ## was priority - _list "$TODO_FILE" "$pri" "$@" + _list "$TODO_FILE" "$pri" ;; "move" | "mv" ) @@ -688,7 +693,7 @@ case $action in fi echo "$MOVEME" >> "$dest" - [[ $TODOTXT_VERBOSE = 1 ]] && echo "TODO: '$MOVEME' moved from '$src' to '$dest'." + [ $TODOTXT_VERBOSE -gt 0 ] && echo "TODO: '$MOVEME' moved from '$src' to '$dest'." cleanup else echo "TODO: No tasks moved." @@ -723,7 +728,7 @@ case $action in if sed -i.bak $item" s|^.*|$input &|" "$TODO_FILE"; then newtodo=$(sed "$item!d" "$TODO_FILE") - [[ $TODOTXT_VERBOSE = 1 ]] && echo "$item: $newtodo" + [ $TODOTXT_VERBOSE -gt 0 ] && echo "$item: $newtodo" else echo "TODO: Error prepending task $item." fi @@ -746,8 +751,8 @@ note: PRIORITY must be anywhere from A to Z." #it's all good, continue sed -i.bak -e $2"s/^(.*) //" -e $2"s/^/($newpri) /" "$TODO_FILE" NEWTODO=$(sed "$2!d" "$TODO_FILE") - [[ $TODOTXT_VERBOSE = 1 ]] && echo -e "`echo "$item: $NEWTODO"`" - [[ $TODOTXT_VERBOSE = 1 ]] && echo "TODO: $item prioritized ($newpri)." + [ $TODOTXT_VERBOSE -gt 0 ] && echo -e "`echo "$item: $NEWTODO"`" + [ $TODOTXT_VERBOSE -gt 0 ] && echo "TODO: $item prioritized ($newpri)." cleanup else die "$errmsg" @@ -771,9 +776,9 @@ note: PRIORITY must be anywhere from A to Z." fi sed -i.bak $item" s|^.*|$input|" "$TODO_FILE" - [[ $TODOTXT_VERBOSE = 1 ]] && NEWTODO=$(head -$item "$TODO_FILE" | tail -1) - [[ $TODOTXT_VERBOSE = 1 ]] && echo "replaced with" - [[ $TODOTXT_VERBOSE = 1 ]] && echo "$item: $NEWTODO" + [ $TODOTXT_VERBOSE -gt 0 ] && NEWTODO=$(head -$item "$TODO_FILE" | tail -1) + [ $TODOTXT_VERBOSE -gt 0 ] && echo "replaced with" + [ $TODOTXT_VERBOSE -gt 0 ] && echo "$item: $NEWTODO" cleanup;; "report" ) @@ -791,7 +796,7 @@ note: PRIORITY must be anywhere from A to Z." TECHO=$(echo $(date +%Y-%m-%d-%T); echo ' '; echo ${TOTAL:-0}; echo ' '; echo ${TDONE:-0}) echo $TECHO >> "$REPORT_FILE" - [[ $TODOTXT_VERBOSE = 1 ]] && echo "TODO: Report file updated." + [ $TODOTXT_VERBOSE -gt 0 ] && echo "TODO: Report file updated." cat "$REPORT_FILE" cleanup;; From 7b769b2eea8fe5182e1695ff477cc87935045eae Mon Sep 17 00:00:00 2001 From: "David A. Harding" Date: Tue, 17 Mar 2009 19:15:27 -0400 Subject: [PATCH 19/20] s/[A-Z]/[[:upper:]]/ --- todo.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/todo.sh b/todo.sh index 505ea7f..93ecc37 100755 --- a/todo.sh +++ b/todo.sh @@ -653,7 +653,7 @@ case $action in } else ## No priority specified; show all priority tasks - pri="[A-Z]" + pri="[[:upper:]]" fi pri="($pri)" From 02dc030225659b36a972e84302ebae717205e629 Mon Sep 17 00:00:00 2001 From: "David A. Harding" Date: Thu, 19 Mar 2009 07:06:07 -0400 Subject: [PATCH 20/20] Putting Quotes Around $TODO_TMP Suggested by Gina. --- todo.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/todo.sh b/todo.sh index 93ecc37..f1b3645 100755 --- a/todo.sh +++ b/todo.sh @@ -325,7 +325,7 @@ export TODO_SH [ -d "$TODO_DIR" ] || die "Fatal Error: $TODO_DIR is not a directory" ( cd "$TODO_DIR" ) || die "Fatal Error: Unable to cd to $TODO_DIR" -[ -w "$TMP_FILE" ] || echo -n > $TMP_FILE || die "Fatal Error: Unable to write to $TMP_FILE" +[ -w "$TMP_FILE" ] || echo -n > "$TMP_FILE" || die "Fatal Error: Unable to write to $TMP_FILE" [ -f "$TODO_FILE" ] || cp /dev/null "$TODO_FILE" [ -f "$DONE_FILE" ] || cp /dev/null "$DONE_FILE" [ -f "$REPORT_FILE" ] || cp /dev/null "$REPORT_FILE"