Cover V04, I02
Article
Figure 1
Listing 1

mar95.tar


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
###########################################################