3 # Copyright (c) 2015-2024 Paul W. Rankin <rnkn@rnkn.xyz>
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <https://www.gnu.org/licenses/>.
23 t [-aD] [-s REGEX_STRING] [-d [INTEGER|REGEX_STRING]]
24 t [-aD] [-s REGEX_STRING] [-k [INTEGER|REGEX_STRING]]
25 t [-aD] [-s REGEX_STRING] [-b [INTEGER|REGEX_STRING]]
26 t [-aD] [-s REGEX_STRING] [-z [INTEGER|REGEX_STRING]]
29 t print incomplete todos
31 t -D print all done todos
32 t -s call print all todos matching "call"
33 t -s "call|email" print all todos matching "call" or "email"
34 t -D -s read print all done todos matching "read"
35 t -d 12 mark todo item 12 as done
36 t -s read -d 3 mark todo item 3 within todos matching "read" as done
37 t -d burn mark all todos matching "burn" as done
38 t -s burn -d . same as above
39 t -k 7 delete todo item 7
40 t -k bunnies delete all todos matching "bunnies"
41 t -s bunnies -k . same as above
42 t -e edit TODO_FILE in $EDITOR
43 t -T sell horse add todo "sell horse" due today
44 t -n print unnumbered output (suitable for redirection)
49 re_todo_file='[Tt][Oo][Dd][Oo].*'
52 re_either='^- \[[ xX]] '
53 re_date='[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]'
56 expr "$f" : "$re_todo_file" > /dev/null && todo_file="$f" && break
59 if [ ! "$todo_file" ]; then
60 if [ -r "$TODO_FILE" ]; then
61 todo_file="$TODO_FILE"
63 echo 'No todo file found'
69 # returns: sorted list of matching todos
73 expr "$query" : '\(.*[A-Z].*\)' > /dev/null || casematch='-i'
74 todo_list=$(grep $wholeword $casematch "$re_prefix.*$*" "$todo_file")
76 if [ -n "$todo_list" ]; then
77 due_list=$(echo "$todo_list" | grep "$re_date")
78 todo_list=$(echo "$todo_list" | grep -v "$re_date")
79 due_list=$(echo "$due_list" | sed -E "s/.*($re_date).*/\1&/" |
80 sort -n | sed -E "s/^$re_date//")
82 # printf '%s\n%s\n' "$due_list" "$todo_list"
83 if [ -n "$due_list" ] && [ -n "$todo_list" ]; then
84 printf '%s\n%s\n' "$due_list" "$todo_list"
85 elif [ -n "$due_list" ]; then
86 printf '%s\n' "$due_list"
88 printf '%s\n' "$todo_list"
94 # returns: todo list printed to stdout
97 if [ -n "$input" ]; then
99 n_width=$(echo "$input" | wc -l | xargs | wc -c)
101 echo "$input" | while read -r todo; do
102 date=$(expr "$todo" : ".*\($re_date\)" | sed 's/-//g')
105 if [ -n "$date" ] && [ -z "$onlydone$showall" ]; then
106 today=$(date +%Y%m%d)
107 if [ "$today" -ge "$date" ]; then
108 todo=$(echo "$todo" |
109 sed -E "s/($re_prefix)(.*)/\1** \2 **/")
113 if [ -n "$export" ]; then
114 printf "%s\n" "${todo}"
116 printf "%${n_width}s %s\n" "$n" "${todo#- }"
123 # t_select(number|regex)
124 # returns: selected todos
126 if expr "$1" : ^[0-9]*$ > /dev/null; then
130 expr "$1" : '.*[A-Z].*' > /dev/null || casematch='-i'
135 # t_done(number|regex)
136 # returns: altered todo_file
139 while read -r todo; do
142 '$0 == str { gsub (/- \[ ]/, "- [X]") } { print }' \
143 "$todo_file" > "$tmp"
144 mv "$tmp" "$todo_file"
151 while read -r todo; do
153 awk -v str="$todo" '$0 != str' "$todo_file" > "$tmp"
154 mv "$tmp" "$todo_file"
160 while read -r todo; do
163 expr "$todo" : "$re_done" > /dev/null &&
164 check='- [ ]' || check='- [X]'
165 awk -v str="$todo" -v check="$check" \
166 '$0 == str { gsub (/- \[[ xX]]/, check) } { print }' \
167 "$todo_file" > "$tmp"
168 mv "$tmp" "$todo_file"
173 t_select "$1" | grep -Eo "https?://[^ ]+" | xargs open
176 while getopts :ab:Dd:ehk:nS:s:Tz: opt; do
180 (b) openurl=$OPTARG ;;
182 (d) markdone=$OPTARG ;;
183 (e) ${EDITOR:-vi} "$todo_file"; exit 0 ;;
186 (S) query=$OPTARG; wholeword=-w ;;
188 (T) due=" $(date +%F)" ;;
189 (z) toggle=$OPTARG ;;
190 (:) printf "t: option -%s requires an argument\n" "$OPTARG"; exit 2 ;;
191 (*) printf "t: unrecognized option -%s\n\n" "$OPTARG"; usage ;;
195 shift "$(( OPTIND - 1 ))"
197 if [ -n "$onlydone" ]; then
199 elif [ -n "$showall" ]; then
200 re_prefix="$re_either"
205 if [ -n "$markdone" ]; then t_read "$query" | t_done "$markdone"
206 elif [ -n "$toggle" ]; then t_read "$query" | t_toggle "$toggle"
207 elif [ -n "$kill" ]; then t_read "$query" | t_kill "$kill"
208 elif [ -n "$openurl" ]; then t_read "$query" | t_openurl "$openurl"
209 elif [ -n "$query" ]; then t_read "$query" | t_print "$re_prefix"
210 elif [ -n "$*" ]; then
211 echo "- [ ] $*${due}" >> "$todo_file"
212 else t_read | t_print "$re_prefix"