home *** CD-ROM | disk | FTP | other *** search
Text File | 1991-03-15 | 59.0 KB | 2,196 lines |
- Newsgroups: alt.sources
- From: jbr0@cbnews.att.com (joseph.a.brownlee)
- Subject: Pcal v4.0, part 2 of 5
- Date: Thu, 14 Mar 91 12:48:13 GMT
- Message-ID: <1991Mar14.124813.9360@cbnews.att.com>
-
- #!/bin/sh
- # This is part 02 of a multipart archive
- # ============= exprpars.c ==============
- if test -f 'exprpars.c' -a X"$1" != X"-c"; then
- echo 'x - skipping exprpars.c (File already exists)'
- else
- echo 'x - extracting exprpars.c (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'exprpars.c' &&
- /*
- X * exprpars.c - Pcal routines concerned with parsing if{n}def expressions
- X *
- X * Contents:
- X *
- X * do_xxx
- X * lookup_token
- X * next_token
- X * parse_expr
- X *
- X * Revision history:
- X *
- X * 4.0 AWR 02/06/91 Author
- X *
- X */
- X
- /*
- X * Standard headers:
- X */
- X
- #include <ctype.h>
- #include <string.h>
- #include <stdio.h>
- X
- /*
- X * Pcal-specific definitions:
- X */
- X
- #include "pcaldefs.h"
- #include "pcalglob.h"
- X
- /*
- X * Macros:
- X */
- X
- /*
- X * token type code definitions:
- X */
- X
- #define TK_UNKNOWN 0 /* codes returned by next_token() */
- #define TK_IDENT 1
- #define TK_LPAREN 2
- #define TK_RPAREN 3
- #define TK_UNARYOP 4
- #define TK_BINARYOP 5
- #define TK_ENDINPUT 6
- #define TK_STARTINPUT 7 /* special code for start symbol */
- X
- /* bit position for token type codes (cf. where_ok[] below) */
- #define ID_OK (1 << TK_IDENT)
- #define LP_OK (1 << TK_LPAREN)
- #define RP_OK (1 << TK_RPAREN)
- #define UO_OK (1 << TK_UNARYOP)
- #define BO_OK (1 << TK_BINARYOP)
- #define ST_OK (1 << TK_STARTINPUT)
- #define NEVER_OK 0
- X
- /* is token "curr" legal after "prev"? (cf. where_ok[] below) */
- #define IS_LEGAL(curr, prev) (where_ok[curr] & (1 << (prev)))
- X
- /*
- X * operator-related definitions:
- X */
- X
- #define OP_AND 0 /* operator subcodes */
- #define OP_OR 1
- #define OP_XOR 2
- #define OP_NEGATE 3
- X
- #define ENDINPUT_PREC -1 /* arbitrary number < lowest op. prec */
- #define OR_PREC 1 /* operator precedence levels */
- #define XOR_PREC 2
- #define AND_PREC 3
- #define NEGATE_PREC 4
- #define PAREN_PREC 8 /* arbitrary number > highest op. prec */
- X
- /* lower bits of operator stack entry are code; higher are precedence */
- #define OPR_BITS 4
- #define OPR_MASK ((1 << OPR_BITS) - 1)
- #define PREC(op) ((op) >> OPR_BITS)
- #define OPCODE(op) ((op) & OPR_MASK)
- #define MAKE_OPR(p, o) (((p) << OPR_BITS) | (o))
- X
- #define MAX_OP 20 /* size of operand and operator stacks */
- X
- /*
- X * Globals:
- X */
- X
- typedef short OPERAND; /* types for operand and operator stacks */
- typedef short OPERATOR;
- X
- X
- typedef struct {
- X char *name; /* token spelling */
- X short type; /* token type code */
- X short value; /* associated value */
- X } TOKEN;
- X
- /* token table - note that substrings must follow longer strings */
- X
- TOKEN token_tbl[] = {
- X "&&", TK_BINARYOP, OP_AND, /* synonym for "&" */
- X "&", TK_BINARYOP, OP_AND,
- X "||", TK_BINARYOP, OP_OR, /* synonym for "|" */
- X "|", TK_BINARYOP, OP_OR,
- X "!", TK_UNARYOP, OP_NEGATE,
- X "^", TK_BINARYOP, OP_XOR,
- X "(", TK_LPAREN, 0,
- X ")", TK_RPAREN, 0,
- X NULL, TK_UNKNOWN, 0 /* must be last entry */
- X };
- X
- X
- typedef struct {
- X short prec; /* precedence */
- X short type; /* token type (TK_UNARYOP or TK_BINARYOP) */
- #ifdef PROTOS
- X OPERAND (*pfcn)(OPERAND *); /* dispatch function */
- #else
- X OPERAND (*pfcn)(); /* dispatch function */
- #endif
- X } OPR;
- X
- /* operator table - entries must be in same order as OP_XXX */
- X
- #ifdef PROTOS
- static OPERAND do_and(OPERAND *);
- static OPERAND do_or(OPERAND *);
- static OPERAND do_xor(OPERAND *);
- static OPERAND do_negate(OPERAND *);
- #else
- static OPERAND do_and(), do_or(), do_xor(), do_negate(); /* dispatch fcns */
- #endif
- X
- OPR opr_tbl[] = {
- X AND_PREC, TK_BINARYOP, do_and, /* OP_AND */
- X OR_PREC, TK_BINARYOP, do_or, /* OP_OR */
- X XOR_PREC, TK_BINARYOP, do_xor, /* OP_XOR */
- X NEGATE_PREC, TK_UNARYOP, do_negate /* OP_NEGATE */
- X };
- X
- X
- /* set of tokens which each token may legally follow (in TK_XXX order) */
- X
- int where_ok[] = {
- X NEVER_OK , /* TK_UNKNOWN */
- X ST_OK | LP_OK | UO_OK | BO_OK , /* TK_IDENT */
- X ST_OK | LP_OK | UO_OK | BO_OK , /* TK_LPAREN */
- X ID_OK | LP_OK | RP_OK , /* TK_RPAREN */
- X ST_OK | LP_OK | BO_OK , /* TK_UNARYOP */
- X ID_OK | RP_OK , /* TK_BINARYOP */
- X ST_OK | ID_OK | RP_OK /* TK_ENDINPUT */
- X };
- X
- X
- /*
- X * do_xxx - dispatch functions for operators
- X */
- X
- #ifdef PROTOS
- static OPERAND do_and(OPERAND *ptop)
- #else
- static OPERAND do_and(ptop)
- X OPERAND *ptop;
- #endif
- {
- X return ptop[0] & ptop[-1];
- }
- X
- X
- #ifdef PROTOS
- static OPERAND do_or(OPERAND *ptop)
- #else
- static OPERAND do_or(ptop)
- X OPERAND *ptop;
- #endif
- {
- X return ptop[0] | ptop[-1];
- }
- X
- X
- #ifdef PROTOS
- static OPERAND do_xor(OPERAND *ptop)
- #else
- static OPERAND do_xor(ptop)
- X OPERAND *ptop;
- #endif
- {
- X return ptop[0] ^ ptop[-1];
- }
- X
- X
- #ifdef PROTOS
- static OPERAND do_negate(OPERAND *ptop)
- #else
- static OPERAND do_negate(ptop)
- X OPERAND *ptop;
- #endif
- {
- X return ! ptop[0];
- }
- X
- X
- /*
- X * lookup_token - look up token in table; return pointer to table entry
- X */
- #ifdef PROTOS
- static TOKEN *lookup_token(char *p)
- #else
- static TOKEN *lookup_token(p)
- X char *p;
- #endif
- {
- X TOKEN *ptok;
- X
- X for (ptok = token_tbl;
- X ptok->name && strncmp(p, ptok->name, strlen(ptok->name));
- X ptok++)
- X ;
- X
- X return ptok;
- }
- X
- X
- /*
- X * next_token - fetch next token from input string; fill in its type and value
- X * and return pointer to following character
- X */
- #ifdef PROTOS
- static char *next_token(char *p,
- X int *ptype,
- X int *pvalue)
- #else
- static char *next_token(p, ptype, pvalue)
- X char *p;
- X int *ptype;
- X int *pvalue;
- #endif
- {
- X TOKEN *ptok;
- X char tokbuf[STRSIZ], *pb;
- X
- #define NT_RETURN(p, t, v) \
- X if (1) { *ptype = t; *pvalue = v; return p; } else
- X
- X while (*p && isspace(*p)) /* skip whitespace */
- X p++;
- X
- X if (*p == '\0') /* end of input? */
- X NT_RETURN(p, TK_ENDINPUT, 0);
- X
- X if (isalpha(*p)) { /* identifier? */
- X
- X pb = tokbuf; /* make local copy and look up */
- X while (*p && (isalpha(*p) || isdigit(*p) || *p == '_'))
- X *pb++ = *p++;
- X *pb = '\0';
- X
- X NT_RETURN(p, TK_IDENT, find_sym(tokbuf));
- X }
- X
- X ptok = lookup_token(p); /* other token */
- X NT_RETURN(p + (ptok->name ? strlen(ptok->name) : 1), ptok->type,
- X ptok->value);
- }
- X
- X
- /*
- X * parse_expr - parses expression consisting of identifiers and logical
- X * operators; return TRUE if expression is true (identifier defined => true);
- X * FALSE if false; EXPR_ERR if syntax error in expression
- X */
- #ifdef PROTOS
- int parse_expr(char *pbuf)
- #else
- int parse_expr(pbuf)
- X char *pbuf;
- #endif
- {
- X OPERAND opd_stack[MAX_OP]; /* operand stack - TRUE/FALSE values */
- X OPERATOR opr_stack[MAX_OP]; /* operator stack - precedence | op */
- X int value, token, plevel, prec, result, npop, opr, opd, prev_token, op;
- X
- X plevel = 0; /* paren nesting level */
- X opd = opr = -1; /* indices of stack tops */
- X prev_token = TK_STARTINPUT; /* to detect null expressions */
- X
- X do {
- X pbuf = next_token(pbuf, &token, &value);
- X
- X /* check that the current token may follow the previous one */
- X if (! IS_LEGAL(token, prev_token))
- X return EXPR_ERR;
- X
- X switch(token) {
- X
- X case TK_IDENT: /* identifier => 1 if def, 0 if not */
- X opd_stack[++opd] = value != PP_SYM_UNDEF;
- X break;
- X
- X case TK_LPAREN: /* left paren - bump nesting level */
- X ++plevel;
- X break;
- X
- X case TK_RPAREN: /* right paren - decrement nesting */
- X if (--plevel < 0)
- X return EXPR_ERR;
- X break;
- X
- X case TK_ENDINPUT: /* end-of-input - treat as operator */
- X if (prev_token == TK_STARTINPUT)
- X return FALSE; /* null expr => FALSE */
- X /* fall through */
- X
- X case TK_UNARYOP:
- X case TK_BINARYOP:
- X
- X /* get precedence of operator, adjusting for paren
- X * nesting (TK_ENDINPUT has the lowest precedence
- X * of all, to unwind operand/operator stacks at end)
- X */
- X
- X prec = token == TK_ENDINPUT ? ENDINPUT_PREC :
- X (plevel * PAREN_PREC) + opr_tbl[value].prec;
- X
- X /* pop (and perform) any equal- or higher-precedence
- X * operators on operator stack: extract operator,
- X * check for operand stack underflow, execute
- X * operator, adjust operand stack height and place
- X * result of operator on top
- X */
- X
- X for ( ;
- X opr >= 0 && PREC(opr_stack[opr]) >= prec;
- X opr--) {
- X op = OPCODE(opr_stack[opr]);
- X npop = opr_tbl[op].type == TK_UNARYOP ? 0 : 1;
- X if (opd < npop)
- X return EXPR_ERR;
- X result = (*opr_tbl[op].pfcn)(opd_stack + opd);
- X opd_stack[opd -= npop] = result;
- X }
- X
- X /* push operator (if any) onto stack */
- X
- X if (token != TK_ENDINPUT)
- X opr_stack[++opr] = MAKE_OPR(prec, value);
- X
- X break;
- X
- X default: /* should never get here */
- X return EXPR_ERR;
- X break;
- X
- X }
- X
- X prev_token = token;
- X
- X } while (token != TK_ENDINPUT);
- X
- X /* done - check for dangling parens, and leftover operand/operators */
- X
- X return plevel != 0 || opd != 0 || opr != -1 ?
- X EXPR_ERR : /* leftover junk - return error */
- X opd_stack[0]; /* all OK - return final value */
- }
- X
- SHAR_EOF
- chmod 0666 exprpars.c ||
- echo 'restore of exprpars.c failed'
- Wc_c="`wc -c < 'exprpars.c'`"
- test 8311 -eq "$Wc_c" ||
- echo 'exprpars.c: original size 8311, current size' "$Wc_c"
- fi
- # ============= moon91 ==============
- if test -f 'moon91' -a X"$1" != X"-c"; then
- echo 'x - skipping moon91 (File already exists)'
- else
- echo 'x - extracting moon91 (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'moon91' &&
- #
- # 1991 moon phase information (from Old Farmer's Almanac)
- #
- # This file is to be called .moon91 for Un*x, moon91.dat for VMS; it is
- # to live in the same directory as the .calendar file.
- #
- # Dates and times below are for Boston, EST. The date (numeric form only)
- # is parsed as mm/dd or dd/mm as specified by the -A and -E flags respectively.
- # The time (24-hour clock) is optional; if supplied, Pcal uses it to more
- # accurately calculate the phase of the moon at a fixed time each day. You
- # may wish to adjust these dates and times to conform to your location.
- #
- # If Pcal detects an error (invalid date, date or phase out of sequence,
- # unrecognizable line) in this file, it generates an error message, closes
- # the file, and resorts to the default moon phase calculation algorithm.
- #
- # Moon file syntax:
- #
- # Pcal normally calculates the approximate phase of the moon using
- # a simple algorithm which assumes (among other things) that the
- # length of the lunar month is constant and that the quarter moons
- # will occur on the same day worldwide. For most users, that is
- # adequate; however, moon-phase freaks may enter the dates and
- # (optionally) times of quarter moons (from a reliable source such
- # as an almanac or astronomical table) into a file called .moonXX
- # (moonXX.dat on VMS), where XX is the last two digits of the year.
- # If such a file exists (in the same directory as the date file),
- # pcal will interpolate the phase of the moon from the information
- # in this file instead of using the default algorithm.
- #
- # Entries in the moon file must conform to the following syntax:
- #
- # if -A flag (American date formats) specified:
- # <quarter> <month><sep><day> {<hour><sep><min>}
- #
- # if -E flag (European date formats) specified:
- # <quarter> <day><sep><month> {<hour><sep><min>}
- #
- # where
- #
- # <quarter> := "nm", "fq" or "1q", "fm", "3q" or "lq" (new
- # moon, first quarter, full moon, last quarter)
- # <hour> := number 0-23 (24-hour clock)
- # <min> := number 0-59
- #
- # This file must contain entries for all quarter moons in the year,
- # in chronological order; if any errors are encountered, pcal will
- # revert to using its default algorithm.
- #
- # As in the date file, comments start with '#' and run through
- # end-of-line.
- X
- 3q 01/07 13:37 # third quarter
- nm 01/15 18:51 # new moon
- 1q 01/23 09:23 # first quarter
- fm 01/30 01:10 # full moon
- X
- 3q 02/06 08:53
- nm 02/14 12:33
- 1q 02/21 17:59
- fm 02/28 13:26
- X
- 3q 03/08 05:33
- nm 03/16 03:11
- 1q 03/23 01:03
- fm 03/30 02:18
- X
- 3q 04/07 01:47
- nm 04/14 14:38
- 1q 04/21 07:40
- fm 04/28 16:00
- X
- 3q 05/06 19:48
- nm 05/13 23:37
- 1q 05/20 14:47
- fm 05/28 06:38
- X
- 3q 06/05 10:31
- nm 06/12 07:07
- 1q 06/18 23:20
- fm 06/26 22:00
- X
- 3q 07/04 21:51
- nm 07/11 14:07
- 1q 07/18 10:12
- fm 07/26 13:25
- X
- 3q 08/03 06:27
- nm 08/09 21:28
- 1q 08/17 00:02
- fm 08/25 04:08
- X
- 3q 09/01 13:17
- nm 09/08 06:02
- 1q 09/15 17:02
- fm 09/23 17:41
- 3q 09/30 19:31
- X
- nm 10/07 16:39
- 1q 10/15 12:34
- fm 10/23 06:09
- 3q 10/30 02:12
- X
- nm 11/06 06:12
- 1q 11/14 09:02
- fm 11/21 17:58
- 3q 11/28 10:22
- X
- nm 12/05 22:57
- 1q 12/14 04:33
- fm 12/21 05:24
- 3q 12/27 20:56
- SHAR_EOF
- chmod 0666 moon91 ||
- echo 'restore of moon91 failed'
- Wc_c="`wc -c < 'moon91'`"
- test 3095 -eq "$Wc_c" ||
- echo 'moon91: original size 3095, current size' "$Wc_c"
- fi
- # ============= moonphas.c ==============
- if test -f 'moonphas.c' -a X"$1" != X"-c"; then
- echo 'x - skipping moonphas.c (File already exists)'
- else
- echo 'x - extracting moonphas.c (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'moonphas.c' &&
- /*
- X * moonphas.c - encapsulates routines used by Pcal for moon phase calculation
- X *
- X * Contents:
- X *
- X * calc_phase
- X * find_moonfile
- X * find_phase
- X * read_moonfile
- X *
- X * Revision history:
- X *
- X * 4.0 AWR 03/07/91 Add find_moonfile()
- X *
- X * 01/15/91 Author: translated PostScript
- X * routines to C and added moon
- X * file routines
- X *
- X */
- X
- /*
- X * Standard headers:
- X */
- X
- #include <stdio.h>
- #include <string.h>
- #include <ctype.h>
- X
- /*
- X * Pcal-specific definitions:
- X */
- X
- #include "pcaldefs.h"
- #include "pcalglob.h"
- #include "pcallang.h"
- X
- /*
- X * Macros:
- X */
- X
- #define PERIOD 29.5306 /* average lunar month */
- #define DAYS_PER_YEAR 365.2422 /* true length of year */
- X
- #define FM_MONTH 2 /* reference date of known full moon */
- #define FM_DAY 9
- #define FM_YEAR 1990
- X
- #define HOUR 12 /* hour of day when phase calculated */
- X
- /* convert "n" so that 0.0 <= n < 1.0 */
- #define NORMALIZE(n) \
- X if (1) { while (n < 0.0) n++; while (n >= 1.0) n--; } else
- X
- /* interpolate phase for day "d" from moon_info array elements "n1" and "n2" */
- #define CALC_PHASE(d, n1, n2) \
- X moon_info[n1].phase + ((d) - moon_info[n1].doy) * \
- X ((moon_info[n2].phase - moon_info[n1].phase) / \
- X (moon_info[n2].doy - moon_info[n1].doy))
- X
- /* generate error message, close file, and quit */
- #define ERR_EXIT(msg) \
- X if (1) { ERR(msg); fclose(fp); return FALSE; } else
- X
- /* day and phase sequence error conditions - cf. read_moonfile() */
- #define DAY_TOO_SOON (nrec > 1 && doy < prevdoy + 6)
- #define DAY_TOO_LATE (doy > prevdoy + 9)
- #define WRONG_PHASE (nrec > 1 && ph != (prevph + 1) % 4)
- X
- X
- /*
- X * Globals:
- X */
- X
- typedef struct {
- X int doy; /* day of year (1..366) */
- X double phase; /* moon phase (cycles since new moon prior to 1/1) */
- } MOON_INFO;
- X
- static MOON_INFO moon_info[60]; /* quarter moons for year + dummies */
- X
- X
- /*
- X * Routines to calculate moon phase when no moon file exists:
- X *
- X * User may substitute any phase-of-the-moon routine desired for calc_phase()
- X * as long as it returns a double value in range 0.0 <= val < 1.0:
- X *
- X * 0.0 new moon
- X * 0.25 first quarter
- X * 0.5 full moon
- X * 0.75 third quarter
- X *
- X * (N.B.: The most accurate moon phase routines compensate for variations
- X * in the length of the lunar month. In that case, is_quarter() might also
- X * require some modification to prevent spurious or missing quarter-moon
- X * dates when the lunar month is shorter or longer than average.)
- X */
- X
- X
- /*
- X * calc_phase - return phase of moon on month/day/year (adapted from Mark
- X * Hanson's PostScript version)
- X */
- X
- #ifdef PROTOS
- double calc_phase(int month,
- X int day,
- X int year)
- #else
- double calc_phase(month, day, year)
- X int month, day, year;
- #endif
- {
- X double daysdiff, phase;
- X long yearsdiff, cycles;
- X
- X daysdiff = (DAY_OF_YEAR(month, day, year) - DAY_OF_YEAR(FM_MONTH,
- X FM_DAY, FM_YEAR)) * (DAYS_PER_YEAR / 365.0);
- X
- X if ((yearsdiff = year - FM_YEAR) != 0)
- X daysdiff += (yearsdiff * DAYS_PER_YEAR) - ((yearsdiff / 100) -
- X (yearsdiff / 400));
- X
- X cycles = (long) (daysdiff / PERIOD);
- X phase = (daysdiff - (cycles * PERIOD) - 0.5 * PERIOD) / PERIOD;
- X NORMALIZE(phase); /* tweak so 0.0 <= phase < 1.0 */
- X return phase;
- }
- X
- X
- /*
- X * is_quarter - is "phase" within 0.5 day of a quarter moon
- X */
- #ifdef PROTOS
- static int is_quarter(double phase)
- #else
- static int is_quarter(phase)
- X double phase;
- #endif
- {
- X
- X phase *= PERIOD;
- X return (phase >= PERIOD - 0.5 || phase < 0.5) ||
- X (phase >= 0.25 * PERIOD - 0.5 && phase < 0.25 * PERIOD + 0.5) ||
- X (phase >= 0.50 * PERIOD - 0.5 && phase < 0.50 * PERIOD + 0.5) ||
- X (phase >= 0.75 * PERIOD - 0.5 && phase < 0.75 * PERIOD + 0.5);
- }
- X
- X
- /*
- X * Routines to read moon file and calculate moon phase from data within
- X */
- X
- X
- /*
- X * get_phase - convert moon phase string to appropriate value
- X */
- #ifdef PROTOS
- static int get_phase(char *cp)
- #else
- static int get_phase(cp)
- X char *cp;
- #endif
- {
- X KWD *p;
- X
- X if (!cp)
- X return MOON_OTHER;
- X
- X for (p = phases; p->name && ci_strcmp(cp, p->name); p++)
- X ;
- X
- X return p->code;
- }
- X
- X
- /*
- X * make_moonpath - create the full path for the moon file in 'filename';
- X * return pointer to 'filename'
- X */
- #ifdef PROTOS
- static char *make_moonpath(char *filename, char *name, int year)
- #else
- static char *make_moonpath(filename, name, year)
- X char *filename; /* full path name (output) */
- X char *name; /* base file name */
- X int year; /* year */
- #endif
- {
- X char tmp[20], path[STRSIZ], *p;
- X
- X strcpy(tmp, name);
- X p = strchr(tmp, 'X'); /* replace XX with year % 100 */
- X *p++ = '0' + (year / 10) % 10;
- X *p = '0' + year % 10;
- X
- X mk_path(path, datefile); /* get datefile path */
- X mk_filespec(filename, path, tmp); /* append file name */
- X
- X return filename;
- }
- X
- X
- /*
- X * find_moonfile - look for moon file for specified year. If it exists
- X * and is readable, return its full path name; else return NULL. (There
- X * are admittedly ways to do this without attempting to open the file,
- X * but they may not be portable.)
- X */
- #ifdef PROTOS
- char *find_moonfile(int year)
- #else
- char *find_moonfile(year)
- X int year;
- #endif
- {
- X static char filename[STRSIZ];
- X FILE *fp;
- X
- X fp = fopen(make_moonpath(filename, MOONFILE, year), "r");
- X
- #ifdef ALT_MOONFILE
- X if (!fp) /* try again with alternate name */
- X fp = fopen(make_moonpath(filename, ALT_MOONFILE, year), "r");
- #endif
- X return fp ? (fclose(fp), filename) : NULL;
- }
- X
- X
- /*
- X * read_moonfile - looks for moon data file (in same directory as .calendar);
- X * if found, reads file, fills in moon_info[] and returns TRUE; if not found
- X * (or error encountered), returns FALSE
- X */
- #ifdef PROTOS
- int read_moonfile(int year)
- #else
- int read_moonfile(year)
- X int year;
- #endif
- {
- X char *filename;
- X int line, nrec, month, day, hh, mm;
- X int ph, prevph = MOON_OTHER, doy, prevdoy, n, quarter;
- X double phase;
- X FILE *fp;
- X
- X if (! *datefile) /* skip if no datefile */
- X return FALSE;
- X
- X /* get name of moon file and attempt to open it */
- X
- X if ((filename = find_moonfile(year)) == NULL ||
- X (fp = fopen(filename, "r")) == NULL)
- X return FALSE;
- X
- X /*
- X * Moon file entries are of the form <phase> <date> {<time>}; each
- X * is converted below to a moon_info[] record (note that only the
- X * initial phase of the moon is directly calculated from <phase>;
- X * it is subsequently used only for error checking). Dummy entries
- X * in moon_info[] precede and follow the information read from the
- X * moon file; these are used for subsequent interpolation of dates
- X * before the first / after the last quarter of the year.
- X */
- X
- X nrec = 1; /* skip dummy entry */
- X prevdoy = 0;
- X line = 0;
- X
- X while (getline(fp, &line)) {
- X
- X if ((n = loadwords()) < 2 || /* recognizable line? */
- X (ph = get_phase(words[0])) == MOON_OTHER)
- X ERR_EXIT(E_INV_LINE);
- X
- X if (nrec == 1) /* phase at initial quarter */
- X quarter = ph == MOON_NM ? 4 : ph;
- X
- X /* extract the month and day fields (in appropriate order) */
- X
- X (void) split_date(words[1],
- X date_style == USA_DATES ? &month : &day,
- X date_style == USA_DATES ? &day : &month,
- X NULL);
- X
- X /* validate the date and phase */
- X
- X if (!is_valid(month, day, year)) /* date OK? */
- X ERR_EXIT(E_INV_DATE);
- X
- X doy = DAY_OF_YEAR(month, day, year); /* in sequence? */
- X if (DAY_TOO_SOON || DAY_TOO_LATE || WRONG_PHASE)
- X ERR_EXIT(E_DATE_SEQ);
- X
- X prevdoy = doy; /* save for sequence check */
- X prevph = ph;
- X
- X /* calculate moon phase, factoring in time (if present) */
- X
- X phase = 0.25 * quarter++;
- X if (n > 2) { /* extract hour and minute */
- X (void) split_date(words[2], &hh, &mm, NULL);
- X phase += (HOUR - (hh + (mm / 60.0))) / (24 * PERIOD);
- X }
- X moon_info[nrec].doy = doy; /* enter day and phase */
- X moon_info[nrec++].phase = phase;
- X }
- X
- X /* check to see that file is all there */
- X
- X doy = YEAR_LEN(year) + 1; /* day after end of year */
- X if (DAY_TOO_LATE)
- X ERR_EXIT(E_PREM_EOF);
- X
- X /* extrapolate dummy entries from nearest lunar month */
- X
- X moon_info[nrec].doy = doy; /* day after end of year */
- X moon_info[nrec].phase = CALC_PHASE(doy, nrec-5, nrec-1);
- X
- X moon_info[0].doy = 0; /* day before start of year */
- X moon_info[0].phase = CALC_PHASE(0, 1, 5);
- X
- X fclose(fp);
- X return TRUE;
- }
- X
- X
- /*
- X * find_phase - look up phase of moon in moon phase file (if possible);
- X * otherwise calculate it using calc_phase() above. Sets *pquarter to
- X * TRUE if date is a quarter moon, FALSE if not
- X */
- #ifdef PROTOS
- double find_phase(int month,
- X int day,
- X int year,
- X int *pquarter)
- #else
- double find_phase(month, day, year, pquarter)
- X int month, day, year;
- X int *pquarter;
- #endif
- {
- X static int sv_year = 0;
- X static int use_file;
- X int i, doy;
- X double phase;
- X
- X if (year != sv_year) { /* look for file for new year */
- X use_file = read_moonfile(year);
- X sv_year = year;
- X }
- X
- X if (! use_file) { /* no file - calculate date */
- X phase = calc_phase(month, day, year);
- X *pquarter = is_quarter(phase);
- X return phase;
- X }
- X
- X /* moon file found - use the data extracted from it */
- X
- X doy = DAY_OF_YEAR(month, day, year);
- X
- X for (i = 1; doy > moon_info[i].doy; i++) /* find interval */
- X ;
- X
- X /* if day appears in table, return exact value; else interpolate */
- X
- X phase = (*pquarter = (doy == moon_info[i].doy)) ? moon_info[i].phase :
- X CALC_PHASE(doy, i-1, i);
- X return phase - (int)phase; /* 0.0 <= phase < 1.0 */
- }
- X
- SHAR_EOF
- chmod 0666 moonphas.c ||
- echo 'restore of moonphas.c failed'
- Wc_c="`wc -c < 'moonphas.c'`"
- test 9127 -eq "$Wc_c" ||
- echo 'moonphas.c: original size 9127, current size' "$Wc_c"
- fi
- # ============= noprotos.h ==============
- if test -f 'noprotos.h' -a X"$1" != X"-c"; then
- echo 'x - skipping noprotos.h (File already exists)'
- else
- echo 'x - extracting noprotos.h (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'noprotos.h' &&
- /*
- X * noprotos.h - K&R-style function declarations for Pcal sources
- X *
- X * Revision history:
- X *
- X * 4.0 AWR 03/01/91 use <stdlib.h> where possible
- X *
- X * AWR 02/19/91 adapted from protos.h (q.v.)
- X *
- X */
- X
- X
- /*
- X * Declarations for functions defined in exprpars.c:
- X */
- int parse_expr();
- X
- X
- /*
- X * Declarations for functions defined in moonphas.c:
- X */
- double calc_phase();
- double find_phase();
- char *find_moonfile();
- int read_moonfile();
- X
- X
- /*
- X * Declarations for functions defined in pcal.c:
- X */
- FILE *alt_fopen();
- char *color_msg();
- int get_args();
- FLAG_USAGE *get_flag();
- int main();
- void set_color();
- void usage();
- X
- X
- /*
- X * Declarations for functions defined in pcalutil.c:
- X */
- char *alloc();
- int calc_day();
- int calc_weekday();
- int calc_year_day();
- int ci_strcmp();
- int ci_strncmp();
- void copy_text();
- int getline();
- int is_valid();
- int loadwords();
- char *mk_filespec();
- char *mk_path();
- void normalize();
- int split_date();
- char *trnlog();
- X
- X
- /*
- X * Declarations for functions defined in readfile.c:
- X */
- void cleanup();
- void clear_syms();
- int date_type();
- int do_define();
- int do_ifdef();
- int do_ifndef();
- int do_include();
- int do_undef();
- int enter_day_info();
- int find_sym();
- year_info *find_year();
- int get_keywd();
- int get_month();
- int get_ordinal();
- int get_prep();
- int get_token();
- int get_weekday();
- int is_anyday();
- int is_holiday();
- int is_weekday();
- int is_workday();
- int not_holiday();
- int not_weekday();
- int not_workday();
- int parse();
- int parse_date();
- int parse_ord();
- int parse_rel();
- void read_datefile();
- X
- X
- /*
- X * Declarations for functions defined in writefil.c:
- X */
- void def_footstring();
- void find_daytext();
- void find_holidays();
- void print_julian_info();
- void print_month();
- void print_moon_info();
- void print_text();
- char *print_word();
- void write_psfile();
- X
- X
- /*
- X * Prototypes for miscellaneous library routines (if not already included
- X * via <stdlib.h> - cf. pcaldefs.h)
- X */
- #ifndef STDLIB
- extern int atoi();
- extern char *calloc();
- extern char *getenv();
- #endif
- SHAR_EOF
- chmod 0666 noprotos.h ||
- echo 'restore of noprotos.h failed'
- Wc_c="`wc -c < 'noprotos.h'`"
- test 1978 -eq "$Wc_c" ||
- echo 'noprotos.h: original size 1978, current size' "$Wc_c"
- fi
- # ============= pcal.c ==============
- if test -f 'pcal.c' -a X"$1" != X"-c"; then
- echo 'x - skipping pcal.c (File already exists)'
- else
- echo 'x - extracting pcal.c (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'pcal.c' &&
- static char VERSION_STRING[] = "@(#)pcal v4.0 - print Postscript calendars";
- /*
- X * pcal.c - generate PostScript file to print calendar for any month and year
- X *
- X * The original PostScript code to generate the calendars was written by
- X * Patrick Wood (Copyright (c) 1987 by Patrick Wood of Pipeline Associates,
- X * Inc.), and authorized for modification and redistribution. The calendar
- X * file inclusion code was originally written in "bs(1)" by Bill Vogel of
- X * AT&T. Patrick's original PostScript was modified and enhanced several
- X * times by King Ables, Tom Tessin, and others whose names have regrettably
- X * been lost. This C version was originally created by Ken Keirnan of Pacific
- X * Bell; additional enhancements by Joseph P. Larson, Ed Hand, Andrew Rogers,
- X * Mark Kantrowitz, and Joe Brownlee. The moon routines were originally
- X * written by Mark Hanson, were improved and incorporated into this version
- X * by Jamie Zawinski, and were translated from PostScript to C by Andrew Rogers.
- X *
- X * Contents:
- X *
- X * alt_fopen
- X * color_msg
- X * get_args
- X * get_flag
- X * main
- X * set_color
- X * usage
- X *
- X * Revision history:
- X *
- X * 4.0 AWR 02/24/91 Add alt_fopen() to search for file
- X * in alternate path; use to look for
- X * date file in same directory as
- X * Pcal executable (as per Floyd Miller)
- X *
- X * AWR 02/19/91 Support negative ordinals (cf.
- X * readfile.c, pcalutil.c)
- X *
- X * AWR 02/06/91 Support expressions in preprocessor
- X * "if{n}def" lines (cf. exprpars.c)
- X *
- X * AWR 02/04/91 Support "even", "odd" ordinals (cf.
- X * readfile.c) and ordinals > 5th
- X *
- X * AWR 01/28/91 Support -B (leave unused boxes blank)
- X * flag
- X *
- X * AWR 01/15/91 Separated into moonphas.c, pcal.c,
- X * pcalutil.c, readfile.c, and writefil.c;
- X * added support for moon phase file
- X *
- X * AWR 01/07/91 Support -w (whole year) flag; fix
- X * various bugs and nonportable constructs
- X *
- X * 3.0 AWR 12/10/90 Support concept of "weekday", "workday",
- X * and "holiday" (and converses)
- X *
- X * AWR 11/13/90 Substantial revision of program logic:
- X * extracted pcaldefs.h and pcallang.h,
- X * moving all language dependencies (even
- X * flag names) to the latter.
- X *
- X * add -I flag to reinitialize all
- X * flags to program defaults; -j and -J
- X * flags (print Julian dates); add -x,
- X * -y, -X, -Y flags (as per Ed Hand)
- X * for output scaling and translation
- X *
- X * allow "wildcard" dates (e.g., "all
- X * Thursday{s} in Oct", "last Friday in
- X * all") and notes ("note all <text>);
- X * print full "help" message (including
- X * date file syntax)
- X *
- X * 2.6 AWR 10/15/90 parse floating dates (e.g. "first
- X * Monday in September") and relative
- X * floating dates (e.g., "Friday after
- X * fourth Thursday in November"); simplify
- X * logic of -F option; add -F to usage
- X * message; replace COLOR_MSG() string
- X * with color_msg() routine; add -h
- X * (help message) and -A | -E (American |
- X * European date format) flags; renamed
- X * flag sets for clarity; more comments
- X *
- X * 2.5 JAB 10/04/90 added -F option
- X *
- X * 2.4 --- 10/01/90 * no modifications *
- X *
- X * 2.3 JWZ/AWR 09/18/90 added moon routines
- X *
- X * 2.2 AWR 09/17/90 revise logic of parse(); new usage
- X * message
- X *
- X * JAB/AWR 09/14/90 support "note" lines in date file
- X *
- X * 2.1 MK/AWR 08/27/90 support -L, -C, -R, -n options;
- X * print holiday text next to date
- X *
- X * AWR 08/24/90 incorporate cpp-like functionality;
- X * add -D and -U options; save date file
- X * information in internal data structure;
- X * look for PCAL_OPTS and PCAL_DIR; look
- X * for ~/.calendar and ~/calendar
- X *
- X * 2.0 AWR 08/08/90 included revision history; replaced -r
- X * flag with -l and -p; replaced -s and -S
- X * flags with -b and -g; recognize flags
- X * set in date file; translate ( and ) in
- X * text to octal escape sequence; usage()
- X * message condensed to fit 24x80 screen
- X *
- X * Parameters:
- X *
- X * pcal [opts] generate calendar for current month/year
- X * (current year if -w flag specified)
- X *
- X * pcal [opts] yy generate calendar for entire year yy
- X *
- X * pcal [opts] mm yy generate calendar for month mm
- X * (Jan = 1), year yy (19yy if yy < 100)
- X * (12 months starting with mm/yy if -w
- X * specified)
- X *
- X * pcal [opts] mm yy n as above, for n consecutive months (n
- X * rounded to next multiple of 12 if -w
- X * specified)
- X *
- X * Output:
- X *
- X * PostScript file to print calendars for all selected months.
- X *
- X * Options:
- X *
- X * -I initialize all parameters to program defaults
- X *
- X * -b <DAY> print specified weekday in black
- X * -g <DAY> print specified weekday in gray
- X * (default: print Saturdays and Sundays in gray)
- X *
- X * -O print "gray" dates as outlined characters
- X *
- X * -d <FONT> specify alternate font for day names
- X * (default: Times-Bold)
- X *
- X * -n <FONT> specify alternate font for notes in boxes
- X * (default: Helvetica-Narrow)
- X *
- X * -t <FONT> specify alternate font for titles
- X * (default: Times-Bold)
- X *
- X * -D <SYM> define preprocessor symbol
- X * -U <SYM> un-define preprocessor symbol
- X *
- X * -e generate empty calendar (ignore date file)
- X *
- X * -f <FILE> specify alternate date file (default:
- X * ~/.calendar on Un*x, SYS$LOGIN:CALENDAR.DAT
- X * on VMS, s:calendar.dat on Amiga; if
- X * environment variable [logical name on VMS]
- X * PCAL_DIR exists, looks there instead; if
- X * not found in either place, looks in same
- X * directory as Pcal executable)
- X *
- X * -o <FILE> specify alternate output file (default:
- X * stdout on Un*x, CALENDAR.PS on VMS,
- X * RAM:calendar.ps on Amiga)
- X *
- X * -L <STRING> specify left foot string (default: "")
- X * -C <STRING> specify center foot string (default: "")
- X * -R <STRING> specify right foot string (default: "")
- X *
- X * -l generate landscape-mode calendars
- X * -p generate portrait-mode calendars
- X * (default: landscape-mode)
- X *
- X * -h (command line only) write "usage" message
- X * to stdout
- X *
- X * -m draw a small moon icon on the days of the
- X * full, new, and half moons.
- X * -M draw a small moon icon every day.
- X * (default: no moons)
- X *
- X * -F <DAY> select alternate day to be displayed as the
- X * first day of the week (default: Sunday)
- X *
- X * -A dates are in American format (e.g., 10/15/90,
- X * Oct 15) (default)
- X * -E dates are in European format (e.g., 15.10.90,
- X * 15 Oct)
- X *
- X * -x <XSCALE> These two options can be used to change
- X * -y <YSCALE> the size of the calendar.
- X *
- X * -X <XTRANS> These two options can be used to relocate
- X * -Y <YTRANS> the position of the calendar on the page.
- X *
- X * -j print Julian dates (day of year)
- X * -J print Julian dates and days remaining
- X * (default: neither)
- X *
- X * -w print whole year (12 months) per page
- X *
- X * -B leave unused calendar boxes blank
- X *
- X *
- X * There are many ways to specify these options in addition to using the
- X * command line; this facilitates customization to the user's needs.
- X *
- X * If the environment variable (global symbol on VMS) PCAL_OPTS is
- X * present, its value will be parsed as if it were a command line.
- X * Any options specified will override the program defaults.
- X *
- X * All but the -e, -f, -h, -D, and -U options may be specified in the
- X * date file by including one or more lines of the form "opt <options>".
- X * Any such options override any previous values set either as program
- X * defaults, via PCAL_OPTS, or in previous "opt" lines.
- X *
- X * Options explicitly specified on the command line in turn override all
- X * of the above.
- X *
- X * Any flag which normally takes an argument may also be specified without
- X * an argument; this resets the corresponding option to its default. -D
- X * alone un-defines all symbols; -U alone has no effect.
- X *
- X * Parameters and flags may be mixed on the command line. In some cases
- X * (e.g., when a parameter follows a flag without its optional argument)
- X * this may lead to ambiguity; the dummy flag '-' (or '--') may be used
- X * to separate them, i.e. "pcal -t - 9 90".
- X *
- X *
- X * Date file syntax:
- X *
- X * The following rules describe the syntax of date file entries:
- X *
- X * year <year>
- X *
- X * opt <options>
- X *
- X * note <month_spec> <text>
- X * note <month> <text>
- X *
- X * if -A flag (American date formats) specified:
- X * <month_name> <day>{*} {<text>}
- X * <month><sep><day>{<sep><year>}{*} {<text>}
- X *
- X * if -E flag (European date formats) specified:
- X * <day> <month_name>{*} {<text>}
- X * <day><sep><month>{<sep><year>}{*} {<text>}
- X *
- X * <ordinal> <day_spec> in <month_spec>{*} {<text>}
- X * <day_spec> <prep> <date_spec>
- X *
- X * where
- X *
- X * {x} means x is optional
- X *
- X * <date_spec> := any of the above date specs (not year, note, or opt)
- X * <month_name> := first 3+ characters of name of month, or "all"
- X * <month_spec> := <month_name>, or "year"
- X * <day_name> := first 3+ characters of name of weekday, "day",
- X * "weekday", "workday", "holiday", "nonweekday",
- X * "nonworkday", or "nonholiday"
- X * <ordinal> := ordinal number ("1st", "2nd", etc.), "first" .. "fifth",
- X * "last", "even", "odd", or "all"
- X * <prep> := "before", "preceding", "after", "following", "on_or_before",
- X * or "on_or_after"
- X * <sep> := one or more non-numeric, non-space, non-'*' characters
- X * <month>, <day>, <year> are the numeric forms
- X *
- X * <options> := any command-line option except -e, -f, -h, -D, -U
- X *
- X * Comments start with '#' and run through end-of-line.
- X *
- X * Holidays may be flagged by specifying '*' as the last character of
- X * the date field(s), e.g. "10/12* Columbus Day", "July 4* Independence
- X * Day", etc. Any dates flagged as holidays will be printed in gray, and
- X * any associated text will appear adjacent to the date.
- X *
- X * Note that the numeric date formats (mm/dd{/yy}, dd.mm{.yy}) support
- X * an optional year, which will become the subsequent default year. The
- X * alphabetic date formats (month dd, dd month) do not support a year
- X * field; the "year yy" command is provided to reset the default year.
- X *
- X * "Floating" days may be specified in the date file as "first Mon in
- X * Sep", "last Mon in May", "4th Thu in Nov", etc.; any word may be
- X * used in place of "in". "Relative floating" days (e.g. "Fri after 4th
- X * Thu in Nov") are also accepted; they may span month/year bounds.
- X * Pcal also accepts date specs such as "all Friday{s} in October", "last
- X * Thursday in all", etc., and produces the expected results; "each" and
- X * "every" are accepted as synonyms for "all". Negative ordinals are
- X * allowed; "-2nd" means "next to last".
- X *
- X * The words "day", "weekday", "workday", and "holiday" may be used as
- X * wildcards: "day" matches any day, "weekday" matches any day normally
- X * printed in black, "workday" matches any day normally printed in black
- X * and not explicitly flagged as a holiday, and "holiday" matches any
- X * day explicitly flagged as a holiday. "Nonweekday", "nonworkday",
- X * and "nonholiday" are also supported and have the obvious meanings.
- X *
- X * "Odd" and "even" do not refer to the actual date; instead, "odd"
- X * means "alternate, starting with the first"; "even" means "alternate,
- X * starting with the second". Thus, "odd Fridays in March" refers to
- X * the first, third, and (if present) fifth Fridays in March - not to
- X * those Fridays falling on odd dates.
- X *
- X * "All" refers to each individual month; "year" refers to the year
- X * as an entity. Thus "odd Fridays in all" refers to the first/third/
- X * fifth Friday of each month, while "odd Fridays in year" refers to
- X * the first Friday of January and every other Friday thereafter.
- X *
- X * Additional notes may be propagated to an empty calendar box by the
- X * inclusion of one or more lines of the form "note <month> <text>",
- X * where <month> may be numeric or alphabetic; "note all <text>"
- X * propagates <text> to each month in the current year.
- X *
- X * Simple cpp-like functionality is provided. The date file may include
- X * the following commands, which work like their cpp counterparts:
- X *
- X * define <sym>
- X * undef <sym>
- X *
- X * if{n}def <expr>
- X * ...
- X * { else
- X * ... }
- X * endif
- X *
- X * include <file>
- X *
- X * Note that these do not start with '#', which is reserved as a comment
- X * character.
- X *
- X * <sym> is a symbol name consisting of a letter followed by zero or
- X * more letters, digits, or underscores ('_'). Symbol names are always
- X * treated in a case-insensitive manner.
- X *
- X * <expr> is an expression consisting of symbol names joined by the logical
- X * operators (in order of precedence, high to low) '!' (unary negate), '&'
- X * (and), '^' (exclusive or), and '|' (inclusive or). '&&' and '||' are
- X * accepted as synonyms for '&' and '|' respectively; the order of
- X * evaluation may be altered by the use of parentheses. A symbol whose
- X * name is currently defined evaluates to TRUE; one whose name is not
- X * currently defined evaluates to FALSE. Thus "ifdef A | B | C" is TRUE
- X * if any of the symbols A, B, and C is currently defined, and
- X * "ifdef A & B & C" is TRUE if all of them are.
- X *
- X * "ifndef A | B | C" is equivalent to "ifdef !(A | B | C)" (or, using
- X * DeMorgan's Law, "ifdef !A & !B & !C") - in other words, TRUE if none of
- X * the symbols A, B, and C is currently defined.
- X *
- X * "define" alone deletes all the current definitions; "ifdef" alone is
- X * always false; "ifndef" alone is always true.
- X *
- X * The file name in the "include" directive may optionally be surrounded
- X * by "" or <>. In any case, path names are taken to be relative to
- X * the location of the file containing the "include" directive.
- X *
- X *
- X * Moon file syntax:
- X *
- X * Pcal normally calculates the approximate phase of the moon using
- X * a simple algorithm which assumes (among other things) that the
- X * length of the lunar month is constant and that the quarter moons
- X * will occur on the same day worldwide. For most users, that is
- X * adequate; however, moon-phase freaks may enter the dates and
- X * (optionally) times of quarter moons (from a reliable source such
- X * as an almanac or astronomical table) into a file called .moonXX
- X * (moonXX.dat on VMS), where XX is the last two digits of the year.
- X * If such a file exists (in the same directory as the date file),
- X * pcal will interpolate the phase of the moon from the information
- X * in this file instead of using the default algorithm.
- X *
- X * Entries in the moon file must conform to the following syntax:
- X *
- X * if -A flag (American date formats) specified:
- X * <quarter> <month><sep><day> {<hour><sep><min>}
- X *
- X * if -E flag (European date formats) specified:
- X * <quarter> <day><sep><month> {<hour><sep><min>}
- X *
- X * where
- X *
- X * <quarter> := "nm", "fq" or "1q", "fm", "3q" or "lq" (new
- X * moon, first quarter, full moon, last quarter)
- X * <hour> := number 0-23 (24-hour clock)
- X * <min> := number 0-59
- X *
- X * This file must contain entries for all quarter moons in the year,
- X * in chronological order; if any errors are encountered, pcal will
- X * revert to using its default algorithm.
- X *
- X * As in the date file, comments start with '#' and run through
- X * end-of-line.
- X */
- X
- X
- /*
- X * Standard headers:
- X */
- X
- #include <stdio.h>
- #include <ctype.h>
- #include <time.h>
- #include <string.h>
- X
- /*
- X * Pcal-specific definitions:
- X */
- X
- #define MAIN_MODULE 1
- #include "pcaldefs.h"
- #include "pcalglob.h"
- #include "pcallang.h"
- X
- /*
- X * Globals:
- X */
- X
- static int init_month, init_year, nmonths;
- X
- X
- /*
- X * Main program - parse and validate command-line arguments, open files,
- X * generate PostScript boilerplate and code to generate calendars.
- X *
- X * Program structure:
- X *
- X * main() looks for the environment variable (global symbol on VMS) PCAL_OPTS
- X * and, if present, calls get_args() to parse it. It then calls get_args()
- X * again to parse the command line for the date file name, any options to be
- X * in effect prior to reading the date file, and any numeric arguments (month,
- X * year, number of months). It then calls read_datefile() to read and parse
- X * the date file; any "opt" lines present will override the defaults for the
- X * command-line flags. It then calls get_args() yet again to process the
- X * remaining command-line flags, which in turn override any specified earlier.
- X *
- X * main() then attempts to open the output file (if any), and, if successful,
- X * calls write_psfile() to generate the PostScript output.
- X *
- X */
- #ifdef PROTOS
- int main(int argc,
- X char **argv)
- #else
- int main(argc, argv)
- X int argc;
- X char **argv;
- #endif
- {
- X FILE *dfp = NULL; /* date file pointer */
- X char *p, *pathlist[10];
- X char tmp[STRSIZ], progpath[STRSIZ];
- X
- X INIT_COLORS; /* copy default_color to day_color */
- X
- X /* extract root program name and program path */
- X
- X strcpy(progname, **argv ? *argv : "pcal");
- X
- X if ((p = strrchr(progname, END_PATH)) != NULL)
- X strcpy(progname, ++p);
- X if ((p = strchr(progname, '.')) != NULL)
- X *p = '\0';
- X
- X mk_path(progpath, *argv);
- X
- X /* get version from VERSION_STRING (for use in PostScript comment) */
- X strcpy(tmp, VERSION_STRING + 4);
- X p = strchr(tmp, ' ') + 1; /* skip program name */
- X *strchr(p, ' ') = '\0'; /* terminate after version */
- X strcpy(version, p);
- X
- X /*
- X * Get the arguments from a) the environment variable, b) "opt" lines
- X * in the date file, and c) the command line, in that order
- X */
- X
- X /* parse environment variable PCAL_OPTS as a command line */
- X
- X if ((p = getenv(PCAL_OPTS)) != NULL) {
- X strcpy(lbuf, "pcal "); /* dummy program name */
- X strcat(lbuf, p);
- X (void) loadwords(); /* split string into words */
- X if (! get_args(words, P_ENV, PCAL_OPTS)) {
- X usage(stderr, FALSE);
- X exit(EXIT_FAILURE);
- X }
- X }
- X
- X /* parse command-line arguments once to find name of date file, etc. */
- X
- X if (!get_args(argv, P_CMD1, NULL)) {
- X usage(stderr, FALSE);
- X exit(EXIT_FAILURE);
- X }
- X
- X /* Attempt to open the date file as specified by the [-e | -f] flags */
- X
- X switch (datefile_type) {
- X case NO_DATEFILE:
- X dfp = NULL;
- X break;
- X
- X case USER_DATEFILE:
- X /* Attempt to open user-specified calendar file: search
- X * first in PCAL_DIR (current directory if not defined)
- X * and then in the directory where the Pcal executable
- X * lives. It is a fatal error if the user-specified
- X * date file cannot be found.
- X */
- X pathlist[0] = (p = trnlog(PCAL_DIR)) ? p : "";
- X pathlist[1] = progpath;
- X pathlist[2] = NULL;
- X
- X strcpy(tmp, datefile); /* save original name for error msg */
- X
- X if ((dfp = alt_fopen(datefile, tmp, pathlist, "r")) == NULL) {
- X FPR(stderr, E_FOPEN_ERR, progname, tmp);
- X exit(EXIT_FAILURE);
- X }
- X break;
- X
- X case SYS_DATEFILE:
- X /* Attempt to open system-specified calendar file: search
- X * first in PCAL_DIR or HOME_DIR (current directory if
- X * neither is defined) and then in the directory where
- X * the Pcal executable lives. It is not an error if the
- X * system-specified date file cannot be found; Pcal will
- X * simply generate an empty calendar.
- X */
- X pathlist[0] = ((p = trnlog(PCAL_DIR)) ||
- X (p = trnlog(HOME_DIR))) ? p : "";
- X pathlist[1] = progpath;
- X pathlist[2] = NULL;
- X
- X dfp = alt_fopen(datefile, DATEFILE, pathlist, "r");
- X
- X /* if the date file has not been found and ALT_DATEFILE is
- X * defined, search same paths for ALT_DATEFILE before
- X * giving up
- X */
- #ifdef ALT_DATEFILE
- X if (!dfp)
- X dfp = alt_fopen(datefile, ALT_DATEFILE, pathlist, "r");
- #endif
- X break;
- X }
- X
- X /* read the date file (if any) and build internal data structure */
- X
- X if (dfp) {
- X curr_year = init_year;
- X read_datefile(dfp, datefile);
- X fclose(dfp);
- X } else
- X datefile[0] = '\0'; /* for PostScript comment */
- X
- X /* reparse command line - flags there supersede those in date file */
- X
- X (void) get_args(argv, P_CMD2, NULL);
- X
- X /* done with the arguments and flags - try to open the output file */
- X
- X if (*outfile && freopen(outfile, "w", stdout) == (FILE *) NULL) {
- X FPR(stderr, E_FOPEN_ERR, progname, outfile);
- X exit(EXIT_FAILURE);
- X }
- X
- X /* generate the PostScript code (cf. writefil.c) */
- X
- X write_psfile(init_month, init_year, nmonths);
- X
- X cleanup(); /* free allocated data */
- X
- X /* if output was written to a non-obvious location, tell user where */
- X
- #ifdef DEFAULT_OUTFILE
- X FPR(stderr, I_OUT_NAME, progname, outfile);
- #endif
- X
- X exit(EXIT_SUCCESS);
- }
- X
- X
- /*
- X * set_color - set one or all weekdays to print in black or gray
- X */
- #ifdef PROTOS
- void set_color(char *day,
- X int col)
- #else
- void set_color(day, col)
- X char *day; /* weekday name (or "all") */
- X int col; /* select black or gray */
- #endif
- {
- X int i;
- X
- X if (ci_strncmp(day, ALL, strlen(ALL)) == 0) /* set all days */
- X for (i = 0; i < 7; i++)
- X day_color[i] = col;
- X else /* set single day */
- X if ((i = get_weekday(day, FALSE)) != NOT_WEEKDAY)
- X day_color[i] = col;
- X
- }
- X
- X
- /*
- X * get_flag() - look up flag in flag_tbl; return pointer to its entry
- X * (NULL if not found)
- X */
- #ifdef PROTOS
- FLAG_USAGE *get_flag(char flag)
- #else
- FLAG_USAGE *get_flag(flag)
- X char flag;
- #endif
- {
- X FLAG_USAGE *pflag;
- X
- X for (pflag = flag_tbl; pflag->flag; pflag++)
- X if (flag == pflag->flag)
- X return pflag;
- X
- X return flag ? NULL : pflag; /* '\0' is a valid flag */
- }
- X
- X
- /*
- X * get_args - walk the argument list, parsing all arguments but processing only
- X * those specified (in flag_tbl[]) to be processed this pass.
- X */
- #ifdef PROTOS
- int get_args(char **argv,
- X int curr_pass,
- X char *where)
- #else
- int get_args(argv, curr_pass, where)
- X char **argv; /* argument list */
- X int curr_pass; /* current pass (P_xxx) */
- X char *where; /* for error messages */
- #endif
- {
- X char *parg, *opt, *p;
- X FLAG_USAGE *pflag, *pf;
- X int i, flag;
- X long tmp; /* for getting current month/year */
- X struct tm *p_tm;
- X int badopt = FALSE; /* flag set if bad param */
- X int nargs = 0; /* count of non-flag args */
- X int numargs[MAXARGS]; /* non-flag (numeric) args */
- X
- /*
- X * If argument follows flag (immediately or as next parameter), return
- X * pointer to it (and bump argv if necessary); else return NULL
- X */
- #define GETARG() (*(*argv + 2) ? *argv + 2 : \
- X (*(argv+1) && **(argv+1) != '-' ? *++argv : NULL))
- X
- /*
- X * Must parse numeric parameters on both command-line passes: before reading
- X * datefile (in order to set default year) and again after reading datefile
- X * (in order to set the default first month to January if -w flag was set
- X * inside the datefile)
- X */
- #define PARSE_NUM_PARAMS (curr_pass == P_CMD1 || curr_pass == P_CMD2)
- X
- X /* Walk argument list, ignoring first element (program name) */
- X
- X while (opt = *++argv) {
- X
- X /* Assume that any non-flag argument is a numeric argument */
- X if (*opt != '-') {
- X if (PARSE_NUM_PARAMS && nargs < MAXARGS) {
- X if (! IS_NUMERIC(opt))
- X goto bad_par;
- X numargs[nargs++] = atoi(opt);
- X }
- X continue;
- X }
- X
- X /* Check that flag is a) legal, and b) to be processed this pass */
- X
- X if (! (pflag = get_flag(flag = opt[1])) )
- X goto bad_par;
- X
- X /* get optional argument even if flag not processed this pass */
- X
- X parg = pflag->has_arg ? GETARG() : NULL;
- X
- X if (! (pflag->passes & curr_pass)) { /* skip flag this pass? */
- X if (curr_pass == P_OPT)
- X FPR(stderr, E_FLAG_IGNORED, progname, flag,
- X DATE_FILE, where);
- X continue;
- X }
- X
- X switch (flag) {
- X
- X case F_INITIALIZE: /* reset all flags to defaults */
- X
- X /* set up a command line to reset all of the
- X * flags; call get_args() recursively to parse it
- X * (note that some of the flags must be reset
- X * explicitly, as no command-line flags exist to
- X * reset them)
- X */
- X
- X /* reset flags described above */
- X julian_dates = JULIAN_DATES;
- X draw_moons = DRAW_MOONS;
- X do_whole_year = FALSE;
- X blank_boxes = FALSE;
- X outline_nums = FALSE;
- X
- X /* select program default for landscape/portrait
- X * mode and US/European date styles
- X */
- X sprintf(lbuf, "pcal -%c -%c",
- #if (ROTATE == LANDSCAPE)
- X F_LANDSCAPE,
- #else
- X F_PORTRAIT,
- #endif
- #if (DATE_STYLE == USA_DATES)
- X F_USA_DATES);
- #else
- X F_EUR_DATES);
- #endif
- X p = lbuf + strlen(lbuf);
- X
- X /* all other flags take arguments and are reset
- X * by specifying the flag without an argument
- X */
- X for (pf = flag_tbl; pf->flag; pf++)
- X if ((pf->passes & curr_pass) && pf->has_arg) {
- X sprintf(p, " -%c", pf->flag);
- X p += strlen(p);
- X }
- X
- X /* split new command line into words; parse it */
- X (void) loadwords();
- X (void) get_args(words, curr_pass, NULL);
- X
- X break;
- X
- X case F_BLACK_DAY: /* print day in black or gray */
- X case F_GRAY_DAY:
- X if (parg)
- X set_color(parg, flag == F_BLACK_DAY ?
- X BLACK : GRAY);
- X else
- X INIT_COLORS; /* reset to defaults */
- X break;
- X
- X case F_DAY_FONT: /* specify alternate day font */
- X strcpy(dayfont, parg ? parg : DAYFONT);
- X break;
- X
- X case F_NOTES_FONT: /* specify alternate notes font */
- X strcpy(notesfont, parg ? parg : NOTESFONT);
- X break;
- X
- X case F_TITLE_FONT: /* specify alternate title font */
- X strcpy(titlefont, parg ? parg : TITLEFONT);
- X break;
- X
- X case F_EMPTY_CAL: /* generate empty calendar */
- X datefile_type = NO_DATEFILE;
- X strcpy(datefile, "");
- X break;
- X
- X case F_DATE_FILE: /* specify alternate date file */
- X datefile_type = parg ? USER_DATEFILE : SYS_DATEFILE;
- X strcpy(datefile, parg ? parg : "");
- X break;
- X
- X case F_OUT_FILE: /* specify alternate output file */
- X strcpy(outfile, parg ? parg : OUTFILE);
- X break;
- X
- X case F_LANDSCAPE: /* generate landscape calendar */
- X rotate = LANDSCAPE;
- X strcpy(xsval, XSVAL_L);
- X strcpy(ysval, YSVAL_L);
- X strcpy(xtval, XTVAL_L);
- X strcpy(ytval, YTVAL_L);
- X break;
- X
- X case F_PORTRAIT: /* generate portrait calendar */
- X rotate = PORTRAIT;
- X strcpy(xsval, XSVAL_P);
- X strcpy(ysval, YSVAL_P);
- X strcpy(xtval, XTVAL_P);
- X strcpy(ytval, YTVAL_P);
- X break;
- X
- X case F_HELP: /* request "help" message */
- X FPR(stdout, "%s\n", VERSION_STRING + 4);
- X usage(stdout, TRUE);
- X exit(EXIT_SUCCESS);
- X break;
- X
- X case F_MOON_4: /* draw four moons */
- X case F_MOON_ALL: /* draw a moon for each day */
- X draw_moons = flag == F_MOON_ALL ? ALL_MOONS : SOME_MOONS;
- X break;
- X
- X case F_DEFINE: /* define preprocessor symbol */
- X (void) do_define(parg);
- X break;
- X
- X case F_UNDEF: /* undef preprocessor symbol */
- X (void) do_undef(parg);
- X break;
- X
- X case F_L_FOOT: /* specify alternate left foot */
- X strcpy(lfoot, parg ? parg : LFOOT);
- X break;
- X
- X case F_C_FOOT: /* specify alternate center foot */
- X strcpy(cfoot, parg ? parg : CFOOT);
- X break;
- X
- X case F_R_FOOT: /* specify alternate right foot */
- X strcpy(rfoot, parg ? parg : RFOOT);
- X break;
- X
- X case F_FIRST_DAY: /* select starting day of week */
- X if (parg) {
- X if ((i = get_weekday(parg, FALSE)) != NOT_WEEKDAY)
- X first_day_of_week = i;
- X }
- X else
- X first_day_of_week = FIRST_DAY;
- X break;
- X
- X case F_USA_DATES: /* select American date style */
- X case F_EUR_DATES: /* select European date style */
- X date_style = flag == F_USA_DATES ? USA_DATES : EUR_DATES;
- X break;
- X
- X case F_X_TRANS: /* set x-axis translation factor */
- X strcpy(xtval, parg ? parg :
- X (rotate == LANDSCAPE ? XTVAL_L : XTVAL_P));
- X break;
- X
- X case F_Y_TRANS: /* set y-axis translation factor */
- X strcpy(ytval, parg ? parg :
- X (rotate == LANDSCAPE ? YTVAL_L : YTVAL_P));
- X break;
- X
- X case F_X_SCALE: /* set x-axis scaling factor */
- X strcpy(xsval, parg ? parg :
- X (rotate == LANDSCAPE ? XSVAL_L : XSVAL_P));
- X break;
- X
- X case F_Y_SCALE: /* set y-axis scaling factor */
- X strcpy(ysval, parg ? parg :
- X (rotate == LANDSCAPE ? YSVAL_L : YSVAL_P));
- X break;
- X
- X case F_JULIAN:
- X case F_JULIAN_ALL:
- X julian_dates = flag == F_JULIAN_ALL ? ALL_JULIANS :
- X SOME_JULIANS;
- X break;
- X
- X case F_WHOLE_YEAR:
- X do_whole_year = TRUE;
- X break;
- X
- X case F_BLANK_BOXES:
- X blank_boxes = TRUE;
- X break;
- X
- X case F_OUTLINE:
- X outline_nums = TRUE;
- X break;
- X
- X case '-' : /* accept - and -- as dummy flags */
- X case '\0':
- X break;
- X
- X default: /* missing case label if reached!!! */
- X
- bad_par: /* unrecognized parameter */
- X
- X FPR(stderr, E_ILL_OPT, progname, opt);
- X if (where)
- X FPR(stderr, E_ILL_OPT2,
- X curr_pass == P_ENV ? ENV_VAR :
- X curr_pass == P_OPT ? DATE_FILE : "",
- X where);
- X FPR(stderr, "\n");
- X badopt = TRUE;
- X break;
- X }
- X }
- X
- X if (! PARSE_NUM_PARAMS)
- X return !badopt; /* return TRUE if OK, FALSE if error */
- X
- X /* Validate non-flag (numeric) parameters */
- X
- X switch (nargs) {
- X case 0: /* no arguments - print current month and/or year */
- X time(&tmp);
- X p_tm = localtime(&tmp);
- X init_month = do_whole_year ? JAN : p_tm->tm_mon + 1;
- X init_year = p_tm->tm_year;
- X nmonths = 1;
- X break;
- X case 1: /* one argument - print entire year */
- X init_month = JAN;
- X init_year = numargs[0];
- X nmonths = 12;
- X break;
- X default: /* two or three arguments - print one or more months */
- X init_month = numargs[0];
- X init_year = numargs[1];
- X nmonths = nargs > 2 ? numargs[2] : 1;
- X break;
- X }
- X
- X if (nmonths < 1) /* ensure at least one month */
- X nmonths = 1;
- X
- X /* check range of month and year */
- X
- X if (init_month < JAN || init_month > DEC) {
- X FPR(stderr, E_ILL_MONTH, progname, init_month, JAN, DEC);
- X badopt = TRUE;
- X }
- X
- X if (init_year > 0 && init_year < 100) /* treat nn as 19nn */
- X init_year += CENTURY;
- X
- X if (init_year < MIN_YR || init_year > MAX_YR) {
- X FPR(stderr, E_ILL_YEAR, progname, init_year, MIN_YR, MAX_YR);
- X badopt = TRUE;
- X }
- X
- X return !badopt; /* return TRUE if OK, FALSE if error */
- }
- X
- X
- X
- /*
- X * color_msg - return character string explaining default day colors
- X */
- #ifdef PROTOS
- char *color_msg(void)
- #else
- char *color_msg()
- #endif
- {
- X int i, ngray = 0, others;
- X static char msg[80];
- X
- X for (i = SUN; i <= SAT; i++) /* count gray weekdays */
- X if (default_color[i] == GRAY)
- X ngray++;
- X
- X if (ngray == 0 || ngray == 7) { /* all same color? */
- X sprintf(msg, COLOR_MSG_1, ngray ? W_GRAY : W_BLACK);
- X return msg;
- X }
- X
- X others = ngray <= 3 ? BLACK : GRAY; /* no - get predominant color */
- X msg[0] = '\0';
- X for (i = SUN; i <= SAT; i++)
- X if (default_color[i] != others) {
- X strncat(msg, days[i], MIN_DAY_LEN);
- X strcat(msg, "/");
- X }
- X LASTCHAR(msg) = ' ';
- X
- X sprintf(msg + strlen(msg), COLOR_MSG_2,
- X others == BLACK ? W_GRAY : W_BLACK,
- X others == BLACK ? W_BLACK : W_GRAY);
- X return msg;
- }
- X
- X
- /*
- X * usage - print message explaining correct usage of the command-line
- X * arguments and flags. If "fullmsg" is true, print associated text
- X */
- #ifdef PROTOS
- void usage(FILE *fp,
- X int fullmsg)
- #else
- void usage(fp, fullmsg)
- X FILE *fp; /* destination of usage message */
- X int fullmsg; /* print complete message? */
- #endif
- {
- X FLAG_MSG *pflag;
- X PARAM_MSG *ppar;
- X DATE_MSG *pdate;
- X char buf[30], *p, flag, *meta;
- X int nchars, first, i, indent, n;
- X
- X sprintf(buf, "%s: %s", W_USAGE, progname);
- X nchars = indent = strlen(buf);
- X first = TRUE;
- X meta = p = NULL;
- X FPR(fp, "\n%s", buf);
- X
- X /* loop to print command-line syntax message (by group of flags) */
- X
- X for (pflag = flag_msg; (flag = pflag->flag) != '\0'; pflag++) {
- X if (flag == '\n') { /* end of group? */
- X if (p)
- X *p = '\0';
- X if (meta) { /* append metavariable name */
- X strcat(buf, " ");
- X strcat(buf, meta);
- X }
- X strcat(buf, "]");
- X n = strlen(buf);
- X if (nchars + n > SCREENWIDTH) { /* does it fit on line? */
- X FPR(fp, "\n"); /* no - start new one */
- X for (nchars = 0; nchars < indent; nchars++)
- X FPR(fp, " ");
- X }
- X FPR(fp, "%s", buf);
- X nchars += n;
- X first = TRUE;
- X }
- X else if (flag != ' ') { /* accumulate flags for group */
- X if (first) {
- X sprintf(buf, " [");
- X p = buf + strlen(buf);
- X }
- X else
- X *p++ = '|';
- X *p++ = '-';
- X *p++ = flag;
- X meta = pflag->meta; /* save metavariable name */
- X first = FALSE;
- X }
- X }
- X
- X /* loop to print selected numeric parameter descriptions */
- X
- X for (i = 0; i < PARAM_MSGS; i++) {
- X sprintf(buf, " [%s]%s", param_msg[i].desc,
- X i < PARAM_MSGS - 1 ? " |" : "");
- X n = strlen(buf);
- X if (nchars + n > SCREENWIDTH) { /* does it fit on line? */
- X FPR(fp, "\n"); /* no - start new one */
- X for (nchars = 0; nchars < indent; nchars++)
- X FPR(fp, " ");
- X }
- X FPR(fp, "%s", buf);
- X nchars += n;
- X }
- X
- X FPR(fp, "\n\n");
- X if (! fullmsg) {
- X FPR(fp, USAGE_MSG, progname, F_HELP);
- X return;
- X }
- X
- X /* loop to print the full flag descriptions */
- X
- X for (pflag = flag_msg; (flag = pflag->flag) != '\0'; pflag++) {
- X if (flag == '\n') { /* newline? print and quit */
- X FPR(fp, "\n");
- X continue;
- X }
- X p = buf; /* copy flag and metavariable to buffer */
- X if (flag != ' ')
- X *p++ = '-';
- X /* special hack for VMS - surround upper-case flags in quotes */
- #ifdef VMS
- X if (isupper(flag)) {
- X *p++ = '"';
- X *p++ = flag;
- X *p++ = '"';
- X }
- X else
- X *p++ = flag;
- #else
- X *p++ = flag;
- #endif
- X *p = '\0';
- X if (pflag->meta)
- X sprintf(p, " <%s>", pflag->meta);
- X FPR(fp, "\t%-16.16s", buf);
- X if (pflag->text)
- X FPR(fp, "%s", pflag->text);
- X
- X /* print default value if specified */
- X if (pflag->def)
- X FPR(fp, " (%s: %s)", W_DEFAULT, pflag->def[0] ? pflag->def : "\"\"" );
- X FPR(fp, "\n");
- X
- X /* special case - print color messages after F_GRAY_DAY */
- X if (flag == F_GRAY_DAY)
- X FPR(fp, "\t\t\t (%s: %s)\n", W_DEFAULT, color_msg());
- X
- X }
- X
- X /* now print the information about the numeric parameters */
- X
- X for (ppar = param_msg; ppar->desc; ppar++)
- X FPR(fp, "\t%-16.16s%s\n", ppar->desc, ppar->text);
- X
- X /* print the date file syntax message */
- X
- X FPR(fp, "\n");
- X for (pdate = date_msg; *pdate; pdate++)
- X FPR(fp, "\t%s\n", *pdate);
- X
- }
- X
- X
- /*
- X * alt_fopen - attempt to open a file in one of several paths in a
- X * NULL-terminated path list. If successful, return (opened) file pointer
- X * and fill in full path name; if not, return NULL
- X */
- #ifdef PROTOS
- FILE *alt_fopen(char *fullpath, char *name, char *pathlist[], char *access)
- #else
- FILE *alt_fopen(fullpath, name, pathlist, access)
- X char *fullpath; /* full path name (output) */
- X char *name; /* base name (or full path spec) */
- X char *pathlist[]; /* NULL-terminated path list */
- X char *access; /* permission requested */
- #endif
- {
- X char **path;
- X FILE *fp;
- X
- X for (path = pathlist; *path; path++) {
- X mk_filespec(fullpath, *path, name);
- X if ((fp = fopen(fullpath, access)) != NULL)
- X return fp;
- X }
- X
- X fullpath[0] = '\0'; /* file not found */
- X return NULL;
- }
- X
- SHAR_EOF
- chmod 0666 pcal.c ||
- echo 'restore of pcal.c failed'
- Wc_c="`wc -c < 'pcal.c'`"
- test 33851 -eq "$Wc_c" ||
- echo 'pcal.c: original size 33851, current size' "$Wc_c"
- fi
- true || echo 'restore of pcal.man failed'
- echo End of part 2, continue with part 3
- exit 0
-