| 1 |
#!/usr/bin/env perl |
| 2 |
|
| 3 |
# lite2do, a lightweight text-based todo manager |
| 4 |
# Copyright (C) 2008, 2009 Jaromir Hradilek |
| 5 |
|
| 6 |
# This program is free software; you can redistribute it and/or modify it |
| 7 |
# under the terms of the GNU General Public License as published by the |
| 8 |
# Free Software Foundation; version 3 of the License. |
| 9 |
# |
| 10 |
# This program is distributed in the hope that it will be useful, but |
| 11 |
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABI- |
| 12 |
# LITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public |
| 13 |
# License for more details. |
| 14 |
# |
| 15 |
# You should have received a copy of the GNU General Public License along |
| 16 |
# with this program. If not, see <http://www.gnu.org/licenses/>. |
| 17 |
|
| 18 |
use strict; |
| 19 |
use warnings; |
| 20 |
use locale; |
| 21 |
use File::Copy; |
| 22 |
use File::Basename; |
| 23 |
use File::Spec::Functions; |
| 24 |
use Term::ANSIColor; |
| 25 |
use Getopt::Long; |
| 26 |
|
| 27 |
# General script information: |
| 28 |
use constant NAME => basename($0, '.pl'); # Script name. |
| 29 |
use constant VERSION => '1.1.1'; # Script version. |
| 30 |
|
| 31 |
# General script settings: |
| 32 |
our $HOMEDIR = $ENV{HOME} || $ENV{USERPROFILE} || '.'; # Home directory. |
| 33 |
our $savefile = catfile($HOMEDIR, '.lite2do'); # Save file name. |
| 34 |
our $backext = '.bak'; # Backup suffix. |
| 35 |
our $verbose = 1; # Verbosity level. |
| 36 |
our $coloured = 0; # Set up colours. |
| 37 |
|
| 38 |
# Colours settings: |
| 39 |
our $done = 'green'; # Finished tasks. |
| 40 |
our $undone = ''; # Undone tasks. |
| 41 |
|
| 42 |
# Allowed colours: |
| 43 |
my %valid = map { $_, 1 } qw( black green yellow magenta red blue |
| 44 |
cyan white ); |
| 45 |
|
| 46 |
# Command line options: |
| 47 |
my ($command, $response); |
| 48 |
|
| 49 |
# Signal handlers: |
| 50 |
$SIG{__WARN__} = sub { |
| 51 |
print STDERR NAME . ": " . (shift); |
| 52 |
}; |
| 53 |
|
| 54 |
# Display given message and immediately terminate the script: |
| 55 |
sub exit_with_error { |
| 56 |
my $message = shift || 'An unspecified error has occurred.'; |
| 57 |
my $return_value = shift || 1; |
| 58 |
|
| 59 |
print STDERR NAME . ": $message\n"; |
| 60 |
exit $return_value; |
| 61 |
} |
| 62 |
|
| 63 |
# Display script help: |
| 64 |
sub display_help { |
| 65 |
my $command = shift || ''; |
| 66 |
my $NAME = NAME; |
| 67 |
|
| 68 |
# Parse command and display appropriate usage information: |
| 69 |
if ($command =~ /^(list|ls)$/) { |
| 70 |
print "Displays items in the task list.\n"; |
| 71 |
print "Usage: $NAME list [\@GROUP|%ID] [TEXT...]\n"; |
| 72 |
} |
| 73 |
elsif ($command =~ /^add$/) { |
| 74 |
print "Adds new item to the task list.\n"; |
| 75 |
print "Usage: $NAME add [\@GROUP] TEXT...\n"; |
| 76 |
} |
| 77 |
elsif ($command =~ /^(change|mv)$/) { |
| 78 |
print "Changes selected item in the task list.\n"; |
| 79 |
print "Usage: $NAME change ID \@GROUP|TEXT...\n"; |
| 80 |
} |
| 81 |
elsif ($command =~ /^(finish|fn)$/) { |
| 82 |
print "Finishes selected item in the task list.\n"; |
| 83 |
print "Usage: $NAME finish ID\n"; |
| 84 |
} |
| 85 |
elsif ($command =~ /^(revive|re)$/) { |
| 86 |
print "Revives selected item in the task list.\n"; |
| 87 |
print "Usage: $NAME revive ID\n"; |
| 88 |
} |
| 89 |
elsif ($command =~ /^(remove|rm)$/) { |
| 90 |
print "Removes selected item from the task list.\n"; |
| 91 |
print "Usage: $NAME remove ID\n"; |
| 92 |
} |
| 93 |
elsif ($command =~ /^(undo|ud)$/) { |
| 94 |
print "Reverts last action.\n"; |
| 95 |
print "Usage: $NAME undo\n"; |
| 96 |
} |
| 97 |
elsif ($command =~ /^(groups|gr)$/) { |
| 98 |
print "Displays groups in the task list.\n"; |
| 99 |
print "Usage: $NAME groups\n"; |
| 100 |
} |
| 101 |
elsif ($command =~ /^version$/) { |
| 102 |
print "Displays version information.\n"; |
| 103 |
print "Usage: $NAME version\n"; |
| 104 |
} |
| 105 |
elsif ($command =~ /^help$/) { |
| 106 |
print "Displays usage information.\n"; |
| 107 |
print "Usage: $NAME help [COMMAND]\n"; |
| 108 |
} |
| 109 |
else { |
| 110 |
print << "END_HELP"; |
| 111 |
Usage: $NAME [OPTION...] COMMAND [ARGUMENT...] |
| 112 |
|
| 113 |
Commands: |
| 114 |
list [\@GROUP|%ID] [TEXT] display items in the task list |
| 115 |
add [\@GROUP] TEXT add new item to the task list |
| 116 |
change ID \@GROUP|TEXT change item in the task list |
| 117 |
finish ID finish item in the task list |
| 118 |
revive ID revive item in the task list |
| 119 |
remove ID remove item from the task list |
| 120 |
undo revert last action |
| 121 |
groups display groups in the task list |
| 122 |
help [COMMAND] display usage information |
| 123 |
version display version information |
| 124 |
|
| 125 |
Options: |
| 126 |
-c, --color, --colour use coloured output; turned off by default |
| 127 |
-s, --savefile FILE use selected file instead of default ~/.lite2do |
| 128 |
-f, --finished COLOUR use selected colour for finished tasks; suppor- |
| 129 |
ted options are: black, green, yellow, magenta, |
| 130 |
red, blue, cyan, and white |
| 131 |
-u, --unfinished COLOUR use selected colour for unfinished tasks |
| 132 |
-q, --quiet avoid displaying unnecessary messages |
| 133 |
-h, --help display this help and exit |
| 134 |
-v, --version display version information and exit |
| 135 |
END_HELP |
| 136 |
} |
| 137 |
|
| 138 |
# Return success: |
| 139 |
return 1; |
| 140 |
} |
| 141 |
|
| 142 |
# Display script version: |
| 143 |
sub display_version { |
| 144 |
my ($NAME, $VERSION) = (NAME, VERSION); |
| 145 |
|
| 146 |
# Print message to the STDOUT: |
| 147 |
print << "END_VERSION"; |
| 148 |
$NAME $VERSION |
| 149 |
|
| 150 |
Copyright (C) 2008, 2009 Jaromir Hradilek |
| 151 |
This program is free software; see the source for copying conditions. It is |
| 152 |
distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; |
| 153 |
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PAR- |
| 154 |
TICULAR PURPOSE. |
| 155 |
END_VERSION |
| 156 |
|
| 157 |
# Return success: |
| 158 |
return 1; |
| 159 |
} |
| 160 |
|
| 161 |
# Load selected data from the save file: |
| 162 |
sub load_selection { |
| 163 |
my ($selected, $rest, $id, $group, $task) = @_; |
| 164 |
|
| 165 |
# Escape reserved characters: |
| 166 |
$group =~ s/([\\\^\.\$\|\(\)\[\]\*\+\?\{\}])/\\$1/g if $group; |
| 167 |
$task =~ s/([\\\^\.\$\|\(\)\[\]\*\+\?\{\}])/\\$1/g if $task; |
| 168 |
|
| 169 |
# Remove colons if any: |
| 170 |
$group =~ s/://g if $group; |
| 171 |
|
| 172 |
# Use default pattern when none is provided: |
| 173 |
$id ||= '\d+'; |
| 174 |
$group ||= '[^:]*'; |
| 175 |
$task ||= ''; |
| 176 |
|
| 177 |
# Open the save file for reading: |
| 178 |
if (open(SAVEFILE, "$savefile")) { |
| 179 |
# Process each line: |
| 180 |
while (my $line = <SAVEFILE>) { |
| 181 |
# Check whether the line matches given pattern: |
| 182 |
if ($line =~ /^$group:[^:]*:[1-5]:[ft]:.*$task.*:$id$/i) { |
| 183 |
# Add the line to selected tasks: |
| 184 |
push(@$selected, $line); |
| 185 |
} |
| 186 |
else { |
| 187 |
# Add the line to unselected tasks: |
| 188 |
push(@$rest, $line); |
| 189 |
} |
| 190 |
} |
| 191 |
|
| 192 |
# Close the save file: |
| 193 |
close(SAVEFILE); |
| 194 |
} |
| 195 |
|
| 196 |
# Return success: |
| 197 |
return 1; |
| 198 |
} |
| 199 |
|
| 200 |
# Save given data to the save file: |
| 201 |
sub save_data { |
| 202 |
my $data = shift || die 'Missing argument'; |
| 203 |
|
| 204 |
# Backup the save file: |
| 205 |
copy($savefile, "$savefile$backext") if (-r $savefile); |
| 206 |
|
| 207 |
# Open the save file for writing: |
| 208 |
if (open(SAVEFILE, ">$savefile")) { |
| 209 |
# Write data to the save file: |
| 210 |
foreach my $line (@$data) { |
| 211 |
print SAVEFILE $line; |
| 212 |
} |
| 213 |
|
| 214 |
# Close the save file: |
| 215 |
close(SAVEFILE); |
| 216 |
} |
| 217 |
else { |
| 218 |
# Report failure and exit: |
| 219 |
exit_with_error("Unable to write to `$savefile'.", 13); |
| 220 |
} |
| 221 |
|
| 222 |
# Return success: |
| 223 |
return 1; |
| 224 |
} |
| 225 |
|
| 226 |
# Add given data to the end of the save file: |
| 227 |
sub add_data { |
| 228 |
my $data = shift || die 'Missing argument'; |
| 229 |
|
| 230 |
# Backup the save file: |
| 231 |
copy($savefile, "$savefile$backext") if (-r $savefile); |
| 232 |
|
| 233 |
# Open the save file for appending: |
| 234 |
if (open(SAVEFILE, ">>$savefile")) { |
| 235 |
# Write data to the save file: |
| 236 |
foreach my $line (@$data) { |
| 237 |
print SAVEFILE $line; |
| 238 |
} |
| 239 |
|
| 240 |
# Close the save file: |
| 241 |
close(SAVEFILE); |
| 242 |
} |
| 243 |
else { |
| 244 |
# Report failure and exit: |
| 245 |
exit_with_error("Unable to write to `$savefile'.", 13); |
| 246 |
} |
| 247 |
|
| 248 |
# Return success: |
| 249 |
return 1; |
| 250 |
} |
| 251 |
|
| 252 |
# Get hash of all groups: |
| 253 |
sub get_groups { |
| 254 |
my %groups = (); |
| 255 |
|
| 256 |
# Open the save file for reading: |
| 257 |
if (open(SAVEFILE, "$savefile")) { |
| 258 |
# Build the list of used groups: |
| 259 |
while (my $line = <SAVEFILE>) { |
| 260 |
# Parse the task record: |
| 261 |
if ($line =~ /^([^:]*):/) { |
| 262 |
my $group = lc($1); |
| 263 |
|
| 264 |
# Check whether the group is already added: |
| 265 |
if ($groups{$group}) { |
| 266 |
# Increment the counter: |
| 267 |
$groups{$group} += 1; |
| 268 |
} |
| 269 |
else { |
| 270 |
# Initialize the counter: |
| 271 |
$groups{$group} = 1; |
| 272 |
} |
| 273 |
} |
| 274 |
} |
| 275 |
|
| 276 |
# Close the save file: |
| 277 |
close(SAVEFILE); |
| 278 |
} |
| 279 |
|
| 280 |
# Return the result: |
| 281 |
return %groups; |
| 282 |
} |
| 283 |
|
| 284 |
# Choose first available ID: |
| 285 |
sub choose_id { |
| 286 |
my @used = (); |
| 287 |
my $chosen = 1; |
| 288 |
|
| 289 |
# Open the save file for reading: |
| 290 |
if (open(SAVEFILE, "$savefile")) { |
| 291 |
# Build the list of used IDs: |
| 292 |
while (my $line = <SAVEFILE>) { |
| 293 |
push(@used, int($1)) if ($line =~ /:(\d+)$/); |
| 294 |
} |
| 295 |
|
| 296 |
# Close the save file: |
| 297 |
close(SAVEFILE); |
| 298 |
|
| 299 |
# Find first unused ID: |
| 300 |
foreach my $id (sort {$a <=> $b} @used) { |
| 301 |
$chosen++ if ($chosen == $id); |
| 302 |
} |
| 303 |
} |
| 304 |
|
| 305 |
# Return the result: |
| 306 |
return $chosen; |
| 307 |
} |
| 308 |
|
| 309 |
# Fix the group name: |
| 310 |
sub fix_group { |
| 311 |
my $group = shift || die 'Missing argument'; |
| 312 |
|
| 313 |
# Check whether it contains forbidden characters: |
| 314 |
if ($group =~ /:/) { |
| 315 |
# Display warning: |
| 316 |
print STDERR "Colon is not allowed in the group name. Removing.\n"; |
| 317 |
|
| 318 |
# Remove forbidden characters: |
| 319 |
$group =~ s/://g; |
| 320 |
} |
| 321 |
|
| 322 |
# Check the group name length: |
| 323 |
if (length($group) > 10) { |
| 324 |
# Display warning: |
| 325 |
print STDERR "Group name too long. Stripping.\n"; |
| 326 |
|
| 327 |
# Strip it to the maximal allowed length: |
| 328 |
$group = substr($group, 0, 10); |
| 329 |
} |
| 330 |
|
| 331 |
# Make sure the result is not empty: |
| 332 |
unless ($group) { |
| 333 |
# Display warning: |
| 334 |
print STDERR "Group name is empty. Using the default group instead.\n"; |
| 335 |
|
| 336 |
# Use default group instead: |
| 337 |
$group = 'general'; |
| 338 |
} |
| 339 |
|
| 340 |
# Return the result: |
| 341 |
return $group; |
| 342 |
} |
| 343 |
|
| 344 |
# List items in the task list: |
| 345 |
sub list_tasks { |
| 346 |
my ($group, $task, $id) = @_; |
| 347 |
my (@selected, $state); |
| 348 |
|
| 349 |
# Load matching tasks: |
| 350 |
load_selection(\@selected, undef, $id, $group, $task); |
| 351 |
|
| 352 |
# Check whether the list is not empty: |
| 353 |
if (@selected) { |
| 354 |
# Process each task: |
| 355 |
foreach my $line (sort @selected) { |
| 356 |
# Parse the task record: |
| 357 |
$line =~ /^([^:]*):[^:]*:[1-5]:([ft]):(.*):(\d+)$/; |
| 358 |
$state = ($2 eq 'f') ? '-' : 'f'; |
| 359 |
|
| 360 |
# Check whether to use coloured output: |
| 361 |
if ($coloured) { |
| 362 |
# Decide which colour to use: |
| 363 |
my $colour = ($2 eq 'f') ? $undone : $done; |
| 364 |
|
| 365 |
# Print the task entry: |
| 366 |
print colored (sprintf("%2d. ", $4), "bold"), |
| 367 |
colored ("\@$1 ", "bold $colour"), |
| 368 |
colored ("[$state]", "bold"), |
| 369 |
colored (": $3", "$colour"), |
| 370 |
"\n"; |
| 371 |
} |
| 372 |
else { |
| 373 |
# Print the task entry: |
| 374 |
printf "%2d. @%s [%s]: %s\n", $4, $1, $state, $3; |
| 375 |
} |
| 376 |
} |
| 377 |
} |
| 378 |
else { |
| 379 |
# Report empty list: |
| 380 |
print "No matching task found.\n" if $verbose; |
| 381 |
} |
| 382 |
|
| 383 |
# Return success: |
| 384 |
return 1; |
| 385 |
} |
| 386 |
|
| 387 |
# List groups in the task list: |
| 388 |
sub list_groups { |
| 389 |
# Get list of all groups: |
| 390 |
my %groups = get_groups(); |
| 391 |
|
| 392 |
# Make sure the task list is not empty: |
| 393 |
if (scalar(keys %groups)) { |
| 394 |
# Display the list of groups: |
| 395 |
print join(', ', map { "$_ (" . $groups{$_} . ")" } |
| 396 |
sort keys(%groups)), "\n"; |
| 397 |
} |
| 398 |
else { |
| 399 |
# Report empty list: |
| 400 |
print "No matching task found.\n" if $verbose; |
| 401 |
} |
| 402 |
|
| 403 |
# Return success: |
| 404 |
return 1; |
| 405 |
} |
| 406 |
|
| 407 |
# Add new item to the task list: |
| 408 |
sub add_task { |
| 409 |
my $task = shift || die 'Missing argument'; |
| 410 |
my $group = shift || 'general'; |
| 411 |
my $id = choose_id(); |
| 412 |
|
| 413 |
# Create the task record: |
| 414 |
my @data = (fix_group($group) . ":anytime:3:f:$task:$id\n"); |
| 415 |
|
| 416 |
# Add data to the end of the save file: |
| 417 |
add_data(\@data); |
| 418 |
|
| 419 |
# Report success: |
| 420 |
print "Task has been successfully added with id $id.\n" if $verbose; |
| 421 |
|
| 422 |
# Return success: |
| 423 |
return 1; |
| 424 |
} |
| 425 |
|
| 426 |
# Change selected item in the task list: |
| 427 |
sub change_task { |
| 428 |
my $id = shift || die 'Missing argument'; |
| 429 |
my $text = shift || die 'Missing argument'; |
| 430 |
my $group = shift || 0; |
| 431 |
my (@selected, @rest); |
| 432 |
|
| 433 |
# Load tasks: |
| 434 |
load_selection(\@selected, \@rest, $id); |
| 435 |
|
| 436 |
# Check whether the list is not empty: |
| 437 |
if (@selected) { |
| 438 |
# Parse the task record: |
| 439 |
pop(@selected) =~ /^([^:]*):([^:]*):([1-5]):([ft]):(.*):\d+$/; |
| 440 |
|
| 441 |
# Decide which part to edit: |
| 442 |
unless ($group) { |
| 443 |
# Update the task record: |
| 444 |
push(@rest, "$1:$2:$3:$4:$text:$id\n"); |
| 445 |
} |
| 446 |
else { |
| 447 |
# Update the group record: |
| 448 |
push(@rest, fix_group($text) . ":$2:$3:$4:$5:$id\n"); |
| 449 |
} |
| 450 |
|
| 451 |
# Store data to the save file: |
| 452 |
save_data(\@rest); |
| 453 |
|
| 454 |
# Report success: |
| 455 |
print "Task has been successfully changed.\n" if $verbose; |
| 456 |
} |
| 457 |
else { |
| 458 |
# Report empty list: |
| 459 |
print "No matching task found.\n" if $verbose; |
| 460 |
} |
| 461 |
|
| 462 |
# Return success: |
| 463 |
return 1; |
| 464 |
} |
| 465 |
|
| 466 |
# Mark selected item in the task list as finished: |
| 467 |
sub finish_task { |
| 468 |
my $id = shift || die 'Missing argument'; |
| 469 |
my (@selected, @rest); |
| 470 |
|
| 471 |
# Load tasks: |
| 472 |
load_selection(\@selected, \@rest, $id); |
| 473 |
|
| 474 |
# Check whether the list is not empty: |
| 475 |
if (@selected) { |
| 476 |
# Parse the task record: |
| 477 |
pop(@selected) =~ /^([^:]*):([^:]*):([1-5]):[ft]:(.*):\d+$/; |
| 478 |
|
| 479 |
# Update the task record: |
| 480 |
push(@rest, "$1:$2:$3:t:$4:$id\n"); |
| 481 |
|
| 482 |
# Store data to the save file: |
| 483 |
save_data(\@rest); |
| 484 |
|
| 485 |
# Report success: |
| 486 |
print "Task has been finished.\n" if $verbose; |
| 487 |
} |
| 488 |
else { |
| 489 |
# Report empty list: |
| 490 |
print "No matching task found.\n" if $verbose; |
| 491 |
} |
| 492 |
|
| 493 |
# Return success: |
| 494 |
return 1; |
| 495 |
} |
| 496 |
|
| 497 |
# Mark selected item in the task list as unfinished: |
| 498 |
sub revive_task { |
| 499 |
my $id = shift || die 'Missing argument'; |
| 500 |
my (@selected, @rest); |
| 501 |
|
| 502 |
# Load tasks: |
| 503 |
load_selection(\@selected, \@rest, $id); |
| 504 |
|
| 505 |
# Check whether the list is not empty: |
| 506 |
if (@selected) { |
| 507 |
# Parse the task record: |
| 508 |
pop(@selected) =~ /^([^:]*):([^:]*):([1-5]):[ft]:(.*):\d+$/; |
| 509 |
|
| 510 |
# Update the task record: |
| 511 |
push(@rest, "$1:$2:$3:f:$4:$id\n"); |
| 512 |
|
| 513 |
# Store data to the task list: |
| 514 |
save_data(\@rest); |
| 515 |
|
| 516 |
# Report success: |
| 517 |
print "Task has been revived.\n" if $verbose; |
| 518 |
} |
| 519 |
else { |
| 520 |
# Report empty list: |
| 521 |
print "No matching task found.\n" if $verbose; |
| 522 |
} |
| 523 |
|
| 524 |
# Return success: |
| 525 |
return 1; |
| 526 |
} |
| 527 |
|
| 528 |
# Remove selected item from the task list: |
| 529 |
sub remove_task { |
| 530 |
my $id = shift || die 'Missing argument'; |
| 531 |
my (@selected, @rest); |
| 532 |
|
| 533 |
# Load tasks: |
| 534 |
load_selection(\@selected, \@rest, $id); |
| 535 |
|
| 536 |
# Check whether the list is not empty: |
| 537 |
if (@selected) { |
| 538 |
# Store data to the save file: |
| 539 |
save_data(\@rest); |
| 540 |
|
| 541 |
# Report success: |
| 542 |
print "Task has been successfully removed.\n" if $verbose; |
| 543 |
} |
| 544 |
else { |
| 545 |
# Report empty list: |
| 546 |
print "No matching task found.\n" if $verbose; |
| 547 |
} |
| 548 |
|
| 549 |
# Return success: |
| 550 |
return 1; |
| 551 |
} |
| 552 |
|
| 553 |
# Revert last action: |
| 554 |
sub revert_last_action { |
| 555 |
# Try to restore data from tha backup: |
| 556 |
if (move("$savefile$backext", $savefile)) { |
| 557 |
# Report success: |
| 558 |
print "Last action has been successfully reverted.\n" if $verbose; |
| 559 |
} |
| 560 |
else { |
| 561 |
# Report failure: |
| 562 |
print "Already at oldest change.\n" if $verbose; |
| 563 |
} |
| 564 |
|
| 565 |
# Return success: |
| 566 |
return 1; |
| 567 |
} |
| 568 |
|
| 569 |
# Set up the option parser: |
| 570 |
Getopt::Long::Configure('no_auto_abbrev', 'no_ignore_case', 'bundling'); |
| 571 |
|
| 572 |
# Parse command line options: |
| 573 |
GetOptions( |
| 574 |
# General options: |
| 575 |
'help|h' => sub { display_help(); exit 0 }, |
| 576 |
'version|v' => sub { display_version(); exit 0 }, |
| 577 |
|
| 578 |
# Additional options: |
| 579 |
'verbose|V' => sub { $verbose = 1 }, |
| 580 |
'quiet|q' => sub { $verbose = 0 }, |
| 581 |
'colour|color|c' => sub { $coloured = 1 }, |
| 582 |
'plain|p' => sub { $coloured = 0 }, |
| 583 |
'savefile|s=s' => \$savefile, |
| 584 |
'finished|f=s' => \$done, |
| 585 |
'unfinished|u=s' => \$undone, |
| 586 |
); |
| 587 |
|
| 588 |
# Check whether the supplied colour is valid: |
| 589 |
unless ((!$done || $valid{$done}) && (!$undone || $valid{$undone})) { |
| 590 |
exit_with_error("Invalid colour option.\n" . |
| 591 |
"Try `--help' for more information.", 22); |
| 592 |
} |
| 593 |
|
| 594 |
# Get command line arguments: |
| 595 |
$command = join(' ', @ARGV); |
| 596 |
|
| 597 |
# Parse command: |
| 598 |
if ($command =~ /^(|list|ls)\s*$/) { |
| 599 |
# List all items in the task list: |
| 600 |
list_tasks(); |
| 601 |
} |
| 602 |
elsif ($command =~ /^(list|ls)\s+@(\S+)\s*(\S.*|)$/) { |
| 603 |
# List items in the selected group, optionally matching the pattern: |
| 604 |
list_tasks($2, $3); |
| 605 |
} |
| 606 |
elsif ($command =~ /^(list|ls)\s+%(\d+)/) { |
| 607 |
# List item with selected ID: |
| 608 |
list_tasks(undef, undef, $2); |
| 609 |
} |
| 610 |
elsif ($command =~ /^(list|ls)\s+([^@^%\s].*)$/) { |
| 611 |
# List items matching the pattern: |
| 612 |
list_tasks(undef, $2); |
| 613 |
} |
| 614 |
elsif ($command =~ /^add\s+@(\S+)\s+(\S.*)/) { |
| 615 |
# Add new item to the task list, belonging to the selected group: |
| 616 |
add_task($2, $1); |
| 617 |
} |
| 618 |
elsif ($command =~ /^add\s+([^@\s].*)/) { |
| 619 |
# Add new item to the task list, belonging to the default group: |
| 620 |
add_task($1); |
| 621 |
} |
| 622 |
elsif ($command =~ /^(change|mv)\s+%?(\d+)\s+@(\S+)\s*$/) { |
| 623 |
# Change group the selected item in the task list belongs to: |
| 624 |
change_task($2, $3, 1); |
| 625 |
} |
| 626 |
elsif ($command =~ /^(change|mv)\s+%?(\d+)\s+([^@\s].*)/) { |
| 627 |
# Change selected item in the task list: |
| 628 |
change_task($2, $3); |
| 629 |
} |
| 630 |
elsif ($command =~ /^(finish|fn)\s+%?(\d+)/) { |
| 631 |
# Mark selected item in the task list as finished: |
| 632 |
finish_task($2); |
| 633 |
} |
| 634 |
elsif ($command =~ /^(revive|re)\s+%?(\d+)/) { |
| 635 |
# Mark selected item in the task list as unfinished: |
| 636 |
revive_task($2); |
| 637 |
} |
| 638 |
elsif ($command =~ /^(remove|rm)\s+%?(\d+)/) { |
| 639 |
# Remove selected item from the task list: |
| 640 |
remove_task($2); |
| 641 |
} |
| 642 |
elsif ($command =~ /^(undo|ud)\s*$/) { |
| 643 |
# Revert last action: |
| 644 |
revert_last_action(); |
| 645 |
} |
| 646 |
elsif ($command =~ /^(groups|gr)\s*$/) { |
| 647 |
# Display groups in the task list: |
| 648 |
list_groups(); |
| 649 |
} |
| 650 |
elsif ($command =~ /^version\s*$/) { |
| 651 |
# Display version information: |
| 652 |
display_version(); |
| 653 |
} |
| 654 |
elsif ($command =~ /^help\s*$/) { |
| 655 |
# Display list of all supported commands and command-line options: |
| 656 |
display_help(); |
| 657 |
} |
| 658 |
elsif ($command =~ /^help\s+(\S+)/) { |
| 659 |
# Display information on a specific command: |
| 660 |
display_help($1); |
| 661 |
} |
| 662 |
else { |
| 663 |
# Report invalid command: |
| 664 |
exit_with_error("Invalid command or argument.\n" . |
| 665 |
"Try `--help' for more information.", 22); |
| 666 |
} |
| 667 |
|
| 668 |
# Return success: |
| 669 |
exit 0; |
| 670 |
|
| 671 |
__END__ |
| 672 |
|
| 673 |
=head1 NAME |
| 674 |
|
| 675 |
lite2do - a lightweight text-based todo manager |
| 676 |
|
| 677 |
=head1 SYNOPSIS |
| 678 |
|
| 679 |
B<lite2do> [I<option>...] I<command> [I<argument>...] |
| 680 |
|
| 681 |
B<lite2do> B<-h> | B<-v> |
| 682 |
|
| 683 |
=head1 DESCRIPTION |
| 684 |
|
| 685 |
B<lite2do> is a lightweight command-line todo manager written in Perl. |
| 686 |
Being based on w2do and fully compatible with its save file format, it |
| 687 |
tries to provide much simpler alternative for those who do not appreciate |
| 688 |
nor require w2do's complexity. |
| 689 |
|
| 690 |
=head1 COMMANDS |
| 691 |
|
| 692 |
=over |
| 693 |
|
| 694 |
=item B<list> [@I<group>] [I<text>...] |
| 695 |
|
| 696 |
=item B<ls> [@I<group>] [I<text>...] |
| 697 |
|
| 698 |
Display items in the task list. All tasks are listed by default, but |
| 699 |
desired subset can be easily selected giving a I<group> name, I<text> |
| 700 |
pattern, or combination of both. |
| 701 |
|
| 702 |
=item B<list> %I<id> |
| 703 |
|
| 704 |
=item B<ls> %I<id> |
| 705 |
|
| 706 |
Display task with selected I<id>. |
| 707 |
|
| 708 |
=item B<add> [@I<group>] I<text>... |
| 709 |
|
| 710 |
Add new item to the task list. |
| 711 |
|
| 712 |
=item B<change> I<id> I<text>... |
| 713 |
|
| 714 |
=item B<mv> I<id> I<text>... |
| 715 |
|
| 716 |
Change item with selected I<id> in the task list. |
| 717 |
|
| 718 |
=item B<change> I<id> @I<group> |
| 719 |
|
| 720 |
=item B<mv> I<id> @I<group> |
| 721 |
|
| 722 |
Change I<group> the item with selected I<id> belongs to. |
| 723 |
|
| 724 |
=item B<finish> I<id> |
| 725 |
|
| 726 |
=item B<fn> I<id> |
| 727 |
|
| 728 |
Mark item with selected I<id> as finished. |
| 729 |
|
| 730 |
=item B<revive> I<id> |
| 731 |
|
| 732 |
=item B<re> I<id> |
| 733 |
|
| 734 |
Mark item with selected I<id> as unfinished. |
| 735 |
|
| 736 |
=item B<remove> I<id> |
| 737 |
|
| 738 |
=item B<rm> I<id> |
| 739 |
|
| 740 |
Remove item with selected I<id> from the task list. |
| 741 |
|
| 742 |
=item B<undo> |
| 743 |
|
| 744 |
=item B<ud> |
| 745 |
|
| 746 |
Revert last action. When invoked, the data are restored from the backup |
| 747 |
file (i.e. I<~/.lite2do.bak> by default), which is deleted at the same |
| 748 |
time. |
| 749 |
|
| 750 |
=item B<groups> |
| 751 |
|
| 752 |
=item B<gr> |
| 753 |
|
| 754 |
Display list of groups in the task list along with the number of tasks |
| 755 |
belonging to them. |
| 756 |
|
| 757 |
=item B<help> [I<command>] |
| 758 |
|
| 759 |
Display usage information. By default, list of all supported commands and |
| 760 |
command-line options is displayed. If the I<command> is supplied, further |
| 761 |
information on its usage are displayed instead. |
| 762 |
|
| 763 |
=item B<version> |
| 764 |
|
| 765 |
Display version information. |
| 766 |
|
| 767 |
=back |
| 768 |
|
| 769 |
=head1 OPTIONS |
| 770 |
|
| 771 |
=over |
| 772 |
|
| 773 |
=item B<-s> I<file>, B<--savefile> I<file> |
| 774 |
|
| 775 |
Use selected I<file> instead of the default I<~/.lite2do> as a save file. |
| 776 |
|
| 777 |
=item B<-c>, B<--colour>, B<--color> |
| 778 |
|
| 779 |
Use coloured output. |
| 780 |
|
| 781 |
=item B<-p>, B<--plain> |
| 782 |
|
| 783 |
Use plain-text output; this is the default behaviour, as most users do not |
| 784 |
usually fancy having bright colours in their terminal. |
| 785 |
|
| 786 |
=item B<-f> I<colour>, B<--finished> I<colour> |
| 787 |
|
| 788 |
Use selected I<colour> for finished tasks; available options are: B<black>, |
| 789 |
B<red>, B<green>, B<yellow>, B<blue>, B<magenta>, B<cyan> and B<white>. |
| 790 |
|
| 791 |
=item B<-u> I<colour>, B<--unfinished> I<colour> |
| 792 |
|
| 793 |
Use selected I<colour> for unfinished tasks; available options are: |
| 794 |
B<black>, B<red>, B<green>, B<yellow>, B<blue>, B<magenta>, B<cyan> and |
| 795 |
B<white>. |
| 796 |
|
| 797 |
=item B<-q>, B<--quiet> |
| 798 |
|
| 799 |
Avoid displaying messages that are not necessary. |
| 800 |
|
| 801 |
=item B<-V>, B<--verbose> |
| 802 |
|
| 803 |
Display all messages; this is the default behaviour. |
| 804 |
|
| 805 |
=item B<-h>, B<--help> |
| 806 |
|
| 807 |
Display help message and exit. |
| 808 |
|
| 809 |
=item B<-v>, B<--version> |
| 810 |
|
| 811 |
Display version information and exit. |
| 812 |
|
| 813 |
=back |
| 814 |
|
| 815 |
=head1 FILES |
| 816 |
|
| 817 |
=over |
| 818 |
|
| 819 |
=item I<~/.lite2do> |
| 820 |
|
| 821 |
Default save file. |
| 822 |
|
| 823 |
=item I<~/.lite2do.bak> |
| 824 |
|
| 825 |
Default backup file. |
| 826 |
|
| 827 |
=back |
| 828 |
|
| 829 |
=head1 SEE ALSO |
| 830 |
|
| 831 |
B<w2do>(1), B<w2html>(1), B<w2text>(1), B<perl>(1). |
| 832 |
|
| 833 |
=head1 BUGS |
| 834 |
|
| 835 |
To report bugs or even send patches, you can either add new issue to the |
| 836 |
project bugtracker at <http://code.google.com/p/w2do/issues/>, visit the |
| 837 |
discussion group at <http://groups.google.com/group/w2do/>, or you can |
| 838 |
contact the author directly via e-mail. |
| 839 |
|
| 840 |
=head1 AUTHOR |
| 841 |
|
| 842 |
Written by Jaromir Hradilek <jhradilek@gmail.com>. |
| 843 |
|
| 844 |
Permission is granted to copy, distribute and/or modify this document under |
| 845 |
the terms of the GNU Free Documentation License, Version 1.3 or any later |
| 846 |
version published by the Free Software Foundation; with no Invariant |
| 847 |
Sections, no Front-Cover Texts, and no Back-Cover Texts. |
| 848 |
|
| 849 |
A copy of the license is included as a file called FDL in the main |
| 850 |
directory of the lite2do source package. |
| 851 |
|
| 852 |
=head1 COPYRIGHT |
| 853 |
|
| 854 |
Copyright (C) 2008, 2009 Jaromir Hradilek |
| 855 |
|
| 856 |
This program is free software; see the source for copying conditions. It is |
| 857 |
distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; |
| 858 |
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A |
| 859 |
PARTICULAR PURPOSE. |
| 860 |
|
| 861 |
=cut |