home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1412 / pcal.c < prev    next >
C/C++ Source or Header  |  1990-12-28  |  14KB  |  650 lines

  1. /*
  2.  * pcal.c - generate PostScript file to print calendar for any month/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 W. Rogers.
  12.  */
  13.  
  14. #include <stdio.h>
  15. #include <ctype.h>
  16. #include <time.h>
  17. #include <string.h>
  18.  
  19. #define PRT    (void)printf
  20. #define FPR    (void)fprintf
  21.  
  22. #define HOLIDAY    (1 << 6)    /* bit set to flag day as holiday */
  23.  
  24. char *words[100];    /* maximum number of words on a line */
  25. char lbuf[512];        /* maximum line size */
  26.  
  27. char *months[] = {    /* used to match alpha months */
  28.     "jan", "feb", "mar", "apr", "may", "jun",
  29.     "jul", "aug", "sep", "oct", "nov", "dec",
  30.     (char *)0,
  31. };
  32.  
  33. /*
  34.  * the PostScript routines for pcal.c
  35.  */
  36.  
  37. /* modified by AWR to skip printing days of week on small calendars */
  38.  
  39. char *header_1[] = {
  40.   "/month_names [ (January) (February) (March) (April) (May) (June) (July)",
  41.   "\t\t(August) (September) (October) (November) (December) ] def",
  42.   "/prtnum { 3 string cvs show} def",
  43.   "/drawgrid {\t\t% draw calendar boxes",
  44.   "\tdayfont findfont 10 scalefont setfont",
  45.   "\t0 1 6 {",
  46.   "\t\t/i exch def",
  47.   "\t\tsubmonth 0 eq {",
  48.   "\t\t\ti 100 mul 40 moveto",
  49.   "\t\t\t[ (Sunday) (Monday) (Tuesday) (Wednesday) (Thursday) (Friday) (Saturday) ] i get",
  50.   "\t\t\t100 center",
  51.   "\t\t} if",
  52.   "\t\ti 100 mul 35 moveto",
  53.   "\t\t1.0 setlinewidth",
  54.   "\t\t0 1 5 {",
  55.   "\t\t\tgsave",
  56.   "\t\t\t100 0 rlineto ",
  57.   "\t\t\t0 -80 rlineto",
  58.   "\t\t\t-100 0 rlineto",
  59.   "\t\t\tclosepath stroke",
  60.   "\t\t\tgrestore",
  61.   "\t\t\t0 -80 rmoveto",
  62.   "\t\t pop } for",
  63.   "\t} for",
  64.   "} def",
  65.   "/drawnums {\t\t% place day numbers on calendar",
  66.   "\tdayfont findfont 30 scalefont setfont",
  67.   "\t/start startday def",
  68.   "\t/days ndays def",
  69.   "\t/n 0 def",
  70.   "\tstart 100 mul 5 add 10 rmoveto",
  71.   "\t1 1 days {",
  72.   "\t\t/day exch def",
  73.   "\t\tgsave",
  74.   "\t\tsubmonth 0 eq {",
  75.   (char *)0
  76.   };
  77.  
  78. /* modified by AWR: choice of black or gray Saturdays now command-line option */
  79.  
  80. char *header_2[] = {            /* set Saturdays to gray */
  81.   "\t\t\tday start add 7 mod 0 eq {",
  82.   "\t\t\t\t.8 setgray",
  83.   "\t\t\t} if",
  84.   (char *)0
  85.   };
  86.  
  87. /* modified by AWR: calculate leap years correctly, print holidays in gray */
  88.  
  89. char *header_3[] = {
  90.   "\t\t\tday start add 7 mod 1 eq {",
  91.   "\t\t\t\t.8 setgray",
  92.   "\t\t\t} if",
  93.   "\t\t\tday holidays n get eq {",
  94.   "\t\t\t\t.8 setgray",
  95.   "\t\t\t\t/n n 1 add def",
  96.   "\t\t\t} if",
  97.   "\t\t} if",
  98.   "\t\tday prtnum",
  99.   "\t\tgrestore",
  100.   "\t\tday start add 7 mod 0 eq",
  101.   "\t\t{",
  102.   "\t\t\tcurrentpoint exch pop 80 sub 5 exch moveto",
  103.   "\t\t}",
  104.   "\t\t{",
  105.   "\t\t\t100 0 rmoveto",
  106.   "\t\t} ifelse",
  107.   "\t} for",
  108.   "} def",
  109.   "/drawfill {\t\t% place fill squares on calendar",
  110.   "\t/start startday def",
  111.   "\t/days ndays def",
  112.   "\t0 35 rmoveto",
  113.   "\t1.0 setlinewidth",
  114.   "\t0 1 start 1 sub {",
  115.   "\t\tgsave",
  116.   "\t\t.9 setgray",
  117.   "\t\t100 0 rlineto ",
  118.   "\t\t0 -80 rlineto",
  119.   "\t\t-100 0 rlineto",
  120.   "\t\tclosepath fill",
  121.   "\t\tgrestore",
  122.   "\t\t100 0 rmoveto",
  123.   "\tpop } for",
  124.   "\tsubmonth 1 eq",
  125.   "\t{",
  126.   "\t\t/lastday 42 def",
  127.   "\t\t600 -365 moveto",
  128.   "\t}",
  129.   "\t{",
  130.   "\t\t/lastday 40 def",
  131.   "\t\t400 -365 moveto",
  132.   "\t} ifelse",
  133.   "\tlastday -1 ndays start 1 add add",
  134.   "\t{",
  135.   "\t\t/day exch def",
  136.   "\t\tgsave",
  137.   "\t\t.9 setgray",
  138.   "\t\t100 0 rlineto ",
  139.   "\t\t0 -80 rlineto",
  140.   "\t\t-100 0 rlineto",
  141.   "\t\tclosepath fill",
  142.   "\t\tgrestore",
  143.   "\t\tday 7 mod 1 eq",
  144.   "\t\t{",
  145.   "\t\t\t600 -365 80 add moveto",
  146.   "\t\t}",
  147.   "\t\t{",
  148.   "\t\t\t-100 0 rmoveto",
  149.   "\t\t} ifelse",
  150.   "\t} for",
  151.   "} def",
  152.   "/isleap {\t\t% is this a leap year?",
  153.   "\tyear 4 mod 0 eq\t\t% multiple of 4",
  154.   "\tyear 100 mod 0 ne \t% not century",
  155.   "\tyear 400 mod 0 eq or and\t% or divisible by 400",
  156.   "} def",
  157.   "/days_month [ 31 28 31 30 31 30 31 31 30 31 30 31 ] def",
  158.   "/ndays {\t\t% number of days in this month",
  159.   "\tdays_month month 1 sub get",
  160.   "\tmonth 2 eq\t% Feb",
  161.   "\tisleap and",
  162.   "\t{",
  163.   "\t\t1 add",
  164.   "\t} if",
  165.   "} def",
  166.   "/startday {\t\t% starting day-of-week for this month",
  167.   "\t/off year 2000 sub def\t% offset from start of epoch",
  168.   "\toff",
  169.   "\toff 4 idiv add\t\t% number of leap years",
  170.   "\toff 100 idiv sub\t% number of centuries",
  171.   "\toff 400 idiv add\t% number of years divisible by 400",
  172.   "\t6 add 7 mod 7 add \t% offset from Jan 1 2000",
  173.   "\t/off exch def",
  174.   "\t1 1 month 1 sub {",
  175.   "\t\t/idx exch def",
  176.   "\t\tdays_month idx 1 sub get",
  177.   "\t\tidx 2 eq",
  178.   "\t\tisleap and",
  179.   "\t\t{",
  180.   "\t\t\t1 add",
  181.   "\t\t} if",
  182.   "\t\t/off exch off add def",
  183.   "\t} for",
  184.   "\toff 7 mod\t\t% 0--Sunday, 1--monday, etc.",
  185.   "} def",
  186.   "/center {\t\t% center string in given width",
  187.   "\t/width exch def",
  188.   "\t/str exch def width str ",
  189.   "\tstringwidth pop sub 2 div 0 rmoveto str show",
  190.   "} def",
  191.   "/calendar",
  192.   "{",
  193.   "\ttitlefont findfont 48 scalefont setfont",
  194.   "\t0 60 moveto",
  195.   "\t/month_name month_names month 1 sub get def",
  196.   "\tmonth_name show",
  197.   "\t/yearstring year 10 string cvs def",
  198.   "\t700 yearstring stringwidth pop sub 60 moveto",
  199.   "\tyearstring show",
  200.   "\t0 0 moveto",
  201.   "\tdrawnums",
  202.   "\t0 0 moveto",
  203.   "\tdrawfill",
  204.   "\t0 0 moveto",
  205.   "\tdrawgrid",
  206.   "} def",
  207.   "/daytext {",
  208.   "\t/Helvetica-Narrow findfont 6 scalefont setfont",
  209.   "\t/mytext\texch def /myday exch def",
  210.   "\tstartday myday 1 sub add dup 7 mod 100 mul 5 add % gives column",
  211.   "\texch 7 idiv -80 mul % gives row",
  212.   "\tdup /ypos exch def moveto",
  213.   "\t/LM currentpoint pop def /RM LM 95 add def",
  214.   "        mytext { dup (.p) eq { crlf pop} {prstr ( ) show} ifelse } forall",
  215.   "} def",
  216.   "/crlf {",
  217.   "    ypos 8 sub /ypos exch def LM ypos moveto",
  218.   "} def",
  219.   "/prstr {",
  220.   "    dup stringwidth pop currentpoint pop",
  221.   "    add RM gt {crlf} if show",
  222.   "} def",
  223.   "/printmonth {",
  224.    (char *)0,
  225.  };
  226.  
  227.  char *header_4[] = {
  228.   "\t/submonth 0 def",
  229.   "\tcalendar",
  230.   "\tmonth 1 sub 0 eq",
  231.   "\t{",
  232.   "\t\t/lmonth 12 def",
  233.   "\t\t/lyear year 1 sub def",
  234.   "\t}",
  235.   "\t{",
  236.   "\t\t/lmonth month 1 sub def",
  237.   "\t\t/lyear year def",
  238.   "\t} ifelse",
  239.   "\tmonth 1 add 13 eq",
  240.   "\t{",
  241.   "\t\t/nmonth 1 def",
  242.   "\t\t/nyear year 1 add def",
  243.   "\t} ",
  244.   "\t{",
  245.   "\t\t/nmonth month 1 add def",
  246.   "\t\t/nyear year def",
  247.   "\t} ifelse",
  248.   "\t/savemonth month def",
  249.   "\t/saveyear year def",
  250.   "\t/submonth 1 def",
  251.   "\t/year lyear def",
  252.   "\t/month lmonth def",
  253.   "\tgsave",
  254.   "\t500 -365 translate",
  255.   "\tgsave",
  256.   "\t.138 .138 scale",
  257.   "\t10 -120 translate",
  258.   "\tcalendar",
  259.   "\tgrestore",
  260.   "\t/submonth 1 def",
  261.   "\t/year nyear def",
  262.   "\t/month nmonth def",
  263.   "\t100 0 translate",
  264.   "\tgsave",
  265.   "\t.138 .138 scale",
  266.   "\t10 -120 translate",
  267.   "\tcalendar",
  268.   "\tgrestore",
  269.   "\t/month savemonth def",
  270.   "\t/year saveyear def",
  271.   "\t/submonth 0 def",
  272.   "\tgrestore",
  273.   "} def",
  274.   (char *)0,
  275. };
  276.  
  277. FILE *cfp = NULL;
  278. int year;
  279. int cyear;
  280.  
  281. void exit();
  282. char *getenv();
  283.  
  284. main(argc, argv)
  285. int argc;
  286. char **argv;
  287. {
  288.     extern char *optarg;
  289.     extern int optind;
  290.     register struct tm *lt;
  291.     register char **ap;
  292.     register char *cp;
  293.     char *cfile = NULL;
  294.     char cbuf[80];
  295.     long t, time();
  296.     int errflg = 0;
  297.     int nocal = 0;
  298.     int sat = 0;
  299.     char *titlefont = "Times-Bold";
  300.      char *dayfont = "Times-Bold";
  301.      int rotate = 90;
  302.      int month = 0;
  303.     int m;
  304.     char doyear = 0;
  305.  
  306. #define DOHEADER(phdr) for(ap = phdr; *ap; ap++) PRT("%s\n", *ap);
  307.  
  308.     while ((m = getopt(argc, argv, "d:ef:m:rst:y:")) != EOF)
  309.  
  310.         switch (m) {
  311.  
  312.         case 'd':        /* select font for day names/numbers */
  313.             dayfont = optarg;
  314.             break;
  315.  
  316.         case 'e':        /* print empty calendar */
  317.             nocal++;
  318.             cfile = NULL;
  319.             break;
  320.  
  321.         case 'f':        /* use alternate calendar file */
  322.             cfile = optarg;
  323.             nocal = 0;
  324.             break;
  325.  
  326.         case 'm':        /* select month */
  327.             month = atoi(optarg);
  328.             if (!month) doyear = 1;
  329.             break;
  330.  
  331.         case 'r':        /* generate portrait calendar */
  332.             rotate = 0;
  333.             break;
  334.  
  335.         case 's':        /* print Saturdays in black */
  336.             sat++;
  337.             break;
  338.  
  339.         case 't':        /* select font for month/year */
  340.             titlefont = optarg;
  341.             break;
  342.  
  343.         case 'y':        /* select year */
  344.             year = atoi(optarg);
  345.             if (year && year < 1900) year = year % 100 + 1900;
  346.             break;
  347.  
  348.         case '?':
  349.             errflg = 1;
  350.             break;
  351.         }
  352.  
  353.     if (errflg) {
  354.         FPR(stderr,
  355.             "Usage: pcal [ -r ] [ -s ] [ -e | -f <cal> ] [ -m month] [ -y <year> ]\n");
  356.         FPR(stderr, 
  357.             "\t\t[ -t <title font> ] [ -d <day font> ]\n");
  358.         exit(1);
  359.     }
  360.     t = time((long *)0);
  361.     lt = localtime(&t);
  362.  
  363.     if (!month && !doyear)
  364.         month = lt->tm_mon + 1;
  365.     if (!year)
  366.         year = lt->tm_year + 1900;
  367.  
  368.     /*
  369.      * In case we don't encounter any year data in the
  370.      * calendar file, assume the current year.
  371.      */
  372.     cyear = year;
  373.  
  374.     /*
  375.      * Open a supplied calendar file (if any)
  376.      */
  377.     if (cfile != NULL) {
  378.         if ((cfp = fopen(cfile, "r")) == NULL) {
  379.             FPR(stderr, "pcal: can't open file: %s\n", cfile);
  380.             exit(1);
  381.         }
  382.     }
  383.     /*
  384.      * Else see if a calendar file exists in the home directory
  385.      */
  386.     else if (nocal == 0 && (cp = getenv("HOME")) != NULL) {
  387.         (void)strcpy(cbuf, cp);
  388.         (void)strcat(cbuf, "/calendar");
  389.         cfp = fopen(cbuf, "r");
  390.     }
  391.  
  392.     /*
  393.      * Write out PostScript prolog
  394.      */
  395.      PRT("%%!\n");
  396.      PRT("/titlefont /%s def\n/dayfont /%s def\n", titlefont, dayfont);
  397.  
  398.     DOHEADER(header_1);
  399.     if (sat == 0)
  400.         DOHEADER(header_2);
  401.     DOHEADER(header_3);
  402.  
  403.      PRT("\t%d rotate\n", rotate);
  404.      if (rotate)
  405.          PRT("\t50 -120 translate\n");
  406.      else
  407.          PRT("\t0.75 0.75 scale\n\t50 460 translate\n");
  408.  
  409.     DOHEADER(header_4);
  410.  
  411.     if (month)
  412.         pmonth(month);
  413.     else
  414.         for (month = 1; month <= 12; month++)
  415.             pmonth(month);
  416.  
  417.     exit(0);
  418. }
  419.  
  420. /*
  421.  * Browse through the calendar file looking for day info in current month
  422.  */
  423. find_daytext(m)
  424. int m;
  425. {
  426.     register char **s;
  427.     register int oldday = -1;
  428.     register int day;
  429.     int reset;
  430.  
  431.     for (reset = 1; (day = getday(m, reset)) != 0; reset = 0)
  432.         if (*words) {
  433.             day &= ~HOLIDAY;
  434.             if (day != oldday) {
  435.                 if (oldday == -1)
  436.                     PRT("%d [ \n", day);
  437.                 else
  438.                     PRT("] daytext\n%d [ \n", day);
  439.                 oldday = day;
  440.             } else
  441.                 PRT("(.p)\n");
  442.             for (s = words; *s; s++)
  443.                 PRT("(%s)\n", *s);
  444.         }
  445.  
  446.     if (oldday != -1)        /* terminate call to daytext */
  447.         PRT("] daytext\n");
  448. }
  449.  
  450.  
  451. /*
  452.  * Browse through the calendar file looking for holidays in current month
  453.  */
  454. find_holidays(m)
  455. int m;
  456. {
  457.     register int oldday = -1;
  458.     register int day;
  459.     int reset;
  460.  
  461.     PRT("/holidays [");    /* start definition of list */
  462.  
  463.     for (reset = 1; (day = getday(m, reset)) != 0; reset = 0)
  464.         if (day & HOLIDAY) {
  465.             day &= ~HOLIDAY;
  466.             if (day != oldday) {
  467.                 PRT(" %d", day);
  468.                 oldday = day;
  469.             }
  470.         }
  471.  
  472.     PRT(" 99 ] def\n");    /* terminate with dummy entry */
  473. }
  474.  
  475.  
  476. /*
  477.  * pmonth - do calendar for month "m"
  478.  */
  479. pmonth(m)
  480. int m;
  481. {
  482.  
  483.     PRT("/year %d def\n", year);    /* set up year and month */
  484.     PRT("/month %d def\n", m);
  485.     find_holidays(m);        /* first pass - make list of holidays */
  486.     PRT("printmonth\n");
  487.     find_daytext(m);        /* second pass - add text to boxes */
  488.     PRT("showpage\n");
  489. }
  490.  
  491.  
  492. /*
  493.  * getday - find next day entry for desired month in the calendar file
  494.  */
  495. int getday(m, reset)
  496. register int m;
  497. int reset;
  498. {
  499.     static eof = 0;
  500.     register char *cp;
  501.     register c;
  502.  
  503.     if (cfp == NULL)    /* whoops, no calendar file */
  504.         return(0);
  505.  
  506.     if (reset) {        /* new month, rewind */
  507.         rewind(cfp);
  508.         eof = 0;
  509.     }
  510.     if (eof)
  511.         return(0);
  512. nextline:
  513.     cp = lbuf;
  514.     do {
  515.         while ((c = getc(cfp)) != '\n' && c != EOF) {
  516.             /* ignore leading white space */
  517.             if (cp == lbuf && (c == ' ' || c == '\t'))
  518.                 continue;
  519.             *cp++ = c;
  520.         }
  521.         if (c == EOF) {
  522.             eof = 1;
  523.             return(0);
  524.         }
  525.     } while (cp == lbuf);    /* ignore empty lines */
  526.     *cp = 0;
  527.  
  528.     /* examine the line, see if its one we want */
  529.     if ((c = parse(m)) == 0)
  530.         goto nextline;
  531.  
  532.     return(c);
  533. }
  534.  
  535. /*
  536.  * parse - check calendar entry for desired month, break line into fields
  537.  */
  538. parse(m)
  539. register m;
  540. {
  541.     register char *cp;
  542.     register i;
  543.     int is_holiday = 0;        /* '*' after date flags it as holiday */
  544.     int valid = 1;
  545.  
  546.     cp = strtok(lbuf, " \t");    /* get first field */
  547.  
  548.     while (*cp) {
  549.         if (isupper(*cp))
  550.             *cp = tolower(*cp);
  551.         cp++;
  552.     }
  553.     cp = lbuf;
  554.  
  555.     /*
  556.      * Check for "year" line
  557.      */
  558.     if (strcmp(cp, "year") == 0) {
  559.         cp = strtok((char *)0, " \t");
  560.         if ((i = atoi(cp)) > 0) {
  561.             if (i < 100)
  562.                 i += 1900;
  563.             cyear = i;
  564.         }
  565.         return(0);
  566.     }
  567.  
  568.     /*
  569.      * If field begins with alpha, try to decode month name
  570.      */
  571.     if (isalpha(*cp)) {
  572.         if (cyear != year)
  573.             return(0);
  574.  
  575.         for (i = 0; months[i]; i++)
  576.             if (strncmp(cp, months[i], 3) == 0) {
  577.                 if (++i != m)
  578.                     return(0);
  579.  
  580.                 /* month found, get day */
  581.  
  582.                 if ((cp = strtok((char *)0, " \t")) == NULL)
  583.                     return(0);
  584.                 if ((i = atoi(cp)) < 1 || i > 31)
  585.                     return(0);
  586.                 while (isdigit(*cp))    /* skip over day field */
  587.                     cp++;
  588.                 if (*cp == '*')        /* holiday? */
  589.                     is_holiday = 1;
  590.                 if (loadwords() || is_holiday)
  591.                     return(i | is_holiday * HOLIDAY);
  592.                 return(0);
  593.             }
  594.         return(0);
  595.     }
  596.     /*
  597.      * Not alpha month, try numeric (parse full date to see if year has changed)
  598.      */
  599.     if ((i = atoi(cp)) != m)
  600.         valid = 0;
  601.     while (isdigit(*cp))
  602.         cp++;
  603.     while (*cp && !isdigit(*cp))
  604.         cp++;
  605.  
  606.     /* now get day */
  607.  
  608.     if ((i = atoi(cp)) < 1 || i > 31)
  609.         valid = 0;
  610.  
  611.      /* Numeric dates may have a year */
  612.  
  613.     while (isdigit(*cp))        /* skip over day field */
  614.         cp++;
  615.     if (*cp == '*')            /* holiday? */
  616.         is_holiday = 1;
  617.     while (*cp && !isdigit(*cp))
  618.         cp++;
  619.     if ((m = atoi(cp)) > 0) {
  620.         if (m < 100)
  621.             m += 1900;
  622.         cyear = m;
  623.         while (isdigit(*cp))    /* skip over year field */
  624.             cp++;
  625.         if (*cp == '*')        /* holiday? */
  626.             is_holiday = 1;
  627.     }
  628.  
  629.     if (!valid || cyear != year)    /* date not applicable - return 0 */
  630.         return(0);
  631.  
  632.     if (loadwords() || is_holiday)    /* date of some significance */
  633.         return(i | is_holiday * HOLIDAY);
  634.     return(0);
  635. }
  636.  
  637.  
  638. /*
  639.  * loadwords - tokenize line buffer into word array, return word count
  640.  */
  641. loadwords()
  642. {
  643.     register char **ap = words;
  644.     register i;
  645.  
  646.     for (i = 0; *ap = strtok((char *)0, " \t") ; ap++, i++) ;
  647.     return(i);
  648. }
  649.  
  650.