[comp.sources.misc] v06i081: another calendar program

allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) (03/26/89)

Posting-number: Volume 6, Issue 81
Submitted-by: awr@genrad.COM (Andrew W. Rogers)
Archive-name: calen

Here's another calendar program which I think is considerably more
useful than pcal.  For starters, the calendars really do occupy a full
line-printer page; they also include small calendars for the previous
and subsequent months along with a month/year heading in 5x9
dot-matrix characters, printed with overstrikes (on line printers
capable of handling them).  Options are available to select:

	1) right- or left- justification of the dates within the boxes
	2) mixed- or upper-case names of months and days
	3) overstrike sequence used to print month/year heading (useful for
	   printers which do not support overstrikes, since a single character
	   can be specified - try "-o@")
	4) number of blank lines at top of page (useful to center calendar
	   vertically when operators are careless about aligning paper)

I wrote this in GE Time-Sharing FORTRAN when I was a teenager and have
continued to tweak it through the years; for many years I used it as
my first post-"Hello, world!" program when learning new languages, and
now use it to test the compilers I write.
	   
Have fun...
--
Andrew W. Rogers	{decvax,husc6,mit-eddie}!genrad!teddy!awr
			awr@teddy.genrad.com
#! /bin/sh
# This file was wrapped with "dummyshar".  "sh" this file to extract.
# Contents:  calen.c
echo extracting 'calen.c'
if test -f 'calen.c' -a -z "$1"; then echo Not overwriting 'calen.c'; else
sed 's/^X//' << \EOF > 'calen.c'
X/*
X *	Calendar program - one month per page
X *
X *	Originally written in FORTRAN-IV for GE Timesharing, 10/65
X *	Re-coded in C for UNIX, 3/83
X *
X *	Author: AW Rogers
X *
X *	Parameters:
X *
X *		calen yy		generates calendar for year yy
X *
X *		calen mm yy [len]	generates calendar for len months
X *					(default = 1) starting with mm/yy
X *
X * 	Option flags (must precede params):
X *
X *		-l			left-justify dates (default)
X *		-r			right-justify dates
X *		-m			mixed-case output (default)
X *		-u			upper-case output
X *		-o[seq]			use "seq" as overstrike sequence
X *					 for heading (default: HIX)
X *		-bN			add N blank lines after form-feed
X *
X *		Output is to standard output.
X *		
X */
X 
X#include <stdio.h>
X#include <ctype.h>
X
X#define FALSE   0
X#define TRUE    1
X 
X#define JAN 	1			/* significant months/years */
X#define FEB	2
X#define DEC	12
X#define MINYR	1753
X#define MAXYR	9999
X 
X#define SOLID	0			/* pseudo-enumeration for line styles */
X#define OPEN	1
X
X#define LEFT	0			/* ... and justification of dates     */
X#define RIGHT	1
X
X#define MIXED	0			/* ... and case of output text	      */
X
X#define UPPER	1
X
X#define	OVERSTRIKE	"HIX"		/* overstrike sequence for month/year */
X#define MAX_OVERSTR	3		/* maximum overstrikes permitted */
X
X#define isLeap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))	/* leap year macro */
X 
Xtypedef struct				/* info for a single month */
X    {
X    int 	mm;
X    int		yy;
X    char 	mmname[10];
X    char	dates[6][7][3];
X    } monthRec;
X 
Xtypedef monthRec *mptr;			/* pointer to above struct */
X
X/* globals corresponding to command-line flags */
X
Xint just = LEFT;			/* default justification of dates */
Xint ocase = MIXED;			/* default case for output */
Xint nblank = 0;				/* default blank lines after FF */
Xchar *seq = OVERSTRIKE;			/* default overstrike sequence */
X
X
X/*
X *	Main - gets and validates parameters, opens output file, executes
X *	loop to fill and print months of calendar, closes output file
X */
Xmain(argc, argv)
X    int argc;
X    char *argv[];
X    {
X    int nmonths;			/* consecutive months to print */
X    int badopt = FALSE;			/* flag set if bad option */
X    int badpar = FALSE;			/* flag set if bad param  */
X    monthRec mRec[3];			/* space for main and small calendars */
X    mptr prev = &mRec[0], 		/* pointers to calendars (initially) */
X	 curr = &mRec[1],
X	 next = &mRec[2],
X	 temp;
X
X    /* Get command line flags */
X
X    while (argc > 1 && argv[1][0] == '-')
X        {
X        switch (argv[1][1])
X	    {
X	    case 'b':
X		sscanf(&argv[1][2], "%d", &nblank);
X		break;
X	    case 'l':
X		just = LEFT;
X		break;
X	    case 'r':
X		just = RIGHT;
X		break;
X	    case 'm':
X		ocase = MIXED;
X		break;
X	    case 'u':
X		ocase = UPPER;
X		break;
X	    case 'o':
X		if (argv[1][2] != '\0')
X		    seq = &argv[1][2];
X		break;
X	    default:
X		fprintf(stderr, "Invalid flag: %s\n", argv[1]);
X		badopt = TRUE;
X		break;
X	    }
X        argv++;
X	argc--;
X	}
X
X    if (badopt)
X	fprintf(stderr, "Valid flags are -b -l -m -o -r -u\n");
X
X    /* Get and validate parameters */
X    
X    if (argc == 2)		/* only one arg - treat as yy */
X	{
X	sscanf(argv[1], "%d", &curr->yy);
X	curr->mm = JAN;
X	nmonths = 12;
X	}
X 
X    else if (argc >= 3)		/* two or more - treat as mm yy [len] */
X	{
X	sscanf(argv[1], "%d", &curr->mm);
X	sscanf(argv[2], "%d", &curr->yy);
X	if (argc >= 4)
X	    sscanf(argv[3], "%d", &nmonths);
X	}
X 
X    else			/* none specified - get interactively */
X	{
X	fprintf(stderr, "Enter calendar specs (month year length): ");
X	scanf("%d %d %d", &curr->mm, &curr->yy, &nmonths);
X	}
X 
X    if (curr->yy > 0 && curr->yy < 100)		/* nn -> 19nn */
X	curr->yy += 1900;
X 
X    if (nmonths < 1)				/* default for month count */
X	nmonths = 1;
X 
X    if (curr->mm < JAN || curr->mm > DEC)	/* validate month/year */
X      {
X      fprintf(stderr, "Month %d not in range %d .. %d\n", curr->mm, JAN, DEC);
X      badpar = TRUE;
X      }
X
X    if (curr->yy < MINYR || curr->yy > MAXYR)
X      {
X      fprintf(stderr, "Year %d not in range %d .. %d\n", curr->yy, MINYR,
X	      MAXYR);
X      badpar = TRUE;
X      }
X
X    if (badpar)		/* quit if month or year invalid */
X      exit(1);
X 
X    /* fill in calendars for previous and current month */
X 
X    prev->mm = (curr->mm == JAN) ? DEC : curr->mm - 1;
X    prev->yy = (curr->mm == JAN) ? curr->yy - 1 : curr->yy;
X    fillCalendar(prev);
X 
X    fillCalendar(curr);
X 
X    /*
X     * Main loop: print each month of the calendar (with small calendars for
X     * the preceding and following months in the upper corners).  The current
X     * and next months' calendars can be reused the following month; only
X     * the 'next' calendar need be recalculated each time.
X     */
X 
X    for (; nmonths > 0 && curr->yy <= MAXYR; nmonths--)	/* main loop */
X	{
X	next->mm = (curr->mm == DEC) ? JAN : curr->mm + 1;
X	next->yy = (curr->mm == DEC) ? curr->yy + 1 : curr->yy;
X	fillCalendar(next);			/* fill in following month */
X 
X	printCalendar(prev, curr, next);
X 
X	temp = prev;				/* swap the three months */
X	prev = curr;
X	curr = next;
X	next = temp;
X	}
X 
X    }
X
X/*
X *	Print the calendar for the current month, generating small calendars
X *	for the previous and following months in the upper corners and the
X *	month/year (in large characters) centered at the top.
X */
XprintCalendar(prev, curr, next)
X    mptr prev;			/* Previous month (upper-left corner)  */
X    mptr curr;			/* Current month (main calendar)       */
X    mptr next;			/* Next month (upper-right corner)     */
X    {
X    int nchars, i, j;
X    static char *mc_wkday[] =
X	{
X	" Sunday  ", " Monday  ", " Tuesday ", "Wednesday", "Thursday ",
X	" Friday  ", "Saturday "
X	};
X    static char *uc_wkday[] =
X	{
X	" SUNDAY  ", " MONDAY  ", " TUESDAY ", "WEDNESDAY", "THURSDAY ",
X	" FRIDAY  ", "SATURDAY "
X	};
X
X    char **wkday;				/* pointer to one of above */
X    char *blanks = "                     "; 	/* 21 blanks for centering */
X    char *padding;				/* Pointer into 'blanks'   */
X    char monthAndYear[20];			/* Work area 		   */
X    char *ovr;					/* overstrike sequence	   */
X 
X    nchars = strlen(curr->mmname);		/* set up month/year heading */
X    padding = blanks + (3 * (nchars - 3));	/* and center it  	     */
X    sprintf(monthAndYear, "%s%5d", curr->mmname, curr->yy);
X 
X    printf("\f\n");			/* print month/year in large chars */
X    for (i = 0; i < nblank; i++)
X	printf("\n");
X
X    for (i = 0; i < 9; i++)		/* surrounded by small calendars   */
X        {
X	for (ovr = seq;			/* overstruck lines first */
X	     ovr < seq + MAX_OVERSTR - 1 && *(ovr+1);
X	     ovr++) 
X	    {
X	    printf("%20s%s", " ", padding);
X	    printHdr(monthAndYear, i, *ovr);
X	    printf("\r");
X	    }
X	printSmallCal(prev, i);		/* then small calendars, etc. */
X	printf("%s", padding);
X	printHdr(monthAndYear, i, *ovr);
X	printf(" %s", padding);
X        printSmallCal(next, i);
X        printf("\n");
X        }
X 
X    printf("\n");			/* print the weekday names */
X    print_line(1, SOLID);
X    print_line(1, OPEN);
X    printf("  ");
X    wkday = ocase == UPPER ? uc_wkday : mc_wkday;
X    for (j = 0; j < 7; j++)
X        printf("|%13.9s    ", wkday[j]);
X    printf("|\n");
X    print_line(1, OPEN);
X    
X    for (i = 0; i < 4; i++)		/* print first four rows */
X        {
X        print_line(1, SOLID);
X        print_dates(curr, i, just);
X        print_line(7, OPEN);
X        }
X 
X    print_line(1, SOLID);		/* print bottom row */
X    print_dates(curr, 4, just);
X    print_line(2, OPEN);
X    print_divider(curr->dates[5]);	/* divider for 23/30, 24/31 */
X
X    print_line(3, OPEN);
X    print_dates(curr, 5, !just);	/* print 6th line (30/31) at bottom */
X    print_line(1, SOLID);
X    }
X
X/*
X *	Fill in the month name and date fields of a specified calendar record
X *	(assumes mm, yy fields already filled in)
X */
XfillCalendar(month)
X    mptr month;			/* Pointer to month info record */
X    {
X    typedef struct		/* Local info about months */
X	{
X	char *name[2];		/* Name of month (mixed/upper-case)	*/
X	int offset[2];		/* Offset of m/1 from 1/1 (non-leap/leap) */
X	int length[2];		/* Length (non-leap/leap)		*/
X	} monthInfo;
X 
X    static monthInfo info[12] = {
X	{ {"January",	"JANUARY"},	{0, 0},	{31, 31} },
X	{ {"February",	"FEBRUARY"},	{3, 3},	{28, 29} },
X	{ {"March",	"MARCH"},	{3, 4},	{31, 31} },
X	{ {"April",	"APRIL"},	{6, 0},	{30, 30} },
X	{ {"May",	"MAY"},		{1, 2},	{31, 31} },
X	{ {"June",	"JUNE"},	{4, 5},	{30, 30} },
X	{ {"July",	"JULY"},	{6, 0},	{31, 31} },
X	{ {"August",	"AUGUST"},	{2, 3},	{31, 31} },
X	{ {"September",	"SEPTEMBER"},	{5, 6},	{30, 30} },
X	{ {"October",	"OCTOBER"},	{0, 1},	{31, 31} },
X	{ {"November",	"NOVEMBER"},	{3, 4},	{30, 30} },
X	{ {"December",	"DECEMBER"},	{5, 6},	{31, 31} }
X    };
X 
X    int i, first, last, date = 0, y = month->yy, m = month->mm-1;
X    int leap = isLeap(y);
X 
X    first = (y + (y-1)/4 - (y-1)/100 + (y-1)/400 + info[m].offset[leap]) % 7;
X    last = first + info[m].length[leap] - 1;
X 
X    for (i = 0; i < 42; i++)			/* fill in the dates */
X	if (i < first || i > last)
X	    strcpy(month->dates[i/7][i%7], "  ");
X	else
X	    sprintf(month->dates[i/7][i%7], "%2d", ++date);
X 
X    strcpy(month->mmname, info[m].name[ocase]);	/* copy name of month */
X    } 
X
X/*
X *	Print one line of a small calendar (previous and next months in
X *	upper left and right corners of output)
X */
XprintSmallCal(month, line)
X    mptr month;			/* Month info record pointer	*/
X    int line;			/* Line to print (see below)	*/
X    {
X    int i;
X  
X    switch (line)
X	{
X	case 0:		/* month/year at top */
X	    printf("   %-10s%4d   ", month->mmname, month->yy);
X	    break;
X	case 1:		/* blank line */
X	    printf("%20s", " ");
X	    break;
X	case 2:		/* weekdays */
X	    printf(ocase == UPPER ? "SU MO TU WE TH FR SA" :
X				    "Su Mo Tu We Th Fr Sa");
X	    break;
X	default:	/* line of calendar */
X	    for (i = 0; i <= 5; i++)
X    		printf("%s ", month->dates[line-3][i]);
X	    printf("%s", month->dates[line-3][6]);
X	    break;
X	}
X    }
X
X/*
X *	Print n lines in selected style
X */
Xprint_line(n, style)
X    int n;			/* Number of lines to print (> 0)   */
X    int style;			/* SOLID or OPEN 		    */
X    {
X    int i;
X    char *fmt1 = (style == SOLID) ? "+-----------------" :
X				    "|                 " ;
X    char *fmt2 = (style == SOLID) ? "+\n" : "|\n" ;
X
X    for (; n > 0; n--)
X	{
X	printf("  ");
X	for (i = 0; i < 7; i++)
X	    printf(fmt1);
X	printf(fmt2);
X	}
X    }
X
X
X
X/*
X *	Print line of large calendar (open w/left- or right-justified dates)
X */
Xprint_dates(month, line, just)
X    mptr month;			/* Month info record pointer	*/
X    int line;			/* Line to print (0-5)		*/
X    int just;			/* justification (LEFT / RIGHT)	*/
X    {
X    int i;
X    char *fmt = (just == LEFT) ? "| %-16s" : "|%16s " ;
X
X    printf("  ");
X    for (i = 0; i < 7; i++)
X	printf(fmt, month->dates[line][i]);
X    printf("|\n");
X    }
X
X/*
X *	Print divider between 23/30 and 24/31
X */
Xprint_divider(dates)
X    char dates[7][3];
X    {
X    int j;
X
X    printf("  ");
X    for (j = 0; j < 7; j++)
X        if (strcmp(dates[j], "  ") == 0)
X    	    printf("|                 ");
X        else
X    	    printf("|_________________");
X    printf("|\n");
X    }
X
X 
X/*
X *	Print LS 6 bits of n (0 = ' '; 1 = selected non-blank)
X */
Xdecode(n, c)
X    int n;				/* Number to print (0-31) */
X    char c;
X    {
X    int msk = 1 << 5;
X 
X    for (; msk; msk /= 2) 
X	printf("%c", (n & msk) ? c : ' ');
X    }
X 
X 
X/*
X *	Print one line of string in large characters
X */
XprintHdr(str, line, c)
X    char *str;				/* string to print		    */
X    int line;				/* line to print (0-8; else blanks) */
X    char c;				/* output character to use	    */
X    {
X 
X    /* 5x9 dot-matrix representations of A-Z, a-z, 0-9 */
X 
X    static char uppers[26][9] =
X	{
X	{14, 17, 17, 31, 17, 17, 17,  0,  0},	{30, 17, 17, 30, 17, 17, 30,  0,  0},	/* AB */
X	{14, 17, 16, 16, 16, 17, 14,  0,  0},	{30, 17, 17, 17, 17, 17, 30,  0,  0},	/* CD */
X	{31, 16, 16, 30, 16, 16, 31,  0,  0},	{31, 16, 16, 30, 16, 16, 16,  0,  0},	/* EF */
X	{14, 17, 16, 23, 17, 17, 14,  0,  0},	{17, 17, 17, 31, 17, 17, 17,  0,  0},	/* GH */
X	{31,  4,  4,  4,  4,  4, 31,  0,  0},	{ 1,  1,  1,  1,  1, 17, 14,  0,  0},	/* IJ */
X	{17, 18, 20, 24, 20, 18, 17,  0,  0},	{16, 16, 16, 16, 16, 16, 31,  0,  0},	/* KL */
X	{17, 27, 21, 21, 17, 17, 17,  0,  0},	{17, 17, 25, 21, 19, 17, 17,  0,  0},	/* MN */
X	{14, 17, 17, 17, 17, 17, 14,  0,  0},	{30, 17, 17, 30, 16, 16, 16,  0,  0},	/* OP */
X	{14, 17, 17, 17, 21, 18, 13,  0,  0},	{30, 17, 17, 30, 20, 18, 17,  0,  0},	/* QR */
X	{14, 17, 16, 14,  1, 17, 14,  0,  0},	{31,  4,  4,  4,  4,  4,  4,  0,  0},	/* ST */
X	{17, 17, 17, 17, 17, 17, 14,  0,  0},	{17, 17, 17, 17, 17, 10,  4,  0,  0},	/* UV */
X	{17, 17, 17, 21, 21, 21, 10,  0,  0},	{17, 17, 10,  4, 10, 17, 17,  0,  0},	/* WX */
X	{17, 17, 17, 14,  4,  4,  4,  0,  0},	{31,  1,  2,  4,  8, 16, 31,  0,  0}	/* YZ */
X	}; 
X 
X    static char lowers[26][9] =
X	{
X	{ 0,  0, 14,  1, 15, 17, 15,  0,  0},	{16, 16, 30, 17, 17, 17, 30,  0,  0},	/* ab */
X	{ 0,  0, 15, 16, 16, 16, 15,  0,  0},	{ 1,  1, 15, 17, 17, 17, 15,  0,  0},	/* cd */
X	{ 0,  0, 14, 17, 31, 16, 14,  0,  0},	{ 6,  9, 28,  8,  8,  8,  8,  0,  0},	/* ef */
X	{ 0,  0, 14, 17, 17, 17, 15,  1, 14},	{16, 16, 30, 17, 17, 17, 17,  0,  0},	/* gh */
X	{ 4,  0, 12,  4,  4,  4, 31,  0,  0},	{ 1,  0,  3,  1,  1,  1,  1, 17, 14},	/* ij */
X	{16, 16, 17, 18, 28, 18, 17,  0,  0},	{12,  4,  4,  4,  4,  4, 31,  0,  0},	/* kl */
X	{ 0,  0, 30, 21, 21, 21, 21,  0,  0},	{ 0,  0, 30, 17, 17, 17, 17,  0,  0},	/* mn */
X	{ 0,  0, 14, 17, 17, 17, 14,  0,  0},	{ 0,  0, 30, 17, 17, 17, 30, 16, 16},	/* op */
X	{ 0,  0, 15, 17, 17, 17, 15,  1,  1},	{ 0,  0, 30, 17, 16, 16, 16,  0,  0},	/* qr */
X	{ 0,  0, 15, 16, 14,  1, 30,  0,  0},	{ 8,  8, 30,  8,  8,  9,  6,  0,  0},	/* st */
X	{ 0,  0, 17, 17, 17, 17, 14,  0,  0},	{ 0,  0, 17, 17, 17, 10,  4,  0,  0},	/* uv */
X	{ 0,  0, 17, 21, 21, 21, 10,  0,  0},	{ 0,  0, 17, 10,  4, 10, 17,  0,  0},	/* wx */
X	{ 0,  0, 17, 17, 17, 17, 15,  1, 14},	{ 0,  0, 31,  2,  4,  8, 31,  0,  0}	/* yz */
X	};
X
X    static char digits[10][9] =
X	{
X	{14, 17, 17, 17, 17, 17, 14,  0,  0},	{ 2,  6, 10,  2,  2,  2, 31,  0,  0},	/* 01 */
X	{14, 17,  2,  4,  8, 16, 31,  0,  0},	{14, 17,  1, 14,  1, 17, 14,  0,  0},	/* 23 */
X	{ 2,  6, 10, 31,  2,  2,  2,  0,  0},	{31, 16, 16, 30,  1, 17, 14,  0,  0},	/* 45 */
X	{14, 17, 16, 30, 17, 17, 14,  0,  0},	{31,  1,  2,  4,  8, 16, 16,  0,  0},	/* 67 */
X	{14, 17, 17, 14, 17, 17, 14,  0,  0},	{14, 17, 17, 15,  1, 17, 14,  0,  0}	/* 89 */
X	};
X 
X    char ch;
X 
X    for ( ; *str; str++)
X	{
X	ch = (line >= 0 && line <= 8) ? *str : ' ';
X	if (isupper(ch))
X	    decode(uppers[ch-'A'][line], c);
X	else if (islower(ch))
X	    decode(lowers[ch-'a'][line], c);
X	else if (isdigit(ch))
X	    decode(digits[ch-'0'][line], c);
X	else
X	    decode(0, c);
X	}
X    }
EOF
chars=`wc -c < 'calen.c'`
if test $chars !=    15111; then echo 'calen.c' is $chars characters, should be    15111 characters!; fi
fi
exit 0