jbr0@cbnews.att.com (joseph.a.brownlee) (03/14/91)
#!/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