Listing 1: calendar.sh, the enhanced calendar program
###########################################################
# BEGIN PROGRAM LISTING
###########################################################
:
# calendar.sh - enhanced calendar program. Add a .calendar file to
# your $HOME directory and call this program nightly from cron.
# Copyright (C) 1995 Steven G. Isaacson
# syntax
test "$1" = "-h" && {
echo "Syntax: `basename $0` [calendar]"
exit 1
}
# calendar file. If not specifed on command-line, use $HOME/.calendar
calfile=${1:-$HOME/.calendar}
# do we have a valid file?
test -f "$calfile" || {
echo "Error: could not read: $calfile"
exit 1
}
# make sure we have all that we need. set appropriately for your
# system.
PATH=$PATH:/usr/bin ; export PATH
# we're currently using elm
: ${mailer:=/usr/bin/elm}
# set -xv # uncomment for debugging
# if no mail-to is specified in calendar file, send mail to whoever is
# running this program. If logname program not available you can strip
# out the name from id.
def_mail="`logname`"
# tmp file for Regular Expressions
refile=/tmp/refile.$$
# tmp file
tfile=/tmp/tfile.$$
# get 3-character day of week, to be used with our custom keywords.
today=`date '+%a'`
{ # BEGIN BRACE
# all stdout between the braces will be funneled through the closing
# brace. This is similiar to using parens to spawn a sub-shell, but
# we don't need a subshell. Could also have done something like this:
# prog1 > outfile
# prog2 >> outfile
# prog_etc >> outfile
# and then used the contents of outfile.
# This is how the stock calendar program generates r.e. for dates
/usr/lib/calprog
# Look for our custom 'daily' keyword, but not on comment lines.
echo "^[^#]*[Dd]aily"
# Look for custom day keywords. If this is Mon, then look for
# monday, etc. Again, skip comment lines. Case sensitivity is
# consistent with calprog.
case "$today" in
Mon) echo "^[^#]*[Mm]onday" ;;
Tue) echo "^[^#]*[Tt]uesday" ;;
Wed) echo "^[^#]*[Ww]ednesday" ;;
Thu) echo "^[^#]*[Tt]hursday" ;;
Fri) echo "^[^#]*[Ff]riday" ;;
Sat) echo "^[^#]*[Ss]aturday" ;;
Sun) echo "^[^#]*[Ss]unday" ;;
*) echo "ERROR: date" >&2 ; exit 1 ;;
esac
# Custom weekday and weekend keywords
case "$today" in
Mon|Tue|Wed|Thu|Fri) echo "^[^#]*[Ww]eekday" ;;
Sat|Sun) echo "^[^#]*[Ww]eekend" ;;
esac
} | # END BRACE
awk '{
# Now that we have all of our regular expressions ... egrep
# complains that we have too many and fails. So forget about
# egrep. Create an awk script egrep work-alike, on-the-fly. Like
# this:
# /Oct 20/ { print; next; }
# /Oct 21/ { print; next; }
# Need to escape slashes from calprog output because slashes are
# the delimiter for awk. old awk does not have gsub. use nawk or
# gawk or sed.
gsub(/\//, "\\/")
printf("/%s/ {print; next;}\n", $0)
}' > $refile
# We've got the regular expression file. Time to pre-process the
# calendar file. Do two things: convert multi-line entries (designated
# with escapes at end of line) into single-line entries. And second...
#
# The following calendar file line does not work because we need a
# space between the pipe and Oct
#
# mail_to|Oct 3 does not work. [need space]
#
# but I expected it to, so, make it work by adding a space after
# the first pipe and then, after we're done with the regular
# expressions, taking the space out again.
awk '/^[^#]*\|/ {
# sub not gsub; only do first one
sub(/\|/, "| <PIPE_SPACE> ")
}
/\\[ ]*$/ {
# convert multi-line entries
printf("%s <nl>", $0)
next
}
{ print }' $calfile |
# pipe the calendar entries through the date/r.e. filter
awk -f $refile |
# read the lines that come out and send mail, etc.
while read line
do
# get the mail-to address, everything up to the first pipe symbol
mail_to=`echo $line | awk -F\| 'NF > 1 {print $1}'`
# if none found, use default address
test "$mail_to" || mail_to="$def_mail"
# initialize temp file
> $tfile
# create subject line for mailer
subject="calendar.sh `date`"
# undo the multi-line and pipe-space conversion that was possibly
# done above
line=`echo "$line" | awk '{
gsub(/<nl>/, "\n")
sub(/\| <PIPE_SPACE> /, "|")
print
}'`
# echo the entry to the temp file
echo "$line
" >> $tfile
# Check for additional file or system command to be run.
echo "$line" | awk '
/#[f!]:.*#[f!]:/ {
print "ERROR: multiple #f: or #!: expressions"
next
}
/#f:/ {
# delete everything up to #f:
sub(/^.*#f:/,"")
# delete everything after the first space, which means only one
# file at a time; one per line.
sub(/ .*/, "")
str=sprintf("cat %s", $0)
system(str)
next
}
/#!:/ {
sub(/^.*#!:/,"")
sub(/#.*/, "")
str=sprintf("%s", $0)
system(str)
next
}' >> $tfile
# send the mail.
$mailer -s "$subject" "$mail_to" < $tfile > /dev/null
done
# cleanup
rm -f $refile $tfile
exit 0
###########################################################
# END PROGRAM LISTING
###########################################################
|