Hello everyone. I want to share with you a small script I wrote days ago that I use to manage a plain-text ToDo list.
The usage is IMHO very simple and it is fully documented in the POD section. Just use perldoc.
Despite it was initially written for Unix like environments it works quite well also under Windows (it is all a matter of file management).
Hope you'll find it useful.
#!/usr/bin/perl
# Simple command-line todo list manager.
#
# Copyright 2010 Alessandro Ghedini <al3xbio@gmail.com>
# --------------------------------------------------------------
# "THE BEER-WARE LICENSE" (Revision 42):
# Alessandro Ghedini wrote this file. As long as you retain this
# notice you can do whatever you want with this stuff. If we
# meet some day, and you think this stuff is worth it, you can
# buy me a beer in return.
# --------------------------------------------------------------
use strict;
die "For info type 'perldoc $0'\n" unless $#ARGV >= 0;
my ($default_todo, $default_done);
if ($^O eq 'MSWin32') {
$default_todo = $ENV{USERPROFILE}."\\todo.txt";
$default_done = $ENV{USERPROFILE}."\\done.txt";
} else {
$default_todo = $ENV{HOME}."/.todo";
$default_done = $ENV{HOME}."/.done";
}
my $todo_file = $ENV{TODO_FILE} ne "" ? $ENV{TODO_FILE} : $default_tod
+o;
my $done_file = $ENV{DONE_FILE} ne "" ? $ENV{DONE_FILE} : $default_don
+e;
if (!(-e $todo_file)) {
open(TODO_FILE, ">$todo_file") or
die("Cannot create '$todo_file'. Check TODO_FILE and DONE_FILE
+ env variables.");
close TODO_FILE;
}
if (!(-e $done_file)) {
open(DONE_FILE, ">$done_file") or
die("Cannot create '$done_file'. Check TODO_FILE and DONE_FILE
+ env variables.");
close DONE_FILE;
}
my $action = $ARGV[0];
if ($action eq 'add') {
splice @ARGV, 0, 1;
my $msg = join ' ', @ARGV;
my $txt = $msg." {".time()."}";
append_file($todo_file, $txt);
print "Added new task.\n";
} elsif ($action eq 'rm') {
my $id = $ARGV[1];
my @data = read_file($todo_file);
splice(@data, $id-1, 1);
write_file($todo_file, \@data);
print "Removed task number ".$id.".\n";
} elsif ($action eq 'ls') {
my $grep = $ARGV[1] ne "" ? $ARGV[1] : "(.*?)";
my @data = read_file($todo_file);
print_tasks($grep, @data);
} elsif ($action eq 'do') {
my $id = $ARGV[1];
my @data = read_file($todo_file);
my $done = $data[$id-1];
splice(@data, $id-1, 1);
write_file($todo_file, \@data);
chomp $done;
append_file($done_file, $done);
print "Task number $id marked as done and moved to $done_file.\n";
} elsif ($action eq 'ed') {
my $id = $ARGV[1];
splice @ARGV, 0, 2;
my $msg = join ' ', @ARGV;
my @data = read_file($todo_file);
$data[$id-1] = $msg." {".time()."}\n";
write_file($todo_file, \@data);
print "Task number $id successfully replaced.\n";
} elsif ($action eq 'mv') {
my $id = $ARGV[1];
my $new_pos = $ARGV[2];
my @data = read_file($todo_file);
my $tmp = $data[$new_pos-1];
$data[$new_pos-1] = $data[$id-1];
$data[$id-1] = $tmp;
write_file($todo_file, \@data);
} elsif ($action eq 'done') {
my $grep = $ARGV[1] ne "" ? $ARGV[1] : "(.*?)";
my @data = read_file($done_file);
print_tasks($grep, @data);
} else {
print "ERROR: Invalid action '$action'.\n";
print "Type 'perldoc $0' for manual\n";
}
sub print_tasks {
my $grep = shift;
my @tasks = @_;
my $i = 0;
foreach my $task(@tasks) {
$i++;
next unless $task =~ m/$grep/;
$task =~ m/{(.*?)}/;
my $rel_time = relative_time($1);
$task =~ s/\{$1\}/\ ($rel_time\)/g;
print $i." ".$task;
}
}
sub read_file {
my $file = shift;
open(FILE, "<$file") or die("Cannot open $file in read mode.\n");
my @data = <FILE>;
close(FILE);
return @data;
}
sub write_file {
my $file = shift;
my $data_ref = shift;
open(FILE, ">$file") or die("Cannot open $file in write mode.\n");
print FILE @$data_ref;
close(FILE);
}
sub append_file {
my $file = shift;
my $data = shift;
open(FILE, ">>$file") or die("Cannot open $file in append mode.\n"
+);
print FILE $data."\n";
close(FILE);
}
sub relative_time {
my $time_value = shift;
my $now = time();
my $delta = $now - $time_value;
if($delta < 60) {
return 'less than a minute ago';
} elsif($delta < 120) {
return 'about a minute ago';
} elsif($delta < (45*60)) {
return int($delta / 60) . ' minutes ago';
} elsif($delta < (90*60)) {
return 'about an hour ago';
} elsif($delta < (24*60*60)) {
return 'about ' . int($delta / 3600) . ' hours ago';
} elsif($delta < (48*60*60)) {
return '1 day ago';
} else {
return int($delta / 86400) . ' days ago';
}
}
__END__
=head1 NAME
ToDo - A simple command-line todo list manager.
=head1 USAGE
B<ToDo> I<ACTION> I<ARGS>
=head1 ACTIONS
=over
=item B<add SOME TEXT @LIST>
Add a task to your todo list. List notation optional.
=item B<rm NUMBER>
Remove task on line NUMBER from your todo list.
=item B<ls TERM>
Show tasks that contain TERM. If no TERM specified, the entire list is
showed.
=item B<done TERM>
As ls but for already done tasks.
=item B<do NUMBER>
Mark the task on line NUMBER as done. It moves the task from todo file
to done file.
=item B<ed NUMBER "Some text">
Replace the task on line NUMBER with the text given.
=item B<mv NUMBER POSITION>
Move the task on line NUMBER to line POSITION and the task on POSITION
to NUMBER.
=back
=head1 CONFIGURATION
Set TODO_FILE and DONE_FILE environment variables, to your todo and do
+ne
files locations. Defaults ~/.todo and ~/.done.
=cut