#!/usr/bin/perl # Number of days to look ahead $lookahead = shift || 4; # Where the birthday database is $database = "/home/set/addressbook/cumpleaƱos"; # Here be dragons below... # This is a re-write, since I lost the original when I formatted and # installed Fedora Core 2. This is *a lot* faster than the original, # since we pre-hash all of the birthdays. It also has a fix, albeit # ugly, for the 2/29 birthday bug the last version had. # We actually use functions in this version, which makes the code cleaner # Routine to convert dates to some more human friendly # Input: A UNIX time stamp # Output: A three-element list with the normal year, month, and day sub convert { my($a,$b,$c,$day,$mon,$year) = localtime(shift); $mon++; $year += 1900; return ($year,$mon,$day); } # Routine to convert the output of "convert" in to a hash index sub dindex { my($y) = shift; my($m) = shift; my($d) = shift; return sprintf("%02d.%02d",$m,$d); } # Given $n, a person's name, $c, the number of days when they have a birthday # (if c is 0, today's their lucky day), and $q, their (possible) contact info, # make a human-readable string of who has a birthday when sub output { my($n) = shift; my($c) = shift; my($d) = shift; my($o); while($n =~ /\s$/) { $n =~ s/\s$//g; } if($c >= 2) { $o = "$n has a birthday in $c days\n"; } elsif($c == 1) { $o = "$n has a birthday tomorrow.\n"; } elsif($c == 0) { $o = "Today is $n\'s birhtday!\n"; } return $o; } # Given a string that tells us who has a birthday when (in human-reabable # format, inform the user of this fact. We modularize this way so that # we can both output to standard output, or send out mail, or IM them, or # what not sub inform { my($o) = shift; print $o; } # main() # First, open up the database open(B,"< $database") || die "Can\'t open $database: $!\n"; # Make a hash, d, which has, with the day of year in MM.DD format as the # index, the line number of everyone who has a birthday on that day. This # hash should speed up execution time of the program. while() { $num++; $line = $_; chop($line); $line =~ s/\#.*//g; # We only process lines with XXXX.01.23 or what not in them; # other lines are simply ignored if($line =~ /\w\w\w\w\.(\d\d\.\d\d)/) { $day = $1; # Ugly hack to handle people born on 2/29: We'll just # always celebrate their birthdays on the 28th if($day =~ /02\.29/) { $day = "02.28"; } # We put the reference to the actual data in the "l" hash # in the "d" hash. "l" is indexed by line number; "d" is # indexed by the day they were born. An entry in the "d" # hash looks like "2" or "2,19" or "2,19,83"; the numbers # are separated by commas; 2, 19, and 83 are line numbers # of where the real data about the people are. if($d{$day}) { $d{$day} .= "," . $num; } else { $d{$day} = $num; } # OK, we put references in the "d" hash; put the real data # in the "l" hash. $l{$num} = $line; } } # Now, who has a birthday coming up soon? $t = time(); $o = ""; for($c = 0; $c <= $lookahead; $c++) { # We convert the day we are looking at in to a form like "02.19" $s = dindex(convert($t)); # If anyone has a birthday soon... if($d{$s}) { # Make a list of the line numbers of who has a birthday on # the day we are looking at my(@p) = split(/\,/,$d{$s}); # For each of those line numbers foreach $a (@p) { # Get the raw data for that person my($l); $l = ($l{$a}); # And process the data in question if($l =~ /^(.*)\w\w\w\w\.\d\d\.\d\d\s+(.*)\s*$/) { $n = $1; $q = $2; $o .= output($n,$c,$q); } } # We now remove this element from the "d" (sorted by bday) hash # This means we can only look a year ahead for birthdays delete($d{$s}); } $t += 86400; # 86400 seconds: One day; we look at the next day now } # Finally, inform the user who has a birthday inform($o);