Cover V04, I02
Article
Figure 1
Listing 1

mar95.tar


Customizing Your Calendar Program

Steven G. Isaacson

Our Human Resources department asked if it was possible to automatically mail reminders about employee annual reviews. An additional requirement was that the reminders be mailed to more than one person: the department leader, team leader, etc.

We considered writing a custom reminder program -- easy to do with cron and a shell script -- but when it came time to decide upon a date format (would it be mm/dd/yy, mm/dd/yyyy, September 20, 1995?), we decided instead to use the existing calendar program, which already knows how to handle dates in a variety of formats.

This article describes a shell script that works like /bin/calendar but adds the ability to send mail to more than one user, allows multiple lines per calendar entry, supports include files, and adds support for keywords like Daily, Monday, Tuesday, Weekend, etc.

calendar

The /usr/bin/calendar program is a handy reminder service. By default it looks in your $HOME directory for a file called calendar and examines each line for a date. If the date matches today or tomorrow (Monday is "tomorrow" for Friday) then you are sent reminder mail. On most systems, calendar is run automatically from cron.

Here are sample calendar entries:

March 10 -- Sys Admin budget meeting
Jan 5 1995 -- new workstations should arrive next week
Sep 10 -- Anniversary on Sep 20th Send flowers!

Note that line three contains two dates, the 10th and the 20th. So mail is sent on September 9th and 10th, and then again on the 19th and 20th.

Although handy, the calendar program is limited. Only one line per entry is allowed and mail is sent to only one user. Both of these problems were addressed with our custom calendar.sh (Listing 1).

Multiple Address

To support multiple email addresses, we had to do two things: 1) decide upon an extended format for the calendar entry (i.e., some way to specify the email addresses), and 2) run calendar manually.

The extended format is as follows:

[email address(es)]| [calendar entry]

Example:

petera sysadm@central| March 10 Sys Admin budget meeting

On March 9th and 10th this calendar entry will be sent to petera and sysadm@central.

Running calendar Manually

/usr/bin/calendar is actually a shell script that uses /usr/lib/calprog (a binary program) to figure out what dates to look for in the calendar file. Specifically, calprog figures out which dates to look for by generating regular expressions -- one for today and one for tomorrow. On Fridays -- recall that Monday is "tomorrow" for Friday -- four regular expressions are generated. One for Friday, Saturday, Sunday, and Monday.

On September 7th, for example, calprog generated these two regular expressions. The first matches September 7 dates; the second matches September 8 dates.

(^|[    (,;])([Ss]ep[^ ]* *|0*9/|\*/)0*7)([^0123456789]|$)
(^|[    (,;])([Ss]ep[^ ]* *|0*9/|\*/)0*8)([^0123456789]|$)

You'll probably never see a more complex regular expression (for more information on regular expressions see Larry Reznick's "Using Regular Expressions," Sys Admin September/October 1992, vol. 1, no. 3). I was surprised to see that the second and third letters of the month are case-sensitive. Sep 7 is a valid calendar entry, but SEP 7 isn't!

The calendar program uses calprog to write the regular expressions to a file. Then egrep, using the regular expressions written to the file, scans the calendar file in your $HOME directory. To wit:

# create regular expression file
calprog > re.file

# egrep for matches
egrep -f re.file $HOME/calendar

The output, if any, is mailed to you. That's the stock calendar program -- crude but effective.

Our version of calendar does the same thing, and more. The regular expressions for grabbing the correct dates are generated in the same way, with /usr/lib/calprog, but instead of always sending mail to whoever happens to be running the program, mail is sent to the name or names in the first field, the first field being everything up to the first pipe symbol. If there is no first field, that is, no pipe symbol, then mail is sent to the current user.

Adding calendar.sh to a nightly cron job and specifing the calendar file was now all that it took to send calendar entries to more than one person. Our Human Resources department was happy -- but only for a while. Almost immediately there were requests for multiple-line calendar entries.

Multiple-Line Calendar Entries

Making calendar.sh send more than one line of information per entry was easy once the "trick" was figured out. Consider the problem. If all entries are only one line, there isn't a problem. But some entries are more than one line. So . . . convert all multi-line entries to one line, do what you normally do, then undo the multi-line conversion before sending the mail.

Here's an example of how it works. This three-line entry

sysadm| Sep 20 Big meeting Be \

There!

is first converted to

sysadm| Sep 20 Big meeting <nl>      Be <nl>   There!

Each escape character at the end of the line is converted to <nl> and then converted back again after the regular calendar processing is done. So, there are no multiple-line entries; the problem is solved by ignoring it for a while.

Include Files

Another way to support multiple-line calendar entries is with include files. For example: suppose you have quarterly employee reviews and want to remind the mail recipients to, say, use a particular form. One way to remind them to use the form is by including the form in the mail. This feature was easy to add.

The include file syntax is:

[address]| [calendar entry  [#f:[filename]]]

as, for example:

sysadm| Sep 20 Big meeting #f:/usr/uubob/big_notes

What happens is that after the calendar entry is selected, the entry is scanned for the #f: keyword. If the keyword is found, the specified file is included in the email.

Shell Commands

Included files are included via cat. That is, a shell command (cat [filename]) is executed from within the awk program that searches for the #f: keyword. If cat is useful, how about other commands? For example, df to show the free disk space, or who, to show who is still logged in early in the morning when cron runs calendar.sh.

The shell command syntax is:

[address]| [calendar entry  [#!:[shell command]]]

Example:

sysadm| Sep 20 Big meeting #!:df bring disk space report

Recurring Entries

Suppose, now that you have a truly simple means of sending yourself or anyone else mail about current disk space, that you'd like to have the mail sent daily. You can't do it with the present calendar scheme, because once an entry is used, that's it -- no more mail about September 20 meetings until the next Sep 20, next year. So, a nice addition would be support for keywords such as "Daily," "Monday," "Tuesday," etc. Adding new keywords to calendar.sh is simple. For example, to support the days of the week, add "Monday" to the regular expression list on Mondays, add "Tuesday" to the regular expression list on Tuesday, and so on. To support "Daily," add "Daily" to the list each day. To support "Weekend," add "Weekend" to the regular expression list on Saturday and Sunday.

Installing calendar.sh

Installing calendar.sh is easy. Create a calendar file (or .calendar to avoid conflicts with /usr/bin/calendar) in your HOME directory, then add an entry to your crontab to run calendar.sh. Figure 1 shows a sample crontab entry.

About the Author

Steven G. Isaacson has been programming professionally since 1985. He works for FourGen Software, the leading developers of accounting software and CASE Tools for the UNIX market. He may be reached at uunet!4gen!stevei or stevei@fourgen.com.