#!/usr/bin/perl # Simple command-line todo list manager. # # Copyright 2010 Alessandro Ghedini # -------------------------------------------------------------- # "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_todo; my $done_file = $ENV{DONE_FILE} ne "" ? $ENV{DONE_FILE} : $default_done; 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 = ; 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 I I =head1 ACTIONS =over =item B Add a task to your todo list. List notation optional. =item B Remove task on line NUMBER from your todo list. =item B Show tasks that contain TERM. If no TERM specified, the entire list is showed. =item B As ls but for already done tasks. =item B Mark the task on line NUMBER as done. It moves the task from todo file to done file. =item B Replace the task on line NUMBER with the text given. =item B 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 done files locations. Defaults ~/.todo and ~/.done. =cut