home *** CD-ROM | disk | FTP | other *** search
Text File | 1991-08-22 | 55.3 KB | 1,945 lines |
- Path: wupost!zaphod.mps.ohio-state.edu!pacific.mps.ohio-state.edu!linac!att!cbnews!jbr0
- From: jbr0@cbnews.cb.att.com (joseph.a.brownlee)
- Newsgroups: alt.sources
- Subject: Pcal v4.1, part 3 of 6
- Keywords: pcal calendar postscript
- Message-ID: <1991Aug19.121551.678@cbnews.cb.att.com>
- Date: 19 Aug 91 12:15:51 GMT
- Followup-To: alt.sources.d
- Organization: AT&T Bell Laboratories
- Lines: 1933
-
- #!/bin/sh
- # This is part 03 of a multipart archive
- # ============= 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 * The following suite of routines are to calculate the phase of the moon
- X * for a given month, day, year. They compute the phase of the moon for
- X * noon (UT) on the day requested, the start of the Julian day.
- X *
- X * Contents:
- X *
- X * calc_phase
- X * find_moonfile
- X * find_phase
- X * julday
- X * read_moonfile
- X *
- X * Revision history:
- X *
- X * 4.1 AWR 08/02/91 Fix bug in julday() where result is
- X * calculated incorrectly if floor() has
- X * no prototype
- X *
- X * 4.01 RLD 03/19/91 Upgraded calc_phase() to accurately
- X * calculate the lunar phase for any
- X * day without needing to resort to
- X * a moon file.
- 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 * Standard headers:
- X */
- X
- #include <stdio.h>
- #include <string.h>
- #include <ctype.h>
- #include <math.h>
- X
- /*
- X * Pcal-specific definitions:
- X */
- X
- #include "pcaldefs.h"
- #include "pcalglob.h"
- #include "pcallang.h"
- X
- /*
- X * Macros:
- X */
- X
- /* Astronomical constants. */
- X
- #define epoch 2444238.5 /* 1980 January 0.0 */
- X
- /* Constants defining the Sun's apparent orbit. */
- X
- #define elonge 278.833540 /* ecliptic longitude of the Sun
- X at epoch 1980.0 */
- #define elongp 282.596403 /* ecliptic longitude of the Sun at
- X perigee */
- #define eccent 0.016718 /* eccentricity of Earth's orbit */
- X
- /* Elements of the Moon's orbit, epoch 1980.0. */
- X
- #define mmlong 64.975464 /* moon's mean lonigitude at the epoch */
- #define mmlongp 349.383063 /* mean longitude of the perigee at the
- X epoch */
- #define mlnode 151.950429 /* mean longitude of the node at the
- X epoch */
- #define synmonth 29.53058868 /* synodic month (new Moon to new Moon) */
- X
- #define PI 3.14159265358979323846 /* assume not near black hole nor in
- X Tennessee ;) */
- X
- X
- /* Handy mathematical functions. */
- X
- #define sgn(x) (((x) < 0) ? -1 : ((x) > 0 ? 1 : 0)) /* extract sign */
- #ifndef abs
- #define abs(x) ((x) < 0 ? (-(x)) : (x)) /* absolute val */
- #endif
- #define fixangle(a) ((a) - 360.0 * (floor((a) / 360.0))) /* fix angle */
- #define torad(d) ((d) * (PI / 180.0)) /* deg->rad */
- #define todeg(d) ((d) * (180.0 / PI)) /* rad->deg */
- #define dsin(x) (sin(torad((x)))) /* sin from deg */
- #define dcos(x) (cos(torad((x)))) /* cos from deg */
- #define FNITG(x) (sgn (x) * floor (abs (x)))
- X
- X
- /* Misc. stuff, to disappear someday along with moon file routines */
- X
- #define HOUR 12 /* hour of day when phase calculated */
- #define PERIOD synmonth /* for backward compatibility */
- 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 accurately calculate the phase of the moon
- X *
- X * Originally adapted from "moontool.c" by John Walker, Release 2.0.
- X *
- X * This routine (calc_phase) and its support routines were adapted from
- X * phase.c (v 1.2 88/08/26 22:29:42 jef) in the program "xphoon"
- X * (v 1.9 88/08/26 22:29:47 jef) by Jef Poskanzer and Craig Leres.
- X * The necessary notice follows...
- X *
- X * Copyright (C) 1988 by Jef Poskanzer and Craig Leres.
- X *
- X * Permission to use, copy, modify, and distribute this software and its
- X * documentation for any purpose and without fee is hereby granted,
- X * provided that the above copyright notice appear in all copies and that
- X * both that copyright notice and this permission notice appear in
- X * supporting documentation. This software is provided "as is" without
- X * express or implied warranty.
- X *
- X * These were added to "pcal" by RLD on 19-MAR-1991
- X */
- X
- X
- /*
- X * julday -- calculate the julian date from input month, day, year
- X * N.B. - The Julian date is computed for noon UT.
- X *
- X * Adopted from Peter Duffett-Smith's book `Astronomy With Your
- X * Personal Computer' by Rick Dyson 18-MAR-1991
- X */
- #ifdef PROTOS
- double julday(int month,
- X int day,
- X int year)
- #else
- double julday(month, day, year)
- X int month, day, year;
- #endif
- {
- X int mn1, yr1;
- X double a, b, c, d, djd;
- X
- X mn1 = month;
- X yr1 = year;
- X if ( yr1 < 0 ) yr1 = yr1 + 1;
- X if ( month < 3 )
- X {
- X mn1 = month + 12;
- X yr1 = yr1 - 1;
- X }
- X if (( year < 1582 ) ||
- X ( year == 1582 && month < 10 ) ||
- X ( year == 1582 && month == 10 && day < 15.0 ))
- X b = 0;
- X else
- X {
- X a = floor (yr1 / 100.0);
- X b = 2 - a + floor (a / 4);
- X }
- X if ( yr1 >= 0 )
- X c = floor (365.25 * yr1) - 694025;
- X else
- X c = FNITG ((365.25 * yr1) - 0.75) - 694025;
- X d = floor (30.6001 * (mn1 + 1));
- X djd = b + c + d + day + 2415020.0;
- X return djd;
- }
- X
- X
- /*
- X * kepler - solve the equation of Kepler
- X */
- #ifdef PROTOS
- static double kepler(double m,
- X double ecc)
- #else
- static double kepler(m, ecc)
- X double m, ecc;
- #endif
- {
- X double e, delta;
- #define EPSILON 1E-6
- X
- X e = m = torad(m);
- X do {
- X delta = e - ecc * sin(e) - m;
- X e -= delta / (1 - ecc * cos(e));
- X } while (abs(delta) > EPSILON);
- X return e;
- }
- X
- X
- /*
- X * calc_phase - calculate phase of moon as a fraction:
- X *
- X * The argument is the time for which the phase is requested,
- X * expressed as the month, day and year. It returns the phase of
- X * the moon (0.0 -> 0.99) with the ordering as New Moon, First Quarter,
- X * Full Moon, and Last Quarter.
- X *
- X * Converted from the subroutine phase.c used by "xphoon.c" (see
- X * above disclaimer) into calc_phase() for use in "moonphas.c"
- X * by Rick Dyson 18-MAR-1991
- X */
- #ifdef PROTOS
- double calc_phase(int month,
- X int inday,
- X int year)
- #else
- double calc_phase(month, inday, year)
- X int month, inday, year;
- #endif
- {
- X double Day, N, M, Ec, Lambdasun, ml, MM, MN, Ev, Ae, A3, MmP,
- X mEc, A4, lP, V, lPP, NP, y, x, MoonAge, pdate;
- X
- X /* need to convert month, day, year into a Julian pdate */
- X pdate = julday (month, inday, year);
- X
- X /* Calculation of the Sun's position. */
- X
- X Day = pdate - epoch; /* date within epoch */
- X N = fixangle((360 / 365.2422) * Day); /* mean anomaly of the Sun */
- X M = fixangle(N + elonge - elongp); /* convert from perigee
- X co-ordinates to epoch 1980.0 */
- X Ec = kepler(M, eccent); /* solve equation of Kepler */
- X Ec = sqrt((1 + eccent) / (1 - eccent)) * tan(Ec / 2);
- X Ec = 2 * todeg(atan(Ec)); /* true anomaly */
- X Lambdasun = fixangle(Ec + elongp); /* Sun's geocentric ecliptic
- X longitude */
- X
- X /* Calculation of the Moon's position. */
- X
- X /* Moon's mean longitude. */
- X ml = fixangle(13.1763966 * Day + mmlong);
- X
- X /* Moon's mean anomaly. */
- X MM = fixangle(ml - 0.1114041 * Day - mmlongp);
- X
- X /* Moon's ascending node mean longitude. */
- X MN = fixangle(mlnode - 0.0529539 * Day);
- X
- X /* Evection. */
- X Ev = 1.2739 * sin(torad(2 * (ml - Lambdasun) - MM));
- X
- X /* Annual equation. */
- X Ae = 0.1858 * sin(torad(M));
- X
- X /* Correction term. */
- X A3 = 0.37 * sin(torad(M));
- X
- X /* Corrected anomaly. */
- X MmP = MM + Ev - Ae - A3;
- X
- X /* Correction for the equation of the centre. */
- X mEc = 6.2886 * sin(torad(MmP));
- X
- X /* Another correction term. */
- X A4 = 0.214 * sin(torad(2 * MmP));
- X
- X /* Corrected longitude. */
- X lP = ml + Ev + mEc - Ae + A4;
- X
- X /* Variation. */
- X V = 0.6583 * sin(torad(2 * (lP - Lambdasun)));
- X
- X /* True longitude. */
- X lPP = lP + V;
- X
- X /* Calculation of the phase of the Moon. */
- X
- X /* Age of the Moon in degrees. */
- X MoonAge = lPP - Lambdasun;
- X
- X return (fixangle (MoonAge) / 360.0);
- }
- 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 phase *= synmonth;
- X return (phase >= synmonth - 0.5 || phase < 0.5) ||
- X (phase >= 0.25 * synmonth - 0.5 && phase < 0.25 * synmonth + 0.5) ||
- X (phase >= 0.50 * synmonth - 0.5 && phase < 0.50 * synmonth + 0.5) ||
- X (phase >= 0.75 * synmonth - 0.5 && phase < 0.75 * synmonth + 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 * cf. find_executable() in pcalutil.c - 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 14863 -eq "$Wc_c" ||
- echo 'moonphas.c: original size 14863, 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.02 AWR 06/07/91 added find_executable()
- 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();
- char *find_executable();
- 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 2051 -eq "$Wc_c" ||
- echo 'noprotos.h: original size 2051, 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.1 - generate 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 Jef Poskanzer and Craig Leres, and were incorporated into Pcal
- X * by Richard Dyson.
- 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.1 AWR 08/02/91 Add -G flag to print "gray" dates as
- X * outlined, gray-filled characters
- X *
- X * Fix potential bug in julday() (cf.
- X * moonphas.c)
- X *
- X * 4.02 AWR 07/02/91 Add -v flag to print version info only;
- X * call find_executable() to get true
- X * program pathname (cf. pcalutil.c);
- X * add format specifiers to text strings
- X * (cf. writefil.c)
- X *
- X * 4.01 AWR 03/19/91 Incorporate revised moonphas.c (q.v.)
- 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 * Support negative ordinals (cf.
- X * readfile.c, pcalutil.c)
- X *
- X * Support expressions in preprocessor
- X * "if{n}def" lines (cf. exprpars.c)
- X *
- X * Support "even", "odd" ordinals (cf.
- X * readfile.c) and ordinals > 5th
- X *
- X * Support -B (leave unused boxes blank)
- X * flag
- X *
- X * Separated into moonphas.c, pcal.c,
- X * pcalutil.c, readfile.c, and writefil.c;
- X * added support for moon phase file
- X *
- X * 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 * 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 * -G print "gray" dates as filled outlines
- X * -O print "gray" dates as unfilled outlines
- X * (default: gray 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 version information
- X * and "usage" message to stdout
- X * -v (command line only) write version information
- X * alone 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, -v, -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_name> in <month_spec>{*} {<text>}
- X * <day_name> <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, -v, -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 * Pcal also allows format specifiers in the text (and foot strings -
- X * cf. the -L, -C, and -R options); each will be replaced by its
- X * equivalent string as outlined in the table below. (Most of these are
- X * derived from the strftime() function; %[lo0+-] are Pcal-specific.)
- X *
- X * %a : abbreviated weekday
- X * %A : full weekday
- X * %b : abbreviated month name
- X * %B : full month name
- X * %d : day of month (1-31)
- X * %j : day of year (1-366)
- X * %l : days left in year (0-365)
- X * %m : month (1-12)
- X * %U : week number (0-53)
- X * %W : week number (0-53)
- X * %y : year w/o century (00-99)
- X * %Y : year w/century
- X * %% : '%' character
- X *
- X * %o : print number as ordinal
- X * %0 : print number with leading zeroes
- X * %+ : use following month or year
- X * %- : use previous month or year
- X *
- X * %U considers the first logical Sunday (the first day of the week as
- X * printed; cf. the -F flag) of the year as the first day of week 1;
- X * %W uses the first logical Monday instead. This is an extension of
- X * strftime()'s behavior.
- X *
- X * %o prints a number as an ordinal, with the appropriate suffix ("st",
- X * "nd", "rd", or "th" in English) appended; for example, "%od" prints
- X * the day of the month as "1st, 2nd, 3rd, ...".
- X *
- X * Unlike strftime(), Pcal's default is to print numbers (except %y)
- X * without leading zeroes. If leading zeroes are desired, the '0'
- X * prefix may be used; for example, "%0j" prints the day of year as
- X * 001-365.
- X *
- X * %+ and %- direct Pcal to substitute the following/previous month/year
- X * in the following [bBmyY] specifier; for example, %+B prints the name
- X * of the next month.
- 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 * The user may enter the dates and (optionally) times of quarter
- X * moons (from a reliable source such as an almanac or astronomical
- X * table) into a file called .moonXX (moonXX.dat on VMS), where XX is
- X * the last two digits of the year. If such a file exists (in the
- X * same directory as the date file), Pcal will interpolate the phase
- X * of the moon from the information in this file instead of using the
- X * default algorithm.
- X *
- X * (Pcal originally used an extremely simplistic moon phase algorithm;
- X * the moon file was added to v4.0 to enable Pcal to interpolate the
- X * phase of the moon from the [presumably more accurate] information
- X * within. More recently, the original moon phase algorithm was
- X * superseded by an astronomer-strength version, largely obviating
- X * the need for a moon file; however, it will continue to be
- X * supported for the foreseeable future.)
- 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", "lq" or "3q" (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 - note that some
- X * systems supply the full pathname and others just the root
- X */
- 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, find_executable(*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 num_style = NUM_STYLE;
- 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 or version */
- X case F_VERSION:
- X FPR(stdout, "%s\n", VERSION_STRING + 4);
- X if (flag == F_HELP)
- 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 case F_OUTLINE_GRAY:
- X num_style = flag == F_OUTLINE ? OUTLINE_NUMS :
- X FILLED_NUMS;
- 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 36344 -eq "$Wc_c" ||
- echo 'pcal.c: original size 36344, current size' "$Wc_c"
- fi
- true || echo 'restore of pcal.man failed'
- echo End of part 3, continue with part 4
- exit 0
-