home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1617 / pcal.c < prev   
Encoding:
C/C++ Source or Header  |  1990-12-28  |  16.8 KB  |  684 lines

  1. /*
  2.  * pcal.c - generate PostScript file to print calendar for any month and year
  3.  *
  4.  * The original PostScript code to generate the calendars was written by
  5.  * Patrick Wood (Copywrite (c) 1987 by Patrick Wood of Pipeline Associates,
  6.  * Inc.), and authorized for modification and redistribution.  The calendar
  7.  * file inclusion code was originally written in "bs(1)" by Bill Vogel of
  8.  * AT&T.  Patrick's original PostScript was modified and enhanced several
  9.  * times by others whose names have regrettably been lost.  This C version
  10.  * was originally created by Ken Keirnan of Pacific Bell; additional
  11.  * enhancements by Joseph P. Larson, Ed Hand, and Andrew Rogers (who also
  12.  * did the VMS port).
  13.  *
  14.  *    Parameters:
  15.  *
  16.  *        pcal [opts]        generate calendar for current month/year
  17.  *
  18.  *        pcal [opts] yy        generate calendar for entire year yy
  19.  *
  20.  *        pcal [opts] mm yy    generate calendar for month mm
  21.  *                    (Jan = 1), year yy (19yy if yy < 100)
  22.  *
  23.  *        pcal [opts] mm yy n    as above, for n consecutive months
  24.  *
  25.  *    Output:
  26.  *
  27.  *        PostScript file to print calendars for all selected months.
  28.  *
  29.  *    Options:
  30.  *
  31.  *        -d <FONT>    specify alternate font for day names
  32.  *                (default: Times-Bold)
  33.  *
  34.  *        -e        generate empty calendar (ignore date file)
  35.  *
  36.  *        -f <FILE>    specify alternate date file (default:
  37.  *                ~/calendar on Un*x, SYS$LOGIN:CALENDAR.DAT
  38.  *                on VMS)
  39.  *
  40.  *        -o <FILE>    specify alternate output file (default:
  41.  *                stdout on Un*x, CALENDAR.PS on VMS)
  42.  *
  43.  *        -r        generate portrait-style calendars
  44.  *                (default: landscape)
  45.  *
  46.  *        -s        print Saturdays in black
  47.  *        -S        print Saturdays and Sundays in black
  48.  *                (default: print Saturdays and Sundays in gray)
  49.  *        
  50.  *        -t <FONT>    specify alternate font for titles
  51.  *                (default: Times-Bold)
  52.  *
  53.  *        Parameters and flags may be mixed on the command line.
  54.  */
  55.  
  56.  
  57. #include <stdio.h>
  58. #include <ctype.h>
  59. #include <time.h>
  60. #include <string.h>
  61.  
  62. #ifdef VMS        /* VMS oddities isolated here */
  63.  
  64. #define INFILE  "CALENDAR.DAT"
  65. #define OUTFILE "CALENDAR.PS"
  66. #define S_OPT "\"S\""
  67. #define END_PATH ']'
  68. #define EXIT_SUCCESS 1
  69. #define EXIT_FAILURE 3
  70.  
  71. #else            /* non-VMS - assume Un*x of some sort */
  72.  
  73. #define INFILE  "calendar"
  74. #define OUTFILE NULL
  75. #define S_OPT "S"
  76. #define END_PATH '/'
  77. #define EXIT_SUCCESS 0
  78. #define EXIT_FAILURE 1
  79. extern char *getenv();    /* to translate "HOME" to path name */
  80.  
  81. #endif
  82.  
  83.  
  84. #define DAYFONT   "Times-Bold"
  85. #define TITLEFONT "Times-Bold"
  86.  
  87. #define FALSE    0
  88. #define TRUE    1
  89.  
  90. #define PRT    (void)printf
  91. #define FPR    (void)fprintf
  92.  
  93. #define is_leap(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
  94.  
  95. #define MIN_YR    1900        /* significant years (calendar limits) */
  96. #define MAX_YR    9999
  97.  
  98. #define JAN     1        /* significant months */
  99. #define FEB     2
  100. #define DEC    12
  101.  
  102. #define MAXARGS 3        /* numeric command-line args */
  103. #define HOLIDAY    (1 << 6)    /* bit set to flag day as holiday */
  104.  
  105. #define WHITESPACE " \t"    /* token delimiters in date file */
  106.  
  107. #include "pcalinit.h"        /* PostScript boilerplate */
  108.  
  109. /*
  110.  * Global variables:
  111.  */
  112.  
  113. FILE *dfp = NULL;        /* date file pointer */
  114. int init_year;            /* initial default year for date file entries */
  115. int curr_year;            /* current default year for date file entries */
  116. char *words[100];        /* maximum number of words per date file line */
  117. char lbuf[512];            /* maximum date file line size */
  118.  
  119.  
  120. /*
  121.  * Main program - parse and validate command-line arguments, open files,
  122.  * generate PostScript boilerplate and code to generate calendars.
  123.  *
  124.  * Program structure:
  125.  *
  126.  * main() generates the common PostScript code and then calls pmonth() to
  127.  * print the calendars.
  128.  *
  129.  * pmonth() calls find_holidays() to make a first pass through the date file
  130.  * to generate the list of holidays to be printed in gray; it then calls
  131.  * find_daytext() to make a second pass to generate the text to be printed
  132.  * inside the calendar boxes.
  133.  *
  134.  * find_holidays() and find_daytext() both call getday() to retrieve the next
  135.  * day of interest from the date file.
  136.  *
  137.  * getday() reads successive lines from the date file (stripping comments and
  138.  * ignoring blank lines), stopping when parse() determines that the line
  139.  * contains a usable date or when EOF is reached.
  140.  *
  141.  * parse() parses a line from the date file, determining whether or not it
  142.  * contains a date in the current month and year.  It calls utility routines
  143.  * is_valid() to validate the date found and loadwords() to split any
  144.  * accompanying text into individual tokens for PostScript to print.
  145.  * 
  146.  */
  147. main(argc, argv)
  148.     int argc;
  149.     char **argv;
  150. {
  151.  
  152. /* Look for the argument following a flag - may be separated by spaces or
  153.  * not.  If no non-flag argument appears, leave "arg" alone
  154.  */
  155. #define GETARG(arg) if ((parg = *++opt ? opt : \
  156.             (*(argv+1) && **(argv+1) != '-' ? *++argv : NULL) ) \
  157.             != NULL) arg = parg; else
  158.  
  159. /* Loop through one of the header sections in pcalinit.h */
  160. #define DOHEADER(phdr) for (ap = phdr; *ap; ap++) PRT("%s\n", *ap)
  161.  
  162.     char *progname = **argv ? *argv : "pcal";
  163.     struct tm *p_tm;
  164.     register char **ap;
  165.     char *date_file = NULL;
  166.     char *opt, *parg, *p;
  167.     long tmp;
  168.     int nocal = FALSE;
  169.     int sat_gray = TRUE;
  170.     int sun_gray = TRUE;
  171.      char *titlefont = TITLEFONT;
  172.      char *dayfont = DAYFONT;
  173.     char *outfile = OUTFILE;
  174.      int rotate = 90;
  175.      int month, year, nmonths;
  176.     int badopt = FALSE;        /* flag set if bad option  */
  177.     int badpar = FALSE;        /* flag set if bad param   */
  178.     int default_out = TRUE;        /* flag if default output file (VMS) */
  179.     int nargs = 0;            /* count of non-flag args  */
  180.     int numargs[MAXARGS];        /* non-flag (numeric) args */
  181.  
  182.     /* isolate root program name (for use in error messages) */
  183.  
  184.     if ((p = strrchr(progname, END_PATH)) != NULL)
  185.         progname = ++p;
  186.     if ((p = strchr(progname, '.')) != NULL)
  187.         *p = '\0';
  188.  
  189.     /* walk through command-line arguments */
  190.  
  191.      while (*++argv) {
  192.  
  193.         if (**argv != '-') {    /* assume numeric argument */
  194.                 if (nargs < MAXARGS)
  195.                 numargs[nargs++] = atoi(*argv);
  196.             continue;
  197.             }
  198.  
  199.         opt = (*argv) + 1;
  200.         switch (*opt) {
  201.  
  202.          case 'd':        /* specify alternate day font */
  203.              GETARG(dayfont);
  204.              break;
  205.  
  206.         case 'e':        /* generate empty calendar */
  207.             nocal = TRUE;
  208.             date_file = NULL;
  209.             break;
  210.  
  211.         case 'f':        /* specify alternate date file */
  212.             GETARG(date_file);
  213.             nocal = FALSE;
  214.             break;
  215.  
  216.         case 'o':        /* specify alternate output file */
  217.             GETARG(outfile);
  218.             default_out = FALSE;
  219.             break;
  220.  
  221.          case 'r':        /* generate portrait calendar */
  222.              rotate = 0;
  223.              break;
  224.  
  225.         case 'S':        /* Saturdays and Sundays in black */
  226.             sun_gray = FALSE;
  227.         case 's':        /* Saturdays in black */
  228.             sat_gray = FALSE;
  229.             break;
  230.  
  231.          case 't':        /* specify alternate title font */
  232.              GETARG(titlefont);
  233.              break;
  234.  
  235.         default:
  236.             FPR(stderr, "%s: illegal option -%s\n", progname, opt);
  237.             badopt = TRUE;
  238.             break;
  239.         }
  240.         }
  241.  
  242.     /* Get and validate non-flag (numeric) parameters */
  243.  
  244.     switch (nargs) {
  245.     case 0:        /* no arguments - print current month/year */
  246.         time(&tmp);
  247.         p_tm = localtime(&tmp);
  248.         month = p_tm->tm_mon + 1;
  249.         year = p_tm->tm_year;
  250.         nmonths = 1;
  251.         break;            
  252.     case 1:        /* one argument - print entire year */
  253.         month = 1;
  254.         year = numargs[0];
  255.         nmonths = 12;
  256.         break;
  257.     default:    /* two or three arguments - print one or more months */
  258.         month = numargs[0];
  259.         year = numargs[1];
  260.         nmonths = nargs > 2 ? numargs[2] : 1;
  261.         break;
  262.     }
  263.  
  264.     if (year > 0 && year < 100)    /* treat nn as 19nn */
  265.         year += 1900;
  266.     
  267.     if (nmonths < 1)        /* ensure at least one month */
  268.         nmonths = 1;
  269.     
  270.     if (month < 1 || month > 12) {    /* check range of month and year */
  271.         FPR(stderr, "%s: month %d not in range 1 .. 12\n", progname,
  272.             month);
  273.         badpar = TRUE;
  274.     }
  275.     
  276.     if (year < MIN_YR || year > MAX_YR) {
  277.         FPR(stderr, "%s year %d not in range %d .. %d\n", progname,
  278.             year, MIN_YR, MAX_YR);
  279.         badpar = TRUE;
  280.     }
  281.  
  282.     /* command-line errors?  generate usage message and quit */
  283.        
  284.     if (badpar || badopt) {
  285.         usage(progname);
  286.         exit(EXIT_FAILURE);
  287.         }
  288.  
  289.     /* flag and numeric parameters OK - now try to open the files */
  290.  
  291.     if (outfile && freopen(outfile, "w", stdout) == (FILE *) NULL) {
  292.         FPR(stderr, "%s: can't open file %s\n", progname, outfile);
  293.         exit(EXIT_FAILURE);
  294.         }
  295.  
  296.     /*
  297.      * In case we don't encounter any year data in the date file,
  298.      * assume the current year.
  299.      */
  300.     init_year = year;
  301.  
  302.     /*
  303.      * Attempt to open user-specified calendar file first
  304.      */
  305.     if (date_file != NULL) {
  306.         if ((dfp = fopen(date_file, "r")) == NULL) {
  307.             FPR(stderr, "%s: can't open file %s\n", progname, 
  308.                 date_file);
  309.             exit(EXIT_FAILURE);
  310.         }
  311.     }
  312.  
  313.     /*
  314.      * Else see if the default calendar file exists (no error if
  315.      * nonexistent; program will just print empty calendar)
  316.      */
  317.     else if (nocal == FALSE) {
  318. #ifdef VMS
  319.         strcpy(lbuf, "SYS$LOGIN:");    /* get home directory (VMS) */
  320. #else
  321.         *lbuf = '\0';        /* translate "HOME" to path (Un*x) */
  322.         if ((p = getenv("HOME")) != NULL) {
  323.             strcat(lbuf, p);
  324.             strcat(lbuf, "/");
  325.         }
  326. #endif
  327.         strcat(lbuf, INFILE);
  328.         dfp = fopen(lbuf, "r");
  329.     }
  330.  
  331.     /*
  332.      * Write out PostScript prolog
  333.      */
  334.      PRT("%%!\n");
  335.      PRT("/titlefont /%s def\n/dayfont /%s def\n", titlefont, dayfont);
  336.  
  337.     DOHEADER(header_1);
  338.     if (sun_gray) {
  339.         PRT("\t\t\tday start add 7 mod 1 %s {\n",
  340.             sat_gray ? "le" : "eq" );
  341.           PRT("\t\t\t\t.8 setgray\n");
  342.           PRT("\t\t\t} if\n");
  343.         }
  344.     DOHEADER(header_2);
  345.  
  346.      PRT("\t%d rotate\n", rotate);
  347.      if (rotate)
  348.          PRT("\t50 -120 translate\n");
  349.      else
  350.          PRT("\t0.75 0.75 scale\n\t50 460 translate\n");
  351.  
  352.     DOHEADER(header_3);
  353.  
  354.     /*
  355.      * Loop through all the requested months
  356.      */
  357.     while (nmonths--) {
  358.         pmonth(month, year);
  359.         if (++month > DEC) {
  360.             month = JAN;
  361.             year++;
  362.         }
  363.     }
  364.  
  365.     if (dfp)        /* close date file */
  366.         fclose(dfp);
  367.  
  368. #ifdef VMS
  369.     if (default_out)    /* inform VMS users where output is */
  370.         FPR(stderr, "Output is in file %s\n", outfile);
  371. #endif
  372.     exit(EXIT_SUCCESS);
  373. }
  374.  
  375. /*
  376.  *    Print message explaining correct usage of the command-line
  377.  *    arguments and flags
  378.  */
  379. usage(prog)
  380.     char *prog;
  381. {
  382.     FPR(stderr, "\nUsage:\n\n");
  383.     FPR(stderr, "\t%s [-d FONT] [-e | -f FILE] [-o FILE] [-r] [-s | -%s] [-t FONT]\n",
  384.         prog, S_OPT);
  385.     FPR(stderr, "\t\t[ [ [mm] yy ] | [mm yy n] ]\n\n");
  386.     FPR(stderr, "\t\t-d FONT\t\tspecify alternate font for day names\n");
  387.     FPR(stderr, "\t\t\t\t(default: %s)\n", DAYFONT);
  388.     FPR(stderr, "\n");
  389.     FPR(stderr, "\t\t-e\t\tgenerate empty calendar (ignore date file)\n");
  390.     FPR(stderr, "\n");
  391.     FPR(stderr, "\t\t-f FILE\t\tspecify alternate date file\n");
  392.     FPR(stderr, "\t\t\t\t(default: %s)\n", INFILE);
  393.     FPR(stderr, "\n");
  394.     FPR(stderr, "\t\t-o FILE\t\tspecify alternate output file\n");
  395.     FPR(stderr, "\t\t\t\t(default: %s)\n", OUTFILE ? OUTFILE : "stdout");
  396.     FPR(stderr, "\n");
  397.     FPR(stderr, "\t\t-r\t\tgenerate portrait-style calendars\n");
  398.     FPR(stderr, "\t\t\t\t(default: landscape)\n");
  399.     FPR(stderr, "\n");
  400.     FPR(stderr, "\t\t-s\t\tprint Saturdays in black\n");
  401.     FPR(stderr, "\t\t-%s\t\tprint Saturdays and Sundays in black\n", S_OPT);
  402.     FPR(stderr, "\t\t\t\t(default: print Saturdays and Sundays in gray)\n");
  403.     FPR(stderr, "\n");
  404.     FPR(stderr, "\t\t-t FONT\t\tspecify alternate font for titles\n");
  405.     FPR(stderr, "\t\t\t\t(default: %s)\n", TITLEFONT);
  406.     FPR(stderr, "\n");
  407.     FPR(stderr, "\t%s [opts]\t\tgenerate calendar for current month/year\n",
  408.         prog);
  409.     FPR(stderr, "\n");
  410.     FPR(stderr, "\t%s [opts] yy\t\tgenerate calendar for entire year yy\n",
  411.         prog);
  412.     FPR(stderr, "\n");
  413.     FPR(stderr, "\t%s [opts] mm yy\tgenerate calendar for month mm\n", prog);
  414.     FPR(stderr, "\t\t\t\t(Jan = 1), year yy (19yy if yy < 100)\n");
  415.     FPR(stderr, "\n");
  416.     FPR(stderr, "\t%s [opts] mm yy n\tas above, for n consecutive months\n",
  417.         prog);
  418.     FPR(stderr, "\n");
  419. }
  420.  
  421.  
  422. /*
  423.  * is_valid - return TRUE if m/d/y is a valid date
  424.  */
  425. int is_valid(m, d, y)
  426.     register int m, d, y;
  427. {
  428.     static char len[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  429.  
  430.     return (d >= 1 && d <= (len[m] + (m == FEB && is_leap(y))) );
  431. }
  432.  
  433.  
  434. /*
  435.  * loadwords - tokenize line buffer into word array, return word count
  436.  * (assumes strtok() has already been called with non-null first arg)
  437.  */
  438. int loadwords()
  439. {
  440.     register char **ap = words;
  441.     register i;
  442.  
  443.     for (i = 0; *ap = strtok(NULL, WHITESPACE) ; ap++, i++) ;
  444.     return(i);
  445. }
  446.  
  447.  
  448. /*
  449.  * parse - check date file entry for (in lbuf[]) for desired month/year
  450.  *
  451.  * Looks for an entry of one of the following forms:
  452.  *
  453.  *    year <year>
  454.  *    <month_name> <day>{*} {<text>}
  455.  *    <month><sep><day>{<sep><year>}{*} {<text>}
  456.  *
  457.  * where
  458.  *    <month_name> := first 3+ characters of name of month (in English)
  459.  *    <sep> := one or more non-numeric, non-space, non-'*' characters
  460.  *
  461.  * Returns day (with holiday flag set if specified) if date file entry
  462.  * is applicable to current month/year; 0 if not; parses any following
  463.  * text into global array words[].
  464.  */
  465. int parse(month, year)
  466.     register int month, year;
  467. {
  468.     register char *cp;
  469.     int mm, dd, yy;
  470.     int is_holiday, valid;
  471.  
  472.     static char *months[13] = {    /* used to match alpha months */
  473.         "", "jan", "feb", "mar", "apr", "may", "jun",
  474.             "jul", "aug", "sep", "oct", "nov", "dec",
  475.     };
  476.  
  477. #define SKIP_FIELD(p) \
  478.     if (1) {while (isdigit(*p)) p++; while (*p && !isdigit(*p)) p++;} else
  479.  
  480.     /*
  481.      * Get first field - can be either "year", a month name, or a (complete)
  482.      * numeric date spec
  483.          */
  484.     cp = strtok(lbuf, WHITESPACE);
  485.  
  486.     while (*cp) {
  487.         if (isupper(*cp))
  488.             *cp = tolower(*cp);
  489.         cp++;
  490.     }
  491.     cp = lbuf;
  492.  
  493.     /*
  494.      * Check for "year" line
  495.      */
  496.     if (strcmp(cp, "year") == 0) {
  497.         cp = strtok(NULL, WHITESPACE);
  498.         if ((yy = atoi(cp)) > 0) {
  499.             if (yy < 100)
  500.                 yy += 1900;
  501.             curr_year = yy;
  502.         }
  503.         return(0);
  504.     }
  505.  
  506.     /*
  507.      * If field begins with alpha, try to decode month name
  508.      */
  509.     if (isalpha(*cp)) {
  510.         /* are month and year the ones we want? */
  511.         if (year != curr_year || strncmp(cp, months[month], 3) != 0)
  512.             return(0);
  513.  
  514.         /* month found, get and validate day */
  515.  
  516.         if ((cp = strtok(NULL, WHITESPACE)) == NULL || 
  517.             !is_valid(month, dd = atoi(cp), year))
  518.             return(0);
  519.  
  520.         is_holiday = cp[strlen(cp) - 1] == '*';    /* look for holiday flag */
  521.  
  522.         if (loadwords() || is_holiday)
  523.             return(dd | is_holiday * HOLIDAY);
  524.         return(0);
  525.     }
  526.  
  527.     /*
  528.      * Not alpha month, try numeric date (parse completely in case year
  529.      * has changed)
  530.      */
  531.  
  532.     is_holiday = cp[strlen(cp) - 1] == '*';    /* look for holiday flag */
  533.  
  534.     /* get month and compare against desired month */
  535.  
  536.     valid = (mm = atoi(cp)) == month;
  537.     SKIP_FIELD(cp);
  538.  
  539.     /* now get and validate day */
  540.  
  541.     valid &= is_valid(month, dd = atoi(cp), year);
  542.     SKIP_FIELD(cp);
  543.  
  544.     /* Numeric dates may (or may not) have a year */
  545.  
  546.     if ((yy = atoi(cp)) > 0) {
  547.         if (yy < 100)
  548.             yy += 1900;
  549.         curr_year = yy;
  550.     }
  551.  
  552.     valid &= curr_year == year;    /* is year the desired one? */
  553.  
  554.     /* if date is valid and significant (text or holiday flag), return it */
  555.  
  556.     if (valid && (loadwords() || is_holiday))
  557.         return(dd | is_holiday * HOLIDAY);
  558.     return(0);
  559. }
  560.  
  561.  
  562. /*
  563.  * getday - find next day entry for specified month and year in the date file
  564.  */
  565. int getday(month, year, reset)
  566.     register int month, year;
  567.     int reset;        /* TRUE: rewind date file */
  568. {
  569.     static int eof = FALSE;
  570.     register char *cp;
  571.     register int c;
  572.     int day;
  573.     int in_comment;        /* comments: from '#' to end-of-line */
  574.  
  575.     if (dfp == NULL)    /* whoops, no date file */
  576.         return(0);
  577.  
  578.     if (reset) {        /* rewind file, reset default year, clear eof */
  579.         rewind(dfp);
  580.         curr_year = init_year;
  581.         eof = FALSE;
  582.     }
  583.  
  584.     if (eof)
  585.         return(0);
  586.  
  587.     do {
  588.         cp = lbuf;
  589.         do {
  590.             in_comment = FALSE;
  591.             while ((c = getc(dfp)) != '\n' && c != EOF) {
  592.                 if (c == '#')
  593.                     in_comment = TRUE;
  594.                 /* ignore comments and leading white space */
  595.                 if (in_comment ||
  596.                     (cp == lbuf && (c == ' ' || c == '\t')))
  597.                     continue;
  598.                 *cp++ = c;
  599.             }
  600.             if (c == EOF) {
  601.                 eof = TRUE;
  602.                 return(0);
  603.             }
  604.         } while (cp == lbuf);    /* ignore empty lines */
  605.  
  606.         *cp = '\0';
  607.  
  608.     /* examine the line, see if its one we want */
  609.     } while ( (day = parse(month, year)) == 0);
  610.  
  611.     return(day);
  612. }
  613.  
  614. /*
  615.  * Browse through the date file looking for day text in specified month/year
  616.  */
  617. find_daytext(month, year)
  618.     int month, year;
  619. {
  620.     register char **s;
  621.     register int oldday = -1;
  622.     register int day;
  623.  
  624.     for (day = getday(month, year, TRUE);
  625.          day != 0;
  626.          day = getday(month, year, FALSE))
  627.         if (*words) {
  628.             day &= ~HOLIDAY;
  629.             if (day != oldday) {
  630.                 if (oldday != -1)
  631.                     PRT("] daytext\n");
  632.                 PRT("%d [ \n", day);
  633.                 oldday = day;
  634.             } else
  635.                 PRT("(.p)\n");
  636.             for (s = words; *s; s++)
  637.                 PRT("(%s)\n", *s);
  638.         }
  639.  
  640.     if (oldday != -1)    /* terminate last call to daytext (if any) */
  641.         PRT("] daytext\n");
  642. }
  643.  
  644.  
  645. /*
  646.  * Browse through the date file looking for holidays in specified month/year
  647.  */
  648. find_holidays(month, year)
  649.     int month, year;
  650. {
  651.     register int day;
  652.     unsigned long holidays = 0;
  653.  
  654.     /* get unique, sorted list of holidays by setting bits in flag word */
  655.  
  656.     for (day = getday(month, year, TRUE);
  657.          day != 0;
  658.          day = getday(month, year, FALSE))
  659.         if (day & HOLIDAY)
  660.             holidays |= 1 << (day & ~HOLIDAY);
  661.  
  662.     PRT("/holidays [");    /* start definition of list */
  663.     for (day = 1; day <= 31; day++)
  664.         if (holidays & (1 << day))
  665.             PRT(" %d", day);
  666.     PRT(" 99 ] def\n");    /* terminate with dummy entry */
  667. }
  668.  
  669.  
  670. /*
  671.  * pmonth - generate calendar for specified month/year
  672.  */
  673. pmonth(month, year)
  674.     int month, year;
  675. {
  676.  
  677.     PRT("/year %d def\n", year);    /* set up year and month */
  678.     PRT("/month %d def\n", month);
  679.     find_holidays(month, year);    /* first pass - make list of holidays */
  680.     PRT("printmonth\n");
  681.     find_daytext(month, year);    /* second pass - add text to boxes */
  682.     PRT("showpage\n");
  683. }
  684.