home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1608 / pcal.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-12-28  |  13.6 KB  |  595 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 Unix, SYS$LOGIN:CALENDAR.DAT
  38.  *                on VMS)
  39.  *
  40.  *        -o <FILE>    specify alternate output file (default:
  41.  *                stdout on Unix, 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. #include <stdio.h>
  57. #include <ctype.h>
  58. #include <time.h>
  59. #include <string.h>
  60.  
  61. #ifdef VMS
  62. #define INFILE  "SYS$LOGIN:CALENDAR.DAT"
  63. #define OUTFILE "CALENDAR.PS"
  64. #define S_OPT "\"S\""
  65. #define END_PATH ']'
  66. #define EXIT_SUCCESS 1
  67. #define EXIT_FAILURE 3
  68. #else
  69. #define INFILE  "~/calendar"
  70. #define OUTFILE ((char *)0)
  71. #define S_OPT "S"
  72. #define END_PATH '/'
  73. #define EXIT_SUCCESS 0
  74. #define EXIT_FAILURE 1
  75. #endif
  76.  
  77. #define DAYFONT   "Times-Bold"
  78. #define TITLEFONT "Times-Bold"
  79.  
  80. #define FALSE    0
  81. #define TRUE    1
  82.  
  83. #define PRT    (void)printf
  84. #define FPR    (void)fprintf
  85.  
  86. #define MIN_YR    1900
  87. #define MAX_YR    9999
  88.  
  89. #define MAXARGS 3
  90. #define HOLIDAY    (1 << 6)    /* bit set to flag day as holiday */
  91.  
  92. char *words[100];    /* maximum number of words on a line */
  93. char lbuf[512];        /* maximum line size */
  94.  
  95. char *months[] = {    /* used to match alpha months */
  96.     "jan", "feb", "mar", "apr", "may", "jun",
  97.     "jul", "aug", "sep", "oct", "nov", "dec",
  98.     (char *)0,
  99. };
  100.  
  101. #include "pcalinit.h"    /* PostScript boilerplate */
  102.  
  103. FILE *cfp = NULL;
  104. int curr_year;
  105.  
  106.  
  107. main(argc, argv)
  108.     int argc;
  109.     char **argv;
  110. {
  111.  
  112. /* Look for the argument following a flag - may be separated by spaces or
  113.  * not.  If no argument appears, leave "arg" alone
  114.  */
  115. #define GETARG(arg) if ((parg = *++opt ? opt : \
  116.             (*(argv+1) && **(argv+1) != '-' ? *++argv : NULL) ) \
  117.             != NULL) arg = parg; else
  118.  
  119. /* Loop through one of the header sections in pcalinit.h */
  120. #define DOHEADER(phdr) for (ap = phdr; *ap; ap++) PRT("%s\n", *ap)
  121.  
  122.     char *progname = **argv ? *argv : "pcal";
  123.     struct tm *p_tm;
  124.     register char **ap;
  125.     register char *cp;
  126.     char *date_file = NULL;
  127.     char *opt, *pnum, *parg, *p;
  128.     long tmp;
  129.     int nocal = FALSE;
  130.     int sat_gray = TRUE;
  131.     int sun_gray = TRUE;
  132.      char *titlefont = TITLEFONT;
  133.      char *dayfont = DAYFONT;
  134.     char *outfile = OUTFILE;
  135.      int rotate = 90;
  136.      int month, year, nmonths;
  137.     int badopt = FALSE;        /* flag set if bad option  */
  138.     int badpar = FALSE;        /* flag set if bad param   */
  139.     int nargs = 0;            /* count of non-flag args  */
  140.     int numargs[MAXARGS];        /* non-flag (numeric) args */
  141.  
  142.     /* isolate root program name (for use in error messages) */
  143.  
  144.     if ((p = strrchr(progname, END_PATH)) != NULL)
  145.         progname = ++p;
  146.     if ((p = strchr(progname, '.')) != NULL)
  147.         *p = '\0';
  148.  
  149.     /* walk through command-line arguments */
  150.  
  151.      while (*++argv) {
  152.  
  153.         if (**argv != '-') {    /* assume numeric argument */
  154.                 if (nargs < MAXARGS)
  155.                 numargs[nargs++] = atoi(*argv);
  156.             continue;
  157.             }
  158.  
  159.         opt = (*argv) + 1;
  160.         switch (*opt) {
  161.  
  162.          case 'd':        /* specify alternate day font */
  163.              GETARG(dayfont);
  164.              break;
  165.  
  166.         case 'e':        /* generate empty calendar */
  167.             nocal = TRUE;
  168.             date_file = NULL;
  169.             break;
  170.  
  171.         case 'f':        /* specify alternate calendar file */
  172.             GETARG(date_file);
  173.             nocal = FALSE;
  174.             break;
  175.  
  176.         case 'o':        /* specify alternate output file */
  177.             GETARG(outfile);
  178.             break;
  179.  
  180.          case 'r':        /* generate portrait calendar */
  181.              rotate = 0;
  182.              break;
  183.  
  184.         case 'S':        /* Saturdays and Sundays in black */
  185.             sun_gray = FALSE;
  186.         case 's':        /* Saturdays in black */
  187.             sat_gray = FALSE;
  188.             break;
  189.  
  190.          case 't':        /* specify alternate title font */
  191.              GETARG(titlefont);
  192.              break;
  193.  
  194.         default:
  195.             FPR(stderr, "%s: illegal option -%s\n", progname, opt);
  196.             badopt = TRUE;
  197.             break;
  198.         }
  199.         }
  200.  
  201.     /* Get and validate non-flag (numeric) parameters */
  202.  
  203.     switch (nargs) {
  204.     case 0:        /* no arguments - print current month/year */
  205.         time(&tmp);
  206.         p_tm = localtime(&tmp);
  207.         month = p_tm->tm_mon + 1;
  208.         year = p_tm->tm_year;
  209.         nmonths = 1;
  210.         break;            
  211.     case 1:        /* one argument - print entire year */
  212.         month = 1;
  213.         year = numargs[0];
  214.         nmonths = 12;
  215.         break;
  216.     default:    /* two or three arguments - print one or more months */
  217.         month = numargs[0];
  218.         year = numargs[1];
  219.         nmonths = nargs > 2 ? numargs[2] : 1;
  220.         break;
  221.     }
  222.  
  223.     if (year > 0 && year < 100)    /* treat nn as 19nn */
  224.         year += 1900;
  225.     
  226.     if (nmonths < 1)        /* ensure at least one month */
  227.         nmonths = 1;
  228.     
  229.     if (month < 1 || month > 12) {    /* check range of month and year */
  230.         FPR(stderr, "%s: month %d not in range 1 .. 12\n", progname,
  231.             month);
  232.         badpar = TRUE;
  233.     }
  234.     
  235.     if (year < MIN_YR || year > MAX_YR) {
  236.         FPR(stderr, "%s year %d not in range %d .. %d\n", progname,
  237.             year, MIN_YR, MAX_YR);
  238.         badpar = TRUE;
  239.     }
  240.  
  241.     /* command-line errors?  generate usage message and quit */
  242.        
  243.     if (badpar || badopt) {
  244.         usage(progname);
  245.         exit(EXIT_FAILURE);
  246.         }
  247.  
  248.     /* flag and numeric parameters OK - now try to open the files */
  249.  
  250.     if (outfile && freopen(outfile, "w", stdout) == (FILE *) NULL) {
  251.         FPR(stderr, "%s: can't open file %s\n", progname, outfile);
  252.         exit(EXIT_FAILURE);
  253.         }
  254.  
  255.     /*
  256.      * In case we don't encounter any year data in the
  257.      * calendar file, assume the current year.
  258.      */
  259.     curr_year = year;
  260.  
  261.     /*
  262.      * Attempt to open user-specified calendar file first
  263.      */
  264.     if (date_file != NULL) {
  265.         if ((cfp = fopen(date_file, "r")) == NULL) {
  266.             FPR(stderr, "%s: can't open file %s\n", progname, 
  267.                 date_file);
  268.             exit(EXIT_FAILURE);
  269.         }
  270.     }
  271.  
  272.     /*
  273.      * Else see if the default calendar file exists (no error if
  274.      * nonexistent; program will just print empty calendar)
  275.      */
  276.     else if (nocal == FALSE)
  277.         cfp = fopen(INFILE, "r");
  278.  
  279.     /*
  280.      * Write out PostScript prolog
  281.      */
  282.      PRT("%%!\n");
  283.      PRT("/titlefont /%s def\n/dayfont /%s def\n", titlefont, dayfont);
  284.  
  285.     DOHEADER(header_1);
  286.     if (sun_gray) {
  287.         PRT("\t\t\tday start add 7 mod 1 %s {\n",
  288.             sat_gray ? "le" : "eq" );
  289.           PRT("\t\t\t\t.8 setgray\n");
  290.           PRT("\t\t\t} if\n");
  291.         }
  292.     DOHEADER(header_2);
  293.  
  294.      PRT("\t%d rotate\n", rotate);
  295.      if (rotate)
  296.          PRT("\t50 -120 translate\n");
  297.      else
  298.          PRT("\t0.75 0.75 scale\n\t50 460 translate\n");
  299.  
  300.     DOHEADER(header_3);
  301.  
  302.     while (nmonths--) {
  303.         pmonth(month, year);
  304.         if (++month > 12) {
  305.             month = 1;
  306.             year++;
  307.         }
  308.     }
  309.  
  310.     if (outfile)
  311.         FPR(stderr, "Output is in file %s\n", outfile);
  312.  
  313.     exit(EXIT_SUCCESS);
  314. }
  315.  
  316.  
  317. /*
  318.  *    Print message explaining correct usage of the command-line
  319.  *    arguments and flags
  320.  */
  321. usage(prog)
  322.     char *prog;
  323. {
  324.     FPR(stderr, "\nUsage:\n\n");
  325.     FPR(stderr, "\t%s [-d FONT] [-e | -f FILE] [-o FILE] [-r] [-s | -%s] [-t FONT]\n",
  326.         prog, S_OPT);
  327.     FPR(stderr, "\t\t[ [ [mm] yy ] | [mm yy n] ]\n\n");
  328.     FPR(stderr, "\t\t-d FONT\t\tspecify alternate font for day names\n");
  329.     FPR(stderr, "\t\t\t\t(default: %s)\n", DAYFONT);
  330.     FPR(stderr, "\n");
  331.     FPR(stderr, "\t\t-e\t\tgenerate empty calendar (ignore date file)\n");
  332.     FPR(stderr, "\n");
  333.     FPR(stderr, "\t\t-f FILE\t\tspecify alternate date file\n");
  334.     FPR(stderr, "\t\t\t\t(default: %s)\n", INFILE);
  335.     FPR(stderr, "\n");
  336.     FPR(stderr, "\t\t-o FILE\t\tspecify alternate output file\n");
  337.     FPR(stderr, "\t\t\t\t(default: %s)\n", OUTFILE ? OUTFILE : "stdout");
  338.     FPR(stderr, "\n");
  339.     FPR(stderr, "\t\t-r\t\tgenerate portrait-style calendars\n");
  340.     FPR(stderr, "\t\t\t\t(default: landscape)\n");
  341.     FPR(stderr, "\n");
  342.     FPR(stderr, "\t\t-s\t\tprint Saturdays in black\n");
  343.     FPR(stderr, "\t\t-%s\t\tprint Saturdays and Sundays in black\n", S_OPT);
  344.     FPR(stderr, "\t\t\t\t(default: print Saturdays and Sundays in gray)\n");
  345.     FPR(stderr, "\n");
  346.     FPR(stderr, "\t\t-t FONT\t\tspecify alternate font for titles\n");
  347.     FPR(stderr, "\t\t\t\t(default: %s)\n", TITLEFONT);
  348.     FPR(stderr, "\n");
  349.     FPR(stderr, "\t%s [opts]\t\tgenerate calendar for current month/year\n",
  350.         prog);
  351.     FPR(stderr, "\n");
  352.     FPR(stderr, "\t%s [opts] yy\t\tgenerate calendar for entire year yy\n",
  353.         prog);
  354.     FPR(stderr, "\n");
  355.     FPR(stderr, "\t%s [opts] mm yy\tgenerate calendar for month mm\n", prog);
  356.     FPR(stderr, "\t\t\t\t(Jan = 1), year yy (19yy if yy < 100)\n");
  357.     FPR(stderr, "\n");
  358.     FPR(stderr, "\t%s [opts] mm yy n\tas above, for n consecutive months\n",
  359.         prog);
  360.     FPR(stderr, "\n");
  361. }
  362.  
  363.  
  364. /*
  365.  * Browse through the calendar file looking for day info in current month
  366.  */
  367. find_daytext(m, y)
  368.     int m, y;
  369. {
  370.     register char **s;
  371.     register int oldday = -1;
  372.     register int day;
  373.  
  374.     for (day = getday(m, y, TRUE); day != 0; day = getday(m, y, FALSE))
  375.         if (*words) {
  376.             day &= ~HOLIDAY;
  377.             if (day != oldday) {
  378.                 if (oldday == -1)
  379.                     PRT("%d [ \n", day);
  380.                 else
  381.                     PRT("] daytext\n%d [ \n", day);
  382.                 oldday = day;
  383.             } else
  384.                 PRT("(.p)\n");
  385.             for (s = words; *s; s++)
  386.                 PRT("(%s)\n", *s);
  387.         }
  388.  
  389.     if (oldday != -1)        /* terminate call to daytext */
  390.         PRT("] daytext\n");
  391. }
  392.  
  393.  
  394. /*
  395.  * Browse through the calendar file looking for holidays in current month
  396.  */
  397. find_holidays(m, y)
  398.     int m, y;
  399. {
  400.     register int day;
  401.     unsigned long holidays = 0;
  402.  
  403.     /* sort holidays by setting bits in flag word */
  404.     for (day = getday(m, y, TRUE); day != 0; day = getday(m, y, FALSE))
  405.         if (day & HOLIDAY)
  406.             holidays |= 1 << (day & ~HOLIDAY);
  407.  
  408.     PRT("/holidays [");    /* start definition of list */
  409.     for (day = 1; day <= 31; day++)
  410.         if (holidays & (1 << day))
  411.             PRT(" %d", day);
  412.     PRT(" 99 ] def\n");    /* terminate with dummy entry */
  413. }
  414.  
  415.  
  416. /*
  417.  * pmonth - do calendar for month "m"
  418.  */
  419. pmonth(m, y)
  420.     int m, y;
  421. {
  422.  
  423.     PRT("/year %d def\n", y);    /* set up year and month */
  424.     PRT("/month %d def\n", m);
  425.     find_holidays(m, y);        /* first pass - make list of holidays */
  426.     PRT("printmonth\n");
  427.     find_daytext(m, y);        /* second pass - add text to boxes */
  428.     PRT("showpage\n");
  429. }
  430.  
  431.  
  432. /*
  433.  * getday - find next day entry for desired month in the calendar file
  434.  */
  435. int getday(m, y, reset)
  436.     register int m, y;
  437.     int reset;
  438. {
  439.     static eof = 0;
  440.     register char *cp;
  441.     register c;
  442.     int in_comment;        /* comments: from '#' to end-of-line */
  443.  
  444.     if (cfp == NULL)    /* whoops, no calendar file */
  445.         return(0);
  446.  
  447.     if (reset) {        /* new month, rewind */
  448.         rewind(cfp);
  449.         eof = 0;
  450.     }
  451.     if (eof)
  452.         return(0);
  453.  
  454.     do {
  455.         cp = lbuf;
  456.         do {
  457.             in_comment = FALSE;
  458.             while ((c = getc(cfp)) != '\n' && c != EOF) {
  459.                 if (c == '#')
  460.                     in_comment = TRUE;
  461.                 /* ignore comments and leading white space */
  462.                 if (in_comment ||
  463.                     (cp == lbuf && (c == ' ' || c == '\t')))
  464.                     continue;
  465.                 *cp++ = c;
  466.             }
  467.             if (c == EOF) {
  468.                 eof = 1;
  469.                 return(0);
  470.             }
  471.         } while (cp == lbuf);    /* ignore empty lines */
  472.         *cp = 0;
  473.  
  474.     /* examine the line, see if its one we want */
  475.     } while ( (c = parse(m, y)) == 0);
  476.  
  477.     return(c);
  478. }
  479.  
  480. /*
  481.  * parse - check calendar entry for desired month, break line into fields
  482.  */
  483. parse(m, y)
  484.     register int m, y;
  485. {
  486.     register char *cp;
  487.     register i;
  488.     int is_holiday = 0;        /* '*' after date flags it as holiday */
  489.     int valid = 1;
  490.  
  491.     cp = strtok(lbuf, " \t");    /* get first field */
  492.  
  493.     while (*cp) {
  494.         if (isupper(*cp))
  495.             *cp = tolower(*cp);
  496.         cp++;
  497.     }
  498.     cp = lbuf;
  499.  
  500.     /*
  501.      * Check for "year" line
  502.      */
  503.     if (strcmp(cp, "year") == 0) {
  504.         cp = strtok((char *)0, " \t");
  505.         if ((i = atoi(cp)) > 0) {
  506.             if (i < 100)
  507.                 i += 1900;
  508.             curr_year = i;
  509.         }
  510.         return(0);
  511.     }
  512.  
  513.     /*
  514.      * If field begins with alpha, try to decode month name
  515.      */
  516.     if (isalpha(*cp)) {
  517.         if (curr_year != y)
  518.             return(0);
  519.  
  520.         for (i = 0; months[i]; i++)
  521.             if (strncmp(cp, months[i], 3) == 0) {
  522.                 if (++i != m)
  523.                     return(0);
  524.  
  525.                 /* month found, get day */
  526.  
  527.                 if ((cp = strtok((char *)0, " \t")) == NULL)
  528.                     return(0);
  529.                 if ((i = atoi(cp)) < 1 || i > 31)
  530.                     return(0);
  531.                 while (isdigit(*cp))    /* skip over day field */
  532.                     cp++;
  533.                 if (*cp == '*')        /* holiday? */
  534.                     is_holiday = 1;
  535.                 if (loadwords() || is_holiday)
  536.                     return(i | is_holiday * HOLIDAY);
  537.                 return(0);
  538.             }
  539.         return(0);
  540.     }
  541.     /*
  542.      * Not alpha month, try numeric (parse full date to see if year OK)
  543.      */
  544.     if ((i = atoi(cp)) != m)
  545.         valid = 0;
  546.     while (isdigit(*cp))
  547.         cp++;
  548.     while (*cp && !isdigit(*cp))
  549.         cp++;
  550.  
  551.     /* now get day */
  552.  
  553.     if ((i = atoi(cp)) < 1 || i > 31)
  554.         valid = 0;
  555.  
  556.      /* Numeric dates may have a year */
  557.  
  558.     while (isdigit(*cp))        /* skip over day field */
  559.         cp++;
  560.     if (*cp == '*')            /* holiday? */
  561.         is_holiday = 1;
  562.     while (*cp && !isdigit(*cp))
  563.         cp++;
  564.     if ((m = atoi(cp)) > 0) {
  565.         if (m < 100)
  566.             m += 1900;
  567.         curr_year = m;
  568.         while (isdigit(*cp))    /* skip over year field */
  569.             cp++;
  570.         if (*cp == '*')        /* holiday? */
  571.             is_holiday = 1;
  572.     }
  573.  
  574.     if (!valid || curr_year != y)    /* date not applicable - return 0 */
  575.         return(0);
  576.  
  577.     if (loadwords() || is_holiday)    /* date of some significance */
  578.         return(i | is_holiday * HOLIDAY);
  579.     return(0);
  580. }
  581.  
  582.  
  583. /*
  584.  * loadwords - tokenize line buffer into word array, return word count
  585.  */
  586. loadwords()
  587. {
  588.     register char **ap = words;
  589.     register i;
  590.  
  591.     for (i = 0; *ap = strtok((char *)0, " \t") ; ap++, i++) ;
  592.     return(i);
  593. }
  594.  
  595.