[alt.sources] Pcal v4.0, part 4 of 5

jbr0@cbnews.att.com (joseph.a.brownlee) (03/14/91)

#!/bin/sh
# This is part 04 of a multipart archive
# ============= pcalinit.c ==============
if test -f 'pcalinit.c' -a X"$1" != X"-c"; then
	echo 'x - skipping pcalinit.c (File already exists)'
else
echo 'x - extracting pcalinit.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'pcalinit.c' &&
/*
X * Create a .h file from a .ps file.  Strips out leading and trailing 
X * whitespace, blank lines, and lines consisting solely of comments,
X * except for the very first block of comments/blanklines, which are
X * turned into C comments at the top of the file.
X * 
X *   14-sep-90  Jamie Zawinski  created.
X *
X * Revision history:
X *
X *	4.0	AWR	02/25/91	added optional third argument for
X *					name of array
X *
X *		AWR	02/19/91	added function prototypes; documented
X *
X *		AWR	01/16/91	Escape " and \ in quoted strings;
X *					strip trailing comments; skip FF
X *
X *	2.6	JAB	10/18/90	Add exit(0).
X *
X *	2.3	JWZ	09/14/90	Author
X */
X
#include <stdio.h>
#include <ctype.h>
#include <string.h>
X
#if defined(__STDC__) || defined(AMIGA)
#define PROTOS
#endif
X
#define FALSE	0
#define TRUE	1
X
#define ARRAY_NAME	"header"	/* default name of array in .h file */
X
#define IS_WHITESPACE(c) \
X	((c) == ' ' || (c) == '\t' || (c) == '\n' || c == '\f')
X
#define IS_POSTSCRIPT(s)	((s)[0] != '%' && (s)[0] != '\0')
X
X
/*
X * strip_white: strip leading and trailing whitespace from 'string'; return
X * pointer to first non-whitespace character
X */
#ifdef PROTOS
char *strip_white (char *string)
#else
char *strip_white (string)
X     char *string;
#endif
{
X    int n;
X    for (; IS_WHITESPACE(*string); string++)
X	;
X    n = strlen(string)-1;
X    for (; IS_WHITESPACE(string[n]); n--)
X	string[n] = '\0';
X    return string;
}
X
X
/*
X * strip_comment: strip comment and any preceding whitespace from 'string';
X * return pointer to 'string'
X */
#ifdef PROTOS
char *strip_comment (char *string)
#else
char *strip_comment (string)
X     char *string;
#endif
{
X    char *p;
X    if ((p = strchr(string, '%')) != NULL) {
X	*p = '\0';
X	string = strip_white(string);
X    }
X    return string;
}
X 
X
/*
X * escape: copy string 'in' to string 'out', escaping the characters \ and ";
X * return pointer to 'out'
X */
#ifdef PROTOS
char *escape(char *out, char *in)
#else
char *escape(out, in)
X     char *out, *in;
#endif
{
X   char c, *sv_out = out;
X
X   for (; c = *in; *out++ = *in++)
X       if (c == '\\' || c == '"')
X	  *out++ = '\\';
X
X   *out = '\0';
X   return sv_out;
}
X
X
#ifdef PROTOS
int main(int argc, char *argv[])
#else
int main (argc, argv)
X     int argc; char *argv[];
#endif
{
X    FILE *in, *out;
X    char line[256], line2[512], *L, *array;
X    int in_initial_comments = TRUE;
X
X    /* retrieve arguments and attempt to open input and output files */
X
X    if (argc < 3 || argc > 4) {
X       fprintf(stderr, "usage: %s <infile>.ps <outfile>.h [<arrayname>]\n",
X		argv[0]);
X       exit(-1); }
X    
X    in = fopen(argv[1], "r");
X    if (NULL == in) {
X       fprintf(stderr, "%s: couldn't open %s\n", argv[0], argv[1]);
X       exit(-1); }
X    
X    out = fopen(argv[2], "w");
X    if (NULL == out) {
X       fprintf(stderr, "%s: couldn't open %s\n", argv[0], argv[2]);
X       exit(-1); }
X    
X    array = argc == 4 ? argv[3] : ARRAY_NAME;
X
X    /* print topline comment on output file */
X
X    fprintf (out, "/*\n * %s: automatically generated by %s from %s\n",
X       argv[2], argv[0], argv[1]);
X    fprintf (out, " *\n *\tDO NOT EDIT THIS FILE!\n *\n");
X
X    /* main loop - copy lines from input file, to output file, preserving
X     * only initial block of comments and blank lines
X     */
X
X    while ( fgets(line, 255, in) != NULL ) {
X       L = strip_white(line);			/* strip whitespace */
X
X       if ( IS_POSTSCRIPT(L) ) {		/* PostScript source? */
X	  if ( in_initial_comments ) {		/* first PS line? */
X	     in_initial_comments = FALSE;
X	     fprintf(out, " */\n\nchar *%s[] = {\n", array);
X	  }
X	  L = strip_comment(L);			/* copy string to output */
X	  L = escape(line2, L);
X	  fprintf(out, "  \"%s\",\n", L);
X       } else					/* blank or comment line */
X	  if ( in_initial_comments )		/* copy only initial block */
X	     fprintf(out, " * %s\n", L);
X    }
X
X    fprintf(out, "  (char *)0,\n};\n");		/* terminate array decl */
X
X    fclose(out);    		/* close files and exit */
X    fclose(in);
X    exit (0);
}
SHAR_EOF
chmod 0644 pcalinit.c ||
echo 'restore of pcalinit.c failed'
Wc_c="`wc -c < 'pcalinit.c'`"
test 3975 -eq "$Wc_c" ||
	echo 'pcalinit.c: original size 3975, current size' "$Wc_c"
fi
# ============= pcalinit.ps ==============
if test -f 'pcalinit.ps' -a X"$1" != X"-c"; then
	echo 'x - skipping pcalinit.ps (File already exists)'
else
echo 'x - extracting pcalinit.ps (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'pcalinit.ps' &&
% pcalinit.ps - provides the PostScript routines for pcal.c
%
% 4.0	modified by Andrew Rogers:
%
%	support -w ("whole year") option - cf. printmonth_[pl], startpage
%	moved all the calendar calculations to pcal.c and moonphas.c (q.v.)
%
%	support -B option (leave unused boxes blank)
%
%	support -O option (print "gray" numbers as outlines)
%
%	revised several of the basic routines and added some others; dates,
%	moons, text, Julian days are now relative to upper-left corner of box
%
%	enlarged title and dates in small calendars
%
% 3.0	modified by Andrew Rogers:
%
%	added xsval, ysval, xtval, ytval for scaling and translation
%	as per Ed Hand
%
%	added day-of-year support (-j, -J flags)
%
% 2.6	* no modifications *
%
% 2.5	modified by Joe Brownlee:
%
%	made day numbers smaller, providing more room for event text
%	repositioned event text and moons accordingly
%	added support for variable first day of week
%
% 2.4	* no modifications *
%
% 2.3	modified by Jamie Zawinski <jwz@lucid.com>:
%
%	merged in moon routines (originally by Mark Hanson)
%
% 2.2	modified by Joe Brownlee:
%
%	add "notetext" to print notes in unused calendar box
%
% 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
X
/SM 0 def				% definitions for calendar sizes
/MED 1 def
/LG 2 def
X
/titlefontsize   [ 60 48 48 ] def	% font sizes for SM/MED/LG calendars
/datefontsize    [ 54 40 25 ] def
/weekdayfontsize [  0 24 12 ] def
/footfontsize 12 def
/notesfontsize 6 def
X
/titlepos [ 19 40 25 ] def		% Y-offset (SM/MED/LG) of month/year title
/Y0 35 def				% Y-coordinate of calendar grid origin
X
/daywidth 100 def			% dimensions of grid boxes
/dayheight 80 def
/gridwidth daywidth 7 mul def
/gridheight dayheight 6 mul def
/negdaywidth daywidth neg def
/negdayheight dayheight neg def
/neggridwidth gridwidth neg def
/neggridheight gridheight neg def
X
/gridlinewidth 1.0 def			% width of grid lines
/charlinewidth 0.1 def			% width of outline characters
/moonlinewidth 0.1 def			% width of moon icon line
/dategray 0.8 def			% density of gray for dates
/fillgray 0.9 def			% density of gray for fill squares
X
/hangingindent (   ) def		% for indenting continued text lines
X
X
%
% Utility functions:
%
X
X
/center {		% print string centered in given width
X	/width exch def
X	/str exch def
X	width str stringwidth pop sub 2 div 0 rmoveto str show
} def
X
X
/strcat {		% concatenate two strings
X	2 copy
X	length exch length
X	dup 3 -1 roll add
X	string
X	dup 0 6 -1 roll putinterval
X	dup 3 -1 roll 4 -1 roll putinterval
} def
X
X
/prtday {		% print the date in black, gray, or outline
X	day 3 string cvs				% convert to string
X	color (outline) eq {
X		true charpath stroke			% print as outline
X	}
X	{
X		color (gray) eq { dategray setgray } if	% print in gray
X		show
X	} ifelse
} def
X
X
/nextbox {		% go to next column or start of next row
X	day startday add 7 mod 0 eq				% end of week?
X		{ neggridwidth daywidth add negdayheight rmoveto }  % next row
X		{ daywidth 0 rmoveto }				    % next col
X	ifelse
} def
X
X
/datepos {		% push coords of upper-left corner of box for day <arg>
X	startday add 1 sub dup 7 mod daywidth mul		% x-coord
X	exch 7 idiv negdayheight mul Y0 add			% y-coord
} def
X	
X
%
% Functions for drawing parts of calendar:
%
X
X
/drawtitle {		% draw month and year title
X	titlefont findfont titlefontsize calsize get scalefont setfont
X	/month_name month_names month 1 sub get def
X	/yearstring year 10 string cvs def
X	0 Y0 titlepos calsize get add moveto
X	month_name (  ) strcat yearstring strcat gridwidth center
} def
X
X
/drawdaynames {		% draw day names above respective columns
X	dayfont findfont weekdayfontsize calsize get scalefont setfont
X	0 1 6 {
X		/i exch def
X		i daywidth mul Y0 5 add moveto
X		day_names i get
X		daywidth center
X	} for
} def
X
X
/drawgrid {		% draw the grid for the calendar
X	gridlinewidth setlinewidth
X	0 daywidth gridwidth {			% vertical lines
X		Y0 moveto
X		0 neggridheight rlineto
X		stroke
X	} for
X	0 negdayheight neggridheight {		% horizontal lines
X		0 exch Y0 add moveto
X		gridwidth 0 rlineto
X		stroke
X	} for
} def
X
X
/drawnums {		% place day numbers on calendar
X	dayfont findfont datefontsize calsize get scalefont setfont
X	/fontdiff datefontsize calsize get datefontsize LG get sub def
X	charlinewidth setlinewidth
X	1 datepos 20 fontdiff add sub exch 5 add exch moveto
X
X	1 1 ndays {
X		/day exch def
X		/color (black) def
X		calsize SM ne {		% select alternate color
X			/gray day_gray day startday add 1 sub 7 mod get def
X			holidays { day eq { /gray holiday_gray def } if } forall
X			gray {
X				/color outline {(outline)} {(gray)} ifelse def
X			} if
X		} if
X		gsave
X		prtday
X		grestore
X		nextbox
X	} for
} def
X
X
/drawjnums {		% place day-of-year numbers on calendar
X	notesfont findfont notesfontsize scalefont setfont
X	1 datepos dayheight 3 sub sub exch daywidth 3 sub add exch moveto
X
X	1 1 ndays {
X		/day exch def
X		/jday jdstart day add 1 sub def
X		/str jday 3 string cvs def
X		julian-dates true eq {		% print days left in year?
X			/str str ( \050) strcat yearlen jday sub 3 string cvs
X				strcat (\051) strcat def
X		} if
X		gsave
X		str dup stringwidth pop 0 exch sub 0 rmoveto show
X		grestore
X		nextbox
X	} for
} def
X
X
/fillboxes {		% used by drawfill to generate row of fill squares
X	/last exch def		% last box to fill (0 = Sun)
X	/first exch def		% first box to fill (0 = Sun)
X	/width last first sub 1 add daywidth mul def
X
X	width 0 gt {
X		gsave
X		first daywidth mul 0 rmoveto
X		fillgray setgray
X		width 0 rlineto
X		0 negdayheight rlineto
X		width neg 0 rlineto
X		closepath fill
X		grestore
X	} if
} def
X
X
/drawfill {		% generate fill squares where necessary
X	/firstbox startday ndays add def
X	/lastbox calsize LG ne {41} { note_block {38} {39} ifelse } ifelse def
X
X	0 Y0 moveto			% boxes (if any) at start of first row
X	0 startday 1 sub fillboxes
X
X	0 4 negdayheight mul rmoveto	% boxes (if any) at end of fifth row
X	firstbox 35 lt {
X		firstbox 7 mod 6 fillboxes
X		/firstbox 35 def
X	} if
X
X	0 negdayheight rmoveto		% boxes at end of bottom row
X	firstbox 7 mod lastbox 7 mod fillboxes
} def
X
X
/footstrings {		% print foot strings at bottom of page
X	titlefont findfont footfontsize scalefont setfont
X	/bottomrow { neggridheight 20 add } def
X	0 bottomrow moveto
X	Lfootstring show
X	gridwidth Rfootstring stringwidth pop sub bottomrow moveto
X	Rfootstring show
X	0 bottomrow moveto
X	Cfootstring gridwidth center
} def
X
X
%
% Functions for printing text inside boxes:
%
X
X
/daytext {
X	notesfont findfont notesfontsize scalefont setfont
X	/mytext	exch def /day exch def
X	day datepos 29 sub dup /ypos exch def exch 2 add exch moveto
X	currentpoint pop /LM exch def /RM LM 95 add def
X	showtext
} def
X	
X
/holidaytext {		% print text for holiday (next to date)
X	notesfont findfont notesfontsize scalefont setfont
X	/mytext	exch def /day exch def
X	/dwidth day 10 lt {20} {33} ifelse def		% width of date
X	/mwidth do-moon-p {16} {0} ifelse def		% width of moon icon
X	day datepos 8 sub dup /ypos exch def exch dwidth add exch moveto
X	currentpoint pop /LM exch def /RM LM 97 dwidth mwidth add sub add def
X	showtext
} def
X
X
/notetext {		% print text in notes box
X	dayfont findfont 12 scalefont setfont
X	/day 40 startday sub def		% "date" of notes box
X	day datepos 11 sub exch 4 add exch moveto
X	notesheading show
X
X	notesfont findfont notesfontsize scalefont setfont
X	/mytext	exch def
X	day datepos 19 sub dup /ypos exch def exch 4 add exch moveto
X	/LM currentpoint pop def /RM LM 93 add def
X	showtext
} def
X
X
/crlf {			% simulate CR/LF sequence
X    ypos notesfontsize sub /ypos exch def LM ypos moveto
} def
X
X
/prstr {		% print string on current or next line
X    dup stringwidth pop currentpoint pop
X    add RM gt { crlf hangingindent show } if show
} def
X
X
/showtext {		% print words in "mytext", splitting into lines
X	mytext {
X		dup linesep eq			% force new line?
X			{ crlf pop }		% yes - discard text
X			{ prstr ( ) show }	% no - print string + space
X		ifelse
X	} forall
} def
X
X
%
% Functions for printing months of various sizes and orientations:
%
X
X
/startpage {		% initialize new physical page
X	rval rotate
X	xsval ysval scale
X	xtval ytval translate
} def
X
X
/calendar		% draw calendar for month/year
{
X	drawtitle					% month/year heading
X
X	calsize SM ne {					% day names
X		drawdaynames
X	} if
X
X	calsize LG eq {					% foot strings
X		footstrings
X	} if
X
X	drawnums					% calendar dates
X
X	julian-dates false ne calsize LG eq and {	% julian dates
X		drawjnums
X	} if
X
X	fill-boxes {					% fill unused boxes
X		drawfill
X	} if
X
X	drawgrid					% calendar grid
X
X	draw-moons false ne calsize LG eq and {		% moon icons
X		drawmoons
X	} if
X
X	0 0 moveto					% return to origin
} def
X
X
/printmonth_l {		% print month on landscape page ("posn" = 0..11)
X	/calsize MED def
X
X	posn 0 eq {		% assume first month printed on page is posn 0
X		startpage
X		footstrings
X	} if
X
X	gsave			% draw medium calendar at selected position
X	.226 .25 scale		% landscape mode - 3 rows, 4 cols
X	posn 4 mod 800 mul
X	posn 4 idiv -700 mul 150 add
X	translate
X	calendar
X	grestore
} def
X
X
/printmonth_p {		% print month on portrait page ("posn" = 0..11)
X	/calsize MED def
X	/footfontsize 15 def	% compensate for scaling
X
X	posn 0 eq {		% assume first month printed on page is posn 0
X		gsave		% print foot strings at original scale
X		startpage
X		0 20 translate  % move foot strings up slightly 
X		footstrings
X		grestore	% re-scale Y axis for portrait mode
X		/sv_ysval ysval def
X		/ysval ysval 1.675 mul def
X		startpage
X		/ysval sv_ysval def
X	} if
X
X	gsave			% draw medium calendar at selected position
X	.304 .194 scale		% portrait mode - 4 rows, 3 cols
X	posn 3 mod 800 mul
X	posn 3 idiv -700 mul 300 add
X	translate
X	calendar
X	grestore
} def
X
X
/printmonth {		% print single month on page
X	startpage
X
X	/calsize LG def			% main (large) calendar
X	calendar
X	
X	gsave				% small calendars
X	/calsize SM def
X	/sv_startday startday def
X
X	% calculate previous and next month, year, and starting day
X
X	/lmonth month 1 eq { 12 } { month 1 sub } ifelse def
X	/lyear  month 1 eq { year 1 sub } { year } ifelse def
X	/lstartday startday 35 add lndays sub 7 mod def
X	/nmonth month 12 eq { 1 } { month 1 add } ifelse def
X	/nyear  month 12 eq { year 1 add } { year } ifelse def
X	/nstartday startday ndays add 7 mod def
X
X	/year lyear def			% prev month/year
X	/month lmonth def
X	/startday lstartday def
X	/ndays lndays def
X	5 daywidth mul 5 negdayheight mul Y0 add translate
X	gsave
X	.138 .138 scale
X	12 -120 translate
X	calendar
X	grestore
X
X	/year nyear def			% next month/year
X	/month nmonth def
X	/startday nstartday def
X	/ndays nndays def
X	daywidth 0 translate
X	gsave
X	.138 .138 scale
X	12 -120 translate
X	calendar
X	grestore
X
X	/startday sv_startday def	% restore starting day (for text boxes)
X	grestore
} def
X
X
%
% Moon drawing functions:
%
X
X
/domoon {	% draw moon at phase (0 = new; .25 = 1q; .5 = full; .75 = 3q)
X	/phase exch def
X
X	gsave
X	currentpoint translate
X	newpath
X
X	% if moon is full, just draw unfilled circle
X
X	phase halfperiod .01 sub ge phase halfperiod .01 add le and {
X		0 0 radius
X		0 360 arc stroke
X	}
X	{
X		% draw the line arc now; prepare (but don't draw) the fill arc
X
X		0 0 radius			% for line and fill arcs
X		0 0 radius 
X		phase halfperiod lt {		% phase between new and full
X			270 90 arc stroke	% (line on right, fill on left)
X			0 radius neg moveto
X			270 90 arcn 
X		}
X		{				% phase between full and new
X			90 270 arc stroke	% (line on left, fill on right)
X			0 radius neg moveto
X			270 90 arc 
X			/phase phase halfperiod sub def
X		} ifelse
X
X		% curveto uses (x0,y0) (current point), (x1,y1), (x2,y2),
X		% and (x3,y3) as the control points for drawing a Bezier
X		% cubic section, used here as the curve dividing the moon
X		% icon into dark and light sections.  x1 is in the range
X		% -R*sqrt(2) <= x1 <= R*sqrt(2) and y1 is in the range
X		% 0 <= y1 <= R; note that except in the degenerate case
X		% where x1 = y1 = x2 = y2 = 0, the curve does not actually
X		% pass through (x1,y1) or (x2,y2).
X
X		/x1 quartperiod phase sub rect mul def
X		/y1 x1 abs 2 sqrt div def
X
X		% push control points for curveto
X
X					% x0 = 0   (current
X					% y0 = R    point)
X		x1			% x1
X		y1			% y1
X		x1			% x2 = x1
X		y1 neg			% y2 = -y1
X		0			% x3 = 0
X		radius neg		% y3 = -R
X
X		% draw Bezier curve; fill area between curve and fill arc
X
X		curveto
X		fill
X	} ifelse
X
X	grestore
} def
X
X
/do-moon-p {		% draw a moon on "day"?
X	draw-moons (some) eq {		% printing quarter moons?  look up day
X		/p false def
X		quarter_moons { day eq { /p true def } if } forall
X		p
X	}
X	{
X		draw-moons		% all moons or no moons
X	} ifelse
} def
X
X
/drawmoons {		% main routine to draw moons on calendar
X	/halfperiod 0.5 def
X	/quartperiod 0.25 def
X	/radius 6 def
X	/offset radius 3 add def
X	/rect radius 2 sqrt mul quartperiod div def	% domoon{} scale factor
X	/n 0 def
X
X	gsave
X	moonlinewidth setlinewidth
X	1 datepos offset sub exch daywidth add offset sub exch moveto
X	1 1 ndays {
X		/day exch def
X		do-moon-p {			% draw a moon today?
X			moon_phases n get domoon
X			/n n 1 add def
X		} if
X	nextbox
X	} for
X	grestore
} def
SHAR_EOF
chmod 0666 pcalinit.ps ||
echo 'restore of pcalinit.ps failed'
Wc_c="`wc -c < 'pcalinit.ps'`"
test 13415 -eq "$Wc_c" ||
	echo 'pcalinit.ps: original size 13415, current size' "$Wc_c"
fi
# ============= pcallang.h ==============
if test -f 'pcallang.h' -a X"$1" != X"-c"; then
	echo 'x - skipping pcallang.h (File already exists)'
else
echo 'x - extracting pcallang.h (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'pcallang.h' &&
/*
X * pcallang.h - language-dependent strings (month and day names, option file 
X * keywords, preprocessor tokens, prepositions, etc.):
X *
X * Revision history:
X *
X *	4.0	AWR	03/01/91	expand parameter message to explain
X *					parameter meaning when -w specified
X *
X *		AWR	02/19/91	revise ordinal definitions for
X *					support of negative ordinals
X *
X *		AWR	02/06/91	add text describing expression syntax
X *
X *		AWR	02/04/91	support "odd" and "even" ordinals
X *
X *		AWR	01/28/91	support -B (blank fill squares) flag
X *					and -O (outline "gray" dates) flag
X *
X *			01/16/91	added moon file support (tokens, help
X *					file text, error messages); define
X *					note block heading here
X *
X *			01/07/91	added support for -w (whole year) flag
X *
X *	3.0	AWR	12/10/90	added support for "workday", "weekday",
X *					"holiday", et. al.
X *
X *		AWR	11/15/90	extracted from pcal.c; revised
X *					to contain all language-dependent
X *					strings
X */
X
#define ALL	"all"		/* command-line or date file keyword */
X
#ifdef MAIN_MODULE
X
char *months[12] = {
X	"January", "February", "March", "April", "May", "June",
X	"July", "August", "September", "October", "November", "December"
X	};
X
/* Must be a 2-D array so address within may be used as an initializer;
X * wildcard names must be in same order as symbolic names in pcaldefs.h
X */
char days[14][12] = {
X        "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
X        "Saturday", 		/* weekday names */
X	"day", "weekday", "workday", "holiday", "nonweekday", "nonworkday",
X	"nonholiday"		/* wildcards */
X	};
X
/* preprocessor tokens: token name, token code, dispatch routine */
X
KWD_F pp_info[] = {
X	"define",	PP_DEFINE,	do_define,
X	"else",		PP_ELSE,	NULL,
X	"endif",	PP_ENDIF,	NULL,
X	"ifdef",	PP_IFDEF,	do_ifdef,
X	"ifndef",	PP_IFNDEF,	do_ifndef,
X	"include",	PP_INCLUDE,	NULL,		/* do_include */
X	"undef",	PP_UNDEF,	do_undef,
X	NULL,		PP_OTHER,	NULL };		/* must be last */
X
/* ordinal numbers - e.g. "first Monday in September": ordinal name,
X * ordinal code, ordinal value; note that "all" is parsed as a keyword
X * and (depending on context) may be subsequently treated as an ordinal
X */
X
KWD_O ordinals[] = {
X	"first",	ORD_POSNUM,	 1,
X	"second",	ORD_POSNUM,	 2,
X	"third",	ORD_POSNUM,	 3,
X	"fourth",	ORD_POSNUM,	 4,
X	"fifth",	ORD_POSNUM,	 5,
X	"last",		ORD_NEGNUM,	-1,
X	"odd",		ORD_ODD,	 0,
X	"even",		ORD_EVEN,	 0,
X	NULL,		ORD_OTHER,	 0 };		/* must be last */
X
/* allowable suffixes for ordinal numbers */
X
char *ord_suffix[] = { "st", "nd", "rd", "th", NULL };
X
/* prepositions - e.g., "Friday after fourth Thursday in November" */
X
KWD preps[] = {
X	"before",	PR_BEFORE,
X	"preceding",	PR_BEFORE,
X	"on_or_before",	PR_ON_BEFORE,
X	"oob",		PR_ON_BEFORE,
X	"after",	PR_AFTER,
X	"following",	PR_AFTER,
X	"on_or_after",	PR_ON_AFTER,
X	"ooa",		PR_ON_AFTER,
X	NULL,		PR_OTHER };	/* must be last */
X
/* other keywords */
X
KWD keywds[] = {
X	ALL,	 	DT_ALL,
X	"each",		DT_ALL,
X	"every",	DT_ALL,
X	"note",		DT_NOTE,
X	"opt",		DT_OPT,
X	"year",		DT_YEAR,
X	NULL,		DT_OTHER };	/* must be last */
X
/* moon phases (for moon file) */
X
KWD phases[] = {
X	"NM",		MOON_NM,	/* new moon */
X	"1Q",		MOON_1Q,	/* first quarter */
X	"FQ",		MOON_1Q,	
X	"FM",		MOON_FM,	/* full moon */
X	"3Q",		MOON_3Q,	/* third (last) quarter */
X	"LQ",		MOON_3Q,
X	NULL,		MOON_OTHER };	/* must be last */
X
#else
extern char *months[];
extern char days[14][12];
extern KWD_F pp_info[];
extern KWD preps[];
extern KWD_O ordinals[];
extern char *ord_suffix[];
extern KWD keywds[];
extern KWD phases[];
#endif
X
/* minimum size of abbreviations  */
X
#define MIN_DAY_LEN	3
#define MIN_MONTH_LEN	3
#define MIN_PPTOK_LEN	3
#define MIN_PREP_LEN	7	/* distinguish "on_or_before", "on_or_after" */
#define MIN_ORD_LEN	3	/* distinguish "Thursday" from "third" */
X
X
/*
X * Symbolic names for command-line flags.  These may be changed
X * as desired in order to be meaningful in languages other than
X * English.
X */
X
#define F_INITIALIZE	'I'		/* re-initialize program defaults */
#define	F_BLACK_DAY	'b'		/* print day in black */
#define F_GRAY_DAY	'g'		/* print day in gray */
X
#define F_DAY_FONT	'd'		/* select alternate day font */
#define F_NOTES_FONT	'n'		/* select alternate notes font */
#define F_TITLE_FONT	't'		/* select alternate title font */
X
#define F_EMPTY_CAL	'e'		/* print empty calendar */
#define F_DATE_FILE	'f'		/* select alternate date file */
#define F_OUT_FILE	'o'		/* select alternate output file */
X
#define F_LANDSCAPE	'l'		/* landscape mode */
#define F_PORTRAIT	'p'		/* portrait mode */
X
#define F_HELP		'h'		/* generate help message */
X
#define F_MOON_4	'm'		/* print new/half/full moons */
#define F_MOON_ALL	'M'		/* print all moons */
X
#define F_DEFINE	'D'		/* define preprocessor symbol */
#define F_UNDEF		'U'		/* undefine preprocessor symbol */
X
#define F_L_FOOT	'L'		/* define left foot string */
#define F_C_FOOT	'C'		/* define center foot string */
#define F_R_FOOT	'R'		/* define right foot string */
X
#define F_FIRST_DAY	'F'		/* define alternate starting day */
X
#define F_USA_DATES	'A'		/* parse American date format */
#define F_EUR_DATES	'E'		/* parse European date format */
X
#define F_X_TRANS	'X'		/* X-axis transformation */
#define F_Y_TRANS	'Y'		/* Y-axis transformation */
#define F_X_SCALE	'x'		/* X-axis scale factor */
#define F_Y_SCALE	'y'		/* Y-axis scale factor */
X
#define F_JULIAN	'j'		/* print Julian day (day of year) */
#define F_JULIAN_ALL	'J'		/* print Julian day and days left */
X
#define F_WHOLE_YEAR	'w'		/* print whole year per page */
X					/* (cf. W_WYFLAG below) */
X
#define F_BLANK_BOXES	'B'		/* don't fill unused boxes */
X
#define F_OUTLINE	'O'		/* draw "gray" dates as outlines */
X
/*
X * Flag usage information - not strictly language-dependent, but here anyway
X * (N.B.: all flags must be represented by an entry in this table!)
X *
X * Flags may appear in any of three places: in environment variable
X * PCAL_OPTS, on the command line, or in "opt" lines in the date file.
X * The command line is actually parsed twice: once before reading the date
X * file to get the flags needed in processing it (-e, -f, -b, -g, -D, -U, -A,
X * -E), and again after reading the date file to give the user one last
X * chance to override any of the other flags set earlier.  (Note, however,
X * that the only way to turn off -J|-j [Julian dates], -M|-m [moons], -w
X * [whole year], or -O [outline "gray" dates] once selected is to use -I
X * to reinitialize all program defaults.)
X *
X * The table below supplies the following information about each flag:
X *
X *	- Its name (cf. symbolic definitions above)
X *
X *	- Whether or not it can take an (optional) argument
X *
X *	- Which passes parse it: P_ENV (environment variable), P_CMD1
X *	  (first command line pass), P_OPT ("opt" lines in date file), 
X *	  and P_CMD2 (second command line pass)
X *
X */
X
#ifdef MAIN_MODULE
X
FLAG_USAGE flag_tbl[] = {
X
/*	flag name	arg?	passes where parsed		*/
X
X	F_INITIALIZE,	FALSE,	P_ENV | P_CMD1 | P_OPT | P_CMD2 ,
X
X	F_BLACK_DAY,	TRUE,	P_ENV | P_CMD1 | P_OPT          ,
X	F_GRAY_DAY,	TRUE,	P_ENV | P_CMD1 | P_OPT          ,
X
X	F_DAY_FONT,	TRUE,	P_ENV          | P_OPT | P_CMD2 ,
X	F_NOTES_FONT,	TRUE,	P_ENV          | P_OPT | P_CMD2 ,
X	F_TITLE_FONT,	TRUE,	P_ENV          | P_OPT | P_CMD2 ,
X
X	F_EMPTY_CAL,	FALSE,	P_ENV | P_CMD1                  ,
X	F_DATE_FILE,	TRUE,	P_ENV | P_CMD1                  ,
X	F_OUT_FILE,	TRUE,	P_ENV          | P_OPT | P_CMD2 ,
X
X	F_LANDSCAPE,	FALSE,	P_ENV          | P_OPT | P_CMD2 ,
X	F_PORTRAIT,	FALSE,	P_ENV          | P_OPT | P_CMD2 ,
X
X	F_HELP,		FALSE,	        P_CMD1                  ,
X
X	F_MOON_4,	FALSE,	P_ENV          | P_OPT | P_CMD2 ,
X	F_MOON_ALL,	FALSE,	P_ENV          | P_OPT | P_CMD2 ,
X
X	F_DEFINE,	TRUE,	P_ENV | P_CMD1                  ,
X	F_UNDEF,	TRUE,	P_ENV | P_CMD1                  ,
X
X	F_L_FOOT,	TRUE,	P_ENV          | P_OPT | P_CMD2 ,
X	F_C_FOOT,	TRUE,	P_ENV          | P_OPT | P_CMD2 ,
X	F_R_FOOT,	TRUE,	P_ENV          | P_OPT | P_CMD2 ,
X
X	F_FIRST_DAY,	TRUE,	P_ENV          | P_OPT | P_CMD2 ,
X
X	F_USA_DATES,	FALSE,	P_ENV | P_CMD1 | P_OPT          ,
X	F_EUR_DATES,	FALSE,	P_ENV | P_CMD1 | P_OPT          ,
X
X	F_X_TRANS,	TRUE,	P_ENV          | P_OPT | P_CMD2 ,
X	F_Y_TRANS,	TRUE,	P_ENV          | P_OPT | P_CMD2 ,
X	F_X_SCALE,	TRUE,	P_ENV          | P_OPT | P_CMD2 ,
X	F_Y_SCALE,	TRUE,	P_ENV          | P_OPT | P_CMD2 ,
X
X	F_JULIAN,	FALSE,	P_ENV          | P_OPT | P_CMD2 ,
X	F_JULIAN_ALL,	FALSE,	P_ENV          | P_OPT | P_CMD2 ,
X
X	F_WHOLE_YEAR,	FALSE,	P_ENV | P_CMD1 | P_OPT          ,
X
X	F_BLANK_BOXES,	FALSE,	P_ENV          | P_OPT | P_CMD2 ,
X
X	F_OUTLINE,	FALSE,	P_ENV          | P_OPT | P_CMD2 ,
X
X	'-',		FALSE,	P_ENV | P_CMD1 | P_OPT | P_CMD2 ,
X	'\0',		FALSE,	P_ENV | P_CMD1 | P_OPT | P_CMD2		/* must be last */
X	};
X
#else
extern FLAG_USAGE flag_tbl[];
#endif
X
/*
X * Words used in usage() message - translate as necessary
X */
X
#define W_DEFAULT	"default"		/* translate as required */
#define W_USAGE		"Usage"
X
#define W_FONT		"FONT"			/* names of metavariables */
#define W_DAY		"DAY"
#define W_STRING	"STRING"
#define W_FILE		"FILE"
#define W_SYMBOL	"SYMBOL"
#define W_VALUE		"VALUE"
X
#define W_MM		"MM"			/* abbrev. for month, year */
#define W_YY		"YY"
X
#define W_WYFLAG	"-w"			/* must conform to F_WHOLE_YEAR */
X
#define W_BLACK		"black"			/* cf. color_msg() */
#define W_GRAY		"gray"
X
X
/* special flag_msg[] entries for end of option group, etc. */
X
#define END_GROUP	'\n', NULL, NULL, NULL		/* end of option group */
#define END_LIST	'\0', NULL, NULL, NULL		/* end of list */
#define GROUP_DEFAULT	' ', NULL, " "			/* group default */
X
/*
X * Message strings to be printed by usage() - translate as necessary
X */
#ifdef MAIN_MODULE
X
FLAG_MSG flag_msg[] = {
X
/*	flag name	metasyntax	description						default */
X
X	F_INITIALIZE,	NULL,		"initialize all parameters to program defaults",	NULL,
X	END_GROUP,
X
X	F_BLACK_DAY,	W_DAY,		"print weekday in black",				NULL,
X	F_GRAY_DAY,	W_DAY,		"print weekday in gray (see below)",				NULL,
X	END_GROUP,
X
X	F_OUTLINE,	NULL,		"print \"gray\" dates as outlined characters",			NULL,
X
X	END_GROUP,
X
X	F_DAY_FONT,	W_FONT,		"specify alternate day name font",			DAYFONT,
X	F_NOTES_FONT,	W_FONT,		"specify alternate notes font",				NOTESFONT,
X	F_TITLE_FONT,	W_FONT,		"specify alternate title font",				TITLEFONT,
X	END_GROUP,
X
X	F_EMPTY_CAL,	NULL,		"generate empty calendar (ignore date file)",		NULL,
X	END_GROUP,
X
X	F_DATE_FILE,	W_FILE,		"specify alternate date file",				DATEFILE,
X	END_GROUP,
X
#ifdef DEFAULT_OUTFILE
X	F_OUT_FILE,	W_FILE,		"specify alternate output file",			DEFAULT_OUTFILE,
#else
X	F_OUT_FILE,	W_FILE,		"specify alternate output file",			"stdout",
#endif
X	END_GROUP,
X
X	F_LANDSCAPE,	NULL,		"generate landscape-style calendar",			NULL,
X	F_PORTRAIT,	NULL,		"generate portrait-style calendar",			NULL,
#if ROTATE == LANDSCAPE
X	GROUP_DEFAULT,										"landscape",
#else	
X	GROUP_DEFAULT,										"portrait",
#endif
X	END_GROUP,
X
X	F_HELP,		NULL,		"print this help message",				NULL,
X	END_GROUP,
X
X	F_MOON_4,	NULL,		"draw a \"moon\" icon at full/new/half moons",		NULL,
X	F_MOON_ALL,	NULL,		"draw a \"moon\" icon every day",			NULL,
#if DRAW_MOONS == NO_MOONS
X	GROUP_DEFAULT,										"no moons",
#else
#if DRAW_MOONS == SOME_MOONS
X	GROUP_DEFAULT,										"full/new/half moons",
#else
X	GROUP_DEFAULT,										"every day",
#endif
#endif
X	END_GROUP,
X
X	F_DEFINE,	W_SYMBOL,	"define preprocessor symbol",				NULL,
X	F_UNDEF,	W_SYMBOL,	"undefine preprocessor symbol",				NULL,
X	END_GROUP,
X
X	F_L_FOOT,	W_STRING,	"specify left foot string",				LFOOT,
X	F_C_FOOT,	W_STRING,	"specify center foot string",				CFOOT,
X	F_R_FOOT,	W_STRING,	"specify right foot string",				RFOOT,
X	END_GROUP,
X
X	F_FIRST_DAY,	W_DAY,		"specify starting day of week",				days[FIRST_DAY],
X	END_GROUP,
X
X	F_USA_DATES,	NULL,		"parse American dates (\"mm/dd{/yy}\" and \"month dd\")", NULL,
X	F_EUR_DATES,	NULL,		"parse European dates (\"dd/mm{/yy}\" and \"dd month\")", NULL,
#if DATE_STYLE == USA_DATES
X	GROUP_DEFAULT,										"American",
#else
X	GROUP_DEFAULT,										"European",
#endif
X	END_GROUP,
X
X	F_X_TRANS,	W_VALUE,	"specify x-axis translation",				XTVAL,
X	F_Y_TRANS,	W_VALUE,	"specify y-axis translation",				YTVAL,
X	F_X_SCALE,	W_VALUE,	"specify x-axis scale factor",				XSVAL,
X	F_Y_SCALE,	W_VALUE,	"specify y-axis scale factor",				YSVAL,
X	END_GROUP,
X
X	F_JULIAN,	NULL,		"print Julian day (day of year)",			NULL,
X	F_JULIAN_ALL,	NULL,		"print Julian day and days left in year",		NULL,
#if JULIAN_DATES == NO_JULIANS
X	GROUP_DEFAULT,										"neither",
#else
#if JULIAN_DATES == SOME_JULIANS
X	GROUP_DEFAULT,										"Julian day",
#else
X	GROUP_DEFAULT,										"both",
#endif
#endif
X	END_GROUP,
X
X	F_WHOLE_YEAR,	NULL,		"print whole year (12 consecutive months) per page",	NULL,
X	END_GROUP,
X
X	F_BLANK_BOXES,	NULL,		"leave unused boxes blank",				NULL,
X
X	END_GROUP,			/* must precede END_LIST */
X
X	END_LIST			/* must be last */
};
X
#else
extern FLAG_MSG flag_msg[];
#endif
X
/* Numeric parameter descriptions and text */
X
#ifdef MAIN_MODULE
X
#if __STDC__
PARAM_MSG param_msg[] = {
X	W_YY,			"generate calendar for year " W_YY " (19" W_YY " if " W_YY " < 100)",
X	W_MM " " W_YY,		"generate calendar for month " W_MM " (Jan = 1), year " W_YY,
X	W_MM " " W_YY " N",	"generate calendars for N months, starting at " W_MM "/" W_YY,
X	"(" W_DEFAULT ")",	"generate calendar for current month and/or year",
X	"",			"",
X	"if " W_WYFLAG " specified:",	"",
X	"",			"",
X	W_YY,			"generate calendar for year " W_YY " (19" W_YY " if " W_YY " < 100)",
X	W_MM " " W_YY,		"generate calendars for 12 months, starting at " W_MM "/" W_YY,
X	W_MM " " W_YY " N",	"generate calendars for N months, starting at " W_MM "/" W_YY,
X	"",			"  (N rounded up to next multiple of 12)",
X	"(" W_DEFAULT ")",	"generate calendar for current year",
X	NULL,		NULL		/* must be last */
};
#else
PARAM_MSG param_msg[] = {
X	"YY",		"generate calendar for year YY (19YY if YY < 100)",
X	"MM YY",	"generate calendar for month MM (Jan = 1), year YY",
X	"MM YY N",	"generate calendars for N months, starting at MM/YY",
X	"(default)",	"generate calendar for current month and year",
X	"",		"",
X	"if -w specified:",	"",
X	"",		"",
X	"YY",		"generate calendar for year YY (19YY if YY < 100)",
X	"MM YY",	"generate calendar for 12 months, starting at MM/YY",
X	"MM YY N",	"generate calendars for N months, starting at MM/YY",
X	"",		"  (N rounded up to next multiple of 12)",
X	"(default)",	"generate calendar for current year",
X	NULL,		NULL		/* must be last */
};
#endif
X
#else
extern PARAM_MSG param_msg[];
#endif
X
#define PARAM_MSGS	3	/* number of above to print in command-line syntax message */
X
/* Date file syntax message - lines are copied intact */
X
#ifdef MAIN_MODULE
X
char *date_msg[] = {
X	"",
X	"Date file syntax:",
X	"",
X	"The following rules describe the syntax of date file entries:",
X	"",
X	"  year <year>",
X	"",
X	"  opt <options>",
X	"",
X	"  note <month_spec> <text>",
X	"  note <month> <text>",
X	"",
X	"  if -A flag (American date formats) specified:",
X	"    <month_name> <day>{*} {<text>}",
X	"    <month><sep><day>{<sep><year>}{*} {<text>}",
X	"",
X	"  if -E flag (European date formats) specified:",
X	"    <day> <month_name>{*} {<text>}",
X	"    <day><sep><month>{<sep><year>}{*} {<text>}",
X	"",
X	"  <ordinal> <day_spec> in <month_spec>{*} {<text>}",
X	"  <day_spec> <prep> <date_spec>",
X	"",
X	"where",
X	"",
X	"  {x}          means x is optional",
X	"",
X	"  <date_spec> := any of the above date specs (not year, note, or opt)",
X	"  <month_name> := first 3+ characters of name of month, or \"all\"",
X	"  <month_spec> := <month_name>, or \"year\"",
X	"  <day_name> := first 3+ characters of name of weekday, \"day\",",
X	"                \"weekday\", \"workday\", \"holiday\", \"nonweekday\",",
X	"                \"nonworkday\", or \"nonholiday\"",
X	"  <ordinal> := ordinal number (\"1st\", \"2nd\", etc.), \"first\" .. \"fifth\",",
X	"                \"last\", \"even\", \"odd\", or \"all\"",
X	"  <prep> := \"before\", \"preceding\", \"after\", \"following\", \"on_or_before\",",
X	"                or \"on_or_after\"",
X	"  <sep> := one or more non-numeric, non-space, non-'*' characters",
X	"  <month>, <day>, <year> are the numeric forms",
X	"",
X	"  <options> := any command-line option except -e, -f, -h, -D, -U",
X	"",
X	"Comments start with '#' and run through end-of-line.",
X	"",
X	"Holidays may be flagged by specifying '*' as the last character of",
X	"the date field(s), e.g. \"10/12* Columbus Day\", \"July 4* Independence",
X	"Day\", etc.  Any dates flagged as holidays will be printed in gray, and",
X	"any associated text will appear adjacent to the date.",
X	"",
X	"Note that the numeric date formats (mm/dd{/yy}, dd.mm{.yy}) support",
X	"an optional year, which will become the subsequent default year.  The",
X	"alphabetic date formats (month dd, dd month) do not support a year",
X	"field; the \"year yy\" command is provided to reset the default year.",
X	"",
X	"\"Floating\" days may be specified in the date file as \"first Mon in ",
X	"Sep\", \"last Mon in May\", \"4th Thu in Nov\", etc.; any word may be",
X	"used in place of \"in\".  \"Relative floating\" days (e.g. \"Fri after 4th ",
X	"Thu in Nov\") are also accepted; they may span month/year bounds.",
X	"Pcal also accepts date specs such as \"all Friday{s} in October\", \"last",
X	"Thursday in all\", etc., and produces the expected results; \"each\" and",
X	"\"every\" are accepted as synonyms for \"all\".  Negative ordinals are",
X	"allowed; \"-2nd\" means \"next to last\".",
X	"",
X	"The words \"day\", \"weekday\", \"workday\", and \"holiday\" may be used as",
X	"wildcards: \"day\" matches any day, \"weekday\" matches any day normally",
X	"printed in black, \"workday\" matches any day normally printed in black",
X	"and not explicitly flagged as a holiday, and \"holiday\" matches any",
X	"day explicitly flagged as a holiday.  \"Nonweekday\", \"nonworkday\",",
X	"and \"nonholiday\" are also supported and have the obvious meanings.",
X	"",
X	"\"Odd\" and \"even\" do not refer to the actual date; instead, \"odd\"",
X	"means \"alternate, starting with the first\"; \"even\" means \"alternate,",
X	"starting with the second\".  Thus, \"odd Fridays in March\" refers to",
X	"the first, third, and (if present) fifth Fridays in March - not to",
X	"those Fridays falling on odd dates.",
X	"",
X	"\"All\" refers to each individual month; \"year\" refers to the year",
X	"as an entity.  Thus \"odd Fridays in all\" refers to the first/third/",
X	"fifth Friday of each month, while \"odd Fridays in year\" refers to",
X	"the first Friday of January and every other Friday thereafter.",
X	"",
X	"Additional notes may be propagated to an empty calendar box by the",
X	"inclusion of one or more lines of the form \"note <month> <text>\",",
X	"where <month> may be numeric or alphabetic; \"note all <text>\"",
X	"propagates <text> to each month in the current year.",
X	"",
X	"Simple cpp-like functionality is provided.  The date file may include",
X	"the following commands, which work like their cpp counterparts:",
X	"",
X	"        define <sym>",
X	"        undef <sym>",
X	"",
X	"        if{n}def <expr>",
X	"           ...",
X	"        { else",
X	"           ... }",
X	"        endif",
X	"",
X	"        include <file>",
X	"",
X	"Note that these do not start with '#', which is reserved as a comment",
X	"character.",
X	"",
X	"<sym> is a symbol name consisting of a letter followed by zero or",
X	"more letters, digits, or underscores ('_').  Symbol names are always",
X	"treated in a case-insensitive manner.",
X	"",
X	"<expr> is an expression consisting of symbol names joined by the logical",
X	"operators (in order of precedence, high to low) '!' (unary negate), '&'",
X	"(and), '^' (exclusive or), and '|' (inclusive or).  '&&' and '||' are",
X	"accepted as synonyms for '&' and '|' respectively; the order of",
X	"evaluation may be altered by the use of parentheses.  A symbol whose",
X	"name is currently defined evaluates to TRUE; one whose name is not",
X	"currently defined evaluates to FALSE.  Thus \"ifdef A | B | C\" is TRUE",
X	"if any of the symbols A, B, and C is currently defined, and",
X	"\"ifdef A & B & C\" is TRUE if all of them are.",
X	"",
X	"\"ifndef A | B | C\" is equivalent to \"ifdef !(A | B | C)\" (or, using",
X	"DeMorgan's Law, \"ifdef !A & !B & !C\") - in other words, TRUE if none of",
X	"the symbols A, B, and C is currently defined.",
X	"",
X	"\"define\" alone deletes all the current definitions; \"ifdef\" alone is",
X	"always false; \"ifndef\" alone is always true.",
X	"",
X	"The file name in the \"include\" directive may optionally be surrounded",
X	"by \"\" or <>.  In any case, path names are taken to be relative to",
X	"the location of the file containing the \"include\" directive.",
X	"",
X	"",
X	"Moon file syntax:",
X	"",
X	"Pcal normally calculates the approximate phase of the moon using",
X	"a simple algorithm which assumes (among other things) that the",
X	"length of the lunar month is constant and that the quarter moons",
X	"will occur on the same day worldwide.  For most users, that is",
X	"adequate; however, moon-phase freaks may enter the dates and",
X	"(optionally) times of quarter moons (from a reliable source such",
X	"as an almanac or astronomical table) into a file called .moonXX ",
X	"(moonXX.dat on VMS), where XX is the last two digits of the year.",
X	"If such a file exists (in the same directory as the date file),",
X	"pcal will interpolate the phase of the moon from the information",
X	"in this file instead of using the default algorithm.",
X	"",
X	"Entries in the moon file must conform to the following syntax:",
X	"",
X	"  if -A flag (American date formats) specified:",
X	"    <quarter> <month><sep><day> {<hour><sep><min>}",
X	"",
X	"  if -E flag (European date formats) specified:",
X	"    <quarter> <day><sep><month> {<hour><sep><min>}",
X	"",
X	"where",
X	"",
X	"  <quarter> := \"nm\", \"fq\" or \"1q\", \"fm\", \"3q\" or \"lq\" (new",
X	"                moon, first quarter, full moon, last quarter)",
X	"  <hour>    := number 0-23 (24-hour clock)",
X	"  <min>     := number 0-59",
X	"",
X	"This file must contain entries for all quarter moons in the year,",
X	"in chronological order; if any errors are encountered, pcal will",
X	"revert to using its default algorithm.",
X	"",
X	"As in the date file, comments start with '#' and run through",
X	"end-of-line.  ",
X	NULL
X	};
#else
extern char *date_msg[];
#endif
X
/* format strings for color_msg() - translate as necessary */
#define COLOR_MSG_1	"all days in %s"
#define COLOR_MSG_2	"in %s; others in %s"
X
/* format string for short usage() message */
#define USAGE_MSG	"enter \"%s -%c\" for full description of parameters\n"
X
/* format strings for comment in PostScript output file */
#define VERSION_MSG	"Generated by %s %s"
#define DATEFILE_MSG	" from %s"
X
#define NOTES_HDR	"Notes"		/* title of "notes" box */
#define LINE_SEP	".p"		/* text line separator */
X
/* strings used in error messages */
#define ENV_VAR		"environment variable "
#define DATE_FILE	"date file "
X
/* Error and information messages - translate as necessary */
X
/* program error messages */
#define	E_ALLOC_ERR	"%s: calloc() failed - out of memory\n"
#define	E_FOPEN_ERR	"%s: can't open file %s\n"
#define	E_ILL_LINE	"%s: %s in file %s, line %d\n"
#define	E_ILL_MONTH	"%s: month %d not in range %d .. %d\n"
#define	E_ILL_OPT	"%s: unrecognized flag %s"
#define E_ILL_OPT2	" (%s\"%s\")"
#define	E_ILL_YEAR	"%s: year %d not in range %d .. %d\n"
#define	E_SYMFULL	"%s: symbol table full - can't define %s\n"
#define	E_UNT_IFDEF	"%s: unterminated if{n}def..{else..}endif in file %s\n"
#define E_FLAG_IGNORED	"%s: -%c flag ignored (%s\"%s\")\n"
X
/* preprocessor error strings */
#define E_ELSE_ERR	"unmatched \"else\""
#define E_END_ERR	"unmatched \"endif\""
#define E_GARBAGE	"extraneous data on \"%s\" line"
#define E_INV_DATE	"invalid date (or no match for wildcard)"
#define E_INV_LINE	"unrecognized line"
#define E_NESTING	"maximum file nesting level exceeded"
#define E_EXPR_SYNTAX	"syntax error in expression"
X
/* moon file error strings */
#define E_DATE_SEQ	"date or phase out of sequence"
#define E_PREM_EOF	"premature EOF"
X
/* information message (VMS, Amiga only) */
#define	I_OUT_NAME	"%s: output is in file %s\n"
X
SHAR_EOF
chmod 0666 pcallang.h ||
echo 'restore of pcallang.h failed'
Wc_c="`wc -c < 'pcallang.h'`"
test 23965 -eq "$Wc_c" ||
	echo 'pcallang.h: original size 23965, current size' "$Wc_c"
fi
# ============= pcalutil.c ==============
if test -f 'pcalutil.c' -a X"$1" != X"-c"; then
	echo 'x - skipping pcalutil.c (File already exists)'
else
echo 'x - extracting pcalutil.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'pcalutil.c' &&
/*
X * pcalutil.c - utility routines for Pcal
X *
X * Contents:
X *
X *		alloc
X *		calc_day
X *		calc_weekday
X *		calc_year_day
X *		ci_strcmp
X *		ci_strncmp
X *		copy_text
X *		getline
X *		is_valid
X *		loadwords
X *		mk_filespec
X *		mk_path
X *		normalize
X *		split_date
X *		trnlog
X *
X * Revision history:
X *
X *	4.0	AWR	02/24/91	Revised getline() and copy_text() to
X *					handle C-style escapes of characters
X *					and octal/hex numbers
X *
X *		AWR	02/19/91	Added support for negative ordinals
X *					in calc_day(), calc_year_day()
X *
X *		AWR	02/04/91	Added calc_year_day()
X *
X *		AWR	01/15/91	Extracted from pcal.c
X *
X */
X
X
/*
X * Standard headers:
X */
X
#include <stdio.h>
#include <ctype.h>
#include <string.h>
X
/*
X * Pcal-specific definitions:
X */
X
#include "pcaldefs.h"
#include "pcalglob.h"
#include "pcallang.h"
X
/*
X * Macros:
X */
X
/* skip over numeric field and subsequent non-numeric characters */
#define SKIP_FIELD(p) \
X	do { while (*p && isdigit(*p)) p++; \
X	     while (*p && !isdigit(*p)) p++; } while (0)
X
X
/*
X * General-purpose utility routines
X */
X
X
/*
X * alloc - interface to calloc(); terminates if unsuccessful
X */
#ifdef PROTOS
char *alloc(int size)
#else
char *alloc(size)
X	int size;
#endif
{
X	char *p;
X
X	if (size == 0)		/* not all calloc()s like null requests */
X		size = 1;
X
X	if ((p = calloc(1, size)) == NULL) {
X		FPR(stderr, E_ALLOC_ERR, progname);
X		exit(EXIT_FAILURE);
X	}
X
X	return p;
}
X
X
/*
X * ci_str{n}cmp - case-insensitive flavors of strcmp(), strncmp()
X */
#ifdef PROTOS
int ci_strcmp(register char *s1,
X	      register char *s2)
#else
int ci_strcmp(s1, s2)
register char *s1, *s2;
#endif
{
X	register char c1, c2;
X
X	for ( ; (c1 = TOLOWER(*s1)) == (c2 = TOLOWER(*s2)); s1++, s2++)
X		if (c1 == '\0')
X			return 0;
X
X	return c1 - c2;
}
X
X
#ifdef PROTOS
int ci_strncmp(register char *s1,
X	       register char *s2,
X	       int n)
#else
int ci_strncmp(s1, s2, n)
register char *s1, *s2;
int n;
#endif
{
X	register char c1, c2;
X
X	for ( ; --n >= 0 && (c1 = TOLOWER(*s1)) == (c2 = TOLOWER(*s2)); s1++, s2++)
X		if (c1 == '\0')
X			return 0;
X
X	return n < 0 ? 0 : c1 - c2;
}
X
X
/*
X * Date calculation routines (see also macros in pcaldefs.h)
X */
X
X
/*
X * normalize - adjust day in case it has crossed month (or year) bounds 
X */
#ifdef PROTOS
void normalize(DATE *pd)
#else
void normalize(pd)
X	DATE *pd;		/* pointer to date */
#endif
{
X	int len;
X
X	/* adjust if day is in previous or following month */
X
X	while (pd->dd < 1) {
X		pd->yy = PREV_YEAR(pd->mm, pd->yy);
X		pd->mm = PREV_MONTH(pd->mm, pd->yy);
X		pd->dd += LENGTH_OF(pd->mm, pd->yy);
X	}
X
X	while (pd->dd > (len = LENGTH_OF(pd->mm, pd->yy))) {
X		pd->dd -= len;
X		pd->yy = NEXT_YEAR(pd->mm, pd->yy);
X		pd->mm = NEXT_MONTH(pd->mm, pd->yy);
X	}
}
X
X
/*
X * calc_day - calculate calendar date from ordinal date (e.g., "first Friday
X * in November", "last day in October"); return calendar date if it exists, 
X * 0 if it does not
X */
#ifdef PROTOS
int calc_day(int ord,
X	     int wkd,
X	     int mm)
#else
int calc_day(ord, wkd, mm)
X	int ord;
X	int wkd;
X	int mm;
#endif
{
#ifdef PROTOS
X	int first, last, day, (*pfcn)(int, int, int);
#else
X	int first, last, day, (*pfcn)();
#endif
X
X	if (IS_WILD(wkd)) {	/* "day", "weekday", "workday", or "holiday" */
X		pfcn = pdatefcn[wkd - WILD_FIRST];
X		last = LENGTH_OF(mm, curr_year);
X
X		if (ord < 0) {			/* search backwards */
X			for (day = last; 
X			     day >= 1 &&
X				!((*pfcn)(mm, day, curr_year) && ++ord == 0);
X			     day--)
X				;
X		} else {			/* search forwards */
X			for (day = 1; 
X			     day <= last && 
X				!((*pfcn)(mm, day, curr_year) && --ord == 0);
X			     day++)	
X				;
X		}
X		return is_valid(mm, day, curr_year) ? day : 0; 
X
X	} else {		/* fixed weekday - calculate it */
X		first = (wkd - FIRST_OF(mm, curr_year) + 7) % 7 + 1;
X		if (ord < 0) {		/* get last (try 5th, then 4th) */
X			if (!is_valid(mm, last = first + 28, curr_year))
X				last -= 7;
X			if (!is_valid(mm, day = last + 7 * (ord + 1),
X			    curr_year))
X				day = 0;	
X		}
X		else 
X			if (!is_valid(mm, day = first + 7 * (ord - 1),
X			    curr_year))
X				day = 0;
X
X		return day;
X	}
X
}
X
X
/*
X * calc_year_day - calculate calendar date from ordinal date within year
X * (e.g., "last Friday in year", "10th holiday in year"); if date exists,
X * fill in pdate and return TRUE; else return FALSE
X */
#ifdef PROTOS
int calc_year_day(int ord,
X		  int wkd,
X		  DATE *pdate)
#else
int calc_year_day(ord, wkd, pdate)
X	int ord;
X	int wkd;
X	DATE *pdate;
#endif
{
#ifdef PROTOS
X	int incr, (*pfcn)(int, int, int);
#else
X	int incr, (*pfcn)();
#endif
X	DATE date;
X
X	if (IS_WILD(wkd)) {	/* "day", "weekday", "workday", or "holiday" */
X		pfcn = pdatefcn[wkd - WILD_FIRST];
X
X		if (ord < 0) {			/* nth occurrence backwards */
X			MAKE_DATE(date, DEC, 31, curr_year);
X			ord = -ord;
X			incr = -1;
X		} else {			/* nth occurrence forwards */
X			MAKE_DATE(date, JAN, 1, curr_year);
X			incr = 1;
X		}
X
X		/* search for selected occurrence of specified wildcard */
X
X		while (date.yy == curr_year &&
X		       !((*pfcn)(date.mm, date.dd, date.yy) && --ord == 0)) {
X			date.dd += incr;
X			normalize(&date);
X		}
X
X	} else {		/* fixed weekday - calculate it */
X		if (ord < 0)
X			MAKE_DATE(date, DEC,
X				  calc_day(-1, wkd, DEC) + 7 * (ord + 1),
X				  curr_year);
X		else
X			MAKE_DATE(date, JAN,
X				  calc_day(1, wkd, JAN) + 7 * (ord - 1),
X				  curr_year);
X		normalize(&date);
X	}
X
X	return date.yy == curr_year ? (*pdate = date, TRUE) : FALSE;
}
X
X
/*
X * calc_weekday - return the weekday (0-6) of mm/dd/yy (mm: 1-12)
X */
#ifdef PROTOS
int calc_weekday(int mm,
X		 int dd,
X		 int yy)
#else
int calc_weekday(mm, dd, yy)
X	int mm;
X	int dd;
X	int yy;
#endif
{
X	return (yy + (yy-1)/4 - (yy-1)/100 + (yy-1)/400 + OFFSET_OF(mm, yy) +
X		(dd-1)) % 7;
}
X
X
/*
X * is_valid - return TRUE if m/d/y is a valid date
X */
#ifdef PROTOS
int is_valid(register int m,
X	     register int d,
X	     register int y)
#else
int is_valid(m, d, y)
X	register int m, d, y;
#endif
{
X	return m >= JAN && m <= DEC && 
X		d >= 1 && d <= LENGTH_OF(m, y);
}
X
X
X
/*
X * Token parsing/remerging routines:
X */
X
X
/*
X * loadwords - tokenize line buffer into word array, return word count.
X * differs from old loadwords() in that it handles quoted (" or ') strings
X * and removes escaped quotes
X */
#ifdef PROTOS
int loadwords(void)
#else
int loadwords()
#endif
{
X	register char *ptok;
X	char *delim, **ap, *p1, *p2, c;
X	int nwords;
X
X	for (ptok = lbuf, ap = words; TRUE; ap++) {
X
X		ptok += strspn(ptok, WHITESPACE); /* find next token */
X
X		if (! *ptok) {			/* end of lbuf? */
X			*ap = NULL;		/* add null ptr at end */
X			nwords = ap - words;	/* number of non-null ptrs */
X			break;			/* exit loop */
X			}
X
X		delim =	*ptok == '"'  ? "\"" :	/* set closing delimiter */
X			*ptok == '\'' ? "'"  :
X			WHITESPACE;
X
X		if (*ptok == *delim)		/* skip opening quote */
X			ptok++;
X
X		*ap = ptok;			/* save token ptr */
X
X		do {				/* find unescaped delimiter */
X			ptok += strcspn(ptok, delim);
X			if ((c = ptok[-1]) == '\\')
X				ptok++;
X		} while (c == '\\');
X
X		if (*ptok)			/* terminate token */
X			*ptok++ = '\0';
X	}
X
X	/* now reprocess the word list, removing escapes from quotes */
X
X	for (ap = words; *ap; *ap++)
X		for (p1 = p2 = *ap; c = *p2 = *p1++; *p2++)
X			if (c == '\\')
X				*p2 = *p1++;
X
X	return nwords;				/* return word count */
X
}
X
X
/*
X * copy_text - retrieve remaining text in lbuf and copy to output string,
X * separating tokens by a single blank and condensing runs of blanks (all
X * other whitespace has been converted to blanks by now) to one blank
X */
#ifdef PROTOS
void copy_text(char *pbuf,
X	       char **ptext)
#else
void copy_text(pbuf, ptext)
X	char *pbuf;		/* output buffer - can be lbuf itself */
X	char **ptext;		/* pointer to first text word in "words" */
#endif
{
X	char *p, *pb;
X
X	/* copy words to pbuf, separating by one blank */
X
X	for (*(pb = pbuf) = '\0'; p = *ptext; *pb++ = *++ptext ? ' ' : '\0') {
X		for ( ; *p; *p++)
X			if (! (*p == ' ' && (pb == pbuf || pb[-1] == ' ')))
X				*pb++ = *p;
X		if (pb > pbuf && pb[-1] == ' ')
X			pb--;
X	}
}
X
X
/*
X * split_date - extract 1-3 numeric fields (separated by one or more
X * non-numeric characters) from date string; return number of fields
X */
#ifdef PROTOS
int split_date(char *pstr,
X	       int *pn1,
X	       int *pn2,
X	       int *pn3)
#else
int split_date(pstr, pn1, pn2, pn3)
X	char *pstr;			/* input string */
X	int *pn1, *pn2, *pn3;		/* output numbers */
#endif
{
X	int i, n, *pn;
X
X	/* attempt to extract up to three numeric fields */
X	for (n = 0, i = 1; i <= 3; i++) {
X		pn = i == 1 ? pn1 : i == 2 ? pn2 : pn3;	/* crude but portable */
X		if (pn)
X			*pn = *pstr ? (n++, atoi(pstr)) : 0;
X		SKIP_FIELD(pstr);		/* go to next field */
X	}
X
X	return n;
}
X
X
X
/*
X * File input routines:
X */
X
X
/*
X * octal_esc - read up to 3 octal digits from file; return value of octal
X * constant and leave file pointer at last character
X */
#ifdef PROTOS
static int octal_esc(FILE *fp)
#else
static int octal_esc(fp)
X	FILE *fp;
#endif
{
X	int i, n, c;
X
X	for (n = 0, i = 0; i < 3; i++) {
X		c = getc(fp);
X		if (c == EOF)
X			return EOF;
X		if (!isodigit(c)) {
X			ungetc(c, fp);
X			break;
X		}
X		n = n * 8 + (c - '0');
X	}
X
X	return n & 0377;		/* truncate to 8 bits */
}
X
X
/*
X * hex_esc - read 'x' or 'X' followed by 1 or 2 hex digits from file; return
X * value of hexadecimal constant (or letter if no hex digits follow) and
X * leave file pointer at last character
X */
#ifdef PROTOS
static int hex_esc(FILE *fp)
#else
static int hex_esc(fp)
X	FILE *fp;
#endif
{
X	int i, n, c, sv_c;
X
X	sv_c = c = getc(fp);		/* read leading 'x' or 'X' */
X	if (TOLOWER(c) != 'x')
X		return c;		/* something else - just return it */
X
X	for (n = 0, i = 0; i < 2; i++) {
X		c = getc(fp);
X		if (c == EOF)
X			return EOF;
X		if (!isxdigit(c)) {
X			ungetc(c, fp);
X			break;
X		}
X		n = n * 16 + (isupper(c) ? c - 'A' + 10 :
X			      islower(c) ? c - 'a' + 10 :
X			      c - '0');
X	}
X
X	return i > 0 ? n & 0377 : sv_c;		/* truncate to 8 bits */
}
X
X
/*
X * getline - read next non-null line of input file into lbuf; return 0 on EOF
X * strip leading whitespace, translate other whitespace to blanks, and handle
X * all escapes except \' and \", (cf. loadwords())
X */
#ifdef PROTOS
int getline(FILE *fp,
X	    int *pline)
#else
int getline(fp, pline)
X	FILE *fp;
X	int *pline;
#endif
{
X	register char *cp;
X	register int c, c2;
X	static char escape[] = "abfnrtv";	/* cf. ANSI spec, 2.2.2 */
X	int in_comment;		/* comments: from '#' to end-of-line */
X
X	cp = lbuf;
X	do {
X		in_comment = FALSE;
X		while ((c = getc(fp)) != '\n' && c != EOF) {
X			if (c == '#')
X				in_comment = TRUE;
X
X			if (isspace(c))		/* whitespace => blank */
X				c = ' ';
X
X			/* ignore comments and leading white space */
X			if (in_comment || (cp == lbuf && c == ' '))
X				continue;
X
X			/* handle escape sequences here: escaped whitespace
X			 * and ANSI escapes are all converted to a space;
X			 * octal and hex constants are converted in place
X			 */
X			if (c == '\\') {
X				if ((c2 = getc(fp)) == EOF)
X					return FALSE;
X
X				if (isspace(c2) || strchr(escape, c2)) {
X					c = ' ';
X					if (c2 == '\n')
X						(*pline)++;
X				}
X				else if (isodigit(c2)) {	/* octal */	
X					ungetc(c2, fp);
X					if((c = octal_esc(fp)) == EOF)
X						return FALSE;
X				}
X				else if (TOLOWER(c2) == 'x') {	/* hex */
X					ungetc(c2, fp);
X					if((c = hex_esc(fp)) == EOF)
X						return FALSE;
X				}
X				else if (c2 == '\'' || c2 == '"')
X					ungetc(c2, fp);
X				else
X					c = c2;
X
X			}
X			*cp++ = c;
X		}
X
X		if (c == EOF)			/* no more input lines */
X			return FALSE;
X
X		(*pline)++;			/* bump line number */
X
X	} while (cp == lbuf);			/* ignore empty lines */
X
X	*cp = '\0';
X	return TRUE;
}
X
X
/*
X * Routines dealing with translation of file specifications (VMS, Un*x)
X */
X
#ifdef VMS
/*
X * mk_path - extract the path component from VMS file spec
X */
#ifdef PROTOS
char *mk_path(char *path,
X	      char *filespec)
#else
char *mk_path(path, filespec)
X	char *path;		/* output path */
X	char *filespec;		/* input filespec */
#endif
{
X	char *p;
X
X	strcpy(path, filespec);
X	if (!(p = strchr(path, ']')) && !(p = strchr(path, ':')))
X		p = path - 1;	/* return null string if no path */
X	*++p = '\0';
X
X	return path;
}
X
X
/*
X * mk_filespec - merge VMS path and file names, where latter can be relative
X */
#ifdef PROTOS
char *mk_filespec(char *filespec,
X		  char *path,
X		  char *name)
#else
char *mk_filespec(filespec, path, name)
X	char *filespec;		/* output filespec */
X	char *path;		/* input path */
X	char *name;		/* input file name */
#endif
{
X	char *p;
X
X	*filespec = '\0';
X
X	/* copy name intact if absolute; else merge path and relative name */
X	if (!strchr(name, ':')) {
X		strcpy(filespec, path);
X		if ((p = P_LASTCHAR(filespec)) && *p == END_PATH &&
X		    name[0] == START_PATH && strchr(".-", name[1]))
X			*p = *++name == '-' ? '.' : '\0';
X	}
X
X	return strcat(filespec, name);
}
X
X
/*
X * trnlog - return translation of VMS logical name (null if missing)
X */
#ifdef PROTOS
char *trnlog(char *logname)
#else
char *trnlog(logname)	/* look up logical name */
X	char *logname;
#endif
{
X	static char trnbuf[STRSIZ];
X	
X	$DESCRIPTOR(src, logname);
X	$DESCRIPTOR(dst, trnbuf);
X	short len;
X	int ret;
X	
X	src.dsc$w_length  = strlen(logname);
X	ret = LIB$SYS_TRNLOG(&src, &len, &dst);
X	return ret == SS$_NORMAL ? (trnbuf[len] = '\0', trnbuf) : NULL;
}
X
#else		/* apparently DOS and Amiga can use the Un*x flavors */
X
/*
X * mk_path - extract the path component from a Un*x file spec
X */
#ifdef PROTOS
char *mk_path(char *path,
X	      char *filespec)
#else
char *mk_path(path, filespec)
X	char *path;		/* output path */
X	char *filespec;		/* input filespec */
#endif
{
X	char *p;
X
X	strcpy(path, filespec);
X	if (! (p = strrchr(path, END_PATH)) )
X		p = path - 1;	/* return null string if no path */
X
X	*++p = '\0';
X	return path;
}
X
X
/*
X * mk_filespec - merge Un*x path and file names, where latter can be relative
X */
#ifdef PROTOS
char *mk_filespec(char *filespec,
X		  char *path,
X		  char *name)
#else
char *mk_filespec(filespec, path, name)
X	char *filespec;		/* output filespec */
X	char *path;		/* input path */
X	char *name;		/* input file name */
#endif
{
X	char *p;
X
X	*filespec = '\0';
X
X	/* copy name intact if absolute; else merge path and relative name */
X
X	/* if path starts with "~/", translate it for user */
X	if (strncmp(name, "~/", 2) == 0 && (p = trnlog(HOME_DIR)) != NULL) {
X		strcpy(filespec, p);
X		if ((p = P_LASTCHAR(filespec)) && *p != END_PATH)
X			*++p = END_PATH, *++p = '\0';
X		name += 2;		/* skip "~/" */
X	}
X	else if (*name != START_PATH) {		/* relative path */
X		strcpy(filespec, path);
X		if ((p = P_LASTCHAR(filespec)) && *p != END_PATH)
X			*++p = END_PATH, *++p = '\0';
X	}
X
X	return strcat(filespec, name);
}
X
X
/*
X * trnlog - return translation of Un*x environment variable
X */
#ifdef PROTOS
char *trnlog(char *logname)
#else
char *trnlog(logname)	/* look up logical name */
X	char *logname;
#endif
{
X	return getenv(logname);
}
X
#endif
SHAR_EOF
chmod 0644 pcalutil.c ||
echo 'restore of pcalutil.c failed'
Wc_c="`wc -c < 'pcalutil.c'`"
test 14737 -eq "$Wc_c" ||
	echo 'pcalutil.c: original size 14737, current size' "$Wc_c"
fi
true || echo 'restore of protos.h failed'
echo End of part 4, continue with part 5
exit 0