home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 3 / 3072 < prev    next >
Encoding:
Text File  |  1991-03-15  |  59.0 KB  |  2,196 lines

  1. Newsgroups: alt.sources
  2. From: jbr0@cbnews.att.com (joseph.a.brownlee)
  3. Subject: Pcal v4.0, part 2 of 5
  4. Date: Thu, 14 Mar 91 12:48:13 GMT
  5. Message-ID: <1991Mar14.124813.9360@cbnews.att.com>
  6.  
  7. #!/bin/sh
  8. # This is part 02 of a multipart archive
  9. # ============= exprpars.c ==============
  10. if test -f 'exprpars.c' -a X"$1" != X"-c"; then
  11.     echo 'x - skipping exprpars.c (File already exists)'
  12. else
  13. echo 'x - extracting exprpars.c (Text)'
  14. sed 's/^X//' << 'SHAR_EOF' > 'exprpars.c' &&
  15. /*
  16. X * exprpars.c - Pcal routines concerned with parsing if{n}def expressions
  17. X *
  18. X * Contents:
  19. X *
  20. X *        do_xxx
  21. X *        lookup_token
  22. X *        next_token
  23. X *        parse_expr
  24. X *
  25. X * Revision history:
  26. X *
  27. X *    4.0    AWR    02/06/91    Author
  28. X *
  29. X */
  30. X
  31. /*
  32. X * Standard headers:
  33. X */
  34. X
  35. #include <ctype.h>
  36. #include <string.h>
  37. #include <stdio.h>
  38. X
  39. /*
  40. X * Pcal-specific definitions:
  41. X */
  42. X
  43. #include "pcaldefs.h"
  44. #include "pcalglob.h"
  45. X
  46. /*
  47. X * Macros:
  48. X */
  49. X
  50. /*
  51. X * token type code definitions:
  52. X */
  53. X
  54. #define TK_UNKNOWN     0        /* codes returned by next_token() */
  55. #define TK_IDENT     1
  56. #define TK_LPAREN     2
  57. #define TK_RPAREN     3
  58. #define TK_UNARYOP     4
  59. #define TK_BINARYOP     5
  60. #define TK_ENDINPUT     6
  61. #define TK_STARTINPUT     7        /* special code for start symbol */
  62. X
  63. /* bit position for token type codes (cf. where_ok[] below) */
  64. #define ID_OK        (1 << TK_IDENT)
  65. #define LP_OK        (1 << TK_LPAREN)
  66. #define RP_OK        (1 << TK_RPAREN)
  67. #define UO_OK        (1 << TK_UNARYOP)
  68. #define BO_OK        (1 << TK_BINARYOP)
  69. #define ST_OK        (1 << TK_STARTINPUT)
  70. #define NEVER_OK    0
  71. X
  72. /* is token "curr" legal after "prev"? (cf. where_ok[] below) */
  73. #define IS_LEGAL(curr, prev)    (where_ok[curr] & (1 << (prev)))
  74. X
  75. /*
  76. X * operator-related definitions:
  77. X */
  78. X
  79. #define OP_AND        0    /* operator subcodes */
  80. #define OP_OR        1
  81. #define OP_XOR        2
  82. #define OP_NEGATE    3
  83. X
  84. #define ENDINPUT_PREC    -1    /* arbitrary number < lowest op. prec  */
  85. #define OR_PREC         1    /* operator precedence levels */
  86. #define XOR_PREC     2
  87. #define AND_PREC     3
  88. #define NEGATE_PREC     4
  89. #define PAREN_PREC     8    /* arbitrary number > highest op. prec */
  90. X
  91. /* lower bits of operator stack entry are code; higher are precedence */
  92. #define OPR_BITS    4
  93. #define OPR_MASK    ((1 << OPR_BITS) - 1)
  94. #define PREC(op)    ((op) >> OPR_BITS)
  95. #define OPCODE(op)    ((op) & OPR_MASK)
  96. #define MAKE_OPR(p, o)    (((p) << OPR_BITS) | (o))
  97. X
  98. #define MAX_OP        20    /* size of operand and operator stacks */
  99. X
  100. /*
  101. X * Globals:
  102. X */
  103. X
  104. typedef short OPERAND;        /* types for operand and operator stacks */
  105. typedef short OPERATOR;
  106. X
  107. X
  108. typedef struct {
  109. X    char    *name;        /* token spelling */
  110. X    short    type;        /* token type code */
  111. X    short    value;        /* associated value */
  112. X    } TOKEN;
  113. X
  114. /* token table - note that substrings must follow longer strings */
  115. X
  116. TOKEN token_tbl[] = {
  117. X    "&&",    TK_BINARYOP,    OP_AND,        /* synonym for "&" */
  118. X    "&",    TK_BINARYOP,    OP_AND,
  119. X    "||",    TK_BINARYOP,    OP_OR,        /* synonym for "|" */
  120. X    "|",    TK_BINARYOP,    OP_OR,
  121. X    "!",    TK_UNARYOP,    OP_NEGATE,
  122. X    "^",    TK_BINARYOP,    OP_XOR,
  123. X    "(",    TK_LPAREN,    0,
  124. X    ")",    TK_RPAREN,    0,
  125. X    NULL,    TK_UNKNOWN,    0        /* must be last entry */
  126. X    };
  127. X
  128. X
  129. typedef struct {
  130. X    short prec;        /* precedence */
  131. X    short type;        /* token type (TK_UNARYOP or TK_BINARYOP) */
  132. #ifdef PROTOS
  133. X    OPERAND (*pfcn)(OPERAND *);    /* dispatch function */
  134. #else
  135. X    OPERAND (*pfcn)();        /* dispatch function */
  136. #endif
  137. X    } OPR;
  138. X
  139. /* operator table - entries must be in same order as OP_XXX */
  140. X
  141. #ifdef PROTOS
  142. static OPERAND do_and(OPERAND *);
  143. static OPERAND do_or(OPERAND *);
  144. static OPERAND do_xor(OPERAND *);
  145. static OPERAND do_negate(OPERAND *);
  146. #else
  147. static OPERAND do_and(), do_or(), do_xor(), do_negate();   /* dispatch fcns */
  148. #endif
  149. X
  150. OPR opr_tbl[] = {
  151. X    AND_PREC,    TK_BINARYOP,    do_and,        /* OP_AND    */
  152. X    OR_PREC,    TK_BINARYOP,    do_or,        /* OP_OR    */
  153. X    XOR_PREC,    TK_BINARYOP,    do_xor,        /* OP_XOR    */
  154. X    NEGATE_PREC,    TK_UNARYOP,    do_negate    /* OP_NEGATE    */
  155. X    };
  156. X
  157. X
  158. /* set of tokens which each token may legally follow (in TK_XXX order) */
  159. X
  160. int where_ok[] = {
  161. X    NEVER_OK                                      ,     /* TK_UNKNOWN    */
  162. X    ST_OK         | LP_OK         | UO_OK | BO_OK ,     /* TK_IDENT    */
  163. X    ST_OK         | LP_OK         | UO_OK | BO_OK ,     /* TK_LPAREN    */
  164. X            ID_OK | LP_OK | RP_OK                 ,     /* TK_RPAREN    */
  165. X    ST_OK         | LP_OK                 | BO_OK ,     /* TK_UNARYOP    */
  166. X            ID_OK         | RP_OK                 ,     /* TK_BINARYOP    */
  167. X    ST_OK | ID_OK         | RP_OK                    /* TK_ENDINPUT    */
  168. X    };
  169. X
  170. X
  171. /*
  172. X * do_xxx - dispatch functions for operators
  173. X */
  174. X
  175. #ifdef PROTOS
  176. static OPERAND do_and(OPERAND *ptop)
  177. #else
  178. static OPERAND do_and(ptop)
  179. X    OPERAND *ptop;
  180. #endif
  181. {
  182. X    return ptop[0] & ptop[-1];
  183. }
  184. X
  185. X
  186. #ifdef PROTOS
  187. static OPERAND do_or(OPERAND *ptop)
  188. #else
  189. static OPERAND do_or(ptop)
  190. X    OPERAND *ptop;
  191. #endif
  192. {
  193. X    return ptop[0] | ptop[-1];
  194. }
  195. X
  196. X
  197. #ifdef PROTOS
  198. static OPERAND do_xor(OPERAND *ptop)
  199. #else
  200. static OPERAND do_xor(ptop)
  201. X    OPERAND *ptop;
  202. #endif
  203. {
  204. X    return ptop[0] ^ ptop[-1];
  205. }
  206. X
  207. X
  208. #ifdef PROTOS
  209. static OPERAND do_negate(OPERAND *ptop)
  210. #else
  211. static OPERAND do_negate(ptop)
  212. X    OPERAND *ptop;
  213. #endif
  214. {
  215. X    return ! ptop[0];
  216. }
  217. X
  218. X
  219. /*
  220. X * lookup_token - look up token in table; return pointer to table entry
  221. X */
  222. #ifdef PROTOS
  223. static TOKEN *lookup_token(char *p)
  224. #else
  225. static TOKEN *lookup_token(p)
  226. X    char *p;
  227. #endif
  228. {
  229. X    TOKEN *ptok;
  230. X
  231. X    for (ptok = token_tbl;
  232. X         ptok->name && strncmp(p, ptok->name, strlen(ptok->name));
  233. X         ptok++)
  234. X        ;
  235. X
  236. X    return ptok;
  237. }
  238. X
  239. X
  240. /*
  241. X * next_token - fetch next token from input string; fill in its type and value
  242. X * and return pointer to following character
  243. X */
  244. #ifdef PROTOS
  245. static char *next_token(char *p,
  246. X            int *ptype,
  247. X            int *pvalue)
  248. #else
  249. static char *next_token(p, ptype, pvalue)
  250. X    char *p;
  251. X    int *ptype;
  252. X    int *pvalue;
  253. #endif
  254. {
  255. X    TOKEN *ptok;
  256. X    char tokbuf[STRSIZ], *pb;
  257. X
  258. #define NT_RETURN(p, t, v) \
  259. X    if (1) { *ptype = t; *pvalue = v; return p; } else
  260. X
  261. X    while (*p && isspace(*p))    /* skip whitespace */
  262. X        p++;
  263. X
  264. X    if (*p == '\0')            /* end of input? */
  265. X        NT_RETURN(p, TK_ENDINPUT, 0);
  266. X
  267. X    if (isalpha(*p)) {        /* identifier? */
  268. X
  269. X        pb = tokbuf;        /* make local copy and look up */
  270. X        while (*p && (isalpha(*p) || isdigit(*p) || *p == '_'))
  271. X            *pb++ = *p++;
  272. X        *pb = '\0';
  273. X
  274. X        NT_RETURN(p, TK_IDENT, find_sym(tokbuf));
  275. X    }
  276. X
  277. X    ptok = lookup_token(p);        /* other token */
  278. X    NT_RETURN(p + (ptok->name ? strlen(ptok->name) : 1), ptok->type,
  279. X        ptok->value);
  280. }
  281. X
  282. X
  283. /*
  284. X * parse_expr - parses expression consisting of identifiers and logical
  285. X * operators; return TRUE if expression is true (identifier defined => true);
  286. X * FALSE if false; EXPR_ERR if syntax error in expression
  287. X */
  288. #ifdef PROTOS
  289. int parse_expr(char *pbuf)
  290. #else
  291. int parse_expr(pbuf)
  292. X    char *pbuf;
  293. #endif
  294. {
  295. X    OPERAND opd_stack[MAX_OP];    /* operand stack - TRUE/FALSE values */
  296. X    OPERATOR opr_stack[MAX_OP];    /* operator stack - precedence | op */
  297. X    int value, token, plevel, prec, result, npop, opr, opd, prev_token, op;
  298. X
  299. X    plevel = 0;            /* paren nesting level */
  300. X    opd = opr = -1;            /* indices of stack tops */
  301. X    prev_token = TK_STARTINPUT;    /* to detect null expressions */
  302. X
  303. X    do {
  304. X        pbuf = next_token(pbuf, &token, &value);
  305. X
  306. X        /* check that the current token may follow the previous one */
  307. X        if (! IS_LEGAL(token, prev_token))
  308. X            return EXPR_ERR;
  309. X
  310. X        switch(token) {
  311. X
  312. X        case TK_IDENT:        /* identifier => 1 if def, 0 if not */
  313. X            opd_stack[++opd] = value != PP_SYM_UNDEF;
  314. X            break;
  315. X
  316. X        case TK_LPAREN:        /* left paren - bump nesting level */
  317. X            ++plevel;
  318. X            break;
  319. X
  320. X        case TK_RPAREN:        /* right paren - decrement nesting */
  321. X            if (--plevel < 0)
  322. X                return EXPR_ERR;
  323. X            break;
  324. X
  325. X        case TK_ENDINPUT:    /* end-of-input - treat as operator */
  326. X            if (prev_token == TK_STARTINPUT)
  327. X                return FALSE;    /* null expr => FALSE */
  328. X            /* fall through */
  329. X
  330. X        case TK_UNARYOP:
  331. X        case TK_BINARYOP:
  332. X
  333. X            /* get precedence of operator, adjusting for paren
  334. X             * nesting (TK_ENDINPUT has the lowest precedence
  335. X             * of all, to unwind operand/operator stacks at end)
  336. X             */
  337. X
  338. X            prec = token == TK_ENDINPUT ? ENDINPUT_PREC :
  339. X                (plevel * PAREN_PREC) + opr_tbl[value].prec;
  340. X
  341. X            /* pop (and perform) any equal- or higher-precedence
  342. X             * operators on operator stack: extract operator,
  343. X             * check for operand stack underflow, execute
  344. X             * operator, adjust operand stack height and place
  345. X             * result of operator on top
  346. X             */
  347. X
  348. X            for ( ;
  349. X                 opr >= 0 && PREC(opr_stack[opr]) >= prec;
  350. X                 opr--) {
  351. X                op = OPCODE(opr_stack[opr]);
  352. X                npop = opr_tbl[op].type == TK_UNARYOP ? 0 : 1;
  353. X                if (opd < npop)
  354. X                    return EXPR_ERR;
  355. X                result = (*opr_tbl[op].pfcn)(opd_stack + opd);
  356. X                opd_stack[opd -= npop] = result;
  357. X            }
  358. X
  359. X            /* push operator (if any) onto stack */
  360. X
  361. X            if (token != TK_ENDINPUT)
  362. X                opr_stack[++opr] = MAKE_OPR(prec, value);
  363. X
  364. X            break;
  365. X
  366. X        default:        /* should never get here */
  367. X            return EXPR_ERR;
  368. X            break;
  369. X
  370. X        }
  371. X
  372. X        prev_token = token;
  373. X
  374. X    } while (token != TK_ENDINPUT);
  375. X
  376. X    /* done - check for dangling parens, and leftover operand/operators */
  377. X
  378. X    return plevel != 0 || opd != 0 || opr != -1 ?
  379. X        EXPR_ERR :        /* leftover junk - return error */
  380. X        opd_stack[0];        /* all OK - return final value */
  381. }
  382. X
  383. SHAR_EOF
  384. chmod 0666 exprpars.c ||
  385. echo 'restore of exprpars.c failed'
  386. Wc_c="`wc -c < 'exprpars.c'`"
  387. test 8311 -eq "$Wc_c" ||
  388.     echo 'exprpars.c: original size 8311, current size' "$Wc_c"
  389. fi
  390. # ============= moon91 ==============
  391. if test -f 'moon91' -a X"$1" != X"-c"; then
  392.     echo 'x - skipping moon91 (File already exists)'
  393. else
  394. echo 'x - extracting moon91 (Text)'
  395. sed 's/^X//' << 'SHAR_EOF' > 'moon91' &&
  396. #
  397. # 1991 moon phase information (from Old Farmer's Almanac)
  398. #
  399. # This file is to be called .moon91 for Un*x, moon91.dat for VMS; it is
  400. # to live in the same directory as the .calendar file.
  401. #
  402. # Dates and times below are for Boston, EST.  The date (numeric form only)
  403. # is parsed as mm/dd or dd/mm as specified by the -A and -E flags respectively.
  404. # The time (24-hour clock) is optional; if supplied, Pcal uses it to more 
  405. # accurately calculate the phase of the moon at a fixed time each day.  You
  406. # may wish to adjust these dates and times to conform to your location.
  407. #
  408. # If Pcal detects an error (invalid date, date or phase out of sequence,
  409. # unrecognizable line) in this file, it generates an error message, closes
  410. # the file, and resorts to the default moon phase calculation algorithm.
  411. #
  412. # Moon file syntax:
  413. #    
  414. #    Pcal normally calculates the approximate phase of the moon using
  415. #    a simple algorithm which assumes (among other things) that the
  416. #    length of the lunar month is constant and that the quarter moons
  417. #    will occur on the same day worldwide.  For most users, that is
  418. #    adequate; however, moon-phase freaks may enter the dates and
  419. #    (optionally) times of quarter moons (from a reliable source such
  420. #    as an almanac or astronomical table) into a file called .moonXX 
  421. #    (moonXX.dat on VMS), where XX is the last two digits of the year.
  422. #    If such a file exists (in the same directory as the date file),
  423. #    pcal will interpolate the phase of the moon from the information
  424. #    in this file instead of using the default algorithm.
  425. #    
  426. #    Entries in the moon file must conform to the following syntax:
  427. #    
  428. #      if -A flag (American date formats) specified:
  429. #        <quarter> <month><sep><day> {<hour><sep><min>}
  430. #    
  431. #      if -E flag (European date formats) specified:
  432. #        <quarter> <day><sep><month> {<hour><sep><min>}
  433. #    
  434. #    where
  435. #    
  436. #      <quarter> := "nm", "fq" or "1q", "fm", "3q" or "lq" (new
  437. #                    moon, first quarter, full moon, last quarter)
  438. #      <hour>    := number 0-23 (24-hour clock)
  439. #      <min>     := number 0-59
  440. #    
  441. #    This file must contain entries for all quarter moons in the year,
  442. #    in chronological order; if any errors are encountered, pcal will
  443. #    revert to using its default algorithm.
  444. #    
  445. #    As in the date file, comments start with '#' and run through
  446. #    end-of-line.  
  447. X
  448. 3q 01/07 13:37        # third quarter
  449. nm 01/15 18:51        # new moon
  450. 1q 01/23 09:23        # first quarter
  451. fm 01/30 01:10        # full moon
  452. X
  453. 3q 02/06 08:53
  454. nm 02/14 12:33
  455. 1q 02/21 17:59
  456. fm 02/28 13:26
  457. X
  458. 3q 03/08 05:33
  459. nm 03/16 03:11
  460. 1q 03/23 01:03
  461. fm 03/30 02:18
  462. X
  463. 3q 04/07 01:47
  464. nm 04/14 14:38
  465. 1q 04/21 07:40
  466. fm 04/28 16:00
  467. X
  468. 3q 05/06 19:48
  469. nm 05/13 23:37
  470. 1q 05/20 14:47
  471. fm 05/28 06:38
  472. X
  473. 3q 06/05 10:31
  474. nm 06/12 07:07
  475. 1q 06/18 23:20
  476. fm 06/26 22:00
  477. X
  478. 3q 07/04 21:51
  479. nm 07/11 14:07
  480. 1q 07/18 10:12
  481. fm 07/26 13:25
  482. X
  483. 3q 08/03 06:27
  484. nm 08/09 21:28
  485. 1q 08/17 00:02
  486. fm 08/25 04:08
  487. X
  488. 3q 09/01 13:17
  489. nm 09/08 06:02
  490. 1q 09/15 17:02
  491. fm 09/23 17:41
  492. 3q 09/30 19:31
  493. X
  494. nm 10/07 16:39
  495. 1q 10/15 12:34
  496. fm 10/23 06:09
  497. 3q 10/30 02:12
  498. X
  499. nm 11/06 06:12
  500. 1q 11/14 09:02
  501. fm 11/21 17:58
  502. 3q 11/28 10:22
  503. X
  504. nm 12/05 22:57
  505. 1q 12/14 04:33
  506. fm 12/21 05:24
  507. 3q 12/27 20:56
  508. SHAR_EOF
  509. chmod 0666 moon91 ||
  510. echo 'restore of moon91 failed'
  511. Wc_c="`wc -c < 'moon91'`"
  512. test 3095 -eq "$Wc_c" ||
  513.     echo 'moon91: original size 3095, current size' "$Wc_c"
  514. fi
  515. # ============= moonphas.c ==============
  516. if test -f 'moonphas.c' -a X"$1" != X"-c"; then
  517.     echo 'x - skipping moonphas.c (File already exists)'
  518. else
  519. echo 'x - extracting moonphas.c (Text)'
  520. sed 's/^X//' << 'SHAR_EOF' > 'moonphas.c' &&
  521. /*
  522. X * moonphas.c - encapsulates routines used by Pcal for moon phase calculation
  523. X *
  524. X * Contents:
  525. X *
  526. X *        calc_phase
  527. X *        find_moonfile
  528. X *        find_phase
  529. X *        read_moonfile
  530. X *
  531. X * Revision history:
  532. X *
  533. X *    4.0    AWR    03/07/91    Add find_moonfile()
  534. X *
  535. X *            01/15/91    Author: translated PostScript
  536. X *                    routines to C and added moon
  537. X *                    file routines
  538. X *
  539. X */
  540. X
  541. /*
  542. X * Standard headers:
  543. X */
  544. X
  545. #include <stdio.h>
  546. #include <string.h>
  547. #include <ctype.h>
  548. X
  549. /*
  550. X * Pcal-specific definitions:
  551. X */
  552. X
  553. #include "pcaldefs.h"
  554. #include "pcalglob.h"
  555. #include "pcallang.h"
  556. X
  557. /*
  558. X * Macros:
  559. X */
  560. X
  561. #define PERIOD        29.5306        /* average lunar month */
  562. #define DAYS_PER_YEAR    365.2422    /* true length of year */
  563. X
  564. #define FM_MONTH    2        /* reference date of known full moon */
  565. #define FM_DAY        9
  566. #define FM_YEAR        1990
  567. X
  568. #define HOUR        12        /* hour of day when phase calculated */
  569. X
  570. /* convert "n" so that 0.0 <= n < 1.0 */
  571. #define NORMALIZE(n)    \
  572. X    if (1) { while (n < 0.0) n++; while (n >= 1.0) n--; } else
  573. X
  574. /* interpolate phase for day "d" from moon_info array elements "n1" and "n2" */
  575. #define CALC_PHASE(d, n1, n2) \
  576. X     moon_info[n1].phase + ((d) - moon_info[n1].doy) * \
  577. X    ((moon_info[n2].phase - moon_info[n1].phase) / \
  578. X     (moon_info[n2].doy - moon_info[n1].doy))
  579. X
  580. /* generate error message, close file, and quit */
  581. #define ERR_EXIT(msg)    \
  582. X    if (1) { ERR(msg); fclose(fp); return FALSE; } else
  583. X
  584. /* day and phase sequence error conditions - cf. read_moonfile() */
  585. #define DAY_TOO_SOON    (nrec > 1 && doy < prevdoy + 6)
  586. #define DAY_TOO_LATE    (doy > prevdoy + 9)
  587. #define WRONG_PHASE    (nrec > 1 && ph != (prevph + 1) % 4)
  588. X
  589. X
  590. /*
  591. X * Globals:
  592. X */
  593. X
  594. typedef struct {
  595. X    int    doy;    /* day of year (1..366) */
  596. X    double    phase;    /* moon phase (cycles since new moon prior to 1/1) */
  597. } MOON_INFO;
  598. X
  599. static MOON_INFO moon_info[60];        /* quarter moons for year + dummies */
  600. X
  601. X
  602. /*
  603. X * Routines to calculate moon phase when no moon file exists:
  604. X *
  605. X * User may substitute any phase-of-the-moon routine desired for calc_phase()
  606. X * as long as it returns a double value in range 0.0 <= val < 1.0:
  607. X *
  608. X *        0.0    new moon
  609. X *        0.25    first quarter
  610. X *        0.5    full moon
  611. X *        0.75    third quarter
  612. X *
  613. X * (N.B.: The most accurate moon phase routines compensate for variations
  614. X * in the length of the lunar month.  In that case, is_quarter() might also
  615. X * require some modification to prevent spurious or missing quarter-moon
  616. X * dates when the lunar month is shorter or longer than average.)
  617. X */
  618. X
  619. X
  620. /*
  621. X * calc_phase - return phase of moon on month/day/year (adapted from Mark
  622. X * Hanson's PostScript version)
  623. X */
  624. X
  625. #ifdef PROTOS
  626. double calc_phase(int month,
  627. X          int day,
  628. X          int year)
  629. #else
  630. double calc_phase(month, day, year)
  631. X    int month, day, year;
  632. #endif
  633. {
  634. X    double daysdiff, phase;
  635. X    long yearsdiff, cycles;
  636. X
  637. X    daysdiff = (DAY_OF_YEAR(month, day, year) - DAY_OF_YEAR(FM_MONTH,
  638. X            FM_DAY, FM_YEAR)) * (DAYS_PER_YEAR / 365.0);
  639. X
  640. X    if ((yearsdiff = year - FM_YEAR) != 0)
  641. X        daysdiff += (yearsdiff * DAYS_PER_YEAR) - ((yearsdiff / 100) -
  642. X                (yearsdiff / 400));
  643. X
  644. X    cycles = (long) (daysdiff / PERIOD);
  645. X    phase = (daysdiff - (cycles * PERIOD) - 0.5 * PERIOD) / PERIOD;
  646. X    NORMALIZE(phase);    /* tweak so 0.0 <= phase < 1.0 */
  647. X    return phase;
  648. }
  649. X
  650. X
  651. /*
  652. X * is_quarter - is "phase" within 0.5 day of a quarter moon
  653. X */
  654. #ifdef PROTOS
  655. static int is_quarter(double phase)
  656. #else
  657. static int is_quarter(phase)
  658. X    double phase;
  659. #endif
  660. {
  661. X
  662. X    phase *= PERIOD;
  663. X    return (phase >= PERIOD - 0.5 || phase < 0.5) ||
  664. X           (phase >= 0.25 * PERIOD - 0.5 && phase < 0.25 * PERIOD + 0.5) ||
  665. X           (phase >= 0.50 * PERIOD - 0.5 && phase < 0.50 * PERIOD + 0.5) ||
  666. X           (phase >= 0.75 * PERIOD - 0.5 && phase < 0.75 * PERIOD + 0.5);
  667. }
  668. X
  669. X
  670. /*
  671. X * Routines to read moon file and calculate moon phase from data within
  672. X */
  673. X
  674. /*
  675. X * get_phase - convert moon phase string to appropriate value
  676. X */
  677. #ifdef PROTOS
  678. static int get_phase(char *cp)
  679. #else
  680. static int get_phase(cp)
  681. X    char *cp;
  682. #endif
  683. {
  684. X    KWD *p;
  685. X
  686. X    if (!cp)
  687. X        return MOON_OTHER;
  688. X
  689. X    for (p = phases; p->name && ci_strcmp(cp, p->name); p++)
  690. X        ;
  691. X
  692. X    return p->code;
  693. }
  694. X
  695. X
  696. /*
  697. X * make_moonpath - create the full path for the moon file in 'filename';
  698. X * return pointer to 'filename'
  699. X */
  700. #ifdef PROTOS
  701. static char *make_moonpath(char *filename, char *name, int year)
  702. #else
  703. static char *make_moonpath(filename, name, year)
  704. X    char *filename;        /* full path name (output) */
  705. X    char *name;        /* base file name */
  706. X    int year;        /* year */
  707. #endif
  708. {
  709. X    char tmp[20], path[STRSIZ], *p;
  710. X
  711. X    strcpy(tmp, name);
  712. X    p = strchr(tmp, 'X');            /* replace XX with year % 100 */
  713. X    *p++ = '0' + (year / 10) % 10;
  714. X    *p   = '0' + year % 10;
  715. X
  716. X    mk_path(path, datefile);        /* get datefile path */
  717. X    mk_filespec(filename, path, tmp);    /* append file name */
  718. X    
  719. X    return filename;
  720. }
  721. X
  722. X
  723. /*
  724. X * find_moonfile - look for moon file for specified year.  If it exists
  725. X * and is readable, return its full path name; else return NULL.  (There
  726. X * are admittedly ways to do this without attempting to open the file,
  727. X * but they may not be portable.)
  728. X */
  729. #ifdef PROTOS
  730. char *find_moonfile(int year)
  731. #else
  732. char *find_moonfile(year)
  733. X    int year;
  734. #endif
  735. {
  736. X    static char filename[STRSIZ];
  737. X    FILE *fp;
  738. X
  739. X    fp = fopen(make_moonpath(filename, MOONFILE, year), "r");
  740. X
  741. #ifdef ALT_MOONFILE
  742. X    if (!fp)             /* try again with alternate name */
  743. X        fp = fopen(make_moonpath(filename, ALT_MOONFILE, year), "r");
  744. #endif
  745. X    return fp ? (fclose(fp), filename) : NULL;
  746. }
  747. X
  748. X
  749. /*
  750. X * read_moonfile - looks for moon data file (in same directory as .calendar);
  751. X * if found, reads file, fills in moon_info[] and returns TRUE; if not found
  752. X * (or error encountered), returns FALSE
  753. X */
  754. #ifdef PROTOS
  755. int read_moonfile(int year)
  756. #else
  757. int read_moonfile(year)
  758. X    int year;
  759. #endif
  760. {
  761. X    char *filename;
  762. X    int line, nrec, month, day, hh, mm;
  763. X    int ph, prevph = MOON_OTHER, doy, prevdoy, n, quarter;
  764. X    double phase;
  765. X    FILE *fp;
  766. X
  767. X    if (! *datefile)            /* skip if no datefile */
  768. X        return FALSE;
  769. X
  770. X    /* get name of moon file and attempt to open it */
  771. X
  772. X    if ((filename = find_moonfile(year)) == NULL ||
  773. X        (fp = fopen(filename, "r")) == NULL)
  774. X        return FALSE;
  775. X
  776. X    /*
  777. X     * Moon file entries are of the form <phase> <date> {<time>}; each
  778. X     * is converted below to a moon_info[] record (note that only the
  779. X     * initial phase of the moon is directly calculated from <phase>;
  780. X     * it is subsequently used only for error checking).  Dummy entries
  781. X     * in moon_info[] precede and follow the information read from the
  782. X     * moon file; these are used for subsequent interpolation of dates
  783. X     * before the first / after the last quarter of the year.
  784. X     */
  785. X
  786. X    nrec = 1;                /* skip dummy entry */
  787. X    prevdoy = 0;
  788. X    line = 0;
  789. X
  790. X    while (getline(fp, &line)) {
  791. X
  792. X        if ((n = loadwords()) < 2 ||    /* recognizable line? */
  793. X            (ph = get_phase(words[0])) == MOON_OTHER)
  794. X            ERR_EXIT(E_INV_LINE);
  795. X
  796. X        if (nrec == 1)            /* phase at initial quarter */
  797. X            quarter = ph == MOON_NM ? 4 : ph;
  798. X
  799. X        /* extract the month and day fields (in appropriate order) */
  800. X
  801. X        (void) split_date(words[1],
  802. X                  date_style == USA_DATES ? &month : &day,
  803. X                  date_style == USA_DATES ? &day : &month,
  804. X                  NULL);
  805. X
  806. X        /* validate the date and phase */
  807. X
  808. X        if (!is_valid(month, day, year))    /* date OK? */
  809. X            ERR_EXIT(E_INV_DATE);
  810. X
  811. X        doy = DAY_OF_YEAR(month, day, year);    /* in sequence? */
  812. X        if (DAY_TOO_SOON || DAY_TOO_LATE || WRONG_PHASE)
  813. X            ERR_EXIT(E_DATE_SEQ);
  814. X
  815. X        prevdoy = doy;            /* save for sequence check */
  816. X        prevph = ph;
  817. X
  818. X        /* calculate moon phase, factoring in time (if present) */
  819. X
  820. X        phase = 0.25 * quarter++;
  821. X        if (n > 2) {            /* extract hour and minute */
  822. X            (void) split_date(words[2], &hh, &mm, NULL);
  823. X            phase += (HOUR - (hh + (mm / 60.0))) / (24 * PERIOD); 
  824. X        }
  825. X        moon_info[nrec].doy = doy;    /* enter day and phase */
  826. X        moon_info[nrec++].phase = phase;
  827. X    }
  828. X
  829. X    /* check to see that file is all there */
  830. X
  831. X    doy = YEAR_LEN(year) + 1;    /* day after end of year */
  832. X    if (DAY_TOO_LATE)
  833. X        ERR_EXIT(E_PREM_EOF);
  834. X
  835. X    /* extrapolate dummy entries from nearest lunar month */
  836. X
  837. X    moon_info[nrec].doy = doy;    /* day after end of year */
  838. X    moon_info[nrec].phase = CALC_PHASE(doy, nrec-5, nrec-1);
  839. X
  840. X    moon_info[0].doy = 0;        /* day before start of year */
  841. X    moon_info[0].phase = CALC_PHASE(0, 1, 5);
  842. X    
  843. X    fclose(fp);
  844. X    return TRUE;
  845. }
  846. X
  847. X
  848. /*
  849. X * find_phase - look up phase of moon in moon phase file (if possible);
  850. X * otherwise calculate it using calc_phase() above.  Sets *pquarter to
  851. X * TRUE if date is a quarter moon, FALSE if not
  852. X */
  853. #ifdef PROTOS
  854. double find_phase(int month,
  855. X          int day,
  856. X          int year,
  857. X          int *pquarter)
  858. #else
  859. double find_phase(month, day, year, pquarter)
  860. X    int month, day, year;
  861. X    int *pquarter;
  862. #endif
  863. {
  864. X    static int sv_year = 0;
  865. X    static int use_file;
  866. X    int i, doy;
  867. X    double phase;
  868. X
  869. X    if (year != sv_year) {        /* look for file for new year */
  870. X        use_file = read_moonfile(year);
  871. X        sv_year = year;
  872. X    }
  873. X
  874. X    if (! use_file) {        /* no file - calculate date */
  875. X        phase = calc_phase(month, day, year);
  876. X        *pquarter = is_quarter(phase);
  877. X        return phase;
  878. X    }
  879. X
  880. X    /* moon file found - use the data extracted from it */
  881. X
  882. X    doy = DAY_OF_YEAR(month, day, year);
  883. X
  884. X    for (i = 1; doy > moon_info[i].doy; i++)    /* find interval */
  885. X        ;
  886. X
  887. X    /* if day appears in table, return exact value; else interpolate */
  888. X
  889. X    phase = (*pquarter = (doy == moon_info[i].doy)) ? moon_info[i].phase :
  890. X            CALC_PHASE(doy, i-1, i);
  891. X    return phase - (int)phase;            /* 0.0 <= phase < 1.0 */
  892. }
  893. X
  894. SHAR_EOF
  895. chmod 0666 moonphas.c ||
  896. echo 'restore of moonphas.c failed'
  897. Wc_c="`wc -c < 'moonphas.c'`"
  898. test 9127 -eq "$Wc_c" ||
  899.     echo 'moonphas.c: original size 9127, current size' "$Wc_c"
  900. fi
  901. # ============= noprotos.h ==============
  902. if test -f 'noprotos.h' -a X"$1" != X"-c"; then
  903.     echo 'x - skipping noprotos.h (File already exists)'
  904. else
  905. echo 'x - extracting noprotos.h (Text)'
  906. sed 's/^X//' << 'SHAR_EOF' > 'noprotos.h' &&
  907. /*
  908. X * noprotos.h - K&R-style function declarations for Pcal sources
  909. X *
  910. X * Revision history:
  911. X *
  912. X *    4.0    AWR    03/01/91    use <stdlib.h> where possible
  913. X *
  914. X *        AWR    02/19/91    adapted from protos.h (q.v.)
  915. X *
  916. X */
  917. X
  918. X
  919. /*
  920. X * Declarations for functions defined in exprpars.c:
  921. X */
  922. int parse_expr();
  923. X
  924. X
  925. /*
  926. X * Declarations for functions defined in moonphas.c:
  927. X */
  928. double    calc_phase();
  929. double    find_phase();
  930. char    *find_moonfile();
  931. int    read_moonfile();
  932. X
  933. X
  934. /*
  935. X * Declarations for functions defined in pcal.c:
  936. X */
  937. FILE    *alt_fopen();
  938. char    *color_msg();
  939. int    get_args();
  940. FLAG_USAGE *get_flag();
  941. int    main();
  942. void    set_color();
  943. void    usage();
  944. X
  945. X
  946. /*
  947. X * Declarations for functions defined in pcalutil.c:
  948. X */
  949. char    *alloc();
  950. int    calc_day();
  951. int    calc_weekday();
  952. int    calc_year_day();
  953. int    ci_strcmp();
  954. int    ci_strncmp();
  955. void    copy_text();
  956. int    getline();
  957. int    is_valid();
  958. int    loadwords();
  959. char    *mk_filespec();
  960. char    *mk_path();
  961. void    normalize();
  962. int    split_date();
  963. char    *trnlog();
  964. X
  965. X
  966. /*
  967. X * Declarations for functions defined in readfile.c:
  968. X */
  969. void    cleanup();
  970. void    clear_syms();
  971. int    date_type();
  972. int    do_define();
  973. int    do_ifdef();
  974. int    do_ifndef();
  975. int    do_include();
  976. int    do_undef();
  977. int    enter_day_info();
  978. int    find_sym();
  979. year_info *find_year();
  980. int    get_keywd();
  981. int    get_month();
  982. int    get_ordinal();
  983. int    get_prep();
  984. int    get_token();
  985. int    get_weekday();
  986. int    is_anyday();
  987. int    is_holiday();
  988. int    is_weekday();
  989. int    is_workday();
  990. int    not_holiday();
  991. int    not_weekday();
  992. int    not_workday();
  993. int    parse();
  994. int    parse_date();
  995. int    parse_ord();
  996. int    parse_rel();
  997. void    read_datefile();
  998. X
  999. X
  1000. /*
  1001. X * Declarations for functions defined in writefil.c:
  1002. X */
  1003. void    def_footstring();
  1004. void    find_daytext();
  1005. void    find_holidays();
  1006. void    print_julian_info();
  1007. void    print_month();
  1008. void    print_moon_info();
  1009. void    print_text();
  1010. char    *print_word();
  1011. void    write_psfile();
  1012. X
  1013. X
  1014. /*
  1015. X * Prototypes for miscellaneous library routines (if not already included
  1016. X * via <stdlib.h> - cf. pcaldefs.h)
  1017. X */
  1018. #ifndef STDLIB
  1019. extern int    atoi();
  1020. extern char    *calloc();
  1021. extern char    *getenv();
  1022. #endif
  1023. SHAR_EOF
  1024. chmod 0666 noprotos.h ||
  1025. echo 'restore of noprotos.h failed'
  1026. Wc_c="`wc -c < 'noprotos.h'`"
  1027. test 1978 -eq "$Wc_c" ||
  1028.     echo 'noprotos.h: original size 1978, current size' "$Wc_c"
  1029. fi
  1030. # ============= pcal.c ==============
  1031. if test -f 'pcal.c' -a X"$1" != X"-c"; then
  1032.     echo 'x - skipping pcal.c (File already exists)'
  1033. else
  1034. echo 'x - extracting pcal.c (Text)'
  1035. sed 's/^X//' << 'SHAR_EOF' > 'pcal.c' &&
  1036. static char  VERSION_STRING[]    = "@(#)pcal v4.0 - print Postscript calendars";
  1037. /*
  1038. X * pcal.c - generate PostScript file to print calendar for any month and year
  1039. X *
  1040. X * The original PostScript code to generate the calendars was written by
  1041. X * Patrick Wood (Copyright (c) 1987 by Patrick Wood of Pipeline Associates,
  1042. X * Inc.), and authorized for modification and redistribution.  The calendar
  1043. X * file inclusion code was originally written in "bs(1)" by Bill Vogel of
  1044. X * AT&T.  Patrick's original PostScript was modified and enhanced several
  1045. X * times by King Ables, Tom Tessin, and others whose names have regrettably 
  1046. X * been lost.  This C version was originally created by Ken Keirnan of Pacific
  1047. X * Bell; additional enhancements by Joseph P. Larson, Ed Hand, Andrew Rogers, 
  1048. X * Mark Kantrowitz, and Joe Brownlee.  The moon routines were originally 
  1049. X * written by Mark Hanson, were improved and incorporated into this version
  1050. X * by Jamie Zawinski, and were translated from PostScript to C by Andrew Rogers.
  1051. X *
  1052. X * Contents:
  1053. X *
  1054. X *        alt_fopen
  1055. X *        color_msg
  1056. X *        get_args
  1057. X *        get_flag
  1058. X *        main
  1059. X *        set_color
  1060. X *        usage
  1061. X *
  1062. X * Revision history:
  1063. X *
  1064. X *    4.0    AWR    02/24/91    Add alt_fopen() to search for file
  1065. X *                    in alternate path; use to look for
  1066. X *                    date file in same directory as
  1067. X *                    Pcal executable (as per Floyd Miller)
  1068. X *
  1069. X *        AWR    02/19/91    Support negative ordinals (cf.
  1070. X *                    readfile.c, pcalutil.c)
  1071. X *
  1072. X *        AWR    02/06/91    Support expressions in preprocessor
  1073. X *                    "if{n}def" lines (cf. exprpars.c)
  1074. X *
  1075. X *        AWR    02/04/91    Support "even", "odd" ordinals (cf.
  1076. X *                    readfile.c) and ordinals > 5th
  1077. X *
  1078. X *        AWR    01/28/91    Support -B (leave unused boxes blank)
  1079. X *                    flag
  1080. X *
  1081. X *        AWR    01/15/91    Separated into moonphas.c, pcal.c,
  1082. X *                    pcalutil.c, readfile.c, and writefil.c;
  1083. X *                    added support for moon phase file
  1084. X *
  1085. X *        AWR    01/07/91    Support -w (whole year) flag; fix
  1086. X *                    various bugs and nonportable constructs
  1087. X *
  1088. X *    3.0    AWR    12/10/90    Support concept of "weekday", "workday",
  1089. X *                    and "holiday" (and converses)
  1090. X *
  1091. X *        AWR    11/13/90    Substantial revision of program logic:
  1092. X *                    extracted pcaldefs.h and pcallang.h,
  1093. X *                    moving all language dependencies (even
  1094. X *                    flag names) to the latter.
  1095. X *
  1096. X *                    add -I flag to reinitialize all
  1097. X *                     flags to program defaults; -j and -J
  1098. X *                    flags (print Julian dates); add -x,
  1099. X *                     -y, -X, -Y flags (as per Ed Hand)
  1100. X *                    for output scaling and translation
  1101. X *
  1102. X *                    allow "wildcard" dates (e.g., "all
  1103. X *                    Thursday{s} in Oct", "last Friday in
  1104. X *                    all") and notes ("note all <text>);
  1105. X *                    print full "help" message (including
  1106. X *                    date file syntax)
  1107. X *
  1108. X *    2.6    AWR    10/15/90    parse floating dates (e.g. "first
  1109. X *                    Monday in September") and relative
  1110. X *                    floating dates (e.g., "Friday after
  1111. X *                    fourth Thursday in November"); simplify
  1112. X *                    logic of -F option; add -F to usage 
  1113. X *                    message; replace COLOR_MSG() string
  1114. X *                    with color_msg() routine; add -h
  1115. X *                    (help message) and -A | -E (American |
  1116. X *                    European date format) flags; renamed
  1117. X *                    flag sets for clarity; more comments
  1118. X *
  1119. X *    2.5    JAB    10/04/90    added -F option
  1120. X *
  1121. X *    2.4    ---    10/01/90    * no modifications *
  1122. X *
  1123. X *    2.3    JWZ/AWR    09/18/90    added moon routines
  1124. X *
  1125. X *    2.2    AWR    09/17/90    revise logic of parse(); new usage
  1126. X *                    message
  1127. X *
  1128. X *        JAB/AWR    09/14/90    support "note" lines in date file
  1129. X *
  1130. X *    2.1    MK/AWR    08/27/90    support -L, -C, -R, -n options;
  1131. X *                    print holiday text next to date
  1132. X *
  1133. X *        AWR    08/24/90    incorporate cpp-like functionality;
  1134. X *                    add -D and -U options; save date file
  1135. X *                    information in internal data structure;
  1136. X *                    look for PCAL_OPTS and PCAL_DIR; look
  1137. X *                    for ~/.calendar and ~/calendar
  1138. X *
  1139. X *    2.0    AWR    08/08/90    included revision history; replaced -r
  1140. X *                    flag with -l and -p; replaced -s and -S
  1141. X *                    flags with -b and -g; recognize flags
  1142. X *                    set in date file; translate ( and ) in
  1143. X *                    text to octal escape sequence; usage()
  1144. X *                    message condensed to fit 24x80 screen
  1145. X *
  1146. X *    Parameters:
  1147. X *
  1148. X *        pcal [opts]        generate calendar for current month/year
  1149. X *                    (current year if -w flag specified)
  1150. X *
  1151. X *        pcal [opts] yy        generate calendar for entire year yy
  1152. X *
  1153. X *        pcal [opts] mm yy    generate calendar for month mm
  1154. X *                    (Jan = 1), year yy (19yy if yy < 100)
  1155. X *                    (12 months starting with mm/yy if -w
  1156. X *                    specified)
  1157. X *
  1158. X *        pcal [opts] mm yy n    as above, for n consecutive months (n
  1159. X *                    rounded to next multiple of 12 if -w
  1160. X *                    specified)
  1161. X *
  1162. X *    Output:
  1163. X *
  1164. X *        PostScript file to print calendars for all selected months.
  1165. X *
  1166. X *    Options:
  1167. X *
  1168. X *        -I        initialize all parameters to program defaults
  1169. X *
  1170. X *        -b <DAY>    print specified weekday in black
  1171. X *        -g <DAY>    print specified weekday in gray
  1172. X *                (default: print Saturdays and Sundays in gray)
  1173. X *        
  1174. X *        -O        print "gray" dates as outlined characters
  1175. X *
  1176. X *        -d <FONT>    specify alternate font for day names
  1177. X *                (default: Times-Bold)
  1178. X *
  1179. X *        -n <FONT>    specify alternate font for notes in boxes
  1180. X *                (default: Helvetica-Narrow)
  1181. X *
  1182. X *        -t <FONT>    specify alternate font for titles
  1183. X *                (default: Times-Bold)
  1184. X *
  1185. X *        -D <SYM>    define preprocessor symbol
  1186. X *        -U <SYM>    un-define preprocessor symbol
  1187. X *
  1188. X *        -e        generate empty calendar (ignore date file)
  1189. X *
  1190. X *        -f <FILE>    specify alternate date file (default:
  1191. X *                ~/.calendar on Un*x, SYS$LOGIN:CALENDAR.DAT
  1192. X *                on VMS, s:calendar.dat on Amiga; if
  1193. X *                environment variable [logical name on VMS]
  1194. X *                PCAL_DIR exists, looks there instead; if
  1195. X *                not found in either place, looks in same
  1196. X *                directory as Pcal executable)
  1197. X *
  1198. X *        -o <FILE>    specify alternate output file (default:
  1199. X *                stdout on Un*x, CALENDAR.PS on VMS, 
  1200. X *                RAM:calendar.ps on Amiga)
  1201. X *
  1202. X *        -L <STRING>    specify left foot string   (default: "")
  1203. X *        -C <STRING>    specify center foot string (default: "")
  1204. X *        -R <STRING>    specify right foot string  (default: "")
  1205. X *
  1206. X *        -l        generate landscape-mode calendars
  1207. X *        -p        generate portrait-mode calendars
  1208. X *                (default: landscape-mode)
  1209. X *
  1210. X *        -h        (command line only) write "usage" message
  1211. X *                to stdout
  1212. X *
  1213. X *        -m        draw a small moon icon on the days of the
  1214. X *                full, new, and half moons.
  1215. X *        -M        draw a small moon icon every day.  
  1216. X *                (default: no moons)
  1217. X *
  1218. X *        -F <DAY>    select alternate day to be displayed as the 
  1219. X *                first day of the week (default: Sunday)
  1220. X *
  1221. X *        -A        dates are in American format (e.g., 10/15/90,
  1222. X *                Oct 15) (default)
  1223. X *        -E        dates are in European format (e.g., 15.10.90,
  1224. X *                15 Oct)
  1225. X *
  1226. X *        -x <XSCALE>    These two options can be used to change
  1227. X *        -y <YSCALE>    the size of the calendar.
  1228. X *
  1229. X *        -X <XTRANS>    These two options can be used to relocate
  1230. X *        -Y <YTRANS>    the position of the calendar on the page.
  1231. X *
  1232. X *        -j        print Julian dates (day of year)
  1233. X *        -J        print Julian dates and days remaining
  1234. X *                (default: neither)
  1235. X *
  1236. X *        -w        print whole year (12 months) per page
  1237. X *
  1238. X *        -B        leave unused calendar boxes blank
  1239. X *
  1240. X *
  1241. X *    There are many ways to specify these options in addition to using the
  1242. X *    command line; this facilitates customization to the user's needs.
  1243. X *
  1244. X *    If the environment variable (global symbol on VMS) PCAL_OPTS is
  1245. X *    present, its value will be parsed as if it were a command line.
  1246. X *    Any options specified will override the program defaults.
  1247. X *
  1248. X *    All but the -e, -f, -h, -D, and -U options may be specified in the
  1249. X *    date file by including one or more lines of the form "opt <options>".
  1250. X *    Any such options override any previous values set either as program
  1251. X *    defaults, via PCAL_OPTS, or in previous "opt" lines.
  1252. X *
  1253. X *    Options explicitly specified on the command line in turn override all
  1254. X *    of the above.
  1255. X *
  1256. X *    Any flag which normally takes an argument may also be specified without
  1257. X *    an argument; this resets the corresponding option to its default.  -D
  1258. X *    alone un-defines all symbols; -U alone has no effect.
  1259. X *
  1260. X *    Parameters and flags may be mixed on the command line.  In some cases
  1261. X *    (e.g., when a parameter follows a flag without its optional argument)
  1262. X *    this may lead to ambiguity; the dummy flag '-' (or '--') may be used
  1263. X *    to separate them, i.e. "pcal -t - 9 90".
  1264. X *
  1265. X *
  1266. X *    Date file syntax:
  1267. X *
  1268. X *    The following rules describe the syntax of date file entries:
  1269. X *
  1270. X *      year <year>
  1271. X *
  1272. X *      opt <options>
  1273. X *
  1274. X *      note <month_spec> <text>
  1275. X *      note <month> <text>
  1276. X *
  1277. X *      if -A flag (American date formats) specified:
  1278. X *        <month_name> <day>{*} {<text>}
  1279. X *        <month><sep><day>{<sep><year>}{*} {<text>}
  1280. X *
  1281. X *      if -E flag (European date formats) specified:
  1282. X *        <day> <month_name>{*} {<text>}
  1283. X *        <day><sep><month>{<sep><year>}{*} {<text>}
  1284. X *
  1285. X *      <ordinal> <day_spec> in <month_spec>{*} {<text>}
  1286. X *      <day_spec> <prep> <date_spec>
  1287. X *
  1288. X *    where
  1289. X *
  1290. X *      {x}      means x is optional
  1291. X *
  1292. X *      <date_spec> := any of the above date specs (not year, note, or opt)
  1293. X *      <month_name> := first 3+ characters of name of month, or "all"
  1294. X *      <month_spec> := <month_name>, or "year"
  1295. X *      <day_name> := first 3+ characters of name of weekday, "day",
  1296. X *            "weekday", "workday", "holiday", "nonweekday",
  1297. X *            "nonworkday", or "nonholiday"
  1298. X *      <ordinal> := ordinal number ("1st", "2nd", etc.), "first" .. "fifth",
  1299. X *            "last", "even", "odd", or "all"
  1300. X *      <prep> := "before", "preceding", "after", "following", "on_or_before",
  1301. X *            or "on_or_after"
  1302. X *      <sep> := one or more non-numeric, non-space, non-'*' characters
  1303. X *      <month>, <day>, <year> are the numeric forms
  1304. X *
  1305. X *      <options> := any command-line option except -e, -f, -h, -D, -U
  1306. X *
  1307. X *    Comments start with '#' and run through end-of-line.
  1308. X *
  1309. X *    Holidays may be flagged by specifying '*' as the last character of
  1310. X *    the date field(s), e.g. "10/12* Columbus Day", "July 4* Independence
  1311. X *    Day", etc.  Any dates flagged as holidays will be printed in gray, and
  1312. X *    any associated text will appear adjacent to the date.
  1313. X *
  1314. X *    Note that the numeric date formats (mm/dd{/yy}, dd.mm{.yy}) support
  1315. X *    an optional year, which will become the subsequent default year.  The
  1316. X *    alphabetic date formats (month dd, dd month) do not support a year
  1317. X *    field; the "year yy" command is provided to reset the default year.
  1318. X *
  1319. X *    "Floating" days may be specified in the date file as "first Mon in 
  1320. X *    Sep", "last Mon in May", "4th Thu in Nov", etc.; any word may be
  1321. X *    used in place of "in".  "Relative floating" days (e.g. "Fri after 4th 
  1322. X *    Thu in Nov") are also accepted; they may span month/year bounds.
  1323. X *    Pcal also accepts date specs such as "all Friday{s} in October", "last
  1324. X *    Thursday in all", etc., and produces the expected results; "each" and
  1325. X *    "every" are accepted as synonyms for "all".  Negative ordinals are
  1326. X *    allowed; "-2nd" means "next to last".
  1327. X *
  1328. X *    The words "day", "weekday", "workday", and "holiday" may be used as
  1329. X *    wildcards: "day" matches any day, "weekday" matches any day normally
  1330. X *    printed in black, "workday" matches any day normally printed in black
  1331. X *    and not explicitly flagged as a holiday, and "holiday" matches any
  1332. X *    day explicitly flagged as a holiday.  "Nonweekday", "nonworkday",
  1333. X *    and "nonholiday" are also supported and have the obvious meanings.
  1334. X *    
  1335. X *    "Odd" and "even" do not refer to the actual date; instead, "odd"
  1336. X *    means "alternate, starting with the first"; "even" means "alternate,
  1337. X *    starting with the second".  Thus, "odd Fridays in March" refers to
  1338. X *    the first, third, and (if present) fifth Fridays in March - not to
  1339. X *    those Fridays falling on odd dates.
  1340. X *
  1341. X *    "All" refers to each individual month; "year" refers to the year
  1342. X *    as an entity.  Thus "odd Fridays in all" refers to the first/third/
  1343. X *    fifth Friday of each month, while "odd Fridays in year" refers to
  1344. X *    the first Friday of January and every other Friday thereafter.
  1345. X *
  1346. X *    Additional notes may be propagated to an empty calendar box by the
  1347. X *    inclusion of one or more lines of the form "note <month> <text>",
  1348. X *    where <month> may be numeric or alphabetic; "note all <text>"
  1349. X *    propagates <text> to each month in the current year.
  1350. X *
  1351. X *    Simple cpp-like functionality is provided.  The date file may include
  1352. X *    the following commands, which work like their cpp counterparts:
  1353. X *
  1354. X *        define <sym>
  1355. X *        undef <sym>
  1356. X *
  1357. X *        if{n}def <expr>
  1358. X *           ...
  1359. X *        { else
  1360. X *           ... }
  1361. X *        endif
  1362. X *
  1363. X *        include <file>
  1364. X *
  1365. X *    Note that these do not start with '#', which is reserved as a comment
  1366. X *    character.
  1367. X *
  1368. X *    <sym> is a symbol name consisting of a letter followed by zero or
  1369. X *    more letters, digits, or underscores ('_').  Symbol names are always
  1370. X *    treated in a case-insensitive manner.
  1371. X *
  1372. X *    <expr> is an expression consisting of symbol names joined by the logical
  1373. X *    operators (in order of precedence, high to low) '!' (unary negate), '&'
  1374. X *    (and), '^' (exclusive or), and '|' (inclusive or).  '&&' and '||' are
  1375. X *    accepted as synonyms for '&' and '|' respectively; the order of
  1376. X *    evaluation may be altered by the use of parentheses.  A symbol whose
  1377. X *    name is currently defined evaluates to TRUE; one whose name is not
  1378. X *    currently defined evaluates to FALSE.  Thus "ifdef A | B | C" is TRUE
  1379. X *    if any of the symbols A, B, and C is currently defined, and
  1380. X *    "ifdef A & B & C" is TRUE if all of them are.
  1381. X *
  1382. X *    "ifndef A | B | C" is equivalent to "ifdef !(A | B | C)" (or, using
  1383. X *    DeMorgan's Law, "ifdef !A & !B & !C") - in other words, TRUE if none of
  1384. X *    the symbols A, B, and C is currently defined.
  1385. X *
  1386. X *    "define" alone deletes all the current definitions; "ifdef" alone is
  1387. X *    always false; "ifndef" alone is always true.
  1388. X *
  1389. X *    The file name in the "include" directive may optionally be surrounded
  1390. X *    by "" or <>.  In any case, path names are taken to be relative to
  1391. X *    the location of the file containing the "include" directive.
  1392. X *
  1393. X *    
  1394. X *    Moon file syntax:
  1395. X *    
  1396. X *    Pcal normally calculates the approximate phase of the moon using
  1397. X *    a simple algorithm which assumes (among other things) that the
  1398. X *    length of the lunar month is constant and that the quarter moons
  1399. X *    will occur on the same day worldwide.  For most users, that is
  1400. X *    adequate; however, moon-phase freaks may enter the dates and
  1401. X *    (optionally) times of quarter moons (from a reliable source such
  1402. X *    as an almanac or astronomical table) into a file called .moonXX 
  1403. X *    (moonXX.dat on VMS), where XX is the last two digits of the year.
  1404. X *    If such a file exists (in the same directory as the date file),
  1405. X *    pcal will interpolate the phase of the moon from the information
  1406. X *    in this file instead of using the default algorithm.
  1407. X *    
  1408. X *    Entries in the moon file must conform to the following syntax:
  1409. X *    
  1410. X *      if -A flag (American date formats) specified:
  1411. X *        <quarter> <month><sep><day> {<hour><sep><min>}
  1412. X *    
  1413. X *      if -E flag (European date formats) specified:
  1414. X *        <quarter> <day><sep><month> {<hour><sep><min>}
  1415. X *    
  1416. X *    where
  1417. X *    
  1418. X *      <quarter> := "nm", "fq" or "1q", "fm", "3q" or "lq" (new
  1419. X *               moon, first quarter, full moon, last quarter)
  1420. X *      <hour>    := number 0-23 (24-hour clock)
  1421. X *      <min>     := number 0-59
  1422. X *    
  1423. X *    This file must contain entries for all quarter moons in the year,
  1424. X *    in chronological order; if any errors are encountered, pcal will
  1425. X *    revert to using its default algorithm.
  1426. X *    
  1427. X *    As in the date file, comments start with '#' and run through
  1428. X *    end-of-line.  
  1429. X */
  1430. X
  1431. X
  1432. /*
  1433. X * Standard headers:
  1434. X */
  1435. X
  1436. #include <stdio.h>
  1437. #include <ctype.h>
  1438. #include <time.h>
  1439. #include <string.h>
  1440. X
  1441. /*
  1442. X * Pcal-specific definitions:
  1443. X */
  1444. X
  1445. #define MAIN_MODULE    1
  1446. #include "pcaldefs.h"
  1447. #include "pcalglob.h"
  1448. #include "pcallang.h"
  1449. X
  1450. /*
  1451. X * Globals:
  1452. X */
  1453. X
  1454. static int init_month, init_year, nmonths;
  1455. X
  1456. X
  1457. /*
  1458. X * Main program - parse and validate command-line arguments, open files,
  1459. X * generate PostScript boilerplate and code to generate calendars.
  1460. X *
  1461. X * Program structure:
  1462. X *
  1463. X * main() looks for the environment variable (global symbol on VMS) PCAL_OPTS
  1464. X * and, if present, calls get_args() to parse it.  It then calls get_args()
  1465. X * again to parse the command line for the date file name, any options to be
  1466. X * in effect prior to reading the date file, and any numeric arguments (month,
  1467. X * year, number of months).  It then calls read_datefile() to read and parse
  1468. X * the date file; any "opt" lines present will override the defaults for the
  1469. X * command-line flags.  It then calls get_args() yet again to process the
  1470. X * remaining command-line flags, which in turn override any specified earlier.
  1471. X *
  1472. X * main() then attempts to open the output file (if any), and, if successful,
  1473. X * calls write_psfile() to generate the PostScript output.
  1474. X *
  1475. X */
  1476. #ifdef PROTOS
  1477. int main(int argc,
  1478. X     char **argv)
  1479. #else
  1480. int main(argc, argv)
  1481. X    int argc;
  1482. X    char **argv;
  1483. #endif
  1484. {
  1485. X    FILE *dfp = NULL;        /* date file pointer */
  1486. X    char *p, *pathlist[10];
  1487. X    char tmp[STRSIZ], progpath[STRSIZ];
  1488. X
  1489. X    INIT_COLORS;        /* copy default_color to day_color */
  1490. X
  1491. X    /* extract root program name and program path */
  1492. X
  1493. X    strcpy(progname, **argv ? *argv : "pcal");
  1494. X
  1495. X    if ((p = strrchr(progname, END_PATH)) != NULL)
  1496. X        strcpy(progname, ++p);
  1497. X    if ((p = strchr(progname, '.')) != NULL)
  1498. X        *p = '\0';
  1499. X
  1500. X    mk_path(progpath, *argv);
  1501. X
  1502. X    /* get version from VERSION_STRING (for use in PostScript comment) */
  1503. X    strcpy(tmp, VERSION_STRING + 4);
  1504. X    p = strchr(tmp, ' ') + 1;    /* skip program name */
  1505. X    *strchr(p, ' ') = '\0';        /* terminate after version */
  1506. X    strcpy(version, p);
  1507. X
  1508. X    /*
  1509. X     * Get the arguments from a) the environment variable, b) "opt" lines
  1510. X     * in the date file, and c) the command line, in that order
  1511. X     */
  1512. X
  1513. X    /* parse environment variable PCAL_OPTS as a command line */
  1514. X
  1515. X    if ((p = getenv(PCAL_OPTS)) != NULL) {
  1516. X        strcpy(lbuf, "pcal ");        /* dummy program name */
  1517. X        strcat(lbuf, p);
  1518. X        (void) loadwords();        /* split string into words */
  1519. X        if (! get_args(words, P_ENV, PCAL_OPTS)) {
  1520. X            usage(stderr, FALSE);
  1521. X            exit(EXIT_FAILURE);
  1522. X        }
  1523. X    }
  1524. X
  1525. X    /* parse command-line arguments once to find name of date file, etc. */
  1526. X
  1527. X    if (!get_args(argv, P_CMD1, NULL)) {
  1528. X        usage(stderr, FALSE);
  1529. X        exit(EXIT_FAILURE);
  1530. X    }
  1531. X
  1532. X    /* Attempt to open the date file as specified by the [-e | -f] flags */
  1533. X
  1534. X    switch (datefile_type) {
  1535. X    case NO_DATEFILE:
  1536. X        dfp = NULL;
  1537. X        break;
  1538. X
  1539. X    case USER_DATEFILE:    
  1540. X        /* Attempt to open user-specified calendar file: search
  1541. X         * first in PCAL_DIR (current directory if not defined)
  1542. X         * and then in the directory where the Pcal executable
  1543. X         * lives.  It is a fatal error if the user-specified
  1544. X         * date file cannot be found.
  1545. X         */
  1546. X        pathlist[0] = (p = trnlog(PCAL_DIR)) ? p : "";
  1547. X        pathlist[1] = progpath;
  1548. X        pathlist[2] = NULL;
  1549. X        
  1550. X        strcpy(tmp, datefile);    /* save original name for error msg */
  1551. X
  1552. X        if ((dfp = alt_fopen(datefile, tmp, pathlist, "r")) == NULL) {
  1553. X            FPR(stderr, E_FOPEN_ERR, progname, tmp);
  1554. X            exit(EXIT_FAILURE);
  1555. X        }
  1556. X        break;
  1557. X
  1558. X    case SYS_DATEFILE:
  1559. X        /* Attempt to open system-specified calendar file: search
  1560. X         * first in PCAL_DIR or HOME_DIR (current directory if
  1561. X         * neither is defined) and then in the directory where
  1562. X         * the Pcal executable lives.  It is not an error if the
  1563. X         * system-specified date file cannot be found; Pcal will
  1564. X         * simply generate an empty calendar.
  1565. X         */
  1566. X        pathlist[0] = ((p = trnlog(PCAL_DIR)) ||
  1567. X                   (p = trnlog(HOME_DIR))) ? p : "";
  1568. X        pathlist[1] = progpath;
  1569. X        pathlist[2] = NULL;
  1570. X        
  1571. X        dfp = alt_fopen(datefile, DATEFILE, pathlist, "r");
  1572. X
  1573. X        /* if the date file has not been found and ALT_DATEFILE is
  1574. X         * defined, search same paths for ALT_DATEFILE before
  1575. X         * giving up
  1576. X         */
  1577. #ifdef ALT_DATEFILE
  1578. X        if (!dfp)
  1579. X            dfp = alt_fopen(datefile, ALT_DATEFILE, pathlist, "r");
  1580. #endif
  1581. X        break;
  1582. X    }
  1583. X
  1584. X    /* read the date file (if any) and build internal data structure */
  1585. X
  1586. X    if (dfp) {
  1587. X        curr_year = init_year;
  1588. X        read_datefile(dfp, datefile);
  1589. X        fclose(dfp);
  1590. X    } else
  1591. X        datefile[0] = '\0';        /* for PostScript comment */
  1592. X
  1593. X    /* reparse command line - flags there supersede those in date file */
  1594. X
  1595. X    (void) get_args(argv, P_CMD2, NULL);
  1596. X
  1597. X    /* done with the arguments and flags - try to open the output file */
  1598. X
  1599. X    if (*outfile && freopen(outfile, "w", stdout) == (FILE *) NULL) {
  1600. X        FPR(stderr, E_FOPEN_ERR, progname, outfile);
  1601. X        exit(EXIT_FAILURE);
  1602. X    }
  1603. X
  1604. X    /* generate the PostScript code (cf. writefil.c) */
  1605. X
  1606. X    write_psfile(init_month, init_year, nmonths);
  1607. X    
  1608. X    cleanup();        /* free allocated data */
  1609. X
  1610. X    /* if output was written to a non-obvious location, tell user where */
  1611. X
  1612. #ifdef DEFAULT_OUTFILE
  1613. X    FPR(stderr, I_OUT_NAME, progname, outfile);
  1614. #endif
  1615. X
  1616. X    exit(EXIT_SUCCESS);
  1617. }
  1618. X
  1619. X
  1620. /*
  1621. X * set_color - set one or all weekdays to print in black or gray
  1622. X */
  1623. #ifdef PROTOS
  1624. void set_color(char *day,
  1625. X           int col)
  1626. #else
  1627. void set_color(day, col)
  1628. X    char *day;        /* weekday name (or "all") */
  1629. X    int  col;        /* select black or gray */
  1630. #endif
  1631. {
  1632. X    int i;
  1633. X
  1634. X    if (ci_strncmp(day, ALL, strlen(ALL)) == 0)    /* set all days */
  1635. X        for (i = 0; i < 7; i++)
  1636. X            day_color[i] = col;
  1637. X    else                        /* set single day */
  1638. X        if ((i = get_weekday(day, FALSE)) != NOT_WEEKDAY)
  1639. X            day_color[i] = col;
  1640. X
  1641. }
  1642. X
  1643. X
  1644. /*
  1645. X * get_flag() - look up flag in flag_tbl; return pointer to its entry
  1646. X * (NULL if not found)
  1647. X */
  1648. #ifdef PROTOS
  1649. FLAG_USAGE *get_flag(char flag)
  1650. #else
  1651. FLAG_USAGE *get_flag(flag)
  1652. X    char flag;
  1653. #endif
  1654. {
  1655. X    FLAG_USAGE *pflag;
  1656. X
  1657. X    for (pflag = flag_tbl; pflag->flag; pflag++)
  1658. X        if (flag == pflag->flag)
  1659. X            return pflag;
  1660. X
  1661. X    return flag ? NULL : pflag;        /* '\0' is a valid flag */
  1662. }
  1663. X
  1664. X
  1665. /*
  1666. X * get_args - walk the argument list, parsing all arguments but processing only
  1667. X * those specified (in flag_tbl[]) to be processed this pass.
  1668. X */
  1669. #ifdef PROTOS
  1670. int get_args(char **argv,
  1671. X         int curr_pass,
  1672. X         char *where)
  1673. #else
  1674. int get_args(argv, curr_pass, where)
  1675. X    char **argv;        /* argument list */
  1676. X    int  curr_pass;        /* current pass (P_xxx) */
  1677. X    char *where;        /* for error messages */
  1678. #endif
  1679. {
  1680. X    char *parg, *opt, *p;
  1681. X    FLAG_USAGE *pflag, *pf;
  1682. X    int i, flag;
  1683. X    long tmp;            /* for getting current month/year */
  1684. X    struct tm *p_tm;
  1685. X    int badopt = FALSE;        /* flag set if bad param   */
  1686. X    int nargs = 0;            /* count of non-flag args  */
  1687. X    int numargs[MAXARGS];        /* non-flag (numeric) args */
  1688. X
  1689. /*
  1690. X * If argument follows flag (immediately or as next parameter), return
  1691. X * pointer to it (and bump argv if necessary); else return NULL
  1692. X */
  1693. #define GETARG() (*(*argv + 2) ? *argv + 2 : \
  1694. X          (*(argv+1) && **(argv+1) != '-' ? *++argv : NULL))
  1695. X
  1696. /*
  1697. X * Must parse numeric parameters on both command-line passes: before reading
  1698. X * datefile (in order to set default year) and again after reading datefile
  1699. X * (in order to set the default first month to January if -w flag was set
  1700. X * inside the datefile)
  1701. X */
  1702. #define PARSE_NUM_PARAMS    (curr_pass == P_CMD1 || curr_pass == P_CMD2)
  1703. X
  1704. X    /* Walk argument list, ignoring first element (program name) */
  1705. X
  1706. X     while (opt = *++argv) {
  1707. X
  1708. X        /* Assume that any non-flag argument is a numeric argument */
  1709. X        if (*opt != '-') {
  1710. X                if (PARSE_NUM_PARAMS && nargs < MAXARGS) {
  1711. X                if (! IS_NUMERIC(opt))
  1712. X                    goto bad_par;
  1713. X                numargs[nargs++] = atoi(opt);
  1714. X            }
  1715. X            continue;
  1716. X        }
  1717. X
  1718. X        /* Check that flag is a) legal, and b) to be processed this pass */
  1719. X
  1720. X        if (! (pflag = get_flag(flag = opt[1])) )
  1721. X            goto bad_par;
  1722. X
  1723. X        /* get optional argument even if flag not processed this pass */
  1724. X
  1725. X        parg = pflag->has_arg ? GETARG() : NULL;
  1726. X
  1727. X        if (! (pflag->passes & curr_pass)) {    /* skip flag this pass? */
  1728. X            if (curr_pass == P_OPT)
  1729. X                FPR(stderr, E_FLAG_IGNORED, progname, flag,
  1730. X                    DATE_FILE, where);
  1731. X            continue;
  1732. X        }
  1733. X
  1734. X        switch (flag) {
  1735. X
  1736. X        case F_INITIALIZE:    /* reset all flags to defaults */
  1737. X
  1738. X            /* set up a command line to reset all of the
  1739. X             * flags; call get_args() recursively to parse it
  1740. X             * (note that some of the flags must be reset
  1741. X             * explicitly, as no command-line flags exist to
  1742. X             * reset them)
  1743. X             */
  1744. X
  1745. X            /* reset flags described above */
  1746. X            julian_dates  = JULIAN_DATES;
  1747. X            draw_moons    = DRAW_MOONS;
  1748. X            do_whole_year = FALSE;
  1749. X            blank_boxes   = FALSE;
  1750. X            outline_nums  = FALSE;
  1751. X
  1752. X            /* select program default for landscape/portrait
  1753. X             * mode and US/European date styles
  1754. X             */
  1755. X            sprintf(lbuf, "pcal -%c -%c",
  1756. #if (ROTATE == LANDSCAPE)
  1757. X                F_LANDSCAPE,
  1758. #else
  1759. X                F_PORTRAIT,
  1760. #endif
  1761. #if (DATE_STYLE == USA_DATES)
  1762. X                F_USA_DATES);
  1763. #else
  1764. X                F_EUR_DATES);
  1765. #endif
  1766. X            p = lbuf + strlen(lbuf);
  1767. X
  1768. X            /* all other flags take arguments and are reset
  1769. X             * by specifying the flag without an argument
  1770. X             */
  1771. X            for (pf = flag_tbl; pf->flag; pf++)
  1772. X                if ((pf->passes & curr_pass) && pf->has_arg) {
  1773. X                    sprintf(p, " -%c", pf->flag);
  1774. X                    p += strlen(p);
  1775. X                }
  1776. X
  1777. X            /* split new command line into words; parse it */
  1778. X            (void) loadwords();
  1779. X            (void) get_args(words, curr_pass, NULL);
  1780. X
  1781. X            break;
  1782. X
  1783. X        case F_BLACK_DAY:    /* print day in black or gray */
  1784. X        case F_GRAY_DAY:
  1785. X            if (parg)
  1786. X                set_color(parg, flag == F_BLACK_DAY ?
  1787. X                          BLACK : GRAY);
  1788. X            else
  1789. X                INIT_COLORS;    /* reset to defaults */
  1790. X            break;
  1791. X
  1792. X         case F_DAY_FONT:    /* specify alternate day font */
  1793. X            strcpy(dayfont, parg ? parg : DAYFONT);
  1794. X             break;
  1795. X
  1796. X        case F_NOTES_FONT:    /* specify alternate notes font */
  1797. X            strcpy(notesfont, parg ? parg : NOTESFONT);
  1798. X            break;
  1799. X
  1800. X         case F_TITLE_FONT:    /* specify alternate title font */
  1801. X            strcpy(titlefont, parg ? parg : TITLEFONT);
  1802. X             break;
  1803. X
  1804. X        case F_EMPTY_CAL:    /* generate empty calendar */
  1805. X            datefile_type = NO_DATEFILE;
  1806. X            strcpy(datefile, "");
  1807. X            break;
  1808. X
  1809. X        case F_DATE_FILE:    /* specify alternate date file */
  1810. X            datefile_type = parg ? USER_DATEFILE : SYS_DATEFILE;
  1811. X            strcpy(datefile, parg ? parg : "");
  1812. X            break;
  1813. X
  1814. X        case F_OUT_FILE:    /* specify alternate output file */
  1815. X            strcpy(outfile, parg ? parg : OUTFILE);
  1816. X            break;
  1817. X
  1818. X        case F_LANDSCAPE:    /* generate landscape calendar */
  1819. X             rotate = LANDSCAPE;
  1820. X            strcpy(xsval, XSVAL_L);
  1821. X            strcpy(ysval, YSVAL_L);
  1822. X            strcpy(xtval, XTVAL_L);
  1823. X            strcpy(ytval, YTVAL_L);
  1824. X             break;
  1825. X        case F_PORTRAIT:    /* generate portrait calendar */
  1826. X             rotate = PORTRAIT;
  1827. X            strcpy(xsval, XSVAL_P);
  1828. X            strcpy(ysval, YSVAL_P);
  1829. X            strcpy(xtval, XTVAL_P);
  1830. X            strcpy(ytval, YTVAL_P);
  1831. X             break;
  1832. X
  1833. X        case F_HELP:        /* request "help" message */
  1834. X            FPR(stdout, "%s\n", VERSION_STRING + 4);
  1835. X            usage(stdout, TRUE);
  1836. X            exit(EXIT_SUCCESS);
  1837. X            break;
  1838. X
  1839. X        case F_MOON_4:        /* draw four moons */
  1840. X        case F_MOON_ALL:    /* draw a moon for each day */
  1841. X            draw_moons = flag == F_MOON_ALL ? ALL_MOONS : SOME_MOONS;
  1842. X            break;
  1843. X
  1844. X        case F_DEFINE:        /* define preprocessor symbol */
  1845. X            (void) do_define(parg);
  1846. X            break;
  1847. X
  1848. X        case F_UNDEF:        /* undef preprocessor symbol */
  1849. X            (void) do_undef(parg);
  1850. X            break;
  1851. X
  1852. X        case F_L_FOOT:        /* specify alternate left foot */
  1853. X            strcpy(lfoot, parg ? parg : LFOOT);
  1854. X            break;
  1855. X
  1856. X        case F_C_FOOT:        /* specify alternate center foot */
  1857. X            strcpy(cfoot, parg ? parg : CFOOT);
  1858. X            break;
  1859. X
  1860. X         case F_R_FOOT:        /* specify alternate right foot */
  1861. X            strcpy(rfoot, parg ? parg : RFOOT);
  1862. X            break;
  1863. X
  1864. X        case F_FIRST_DAY:    /* select starting day of week */
  1865. X            if (parg) {
  1866. X                if ((i = get_weekday(parg, FALSE)) != NOT_WEEKDAY)
  1867. X                    first_day_of_week = i;
  1868. X            }
  1869. X            else
  1870. X                first_day_of_week = FIRST_DAY;
  1871. X            break;
  1872. X
  1873. X        case F_USA_DATES:    /* select American date style */
  1874. X        case F_EUR_DATES:    /* select European date style */
  1875. X            date_style = flag == F_USA_DATES ? USA_DATES : EUR_DATES;
  1876. X            break;
  1877. X
  1878. X        case F_X_TRANS:        /* set x-axis translation factor */
  1879. X            strcpy(xtval, parg ? parg :
  1880. X                (rotate == LANDSCAPE ? XTVAL_L : XTVAL_P));
  1881. X            break;
  1882. X
  1883. X        case F_Y_TRANS:        /* set y-axis translation factor */
  1884. X            strcpy(ytval, parg ? parg :
  1885. X                (rotate == LANDSCAPE ? YTVAL_L : YTVAL_P));
  1886. X            break;
  1887. X
  1888. X        case F_X_SCALE:        /* set x-axis scaling factor */
  1889. X            strcpy(xsval, parg ? parg :
  1890. X                (rotate == LANDSCAPE ? XSVAL_L : XSVAL_P));
  1891. X            break;
  1892. X
  1893. X        case F_Y_SCALE:        /* set y-axis scaling factor */
  1894. X            strcpy(ysval, parg ? parg :
  1895. X                (rotate == LANDSCAPE ? YSVAL_L : YSVAL_P));
  1896. X            break;
  1897. X
  1898. X        case F_JULIAN:
  1899. X        case F_JULIAN_ALL:
  1900. X            julian_dates = flag == F_JULIAN_ALL ? ALL_JULIANS :
  1901. X                                  SOME_JULIANS;
  1902. X            break;
  1903. X
  1904. X        case F_WHOLE_YEAR:
  1905. X            do_whole_year = TRUE;
  1906. X            break;
  1907. X
  1908. X        case F_BLANK_BOXES:
  1909. X            blank_boxes = TRUE;
  1910. X            break;
  1911. X
  1912. X        case F_OUTLINE:
  1913. X            outline_nums = TRUE;
  1914. X            break;
  1915. X
  1916. X        case '-' :        /* accept - and -- as dummy flags */
  1917. X        case '\0':
  1918. X            break;
  1919. X
  1920. X        default:        /* missing case label if reached!!! */
  1921. X
  1922. bad_par:                /* unrecognized parameter */
  1923. X
  1924. X            FPR(stderr, E_ILL_OPT, progname, opt);
  1925. X            if (where)
  1926. X                FPR(stderr, E_ILL_OPT2,
  1927. X                    curr_pass == P_ENV ? ENV_VAR :
  1928. X                    curr_pass == P_OPT ? DATE_FILE : "",
  1929. X                    where);
  1930. X            FPR(stderr, "\n");
  1931. X            badopt = TRUE;
  1932. X            break;
  1933. X        }
  1934. X        }
  1935. X
  1936. X    if (! PARSE_NUM_PARAMS)
  1937. X        return !badopt;        /* return TRUE if OK, FALSE if error */
  1938. X
  1939. X    /* Validate non-flag (numeric) parameters */
  1940. X
  1941. X    switch (nargs) {
  1942. X    case 0:        /* no arguments - print current month and/or year */
  1943. X        time(&tmp);
  1944. X        p_tm = localtime(&tmp);
  1945. X        init_month = do_whole_year ? JAN : p_tm->tm_mon + 1;
  1946. X        init_year = p_tm->tm_year;
  1947. X        nmonths = 1;
  1948. X        break;            
  1949. X    case 1:        /* one argument - print entire year */
  1950. X        init_month = JAN;
  1951. X        init_year = numargs[0];
  1952. X        nmonths = 12;
  1953. X        break;
  1954. X    default:    /* two or three arguments - print one or more months */
  1955. X        init_month = numargs[0];
  1956. X        init_year = numargs[1];
  1957. X        nmonths = nargs > 2 ? numargs[2] : 1;
  1958. X        break;
  1959. X    }
  1960. X
  1961. X    if (nmonths < 1)        /* ensure at least one month */
  1962. X        nmonths = 1;
  1963. X
  1964. X    /* check range of month and year */
  1965. X
  1966. X    if (init_month < JAN || init_month > DEC) {
  1967. X        FPR(stderr, E_ILL_MONTH, progname, init_month, JAN, DEC);
  1968. X        badopt = TRUE;
  1969. X    }
  1970. X    
  1971. X    if (init_year > 0 && init_year < 100)    /* treat nn as 19nn */
  1972. X        init_year += CENTURY;
  1973. X    
  1974. X    if (init_year < MIN_YR || init_year > MAX_YR) {
  1975. X        FPR(stderr, E_ILL_YEAR, progname, init_year, MIN_YR, MAX_YR);
  1976. X        badopt = TRUE;
  1977. X    }
  1978. X
  1979. X    return !badopt;        /* return TRUE if OK, FALSE if error */
  1980. }
  1981. X
  1982. X
  1983. X
  1984. /*
  1985. X * color_msg - return character string explaining default day colors
  1986. X */
  1987. #ifdef PROTOS
  1988. char *color_msg(void)
  1989. #else
  1990. char *color_msg()
  1991. #endif
  1992. {
  1993. X    int i, ngray = 0, others;
  1994. X    static char msg[80];
  1995. X
  1996. X    for (i = SUN; i <= SAT; i++)    /* count gray weekdays */
  1997. X        if (default_color[i] == GRAY)
  1998. X            ngray++;
  1999. X
  2000. X    if (ngray == 0 || ngray == 7) {        /* all same color? */
  2001. X        sprintf(msg, COLOR_MSG_1, ngray ? W_GRAY : W_BLACK);
  2002. X        return msg;
  2003. X    }
  2004. X
  2005. X    others = ngray <= 3 ? BLACK : GRAY;    /* no - get predominant color */
  2006. X    msg[0] = '\0';
  2007. X    for (i = SUN; i <= SAT; i++)
  2008. X        if (default_color[i] != others) {
  2009. X            strncat(msg, days[i], MIN_DAY_LEN);
  2010. X            strcat(msg, "/");
  2011. X        }
  2012. X    LASTCHAR(msg) = ' ';
  2013. X
  2014. X    sprintf(msg + strlen(msg), COLOR_MSG_2,
  2015. X        others == BLACK ? W_GRAY : W_BLACK,
  2016. X                others == BLACK ? W_BLACK : W_GRAY);
  2017. X    return msg;
  2018. }
  2019. X
  2020. X
  2021. /*
  2022. X * usage - print message explaining correct usage of the command-line
  2023. X * arguments and flags.  If "fullmsg" is true, print associated text
  2024. X */
  2025. #ifdef PROTOS
  2026. void usage(FILE *fp,
  2027. X       int fullmsg)
  2028. #else
  2029. void usage(fp, fullmsg)
  2030. X    FILE *fp;    /* destination of usage message */
  2031. X    int fullmsg;    /* print complete message? */
  2032. #endif
  2033. {
  2034. X    FLAG_MSG *pflag;
  2035. X    PARAM_MSG *ppar;
  2036. X    DATE_MSG *pdate;
  2037. X    char buf[30], *p, flag, *meta;
  2038. X    int nchars, first, i, indent, n;
  2039. X
  2040. X    sprintf(buf, "%s: %s", W_USAGE, progname);
  2041. X    nchars = indent = strlen(buf);
  2042. X    first = TRUE;
  2043. X    meta = p = NULL;
  2044. X    FPR(fp, "\n%s", buf);
  2045. X
  2046. X    /* loop to print command-line syntax message (by group of flags) */
  2047. X
  2048. X    for (pflag = flag_msg; (flag = pflag->flag) != '\0'; pflag++) {
  2049. X        if (flag == '\n') {        /* end of group? */
  2050. X            if (p)
  2051. X                *p = '\0';
  2052. X            if (meta) {        /* append metavariable name */
  2053. X                strcat(buf, " ");
  2054. X                strcat(buf, meta);
  2055. X            }
  2056. X            strcat(buf, "]");
  2057. X            n = strlen(buf);
  2058. X            if (nchars + n > SCREENWIDTH) {    /* does it fit on line? */
  2059. X                FPR(fp, "\n");        /* no - start new one */
  2060. X                for (nchars = 0; nchars < indent; nchars++)
  2061. X                    FPR(fp, " ");
  2062. X            }
  2063. X            FPR(fp, "%s", buf);
  2064. X            nchars += n;
  2065. X            first = TRUE;
  2066. X        }
  2067. X        else if (flag != ' ') {        /* accumulate flags for group */
  2068. X            if (first) {
  2069. X                sprintf(buf, " [");
  2070. X                p = buf + strlen(buf);
  2071. X            }
  2072. X            else
  2073. X                *p++ = '|';
  2074. X            *p++ = '-';
  2075. X            *p++ = flag;
  2076. X            meta = pflag->meta;    /* save metavariable name */
  2077. X            first = FALSE;
  2078. X        }
  2079. X    }
  2080. X
  2081. X    /* loop to print selected numeric parameter descriptions */
  2082. X
  2083. X    for (i = 0; i < PARAM_MSGS; i++) {
  2084. X        sprintf(buf, " [%s]%s", param_msg[i].desc,
  2085. X            i < PARAM_MSGS - 1 ? " |" : "");
  2086. X        n = strlen(buf);
  2087. X        if (nchars + n > SCREENWIDTH) {    /* does it fit on line? */
  2088. X            FPR(fp, "\n");        /* no - start new one */
  2089. X            for (nchars = 0; nchars < indent; nchars++)
  2090. X                FPR(fp, " ");
  2091. X        }
  2092. X        FPR(fp, "%s", buf);
  2093. X        nchars += n;
  2094. X    }
  2095. X
  2096. X    FPR(fp, "\n\n");
  2097. X    if (! fullmsg) {
  2098. X        FPR(fp, USAGE_MSG, progname, F_HELP);
  2099. X        return;
  2100. X    }
  2101. X    
  2102. X    /* loop to print the full flag descriptions */
  2103. X
  2104. X    for (pflag = flag_msg; (flag = pflag->flag) != '\0'; pflag++) {
  2105. X        if (flag == '\n') {    /* newline?  print and quit */
  2106. X            FPR(fp, "\n");
  2107. X            continue;
  2108. X        }
  2109. X        p = buf;        /* copy flag and metavariable to buffer */
  2110. X        if (flag != ' ')
  2111. X            *p++ = '-';
  2112. X    /* special hack for VMS - surround upper-case flags in quotes */
  2113. #ifdef VMS
  2114. X        if (isupper(flag)) {
  2115. X            *p++ = '"';
  2116. X            *p++ = flag;
  2117. X            *p++ = '"';
  2118. X        }
  2119. X        else
  2120. X            *p++ = flag;
  2121. #else
  2122. X        *p++ = flag;
  2123. #endif
  2124. X        *p = '\0';
  2125. X        if (pflag->meta)
  2126. X            sprintf(p, " <%s>", pflag->meta);
  2127. X        FPR(fp, "\t%-16.16s", buf);
  2128. X        if (pflag->text)
  2129. X            FPR(fp, "%s", pflag->text);
  2130. X
  2131. X        /* print default value if specified */
  2132. X        if (pflag->def)
  2133. X            FPR(fp, " (%s: %s)", W_DEFAULT, pflag->def[0] ? pflag->def : "\"\"" );
  2134. X        FPR(fp, "\n");
  2135. X
  2136. X        /* special case - print color messages after F_GRAY_DAY */
  2137. X        if (flag == F_GRAY_DAY)
  2138. X            FPR(fp, "\t\t\t  (%s: %s)\n", W_DEFAULT, color_msg());
  2139. X
  2140. X    }
  2141. X    
  2142. X    /* now print the information about the numeric parameters */
  2143. X
  2144. X    for (ppar = param_msg; ppar->desc; ppar++)
  2145. X        FPR(fp, "\t%-16.16s%s\n", ppar->desc, ppar->text);
  2146. X    
  2147. X    /* print the date file syntax message */
  2148. X
  2149. X    FPR(fp, "\n");
  2150. X    for (pdate = date_msg; *pdate; pdate++)
  2151. X        FPR(fp, "\t%s\n", *pdate);
  2152. X
  2153. }
  2154. X
  2155. X
  2156. /*
  2157. X * alt_fopen - attempt to open a file in one of several paths in a
  2158. X * NULL-terminated path list.  If successful, return (opened) file pointer
  2159. X * and fill in full path name; if not, return NULL
  2160. X */
  2161. #ifdef PROTOS
  2162. FILE *alt_fopen(char *fullpath, char *name, char *pathlist[], char *access) 
  2163. #else
  2164. FILE *alt_fopen(fullpath, name, pathlist, access)
  2165. X    char *fullpath;        /* full path name (output) */
  2166. X    char *name;        /* base name (or full path spec) */
  2167. X    char *pathlist[];    /* NULL-terminated path list */
  2168. X    char *access;        /* permission requested */
  2169. #endif
  2170. {
  2171. X    char **path;
  2172. X    FILE *fp;
  2173. X
  2174. X    for (path = pathlist; *path; path++) {
  2175. X        mk_filespec(fullpath, *path, name);
  2176. X        if ((fp = fopen(fullpath, access)) != NULL)
  2177. X            return fp;
  2178. X        }
  2179. X
  2180. X    fullpath[0] = '\0';        /* file not found */
  2181. X    return NULL;
  2182. }
  2183. X
  2184. SHAR_EOF
  2185. chmod 0666 pcal.c ||
  2186. echo 'restore of pcal.c failed'
  2187. Wc_c="`wc -c < 'pcal.c'`"
  2188. test 33851 -eq "$Wc_c" ||
  2189.     echo 'pcal.c: original size 33851, current size' "$Wc_c"
  2190. fi
  2191. true || echo 'restore of pcal.man failed'
  2192. echo End of part 2, continue with part 3
  2193. exit 0
  2194.