Annotation of todotxt/Text-Todo/bin/todo.pl, Revision 1.4
1.1 andrew 1: #!/usr/bin/perl
1.4 ! andrew 2: # $RedRiver: todo.pl,v 1.3 2010/01/10 22:59:16 andrew Exp $
1.1 andrew 3: ########################################################################
4: # todo.pl *** a perl version of todo.sh. Uses Text::Todo.
5: #
6: # 2010.01.07 #*#*# andrew fresh <andrew@cpan.org>
7: ########################################################################
8: # Copyright 2010 Andrew Fresh, all rights reserved
9: #
10: # This program is free software; you can redistribute it and/or modify
11: # it under the same terms as Perl itself.
12: ########################################################################
13: use strict;
14: use warnings;
15:
16: use Data::Dumper;
1.2 andrew 17:
18: use Getopt::Std;
1.1 andrew 19: use Text::Todo;
20:
21: use version; our $VERSION = qv('0.0.1');
22:
1.2 andrew 23: # option defaults
24: my $config_file = $ENV{HOME} . '/todo.cfg';
1.3 andrew 25: CONFIG: foreach my $f ( $config_file, $ENV{HOME} . '/.todo.cfg', ) {
26: if ( -e $f ) {
27: $config_file = $f;
28: last CONFIG;
29: }
30: }
1.2 andrew 31:
32: my %actions = (
1.3 andrew 33: add => \&add,
34: addto => \&addto,
35: append => \&append,
36: archive => \&archive,
37: command => \&command,
38: del => \&del,
39: depri => \&depri,
40: do => \&mark_done,
41: help => \&help,
1.2 andrew 42: list => \&list,
1.3 andrew 43: listall => \&listall,
44: listcon => \&listcon,
45: listfile => \&listfile,
46: listpri => \&listpri,
47: listproj => \&listproj,
48: move => \&move,
49: prepend => \&prepend,
50: pri => \&pri,
51: replace => \&replace,
52: report => \&report,
1.2 andrew 53: );
54:
55: my %aliases = (
56: a => 'add',
57: app => 'append',
58: rm => 'del',
59: dp => 'depri',
60: ls => 'list',
61: lsa => 'listall',
62: lsc => 'listcon',
63: lf => 'listfile',
1.3 andrew 64: lsp => 'listpri',
1.2 andrew 65: lsprj => 'listproj',
66: mv => 'move',
67: prep => 'prepend',
68: p => 'pri',
69: );
70:
71: my %opts;
72: getopts( '@+d:fhpPntvV', \%opts );
73:
74: my $action = shift @ARGV;
75: if ( $action && $action eq 'command' ) {
76:
77: # We don't support action scripts so . . .
78: $action = shift @ARGV;
79: }
80: if ( $action && exists $aliases{$action} ) {
81: $action = $aliases{$action};
82: }
83:
84: if ( $opts{h} || !$action ) {
85: usage( $opts{h} );
86: }
87:
88: my @unsupported = grep { defined $opts{$_} } qw( @ + f h p P n t v V );
89: if (@unsupported) {
1.3 andrew 90: warn 'Unsupported options: ' . ( join q{, }, @unsupported ) . "\n";
1.2 andrew 91: }
92:
93: if ( $opts{d} ) {
94: $config_file = $opts{d};
95: }
96:
97: if ( exists $actions{$action} ) {
98: my $config = read_config($config_file);
99: my $action = $actions{$action}->( $config, @ARGV );
100: }
101: else {
102: usage();
103: }
104:
1.3 andrew 105: sub add {
106: my ( $config, $entry ) = @_;
107: if ( !$entry ) {
108: die "usage: todo.pl add 'item'\n";
109: }
110:
111: my $todo = Text::Todo->new($config);
112: if ( $todo->add($entry) ) {
113: my @list = $todo->list;
114: my $lines = scalar @list;
115:
116: print "TODO: '$entry' added on line $lines\n";
117:
118: return $lines;
119: }
120: die "Unable to add [$entry]\n";
121: }
122:
123: sub addto {
124: my ( $config, $file, $entry ) = @_;
125: if ( !( $file && $entry ) ) {
126: die "usage: todo.pl addto DEST 'TODO ITEM'\n";
127: }
128:
129: my $todo = Text::Todo->new($config);
130:
131: $file = $todo->file($file);
132: if ( $todo->addto( $file, $entry ) ) {
133: my @list = $todo->listfile($file);
134: my $lines = scalar @list;
135:
136: print "TODO: '$entry' added to $file on line $lines\n";
137:
138: return $lines;
139: }
140: die "Unable to add [$entry]\n";
141: }
142:
1.4 ! andrew 143: sub append {
! 144: my ( $config, $line, $text ) = @_;
! 145: if ( !( $line && $text && $line =~ /^\d+$/xms ) ) {
! 146: die 'usage: todo.pl append ITEM# "TEXT TO APPEND"' . "\n";
! 147: }
! 148:
! 149: my $todo = Text::Todo->new($config);
! 150: my $entry = $todo->list->[ $line - 1 ];
! 151:
! 152: if ( $entry->append($text) && $todo->save ) {
! 153: return printf "%02d: %s\n", $line, $entry->text;
! 154: }
! 155: die "Unable to append\n";
! 156: }
! 157:
1.3 andrew 158: sub archive { return &unsupported }
159: sub command { return &unsupported }
160: sub del { return &unsupported }
161: sub depri { return &unsupported }
162: sub mark_done { return &unsupported }
163: sub help { return &unsupported }
164:
1.2 andrew 165: sub list {
1.3 andrew 166: my ( $config, $term ) = @_;
167: my $todo = Text::Todo->new($config);
168:
169: my @list = _number_list( $todo->list );
170: my $shown = _show_sorted_list( $term, @list );
171:
172: return _show_list_footer( $shown, scalar @list, $config->{todo_file} );
173: }
174:
175: sub listall {
176: my ( $config, $term ) = @_;
177: my $todo = Text::Todo->new($config);
178:
179: my @list = _number_list(
180: $todo->listfile('todo_file'),
181: $todo->listfile('done_file'),
182: );
183: my $shown = _show_sorted_list( $term, @list );
184:
185: return _show_list_footer( $shown, scalar @list, $config->{'todo_dir'} );
186: }
187:
188: sub listcon {
1.2 andrew 189: my ($config) = @_;
190: my $todo = Text::Todo->new($config);
1.3 andrew 191: return print map {"\@$_\n"} $todo->listcon;
192: }
1.2 andrew 193:
1.3 andrew 194: sub listfile {
195: my ( $config, $file, $term ) = @_;
196: if ( !$file ) {
197: die "usage: todo.pl listfile SRC [TERM]\n";
1.2 andrew 198: }
1.3 andrew 199: my $todo = Text::Todo->new($config);
200:
201: my @list = _number_list( $todo->listfile($file) );
202: my $shown = _show_sorted_list( $term, @list );
203:
204: return _show_list_footer( $shown, scalar @list, $file );
205: }
206:
207: sub listpri {
208: my ( $config, $pri ) = @_;
209:
210: my $todo = Text::Todo->new($config);
211:
212: my @list = _number_list( $todo->listfile('todo_file') );
213: my @pri_list;
214: if ($pri) {
215: $pri = uc $pri;
216: if ( $pri !~ /^[A-Z]$/xms ) {
217: die "usage: todo.pl listpri PRIORITY\n",
218: "note: PRIORITY must a single letter from A to Z.\n";
219: }
220: @pri_list = grep {
221: defined $_->{entry}->priority
222: && $_->{entry}->priority eq $pri
223: } @list;
224: }
225: else {
226: @pri_list = grep { $_->{entry}->priority } @list;
227: }
228:
229: my $shown = _show_sorted_list( undef, @pri_list );
230:
231: return _show_list_footer( $shown, scalar @list, $config->{todo_file} );
232: }
233:
234: sub listproj {
235: my ($config) = @_;
236: my $todo = Text::Todo->new($config);
237: return print map {"\+$_\n"} $todo->listproj;
238: }
239:
240: sub move { return &unsupported }
241: sub prepend { return &unsupported }
242: sub pri { return &unsupported }
243: sub replace { return &unsupported }
244: sub report { return &unsupported }
245:
246: sub _number_list {
247: my (@list) = @_;
248:
249: my $line = 1;
250: return map { { line => $line++, entry => $_, } } @list;
251: }
252:
253: sub _show_sorted_list {
254: my ( $term, @list ) = @_;
255: $term = defined $term ? quotemeta($term) : '';
256:
257: my $shown = 0;
1.4 ! andrew 258: my @sorted
! 259: = map { sprintf "%02d %s", $_->{line}, $_->{entry}->text }
! 260: sort { lc $a->{entry}->text cmp lc $b->{entry}->text } @list;
! 261:
! 262: foreach my $line ( grep {/$term/xms} @sorted ) {
! 263: print $line, "\n";
1.3 andrew 264: $shown++;
265: }
266:
267: return $shown;
268: }
269:
270: sub _show_list_footer {
271: my ( $shown, $total, $file ) = @_;
272:
273: $shown ||= 0;
274: $total ||= 0;
275:
276: print "-- \n";
277: print "TODO: $shown of $total tasks shown from $file\n";
278:
279: return 1;
1.2 andrew 280: }
281:
282: sub unsupported { die "Unsupported action\n" }
283:
284: sub usage {
285: my ($long) = @_;
286:
287: print <<'EOL';
288: * command list taken from todo.sh for compatibility
289: Usage: todo.pl [-fhpantvV] [-d todo_config] action
290: EOL
291:
292: if ($long) {
293: print <<'EOL';
1.3 andrew 294:
1.2 andrew 295: Actions:
296: add|a "THING I NEED TO DO +project @context"
297: addto DEST "TEXT TO ADD"
298: append|app NUMBER "TEXT TO APPEND"
299: archive
300: command [ACTIONS]
301: del|rm NUMBER [TERM]
302: dp|depri NUMBER
303: do NUMBER
304: help
305: list|ls [TERM...]
306: listall|lsa [TERM...]
307: listcon|lsc
308: listfile|lf SRC [TERM...]
309: listpri|lsp [PRIORITY]
310: listproj|lsprj
311: move|mv NUMBER DEST [SRC]
312: prepend|prep NUMBER "TEXT TO PREPEND"
313: pri|p NUMBER PRIORITY
314: replace NUMBER "UPDATED TODO"
315: report
316: EOL
317: }
318: else {
319: print <<'EOL';
320: Try 'todo.pl -h' for more information.
321: EOL
322: }
323:
324: exit;
325: }
326:
327: sub read_config {
328: my ($file) = @_;
329:
330: my %config;
1.3 andrew 331: open my $fh, '< ', $file or die "Unable to open [$file]: $!";
1.2 andrew 332: LINE: while (<$fh>) {
333: s/\r?\n$//xms;
334: s/\s*\#.*$//xms;
335: next LINE unless $_;
336:
337: if (s/^\s*export\s+//xms) {
338: my ( $key, $value ) = /^([^=]+)\s*=\s*"?(.*?)"?\s*$/xms;
339: if ($key) {
340: foreach my $k ( keys %config ) {
341: $value =~ s/\$\Q$k\E/$config{$k}/gxms;
342: $value =~ s/\${\Q$k\E}/$config{$k}/gxms;
343: }
344: foreach my $k ( keys %ENV ) {
345: $value =~ s/\$\Q$k\E/$ENV{$k}/gxms;
346: $value =~ s/\${\Q$k\E}/$ENV{$k}/gxms;
347: }
348: $value =~ s/\$\w+//gxms;
349: $value =~ s/\${\w+}//gxms;
350:
351: $config{$key} = $value;
352: }
353: }
354: }
355: close $fh;
1.1 andrew 356:
1.2 andrew 357: my %lc_config;
358: foreach my $k ( keys %config ) {
359: $lc_config{ lc($k) } = $config{$k};
360: }
1.1 andrew 361:
1.2 andrew 362: return \%lc_config;
1.1 andrew 363: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>