#!/usr/bin/perl -lw # # Copyright (C) 2003 Don Stewart - http://www.cse.unsw.edu.au/~dons # (see http://www.gnu.org/copyleft/gpl.html) # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA # 02111-1307, USA. # # Maintain a record of hours worked sub main(); sub usage(); sub logpath(); sub getargs(); sub subtract($$); sub readlog($); # valid command line flags, and their long forms %ARGS = ( "s" => "start", "bs" => "break start", "be" => "break end", "e" => "end", "nl" => "no lunch", "new" => "insert delimiter", ); # functions to execute, by key %function = ( "s" => \&start, "bs" => \&break, "be" => \&break, "e" => \&end, "nl" => \&no_lunch, "new" => \&new_delim, ); main(); ## subroutines ## # entry point sub main() { my ($mode,$write) = getargs(); my $log = logpath(); my $string = &{$function{$mode}}(); } # parse the command line args, returning the function we want sub getargs() { my $mode = ""; my $write = 0; # must be at least 1 argument if (not @ARGV) { usage(); exit 1; } # always at least 1 arg $mode = shift; # if using the -w flag, then find the real mode if ($mode eq "-w") { # must be another argument then if (not @ARGV) { usage(); exit 1; } $write = 1; $mode = shift; } # sanity check the arguments if (not defined $ARGS{$mode})) { usage(); exit 1; } return ($mode,$write); } # generate the current time, in "start" format sub start() { my $s; $s = `date '+%a %d.%m.%y#%H:%M'`; $s =~ s/#/\n\t/; return $s; } # return current time in "break" format sub break() { return "\t" . `date '+%H:%M'`; } # end: total up the previous 3 entries, producing a sum sub end($) { my $valid = 0; my $log = shift; my $s; my @entries = readlog($log); # grab current time ( I have no idea what I'm doing here ) $s = `date '+%d.%m.%y'`; push (@entries, `date '+%H:%M'`); chomp $entries[-1]; $s .= "\t$entries[-1] ="; # normalise numbers for $i (0 .. $#entries) { $entries[$i] =~ s/\s+//g; } if (not $empty) { # calculate in minutes, to work out the number of hours worked $morn = subtract($day[1],$day[0]); $aft = subtract($day[3],$day[2]); $hour = int(($morn+$aft)/60); $mins = int(($morn+$aft)%60); # didn't go to lunch, so operate on the whole day } else { $morn = subtract($day[3],$day[0]); $hour = int($morn/60); $mins = int($morn%60); } $s = sprintf("%s %d:%02d",$s,$hour,$mins); # sanity test: mustn't be negative if ($s =~ /-/) { print "Error parsing log file: hours worked are negative!"; exit 1; } return $s; } # does some IO: open the log readonly, and return the last 3 lines? sub readlog($) { my @tmp = (); my $t = ""; my $empty = 0; my $valid = 0; open LOGRO, "<$log" or die "$0 : couldn't open $log for RO : $!"; @tmp = ; # urgh close LOGRO; # find the last 3 entries in the file for (;;) { # sanity test : must be some content in file $valid = 0; for my $d (@tmp) { $valid = 1 if ($d !~ /^\s*$/); } if (not $valid) { print "Error parsing log file: empty file"; exit 1; } # last line $t = pop @tmp; # i.e. didn't go to lunch $empty = 1 if ($t =~ /-/); # $t must be in normal form if ($t =~ /^\s+\d\d:\d\d$|^\s+-$/) { unshift(@day, $t); # otherwise it is an error } else { chomp $t; print "Error parsing log file: bogus entry: \"$t\""; exit 1; } # only read 3 lines last if ($#day == 2); # this will also force 3 entries to appear } # two constraints: # (1) if there is a '-' in the last entry, # it must also be the second last if ($day[2] =~ /^\s+-$/) { # then $day[1] must also if ($day[1] !~ /^\s+-$/) { print "Error parsing log file: missing \"-\" field"; exit 1; } # (2) there must be no '-' entries, otherwise } else { for my $d (@day) { if ($d =~ /^\s+-$/) { print "Error parsing log file: unexpected \"-\" field"; exit 1; } } } return @day; } sub new() { return "# a new fortnight"; } sub nolunch() { return "\t-\n\t-"; } sub write($$$) { my $string = shift; my $log = shift; my $write = shift; # please try to run these in order! open LOG, ">>$log" or die "$0 : couldn't open $log : $!"; chomp $s; if ($write) { print LOG $s; print LOG "" if (/^e$/); # newline if end of the day close LOG; } print "$s"; } # takes 2 strings of the from "hh:mm", and subtracts them, giving # a time in minutes sub subtract($$) { my ($ah,$am) = split /:/, shift; my ($bh,$bm) = split /:/, shift; # convert to minutes and subtract return (($ah * 60) + $am) - (($bh * 60) + $bm); } # usage message sub usage() { print "\nusage: $0 [-w] [s bs be e nl new]" ; print " -w : write to file s : start of day bs : start break be : end break e : end day nl : no lunch new: new fortnight" } # find the home directory, this should be portable sub logpath() { my $h = $ENV{"HOME"}; return "$home/.timesheet"; }