Path: utzoo!utgpu!cs.utexas.edu!swrinde!mips!pacbell.com!att!cbnews!jbr0 From: jbr0@cbnews.att.com (joseph.a.brownlee) Newsgroups: alt.sources Subject: Pcal v4.0, part 2 of 5 Summary: pcal, the postscript calendar program Keywords: postscript calendar pcal Message-ID: <1991Mar14.124813.9360@cbnews.att.com> Date: 14 Mar 91 12:48:13 GMT Sender: jbr@cblph.att.com Reply-To: jbr@cblph.att.com Followup-To: alt.sources.d Organization: AT&T Bell Laboratories Lines: 2189 #!/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 #include #include 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: # {} # # if -E flag (European date formats) specified: # {} # # where # # := "nm", "fq" or "1q", "fm", "3q" or "lq" (new # moon, first quarter, full moon, last quarter) # := number 0-23 (24-hour clock) # := 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 #include #include 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 {