todo.rb 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/todotxt/todo.sh ADDED
@@ -0,0 +1,1128 @@
1
+ #! /bin/bash
2
+
3
+ # === HEAVY LIFTING ===
4
+ shopt -s extglob extquote
5
+
6
+ # NOTE: Todo.sh requires the .todo/config configuration file to run.
7
+ # Place the .todo/config file in your home directory or use the -d option for a custom location.
8
+
9
+ [ -f VERSION-FILE ] && . VERSION-FILE || VERSION="2.8"
10
+ version() {
11
+ cat <<-EndVersion
12
+ TODO.TXT Command Line Interface v$VERSION
13
+
14
+ First release: 5/11/2006
15
+ Original conception by: Gina Trapani (http://ginatrapani.org)
16
+ Contributors: http://github.com/ginatrapani/todo.txt-cli/network
17
+ License: GPL, http://www.gnu.org/copyleft/gpl.html
18
+ More information and mailing list at http://todotxt.com
19
+ Code repository: http://github.com/ginatrapani/todo.txt-cli/tree/master
20
+ EndVersion
21
+ exit 1
22
+ }
23
+
24
+ # Set script name and full path early.
25
+ TODO_SH=$(basename "$0")
26
+ TODO_FULL_SH="$0"
27
+ export TODO_SH TODO_FULL_SH
28
+
29
+ oneline_usage="$TODO_SH [-fhpantvV] [-d todo_config] action [task_number] [task_description]"
30
+
31
+ usage()
32
+ {
33
+ cat <<-EndUsage
34
+ Usage: $oneline_usage
35
+ Try '$TODO_SH -h' for more information.
36
+ EndUsage
37
+ exit 1
38
+ }
39
+
40
+ shorthelp()
41
+ {
42
+ cat <<-EndHelp
43
+ Usage: $oneline_usage
44
+
45
+ Actions:
46
+ add|a "THING I NEED TO DO +project @context"
47
+ addto DEST "TEXT TO ADD"
48
+ addm "THINGS I NEED TO DO
49
+ MORE THINGS I NEED TO DO"
50
+ append|app ITEM# "TEXT TO APPEND"
51
+ archive
52
+ command [ACTIONS]
53
+ del|rm ITEM# [TERM]
54
+ dp|depri ITEM#[, ITEM#, ITEM#, ...]
55
+ do ITEM#[, ITEM#, ITEM#, ...]
56
+ help
57
+ list|ls [TERM...]
58
+ listall|lsa [TERM...]
59
+ listcon|lsc
60
+ listfile|lf SRC [TERM...]
61
+ listpri|lsp [PRIORITY] [TERM...]
62
+ listproj|lsprj
63
+ move|mv ITEM# DEST [SRC]
64
+ prepend|prep ITEM# "TEXT TO PREPEND"
65
+ pri|p ITEM# PRIORITY
66
+ replace ITEM# "UPDATED TODO"
67
+ report
68
+
69
+ See "help" for more details.
70
+ EndHelp
71
+ exit 0
72
+ }
73
+
74
+ help()
75
+ {
76
+ cat <<-EndHelp
77
+ Usage: $oneline_usage
78
+
79
+ Actions:
80
+ add "THING I NEED TO DO +project @context"
81
+ a "THING I NEED TO DO +project @context"
82
+ Adds THING I NEED TO DO to your todo.txt file on its own line.
83
+ Project and context notation optional.
84
+ Quotes optional.
85
+
86
+ addm "FIRST THING I NEED TO DO +project1 @context
87
+ SECOND THING I NEED TO DO +project2 @context"
88
+ Adds FIRST THING I NEED TO DO to your todo.txt on its own line and
89
+ Adds SECOND THING I NEED TO DO to you todo.txt on its own line.
90
+ Project and context notation optional.
91
+ Quotes optional.
92
+
93
+ addto DEST "TEXT TO ADD"
94
+ Adds a line of text to any file located in the todo.txt directory.
95
+ For example, addto inbox.txt "decide about vacation"
96
+
97
+ append ITEM# "TEXT TO APPEND"
98
+ app ITEM# "TEXT TO APPEND"
99
+ Adds TEXT TO APPEND to the end of the task on line ITEM#.
100
+ Quotes optional.
101
+
102
+ archive
103
+ Moves all done tasks from todo.txt to done.txt and removes blank lines.
104
+
105
+ command [ACTIONS]
106
+ Runs the remaining arguments using only todo.sh builtins.
107
+ Will not call any .todo.actions.d scripts.
108
+
109
+ del ITEM# [TERM]
110
+ rm ITEM# [TERM]
111
+ Deletes the task on line ITEM# in todo.txt.
112
+ If TERM specified, deletes only TERM from the task.
113
+
114
+ depri ITEM#[, ITEM#, ITEM#, ...]
115
+ dp ITEM#[, ITEM#, ITEM#, ...]
116
+ Deprioritizes (removes the priority) from the task(s)
117
+ on line ITEM# in todo.txt.
118
+
119
+ do ITEM#[, ITEM#, ITEM#, ...]
120
+ Marks task(s) on line ITEM# as done in todo.txt.
121
+
122
+ help
123
+ Display this help message.
124
+
125
+ list [TERM...]
126
+ ls [TERM...]
127
+ Displays all tasks that contain TERM(s) sorted by priority with line
128
+ numbers. If no TERM specified, lists entire todo.txt.
129
+
130
+ listall [TERM...]
131
+ lsa [TERM...]
132
+ Displays all the lines in todo.txt AND done.txt that contain TERM(s)
133
+ sorted by priority with line numbers. If no TERM specified, lists
134
+ entire todo.txt AND done.txt concatenated and sorted.
135
+
136
+ listcon
137
+ lsc
138
+ Lists all the task contexts that start with the @ sign in todo.txt.
139
+
140
+ listfile SRC [TERM...]
141
+ lf SRC [TERM...]
142
+ Displays all the lines in SRC file located in the todo.txt directory,
143
+ sorted by priority with line numbers. If TERM specified, lists
144
+ all lines that contain TERM in SRC file.
145
+
146
+ listpri [PRIORITY] [TERM...]
147
+ lsp [PRIORITY] [TERM...]
148
+ Displays all tasks prioritized PRIORITY.
149
+ If no PRIORITY specified, lists all prioritized tasks.
150
+ If TERM specified, lists only prioritized tasks that contain TERM.
151
+
152
+ listproj
153
+ lsprj
154
+ Lists all the projects that start with the + sign in todo.txt.
155
+
156
+ move ITEM# DEST [SRC]
157
+ mv ITEM# DEST [SRC]
158
+ Moves a line from source text file (SRC) to destination text file (DEST).
159
+ Both source and destination file must be located in the directory defined
160
+ in the configuration directory. When SRC is not defined
161
+ it's by default todo.txt.
162
+
163
+ prepend ITEM# "TEXT TO PREPEND"
164
+ prep ITEM# "TEXT TO PREPEND"
165
+ Adds TEXT TO PREPEND to the beginning of the task on line ITEM#.
166
+ Quotes optional.
167
+
168
+ pri ITEM# PRIORITY
169
+ p ITEM# PRIORITY
170
+ Adds PRIORITY to task on line ITEM#. If the task is already
171
+ prioritized, replaces current priority with new PRIORITY.
172
+ PRIORITY must be an uppercase letter between A and Z.
173
+
174
+ replace ITEM# "UPDATED TODO"
175
+ Replaces task on line ITEM# with UPDATED TODO.
176
+
177
+ report
178
+ Adds the number of open tasks and done tasks to report.txt.
179
+
180
+
181
+
182
+ Options:
183
+ -@
184
+ Hide context names in list output. Use twice to show context
185
+ names (default).
186
+ -+
187
+ Hide project names in list output. Use twice to show project
188
+ names (default).
189
+ -c
190
+ Color mode
191
+ -d CONFIG_FILE
192
+ Use a configuration file other than the default ~/.todo/config
193
+ -f
194
+ Forces actions without confirmation or interactive input
195
+ -h
196
+ Display a short help message
197
+ -p
198
+ Plain mode turns off colors
199
+ -P
200
+ Hide priority labels in list output. Use twice to show
201
+ priority labels (default).
202
+ -a
203
+ Don't auto-archive tasks automatically on completion
204
+ -A
205
+ Auto-archive tasks automatically on completion
206
+ -n
207
+ Don't preserve line numbers; automatically remove blank lines
208
+ on task deletion
209
+ -N
210
+ Preserve line numbers
211
+ -t
212
+ Prepend the current date to a task automatically
213
+ when it's added.
214
+ -T
215
+ Do not prepend the current date to a task automatically
216
+ when it's added.
217
+ -v
218
+ Verbose mode turns on confirmation messages
219
+ -vv
220
+ Extra verbose mode prints some debugging information
221
+ -V
222
+ Displays version, license and credits
223
+ -x
224
+ Disables TODOTXT_FINAL_FILTER
225
+
226
+
227
+ Environment variables:
228
+ TODOTXT_AUTO_ARCHIVE is same as option -a (0)/-A (1)
229
+ TODOTXT_CFG_FILE=CONFIG_FILE is same as option -d CONFIG_FILE
230
+ TODOTXT_FORCE=1 is same as option -f
231
+ TODOTXT_PRESERVE_LINE_NUMBERS is same as option -n (0)/-N (1)
232
+ TODOTXT_PLAIN is same as option -p (1)/-c (0)
233
+ TODOTXT_DATE_ON_ADD is same as option -t (1)/-T (0)
234
+ TODOTXT_VERBOSE=1 is same as option -v
235
+ TODOTXT_DEFAULT_ACTION="" run this when called with no arguments
236
+ TODOTXT_SORT_COMMAND="sort ..." customize list output
237
+ TODOTXT_FINAL_FILTER="sed ..." customize list after color, P@+ hiding
238
+ EndHelp
239
+
240
+ if [ -d "$TODO_ACTIONS_DIR" ]
241
+ then
242
+ echo ""
243
+ for action in "$TODO_ACTIONS_DIR"/*
244
+ do
245
+ if [ -f "$action" -a -x "$action" ]
246
+ then
247
+ "$action" usage
248
+ fi
249
+ done
250
+ echo ""
251
+ fi
252
+
253
+
254
+ exit 1
255
+ }
256
+
257
+ die()
258
+ {
259
+ echo "$*"
260
+ exit 1
261
+ }
262
+
263
+ cleanup()
264
+ {
265
+ [ -f "$TMP_FILE" ] && rm "$TMP_FILE"
266
+ return 0
267
+ }
268
+
269
+ cleaninput()
270
+ {
271
+ # Replace CR and LF with space; tasks always comprise a single line.
272
+ input=${input//$'\r'/ }
273
+ input=${input//$'\n'/ }
274
+
275
+ if [ "$1" = "for sed" ]; then
276
+ # This action uses sed with "|" as the substitution separator, and & as
277
+ # the matched string; these must be escaped.
278
+ # Backslashes must be escaped, too, and before the other stuff.
279
+ input=${input//\\/\\\\}
280
+ input=${input//|/\\|}
281
+ input=${input//&/\\&}
282
+ fi
283
+ }
284
+
285
+ archive()
286
+ {
287
+ #defragment blank lines
288
+ sed -i.bak -e '/./!d' "$TODO_FILE"
289
+ [ $TODOTXT_VERBOSE -gt 0 ] && grep "^x " "$TODO_FILE"
290
+ grep "^x " "$TODO_FILE" >> "$DONE_FILE"
291
+ sed -i.bak '/^x /d' "$TODO_FILE"
292
+ cp "$TODO_FILE" "$TMP_FILE"
293
+ sed -n 'G; s/\n/&&/; /^\([ ~-]*\n\).*\n\1/d; s/\n//; h; P' "$TMP_FILE" > "$TODO_FILE"
294
+ if [ $TODOTXT_VERBOSE -gt 0 ]; then
295
+ echo "TODO: $TODO_FILE archived."
296
+ fi
297
+ }
298
+
299
+ replaceOrPrepend()
300
+ {
301
+ action=$1; shift
302
+ case "$action" in
303
+ replace)
304
+ backref=
305
+ querytext="Replacement: "
306
+ ;;
307
+ prepend)
308
+ backref=' &'
309
+ querytext="Prepend: "
310
+ ;;
311
+ esac
312
+ shift; item=$1; shift
313
+
314
+ [ -z "$item" ] && die "$errmsg"
315
+ [[ "$item" = +([0-9]) ]] || die "$errmsg"
316
+
317
+ todo=$(sed "$item!d" "$TODO_FILE")
318
+ [ -z "$todo" ] && die "TODO: No task $item."
319
+
320
+ if [[ -z "$1" && $TODOTXT_FORCE = 0 ]]; then
321
+ echo -n "$querytext"
322
+ read input
323
+ else
324
+ input=$*
325
+ fi
326
+ cleaninput "for sed"
327
+
328
+ # Retrieve existing priority and prepended date
329
+ 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")
330
+ 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")
331
+
332
+ if [ "$prepdate" -a "$action" = "replace" ] && [ "$(echo "$input"|sed -e 's/^\([0-9]\{2,4\}-[0-9]\{2\}-[0-9]\{2\}\)\{0,1\}.*/\1/')" ]; then
333
+ # If the replaced text starts with a date, it will replace the existing
334
+ # date, too.
335
+ prepdate=
336
+ fi
337
+
338
+ # Temporarily remove any existing priority and prepended date, perform the
339
+ # change (replace/prepend) and re-insert the existing priority and prepended
340
+ # date again.
341
+ sed -i.bak -e "$item s/^${priority}${prepdate}//" -e "$item s|^.*|${priority}${prepdate}${input}${backref}|" "$TODO_FILE"
342
+ if [ $TODOTXT_VERBOSE -gt 0 ]; then
343
+ newtodo=$(sed "$item!d" "$TODO_FILE")
344
+ case "$action" in
345
+ replace)
346
+ echo "$item $todo"
347
+ echo "TODO: Replaced task with:"
348
+ echo "$item $newtodo"
349
+ ;;
350
+ prepend)
351
+ echo "$item $newtodo"
352
+ ;;
353
+ esac
354
+ fi
355
+ }
356
+
357
+ #Preserving environment variables so they don't get clobbered by the config file
358
+ OVR_TODOTXT_AUTO_ARCHIVE="$TODOTXT_AUTO_ARCHIVE"
359
+ OVR_TODOTXT_FORCE="$TODOTXT_FORCE"
360
+ OVR_TODOTXT_PRESERVE_LINE_NUMBERS="$TODOTXT_PRESERVE_LINE_NUMBERS"
361
+ OVR_TODOTXT_PLAIN="$TODOTXT_PLAIN"
362
+ OVR_TODOTXT_DATE_ON_ADD="$TODOTXT_DATE_ON_ADD"
363
+ OVR_TODOTXT_DISABLE_FILTER="$TODOTXT_DISABLE_FILTER"
364
+ OVR_TODOTXT_VERBOSE="$TODOTXT_VERBOSE"
365
+ OVR_TODOTXT_DEFAULT_ACTION="$TODOTXT_DEFAULT_ACTION"
366
+ OVR_TODOTXT_SORT_COMMAND="$TODOTXT_SORT_COMMAND"
367
+ OVR_TODOTXT_FINAL_FILTER="$TODOTXT_FINAL_FILTER"
368
+
369
+ # == PROCESS OPTIONS ==
370
+ while getopts ":fhpcnNaAtTvVx+@Pd:" Option
371
+ do
372
+ case $Option in
373
+ '@' )
374
+ ## HIDE_CONTEXT_NAMES starts at zero (false); increment it to one
375
+ ## (true) the first time this flag is seen. Each time the flag
376
+ ## is seen after that, increment it again so that an even
377
+ ## number shows context names and an odd number hides context
378
+ ## names.
379
+ : $(( HIDE_CONTEXT_NAMES++ ))
380
+ if [ $(( $HIDE_CONTEXT_NAMES % 2 )) -eq 0 ]
381
+ then
382
+ ## Zero or even value -- show context names
383
+ unset HIDE_CONTEXTS_SUBSTITUTION
384
+ else
385
+ ## One or odd value -- hide context names
386
+ export HIDE_CONTEXTS_SUBSTITUTION='[[:space:]]@[[:graph:]]\{1,\}'
387
+ fi
388
+ ;;
389
+ '+' )
390
+ ## HIDE_PROJECT_NAMES starts at zero (false); increment it to one
391
+ ## (true) the first time this flag is seen. Each time the flag
392
+ ## is seen after that, increment it again so that an even
393
+ ## number shows project names and an odd number hides project
394
+ ## names.
395
+ : $(( HIDE_PROJECT_NAMES++ ))
396
+ if [ $(( $HIDE_PROJECT_NAMES % 2 )) -eq 0 ]
397
+ then
398
+ ## Zero or even value -- show project names
399
+ unset HIDE_PROJECTS_SUBSTITUTION
400
+ else
401
+ ## One or odd value -- hide project names
402
+ export HIDE_PROJECTS_SUBSTITUTION='[[:space:]][+][[:graph:]]\{1,\}'
403
+ fi
404
+ ;;
405
+ a )
406
+ OVR_TODOTXT_AUTO_ARCHIVE=0
407
+ ;;
408
+ A )
409
+ OVR_TODOTXT_AUTO_ARCHIVE=1
410
+ ;;
411
+ c )
412
+ OVR_TODOTXT_PLAIN=0
413
+ ;;
414
+ d )
415
+ TODOTXT_CFG_FILE=$OPTARG
416
+ ;;
417
+ f )
418
+ OVR_TODOTXT_FORCE=1
419
+ ;;
420
+ h )
421
+ shorthelp
422
+ ;;
423
+ n )
424
+ OVR_TODOTXT_PRESERVE_LINE_NUMBERS=0
425
+ ;;
426
+ N )
427
+ OVR_TODOTXT_PRESERVE_LINE_NUMBERS=1
428
+ ;;
429
+ p )
430
+ OVR_TODOTXT_PLAIN=1
431
+ ;;
432
+ P )
433
+ ## HIDE_PRIORITY_LABELS starts at zero (false); increment it to one
434
+ ## (true) the first time this flag is seen. Each time the flag
435
+ ## is seen after that, increment it again so that an even
436
+ ## number shows priority labels and an odd number hides priority
437
+ ## labels.
438
+ : $(( HIDE_PRIORITY_LABELS++ ))
439
+ if [ $(( $HIDE_PRIORITY_LABELS % 2 )) -eq 0 ]
440
+ then
441
+ ## Zero or even value -- show priority labels
442
+ unset HIDE_PRIORITY_SUBSTITUTION
443
+ else
444
+ ## One or odd value -- hide priority labels
445
+ export HIDE_PRIORITY_SUBSTITUTION="([A-Z])[[:space:]]"
446
+ fi
447
+ ;;
448
+ t )
449
+ OVR_TODOTXT_DATE_ON_ADD=1
450
+ ;;
451
+ T )
452
+ OVR_TODOTXT_DATE_ON_ADD=0
453
+ ;;
454
+ v )
455
+ : $(( TODOTXT_VERBOSE++ ))
456
+ ;;
457
+ V )
458
+ version
459
+ ;;
460
+ x )
461
+ OVR_TODOTXT_DISABLE_FILTER=1
462
+ ;;
463
+ esac
464
+ done
465
+ shift $(($OPTIND - 1))
466
+
467
+ # defaults if not yet defined
468
+ TODOTXT_VERBOSE=${TODOTXT_VERBOSE:-1}
469
+ TODOTXT_PLAIN=${TODOTXT_PLAIN:-0}
470
+ TODOTXT_CFG_FILE=${TODOTXT_CFG_FILE:-$HOME/.todo/config}
471
+ TODOTXT_FORCE=${TODOTXT_FORCE:-0}
472
+ TODOTXT_PRESERVE_LINE_NUMBERS=${TODOTXT_PRESERVE_LINE_NUMBERS:-1}
473
+ TODOTXT_AUTO_ARCHIVE=${TODOTXT_AUTO_ARCHIVE:-1}
474
+ TODOTXT_DATE_ON_ADD=${TODOTXT_DATE_ON_ADD:-0}
475
+ TODOTXT_DEFAULT_ACTION=${TODOTXT_DEFAULT_ACTION:-}
476
+ TODOTXT_SORT_COMMAND=${TODOTXT_SORT_COMMAND:-env LC_COLLATE=C sort -f -k2}
477
+ TODOTXT_FINAL_FILTER=${TODOTXT_FINAL_FILTER:-cat}
478
+
479
+ # Export all TODOTXT_* variables
480
+ export ${!TODOTXT_@}
481
+
482
+ # Default color map
483
+ export NONE=''
484
+ export BLACK='\\033[0;30m'
485
+ export RED='\\033[0;31m'
486
+ export GREEN='\\033[0;32m'
487
+ export BROWN='\\033[0;33m'
488
+ export BLUE='\\033[0;34m'
489
+ export PURPLE='\\033[0;35m'
490
+ export CYAN='\\033[0;36m'
491
+ export LIGHT_GREY='\\033[0;37m'
492
+ export DARK_GREY='\\033[1;30m'
493
+ export LIGHT_RED='\\033[1;31m'
494
+ export LIGHT_GREEN='\\033[1;32m'
495
+ export YELLOW='\\033[1;33m'
496
+ export LIGHT_BLUE='\\033[1;34m'
497
+ export LIGHT_PURPLE='\\033[1;35m'
498
+ export LIGHT_CYAN='\\033[1;36m'
499
+ export WHITE='\\033[1;37m'
500
+ export DEFAULT='\\033[0m'
501
+
502
+ # Default priority->color map.
503
+ export PRI_A=$YELLOW # color for A priority
504
+ export PRI_B=$GREEN # color for B priority
505
+ export PRI_C=$LIGHT_BLUE # color for C priority
506
+ export PRI_X=$WHITE # color unless explicitly defined
507
+
508
+ # Default highlight colors.
509
+ export COLOR_DONE=$LIGHT_GREY # color for done (but not yet archived) tasks
510
+
511
+ # Default sentence delimiters for todo.sh append.
512
+ # If the text to be appended to the task begins with one of these characters, no
513
+ # whitespace is inserted in between. This makes appending to an enumeration
514
+ # (todo.sh add 42 ", foo") syntactically correct.
515
+ export SENTENCE_DELIMITERS=',.:;'
516
+
517
+ [ -e "$TODOTXT_CFG_FILE" ] || {
518
+ CFG_FILE_ALT="$HOME/todo.cfg"
519
+
520
+ if [ -e "$CFG_FILE_ALT" ]
521
+ then
522
+ TODOTXT_CFG_FILE="$CFG_FILE_ALT"
523
+ fi
524
+ }
525
+
526
+ [ -e "$TODOTXT_CFG_FILE" ] || {
527
+ CFG_FILE_ALT="$HOME/.todo.cfg"
528
+
529
+ if [ -e "$CFG_FILE_ALT" ]
530
+ then
531
+ TODOTXT_CFG_FILE="$CFG_FILE_ALT"
532
+ fi
533
+ }
534
+
535
+ [ -e "$TODOTXT_CFG_FILE" ] || {
536
+ CFG_FILE_ALT=$(dirname "$0")"/todo.cfg"
537
+
538
+ if [ -e "$CFG_FILE_ALT" ]
539
+ then
540
+ TODOTXT_CFG_FILE="$CFG_FILE_ALT"
541
+ fi
542
+ }
543
+
544
+
545
+ if [ -z "$TODO_ACTIONS_DIR" -o ! -d "$TODO_ACTIONS_DIR" ]
546
+ then
547
+ TODO_ACTIONS_DIR="$HOME/.todo/actions"
548
+ export TODO_ACTIONS_DIR
549
+ fi
550
+
551
+ [ -d "$TODO_ACTIONS_DIR" ] || {
552
+ TODO_ACTIONS_DIR_ALT="$HOME/.todo.actions.d"
553
+
554
+ if [ -d "$TODO_ACTIONS_DIR_ALT" ]
555
+ then
556
+ TODO_ACTIONS_DIR="$TODO_ACTIONS_DIR_ALT"
557
+ fi
558
+ }
559
+
560
+ # === SANITY CHECKS (thanks Karl!) ===
561
+ [ -r "$TODOTXT_CFG_FILE" ] || die "Fatal Error: Cannot read configuration file $TODOTXT_CFG_FILE"
562
+
563
+ . "$TODOTXT_CFG_FILE"
564
+
565
+ # === APPLY OVERRIDES
566
+ if [ -n "$OVR_TODOTXT_AUTO_ARCHIVE" ] ; then
567
+ TODOTXT_AUTO_ARCHIVE="$OVR_TODOTXT_AUTO_ARCHIVE"
568
+ fi
569
+ if [ -n "$OVR_TODOTXT_FORCE" ] ; then
570
+ TODOTXT_FORCE="$OVR_TODOTXT_FORCE"
571
+ fi
572
+ if [ -n "$OVR_TODOTXT_PRESERVE_LINE_NUMBERS" ] ; then
573
+ TODOTXT_PRESERVE_LINE_NUMBERS="$OVR_TODOTXT_PRESERVE_LINE_NUMBERS"
574
+ fi
575
+ if [ -n "$OVR_TODOTXT_PLAIN" ] ; then
576
+ TODOTXT_PLAIN="$OVR_TODOTXT_PLAIN"
577
+ fi
578
+ if [ -n "$OVR_TODOTXT_DATE_ON_ADD" ] ; then
579
+ TODOTXT_DATE_ON_ADD="$OVR_TODOTXT_DATE_ON_ADD"
580
+ fi
581
+ if [ -n "$OVR_TODOTXT_DISABLE_FILTER" ] ; then
582
+ TODOTXT_DISABLE_FILTER="$OVR_TODOTXT_DISABLE_FILTER"
583
+ fi
584
+ if [ -n "$OVR_TODOTXT_VERBOSE" ] ; then
585
+ TODOTXT_VERBOSE="$OVR_TODOTXT_VERBOSE"
586
+ fi
587
+ if [ -n "$OVR_TODOTXT_DEFAULT_ACTION" ] ; then
588
+ TODOTXT_DEFAULT_ACTION="$OVR_TODOTXT_DEFAULT_ACTION"
589
+ fi
590
+ if [ -n "$OVR_TODOTXT_SORT_COMMAND" ] ; then
591
+ TODOTXT_SORT_COMMAND="$OVR_TODOTXT_SORT_COMMAND"
592
+ fi
593
+ if [ -n "$OVR_TODOTXT_FINAL_FILTER" ] ; then
594
+ TODOTXT_FINAL_FILTER="$OVR_TODOTXT_FINAL_FILTER"
595
+ fi
596
+
597
+ ACTION=${1:-$TODOTXT_DEFAULT_ACTION}
598
+
599
+ [ -z "$ACTION" ] && usage
600
+ [ -d "$TODO_DIR" ] || die "Fatal Error: $TODO_DIR is not a directory"
601
+ ( cd "$TODO_DIR" ) || die "Fatal Error: Unable to cd to $TODO_DIR"
602
+
603
+ [ -w "$TMP_FILE" ] || echo -n > "$TMP_FILE" || die "Fatal Error: Unable to write to $TMP_FILE"
604
+ [ -f "$TODO_FILE" ] || cp /dev/null "$TODO_FILE"
605
+ [ -f "$DONE_FILE" ] || cp /dev/null "$DONE_FILE"
606
+ [ -f "$REPORT_FILE" ] || cp /dev/null "$REPORT_FILE"
607
+
608
+ if [ $TODOTXT_PLAIN = 1 ]; then
609
+ for clr in ${!PRI_@}; do
610
+ export $clr=$NONE
611
+ done
612
+ PRI_X=$NONE
613
+ DEFAULT=$NONE
614
+ COLOR_DONE=$NONE
615
+ fi
616
+
617
+ _addto() {
618
+ file="$1"
619
+ input="$2"
620
+ cleaninput
621
+
622
+ if [[ $TODOTXT_DATE_ON_ADD = 1 ]]; then
623
+ now=$(date '+%Y-%m-%d')
624
+ input=$(echo "$input" | sed -e 's/^\(([A-Z]) \)\{0,1\}/\1'"$now /")
625
+ fi
626
+ echo "$input" >> "$file"
627
+ if [ $TODOTXT_VERBOSE -gt 0 ]; then
628
+ TASKNUM=$(sed -n '$ =' "$file")
629
+ BASE=$(basename "$file")
630
+ PREFIX=$(echo ${BASE%%.[^.]*} | tr 'a-z' 'A-Z')
631
+ echo "$TASKNUM $input"
632
+ echo "${PREFIX}: $TASKNUM added."
633
+ fi
634
+ }
635
+
636
+ _list() {
637
+ local FILE="$1"
638
+ ## If the file starts with a "/" use absolute path. Otherwise,
639
+ ## try to find it in either $TODO_DIR or using a relative path
640
+ if [ "${1:0:1}" == / ]; then
641
+ ## Absolute path
642
+ src="$FILE"
643
+ elif [ -f "$TODO_DIR/$FILE" ]; then
644
+ ## Path relative to todo.sh directory
645
+ src="$TODO_DIR/$FILE"
646
+ elif [ -f "$FILE" ]; then
647
+ ## Path relative to current working directory
648
+ src="$FILE"
649
+ elif [ -f "$TODO_DIR/${FILE}.txt" ]; then
650
+ ## Path relative to todo.sh directory, missing file extension
651
+ src="$TODO_DIR/${FILE}.txt"
652
+ else
653
+ die "TODO: File $FILE does not exist."
654
+ fi
655
+
656
+ ## Get our search arguments, if any
657
+ shift ## was file name, new $1 is first search term
658
+
659
+ ## Prefix the filter_command with the pre_filter_command
660
+ filter_command="${pre_filter_command:-}"
661
+
662
+ for search_term
663
+ do
664
+ ## See if the first character of $search_term is a dash
665
+ if [ "${search_term:0:1}" != '-' ]
666
+ then
667
+ ## First character isn't a dash: hide lines that don't match
668
+ ## this $search_term
669
+ filter_command="${filter_command:-} ${filter_command:+|} \
670
+ grep -i \"$search_term\" "
671
+ else
672
+ ## First character is a dash: hide lines that match this
673
+ ## $search_term
674
+ #
675
+ ## Remove the first character (-) before adding to our filter command
676
+ filter_command="${filter_command:-} ${filter_command:+|} \
677
+ grep -v -i \"${search_term:1}\" "
678
+ fi
679
+ done
680
+
681
+ ## If post_filter_command is set, append it to the filter_command
682
+ [ -n "$post_filter_command" ] && {
683
+ filter_command="${filter_command:-}${filter_command:+ | }${post_filter_command:-}"
684
+ }
685
+
686
+ ## Figure out how much padding we need to use
687
+ ## We need one level of padding for each power of 10 $LINES uses
688
+ LINES=$( sed -n '$ =' "$src" )
689
+ PADDING=${#LINES}
690
+
691
+ ## Number the file, then run the filter command,
692
+ ## then sort and mangle output some more
693
+ if [[ $TODOTXT_DISABLE_FILTER = 1 ]]; then
694
+ TODOTXT_FINAL_FILTER="cat"
695
+ fi
696
+ items=$(
697
+ sed = "$src" \
698
+ | sed "N; s/^/ /; s/ *\(.\{$PADDING,\}\)\n/\1 /" \
699
+ | grep -v "^[ 0-9]\+ *$"
700
+ )
701
+ if [ "${filter_command}" ]; then
702
+ filtered_items=$(echo -n "$items" | eval ${filter_command})
703
+ else
704
+ filtered_items=$items
705
+ fi
706
+ filtered_items=$(
707
+ echo -n "$filtered_items" \
708
+ | sed '''
709
+ s/^ /00000/;
710
+ s/^ /0000/;
711
+ s/^ /000/;
712
+ s/^ /00/;
713
+ s/^ /0/;
714
+ ''' \
715
+ | eval ${TODOTXT_SORT_COMMAND} \
716
+ | awk '''
717
+ function highlight(colorVar, color) {
718
+ color = ENVIRON[colorVar]
719
+ gsub(/\\+033/, "\033", color)
720
+ return color
721
+ }
722
+ {
723
+ pos = match($0, /\([A-Z]\)/)
724
+ if (match($0, /^[0-9]+ x /)) {
725
+ print highlight("COLOR_DONE") $0 highlight("DEFAULT")
726
+ } else if (pos > 0) {
727
+ clr = highlight("PRI_" substr($0, pos+1, 1))
728
+ print ( clr ? clr : highlight("PRI_X") ) $0 highlight("DEFAULT")
729
+ } else { print }
730
+ }
731
+ ''' \
732
+ | sed '''
733
+ s/'${HIDE_PRIORITY_SUBSTITUTION:-^}'//g
734
+ s/'${HIDE_PROJECTS_SUBSTITUTION:-^}'//g
735
+ s/'${HIDE_CONTEXTS_SUBSTITUTION:-^}'//g
736
+ ''' \
737
+ | eval ${TODOTXT_FINAL_FILTER} \
738
+ )
739
+ [ "$filtered_items" ] && echo "$filtered_items"
740
+
741
+ if [ $TODOTXT_VERBOSE -gt 0 ]; then
742
+ BASE=$(basename "$FILE")
743
+ PREFIX=$(echo ${BASE%%.[^.]*} | tr 'a-z' 'A-Z')
744
+ NUMTASKS=$( echo -n "$filtered_items" | sed -n '$ =' )
745
+ TOTALTASKS=$( echo -n "$items" | sed -n '$ =' )
746
+
747
+ echo "--"
748
+ echo "${PREFIX}: ${NUMTASKS:-0} of ${TOTALTASKS:-0} tasks shown"
749
+ fi
750
+ if [ $TODOTXT_VERBOSE -gt 1 ]; then
751
+ echo "TODO DEBUG: Filter Command was: ${filter_command:-cat}"
752
+ fi
753
+ }
754
+
755
+ export -f cleaninput _list die
756
+
757
+ # == HANDLE ACTION ==
758
+ action=$( printf "%s\n" "$ACTION" | tr 'A-Z' 'a-z' )
759
+
760
+ ## If the first argument is "command", run the rest of the arguments
761
+ ## using todo.sh builtins.
762
+ ## Else, run a actions script with the name of the command if it exists
763
+ ## or fallback to using a builtin
764
+ if [ "$action" == command ]
765
+ then
766
+ ## Get rid of "command" from arguments list
767
+ shift
768
+ ## Reset action to new first argument
769
+ action=$( printf "%s\n" "$1" | tr 'A-Z' 'a-z' )
770
+ elif [ -d "$TODO_ACTIONS_DIR" -a -x "$TODO_ACTIONS_DIR/$action" ]
771
+ then
772
+ "$TODO_ACTIONS_DIR/$action" "$@"
773
+ status=$?
774
+ cleanup
775
+ exit $status
776
+ fi
777
+
778
+ ## Only run if $action isn't found in .todo.actions.d
779
+ case $action in
780
+ "add" | "a")
781
+ if [[ -z "$2" && $TODOTXT_FORCE = 0 ]]; then
782
+ echo -n "Add: "
783
+ read input
784
+ else
785
+ [ -z "$2" ] && die "usage: $TODO_SH add \"TODO ITEM\""
786
+ shift
787
+ input=$*
788
+ fi
789
+ _addto "$TODO_FILE" "$input"
790
+ ;;
791
+
792
+ "addm")
793
+ if [[ -z "$2" && $TODOTXT_FORCE = 0 ]]; then
794
+ echo -n "Add: "
795
+ read input
796
+ else
797
+ [ -z "$2" ] && die "usage: $TODO_SH addm \"TODO ITEM\""
798
+ shift
799
+ input=$*
800
+ fi
801
+
802
+ # Set Internal Field Seperator as newline so we can
803
+ # loop across multiple lines
804
+ SAVEIFS=$IFS
805
+ IFS=$'\n'
806
+
807
+ # Treat each line seperately
808
+ for line in $input ; do
809
+ _addto "$TODO_FILE" "$line"
810
+ done
811
+ IFS=$SAVEIFS
812
+ ;;
813
+
814
+ "addto" )
815
+ [ -z "$2" ] && die "usage: $TODO_SH addto DEST \"TODO ITEM\""
816
+ dest="$TODO_DIR/$2"
817
+ [ -z "$3" ] && die "usage: $TODO_SH addto DEST \"TODO ITEM\""
818
+ shift
819
+ shift
820
+ input=$*
821
+
822
+ if [ -f "$dest" ]; then
823
+ _addto "$dest" "$input"
824
+ else
825
+ die "TODO: Destination file $dest does not exist."
826
+ fi
827
+ ;;
828
+
829
+ "append" | "app" )
830
+ errmsg="usage: $TODO_SH append ITEM# \"TEXT TO APPEND\""
831
+ shift; item=$1; shift
832
+
833
+ [ -z "$item" ] && die "$errmsg"
834
+ [[ "$item" = +([0-9]) ]] || die "$errmsg"
835
+ todo=$(sed "$item!d" "$TODO_FILE")
836
+ [ -z "$todo" ] && die "TODO: No task $item."
837
+ if [[ -z "$1" && $TODOTXT_FORCE = 0 ]]; then
838
+ echo -n "Append: "
839
+ read input
840
+ else
841
+ input=$*
842
+ fi
843
+ case "$input" in
844
+ [$SENTENCE_DELIMITERS]*) appendspace=;;
845
+ *) appendspace=" ";;
846
+ esac
847
+ cleaninput "for sed"
848
+
849
+ if sed -i.bak $item" s|^.*|&${appendspace}${input}|" "$TODO_FILE"; then
850
+ if [ $TODOTXT_VERBOSE -gt 0 ]; then
851
+ newtodo=$(sed "$item!d" "$TODO_FILE")
852
+ echo "$item $newtodo"
853
+ fi
854
+ else
855
+ die "TODO: Error appending task $item."
856
+ fi
857
+ ;;
858
+
859
+ "archive" )
860
+ archive;;
861
+
862
+ "del" | "rm" )
863
+ # replace deleted line with a blank line when TODOTXT_PRESERVE_LINE_NUMBERS is 1
864
+ errmsg="usage: $TODO_SH del ITEM# [TERM]"
865
+ item=$2
866
+ [ -z "$item" ] && die "$errmsg"
867
+ [[ "$item" = +([0-9]) ]] || die "$errmsg"
868
+ DELETEME=$(sed "$item!d" "$TODO_FILE")
869
+ [ -z "$DELETEME" ] && die "TODO: No task $item."
870
+
871
+ if [ -z "$3" ]; then
872
+ if [ $TODOTXT_FORCE = 0 ]; then
873
+ echo "Delete '$DELETEME'? (y/n)"
874
+ read ANSWER
875
+ else
876
+ ANSWER="y"
877
+ fi
878
+ if [ "$ANSWER" = "y" ]; then
879
+ if [ $TODOTXT_PRESERVE_LINE_NUMBERS = 0 ]; then
880
+ # delete line (changes line numbers)
881
+ sed -i.bak -e $item"s/^.*//" -e '/./!d' "$TODO_FILE"
882
+ else
883
+ # leave blank line behind (preserves line numbers)
884
+ sed -i.bak -e $item"s/^.*//" "$TODO_FILE"
885
+ fi
886
+ if [ $TODOTXT_VERBOSE -gt 0 ]; then
887
+ echo "$item $DELETEME"
888
+ echo "TODO: $item deleted."
889
+ fi
890
+ else
891
+ echo "TODO: No tasks were deleted."
892
+ fi
893
+ else
894
+ sed -i.bak \
895
+ -e $item"s/^\((.) \)\{0,1\} *$3 */\1/g" \
896
+ -e $item"s/ *$3 *\$//g" \
897
+ -e $item"s/ *$3 */ /g" \
898
+ -e $item"s/ *$3 */ /g" \
899
+ -e $item"s/$3//g" \
900
+ "$TODO_FILE"
901
+ newtodo=$(sed "$item!d" "$TODO_FILE")
902
+ if [ "$DELETEME" = "$newtodo" ]; then
903
+ [ $TODOTXT_VERBOSE -gt 0 ] && echo "$item $DELETEME"
904
+ die "TODO: '$3' not found; no removal done."
905
+ fi
906
+ if [ $TODOTXT_VERBOSE -gt 0 ]; then
907
+ echo "$item $DELETEME"
908
+ echo "TODO: Removed '$3' from task."
909
+ echo "$item $newtodo"
910
+ fi
911
+ fi
912
+ ;;
913
+
914
+ "depri" | "dp" )
915
+ errmsg="usage: $TODO_SH depri ITEM#[, ITEM#, ITEM#, ...]"
916
+ shift;
917
+ [ $# -eq 0 ] && die "$errmsg"
918
+
919
+ # Split multiple depri's, if comma separated change to whitespace separated
920
+ # Loop the 'depri' function for each item
921
+ for item in $(echo $* | tr ',' ' '); do
922
+ [[ "$item" = +([0-9]) ]] || die "$errmsg"
923
+ todo=$(sed "$item!d" "$TODO_FILE")
924
+ [ -z "$todo" ] && die "TODO: No task $item."
925
+
926
+ if [[ "$todo" = \(?\)\ * ]]; then
927
+ sed -i.bak -e $item"s/^(.) //" "$TODO_FILE"
928
+ if [ $TODOTXT_VERBOSE -gt 0 ]; then
929
+ NEWTODO=$(sed "$item!d" "$TODO_FILE")
930
+ echo "$item $NEWTODO"
931
+ echo "TODO: $item deprioritized."
932
+ fi
933
+ else
934
+ echo "TODO: $item is not prioritized."
935
+ fi
936
+ done
937
+ ;;
938
+
939
+ "do" )
940
+ errmsg="usage: $TODO_SH do ITEM#[, ITEM#, ITEM#, ...]"
941
+ # shift so we get arguments to the do request
942
+ shift;
943
+ [ "$#" -eq 0 ] && die "$errmsg"
944
+
945
+ # Split multiple do's, if comma separated change to whitespace separated
946
+ # Loop the 'do' function for each item
947
+ for item in $(echo $* | tr ',' ' '); do
948
+ [ -z "$item" ] && die "$errmsg"
949
+ [[ "$item" = +([0-9]) ]] || die "$errmsg"
950
+
951
+ todo=$(sed "$item!d" "$TODO_FILE")
952
+ [ -z "$todo" ] && die "TODO: No task $item."
953
+
954
+ # Check if this item has already been done
955
+ if [ "${todo:0:2}" != "x " ]; then
956
+ now=$(date '+%Y-%m-%d')
957
+ # remove priority once item is done
958
+ sed -i.bak $item"s/^(.) //" "$TODO_FILE"
959
+ sed -i.bak $item"s|^|x $now |" "$TODO_FILE"
960
+ if [ $TODOTXT_VERBOSE -gt 0 ]; then
961
+ newtodo=$(sed "$item!d" "$TODO_FILE")
962
+ echo "$item $newtodo"
963
+ echo "TODO: $item marked as done."
964
+ fi
965
+ else
966
+ echo "TODO: $item is already marked done."
967
+ fi
968
+ done
969
+
970
+ if [ $TODOTXT_AUTO_ARCHIVE = 1 ]; then
971
+ archive
972
+ fi
973
+ ;;
974
+
975
+ "help" )
976
+ if [ -t 1 ] ; then # STDOUT is a TTY
977
+ if which "${PAGER:-less}" >/dev/null 2>&1; then
978
+ # we have a working PAGER (or less as a default)
979
+ help | "${PAGER:-less}" && exit 0
980
+ fi
981
+ fi
982
+ help # just in case something failed above, we go ahead and just spew to STDOUT
983
+ ;;
984
+
985
+ "list" | "ls" )
986
+ shift ## Was ls; new $1 is first search term
987
+ _list "$TODO_FILE" "$@"
988
+ ;;
989
+
990
+ "listall" | "lsa" )
991
+ shift ## Was lsa; new $1 is first search term
992
+
993
+ cat "$TODO_FILE" "$DONE_FILE" > "$TMP_FILE"
994
+ _list "$TMP_FILE" "$@"
995
+ ;;
996
+
997
+ "listfile" | "lf" )
998
+ shift ## Was listfile, next $1 is file name
999
+ FILE="$1"
1000
+ shift ## Was filename; next $1 is first search term
1001
+
1002
+ _list "$FILE" "$@"
1003
+ ;;
1004
+
1005
+ "listcon" | "lsc" )
1006
+ grep -o '[^ ]*@[^ ]\+' "$TODO_FILE" | grep '^@' | sort -u
1007
+ ;;
1008
+
1009
+ "listproj" | "lsprj" )
1010
+ grep -o '[^ ]*+[^ ]\+' "$TODO_FILE" | grep '^+' | sort -u
1011
+ ;;
1012
+
1013
+ "listpri" | "lsp" )
1014
+ shift ## was "listpri", new $1 is priority to list or first TERM
1015
+
1016
+ pri=$(printf "%s\n" "$1" | tr 'a-z' 'A-Z' | grep '^[A-Z]$') && shift || pri="[A-Z]"
1017
+ post_filter_command="grep '^ *[0-9]\+ (${pri}) '"
1018
+ _list "$TODO_FILE" "$@"
1019
+ ;;
1020
+
1021
+ "move" | "mv" )
1022
+ # replace moved line with a blank line when TODOTXT_PRESERVE_LINE_NUMBERS is 1
1023
+ errmsg="usage: $TODO_SH mv ITEM# DEST [SRC]"
1024
+ item=$2
1025
+ dest="$TODO_DIR/$3"
1026
+ src="$TODO_DIR/$4"
1027
+
1028
+ [ -z "$item" ] && die "$errmsg"
1029
+ [ -z "$4" ] && src="$TODO_FILE"
1030
+ [ -z "$dest" ] && die "$errmsg"
1031
+
1032
+ [[ "$item" = +([0-9]) ]] || die "$errmsg"
1033
+
1034
+ [ -f "$src" ] || die "TODO: Source file $src does not exist."
1035
+ [ -f "$dest" ] || die "TODO: Destination file $dest does not exist."
1036
+
1037
+ MOVEME=$(sed "$item!d" "$src")
1038
+ [ -z "$MOVEME" ] && die "$item: No such item in $src."
1039
+ if [ $TODOTXT_FORCE = 0 ]; then
1040
+ echo "Move '$MOVEME' from $src to $dest? (y/n)"
1041
+ read ANSWER
1042
+ else
1043
+ ANSWER="y"
1044
+ fi
1045
+ if [ "$ANSWER" = "y" ]; then
1046
+ if [ $TODOTXT_PRESERVE_LINE_NUMBERS = 0 ]; then
1047
+ # delete line (changes line numbers)
1048
+ sed -i.bak -e $item"s/^.*//" -e '/./!d' "$src"
1049
+ else
1050
+ # leave blank line behind (preserves line numbers)
1051
+ sed -i.bak -e $item"s/^.*//" "$src"
1052
+ fi
1053
+ echo "$MOVEME" >> "$dest"
1054
+
1055
+ if [ $TODOTXT_VERBOSE -gt 0 ]; then
1056
+ echo "$item $MOVEME"
1057
+ echo "TODO: $item moved from '$src' to '$dest'."
1058
+ fi
1059
+ else
1060
+ echo "TODO: No tasks moved."
1061
+ fi
1062
+ ;;
1063
+
1064
+ "prepend" | "prep" )
1065
+ errmsg="usage: $TODO_SH prepend ITEM# \"TEXT TO PREPEND\""
1066
+ replaceOrPrepend 'prepend' "$@"
1067
+ ;;
1068
+
1069
+ "pri" | "p" )
1070
+ item=$2
1071
+ newpri=$( printf "%s\n" "$3" | tr 'a-z' 'A-Z' )
1072
+
1073
+ errmsg="usage: $TODO_SH pri ITEM# PRIORITY
1074
+ note: PRIORITY must be anywhere from A to Z."
1075
+
1076
+ [ "$#" -ne 3 ] && die "$errmsg"
1077
+ [[ "$item" = +([0-9]) ]] || die "$errmsg"
1078
+ [[ "$newpri" = @([A-Z]) ]] || die "$errmsg"
1079
+
1080
+ oldpri=$(sed -ne $item's/^(\(.\)) .*/\1/p' "$TODO_FILE")
1081
+ if [ "$oldpri" != "$newpri" ]; then
1082
+ sed -i.bak -e $item"s/^(.) //" -e $item"s/^/($newpri) /" "$TODO_FILE"
1083
+ fi
1084
+ if [ $TODOTXT_VERBOSE -gt 0 ]; then
1085
+ NEWTODO=$(sed "$item!d" "$TODO_FILE")
1086
+ echo "$item $NEWTODO"
1087
+ if [ "$oldpri" != "$newpri" ]; then
1088
+ if [ "$oldpri" ]; then
1089
+ echo "TODO: $item re-prioritized from ($oldpri) to ($newpri)."
1090
+ else
1091
+ echo "TODO: $item prioritized ($newpri)."
1092
+ fi
1093
+ fi
1094
+ fi
1095
+ if [ "$oldpri" = "$newpri" ]; then
1096
+ echo "TODO: $item already prioritized ($newpri)."
1097
+ fi
1098
+ ;;
1099
+
1100
+ "replace" )
1101
+ errmsg="usage: $TODO_SH replace ITEM# \"UPDATED ITEM\""
1102
+ replaceOrPrepend 'replace' "$@"
1103
+ ;;
1104
+
1105
+ "report" )
1106
+ #archive first
1107
+ sed '/^x /!d' "$TODO_FILE" >> "$DONE_FILE"
1108
+ sed -i.bak '/^x /d' "$TODO_FILE"
1109
+
1110
+ NUMLINES=$( sed -n '$ =' "$TODO_FILE" )
1111
+ if [ ${NUMLINES:-0} = "0" ]; then
1112
+ echo "datetime todos dones" >> "$REPORT_FILE"
1113
+ fi
1114
+ #now report
1115
+ TOTAL=$( sed -n '$ =' "$TODO_FILE" )
1116
+ TDONE=$( sed -n '$ =' "$DONE_FILE" )
1117
+ TECHO=$(echo $(date +%Y-%m-%d-%T); echo ' '; echo ${TOTAL:-0}; echo ' ';
1118
+ echo ${TDONE:-0})
1119
+ echo $TECHO >> "$REPORT_FILE"
1120
+ [ $TODOTXT_VERBOSE -gt 0 ] && echo "TODO: Report file updated."
1121
+ cat "$REPORT_FILE"
1122
+ ;;
1123
+
1124
+ * )
1125
+ usage;;
1126
+ esac
1127
+
1128
+ cleanup