#!/bin/ksh
# bigcal: print a big calendar
# @(#) bigcal.ksh 1.0 91/01/13 
# 90/07 John H. DuBois III (john@armory.com)
# 91/01/13 cleaned up slightly.

# A calendar with squares, with digits printed in various fonts,
# optionally filled with data is generated from the output of "cal".
# Use -h to for help.

# The output of "cal" is expected to be in a format like this:

#   June 1990
# S  M Tu  W Th  F  S
#                1  2
# 3  4  5  6  7  8  9
#10 11 12 13 14 15 16
#17 18 19 20 21 22 23
#24 25 26 27 28 29 30

PATH=/bin:/usr/bin

# Set defaults.  The meaning of these vars is described in help.
box=ascii
t=- b=- l=\| r=\| tl=- bl=- tr=- br=- m=+ h=- v=\|	# Set ascii box chars.
hsize=9
vsize=5
month=`date +%h`	# default month is current month
digits=1

# Process arguments.
# Arguments of the form foo=bar set var foo to bar;
# -x sets x to true
# Note: Unrecognized vars in foo=bar var sets are ignored.
while [ $# -gt 0 ]; do
	case $1 in
		*?=*) eval ${1%%=*}=\"${1#*=}\";;
		-h) echo \
'bigcal: print a big (box-style) calendar.
Options:
-h: print this help.

month=<month>: print a calendar for month "month".  "month" should be in the
same format recognized by /bin/cal, except that a single numeric argument
for month will be interpreted as a month number rather than a year.
By default, a calendar for the current month is printed.

box=[ascii|ibm1|ibm2]: The calendar boxes are printed using characters
from the specified set.  ascii is the default and will work on any ascii
device.  ibm1 and ibm2 look much better but will only work on devices that
implement the IBM extended character set; for example, a PC console, IBM-
compatible graphic printers, and Wyse 60 terminals.

digits=[1|5|ibm1_3|ibm1_5|ibm2_3|ibm2_5]: draw date digits using the
specified font.  1 is the normal single-character digits; 5 is a font
drawn with ascii characters that is 5 characters high; ibmn_h is a
font drawn using the n-line box drawing characters that is h characters high.
The ibm work on devices as described for box.'
if [ -t 0 -a -t 1 ]; then
	echo -n "\n-press return for more- "
	read
fi
echo '
t=, b=, l=, r=, tl=, bl=, tr=, br=, m=, h=, v=: Box drawing characters.
The box drawing characters can be set explicitely.  If they are set explicitely
then "box=" should not be given.  The characters are follows:
t, b, l, r: used where a bar joins with the outer {top, left, bottom, right}
bar of the calendar.
tl, bl, tr, br: used for the {top left, bottom left, top right, bottom right}
corner of the calendar.
m: middle; used where a horizontal and vertical bar cross each other.
h, v: used for drawing {horizontal, vertical} bars.

hsize=<width>: The interior width of individual date boxes is set to <width>.
The default is 10.

vsize=<height>: The interior height of individual date boxes is set to 
<height>.  The default is 5.'
			exit 0;;
		-[f]) eval ${1#-}=true;;
		*) echo "bigcal: bad argument $1.  Use -h for help.  Exiting."; exit 1
	esac
	shift
done

# if month is given numerically without a year, 
# add year to it because /bin/date assumes a single numeric argument is a year.
[[ $month = +([0-9]) ]] && month="$month "`date +19%y`

# set chars for drawing box-style digits.  Names explained in help data.
case $digits in
	ibm2_*) t="\0313" b="\0312" l="\0314" r="\0271" tl="\0311" bl="\0310"
		  tr="\0273" br="\0274" m="\0316" h="\0315" v="\0272";;
	ibm1_*) t="\0302" b="\0301" l="\0303" r="\0264" tl="\0332" bl="\0300"
		  tr="\0277" br="\0331" m="\0305" h="\0304" v="\0263";;
esac

# Set arrays n0..n9 and nb (blank) to digit fonts.
# Each element of the array is a row of chars for the digit.
# digheight is the number of rows in each digit. 
# gap is the string used to separate two digits.
# digwidth is the width of two digits + gap
case $digits in
	1) digheight=1 digwidth=2 gap=;
	   n0=0 n1=1 n2=2 n3=3 n4=4 n5=5 n6=6 n7=7 n8=8 n9=9 nb=' ';;
	5) digheight=5 digwidth=9 gap=' ';
set -A n0 \
' __ ' \
'/  \' \
'|  |' \
'|  |' \
'\__/'

set -A n1 \
'    ' \
'  | ' \
'  | ' \
'  | ' \
'  | '

set -A n2 \
' __ ' \
'/  |' \
'  / ' \
' /  ' \
'/___'

set -A n3 \
' __ ' \
'/  \' \
'  _/' \
'   \' \
'\__/'
set -A n4 \
'    ' \
'| | ' \
'|_|_' \
'  | ' \
'  | '

set -A n5 \
'____' \
'|   ' \
'|__ ' \
'   \' \
'\__/'

set -A n6 \
' __ ' \
'/  `' \
'|__ ' \
'|  \' \
'\__/'

set -A n7 \
'____' \
'   /' \
'  / ' \
' /  ' \
'/   '

set -A n8 \
' __ ' \
'/  \' \
'\__/' \
'/  \' \
'\__/'

set -A n9 \
' __ ' \
'/  \' \
'\__|' \
'   |' \
'\__/'

set -A nb \
'    ' \
'    ' \
'    ' \
'    ' \
'    '
	;;
	ibm1_3|ibm2_3) digheight=3 digwidth=5 gap=' '
set -A n0 -- \
"$tl$tr" \
"$v$v" \
"$bl$br"

set -A n1 -- \
"$tr " \
"$v " \
"$b "

set -A n2 -- \
" $tr" \
"$tl$br" \
"$bl "

set -A n3 -- \
"$tr " \
"$r " \
"$br "

set -A n4 -- \
"$v$v" \
"$bl$r" \
" $v"

set -A n5 -- \
"$tl " \
"$bl$tr" \
" $br"

set -A n6 -- \
"$tl " \
"$l$tr" \
"$bl$br"

set -A n7 -- \
"$h$tr" \
" $v" \
" $v"

set -A n8 -- \
"$tl$tr" \
"$l$r" \
"$bl$br"

set -A n9 -- \
"$tl$tr" \
"$bl$r" \
" $v"

set -A nb -- \
"  " \
"  " \
"  "
	;;
	ibm1_5|ibm2_5) digheight=5 digwidth=7 gap=' '
set -A n0 -- \
"$tl$h$tr" \
"$v $v" \
"$v $v" \
"$v $v" \
"$bl$h$br"

set -A n1 -- \
" $tr " \
" $v " \
" $v " \
" $v " \
" $b "

set -A n2 -- \
" $h$tr" \
"  $v" \
"$tl$h$br" \
"$v  " \
"$bl$h "

set -A n3 -- \
" $h$tr" \
"  $v" \
" $h$r" \
"  $v" \
" $h$br"

set -A n4 -- \
"$v $v" \
"$v $v" \
"$bl$h$r" \
"  $v" \
"  $v"

set -A n5 -- \
"$tl$h " \
"$v  " \
"$bl$h$tr" \
"  $v" \
" $h$br"

set -A n6 -- \
"$tl$h " \
"$v  " \
"$l$h$tr" \
"$v $v" \
"$bl$h$br"

set -A n7 -- \
"$h$h$tr" \
"  $v" \
"  $v" \
"  $v" \
"  $v"

set -A n8 -- \
"$tl$h$tr" \
"$v $v" \
"$l$h$r" \
"$v $v" \
"$bl$h$br"

set -A n9 -- \
"$tl$h$tr" \
"$v $v" \
"$bl$h$r" \
"  $v" \
"  $v"

set -A nb -- \
"   " \
"   " \
"   " \
"   " \
"   "
	;;
	ibm1_5w|ibm2_5w) digheight=5 digwidth=9 gap=' '
set -A n0 -- \
"$tl$h$h$tr" \
"$v  $v" \
"$v  $v" \
"$v  $v" \
"$bl$h$h$br"

set -A n1 -- \
" $tr  " \
" $v  " \
" $v  " \
" $v  " \
" $b  "

set -A n2 -- \
" $h$h$tr" \
"   $v" \
"$tl$h$h$br" \
"$v   " \
"$bl$h$h "

set -A n3 -- \
" $h$h$tr" \
"   $v" \
" $h$h$r" \
"   $v" \
" $h$h$br"

set -A n4 -- \
"$v  $v" \
"$v  $v" \
"$bl$h$h$r" \
"   $v" \
"   $v"

set -A n5 -- \
"$tl$h$h " \
"$v   " \
"$bl$h$h$tr" \
"   $v" \
" $h$h$br"

set -A n6 -- \
"$tl$h$h " \
"$v   " \
"$l$h$h$tr" \
"$v  $v" \
"$bl$h$h$br"

set -A n7 -- \
"$h$h$h$tr" \
"   $v" \
"   $v" \
"   $v" \
"   $v"

set -A n8 -- \
"$tl$h$h$tr" \
"$v  $v" \
"$l$h$h$r" \
"$v  $v" \
"$bl$h$h$br"

set -A n9 -- \
"$tl$h$h$tr" \
"$v  $v" \
"$bl$h$h$r" \
"   $v" \
"   $v"

set -A nb -- \
"    " \
"    " \
"    " \
"    " \
"    "
	;;
	*)	echo "bigcal: bad value for digits: $digits.  Exiting."; exit 1;;
esac

# Select box drawing characters.
# If default (ascii) selected, chars are defaults set for ascii
# before argument processing, unless modified during arg processing
# by setting of t, b, etc. on command line.
case $box in
	ascii) ;;
	ibm2) t="\0313" b="\0312" l="\0314" r="\0271" tl="\0311" bl="\0310"
		  tr="\0273" br="\0274" m="\0316" h="\0315" v="\0272";;
	ibm1) t="\0302" b="\0301" l="\0303" r="\0264" tl="\0332" bl="\0300"
		  tr="\0277" br="\0331" m="\0305" h="\0304" v="\0263";;
	*)	  echo "bigcal: bad box font name $box.  Exiting."; exit 1;;
esac

# printhorbar size leftchar middlechar rightchar horchar
# Print a horizontal bar.
# size is the interior length of each section
# leftchar is the char to start the bar with
# middlechar is the char to join two sections with
# rightchar is the char to end the bar with
# horchar is the char to draw each section with
# 7 sections are drawn.
# Example: the bar /---+---+---+---+---+---+---\ is generated by
# printhorbar 3 / + '\' -
printhorbar() {
	typeset -i i=0
	# generate bar section of specified length
	horbar=
	while [ i -lt $1 ]; do
		horbar=$horbar$5
		let i+=1
	done
	echo -n $2$horbar		# print left char & first section
	i=0
	while [ i -lt 6 ]; do
		echo -n $3$horbar
		let i+=1
	done
	echo $4
}

# printver size sepchar datasize [<field1data> [... <field7data>]]
# print a section of a rectangle
# size is the interior (horizontal) length of each section
# sepchar is the char to separate each section with
# datasize is the length each data field will be after escape sequences
# in them are resolved by "echo"
# If it is 0, data are of various lengths, but there are no escape sequences
# so data fields can be forced to a specific length
printver() {
	sep=$2
	if [ $3 = 0 ]; then
		# set field var to length size+length(sep)
		typeset -L$(($1 + ${#sep})) field
	else
		# set blank padding to length size-datasize
		typeset -L$(($1 - $3)) blanks
	fi
	# size typeset does not have effect until assignment to var is made
	blanks=
	shift 3
	typeset -i i=0
	while [ i -lt 7 ]; do
		field="$sep$1"
		echo -n "$field$blanks"
		let i+=1
		[ $# -gt 0 ] && shift
	done
	echo $sep
}

# if -f given, read data for fields into d[] from stdin
typeset -i i=1
if [ -n "$f" ]; then
	while read d[i] && [ i -lt 32 ]; do 
		let i+=1; 
	done
fi

# run cal & put its output on coprocess descriptor
cal $month |&

# read month & year header from calendar
read -p header
if [[ "$header" = cal:* ]]; then
	echo "Bad month specified."
	exit 1
fi

# set header to be right-justified with size such that it
# is printed at the middle of the big calendar
typeset -R$(( ( (hsize + 1) * 7 + 1 + ${#header}) / 2 )) header
echo "$header";

# read & discard weekday header
read -p dateline

# print top bar of calendar
printhorbar $hsize $tl $t $tr $h

# Print weekday header.  If interior size is less than 9 
# (in which case at least Wednesday would not completely fit),
# abbreviate all days to 3-letter prefix
set -A days Sunday Monday Tuesday Wednesday Thursday Friday Saturday
[ hsize -lt 9 ] && typeset -L3 days
printver $hsize $v 0 ${days[*]}

i=0
while read -p dateline; do
	[ -z "$dateline" ] && break;	# discard blank line printed by cal

	# count numbers in this row.  If less than 7 and this is the first
	# row of numbers, set nulldates to a number of null arguments
	# equal to the difference, so that when dateline is prefixed with
	# them and converted to array the numbers will be in the correct
	# array position rather than elements 0, 1, etc.
	set $dateline
	unset nulldates
	if [ $i = 0 -a $# -lt 7 ]; then
		typeset -L$(( (7 - $#) * 3 )) nulldates
		nulldates='"" "" "" "" "" "" "" '
	fi
	eval set -A dateline $nulldates $dateline

	# for each day of the week, use the 0, 1 or 2 digits of the date given
	# by cal to create the digit array for the week in the chosen font.
	# nb[] (the blank digit) is used if there are less than two digits.
	# arrays wdig1n and wdig2n are the first and second digits of the date
	# for weekday n, where n is 0-6 for Sunday-Saturday.
	i=0
	IFS=:
	while [ i -lt 7 ]; do
		dt=${dateline[i]}
		case ${#dt} in
		0) 
			eval set -A wdig1$i '${nb[@]}'
			eval set -A wdig2$i '${nb[@]}';;
		1)
			eval set -A wdig1$i -- '${n'$dt'[@]}';
			eval set -A wdig2$i '${nb[@]}';;
		2)
			eval set -A wdig1$i -- '${n'${dt%?}'[@]}'
			eval set -A wdig2$i -- '${n'${dt#?}'[@]}';;
		esac
		let i+=1
	done

	# print horizontal bar to separate calendar rectangles
	printhorbar $hsize $l $m $r $h

	# for each row of characters that digits are constructed from,
	# print a row of each character separated by $gap.
	# Each pair of char rows and gap becomes a data argument to printver.
	i=0
	while [ i -lt digheight ]; do
		printver $hsize $v $digwidth \
		"${wdig10[i]}$gap${wdig20[i]}" \
		"${wdig11[i]}$gap${wdig21[i]}" \
		"${wdig12[i]}$gap${wdig22[i]}" \
		"${wdig13[i]}$gap${wdig23[i]}" \
		"${wdig14[i]}$gap${wdig24[i]}" \
		"${wdig15[i]}$gap${wdig25[i]}" \
		"${wdig16[i]}$gap${wdig26[i]}"
		let i+=1
	done

	# Print field data & finish drawing vertical lines of boxes.
	# o[] has one element for each day of the week.
	# Each element holds the date for that day of the week.
	# The date is used as an index into d[], which holds the field data
	# for each day of the month.
	# For each line to be printed:
	#    *The first word of the data for each day of the week is sent to 
	#     printver().  
	#    *That word is removed from the data.
	IFS=" "
	set -A o "${dateline[@]}"
	while [ i -lt vsize ]; do
		# eval expands the ${o[n]}'s into real indexes;
		# then ${d[n]%% *} removes all but the first word
		eval printver $hsize '$v' 0 \
		\"${d[${o[0]}]%% *}\" \"${d[${o[1]}]%% *}\" \
		\"${d[${o[2]}]%% *}\" \"${d[${o[3]}]%% *}\" \
		\"${d[${o[4]}]%% *}\" \"${d[${o[5]}]%% *}\" \
		\"${d[${o[6]}]%% *}\"
		# eval expands the ${o[n]}'s into real indexes;
		# then ${d[n]##*([! ])?( )} removes the leftmost word,
		# identified as a string of non-spaces followed by an optional
		# space (optional because the last word may not be followed by
		# a space)
		eval d[${o[0]}]=\"${d[${o[0]}]##*([! ])?( )}\" \
		d[${o[1]}]=\"${d[${o[1]}]##*([! ])?( )}\" \
		d[${o[2]}]=\"${d[${o[2]}]##*([! ])?( )}\" \
		d[${o[3]}]=\"${d[${o[3]}]##*([! ])?( )}\" \
		d[${o[4]}]=\"${d[${o[4]}]##*([! ])?( )}\" \
		d[${o[5]}]=\"${d[${o[5]}]##*([! ])?( )}\" \
		d[${o[6]}]=\"${d[${o[6]}]##*([! ])?( )}\"
		let i+=1
	done
done
printhorbar $hsize $bl $b $br $h	# print bottom of calendar
