Calendar program for non-PostScript sites
Andrew Rogers
rogers at sud509.ed.ray.com
Fri Sep 28 04:25:00 AEST 1990
I've gotten a few requests for this from people who do not have PostScript at
their sites and (consequently) can't use Pcal. This program generates a
calendar which may be printed on any line printer using standard 132x66
fan-fold paper. Unfortunately, it lacks Pcal's most important capability -
to import text from a date file - but it's useful nonetheless. Have fun!
Andrew
-------------------------------- cut here --------------------------------
/*
* Calendar program - one month per page
*
* Author: AW Rogers
*
* Parameters:
*
* calen generate calendar for current month/year
*
* calen yy generate calendar for entire year yy
*
* calen mm yy generate calendar for month mm (1 = January),
* year yy (19yy if yy < 100)
*
* calen mm yy n as above, for n consecutive months
*
* Options:
*
* -b<N> add N blank lines at top of each page
*
* -f<FILE> write output to file FILE (calen.lst if -f alone)
*
* -l left-justify dates within boxes (default)
*
* -r right-justify dates within boxes
*
* -o<STR> use characters in STR as overstrike sequence for
* printing large month/year (default: HIX)
*
* -t print trailing dates (30, 31 in 23/30 and 24/31)
* in vacant box on first line
*
* Output:
*
* Full-page calendar for each of the specified months; written to
* stdout unless -f option used.
*
*/
#include <stdio.h>
#include <ctype.h>
#include <time.h>
#include <string.h>
#define FALSE 0
#define TRUE 1
#define JAN 1 /* significant months/years */
#define FEB 2
#define DEC 12
#define MINYR 1753
#define MAXYR 9999
#define SOLID 0 /* line styles (cf. box_line()) */
#define OPEN 1
#define LEFT 0 /* date justification within boxes */
#define RIGHT 1
#define DEFAULT_JUST LEFT
#define TOP 0 /* trailing date position */
#define BOTTOM 1
#define DEFAULT_TRAIL BOTTOM
#define TOP_ROW 0 /* top and bottom rows of calendar */
#define BOTTOM_ROW 5
#define OVERSTRIKE "HIX" /* overstrike sequence for heading */
#define MAX_OVERSTR 3
#define OUTFILE "calen.lst" /* default output file if -f option used */
#define NUM_BLANKS 0 /* default blank lines after <FF> */
#define NUM_MONTHS 1 /* default number of months */
#define MAXARGS 3 /* maximum non-flag command-line args */
#ifdef VMS
#define EXIT_FAILURE 3
#define END_PATH ']'
#else
#define EXIT_FAILURE 1
#define END_PATH '/'
#endif
#define is_leap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
typedef struct /* information about a single month */
{
int mm;
int yy;
char *mmname;
char dates[6][7][3];
} month_rec;
typedef month_rec *p_month; /* pointer to above structure */
/* globals for defaultable command-line parameters, and their defaults */
int just = DEFAULT_JUST; /* justification of dates */
int trail = DEFAULT_TRAIL; /* format for 23/30, 24/31 */
int nblank = NUM_BLANKS; /* blank lines after <FF> */
char *seq = OVERSTRIKE; /* overstrike sequence for heading */
int nmonths = NUM_MONTHS; /* number of months to print */
char *fname = ""; /* output file name */
main(argc, argv)
int argc;
char *argv[];
{
month_rec mRec[3]; /* space for main and small calendars */
p_month prev = mRec, curr = mRec+1, next = mRec+2, temp;
/* Get and validate command-line parameters and flags */
get_params(argc, argv, curr);
/* Fill in calendars for previous and current month */
prev->mm = curr->mm == JAN ? DEC : curr->mm - 1;
prev->yy = curr->mm == JAN ? curr-> yy - 1 : curr->yy;
fill_calendar(prev);
fill_calendar(curr);
/*
* Main loop: print each month of the calendar (with small calendars for the
* previous and next months in the upper corners). The current and next
* months' calendars can be reused as the previous and current calendars for
* the following month; only the 'next' calendar need be calculated each
* time through the loop.
*/
while (nmonths-- > 0 && curr->yy <= MAXYR)
{
next->mm = curr->mm == DEC ? JAN : curr->mm + 1;
next->yy = curr->mm == DEC ? curr->yy + 1 : curr->yy;
fill_calendar(next); /* fill in following month */
print_calendar(prev, curr, next);
temp = prev; /* swap pointers to months */
prev = curr;
curr = next;
next = temp;
}
if (*fname) /* report output file name */
fprintf(stderr, "Output is in file %s\n", fname);
}
/*
* Get and validate command-line parameters and flags. If month/year not
* specified on command line, generate calendar for current month/year.
* Exit program if month or year out of range; forgive illegal flags.
*/
get_params(argc, argv, curr)
int argc; /* argument count, vector passed in from main() */
char *argv[];
p_month curr; /* current month record (fill in month/year) */
{
int badopt = FALSE; /* flag set if bad option */
int badpar = FALSE; /* flag set if bad param */
int nargs = 0; /* count of non-flag args */
int numargs[MAXARGS]; /* non-flag (numeric) args */
char *parg; /* generic argument ptr */
long tmp; /* temp for system clock */
struct tm *p_tm; /* ptr to date/time struct */
char *progname, *p; /* program name (argv[0)) */
extern int atoi();
/* Isolate root program name (for use in error messages) */
progname = **argv ? *argv : "calen";
if ((p = strrchr(progname, END_PATH)) != NULL)
progname = ++p;
if ((p = strchr(progname, '.')) != NULL)
*p = '\0';
/* Walk command-line argument list */
while (--argc)
{
parg = *++argv;
if (*parg == '-')
{
switch (*++parg)
{
case 'b':
nblank = atoi(++parg);
break;
case 'f':
fname = *++parg ? parg : OUTFILE;
if (freopen(fname, "w", stdout) == (FILE *) NULL)
{
fprintf(stderr, "%s: error opening output file %s\n",
progname, fname);
exit (EXIT_FAILURE);
}
break;
case 'l':
just = LEFT;
break;
case 'r':
just = RIGHT;
break;
case 'o':
if (*++parg)
seq = parg;
break;
case 't':
trail = TOP;
break;
default:
fprintf(stderr, "%s: invalid flag: %s\n", progname, *argv);
badopt = TRUE;
break;
}
}
else /* non-flag argument - add to list */
{
if (nargs < MAXARGS)
numargs[nargs++] = atoi(parg);
}
}
/* Get and validate non-flag (numeric) parameters */
switch (nargs)
{
case 0: /* no arguments - print current month/year */
time(&tmp);
p_tm = localtime(&tmp);
curr->mm = p_tm->tm_mon + 1;
curr->yy = p_tm->tm_year;
break;
case 1: /* one argument - print entire year */
curr->mm = JAN;
curr->yy = numargs[0];
nmonths = 12;
break;
default: /* two or three arguments - print one or more months */
curr->mm = numargs[0];
curr->yy = numargs[1];
nmonths = nargs > 2 ? numargs[2] : NUM_MONTHS;
break;
}
if (curr->yy > 0 && curr->yy < 100) /* treat nn as 19nn */
curr->yy += 1900;
if (nmonths < 1) /* ensure at least one month */
nmonths = 1;
if (curr->mm < JAN || curr->mm > DEC) /* check range of month and year */
{
fprintf(stderr, "%s: month %d not in range %d .. %d\n", progname,
curr->mm, JAN, DEC);
badpar = TRUE;
}
if (curr->yy < MINYR || curr->yy > MAXYR)
{
fprintf(stderr, "%s: year %d not in range %d .. %d\n", progname,
curr->yy, MINYR, MAXYR);
badpar = TRUE;
}
if (badpar || badopt)
usage(progname);
if (badpar)
exit(EXIT_FAILURE);
}
/*
* Print message explaining correct usage of the command-line
* arguments and flags
*/
usage(prog)
char *prog;
{
fprintf(stderr, "\nUsage:\n\n");
fprintf(stderr, "\t%s [-bN] [-fFILE] [-l | -r] [-oSTR] [-t]\n", prog);
fprintf(stderr, "\t\t[ [ [mm] yy ] | [mm yy n] ]\n\n");
fprintf(stderr, "\nValid flags are:\n\n");
fprintf(stderr, "\t-bN\t\tadd N blank lines after each <FF> (default: %d)\n\n",
NUM_BLANKS);
fprintf(stderr, "\t-fFILE\t\twrite output to file FILE (%s if -f alone)\n\n",
OUTFILE);
fprintf(stderr, "\t-l\t\tleft-justify dates within boxes");
fprintf(stderr, "%s\n\n", DEFAULT_JUST == LEFT ? " (default)" : "");
fprintf(stderr, "\t-r\t\tright-justify dates within boxes");
fprintf(stderr, "%s\n\n", DEFAULT_JUST == RIGHT ? " (default)" : "");
fprintf(stderr, "\t-oSTR\t\tuse characters in STR as overstrike sequence for\n");
fprintf(stderr, "\t\t\tprinting large month/year (default: %s)\n\n",
OVERSTRIKE);
fprintf(stderr, "\t-t\t\tmove trailing 30 and 31 to vacant box on top line\n");
fprintf(stderr, "\n");
fprintf(stderr, "\t%s [opts]\t\tgenerate calendar for current month/year\n",
prog);
fprintf(stderr, "\n");
fprintf(stderr, "\t%s [opts] yy\t\tgenerate calendar for entire year yy\n",
prog);
fprintf(stderr, "\n");
fprintf(stderr, "\t%s [opts] mm yy\tgenerate calendar for month mm\n", prog);
fprintf(stderr, "\t\t\t\t(Jan = 1), year yy (19yy if yy < 100)\n");
fprintf(stderr, "\n");
fprintf(stderr, "\t%s [opts] mm yy n\tas above, for n consecutive months\n",
prog);
fprintf(stderr, "\n");
}
/*
* Print the calendar for the current month, generating small calendars
* for the previous and following months in the upper corners and the
* month/year (in 5x9 dot-matrix characters) centered at the top.
*/
print_calendar(prev, curr, next)
p_month prev; /* previous month (upper-left corner) */
p_month curr; /* current month (main calendar) */
p_month next; /* following month (upper-right corner) */
{
static char *wkday[] =
{
" Sunday ", " Monday ", " Tuesday ", "Wednesday", "Thursday",
" Friday ", "Saturday "
};
int nchars, line, week, day;
char *blanks = " "; /* 21 blanks for centering */
char *padding; /* pointer into 'blanks' */
char month_and_year[20]; /* work area */
char *ovr; /* overstrike sequence */
/* Set up month and year heading and appropriate padding to center it */
nchars = strlen(curr->mmname);
padding = blanks + (3 * (nchars - 3));
sprintf(month_and_year, "%s%5d", curr->mmname, curr->yy);
/* Print top-of-form and leading blank lines, if any */
printf("\f\n");
for (line = 0; line < nblank; line++)
printf("\n");
/* Print month and year in large letters, with small calendars on each side */
for (line = 0; line < 9; line++)
{
for (ovr = seq; ovr < seq + MAX_OVERSTR - 1 && *(ovr+1); ovr++)
{
printf("%20s%s", " ", padding); /* overstruck lines first */
header_line(month_and_year, line, *ovr);
printf(" %s", padding);
printf("\r");
}
small_cal_line(prev, line); /* calendars and non-overstruck line */
printf("%s", padding);
header_line(month_and_year, line, *ovr);
printf(" %s", padding);
small_cal_line(next, line);
printf("\n");
}
printf("\n"); /* print the weekday names */
box_line(1, SOLID);
box_line(1, OPEN);
printf(" ");
for (day = 0; day < 7; day++)
printf("|%13.9s ", wkday[day]);
printf("|\n");
box_line(1, OPEN);
for (week = TOP_ROW; week < BOTTOM_ROW - 1; week++) /* first four weeks */
{
box_line(1, SOLID);
date_line(curr, week, just);
box_line(7, OPEN);
}
box_line(1, SOLID); /* fifth week */
date_line(curr, BOTTOM_ROW - 1, just);
box_line(2, OPEN);
divider_line(curr->dates[BOTTOM_ROW]); /* divider for 23/30 and/or 24/31 */
box_line(3, OPEN);
date_line(curr, BOTTOM_ROW, !just); /* sixth week (trailing 31 or 30 31) */
box_line(1, SOLID);
}
/*
* Fill in the month name and date fields of a calendar record according
* to its month and year fields.
*/
fill_calendar(month)
p_month month; /* record to be filled in */
{
typedef struct /* local info about months of year */
{
char *name; /* month name */
int offset[2]; /* offset of m/1 from 1/1 (non-leap, leap) */
int length[2]; /* length of month (non-leap, leap) */
} month_info;
static month_info info[12] = {
{ "January", {0, 0}, {31, 31} }, { "February", {3, 3}, {28, 29} },
{ "March", {3, 4}, {31, 31} }, { "April", {6, 0}, {30, 30} },
{ "May", {1, 2}, {31, 31} }, { "June", {4, 5}, {30, 30} },
{ "July", {6, 0}, {31, 31} }, { "August", {2, 3}, {31, 31} },
{ "September", {5, 6}, {30, 30} }, { "October", {0, 1}, {31, 31} },
{ "November", {3, 4}, {30, 30} }, { "December", {5, 6}, {31, 31} }
} ;
int i, first, last, date = 0, y = month->yy, m = month->mm - 1;
int leap = is_leap(y);
/* Determine when month starts and ends */
first = (y + (y-1)/4 - (y-1)/100 + (y-1)/400 + info[m].offset[leap]) % 7;
last = first + info[m].length[leap] - 1;
for (i = 0; i < 42; i++) /* fill in 7x6 matrix of dates */
if (i < first || i > last)
month->dates[i/7][i%7][0] = '\0';
else
sprintf(month->dates[i/7][i%7], "%2d", ++date);
if (trail == TOP) /* move trailing 30/31 to top row if requested */
for (i = 0; month->dates[BOTTOM_ROW][i][0]; i++)
{
strcpy(month->dates[TOP_ROW][i], month->dates[BOTTOM_ROW][i]);
month->dates[BOTTOM_ROW][i][0] = '\0';
}
month->mmname = info[m].name; /* fill in month name */
}
/*
* Print one line of a small calendar (for upper left and right corners);
* always prints exactly 20 characters.
*/
small_cal_line(month, line)
p_month month; /* information for month to print */
int line; /* line to print (0-8; see below) */
{
int day;
char tmp1[10], tmp2[30];
switch (line)
{
case 0: /* month and year (centered) */
strcpy(tmp1, " ");
tmp1[(15 - strlen(month->mmname)) / 2] = '\0';
sprintf(tmp2, "%s%s %4d ", tmp1, month->mmname, month->yy);
printf("%-20.20s", tmp2);
break;
case 1: /* blank line */
printf("%20s", " ");
break;
case 2: /* weekdays */
printf("Su Mo Tu We Th Fr Sa");
break;
default: /* line of calendar (3 = first) */
for (day = 0; day < 6; day++)
printf("%2s ", month->dates[line-3][day]);
printf("%2s", month->dates[line-3][day]);
break;
}
}
/*
* Print n calendar box lines in selected style
*/
box_line(n, style)
int n; /* number of lines to print */
int style; /* SOLID or OPEN */
{
int day;
char *fmt = style == SOLID ? "+-----------------" :
"| " ;
for (; n > 0; n--)
{
printf(" ");
for (day = 0; day < 7; day++)
printf(fmt);
printf("%c\n", *fmt);
}
}
/*
* Print one week of dates, left- or right-justified
*/
date_line(month, week, just)
p_month month; /* pointer to month data */
int week; /* week to print (0 = first) */
int just; /* justification (LEFT or RIGHT) */
{
int day;
char *fmt = just == LEFT ? "| %-16s" : "|%16s " ;
printf(" ");
for (day = 0; day < 7; day++)
printf(fmt, month->dates[week][day]);
printf("|\n");
}
/*
* Print the divider separating 23/30 and/or 24/31 as needed
*/
divider_line(last_row)
char last_row[7][3]; /* row containing any trailing 30 and/or 31 */
{
int day;
printf(" ");
for (day = 0; day < 7; day++)
printf(last_row[day][0] ? "|_________________" : "| ");
printf("|\n");
}
/*
* Print least-significant 6 bits of n (0 = ' '; 1 = other char)
*/
decode(n, c)
int n; /* number to decode (row of 5x9 character) */
char c; /* character to print for each 1 bit */
{
int msk = 1 << 5;
for (; msk; msk >>= 1)
printf("%c", n & msk ? c : ' ');
}
/*
* Print one line of string in large (5x9) characters
*/
header_line(str, line, c)
char *str; /* string to print */
int line; /* line (0 - 8) */
char c; /* output character */
{
/* 5x7 representations of A-Z, 0-9; 5x9 representation of a-z */
static char uppers[26][7] = {
{14, 17, 17, 31, 17, 17, 17}, {30, 17, 17, 30, 17, 17, 30}, /* AB */
{14, 17, 16, 16, 16, 17, 14}, {30, 17, 17, 17, 17, 17, 30}, /* CD */
{31, 16, 16, 30, 16, 16, 31}, {31, 16, 16, 30, 16, 16, 16}, /* EF */
{14, 17, 16, 23, 17, 17, 14}, {17, 17, 17, 31, 17, 17, 17}, /* GH */
{31, 4, 4, 4, 4, 4, 31}, { 1, 1, 1, 1, 1, 17, 14}, /* IJ */
{17, 18, 20, 24, 20, 18, 17}, {16, 16, 16, 16, 16, 16, 31}, /* KL */
{17, 27, 21, 21, 17, 17, 17}, {17, 17, 25, 21, 19, 17, 17}, /* MN */
{14, 17, 17, 17, 17, 17, 14}, {30, 17, 17, 30, 16, 16, 16}, /* OP */
{14, 17, 17, 17, 21, 18, 13}, {30, 17, 17, 30, 20, 18, 17}, /* QR */
{14, 17, 16, 14, 1, 17, 14}, {31, 4, 4, 4, 4, 4, 4}, /* ST */
{17, 17, 17, 17, 17, 17, 14}, {17, 17, 17, 17, 17, 10, 4}, /* UV */
{17, 17, 17, 21, 21, 21, 10}, {17, 17, 10, 4, 10, 17, 17}, /* WX */
{17, 17, 17, 14, 4, 4, 4}, {31, 1, 2, 4, 8, 16, 31} /* YZ */
};
static char lowers[26][9] = {
{ 0, 0, 14, 1, 15, 17, 15, 0, 0}, {16, 16, 30, 17, 17, 17, 30, 0, 0}, /* ab */
{ 0, 0, 15, 16, 16, 16, 15, 0, 0}, { 1, 1, 15, 17, 17, 17, 15, 0, 0}, /* cd */
{ 0, 0, 14, 17, 31, 16, 14, 0, 0}, { 6, 9, 28, 8, 8, 8, 8, 0, 0}, /* ef */
{ 0, 0, 14, 17, 17, 17, 15, 1, 14}, {16, 16, 30, 17, 17, 17, 17, 0, 0}, /* gh */
{ 4, 0, 12, 4, 4, 4, 31, 0, 0}, { 1, 0, 3, 1, 1, 1, 1, 17, 14}, /* ij */
{16, 16, 17, 18, 28, 18, 17, 0, 0}, {12, 4, 4, 4, 4, 4, 31, 0, 0}, /* kl */
{ 0, 0, 30, 21, 21, 21, 21, 0, 0}, { 0, 0, 30, 17, 17, 17, 17, 0, 0}, /* mn */
{ 0, 0, 14, 17, 17, 17, 14, 0, 0}, { 0, 0, 30, 17, 17, 17, 30, 16, 16}, /* op */
{ 0, 0, 15, 17, 17, 17, 15, 1, 1}, { 0, 0, 30, 17, 16, 16, 16, 0, 0}, /* qr */
{ 0, 0, 15, 16, 14, 1, 30, 0, 0}, { 8, 8, 30, 8, 8, 9, 6, 0, 0}, /* st */
{ 0, 0, 17, 17, 17, 17, 15, 0, 0}, { 0, 0, 17, 17, 17, 10, 4, 0, 0}, /* uv */
{ 0, 0, 17, 21, 21, 21, 10, 0, 0}, { 0, 0, 17, 10, 4, 10, 17, 0, 0}, /* wx */
{ 0, 0, 17, 17, 17, 17, 15, 1, 14}, { 0, 0, 31, 2, 4, 8, 31, 0, 0}, /* yz */
};
static char digits[10][7] = {
{14, 17, 17, 17, 17, 17, 14}, { 2, 6, 10, 2, 2, 2, 31}, /* 01 */
{14, 17, 2, 4, 8, 16, 31}, {14, 17, 1, 14, 1, 17, 14}, /* 23 */
{ 2, 6, 10, 31, 2, 2, 2}, {31, 16, 16, 30, 1, 17, 14}, /* 45 */
{14, 17, 16, 30, 17, 17, 14}, {31, 1, 2, 4, 8, 16, 16}, /* 67 */
{14, 17, 17, 14, 17, 17, 14}, {14, 17, 17, 15, 1, 17, 14} /* 89 */
};
char ch;
/* convert each character of str to dot-matrix representation for line */
for ( ; ch = *str; str++)
{
if (isupper(ch))
decode(line < 7 ? uppers[ch-'A'][line] : 0, c);
else if (islower(ch))
decode(lowers[ch-'a'][line], c);
else if (isdigit(ch))
decode(line < 7 ? digits[ch-'0'][line] : 0, c);
else
decode(0, c);
}
}
More information about the Alt.sources
mailing list