#!/usr/bin/perl # # Calendar 1.5 # Copyright (C) 2008, 2011 # Paul E. Jones # Free open source software, no restrictions or warranty whatsoever # # This file will open the calendar file and display every line that matches # the current date. Blank lines or lines starting with '#' are ignored. # The date may be expressed in any of these forms. Years must be # written as 4-digit years in order to allow differentiation of dates. # Following the date is a message that should be displayed. What this # program actually does is display the date a space and the message text. # The following calendar entry forms are valid: # # # Comment line # include # yyyy-mm-dd Message to be displayed # yyyy/mm/dd Message to be displayed # mm-dd-yyyy Message to be displayed # mm/dd/yyyy Message to be displayed # mm/dd Message to be displayed # mm-dd Message to be displayed # # Either the '-' or '/' character may be used to separate entries in the # date field. # # Note that the dd/mm/yyyy and yyyy/dd/mm formats are NOT supported, since # one cannot distinguish some dates from the mm/dd/yyyy or yyyy/dd/mm formats. # # A wildcard '*' may be used in place of the numeric entries in order # to match any day, month, or year. As examples: # *-16-2007 Match the 16th of every month of 2007 # *-*-* Match every day # 2007-*-16 Match the 16th of every month of 2007 # 2007-*-* Match every day in 2007 # 2007-06-* Match every day June of 2007 # *-06-* This is an ambiguous match and will be interpreted yyyy-06-* # # Note that for wildcard year matching, *-06-* is ambiguous. Should that # be yyyy-06-* or *-06-yyyy (i.e., yyyy-mm-dd or mm-dd-yyyy)? # This program assumes a yyyy-mm-dd format in such ambiguous cases. # # The default name for the calendar file is "$HOME/calendar/calendar" # # The program accepts one command-line argument, which is a timestamp # to the date that the user would like to use as calendar execute # (versus today's date). The timestamp that may be provided must be # in seconds since Jan 1, 1970. That is, the value returned by # that time() API call on most operating systems. A separate program # called daily_calendar can be used to run the calendar program repeatedly # for each day since the last run, which is useful when a machine has # been shut down for a period of time. # use strict; # # Process a calendar file # # Note that the calendars array and print_header scalar are passed by # reference and are modified by this function. # sub process_calendar { my ($calendar_file, $calendars, $print_header, $mon, $mday, $year) = @_; my ($date, @date, $junk, $message, $ignore_calendar, $command, $i, $index); open(CAL, "< $calendar_file") || die "Could not open calendar file\n"; while() { next if /^#/; next if /^[ \t]*$/; if (/^include[ \t]/) { ($command, $junk, $calendar_file) = split(/( |\t)+/, $_, 2); chomp($calendar_file); next unless length($calendar_file) > 0; # Check to ensure that any given calendar file is specified # for processing no more than one time. Ignore requests for # duplicate processing. $ignore_calendar = 0; for($i = 0; $i <= $#{$calendars}; $i++) { if ($calendar_file eq ${$calendars}[$i]) { $ignore_calendar = 1; last; } } if ($ignore_calendar == 0) { push(@{$calendars}, $calendar_file); } else { print "Warning: $calendar_file referenced more than once\n"; } next; } ($date, $junk, $message) = split(/( |\t)+/, $_, 2); @date = split(/(-|\/)/, $date, 3); # If there are only 2 array elements, then a year was not provided. # Provide the current year so that we match correctly. if ($#date == 2) { $date[4] = $year; } else { # Check for a wildcard in a year position if (($date[0] eq "*") && ($date[4] eq "*")) { # Both a wildcard year and month were provided, so let's place # the year in the first position forming yyyy/mm/dd $date[0] = $year; } elsif (($date[0] < 13) && ($date[4] eq "*")) { # A specific month was provided in the first position and # a wildcard year was provided in the last position $date[4] = $year; } elsif (($date[4] < 32) && ($date[0] eq "*")) { # Some digits other than a year were provided in the last # position and a wildcard was provided in the first position, # so assume the first position is the year. $date[0] = $year; } } # Determine the format of the calendar entry if ($date[0] > 1900) { $index = 2; # We must have yyyy-mm-dd format } else { $index = 0; # We must have mm-dd-yyyy format } # Support '*' wildcards in month or day positions if ($date[$index] eq "*") { $date[$index] = $mon; } if ($date[$index+2] eq "*") { $date[$index+2] = $mday; } # Check for a match if ( (($date[0] == $year) && ($date[2] == $mon) && ($date[4] == $mday)) || (($date[4] == $year) && ($date[0] == $mon) && ($date[2] == $mday)) ) { if ($$print_header eq 1) { printf("Calendar Entries for %4d-%02d-%02d\n", $year, $mon, $mday); print "===============================\n"; $$print_header = 0; } print "$date $message"; } } close(CAL); } # # MAIN # { my ($calendar_file, @calendars, $print_header, $timestamp, $sec,$min,$hour,$mday,$mon,$year,$wday,$yday); # Location of the calendar file chdir("$ENV{'HOME'}/calendar"); $calendar_file = "calendar"; push(@calendars, $calendar_file); # Indicate that the first entry found should result in a header printed $print_header = 1; if ($#ARGV >= 0) { $timestamp = int($ARGV[0]); ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = localtime($timestamp); } else { # Check the calendar for today... ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = localtime(); } $mon++; $year += 1900; # Process the calendar array. Initially, there is only one element, # but additional elements might be added by the "include" lines in the # calendar file(s) for(my $cal = 0; $cal <= $#calendars; $cal++) { process_calendar($calendars[$cal], \@calendars, \$print_header, $mon, $mday, $year); } # If the header was printed, there was a calendar entry printed. # Ensure at least one blank line following. if ($print_header == 0) { print "\n"; } }