Pcal v4.0, part 2 of 5
joseph.a.brownlee
jbr0 at cbnews.att.com
Thu Mar 14 22:48:13 AEST 1991
#!/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
More information about the Alt.sources
mailing list