home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1841 < prev    next >
Internet Message Format  |  1990-12-28  |  81KB

  1. From: rogers@sud509.ed.ray.com (Andrew Rogers)
  2. Newsgroups: alt.sources
  3. Subject: Pcal 2.3
  4. Message-ID: <2483@sud509.ed.ray.com>
  5. Date: 19 Sep 90 17:31:55 GMT
  6.  
  7. You guessed it - another revision of Pcal.  Probably my last, as it turns
  8. out, since I will be leaving my current job at the end of the month and
  9. may not have net access (or access to the alt.* groups) at my next job.
  10. Anyway, version 2.3 (2.2 was not released to alt.sources) merges the
  11. following changes:
  12.  
  13.     Joe Brownlee added a mechanism by which notes can be specified in
  14.     one of the unused calendar boxes.  The date file may now contain
  15.     lines of the form
  16.  
  17.         note <month> <text>
  18.  
  19.     where <month> may be numeric or alphabetic in form; any text
  20.     specified is placed in the box next to the small calendars.
  21.  
  22.     Jamie Zawinski added a command-line option to print the phase
  23.     of the moon: -m prints it only on full, new, and half moons,
  24.     while -M prints it on all days.  He also wrote a program,
  25.     pcalinit.c, which generates pcalinit.h automatically from
  26.     PostScript source in pcalinit.ps and tweaked the Makefile
  27.     accordingly.
  28.  
  29.     Richard Dyson supplied a more VMS-specific usage() message.
  30.  
  31.     My own contributions were basically merging all of the above,
  32.     revising the logic of parse(), and supplying a VMS script,
  33.     make_pcal.com, to generate pcalinit.h and build pcal.
  34.  
  35. The accompanying file called "ReadMe.orig" came with the original distribution
  36. as README and states this program is copyrighted but with permission to modify
  37. and redistribute.
  38.  
  39. Andrew W. Rogers
  40. 9/18/90
  41.  
  42. PS: Suggestion for future direction - my desk calendar has the number of
  43. each day (1-365) and days remaining (364-0) on each page.  Anyone want to
  44. add that feature?
  45.  
  46.  
  47. Additional note: This distribution includes a VMS HELP file written by
  48. Richard Dyson.  Countless other people worked on pcal long before me; see
  49. the ReadMe.orig file and topline comments in pcal.c. 
  50.  
  51.  
  52.  
  53. -------------------------------- cut here --------------------------------
  54. #! /bin/sh
  55. # This is a shell archive, meaning:
  56. # 1. Remove everything above the #! /bin/sh line.
  57. # 2. Save the resulting text in a file.
  58. # 3. Execute the file with /bin/sh (not csh) to create:
  59. #     Makefile
  60. #     ReadMe
  61. #     ReadMe.orig
  62. #     calendar
  63. #     make_pcal.com
  64. #     pcal.c
  65. #     pcal.hlp
  66. #     pcal.man
  67. #     pcalinit.c
  68. #     pcalinit.ps
  69. # This archive created: Wed Sep 19 13:23:15 EDT 1990
  70. export PATH; PATH=/bin:/usr/bin:$PATH
  71. if test -f 'Makefile'
  72. then
  73.     echo shar: will not over-write existing file 'Makefile'
  74. else
  75. cat << \SHAR_EOF > 'Makefile'
  76. #
  77. MANDIR=/usr1/jad/man            # must change this
  78.  
  79. pcal:    pcal.c pcalinit.h
  80.     $(CC) $(CFLAGS) $(LDFLAGS) $(COPTS) -o pcal pcal.c
  81.  
  82. pcalinit: pcalinit.c
  83.     $(CC) $(CFLAGS) $(LDFLAGS) $(COPTS) -o pcalinit pcalinit.c
  84.  
  85. pcalinit.h: pcalinit pcalinit.ps
  86.     pcalinit pcalinit.ps pcalinit.h
  87.  
  88. man:    pcal.man
  89.     nroff -man pcal.man > pcal.1
  90.     pack pcal.1
  91. #    mv pcal.1.z $(MANDIR)
  92. SHAR_EOF
  93. fi
  94. if test -f 'ReadMe'
  95. then
  96.     echo shar: will not over-write existing file 'ReadMe'
  97. else
  98. cat << \SHAR_EOF > 'ReadMe'
  99. Version 2.3 (2.2 was not released to alt.sources) merges the following changes:
  100.  
  101.     Joe Brownlee added a mechanism by which notes can be specified in
  102.     one of the unused calendar boxes.  The date file may now contain
  103.     lines of the form
  104.  
  105.         note <month> <text>
  106.  
  107.     where <month> may be numeric or alphabetic in form; any text
  108.     specified is placed in the box next to the small calendars.
  109.  
  110.     Jamie Zawinski added a command-line option to print the phase
  111.     of the moon: -m prints it only on full, new, and half moons,
  112.     while -M prints it on all days.  He also wrote a program,
  113.     pcalinit.c, which generates pcalinit.h automatically from
  114.     PostScript source in pcalinit.ps and tweaked the Makefile
  115.     accordingly.
  116.  
  117.     Richard Dyson supplied a more VMS-specific usage() message.
  118.  
  119.     My own contributions were basically merging all of the above,
  120.     revising the logic of parse(), and supplying a VMS script,
  121.     make_pcal.com, to generate pcalinit.h and build pcal.
  122.  
  123. The accompanying file called "ReadMe.orig" came with the original distribution
  124. as README and states this program is copyrighted but with permission to modify
  125. and redistribute.
  126.  
  127. Andrew W. Rogers
  128.  
  129. Additional note: This distribution includes a VMS HELP file written by
  130. Richard Dyson.  Countless other people worked on pcal long before me; see
  131. the ReadMe.orig file and topline comments in pcal.c. 
  132. SHAR_EOF
  133. fi
  134. if test -f 'ReadMe.orig'
  135. then
  136.     echo shar: will not over-write existing file 'ReadMe.orig'
  137. else
  138. cat << \SHAR_EOF > 'ReadMe.orig'
  139. "Pcal" is a program to print PostScript calendars for any month and year.
  140. By default, it looks for a file in the home directory named "calendar"
  141. for entries with leading dates matching dates on the calendar, and prints
  142. any following text under the appropriate day.
  143.  
  144. The program may be a little System V flavored (getopt, time routines)
  145. but should be easily portable to other vintages of UNIX.
  146.  
  147. Pcal is the combined effort of several people, most notably Patrick Wood
  148. of Pipeline Associates, Inc. for the original PostScript code and Bill
  149. Vogel of AT&T for the calendar file mechanism.  My part was simple
  150. translation to a "C" program, the addition of a couple options and a more
  151. generalized date searching routine (oh yes, and a manual page :-).
  152.  
  153. The original calendar PostScript was Copyright (c) 1987 by Patrick Wood
  154. and Pipeline Associates, Inc. with permission to modify and redistribute.
  155. Please retain this README file with the package.
  156.  
  157.  
  158. Ken Keirnan
  159. Pacific Bell
  160. San Ramon, CA.
  161. SHAR_EOF
  162. fi
  163. if test -f 'calendar'
  164. then
  165.     echo shar: will not over-write existing file 'calendar'
  166. else
  167. cat << \SHAR_EOF > 'calendar'
  168. # Sample calendar file for pcal
  169. #
  170. # This should be ~/.calendar on Unix, SYS$LOGIN:CALENDAR.DAT on VMS
  171. #
  172. # Valid entries are of the following forms:
  173. #
  174. #    opt <options>
  175. #    year <year>
  176. #    <month_name> <day>{*} {<text>}
  177. #    <month><sep><day>{<sep><year>}{*} {<text>}
  178. #    note <month> {<text>}
  179. #    note <month_name> {<text>}
  180. #
  181. # where:
  182. #    <options> := one or more valid command-line options (except -e and -f)
  183. #    <month_name> := first 3+ characters of name of month (in English)
  184. #    <sep> := one or more non-numeric, non-space, non-'*' characters
  185. #    <text> is the text to be printed in the calendar box
  186. #    <day>, <month>, and <year> are appropriate integers
  187. #
  188. #    whitespace is to be used/avoided as implied by the above productions
  189. #    '*' flags the date as a holiday (to be printed in gray)
  190. #    comments run from '#' through end-of-line
  191.  
  192. # A sample "opt" line to change the fonts and output file names, to print
  193. # only Sundays in gray, and to print moons on all days:
  194. #
  195. #opt -d Helvetica-Bold -t Helvetica-Bold -o myfile.ps -b all -g sun -M
  196.  
  197. year 1990                # set year explicitly
  198.  
  199. 5/28* Memorial Day (observed)        # '*' prints holiday in gray
  200. 5/31 Memorial Day
  201.  
  202. 7/4/90* Independence Day        # full date format
  203.  
  204. Sep 3* Labor Day            # month written out
  205.  
  206. 10/8* Columbus Day (observed)
  207. 10/12 Columbus Day
  208.  
  209. 11/22* Thanksgiving
  210. 11/23*                    # holiday without text
  211.  
  212. 12/24* # Christmas Eve
  213. 12/25* Christmas
  214.  
  215. note Dec Some consider Christmas Eve a holiday
  216.  
  217. 1/1/91* New Year's Day            # set new year implicitly
  218. SHAR_EOF
  219. fi
  220. if test -f 'make_pcal.com'
  221. then
  222.     echo shar: will not over-write existing file 'make_pcal.com'
  223. else
  224. cat << \SHAR_EOF > 'make_pcal.com'
  225. $! make_pcal.com - VMS command script to build/run pcalinit, create pcalinit.h
  226. $! from pcalinit.ps, and compile/link pcalinit.c
  227. $
  228. $ cc pcalinit.c
  229. $ link pcalinit
  230. $ pcalinit = "$" + f$environment("DEFAULT") + "pcalinit"
  231. $ pcalinit pcalinit.ps pcalinit.h
  232. $ delete/exclude=(*.c,*.h,*.ps) pcalinit.*;*
  233. $
  234. $ cc pcal.c
  235. $ link pcal.obj
  236. $ pcal == "$" + f$environment("DEFAULT") + "pcal"
  237. $ show symbol pcal
  238. SHAR_EOF
  239. fi
  240. if test -f 'pcal.c'
  241. then
  242.     echo shar: will not over-write existing file 'pcal.c'
  243. else
  244. cat << \SHAR_EOF > 'pcal.c'
  245. /*
  246.  * pcal.c - generate PostScript file to print calendar for any month and year
  247.  *
  248.  * The original PostScript code to generate the calendars was written by
  249.  * Patrick Wood (Copyright (c) 1987 by Patrick Wood of Pipeline Associates,
  250.  * Inc.), and authorized for modification and redistribution.  The calendar
  251.  * file inclusion code was originally written in "bs(1)" by Bill Vogel of
  252.  * AT&T.  Patrick's original PostScript was modified and enhanced several
  253.  * times by others whose names have regrettably been lost.  This C version
  254.  * was originally created by Ken Keirnan of Pacific Bell; additional
  255.  * enhancements by Joseph P. Larson, Ed Hand, and Andrew Rogers (who also
  256.  * did the VMS port), Mark Kantrowitz, and Joe Brownlee.  The moon routines
  257.  * were originally written by Mark Hanson <cs62a12@wind.ucsd.edu>, and were
  258.  * improved and incorporated into this version by Jamie Zawinski
  259.  * <jwz@lucid.com>.
  260.  *
  261.  * Revision history:
  262.  *
  263.  *    2.3    jwz    09/18/90    Added moon routines
  264.  *
  265.  *    2.2    AWR    09/17/90    revise logic of parse(); new usage
  266.  *                    message
  267.  *
  268.  *        JAB/AWR    09/14/90    support "note" lines in date file
  269.  *
  270.  *    2.1    MK/AWR    08/27/90    support -L, -C, -R, -n options;
  271.  *                    print holiday text next to date
  272.  *
  273.  *        AWR    08/24/90    incorporate cpp-like functionality;
  274.  *                    add -D and -U options; save date file
  275.  *                    information in internal data structure;
  276.  *                    look for PCAL_OPTS and PCAL_DIR; look
  277.  *                    for ~/.calendar and ~/calendar
  278.  *
  279.  *    2.0    AWR    08/08/90    included revision history; replaced -r
  280.  *                    flag with -l and -p; replaced -s and -S
  281.  *                    flags with -b and -g; recognize flags
  282.  *                    set in date file; translate ( and ) in
  283.  *                    text to octal escape sequence; usage()
  284.  *                    message condensed to fit 24x80 screen
  285.  *
  286.  *    Parameters:
  287.  *
  288.  *        pcal [opts]        generate calendar for current month/year
  289.  *
  290.  *        pcal [opts] yy        generate calendar for entire year yy
  291.  *
  292.  *        pcal [opts] mm yy    generate calendar for month mm
  293.  *                    (Jan = 1), year yy (19yy if yy < 100)
  294.  *
  295.  *        pcal [opts] mm yy n    as above, for n consecutive months
  296.  *
  297.  *    Output:
  298.  *
  299.  *        PostScript file to print calendars for all selected months.
  300.  *
  301.  *    Options:
  302.  *
  303.  *        -b <DAY>    print specified weekday in black
  304.  *        -g <DAY>    print specified weekday in gray
  305.  *                (default: print Saturdays and Sundays in gray)
  306.  *        
  307.  *        -d <FONT>    specify alternate font for day names
  308.  *                (default: Times-Bold)
  309.  *
  310.  *        -n <FONT>    specify alternate font for notes in boxes
  311.  *                (default: Helvetica-Narrow)
  312.  *
  313.  *        -t <FONT>    specify alternate font for titles
  314.  *                (default: Times-Bold)
  315.  *
  316.  *        -D <SYM>    define preprocessor symbol
  317.  *        -U <SYM>    un-define preprocessor symbol
  318.  *
  319.  *        -e        generate empty calendar (ignore date file)
  320.  *
  321.  *        -f <FILE>    specify alternate date file (default:
  322.  *                ~/.calendar on Un*x, SYS$LOGIN:CALENDAR.DAT
  323.  *                on VMS; if environment variable [logical
  324.  *                name on VMS] PCAL_DIR exists, looks there
  325.  *                instead)
  326.  *
  327.  *        -o <FILE>    specify alternate output file (default:
  328.  *                stdout on Un*x, CALENDAR.PS on VMS)
  329.  *
  330.  *        -L <STRING>    specify left foot string   (default: "")
  331.  *        -C <STRING>    specify center foot string (default: "")
  332.  *        -R <STRING>    specify right foot string  (default: "")
  333.  *
  334.  *        -l        generate landscape-mode calendars
  335.  *        -p        generate portrait-mode calendars
  336.  *                (default: landscape-mode)
  337.  *
  338.  *        -m        draw a small moon icon on the days of the
  339.  *                full, new, and half moons.
  340.  *        -M        draw a small moon icon every day.  
  341.  *                (default: no moons).
  342.  *
  343. *    There are many ways to specify these options in addition to using the
  344.  *    command line; this facilitates customization to the user's needs.
  345.  *
  346.  *    If the environment variable (global symbol on VMS) PCAL_OPTS is
  347.  *    present, its value will be parsed as if it were a command line.
  348.  *    Any options specified will override the program defaults.
  349.  *
  350.  *    All but the -e, -f, -D, and -U options may be specified in the date
  351.  *    file by the inclusion of one or more lines of the form "opt <options>".
  352.  *    Any such options override any previous values set either as program
  353.  *    defaults, via PCAL_OPTS, or in previous "opt" lines.
  354.  *
  355.  *    Options explicitly specified on the command line in turn override all
  356.  *    of the above.
  357.  *
  358.  *    Any flag which normally takes an argument may also be specified without
  359.  *    an argument; this resets the corresponding option to its default.  -D
  360.  *    alone un-defines all symbols; -U alone has no effect.
  361.  *
  362.  *    Parameters and flags may be mixed on the command line.  In some cases
  363.  *    (e.g., when a parameter follows a flag without its optional argument)
  364.  *    this may lead to ambiguity; the dummy flag '-' (or '--') may be used
  365.  *    to separate them, i.e. "pcal -t - 9 90".
  366.  *
  367.  *    Simple cpp-like functionality is provided.  The date file may include
  368.  *    the following commands, which work like their cpp counterparts:
  369.  *
  370.  *        define <sym>
  371.  *        undef <sym>
  372.  *
  373.  *        if{n}def <sym>
  374.  *           ...
  375.  *        { else
  376.  *           ... }
  377.  *        endif
  378.  *
  379.  *        include <file>
  380.  *
  381.  *    Note that these do not start with '#', which is reserved as a comment
  382.  *    character.
  383.  *
  384.  *    "define" alone deletes all the current definitions; "ifdef" alone is
  385.  *    always false; "ifndef" alone is always true.  All defined symbols are
  386.  *    treated in a case-insensitive manner.
  387.  *
  388.  *    The file name in the "include" directive may optionally be surrounded
  389.  *    by "" or <>.
  390.  *
  391.  *    Additional notes may be propagated to an empty calendar box by the
  392.  *    inclusion of one or more lines of the form "note <month> <text>",
  393.  *    where <month> may be numeric or alphabetic.
  394.  *
  395.  */
  396.  
  397.  
  398. #include <stdio.h>
  399. #include <ctype.h>
  400. #include <time.h>
  401. #include <string.h>
  402.  
  403. #ifdef VMS        /* VMS oddities isolated here */
  404.  
  405. #include <ssdef.h>    /* required for trnlog() */
  406. #include <descrip.h>
  407.  
  408. #define HOME_DIR    "SYS$LOGIN"
  409. #define DATEFILE    "calendar.dat"
  410. #define OUTFILE        "calendar.ps"
  411. #define START_PATH    '['
  412. #define END_PATH    ']'
  413.  
  414. #define EXIT_SUCCESS 1
  415. #define EXIT_FAILURE 3
  416.  
  417. #else            /* non-VMS - assume Un*x of some sort */
  418.  
  419. #define HOME_DIR    "HOME"
  420. #define DATEFILE    ".calendar"
  421. #define ALT_DATEFILE    "calendar"    /* for backward compatibility */
  422. #define OUTFILE        ""
  423. #define START_PATH    '/'
  424. #define END_PATH    '/'
  425.  
  426. #define EXIT_SUCCESS 0
  427. #define EXIT_FAILURE 1
  428.  
  429. #endif
  430.  
  431. #define PCAL_OPTS    "PCAL_OPTS"    /* environment variables */
  432. #define PCAL_DIR    "PCAL_DIR"
  433.  
  434. #define IS_LEAP(y)    ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
  435. #define INIT_COLORS    memcpy(color, default_color, sizeof(color))
  436. #define LASTCHAR(p)    ((p) && *(p) ? (p) + strlen(p) - 1 : NULL)
  437.  
  438. #ifdef __STDC__
  439. #define TOLOWER(c)    tolower(c)
  440. #else
  441. #define TOLOWER(c)    (isupper(c) ? tolower(c) : (c))
  442. #endif
  443.  
  444. #define PRT        (void)printf
  445. #define FPR        (void)fprintf
  446.  
  447. #define FALSE    0
  448. #define TRUE    1
  449.  
  450. #define ALL_FLAGS    "bCDdefgLlMmnopRtU"    /* all command-line flags */
  451. #define DATEFILE_FLAGS    "DefU"            /* parsed before opening datefile */
  452. #define OTHER_FLAGS    "bCdgLlMmnopRt"        /* parsed inside datefile */
  453.  
  454. #define DAYFONT        "Times-Bold"        /* default font names */
  455. #define TITLEFONT    "Times-Bold"
  456. #define NOTESFONT    "Helvetica-Narrow"
  457.  
  458. #define LFOOT         ""                        /* default foot strings */
  459. #define CFOOT         ""
  460. #define RFOOT         ""
  461.  
  462. #define LANDSCAPE  90        /* degrees to rotate for landscape/portrait */
  463. #define PORTRAIT    0
  464. #define ROTATE       LANDSCAPE    /* default */
  465.  
  466. #define BLACK        0    /* colors for dates */
  467. #define GRAY        1
  468.  
  469. #define NO_DATEFILE     0    /* date file (if any) to use */
  470. #define USER_DATEFILE    1
  471. #define SYS_DATEFILE    2
  472.  
  473. /* preprocessor token codes - must be contiguous range of integers starting
  474.  * at 0 and ending with code for non-tokens (cf. pp_info[], pp_token())
  475.  */
  476. #define PP_DEFINE    0
  477. #define PP_ELSE        1
  478. #define PP_ENDIF    2
  479. #define PP_IFDEF    3
  480. #define PP_IFNDEF    4
  481. #define PP_INCLUDE    5
  482. #define PP_UNDEF    6
  483. #define PP_OTHER    7    /* not pp token */
  484.  
  485. #define MAX_NESTING    10    /* maximum nesting level for file inclusion */
  486.  
  487. #define MAX_PP_SYMS    100    /* number of definable preprocessor symbols */
  488. #define PP_SYM_UNDEF     -1    /* flag for undefined symbol */
  489.  
  490. #define MIN_YR        1900    /* significant years (calendar limits) */
  491. #define MAX_YR        9999
  492.  
  493. #define JAN         1    /* significant months */
  494. #define FEB         2
  495. #define DEC        12
  496.  
  497. #define DAY_TEXT    0    /* types of text in data structure */
  498. #define HOLIDAY_TEXT    1
  499. #define NOTE_TEXT    2
  500.  
  501. #define NOTE_DAY    32    /* dummy day for notes text */
  502.  
  503. #define DF_YEAR        0    /* returns from date_type() */
  504. #define DF_OPT        1
  505. #define DF_NOTE        2
  506. #define DF_MONTH    3
  507. #define DF_DATE        4
  508. #define DF_OTHER    5
  509.  
  510. #define PARSE_OK    0    /* returns from parse(), enter_day_info() */
  511. #define PARSE_INVDATE    1
  512. #define PARSE_INVLINE    2
  513.  
  514. #define STRSIZ    200        /* size of misc. strings */
  515.  
  516. #define MAXARGS 3        /* numeric command-line args */
  517.  
  518. #define WHITESPACE " \t"    /* token delimiters in date file */
  519.  
  520. /*
  521.  * Global typedef declarations for data structure
  522.  */
  523.  
  524. typedef struct d_i {
  525.     int is_holiday;
  526.     char *text;
  527.     struct d_i *next;
  528.     } day_info;
  529.  
  530. typedef struct m_i {
  531.     unsigned long holidays;
  532.     day_info *day[32];    /* including NOTE_DAY */
  533.     } month_info;
  534.  
  535. typedef struct y_i {
  536.     int year;
  537.     month_info *month[12];
  538.     struct y_i *next;
  539.     } year_info;
  540.  
  541.  
  542. /*
  543.  * Global variables:
  544.  */
  545.  
  546. int do_define(), do_ifdef(), do_ifndef(), do_include(), do_undef();
  547. char *trnlog(), *mk_path(), *mk_filespec();
  548.  
  549. extern char *getenv();
  550.  
  551. year_info *head = NULL;        /* head of internal data structure */
  552. int nesting_level = 0;        /* level of include file nesting */
  553. int init_month;            /* initial month, year, number of months */
  554. int init_year;
  555. int nmonths;
  556. int curr_year;            /* current default year for date file entries */
  557. char *words[100];        /* maximum number of words per date file line */
  558. char lbuf[512];            /* maximum date file line size */
  559. char *pp_sym[MAX_PP_SYMS];    /* preprocessor defined symbols */
  560. char progname[STRSIZ];        /* program name (for error messages) */
  561. char color[7];            /* colors of weekdays - cf. default_color[] */
  562.  
  563. /*
  564.  * Default values for command-line options:
  565.  */
  566.  
  567. char default_color[7] = {        /* -b, -g */
  568.     GRAY, BLACK, BLACK, BLACK, BLACK, BLACK, GRAY    /* cf. COLOR_MSG */
  569.     };
  570.  
  571. int datefile_type = SYS_DATEFILE;    /* -e, -f */
  572. char datefile[STRSIZ] = "";
  573. char default_dir[STRSIZ] = "";
  574.  
  575. int rotate = ROTATE;            /* -l, -p */
  576.  
  577. char *draw_moons = "false";        /* -m, -M */
  578.  
  579. char dayfont[STRSIZ] = DAYFONT;        /* -d, -t, -n */
  580. char titlefont[STRSIZ] = TITLEFONT;
  581. char notesfont[STRSIZ] = NOTESFONT;
  582.  
  583. char lfoot[STRSIZ] = LFOOT;             /* -L, -C, -R */
  584. char cfoot[STRSIZ] = CFOOT;
  585. char rfoot[STRSIZ] = RFOOT;
  586.  
  587. char outfile[STRSIZ] = OUTFILE;        /* -o */
  588.  
  589. /*
  590.  * Language-dependent strings (month and day names, option file keywords,
  591.  * preprocessor tokens):
  592.  */
  593.  
  594. static char *months[12] = {
  595.     "January", "February", "March", "April", "May", "June",
  596.     "July", "August", "September", "October", "November", "December"
  597.     };
  598.  
  599. static char *days[7] = {
  600.     "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
  601.     "Saturday"
  602.     };
  603.  
  604. /* preprocessor tokens - must be in same order as PP_XXXXX (cf. pp_token()) */
  605. static struct pp {
  606.     char    *token;        /* name */
  607.     int    (*pfcn)();    /* dispatch routine */
  608.     } pp_info[] = {
  609.         { "define",  do_define  },        /* PP_DEFINE    */
  610.         { "else",    NULL       },        /* PP_ELSE    */
  611.         { "endif",   NULL       },        /* PP_ENDIF    */
  612.         { "ifdef",   do_ifdef   },        /* PP_IFDEF    */
  613.         { "ifndef",  do_ifndef  },        /* PP_IFNDEF    */
  614.         { "include", do_include },        /* PP_INCLUDE    */
  615.         { "undef",   do_undef   },        /* PP_UNDEF    */
  616.         { NULL,      NULL       }        /* PP_OTHER    */
  617.     };
  618.  
  619. #define MIN_DAY_LEN   2        /* minimum size of abbreviations  */
  620. #define MIN_MONTH_LEN 3
  621. #define MIN_PPTOK_LEN 3
  622.  
  623. #define ALL    "all"        /* command-line or date file keywords */
  624. #define NOTE    "note"
  625. #define OPT    "opt"
  626. #define YEAR    "year"
  627.  
  628. #define COLOR_MSG    "Sat/Sun in gray, others in black"    /* cf. usage() */
  629.  
  630. /*
  631.  * PostScript boilerplate
  632.  */
  633.  
  634. #include "pcalinit.h"
  635.  
  636.  
  637. /*
  638.  * Main program - parse and validate command-line arguments, open files,
  639.  * generate PostScript boilerplate and code to generate calendars.
  640.  *
  641.  * Program structure:
  642.  *
  643.  * main() looks for the environment variable (global symbol on VMS) PCAL_OPTS
  644.  * and calles get_args() to parse it.  It then calls get_args() again to parse
  645.  * the command line for the date file name, -D and -U options to be in effect
  646.  * prior to reading the date file, and any numeric arguments (month, year, 
  647.  * number of months).  It then calls read_datefile() to read and parse the
  648.  * date file; any "opt" lines present will override the defaults for the
  649.  * command-line flags.  It then calls get_args() again to process the other
  650.  * command-line flags, which in turn override any specified earlier.
  651.  *
  652.  * main() then generates the common PostScript code and then calls pmonth() to
  653.  * print the calendars.
  654.  *
  655.  * read_datefile() calls getline() to read the date file, do_xxxxx() to process
  656.  * the preprocessor tokens, and parse() to parse each date line.
  657.  *
  658.  * getline() reads one or more lines from the date file, stripping comments
  659.  * (# through end-of-line) and ignoring blank lines.
  660.  *
  661.  * parse() parses a line from the date file and processes it.  If "opt", it
  662.  * calls loadwords() to split the line into tokens and get_args() to
  663.  * process them.  If the line contains "note" or a date, it calls
  664.  * enter_day_info() to enter the day and related text into the data structure.
  665.  *
  666.  * pmonth() calls find_holidays() to generate the list of holidays to be
  667.  * printed in gray; it then calls find_daytext() to generate the text to
  668.  * be printed inside the calendar boxes.
  669.  *
  670.  */
  671. main(argc, argv)
  672.     int argc;
  673.     char **argv;
  674. {
  675.     FILE *dfp = NULL;        /* date file pointer */
  676.     char *p, **ap;
  677.     int i, month, year, ngray;
  678.  
  679. #define DO_HEADER(phdr)    for (ap = phdr; *ap; ap++) PRT("%s\n", *ap)
  680.  
  681.     INIT_COLORS;        /* set up default colors */
  682.  
  683.     /* isolate root program name (for use in error messages) */
  684.  
  685.     strcpy(progname, **argv ? *argv : "pcal");
  686.  
  687.     if ((p = strrchr(progname, END_PATH)) != NULL)
  688.         strcpy(progname, ++p);
  689.     if ((p = strchr(progname, '.')) != NULL)
  690.         *p = '\0';
  691.  
  692.     /*
  693.      * Get the arguments from a) the environment variable, b) "opt" lines
  694.      * in the date file, and c) the command line, in that order
  695.      */
  696.  
  697.     /* look for environment variable for options */
  698.  
  699.     if ((p = getenv(PCAL_OPTS)) != NULL) {
  700.         strcpy(lbuf, "x ");    /* prepend a dummy token */
  701.         strcat(lbuf, p);
  702.         loadwords();
  703.         if (! get_args(words, ALL_FLAGS, FALSE)) {
  704.             usage();
  705.             exit(EXIT_FAILURE);
  706.         }
  707.     }
  708.  
  709.     /* parse command-line arguments once to find name of date file, etc. */
  710.  
  711.     if (!get_args(argv, DATEFILE_FLAGS, TRUE)) {
  712.         usage();
  713.         exit(EXIT_FAILURE);
  714.     }
  715.  
  716.     /* Attempt to open the date file as specified by the [-e | -f] flags */
  717.  
  718.     switch (datefile_type) {
  719.     case NO_DATEFILE:
  720.         dfp = NULL;
  721.         break;
  722.  
  723.     case USER_DATEFILE:    
  724.         /* Attempt to open user-specified calendar file */
  725.         if ((dfp = fopen(datefile, "r")) == NULL) {
  726.             FPR(stderr, "%s: can't open file %s\n", progname, 
  727.                 datefile);
  728.             exit(EXIT_FAILURE);
  729.         }
  730.         mk_path(default_dir, datefile);    /* extract path */
  731.         break;
  732.  
  733.     case SYS_DATEFILE:
  734.         /* Attempt to open system-specified calendar file */
  735.         if ((p = trnlog(PCAL_DIR)) || (p = trnlog(HOME_DIR)))
  736.             strcpy(default_dir, p);
  737.  
  738.         mk_filespec(datefile, default_dir, DATEFILE);
  739.         dfp = fopen(datefile, "r");    /* no error if nonexistent */
  740. #ifdef ALT_DATEFILE
  741.         if (!dfp) {        /* try again with alternate file */
  742.             mk_filespec(datefile, default_dir, ALT_DATEFILE);
  743.             dfp = fopen(datefile, "r");
  744.         }
  745. #endif
  746.         break;
  747.     }
  748.  
  749.     /* read the date file (if any) and build internal data structure */
  750.  
  751.     if (dfp) {
  752.         curr_year = init_year;
  753.         read_datefile(dfp, datefile);
  754.         fclose(dfp);
  755.     }
  756.  
  757.     /* reparse command line - flags there supersede those in date file */
  758.  
  759.     get_args(argv, OTHER_FLAGS, FALSE);
  760.  
  761.     /* done with the arguments and flags - try to open the output file */
  762.  
  763.     if (*outfile && freopen(outfile, "w", stdout) == (FILE *) NULL) {
  764.         FPR(stderr, "%s: can't open file %s\n", progname, outfile);
  765.         exit(EXIT_FAILURE);
  766.     }
  767.  
  768.     /*
  769.      * Write out PostScript prolog
  770.      */
  771.  
  772.  
  773.     /*
  774.      * Write out PostScript prolog
  775.      */
  776.  
  777.     /* font names */
  778.  
  779.      PRT("%%!\n");
  780.     PRT("/titlefont /%s def\n/dayfont /%s def\n/notesfont /%s def\n", titlefont, dayfont, notesfont);
  781.  
  782.     /* foot strings */
  783.  
  784.     def_footstring(lfoot, 'L');
  785.     def_footstring(cfoot, 'C');
  786.     def_footstring(rfoot, 'R');
  787.  
  788.     /* month names */
  789.  
  790.     PRT("/month_names [");
  791.     for (i = 0; i < 12; i++)
  792.         PRT("%s(%s) ", i % 6 == 0 ? "\n\t" : "", months[i]);
  793.     PRT("] def\n");
  794.  
  795.     /* day names */
  796.  
  797.     PRT("/day_names [");
  798.     for (i = 0; i < 7; i++)
  799.         PRT("%s(%s) ", i % 6 == 0 ? "\n\t" : "", days[i]);
  800.     PRT("] def\n");
  801.  
  802.     /* colors (black/gray) to print weekdays and holidays */
  803.  
  804.     PRT("/day_gray [");
  805.     for (ngray = i = 0; i < 7; ngray += color[i++] == GRAY)
  806.         PRT(" %s", color[i] == GRAY ? "true" : "false");
  807.     PRT(" ] def\n");
  808.     PRT("/holiday_gray %s def\n", ngray <= 3 ? "true" : "false");
  809.  
  810.     /* PostScript boilerplate (part 1 of 1) */
  811.     DO_HEADER(header);
  812.  
  813.     /* landscape or portrait mode */
  814.  
  815.     PRT("\n/landscape-p %s def\n", (rotate == LANDSCAPE) ? "true":"false");
  816.     PRT("/draw-moons %s def\n", draw_moons);
  817.  
  818.     /*
  819.      * Write out PostScript code to print calendars
  820.      */
  821.  
  822.     month = init_month;
  823.     year = init_year;
  824.  
  825.     while (nmonths--) {
  826.         pmonth(month, year);
  827.         if (++month > DEC) {
  828.             month = JAN;
  829.             year++;
  830.         }
  831.     }
  832.  
  833.     cleanup();
  834.  
  835. #ifdef VMS
  836.     FPR(stderr, "Output is in file %s\n", outfile);
  837. #endif
  838.     exit(EXIT_SUCCESS);
  839. }
  840.  
  841. /*
  842.  * get_args - walk the argument list, parsing all arguments but processing only
  843.  * those specified in "flags".  If "do_numargs" is TRUE, processes numeric
  844.  * arguments (month, year, number of months) as well.
  845.  */
  846. int get_args(argv, flags, do_numargs)
  847.     char **argv;        /* argument list */
  848.     char *flags;        /* which flags to process */
  849.     int do_numargs;        /* process numeric arguments? */
  850. {
  851.     char *p, *opt;
  852.     int i, do_flag;
  853.     long tmp;            /* for getting current month/year */
  854.     struct tm *p_tm;
  855.     int badopt = FALSE;        /* flag set if bad param   */
  856.     int nargs = 0;            /* count of non-flag args  */
  857.     int numargs[MAXARGS];        /* non-flag (numeric) args */
  858.  
  859. /* Look for the argument following flag - may be separated by spaces or
  860.  * not (bumps argv in former case).  If no non-flag argument appears, set
  861.  * "arg" to NULL (-b, -C, -d, -g, -L, -n, -o, -R, and -t without an argument
  862.  * reset the corresponding option to its default value).
  863.  */
  864. #define GETARG(arg) arg = *(*argv + 2) ? *argv + 2 : \
  865.             (*(argv+1) && **(argv+1) != '-' ? *++argv : NULL)
  866.  
  867.     /* Walk argument list, ignoring first element (program name) */
  868.  
  869.      while (*++argv) {
  870.  
  871.         /* Assume that any non-flag argument is a numeric argument */
  872.         if (**argv != '-') {
  873.                 if (do_numargs && nargs < MAXARGS)
  874.                 numargs[nargs++] = atoi(*argv);
  875.             continue;
  876.         }
  877.  
  878.         /* Is this flag among those to be processed beyond parsing? */
  879.  
  880.         do_flag = strchr(flags, *(opt = *argv + 1)) != NULL;
  881.  
  882.         switch (*opt) {
  883.  
  884.         case '\0':        /* take - or -- as dummy flags */
  885.         case '-' :
  886.             break;
  887.  
  888.         case 'b':        /* print day in black or gray */
  889.         case 'g':
  890.             GETARG(p);
  891.             if (do_flag)
  892.                 if (p)
  893.                     set_color(p, *opt == 'b' ? BLACK : GRAY);
  894.                 else
  895.                     INIT_COLORS;    /* reset to defaults */
  896.             break;
  897.  
  898.         case 'C':        /* specify alternate center foot */
  899.             GETARG(p);
  900.             if (do_flag)
  901.                 strcpy(cfoot, p ? p : CFOOT);
  902.             break;
  903.  
  904.          case 'd':        /* specify alternate day font */
  905.              GETARG(p);
  906.             if (do_flag)
  907.                 strcpy(dayfont, p ? p : DAYFONT);
  908.              break;
  909.  
  910.         case 'D':        /* define preprocessor symbol */
  911.             GETARG(p);
  912.             if (do_flag)
  913.                 do_define(p);
  914.             break;
  915.  
  916.         case 'e':        /* generate empty calendar */
  917.             if (do_flag) {
  918.                 datefile_type = NO_DATEFILE;
  919.                 datefile[0] = '\0';
  920.             }
  921.             break;
  922.  
  923.         case 'f':        /* specify alternate date file */
  924.             GETARG(p);
  925.             if (p && do_flag) {
  926.                 datefile_type = USER_DATEFILE;
  927.                 strcpy(datefile, p);
  928.             }
  929.             break;
  930.  
  931.         case 'L':        /* specify alternate left foot */
  932.             GETARG(p);
  933.             if (do_flag)
  934.                 strcpy(lfoot, p ? p : LFOOT);
  935.             break;
  936.  
  937.          case 'l':        /* generate landscape calendar */
  938.             if (do_flag)
  939.                  rotate = LANDSCAPE;
  940.              break;
  941.  
  942.         case 'n':        /* specify alternate notes font */
  943.             GETARG(p);
  944.             if (do_flag)
  945.                 strcpy(notesfont, p ? p : NOTESFONT);
  946.             break;
  947.  
  948.         case 'o':        /* specify alternate output file */
  949.             GETARG(p);
  950.             if (do_flag)
  951.                 strcpy(outfile, p ? p : OUTFILE);
  952.             break;
  953.  
  954.          case 'p':        /* generate portrait calendar */
  955.             if (do_flag)
  956.                  rotate = PORTRAIT;
  957.              break;
  958.  
  959.          case 'R':        /* specify alternate right foot */
  960.             GETARG(p);
  961.             if (do_flag)
  962.                 strcpy(rfoot, p ? p : RFOOT);
  963.             break;
  964.  
  965.  
  966.          case 't':        /* specify alternate title font */
  967.              GETARG(p);
  968.             if (do_flag)
  969.                 strcpy(titlefont, p ? p : TITLEFONT);
  970.              break;
  971.  
  972.         case 'U':        /* undef preprocessor symbol */
  973.             GETARG(p);
  974.             if (do_flag)
  975.                 do_undef(p);
  976.             break;
  977.  
  978.         case 'm':        /* draw four moons */
  979.             if (do_flag)
  980.                 draw_moons = "4";
  981.             break;
  982.  
  983.         case 'M':        /* draw a moon for each day */
  984.             if (do_flag)
  985.                 draw_moons = "true";
  986.             break;
  987.  
  988.         default:        /* unrecognized flag */
  989.             FPR(stderr, "%s: illegal option -%s\n", progname, opt);
  990.             badopt = TRUE;
  991.             break;
  992.         }
  993.         }
  994.  
  995.     if (!do_numargs)
  996.         return !badopt;        /* return TRUE if OK, FALSE if error */
  997.  
  998.     /* Validate non-flag (numeric) parameters */
  999.  
  1000.     switch (nargs) {
  1001.     case 0:        /* no arguments - print current month/year */
  1002.         time(&tmp);
  1003.         p_tm = localtime(&tmp);
  1004.         init_month = p_tm->tm_mon + 1;
  1005.         init_year = p_tm->tm_year;
  1006.         nmonths = 1;
  1007.         break;            
  1008.     case 1:        /* one argument - print entire year */
  1009.         init_month = JAN;
  1010.         init_year = numargs[0];
  1011.         nmonths = 12;
  1012.         break;
  1013.     default:    /* two or three arguments - print one or more months */
  1014.         init_month = numargs[0];
  1015.         init_year = numargs[1];
  1016.         nmonths = nargs > 2 ? numargs[2] : 1;
  1017.         break;
  1018.     }
  1019.  
  1020.     if (nmonths < 1)        /* ensure at least one month */
  1021.         nmonths = 1;
  1022.  
  1023.     /* check range of month and year */
  1024.  
  1025.     if (init_month < JAN || init_month > DEC) {
  1026.         FPR(stderr, "%s: month %d not in range 1 .. 12\n", progname,
  1027.             init_month);
  1028.         badopt = TRUE;
  1029.     }
  1030.     
  1031.     if (init_year > 0 && init_year < 100)    /* treat nn as 19nn */
  1032.         init_year += 1900;
  1033.     
  1034.     if (init_year < MIN_YR || init_year > MAX_YR) {
  1035.         FPR(stderr, "%s year %d not in range %d .. %d\n", progname,
  1036.             init_year, MIN_YR, MAX_YR);
  1037.         badopt = TRUE;
  1038.     }
  1039.  
  1040.     return !badopt;        /* return TRUE if OK, FALSE if error */
  1041. }
  1042.  
  1043.  
  1044.  
  1045. /*
  1046.  *    usage - print message explaining correct usage of the command-line
  1047.  *    arguments and flags
  1048.  */
  1049. usage()
  1050. {
  1051.     FPR(stderr, "\nUsage:\t%s [-b|-g DAY]* [-d|-n|-t FONT] [-e | -f FILE] [-o FILE] [-l | -p]\n", progname);
  1052.     FPR(stderr, "\t[-m|-M] [-D|-U SYM] [-L|-C|-R STRING] [ [ [mm] yy ] | [mm yy n] ]\n");
  1053.     FPR(stderr, "\n");
  1054.     FPR(stderr, "\t-b DAY\t\tprint weekday DAY in black\n");
  1055.     FPR(stderr, "\t-g DAY\t\tprint weekday DAY in gray\n");
  1056.     FPR(stderr, "\t\t\t(default: %s)\n", COLOR_MSG);
  1057.     FPR(stderr, "\n");
  1058.     FPR(stderr, "\t-d FONT\t\tspecify alternate day name font (default: %s)\n",
  1059.         DAYFONT);
  1060.     FPR(stderr, "\t-n FONT\t\tspecify alternate notes font (default: %s)\n",
  1061.         NOTESFONT);
  1062.     FPR(stderr, "\t-t FONT\t\tspecify alternate title font (default: %s)\n",
  1063.         TITLEFONT);
  1064.     FPR(stderr, "\n");
  1065.     FPR(stderr, "\t-e\t\tgenerate empty calendar (ignore date file)\n");
  1066.     FPR(stderr, "\t-f FILE\t\tspecify alternate date file (default: %s)\n",
  1067.         DATEFILE);
  1068.     FPR(stderr, "\t-o FILE\t\tspecify alternate output file (default: %s)\n",
  1069.         OUTFILE[0] ? OUTFILE : "stdout");
  1070.     FPR(stderr, "\n");
  1071.     FPR(stderr, "\t-l\t\tgenerate landscape-style calendars");
  1072. #if (ROTATE == LANDSCAPE)
  1073.     FPR(stderr, " (default)");
  1074. #endif
  1075.     FPR(stderr, "\n\t-p\t\tgenerate portrait-style calendars");
  1076. #if (ROTATE == PORTRAIT)
  1077.     FPR(stderr, " (default)");
  1078. #endif
  1079.     FPR(stderr, "\n\n");
  1080.     FPR(stderr, "\t-m\t\t\draw a \"moon\" icon on days of full, new, and half moons\n");
  1081. #ifdef VMS
  1082.     FPR(stderr, "\t-\"M\"\t\t\draw a \"moon\" icon every day (default: no moons)\n");
  1083.     FPR(stderr, "\n");
  1084.     FPR(stderr, "\t-\"D\" SYM\tdefine preprocessor symbol\n");
  1085.     FPR(stderr, "\t-\"U\" SYM\tundefine preprocessor symbol\n");
  1086.     FPR(stderr, "\n");
  1087.     FPR(stderr, "\t-\"L\" STRING\tspecify left foot string (default: \"%s\")\n",
  1088.         LFOOT);
  1089.     FPR(stderr, "\t-\"C\" STRING\tspecify center foot string (default: \"%s\")\n",
  1090.         CFOOT);
  1091.     FPR(stderr, "\t-\"R\" STRING\tspecify right foot string (default: \"%s\")\n",
  1092.         RFOOT);
  1093. #else
  1094.     FPR(stderr, "\t-M\t\t\draw a \"moon\" icon every day (default: no moons)\n");
  1095.     FPR(stderr, "\n");
  1096.     FPR(stderr, "\t-D SYM\t\tdefine preprocessor symbol\n");
  1097.     FPR(stderr, "\t-U SYM\t\tundefine preprocessor symbol\n");
  1098.     FPR(stderr, "\n");
  1099.     FPR(stderr, "\t-L STRING\tspecify left foot string (default: \"%s\")\n",
  1100.         LFOOT);
  1101.     FPR(stderr, "\t-C STRING\tspecify center foot string (default: \"%s\")\n",
  1102.         CFOOT);
  1103.     FPR(stderr, "\t-R STRING\tspecify right foot string (default: \"%s\")\n",
  1104.         RFOOT);
  1105. #endif
  1106.     FPR(stderr, "\n");
  1107.     FPR(stderr, "\tyy\t\tgenerate calendar for year yy (19yy if yy < 100)\n");
  1108.     FPR(stderr, "\tmm yy\t\tgenerate calendar for month mm (Jan = 1), year yy\n");
  1109.     FPR(stderr, "\tmm yy n\t\tgenerate calendars for n months, starting at mm/yy\n");
  1110.     FPR(stderr, "\t(default)\tgenerate calendar for current month/year\n");
  1111. }
  1112.  
  1113.  
  1114. /*
  1115.  * General-purpose utility routines
  1116.  */
  1117.  
  1118.  
  1119. /*
  1120.  * alloc - interface to calloc(); terminates if unsuccessful
  1121.  */
  1122. char *alloc(size)
  1123.     int size;
  1124. {
  1125.     char *p;
  1126.     extern char *calloc();
  1127.  
  1128.     if (size == 0)        /* not all calloc()s like null requests */
  1129.         size = 1;
  1130.  
  1131.     if ((p = calloc(1, size)) == NULL) {
  1132.         FPR(stderr, "%s: out of memory\n", progname);
  1133.         exit(EXIT_FAILURE);
  1134.     }
  1135.  
  1136.     return p;
  1137. }
  1138.  
  1139.  
  1140. /*
  1141.  * ci_str{n}cmp - case-insensitive flavors of strcmp(), strncmp()
  1142.  */
  1143. int ci_strcmp(s1, s2)
  1144. register char *s1, *s2;
  1145. {
  1146.     register char c1, c2;
  1147.  
  1148.     for ( ; (c1 = TOLOWER(*s1)) == (c2 = TOLOWER(*s2)); s1++, s2++)
  1149.         if (c1 == '\0')
  1150.             return 0;
  1151.  
  1152.     return c1 - c2;
  1153. }
  1154.  
  1155.  
  1156. int ci_strncmp(s1, s2, n)
  1157. register char *s1, *s2;
  1158. int n;
  1159. {
  1160.     register char c1, c2;
  1161.  
  1162.     for ( ; --n >= 0 && (c1 = TOLOWER(*s1)) == (c2 = TOLOWER(*s2)); s1++, s2++)
  1163.         if (c1 == '\0')
  1164.             return 0;
  1165.  
  1166.     return n < 0 ? 0 : c1 - c2;
  1167. }
  1168.  
  1169.  
  1170. /*
  1171.  * Preprocessor token and symbol table routines
  1172.  */
  1173.  
  1174.  
  1175. /*
  1176.  * find_sym - look up symbol; return symbol table index if found, PP_SYM_UNDEF
  1177.  * if not found
  1178.  */
  1179. int find_sym(sym)
  1180.     char *sym;
  1181. {
  1182.     int i;
  1183.  
  1184.     if (!sym)
  1185.         return PP_SYM_UNDEF;
  1186.  
  1187.     for (i = 0; i < MAX_PP_SYMS; i++)
  1188.         if (pp_sym[i] && ci_strcmp(pp_sym[i], sym) == 0)
  1189.             return i;
  1190.  
  1191.     return PP_SYM_UNDEF;
  1192. }
  1193.  
  1194.  
  1195. /*
  1196.  * do_ifdef - return TRUE if 'sym' is currently defined; FALSE if not
  1197.  */
  1198. int do_ifdef(sym)
  1199.     char *sym;
  1200. {
  1201.     return find_sym(sym) != PP_SYM_UNDEF;
  1202. }
  1203.  
  1204.  
  1205. /*
  1206.  * do_ifndef - return FALSE if 'sym' is currently defined; TRUE if not
  1207.  */
  1208. int do_ifndef(sym)
  1209.     char *sym;
  1210. {
  1211.     return find_sym(sym) == PP_SYM_UNDEF;
  1212. }
  1213.  
  1214.  
  1215. /*
  1216.  * do_define - enter 'sym' into symbol table; if 'sym' NULL, clear symbol table
  1217.  */
  1218. do_define(sym)
  1219.     char *sym;
  1220. {
  1221.     int i;
  1222.  
  1223.     if (! sym) {        /* null argument - clear all definitions */
  1224.         clear_syms();
  1225.         return;
  1226.     }
  1227.  
  1228.     if (do_ifdef(sym))    /* already defined? */
  1229.         return;
  1230.  
  1231.     for (i = 0; i < MAX_PP_SYMS; i++)    /* find room for it */
  1232.         if (! pp_sym[i]) {
  1233.             strcpy(pp_sym[i] = alloc(strlen(sym)+1), sym);
  1234.             return;
  1235.         }
  1236.  
  1237.     FPR(stderr, "%s: no room to define %s\n", progname, sym);
  1238. }
  1239.  
  1240.  
  1241. /*
  1242.  * do_undef - undefine 'sym' and free its space; no error if not defined
  1243.  */
  1244. do_undef(sym)
  1245.     char *sym;
  1246. {
  1247.     int i;
  1248.  
  1249.     if (! sym) 
  1250.         return;
  1251.  
  1252.     if ((i = find_sym(sym)) != PP_SYM_UNDEF) {
  1253.         free(pp_sym[i]);
  1254.         pp_sym[i] = NULL;
  1255.     }
  1256. }
  1257.  
  1258.  
  1259. /*
  1260.  * do_include - include specified file (optionally in "" or <>)
  1261.  */
  1262. do_include(path, name)
  1263.     char *path;        /* path to file */
  1264.     char *name;        /* file name */
  1265. {
  1266.     FILE *fp;
  1267.     char *p, incfile[STRSIZ], tmpnam[STRSIZ];
  1268.  
  1269.     if (! name)        /* whoops, no date file */
  1270.         return;
  1271.  
  1272.     /* copy name, stripping "" or <> */
  1273.     strcpy(tmpnam, name + (*name == '"' || *name == '<'));
  1274.     if ((p = LASTCHAR(tmpnam)) && *p == '"' || *p == '>')
  1275.         *p = '\0';
  1276.  
  1277.     if ((fp = fopen(mk_filespec(incfile, path, tmpnam), "r")) == NULL) {
  1278.         FPR(stderr, "%s: can't open file %s\n",    progname, incfile);
  1279.         exit(EXIT_FAILURE);
  1280.     }
  1281.  
  1282.     read_datefile(fp, incfile);
  1283.     fclose(fp);
  1284. }
  1285.  
  1286.  
  1287. /*
  1288.  * pp_token - look up 'token' in list of preprocessor tokens; return its
  1289.  * index if found, PP_OTHER if not (N.B.: this relies on the ordering of
  1290.  * PP_XXXXX and pp_info[]; see comments at their definitions).
  1291.  */
  1292. int pp_token(token)
  1293.     char *token;
  1294. {
  1295.     struct pp *p;
  1296.  
  1297.     for (p = pp_info;
  1298.              p->token && ci_strncmp(token, p->token, MIN_PPTOK_LEN);
  1299.          p++)
  1300.         ;
  1301.  
  1302.     return p - pp_info;
  1303. }
  1304.  
  1305. /*
  1306.  * Internal data structure routines
  1307.  */
  1308.  
  1309.  
  1310. /*
  1311.  * find_year - find record in year list; optionally create if not present 
  1312.  */
  1313. year_info *find_year(year, insert)    /* find record in year list */
  1314.     int year;
  1315.     int insert;            /* insert if missing */
  1316. {
  1317.     year_info *pyear, *plast, *p;
  1318.  
  1319.     for (plast = NULL, pyear = head;        /* search linked list */
  1320.          pyear && pyear->year < year;
  1321.          plast = pyear, pyear = pyear->next)
  1322.         ;
  1323.  
  1324.     if (pyear && pyear->year == year)        /* found - return it */
  1325.         return pyear;
  1326.  
  1327.     if (insert) {        /* not found - insert it if requested */
  1328.         p = (year_info *) alloc(sizeof(year_info));    /* create new record */
  1329.         p->year = year;
  1330.  
  1331.         p->next = pyear;                /* link it in */
  1332.         return *(plast ? &plast->next : &head) = p;
  1333.     }
  1334.     else
  1335.         return NULL;
  1336. }
  1337.  
  1338.  
  1339. /*
  1340.  * enter_day_info - enter text for specified day; avoid entering duplicates.
  1341.  * returns PARSE_INVDATE if date invalid, PARSE_OK if OK
  1342.  */
  1343. int enter_day_info(m, d, y, text_type, pword)    /* fill in information for given day */
  1344.     int m, d, y;
  1345.     int text_type;
  1346.     char **pword;
  1347. {
  1348.     static year_info *pyear;
  1349.     static int prev_year = 0;
  1350.     month_info *pmonth;
  1351.     day_info *pday, *plast;
  1352.     int is_holiday = text_type == HOLIDAY_TEXT;
  1353.  
  1354.     if (! is_valid(m, d == NOTE_DAY && text_type == NOTE_TEXT ? 1 : d, y))
  1355.         return PARSE_INVDATE;
  1356.  
  1357.     if (y != prev_year)        /* avoid unnecessary year lookup */
  1358.         pyear = find_year(y, 1);
  1359.  
  1360.     --m, --d;            /* adjust for use as subscripts */
  1361.  
  1362.     if ((pmonth = pyear->month[m]) == NULL)    /* find/create month record */
  1363.         pyear->month[m] = pmonth = (month_info *) alloc(sizeof(month_info));
  1364.  
  1365.     if (is_holiday)
  1366.         pmonth->holidays |= (1 << d);
  1367.  
  1368.     /* insert text for day at end of list (preserving the order of entry
  1369.      * for multiple lines on same day); eliminate those differing only
  1370.      * in spacing and capitalization from existing entries
  1371.          */
  1372.  
  1373.     get_text(pword);    /* consolidate text into lbuf */
  1374.  
  1375.     if (*lbuf) {
  1376.         for (plast = NULL, pday = pmonth->day[d];
  1377.              pday;
  1378.              plast = pday, pday = pday->next)
  1379.             if (ci_strcmp(pday->text, lbuf) == 0) {
  1380.                 pday->is_holiday |= is_holiday;
  1381.                 return PARSE_OK;
  1382.             }
  1383.  
  1384.         /* unique - add to end of list */
  1385.  
  1386.         pday = (day_info *) alloc(sizeof(day_info));
  1387.         pday->is_holiday = is_holiday;
  1388.         strcpy(pday->text = (char *) alloc(strlen(lbuf)+1), lbuf);
  1389.         pday->next = NULL;
  1390.         *(plast ? &plast->next : &pmonth->day[d]) = pday;
  1391.     }
  1392.  
  1393.     return PARSE_OK;
  1394. }
  1395.  
  1396.  
  1397. /*
  1398.  * Housekeeping routines to free allocated data
  1399.  */
  1400.  
  1401.  
  1402. /*
  1403.  * clear_syms - clear and deallocate the symbol table
  1404.  */
  1405. clear_syms()
  1406. {
  1407.     int i;
  1408.  
  1409.     for (i = 0; i < MAX_PP_SYMS; i++)
  1410.         if (pp_sym[i]) {
  1411.             free(pp_sym[i]);
  1412.             pp_sym[i] = NULL;
  1413.         }
  1414. }
  1415.  
  1416.  
  1417. /*
  1418.  * cleanup - free all allocated data
  1419.  */
  1420. cleanup()
  1421. {
  1422.     int i, j;
  1423.     year_info *py, *pny;
  1424.     month_info *pm;
  1425.     day_info *pd, *pnd;
  1426.  
  1427.     for (py = head; py; py = pny) {        /* main data structure */
  1428.         pny = py->next;
  1429.         for (i = 0; i < 12; i++) {
  1430.             if ((pm = py->month[i]) == NULL)
  1431.                 continue;
  1432.             for (j = 0; j < NOTE_DAY; j++)
  1433.                 for (pd = pm->day[j]; pd; pd = pnd) {
  1434.                     pnd = pd->next;
  1435.                     free(pd->text);
  1436.                     free(pd);
  1437.                 }
  1438.             free(pm);
  1439.         }
  1440.     free(py);
  1441.     }
  1442.  
  1443.     clear_syms();                /* symbol table */
  1444.  
  1445. }
  1446.  
  1447.  
  1448. /*
  1449.  * Utility routines for date and text parsing and entry
  1450.  */
  1451.  
  1452. /*
  1453.  * get_month - convert numeric or alpha string to month; return 1-12 if valid,
  1454.  * 0 if not valid
  1455.  */
  1456. int get_month(cp)
  1457.     char *cp;
  1458. {
  1459.     int mm;
  1460.  
  1461.     if (! cp)
  1462.         return 0;
  1463.  
  1464.     if (isdigit(*cp))
  1465.         mm = atoi(cp);
  1466.     else
  1467.         for (mm = JAN;
  1468.              mm <= DEC && ci_strncmp(cp, months[mm-1], MIN_MONTH_LEN);
  1469.              mm++)
  1470.             ;
  1471.  
  1472.     return mm >= JAN && mm <= DEC ? mm : 0;
  1473. }
  1474.  
  1475.  
  1476. /*
  1477.  * date_type - examine token and return date type code; if DF_MONTH, fill
  1478.  * in number of month
  1479.  */
  1480. int date_type(cp, pm)
  1481.     char *cp;    /* pointer to start of token */
  1482.     int *pm;    /* month value (returned)    */
  1483. {
  1484.     int mm;
  1485.  
  1486.     if (isdigit(*cp))
  1487.         return DF_DATE;
  1488.  
  1489.     if (ci_strncmp(cp, YEAR, strlen(YEAR)) == 0)
  1490.         return DF_YEAR;
  1491.  
  1492.     if (ci_strncmp(cp, OPT, strlen(OPT)) == 0)
  1493.         return DF_OPT;
  1494.  
  1495.     if (ci_strncmp(cp, NOTE, strlen(NOTE)) == 0)
  1496.         return DF_NOTE;
  1497.  
  1498.     return (mm = get_month(cp)) != 0 ? (*pm = mm, DF_MONTH) : DF_OTHER;
  1499. }
  1500.  
  1501.  
  1502. /*
  1503.  * is_valid - return TRUE if m/d/y is a valid date
  1504.  */
  1505. int is_valid(m, d, y)
  1506.     register int m, d, y;
  1507. {
  1508.     static char len[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  1509.  
  1510.     return m >= JAN && m <= DEC && 
  1511.         d >= 1 && d <= (len[m] + (m == FEB && IS_LEAP(y)));
  1512. }
  1513.  
  1514.  
  1515. /*
  1516.  * loadwords - tokenize line buffer into word array, return word count.
  1517.  * differs from old loadwords() in that it handles quoted (" or ') strings
  1518.  */
  1519.  
  1520. int loadwords()
  1521. {
  1522.     register char *pstr, *ptok;
  1523.     char *delim, **ap;
  1524.     register int i;
  1525.  
  1526.     pstr = lbuf;
  1527.  
  1528.     for (i = 0, ap = words; TRUE; i++, ap++) {
  1529.         delim =    *pstr == '"'  ? "\"" :
  1530.             *pstr == '\'' ? "'"  :
  1531.             WHITESPACE;
  1532.  
  1533.         ptok = pstr += strspn(pstr, delim); /* look for next token */
  1534.  
  1535.         if (! *pstr) {        /* end of lbuf? */
  1536.             *ap = NULL;    /* add null ptr at end */
  1537.             return i;    /* return count of non-null ptrs */
  1538.             }
  1539.  
  1540.         if (*ptok == '"' || *ptok == '\'')    /* bump past quote */
  1541.             ptok++;
  1542.  
  1543.         *ap = ptok;                /* save token ptr */
  1544.  
  1545.         pstr += strcspn(pstr, delim);         /* skip past token */
  1546.  
  1547.         if (*pstr)                /* terminate token */
  1548.             *pstr++ = '\0';
  1549.         }
  1550.  
  1551. }
  1552.  
  1553.  
  1554. /*
  1555.  * get_text - retrieve remaining text in lbuf and transform in place, removing
  1556.  * leading/trailing whitespace and condensing runs of whitespace to one blank
  1557.  */
  1558. get_text(pword)
  1559.     char **pword;        /* pointer to first desired word in "words" */
  1560. {
  1561. char *pbuf, *p;
  1562.  
  1563. /* copy words back to lbuf in place, separating by one blank */
  1564.  
  1565. for (pbuf = lbuf; p = *pword; *pbuf++ = *++pword ? ' ' : '\0')
  1566.     while (*p)
  1567.         *pbuf++ = *p++;
  1568.  
  1569. if (pbuf == lbuf)
  1570.     *lbuf = '\0';
  1571. }
  1572.  
  1573.  
  1574. /*
  1575.  * print_text - print tokens in text (assumed separated by single blank)
  1576.  * in PostScript format; convert '(' or ')' to octal escape
  1577.  */
  1578. print_text(p)
  1579.     char *p;
  1580. {
  1581.     char c;
  1582.  
  1583.     PRT("(");
  1584.     for ( ; c = *p ; p++) {
  1585.         if (c == ' ')
  1586.             PRT(")\n(");
  1587.         else
  1588.             PRT(c == '(' || c == ')' ? "\\%03o" : "%c", c);
  1589.     }
  1590.     PRT(")\n");
  1591. }
  1592.  
  1593.  
  1594. /*
  1595.  * def_footstring - print definition for foot string, again converting '('
  1596.  * or ')' to octal escape
  1597.  */
  1598. def_footstring(p, c)
  1599.     char *p;            /* definition */
  1600.     char c;                /* L, C, or R */
  1601. {
  1602.  
  1603.     PRT("/%cfootstring (", c);
  1604.  
  1605.     while (c = *p++)
  1606.         PRT(c == '(' || c == ')' ? "\\%03o" : "%c", c);
  1607.  
  1608.     PRT(") def\n");
  1609. }
  1610.  
  1611.  
  1612. /*
  1613.  * set_color - set one or all weekdays to print in black or gray
  1614.  */
  1615. set_color(day, col)
  1616.     char *day;        /* weekday name (or "all") */
  1617.     int  col;        /* select black or gray */
  1618. {
  1619.     int i, do_all;
  1620.  
  1621.     do_all = ci_strncmp(day, ALL, strlen(ALL)) == 0;    /* set all days? */
  1622.  
  1623.     for (i = 0; i < 7; i++)
  1624.         if (do_all || ci_strncmp(day, days[i], MIN_DAY_LEN) == 0)
  1625.             color[i] = col;
  1626. }
  1627.  
  1628. /*
  1629.  * parse - enter date file data (in lbuf[]) into data structure
  1630.  *
  1631.  * Looks for an entry of one of the following forms:
  1632.  *
  1633.  *    year <year>
  1634.  *    <month_name> <day>{*} {<text>}
  1635.  *    <month><sep><day>{<sep><year>}{*} {<text>}
  1636.  *    opt <options>
  1637.  *    note <month_name> <text>
  1638.  *    note <month> <text>
  1639.  *
  1640.  * where
  1641.  *    <month_name> := first 3+ characters of name of month (in English)
  1642.  *    <sep> := one or more non-numeric, non-space, non-'*' characters
  1643.  *    <options> := any command-line option except -e, -f, -D, -U
  1644.  *
  1645.  * Enters information for year, month, and day into data structure for
  1646.  * subsequent retrieval.  Lines of form "opt <options>" override any
  1647.  * defaults.
  1648.  *
  1649.  * N.B.: "inc" and other cpp-like lines are handled in read_datefile().
  1650.  *
  1651.  */
  1652. int parse()
  1653. {
  1654.     register char *cp;
  1655.     char **pword, *p;
  1656.     int mm, dd, yy;
  1657.     int text_type;
  1658.  
  1659. /* macro to skip numeric field */
  1660.  
  1661. #define SKIP_FIELD(p) \
  1662.     if (1) {while (isdigit(*p)) p++; while (*p && !isdigit(*p)) p++;} else
  1663.  
  1664.     /*
  1665.      * Get first field - can be either "year", "opt", "note", a month
  1666.      * name, or a (complete) numeric date spec
  1667.          */
  1668.  
  1669.     cp = *(pword = words);
  1670.  
  1671.     switch (date_type(cp, &mm)) {
  1672.  
  1673.     case DF_YEAR:
  1674.         if ((cp = *++pword) != NULL && (yy = atoi(cp)) > 0) {
  1675.             if (yy < 100)
  1676.                 yy += 1900;
  1677.             curr_year = yy;
  1678.             return PARSE_OK;
  1679.         }
  1680.         return PARSE_INVDATE;    /* year missing or invalid */
  1681.         break;
  1682.  
  1683.     case DF_OPT:
  1684.          if (!get_args(words, OTHER_FLAGS, FALSE)) {
  1685.             usage();
  1686.             exit(EXIT_FAILURE);
  1687.         }
  1688.         return PARSE_OK;
  1689.         break;
  1690.  
  1691.     case DF_NOTE:
  1692.         mm = get_month(*++pword);
  1693.         return enter_day_info(mm, NOTE_DAY, curr_year, NOTE_TEXT,
  1694.             ++pword);
  1695.         break;
  1696.  
  1697.     case DF_MONTH:
  1698.         if ((cp = *++pword) == NULL || (dd = atoi(cp)) == 0)
  1699.             return PARSE_INVDATE;
  1700.         text_type = cp[strlen(cp) - 1] == '*' ? HOLIDAY_TEXT : DAY_TEXT;
  1701.  
  1702.         return enter_day_info(mm, dd, curr_year, text_type, ++pword);
  1703.         break;
  1704.  
  1705.     case DF_DATE:
  1706.         text_type = cp[strlen(cp) - 1] == '*' ? HOLIDAY_TEXT : DAY_TEXT;
  1707.  
  1708.         /* extract month and day fields */
  1709.  
  1710.         mm = atoi(cp);
  1711.         SKIP_FIELD(cp);
  1712.  
  1713.         dd = atoi(cp);
  1714.         SKIP_FIELD(cp);
  1715.  
  1716.         /* Numeric dates may (or may not) have a year */
  1717.  
  1718.         if ((yy = atoi(cp)) > 0) {
  1719.             if (yy < 100)
  1720.                 yy += 1900;
  1721.             curr_year = yy; /* if present, reset current year */
  1722.         }
  1723.  
  1724.         return enter_day_info(mm, dd, curr_year, text_type, ++pword);
  1725.         break;
  1726.  
  1727.     case DF_OTHER:
  1728.         return PARSE_INVLINE;        /* line not recognized */
  1729.         break;
  1730.     }
  1731. }
  1732.  
  1733.  
  1734. /*
  1735.  * getline - read next non-null line of input file into lbuf; return 0 on EOF
  1736.  */
  1737. int getline(dfp, pline)
  1738.     FILE *dfp;
  1739.     int *pline;
  1740. {
  1741.     register char *cp;
  1742.     register int c;
  1743.     int in_comment;        /* comments: from '#' to end-of-line */
  1744.  
  1745.     cp = lbuf;
  1746.     do {
  1747.         in_comment = FALSE;
  1748.         while ((c = getc(dfp)) != '\n' && c != EOF) {
  1749.             if (c == '#')
  1750.                 in_comment = TRUE;
  1751.  
  1752.             /* ignore comments and leading white space */
  1753.             if (in_comment ||
  1754.                 (cp == lbuf && (c == ' ' || c == '\t')))
  1755.                 continue;
  1756.             *cp++ = c;
  1757.         }
  1758.         if (c == EOF)
  1759.             return FALSE;
  1760.  
  1761.         (*pline)++;    /* bump line number */
  1762.  
  1763.     } while (cp == lbuf);    /* ignore empty lines */
  1764.  
  1765.     *cp = '\0';
  1766.     return TRUE;
  1767. }
  1768.  
  1769.  
  1770. /*
  1771.  * read_datefile - read and parse date file, handling preprocessor lines
  1772.  */
  1773. read_datefile(fp, filename)
  1774.     FILE *fp;        /* file pointer (assumed open) */
  1775.     char *filename;        /* file name (for error messages) */
  1776. {
  1777.     static int file_level = 0;
  1778.     int if_level = 0;
  1779.     int restart_level = 0;
  1780.     int line = 0;
  1781.     int processing = TRUE;
  1782.  
  1783.     int pptype, extra, ntokens, save_year;
  1784.     int (*pfcn)();
  1785.     char *ptok;
  1786.     char **pword;
  1787.     char msg[STRSIZ], incpath[STRSIZ];
  1788.  
  1789. #define ERR(errmsg)    FPR(stderr, "%s: %s in file %s, line %d\n", \
  1790.             progname, errmsg, filename, line);
  1791.  
  1792.     if (fp == NULL)        /* whoops, no date file */
  1793.         return;
  1794.  
  1795.     if (++file_level > MAX_NESTING) {
  1796.         ERR("maximum file nesting level exceeded");
  1797.         exit(EXIT_FAILURE);
  1798.     }
  1799.  
  1800.     save_year = curr_year;        /* save default year */
  1801.  
  1802.     /* read lines until EOF */
  1803.  
  1804.     while (getline(fp, &line)) {
  1805.  
  1806.         ntokens = loadwords();        /* split line into tokens */
  1807.         pword = words;            /* point to the first */
  1808.  
  1809.         /* get token type and pointers to function and name */
  1810.  
  1811.         pptype = pp_token(*pword++);
  1812.         pfcn = pp_info[pptype].pfcn;
  1813.         ptok = pp_info[pptype].token;
  1814.  
  1815.         switch (pptype) {
  1816.  
  1817.         case PP_DEFINE:
  1818.         case PP_UNDEF:
  1819.             if (processing)
  1820.                 (*pfcn)(*pword);
  1821.             extra = ntokens > 2;
  1822.             break;
  1823.  
  1824.         case PP_ELSE:
  1825.             if (if_level < 1) {
  1826.                 ERR("unmatched \"else\"");
  1827.                 break;
  1828.             }
  1829.  
  1830.             if (processing) {
  1831.                 processing = FALSE;    /* disable processing */
  1832.                 restart_level = if_level;
  1833.             } else if (if_level == restart_level) {
  1834.                 processing = TRUE;    /* re-enable processing */
  1835.                 restart_level = 0;
  1836.             }
  1837.             extra = ntokens > 1;
  1838.             break;
  1839.  
  1840.         case PP_ENDIF:
  1841.             if (if_level < 1) {
  1842.                 ERR("unmatched \"end\"");
  1843.                 break;
  1844.             }
  1845.  
  1846.             if (! processing && if_level == restart_level) {
  1847.                 processing = TRUE;    /* re-enable processing */
  1848.                 restart_level = 0;
  1849.             }
  1850.             if_level--;
  1851.             extra = ntokens > 1;
  1852.             break;
  1853.  
  1854.         case PP_IFDEF:
  1855.         case PP_IFNDEF:
  1856.             if_level++;
  1857.             if (processing) {
  1858.                 if (! (*pfcn)(*pword)) {
  1859.                     processing = FALSE;
  1860.                     restart_level = if_level;
  1861.                 }
  1862.             }
  1863.             extra = ntokens > 2;
  1864.             break;
  1865.  
  1866.         case PP_INCLUDE:
  1867.             if (processing)
  1868.                 do_include(mk_path(incpath, filename), *pword);
  1869.             extra = ntokens > 2;
  1870.             break;
  1871.  
  1872.         case PP_OTHER:    /* none of the above - parse as date */
  1873.             if (processing) {
  1874.                 switch (parse()) {
  1875.  
  1876.                 case PARSE_INVDATE:
  1877.                     ERR("invalid date");
  1878.                     break;
  1879.  
  1880.                 case PARSE_INVLINE:
  1881.                     ERR("unrecognized line");
  1882.                     break;
  1883.  
  1884.                 }
  1885.             }
  1886.             extra = FALSE;
  1887.             break;
  1888.  
  1889.         } /* end switch */
  1890.  
  1891.         if (extra) {        /* extraneous data? */
  1892.             sprintf(msg, "extraneous data on \"%s\" line", ptok);
  1893.             ERR(msg);
  1894.         }
  1895.  
  1896.     } /* end while */
  1897.  
  1898.     if (if_level > 0)
  1899.         FPR(stderr, "%s: unterminated if{n}def..{else..}endif in %s\n",
  1900.             progname, filename);
  1901.  
  1902.     file_level--;
  1903.     curr_year = save_year;        /* restore default year */
  1904. }
  1905.  
  1906. /*
  1907.  * Routines to extract and print data
  1908.  */
  1909.  
  1910.  
  1911. /*
  1912.  * Browse through the data structure looking for day, holiday, or notes text
  1913.  * in the specified month/year
  1914.  */
  1915. find_daytext(month, year, is_holiday)
  1916.     int month, year;
  1917.     int is_holiday;
  1918. {
  1919.     register int day;
  1920.     year_info *py;
  1921.     month_info *pm;
  1922.     register day_info *pd;
  1923.     int first;
  1924.     char *fcn = is_holiday ? "holidaytext" : "daytext";
  1925.  
  1926.     /* if no text for this year and month, return */
  1927.  
  1928.     if ((py = find_year(year, FALSE)) == NULL ||
  1929.         (pm = py->month[month-1]) == NULL)
  1930.         return;
  1931.  
  1932.     /* walk array of day text pointers and linked lists of text */
  1933.  
  1934.     for (day = 1; day <= NOTE_DAY; day++) {
  1935.         for (pd = pm->day[day-1], first = TRUE;
  1936.              pd;
  1937.              pd = pd->next) {
  1938.             if (pd->is_holiday != is_holiday)
  1939.                 continue;
  1940.             if (first) {
  1941.                 if (day != NOTE_DAY)    /* set up call */
  1942.                     PRT("%d ", day);
  1943.                 printf("[ \n");
  1944.             } else
  1945.                 PRT("(.p)\n");        /* separate text */
  1946.             print_text(pd->text);
  1947.             first = FALSE;
  1948.         }
  1949.         if (! first)        /* wrap up call (if one made) */
  1950.             PRT("] %s\n", day == NOTE_DAY ? "notetext" : fcn);
  1951.     }
  1952. }
  1953.  
  1954.  
  1955. /*
  1956.  * Browse through the date file looking for holidays in specified month/year
  1957.  */
  1958. find_holidays(month, year)
  1959.     int month, year;
  1960. {
  1961.     register int day;
  1962.     register unsigned long holidays;
  1963.     year_info *py;
  1964.     month_info *pm;
  1965.  
  1966.     pm = (py = find_year(year, FALSE)) ? py->month[month-1] : NULL;
  1967.  
  1968.      PRT("/note_block %s def\n", pm && pm->day[NOTE_DAY-1] ? "true" :
  1969.         "false");        /* are there notes? */
  1970.  
  1971.     PRT("/holidays [");    /* start definition of list */
  1972.  
  1973.     for (holidays = pm ? pm->holidays : 0, day = 1;
  1974.          holidays;
  1975.          holidays >>= 1, day++)
  1976.         if (holidays & 01)
  1977.             PRT(" %d", day);
  1978.  
  1979.     PRT(" 99 ] def\n");    /* terminate with dummy entry */
  1980.  
  1981. }
  1982.  
  1983.  
  1984. /*
  1985.  * pmonth - generate calendar for specified month/year
  1986.  */
  1987. pmonth(month, year)
  1988.     int month, year;
  1989. {
  1990.  
  1991.     PRT("/year %d def\n", year);        /* set up year and month */
  1992.     PRT("/month %d def\n", month);
  1993.     find_holidays(month, year);        /* make list of holidays */
  1994.     PRT("printmonth\n");
  1995.     find_daytext(month, year, TRUE);    /* holiday text */
  1996.     find_daytext(month, year, FALSE);    /* day and note text */
  1997.     PRT("showpage\n");
  1998. }
  1999.  
  2000.  
  2001. /*
  2002.  * Routines dealing with translation of file specifications (VMS, Un*x)
  2003.  */
  2004.  
  2005. #ifdef VMS
  2006. /*
  2007.  * mk_path - extract the path component from VMS file spec
  2008.  */
  2009. char *mk_path(path, filespec)
  2010.     char *path;        /* output path */
  2011.     char *filespec;        /* input filespec */
  2012. {
  2013.     char *p;
  2014.  
  2015.     strcpy(path, filespec);
  2016.     if (!(p = strchr(path, ']')) && !(p = strchr(path, ':')))
  2017.         p = path - 1;    /* return null string if no path */
  2018.     *++p = '\0';
  2019.  
  2020.     return path;
  2021. }
  2022.  
  2023.  
  2024. /*
  2025.  * mk_filespec - merge VMS path and file names, where latter can be relative
  2026.  */
  2027.  
  2028. char *mk_filespec(filespec, path, name)
  2029.     char *filespec;        /* output filespec */
  2030.     char *path;        /* input path */
  2031.     char *name;        /* input file name */
  2032. {
  2033.     char *p;
  2034.  
  2035.     *filespec = '\0';
  2036.  
  2037.     /* copy name intact if absolute; else merge path and relative name */
  2038.     if (!strchr(name, ':')) {
  2039.         strcpy(filespec, path);
  2040.         if ((p = LASTCHAR(filespec)) && *p == END_PATH &&
  2041.             name[0] == START_PATH && strchr(".-", name[1]))
  2042.             *p = *++name == '-' ? '.' : '\0';
  2043.     }
  2044.  
  2045.     return strcat(filespec, name);
  2046. }
  2047.  
  2048.  
  2049. /*
  2050.  * trnlog - return translation of VMS logical name (null if missing)
  2051.  */
  2052. char *trnlog(logname)    /* look up logical name */
  2053.     char *logname;
  2054. {
  2055. static char trnbuf[STRSIZ];
  2056.  
  2057. $DESCRIPTOR(src, logname);
  2058. $DESCRIPTOR(dst, trnbuf);
  2059. short len;
  2060. int ret;
  2061.  
  2062. src.dsc$w_length  = strlen(logname);
  2063. ret = LIB$SYS_TRNLOG(&src, &len, &dst);
  2064. return ret == SS$_NORMAL ? (trnbuf[len] = '\0', trnbuf) : NULL;
  2065. }
  2066.  
  2067. #else
  2068.  
  2069. /*
  2070.  * mk_path - extract the path component from a Un*x file spec
  2071.  */
  2072. char *mk_path(path, filespec)
  2073.     char *path;        /* output path */
  2074.     char *filespec;        /* input filespec */
  2075. {
  2076.     char *p;
  2077.  
  2078.     strcpy(path, filespec);
  2079.     if (! (p = strrchr(path, END_PATH)) )
  2080.         p = path - 1;    /* return null string if no path */
  2081.  
  2082.     *++p = '\0';
  2083.     return path;
  2084. }
  2085.  
  2086.  
  2087. /*
  2088.  * mk_filespec - merge Un*x path and file names, where latter can be relative
  2089.  */
  2090.  
  2091. char *mk_filespec(filespec, path, name)
  2092.     char *filespec;        /* output filespec */
  2093.     char *path;        /* input path */
  2094.     char *name;        /* input file name */
  2095. {
  2096.     char *p;
  2097.  
  2098.     *filespec = '\0';
  2099.  
  2100.     /* copy name intact if absolute; else merge path and relative name */
  2101.  
  2102.     /* if path starts with "~/", translate it for user */
  2103.     if (strncmp(name, "~/", 2) == 0 && (p = trnlog(HOME_DIR)) != NULL) {
  2104.         strcpy(filespec, p);
  2105.         if ((p = LASTCHAR(filespec)) && *p != END_PATH)
  2106.             *++p = END_PATH, *++p = '\0';
  2107.         name += 2;        /* skip "~/" */
  2108.     }
  2109.     else if (*name != START_PATH) {        /* relative path */
  2110.         strcpy(filespec, path);
  2111.         if ((p = LASTCHAR(filespec)) && *p != END_PATH)
  2112.             *++p = END_PATH, *++p = '\0';
  2113.     }
  2114.  
  2115.     return strcat(filespec, name);
  2116. }
  2117.  
  2118.  
  2119. /*
  2120.  * trnlog - return translation of Un*x environment variable
  2121.  */
  2122. char *trnlog(logname)    /* look up logical name */
  2123.     char *logname;
  2124. {
  2125. return getenv(logname);
  2126. }
  2127.  
  2128. #endif
  2129. SHAR_EOF
  2130. fi
  2131. if test -f 'pcal.hlp'
  2132. then
  2133.     echo shar: will not over-write existing file 'pcal.hlp'
  2134. else
  2135. cat << \SHAR_EOF > 'pcal.hlp'
  2136. 1 PCAL
  2137.         Pcal  generates  PostScript  to produce landscape or  portrait
  2138.     orientated calendars for  any  month  and  year.  The defaults for
  2139.     month and year are the current month and year.
  2140.  
  2141.         VMS Version
  2142.         Execution format:
  2143.     
  2144.             pcal [options] [mm yy] [n]
  2145.  
  2146.         If a file named CALENDAR.DAT  resides  in  the  caller's  home
  2147.     directory (or in a directory defined by the logical name PCAL_DIR,
  2148.     if it exists), it will be  searched for  lines with  leading dates
  2149.     matching the requested  month  and year (current by default).  Any
  2150.     text following the date  will be printed on the calendar under the
  2151.     appropriate day of the month.   Dates in the CALENDAR.DAT file may
  2152.     consist  of  a  numeric or alpha  month  (at  least  the  first  3
  2153.     characters  for  month  names)  followed  by  a  numeric  day  and
  2154.     optionally  followed  by  a year.  Any non-numeric  character  may
  2155.     separate numeric dates.  Holidays may be flagged by  following the
  2156.     date immediately with '*';  this will cause the date to be printed
  2157.     in gray.  Lines in the CALENDAR.DAT file consisting of "year xxxx"
  2158.     (where  xxxx  is a numeric year) can be used to set the  year  for
  2159.     following entries.  This assumes that the following entries do not
  2160.     contain a year;  any date entries containing year information will
  2161.     set  the  remembered  year  to  that  year.  Lines  consisting  of
  2162.     "opt <options>" can be used to  override defaults for all command-
  2163.     line flags except -e, -f, -D, and -U; any flags set in this manner
  2164.     are themselves  overridden by  flags specified  explicitly  on the
  2165.     command line.  Comments ('#' through  end-of-line) are  permitted.
  2166.  
  2167.     Example:
  2168.  
  2169.     # sample CALENDAR.DAT file - Raytheon holidays and exempt paydays, 1990
  2170.  
  2171.     opt -t Helvetica-Bold -d Helvetica-Bold    # override default fonts
  2172.     opt -M                    # print moon phase
  2173.  
  2174.     year 1990                # set default year
  2175.  
  2176.     7/4* Independence Day            # '*' flags 7/4 as holiday
  2177.     7/12/90 Exempt payday            # full numeric date
  2178.     Aug 16 Exempt payday            # alternate date format
  2179.     9/3* Labor Day
  2180.     9/20 Exempt payday
  2181.     10/8* Columbus Day (observed)
  2182.     10/25 Exempt payday
  2183.     11/20 Exempt payday
  2184.     11/22* Thanksgiving
  2185.     11/23*
  2186.     12/13 Exempt payday
  2187.     12/24*
  2188.     12/25* Christmas
  2189.  
  2190.     Release 2.3 of pcal adds rudimentary cpp-like functionality to the
  2191.     date file, supporting define|undef, if{n}def ... {else ...} endif,
  2192.     and include:
  2193.  
  2194.     define meetings
  2195.  
  2196.     ifdef meetings
  2197.       include meetings.dat
  2198.       undef meetings
  2199.     endif
  2200.     
  2201.     Symbol names are case-insensitive.  It is  not an error to "undef"
  2202.     an undefined symbol, nor to "define" a previously-defined one.
  2203.  
  2204.     "ifdef" alone is always false; "ifndef" alone is always true.
  2205.  
  2206.     The name of the file in the "include" directive may  optionally be
  2207.     surrounded  by either "" or <>, both of which are ignored.  If the
  2208.     name is not  an absolute  path, it is taken to be relative  to the
  2209.     directory where the file containing the directive is located.
  2210.  
  2211.     Release 2.3 allows the user to print additional notes in an unused
  2212.     unused calendar box via lines  of the  form "note <month> <text>",
  2213.     where <month> specifies the month (numeric or alphabetic form) and
  2214.     <text> is  the  text to  add.  All  such text  will  appear in the
  2215.     "Thursday" box on the last line (next to the small calendars).
  2216.  
  2217.     Release 2.3 also  looks  for global  symbol PCAL_OPTS; if defined,
  2218.     its contents are parsed as command-line flags.  These override the
  2219.     program  defaults,  but are overridden  by any specified via "opt"
  2220.     lines in  the date file or on the command line.  Example:
  2221.  
  2222.     $ define PCAL_OPTS "-n Helvetica -D meetings"      ! login.com
  2223.  
  2224.     $ pcal -"U" meetings 9 90    ! un-define symbol at runtime
  2225.  
  2226. 2 parameters
  2227.   mm yy n
  2228.         "mm" and "yy"  are numeric values of the month (1-12) and year
  2229.     (0-99) (i.e., July 1990  would  be 7 90).  If you just include the
  2230.     "yy" option, an entire 12  months  of calendars will be generated.
  2231.     A specific month can be produced  by including the "mm" parameter.
  2232.     The  "n"  parameter will  produce the "n"  consecutive  months  of
  2233.     calendars starting with the requested month.
  2234.  
  2235.         The following flags  may be specified  (in increasing order of
  2236.     precedence) in global symbol PCAL_OPTS, in "opt" lines in the date
  2237.     file (all but -e, -f, -D, -U), or on the command  line.  Any  flag
  2238.     which  normally  takes  an argument  may be specified  without the
  2239.     argument;  this resets  its  value  to the  program  default.  (-D
  2240.     alone thus clears all defined symbols; -U alone has no effect.)
  2241.  
  2242.     The '-' flag has  been added to  disambiguate  cases  where an
  2243.     argument-less flag has been specified immediately before a numeric
  2244.     parameter:
  2245.  
  2246.     $ pcal -t - 9 90
  2247.  
  2248. 2 -e
  2249.         Print an  empty  calendar (i.e., do not  print entries  from a
  2250.     CALENDAR.DAT file.)
  2251.  
  2252. 2 -f <FILE>
  2253.         Directs pcal to use the  file name <FILE> as the input file in
  2254.     place  of the  default  CALENDAR.DAT  file  in  the  callers  home
  2255.     directory or in the  directory specified by logical name PCAL_DIR.
  2256.  
  2257. 2 -o <FILE>
  2258.         Directs  pcal to  write  the  PostScript  calendar  into  FILE
  2259.     (default: CALENDAR.PS in the current directory.)
  2260.  
  2261. 2 -l
  2262.         This will cause the  output  to  come  out  in  landscape mode
  2263.     (default).
  2264.  
  2265. 2 -p
  2266.         This will cause  the  output  to  come  out  in  portrait mode
  2267.     instead of landscape mode.
  2268.  
  2269. 2 -b <DAY> | all
  2270.         This  will cause  all dates  on weekday  DAY to be  printed in 
  2271.     black;  "-b all" causes  all dates  to be printed  in black unless
  2272.     explicitly flagged as a holiday.
  2273.  
  2274. 2 -g <DAY> | all
  2275.         This  will cause  all dates  on weekday  DAY to be  printed in 
  2276.     gray; "-g all" causes all dates to be printed in gray.  Default is
  2277.     to print Saturdays  and Sundays in gray and  other dates in black.
  2278.  
  2279. 2 -t <FONT>
  2280.         This option can be used  to  change  the  font  the  title  is
  2281.     printed in  (ie. pcal -t Times-Roman).  The default is Times-Bold.
  2282.  
  2283. 2 -d <FONT>
  2284.         This option is the same as  -t  except  that  the font used to
  2285.     print the  day  numbers is  changed.  The  default is  Times-Bold.
  2286.  
  2287. 2 -n <FONT>
  2288.         This option is the same as  -n  except  that  the font used to
  2289.     print the notes in the calendar boxes is  changed.  The default is
  2290.     Helvetica-Narrow.
  2291.  
  2292. 2 -m
  2293.         This option causes a  moon to be printed on days corresponding
  2294.     to a full, half, or new moon (default: no moons).
  2295.  
  2296. 2 -"M"
  2297.         This option causes a  moon to be printed on all days (default:
  2298.     no moons).
  2299.  
  2300. 2 -"L" <STRING>
  2301.         This will cause STRING to be printed as a left footer.
  2302.  
  2303. 2 -"C" <STRING>
  2304.         This will cause STRING to be printed as a center footer.
  2305.  
  2306. 2 -"R" <STRING>
  2307.         This will cause STRING to be printed as a right footer.
  2308.  
  2309. 2 -"D" <SYM>
  2310.     This will  define symbol  SYM prior to  reading the date file;
  2311.     -D alone clears all defined symbols.
  2312.  
  2313. 2 -"U" <SYM>
  2314.     This will undefine symbol  SYM prior to reading the date file.
  2315.  
  2316. 2 CREDITS
  2317.         The original PostScript code  to  generate  the  calendars was
  2318.     written by Patrick Wood (Copywrite  (c)  1987  by  Patrick Wood of
  2319.     Pipeline  Associates, Inc.), and authorized for  modification  and
  2320.     redistribution.    The  CALENDAR.DAT  file  inclusion  code    was
  2321.     originally written in "bs(1)" by Bill Vogel of  AT&T.    Patrick's
  2322.     original  PostScript  was  modified  and enhanced several times by
  2323.     others  whose names  have  regrettably been lost.  Ken Keirnan  of
  2324.     Pacific Bell assembled the original "C" version upon which this is
  2325.     based;  additional modifications and enhancements were the work of
  2326.     Joseph P. Larson, Ed Hand, Andrew W. Rogers,  Mark Kantrowitz, Joe
  2327.     Brownlee, and Jamie Zawinski.  This  VMS HELP file was  written by
  2328.     Richard Dyson and updated by Andrew W. Rogers.
  2329.  
  2330. SHAR_EOF
  2331. fi
  2332. if test -f 'pcal.man'
  2333. then
  2334.     echo shar: will not over-write existing file 'pcal.man'
  2335. else
  2336. cat << \SHAR_EOF > 'pcal.man'
  2337. .TH PCAL 1
  2338. .SH NAME
  2339. pcal \- generate PostScript calendars
  2340. .SH SYNOPSIS
  2341. .B pcal
  2342. [
  2343. .BR \-e
  2344. |
  2345. .BR \-f
  2346. <cal>
  2347. ]
  2348. [
  2349. .BR \-o
  2350. <file>
  2351. ]
  2352. [
  2353. .BR \-l
  2354. |
  2355. .BR \-p
  2356. ]
  2357. [
  2358. .BR \-m
  2359. |
  2360. .BR \-M
  2361. ]
  2362. [
  2363. .BR \-b
  2364. <day>
  2365. |
  2366. all
  2367. ]*
  2368. [
  2369. .BR \-g
  2370. <day>
  2371. |
  2372. all
  2373. ]*
  2374. [
  2375. .B -t
  2376. <titlefont name>
  2377. ]
  2378. [
  2379. .B -d
  2380. <dayfont name>
  2381. ]
  2382. [
  2383. .B -n
  2384. <notesfont name>
  2385. ]
  2386. [
  2387. .B -L
  2388. <foot string>
  2389. ]
  2390. [
  2391. .B -C
  2392. <foot string>
  2393. ]
  2394. [
  2395. .B -R
  2396. <foot string>
  2397. ]
  2398. [
  2399. .B -D
  2400. <symbol>
  2401. ]
  2402. [
  2403. .B -U
  2404. <symbol>
  2405. ]
  2406. [
  2407. .B month
  2408. ] [
  2409. .B year
  2410. ] [
  2411. .B nmonths
  2412. ]
  2413. .SH DESCRIPTION
  2414. .I Pcal
  2415. generates PostScript to produce landscape or portrait calendars for any 
  2416. month and year. The arguments
  2417. .BR month ,
  2418. .BR year ,
  2419. and
  2420. .BR nmonths ,
  2421. if provided, should be numeric. The month should be in the range 1 - 12,
  2422. and year should be specified as 1 or 2 digits or as the full 4 digit year.
  2423. .P
  2424. If no numeric arguments are provided, the current month and year will be
  2425. generated.
  2426. .P
  2427. If one numeric argument is provided, it is interpreted as the year; the
  2428. entire year will be generated. Otherwise, 
  2429. .I nmonth
  2430. months, starting with
  2431. .I month
  2432. and
  2433. .I year,
  2434. will be generated.
  2435. .PP
  2436. If a file named
  2437. .I \.calendar
  2438. or
  2439. .I calendar
  2440. (for compatibility with older versions)
  2441. exists in the caller's home directory (or in the directory pointed to by
  2442. environment variable PCAL_DIR), it will be searched for lines with
  2443. leading dates matching the requested month and year (current by default).
  2444. Any text following the date will be printed on the calendar under the
  2445. appropriate day of the month. Dates in the
  2446. .I \.calendar
  2447. file may consist of a numeric or alpha month (at least the first 3 characters
  2448. for month names) followed by a numeric day and optionally followed by a
  2449. year. Any non-numeric character may separate numeric dates. Holidays may
  2450. be flagged by following the date immediately with '*'; this will cause the
  2451. date to be printed in gray.
  2452. .PP
  2453. Lines in the
  2454. .I \.calendar
  2455. file consisting of "year xxxx" (where xxxx is a numeric year) can be used
  2456. to set the year for following entries. This assumes that the following
  2457. entries do not contain a year; any date entries containing year information
  2458. will set the remembered year to that year.
  2459. .PP
  2460. Lines in the
  2461. .I \.calendar
  2462. file consisting of "opt <options>" can be used to override the defaults for
  2463. any command-line flags except -e, -f, -D, and -U. Any flags specified in this
  2464. manner
  2465. are, in turn, overridden by those specified explicitly on the command line.
  2466. .PP
  2467. Lines in the
  2468. .I \.calendar
  2469. file consisting of "note <month>" can be used to place notes regarding the
  2470. entire month is one of the unused blocks of the calendar.  The <month>
  2471. indicator may be either a number 1 through 12 or an alphabetic month name
  2472. as described above.
  2473. .PP
  2474. Comments ('#' through end-of-line) are supported.
  2475. .PP
  2476. .I Pcal
  2477. supports rudimentary cpp-like functionality in the
  2478. date file, allowing the following constructs:
  2479. .BR define |
  2480. .BR undef ,
  2481. .B if{n}def ... {else ...} endif,
  2482. and
  2483. .BR include.
  2484. Note that these are not preceded by '#' as they are in C.
  2485. .PP    
  2486. Symbol names are case-insensitive. It is not an error to "undef"
  2487. an undefined symbol, nor to "define" a previously-defined one.
  2488. .PP
  2489. "ifdef" alone is always false; "ifndef" alone is always true.
  2490. .PP
  2491. The name of the file in the "include" directive may optionally be
  2492. surrounded by either "" or <>, both of which are ignored. If the
  2493. name is not an absolute path, it is taken to be relative to the
  2494. directory where the file containing the directive is located.
  2495. .I Pcal
  2496. is smart enough to translate "~/" to the user's home directory.
  2497. .PP
  2498. .I Pcal
  2499. has many options:
  2500. .P
  2501. .TP
  2502. .B \-e
  2503. Print an empty calendar. Do not print entries from a calendar file.
  2504. .TP
  2505. .B \-f <cal>
  2506. Directs
  2507. .I pcal
  2508. to use the file name <cal> as the input file in place of the default
  2509. .I \.calendar
  2510. file in the callers home directory (or directory pointed to
  2511. by PCAL_DIR).
  2512. .TP
  2513. .B \-o <file>
  2514. Directs
  2515. .I pcal
  2516. to write the output to <file> instead of to stdout.
  2517. .TP
  2518. .B \-l
  2519. This will cause the output to come out in landscape mode (default).
  2520. .TP
  2521. .B \-p
  2522. This will cause the output to come out in portrait mode.
  2523. .TP
  2524. .B \-m
  2525. This will cause moon icons to be printed on dates corresponding to
  2526. new, half, and full moons (default: no moons).
  2527. .TP
  2528. .B \-M
  2529. This will cause moon icons to be printed on all dates (default: no moons).
  2530. .TP
  2531. .B \-b <day> | all
  2532. This will cause all dates falling on weekday <day> to be printed in black;
  2533. "-b all" causes all weekdays to be printed in black.
  2534. .TP
  2535. .B \-g <day> | all
  2536. This will cause all dates falling on weekday <day> to be printed in gray;
  2537. "-g all" causes all weekdays to be printed in gray.
  2538. .IP
  2539. (The default for the -b and -g flags is to print Saturdays and Sundays in
  2540. gray and other days - unless flagged as holidays - in black.)
  2541. .TP
  2542. .B \-t <titlefont name>
  2543. This option can be used to change the font the title
  2544. is printed in. (ie. pcal -t Times-Roman).
  2545. .TP
  2546. .B \-d <dayfont name>
  2547. This option is the same as -t except that the font used
  2548. to print the day numbers is changed.
  2549. .TP
  2550. .B \-n <notesfont name>
  2551. This option is the same as -n except that the font used
  2552. to print the notes in the calendar boxes is changed.
  2553. .TP
  2554. .B \-D <symbol>
  2555. This option defines the named symbol prior to reading the date file.
  2556. .TP
  2557. .B \-U <symbol>
  2558. This option un-defines the named symbol prior to reading the date file.
  2559. .TP
  2560. .B \-L <string>
  2561. This option causes the accompanying string to be printed as a left footer.
  2562. .TP
  2563. .B \-C <string>
  2564. This option causes the accompanying string to be printed as a center footer.
  2565. .TP
  2566. .B \-R <string>
  2567. This option causes the accompanying string to be printed as a right footer.
  2568. .IP
  2569. Any option which normally takes an argument may be specified without
  2570. the argument in order to reset the value to the program default.
  2571. .B \-D
  2572. alone clears all the defined symbols;
  2573. .B \-U
  2574. alone has no effect.  The
  2575. .B \-
  2576. (or
  2577. .BR \-\- ,
  2578. System V people)
  2579. flag may be used to disambiguate cases such as "pcal -t 9 90"; this
  2580. could be written instead as "pcal -t - 9 90" or "pcal -t -- 9 90".
  2581. .IP
  2582. If the environment variable PCAL_OPTS is defined, its contents are
  2583. parsed as a command line.  Flags set via PCAL_OPTS override the
  2584. program defaults, but are overridden by flags set via "opt" lines
  2585. in the date file or explicitly on the command line.
  2586. .SH SEE ALSO
  2587. cal(1)
  2588. .SH CAVEATS
  2589. The original PostScript code to generate the calendars was written by
  2590. Patrick Wood (Copywrite (c) 1987 by Patrick Wood of Pipeline Associates,
  2591. Inc.), and authorized for modification and redistribution. The calendar
  2592. file inclusion code was originally written in "bs(1)" by Bill Vogel of
  2593. AT&T. Patrick's original PostScript was modified and enhanced several
  2594. times by others whos names have regrettably been lost. Ken Keirnan of
  2595. Pacific Bell assembled the original "C" version upon which this is based;
  2596. additional modifications and enhancements were the work of Joseph P.
  2597. Larson, Ed Hand, Andrew W. Rogers, Mark Kantrowitz, Joe Brownlee, and
  2598. Jamie Zawinski.
  2599. SHAR_EOF
  2600. fi
  2601. if test -f 'pcalinit.c'
  2602. then
  2603.     echo shar: will not over-write existing file 'pcalinit.c'
  2604. else
  2605. cat << \SHAR_EOF > 'pcalinit.c'
  2606. /* create a .h file from a .ps file.  Strips out leading and trailing 
  2607.  * whitespace, blank lines, and lines consisting solely of comments,
  2608.  * except for the very first block of comments/blanklines, which are
  2609.  * turned into C comments at the top of the file.
  2610.  * 
  2611.  *   14-sep-90  Jamie Zawinski  created.
  2612.  */
  2613.  
  2614. #include <stdio.h>
  2615. #include <ctype.h>
  2616. #include <string.h>
  2617.  
  2618. char *strip_white (string)
  2619.      char *string;
  2620. {
  2621.     int n;
  2622.     for (; *string == ' ' || *string == '\t' || *string == '\n'; string++);
  2623.     n = strlen(string)-1;
  2624.     for (; string[n] == ' ' || string[n] == '\t' || string[n] == '\n'; n--)
  2625.         string[n] = '\0';
  2626.     return string;
  2627. }
  2628.  
  2629. main (argc, argv)
  2630.      int argc; char *argv[];
  2631. {
  2632.     FILE *in, *out;
  2633.     char line[256], *L;
  2634.     int in_initial_comments = 1;
  2635.     if (argc != 3) {
  2636.        fprintf(stderr, "usage: %s <infile>.ps <outfile>.h\n", argv[0]);
  2637.        exit(-1); }
  2638.     
  2639.     in = fopen(argv[1], "r");
  2640.     if (NULL == in) {
  2641.        fprintf(stderr, "%s: couldn't open %s\n", argv[0], argv[1]);
  2642.        exit(-1); }
  2643.     
  2644.     out = fopen(argv[2], "w");
  2645.     if (NULL == out) {
  2646.        fprintf(stderr, "%s: couldn't open %s\n", argv[0], argv[2]);
  2647.        exit(-1); }
  2648.     
  2649.     fprintf (out,
  2650.        "/* automatically generated from \"%s\" - DO NOT EDIT THIS FILE!\n *\n",
  2651.        argv[1]);
  2652.     for (;;) {
  2653.        if (NULL == fgets (line, 255, in)) break;
  2654.        L = strip_white(line);
  2655.        if ( in_initial_comments ) {
  2656.       if ( L[0] != '%' && L[0] != '\0' ) {
  2657.          in_initial_comments = 0;
  2658.          fprintf(out, " */\n\nchar *header[] = {\n  \"%s\",\n", L); }
  2659.       else
  2660.          fprintf(out, " * %s\n", L);
  2661.        }
  2662.        else if (L[0] != '%' && L[0] != '\0' )
  2663.           fprintf(out, "  \"%s\",\n", L);
  2664.     }
  2665.     fprintf(out, "  (char *)0,\n};\n");
  2666.     fclose(out);
  2667.     fclose(in);
  2668. }
  2669. SHAR_EOF
  2670. fi
  2671. if test -f 'pcalinit.ps'
  2672. then
  2673.     echo shar: will not over-write existing file 'pcalinit.ps'
  2674. else
  2675. cat << \SHAR_EOF > 'pcalinit.ps'
  2676. %!ps
  2677. % pcalinit.ps - provides the PostScript routines for v2.3 of pcal.c
  2678. %
  2679. % 2.3    modified by Jamie Zawinski <jwz@lucid.com>:
  2680. %
  2681. %    merged in moon routines (originally by Mark Hanson)
  2682. %
  2683. % 2.2    modified by Joe Brownlee:
  2684. %
  2685. %    add "notetext" to print notes in unused calendar box
  2686. %
  2687. % 2.1    modified by Mark Kantrowitz:
  2688. %
  2689. %    use symbolic names instead of magic numbers throughout
  2690. %    support -L, -C, -R, -n options (all new)
  2691. %    print holiday text in otherwise-wasted space next to date
  2692. %    use larger text for dates in large calendars
  2693. %
  2694. % 2.0    modified by Andrew W. Rogers:
  2695. %
  2696. %    skip printing days of week on small calendars
  2697. %    center month and year at top of calendar
  2698. %    use correct algorithm for leap year calculation
  2699. %    get month and day names from main program
  2700. %    use table to determine color (black/gray) of weekdays and holidays
  2701. %    use hanging indent to print continued text lines
  2702.  
  2703. /titlefontsize 48 def
  2704. /weekdayfontsize 12 def
  2705. /footfontsize 12 def
  2706. /datefontsize 30 def
  2707. /savedatefontsize datefontsize def
  2708. /notesfontsize 6 def
  2709. /daywidth 100 def
  2710. /dayheight 80 def
  2711. /negdaywidth 0 daywidth sub def
  2712. /negdayheight 0 dayheight sub def
  2713. /prtnum { 3 string cvs show} def
  2714.  
  2715. /drawgrid {        % draw calendar boxes
  2716.     dayfont findfont weekdayfontsize scalefont setfont
  2717.     0 1 6 {
  2718.         /i exch def
  2719.         submonth 0 eq {
  2720.             i daywidth mul 40 moveto
  2721.             day_names i get
  2722.             daywidth center
  2723.         } if
  2724.         i daywidth mul 35 moveto
  2725.         1.0 setlinewidth
  2726.         0 1 5 {
  2727.             gsave
  2728.             daywidth 0 rlineto
  2729.             0 negdayheight rlineto
  2730.             negdaywidth 0 rlineto
  2731.             closepath stroke
  2732.             grestore
  2733.             0 negdayheight rmoveto
  2734.          pop } for
  2735.     } for
  2736. } def
  2737.  
  2738.  
  2739. /drawnums {        % place day numbers on calendar
  2740.     dayfont findfont datefontsize scalefont setfont
  2741.     /start startday def
  2742.     /days ndays def
  2743.     /fontdiff datefontsize savedatefontsize sub def
  2744.     /n 0 def
  2745.     start daywidth mul 5 add 10 fontdiff sub rmoveto
  2746.     1 1 days {
  2747.         /day exch def
  2748.         gsave
  2749.         submonth 0 eq {
  2750.             /gray day_gray day start add 1 sub 7 mod get def
  2751.             day holidays n get eq {
  2752.                 /gray holiday_gray def
  2753.                 /n n 1 add def
  2754.             } if
  2755.             gray {
  2756.                 0.8 setgray
  2757.             } if
  2758.         } if
  2759.         day prtnum
  2760.         grestore
  2761.         day start add 7 mod 0 eq
  2762.         {
  2763.             currentpoint exch pop dayheight sub 5 exch moveto
  2764.         }
  2765.         {
  2766.             daywidth 0 rmoveto
  2767.         } ifelse
  2768.     } for
  2769. } def
  2770.  
  2771.  
  2772. /drawfill {        % place fill squares on calendar
  2773.     /start startday def
  2774.     /days ndays def
  2775.     0 35 rmoveto
  2776.     1.0 setlinewidth
  2777.     0 1 start 1 sub {
  2778.         gsave
  2779.         .9 setgray
  2780.         daywidth 0 rlineto 
  2781.         0 negdayheight rlineto
  2782.         negdaywidth 0 rlineto
  2783.         closepath fill
  2784.         grestore
  2785.         daywidth 0 rmoveto
  2786.     pop } for
  2787.     submonth 1 eq
  2788.     {
  2789.         /lastday 42 def
  2790.         600 -365 moveto
  2791.     }
  2792.     {
  2793.         note_block
  2794.         {
  2795.             /lastday 39 def
  2796.             300 -365 moveto
  2797.         }
  2798.         {
  2799.             /lastday 40 def
  2800.             400 -365 moveto
  2801.         } ifelse
  2802.     } ifelse
  2803.     lastday -1 ndays start 1 add add
  2804.     {
  2805.         /day exch def
  2806.         gsave
  2807.         .9 setgray
  2808.         daywidth 0 rlineto 
  2809.         0 negdayheight rlineto
  2810.         negdaywidth 0 rlineto
  2811.         closepath fill
  2812.         grestore
  2813.         day 7 mod 1 eq
  2814.         { 600 -365 dayheight add moveto }
  2815.         { negdaywidth 0 rmoveto }
  2816.         ifelse
  2817.     } for
  2818. } def
  2819.  
  2820.  
  2821. /isleap {        % is this a leap year?
  2822.     year 4 mod 0 eq        % multiple of 4
  2823.     year 100 mod 0 ne     % not century
  2824.     year 400 mod 0 eq or and    % or divisible by 400
  2825. } def
  2826.  
  2827. /isleap_2 {        % is some other year a leap year?
  2828.     /a_year exch def
  2829.      a_year 4 mod 0 eq        % multiple of 4
  2830.      a_year 100 mod 0 ne     % not century
  2831.      a_year 400 mod 0 eq or and    % or divisible by 400
  2832. } def
  2833.  
  2834. /days_month [ 31 28 31 30 31 30 31 31 30 31 30 31 ] def
  2835.  
  2836. /ndays {        % number of days in this month
  2837.     days_month month 1 sub get
  2838.     month 2 eq    % Feb
  2839.     isleap and
  2840.     {
  2841.         1 add
  2842.     } if
  2843. } def
  2844.  
  2845. /ndays_2 {              % number of days in some other month
  2846.     /a_month exch def
  2847.     /a_year exch def
  2848.     days_month a_month 1 sub get
  2849.     a_month 2 eq            % February
  2850.     a_year isleap_2 and
  2851.     { 1 add }
  2852.     if
  2853. } def
  2854.  
  2855.  
  2856. /startday {        % starting day-of-week for this month
  2857.     /off year 2000 sub def    % offset from start of epoch
  2858.     off
  2859.     off 4 idiv add        % number of leap years
  2860.     off 100 idiv sub    % number of centuries
  2861.     off 400 idiv add    % number of years divisible by 400
  2862.     6 add 7 mod 7 add     % offset from Jan 1 2000
  2863.     /off exch def
  2864.     1 1 month 1 sub {
  2865.         /idx exch def
  2866.         days_month idx 1 sub get
  2867.         idx 2 eq
  2868.         isleap and
  2869.         {
  2870.             1 add
  2871.         } if
  2872.         /off exch off add def
  2873.     } for
  2874.     off 7 mod        % 0--Sunday, 1--monday, etc.
  2875. } def
  2876.  
  2877.  
  2878. /center {        % center string in given width
  2879.     /width exch def
  2880.     /str exch def width str 
  2881.     stringwidth pop sub 2 div 0 rmoveto str show
  2882. } def
  2883.  
  2884.  
  2885. /strcat {        % concatenate two strings
  2886.     2 copy
  2887.     length exch length
  2888.     dup 3 -1 roll add
  2889.     string
  2890.     dup 0 6 -1 roll putinterval
  2891.     dup 3 -1 roll 4 -1 roll putinterval
  2892. } def
  2893.  
  2894.  
  2895. /calendar
  2896. {
  2897.     titlefont findfont titlefontsize scalefont setfont
  2898.     /month_name month_names month 1 sub get def
  2899.     /yearstring year 10 string cvs def
  2900.     0 60 moveto
  2901.     month_name (  ) strcat yearstring strcat 700 center
  2902. %    month_name show
  2903. %    daywidth 7 mul yearstring stringwidth pop sub 60 moveto
  2904. %    yearstring show
  2905.  
  2906.     submonth 0 eq {
  2907.         titlefont findfont footfontsize scalefont setfont
  2908.         /bottomrow { dayheight 6 mul 20 sub neg } def
  2909.         0 bottomrow moveto
  2910.         Lfootstring show
  2911.         daywidth 7 mul Rfootstring stringwidth pop sub
  2912.         bottomrow moveto
  2913.         Rfootstring show
  2914.         daywidth 7 mul Cfootstring stringwidth pop sub 2 div
  2915.         bottomrow moveto
  2916.         Cfootstring show
  2917.     } if
  2918.     0 0 moveto
  2919.     drawnums
  2920.     0 0 moveto
  2921.     drawfill
  2922.     0 0 moveto
  2923.     drawgrid
  2924.     0 0 moveto
  2925.     submonth 0 eq { drawmoons } if
  2926. } def
  2927.  
  2928.  
  2929. /daytext {
  2930.     notesfont findfont notesfontsize scalefont setfont
  2931.     /mytext    exch def /myday exch def
  2932.     startday myday 1 sub add dup 7 mod daywidth mul 2 add % gives column
  2933.     exch 7 idiv negdayheight mul % gives row
  2934.     dup /ypos exch def moveto
  2935.     /LM currentpoint pop def /RM LM 95 add def
  2936.     mytext { dup (.p) eq { crlf pop} {prstr ( ) show} ifelse } forall
  2937. } def
  2938.  
  2939.  
  2940. /holidaytext {
  2941.     notesfont findfont notesfontsize scalefont setfont
  2942.     /mytext    exch def /myday exch def
  2943.     startday myday 1 sub add dup 7 mod daywidth mul
  2944.     myday 10 lt {25} {40} ifelse add % gives column
  2945.     exch 7 idiv negdayheight mul 27 add % gives row
  2946.     dup /ypos exch def moveto
  2947.     /LM currentpoint pop def /RM LM myday 10 lt {75} {60} ifelse add def
  2948.     /day myday def
  2949.     do-moon-p { /RM RM 16 sub def } if
  2950.     mytext { dup (.p) eq { crlf pop} {prstr ( ) show} ifelse } forall
  2951. } def
  2952.  
  2953.  
  2954. /notetext {
  2955.     dayfont findfont 12 scalefont setfont
  2956.     4 daywidth mul 5 add % title column
  2957.     5 negdayheight mul 24 add % title row
  2958.     moveto
  2959.     (Notes) show
  2960.     notesfont findfont notesfontsize scalefont setfont
  2961.     /mytext    exch def
  2962.     4 daywidth mul 5 add % gives column
  2963.     5 negdayheight mul 16 add % gives row
  2964.     dup /ypos exch def moveto
  2965.     /LM currentpoint pop def /RM LM 95 add def
  2966.     mytext { dup (.p) eq { crlf pop} {prstr ( ) show} ifelse } forall
  2967. } def
  2968.  
  2969. /crlf {
  2970.     ypos notesfontsize sub /ypos exch def LM ypos moveto
  2971. } def
  2972.  
  2973. /prstr {
  2974.     dup stringwidth pop currentpoint pop
  2975.     add RM gt {crlf (   ) show} if show
  2976. } def
  2977.  
  2978. /printmonth {
  2979.     landscape-p
  2980.       { 90 rotate 50 -120 translate }
  2981.       { 0.75 0.75 scale 50 500 translate }
  2982.     ifelse
  2983.     /submonth 0 def
  2984.     calendar
  2985.     month 1 sub 0 eq
  2986.     {
  2987.         /lmonth 12 def
  2988.         /lyear year 1 sub def
  2989.     }
  2990.     {
  2991.         /lmonth month 1 sub def
  2992.         /lyear year def
  2993.     } ifelse
  2994.     month 1 add 13 eq
  2995.     {
  2996.         /nmonth 1 def
  2997.         /nyear year 1 add def
  2998.     } 
  2999.     {
  3000.         /nmonth month 1 add def
  3001.         /nyear year def
  3002.     } ifelse
  3003.     /savemonth month def
  3004.     /saveyear year def
  3005.     /submonth 1 def
  3006.     /savedatefontsize datefontsize def
  3007.     /year lyear def
  3008.     /month lmonth def
  3009.     /datefontsize 48 def
  3010.     gsave
  3011.     500 -365 translate
  3012.     gsave
  3013.     .138 .138 scale
  3014.     10 -120 translate
  3015.     calendar
  3016.     grestore
  3017.     /submonth 1 def
  3018.     /year nyear def
  3019.     /month nmonth def
  3020.     100 0 translate
  3021.     gsave
  3022.     .138 .138 scale
  3023.     10 -120 translate
  3024.     calendar
  3025.     grestore
  3026.     /month savemonth def
  3027.     /year saveyear def
  3028.     /submonth 0 def
  3029.     /datefontsize savedatefontsize def
  3030.     grestore
  3031. } def
  3032.  
  3033. % Moons...
  3034.  
  3035. /doy {         % year month day doy -> returns the number of the day 
  3036.   /theday   exch def   % of the year
  3037.   /themonth exch def
  3038.   /theyear  exch def
  3039.   /dayofyear 0 def
  3040.   themonth 1 ne {
  3041.     1 1 themonth .5 sub {
  3042.       /mo exch cvi def
  3043.       /dayofyear theyear mo
  3044.      ndays_2
  3045.     dayofyear add
  3046.     def
  3047.     } for
  3048.   } if
  3049.   dayofyear theday add
  3050. } def            % doy
  3051.     
  3052.  
  3053. /findphase {        % find the difference between any day and the reference
  3054.   /thisday   exch def   % day of the full moon
  3055.   /thismonth exch def   % will probably be one off if the reference is leap yr.
  3056.   /thisyear  exch def
  3057.   /daysdiff  thisyear thismonth thisday doy
  3058.          fullyear fullmonth fullday doy sub 
  3059.          longer mul def      % try to be accurate about it
  3060.   /yearsdiff thisyear fullyear sub def
  3061.   yearsdiff 0 ne {
  3062.     /daysdiff daysdiff yearsdiff daysperyear mul
  3063.           yearsdiff 100 idiv yearsdiff 400 idiv sub sub add def
  3064.   } if
  3065.   daysdiff  % return difference in days
  3066. } def       % findphase
  3067.  
  3068. /shrink { 2 sqrt div } def
  3069.  
  3070. /transmogrify { 10000 mul cvi     % take a real number and 'mod it down'
  3071.         period 10000 mul cvi  % so it is in the range 0->period
  3072.         mod           % or -period->0
  3073.         10000 div } def       % the 10000's preserve the accuracy
  3074.  
  3075.  
  3076. /domoon {       % draw the moon at the current phase
  3077.   /phase exch def
  3078.  
  3079.   0 0 radius               % might as well push these on now
  3080.   0 0 radius 
  3081.   phase halfperiod lt
  3082.     { 270 90 arc stroke        % line on right, fill on left
  3083.       0 radius neg moveto
  3084.       270 90 arcn 
  3085.     }
  3086.     { 90 270 arc stroke        % line on left, fill on right
  3087.       0 radius neg moveto
  3088.       270 90 arc 
  3089.       /phase phase halfperiod sub def  % get rid of top halfperiod
  3090.     }
  3091.   ifelse
  3092.  
  3093.   /phase phase quartperiod sub      % scale it down to -r(root2) -> r(root2)
  3094.      rect mul
  3095.    def
  3096.  
  3097.   phase         % x1
  3098.   phase abs shrink      % y1 need abs!
  3099.   phase         % x2 
  3100.   phase abs shrink neg  % y2 need abs!
  3101.   0             % x3
  3102.   radius neg        % y3 
  3103.   curveto 
  3104.   fill
  3105. } def    % domoon
  3106.  
  3107. /do-moon-p {
  3108.     draw-moons true eq
  3109.     { true }
  3110.     { draw-moons 4 eq
  3111.       { /p startphase day longer mul add
  3112. % AWR: tweaked to more accurately select date of new/half/full moons
  3113. %       transmogrify neg period add
  3114.        0.5 sub transmogrify neg period add
  3115.       def
  3116.     p  0.00 gt  p  1.00 lt and
  3117.     p  7.38 gt  p  8.38 lt and or
  3118.     p 14.76 gt  p 15.76 lt and
  3119.     p 22.15 gt  p 23.15 lt and or
  3120.     or }
  3121.       { false }
  3122.       ifelse
  3123.     }
  3124.     ifelse
  3125. } def
  3126.  
  3127.  
  3128. /shiftdo {
  3129.    do-moon-p
  3130.    { /p startphase day longer mul add
  3131.     transmogrify neg period add
  3132.     def
  3133.      p domoon }
  3134.    if
  3135. } def
  3136.  
  3137.  
  3138. /drawmoons {
  3139.     /fullyear       1990 def      % these are the dates of a full moon,
  3140.     /fullmonth      2 def     % any date should work if it is that
  3141.     /fullday    9 def     % of a full moon, but I haven't tried many
  3142.     % I wouldn't make this reference date fall in a leap year, wierdness
  3143.     % will probably happen in findphase. You will probably gain or lose a day
  3144.     % somewhere. MBH
  3145.     /period     29.5306 def
  3146.     /daysperyear    365.2422 def
  3147.     /longer     daysperyear 365 div def
  3148.     /halfperiod     period 2 div def
  3149.     /quartperiod    period 4 div def
  3150.     /radius     6 def
  3151.     /rect       radius 2 sqrt mul quartperiod div def
  3152.     /startphase     year month 0 findphase transmogrify
  3153.                 dup 0 lt { period add } if def
  3154.     /start      startday def
  3155.     /days       year month ndays_2 def
  3156.  
  3157.     gsave
  3158.     0.1 setlinewidth
  3159.     newpath
  3160.     daywidth radius sub 3 sub 30 radius 3 add sub translate
  3161.     start daywidth mul 0 translate
  3162.     1 1 days {
  3163.     /day exch def
  3164.     shiftdo
  3165.     day start add 1 sub 7 mod 6 eq
  3166.       { daywidth 6 mul neg dayheight neg translate }
  3167.       { daywidth 0 translate }
  3168.     ifelse
  3169.     } for
  3170.     grestore
  3171. } def      % drawmoons
  3172.  
  3173. SHAR_EOF
  3174. fi
  3175. exit 0
  3176. #    End of shell archive
  3177.