[alt.sources] Pcal 2.1 - back despite popular demand

rogers@sud509.ed.ray.com (Andrew Rogers) (09/13/90)

OK, here is the latest revision of Pcal.  There are some new flags, but the
most significant change is the addition of elementary cpp-like functionality
to the date file.  The date file directory and program defaults may be 
overridden via environment variables (or their equivalents on VMS) - see
the ReadMe file for more details.

Thanks to Mark Kantrowitz, Joe Brownlee, and Eric Hammond for their
contributions and suggestions.

Andrew W. Rogers

-------------------------------- cut here --------------------------------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	 Makefile
#	 ReadMe
#	 ReadMe.orig
#	 calendar
#	 pcal.c
#	 pcal.hlp
#	 pcal.man
#	 pcalinit.h
# This archive created: Wed Sep 12 13:55:04 EDT 1990
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'Makefile'
then
	echo shar: will not over-write existing file 'Makefile'
else
cat << \SHAR_EOF > 'Makefile'
#
MANDIR=/usr1/jad/man			# must change this

pcal:	pcal.c pcalinit.h
	$(CC) $(CFLAGS) $(LDFLAGS) $(COPTS) -o pcal pcal.c

man:	pcal.man
	nroff -man pcal.man > pcal.1
	pack pcal.1
#	mv pcal.1.z $(MANDIR)
SHAR_EOF
fi
if test -f 'ReadMe'
then
	echo shar: will not over-write existing file 'ReadMe'
else
cat << \SHAR_EOF > 'ReadMe'
This is still another new and improved release (2.1) of the infamous Pcal
program.  The changes this time are:

	1) Elementary cpp-like functionality has been added; the date file
	   now supports define|undef, if{n}def ... { else ... } endif, and
	   include:

		ifdef meetings
		include meetings.dat
		undef meetings
		endif

	   Symbols may be {un}defined on the command line via the new -D
	   and -U options described below; symbol names are treated in a
	   case-insensitive manner.  "define" alone deletes all symbol
	   definitions; "ifdef" alone is always false; "ifndef" alone is
	   always true.

	   The names of files to be included may be optionally surrounded
	   by "" or <>; non-absolute path names are taken to be relative
	   to the directory where the file containing the include directive
	   exists.  The Un*x flavor is smart enough to translate file names
	   starting with "~/".

	2) Pcal now looks for environment variable (global symbol on VMS)
	   PCAL_OPTS, which is parsed for command line options; flags set
	   there override the program defaults but are overridden by values
	   set in the date file or explicitly on the command line:

		setenv PCAL_OPTS "-t Times-Bold"	(Un*x)
		$ PCAL_OPTS == "-t Times-Bold"		(VMS)

	3) Pcal also looks for environment variable (logical name on VMS)
	   PCAL_DIR which points to the default directory for the calendar/
	   CALENDAR.DAT file.

		setenv PCAL_DIR "/u4/users/awr/pcal"	(Un*x)
		$ define PCAL_DIR "DISK11:[AWR.PCAL]"	(VMS)

	   In response to many requests, Un*x pcal looks for .calendar; if
	   that does not exist, it looks for calendar as before.

	4) The -D and -U flags have been added to define or undefine symbols
	   for the preprocessor; if these are specified on the command line,
	   they take effect prior to reading the date file.  -D alone clears
	   all previously-defined symbols.

		pcal -D meetings			(Un*x)
		$ pcal -"D" meetings			(VMS)

	5) The -L, -C, and -R flags have been added to specify left, center,
	   and right foot strings.  Foot strings consisting of multiple words
	   should be specified in single or double quotes:

		opt -C "Languages and Tools Group"

	6) The '-' (also '--') dummy flag has been added to disambiguate
	   cases such as the following:

		pcal -t 9 90

	   which may now be specified as

		pcal -t - 9 90		or	pcal -t -- 9 90

	7) As many have noticed, Pcal used to overstrike multiple text lines
	   for the same date if the lines were not contiguous in the date
	   file; this has been fixed.  Also, the text accompanying holidays
	   is written in the otherwise-unused space adjacent to the date.

The accompanying file called "ReadMe.orig" came with the original distribution
as README and states this program is copyrighted but with permission to modify
and redistribute.

Andrew W. Rogers

Additional note: This distribution includes a VMS HELP file written by
Richard Dyson.  Countless other people worked on pcal long before me; see
the ReadMe.orig file and topline comments in pcal.c; special thanks to
Mark Kantrowitz, Joe Brownlee, and Eric Hammond for their contributions
and suggestions which I have incorporated into release 2.1.
SHAR_EOF
fi
if test -f 'ReadMe.orig'
then
	echo shar: will not over-write existing file 'ReadMe.orig'
else
cat << \SHAR_EOF > 'ReadMe.orig'
"Pcal" is a program to print PostScript calendars for any month and year.
By default, it looks for a file in the home directory named "calendar"
for entries with leading dates matching dates on the calendar, and prints
any following text under the appropriate day.

The program may be a little System V flavored (getopt, time routines)
but should be easily portable to other vintages of UNIX.

Pcal is the combined effort of several people, most notably Patrick Wood
of Pipeline Associates, Inc. for the original PostScript code and Bill
Vogel of AT&T for the calendar file mechanism.  My part was simple
translation to a "C" program, the addition of a couple options and a more
generalized date searching routine (oh yes, and a manual page :-).

The original calendar PostScript was Copyright (c) 1987 by Patrick Wood
and Pipeline Associates, Inc. with permission to modify and redistribute.
Please retain this README file with the package.


Ken Keirnan
Pacific Bell
San Ramon, CA.
SHAR_EOF
fi
if test -f 'calendar'
then
	echo shar: will not over-write existing file 'calendar'
else
cat << \SHAR_EOF > 'calendar'
# Sample calendar file for pcal
#
# This should be ~/calendar on Unix, SYS$LOGIN:CALENDAR.DAT on VMS
#
# Valid entries are of the following forms:
#
#	opt <options>
#	year <year>
#	<month_name> <day>{*} {<text>}
#	<month><sep><day>{<sep><year>}{*} {<text>}
#
# where:
#	<options> := one or more valid command-line options (except -e and -f)
#	<month_name> := first 3+ characters of name of month (in English)
#	<sep> := one or more non-numeric, non-space, non-'*' characters
#	<text> is the text to be printed in the calendar box
#	<day>, <month>, and <year> are appropriate integers
#
#	whitespace is to be used/avoided as implied by the above productions
#	'*' flags the date as a holiday (to be printed in gray)
#	comments run from '#' through end-of-line

# A sample "opt" line to change the fonts and output file names and to print
# only Sundays in gray:
#
#opt -d Helvetica-Bold -t Helvetica-Bold -o myfile.ps -b all -g sun

year 1990				# set year explicitly

5/28* Memorial Day (observed)		# '*' prints holiday in gray
5/31 Memorial Day

7/4/90* Independence Day		# full date format

Sep 3* Labor Day			# month written out

10/8* Columbus Day (observed)
10/12 Columbus Day

11/22* Thanksgiving
11/23*					# holiday without text

12/24* # Christmas Eve
12/25* Christmas

1/1/91* New Year's Day			# set new year implicitly
SHAR_EOF
fi
if test -f 'pcal.c'
then
	echo shar: will not over-write existing file 'pcal.c'
else
cat << \SHAR_EOF > 'pcal.c'
/*
 * pcal.c - generate PostScript file to print calendar for any month and year
 *
 * The original PostScript code to generate the calendars was written by
 * Patrick Wood (Copyright (c) 1987 by Patrick Wood of Pipeline Associates,
 * Inc.), and authorized for modification and redistribution.  The calendar
 * file inclusion code was originally written in "bs(1)" by Bill Vogel of
 * AT&T.  Patrick's original PostScript was modified and enhanced several
 * times by others whose names have regrettably been lost.  This C version
 * was originally created by Ken Keirnan of Pacific Bell; additional
 * enhancements by Joseph P. Larson, Ed Hand, and Andrew Rogers (who also
 * did the VMS port), and Mark Kantrowitz.  Many thanks also to Joe Brownlee
 * and Eric Hammond for their suggestions.
 *
 * Revision history:
 *
 *	2.1	MK/AWR	08/27/90	support -L, -C, -R, -n options;
 *					print holiday text next to date
 *
 *		AWR	08/24/90	incorporate cpp-like functionality;
 *					add -D and -U options; save date file
 *					information in internal data structure;
 *					look for PCAL_OPTS and PCAL_DIR; look
 *					for ~/.calendar and ~/calendar
 *
 *	2.0	AWR	08/08/90	included revision history; replaced -r
 *					flag with -l and -p; replaced -s and -S
 *					flags with -b and -g; recognize flags
 *					set in date file; translate ( and ) in
 *					text to octal escape sequence; usage()
 *					message condensed to fit 24x80 screen
 *
 *	Parameters:
 *
 *		pcal [opts]		generate calendar for current month/year
 *
 *		pcal [opts] yy		generate calendar for entire year yy
 *
 *		pcal [opts] mm yy	generate calendar for month mm
 *					(Jan = 1), year yy (19yy if yy < 100)
 *
 *		pcal [opts] mm yy n	as above, for n consecutive months
 *
 *	Output:
 *
 *		PostScript file to print calendars for all selected months.
 *
 *	Options:
 *
 *		-b <DAY>	print specified weekday in black
 *		-g <DAY>	print specified weekday in gray
 *				(default: print Saturdays and Sundays in gray)
 *		
 *		-d <FONT>	specify alternate font for day names
 *				(default: Times-Bold)
 *
 *		-n <FONT>	specify alternate font for notes in boxes
 *				(default: Helvetica-Narrow)
 *
 *		-t <FONT>	specify alternate font for titles
 *				(default: Times-Bold)
 *
 *		-D <SYM>	define preprocessor symbol
 *		-U <SYM>	un-define preprocessor symbol
 *
 *		-e		generate empty calendar (ignore date file)
 *
 *		-f <FILE>	specify alternate date file (default:
 *				~/.calendar on Un*x, SYS$LOGIN:CALENDAR.DAT
 *				on VMS; if environment variable [logical
 *				name on VMS] PCAL_DIR exists, looks there
 *				instead)
 *
 *		-o <FILE>	specify alternate output file (default:
 *				stdout on Un*x, CALENDAR.PS on VMS)
 *
 *		-L <STRING>	specify left foot string   (default: "")
 *		-C <STRING>	specify center foot string (default: "")
 *		-R <STRING>	specify right foot string  (default: "")
 *
 *		-l		generate landscape-mode calendars
 *		-p		generate portrait-mode calendars
 *				(default: landscape-mode)
 *
 *	There are many ways to specify these options in addition to using the
 *	command line; this facilitates customization to the user's needs.
 *
 *	If the environment variable (global symbol on VMS) PCAL_OPTS is
 *	present, its value will be parsed as if it were a command line.
 *	Any options specified will override the program defaults.
 *
 *	All but the -e, -f, -D, and -U options may be specified in the date
 *	file by the inclusion of one or more lines of the form "opt <options>".
 *	Any such options override any previous values set either as program
 *	defaults, via PCAL_OPTS, or in previous "opt" lines.
 *
 *	Options explicitly specified on the command line in turn override all
 *	of the above.
 *
 *	The -b, -d, -g, -n, -o, -t, -C, -L, and -R flags may be specified at
 *	any point to reset the corresponding option to its default.  -D may
 *	be specified without an argument in order to un-define all symbols.
 *
 *	Parameters and flags may be mixed on the command line.  In some cases
 *	(e.g., when a parameter follows a flag without its optional argument)
 *	this may lead to ambiguity; the dummy flag '-' (or '--') may be used
 *	to separate them, i.e. "pcal -t - 9 90".
 *
 *	Simple cpp-like functionality is provided.  The date file may include
 *	the following commands, which work like their cpp counterparts:
 *
 *		define <sym>
 *		undef <sym>
 *
 *		if{n}def <sym>
 *		   ...
 *		{ else
 *		   ... }
 *		endif
 *
 *		include <file>
 *
 *	Note that these do not start with '#', which is reserved as a comment
 *	character.
 *
 *	"define" alone deletes all the current definitions; "ifdef" alone is
 *	always false; "ifndef" alone is always true.  All defined symbols are
 *	treated in a case-insensitive manner.
 *
 *	The file name in the "include" directive may optionally be surrounded
 *	by "" or <>.
 *
 */


#include <stdio.h>
#include <ctype.h>
#include <time.h>
#include <string.h>

#ifdef VMS		/* VMS oddities isolated here */

#include <ssdef.h>	/* required for trnlog() */
#include <descrip.h>

#define HOME_DIR	"SYS$LOGIN"
#define DATEFILE	"calendar.dat"
#define OUTFILE		"calendar.ps"
#define START_PATH	'['
#define END_PATH	']'

#define EXIT_SUCCESS 1
#define EXIT_FAILURE 3

#else			/* non-VMS - assume Un*x of some sort */

#define HOME_DIR	"HOME"
#define DATEFILE	".calendar"
#define ALT_DATEFILE	"calendar"	/* for backward compatibility */
#define OUTFILE		""
#define START_PATH	'/'
#define END_PATH	'/'

#define EXIT_SUCCESS 0
#define EXIT_FAILURE 1

#endif

#define PCAL_OPTS	"PCAL_OPTS"	/* environment variables */
#define PCAL_DIR	"PCAL_DIR"

#define IS_LEAP(y)	((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
#define INIT_COLORS	memcpy(color, default_color, sizeof(color))
#define LASTCHAR(p)	((p) && *(p) ? (p) + strlen(p) - 1 : NULL)

#ifdef __STDC__
#define TOLOWER(c)	tolower(c)
#else
#define TOLOWER(c)	(isupper(c) ? tolower(c) : (c))
#endif

#define PRT		(void)printf
#define FPR		(void)fprintf

#define FALSE	0
#define TRUE	1

#define ALL_FLAGS	"bCDdefgLlnopRtU"	/* all command-line flags */
#define DATEFILE_FLAGS	"DefU"			/* parsed before opening datefile */
#define OTHER_FLAGS	"bCdgLlnopRt"		/* parsed inside datefile */

#define DAYFONT		"Times-Bold"		/* default font names */
#define TITLEFONT	"Times-Bold"
#define NOTESFONT	"Helvetica-Narrow"

#define LFOOT 		""                        /* default foot strings */
#define CFOOT 		""
#define RFOOT 		""

#define LANDSCAPE  90		/* degrees to rotate for landscape/portrait */
#define PORTRAIT    0
#define ROTATE	   LANDSCAPE	/* default */

#define BLACK		0	/* colors for dates */
#define GRAY		1

#define NO_DATEFILE 	0	/* date file (if any) to use */
#define USER_DATEFILE	1
#define SYS_DATEFILE	2

/* preprocessor token codes - must be contiguous range of integers starting
 * at 0 and ending with code for non-tokens (cf. pp_info[], pp_token())
 */
#define PP_DEFINE	0
#define PP_ELSE		1
#define PP_ENDIF	2
#define PP_IFDEF	3
#define PP_IFNDEF	4
#define PP_INCLUDE	5
#define PP_UNDEF	6
#define PP_OTHER	7	/* not pp token */

#define MAX_NESTING	10	/* maximum nesting level for file inclusion */

#define MAX_PP_SYMS	100	/* number of definable preprocessor symbols */
#define PP_SYM_UNDEF     -1	/* flag for undefined symbol */

#define MIN_YR		1900	/* significant years (calendar limits) */
#define MAX_YR		9999

#define JAN		 1	/* significant months */
#define FEB		 2
#define DEC		12

#define PARSE_OK	0	/* returns from parse(), enter_day_info() */
#define PARSE_INVDATE	1
#define PARSE_INVLINE	2

#define STRSIZ	200		/* size of misc. strings */

#define MAXARGS 3		/* numeric command-line args */

#define WHITESPACE " \t"	/* token delimiters in date file */

/*
 * Global typedef declarations for data structure
 */

typedef struct d_i {
	int is_holiday;
	char *text;
	struct d_i *next;
	} day_info;

typedef struct m_i {
	unsigned long holidays;
	day_info *day[31];
	} month_info;

typedef struct y_i {
	int year;
	month_info *month[12];
	struct y_i *next;
	} year_info;


/*
 * Global variables:
 */

int do_define(), do_ifdef(), do_ifndef(), do_include(), do_undef();
char *trnlog(), *mk_path(), *mk_filespec();

extern char *getenv();

year_info *head = NULL;		/* head of internal data structure */
int nesting_level = 0;		/* level of include file nesting */
int init_month;			/* initial month, year, number of months */
int init_year;
int nmonths;
int curr_year;			/* current default year for date file entries */
char *words[100];		/* maximum number of words per date file line */
char lbuf[512];			/* maximum date file line size */
char *pp_sym[MAX_PP_SYMS];	/* preprocessor defined symbols */
char progname[STRSIZ];		/* program name (for error messages) */
char color[7];			/* colors of weekdays - cf. default_color[] */

/*
 * Default values for command-line options:
 */

char default_color[7] = {		/* -b, -g */
	GRAY, BLACK, BLACK, BLACK, BLACK, BLACK, GRAY	/* cf. COLOR_MSG */
	};

int datefile_type = SYS_DATEFILE;	/* -e, -f */
char datefile[STRSIZ] = "";
char default_dir[STRSIZ] = "";

int rotate = ROTATE;			/* -l, -p */

char dayfont[STRSIZ] = DAYFONT;		/* -d, -t, -n */
char titlefont[STRSIZ] = TITLEFONT;
char notesfont[STRSIZ] = NOTESFONT;

char lfoot[STRSIZ] = LFOOT;             /* -L, -C, -R */
char cfoot[STRSIZ] = CFOOT;
char rfoot[STRSIZ] = RFOOT;

char outfile[STRSIZ] = OUTFILE;		/* -o */

/*
 * Language-dependent strings (month and day names, option file keywords,
 * preprocessor tokens):
 */

static char *months[12] = {
	"January", "February", "March", "April", "May", "June",
	"July", "August", "September", "October", "November", "December"
	};

static char *days[7] = {
	"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
	"Saturday"
	};

/* preprocessor tokens - must be in same order as PP_XXXXX (cf. pp_token()) */
static struct pp {
	char	*token;		/* name */
	int	(*pfcn)();	/* dispatch routine */
	} pp_info[] = {
		{ "define",  do_define  },		/* PP_DEFINE	*/
		{ "else",    NULL       },		/* PP_ELSE	*/
		{ "endif",   NULL       },		/* PP_ENDIF	*/
		{ "ifdef",   do_ifdef   },		/* PP_IFDEF	*/
		{ "ifndef",  do_ifndef  },		/* PP_IFNDEF	*/
		{ "include", do_include },		/* PP_INCLUDE	*/
		{ "undef",   do_undef   },		/* PP_UNDEF	*/
		{ NULL,      NULL       }		/* PP_OTHER	*/
	};

#define MIN_DAY_LEN   2		/* minimum size of abbreviations  */
#define MIN_MONTH_LEN 3
#define MIN_PPTOK_LEN 3

#define ALL	"all"		/* command-line or date file keywords */
#define OPT	"opt"
#define YEAR	"year"

#define COLOR_MSG	"Sat/Sun in gray, others in black"    /* cf. usage() */

/*
 * PostScript boilerplate
 */

#include "pcalinit.h"


/*
 * Main program - parse and validate command-line arguments, open files,
 * generate PostScript boilerplate and code to generate calendars.
 *
 * Program structure:
 *
 * main() looks for the environment variable (global symbol on VMS) PCAL_OPTS
 * and calles get_args() to parse it.  It then calls get_args() again to parse
 * the command line for the date file name, -D and -U options to be in effect
 * prior to reading the date file, and any numeric arguments (month, year, 
 * number of months).  It then calls read_datefile() to read and parse the
 * date file; any "opt" lines present will override the defaults for the
 * command-line flags.  It then calls get_args() again to process the other
 * command-line flags, which in turn override any specified earlier.
 *
 * main() then generates the common PostScript code and then calls pmonth() to
 * print the calendars.
 *
 * read_datefile() calls getline() to read the date file, do_xxxxx() to process
 * the preprocessor tokens, and parse() to parse each date line.
 *
 * getline() reads one or more lines from the date file, stripping comments
 * (# through end-of-line) and ignoring blank lines.
 *
 * parse() parses a line from the date file and processes it.  If "opt", it
 * calls loadwords() to split the line into tokens and get_args() to
 * process them.  If the line contains a date, it calls enter_day_info() to
 * enter the day and related text into the data structure.
 *
 * pmonth() calls find_holidays() to generate the list of holidays to be
 * printed in gray; it then calls find_daytext() to generate the text to
 * be printed inside the calendar boxes.
 *
 */
main(argc, argv)
	int argc;
	char **argv;
{
	FILE *dfp = NULL;		/* date file pointer */
	char *p, **ap;
	int i, month, year, ngray;

#define DO_HEADER(phdr)	for (ap = phdr; *ap; ap++) PRT("%s\n", *ap)

	INIT_COLORS;		/* set up default colors */

	/* isolate root program name (for use in error messages) */

	strcpy(progname, **argv ? *argv : "pcal");

	if ((p = strrchr(progname, END_PATH)) != NULL)
		strcpy(progname, ++p);
	if ((p = strchr(progname, '.')) != NULL)
		*p = '\0';

	/*
	 * Get the arguments from a) the environment variable, b) "opt" lines
	 * in the date file, and c) the command line, in that order
	 */

	/* look for environment variable for options */

	if ((p = getenv(PCAL_OPTS)) != NULL) {
		strcpy(lbuf, "x ");	/* prepend a dummy token */
		strcat(lbuf, p);
		loadwords();
		if (! get_args(words, ALL_FLAGS, FALSE)) {
			usage();
			exit(EXIT_FAILURE);
		}
	}

	/* parse command-line arguments once to find name of date file, etc. */

	if (!get_args(argv, DATEFILE_FLAGS, TRUE)) {
		usage();
		exit(EXIT_FAILURE);
	}

	/* Attempt to open the date file as specified by the [-e | -f] flags */

	switch (datefile_type) {
	case NO_DATEFILE:
		dfp = NULL;
		break;

	case USER_DATEFILE:	
		/* Attempt to open user-specified calendar file */
		if ((dfp = fopen(datefile, "r")) == NULL) {
			FPR(stderr, "%s: can't open file %s\n", progname, 
				datefile);
			exit(EXIT_FAILURE);
		}
		mk_path(default_dir, datefile);	/* extract path */
		break;

	case SYS_DATEFILE:
		/* Attempt to open system-specified calendar file */
		if ((p = trnlog(PCAL_DIR)) || (p = trnlog(HOME_DIR)))
			strcpy(default_dir, p);

		mk_filespec(datefile, default_dir, DATEFILE);
		dfp = fopen(datefile, "r");	/* no error if nonexistent */
#ifdef ALT_DATEFILE
		if (!dfp) {		/* try again with alternate file */
			mk_filespec(datefile, default_dir, ALT_DATEFILE);
			dfp = fopen(datefile, "r");
		}
#endif
		break;
	}

	/* read the date file (if any) and build internal data structure */

	if (dfp) {
		curr_year = init_year;
		read_datefile(dfp, datefile);
		fclose(dfp);
	}

	/* reparse command line - flags there supersede those in date file */

	get_args(argv, OTHER_FLAGS, FALSE);

	/* done with the arguments and flags - try to open the output file */

	if (*outfile && freopen(outfile, "w", stdout) == (FILE *) NULL) {
		FPR(stderr, "%s: can't open file %s\n", progname, outfile);
		exit(EXIT_FAILURE);
	}

	/*
	 * Write out PostScript prolog
	 */

	/* font names */

 	PRT("%%!\n");
	PRT("/titlefont /%s def\n/dayfont /%s def\n/notesfont /%s def\n", titlefont, dayfont, notesfont);

	/* foot strings */

	def_footstring(lfoot, 'L');
	def_footstring(cfoot, 'C');
	def_footstring(rfoot, 'R');

	/* month names */

	PRT("/month_names [");
	for (i = 0; i < 12; i++)
		PRT("%s(%s) ", i % 6 == 0 ? "\n\t" : "", months[i]);
	PRT("] def\n");

	/* day names */

	PRT("/day_names [");
	for (i = 0; i < 7; i++)
		PRT("%s(%s) ", i % 6 == 0 ? "\n\t" : "", days[i]);
	PRT("] def\n");

	/* colors (black/gray) to print weekdays and holidays */

	PRT("/day_gray [");
	for (ngray = i = 0; i < 7; ngray += color[i++] == GRAY)
		PRT(" %s", color[i] == GRAY ? "true" : "false");
	PRT(" ] def\n");
	PRT("/holiday_gray %s def\n", ngray <= 3 ? "true" : "false");

	/* PostScript boilerplate (part 1) */

	DO_HEADER(header_1);

	/* landscape or portrait mode */

 	PRT("\t%d rotate\n", rotate);
 	if (rotate == LANDSCAPE)
 		PRT("\t50 -120 translate\n");
 	else
		PRT("\t0.75 0.75 scale\n\t50 500 translate\n");

	/* PostScript boilerplate (part 2) */

	DO_HEADER(header_2);

	/*
	 * Write out PostScript code to print calendars
	 */

	month = init_month;
	year = init_year;

	while (nmonths--) {
		pmonth(month, year);
		if (++month > DEC) {
			month = JAN;
			year++;
		}
	}

	cleanup();

#ifdef VMS
	FPR(stderr, "Output is in file %s\n", outfile);
#endif
	exit(EXIT_SUCCESS);
}

/*
 * get_args - walk the argument list, parsing all arguments but processing only
 * those specified in "flags".  If "do_numargs" is TRUE, processes numeric
 * arguments (month, year, number of months) as well.
 */
int get_args(argv, flags, do_numargs)
	char **argv;		/* argument list */
	char *flags;		/* which flags to process */
	int do_numargs;		/* process numeric arguments? */
{
	char *p, *opt;
	int i, do_flag;
	long tmp;			/* for getting current month/year */
	struct tm *p_tm;
	int badopt = FALSE;		/* flag set if bad param   */
	int nargs = 0;			/* count of non-flag args  */
	int numargs[MAXARGS];		/* non-flag (numeric) args */

/* Look for the argument following flag - may be separated by spaces or
 * not (bumps argv in former case).  If no non-flag argument appears, set
 * "arg" to NULL (-b, -C, -d, -g, -L, -n, -o, -R, and -t without an argument
 * reset the corresponding option to its default value).
 */
#define GETARG(arg) arg = *(*argv + 2) ? *argv + 2 : \
			(*(argv+1) && **(argv+1) != '-' ? *++argv : NULL)

	/* Walk argument list, ignoring first element (program name) */

 	while (*++argv) {

		/* Assume that any non-flag argument is a numeric argument */
		if (**argv != '-') {
		    	if (do_numargs && nargs < MAXARGS)
				numargs[nargs++] = atoi(*argv);
			continue;
		}

		/* Is this flag among those to be processed beyond parsing? */

		do_flag = strchr(flags, *(opt = *argv + 1)) != NULL;

		switch (*opt) {

		case '\0':		/* take - or -- as dummy flags */
		case '-' :
			break;

		case 'b':		/* print day in black or gray */
		case 'g':
			GETARG(p);
			if (do_flag)
				if (p)
					set_color(p, *opt == 'b' ? BLACK : GRAY);
				else
					INIT_COLORS;	/* reset to defaults */
			break;

		case 'C':		/* specify alternate center foot */
			GETARG(p);
			if (do_flag)
				strcpy(cfoot, p ? p : CFOOT);
			break;

 		case 'd':		/* specify alternate day font */
 			GETARG(p);
			if (do_flag)
				strcpy(dayfont, p ? p : DAYFONT);
 			break;

		case 'D':		/* define preprocessor symbol */
			GETARG(p);
			if (do_flag)
				do_define(p);
			break;

		case 'e':		/* generate empty calendar */
			if (do_flag) {
				datefile_type = NO_DATEFILE;
				datefile[0] = '\0';
			}
			break;

		case 'f':		/* specify alternate date file */
			GETARG(p);
			if (p && do_flag) {
				datefile_type = USER_DATEFILE;
				strcpy(datefile, p);
			}
			break;

		case 'L':		/* specify alternate left foot */
			GETARG(p);
			if (do_flag)
				strcpy(lfoot, p ? p : LFOOT);
			break;

 		case 'l':		/* generate landscape calendar */
			if (do_flag)
	 			rotate = LANDSCAPE;
 			break;
 
		case 'n':		/* specify alternate notes font */
			GETARG(p);
			if (do_flag)
				strcpy(notesfont, p ? p : NOTESFONT);
			break;

		case 'o':		/* specify alternate output file */
			GETARG(p);
			if (do_flag)
				strcpy(outfile, p ? p : OUTFILE);
			break;

 		case 'p':		/* generate portrait calendar */
			if (do_flag)
	 			rotate = PORTRAIT;
 			break;

 		case 'R':		/* specify alternate right foot */
			GETARG(p);
			if (do_flag)
				strcpy(rfoot, p ? p : RFOOT);
			break;


 		case 't':		/* specify alternate title font */
 			GETARG(p);
			if (do_flag)
				strcpy(titlefont, p ? p : TITLEFONT);
 			break;

		case 'U':		/* undef preprocessor symbol */
			GETARG(p);
			if (do_flag)
				do_undef(p);
			break;

		default:		/* unrecognized flag */
			FPR(stderr, "%s: illegal option -%s\n", progname, opt);
			badopt = TRUE;
			break;
		}
        }

	if (!do_numargs)
		return !badopt;		/* return TRUE if OK, FALSE if error */

	/* Validate non-flag (numeric) parameters */

	switch (nargs) {
	case 0:		/* no arguments - print current month/year */
		time(&tmp);
		p_tm = localtime(&tmp);
		init_month = p_tm->tm_mon + 1;
		init_year = p_tm->tm_year;
		nmonths = 1;
		break;			
	case 1:		/* one argument - print entire year */
		init_month = JAN;
		init_year = numargs[0];
		nmonths = 12;
		break;
	default:	/* two or three arguments - print one or more months */
		init_month = numargs[0];
		init_year = numargs[1];
		nmonths = nargs > 2 ? numargs[2] : 1;
		break;
	}

	if (nmonths < 1)		/* ensure at least one month */
		nmonths = 1;

	/* check range of month and year */

	if (init_month < JAN || init_month > DEC) {
		FPR(stderr, "%s: month %d not in range 1 .. 12\n", progname,
			init_month);
		badopt = TRUE;
	}
	
	if (init_year > 0 && init_year < 100)	/* treat nn as 19nn */
		init_year += 1900;
	
	if (init_year < MIN_YR || init_year > MAX_YR) {
		FPR(stderr, "%s year %d not in range %d .. %d\n", progname,
			init_year, MIN_YR, MAX_YR);
		badopt = TRUE;
	}

	return !badopt;		/* return TRUE if OK, FALSE if error */
}



/*
 *	usage - print message explaining correct usage of the command-line
 *	arguments and flags
 */
usage()
{
	FPR(stderr, "\nUsage:\t%s [-b DAY]* [-d FONT] [-n FONT] [-e | -f FILE] [-g DAY]* [-o FILE]\n", progname);
	FPR(stderr, "\t\t[-l|-p] [-L|-C|-R STRING] [-t FONT] [ [ [mm] yy ] | [mm yy n] ]\n");
	FPR(stderr, "\n");
	FPR(stderr, "\t-b DAY\t\tprint weekday DAY in black\n");
	FPR(stderr, "\t-g DAY\t\tprint weekday DAY in gray\n");
	FPR(stderr, "\t\t\t(default: %s)\n", COLOR_MSG);
	FPR(stderr, "\n");
	FPR(stderr, "\t-d FONT\t\tspecify alternate day name font (default: %s)\n",
		DAYFONT);
	FPR(stderr, "\t-t FONT\t\tspecify alternate title font (default: %s)\n",
		TITLEFONT);
	FPR(stderr, "\t-n FONT\t\tspecify alternate notes font (default: %s)\n",
		NOTESFONT);
	FPR(stderr, "\n");
	FPR(stderr, "\t-L STRING\t\tspecify left foot string (default: %s)\n",
	    LFOOT);
	FPR(stderr, "\t-C STRING\t\tspecify center foot string (default: %s)\n",
	    CFOOT);
	FPR(stderr, "\t-R STRING\t\tspecify right foot string (default: %s)\n",
	    RFOOT);
	FPR(stderr, "\n");
	FPR(stderr, "\t-D SYM\t\tdefine preprocessor symbol\n");
	FPR(stderr, "\t-U SYM\t\tundefine preprocessor symbol\n");
	FPR(stderr, "\n");
	FPR(stderr, "\t-e\t\tgenerate empty calendar (ignore date file)\n");
	FPR(stderr, "\t-f FILE\t\tspecify alternate date file (default: %s)\n",
		DATEFILE);
	FPR(stderr, "\t-o FILE\t\tspecify alternate output file (default: %s)\n",
		OUTFILE[0] ? OUTFILE : "stdout");
	FPR(stderr, "\n");
	FPR(stderr, "\t-l\t\tgenerate landscape-style calendars");
#if (ROTATE == LANDSCAPE)
	FPR(stderr, " (default)");
#endif
	FPR(stderr, "\n\t-p\t\tgenerate portrait-style calendars");
#if (ROTATE == PORTRAIT)
	FPR(stderr, " (default)");
#endif
	FPR(stderr, "\n\n");
	FPR(stderr, "\tyy\t\tgenerate calendar for year yy (19yy if yy < 100)\n");
	FPR(stderr, "\tmm yy\t\tgenerate calendar for month mm (Jan = 1), year yy\n");
	FPR(stderr, "\tmm yy n\t\tgenerate calendars for n months, starting at mm/yy\n");
	FPR(stderr, "\t(default)\tgenerate calendar for current month/year\n");
}


/*
 * General-purpose utility routines
 */


/*
 * alloc - interface to calloc(); terminates if unsuccessful
 */
char *alloc(size)
	int size;
{
	char *p;
	extern char *calloc();

	if (size == 0)		/* not all calloc()s like null requests */
		size = 1;

	if ((p = calloc(1, size)) == NULL) {
		FPR(stderr, "%s: out of memory\n", progname);
		exit(EXIT_FAILURE);
	}

	return p;
}


/*
 * ci_str{n}cmp - case-insensitive flavors of strcmp(), strncmp()
 */
int ci_strcmp(s1, s2)
register char *s1, *s2;
{
	register char c1, c2;

	for ( ; (c1 = TOLOWER(*s1)) == (c2 = TOLOWER(*s2)); s1++, s2++)
		if (c1 == '\0')
			return 0;

	return c1 - c2;
}


int ci_strncmp(s1, s2, n)
register char *s1, *s2;
int n;
{
	register char c1, c2;

	for ( ; --n >= 0 && (c1 = TOLOWER(*s1)) == (c2 = TOLOWER(*s2)); s1++, s2++)
		if (c1 == '\0')
			return 0;

	return n < 0 ? 0 : c1 - c2;
}


/*
 * Preprocessor token and symbol table routines
 */


/*
 * find_sym - look up symbol; return symbol table index if found, PP_SYM_UNDEF
 * if not found
 */
int find_sym(sym)
	char *sym;
{
	int i;

	if (!sym)
		return PP_SYM_UNDEF;

	for (i = 0; i < MAX_PP_SYMS; i++)
		if (pp_sym[i] && ci_strcmp(pp_sym[i], sym) == 0)
			return i;

	return PP_SYM_UNDEF;
}


/*
 * do_ifdef - return TRUE if 'sym' is currently defined; FALSE if not
 */
int do_ifdef(sym)
	char *sym;
{
	return find_sym(sym) != PP_SYM_UNDEF;
}


/*
 * do_ifndef - return FALSE if 'sym' is currently defined; TRUE if not
 */
int do_ifndef(sym)
	char *sym;
{
	return find_sym(sym) == PP_SYM_UNDEF;
}


/*
 * do_define - enter 'sym' into symbol table; if 'sym' NULL, clear symbol table
 */
do_define(sym)
	char *sym;
{
	int i;

	if (! sym) {		/* null argument - clear all definitions */
		clear_syms();
		return;
	}

	if (do_ifdef(sym))	/* already defined? */
		return;

	for (i = 0; i < MAX_PP_SYMS; i++)	/* find room for it */
		if (! pp_sym[i]) {
			strcpy(pp_sym[i] = alloc(strlen(sym)+1), sym);
			return;
		}

	FPR(stderr, "%s: no room to define %s\n", progname, sym);
}


/*
 * do_undef - undefine 'sym' and free its space; no error if not defined
 */
do_undef(sym)
	char *sym;
{
	int i;

	if (! sym) 
		return;

	if ((i = find_sym(sym)) != PP_SYM_UNDEF) {
		free(pp_sym[i]);
		pp_sym[i] = NULL;
	}
}


/*
 * do_include - include specified file (optionally in "" or <>)
 */
do_include(path, name)
	char *path;		/* path to file */
	char *name;		/* file name */
{
	FILE *fp;
	char *p, incfile[STRSIZ], tmpnam[STRSIZ];

	if (! name)		/* whoops, no date file */
		return;

	/* copy name, stripping "" or <> */
	strcpy(tmpnam, name + (*name == '"' || *name == '<'));
	if ((p = LASTCHAR(tmpnam)) && *p == '"' || *p == '>')
		*p = '\0';

	if ((fp = fopen(mk_filespec(incfile, path, tmpnam), "r")) == NULL) {
		FPR(stderr, "%s: can't open file %s\n",	progname, incfile);
		exit(EXIT_FAILURE);
	}

	read_datefile(fp, incfile);
	fclose(fp);
}


/*
 * pp_token - look up 'token' in list of preprocessor tokens; return its
 * index if found, PP_OTHER if not (N.B.: this relies on the ordering of
 * PP_XXXXX and pp_info[]; see comments at their definitions).
 */
int pp_token(token)
	char *token;
{
	struct pp *p;

	for (p = pp_info;
             p->token && ci_strncmp(token, p->token, MIN_PPTOK_LEN);
	     p++)
		;

	return p - pp_info;
}

/*
 * Internal data structure routines
 */


/*
 * find_year - find record in year list; optionally create if not present 
 */
year_info *find_year(year, insert)	/* find record in year list */
	int year;
	int insert;			/* insert if missing */
{
	year_info *pyear, *plast, *p;

	for (plast = NULL, pyear = head;		/* search linked list */
	     pyear && pyear->year < year;
	     plast = pyear, pyear = pyear->next)
		;

	if (pyear && pyear->year == year)		/* found - return it */
		return pyear;

	if (insert) {		/* not found - insert it if requested */
		p = (year_info *) alloc(sizeof(year_info));	/* create new record */
		p->year = year;

		p->next = pyear;				/* link it in */
		return *(plast ? &plast->next : &head) = p;
	}
	else
		return NULL;
}


/*
 * enter_day_info - enter text for specified day; avoid entering duplicates.
 * returns PARSE_INVDATE if date invalid, PARSE_OK if OK
 */
int enter_day_info(m, d, y, is_holiday, pword)	/* fill in information for given day */
	int m, d, y;
	int is_holiday;
	char **pword;
{
	static year_info *pyear;
	static int prev_year = 0;
	month_info *pmonth;
	day_info *pday, *plast;

	if (! is_valid(m, d, y))
		return PARSE_INVDATE;

	if (y != prev_year)		/* avoid unnecessary year lookup */
		pyear = find_year(y, 1);

	--m, --d;			/* adjust for use as subscripts */

	if ((pmonth = pyear->month[m]) == NULL)	/* find/create month record */
		pyear->month[m] = pmonth = (month_info *) alloc(sizeof(month_info));

	if (is_holiday)
		pmonth->holidays |= (1 << d);

	/* insert text for day at end of list (preserving the order of entry
	 * for multiple lines on same day); eliminate those differing only
	 * in spacing and capitalization from existing entries
         */

	get_text(pword);	/* consolidate text into lbuf */

	if (*lbuf) {
		for (plast = NULL, pday = pmonth->day[d];
		     pday;
		     plast = pday, pday = pday->next)
			if (ci_strcmp(pday->text, lbuf) == 0) {
				pday->is_holiday |= is_holiday;
				return PARSE_OK;
			}

		/* unique - add to end of list */

		pday = (day_info *) alloc(sizeof(day_info));
		pday->is_holiday = is_holiday;
		strcpy(pday->text = (char *) alloc(strlen(lbuf)+1), lbuf);
		pday->next = NULL;
		*(plast ? &plast->next : &pmonth->day[d]) = pday;
	}

	return PARSE_OK;
}


/*
 * Housekeeping routines to free allocated data
 */


/*
 * clear_syms - clear and deallocate the symbol table
 */
clear_syms()
{
	int i;

	for (i = 0; i < MAX_PP_SYMS; i++)
		if (pp_sym[i]) {
			free(pp_sym[i]);
			pp_sym[i] = NULL;
		}
}


/*
 * cleanup - free all allocated data
 */
cleanup()
{
	int i, j;
	year_info *py, *pny;
	month_info *pm;
	day_info *pd, *pnd;

	for (py = head; py; py = pny) {		/* main data structure */
		pny = py->next;
		for (i = 0; i < 12; i++) {
			if ((pm = py->month[i]) == NULL)
				continue;
			for (j = 0; j < 31; j++)
				for (pd = pm->day[j]; pd; pd = pnd) {
					pnd = pd->next;
					free(pd->text);
					free(pd);
				}
			free(pm);
		}
	free(py);
	}

	clear_syms();				/* symbol table */

}


/*
 * Utility routines for date and text parsing and entry
 */


/*
 * is_valid - return TRUE if m/d/y is a valid date
 */
int is_valid(m, d, y)
	register int m, d, y;
{
	static char len[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

	return m >= JAN && m <= DEC && 
		d >= 1 && d <= (len[m] + (m == FEB && IS_LEAP(y)));
}


/*
 * loadwords - tokenize line buffer into word array, return word count.
 * differs from old loadwords() in that it handles quoted (" or ') strings
 */

int loadwords()
{
	register char *pstr, *ptok;
	char *delim, **ap;
	register int i;

	pstr = lbuf;

	for (i = 0, ap = words; TRUE; i++, ap++) {
		delim =	*pstr == '"'  ? "\"" :
			*pstr == '\'' ? "'"  :
			WHITESPACE;

		ptok = pstr += strspn(pstr, delim); /* look for next token */

		if (! *pstr) {		/* end of lbuf? */
			*ap = NULL;	/* add null ptr at end */
			return i;	/* return count of non-null ptrs */
			}

		if (*ptok == '"' || *ptok == '\'')	/* bump past quote */
			ptok++;

		*ap = ptok;				/* save token ptr */

		pstr += strcspn(pstr, delim);	 	/* skip past token */

		if (*pstr)				/* terminate token */
			*pstr++ = '\0';
		}

}


/*
 * get_text - retrieve remaining text in lbuf and transform in place, removing
 * leading/trailing whitespace and condensing runs of whitespace to one blank
 */
get_text(pword)
	char **pword;		/* pointer to first desired word in "words" */
{
char *pbuf, *p;

/* copy words back to lbuf in place, separating by one blank */

for (pbuf = lbuf; p = *pword; *pbuf++ = *++pword ? ' ' : '\0')
	while (*p)
		*pbuf++ = *p++;

if (pbuf == lbuf)
	*lbuf = '\0';
}


/*
 * print_text - print tokens in text (assumed separated by single blank)
 * in PostScript format; convert '(' or ')' to octal escape
 */
print_text(p)
	char *p;
{
	char c;

	PRT("(");
	for ( ; c = *p ; p++) {
		if (c == ' ')
			PRT(")\n(");
		else
			PRT(c == '(' || c == ')' ? "\\%03o" : "%c", c);
	}
	PRT(")\n");
}


/*
 * def_footstring - print definition for foot string, again converting '('
 * or ')' to octal escape
 */
def_footstring(p, c)
	char *p;			/* definition */
	char c;				/* L, C, or R */
{

	PRT("/%cfootstring (", c);

	while (c = *p++)
		PRT(c == '(' || c == ')' ? "\\%03o" : "%c", c);

	PRT(") def\n");
}


/*
 * set_color - set one or all weekdays to print in black or gray
 */
set_color(day, col)
	char *day;		/* weekday name (or "all") */
	int  col;		/* select black or gray */
{
	int i, do_all;

	do_all = ci_strncmp(day, ALL, strlen(ALL)) == 0;	/* set all days? */

	for (i = 0; i < 7; i++)
		if (do_all || ci_strncmp(day, days[i], MIN_DAY_LEN) == 0)
			color[i] = col;
}

/*
 * parse - enter date file data (in lbuf[]) into data structure
 *
 * Looks for an entry of one of the following forms:
 *
 *	year <year>
 *	<month_name> <day>{*} {<text>}
 *	<month><sep><day>{<sep><year>}{*} {<text>}
 *	opt <options>
 *
 * where
 *	<month_name> := first 3+ characters of name of month (in English)
 *	<sep> := one or more non-numeric, non-space, non-'*' characters
 *	<options> := any command-line option except -e, -f, -D, -U
 *
 * Enters information for year, month, and day into data structure for
 * subsequent retrieval.  Lines of form "opt <options>" override any
 * defaults.
 *
 * N.B.: "inc" and other cpp-like lines are handled in read_datefile().
 *
 */
int parse()
{
	register char *cp;
	char **pword;
	int mm, dd, yy;
	int is_holiday;

/* macro to skip numeric field */

#define SKIP_FIELD(p) \
	if (1) {while (isdigit(*p)) p++; while (*p && !isdigit(*p)) p++;} else

	/*
	 * Get first field - can be either "year", "opt", a month name, or
	 * a (complete) numeric date spec
         */

	cp = *(pword = words);

	/*
	 * If field begins with alpha, look for one of the following:
	 * "year", "opt", or month name
	 */
	if (isalpha(*cp)) {

		/* Check for "year" line */

		if (ci_strcmp(cp, YEAR) == 0) {
			if ((cp = *++pword) != NULL && (yy = atoi(cp)) > 0) {
				if (yy < 100)
					yy += 1900;
				curr_year = yy;
				return PARSE_OK;
			}
			return PARSE_INVDATE;	/* year missing or invalid */
		}

		/* Check for "opt" line */

		if (ci_strncmp(cp, OPT, strlen(OPT)) == 0) {
		 	if (!get_args(words, OTHER_FLAGS, FALSE)) {
				usage();
				exit(EXIT_FAILURE);
			}
			return PARSE_OK;
		}

		/* Check for month name */

		for (mm = JAN;
		     mm <= DEC && ci_strncmp(cp, months[mm-1], MIN_MONTH_LEN);
		     mm++)
			;

		if (mm > DEC)		/* not a month - reject line */
			return PARSE_INVLINE;

		/* month found, get and validate day */

		if ((cp = *++pword) == NULL || (dd = atoi(cp)) == 0)
			return PARSE_INVDATE;
		is_holiday = cp[strlen(cp) - 1] == '*';	/* look for holiday flag */

		return enter_day_info(mm, dd, curr_year, is_holiday, ++pword);
	}

	/*
	 * Not alpha month, try numeric date (parse completely in case year
	 * has changed)
	 */

	if (isdigit(*cp)) {

		is_holiday = cp[strlen(cp) - 1] == '*';	/* look for holiday flag */
 
		/* extract month and day fields */

		mm = atoi(cp);
		SKIP_FIELD(cp);

		dd = atoi(cp);
		SKIP_FIELD(cp);

		/* Numeric dates may (or may not) have a year */

		if ((yy = atoi(cp)) > 0) {
			if (yy < 100)
				yy += 1900;
			curr_year = yy; /* if present, reset current year */
		}

		return enter_day_info(mm, dd, curr_year, is_holiday, ++pword);
	}

	return PARSE_INVLINE;		/* line not recognized */
}


/*
 * getline - read next non-null line of input file into lbuf; return 0 on EOF
 */
int getline(dfp, pline)
	FILE *dfp;
	int *pline;
{
	register char *cp;
	register int c;
	int in_comment;		/* comments: from '#' to end-of-line */

	cp = lbuf;
	do {
		in_comment = FALSE;
		while ((c = getc(dfp)) != '\n' && c != EOF) {
			if (c == '#')
				in_comment = TRUE;

			/* ignore comments and leading white space */
			if (in_comment ||
			    (cp == lbuf && (c == ' ' || c == '\t')))
				continue;
			*cp++ = c;
		}
		if (c == EOF)
			return FALSE;

		(*pline)++;	/* bump line number */

	} while (cp == lbuf);	/* ignore empty lines */

	*cp = '\0';
	return TRUE;
}


/*
 * read_datefile - read and parse date file, handling preprocessor lines
 */
read_datefile(fp, filename)
	FILE *fp;		/* file pointer (assumed open) */
	char *filename;		/* file name (for error messages) */
{
	static int file_level = 0;
	int if_level = 0;
	int restart_level = 0;
	int line = 0;
	int processing = TRUE;

	int pptype, extra, ntokens, save_year;
	int (*pfcn)();
	char *ptok;
	char **pword;
	char msg[STRSIZ], incpath[STRSIZ];

#define ERR(errmsg)	FPR(stderr, "%s: %s in file %s, line %d\n", \
			progname, errmsg, filename, line);

	if (fp == NULL)		/* whoops, no date file */
		return;

	if (++file_level > MAX_NESTING) {
		ERR("maximum file nesting level exceeded");
		exit(EXIT_FAILURE);
	}

	save_year = curr_year;		/* save default year */

	/* read lines until EOF */

	while (getline(fp, &line)) {

		ntokens = loadwords();		/* split line into tokens */
		pword = words;			/* point to the first */

		/* get token type and pointers to function and name */

		pptype = pp_token(*pword++);
		pfcn = pp_info[pptype].pfcn;
		ptok = pp_info[pptype].token;

		switch (pptype) {

		case PP_DEFINE:
		case PP_UNDEF:
			if (processing)
				(*pfcn)(*pword);
			extra = ntokens > 2;
			break;

		case PP_ELSE:
			if (if_level < 1) {
				ERR("unmatched \"else\"");
				break;
			}

			if (processing) {
				processing = FALSE;	/* disable processing */
				restart_level = if_level;
			} else if (if_level == restart_level) {
				processing = TRUE;	/* re-enable processing */
				restart_level = 0;
			}
			extra = ntokens > 1;
			break;

		case PP_ENDIF:
			if (if_level < 1) {
				ERR("unmatched \"end\"");
				break;
			}

			if (! processing && if_level == restart_level) {
				processing = TRUE;	/* re-enable processing */
				restart_level = 0;
			}
			if_level--;
			extra = ntokens > 1;
			break;

		case PP_IFDEF:
		case PP_IFNDEF:
			if_level++;
			if (processing) {
				if (! (*pfcn)(*pword)) {
					processing = FALSE;
					restart_level = if_level;
				}
			}
			extra = ntokens > 2;
			break;

		case PP_INCLUDE:
			if (processing)
				do_include(mk_path(incpath, filename), *pword);
			extra = ntokens > 2;
			break;

		case PP_OTHER:	/* none of the above - parse as date */
			if (processing) {
				switch (parse()) {

				case PARSE_INVDATE:
					ERR("invalid date");
					break;

				case PARSE_INVLINE:
					ERR("unrecognized line");
					break;

				}
			}
			extra = FALSE;
			break;

		} /* end switch */

		if (extra) {		/* extraneous data? */
			sprintf(msg, "extraneous data on \"%s\" line", ptok);
			ERR(msg);
		}

	} /* end while */

	if (if_level > 0)
		FPR(stderr, "%s: unterminated if{n}def..{else..}endif in %s\n",
			progname, filename);

	file_level--;
	curr_year = save_year;		/* restore default year */
}

/*
 * Routines to extract and print data
 */


/*
 * Browse through the data structure looking for day or holiday text in 
 * specified month/year
 */
find_daytext(month, year, is_holiday)
	int month, year;
	int is_holiday;
{
	register int day;
	year_info *py;
	month_info *pm;
	register day_info *pd;
	char *fcn = is_holiday ? "holidaytext" : "daytext";
	int first;

	/* if no text for this year and month, return */

	if ((py = find_year(year, FALSE)) == NULL ||
	    (pm = py->month[month-1]) == NULL ||
	    is_holiday && pm->holidays == 0)
		return;

	/* walk array of day text pointers and linked lists of text */

	for (day = 1; day <= 31; day++) {
		for (pd = pm->day[day-1], first = TRUE;
		     pd;
		     pd = pd->next) {
			if (pd->is_holiday != is_holiday)
				continue;
			if (first)
				PRT("%d [ \n", day);	/* set up call */
			else
				PRT("(.p)\n");		/* separate text */
			print_text(pd->text);
			first = FALSE;
		}
		if (! first)		/* wrap up call (if one made) */
			PRT("] %s\n", fcn);
	}
}


/*
 * Browse through the date file looking for holidays in specified month/year
 */
find_holidays(month, year)
	int month, year;
{
	register int day;
	register unsigned long holidays;
	year_info *py;
	month_info *pm;

	pm = (py = find_year(year, FALSE)) ? py->month[month-1] : NULL;

	PRT("/holidays [");	/* start definition of list */

	for (holidays = pm ? pm->holidays : 0, day = 1;
	     holidays;
	     holidays >>= 1, day++)
		if (holidays & 01)
			PRT(" %d", day);

	PRT(" 99 ] def\n");	/* terminate with dummy entry */
}


/*
 * pmonth - generate calendar for specified month/year
 */
pmonth(month, year)
	int month, year;
{

	PRT("/year %d def\n", year);		/* set up year and month */
	PRT("/month %d def\n", month);
	find_holidays(month, year);		/* make list of holidays */
	PRT("printmonth\n");
	find_daytext(month, year, TRUE);	/* add holiday text to boxes */
	find_daytext(month, year, FALSE);	/* add non-holiday text to boxes */
	PRT("showpage\n");
}


/*
 * Routines dealing with translation of file specifications (VMS, Un*x)
 */

#ifdef VMS
/*
 * mk_path - extract the path component from VMS file spec
 */
char *mk_path(path, filespec)
	char *path;		/* output path */
	char *filespec;		/* input filespec */
{
	char *p;

	strcpy(path, filespec);
	if (!(p = strchr(path, ']')) && !(p = strchr(path, ':')))
		p = path - 1;	/* return null string if no path */
	*++p = '\0';

	return path;
}


/*
 * mk_filespec - merge VMS path and file names, where latter can be relative
 */

char *mk_filespec(filespec, path, name)
	char *filespec;		/* output filespec */
	char *path;		/* input path */
	char *name;		/* input file name */
{
	char *p;

	*filespec = '\0';

	/* copy name intact if absolute; else merge path and relative name */
	if (!strchr(name, ':')) {
		strcpy(filespec, path);
		if ((p = LASTCHAR(filespec)) && *p == END_PATH &&
		    name[0] == START_PATH && strchr(".-", name[1]))
			*p = *++name == '-' ? '.' : '\0';
	}

	return strcat(filespec, name);
}


/*
 * trnlog - return translation of VMS logical name (null if missing)
 */
char *trnlog(logname)	/* look up logical name */
    char *logname;
{
static char trnbuf[STRSIZ];

$DESCRIPTOR(src, logname);
$DESCRIPTOR(dst, trnbuf);
short len;
int ret;

src.dsc$w_length  = strlen(logname);
ret = LIB$SYS_TRNLOG(&src, &len, &dst);
return ret == SS$_NORMAL ? trnbuf[len] = '\0', trnbuf : 0;
}

#else

/*
 * mk_path - extract the path component from a Un*x file spec
 */
char *mk_path(path, filespec)
	char *path;		/* output path */
	char *filespec;		/* input filespec */
{
	char *p;

	strcpy(path, filespec);
	if (! (p = strrchr(path, END_PATH)) )
		p = path - 1;	/* return null string if no path */

	*++p = '\0';
	return path;
}


/*
 * mk_filespec - merge Un*x path and file names, where latter can be relative
 */

char *mk_filespec(filespec, path, name)
	char *filespec;		/* output filespec */
	char *path;		/* input path */
	char *name;		/* input file name */
{
	char *p;

	*filespec = '\0';

	/* copy name intact if absolute; else merge path and relative name */

	/* if path starts with "~/", translate it for user */
	if (strncmp(name, "~/", 2) == 0 && (p = trnlog(HOME_DIR)) != NULL) {
		strcpy(filespec, p);
		if ((p = LASTCHAR(filespec)) && *p != END_PATH)
			*++p = END_PATH, *++p = '\0';
		name += 2;		/* skip "~/" */
	}
	else if (*name != START_PATH) {		/* relative path */
		strcpy(filespec, path);
		if ((p = LASTCHAR(filespec)) && *p != END_PATH)
			*++p = END_PATH, *++p = '\0';
	}

	return strcat(filespec, name);
}


/*
 * trnlog - return translation of Un*x environment variable
 */
char *trnlog(logname)	/* look up logical name */
    char *logname;
{
return getenv(logname);
}

#endif
SHAR_EOF
fi
if test -f 'pcal.hlp'
then
	echo shar: will not over-write existing file 'pcal.hlp'
else
cat << \SHAR_EOF > 'pcal.hlp'
1 PCAL
        Pcal  generates  PostScript  to produce landscape or  portrait
    orientated calendars for  any  month  and  year.  The defaults for
    month and year are the current month and year.

        VMS Version
        Execution format:
    
            pcal [options] [mm yy] [n]

        If a file named CALENDAR.DAT  resides  in  the  caller's  home
    directory (or in a directory defined by the logical name PCAL_DIR,
    if it exists), it will be  searched for  lines with  leading dates
    matching the requested  month  and year (current by default).  Any
    text following the date  will be printed on the calendar under the
    appropriate day of the month.   Dates in the CALENDAR.DAT file may
    consist  of  a  numeric or alpha  month  (at  least  the  first  3
    characters  for  month  names)  followed  by  a  numeric  day  and
    optionally  followed  by  a year.  Any non-numeric  character  may
    separate numeric dates.  Holidays may be flagged by  following the
    date immediately with '*';  this will cause the date to be printed
    in gray.  Lines in the CALENDAR.DAT file consisting of "year xxxx"
    (where  xxxx  is a numeric year) can be used to set the  year  for
    following entries.  This assumes that the following entries do not
    contain a year;  any date entries containing year information will
    set  the  remembered  year  to  that  year.  Lines  consisting  of
    "opt <options>" can be used to  override defaults for all command-
    line flags except -e, -f, -D, and -U; any flags set in this manner
    are themselves  overridden by  flags specified  explicitly  on the
    command line.  Comments ('#' through  end-of-line) are  permitted.

    Example:

	# sample CALENDAR.DAT file - Raytheon holidays and exempt paydays, 1990

	opt -t Helvetica-Bold -d Helvetica-Bold	# override default fonts

	year 1990				# set default year

	7/4* Independence Day			# '*' flags 7/4 as holiday
	7/12/90 Exempt payday			# full numeric date
	Aug 16 Exempt payday			# alternate date format
	9/3* Labor Day
	9/20 Exempt payday
	10/8* Columbus Day (observed)
	10/25 Exempt payday
	11/20 Exempt payday
	11/22* Thanksgiving
	11/23*
	12/13 Exempt payday
	12/24*
	12/25* Christmas

    Release 2.1 of pcal adds rudimentary cpp-like functionality to the
    date file, supporting define|undef, if{n}def ... {else ...} endif,
    and include:

	define meetings

	ifdef meetings
	  include meetings.dat
	  undef meetings
	endif
	
    Symbol names are case-insensitive.  It is  not an error to "undef"
    an undefined symbol, nor to "define" a previously-defined one.

    "ifdef" alone is always false; "ifndef" alone is always true.

    The name of the file in the "include" directive may  optionally be
    surrounded  by either "" or <>, both of which are ignored.  If the
    name is not  an absolute  path, it is taken to be relative  to the
    directory where the file containing the directive is located.

    Release 2.1 also  looks  for global  symbol PCAL_OPTS; if defined,
    its contents are parsed as command-line flags.  These override the
    program  defaults,  but are overridden  by any specified via "opt"
    lines in  the date file or on the command line.  Example:

	$ define PCAL_OPTS "-n Helvetica -D meetings"	  ! login.com

	$ pcal -"U" meetings 9 90	! un-define symbol at runtime

2 parameters
  mm yy n
        "mm" and "yy"  are numeric values of the month (1-12) and year
    (0-99) (i.e., July 1990  would  be 7 90).  If you just include the
    "yy" option, an entire 12  months  of calendars will be generated.
    A specific month can be produced  by including the "mm" parameter.
    The  "n"  parameter will  produce the "n"  consecutive  months  of
    calendars starting with the requested month.

        The following flags  may be specified  (in increasing order of
    precedence) in global symbol PCAL_OPTS, in "opt" lines in the date
    file (all but -e, -f, -D, -U), or on the command  line.  Any  flag
    which  normally  takes  an argument  may be specified  without the
    argument;  this resets  its  value  to the  program  default.  (-D
    alone thus clears all defined symbols; -U alone has no effect.)

	The '-' flag has  been added to  disambiguate  cases  where an
    argument-less flag has been specified immediately before a numeric
    parameter:

	$ pcal -t - 9 90

2 -e
        Print an  empty  calendar (i.e., do not  print entries  from a
    CALENDAR.DAT file.)

2 -f <FILE>
        Directs pcal to use the  file name <FILE> as the input file in
    place  of the  default  CALENDAR.DAT  file  in  the  callers  home
    directory or in the  directory specified by logical name PCAL_DIR.

2 -o <FILE>
        Directs  pcal to  write  the  PostScript  calendar  into  FILE
    (default: CALENDAR.PS in the current directory.)

2 -l
        This will cause the  output  to  come  out  in  landscape mode
    (default).

2 -p
        This will cause  the  output  to  come  out  in  portrait mode
    instead of landscape mode.

2 -b <DAY> | all
        This  will cause  all dates  on weekday  DAY to be  printed in 
    black;  "-b all" causes  all dates  to be printed  in black unless
    explicitly flagged as a holiday.

2 -g <DAY> | all
        This  will cause  all dates  on weekday  DAY to be  printed in 
    gray; "-g all" causes all dates to be printed in gray.  Default is
    to print Saturdays  and Sundays in gray and  other dates in black.

2 -t <FONT>
        This option can  be used to change the font  used to print the
    title (i.e. $ pcal -t "Times-Roman").  The default  is Times-Bold.

2 -d <FONT>
        This option is the same as  -t  except  that  the font used to
    print the  day  numbers is  changed.  The  default is  Times-Bold.

2 -n <FONT>
        This option is the same as  -n  except  that  the font used to
    print the notes in the calendar boxes is  changed.  The default is
    Helvetica-Narrow.

2 -"L" <STRING>
        This will cause  STRING to be  printed as  a left footer.

2 -"C" <STRING>
        This will cause STRING to be printed as a center footer.

2 -"R" <STRING>
        This will cause STRING to be printed as a right footer.

2 -"D" <SYM>
	This will  define symbol  SYM prior to  reading the date file;
    -D alone clears all defined symbols.

2 -"U" <SYM>
	This will undefine symbol  SYM prior to reading the date file.

2 CREDITS
        The original PostScript code  to  generate  the  calendars was
    written by Patrick Wood (Copywrite  (c)  1987  by  Patrick Wood of
    Pipeline  Associates, Inc.), and authorized for  modification  and
    redistribution.    The  CALENDAR.DAT  file  inclusion  code    was
    originally written in "bs(1)" by Bill Vogel of  AT&T.    Patrick's
    original  PostScript  was  modified  and enhanced several times by
    others  whose names  have  regrettably been lost.  Ken Keirnan  of
    Pacific Bell assembled the original "C" version upon which this is
    based;  additional modifications and enhancements were the work of
    Joseph P. Larson, Ed Hand, Andrew W. Rogers,  Mark Kantrowitz, Joe
    Brownlee, and Eric  Hammond.  This  VMS  HELP file was  written by
    Richard Dyson and updated by Andrew W. Rogers.
SHAR_EOF
fi
if test -f 'pcal.man'
then
	echo shar: will not over-write existing file 'pcal.man'
else
cat << \SHAR_EOF > 'pcal.man'
.TH PCAL 1
.SH NAME
pcal \- generate PostScript calendars
.SH SYNOPSIS
.B pcal
[
.BR \-e
|
.BR \-f
<cal>
]
[
.BR \-o
<file>
]
[
.BR \-l
|
.BR \-p
]
[
.BR \-b
<day>
|
all
]*
[
.BR \-g
<day>
|
all
]*
[
.B -t
<titlefont name>
]
[
.B -d
<dayfont name>
]
[
.B -n
<notesfont name>
]
[
.B -L
<foot string>
]
[
.B -C
<foot string>
]
[
.B -R
<foot string>
]
[
.B -D
<symbol>
]
[
.B -U
<symbol>
]
[
.B month
] [
.B year
] [
.B nmonths
]
.SH DESCRIPTION
.I Pcal
generates PostScript to produce landscape or portrait calendars for any 
month and year. The arguments
.BR month ,
.BR year ,
and
.BR nmonths ,
if provided, should be numeric. The month should be in the range 1 - 12,
and year should be specified as 1 or 2 digits or as the full 4 digit year.
.P
If no numeric arguments are provided, the current month and year will be
generated.
.P
If one numeric argument is provided, it is interpreted as the year; the
entire year will be generated. Otherwise, 
.I nmonth
months, starting with
.I month
and
.I year,
will be generated.
.PP
If a file named
.I \.calendar
or
.I calendar
(for compatibility with older versions)
exists in the caller's home directory (or in the directory pointed to by
environment variable PCAL_DIR), it will be searched for lines with
leading dates matching the requested month and year (current by default).
Any text following the date will be printed on the calendar under the
appropriate day of the month. Dates in the
.I \.calendar
file may consist of a numeric or alpha month (at least the first 3 characters
for month names) followed by a numeric day and optionally followed by a
year. Any non-numeric character may separate numeric dates. Holidays may
be flagged by following the date immediately with '*'; this will cause the
date to be printed in gray.
.PP
Lines in the
.I \.calendar
file consisting of "year xxxx" (where xxxx is a numeric year) can be used
to set the year for following entries. This assumes that the following
entries do not contain a year; any date entries containing year information
will set the remembered year to that year.
.PP
Lines in the
.I \.calendar
file consisting of "opt <options>" can be used to override the defaults for
any command-line flags except -e, -f, -D, and -U. Any flags specified in this
manner
are, in turn, overridden by those specified explicitly on the command line.
.PP
Comments ('#' through end-of-line) are supported.
.PP
Release 2.1 of pcal adds rudimentary cpp-like functionality to the
date file, supporting 
.BR define |
.BR undef ,
.B if{n}def ... {else ...} endif,
and
.BR include.
Note that these are not preceded by '#' as they are in C.
.PP	
Symbol names are case-insensitive. It is not an error to "undef"
an undefined symbol, nor to "define" a previously-defined one.
.PP
"ifdef" alone is always false; "ifndef" alone is always true.
.PP
The name of the file in the "include" directive may optionally be
surrounded by either "" or <>, both of which are ignored. If the
name is not an absolute path, it is taken to be relative to the
directory where the file containing the directive is located.
.I Pcal
is smart enough to translate "~/" to the user's home directory.
.PP
.I Pcal
has many options:
.P
.TP
.B \-e
Print an empty calendar. Do not print entries from a calendar file.
.TP
.B \-f <cal>
Directs
.I pcal
to use the file name <cal> as the input file in place of the default
.I \.calendar
file in the callers home directory (or directory pointed to
by PCAL_DIR).
.TP
.B \-o <file>
Directs
.I pcal
to write the output to <file> instead of to stdout.
.TP
.B \-l
This will cause the output to come out in landscape mode (default).
.TP
.B \-p
This will cause the output to come out in portrait mode.
.TP
.B \-b <day> | all
This will cause all dates falling on weekday <day> to be printed in black;
"-b all" causes all weekdays to be printed in black.
.TP
.B \-g <day> | all
This will cause all dates falling on weekday <day> to be printed in gray;
"-g all" causes all weekdays to be printed in gray.
.IP
(The default for the -b and -g flags is to print Saturdays and Sundays in
gray and other days - unless flagged as holidays - in black.)
.TP
.B \-t <titlefont name>
This option can be used to change the font the title
is printed in. (ie. pcal -t Times-Roman).
.TP
.B \-d <dayfont name>
This option is the same as -t except that the font used
to print the day numbers is changed.
.TP
.B \-n <notesfont name>
This option is the same as -n except that the font used
to print the notes in the calendar boxes is changed.
.TP
.B \-D <symbol>
This option defines the named symbol prior to reading the date file.
.TP
.B \-U <symbol>
This option un-defines the named symbol prior to reading the date file.
.TP
.B \-L <string>
This option causes the accompanying string to be printed as a left footer.
.TP
.B \-C <string>
This option causes the accompanying string to be printed as a center footer.
.TP
.B \-R <string>
This option causes the accompanying string to be printed as a right footer.
.IP
Any option which normally takes an argument may be specified without
the argument in order to reset the value to the program default.
.B \-D
alone clears all the defined symbols;
.B \-U
alone has no effect.  The
.B \-
(or
.BR \-\- ,
System V people)
flag may be used to disambiguate cases such as "pcal -t 9 90"; this
could be written instead as "pcal -t - 9 90" or "pcal -t -- 9 90".
.IP
If the environment variable PCAL_OPTS is defined, its contents are
parsed as a command line.  Flags set via PCAL_OPTS override the
program defaults, but are overridden by flags set via "opt" lines
in the date file or explicitly on the command line.
.SH SEE ALSO
cal(1)
.SH CAVEATS
The original PostScript code to generate the calendars was written by
Patrick Wood (Copywrite (c) 1987 by Patrick Wood of Pipeline Associates,
Inc.), and authorized for modification and redistribution. The calendar
file inclusion code was originally written in "bs(1)" by Bill Vogel of
AT&T. Patrick's original PostScript was modified and enhanced several
times by others whos names have regrettably been lost. Ken Keirnan of
Pacific Bell assembled the original "C" version upon which this is based;
additional modifications and enhancements were the work of Joseph P.
Larson, Ed Hand, Andrew W. Rogers, Mark Kantrowitz, Joe Brownlee, and
Eric Hammond.
SHAR_EOF
fi
if test -f 'pcalinit.h'
then
	echo shar: will not over-write existing file 'pcalinit.h'
else
cat << \SHAR_EOF > 'pcalinit.h'
/*
 * pcalinit.h - provides the PostScript routines for v2.1 of pcal.c
 *
 * 2.1	modified by Mark Kantrowitz:
 *
 *	use symbolic names instead of magic numbers throughout
 *	support -L, -C, -R, -n options (all new)
 *	print holiday text in otherwise-wasted space next to date
 *	use larger text for dates in large calendars
 *
 * 2.0	modified by Andrew W. Rogers:
 *
 *	skip printing days of week on small calendars
 *	center month and year at top of calendar
 *	use correct algorithm for leap year calculation
 *	get month and day names from main program
 *	use table to determine color (black/gray) of weekdays and holidays
 *      use hanging indent to print continued text lines
 *
 */

char *header_1[] = {
  "/titlefontsize 48 def",
  "/weekdayfontsize 12 def",
  "/footfontsize 12 def",
  "/datefontsize 30 def",
  "/savedatefontsize datefontsize def",
  "/notesfontsize 6 def",
  "/daywidth 100 def",
  "/dayheight 80 def",
  "/negdaywidth 0 daywidth sub def",
  "/negdayheight 0 dayheight sub def",
  "/prtnum { 3 string cvs show} def",
  "/drawgrid {\t\t% draw calendar boxes",
  "\tdayfont findfont weekdayfontsize scalefont setfont",
  "\t0 1 6 {",
  "\t\t/i exch def",
  "\t\tsubmonth 0 eq {",
  "\t\t\ti daywidth mul 40 moveto",
  "\t\t\tday_names i get",
  "\t\t\tdaywidth center",
  "\t\t} if",
  "\t\ti daywidth mul 35 moveto",
  "\t\t1.0 setlinewidth",
  "\t\t0 1 5 {",
  "\t\t\tgsave",
  "\t\t\tdaywidth 0 rlineto ",
  "\t\t\t0 negdayheight rlineto",
  "\t\t\tnegdaywidth 0 rlineto",
  "\t\t\tclosepath stroke",
  "\t\t\tgrestore",
  "\t\t\t0 negdayheight rmoveto",
  "\t\t pop } for",
  "\t} for",
  "} def",
  "/drawnums {\t\t% place day numbers on calendar",
  "\tdayfont findfont datefontsize scalefont setfont",
  "\t/start startday def",
  "\t/days ndays def",
  "\t/fontdiff datefontsize savedatefontsize sub def",
  "\t/n 0 def",
  "\tstart daywidth mul 5 add 10 fontdiff sub rmoveto",
  "\t1 1 days {",
  "\t\t/day exch def",
  "\t\tgsave",
  "\t\tsubmonth 0 eq {",
  "\t\t\t/gray day_gray day start add 1 sub 7 mod get def",
  "\t\t\tday holidays n get eq {",
  "\t\t\t\t/gray holiday_gray def",
  "\t\t\t\t/n n 1 add def",
  "\t\t\t} if",
  "\t\t\tgray {",
  "\t\t\t\t0.8 setgray",
  "\t\t\t} if",
  "\t\t} if",
  "\t\tday prtnum",
  "\t\tgrestore",
  "\t\tday start add 7 mod 0 eq",
  "\t\t{",
  "\t\t\tcurrentpoint exch pop dayheight sub 5 exch moveto",
  "\t\t}",
  "\t\t{",
  "\t\t\tdaywidth 0 rmoveto",
  "\t\t} ifelse",
  "\t} for",
  "} def",
  "/drawfill {\t\t% place fill squares on calendar",
  "\t/start startday def",
  "\t/days ndays def",
  "\t0 35 rmoveto",
  "\t1.0 setlinewidth",
  "\t0 1 start 1 sub {",
  "\t\tgsave",
  "\t\t.9 setgray",
  "\t\tdaywidth 0 rlineto ",
  "\t\t0 negdayheight rlineto",
  "\t\tnegdaywidth 0 rlineto",
  "\t\tclosepath fill",
  "\t\tgrestore",
  "\t\tdaywidth 0 rmoveto",
  "\tpop } for",
  "\tsubmonth 1 eq",
  "\t{",
  "\t\t/lastday 42 def",
  "\t\t600 -365 moveto",
  "\t}",
  "\t{",
  "\t\t/lastday 40 def",
  "\t\t400 -365 moveto",
  "\t} ifelse",
  "\tlastday -1 ndays start 1 add add",
  "\t{",
  "\t\t/day exch def",
  "\t\tgsave",
  "\t\t.9 setgray",
  "\t\tdaywidth 0 rlineto ",
  "\t\t0 negdayheight rlineto",
  "\t\tnegdaywidth 0 rlineto",
  "\t\tclosepath fill",
  "\t\tgrestore",
  "\t\tday 7 mod 1 eq",
  "\t\t{",
  "\t\t\t600 -365 dayheight add moveto",
  "\t\t}",
  "\t\t{",
  "\t\t\tnegdaywidth 0 rmoveto",
  "\t\t} ifelse",
  "\t} for",
  "} def",
  "/isleap {\t\t% is this a leap year?",
  "\tyear 4 mod 0 eq\t\t% multiple of 4",
  "\tyear 100 mod 0 ne \t% not century",
  "\tyear 400 mod 0 eq or and\t% or divisible by 400",
  "} def",
  "/days_month [ 31 28 31 30 31 30 31 31 30 31 30 31 ] def",
  "/ndays {\t\t% number of days in this month",
  "\tdays_month month 1 sub get",
  "\tmonth 2 eq\t% Feb",
  "\tisleap and",
  "\t{",
  "\t\t1 add",
  "\t} if",
  "} def",
  "/startday {\t\t% starting day-of-week for this month",
  "\t/off year 2000 sub def\t% offset from start of epoch",
  "\toff",
  "\toff 4 idiv add\t\t% number of leap years",
  "\toff 100 idiv sub\t% number of centuries",
  "\toff 400 idiv add\t% number of years divisible by 400",
  "\t6 add 7 mod 7 add \t% offset from Jan 1 2000",
  "\t/off exch def",
  "\t1 1 month 1 sub {",
  "\t\t/idx exch def",
  "\t\tdays_month idx 1 sub get",
  "\t\tidx 2 eq",
  "\t\tisleap and",
  "\t\t{",
  "\t\t\t1 add",
  "\t\t} if",
  "\t\t/off exch off add def",
  "\t} for",
  "\toff 7 mod\t\t% 0--Sunday, 1--monday, etc.",
  "} def",
  "/center {\t\t% center string in given width",
  "\t/width exch def",
  "\t/str exch def width str ",
  "\tstringwidth pop sub 2 div 0 rmoveto str show",
  "} def",
  "/strcat {\t\t% concatenate two strings",
  "\t2 copy",
  "\tlength exch length",
  "\tdup 3 -1 roll add",
  "\tstring",
  "\tdup 0 6 -1 roll putinterval",
  "\tdup 3 -1 roll 4 -1 roll putinterval",
  "} def",
  "/calendar",
  "{",
  "\ttitlefont findfont titlefontsize scalefont setfont",
  "\t/month_name month_names month 1 sub get def",
  "\t/yearstring year 10 string cvs def",
  "\t0 60 moveto",
  "\tmonth_name (  ) strcat yearstring strcat 700 center",
  "\tsubmonth 0 eq {",
  "\t\ttitlefont findfont footfontsize scalefont setfont",
  "\t\t/bottomrow { dayheight 6 mul 20 sub neg } def",
  "\t\t0 bottomrow moveto",
  "\t\tLfootstring show",
  "\t\tdaywidth 7 mul Rfootstring stringwidth pop sub",
  "\t\tbottomrow moveto",
  "\t\tRfootstring show",
  "\t\tdaywidth 7 mul Cfootstring stringwidth pop sub 2 div",
  "\t\tbottomrow moveto",
  "\t\tCfootstring show",
  "\t} if",
  "\t0 0 moveto",
  "\tdrawnums",
  "\t0 0 moveto",
  "\tdrawfill",
  "\t0 0 moveto",
  "\tdrawgrid",
  "} def",
  "/daytext {",
  "\tnotesfont findfont notesfontsize scalefont setfont",
  "\t/mytext\texch def /myday exch def",
  "\tstartday myday 1 sub add dup 7 mod daywidth mul 5 add % gives column",
  "\texch 7 idiv negdayheight mul % gives row",
  "\tdup /ypos exch def moveto",
  "\t/LM currentpoint pop def /RM LM 95 add def",
  "        mytext { dup (.p) eq { crlf pop} {prstr ( ) show} ifelse } forall",
  "} def",
  "/holidaytext {",
  "\tnotesfont findfont notesfontsize scalefont setfont",
  "\t/mytext\texch def /myday exch def",
  "\tstartday myday 1 sub add dup 7 mod daywidth mul",
  "\tmyday 10 lt {25} {40} ifelse add % gives column",
  "\texch 7 idiv negdayheight mul 27 add % gives row",
  "\tdup /ypos exch def moveto",
  "\t/LM currentpoint pop def /RM LM myday 10 lt {75} {60} ifelse add def",
  "        mytext { dup (.p) eq { crlf pop} {prstr ( ) show} ifelse } forall",
  "} def",
  "/crlf {",
  "    ypos 7 sub /ypos exch def LM ypos moveto",
  "} def",
  "/prstr {",
  "    dup stringwidth pop currentpoint pop",
  "    add RM gt {crlf (   ) show} if show",
  "} def",
  "/printmonth {",
   (char *)0,
 };
 
 char *header_2[] = {
  "\t/submonth 0 def",
  "\tcalendar",
  "\tmonth 1 sub 0 eq",
  "\t{",
  "\t\t/lmonth 12 def",
  "\t\t/lyear year 1 sub def",
  "\t}",
  "\t{",
  "\t\t/lmonth month 1 sub def",
  "\t\t/lyear year def",
  "\t} ifelse",
  "\tmonth 1 add 13 eq",
  "\t{",
  "\t\t/nmonth 1 def",
  "\t\t/nyear year 1 add def",
  "\t} ",
  "\t{",
  "\t\t/nmonth month 1 add def",
  "\t\t/nyear year def",
  "\t} ifelse",
  "\t/savemonth month def",
  "\t/saveyear year def",
  "\t/submonth 1 def",
  "\t/savedatefontsize datefontsize def",
  "\t/year lyear def",
  "\t/month lmonth def",
  "\t/datefontsize 48 def",
  "\tgsave",
  "\t500 -365 translate",
  "\tgsave",
  "\t.138 .138 scale",
  "\t10 -120 translate",
  "\tcalendar",
  "\tgrestore",
  "\t/submonth 1 def",
  "\t/year nyear def",
  "\t/month nmonth def",
  "\t100 0 translate",
  "\tgsave",
  "\t.138 .138 scale",
  "\t10 -120 translate",
  "\tcalendar",
  "\tgrestore",
  "\t/month savemonth def",
  "\t/year saveyear def",
  "\t/submonth 0 def",
  "\t/datefontsize savedatefontsize def",
  "\tgrestore",
  "} def",
  (char *)0,
};
SHAR_EOF
fi
exit 0
#	End of shell archive