home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 3 / 3077 < prev    next >
Encoding:
Internet Message Format  |  1991-03-15  |  42.8 KB

  1. From: jbr0@cbnews.att.com (joseph.a.brownlee)
  2. Newsgroups: alt.sources
  3. Subject: Pcal v4.0, part 5 of 5
  4. Message-ID: <1991Mar14.125242.9791@cbnews.att.com>
  5. Date: 14 Mar 91 12:52:42 GMT
  6.  
  7. #!/bin/sh
  8. # This is part 05 of a multipart archive
  9. # ============= protos.h ==============
  10. if test -f 'protos.h' -a X"$1" != X"-c"; then
  11.     echo 'x - skipping protos.h (File already exists)'
  12. else
  13. echo 'x - extracting protos.h (Text)'
  14. sed 's/^X//' << 'SHAR_EOF' > 'protos.h' &&
  15. /*
  16. X * protos.h - ANSI-style function prototypes for Pcal sources
  17. X *
  18. X * Revision history:
  19. X *
  20. X *    4.0    AWR    03/01/91    use <stdlib.h> where possible
  21. X *
  22. X *        AWR    02/19/91    adapted from Bill Hogsett's source
  23. X *
  24. X */
  25. X
  26. X
  27. /*
  28. X * Prototypes for functions defined in exprpars.c:
  29. X */
  30. int parse_expr(char *pbuf);
  31. X
  32. X
  33. /*
  34. X * Prototypes for functions defined in moonphas.c:
  35. X */
  36. double    calc_phase(int month, int day, int year);
  37. double    find_phase(int month, int day, int year, int *pquarter);
  38. char    *find_moonfile(int year);
  39. int    read_moonfile(int year);
  40. X
  41. X
  42. /*
  43. X * Prototypes for functions defined in pcal.c:
  44. X */
  45. FILE    *alt_fopen(char *fullpath, char *name, char *pathlist[], char *access);
  46. char    *color_msg(void);
  47. int    get_args(char **argv, int curr_pass, char *where);
  48. FLAG_USAGE *get_flag(char flag);
  49. int    main(int argc, char **argv);
  50. void    set_color(char *day, int col);
  51. void    usage(FILE *fp, int fullmsg);
  52. X
  53. X
  54. /*
  55. X * Prototypes for functions defined in pcalutil.c:
  56. X */
  57. char    *alloc(int size);
  58. int    calc_day(int ord, int wkd, int mm);
  59. int    calc_weekday(int mm, int dd, int yy);
  60. int    calc_year_day(int ord, int wkd, DATE *pdate);
  61. int    ci_strcmp(register char *s1, register char *s2);
  62. int    ci_strncmp(register char *s1, register char *s2, int n);
  63. void    copy_text(char *pbuf, char **ptext);
  64. int    getline(FILE *fp, int *pline);
  65. int    is_valid(register int m, register int d, register int y);
  66. int    loadwords(void);
  67. char    *mk_filespec(char *filespec, char *path, char *name);
  68. char    *mk_path(char *path, char *filespec);
  69. void    normalize(DATE *pd);
  70. int    split_date(char *pstr, int *pn1, int *pn2, int *pn3);
  71. char    *trnlog(char *logname);
  72. X
  73. X
  74. /*
  75. X * Prototypes for functions defined in readfile.c:
  76. X */
  77. void    cleanup(void);
  78. void    clear_syms(void);
  79. int    date_type(char *cp, int *pn, int *pv);
  80. int    do_define(char *sym);
  81. int    do_ifdef(char *expr);
  82. int    do_ifndef(char *expr);
  83. int    do_include(char *path, char *name);
  84. int    do_undef(char *sym);
  85. int    enter_day_info(int m, int d, int y, int text_type, char **pword);
  86. int    find_sym(char *sym);
  87. year_info *find_year(int year, int insert);
  88. int    get_keywd(char *cp);
  89. int    get_month(char *cp, int numeric_ok, int year_ok);
  90. int    get_ordinal(char *cp, int *pval);
  91. int    get_prep(char *cp);
  92. int    get_token(char *token);
  93. int    get_weekday(char *cp, int wild_ok);
  94. int    is_anyday(int mm, int dd, int yy);
  95. int    is_holiday(int mm, int dd, int yy);
  96. int    is_weekday(int mm, int dd, int yy);
  97. int    is_workday(int mm, int dd, int yy);
  98. int    not_holiday(int mm, int dd, int yy);
  99. int    not_weekday(int mm, int dd, int yy);
  100. int    not_workday(int mm, int dd, int yy);
  101. int    parse(char **pword, char *filename);
  102. int    parse_date(char **pword, int *ptype, char ***pptext);
  103. int    parse_ord(int ord, int val, char **pword);
  104. int    parse_rel(int wkd, char **pword, int *ptype, char ***pptext);
  105. void    read_datefile(FILE *fp, char *filename);
  106. X
  107. X
  108. /*
  109. X * Prototypes for functions defined in writefil.c:
  110. X */
  111. void    def_footstring(char *p, char c);
  112. void    find_daytext(int month, int year, int is_holiday);
  113. void    find_holidays(int month, int year);
  114. void    print_julian_info(int month, int year);
  115. void    print_month(int month, int year);
  116. void    print_moon_info(int month, int year);
  117. void    print_text(char *p);
  118. char    *print_word(char *p);
  119. void    write_psfile(int month, int year, int nmonths);
  120. X
  121. X
  122. /*
  123. X * Prototypes for miscellaneous library routines (if not already included
  124. X * via <stdlib.h> - cf. pcaldefs.h):
  125. X */
  126. #ifndef STDLIB
  127. extern int    atoi(char *);
  128. extern char    *calloc(unsigned int, unsigned int);
  129. extern char    *getenv(char *);
  130. #endif
  131. SHAR_EOF
  132. chmod 0666 protos.h ||
  133. echo 'restore of protos.h failed'
  134. Wc_c="`wc -c < 'protos.h'`"
  135. test 3428 -eq "$Wc_c" ||
  136.     echo 'protos.h: original size 3428, current size' "$Wc_c"
  137. fi
  138. # ============= readfile.c ==============
  139. if test -f 'readfile.c' -a X"$1" != X"-c"; then
  140.     echo 'x - skipping readfile.c (File already exists)'
  141. else
  142. echo 'x - extracting readfile.c (Text)'
  143. sed 's/^X//' << 'SHAR_EOF' > 'readfile.c' &&
  144. /*
  145. X * readfile.c - Pcal routines concerned with reading and parsing the datefile
  146. X *
  147. X * Contents:
  148. X *
  149. X *        cleanup
  150. X *        clear_syms
  151. X *        date_type
  152. X *        do_define
  153. X *        do_ifdef
  154. X *        do_ifndef
  155. X *        do_include
  156. X *        do_undef
  157. X *        enter_day_info
  158. X *        find_sym
  159. X *        find_year
  160. X *        get_keywd
  161. X *        get_month
  162. X *        get_ordinal
  163. X *        get_prep
  164. X *        get_token
  165. X *        get_weekday
  166. X *        is_anyday
  167. X *        is_holiday
  168. X *        is_weekday
  169. X *        is_workday
  170. X *        not_holiday
  171. X *        not_weekday
  172. X *        not_workday
  173. X *        parse
  174. X *        parse_date
  175. X *        parse_ord
  176. X *        parse_rel
  177. X *        read_datefile
  178. X *
  179. X * Revision history:
  180. X *
  181. X *    4.0    AWR    02/19/91    Support negative ordinals
  182. X *
  183. X *        AWR    02/06/91    Support expressions in "if{n}def"
  184. X *
  185. X *        AWR    02/04/91    Support "even" and "odd" ordinals
  186. X *                    and ordinals > 5th; support "year"
  187. X *
  188. X *        AWR    01/15/91    Extracted from pcal.c
  189. X *
  190. X */
  191. X
  192. /*
  193. X * Standard headers:
  194. X */
  195. X
  196. #include <stdio.h>
  197. #include <string.h>
  198. #include <ctype.h>
  199. X
  200. /*
  201. X * Pcal-specific definitions:
  202. X */
  203. X
  204. #include "pcaldefs.h"
  205. #include "pcalglob.h"
  206. #include "pcallang.h"
  207. X
  208. /*
  209. X * Macros:
  210. X */
  211. X
  212. /* status codes returned by parse(), enter_day_info() */
  213. #define PARSE_OK    0    /* successful date parse */
  214. #define PARSE_INVDATE    1    /* nonexistent date */
  215. #define PARSE_INVLINE    2    /* syntax error */
  216. X
  217. /* append date to list; terminate list */
  218. #define ADD_DATE(m, d, y)    pdate->mm = m, pdate->dd = d, pdate++->yy = y
  219. #define TERM_DATES        pdate->mm = pdate->dd = pdate->yy = 0
  220. X
  221. /*
  222. X * Globals:
  223. X */
  224. X
  225. static DATE dates[MAX_DATES+1];        /* array of date structures */
  226. static char *pp_sym[MAX_PP_SYMS];    /* preprocessor defined symbols */
  227. X
  228. X
  229. /*
  230. X * read_datefile - read and parse date file, handling preprocessor lines
  231. X *
  232. X * This is the main routine of this module.  It calls getline() to read each
  233. X * non-null line (stripped of leading blanks and trailing comments), loadwords()
  234. X * to "tokenize" it, and get_token() to classify it as a preprocessor directive
  235. X * or "other".  A switch{} statement takes the appropriate action for each
  236. X * token type; "other" lines are further classified by parse() (q.v.) which
  237. X * calls parse_date() (q.v.) to parse date entries and enter them in the data
  238. X * structure (as described in pcaldefs.h).
  239. X *
  240. X */
  241. #ifdef PROTOS
  242. void read_datefile(FILE *fp,
  243. X           char *filename)
  244. #else
  245. void read_datefile(fp, filename)
  246. X    FILE *fp;        /* file pointer (assumed open) */
  247. X    char *filename;        /* file name (for error messages) */
  248. #endif
  249. {
  250. X    static int file_level = 0;
  251. X    int if_level = 0;
  252. X    int restart_level = 0;
  253. X    int line = 0;
  254. X    int processing = TRUE;
  255. X
  256. X    int pptype, extra, ntokens, save_year, expr;
  257. X    int (*pfcn)();
  258. X    char *ptok;
  259. X    char **pword;
  260. X    char msg[STRSIZ], incpath[STRSIZ];
  261. X
  262. X    if (fp == NULL)                /* whoops, no date file */
  263. X        return;
  264. X
  265. X    if (++file_level > MAX_NESTING) {    /* nesting too deep? */
  266. X        ERR(E_NESTING);
  267. X        exit(EXIT_FAILURE);
  268. X    }
  269. X
  270. X    save_year = curr_year;            /* save default year */
  271. X
  272. X    /* read lines until EOF */
  273. X
  274. X    while (getline(fp, &line)) {
  275. X
  276. X        ntokens = loadwords();        /* split line into tokens */
  277. X        pword = words;            /* point to the first */
  278. X
  279. X        /* get token type and pointers to function and name */
  280. X
  281. X        pptype = get_token(*pword++);
  282. X        pfcn = pp_info[pptype].pfcn;
  283. X        ptok = pp_info[pptype].name;
  284. X
  285. X        switch (pptype) {
  286. X
  287. X        case PP_DEFINE:
  288. X        case PP_UNDEF:
  289. X            if (processing)
  290. X                (void) (*pfcn)(*pword);
  291. X            extra = ntokens > 2;
  292. X            break;
  293. X
  294. X        case PP_ELSE:
  295. X            if (if_level < 1) {
  296. X                ERR(E_ELSE_ERR);
  297. X                break;
  298. X            }
  299. X
  300. X            if (processing) {
  301. X                processing = FALSE;    /* disable processing */
  302. X                restart_level = if_level;
  303. X            }
  304. X            else
  305. X            if (if_level == restart_level) {
  306. X                processing = TRUE;    /* re-enable processing */
  307. X                restart_level = 0;
  308. X            }
  309. X            extra = ntokens > 1;
  310. X            break;
  311. X
  312. X        case PP_ENDIF:
  313. X            if (if_level < 1) {
  314. X                ERR(E_END_ERR);
  315. X                break;
  316. X            }
  317. X
  318. X            if (! processing && if_level == restart_level) {
  319. X                processing = TRUE;    /* re-enable processing */
  320. X                restart_level = 0;
  321. X            }
  322. X            if_level--;
  323. X            extra = ntokens > 1;
  324. X            break;
  325. X
  326. X        case PP_IFDEF:
  327. X        case PP_IFNDEF:
  328. X            if_level++;
  329. X            if (processing) {
  330. X                copy_text(lbuf, pword);    /* reconstruct string */
  331. X                if ((expr = (*pfcn)(lbuf)) == EXPR_ERR) {
  332. X                    ERR(E_EXPR_SYNTAX);
  333. X                    expr = FALSE;
  334. X                }
  335. X                if (! expr) {        /* disable processing */
  336. X                    processing = FALSE;
  337. X                    restart_level = if_level;
  338. X                }
  339. X            }
  340. X            extra = FALSE;
  341. X            break;
  342. X
  343. X        case PP_INCLUDE:
  344. X            if (processing)
  345. X                do_include(mk_path(incpath, filename), *pword);
  346. X            extra = ntokens > 2;
  347. X            break;
  348. X
  349. X        case PP_OTHER:    /* none of the above - parse as date */
  350. X            if (processing) {
  351. X                switch (parse(words, filename)) {
  352. X
  353. X                case PARSE_INVDATE:
  354. X                    ERR(E_INV_DATE);
  355. X                    break;
  356. X
  357. X                case PARSE_INVLINE:
  358. X                    ERR(E_INV_LINE);
  359. X                    break;
  360. X
  361. X                }
  362. X            }
  363. X            extra = FALSE;
  364. X            break;
  365. X
  366. X        } /* end switch */
  367. X
  368. X        if (extra) {        /* extraneous data? */
  369. X            sprintf(msg, E_GARBAGE, ptok);
  370. X            ERR(msg);
  371. X        }
  372. X
  373. X    } /* end while */
  374. X
  375. X    if (if_level > 0)
  376. X        FPR(stderr, E_UNT_IFDEF, progname, filename);
  377. X
  378. X    file_level--;
  379. X    curr_year = save_year;        /* restore default year */
  380. }
  381. X
  382. X
  383. /*
  384. X * Routines to free allocated data (symbol table and data structure) 
  385. X */
  386. X
  387. X
  388. /*
  389. X * clear_syms - clear and deallocate the symbol table
  390. X */
  391. #ifdef PROTOS
  392. void clear_syms(void)
  393. #else
  394. void clear_syms()
  395. #endif
  396. {
  397. X    int i;
  398. X
  399. X    for (i = 0; i < MAX_PP_SYMS; i++)
  400. X        if (pp_sym[i]) {
  401. X            free(pp_sym[i]);
  402. X            pp_sym[i] = NULL;
  403. X        }
  404. }
  405. X
  406. X
  407. /*
  408. X * cleanup - free all allocated data
  409. X */
  410. #ifdef PROTOS
  411. void cleanup(void)
  412. #else
  413. void cleanup()
  414. #endif
  415. {
  416. X    int i, j;
  417. X    year_info *py, *pny;
  418. X    month_info *pm;
  419. X    day_info *pd, *pnd;
  420. X
  421. X    for (py = head; py; py = pny) {        /* main data structure */
  422. X        pny = py->next;
  423. X        for (i = 0; i < 12; i++) {
  424. X            if ((pm = py->month[i]) == NULL)
  425. X                continue;
  426. X            for (j = 0; j < NOTE_DAY; j++)
  427. X                for (pd = pm->day[j]; pd; pd = pnd) {
  428. X                    pnd = pd->next;
  429. X                    free(pd->text);
  430. X                    free(pd);
  431. X                }
  432. X            free(pm);
  433. X        }
  434. X    free(py);
  435. X    }
  436. X
  437. X    clear_syms();                /* symbol table */
  438. X
  439. }
  440. X
  441. X
  442. /*
  443. X * Preprocessor token and symbol table routines
  444. X */
  445. X
  446. X
  447. /*
  448. X * find_sym - look up symbol; return symbol table index if found, PP_SYM_UNDEF
  449. X * if not found
  450. X */
  451. #ifdef PROTOS
  452. int find_sym(char *sym)
  453. #else
  454. int find_sym(sym)
  455. X    char *sym;
  456. #endif
  457. {
  458. X    int i;
  459. X
  460. X    if (!sym)
  461. X        return PP_SYM_UNDEF;
  462. X
  463. X    for (i = 0; i < MAX_PP_SYMS; i++)
  464. X        if (pp_sym[i] && ci_strcmp(pp_sym[i], sym) == 0)
  465. X            return i;
  466. X
  467. X    return PP_SYM_UNDEF;
  468. }
  469. X
  470. X
  471. /*
  472. X * do_ifdef - return TRUE if 'expr' is true; FALSE if not; EXPR_ERR if invalid
  473. X */
  474. #ifdef PROTOS
  475. int do_ifdef(char *expr)
  476. #else
  477. int do_ifdef(expr)
  478. X    char *expr;
  479. #endif
  480. {
  481. X    return parse_expr(expr);
  482. }
  483. X
  484. X
  485. /*
  486. X * do_ifndef - return FALSE if 'expr' is true; TRUE if not; EXPR_ERR if invalid
  487. X */
  488. #ifdef PROTOS
  489. int do_ifndef(char *expr)
  490. #else
  491. int do_ifndef(expr)
  492. X    char *expr;
  493. #endif
  494. {
  495. X    int val;
  496. X
  497. X    return (val = parse_expr(expr)) == EXPR_ERR ? EXPR_ERR : ! val;
  498. }
  499. X
  500. X
  501. /*
  502. X * do_define - enter 'sym' into symbol table; if 'sym' NULL, clear symbol table.
  503. X * Always returns 0 (for compatibility with other dispatch functions).
  504. X */
  505. #ifdef PROTOS
  506. int do_define(char *sym)
  507. #else
  508. int do_define(sym)
  509. X    char *sym;
  510. #endif
  511. {
  512. X    int i;
  513. X
  514. X    if (! sym) {        /* null argument - clear all definitions */
  515. X        clear_syms();
  516. X        return 0;
  517. X    }
  518. X
  519. X    if (do_ifdef(sym))    /* already defined? */
  520. X        return 0;
  521. X
  522. X    for (i = 0; i < MAX_PP_SYMS; i++)    /* find room for it */
  523. X        if (! pp_sym[i]) {
  524. X            strcpy(pp_sym[i] = alloc(strlen(sym)+1), sym);
  525. X            return 0;
  526. X        }
  527. X
  528. X    FPR(stderr, E_SYMFULL, progname, sym);
  529. X    return 0;
  530. }
  531. X
  532. X
  533. /*
  534. X * do_undef - undefine 'sym' and free its space; no error if not defined.
  535. X * Always return 0 (for compatibility with other dispatch fcns).
  536. X */
  537. #ifdef PROTOS
  538. int do_undef(char *sym)
  539. #else
  540. int do_undef(sym)
  541. X    char *sym;
  542. #endif
  543. {
  544. X    int i;
  545. X
  546. X    if (! sym) 
  547. X        return 0;
  548. X
  549. X    if ((i = find_sym(sym)) != PP_SYM_UNDEF) {
  550. X        free(pp_sym[i]);
  551. X        pp_sym[i] = NULL;
  552. X    }
  553. X
  554. X    return 0;
  555. }
  556. X
  557. X
  558. /*
  559. X * do_include - include specified file (optionally in "" or <>); always
  560. X * returns 0 (for compatibility with related functions returning int)
  561. X */
  562. #ifdef PROTOS
  563. int do_include(char *path,
  564. X           char *name)
  565. #else
  566. int do_include(path, name)
  567. X    char *path;        /* path to file */
  568. X    char *name;        /* file name */
  569. #endif
  570. {
  571. X    FILE *fp;
  572. X    char *p, incfile[STRSIZ], tmpnam[STRSIZ];
  573. X
  574. X    if (! name)        /* whoops, no date file */
  575. X        return 0;
  576. X
  577. X    /* copy name, stripping "" or <> */
  578. X    strcpy(tmpnam, name + (*name == '"' || *name == '<'));
  579. X    if ((p = P_LASTCHAR(tmpnam)) && *p == '"' || *p == '>')
  580. X        *p = '\0';
  581. X
  582. X    if ((fp = fopen(mk_filespec(incfile, path, tmpnam), "r")) == NULL) {
  583. X        FPR(stderr, E_FOPEN_ERR, progname, incfile);
  584. X        exit(EXIT_FAILURE);
  585. X    }
  586. X
  587. X    read_datefile(fp, incfile);    /* recursive call */
  588. X    fclose(fp);
  589. X
  590. X    return 0;
  591. }
  592. X
  593. X
  594. X
  595. /*
  596. X * Dispatch functions for wildcard matching
  597. X */
  598. X
  599. X
  600. /*
  601. X * is_anyday - dummy function which always returns TRUE
  602. X */
  603. #ifdef PROTOS
  604. int is_anyday(int mm,
  605. X          int dd,
  606. X          int yy)
  607. #else
  608. int is_anyday(mm, dd, yy)
  609. X    int mm;
  610. X    int dd;
  611. X    int yy;
  612. #endif
  613. {
  614. X    return TRUE;
  615. }
  616. X
  617. X
  618. /*
  619. X * is_weekday - determine whether or not mm/dd/yy is a weekday (i.e., the
  620. X * day of the week normally prints in black)
  621. X */
  622. #ifdef PROTOS
  623. int is_weekday(int mm,
  624. X           int dd,
  625. X           int yy)
  626. #else
  627. int is_weekday(mm, dd, yy)
  628. X    int mm;
  629. X    int dd;
  630. X    int yy;
  631. #endif
  632. {
  633. X    return day_color[calc_weekday(mm, dd, yy)] == BLACK;
  634. }
  635. X
  636. X
  637. /*
  638. X * is_workday - determine whether or not mm/dd/yy is a workday (i.e., the
  639. X * day of the week normally prints in black and the date is not a holiday)
  640. X */
  641. #ifdef PROTOS
  642. int is_workday(int mm,
  643. X           int dd,
  644. X           int yy)
  645. #else
  646. int is_workday(mm, dd, yy)
  647. X    int mm;
  648. X    int dd;
  649. X    int yy;
  650. #endif
  651. {
  652. X    return is_weekday(mm, dd, yy) && ! is_holiday(mm, dd, yy);
  653. }
  654. X
  655. X
  656. /*
  657. X * is_holiday - determine whether or not mm/dd/yy is a holiday
  658. X */
  659. #ifdef PROTOS
  660. int is_holiday(int mm,
  661. X           int dd,
  662. X           int yy)
  663. #else
  664. int is_holiday(mm, dd, yy)
  665. X    int mm;
  666. X    int dd;
  667. X    int yy;
  668. #endif
  669. {
  670. X    year_info *py;
  671. X    month_info *pm;
  672. X
  673. X    pm = (py = find_year(yy, FALSE)) ? py->month[mm-1] : NULL;
  674. X    return pm ? (pm->holidays & (1L << (dd-1))) != 0 : FALSE;
  675. }
  676. X
  677. X
  678. /*
  679. X * not_XXXXX - converses of is_XXXXX above
  680. X */
  681. #ifdef PROTOS
  682. int not_weekday(int mm,
  683. X        int dd,
  684. X        int yy)
  685. #else
  686. int not_weekday(mm, dd, yy)
  687. X    int mm;
  688. X    int dd;
  689. X    int yy;
  690. #endif
  691. {
  692. X    return !is_weekday(mm, dd, yy);
  693. }
  694. X
  695. X
  696. #ifdef PROTOS
  697. int not_workday(int mm,
  698. X        int dd,
  699. X        int yy)
  700. #else
  701. int not_workday(mm, dd, yy)
  702. X    int mm;
  703. X    int dd;
  704. X    int yy;
  705. #endif
  706. {
  707. X    return !is_workday(mm, dd, yy);
  708. }
  709. X
  710. X
  711. #ifdef PROTOS
  712. int not_holiday(int mm,
  713. X        int dd,
  714. X        int yy)
  715. #else
  716. int not_holiday(mm, dd, yy)
  717. X    int mm;
  718. X    int dd;
  719. X    int yy;
  720. #endif
  721. {
  722. X    return !is_holiday(mm, dd, yy);
  723. }
  724. X
  725. X
  726. X
  727. /*
  728. X * Keyword classification routines
  729. X */
  730. X
  731. /*
  732. X * get_month - convert alpha (or, optionally, numeric) string to month; return 
  733. X * 1..12 if valid, NOT_MONTH if not, ALL_MONTHS if "all", ENTIRE_YEAR if "year"
  734. X */
  735. #ifdef PROTOS
  736. int get_month(char *cp,
  737. X          int numeric_ok,
  738. X          int year_ok)
  739. #else
  740. int get_month(cp, numeric_ok, year_ok)
  741. X    char *cp;        /* string to convert */
  742. X    int numeric_ok;        /* accept numeric string ? */
  743. X    int year_ok;        /* accept "year"? */
  744. #endif
  745. {
  746. X    int mm;
  747. X
  748. X    if (! cp)
  749. X        return NOT_MONTH;
  750. X
  751. X    if (get_keywd(cp) == DT_ALL)
  752. X        return ALL_MONTHS;
  753. X
  754. X    if (year_ok && get_keywd(cp) == DT_YEAR)
  755. X        return ENTIRE_YEAR;
  756. X
  757. X    if (numeric_ok && isdigit(*cp))
  758. X        mm = atoi(cp);
  759. X    else
  760. X        for (mm = JAN;
  761. X             mm <= DEC && ci_strncmp(cp, months[mm-1], MIN_MONTH_LEN);
  762. X             mm++)
  763. X            ;
  764. X
  765. X    return mm >= JAN && mm <= DEC ? mm : NOT_MONTH;
  766. }
  767. X
  768. X
  769. /*
  770. X * get_weekday - look up string in weekday list; return 0-6 if valid,
  771. X * NOT_WEEKDAY if not.  If wild_ok flag is set, accept "day", "weekday",
  772. X * "workday", or "holiday" and return appropriate value.
  773. X */
  774. #ifdef PROTOS
  775. int get_weekday(char *cp,
  776. X        int wild_ok)
  777. #else
  778. int get_weekday(cp, wild_ok)
  779. X    char *cp;
  780. X    int wild_ok;
  781. #endif
  782. {
  783. X    int w;
  784. X
  785. X    if (!cp)
  786. X        return NOT_WEEKDAY;
  787. X
  788. X    if (wild_ok) {        /* try wildcards first */
  789. X        for (w = WILD_FIRST; w <= WILD_LAST; w++)
  790. X            if (ci_strncmp(cp, days[w], strlen(days[w])) == 0)
  791. X                return w;
  792. X    }
  793. X
  794. X    for (w = SUN; w <= SAT; w++)
  795. X        if (ci_strncmp(cp, days[w], MIN_DAY_LEN) == 0)
  796. X            return w;
  797. X
  798. X    return NOT_WEEKDAY;
  799. }
  800. X
  801. X
  802. /*
  803. X * get_keywd - look up string in misc. keyword list; return keyword code
  804. X * if valid, DT_OTHER if not
  805. X */
  806. #ifdef PROTOS
  807. int get_keywd(char *cp)
  808. #else
  809. int get_keywd(cp)
  810. X    char *cp;
  811. #endif
  812. {
  813. X    KWD *k;
  814. X
  815. X    if (!cp)
  816. X        return DT_OTHER;
  817. X
  818. X    for (k = keywds;
  819. X         k->name && ci_strncmp(cp, k->name, strlen(k->name));
  820. X         k++)
  821. X        ;
  822. X
  823. X    return k->code;
  824. }
  825. X
  826. X
  827. /*
  828. X * get_ordinal - look up string in ordinal list; return ordinal code (and
  829. X * fill in ordinal value) if valid, return ORD_OTHER if not
  830. X */
  831. #ifdef PROTOS
  832. int get_ordinal(char *cp,
  833. X        int *pval)
  834. #else
  835. int get_ordinal(cp, pval)
  836. X    char *cp;
  837. X    int *pval;
  838. #endif
  839. {
  840. X    KWD_O *o;
  841. X    int val;
  842. X    char **psuf;
  843. X
  844. X    if (!cp)
  845. X        return ORD_OTHER;
  846. X
  847. X    if (isdigit(*cp) || *cp == '-') {        /* numeric? */
  848. X        if ((val = atoi(cp)) == 0)
  849. X            return ORD_OTHER;
  850. X
  851. X        if (*cp == '-')                /* skip over number */
  852. X            cp++;
  853. X        cp += strspn(cp, DIGITS);
  854. X
  855. X        for (psuf = ord_suffix; *psuf; psuf++)    /* find suffix */
  856. X            if (ci_strcmp(cp, *psuf) == 0) {
  857. X                *pval = val;
  858. X                return val < 0 ? ORD_NEGNUM : ORD_POSNUM;
  859. X            }
  860. X
  861. X        return ORD_OTHER;
  862. X    }
  863. X
  864. X    /* look for word in ordinals list */
  865. X
  866. X    for (o = ordinals; o->name && ci_strncmp(cp, o->name, MIN_ORD_LEN); o++)
  867. X        ;
  868. X
  869. X    *pval = o->value;
  870. X    return o->code;
  871. }
  872. X
  873. X
  874. /*
  875. X * get_prep - look up string in preposition list; return preposition code if 
  876. X * valid, PR_OTHER if not
  877. X */
  878. #ifdef PROTOS
  879. int get_prep(char *cp)
  880. #else
  881. int get_prep(cp)
  882. X    char *cp;
  883. #endif
  884. {
  885. X    KWD *p;
  886. X
  887. X    if (!cp)
  888. X        return PR_OTHER;
  889. X
  890. X    for (p = preps; p->name && ci_strncmp(cp, p->name, MIN_PREP_LEN); p++)
  891. X        ;
  892. X
  893. X    return p->code;
  894. }
  895. X
  896. X
  897. /*
  898. X * get_token - look up 'token' in list of preprocessor tokens; return its
  899. X * index if found, PP_OTHER if not
  900. X */
  901. #ifdef PROTOS
  902. int get_token(char *token)
  903. #else
  904. int get_token(token)
  905. X    char *token;
  906. #endif
  907. {
  908. X    KWD_F *p;
  909. X
  910. X    for (p = pp_info;
  911. X             p->name && ci_strncmp(token, p->name, MIN_PPTOK_LEN);
  912. X         p++)
  913. X        ;
  914. X
  915. X    return p->code;
  916. }
  917. X
  918. X
  919. /*
  920. X * date_type - examine token and return date type code; if DT_MONTH, DT_ORDINAL,
  921. X * or DT_WEEKDAY, fill in appropriate code (and value if DT_ORDINAL)
  922. X */
  923. #ifdef PROTOS
  924. int date_type(char *cp,
  925. X          int *pn,
  926. X          int *pv)
  927. #else
  928. int date_type(cp, pn, pv)
  929. X    char *cp;    /* pointer to start of token  */
  930. X    int *pn;    /* token type code (returned) */
  931. X    int *pv;    /* ordinal value (returned)   */
  932. #endif
  933. {
  934. X    int n, v;
  935. X
  936. X    if ((n = get_ordinal(cp, &v)) != ORD_OTHER)    /* ordinal? */
  937. X        return (*pn = n, *pv = v, DT_ORDINAL);
  938. X
  939. X    if (isdigit(*cp))                /* other digit? */
  940. X        return IS_NUMERIC(cp) ? DT_EURDATE : DT_DATE;
  941. X
  942. X    if ((n = get_weekday(cp, TRUE)) != NOT_WEEKDAY)    /* weekday name? */
  943. X        return (*pn = n, DT_WEEKDAY);
  944. X
  945. X    /* "all" can be either a keyword or a month wildcard - look for
  946. X       the former usage first */
  947. X
  948. X    if ((n = get_keywd(cp)) != DT_OTHER)
  949. X        return n;
  950. X
  951. X    if ((n = get_month(cp, FALSE, FALSE)) != NOT_MONTH)  /* month name? */
  952. X        return (*pn = n, DT_MONTH);
  953. X
  954. X    return DT_OTHER;        /* unrecognized keyword - give up */
  955. X
  956. }
  957. X
  958. X
  959. X
  960. /*
  961. X * Routines for entering data in the data structure (described in pcaldefs.h)
  962. X */
  963. X
  964. X
  965. /*
  966. X * find_year - find record in year list; optionally create if not present 
  967. X */
  968. #ifdef PROTOS
  969. year_info *find_year(int year,
  970. X             int insert)
  971. #else
  972. year_info *find_year(year, insert)    /* find record in year list */
  973. X    int year;
  974. X    int insert;            /* insert if missing */
  975. #endif
  976. {
  977. X    year_info *pyear, *plast, *p;
  978. X
  979. X    for (plast = NULL, pyear = head;        /* search linked list */
  980. X         pyear && pyear->year < year;
  981. X         plast = pyear, pyear = pyear->next)
  982. X        ;
  983. X
  984. X    if (pyear && pyear->year == year)        /* found - return it */
  985. X        return pyear;
  986. X
  987. X    if (insert) {        /* not found - insert it if requested */
  988. X        p = (year_info *) alloc((int) sizeof(year_info));    /* create new record */
  989. X        p->year = year;
  990. X
  991. X        p->next = pyear;                /* link it in */
  992. X        return *(plast ? &plast->next : &head) = p;
  993. X    }
  994. X    else
  995. X        return NULL;
  996. }
  997. X
  998. X
  999. /*
  1000. X * enter_day_info - enter text for specified day; avoid entering duplicates.
  1001. X * Returns PARSE_INVDATE if date invalid, PARSE_OK if OK; if symbol FEB_29_OK
  1002. X * is non-zero (cf. pcaldefs.h), will silently ignore Feb 29 of common year.
  1003. X */
  1004. #ifdef PROTOS
  1005. int enter_day_info(int m,
  1006. X           int d,
  1007. X           int y,
  1008. X           int text_type,
  1009. X           char **pword)
  1010. #else
  1011. int enter_day_info(m, d, y, text_type, pword)    /* fill in information for given day */
  1012. X    int m, d, y;
  1013. X    int text_type;
  1014. X    char **pword;
  1015. #endif
  1016. {
  1017. X    static year_info *pyear;
  1018. X    static int prev_year = 0;
  1019. X    month_info *pmonth;
  1020. X    day_info *pday, *plast;
  1021. X    int is_holiday = text_type == HOLIDAY_TEXT;
  1022. X    char text[LINSIZ];
  1023. X
  1024. X    if (! is_valid(m, d == NOTE_DAY && text_type == NOTE_TEXT ? 1 : d, y))
  1025. X        return (m == FEB && d == 29 && FEB_29_OK) ? PARSE_OK : PARSE_INVDATE;
  1026. X
  1027. X    if (y != prev_year)        /* avoid unnecessary year lookup */
  1028. X        pyear = find_year(y, 1);
  1029. X
  1030. X    --m, --d;            /* adjust for use as subscripts */
  1031. X
  1032. X    if ((pmonth = pyear->month[m]) == NULL)    /* find/create month record */
  1033. X        pyear->month[m] = pmonth = (month_info *) alloc((int) sizeof(month_info));
  1034. X
  1035. X    if (is_holiday)
  1036. X        pmonth->holidays |= (1L << d);
  1037. X
  1038. X    /* insert text for day at end of list (preserving the order of entry
  1039. X     * for multiple lines on same day); eliminate those differing only
  1040. X     * in spacing and capitalization from existing entries
  1041. X         */
  1042. X
  1043. X    copy_text(text, pword);    /* consolidate text from lbuf into text */
  1044. X
  1045. #ifdef DATE_DEBUG
  1046. X    {
  1047. X    char *p;
  1048. X    fprintf(stderr, "%02d/%02d/%d%c '", m+1, d+1, y, is_holiday ? '*' : ' ');
  1049. X    for (p = text; *p; p++)
  1050. X        fprintf(stderr, isprint(*p) ? "%c" : "\\%03o", *p & 0377);
  1051. X    fprintf(stderr, "'\n");
  1052. X    }
  1053. #endif
  1054. X
  1055. X    if (*text) {
  1056. X        for (plast = NULL, pday = pmonth->day[d];
  1057. X             pday;
  1058. X             plast = pday, pday = pday->next)
  1059. X            if (ci_strcmp(pday->text, text) == 0) {
  1060. X                pday->is_holiday |= is_holiday;
  1061. X                return PARSE_OK;
  1062. X            }
  1063. X
  1064. X        /* unique - add to end of list */
  1065. X
  1066. X        pday = (day_info *) alloc(sizeof(day_info));
  1067. X        pday->is_holiday = is_holiday;
  1068. X        strcpy(pday->text = (char *) alloc(strlen(text)+1), text);
  1069. X        pday->next = NULL;
  1070. X        *(plast ? &plast->next : &pmonth->day[d]) = pday;
  1071. X    }
  1072. X
  1073. X    return PARSE_OK;
  1074. }
  1075. X
  1076. X
  1077. X
  1078. /*
  1079. X * Date parsing routines:
  1080. X */
  1081. X
  1082. X
  1083. /*
  1084. X * parse_ord - parse an ordinal date spec (e.g. "first Monday in September",
  1085. X * "every Sunday in October", "last workday in all"); return PARSE_OK if line
  1086. X * syntax valid, PARSE_INVLINE if not.  Write all matching dates (if any) to
  1087. X * global array dates[]; terminate date list with null entry.
  1088. X */
  1089. #ifdef PROTOS
  1090. int parse_ord(int ord,
  1091. X          int val,
  1092. X          char **pword)
  1093. #else
  1094. int parse_ord(ord, val, pword)
  1095. X    int ord;        /* valid ordinal code - from get_ordinal() */
  1096. X    int val;        /* ordinal value - also from get_ordinal() */
  1097. X    char **pword;        /* pointer to word after ordinal */
  1098. #endif
  1099. {
  1100. X    int wkd, mon, mm, dd, len, (*pfcn)(), doit;
  1101. X    int val_first, val_last, val_incr, mon_first, mon_last;
  1102. X    DATE *pdate, date;
  1103. X
  1104. X    if ((wkd = get_weekday(*pword, TRUE)) == NOT_WEEKDAY ||    /* weekday */
  1105. X        *++pword == NULL ||                    /* any word */
  1106. X        (mon = get_month(*++pword, FALSE, TRUE)) == NOT_MONTH) /* month */
  1107. X        return PARSE_INVLINE;
  1108. X
  1109. X    /* set up loop boundaries for month loop */
  1110. X
  1111. X    mon_first = mon == ALL_MONTHS || mon == ENTIRE_YEAR ? JAN : mon;
  1112. X    mon_last  = mon == ALL_MONTHS || mon == ENTIRE_YEAR ? DEC : mon;
  1113. X
  1114. X    pdate = dates;            /* start of date array */
  1115. X
  1116. X    /* special case of "all|odd|even <wildcard> in <month>|all|year" */
  1117. X
  1118. X    if ((ord == ORD_ALL || ord == ORD_EVEN || ord == ORD_ODD) &&
  1119. X        IS_WILD(wkd)) {
  1120. X        pfcn = pdatefcn[wkd - ANY_DAY];
  1121. X        doit = ord != ORD_EVEN;
  1122. X        for (mm = mon_first; mm <= mon_last; mm++) {
  1123. X            len = LENGTH_OF(mm, curr_year);
  1124. X            if (mon != ENTIRE_YEAR)
  1125. X                doit = ord != ORD_EVEN;
  1126. X            for (dd = 1; dd <= len; dd++)
  1127. X                if ((*pfcn)(mm, dd, curr_year)) {
  1128. X                    if (doit)
  1129. X                        ADD_DATE(mm, dd, curr_year);
  1130. X                    if (ord != ORD_ALL)
  1131. X                        doit = ! doit;
  1132. X                }
  1133. X        }
  1134. X    }
  1135. X
  1136. X    /* special case of "odd|even <weekday> in year" */
  1137. X
  1138. X    else if ((ord == ORD_EVEN || ord == ORD_ODD) && mon == ENTIRE_YEAR) {
  1139. X        date.mm = JAN;            /* starting date */
  1140. X        date.dd = calc_day(ord == ORD_EVEN ? 2 : 1, wkd, JAN);
  1141. X        date.yy = curr_year;
  1142. X        do {                /* alternates throughout year */
  1143. X            ADD_DATE(date.mm, date.dd, date.yy);
  1144. X            date.dd += 14;
  1145. X            normalize(&date);
  1146. X        } while (date.yy == curr_year);
  1147. X    }
  1148. X
  1149. X    /* special case of "<ordinal>|last <weekday>|<wildcard> in year" */
  1150. X
  1151. X    else if ((ord == ORD_NEGNUM || ord == ORD_POSNUM) &&
  1152. X             mon == ENTIRE_YEAR) {
  1153. X        if (calc_year_day(val, wkd, &date))
  1154. X            ADD_DATE(date.mm, date.dd, date.yy);
  1155. X    }
  1156. X
  1157. X    /* all other combinations of ordinal and day */
  1158. X
  1159. X    else {
  1160. X        /* set up loop boundaries for "wildcard" ordinals */
  1161. X
  1162. X        val_first = ord == ORD_ALL || ord == ORD_ODD ? 1 :
  1163. X                ord == ORD_EVEN ? 2 : val;
  1164. X        val_last  = ord == ORD_ALL || ord == ORD_ODD ? 5 :
  1165. X                ord == ORD_EVEN ? 4 : val;
  1166. X        val_incr  = ord == ORD_ODD || ord == ORD_EVEN ? 2 : 1;
  1167. X
  1168. X        for (mm = mon_first; mm <= mon_last; mm++)
  1169. X            for (val = val_first; val <= val_last; val += val_incr)
  1170. X                if ((dd = calc_day(val, wkd, mm)) != 0)
  1171. X                    ADD_DATE(mm, dd, curr_year);
  1172. X    }
  1173. X
  1174. X    TERM_DATES;        /* terminate array with null entry */
  1175. X    return PARSE_OK;
  1176. X
  1177. }
  1178. X
  1179. X
  1180. /*
  1181. X * parse_rel - parse a relative date spec (e.g. "Friday after fourth Thursday
  1182. X * in November", "Saturday after first Friday in all"; return PARSE_OK if
  1183. X * line syntax valid, PARSE_INVLINE if not.  Transform all dates that match
  1184. X * the base date to the appropriate day, month, and year.
  1185. X *
  1186. X * This calls parse_date() recursively in order to handle cases such as
  1187. X * "Friday after Tuesday before last day in all".
  1188. X */
  1189. #ifdef PROTOS
  1190. int parse_rel(int wkd,
  1191. X          char **pword,
  1192. X          int *ptype,
  1193. X          char ***pptext)
  1194. #else
  1195. int parse_rel(wkd, pword, ptype, pptext)
  1196. X    int wkd;        /* valid weekday code - from get_weekday() */
  1197. X    char **pword;        /* pointer to word after weekday */
  1198. X    int *ptype;        /* return text type (holiday/non-holiday) */
  1199. X    char ***pptext;        /* return pointer to first word of text */
  1200. #endif
  1201. {
  1202. X    int prep, rtn, base_wkd, incr, (*pfcn)();
  1203. X    DATE *pd;
  1204. X
  1205. X    /* we have the weekday - now look for the preposition */
  1206. X    if ((prep = get_prep(*pword++)) == PR_OTHER)
  1207. X        return PARSE_INVLINE;
  1208. X
  1209. X    /* get the base date */
  1210. X    if ((rtn = parse_date(pword, ptype, pptext)) != PARSE_OK)
  1211. X        return rtn;
  1212. X
  1213. X    /* transform date array in place - note that the relative date may
  1214. X       not be in the same month or even year */
  1215. X
  1216. X    if (IS_WILD(wkd)) {        /* wildcard for weekday name? */
  1217. X        pfcn = pdatefcn[wkd - ANY_DAY];
  1218. X        incr = prep == PR_BEFORE || prep == PR_ON_BEFORE ? -1 : 1;
  1219. X
  1220. X        for (pd = dates; pd->mm; pd++) {
  1221. X            /* search for nearest matching date */
  1222. X
  1223. X            if (prep == PR_BEFORE || prep == PR_AFTER) {
  1224. X                pd->dd += incr;
  1225. X                normalize(pd);
  1226. X            }
  1227. X            while (!(*pfcn)(pd->mm, pd->dd, pd->yy)) {
  1228. X                pd->dd += incr;
  1229. X                normalize(pd);
  1230. X            }
  1231. X        }
  1232. X
  1233. X    } else  {            /* explicit weekday name */
  1234. X        for (pd = dates; pd->mm; pd++) {
  1235. X            /* calculate nearest matching date */
  1236. X
  1237. X            base_wkd = calc_weekday(pd->mm, pd->dd, pd->yy);
  1238. X
  1239. X            if (prep == PR_BEFORE ||
  1240. X                (prep == PR_ON_BEFORE && wkd != base_wkd))
  1241. X                pd->dd -= 7 - (wkd - base_wkd + 7) % 7;
  1242. X
  1243. X            if (prep == PR_AFTER ||
  1244. X                (prep == PR_ON_AFTER && wkd != base_wkd))
  1245. X                pd->dd += (wkd - base_wkd + 6) % 7 + 1;
  1246. X
  1247. X            normalize(pd);    /* adjust for month/year crossing */
  1248. X        }
  1249. X    }
  1250. X
  1251. X    return PARSE_OK;
  1252. }
  1253. X
  1254. X
  1255. /*
  1256. X * parse_date - parse a date specification in any of its myriad forms; upon
  1257. X * return, array dates[] will contain a list of all the dates that matched,
  1258. X * terminated by a null entry.  Also fill in the date type (holiday/non-
  1259. X * holiday) code and the pointer to the first word of text.
  1260. X */
  1261. #ifdef PROTOS
  1262. int parse_date(char **pword,
  1263. X           int *ptype,
  1264. X           char ***pptext)
  1265. #else
  1266. int parse_date(pword, ptype, pptext)
  1267. X    char **pword;        /* first word to parse */
  1268. X    int *ptype;        /* return date type (holiday/non-holiday) */
  1269. X    char ***pptext;        /* return pointer to first word of text */
  1270. #endif
  1271. {
  1272. X    int mm, dd, yy;
  1273. X    int token, n, v, ord, val, wkd, rtn;
  1274. X    DATE *pdate;
  1275. X    char *cp;
  1276. X
  1277. X    pdate = dates;
  1278. X
  1279. X    switch (token = date_type(*pword, &n, &v)) {
  1280. X
  1281. X    case DT_MONTH:        /* <month> dd */
  1282. X        if (date_style != USA_DATES)
  1283. X            return PARSE_INVLINE;
  1284. X
  1285. X        if ((cp = *++pword) == NULL)
  1286. X            return PARSE_INVLINE;
  1287. X
  1288. X        ADD_DATE(n, atoi(cp), curr_year);
  1289. X        TERM_DATES;
  1290. X
  1291. X        break;
  1292. X
  1293. X    case DT_DATE:        /* mm/dd{/yy} | dd/mm{/yy} */
  1294. X        n = split_date(*pword,
  1295. X                   date_style == USA_DATES ? &mm : &dd,
  1296. X                   date_style == USA_DATES ? &dd : &mm,
  1297. X                   &yy);
  1298. X
  1299. X        if (n > 2) {            /* year present? */
  1300. X            if (yy < 100)
  1301. X                yy += CENTURY;
  1302. X            curr_year = yy;        /* reset current year */
  1303. X        }
  1304. X
  1305. X        ADD_DATE(mm, dd, curr_year);
  1306. X        TERM_DATES;
  1307. X
  1308. X        break;
  1309. X
  1310. X    case DT_EURDATE:    /* dd [ <month> | "all" ] */
  1311. X        if (date_style != EUR_DATES)
  1312. X            return PARSE_INVLINE;
  1313. X
  1314. X        dd = atoi(*pword);
  1315. X
  1316. X        if (get_keywd(*++pword) == DT_ALL) {
  1317. X            for (mm = JAN; mm <= DEC; mm++)        /* wildcard */
  1318. X                ADD_DATE(mm, dd, curr_year);
  1319. X        }
  1320. X        else {                        /* one month */
  1321. X            if ((mm = get_month(*pword, FALSE, FALSE)) == NOT_MONTH)
  1322. X                return PARSE_INVLINE;
  1323. X
  1324. X            ADD_DATE(mm, dd, curr_year);
  1325. X        }
  1326. X
  1327. X        TERM_DATES;
  1328. X        break;
  1329. X
  1330. X    case DT_ALL:        /* "all" <weekday> "in" [ <month> | "all" ] */
  1331. X                /* or "all" <day>" */
  1332. X
  1333. X        if ((cp = *(pword+1)) && (*(cp += strspn(cp, DIGITS)) == '\0' ||
  1334. X            *cp == '*')) {
  1335. X            dd = atoi(*++pword);        /* "all" <day> */
  1336. X            for (mm = JAN; mm <= DEC; mm++)
  1337. X                ADD_DATE(mm, dd, curr_year);
  1338. X            TERM_DATES;
  1339. X            break;        /* leave switch */
  1340. X        }
  1341. X
  1342. X        n = ORD_ALL;    /* "all" <weekday> ... */
  1343. X        v = 0;
  1344. X         /* fall through */
  1345. X
  1346. X    case DT_ORDINAL:    /* <ordinal> <weekday> in [ <month> | "all" ] */
  1347. X        ord = n;
  1348. X        val = v;
  1349. X        if ((rtn = parse_ord(ord, val, pword + 1)) != PARSE_OK)
  1350. X            return rtn;
  1351. X
  1352. X        pword += 3;        /* last word of date */
  1353. X        break;
  1354. X
  1355. X    case DT_WEEKDAY:    /* <weekday> <prep> <date> */
  1356. X        wkd = n;
  1357. X
  1358. X        /* parse_rel() calls parse_date() recursively */
  1359. X        return parse_rel(wkd, ++pword, ptype, pptext);
  1360. X        break;
  1361. X
  1362. X    default:
  1363. X        return PARSE_INVLINE;
  1364. X        break;
  1365. X    }
  1366. X
  1367. X    /* at this point, pword points to the last component of the date;
  1368. X     * fill in type code and pointer to following word (start of text)
  1369. X     */
  1370. X    *ptype = LASTCHAR(*pword) == '*' ? HOLIDAY_TEXT : DAY_TEXT;
  1371. X    *pptext = ++pword;
  1372. X
  1373. X    return PARSE_OK;
  1374. }
  1375. X
  1376. /*
  1377. X * parse - parse non-preprocessor lines in date file
  1378. X *
  1379. X * This routine parses "year", "opt", "note", and date entries in the date
  1380. X * file.  It calls parse_date() to parse date entries (and enter the date(s)
  1381. X * matched in global array "dates"), and then calls enter_day_info() to
  1382. X * enter each date found (and its associated text) in the date file.
  1383. X *
  1384. X * N.B.: "inc" and other cpp-like lines are handled in read_datefile().
  1385. X *
  1386. X */
  1387. #ifdef PROTOS
  1388. int parse(char **pword,
  1389. X      char *filename)
  1390. #else
  1391. int parse(pword, filename)
  1392. X    char **pword;        /* pointer to first word to parse */
  1393. X    char *filename;        /* name of file (for error messages) */
  1394. #endif
  1395. {
  1396. X    register char *cp;
  1397. X    char **ptext;
  1398. X    int mm, yy;
  1399. X    int text_type, n, v, rtn, match;
  1400. X    int token;
  1401. X    DATE *pd;
  1402. X
  1403. X    /*
  1404. X     * Get first field and call date_type() to decode it
  1405. X         */
  1406. X    cp = *pword;
  1407. X
  1408. X    switch (token = date_type(cp, &n, &v)) {
  1409. X
  1410. X    case DT_YEAR:
  1411. X        if ((cp = *++pword) != NULL && (yy = atoi(cp)) > 0) {
  1412. X            if (yy < 100)
  1413. X                yy += CENTURY;
  1414. X            curr_year = yy;
  1415. X            return PARSE_OK;
  1416. X        }
  1417. X        return PARSE_INVLINE;    /* year missing or non-numeric */
  1418. X        break;
  1419. X
  1420. X    case DT_OPT:
  1421. X         if (!get_args(pword, P_OPT, filename)) {
  1422. X            usage(stderr, FALSE);
  1423. X            exit(EXIT_FAILURE);
  1424. X        }
  1425. X        return PARSE_OK;
  1426. X        break;
  1427. X
  1428. X    case DT_NOTE:
  1429. X        if ((mm = get_month(*++pword, TRUE, TRUE)) == NOT_MONTH)
  1430. X            return PARSE_INVLINE;
  1431. X
  1432. X        if (mm == ALL_MONTHS || mm == ENTIRE_YEAR)    /* "note all"? */
  1433. X            for (mm = JAN; mm <= DEC; mm++)
  1434. X                enter_day_info(mm, NOTE_DAY, curr_year,
  1435. X                    NOTE_TEXT, pword+1);
  1436. X        else
  1437. X            enter_day_info(mm, NOTE_DAY, curr_year, NOTE_TEXT,
  1438. X                pword+1);
  1439. X
  1440. X        return PARSE_OK;
  1441. X        break;
  1442. X
  1443. X    case DT_OTHER:        /* unrecognized token */
  1444. X        return PARSE_INVLINE;
  1445. X        break;
  1446. X
  1447. X    /* assume anything else is a date */
  1448. X
  1449. X    default:
  1450. X        if ((rtn = parse_date(pword, &text_type, &ptext)) == PARSE_OK) {
  1451. X            match = FALSE;    /* is at least one date valid? */
  1452. X            for (pd = dates; pd->mm; pd++)
  1453. X                match |= enter_day_info(pd->mm, pd->dd, pd->yy,
  1454. X                           text_type, ptext) == PARSE_OK;
  1455. X            rtn = match ? PARSE_OK : PARSE_INVDATE;
  1456. X        }
  1457. X        return rtn;
  1458. X        break;
  1459. X
  1460. X    }
  1461. }
  1462. SHAR_EOF
  1463. chmod 0644 readfile.c ||
  1464. echo 'restore of readfile.c failed'
  1465. Wc_c="`wc -c < 'readfile.c'`"
  1466. test 27599 -eq "$Wc_c" ||
  1467.     echo 'readfile.c: original size 27599, current size' "$Wc_c"
  1468. fi
  1469. # ============= writefil.c ==============
  1470. if test -f 'writefil.c' -a X"$1" != X"-c"; then
  1471.     echo 'x - skipping writefil.c (File already exists)'
  1472. else
  1473. echo 'x - extracting writefil.c (Text)'
  1474. sed 's/^X//' << 'SHAR_EOF' > 'writefil.c' &&
  1475. /*
  1476. X * writefil.c - Pcal routines concerned with writing the PostScript output
  1477. X *
  1478. X * Contents:
  1479. X *
  1480. X *        def_footstring
  1481. X *        find_daytext
  1482. X *        find_holidays
  1483. X *        print_julian_info
  1484. X *        print_month
  1485. X *        print_moon_info
  1486. X *        print_text
  1487. X *        print_word
  1488. X *        write_psfile
  1489. X *
  1490. X * Revision history:
  1491. X *
  1492. X *    4.0    AWR    01/28/91    Support -B, -w flags and moon file
  1493. X *
  1494. X *            01/15/91    Extracted from pcal.c
  1495. X *
  1496. X */
  1497. X
  1498. /*
  1499. X * Standard headers:
  1500. X */
  1501. X
  1502. #include <stdio.h>
  1503. #include <ctype.h>
  1504. X
  1505. /*
  1506. X * Pcal-specific definitions:
  1507. X */
  1508. X
  1509. #include "pcaldefs.h"
  1510. #include "pcalglob.h"
  1511. #include "pcallang.h"
  1512. #include "pcalinit.h"        /* PostScript boilerplate */
  1513. X
  1514. /*
  1515. X * Macros:
  1516. X */
  1517. X
  1518. /* convert "ph" (approximate quarter moon) to exact quarter moon */
  1519. #define EXACT_QUARTER(ph)    ((((int) (((ph) + .05) * 4.0)) % 4) / 4.0)
  1520. X
  1521. /* make sure printf() doesn't round "ph" up to 1.0 when printing it */
  1522. #define PRT_TWEAK(ph)        ((ph) >= 0.9995 ? 0.0 : (ph))
  1523. X
  1524. /*
  1525. X * Globals:
  1526. X */
  1527. X
  1528. static char *cond[3] = {"false", "true", "(some)"};
  1529. X
  1530. X
  1531. /*
  1532. X * write_psfile - write PostScript code to stdout
  1533. X *
  1534. X * The actual output of the PostScript code is straightforward.  This routine
  1535. X * writes a PostScript header followed by declarations of all the PostScript
  1536. X * variables affected by command-line flags and/or language dependencies.  It
  1537. X * the generates the PostScript boilerplate generated from pcalinit.ps, and
  1538. X * finally calls print_month() to generate the PostScript code for each
  1539. X * requested month.
  1540. X */
  1541. #ifdef PROTOS
  1542. void write_psfile(int month,
  1543. X          int year,
  1544. X          int nmonths)
  1545. #else
  1546. void write_psfile(month, year, nmonths)
  1547. X    int month;            /* starting month   */
  1548. X    int year;            /* starting year    */
  1549. X    int nmonths;            /* number of months */
  1550. #endif
  1551. {
  1552. X    int i, ngray;
  1553. X    char **ap, tmp[20];
  1554. X
  1555. X    /*
  1556. X     * Write out PostScript prolog
  1557. X     */
  1558. X
  1559. X     PRT("%%!\n%%\t");
  1560. X    PRT(VERSION_MSG, progname, version);
  1561. X    if (*datefile)
  1562. X        PRT(DATEFILE_MSG, datefile);
  1563. X    PRT("\n%%\n");
  1564. X
  1565. X    /* font names */
  1566. X
  1567. X    PRT("/titlefont /%s def\n/dayfont /%s def\n/notesfont /%s def\n",
  1568. X        titlefont, dayfont, notesfont);
  1569. X
  1570. X    /* foot strings */
  1571. X
  1572. X    def_footstring(lfoot, 'L');
  1573. X    def_footstring(cfoot, 'C');
  1574. X    def_footstring(rfoot, 'R');
  1575. X
  1576. X    /* month names */
  1577. X
  1578. X    PRT("/month_names [");
  1579. X    for (i = JAN; i <= DEC; i++) {
  1580. X        PRT(i % 6 == 1 ? "\n\t" : " ");
  1581. X        (void) print_word(months[i-1]);
  1582. X    }
  1583. X    PRT(" ] def\n");
  1584. X
  1585. X    /* day names - abbreviate if printing entire year on page */
  1586. X
  1587. X    PRT("/day_names [");
  1588. X    for (i = SUN; i <= SAT; i++) {
  1589. X        PRT(i % 6 == 0 && ! do_whole_year ? "\n\t" : " ");
  1590. X        strcpy(tmp, days[(i + first_day_of_week) % 7]);
  1591. X        if (do_whole_year)
  1592. X            tmp[MIN_DAY_LEN] = '\0';
  1593. X        (void) print_word(tmp);
  1594. X        }
  1595. X    PRT(" ] def\n");
  1596. X
  1597. X    /* title for "notes" box */
  1598. X
  1599. X    PRT("/notesheading (");
  1600. X    PUTSTR(NOTES_HDR);
  1601. X    PRT(") def\n");
  1602. X
  1603. X    /* line separator */
  1604. X
  1605. X    PRT("/linesep ");
  1606. X    print_word(LINE_SEP);
  1607. X    PRT(" def\n");
  1608. X
  1609. X    /* colors (black/gray) to print weekdays and holidays */
  1610. X
  1611. X    PRT("/day_gray [");
  1612. X    for (ngray = 0, i = SUN; i <= SAT; ngray += day_color[i++] == GRAY)
  1613. X        PRT(" %s", cond[day_color[(i + first_day_of_week) % 7]]);
  1614. X    PRT(" ] def\n");
  1615. X    PRT("/holiday_gray %s def\n", cond[ngray <= 3]);
  1616. X
  1617. X     /* rotation, scale, and translate values. */
  1618. X     PRT("/rval %d def\n", rotate);
  1619. X     PRT("/xsval %s def\n/ysval %s def\n", xsval, ysval);
  1620. X     PRT("/xtval %s def\n/ytval %s def\n", xtval, ytval);
  1621. X
  1622. X    /* moon, Julian date, box fill, and outline flags */
  1623. X
  1624. X    PRT("/draw-moons %s def\n", cond[draw_moons]);
  1625. X    PRT("/julian-dates %s def\n", cond[julian_dates]);
  1626. X    PRT("/fill-boxes %s def\n", cond[! blank_boxes]);
  1627. X    PRT("/outline %s def\n", cond[outline_nums]);
  1628. X
  1629. X    /* PostScript boilerplate (part 1 of 1) */
  1630. X
  1631. X    for (ap = header; *ap; ap++)
  1632. X        PRT("%s\n", *ap);
  1633. X    PRT("\n");
  1634. X
  1635. X    /*
  1636. X     * Write out PostScript code to print calendars
  1637. X     */
  1638. X
  1639. X    if (do_whole_year)    /* round up to multiple of 12 months */
  1640. X        nmonths = ((nmonths + 11) / 12) * 12;
  1641. X
  1642. X    while (nmonths--) {
  1643. X        print_month(month, year);
  1644. X        year = NEXT_YEAR(month, year);
  1645. X        month = NEXT_MONTH(month, year);
  1646. X    }
  1647. X
  1648. }
  1649. X
  1650. X
  1651. /*
  1652. X * low-level utilities for PostScript generation
  1653. X */
  1654. X
  1655. X
  1656. /*
  1657. X * print_word - print a single word, representing parentheses and non-ASCII
  1658. X * characters as octal literals; return pointer to character following word
  1659. X * (NULL if no word follows)
  1660. X */
  1661. #ifdef PROTOS
  1662. char *print_word(char *p)
  1663. #else
  1664. char *print_word(p)
  1665. X    char *p;
  1666. #endif
  1667. {
  1668. X    char c;
  1669. X
  1670. X    if (*p == '\0' || *(p += strspn(p, WHITESPACE)) == '\0')
  1671. X        return NULL;
  1672. X
  1673. X    PRT("(");
  1674. X    for ( ; (c = *p) && !isspace(c); p++)
  1675. X        PUTCHAR(c);
  1676. X    PRT(")");
  1677. X
  1678. X    return p;
  1679. }
  1680. X
  1681. X
  1682. /*
  1683. X * print_text - print tokens in text (assumed separated by single blank)
  1684. X * in PostScript format
  1685. X */
  1686. #ifdef PROTOS
  1687. void print_text(char *p)
  1688. #else
  1689. void print_text(p)
  1690. X    char *p;
  1691. #endif
  1692. {
  1693. X
  1694. X    while (p = print_word(p))
  1695. X        PRT(" ");
  1696. }
  1697. X
  1698. X
  1699. /*
  1700. X * def_footstring - print definition for foot string, again converting 
  1701. X * all characters other than letters, digits, or space to octal escape
  1702. X */
  1703. #ifdef PROTOS
  1704. void def_footstring(char *p,
  1705. X            char c)
  1706. #else
  1707. void def_footstring(p, c)
  1708. X    char *p;            /* definition */
  1709. X    char c;                /* L, C, or R */
  1710. #endif
  1711. {
  1712. X
  1713. X    PRT("/%cfootstring (", c);
  1714. X    PUTSTR(p);
  1715. X    PRT(") def\n");
  1716. }
  1717. X
  1718. X
  1719. /*
  1720. X * Routines to extract and print data
  1721. X */
  1722. X
  1723. X
  1724. /*
  1725. X * find_daytext - find and print the day (including notes) or holiday text
  1726. X * for the specified month/year
  1727. X */
  1728. #ifdef PROTOS
  1729. void find_daytext(int month,
  1730. X          int year,
  1731. X          int is_holiday)
  1732. #else
  1733. void find_daytext(month, year, is_holiday)
  1734. X    int month, year;
  1735. X    int is_holiday;        /* TRUE: print holiday text */
  1736. #endif
  1737. {
  1738. X    register int day;
  1739. X    year_info *py;
  1740. X    month_info *pm;
  1741. X    register day_info *pd;
  1742. X    int first;
  1743. X    char *fcn = is_holiday ? "holidaytext" : "daytext";
  1744. X
  1745. X    /* if no text for this year and month, return */
  1746. X
  1747. X    if ((py = find_year(year, FALSE)) == NULL ||
  1748. X        (pm = py->month[month-1]) == NULL)
  1749. X        return;
  1750. X
  1751. X    /* walk array of day text pointers and linked lists of text */
  1752. X
  1753. X    for (day = 1; day <= NOTE_DAY; day++) {
  1754. X        for (pd = pm->day[day-1], first = TRUE;
  1755. X             pd;
  1756. X             pd = pd->next) {
  1757. X            if (pd->is_holiday != is_holiday)
  1758. X                continue;
  1759. X            if (first) {
  1760. X                if (day != NOTE_DAY)    /* set up call */
  1761. X                    PRT("%d ", day);
  1762. X                printf("[ \n");
  1763. X            }
  1764. X            else {
  1765. X                PRT("\n");
  1766. X                print_word(LINE_SEP);    /* separate lines */
  1767. X                PRT("\n");
  1768. X            }
  1769. X            print_text(pd->text);
  1770. X            first = FALSE;
  1771. X        }
  1772. X        if (! first)        /* wrap up call (if one made) */
  1773. X            PRT("\n] %s\n", day == NOTE_DAY ? "notetext" : fcn);
  1774. X    }
  1775. }
  1776. X
  1777. X
  1778. /*
  1779. X * find_holidays - find and print the note block flag and holidays for
  1780. X * specified month/year
  1781. X */
  1782. #ifdef PROTOS
  1783. void find_holidays(int month,
  1784. X           int year)
  1785. #else
  1786. void find_holidays(month, year)
  1787. X    int month, year;
  1788. #endif
  1789. {
  1790. X    register int day;
  1791. X    register unsigned long holidays;
  1792. X    year_info *py;
  1793. X    month_info *pm;
  1794. X
  1795. X    pm = (py = find_year(year, FALSE)) ? py->month[month-1] : NULL;
  1796. X
  1797. X    if (! do_whole_year)
  1798. X         PRT("/note_block %s def\n", cond[pm && pm->day[NOTE_DAY-1]]);
  1799. X
  1800. X    PRT("/holidays [");    /* start definition of list */
  1801. X
  1802. X    for (holidays = pm ? pm->holidays : 0, day = 1;
  1803. X         holidays;
  1804. X         holidays >>= 1, day++)
  1805. X        if (holidays & 01)
  1806. X            PRT(" %d", day);
  1807. X
  1808. X    PRT(" ] def\n");
  1809. X
  1810. }
  1811. X
  1812. X
  1813. /*
  1814. X * print_moon_info - print the information necessary to draw moons.  If
  1815. X * printing moons on all days, print the phase for each day; if printing
  1816. X * only quarter moons, tweak the phase to an exact quarter (so the icon
  1817. X * is printed correctly) and generate a list of the quarter-moon dates
  1818. X */
  1819. #ifdef PROTOS
  1820. void print_moon_info(int month,
  1821. X             int year)
  1822. #else
  1823. void print_moon_info(month, year)
  1824. X    int month, year;
  1825. #endif
  1826. {
  1827. X    int n, ndays, day, is_q;
  1828. X    char *p;
  1829. X    unsigned long qdays;
  1830. X    double phase;
  1831. X
  1832. X    if (draw_moons == NO_MOONS)
  1833. X        return;
  1834. X
  1835. X    /* print the phase of the moon for each day of the month */
  1836. X
  1837. X    PRT("/moon_phases [\t\t%% from %s\n\t",
  1838. X        (p = find_moonfile(year)) ? p : "algorithm");
  1839. X
  1840. X    for (n = 0, qdays = 0L, day = 1, ndays = LENGTH_OF(month, year);
  1841. X         day <= ndays;
  1842. X         day++) {
  1843. X        phase = find_phase(month, day, year, &is_q);
  1844. X        if (draw_moons == SOME_MOONS && is_q)
  1845. X            phase = EXACT_QUARTER(phase);
  1846. X        if (draw_moons == ALL_MOONS || is_q)
  1847. X            PRT("%.3f%s", PRT_TWEAK(phase), ++n % 10 == 0 ?
  1848. X                "\n\t" : " ");
  1849. X        if (is_q)
  1850. X            qdays |= 1L << (day - 1);
  1851. X        }
  1852. X    PRT("] def\n");
  1853. X
  1854. X    /* if drawing only quarter moons, print days when they occur */
  1855. X
  1856. X    if (draw_moons == SOME_MOONS) {
  1857. X        PRT("/quarter_moons [ ");
  1858. X        for (day = 1; qdays; day++, qdays >>= 1)
  1859. X            if (qdays & 01)
  1860. X                PRT("%d ", day);
  1861. X        PRT("] def\n");
  1862. X    }
  1863. }
  1864. X
  1865. X
  1866. /*
  1867. X * print_julian_info - print the information necessary to print Julian dates
  1868. X */
  1869. #ifdef PROTOS
  1870. void print_julian_info(int month,
  1871. X               int year)
  1872. #else
  1873. void print_julian_info(month, year)
  1874. X    int month, year;
  1875. #endif
  1876. {
  1877. X
  1878. X    if (julian_dates != NO_JULIANS)
  1879. X        PRT("/jdstart %d def\n", DAY_OF_YEAR(month, 1, year));
  1880. X    if (julian_dates == ALL_JULIANS)
  1881. X        PRT("/yearlen %d def\n", YEAR_LEN(year));
  1882. }
  1883. X
  1884. X
  1885. /*
  1886. X * print_month - generate calendar for specified month/year
  1887. X */
  1888. #ifdef PROTOS
  1889. void print_month(int month,
  1890. X         int year)
  1891. #else
  1892. void print_month(month, year)
  1893. X    int month, year;
  1894. #endif
  1895. {
  1896. X    static int nmonths = 0;
  1897. X
  1898. X    PRT("/year %d def\n", year);        /* set up year and month */
  1899. X    PRT("/month %d def\n", month);
  1900. X    PRT("/startday %d def\n",
  1901. X        (FIRST_OF(month, year) - first_day_of_week + 7) % 7);
  1902. X    PRT("/ndays %d def\n", LENGTH_OF(month, year));
  1903. X
  1904. X    find_holidays(month, year);        /* make list of holidays */
  1905. X
  1906. X    /* are we printing 12 months per page or only one? */
  1907. X
  1908. X    if (do_whole_year) {
  1909. X        PRT("/posn %d def\n", nmonths % 12);    /* location on page */
  1910. X        PRT("printmonth_%c\n", rotate == LANDSCAPE ? 'l' : 'p');
  1911. X        if (++nmonths % 12 == 0)
  1912. X            PRT("showpage\n");
  1913. X    }
  1914. X    else {
  1915. X        /* generate lengths of previous and next months */
  1916. X        PRT("/lndays %d def\n", LENGTH_OF(PREV_MONTH(month, year),
  1917. X                    PREV_YEAR(month, year)));
  1918. X        PRT("/nndays %d def\n", LENGTH_OF(NEXT_MONTH(month, year),
  1919. X                    NEXT_YEAR(month, year)));
  1920. X
  1921. X        print_julian_info(month, year);        /* Julian date info */
  1922. X        print_moon_info(month, year);        /* moon info */
  1923. X
  1924. X        PRT("printmonth\n");
  1925. X        find_daytext(month, year, TRUE);    /* holiday text */
  1926. X        find_daytext(month, year, FALSE);    /* day and note text */
  1927. X        PRT("showpage\n");
  1928. X    }
  1929. }
  1930. SHAR_EOF
  1931. chmod 0644 writefil.c ||
  1932. echo 'restore of writefil.c failed'
  1933. Wc_c="`wc -c < 'writefil.c'`"
  1934. test 9889 -eq "$Wc_c" ||
  1935.     echo 'writefil.c: original size 9889, current size' "$Wc_c"
  1936. fi
  1937. exit 0
  1938.