home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 2: PC / frozenfish_august_1995.bin / bbs / d09xx / d0975.lha / PCal / readfile.c < prev    next >
C/C++ Source or Header  |  1992-02-19  |  33KB  |  1,536 lines

  1. /*
  2.  * readfile.c - Pcal routines concerned with reading and parsing the datefile
  3.  *
  4.  * Contents:
  5.  *
  6.  *        cleanup
  7.  *        clear_syms
  8.  *        date_type
  9.  *        do_define
  10.  *        do_ifdef
  11.  *        do_ifndef
  12.  *        do_include
  13.  *        do_undef
  14.  *        enter_day_info
  15.  *        find_sym
  16.  *        find_year
  17.  *        get_keywd
  18.  *        get_month
  19.  *        get_ordinal
  20.  *        get_phase
  21.  *        get_prep
  22.  *        get_token
  23.  *        get_weekday
  24.  *        is_anyday
  25.  *        is_fquarter
  26.  *        is_fullmoon
  27.  *        is_holiday
  28.  *        is_lquarter
  29.  *        is_newmoon
  30.  *        is_weekday
  31.  *        is_workday
  32.  *        not_holiday
  33.  *        not_weekday
  34.  *        not_workday
  35.  *        parse
  36.  *        parse_date
  37.  *        parse_ord
  38.  *        parse_rel
  39.  *        read_datefile
  40.  *
  41.  * Revision history:
  42.  *
  43.  *    4.3    AWR    10/25/91    Support moon phase wildcards and
  44.  *                    -Z flag (debug information)
  45.  *
  46.  *    4.2    AWR    10/03/91    Support "note/<n>" (user-selected
  47.  *                    notes box) as per Geoff Kuenning
  48.  *
  49.  *            09/30/91    Support "elif" in datefile
  50.  *
  51.  *    4.11    AWR    08/20/91    Support "nearest" keyword as per
  52.  *                    Andy Fyfe
  53.  *
  54.  *    4.0    AWR    02/19/91    Support negative ordinals
  55.  *
  56.  *        AWR    02/06/91    Support expressions in "if{n}def"
  57.  *
  58.  *        AWR    02/04/91    Support "even" and "odd" ordinals
  59.  *                    and ordinals > 5th; support "year"
  60.  *
  61.  *        AWR    01/15/91    Extracted from pcal.c
  62.  *
  63.  */
  64.  
  65. /*
  66.  * Standard headers:
  67.  */
  68.  
  69. #include <stdio.h>
  70. #include <string.h>
  71. #include <ctype.h>
  72.  
  73. /*
  74.  * Pcal-specific definitions:
  75.  */
  76.  
  77. #include "pcaldefs.h"
  78. #include "pcalglob.h"
  79. #include "pcallang.h"
  80.  
  81. /*
  82.  * Macros:
  83.  */
  84.  
  85. /* status codes returned by parse(), enter_day_info() */
  86. #define PARSE_OK    0    /* successful date parse */
  87. #define PARSE_INVDATE    1    /* nonexistent date */
  88. #define PARSE_INVLINE    2    /* syntax error */
  89. #define PARSE_NOMATCH    3    /* no match for wildcard */
  90.  
  91. /* codes for states in read_datefile() */
  92. #define PROCESSING    0    /* currently processing datefile lines */
  93. #define AWAITING_TRUE    1    /* awaiting first TRUE branch in "if{n}def" */
  94. #define SKIP_TO_ENDIF    2    /* finished processing first TRUE branch */
  95.  
  96. /* append date to list; terminate list */
  97. #define ADD_DATE(_m, _d, _y)    if (1) { \
  98.                 if (DEBUG(DEBUG_DATES)) \
  99.                     FPR(stderr, "matched %d/%d/%d\n", \
  100.                         _m, _d, _y); \
  101.                 pdate->mm = _m, pdate->dd = _d, pdate++->yy = _y; \
  102.                 } else
  103.  
  104. #define TERM_DATES        pdate->mm = pdate->dd = pdate->yy = 0
  105.  
  106. /*
  107.  * Globals:
  108.  */
  109.  
  110. static DATE dates[MAX_DATES+1];        /* array of date structures */
  111. static char *pp_sym[MAX_PP_SYMS];    /* preprocessor defined symbols */
  112.  
  113.  
  114. /*
  115.  * read_datefile - read and parse date file, handling preprocessor lines
  116.  *
  117.  * This is the main routine of this module.  It calls getline() to read each
  118.  * non-null line (stripped of leading blanks and trailing comments), loadwords()
  119.  * to "tokenize" it, and get_token() to classify it as a preprocessor directive
  120.  * or "other".  A switch{} statement takes the appropriate action for each
  121.  * token type; "other" lines are further classified by parse() (q.v.) which
  122.  * calls parse_date() (q.v.) to parse date entries and enter them in the data
  123.  * structure (as described in pcaldefs.h).
  124.  *
  125.  */
  126. #ifdef PROTOS
  127. void read_datefile(FILE *fp,
  128.            char *filename)
  129. #else
  130. void read_datefile(fp, filename)
  131.     FILE *fp;        /* file pointer (assumed open) */
  132.     char *filename;        /* file name (for error messages) */
  133. #endif
  134. {
  135.     static int file_level = 0;
  136.     int if_level = 0;
  137.     int line = 0;
  138.  
  139.     int pptype, extra, ntokens, save_year, expr;
  140.     int (*pfcn)();
  141.     char *ptok;
  142.     char **pword;
  143.     char msg[STRSIZ], incpath[STRSIZ];
  144.  
  145.     /* stack for processing nested "if{n}defs" - required for "elif" */
  146.     struct {
  147.         int state;    /* PROCESSING, AWAITING_TRUE, SKIP_TO_ENDIF */
  148.         int else_ok;    /* is "elif" or "else" legal at this point? */
  149.     } if_state[MAX_IF_NESTING+1];
  150.  
  151.     if (fp == NULL)                /* whoops, no date file */
  152.         return;
  153.  
  154.     /* note that there is no functional limit on file nesting; this is
  155.      * mostly to catch infinite loops (e.g., a includes b, b includes a)
  156.      */
  157.     if (++file_level > MAX_FILE_NESTING) {
  158.         ERR(E_FILE_NESTING);
  159.         exit(EXIT_FAILURE);
  160.     }
  161.  
  162.     save_year = curr_year;            /* save default year */
  163.  
  164.     if_state[0].state = PROCESSING;        /* set up initial state */
  165.     if_state[0].else_ok = FALSE;
  166.  
  167.     /* read lines until EOF */
  168.  
  169.     while (getline(fp, lbuf, &line)) {
  170.  
  171.         if (DEBUG(DEBUG_PP)) {
  172.             FPR(stderr, "%s (%d)", filename, line);
  173.             if (if_state[if_level].state == PROCESSING)
  174.                 FPR(stderr, ": '%s'", lbuf);
  175.             FPR(stderr, "\n");
  176.         }
  177.  
  178.         ntokens = loadwords(words, lbuf); /* split line into tokens */
  179.         pword = words;            /* point to the first */
  180.  
  181.         /* get token type and pointers to function and name */
  182.  
  183.         pptype = get_token(*pword++);
  184.         pfcn = pp_info[pptype].pfcn;
  185.         ptok = pp_info[pptype].name;
  186.  
  187.         switch (pptype) {
  188.  
  189.         case PP_DEFINE:
  190.         case PP_UNDEF:
  191.             if (if_state[if_level].state == PROCESSING)
  192.                 (void) (*pfcn)(*pword);
  193.             extra = ntokens > 2;
  194.             break;
  195.  
  196.         case PP_ELIF:
  197.             if (!if_state[if_level].else_ok) {
  198.                 ERR(E_ELIF_ERR);
  199.                 break;
  200.             }
  201.  
  202.             /* if a true expression has just been processed, disable
  203.              * processing and skip to endif; if no true expression
  204.              * has been found yet and the current expression is
  205.              * true, enable processing
  206.              */
  207.             switch (if_state[if_level].state) {
  208.             case PROCESSING:
  209.                 if_state[if_level].state = SKIP_TO_ENDIF;
  210.                 break;
  211.             case AWAITING_TRUE:
  212.                 copy_text(lbuf, pword);    /* reconstruct string */
  213.                 if ((expr = (*pfcn)(lbuf)) == EXPR_ERR) {
  214.                     ERR(E_EXPR_SYNTAX);
  215.                     expr = FALSE;
  216.                 }
  217.                 if (expr)
  218.                     if_state[if_level].state = PROCESSING;
  219.                 break;
  220.             }
  221.  
  222.             extra = FALSE;
  223.             break;
  224.  
  225.         case PP_ELSE:
  226.             if (!if_state[if_level].else_ok) {
  227.                 ERR(E_ELSE_ERR);
  228.                 break;
  229.             }
  230.  
  231.             /* if a true condition has just been processed, disable
  232.              * processing and skip to endif; if no true condition
  233.              * has been found yet, enable processing
  234.              */
  235.             switch (if_state[if_level].state) {
  236.             case PROCESSING:
  237.                 if_state[if_level].state = SKIP_TO_ENDIF;
  238.                 break;
  239.             case AWAITING_TRUE:
  240.                 if_state[if_level].state = PROCESSING;
  241.                 break;
  242.             }
  243.  
  244.             /* subsequent "elif" or "else" forbidden */
  245.             if_state[if_level].else_ok = FALSE;
  246.             extra = ntokens > 1;
  247.             break;
  248.  
  249.         case PP_ENDIF:
  250.             if (if_level < 1) {
  251.                 ERR(E_END_ERR);
  252.                 break;
  253.             }
  254.             if_level--;
  255.             extra = ntokens > 1;
  256.             break;
  257.  
  258.         case PP_IFDEF:
  259.         case PP_IFNDEF:
  260.             /* "if{n}def"s nested too deeply? */
  261.             if (++if_level > MAX_IF_NESTING) {
  262.                 ERR(E_IF_NESTING);
  263.                 exit(EXIT_FAILURE);
  264.                 break;
  265.             }
  266.  
  267.             /* if processing enabled at outer level, evaluate
  268.              * expression and enable/disable processing for
  269.              * following clause; if not, skip to matching endif
  270.              */
  271.             if (if_state[if_level-1].state == PROCESSING) {
  272.                 copy_text(lbuf, pword);    /* reconstruct string */
  273.                 if ((expr = (*pfcn)(lbuf)) == EXPR_ERR) {
  274.                     ERR(E_EXPR_SYNTAX);
  275.                     expr = FALSE;
  276.                 }
  277.                 if_state[if_level].state = expr ? PROCESSING :
  278.                                   AWAITING_TRUE;
  279.             } else
  280.                 if_state[if_level].state = SKIP_TO_ENDIF;
  281.  
  282.             if_state[if_level].else_ok = TRUE;
  283.             extra = FALSE;
  284.             break;
  285.  
  286.         case PP_INCLUDE:
  287.             if (if_state[if_level].state == PROCESSING)
  288.                 do_include(mk_path(incpath, filename), *pword);
  289.             extra = ntokens > 2;
  290.             break;
  291.  
  292.         case PP_OTHER:    /* none of the above - parse as date */
  293.             if (if_state[if_level].state == PROCESSING) {
  294.                 switch (parse(words, filename)) {
  295.                 case PARSE_INVDATE:
  296.                     ERR(E_INV_DATE);
  297.                     break;
  298.  
  299.                 case PARSE_INVLINE:
  300.                     ERR(E_INV_LINE);
  301.                     break;
  302.  
  303.                 case PARSE_NOMATCH:
  304.                     ERR(E_NO_MATCH);
  305.                     break;
  306.                 }
  307.             }
  308.             extra = FALSE;
  309.             break;
  310.  
  311.         } /* end switch */
  312.  
  313.         if (extra) {        /* extraneous data? */
  314.             sprintf(msg, E_GARBAGE, ptok);
  315.             ERR(msg);
  316.         }
  317.  
  318.     } /* end while */
  319.  
  320.     if (if_level > 0)
  321.         FPR(stderr, E_UNT_IFDEF, progname, filename);
  322.  
  323.     file_level--;
  324.     curr_year = save_year;        /* restore default year */
  325. }
  326.  
  327.  
  328. /*
  329.  * Routines to free allocated data (symbol table and data structure) 
  330.  */
  331.  
  332.  
  333. /*
  334.  * clear_syms - clear and deallocate the symbol table
  335.  */
  336. #ifdef PROTOS
  337. void clear_syms(void)
  338. #else
  339. void clear_syms()
  340. #endif
  341. {
  342.     int i;
  343.  
  344.     for (i = 0; i < MAX_PP_SYMS; i++)
  345.         if (pp_sym[i]) {
  346.             free(pp_sym[i]);
  347.             pp_sym[i] = NULL;
  348.         }
  349. }
  350.  
  351.  
  352. /*
  353.  * cleanup - free all allocated data
  354.  */
  355. #ifdef PROTOS
  356. void cleanup(void)
  357. #else
  358. void cleanup()
  359. #endif
  360. {
  361.     int i, j;
  362.     year_info *py, *pny;
  363.     month_info *pm;
  364.     day_info *pd, *pnd;
  365.  
  366.     for (py = head; py; py = pny) {        /* main data structure */
  367.         pny = py->next;
  368.         for (i = 0; i < 12; i++) {
  369.             if ((pm = py->month[i]) == NULL)
  370.                 continue;
  371.             for (j = 0; j < LAST_NOTE_DAY; j++)
  372.                 for (pd = pm->day[j]; pd; pd = pnd) {
  373.                     pnd = pd->next;
  374.                     free(pd->text);
  375.                     free(pd);
  376.                 }
  377.             free(pm);
  378.         }
  379.     free(py);
  380.     }
  381.  
  382.     clear_syms();                /* symbol table */
  383.  
  384. }
  385.  
  386.  
  387. /*
  388.  * Preprocessor token and symbol table routines
  389.  */
  390.  
  391.  
  392. /*
  393.  * find_sym - look up symbol; return symbol table index if found, PP_SYM_UNDEF
  394.  * if not found
  395.  */
  396. #ifdef PROTOS
  397. int find_sym(char *sym)
  398. #else
  399. int find_sym(sym)
  400.     char *sym;
  401. #endif
  402. {
  403.     int i;
  404.  
  405.     if (!sym)
  406.         return PP_SYM_UNDEF;
  407.  
  408.     for (i = 0; i < MAX_PP_SYMS; i++)
  409.         if (pp_sym[i] && ci_strcmp(pp_sym[i], sym) == 0)
  410.             return i;
  411.  
  412.     return PP_SYM_UNDEF;
  413. }
  414.  
  415.  
  416. /*
  417.  * do_ifdef - return TRUE if 'expr' is true; FALSE if not; EXPR_ERR if invalid
  418.  */
  419. #ifdef PROTOS
  420. int do_ifdef(char *expr)
  421. #else
  422. int do_ifdef(expr)
  423.     char *expr;
  424. #endif
  425. {
  426.     return parse_expr(expr);
  427. }
  428.  
  429.  
  430. /*
  431.  * do_ifndef - return FALSE if 'expr' is true; TRUE if not; EXPR_ERR if invalid
  432.  */
  433. #ifdef PROTOS
  434. int do_ifndef(char *expr)
  435. #else
  436. int do_ifndef(expr)
  437.     char *expr;
  438. #endif
  439. {
  440.     int val;
  441.  
  442.     return (val = parse_expr(expr)) == EXPR_ERR ? EXPR_ERR : ! val;
  443. }
  444.  
  445.  
  446. /*
  447.  * do_define - enter 'sym' into symbol table; if 'sym' NULL, clear symbol table.
  448.  * Always returns 0 (for compatibility with other dispatch functions).
  449.  */
  450. #ifdef PROTOS
  451. int do_define(char *sym)
  452. #else
  453. int do_define(sym)
  454.     char *sym;
  455. #endif
  456. {
  457.     int i;
  458.  
  459.     if (! sym) {        /* null argument - clear all definitions */
  460.         clear_syms();
  461.         return 0;
  462.     }
  463.  
  464.     if (do_ifdef(sym))    /* already defined? */
  465.         return 0;
  466.  
  467.     for (i = 0; i < MAX_PP_SYMS; i++)    /* find room for it */
  468.         if (! pp_sym[i]) {
  469.             strcpy(pp_sym[i] = alloc(strlen(sym)+1), sym);
  470.             return 0;
  471.         }
  472.  
  473.     FPR(stderr, E_SYMFULL, progname, sym);
  474.     return 0;
  475. }
  476.  
  477.  
  478. /*
  479.  * do_undef - undefine 'sym' and free its space; no error if not defined.
  480.  * Always return 0 (for compatibility with other dispatch fcns).
  481.  */
  482. #ifdef PROTOS
  483. int do_undef(char *sym)
  484. #else
  485. int do_undef(sym)
  486.     char *sym;
  487. #endif
  488. {
  489.     int i;
  490.  
  491.     if (! sym) 
  492.         return 0;
  493.  
  494.     if ((i = find_sym(sym)) != PP_SYM_UNDEF) {
  495.         free(pp_sym[i]);
  496.         pp_sym[i] = NULL;
  497.     }
  498.  
  499.     return 0;
  500. }
  501.  
  502.  
  503. /*
  504.  * do_include - include specified file (optionally in "" or <>); always
  505.  * returns 0 (for compatibility with related functions returning int)
  506.  */
  507. #ifdef PROTOS
  508. int do_include(char *path,
  509.            char *name)
  510. #else
  511. int do_include(path, name)
  512.     char *path;        /* path to file */
  513.     char *name;        /* file name */
  514. #endif
  515. {
  516.     FILE *fp;
  517.     char *p, incfile[STRSIZ], tmpnam[STRSIZ];
  518.  
  519.     if (! name)        /* whoops, no date file */
  520.         return 0;
  521.  
  522.     /* copy name, stripping "" or <> */
  523.     strcpy(tmpnam, name + (*name == '"' || *name == '<'));
  524.     if ((p = P_LASTCHAR(tmpnam)) && *p == '"' || *p == '>')
  525.         *p = '\0';
  526.  
  527.     /* special hack - replace %y with last two digits of curr_year */
  528.     while ((p = strchr(tmpnam, '%')) != NULL && *(p+1) == 'y') {
  529.         *p++ = (curr_year / 10) % 10 + '0';
  530.         *p = curr_year % 10 + '0';
  531.     }
  532.  
  533.     if ((fp = fopen(mk_filespec(incfile, path, tmpnam), "r")) == NULL) {
  534.         FPR(stderr, E_FOPEN_ERR, progname, incfile);
  535.         exit(EXIT_FAILURE);
  536.     }
  537.  
  538.     read_datefile(fp, incfile);    /* recursive call */
  539.     fclose(fp);
  540.  
  541.     return 0;
  542. }
  543.  
  544.  
  545.  
  546. /*
  547.  * Dispatch functions for wildcard matching
  548.  */
  549.  
  550.  
  551. /*
  552.  * is_anyday - dummy function which always returns TRUE
  553.  */
  554. #ifdef PROTOS
  555. int is_anyday(int mm,
  556.           int dd,
  557.           int yy)
  558. #else
  559. int is_anyday(mm, dd, yy)
  560.     int mm;
  561.     int dd;
  562.     int yy;
  563. #endif
  564. {
  565.     return TRUE;
  566. }
  567.  
  568.  
  569. /*
  570.  * is_weekday - determine whether or not mm/dd/yy is a weekday (i.e., the
  571.  * day of the week normally prints in black)
  572.  */
  573. #ifdef PROTOS
  574. int is_weekday(int mm,
  575.            int dd,
  576.            int yy)
  577. #else
  578. int is_weekday(mm, dd, yy)
  579.     int mm;
  580.     int dd;
  581.     int yy;
  582. #endif
  583. {
  584.     return day_color[calc_weekday(mm, dd, yy)] == BLACK;
  585. }
  586.  
  587.  
  588. /*
  589.  * is_workday - determine whether or not mm/dd/yy is a workday (i.e., the
  590.  * day of the week normally prints in black and the date is not a holiday)
  591.  */
  592. #ifdef PROTOS
  593. int is_workday(int mm,
  594.            int dd,
  595.            int yy)
  596. #else
  597. int is_workday(mm, dd, yy)
  598.     int mm;
  599.     int dd;
  600.     int yy;
  601. #endif
  602. {
  603.     return is_weekday(mm, dd, yy) && ! is_holiday(mm, dd, yy);
  604. }
  605.  
  606.  
  607. /*
  608.  * is_holiday - determine whether or not mm/dd/yy is a holiday
  609.  */
  610. #ifdef PROTOS
  611. int is_holiday(int mm,
  612.            int dd,
  613.            int yy)
  614. #else
  615. int is_holiday(mm, dd, yy)
  616.     int mm;
  617.     int dd;
  618.     int yy;
  619. #endif
  620. {
  621.     year_info *py;
  622.     month_info *pm;
  623.  
  624.     pm = (py = find_year(yy, FALSE)) ? py->month[mm-1] : NULL;
  625.     return pm ? (pm->holidays & (1L << (dd-1))) != 0 : FALSE;
  626. }
  627.  
  628.  
  629. /*
  630.  * not_XXXXX - converses of is_XXXXX above
  631.  */
  632. #ifdef PROTOS
  633. int not_weekday(int mm,
  634.         int dd,
  635.         int yy)
  636. #else
  637. int not_weekday(mm, dd, yy)
  638.     int mm;
  639.     int dd;
  640.     int yy;
  641. #endif
  642. {
  643.     return !is_weekday(mm, dd, yy);
  644. }
  645.  
  646.  
  647. #ifdef PROTOS
  648. int not_workday(int mm,
  649.         int dd,
  650.         int yy)
  651. #else
  652. int not_workday(mm, dd, yy)
  653.     int mm;
  654.     int dd;
  655.     int yy;
  656. #endif
  657. {
  658.     return !is_workday(mm, dd, yy);
  659. }
  660.  
  661.  
  662. #ifdef PROTOS
  663. int not_holiday(int mm,
  664.         int dd,
  665.         int yy)
  666. #else
  667. int not_holiday(mm, dd, yy)
  668.     int mm;
  669.     int dd;
  670.     int yy;
  671. #endif
  672. {
  673.     return !is_holiday(mm, dd, yy);
  674. }
  675.  
  676.  
  677. /*
  678.  * is_newmoon - determine whether or not mm/dd/yy is the date of a new moon
  679.  */
  680. #ifdef PROTOS
  681. int is_newmoon(int mm,
  682.            int dd,
  683.            int yy)
  684. #else
  685. int is_newmoon(mm, dd, yy)
  686.     int mm;
  687.     int dd;
  688.     int yy;
  689. #endif
  690. {
  691.     int quarter;
  692.  
  693.     (void) find_phase(mm, dd, yy, &quarter);
  694.     return quarter == MOON_NM;
  695. }
  696.  
  697.  
  698. /*
  699.  * is_firstq - determine whether or not mm/dd/yy is the date of a first quarter
  700.  */
  701. #ifdef PROTOS
  702. int is_firstq(int mm,
  703.           int dd,
  704.           int yy)
  705. #else
  706. int is_firstq(mm, dd, yy)
  707.     int mm;
  708.     int dd;
  709.     int yy;
  710. #endif
  711. {
  712.     int quarter;
  713.  
  714.     (void) find_phase(mm, dd, yy, &quarter);
  715.     return quarter == MOON_1Q;
  716. }
  717.  
  718.  
  719. /*
  720.  * is_fullmoon - determine whether or not mm/dd/yy is the date of a full moon
  721.  */
  722. #ifdef PROTOS
  723. int is_fullmoon(int mm,
  724.             int dd,
  725.             int yy)
  726. #else
  727. int is_fullmoon(mm, dd, yy)
  728.     int mm;
  729.     int dd;
  730.     int yy;
  731. #endif
  732. {
  733.     int quarter;
  734.  
  735.     (void) find_phase(mm, dd, yy, &quarter);
  736.     return quarter == MOON_FM;
  737. }
  738.  
  739.  
  740. /*
  741.  * is_lastq - determine whether or not mm/dd/yy is the date of a last quarter
  742.  */
  743. #ifdef PROTOS
  744. int is_lastq(int mm,
  745.          int dd,
  746.          int yy)
  747. #else
  748. int is_lastq(mm, dd, yy)
  749.     int mm;
  750.     int dd;
  751.     int yy;
  752. #endif
  753. {
  754.     int quarter;
  755.  
  756.     (void) find_phase(mm, dd, yy, &quarter);
  757.     return quarter == MOON_3Q;
  758. }
  759.  
  760.  
  761.  
  762. /*
  763.  * Keyword classification routines
  764.  */
  765.  
  766. /*
  767.  * get_month - convert alpha (or, optionally, numeric) string to month; return 
  768.  * 1..12 if valid, NOT_MONTH if not, ALL_MONTHS if "all", ENTIRE_YEAR if "year"
  769.  */
  770. #ifdef PROTOS
  771. int get_month(char *cp,
  772.           int numeric_ok,
  773.           int year_ok)
  774. #else
  775. int get_month(cp, numeric_ok, year_ok)
  776.     char *cp;        /* string to convert */
  777.     int numeric_ok;        /* accept numeric string ? */
  778.     int year_ok;        /* accept "year"? */
  779. #endif
  780. {
  781.     int mm;
  782.  
  783.     if (! cp)
  784.         return NOT_MONTH;
  785.  
  786.     if (get_keywd(cp) == DT_ALL)
  787.         return ALL_MONTHS;
  788.  
  789.     if (year_ok && get_keywd(cp) == DT_YEAR)
  790.         return ENTIRE_YEAR;
  791.  
  792.     if (numeric_ok && isdigit(*cp))
  793.         mm = atoi(cp);
  794.     else
  795.         for (mm = JAN;
  796.              mm <= DEC && ci_strncmp(cp, months[mm-1], MIN_MONTH_LEN);
  797.              mm++)
  798.             ;
  799.  
  800.     return mm >= JAN && mm <= DEC ? mm : NOT_MONTH;
  801. }
  802.  
  803.  
  804. /*
  805.  * get_weekday - look up string in weekday list; return 0-6 if valid,
  806.  * NOT_WEEKDAY if not.  If wild_ok flag is set, accept "day", "weekday",
  807.  * "workday", "holiday", or moon phase and return appropriate value.
  808.  */
  809. #ifdef PROTOS
  810. int get_weekday(char *cp,
  811.         int wild_ok)
  812. #else
  813. int get_weekday(cp, wild_ok)
  814.     char *cp;
  815.     int wild_ok;
  816. #endif
  817. {
  818.     int w;
  819.  
  820.     if (!cp)
  821.         return NOT_WEEKDAY;
  822.  
  823.     if (wild_ok) {        /* try wildcards first */
  824.         for (w = WILD_FIRST_WKD; w <= WILD_LAST_WKD; w++)
  825.             if (ci_strncmp(cp, days[w], strlen(days[w])) == 0)
  826.                 return w;
  827.         if ((w = get_phase(cp)) != MOON_OTHER)
  828.             return w + WILD_FIRST_MOON;
  829.     }
  830.  
  831.     for (w = SUN; w <= SAT; w++)
  832.         if (ci_strncmp(cp, days[w], MIN_DAY_LEN) == 0)
  833.             return w;
  834.  
  835.     return NOT_WEEKDAY;
  836. }
  837.  
  838.  
  839. /*
  840.  * get_keywd - look up string in misc. keyword list; return keyword code
  841.  * if valid, DT_OTHER if not
  842.  */
  843. #ifdef PROTOS
  844. int get_keywd(char *cp)
  845. #else
  846. int get_keywd(cp)
  847.     char *cp;
  848. #endif
  849. {
  850.     KWD *k;
  851.  
  852.     if (!cp)
  853.         return DT_OTHER;
  854.  
  855.     for (k = keywds;
  856.          k->name && ci_strncmp(cp, k->name, strlen(k->name));
  857.          k++)
  858.         ;
  859.  
  860.     return k->code;
  861. }
  862.  
  863.  
  864. /*
  865.  * get_ordinal - look up string in ordinal list; return ordinal code (and
  866.  * fill in ordinal value) if valid, return ORD_OTHER if not
  867.  */
  868. #ifdef PROTOS
  869. int get_ordinal(char *cp,
  870.         int *pval)
  871. #else
  872. int get_ordinal(cp, pval)
  873.     char *cp;
  874.     int *pval;
  875. #endif
  876. {
  877.     KWD_O *o;
  878.     int val;
  879.     char **psuf;
  880.  
  881.     if (!cp)
  882.         return ORD_OTHER;
  883.  
  884.     if (isdigit(*cp) || *cp == '-') {        /* numeric? */
  885.         if ((val = atoi(cp)) == 0)
  886.             return ORD_OTHER;
  887.  
  888.         if (*cp == '-')                /* skip over number */
  889.             cp++;
  890.         cp += strspn(cp, DIGITS);
  891.  
  892.         for (psuf = ord_suffix; *psuf; psuf++)    /* find suffix */
  893.             if (ci_strcmp(cp, *psuf) == 0) {
  894.                 *pval = val;
  895.                 return val < 0 ? ORD_NEGNUM : ORD_POSNUM;
  896.             }
  897.  
  898.         return ORD_OTHER;
  899.     }
  900.  
  901.     /* look for word in ordinals list */
  902.  
  903.     for (o = ordinals; o->name && ci_strncmp(cp, o->name, MIN_ORD_LEN); o++)
  904.         ;
  905.  
  906.     *pval = o->value;
  907.     return o->code;
  908. }
  909.  
  910.  
  911. /*
  912.  * get_phase - convert moon phase string to appropriate value
  913.  */
  914. #ifdef PROTOS
  915. int get_phase(char *cp)
  916. #else
  917. int get_phase(cp)
  918.     char *cp;
  919. #endif
  920. {
  921.     KWD *p;
  922.  
  923.     if (!cp)
  924.         return MOON_OTHER;
  925.  
  926.     for (p = phases; p->name && ci_strcmp(cp, p->name); p++)
  927.         ;
  928.  
  929.     return p->code;
  930. }
  931.  
  932.  
  933. /*
  934.  * get_prep - look up string in preposition list; return preposition code if 
  935.  * valid, PR_OTHER if not
  936.  */
  937. #ifdef PROTOS
  938. int get_prep(char *cp)
  939. #else
  940. int get_prep(cp)
  941.     char *cp;
  942. #endif
  943. {
  944.     KWD *p;
  945.  
  946.     if (!cp)
  947.         return PR_OTHER;
  948.  
  949.     for (p = preps; p->name && ci_strncmp(cp, p->name, MIN_PREP_LEN); p++)
  950.         ;
  951.  
  952.     return p->code;
  953. }
  954.  
  955.  
  956. /*
  957.  * get_token - look up 'token' in list of preprocessor tokens; return its
  958.  * index if found, PP_OTHER if not
  959.  */
  960. #ifdef PROTOS
  961. int get_token(char *token)
  962. #else
  963. int get_token(token)
  964.     char *token;
  965. #endif
  966. {
  967.     KWD_F *p;
  968.  
  969.     for (p = pp_info;
  970.              p->name && ci_strncmp(token, p->name, MIN_PPTOK_LEN);
  971.          p++)
  972.         ;
  973.  
  974.     return p->code;
  975. }
  976.  
  977.  
  978. /*
  979.  * date_type - examine token and return date type code; if DT_MONTH, DT_ORDINAL,
  980.  * or DT_WEEKDAY, fill in appropriate code (and value if DT_ORDINAL)
  981.  */
  982. #ifdef PROTOS
  983. int date_type(char *cp,
  984.           int *pn,
  985.           int *pv)
  986. #else
  987. int date_type(cp, pn, pv)
  988.     char *cp;    /* pointer to start of token  */
  989.     int *pn;    /* token type code (returned) */
  990.     int *pv;    /* ordinal value (returned)   */
  991. #endif
  992. {
  993.     int n, v;
  994.  
  995.     /* look for weekdays first, to catch wildcards "1q", "3q", etc. */
  996.     if ((n = get_weekday(cp, TRUE)) != NOT_WEEKDAY)    /* weekday name? */
  997.         return (*pn = n, DT_WEEKDAY);
  998.  
  999.     if ((n = get_ordinal(cp, &v)) != ORD_OTHER)    /* ordinal? */
  1000.         return (*pn = n, *pv = v, DT_ORDINAL);
  1001.  
  1002.     if (isdigit(*cp))                /* other digit? */
  1003.         return IS_NUMERIC(cp) ? DT_EURDATE : DT_DATE;
  1004.  
  1005.     /* "all" can be either a keyword or a month wildcard - look for
  1006.        the former usage first */
  1007.  
  1008.     if ((n = get_keywd(cp)) != DT_OTHER)
  1009.         return n;
  1010.  
  1011.     if ((n = get_month(cp, FALSE, FALSE)) != NOT_MONTH)  /* month name? */
  1012.         return (*pn = n, DT_MONTH);
  1013.  
  1014.     return DT_OTHER;        /* unrecognized keyword - give up */
  1015.  
  1016. }
  1017.  
  1018.  
  1019.  
  1020. /*
  1021.  * Routines for entering data in the data structure (described in pcaldefs.h)
  1022.  */
  1023.  
  1024.  
  1025. /*
  1026.  * find_year - find record in year list; optionally create if not present 
  1027.  */
  1028. #ifdef PROTOS
  1029. year_info *find_year(int year,
  1030.              int insert)
  1031. #else
  1032. year_info *find_year(year, insert)    /* find record in year list */
  1033.     int year;
  1034.     int insert;            /* insert if missing */
  1035. #endif
  1036. {
  1037.     year_info *pyear, *plast, *p;
  1038.  
  1039.     for (plast = NULL, pyear = head;        /* search linked list */
  1040.          pyear && pyear->year < year;
  1041.          plast = pyear, pyear = pyear->next)
  1042.         ;
  1043.  
  1044.     if (pyear && pyear->year == year)        /* found - return it */
  1045.         return pyear;
  1046.  
  1047.     if (insert) {        /* not found - insert it if requested */
  1048.         p = (year_info *) alloc((int) sizeof(year_info));    /* create new record */
  1049.         p->year = year;
  1050.  
  1051.         p->next = pyear;                /* link it in */
  1052.         return *(plast ? &plast->next : &head) = p;
  1053.     }
  1054.     else
  1055.         return NULL;
  1056. }
  1057.  
  1058.  
  1059. /*
  1060.  * enter_day_info - enter text for specified day; avoid entering duplicates.
  1061.  * Returns PARSE_INVDATE if date invalid, PARSE_OK if OK; if symbol FEB_29_OK
  1062.  * is non-zero (cf. pcaldefs.h), will silently ignore Feb 29 of common year.
  1063.  */
  1064. #ifdef PROTOS
  1065. int enter_day_info(int m,
  1066.            int d,
  1067.            int y,
  1068.            int text_type,
  1069.            char **pword)
  1070. #else
  1071. int enter_day_info(m, d, y, text_type, pword)    /* fill in information for given day */
  1072.     int m, d, y;
  1073.     int text_type;
  1074.     char **pword;
  1075. #endif
  1076. {
  1077.     static year_info *pyear;
  1078.     static int prev_year = 0;
  1079.     month_info *pmonth;
  1080.     day_info *pday, *plast;
  1081.     int is_holiday = text_type == HOLIDAY_TEXT;
  1082.     char text[LINSIZ];
  1083.  
  1084.     if (! is_valid(m, d >= FIRST_NOTE_DAY && text_type == NOTE_TEXT ? 1 : d, y))
  1085.         return (m == FEB && d == 29 && FEB_29_OK) ? PARSE_OK : PARSE_INVDATE;
  1086.  
  1087.     if (y != prev_year)        /* avoid unnecessary year lookup */
  1088.         pyear = find_year(y, 1);
  1089.  
  1090.     --m, --d;            /* adjust for use as subscripts */
  1091.  
  1092.     if ((pmonth = pyear->month[m]) == NULL)    /* find/create month record */
  1093.         pyear->month[m] = pmonth = (month_info *) alloc((int) sizeof(month_info));
  1094.  
  1095.     if (is_holiday)
  1096.         pmonth->holidays |= (1L << d);
  1097.  
  1098.     /* insert text for day at end of list (preserving the order of entry
  1099.      * for multiple lines on same day); eliminate those differing only
  1100.      * in spacing and capitalization from existing entries
  1101.          */
  1102.  
  1103.     copy_text(text, pword);    /* consolidate text from lbuf into text */
  1104.  
  1105.     if (DEBUG(DEBUG_DATES)) {
  1106.         char *p;
  1107.         FPR(stderr, "%02d/%02d/%d%c '", m+1, d+1, y,
  1108.             is_holiday ? '*' : ' ');
  1109.         for (p = text; *p; p++)
  1110.             FPR(stderr, isprint(*p) ? "%c" : "\\%03o", *p & 0377);
  1111.         FPR(stderr, "'\n");
  1112.     }
  1113.  
  1114.     if (*text) {
  1115.         for (plast = NULL, pday = pmonth->day[d];
  1116.              pday;
  1117.              plast = pday, pday = pday->next)
  1118.             if (ci_strcmp(pday->text, text) == 0) {
  1119.                 pday->is_holiday |= is_holiday;
  1120.                 return PARSE_OK;
  1121.             }
  1122.  
  1123.         /* unique - add to end of list */
  1124.  
  1125.         pday = (day_info *) alloc(sizeof(day_info));
  1126.         pday->is_holiday = is_holiday;
  1127.         strcpy(pday->text = (char *) alloc(strlen(text)+1), text);
  1128.         pday->next = NULL;
  1129.         *(plast ? &plast->next : &pmonth->day[d]) = pday;
  1130.     }
  1131.  
  1132.     return PARSE_OK;
  1133. }
  1134.  
  1135.  
  1136.  
  1137. /*
  1138.  * Date parsing routines:
  1139.  */
  1140.  
  1141.  
  1142. /*
  1143.  * parse_ord - parse an ordinal date spec (e.g. "first Monday in September",
  1144.  * "every Sunday in October", "last workday in all"); return PARSE_OK if line
  1145.  * syntax valid, PARSE_INVLINE if not.  Write all matching dates (if any) to
  1146.  * global array dates[]; terminate date list with null entry.
  1147.  */
  1148. #ifdef PROTOS
  1149. int parse_ord(int ord,
  1150.           int val,
  1151.           char **pword)
  1152. #else
  1153. int parse_ord(ord, val, pword)
  1154.     int ord;        /* valid ordinal code - from get_ordinal() */
  1155.     int val;        /* ordinal value - also from get_ordinal() */
  1156.     char **pword;        /* pointer to word after ordinal */
  1157. #endif
  1158. {
  1159.     int wkd, mon, mm, dd, len, (*pfcn)(), doit;
  1160.     int val_first, val_last, val_incr, mon_first, mon_last;
  1161.     DATE *pdate, date;
  1162.  
  1163.     if ((wkd = get_weekday(*pword, TRUE)) == NOT_WEEKDAY ||    /* weekday */
  1164.         *++pword == NULL ||                    /* any word */
  1165.         (mon = get_month(*++pword, FALSE, TRUE)) == NOT_MONTH) /* month */
  1166.         return PARSE_INVLINE;
  1167.  
  1168.     /* set up loop boundaries for month loop */
  1169.     mon_first = mon == ALL_MONTHS || mon == ENTIRE_YEAR ? JAN : mon;
  1170.     mon_last  = mon == ALL_MONTHS || mon == ENTIRE_YEAR ? DEC : mon;
  1171.  
  1172.     pdate = dates;            /* start of date array */
  1173.  
  1174.     /* special case of "all|odd|even <wildcard> in <month>|all|year" */
  1175.  
  1176.     if ((ord == ORD_ALL || ord == ORD_EVEN || ord == ORD_ODD) &&
  1177.         IS_WILD(wkd)) {
  1178.         pfcn = pdatefcn[wkd - WILD_FIRST];
  1179.         doit = ord != ORD_EVEN;
  1180.         for (mm = mon_first; mm <= mon_last; mm++) {
  1181.             len = LENGTH_OF(mm, curr_year);
  1182.             if (mon != ENTIRE_YEAR)
  1183.                 doit = ord != ORD_EVEN;
  1184.             for (dd = 1; dd <= len; dd++)
  1185.                 if ((*pfcn)(mm, dd, curr_year)) {
  1186.                     if (doit)
  1187.                         ADD_DATE(mm, dd, curr_year);
  1188.                     if (ord != ORD_ALL)
  1189.                         doit = ! doit;
  1190.                 }
  1191.         }
  1192.     }
  1193.  
  1194.     /* special case of "odd|even <weekday> in year" */
  1195.  
  1196.     else if ((ord == ORD_EVEN || ord == ORD_ODD) && mon == ENTIRE_YEAR) {
  1197.         date.mm = JAN;            /* starting date */
  1198.         date.dd = calc_day(ord == ORD_EVEN ? 2 : 1, wkd, JAN);
  1199.         date.yy = curr_year;
  1200.         do {                /* alternates throughout year */
  1201.             ADD_DATE(date.mm, date.dd, date.yy);
  1202.             date.dd += 14;
  1203.             normalize(&date);
  1204.         } while (date.yy == curr_year);
  1205.     }
  1206.  
  1207.     /* special case of "<ordinal>|last <weekday>|<wildcard> in year" */
  1208.  
  1209.     else if ((ord == ORD_NEGNUM || ord == ORD_POSNUM) &&
  1210.              mon == ENTIRE_YEAR) {
  1211.         if (calc_year_day(val, wkd, &date))
  1212.             ADD_DATE(date.mm, date.dd, date.yy);
  1213.     }
  1214.  
  1215.     /* all other combinations of ordinal and day */
  1216.  
  1217.     else {
  1218.         /* set up loop boundaries for "wildcard" ordinals */
  1219.  
  1220.         val_first = ord == ORD_ALL || ord == ORD_ODD ? 1 :
  1221.                 ord == ORD_EVEN ? 2 : val;
  1222.         val_last  = ord == ORD_ALL || ord == ORD_ODD ? 5 :
  1223.                 ord == ORD_EVEN ? 4 : val;
  1224.         val_incr  = ord == ORD_ODD || ord == ORD_EVEN ? 2 : 1;
  1225.  
  1226.         for (mm = mon_first; mm <= mon_last; mm++)
  1227.             for (val = val_first; val <= val_last; val += val_incr)
  1228.                 if ((dd = calc_day(val, wkd, mm)) != 0)
  1229.                     ADD_DATE(mm, dd, curr_year);
  1230.     }
  1231.  
  1232.     TERM_DATES;        /* terminate array with null entry */
  1233.     return PARSE_OK;
  1234. }
  1235.  
  1236.  
  1237. /*
  1238.  * parse_rel - parse a relative date spec (e.g. "Friday after fourth Thursday
  1239.  * in November", "Saturday after first Friday in all"; return PARSE_OK if
  1240.  * line syntax valid, PARSE_INVLINE if not.  Transform all dates that match
  1241.  * the base date to the appropriate day, month, and year.
  1242.  *
  1243.  * This calls parse_date() recursively in order to handle cases such as
  1244.  * "Friday after Tuesday before last day in all".
  1245.  */
  1246. #ifdef PROTOS
  1247. int parse_rel(int wkd,
  1248.           char **pword,
  1249.           int *ptype,
  1250.           char ***pptext)
  1251. #else
  1252. int parse_rel(wkd, pword, ptype, pptext)
  1253.     int wkd;        /* valid weekday code - from get_weekday() */
  1254.     char **pword;        /* pointer to word after weekday */
  1255.     int *ptype;        /* return text type (holiday/non-holiday) */
  1256.     char ***pptext;        /* return pointer to first word of text */
  1257. #endif
  1258. {
  1259.     int prep, rtn, base_wkd, incr, (*pfcn)();
  1260.     DATE *pd;
  1261.  
  1262.     /* we have the weekday - now look for the preposition */
  1263.     if ((prep = get_prep(*pword++)) == PR_OTHER)
  1264.         return PARSE_INVLINE;
  1265.  
  1266.     /* get the base date */
  1267.     if ((rtn = parse_date(pword, ptype, pptext)) != PARSE_OK)
  1268.         return rtn;
  1269.  
  1270.     /* transform date array in place - note that the relative date may
  1271.        not be in the same month or even year */
  1272.  
  1273.     if (IS_WILD(wkd)) {        /* wildcard for weekday name? */
  1274.         pfcn = pdatefcn[wkd - WILD_FIRST];
  1275.         incr = prep == PR_BEFORE || prep == PR_ON_BEFORE ? -1 : 1;
  1276.  
  1277.         for (pd = dates; pd->mm; pd++) {
  1278.             /* search for nearest matching date */
  1279.  
  1280.             if (prep == PR_BEFORE || prep == PR_AFTER) {
  1281.                 pd->dd += incr;
  1282.                 normalize(pd);
  1283.             }
  1284.             /* If NEAREST_INCR (cf. pcaldefs.h) is 1, Pcal will
  1285.              * disambiguate "nearest" in favor of the later date;
  1286.              * if -1, in favor of the earlier.  "incr" will take
  1287.              * the values 1, -2, 3, -4, ... or -1, 2, -3, 4 ...
  1288.              * respectively.
  1289.              */
  1290.             if (prep == PR_NEAREST)
  1291.                 incr = NEAREST_INCR;
  1292.             while (!(*pfcn)(pd->mm, pd->dd, pd->yy)) {
  1293.                 pd->dd += incr;
  1294.                 normalize(pd);
  1295.                 if (prep == PR_NEAREST)
  1296.                     incr -= (incr > 0) ? (2 * incr + 1) :
  1297.                                  (2 * incr - 1);
  1298.             }
  1299.         }
  1300.  
  1301.     } else  {            /* explicit weekday name */
  1302.         for (pd = dates; pd->mm; pd++) {
  1303.             /* calculate nearest matching date */
  1304.  
  1305.             base_wkd = calc_weekday(pd->mm, pd->dd, pd->yy);
  1306.  
  1307.             if (prep == PR_BEFORE ||
  1308.                 (prep == PR_ON_BEFORE && wkd != base_wkd))
  1309.                 pd->dd -= 7 - (wkd - base_wkd + 7) % 7;
  1310.  
  1311.             if (prep == PR_AFTER ||
  1312.                 (prep == PR_ON_AFTER && wkd != base_wkd))
  1313.                 pd->dd += (wkd - base_wkd + 6) % 7 + 1;
  1314.  
  1315.             normalize(pd);    /* adjust for month/year crossing */
  1316.         }
  1317.     }
  1318.  
  1319.     return PARSE_OK;
  1320. }
  1321.  
  1322.  
  1323. /*
  1324.  * parse_date - parse a date specification in any of its myriad forms; upon
  1325.  * return, array dates[] will contain a list of all the dates that matched,
  1326.  * terminated by a null entry.  Also fill in the date type (holiday/non-
  1327.  * holiday) code and the pointer to the first word of text.
  1328.  */
  1329. #ifdef PROTOS
  1330. int parse_date(char **pword,
  1331.            int *ptype,
  1332.            char ***pptext)
  1333. #else
  1334. int parse_date(pword, ptype, pptext)
  1335.     char **pword;        /* first word to parse */
  1336.     int *ptype;        /* return date type (holiday/non-holiday) */
  1337.     char ***pptext;        /* return pointer to first word of text */
  1338. #endif
  1339. {
  1340.     int mm, dd, yy;
  1341.     int token, n, v, ord, val, wkd, rtn;
  1342.     DATE *pdate;
  1343.     char *cp;
  1344.  
  1345.     pdate = dates;
  1346.  
  1347.     switch (token = date_type(*pword, &n, &v)) {
  1348.  
  1349.     case DT_MONTH:        /* <month> dd */
  1350.         if (date_style != USA_DATES)
  1351.             return PARSE_INVLINE;
  1352.  
  1353.         if ((cp = *++pword) == NULL)
  1354.             return PARSE_INVLINE;
  1355.  
  1356.         ADD_DATE(n, atoi(cp), curr_year);
  1357.         TERM_DATES;
  1358.  
  1359.         break;
  1360.  
  1361.     case DT_DATE:        /* mm/dd{/yy} | dd/mm{/yy} */
  1362.         n = split_date(*pword,
  1363.                    date_style == USA_DATES ? &mm : &dd,
  1364.                    date_style == USA_DATES ? &dd : &mm,
  1365.                    &yy);
  1366.  
  1367.         if (n > 2) {            /* year present? */
  1368.             if (yy < 100)
  1369.                 yy += CENTURY;
  1370.             curr_year = yy;        /* reset current year */
  1371.         }
  1372.  
  1373.         ADD_DATE(mm, dd, curr_year);
  1374.         TERM_DATES;
  1375.  
  1376.         break;
  1377.  
  1378.     case DT_EURDATE:    /* dd [ <month> | "all" ] */
  1379.         if (date_style != EUR_DATES)
  1380.             return PARSE_INVLINE;
  1381.  
  1382.         dd = atoi(*pword);
  1383.  
  1384.         if (get_keywd(*++pword) == DT_ALL) {
  1385.             for (mm = JAN; mm <= DEC; mm++)        /* wildcard */
  1386.                 ADD_DATE(mm, dd, curr_year);
  1387.         }
  1388.         else {                        /* one month */
  1389.             if ((mm = get_month(*pword, FALSE, FALSE)) == NOT_MONTH)
  1390.                 return PARSE_INVLINE;
  1391.  
  1392.             ADD_DATE(mm, dd, curr_year);
  1393.         }
  1394.  
  1395.         TERM_DATES;
  1396.         break;
  1397.  
  1398.     case DT_ALL:        /* "all" <weekday> "in" [ <month> | "all" ] */
  1399.                 /* or "all" <day>" */
  1400.  
  1401.         if ((cp = *(pword+1)) && (*(cp += strspn(cp, DIGITS)) == '\0' ||
  1402.             *cp == '*')) {
  1403.             dd = atoi(*++pword);        /* "all" <day> */
  1404.             for (mm = JAN; mm <= DEC; mm++)
  1405.                 ADD_DATE(mm, dd, curr_year);
  1406.             TERM_DATES;
  1407.             break;        /* leave switch */
  1408.         }
  1409.  
  1410.         n = ORD_ALL;    /* "all" <weekday> ... */
  1411.         v = 0;
  1412.          /* fall through */
  1413.  
  1414.     case DT_ORDINAL:    /* <ordinal> <weekday> in [ <month> | "all" ] */
  1415.         ord = n;
  1416.         val = v;
  1417.         if ((rtn = parse_ord(ord, val, pword + 1)) != PARSE_OK)
  1418.             return rtn;
  1419.  
  1420.         pword += 3;        /* last word of date */
  1421.         break;
  1422.  
  1423.     case DT_WEEKDAY:    /* <weekday> <prep> <date> */
  1424.         wkd = n;
  1425.         /* parse_rel() calls parse_date() recursively */
  1426.         return parse_rel(wkd, ++pword, ptype, pptext);
  1427.         break;
  1428.  
  1429.     default:
  1430.         return PARSE_INVLINE;
  1431.         break;
  1432.     }
  1433.  
  1434.     /* at this point, pword points to the last component of the date;
  1435.      * fill in type code and pointer to following word (start of text)
  1436.      */
  1437.     *ptype = LASTCHAR(*pword) == '*' ? HOLIDAY_TEXT : DAY_TEXT;
  1438.     *pptext = ++pword;
  1439.  
  1440.     return PARSE_OK;
  1441. }
  1442.  
  1443.  
  1444. /*
  1445.  * parse - parse non-preprocessor lines in date file
  1446.  *
  1447.  * This routine parses "year", "opt", "note", and date entries in the date
  1448.  * file.  It calls parse_date() to parse date entries (and enter the date(s)
  1449.  * matched in global array "dates"), and then calls enter_day_info() to
  1450.  * enter each date found (and its associated text) in the date file.
  1451.  *
  1452.  * N.B.: "inc" and other cpp-like lines are handled in read_datefile().
  1453.  *
  1454.  */
  1455. #ifdef PROTOS
  1456. int parse(char **pword,
  1457.       char *filename)
  1458. #else
  1459. int parse(pword, filename)
  1460.     char **pword;        /* pointer to first word to parse */
  1461.     char *filename;        /* name of file (for error messages) */
  1462. #endif
  1463. {
  1464.     register char *cp;
  1465.     char **ptext;
  1466.     int mm, yy;
  1467.     int text_type, n, v, rtn, match, valid;
  1468.     int token;
  1469.     DATE *pd;
  1470.  
  1471.     /*
  1472.      * Get first field and call date_type() to decode it
  1473.          */
  1474.     cp = *pword;
  1475.  
  1476.     switch (token = date_type(cp, &n, &v)) {
  1477.  
  1478.     case DT_YEAR:
  1479.         if ((cp = *++pword) != NULL && (yy = atoi(cp)) > 0) {
  1480.             if (yy < 100)
  1481.                 yy += CENTURY;
  1482.             curr_year = yy;
  1483.             return PARSE_OK;
  1484.         }
  1485.         return PARSE_INVLINE;    /* year missing or non-numeric */
  1486.         break;
  1487.  
  1488.     case DT_OPT:
  1489.          if (!get_args(pword, P_OPT, filename, FALSE)) {
  1490.             usage(stderr, FALSE);
  1491.             exit(EXIT_FAILURE);
  1492.         }
  1493.         return PARSE_OK;
  1494.         break;
  1495.  
  1496.     case DT_NOTE:
  1497.         /* look for optional "/<n>" following keyword */
  1498.         n = (cp = strrchr(cp, '/')) ? atoi(++cp) : 0;
  1499.  
  1500.         if ((mm = get_month(*++pword, TRUE, TRUE)) == NOT_MONTH)
  1501.             return PARSE_INVLINE;
  1502.  
  1503.         if (mm == ALL_MONTHS || mm == ENTIRE_YEAR) {   /* "note all"? */
  1504.             valid = FALSE;    /* is at least one note box valid? */
  1505.             for (mm = JAN; mm <= DEC; mm++)
  1506.                 valid |= enter_day_info(mm,
  1507.                         note_day(mm, n, curr_year),
  1508.                         curr_year, NOTE_TEXT,
  1509.                         pword+1) == PARSE_OK;
  1510.             return valid ? PARSE_OK : PARSE_NOMATCH;
  1511.         }
  1512.  
  1513.         return enter_day_info(mm, note_day(mm, n, curr_year),
  1514.                 curr_year, NOTE_TEXT, pword+1);
  1515.         break;
  1516.  
  1517.     case DT_OTHER:        /* unrecognized token */
  1518.         return PARSE_INVLINE;
  1519.         break;
  1520.  
  1521.     /* assume anything else is a date */
  1522.  
  1523.     default:
  1524.         if ((rtn = parse_date(pword, &text_type, &ptext)) == PARSE_OK) {
  1525.             match = FALSE;    /* is at least one date valid? */
  1526.             for (pd = dates; pd->mm; pd++)
  1527.                 match |= enter_day_info(pd->mm, pd->dd, pd->yy,
  1528.                            text_type, ptext) == PARSE_OK;
  1529.             rtn = match ? PARSE_OK : PARSE_NOMATCH;
  1530.         }
  1531.         return rtn;
  1532.         break;
  1533.  
  1534.     }
  1535. }
  1536.