rogers@sud509.ed.ray.com (Andrew Rogers) (09/28/90)
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); } }