[comp.lang.postscript] pscal again

smann@cs.washington.edu (Stephen Mann) (01/09/90)

Well, as long as everyone else is posting their version of pscal, I may
as well post my latest version.  It's still a csh script.  Although I
like the look of the 5 row format, the 6 row format avoids the doubling
of days (and holidays for those days).  Changes others made didn't make
it into my version.

Changes from the last version I posted include:

	- PostScript will appear on standard output instead of sending
	  it directly it to a printer.  The user now must redirect the
	  output to a printer.
	- The command line semantics were changed as follows:
		- with no arguments, print the calendar for the current month;
		- with one argument (a year) print the calendar for the 
		  entire year;
		- with two arguments (month and year) print the calendar
		  for that month and year.  If month is 0, then print it
		  for the entire year;
		- If the lone arguement of '-man' is given, print the man page;
		- Months can now be specified as a string of 3 characters
		  (first three characters of the month, first letter
		   capitalized)
	- Corrections to 'isleap' and 'startday' mentioned earlier were
		incorporated
	- A crummy facility for showing full and new moons was added
	  (basically, you list them in a file).

Maybe someday I'll get around to allowing arbitrary PostScript in
the holiday file, clipping of holidays to the day box, and writing a better
holiday processor.  Maybe.

Steve
-----------------
#!/bin/csh -f
#+
#
# NAME:
#	pscal
#
# SYNOPSIS:
#	pscal [[month] year]
#
# DESCRIPTION:
#	`Pscal' is a PostScript program to generate calendars.
#	There are multiple ways of invoking the program.
#	If no arguments are given, then the calendar for the
#	current month/year is generated.
#	If one argument is given, then it is assumed to be a year,
#	and the calendar for the entire year is printed.
#	If two arguments are given, then the first is taken to
#	be the month, and the second the year.  
#
#	The month may be in one of two formats: first, it can be
#	a number from 0 to 12.  The numbers correspond to the
#	obvious month; '0' is special, and will indicates that
#	the calendar for the entire year should be generated.
#	The second format for the month is as a string of three
#	characters.  The format is the same one that the Unix 'date'
#	program uses: first three letters of the month, first
#	letter capitolized.
#
#	The file $home/.holiday is read and used to print short messages
#	on specified days.  The .holiday file should consist of lines of
#	the form 
#		month:day:message string
#	Messages should be 20 characters or less, with no more than 4
#	messages per day.  No spaces should appear from the beginning
#	of a line until after the second colon.
#	Month and day should be numbers in the obvious ranges.
#
#	Also, you may have a ~/.holiday.moon.<year> file.
#	This is a real ugly hack to put pictures of the full
#	and new moon on the calendar.  Form of the lines
#	should be
#		month day full
#	(or new, if it is a new moon).  If there are two full (new)
#	moons in a month, an entry should be made for each.
#	Like I said, a real ugly hack.
#
#	If invoked with the single argument '-man', pscal will
#	print this man page.
#
# NOTES:
#	This version differs from previous versions!  The calendar
#	is printed on standard output.  It is up to the user
#	to redirect the output to a printer.
#
# AUTHOR:
#	Patrick Wood
#	Copyright (C) 1987 by Pipeline Associates, Inc.
#	Permission is granted to modify and distribute this free of charge.
# 
# HISTORY:
#	@Original From: patwood@unirot.UUCP (Patrick Wood)
#	@Shell stuff added 3/9/87 by King Ables
#	@Made pretty by tjt 1988
#	@Holiday and printer flag passing hacks added Dec 1988 
#	@ by smann@june.cs.washington.edu 
#	@Command line processing changed; character string months
#	@ added; printing of entire year added; phase of moon
#	@ hacked in; corrections to isleap and startday (noted
#	@ elsewhere) made; added -man switch; 
#	@ by smann@june.cs.washington.edu  Jan 1990
#
# BUGS:
#	`Pscal' doesn't work for months before 1753 (weird stuff happened
#	in September, 1752).
#
#	A better format for the dates of holidays would be nice.
#	An escape to allow holiday messages to be raw PostScript would
#	also be nice.
#	The holiday messages should be handled more intelligently (ie,
#	the messages should be clipped to the day).
#
# END-OF-MAN

set dmon = (Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)
set flags
set date = (`date`)

switch ($#argv)
	case 0:
	    set month = $date[2]
	    set i = 1
	    foreach m ($dmon)
		if ( $m == $month ) then
		    set month = $i
		    break
		endif
		@ i++
	    end
	    set year = $date[6]
	    breaksw
	case 1:
	    switch ($1)
		case -man:
		    cat $0 | awk '{if ($2 == "END-OF-MAN") exit;else print}' | more
		    exit
		case -*:
		    echo "usage: $0 [[month] year]"
		    exit 1
	    endsw
	    set month = 0
	    set year = $argv[1]
	    breaksw
	case 2:
	    set month = $argv[1]
	    set i = 1
	    foreach m ($dmon)
		if ( $m == $month ) then
		    set month = $i
		    break
		endif
		@ i++
	    end
	    set year = $argv[2]
	    breaksw
	default:
	    echo "usage: $0 [[month] year]"
	    exit 1
	    breaksw
endsw


if ( $month != 0 ) then
	set monthlist = ($month)
else
	set monthlist = (12 11 10 9 8 7 6 5 4 3 2 1)
endif

cat <<END-OF-HEADER
%!
% PostScript program to draw calendar
% Copyright (C) 1987 by Pipeline Associates, Inc.
% Permission is granted to modify and distribute this free of charge.

% /month should be set to a number from 1 to 12
% /year should be set to the year you want
% you can change the title and date fonts, if you want
% we figure out the rest
% won't produce valid calendars before 1800 (weird stuff happened
% in September of 1752)


/titlefont /Times-Bold def
/dayfont /Helvetica-Bold def
/eventfont /Times-Roman def

/month_names [ (January) (February) (March) (April) (May) (June) (July)
		(August) (September) (October) (November) (December) ] def

/prtnum { 3 string cvs show} def

/drawgrid {		% draw calendar boxes
	dayfont findfont 10 scalefont setfont
	0 1 6 {
		dup dup 100 mul 40 moveto
		[ (Sunday) (Monday) (Tuesday) (Wednesday) (Thursday) (Friday) (Saturday) ] exch get
		100 center
		100 mul 35 moveto
		1.0 setlinewidth
		0 1 5 {
			gsave
			100 0 rlineto 
			0 -80 rlineto
			-100 0 rlineto
			closepath stroke
			grestore
			0 -80 rmoveto
		} for
	} for
} def

/drawnums {		% place day numbers on calendar
	dayfont findfont 30 scalefont setfont
	/start startday def
	/days ndays def
	start 100 mul 5 add 10 rmoveto
	1 1 days {
		/day exch def
		gsave
		day start add 7 mod 0 eq
		{
			submonth 0 eq
			{
				.8 setgray
			} if
		} if
		day start add 7 mod 1 eq
		{
			submonth 0 eq
			{
				.8 setgray
			} if
		} if
		day prtnum
		grestore
		day start add 7 mod 0 eq
		{
			currentpoint exch pop 80 sub 5 exch moveto
		}
		{
			100 0 rmoveto
		} ifelse
	} for
} def

/drawfill {		% place fill squares on calendar
	/start startday def
	/days ndays def
	0 35 rmoveto
	1.0 setlinewidth
	0 1 start 1 sub {
		gsave
		.9 setgray
		100 0 rlineto 
		0 -80 rlineto
		-100 0 rlineto
		closepath fill
		grestore
		100 0 rmoveto
	} for
	submonth 1 eq
	{
		/lastday 42 def
		600 -365 moveto
	}
	{
		/lastday 40 def
		400 -365 moveto
	} ifelse
	lastday -1 ndays start 1 add add
	{
		/day exch def
		gsave
		.9 setgray
		100 0 rlineto 
		0 -80 rlineto
		-100 0 rlineto
		closepath fill
		grestore
		day 7 mod 1 eq
		{
			600 -365 80 add moveto
		}
		{
			-100 0 rmoveto
		} ifelse
	} for
} def

/isleap {               % is this a leap year?
	year 4 mod 0 eq         % multiple of 4
	year 100 mod 0 ne       % not century
	year 400 mod 0 eq or and       % unless its a century divisible by 400
} def


/days_month [ 31 28 31 30 31 30 31 31 30 31 30 31 ] def

/ndays {		% number of days in this month
	days_month month 1 sub get
	month 2 eq	% Feb
	isleap and
	{
		1 add
	} if
} def

/startday {		% starting day-of-week for this month
	/off year 2000 sub def	% offset from start of "epoch"
	off
	off 4 idiv add		% number of leap years
	off 100 idiv sub	% number of centuries
	off 400 idiv add        % Gregorian correction
	6 add 7 mod 7 add 	% offset from Jan 1 2000
	/off exch def
	1 1 month 1 sub {
		/idx exch def
		days_month idx 1 sub get
		idx 2 eq
		isleap and
		{
			1 add
		} if
		/off exch off add def
	} for
	off 7 mod		% 0--Sunday, 1--monday, etc.
} def

/prtmoon  {	% phase day prtmoon -
	   dup 0 ne
	   {
	    gsave
	    .1 setlinewidth
	    0 setgray
	    /day 2 1 roll def
	    day start add 1 sub 7 mod 100 mul 85 add
	    day start add 1 sub 7 div truncate -80 mul 20 add
	    newpath
	    10 0 360 arc
            0 ne {stroke} {fill} ifelse
	    grestore
           }
	   {
		pop pop
	   }
	   ifelse
} def

/prtevent {		% event-string day prtevent -
			%  print out an event
	    /day 2 1 roll def
	    day start add 1 sub 7 mod 100 mul
	    day start add 1 sub 7 div truncate -80 mul 
	    -5 
	    numevents day start add get -10 mul add
	    numevents
	    day start add 
	    numevents day start add get 1 add
	    put
	    add moveto
	    show
} def

/drawevents {		% read in a file full of events; print
			%  the events for this month
	/numevents
	[0 0 0 0 0 0 0
	 0 0 0 0 0 0 0
	 0 0 0 0 0 0 0
	 0 0 0 0 0 0 0
	 0 0 0 0 0 0 0
	 0 0 0 0 0 0 0] def 
	 eventfont findfont 9 scalefont setfont
	 0 2 holidays length 2 sub {
		dup
		1 add holidays 2 1 roll get
		2 1 roll holidays 2 1 roll get
		prtevent
	} for
	0 newmoon1 prtmoon
	1 fullmoon1 prtmoon
	0 newmoon2 prtmoon
	1 fullmoon2 prtmoon
} def

/center {		% center string in given width
	/width exch def
	/str exch def width str 
	stringwidth pop sub 2 div 0 rmoveto str show
} def

/calendar
{
	titlefont findfont 48 scalefont setfont
	0 60 moveto
	/month_name month_names month 1 sub get def
	month_name show
	/yearstring year 10 string cvs def
	700 yearstring stringwidth pop sub 60 moveto
	yearstring show

	0 0 moveto
	drawnums

	0 0 moveto
	drawfill

	eventflag {
		0 0 moveto
		drawevents
	} if

	0 0 moveto
	drawgrid
} def

%% EndProlog

END-OF-HEADER


foreach month ($monthlist)
if ( -e $home/.holiday ) then
    set noglob
    set holidays = \
       (   `grep  ^$month\: $home/.holiday | awk -F: '{print $2,"(",$3,")" }'`  )
else
    set noglob
    set holidays
endif
if ( -e $home/.holiday.$year ) then
    set noglob
    set holidays = ( \[ "$holidays" `grep  ^$month\: $home/.holiday.$year | awk -F: '{print $2,"(",$3,")" }'` ] )
else
    set noglob
    set holidays = ( \[ "$holidays" ] )
endif
if ( -e $home/.holiday.moon.$year ) then
    set fullmoon = `grep ^$month $home/.holiday.moon.$year | grep full | awk '{print $2}'`
    set newmoon =  `grep ^$month $home/.holiday.moon.$year | grep new | awk '{print $2}'`
else
    set fullmoon = (0 0)
    set newmoon = (0 0)
endif
if ( "$fullmoon" == "" ) set fullmoon = (0 0)
if ( "$newmoon" == "" ) set newmoon = (0 0)
if ( $#fullmoon == 1 ) set fullmoon = ($fullmoon 0)
if ( $#newmoon == 1 ) set newmoon = ($newmoon 0)

cat <<END-OF-CALENDAR

/month $month def
/year $year def

/holidays $holidays def
/fullmoon1 $fullmoon[1] def
/newmoon1 $newmoon[1] def
/fullmoon2 $fullmoon[2] def
/newmoon2 $newmoon[2] def


/eventflag true def
90 rotate
50 -120 translate
/submonth 0 def
calendar

/eventflag false def
month 1 sub 0 eq
{
	/lmonth 12 def
	/lyear year 1 sub def
}
{
	/lmonth month 1 sub def
	/lyear year def
} ifelse
month 1 add 13 eq
{
	/nmonth 1 def
	/nyear year 1 add def
} 
{
	/nmonth month 1 add def
	/nyear year def
} ifelse
/submonth 1 def
/year lyear def
/month lmonth def
500 -365 translate
gsave
.138 .138 scale
10 -120 translate
calendar
grestore
/submonth 1 def
/year nyear def
/month nmonth def
100 0 translate
gsave
.138 .138 scale
10 -120 translate
calendar
grestore

showpage
END-OF-CALENDAR

end