home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1665 / pcal.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-12-28  |  23.1 KB  |  938 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.  * Revision history:
  15.  *
  16.  *    2.00    AWR    08/08/90    included revision history; replaced -r
  17.  *                    flag with -l and -p; replaced -s and -S
  18.  *                    flags with -b and -g; recognize flags
  19.  *                    set in date file; translate ( and ) in
  20.  *                    text to octal escape sequence; usage()
  21.  *                    message condensed to fit 24x80 screen
  22.  *
  23.  *    Parameters:
  24.  *
  25.  *        pcal [opts]        generate calendar for current month/year
  26.  *
  27.  *        pcal [opts] yy        generate calendar for entire year yy
  28.  *
  29.  *        pcal [opts] mm yy    generate calendar for month mm
  30.  *                    (Jan = 1), year yy (19yy if yy < 100)
  31.  *
  32.  *        pcal [opts] mm yy n    as above, for n consecutive months
  33.  *
  34.  *    Output:
  35.  *
  36.  *        PostScript file to print calendars for all selected months.
  37.  *
  38.  *    Options:
  39.  *
  40.  *        -b <DAY>    print specified weekday in black
  41.  *        -g <DAY>    print specified weekday in gray
  42.  *                (default: print Saturdays and Sundays in gray)
  43.  *        
  44.  *        -d <FONT>    specify alternate font for day names
  45.  *                (default: Times-Bold)
  46.  *
  47.  *        -e        generate empty calendar (ignore date file)
  48.  *
  49.  *        -f <FILE>    specify alternate date file (default:
  50.  *                ~/calendar on Un*x, SYS$LOGIN:CALENDAR.DAT
  51.  *                on VMS)
  52.  *
  53.  *        -o <FILE>    specify alternate output file (default:
  54.  *                stdout on Un*x, CALENDAR.PS on VMS)
  55.  *
  56.  *        -l        generate landscape-mode calendars
  57.  *        -p        generate portrait-mode calendars
  58.  *                (default: landscape-mode)
  59.  *
  60.  *        -t <FONT>    specify alternate font for titles
  61.  *                (default: Times-Bold)
  62.  *
  63.  *        Parameters and flags may be mixed on the command line.
  64.  *
  65.  *    All but the -e and -f options may be specified in the date file by
  66.  *    the inclusion of one or more lines of the form "opt <options>".  Any
  67.  *    such options override the defaults, but are themselves overridden
  68.  *    by options specified explicitly on the command line.  (The -b, -d,
  69.  *    -g, -o, and -t flags may be specified without arguments to reset
  70.  *    the corresponding options to the defaults.)
  71.  */
  72.  
  73.  
  74. #include <stdio.h>
  75. #include <ctype.h>
  76. #include <time.h>
  77. #include <string.h>
  78.  
  79. #ifdef VMS        /* VMS oddities isolated here */
  80.  
  81. #define DATEFILE  "calendar.dat"
  82. #define OUTFILE   "calendar.ps"
  83. #define END_PATH ']'
  84. #define EXIT_SUCCESS 1
  85. #define EXIT_FAILURE 3
  86.  
  87. #else            /* non-VMS - assume Un*x of some sort */
  88.  
  89. #define DATEFILE  "calendar"
  90. #define OUTFILE   ""
  91. #define END_PATH '/'
  92. #define EXIT_SUCCESS 0
  93. #define EXIT_FAILURE 1
  94. extern char *getenv();    /* to translate "HOME" to path name */
  95.  
  96. #endif
  97.  
  98. #define IS_LEAP(y)    ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
  99. #define INIT_COLORS    memcpy(color, default_color, sizeof(color))
  100.  
  101. #define PRT        (void)printf
  102. #define FPR        (void)fprintf
  103.  
  104. #define FALSE    0
  105. #define TRUE    1
  106.  
  107. #define DATEFILE_FLAGS    "ef"        /* flags relating to date file name */
  108. #define OTHER_FLAGS    "bdglopt"    /* all other flags */
  109.  
  110. #define DAYFONT   "Times-Bold"        /* default font names */
  111. #define TITLEFONT "Times-Bold"
  112.  
  113. #define LANDSCAPE  90        /* degrees to rotate for landscape/portrait */
  114. #define PORTRAIT    0
  115. #define ROTATE       LANDSCAPE    /* default */
  116.  
  117. #define BLACK    0        /* colors for dates */
  118. #define GRAY    1
  119.  
  120. #define NO_DATEFILE     0    /* date file (if any) to use */
  121. #define USER_DATEFILE    1
  122. #define SYS_DATEFILE    2
  123.  
  124. #define MIN_YR    1900        /* significant years (calendar limits) */
  125. #define MAX_YR    9999
  126.  
  127. #define JAN     1        /* significant months */
  128. #define FEB     2
  129. #define DEC    12
  130.  
  131. #define STRSIZ    100        /* size of misc. strings */
  132.  
  133. #define MAXARGS 3        /* numeric command-line args */
  134.  
  135. #define HOLIDAY    (1 << 6)    /* bit set to flag day as holiday */
  136.  
  137. #define WHITESPACE " \t"    /* token delimiters in date file */
  138.  
  139. /*
  140.  * Global variables:
  141.  */
  142.  
  143. FILE *dfp = NULL;        /* date file pointer */
  144. int init_month;            /* initial month, year, number of months */
  145. int init_year;
  146. int nmonths;
  147. int curr_year;            /* current default year for date file entries */
  148. char *words[100];        /* maximum number of words per date file line */
  149. char lbuf[512];            /* maximum date file line size */
  150. char progname[STRSIZ];        /* program name (for error messages) */
  151. char color[7];            /* colors of weekdays - cf. default_color[] */
  152.  
  153. /*
  154.  * Default values for command-line options:
  155.  */
  156.  
  157. char default_color[7] = {        /* -b, -g */
  158.     GRAY, BLACK, BLACK, BLACK, BLACK, BLACK, GRAY    /* cf. COLOR_MSG */
  159.     };
  160.  
  161. int datefile_type = SYS_DATEFILE;    /* -e, -f */
  162. char datefile[STRSIZ] = "";
  163.  
  164. int rotate = ROTATE;            /* -l, -p */
  165.  
  166. char dayfont[STRSIZ] = DAYFONT;        /* -d, -t */
  167. char titlefont[STRSIZ] = TITLEFONT;
  168.  
  169. char outfile[STRSIZ] = OUTFILE;        /* -o */
  170.  
  171. /*
  172.  * Language-dependent strings (month and day names, option file keywords):
  173.  */
  174.  
  175. static char *months[12] = {
  176.     "January", "February", "March", "April", "May", "June",
  177.     "July", "August", "September", "October", "November", "December"
  178.     };
  179.  
  180. static char *days[7] = {
  181.     "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
  182.     "Saturday"
  183.     };
  184.  
  185. #define MIN_DAY_LEN   2        /* minimum size of day/month abbreviations  */
  186. #define MIN_MONTH_LEN 3
  187.  
  188. #define ALL    "All"        /* command-line or date file keywords */
  189. #define OPT    "Opt"
  190. #define YEAR    "Year"
  191.  
  192. #define COLOR_MSG    "Sat/Sun in gray, others in black"    /* cf. usage() */
  193.  
  194. /*
  195.  * PostScript boilerplate
  196.  */
  197.  
  198. #include "pcalinit.h"
  199.  
  200.  
  201. /*
  202.  * Main program - parse and validate command-line arguments, open files,
  203.  * generate PostScript boilerplate and code to generate calendars.
  204.  *
  205.  * Program structure:
  206.  *
  207.  * main() calls get_args() once just to get the date file name and any numeric
  208.  * arguments (month, year, number of months), and calls find_options() to make
  209.  * a preliminary pass through the date file looking for "opt" lines (which
  210.  * override the defaults for the command-line flags).  It then calls get_args()
  211.  * again to process the other command-line flags, which in turn override any
  212.  * specified in the date file.
  213.  *
  214.  * main() then generates the common PostScript code and then calls pmonth() to
  215.  * print the calendars.
  216.  *
  217.  * find_options() calls getline() to read the date file.  Upon encountering
  218.  * an "opt" line, it calls loadwords() to tokenize the line and get_args()
  219.  * to parse it.
  220.  *
  221.  * pmonth() calls find_holidays() to make a first pass through the date file
  222.  * to generate the list of holidays to be printed in gray; it then calls
  223.  * find_daytext() to make a second pass to generate the text to be printed
  224.  * inside the calendar boxes.
  225.  *
  226.  * find_holidays() and find_daytext() both call getday() to retrieve the next
  227.  * day of interest from the date file.
  228.  *
  229.  * getday() calls getline() to read successive lines from the date file and
  230.  * calls parse() to interpret them, stopping when parse() determines that the
  231.  * line contains a usable date or when EOF is reached.
  232.  *
  233.  * getline() reads one or more lines from the date file, stripping comments
  234.  * (# through end-of-line) and ignoring blank lines.
  235.  *
  236.  * parse() parses a line from the date file, determining whether or not it
  237.  * contains a date in the current month and year.  It calls utility routines
  238.  * is_valid() to validate the date found and loadwords() to split any
  239.  * accompanying text into individual tokens for PostScript to print.
  240.  * 
  241.  */
  242. main(argc, argv)
  243.     int argc;
  244.     char **argv;
  245. {
  246.     char *p, **ap;
  247.     int i, month, year, ngray;
  248.  
  249. #define DO_HEADER(phdr)    for (ap = phdr; *ap; ap++) PRT("%s\n", *ap)
  250.  
  251.     INIT_COLORS;        /* set up default colors */
  252.  
  253.     /* isolate root program name (for use in error messages) */
  254.  
  255.     strcpy(progname, **argv ? *argv : "pcal");
  256.  
  257.     if ((p = strrchr(progname, END_PATH)) != NULL)
  258.         strcpy(progname, ++p);
  259.     if ((p = strchr(progname, '.')) != NULL)
  260.         *p = '\0';
  261.  
  262.     /*
  263.      * Get the arguments from a) "opt" lines in the date file, and b)
  264.      * the command line, in that order
  265.      */
  266.  
  267.     /* parse command-line arguments once to find name of date file */
  268.  
  269.     if (!get_args(argv, DATEFILE_FLAGS, TRUE)) {
  270.         usage();
  271.         exit(EXIT_FAILURE);
  272.     }
  273.  
  274.     /* Attempt to open the date file as specified by the [-e | -f] flags */
  275.  
  276.     switch (datefile_type) {
  277.     case NO_DATEFILE:
  278.         dfp = NULL;
  279.         break;
  280.  
  281.     case USER_DATEFILE:    
  282.         /* Attempt to open user-specified calendar file */
  283.         if ((dfp = fopen(datefile, "r")) == NULL) {
  284.             FPR(stderr, "%s: can't open file %s\n", progname, 
  285.                 datefile);
  286.             exit(EXIT_FAILURE);
  287.         }
  288.         break;
  289.  
  290.     case SYS_DATEFILE:
  291.         /* Look for the system (default) calendar file */
  292. #ifdef VMS
  293.         strcpy(datefile, "SYS$LOGIN:");    /* get home directory (VMS) */
  294. #else
  295.         *datefile = '\0';        /* translate "HOME" to path (Un*x) */
  296.         if ((p = getenv("HOME")) != NULL) {
  297.             strcat(datefile, p);
  298.             strcat(datefile, "/");
  299.         }
  300. #endif
  301.         strcat(datefile, DATEFILE);
  302.         dfp = fopen(datefile, "r");    /* no error if nonexistent */
  303.         break;
  304.     }
  305.  
  306.     find_options();        /* read date file looking for option lines */
  307.  
  308.     /* reparse command line - flags there supersede those in date file */
  309.  
  310.     get_args(argv, OTHER_FLAGS, FALSE);
  311.  
  312.     /* done with the arguments and flags - try to open the output file */
  313.  
  314.     if (*outfile && freopen(outfile, "w", stdout) == (FILE *) NULL) {
  315.         FPR(stderr, "%s: can't open file %s\n", progname, outfile);
  316.         exit(EXIT_FAILURE);
  317.     }
  318.  
  319.     /*
  320.      * Write out PostScript prolog
  321.      */
  322.  
  323.     /* font names */
  324.  
  325.      PRT("%%!\n");
  326.      PRT("/titlefont /%s def\n/dayfont /%s def\n", titlefont, dayfont);
  327.  
  328.     /* month names */
  329.  
  330.     PRT("/month_names [");
  331.     for (i = 0; i < 12; i++)
  332.         PRT("%s(%s) ", i % 6 == 0 ? "\n\t" : "", months[i]);
  333.     PRT("] def\n");
  334.  
  335.     /* day names */
  336.  
  337.     PRT("/day_names [");
  338.     for (i = 0; i < 7; i++)
  339.         PRT("%s(%s) ", i % 6 == 0 ? "\n\t" : "", days[i]);
  340.     PRT("] def\n");
  341.  
  342.     /* colors (black/gray) to print weekdays and holidays */
  343.  
  344.     PRT("/day_gray [");
  345.     for (ngray = i = 0; i < 7; ngray += color[i++] == GRAY)
  346.         PRT(" %s", color[i] == GRAY ? "true" : "false");
  347.     PRT(" ] def\n");
  348.     PRT("/holiday_gray %s def\n", ngray <= 3 ? "true" : "false");
  349.  
  350.     /* PostScript boilerplate (part 1) */
  351.  
  352.     DO_HEADER(header_1);
  353.  
  354.     /* landscape or portrait mode */
  355.  
  356.      PRT("\t%d rotate\n", rotate);
  357.      if (rotate == LANDSCAPE)
  358.          PRT("\t50 -120 translate\n");
  359.      else
  360.          PRT("\t0.75 0.75 scale\n\t50 460 translate\n");
  361.  
  362.     /* PostScript boilerplate (part 2) */
  363.  
  364.     DO_HEADER(header_2);
  365.  
  366.     /*
  367.      * Write out PostScript code to print calendars
  368.      */
  369.  
  370.     month = init_month;
  371.     year = init_year;
  372.  
  373.     while (nmonths--) {
  374.         pmonth(month, year);
  375.         if (++month > DEC) {
  376.             month = JAN;
  377.             year++;
  378.         }
  379.     }
  380.  
  381.     if (dfp)        /* close date file */
  382.         fclose(dfp);
  383.  
  384. #ifdef VMS
  385.     FPR(stderr, "Output is in file %s\n", outfile);
  386. #endif
  387.     exit(EXIT_SUCCESS);
  388. }
  389.  
  390. /*
  391.  * get_args - walk the argument list, parsing all arguments but processing only
  392.  * those specified in "flags".  If "do_numargs" is TRUE, processes numeric
  393.  * arguments (month, year, number of months) as well.
  394.  */
  395. int get_args(argv, flags, do_numargs)
  396.     char **argv;        /* argument list */
  397.     char *flags;        /* which flags to process */
  398.     int do_numargs;        /* process numeric arguments? */
  399. {
  400.     char *p, *opt;
  401.     int i, do_flag;
  402.     long tmp;            /* for getting current month/year */
  403.     struct tm *p_tm;
  404.     int badopt = FALSE;        /* flag set if bad param   */
  405.     int nargs = 0;            /* count of non-flag args  */
  406.     int numargs[MAXARGS];        /* non-flag (numeric) args */
  407.  
  408. /* Look for the argument following flag - may be separated by spaces or
  409.  * not (bumps argv in former case).  If no non-flag argument appears, set
  410.  * "arg" to NULL (-b, -d, -g, -o, and -t without an argument reset the
  411.  * corresponding option to its default value).
  412.  */
  413. #define GETARG(arg) arg = *(*argv + 2) ? *argv + 2 : \
  414.             (*(argv+1) && **(argv+1) != '-' ? *++argv : NULL)
  415.  
  416.     /* Walk argument list, ignoring first element (program name) */
  417.  
  418.      while (*++argv) {
  419.  
  420.         /* Assume that any non-flag argument is a numeric argument */
  421.         if (**argv != '-') {
  422.                 if (do_numargs && nargs < MAXARGS)
  423.                 numargs[nargs++] = atoi(*argv);
  424.             continue;
  425.         }
  426.  
  427.         /* Is this flag among those to be processed beyond parsing? */
  428.  
  429.         do_flag = strchr(flags, *(opt = *argv + 1)) != NULL;
  430.  
  431.         switch (*opt) {
  432.  
  433.         case 'b':        /* print day in black or gray */
  434.         case 'g':
  435.             GETARG(p);
  436.             if (do_flag)
  437.                 if (p)
  438.                     set_color(p, *opt == 'b' ? BLACK : GRAY);
  439.                 else
  440.                     INIT_COLORS;    /* reset to defaults */
  441.             break;
  442.  
  443.          case 'd':        /* specify alternate day font */
  444.              GETARG(p);
  445.             if (do_flag)
  446.                 strcpy(dayfont, p ? p : DAYFONT);
  447.              break;
  448.  
  449.         case 'e':        /* generate empty calendar */
  450.             if (do_flag) {
  451.                 datefile_type = NO_DATEFILE;
  452.                 datefile[0] = '\0';
  453.             }
  454.             break;
  455.  
  456.         case 'f':        /* specify alternate date file */
  457.             GETARG(p);
  458.             if (p && do_flag) {
  459.                 datefile_type = USER_DATEFILE;
  460.                 strcpy(datefile, p);
  461.             }
  462.             break;
  463.  
  464.          case 'l':        /* generate landscape calendar */
  465.             if (do_flag)
  466.                  rotate = LANDSCAPE;
  467.              break;
  468.  
  469.         case 'o':        /* specify alternate output file */
  470.             GETARG(p);
  471.             if (do_flag)
  472.                 strcpy(outfile, p ? p : OUTFILE);
  473.             break;
  474.  
  475.          case 'p':        /* generate portrait calendar */
  476.             if (do_flag)
  477.                  rotate = PORTRAIT;
  478.              break;
  479.  
  480.          case 't':        /* specify alternate title font */
  481.              GETARG(p);
  482.             if (do_flag)
  483.                 strcpy(titlefont, p ? p : TITLEFONT);
  484.              break;
  485.  
  486.         default:        /* unrecognized flag */
  487.             FPR(stderr, "%s: illegal option -%s\n", progname, opt);
  488.             badopt = TRUE;
  489.             break;
  490.         }
  491.         }
  492.  
  493.     if (!do_numargs)
  494.         return (!badopt);    /* return TRUE if OK, FALSE if error */
  495.  
  496.     /* Validate non-flag (numeric) parameters */
  497.  
  498.     switch (nargs) {
  499.     case 0:        /* no arguments - print current month/year */
  500.         time(&tmp);
  501.         p_tm = localtime(&tmp);
  502.         init_month = p_tm->tm_mon + 1;
  503.         init_year = p_tm->tm_year;
  504.         nmonths = 1;
  505.         break;            
  506.     case 1:        /* one argument - print entire year */
  507.         init_month = JAN;
  508.         init_year = numargs[0];
  509.         nmonths = 12;
  510.         break;
  511.     default:    /* two or three arguments - print one or more months */
  512.         init_month = numargs[0];
  513.         init_year = numargs[1];
  514.         nmonths = nargs > 2 ? numargs[2] : 1;
  515.         break;
  516.     }
  517.  
  518.     if (nmonths < 1)        /* ensure at least one month */
  519.         nmonths = 1;
  520.  
  521.     /* check range of month and year */
  522.  
  523.     if (init_month < JAN || init_month > DEC) {
  524.         FPR(stderr, "%s: month %d not in range 1 .. 12\n", progname,
  525.             init_month);
  526.         badopt = TRUE;
  527.     }
  528.     
  529.     if (init_year > 0 && init_year < 100)    /* treat nn as 19nn */
  530.         init_year += 1900;
  531.     
  532.     if (init_year < MIN_YR || init_year > MAX_YR) {
  533.         FPR(stderr, "%s year %d not in range %d .. %d\n", progname,
  534.             init_year, MIN_YR, MAX_YR);
  535.         badopt = TRUE;
  536.     }
  537.  
  538.     return (!badopt);    /* return TRUE if OK, FALSE if error */
  539. }
  540.  
  541.  
  542.  
  543. /*
  544.  *    usage - print message explaining correct usage of the command-line
  545.  *    arguments and flags
  546.  */
  547. usage()
  548. {
  549.     FPR(stderr, "\nUsage:\t%s [-b DAY]* [-d FONT] [-e | -f FILE] [-g DAY]* [-o FILE]\n", progname);
  550.     FPR(stderr, "\t\t[-l|-p] [-t FONT] [ [ [mm] yy ] | [mm yy n] ]\n");
  551.     FPR(stderr, "\n");
  552.     FPR(stderr, "\t-b DAY\t\tprint weekday DAY in black\n");
  553.     FPR(stderr, "\t-g DAY\t\tprint weekday DAY in gray\n");
  554.     FPR(stderr, "\t\t\t(default: %s)\n", COLOR_MSG);
  555.     FPR(stderr, "\n");
  556.     FPR(stderr, "\t-d FONT\t\tspecify alternate day name font (default: %s)\n",
  557.         DAYFONT);
  558.     FPR(stderr, "\t-t FONT\t\tspecify alternate title font (default: %s)\n",
  559.         TITLEFONT);
  560.     FPR(stderr, "\n");
  561.     FPR(stderr, "\t-e\t\tgenerate empty calendar (ignore date file)\n");
  562.     FPR(stderr, "\t-f FILE\t\tspecify alternate date file (default: %s)\n",
  563.         DATEFILE);
  564.     FPR(stderr, "\t-o FILE\t\tspecify alternate output file (default: %s)\n",
  565.         OUTFILE[0] ? OUTFILE : "stdout");
  566.     FPR(stderr, "\n");
  567.     FPR(stderr, "\t-l\t\tgenerate landscape-style calendars");
  568. #if (ROTATE == LANDSCAPE)
  569.     FPR(stderr, " (default)");
  570. #endif
  571.     FPR(stderr, "\n\t-p\t\tgenerate portrait-style calendars");
  572. #if (ROTATE == PORTRAIT)
  573.     FPR(stderr, " (default)");
  574. #endif
  575.     FPR(stderr, "\n\n");
  576.     FPR(stderr, "\tyy\t\tgenerate calendar for year yy (19yy if yy < 100)\n");
  577.     FPR(stderr, "\tmm yy\t\tgenerate calendar for month mm (Jan = 1), year yy\n");
  578.     FPR(stderr, "\tmm yy n\t\tgenerate calendars for n months, starting at mm/yy\n");
  579.     FPR(stderr, "\t(default)\tgenerate calendar for current month/year\n");
  580. }
  581.  
  582.  
  583. /*
  584.  * is_valid - return TRUE if d is a valid date in m/y (assumed valid)
  585.  */
  586. int is_valid(m, d, y)
  587.     register int m, d, y;
  588. {
  589.     static char len[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  590.  
  591.     return (d >= 1 && d <= (len[m] + (m == FEB && IS_LEAP(y))) );
  592. }
  593.  
  594.  
  595. /*
  596.  * loadwords - tokenize line buffer into word array, return word count
  597.  * (assumes strtok() has already been called with non-null first arg)
  598.  */
  599. int loadwords()
  600. {
  601.     register char **ap = words;
  602.     register i;
  603.  
  604.     for (i = 0; *ap = strtok(NULL, WHITESPACE) ; ap++, i++) ;
  605.     return(i);
  606. }
  607.  
  608.  
  609. /*
  610.  * strcap - convert first character of string to uppercase, rest to lowercase
  611.  */
  612. char *strcap(s)
  613.     char *s;
  614. {
  615.     char *p = s;
  616.  
  617.     if (*p) {
  618.         if (islower(*p))
  619.             *p = toupper(*p);
  620.         while (*++p)
  621.             if (isupper(*p))
  622.                 *p = tolower(*p);
  623.     }
  624.     return(s);
  625. }
  626.  
  627.  
  628. /*
  629.  * set_color - set one or all weekdays to print in black or gray
  630.  */
  631. set_color(day, col)
  632.     char *day;        /* weekday name (or "all") */
  633.     int  col;        /* select black or gray */
  634. {
  635.     int i, do_all;
  636.  
  637.     do_all = strncmp(strcap(day), ALL, strlen(ALL)) == 0;    /* set all days? */
  638.  
  639.     for (i = 0; i < 7; i++)
  640.         if (do_all || strncmp(day, days[i], MIN_DAY_LEN) == 0)
  641.             color[i] = col;
  642. }
  643.  
  644.  
  645. /*
  646.  * prt_word - print word as PostScript string, converting '(' and ')' to octal
  647.  */
  648. prt_word(p)
  649.     char *p;
  650. {
  651.     PRT("(");
  652.     for ( ; *p ; p++)
  653.         if (*p == '(' || *p == ')')
  654.             PRT("\\%03o", *p);
  655.         else
  656.             PRT("%c", *p);
  657.     PRT(")\n");
  658. }
  659.  
  660. /*
  661.  * parse - check date file entry for (in lbuf[]) for desired month/year
  662.  *
  663.  * Looks for an entry of one of the following forms:
  664.  *
  665.  *    year <year>
  666.  *    <month_name> <day>{*} {<text>}
  667.  *    <month><sep><day>{<sep><year>}{*} {<text>}
  668.  *
  669.  * where
  670.  *    <month_name> := first 3+ characters of name of month (in English)
  671.  *    <sep> := one or more non-numeric, non-space, non-'*' characters
  672.  *
  673.  * Returns day (with holiday flag set if specified) if date file entry
  674.  * is applicable to current month/year; 0 if not.  If applicable, parses
  675.  * any following text into global array words[].
  676.  *
  677.  * Also see find_options(), which is used during an initial pass to identify
  678.  * and parse only lines of the form
  679.  *
  680.  *    opt <options>
  681.  *
  682.  * which override defaults for command-line options.
  683.  */
  684. int parse(month, year)
  685.     register int month, year;
  686. {
  687.     register char *cp;
  688.     int mm, dd, yy;
  689.     int is_holiday;
  690.  
  691. /* macro to skip numeric field */
  692.  
  693. #define SKIP_FIELD(p) \
  694.     if (1) {while (isdigit(*p)) p++; while (*p && !isdigit(*p)) p++;} else
  695.  
  696.     /*
  697.      * Get first field - can be either "year", "opt" (ignored), a month
  698.      * name, or a (complete) numeric date spec
  699.          */
  700.  
  701.     cp = strtok(lbuf, WHITESPACE);    /* set up strtok() to parse line */
  702.  
  703.     /*
  704.      * If field begins with alpha, look for "year" or month name
  705.      */
  706.     if (isalpha(*cp)) {
  707.         strcap(cp);
  708.  
  709.         /* Check for "year" line */
  710.  
  711.         if (strcmp(cp, YEAR) == 0) {
  712.             cp = strtok(NULL, WHITESPACE);
  713.             if ((yy = atoi(cp)) > 0) {
  714.                 if (yy < 100)
  715.                     yy += 1900;
  716.                 curr_year = yy;
  717.             }
  718.             return(0);
  719.         }
  720.  
  721.         /* Check month and year.  "opt" falls out here */
  722.  
  723.         if (year != curr_year || 
  724.             strncmp(cp, months[month-1], MIN_MONTH_LEN) != 0)
  725.             return(0);
  726.  
  727.         /* month found, get and validate day */
  728.  
  729.         if ((cp = strtok(NULL, WHITESPACE)) == NULL || 
  730.             !is_valid(month, dd = atoi(cp), year))
  731.             return(0);
  732.  
  733.         is_holiday = cp[strlen(cp) - 1] == '*';    /* look for holiday flag */
  734.  
  735.         if (loadwords() || is_holiday)
  736.             return(dd | is_holiday * HOLIDAY);
  737.         return(0);
  738.     }
  739.  
  740.     /*
  741.      * Not alpha month, try numeric date (parse completely in case year
  742.      * has changed)
  743.      */
  744.  
  745.     is_holiday = cp[strlen(cp) - 1] == '*';    /* look for holiday flag */
  746.  
  747.     /* extract month and day fields */
  748.  
  749.     mm = atoi(cp);
  750.     SKIP_FIELD(cp);
  751.  
  752.     dd = atoi(cp);
  753.     SKIP_FIELD(cp);
  754.  
  755.     /* Numeric dates may (or may not) have a year */
  756.  
  757.     if ((yy = atoi(cp)) > 0) {
  758.         if (yy < 100)
  759.             yy += 1900;
  760.         curr_year = yy;
  761.     }
  762.  
  763.     /* if date is valid and significant (text or holiday flag), return it */
  764.  
  765.     if (mm == month && curr_year == year && is_valid(month, dd, year) &&
  766.         (loadwords() || is_holiday))
  767.         return(dd | is_holiday * HOLIDAY);
  768.     return(0);
  769. }
  770.  
  771.  
  772. /*
  773.  * getline - read next non-null line of input file into lbuf; return 0 on EOF
  774.  */
  775. int getline()
  776. {
  777.     register char *cp;
  778.     register int c;
  779.     int in_comment;        /* comments: from '#' to end-of-line */
  780.  
  781.     cp = lbuf;
  782.     do {
  783.         in_comment = FALSE;
  784.         while ((c = getc(dfp)) != '\n' && c != EOF) {
  785.             if (c == '#')
  786.                 in_comment = TRUE;
  787.  
  788.             /* ignore comments and leading white space */
  789.             if (in_comment ||
  790.                 (cp == lbuf && (c == ' ' || c == '\t')))
  791.                 continue;
  792.             *cp++ = c;
  793.         }
  794.         if (c == EOF)
  795.             return(0);
  796.  
  797.     } while (cp == lbuf);    /* ignore empty lines */
  798.  
  799.     *cp = '\0';
  800.     return(1);
  801. }
  802.  
  803.  
  804. /*
  805.  * getday - find next day entry for specified month and year in the date file
  806.  */
  807. int getday(month, year, reset)
  808.     register int month, year;
  809.     int reset;        /* TRUE: rewind date file */
  810. {
  811.     static int eof = FALSE;
  812.     int day;
  813.  
  814.     if (dfp == NULL)    /* whoops, no date file */
  815.         return(0);
  816.  
  817.     if (reset) {        /* rewind file, reset default year, clear eof */
  818.         rewind(dfp);
  819.         curr_year = init_year;
  820.         eof = FALSE;
  821.     }
  822.  
  823.     if (eof)
  824.         return(0);
  825.  
  826.     /* read lines until we find a line we want or until EOF */
  827.  
  828.     do {
  829.            if (!getline())    {
  830.             eof = TRUE;
  831.             return(0);
  832.         }
  833.     } while ( (day = parse(month, year)) == 0);
  834.  
  835.     return(day);
  836. }
  837.  
  838. /*
  839.  * Browse through the date file looking for day text in specified month/year
  840.  */
  841. find_daytext(month, year)
  842.     int month, year;
  843. {
  844.     register char **s;
  845.     register int oldday = -1;
  846.     register int day;
  847.  
  848.     for (day = getday(month, year, TRUE);
  849.          day != 0;
  850.          day = getday(month, year, FALSE))
  851.         if (*words) {
  852.             day &= ~HOLIDAY;
  853.             if (day != oldday) {
  854.                 if (oldday != -1)
  855.                     PRT("] daytext\n");
  856.                 PRT("%d [ \n", day);
  857.                 oldday = day;
  858.             } else
  859.                 PRT("(.p)\n");
  860.             for (s = words; *s; s++)
  861.                 prt_word(*s);
  862.         }
  863.  
  864.     if (oldday != -1)    /* terminate last call to daytext (if any) */
  865.         PRT("] daytext\n");
  866. }
  867.  
  868.  
  869. /*
  870.  * Browse through the date file looking for holidays in specified month/year
  871.  */
  872. find_holidays(month, year)
  873.     int month, year;
  874. {
  875.     register int day;
  876.     unsigned long holidays = 0;
  877.  
  878.     /* get unique, sorted list of holidays by setting bits in flag word */
  879.  
  880.     for (day = getday(month, year, TRUE);
  881.          day != 0;
  882.          day = getday(month, year, FALSE))
  883.         if (day & HOLIDAY)
  884.             holidays |= 1 << (day & ~HOLIDAY);
  885.  
  886.     PRT("/holidays [");    /* start definition of list */
  887.     for (day = 1; day <= 31; day++)
  888.         if (holidays & (1 << day))
  889.             PRT(" %d", day);
  890.     PRT(" 99 ] def\n");    /* terminate with dummy entry */
  891. }
  892.  
  893. /*
  894.  * Browse through the date file looking for "opt" lines; parse any found
  895.  */
  896. find_options()
  897. {
  898.     char *cp, **argv;
  899.  
  900.     if (!dfp)
  901.         return;
  902.  
  903.     rewind(dfp);
  904.  
  905.     /* read all lines in date file; if "opt" found, tokenize and parse it */
  906.  
  907.     while (getline()) {
  908.  
  909.         cp = strtok(lbuf, WHITESPACE);    /* initialize strtok() */
  910.  
  911.         if (isalpha(*cp) && strncmp(strcap(cp), OPT, strlen(OPT)) == 0) {
  912.             loadwords();        /* tokenize the option line */
  913.             argv = &words[-1];    /* argv[1] points to words[0] */
  914.  
  915.              if (!get_args(argv, OTHER_FLAGS, FALSE)) {
  916.                 usage();
  917.                 exit(EXIT_FAILURE);
  918.             }
  919.         }
  920.     }
  921. }
  922.  
  923.  
  924. /*
  925.  * pmonth - generate calendar for specified month/year
  926.  */
  927. pmonth(month, year)
  928.     int month, year;
  929. {
  930.  
  931.     PRT("/year %d def\n", year);    /* set up year and month */
  932.     PRT("/month %d def\n", month);
  933.     find_holidays(month, year);    /* first pass - make list of holidays */
  934.     PRT("printmonth\n");
  935.     find_daytext(month, year);    /* second pass - add text to boxes */
  936.     PRT("showpage\n");
  937. }
  938.