todo.rb 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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