Implemented `todo -l`, like `todo -L` but showing the subject of each entry
[todo.git] / todo.sh
blobf1368b4a4a38e90d2245b47ac9dd41ac7914fbab
1 #!/bin/sh
2 # todo, A command line TODO manager
3 # Copyright (C) 2008 Alejandro Mery <amery@geeks.cl>
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,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU 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 <http://www.gnu.org/licenses/>.
18 todo_usage()
20 local cmd="${0##*/}"
22 [ $# -eq 0 ] || echo "ERROR: $*" >&2
23 cat <<-EOT >&2
24 Usage: $0 [options] [<object>]
25 ${cmd} [-t <tag>*] [<message>] - new entry
26 ${cmd} [-T|--tags] - list tags
27 ${cmd} [-L] [<tag>*] - list entry hashes
28 ${cmd} [-l|--list] [<tag>*] - list entries
30 EOT
31 exit 1
34 todo_trap()
36 echo "Ctrl-C pressed, aborting." >&2
37 rm -rf "$TODOTMP"
38 exit 1
41 todo_sane_tags()
43 echo "$*" | tr 'a-z' 'A-Z' | sed \
44 -e 's/^ *//' -e 's/ *$//' -e 's/ */ /g'
47 trap ':' INT
49 options=$( getopt -o "t:TLl" -l "tags,list" -- "$@" )
50 if [ $? -ne 0 ]; then
51 todo_usage
54 # load new arguments list
55 eval set -- "$options"
57 TAGS=
58 MODE=new
60 while [ $# -gt 0 ]; do
61 case "$1" in
62 -T|--tags)
63 MODE=tags ;;
64 -l|--list)
65 MODE=list ;;
66 -L)
67 MODE=list_raw ;;
69 -t) TAGS="$TAGS $2"; shift ;;
71 --) shift; break ;;
72 -*) todo_usage unknown option "$1"
74 esac
75 shift
76 done
78 TODODIR="$HOME/.todo"
80 todo_new()
82 TODOTMP="${TMP:-/tmp}/.todo.$$"
83 DATE=
84 # find a unique date/hash
86 while [ -z "$DATE" ]; do
87 DATE=$(date --rfc-3339=seconds)
88 DATEHASH=$( echo "$DATE" | sha1sum | cut -d' ' -f1 )
89 DATEHASH_1=$( echo "$DATEHASH" | cut -c 1 )
90 ENTRYDIR="$TODODIR/entries/$DATEHASH_1"
91 ENTRY="$ENTRYDIR/$DATEHASH"
93 if [ -e "$ENTRY" ]; then
94 DATE=
95 sleep 1
96 else
97 mkdir -p "$ENTRYDIR"
99 done
101 if [ $# -gt 0 ]; then
102 echo "$*" > "$TODOTMP"
103 echo "Added: $DATE" >> "$TODOTMP"
104 else
105 echo "Subject: ..." > "$TODOTMP.empty"
106 echo "Added: $DATE" >> "$TODOTMP.empty"
107 cp "$TODOTMP.empty" "$TODOTMP"
109 ${EDITOR:-vi} "$TODOTMP"
111 if cmp -s "$TODOTMP" "$TODOTMP.empty"; then
112 rm -f "$TODOTMP" "$TODOTMP.empty"
113 return
117 mv "$TODOTMP" "$ENTRY"
119 for tag in $(todo_sane_tags $TAGS) INBOX; do
120 [ -d "$TODODIR/$tag" ] || mkdir "$TODODIR/$tag"
122 ln -s "../entries/$DATEHASH_1/$DATEHASH" "$TODODIR/$tag/$DATEHASH"
123 done
125 echo "$DATEHASH"
128 todo_tags()
130 ls -1d "$TODODIR"/* | sed -e "s,^$TODODIR/,," | grep -v '^entries$'
133 todo_list_raw()
135 local tags="$(todo_sane_tags $TAGS $*)"
136 local tag=
137 if [ -n "$tags" ]; then
138 for tag in $tags; do
139 ls -1 "$TODODIR/$tag"/*
140 done
141 else
142 ls -1 "$TODODIR/entries"/*/*
143 fi 2> /dev/null | sed -e 's,.*/,,' | sort -u
146 todo_list()
148 local hash= hash_1= entry= subject=
149 for hash in $( $0 -L $(todo_sane_tags $TAGS $* )); do
150 hash_1=$( echo $hash | cut -c1 )
151 entry="$TODODIR/entries/$hash_1/$hash"
152 subject=$( grep '^Subject:' "$entry" | cut -d' ' -f2- )
153 [ -n "$subject" ] || subject=$( head -n1 "$entry" )
154 [ -n "$subject" ] || subject="empty"
156 echo "$hash $subject"
157 done
160 case "$MODE" in
161 new) todo_new "$*" ;;
162 tags) todo_tags ;;
163 list) todo_list "$*" ;;
164 list_raw) todo_list_raw "$*" ;;
165 esac