home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 3 / 3863 < prev    next >
Encoding:
Text File  |  1991-08-22  |  55.3 KB  |  1,945 lines

  1. Path: wupost!zaphod.mps.ohio-state.edu!pacific.mps.ohio-state.edu!linac!att!cbnews!jbr0
  2. From: jbr0@cbnews.cb.att.com (joseph.a.brownlee)
  3. Newsgroups: alt.sources
  4. Subject: Pcal v4.1, part 3 of 6
  5. Keywords: pcal calendar postscript
  6. Message-ID: <1991Aug19.121551.678@cbnews.cb.att.com>
  7. Date: 19 Aug 91 12:15:51 GMT
  8. Followup-To: alt.sources.d
  9. Organization: AT&T Bell Laboratories
  10. Lines: 1933
  11.  
  12. #!/bin/sh
  13. # This is part 03 of a multipart archive
  14. # ============= moonphas.c ==============
  15. if test -f 'moonphas.c' -a X"$1" != X"-c"; then
  16.     echo 'x - skipping moonphas.c (File already exists)'
  17. else
  18. echo 'x - extracting moonphas.c (Text)'
  19. sed 's/^X//' << 'SHAR_EOF' > 'moonphas.c' &&
  20. /*
  21. X *  moonphas.c - encapsulates routines used by Pcal for moon phase calculation
  22. X *
  23. X *  The following suite of routines are to calculate the phase of the moon
  24. X *  for a given month, day, year.  They compute the phase of the moon for
  25. X *  noon (UT) on the day requested, the start of the Julian day.
  26. X *
  27. X *  Contents:
  28. X *
  29. X *        calc_phase
  30. X *        find_moonfile
  31. X *        find_phase
  32. X *        julday
  33. X *        read_moonfile
  34. X *
  35. X *  Revision history:
  36. X *
  37. X *    4.1    AWR    08/02/91    Fix bug in julday() where result is
  38. X *                    calculated incorrectly if floor() has
  39. X *                    no prototype
  40. X *
  41. X *      4.01    RLD     03/19/91        Upgraded calc_phase() to accurately
  42. X *                                      calculate the lunar phase for any
  43. X *                                      day without needing to resort to
  44. X *                                      a moon file.
  45. X *
  46. X *    4.0    AWR    03/07/91    Add find_moonfile()
  47. X *
  48. X *            01/15/91    Author: translated PostScript
  49. X *                    routines to C and added moon
  50. X *                    file routines
  51. X */
  52. X
  53. /*
  54. X * Standard headers:
  55. X */
  56. X
  57. #include <stdio.h>
  58. #include <string.h>
  59. #include <ctype.h>
  60. #include <math.h>
  61. X
  62. /*
  63. X * Pcal-specific definitions:
  64. X */
  65. X
  66. #include "pcaldefs.h"
  67. #include "pcalglob.h"
  68. #include "pcallang.h"
  69. X
  70. /*
  71. X * Macros:
  72. X */
  73. X
  74. /*  Astronomical constants. */
  75. X
  76. #define epoch        2444238.5       /* 1980 January 0.0 */
  77. X
  78. /*  Constants defining the Sun's apparent orbit. */
  79. X
  80. #define elonge        278.833540       /* ecliptic longitude of the Sun
  81. X                        at epoch 1980.0 */
  82. #define elongp        282.596403       /* ecliptic longitude of the Sun at
  83. X                        perigee */
  84. #define eccent      0.016718       /* eccentricity of Earth's orbit */
  85. X
  86. /*  Elements of the Moon's orbit, epoch 1980.0. */
  87. X
  88. #define mmlong      64.975464      /* moon's mean lonigitude at the epoch */
  89. #define mmlongp     349.383063       /* mean longitude of the perigee at the
  90. X                                        epoch */
  91. #define mlnode        151.950429       /* mean longitude of the node at the
  92. X                        epoch */
  93. #define synmonth    29.53058868    /* synodic month (new Moon to new Moon) */
  94. X
  95. #define PI 3.14159265358979323846  /* assume not near black hole nor in
  96. X                        Tennessee ;) */
  97. X
  98. X
  99. /*  Handy mathematical functions. */
  100. X
  101. #define sgn(x) (((x) < 0) ? -1 : ((x) > 0 ? 1 : 0))      /* extract sign */
  102. #ifndef abs
  103. #define abs(x) ((x) < 0 ? (-(x)) : (x))           /* absolute val */
  104. #endif
  105. #define fixangle(a) ((a) - 360.0 * (floor((a) / 360.0)))  /* fix angle      */
  106. #define torad(d) ((d) * (PI / 180.0))              /* deg->rad      */
  107. #define todeg(d) ((d) * (180.0 / PI))              /* rad->deg      */
  108. #define dsin(x) (sin(torad((x))))              /* sin from deg */
  109. #define dcos(x) (cos(torad((x))))              /* cos from deg */
  110. #define FNITG(x) (sgn (x) * floor (abs (x)))
  111. X
  112. X
  113. /* Misc. stuff, to disappear someday along with moon file routines */
  114. X
  115. #define HOUR        12        /* hour of day when phase calculated */
  116. #define PERIOD        synmonth    /* for backward compatibility */
  117. X
  118. /* convert "n" so that 0.0 <= n < 1.0 */
  119. #define NORMALIZE(n)    \
  120. X    if (1) { while (n < 0.0) n++; while (n >= 1.0) n--; } else
  121. X
  122. /* interpolate phase for day "d" from moon_info array elements "n1" and "n2" */
  123. #define CALC_PHASE(d, n1, n2) \
  124. X     moon_info[n1].phase + ((d) - moon_info[n1].doy) * \
  125. X    ((moon_info[n2].phase - moon_info[n1].phase) / \
  126. X     (moon_info[n2].doy - moon_info[n1].doy))
  127. X
  128. /* generate error message, close file, and quit */
  129. #define ERR_EXIT(msg)    \
  130. X    if (1) { ERR(msg); fclose(fp); return FALSE; } else
  131. X
  132. /* day and phase sequence error conditions - cf. read_moonfile() */
  133. #define DAY_TOO_SOON    (nrec > 1 && doy < prevdoy + 6)
  134. #define DAY_TOO_LATE    (doy > prevdoy + 9)
  135. #define WRONG_PHASE    (nrec > 1 && ph != (prevph + 1) % 4)
  136. X
  137. X
  138. /*
  139. X * Globals:
  140. X */
  141. X
  142. typedef struct {
  143. X    int    doy;    /* day of year (1..366) */
  144. X    double    phase;    /* moon phase (cycles since new moon prior to 1/1) */
  145. } MOON_INFO;
  146. X
  147. static MOON_INFO moon_info[60];        /* quarter moons for year + dummies */
  148. X
  149. X
  150. /*
  151. X *  Routines to accurately calculate the phase of the moon
  152. X *
  153. X *  Originally adapted from "moontool.c" by John Walker, Release 2.0.
  154. X *
  155. X *      This routine (calc_phase) and its support routines were adapted from
  156. X *      phase.c (v 1.2 88/08/26 22:29:42 jef) in the program "xphoon"        
  157. X *      (v 1.9 88/08/26 22:29:47 jef) by Jef Poskanzer and Craig Leres.
  158. X *      The necessary notice follows...
  159. X *
  160. X *      Copyright (C) 1988 by Jef Poskanzer and Craig Leres.
  161. X *
  162. X *      Permission to use, copy, modify, and distribute this software and its
  163. X *      documentation for any purpose and without fee is hereby granted,
  164. X *      provided that the above copyright notice appear in all copies and that
  165. X *      both that copyright notice and this permission notice appear in
  166. X *      supporting documentation.  This software is provided "as is" without
  167. X *      express or implied warranty.
  168. X *
  169. X *      These were added to "pcal" by RLD on 19-MAR-1991
  170. X */
  171. X
  172. X
  173. /*
  174. X *  julday -- calculate the julian date from input month, day, year
  175. X *      N.B. - The Julian date is computed for noon UT.
  176. X *
  177. X *      Adopted from Peter Duffett-Smith's book `Astronomy With Your
  178. X *      Personal Computer' by Rick Dyson 18-MAR-1991
  179. X */
  180. #ifdef PROTOS
  181. double julday(int month,
  182. X              int day,
  183. X              int year)
  184. #else
  185. double julday(month, day, year)
  186. X    int month, day, year;
  187. #endif
  188. {
  189. X        int mn1, yr1;
  190. X        double a, b, c, d, djd;
  191. X
  192. X        mn1 = month;
  193. X        yr1 = year;
  194. X        if ( yr1 < 0 ) yr1 = yr1 + 1;
  195. X        if ( month < 3 )
  196. X            {
  197. X                mn1 = month + 12;
  198. X                yr1 = yr1 - 1;
  199. X            }
  200. X        if (( year < 1582 ) ||
  201. X            ( year == 1582  && month < 10 ) ||
  202. X                ( year == 1582  && month == 10 && day < 15.0 ))
  203. X                    b = 0;
  204. X                else
  205. X                {
  206. X                    a = floor (yr1 / 100.0);
  207. X                    b = 2 - a + floor (a / 4);
  208. X                }
  209. X        if ( yr1 >= 0 )
  210. X            c = floor (365.25 * yr1) - 694025;
  211. X        else
  212. X            c = FNITG ((365.25 * yr1) - 0.75) - 694025;
  213. X        d = floor (30.6001 * (mn1 + 1));
  214. X        djd = b + c + d + day + 2415020.0;
  215. X        return djd;
  216. }
  217. X
  218. X
  219. /*
  220. X *  kepler - solve the equation of Kepler
  221. X */
  222. #ifdef PROTOS
  223. static double kepler(double m,
  224. X                     double ecc)
  225. #else
  226. static double kepler(m, ecc)
  227. X    double m, ecc;
  228. #endif
  229. {
  230. X    double e, delta;
  231. #define EPSILON 1E-6
  232. X
  233. X    e = m = torad(m);
  234. X    do {
  235. X       delta = e - ecc * sin(e) - m;
  236. X       e -= delta / (1 - ecc * cos(e));
  237. X    } while (abs(delta) > EPSILON);
  238. X    return e;
  239. }
  240. X
  241. X
  242. /*
  243. X *  calc_phase - calculate phase of moon as a fraction:
  244. X *
  245. X *    The argument is the time for which the phase is requested,
  246. X *    expressed as the month, day and year.  It returns the phase of
  247. X *    the moon (0.0 -> 0.99) with the ordering as New Moon, First Quarter,
  248. X *      Full Moon, and Last Quarter.
  249. X *
  250. X *    Converted from the subroutine phase.c used by "xphoon.c" (see
  251. X *      above disclaimer) into calc_phase() for use in "moonphas.c"
  252. X *    by Rick Dyson 18-MAR-1991
  253. X */
  254. #ifdef PROTOS
  255. double calc_phase(int month,
  256. X          int inday,
  257. X          int year)
  258. #else
  259. double calc_phase(month, inday, year)
  260. X    int month, inday, year;
  261. #endif
  262. {
  263. X    double Day, N, M, Ec, Lambdasun, ml, MM, MN, Ev, Ae, A3, MmP,
  264. X           mEc, A4, lP, V, lPP, NP, y, x, MoonAge, pdate;
  265. X
  266. X    /*  need to convert month, day, year into a Julian pdate */
  267. X    pdate = julday (month, inday, year);
  268. X
  269. X        /*  Calculation of the Sun's position. */
  270. X
  271. X    Day = pdate - epoch;            /* date within epoch */
  272. X    N = fixangle((360 / 365.2422) * Day);    /* mean anomaly of the Sun */
  273. X    M = fixangle(N + elonge - elongp);      /* convert from perigee
  274. X                             co-ordinates to epoch 1980.0 */
  275. X    Ec = kepler(M, eccent);            /* solve equation of Kepler */
  276. X    Ec = sqrt((1 + eccent) / (1 - eccent)) * tan(Ec / 2);
  277. X    Ec = 2 * todeg(atan(Ec));        /* true anomaly */
  278. X        Lambdasun = fixangle(Ec + elongp);    /* Sun's geocentric ecliptic
  279. X                             longitude */
  280. X
  281. X        /*  Calculation of the Moon's position. */
  282. X
  283. X        /*  Moon's mean longitude. */
  284. X    ml = fixangle(13.1763966 * Day + mmlong);
  285. X
  286. X        /*  Moon's mean anomaly. */
  287. X    MM = fixangle(ml - 0.1114041 * Day - mmlongp);
  288. X
  289. X        /*  Moon's ascending node mean longitude. */
  290. X    MN = fixangle(mlnode - 0.0529539 * Day);
  291. X
  292. X    /*  Evection. */
  293. X    Ev = 1.2739 * sin(torad(2 * (ml - Lambdasun) - MM));
  294. X
  295. X    /*  Annual equation. */
  296. X    Ae = 0.1858 * sin(torad(M));
  297. X
  298. X    /*  Correction term. */
  299. X    A3 = 0.37 * sin(torad(M));
  300. X
  301. X    /*  Corrected anomaly. */
  302. X    MmP = MM + Ev - Ae - A3;
  303. X
  304. X    /*  Correction for the equation of the centre. */
  305. X    mEc = 6.2886 * sin(torad(MmP));
  306. X
  307. X    /*  Another correction term. */
  308. X    A4 = 0.214 * sin(torad(2 * MmP));
  309. X
  310. X    /*  Corrected longitude. */
  311. X    lP = ml + Ev + mEc - Ae + A4;
  312. X
  313. X    /*  Variation. */
  314. X    V = 0.6583 * sin(torad(2 * (lP - Lambdasun)));
  315. X
  316. X    /*  True longitude. */
  317. X    lPP = lP + V;
  318. X
  319. X    /*  Calculation of the phase of the Moon. */
  320. X
  321. X    /*  Age of the Moon in degrees. */
  322. X    MoonAge = lPP - Lambdasun;
  323. X
  324. X        return (fixangle (MoonAge) / 360.0);
  325. }
  326. X
  327. X
  328. /*
  329. X *  is_quarter - is "phase" within +/- 0.5 day of a quarter moon
  330. X */
  331. #ifdef PROTOS
  332. static int is_quarter(double phase)
  333. #else
  334. static int is_quarter(phase)
  335. X    double phase;
  336. #endif
  337. {
  338. X    phase *= synmonth;
  339. X    return (phase >= synmonth - 0.5 || phase < 0.5) ||
  340. X           (phase >= 0.25 * synmonth - 0.5 && phase < 0.25 * synmonth + 0.5) ||
  341. X           (phase >= 0.50 * synmonth - 0.5 && phase < 0.50 * synmonth + 0.5) ||
  342. X           (phase >= 0.75 * synmonth - 0.5 && phase < 0.75 * synmonth + 0.5);
  343. }
  344. X
  345. X
  346. /*
  347. X * Routines to read moon file and calculate moon phase from data within
  348. X */
  349. X
  350. /*
  351. X * get_phase - convert moon phase string to appropriate value
  352. X */
  353. #ifdef PROTOS
  354. static int get_phase(char *cp)
  355. #else
  356. static int get_phase(cp)
  357. X    char *cp;
  358. #endif
  359. {
  360. X    KWD *p;
  361. X
  362. X    if (!cp)
  363. X        return MOON_OTHER;
  364. X
  365. X    for (p = phases; p->name && ci_strcmp(cp, p->name); p++)
  366. X        ;
  367. X
  368. X    return p->code;
  369. }
  370. X
  371. X
  372. /*
  373. X * make_moonpath - create the full path for the moon file in 'filename';
  374. X * return pointer to 'filename'
  375. X */
  376. #ifdef PROTOS
  377. static char *make_moonpath(char *filename, char *name, int year)
  378. #else
  379. static char *make_moonpath(filename, name, year)
  380. X    char *filename;        /* full path name (output) */
  381. X    char *name;        /* base file name */
  382. X    int year;        /* year */
  383. #endif
  384. {
  385. X    char tmp[20], path[STRSIZ], *p;
  386. X
  387. X    strcpy(tmp, name);
  388. X    p = strchr(tmp, 'X');            /* replace XX with year % 100 */
  389. X    *p++ = '0' + (year / 10) % 10;
  390. X    *p   = '0' + year % 10;
  391. X
  392. X    mk_path(path, datefile);        /* get datefile path */
  393. X    mk_filespec(filename, path, tmp);    /* append file name */
  394. X    
  395. X    return filename;
  396. }
  397. X
  398. X
  399. /*
  400. X * find_moonfile - look for moon file for specified year.  If it exists
  401. X * and is readable, return its full path name; else return NULL.  (There
  402. X * are admittedly ways to do this without attempting to open the file -
  403. X * cf. find_executable() in pcalutil.c - but they may not be portable.)
  404. X */
  405. #ifdef PROTOS
  406. char *find_moonfile(int year)
  407. #else
  408. char *find_moonfile(year)
  409. X    int year;
  410. #endif
  411. {
  412. X    static char filename[STRSIZ];
  413. X    FILE *fp;
  414. X
  415. X    fp = fopen(make_moonpath(filename, MOONFILE, year), "r");
  416. X
  417. #ifdef ALT_MOONFILE
  418. X    if (!fp)             /* try again with alternate name */
  419. X        fp = fopen(make_moonpath(filename, ALT_MOONFILE, year), "r");
  420. #endif
  421. X    return fp ? (fclose(fp), filename) : NULL;
  422. }
  423. X
  424. X
  425. /*
  426. X * read_moonfile - looks for moon data file (in same directory as .calendar);
  427. X * if found, reads file, fills in moon_info[] and returns TRUE; if not found
  428. X * (or error encountered), returns FALSE
  429. X */
  430. #ifdef PROTOS
  431. int read_moonfile(int year)
  432. #else
  433. int read_moonfile(year)
  434. X    int year;
  435. #endif
  436. {
  437. X    char *filename;
  438. X    int line, nrec, month, day, hh, mm;
  439. X    int ph, prevph = MOON_OTHER, doy, prevdoy, n, quarter;
  440. X    double phase;
  441. X    FILE *fp;
  442. X
  443. X    if (! *datefile)            /* skip if no datefile */
  444. X        return FALSE;
  445. X
  446. X    /* get name of moon file and attempt to open it */
  447. X
  448. X    if ((filename = find_moonfile(year)) == NULL ||
  449. X        (fp = fopen(filename, "r")) == NULL)
  450. X        return FALSE;
  451. X
  452. X    /*
  453. X     * Moon file entries are of the form <phase> <date> {<time>}; each
  454. X     * is converted below to a moon_info[] record (note that only the
  455. X     * initial phase of the moon is directly calculated from <phase>;
  456. X     * it is subsequently used only for error checking).  Dummy entries
  457. X     * in moon_info[] precede and follow the information read from the
  458. X     * moon file; these are used for subsequent interpolation of dates
  459. X     * before the first / after the last quarter of the year.
  460. X     */
  461. X
  462. X    nrec = 1;                /* skip dummy entry */
  463. X    prevdoy = 0;
  464. X    line = 0;
  465. X
  466. X    while (getline(fp, &line)) {
  467. X
  468. X        if ((n = loadwords()) < 2 ||    /* recognizable line? */
  469. X            (ph = get_phase(words[0])) == MOON_OTHER)
  470. X            ERR_EXIT(E_INV_LINE);
  471. X
  472. X        if (nrec == 1)            /* phase at initial quarter */
  473. X            quarter = ph == MOON_NM ? 4 : ph;
  474. X
  475. X        /* extract the month and day fields (in appropriate order) */
  476. X
  477. X        (void) split_date(words[1],
  478. X                  date_style == USA_DATES ? &month : &day,
  479. X                  date_style == USA_DATES ? &day : &month,
  480. X                  NULL);
  481. X
  482. X        /* validate the date and phase */
  483. X
  484. X        if (!is_valid(month, day, year))    /* date OK? */
  485. X            ERR_EXIT(E_INV_DATE);
  486. X
  487. X        doy = DAY_OF_YEAR(month, day, year);    /* in sequence? */
  488. X        if (DAY_TOO_SOON || DAY_TOO_LATE || WRONG_PHASE)
  489. X            ERR_EXIT(E_DATE_SEQ);
  490. X
  491. X        prevdoy = doy;            /* save for sequence check */
  492. X        prevph = ph;
  493. X
  494. X        /* calculate moon phase, factoring in time (if present) */
  495. X
  496. X        phase = 0.25 * quarter++;
  497. X        if (n > 2) {            /* extract hour and minute */
  498. X            (void) split_date(words[2], &hh, &mm, NULL);
  499. X            phase += (HOUR - (hh + (mm / 60.0))) / (24 * PERIOD); 
  500. X        }
  501. X        moon_info[nrec].doy = doy;    /* enter day and phase */
  502. X        moon_info[nrec++].phase = phase;
  503. X    }
  504. X
  505. X    /* check to see that file is all there */
  506. X
  507. X    doy = YEAR_LEN(year) + 1;    /* day after end of year */
  508. X    if (DAY_TOO_LATE)
  509. X        ERR_EXIT(E_PREM_EOF);
  510. X
  511. X    /* extrapolate dummy entries from nearest lunar month */
  512. X
  513. X    moon_info[nrec].doy = doy;    /* day after end of year */
  514. X    moon_info[nrec].phase = CALC_PHASE(doy, nrec-5, nrec-1);
  515. X
  516. X    moon_info[0].doy = 0;        /* day before start of year */
  517. X    moon_info[0].phase = CALC_PHASE(0, 1, 5);
  518. X    
  519. X    fclose(fp);
  520. X    return TRUE;
  521. }
  522. X
  523. X
  524. /*
  525. X * find_phase - look up phase of moon in moon phase file (if possible);
  526. X * otherwise calculate it using calc_phase() above.  Sets *pquarter to
  527. X * TRUE if date is a quarter moon, FALSE if not
  528. X */
  529. #ifdef PROTOS
  530. double find_phase(int month,
  531. X          int day,
  532. X          int year,
  533. X          int *pquarter)
  534. #else
  535. double find_phase(month, day, year, pquarter)
  536. X    int month, day, year;
  537. X    int *pquarter;
  538. #endif
  539. {
  540. X    static int sv_year = 0;
  541. X    static int use_file;
  542. X    int i, doy;
  543. X    double phase;
  544. X
  545. X    if (year != sv_year) {        /* look for file for new year */
  546. X        use_file = read_moonfile(year);
  547. X        sv_year = year;
  548. X    }
  549. X
  550. X    if (! use_file) {        /* no file - calculate date */
  551. X        phase = calc_phase(month, day, year);
  552. X        *pquarter = is_quarter(phase);
  553. X        return phase;
  554. X    }
  555. X
  556. X    /* moon file found - use the data extracted from it */
  557. X
  558. X    doy = DAY_OF_YEAR(month, day, year);
  559. X
  560. X    for (i = 1; doy > moon_info[i].doy; i++)    /* find interval */
  561. X        ;
  562. X
  563. X    /* if day appears in table, return exact value; else interpolate */
  564. X
  565. X    phase = (*pquarter = (doy == moon_info[i].doy)) ? moon_info[i].phase :
  566. X            CALC_PHASE(doy, i-1, i);
  567. X    return phase - (int)phase;            /* 0.0 <= phase < 1.0 */
  568. }
  569. X
  570. SHAR_EOF
  571. chmod 0666 moonphas.c ||
  572. echo 'restore of moonphas.c failed'
  573. Wc_c="`wc -c < 'moonphas.c'`"
  574. test 14863 -eq "$Wc_c" ||
  575.     echo 'moonphas.c: original size 14863, current size' "$Wc_c"
  576. fi
  577. # ============= noprotos.h ==============
  578. if test -f 'noprotos.h' -a X"$1" != X"-c"; then
  579.     echo 'x - skipping noprotos.h (File already exists)'
  580. else
  581. echo 'x - extracting noprotos.h (Text)'
  582. sed 's/^X//' << 'SHAR_EOF' > 'noprotos.h' &&
  583. /*
  584. X * noprotos.h - K&R-style function declarations for Pcal sources
  585. X *
  586. X * Revision history:
  587. X *
  588. X *    4.02    AWR    06/07/91    added find_executable()
  589. X *
  590. X *    4.0    AWR    03/01/91    use <stdlib.h> where possible
  591. X *
  592. X *        AWR    02/19/91    adapted from protos.h (q.v.)
  593. X *
  594. X */
  595. X
  596. X
  597. /*
  598. X * Declarations for functions defined in exprpars.c:
  599. X */
  600. int parse_expr();
  601. X
  602. X
  603. /*
  604. X * Declarations for functions defined in moonphas.c:
  605. X */
  606. double    calc_phase();
  607. double    find_phase();
  608. char    *find_moonfile();
  609. int    read_moonfile();
  610. X
  611. X
  612. /*
  613. X * Declarations for functions defined in pcal.c:
  614. X */
  615. FILE    *alt_fopen();
  616. char    *color_msg();
  617. int    get_args();
  618. FLAG_USAGE *get_flag();
  619. int    main();
  620. void    set_color();
  621. void    usage();
  622. X
  623. X
  624. /*
  625. X * Declarations for functions defined in pcalutil.c:
  626. X */
  627. char    *alloc();
  628. int    calc_day();
  629. int    calc_weekday();
  630. int    calc_year_day();
  631. int    ci_strcmp();
  632. int    ci_strncmp();
  633. void    copy_text();
  634. char    *find_executable();
  635. int    getline();
  636. int    is_valid();
  637. int    loadwords();
  638. char    *mk_filespec();
  639. char    *mk_path();
  640. void    normalize();
  641. int    split_date();
  642. char    *trnlog();
  643. X
  644. X
  645. /*
  646. X * Declarations for functions defined in readfile.c:
  647. X */
  648. void    cleanup();
  649. void    clear_syms();
  650. int    date_type();
  651. int    do_define();
  652. int    do_ifdef();
  653. int    do_ifndef();
  654. int    do_include();
  655. int    do_undef();
  656. int    enter_day_info();
  657. int    find_sym();
  658. year_info *find_year();
  659. int    get_keywd();
  660. int    get_month();
  661. int    get_ordinal();
  662. int    get_prep();
  663. int    get_token();
  664. int    get_weekday();
  665. int    is_anyday();
  666. int    is_holiday();
  667. int    is_weekday();
  668. int    is_workday();
  669. int    not_holiday();
  670. int    not_weekday();
  671. int    not_workday();
  672. int    parse();
  673. int    parse_date();
  674. int    parse_ord();
  675. int    parse_rel();
  676. void    read_datefile();
  677. X
  678. X
  679. /*
  680. X * Declarations for functions defined in writefil.c:
  681. X */
  682. void    def_footstring();
  683. void    find_daytext();
  684. void    find_holidays();
  685. void    print_julian_info();
  686. void    print_month();
  687. void    print_moon_info();
  688. void    print_text();
  689. char    *print_word();
  690. void    write_psfile();
  691. X
  692. X
  693. /*
  694. X * Prototypes for miscellaneous library routines (if not already included
  695. X * via <stdlib.h> - cf. pcaldefs.h)
  696. X */
  697. #ifndef STDLIB
  698. extern int    atoi();
  699. extern char    *calloc();
  700. extern char    *getenv();
  701. #endif
  702. SHAR_EOF
  703. chmod 0666 noprotos.h ||
  704. echo 'restore of noprotos.h failed'
  705. Wc_c="`wc -c < 'noprotos.h'`"
  706. test 2051 -eq "$Wc_c" ||
  707.     echo 'noprotos.h: original size 2051, current size' "$Wc_c"
  708. fi
  709. # ============= pcal.c ==============
  710. if test -f 'pcal.c' -a X"$1" != X"-c"; then
  711.     echo 'x - skipping pcal.c (File already exists)'
  712. else
  713. echo 'x - extracting pcal.c (Text)'
  714. sed 's/^X//' << 'SHAR_EOF' > 'pcal.c' &&
  715. static char  VERSION_STRING[]    = "@(#)pcal v4.1 - generate Postscript calendars";
  716. /*
  717. X * pcal.c - generate PostScript file to print calendar for any month and year
  718. X *
  719. X * The original PostScript code to generate the calendars was written by
  720. X * Patrick Wood (Copyright (c) 1987 by Patrick Wood of Pipeline Associates,
  721. X * Inc.), and authorized for modification and redistribution.  The calendar
  722. X * file inclusion code was originally written in "bs(1)" by Bill Vogel of
  723. X * AT&T.  Patrick's original PostScript was modified and enhanced several
  724. X * times by King Ables, Tom Tessin, and others whose names have regrettably 
  725. X * been lost.  This C version was originally created by Ken Keirnan of Pacific
  726. X * Bell; additional enhancements by Joseph P. Larson, Ed Hand, Andrew Rogers, 
  727. X * Mark Kantrowitz, and Joe Brownlee.  The moon routines were originally 
  728. X * written by Jef Poskanzer and Craig Leres, and were incorporated into Pcal
  729. X * by Richard Dyson.
  730. X *
  731. X * Contents:
  732. X *
  733. X *        alt_fopen
  734. X *        color_msg
  735. X *        get_args
  736. X *        get_flag
  737. X *        main
  738. X *        set_color
  739. X *        usage
  740. X *
  741. X * Revision history:
  742. X *
  743. X *    4.1    AWR    08/02/91    Add -G flag to print "gray" dates as
  744. X *                    outlined, gray-filled characters
  745. X *
  746. X *                    Fix potential bug in julday() (cf.
  747. X *                    moonphas.c)
  748. X *
  749. X *    4.02    AWR    07/02/91    Add -v flag to print version info only;
  750. X *                    call find_executable() to get true
  751. X *                    program pathname (cf. pcalutil.c);
  752. X *                    add format specifiers to text strings
  753. X *                    (cf. writefil.c)
  754. X *
  755. X *    4.01    AWR    03/19/91    Incorporate revised moonphas.c (q.v.)
  756. X *
  757. X *    4.0    AWR    02/24/91    Add alt_fopen() to search for file
  758. X *                    in alternate path; use to look for
  759. X *                    date file in same directory as
  760. X *                    Pcal executable (as per Floyd Miller)
  761. X *
  762. X *                    Support negative ordinals (cf.
  763. X *                    readfile.c, pcalutil.c)
  764. X *
  765. X *                    Support expressions in preprocessor
  766. X *                    "if{n}def" lines (cf. exprpars.c)
  767. X *
  768. X *                    Support "even", "odd" ordinals (cf.
  769. X *                    readfile.c) and ordinals > 5th
  770. X *
  771. X *                    Support -B (leave unused boxes blank)
  772. X *                    flag
  773. X *
  774. X *                    Separated into moonphas.c, pcal.c,
  775. X *                    pcalutil.c, readfile.c, and writefil.c;
  776. X *                    added support for moon phase file
  777. X *
  778. X *                    Support -w (whole year) flag; fix
  779. X *                    various bugs and nonportable constructs
  780. X *
  781. X *    3.0    AWR    12/10/90    Support concept of "weekday", "workday",
  782. X *                    and "holiday" (and converses)
  783. X *
  784. X *                    Substantial revision of program logic:
  785. X *                    extracted pcaldefs.h and pcallang.h,
  786. X *                    moving all language dependencies (even
  787. X *                    flag names) to the latter.
  788. X *
  789. X *                    add -I flag to reinitialize all
  790. X *                     flags to program defaults; -j and -J
  791. X *                    flags (print Julian dates); add -x,
  792. X *                     -y, -X, -Y flags (as per Ed Hand)
  793. X *                    for output scaling and translation
  794. X *
  795. X *                    allow "wildcard" dates (e.g., "all
  796. X *                    Thursday{s} in Oct", "last Friday in
  797. X *                    all") and notes ("note all <text>);
  798. X *                    print full "help" message (including
  799. X *                    date file syntax)
  800. X *
  801. X *    2.6    AWR    10/15/90    parse floating dates (e.g. "first
  802. X *                    Monday in September") and relative
  803. X *                    floating dates (e.g., "Friday after
  804. X *                    fourth Thursday in November"); simplify
  805. X *                    logic of -F option; add -F to usage 
  806. X *                    message; replace COLOR_MSG() string
  807. X *                    with color_msg() routine; add -h
  808. X *                    (help message) and -A | -E (American |
  809. X *                    European date format) flags; renamed
  810. X *                    flag sets for clarity; more comments
  811. X *
  812. X *    2.5    JAB    10/04/90    added -F option
  813. X *
  814. X *    2.4    ---    10/01/90    * no modifications *
  815. X *
  816. X *    2.3    JWZ/AWR    09/18/90    added moon routines
  817. X *
  818. X *    2.2    AWR    09/17/90    revise logic of parse(); new usage
  819. X *                    message
  820. X *
  821. X *        JAB/AWR    09/14/90    support "note" lines in date file
  822. X *
  823. X *    2.1    MK/AWR    08/27/90    support -L, -C, -R, -n options;
  824. X *                    print holiday text next to date
  825. X *
  826. X *        AWR    08/24/90    incorporate cpp-like functionality;
  827. X *                    add -D and -U options; save date file
  828. X *                    information in internal data structure;
  829. X *                    look for PCAL_OPTS and PCAL_DIR; look
  830. X *                    for ~/.calendar and ~/calendar
  831. X *
  832. X *    2.0    AWR    08/08/90    included revision history; replaced -r
  833. X *                    flag with -l and -p; replaced -s and -S
  834. X *                    flags with -b and -g; recognize flags
  835. X *                    set in date file; translate ( and ) in
  836. X *                    text to octal escape sequence; usage()
  837. X *                    message condensed to fit 24x80 screen
  838. X *
  839. X *    Parameters:
  840. X *
  841. X *        pcal [opts]        generate calendar for current month/year
  842. X *                    (current year if -w flag specified)
  843. X *
  844. X *        pcal [opts] yy        generate calendar for entire year yy
  845. X *
  846. X *        pcal [opts] mm yy    generate calendar for month mm
  847. X *                    (Jan = 1), year yy (19yy if yy < 100)
  848. X *                    (12 months starting with mm/yy if -w
  849. X *                    specified)
  850. X *
  851. X *        pcal [opts] mm yy n    as above, for n consecutive months (n
  852. X *                    rounded to next multiple of 12 if -w
  853. X *                    specified)
  854. X *
  855. X *    Output:
  856. X *
  857. X *        PostScript file to print calendars for all selected months.
  858. X *
  859. X *    Options:
  860. X *
  861. X *        -I        initialize all parameters to program defaults
  862. X *
  863. X *        -b <DAY>    print specified weekday in black
  864. X *        -g <DAY>    print specified weekday in gray
  865. X *                (default: print Saturdays and Sundays in gray)
  866. X *
  867. X *        -G        print "gray" dates as filled outlines
  868. X *        -O        print "gray" dates as unfilled outlines
  869. X *                (default: gray characters)
  870. X *
  871. X *        -d <FONT>    specify alternate font for day names
  872. X *                (default: Times-Bold)
  873. X *
  874. X *        -n <FONT>    specify alternate font for notes in boxes
  875. X *                (default: Helvetica-Narrow)
  876. X *
  877. X *        -t <FONT>    specify alternate font for titles
  878. X *                (default: Times-Bold)
  879. X *
  880. X *        -D <SYM>    define preprocessor symbol
  881. X *        -U <SYM>    un-define preprocessor symbol
  882. X *
  883. X *        -e        generate empty calendar (ignore date file)
  884. X *
  885. X *        -f <FILE>    specify alternate date file (default:
  886. X *                ~/.calendar on Un*x, SYS$LOGIN:CALENDAR.DAT
  887. X *                on VMS, s:calendar.dat on Amiga; if
  888. X *                environment variable [logical name on VMS]
  889. X *                PCAL_DIR exists, looks there instead; if
  890. X *                not found in either place, looks in same
  891. X *                directory as Pcal executable)
  892. X *
  893. X *        -o <FILE>    specify alternate output file (default:
  894. X *                stdout on Un*x, CALENDAR.PS on VMS, 
  895. X *                RAM:calendar.ps on Amiga)
  896. X *
  897. X *        -L <STRING>    specify left foot string   (default: "")
  898. X *        -C <STRING>    specify center foot string (default: "")
  899. X *        -R <STRING>    specify right foot string  (default: "")
  900. X *
  901. X *        -l        generate landscape-mode calendars
  902. X *        -p        generate portrait-mode calendars
  903. X *                (default: landscape-mode)
  904. X *
  905. X *        -h        (command line only) write version information
  906. X *                and "usage" message to stdout
  907. X *        -v        (command line only) write version information
  908. X *                alone to stdout
  909. X *
  910. X *        -m        draw a small moon icon on the days of the
  911. X *                full, new, and half moons.
  912. X *        -M        draw a small moon icon every day.  
  913. X *                (default: no moons)
  914. X *
  915. X *        -F <DAY>    select alternate day to be displayed as the 
  916. X *                first day of the week (default: Sunday)
  917. X *
  918. X *        -A        dates are in American format (e.g., 10/15/90,
  919. X *                Oct 15) (default)
  920. X *        -E        dates are in European format (e.g., 15.10.90,
  921. X *                15 Oct)
  922. X *
  923. X *        -x <XSCALE>    These two options can be used to change
  924. X *        -y <YSCALE>    the size of the calendar.
  925. X *
  926. X *        -X <XTRANS>    These two options can be used to relocate
  927. X *        -Y <YTRANS>    the position of the calendar on the page.
  928. X *
  929. X *        -j        print Julian dates (day of year)
  930. X *        -J        print Julian dates and days remaining
  931. X *                (default: neither)
  932. X *
  933. X *        -w        print whole year (12 months) per page
  934. X *
  935. X *        -B        leave unused calendar boxes blank
  936. X *
  937. X *
  938. X *    There are many ways to specify these options in addition to using the
  939. X *    command line; this facilitates customization to the user's needs.
  940. X *
  941. X *    If the environment variable (global symbol on VMS) PCAL_OPTS is
  942. X *    present, its value will be parsed as if it were a command line.
  943. X *    Any options specified will override the program defaults.
  944. X *
  945. X *    All but the -e, -f, -h, -v, -D, and -U options may be specified in the
  946. X *    date file by including one or more lines of the form "opt <options>".
  947. X *    Any such options override any previous values set either as program
  948. X *    defaults, via PCAL_OPTS, or in previous "opt" lines.
  949. X *
  950. X *    Options explicitly specified on the command line in turn override all
  951. X *    of the above.
  952. X *
  953. X *    Any flag which normally takes an argument may also be specified without
  954. X *    an argument; this resets the corresponding option to its default.  -D
  955. X *    alone un-defines all symbols; -U alone has no effect.
  956. X *
  957. X *    Parameters and flags may be mixed on the command line.  In some cases
  958. X *    (e.g., when a parameter follows a flag without its optional argument)
  959. X *    this may lead to ambiguity; the dummy flag '-' (or '--') may be used
  960. X *    to separate them, i.e. "pcal -t - 9 90".
  961. X *
  962. X *
  963. X *    Date file syntax:
  964. X *
  965. X *    The following rules describe the syntax of date file entries:
  966. X *
  967. X *      year <year>
  968. X *
  969. X *      opt <options>
  970. X *
  971. X *      note <month_spec> <text>
  972. X *      note <month> <text>
  973. X *
  974. X *      if -A flag (American date formats) specified:
  975. X *        <month_name> <day>{*} {<text>}
  976. X *        <month><sep><day>{<sep><year>}{*} {<text>}
  977. X *
  978. X *      if -E flag (European date formats) specified:
  979. X *        <day> <month_name>{*} {<text>}
  980. X *        <day><sep><month>{<sep><year>}{*} {<text>}
  981. X *
  982. X *      <ordinal> <day_name> in <month_spec>{*} {<text>}
  983. X *      <day_name> <prep> <date_spec>
  984. X *
  985. X *    where
  986. X *
  987. X *      {x}      means x is optional
  988. X *
  989. X *      <date_spec> := any of the above date specs (not year, note, or opt)
  990. X *      <month_name> := first 3+ characters of name of month, or "all"
  991. X *      <month_spec> := <month_name>, or "year"
  992. X *      <day_name> := first 3+ characters of name of weekday, "day",
  993. X *            "weekday", "workday", "holiday", "nonweekday",
  994. X *            "nonworkday", or "nonholiday"
  995. X *      <ordinal> := ordinal number ("1st", "2nd", etc.), "first" .. "fifth",
  996. X *            "last", "even", "odd", or "all"
  997. X *      <prep> := "before", "preceding", "after", "following", "on_or_before",
  998. X *            or "on_or_after"
  999. X *      <sep> := one or more non-numeric, non-space, non-'*' characters
  1000. X *      <month>, <day>, <year> are the numeric forms
  1001. X *
  1002. X *      <options> := any command-line option except -e, -f, -h, -v, -D, -U
  1003. X *
  1004. X *    Comments start with '#' and run through end-of-line.
  1005. X *
  1006. X *    Holidays may be flagged by specifying '*' as the last character of
  1007. X *    the date field(s), e.g. "10/12* Columbus Day", "July 4* Independence
  1008. X *    Day", etc.  Any dates flagged as holidays will be printed in gray, and
  1009. X *    any associated text will appear adjacent to the date.
  1010. X *
  1011. X *    Note that the numeric date formats (mm/dd{/yy}, dd.mm{.yy}) support
  1012. X *    an optional year, which will become the subsequent default year.  The
  1013. X *    alphabetic date formats (month dd, dd month) do not support a year
  1014. X *    field; the "year yy" command is provided to reset the default year.
  1015. X *
  1016. X *    "Floating" days may be specified in the date file as "first Mon in 
  1017. X *    Sep", "last Mon in May", "4th Thu in Nov", etc.; any word may be
  1018. X *    used in place of "in".  "Relative floating" days (e.g. "Fri after 4th 
  1019. X *    Thu in Nov") are also accepted; they may span month/year bounds.
  1020. X *    Pcal also accepts date specs such as "all Friday{s} in October", "last
  1021. X *    Thursday in all", etc., and produces the expected results; "each" and
  1022. X *    "every" are accepted as synonyms for "all".  Negative ordinals are
  1023. X *    allowed; "-2nd" means "next to last".
  1024. X *
  1025. X *    The words "day", "weekday", "workday", and "holiday" may be used as
  1026. X *    wildcards: "day" matches any day, "weekday" matches any day normally
  1027. X *    printed in black, "workday" matches any day normally printed in black
  1028. X *    and not explicitly flagged as a holiday, and "holiday" matches any
  1029. X *    day explicitly flagged as a holiday.  "Nonweekday", "nonworkday",
  1030. X *    and "nonholiday" are also supported and have the obvious meanings.
  1031. X *    
  1032. X *    "Odd" and "even" do not refer to the actual date; instead, "odd"
  1033. X *    means "alternate, starting with the first"; "even" means "alternate,
  1034. X *    starting with the second".  Thus, "odd Fridays in March" refers to
  1035. X *    the first, third, and (if present) fifth Fridays in March - not to
  1036. X *    those Fridays falling on odd dates.
  1037. X *
  1038. X *    "All" refers to each individual month; "year" refers to the year
  1039. X *    as an entity.  Thus "odd Fridays in all" refers to the first/third/
  1040. X *    fifth Friday of each month, while "odd Fridays in year" refers to
  1041. X *    the first Friday of January and every other Friday thereafter.
  1042. X *
  1043. X *    Additional notes may be propagated to an empty calendar box by the
  1044. X *    inclusion of one or more lines of the form "note <month> <text>",
  1045. X *    where <month> may be numeric or alphabetic; "note all <text>"
  1046. X *    propagates <text> to each month in the current year.
  1047. X *
  1048. X *    Pcal also allows format specifiers in the text (and foot strings -
  1049. X *    cf. the -L, -C, and -R options); each will be replaced by its
  1050. X *    equivalent string as outlined in the table below.  (Most of these are
  1051. X *    derived from the strftime() function; %[lo0+-] are Pcal-specific.)
  1052. X *
  1053. X *        %a : abbreviated weekday
  1054. X *        %A : full weekday
  1055. X *        %b : abbreviated month name
  1056. X *        %B : full month name
  1057. X *        %d : day of month (1-31)
  1058. X *        %j : day of year (1-366)
  1059. X *        %l : days left in year (0-365)
  1060. X *        %m : month (1-12)
  1061. X *        %U : week number (0-53)
  1062. X *        %W : week number (0-53)
  1063. X *        %y : year w/o century (00-99)
  1064. X *        %Y : year w/century
  1065. X *        %% : '%' character
  1066. X *
  1067. X *        %o : print number as ordinal
  1068. X *        %0 : print number with leading zeroes
  1069. X *        %+ : use following month or year
  1070. X *        %- : use previous month or year
  1071. X *
  1072. X *    %U considers the first logical Sunday (the first day of the week as
  1073. X *    printed; cf. the -F flag) of the year as the first day of week 1;
  1074. X *    %W uses the first logical Monday instead.  This is an extension of
  1075. X *    strftime()'s behavior.
  1076. X *
  1077. X *    %o prints a number as an ordinal, with the appropriate suffix ("st",
  1078. X *    "nd", "rd", or "th" in English) appended; for example, "%od" prints
  1079. X *    the day of the month as "1st, 2nd, 3rd, ...".
  1080. X *
  1081. X *    Unlike strftime(), Pcal's default is to print numbers (except %y)
  1082. X *    without leading zeroes.  If leading zeroes are desired, the '0'
  1083. X *    prefix may be used; for example, "%0j" prints the day of year as
  1084. X *    001-365.
  1085. X *
  1086. X *    %+ and %- direct Pcal to substitute the following/previous month/year
  1087. X *    in the following [bBmyY] specifier; for example, %+B prints the name
  1088. X *    of the next month.
  1089. X *
  1090. X *    Simple cpp-like functionality is provided.  The date file may include
  1091. X *    the following commands, which work like their cpp counterparts:
  1092. X *
  1093. X *        define <sym>
  1094. X *        undef <sym>
  1095. X *
  1096. X *        if{n}def <expr>
  1097. X *           ...
  1098. X *        { else
  1099. X *           ... }
  1100. X *        endif
  1101. X *
  1102. X *        include <file>
  1103. X *
  1104. X *    Note that these do not start with '#', which is reserved as a comment
  1105. X *    character.
  1106. X *
  1107. X *    <sym> is a symbol name consisting of a letter followed by zero or
  1108. X *    more letters, digits, or underscores ('_').  Symbol names are always
  1109. X *    treated in a case-insensitive manner.
  1110. X *
  1111. X *    <expr> is an expression consisting of symbol names joined by the logical
  1112. X *    operators (in order of precedence, high to low) '!' (unary negate), '&'
  1113. X *    (and), '^' (exclusive or), and '|' (inclusive or).  '&&' and '||' are
  1114. X *    accepted as synonyms for '&' and '|' respectively; the order of
  1115. X *    evaluation may be altered by the use of parentheses.  A symbol whose
  1116. X *    name is currently defined evaluates to TRUE; one whose name is not
  1117. X *    currently defined evaluates to FALSE.  Thus "ifdef A | B | C" is TRUE
  1118. X *    if any of the symbols A, B, and C is currently defined, and
  1119. X *    "ifdef A & B & C" is TRUE if all of them are.
  1120. X *
  1121. X *    "ifndef A | B | C" is equivalent to "ifdef !(A | B | C)" (or, using
  1122. X *    DeMorgan's Law, "ifdef !A & !B & !C") - in other words, TRUE if none of
  1123. X *    the symbols A, B, and C is currently defined.
  1124. X *
  1125. X *    "define" alone deletes all the current definitions; "ifdef" alone is
  1126. X *    always false; "ifndef" alone is always true.
  1127. X *
  1128. X *    The file name in the "include" directive may optionally be surrounded
  1129. X *    by "" or <>.  In any case, path names are taken to be relative to
  1130. X *    the location of the file containing the "include" directive.
  1131. X *
  1132. X *    
  1133. X *    Moon file syntax:
  1134. X *    
  1135. X *    The user may enter the dates and (optionally) times of quarter
  1136. X *    moons (from a reliable source such as an almanac or astronomical
  1137. X *    table) into a file called .moonXX (moonXX.dat on VMS), where XX is
  1138. X *    the last two digits of the year.  If such a file exists (in the
  1139. X *    same directory as the date file), Pcal will interpolate the phase
  1140. X *    of the moon from the information in this file instead of using the
  1141. X *    default algorithm.
  1142. X *    
  1143. X *    (Pcal originally used an extremely simplistic moon phase algorithm;
  1144. X *    the moon file was added to v4.0 to enable Pcal to interpolate the
  1145. X *    phase of the moon from the [presumably more accurate] information
  1146. X *    within.  More recently, the original moon phase algorithm was
  1147. X *    superseded by an astronomer-strength version, largely obviating
  1148. X *    the need for a moon file; however, it will continue to be
  1149. X *    supported for the foreseeable future.)
  1150. X *
  1151. X *    Entries in the moon file must conform to the following syntax:
  1152. X *    
  1153. X *      if -A flag (American date formats) specified:
  1154. X *        <quarter> <month><sep><day> {<hour><sep><min>}
  1155. X *    
  1156. X *      if -E flag (European date formats) specified:
  1157. X *        <quarter> <day><sep><month> {<hour><sep><min>}
  1158. X *    
  1159. X *    where
  1160. X *    
  1161. X *      <quarter> := "nm", "fq" or "1q", "fm", "lq" or "3q" (new
  1162. X *               moon, first quarter, full moon, last quarter)
  1163. X *      <hour>    := number 0-23 (24-hour clock)
  1164. X *      <min>     := number 0-59
  1165. X *    
  1166. X *    This file must contain entries for all quarter moons in the year,
  1167. X *    in chronological order; if any errors are encountered, Pcal will
  1168. X *    revert to using its default algorithm.
  1169. X *    
  1170. X *    As in the date file, comments start with '#' and run through
  1171. X *    end-of-line.  
  1172. X */
  1173. X
  1174. X
  1175. /*
  1176. X * Standard headers:
  1177. X */
  1178. X
  1179. #include <stdio.h>
  1180. #include <ctype.h>
  1181. #include <time.h>
  1182. #include <string.h>
  1183. X
  1184. /*
  1185. X * Pcal-specific definitions:
  1186. X */
  1187. X
  1188. #define MAIN_MODULE    1
  1189. #include "pcaldefs.h"
  1190. #include "pcalglob.h"
  1191. #include "pcallang.h"
  1192. X
  1193. /*
  1194. X * Globals:
  1195. X */
  1196. X
  1197. static int init_month, init_year, nmonths;
  1198. X
  1199. X
  1200. /*
  1201. X * Main program - parse and validate command-line arguments, open files,
  1202. X * generate PostScript boilerplate and code to generate calendars.
  1203. X *
  1204. X * Program structure:
  1205. X *
  1206. X * main() looks for the environment variable (global symbol on VMS) PCAL_OPTS
  1207. X * and, if present, calls get_args() to parse it.  It then calls get_args()
  1208. X * again to parse the command line for the date file name, any options to be
  1209. X * in effect prior to reading the date file, and any numeric arguments (month,
  1210. X * year, number of months).  It then calls read_datefile() to read and parse
  1211. X * the date file; any "opt" lines present will override the defaults for the
  1212. X * command-line flags.  It then calls get_args() yet again to process the
  1213. X * remaining command-line flags, which in turn override any specified earlier.
  1214. X *
  1215. X * main() then attempts to open the output file (if any), and, if successful,
  1216. X * calls write_psfile() to generate the PostScript output.
  1217. X *
  1218. X */
  1219. #ifdef PROTOS
  1220. int main(int argc,
  1221. X     char **argv)
  1222. #else
  1223. int main(argc, argv)
  1224. X    int argc;
  1225. X    char **argv;
  1226. #endif
  1227. {
  1228. X    FILE *dfp = NULL;        /* date file pointer */
  1229. X    char *p, *pathlist[10];
  1230. X    char tmp[STRSIZ], progpath[STRSIZ];
  1231. X
  1232. X    INIT_COLORS;        /* copy default_color to day_color */
  1233. X
  1234. X    /* extract root program name and program path - note that some
  1235. X     * systems supply the full pathname and others just the root
  1236. X     */
  1237. X
  1238. X    strcpy(progname, **argv ? *argv : "pcal");
  1239. X
  1240. X    if ((p = strrchr(progname, END_PATH)) != NULL)
  1241. X        strcpy(progname, ++p);
  1242. X    if ((p = strchr(progname, '.')) != NULL)
  1243. X        *p = '\0';
  1244. X
  1245. X    mk_path(progpath, find_executable(*argv));
  1246. X
  1247. X    /* get version from VERSION_STRING (for use in PostScript comment) */
  1248. X    strcpy(tmp, VERSION_STRING + 4);
  1249. X    p = strchr(tmp, ' ') + 1;    /* skip program name */
  1250. X    *strchr(p, ' ') = '\0';        /* terminate after version */
  1251. X    strcpy(version, p);
  1252. X
  1253. X    /*
  1254. X     * Get the arguments from a) the environment variable, b) "opt" lines
  1255. X     * in the date file, and c) the command line, in that order
  1256. X     */
  1257. X
  1258. X    /* parse environment variable PCAL_OPTS as a command line */
  1259. X
  1260. X    if ((p = getenv(PCAL_OPTS)) != NULL) {
  1261. X        strcpy(lbuf, "pcal ");        /* dummy program name */
  1262. X        strcat(lbuf, p);
  1263. X        (void) loadwords();        /* split string into words */
  1264. X        if (! get_args(words, P_ENV, PCAL_OPTS)) {
  1265. X            usage(stderr, FALSE);
  1266. X            exit(EXIT_FAILURE);
  1267. X        }
  1268. X    }
  1269. X
  1270. X    /* parse command-line arguments once to find name of date file, etc. */
  1271. X
  1272. X    if (!get_args(argv, P_CMD1, NULL)) {
  1273. X        usage(stderr, FALSE);
  1274. X        exit(EXIT_FAILURE);
  1275. X    }
  1276. X
  1277. X    /* Attempt to open the date file as specified by the [-e | -f] flags */
  1278. X
  1279. X    switch (datefile_type) {
  1280. X    case NO_DATEFILE:
  1281. X        dfp = NULL;
  1282. X        break;
  1283. X
  1284. X    case USER_DATEFILE:    
  1285. X        /* Attempt to open user-specified calendar file: search
  1286. X         * first in PCAL_DIR (current directory if not defined)
  1287. X         * and then in the directory where the Pcal executable
  1288. X         * lives.  It is a fatal error if the user-specified
  1289. X         * date file cannot be found.
  1290. X         */
  1291. X        pathlist[0] = (p = trnlog(PCAL_DIR)) ? p : "";
  1292. X        pathlist[1] = progpath;
  1293. X        pathlist[2] = NULL;
  1294. X        
  1295. X        strcpy(tmp, datefile);    /* save original name for error msg */
  1296. X
  1297. X        if ((dfp = alt_fopen(datefile, tmp, pathlist, "r")) == NULL) {
  1298. X            FPR(stderr, E_FOPEN_ERR, progname, tmp);
  1299. X            exit(EXIT_FAILURE);
  1300. X        }
  1301. X        break;
  1302. X
  1303. X    case SYS_DATEFILE:
  1304. X        /* Attempt to open system-specified calendar file: search
  1305. X         * first in PCAL_DIR or HOME_DIR (current directory if
  1306. X         * neither is defined) and then in the directory where
  1307. X         * the Pcal executable lives.  It is not an error if the
  1308. X         * system-specified date file cannot be found; Pcal will
  1309. X         * simply generate an empty calendar.
  1310. X         */
  1311. X        pathlist[0] = ((p = trnlog(PCAL_DIR)) ||
  1312. X                   (p = trnlog(HOME_DIR))) ? p : "";
  1313. X        pathlist[1] = progpath;
  1314. X        pathlist[2] = NULL;
  1315. X        
  1316. X        dfp = alt_fopen(datefile, DATEFILE, pathlist, "r");
  1317. X
  1318. X        /* if the date file has not been found and ALT_DATEFILE is
  1319. X         * defined, search same paths for ALT_DATEFILE before
  1320. X         * giving up
  1321. X         */
  1322. #ifdef ALT_DATEFILE
  1323. X        if (!dfp)
  1324. X            dfp = alt_fopen(datefile, ALT_DATEFILE, pathlist, "r");
  1325. #endif
  1326. X        break;
  1327. X    }
  1328. X
  1329. X    /* read the date file (if any) and build internal data structure */
  1330. X
  1331. X    if (dfp) {
  1332. X        curr_year = init_year;
  1333. X        read_datefile(dfp, datefile);
  1334. X        fclose(dfp);
  1335. X    } else
  1336. X        datefile[0] = '\0';        /* for PostScript comment */
  1337. X
  1338. X    /* reparse command line - flags there supersede those in date file */
  1339. X
  1340. X    (void) get_args(argv, P_CMD2, NULL);
  1341. X
  1342. X    /* done with the arguments and flags - try to open the output file */
  1343. X
  1344. X    if (*outfile && freopen(outfile, "w", stdout) == (FILE *) NULL) {
  1345. X        FPR(stderr, E_FOPEN_ERR, progname, outfile);
  1346. X        exit(EXIT_FAILURE);
  1347. X    }
  1348. X
  1349. X    /* generate the PostScript code (cf. writefil.c) */
  1350. X
  1351. X    write_psfile(init_month, init_year, nmonths);
  1352. X    
  1353. X    cleanup();        /* free allocated data */
  1354. X
  1355. X    /* if output was written to a non-obvious location, tell user where */
  1356. X
  1357. #ifdef DEFAULT_OUTFILE
  1358. X    FPR(stderr, I_OUT_NAME, progname, outfile);
  1359. #endif
  1360. X
  1361. X    exit(EXIT_SUCCESS);
  1362. }
  1363. X
  1364. X
  1365. /*
  1366. X * set_color - set one or all weekdays to print in black or gray
  1367. X */
  1368. #ifdef PROTOS
  1369. void set_color(char *day,
  1370. X           int col)
  1371. #else
  1372. void set_color(day, col)
  1373. X    char *day;        /* weekday name (or "all") */
  1374. X    int  col;        /* select black or gray */
  1375. #endif
  1376. {
  1377. X    int i;
  1378. X
  1379. X    if (ci_strncmp(day, ALL, strlen(ALL)) == 0)    /* set all days */
  1380. X        for (i = 0; i < 7; i++)
  1381. X            day_color[i] = col;
  1382. X    else                        /* set single day */
  1383. X        if ((i = get_weekday(day, FALSE)) != NOT_WEEKDAY)
  1384. X            day_color[i] = col;
  1385. X
  1386. }
  1387. X
  1388. X
  1389. /*
  1390. X * get_flag() - look up flag in flag_tbl; return pointer to its entry
  1391. X * (NULL if not found)
  1392. X */
  1393. #ifdef PROTOS
  1394. FLAG_USAGE *get_flag(char flag)
  1395. #else
  1396. FLAG_USAGE *get_flag(flag)
  1397. X    char flag;
  1398. #endif
  1399. {
  1400. X    FLAG_USAGE *pflag;
  1401. X
  1402. X    for (pflag = flag_tbl; pflag->flag; pflag++)
  1403. X        if (flag == pflag->flag)
  1404. X            return pflag;
  1405. X
  1406. X    return flag ? NULL : pflag;        /* '\0' is a valid flag */
  1407. }
  1408. X
  1409. X
  1410. /*
  1411. X * get_args - walk the argument list, parsing all arguments but processing only
  1412. X * those specified (in flag_tbl[]) to be processed this pass.
  1413. X */
  1414. #ifdef PROTOS
  1415. int get_args(char **argv,
  1416. X         int curr_pass,
  1417. X         char *where)
  1418. #else
  1419. int get_args(argv, curr_pass, where)
  1420. X    char **argv;        /* argument list */
  1421. X    int  curr_pass;        /* current pass (P_xxx) */
  1422. X    char *where;        /* for error messages */
  1423. #endif
  1424. {
  1425. X    char *parg, *opt, *p;
  1426. X    FLAG_USAGE *pflag, *pf;
  1427. X    int i, flag;
  1428. X    long tmp;            /* for getting current month/year */
  1429. X    struct tm *p_tm;
  1430. X    int badopt = FALSE;        /* flag set if bad param   */
  1431. X    int nargs = 0;            /* count of non-flag args  */
  1432. X    int numargs[MAXARGS];        /* non-flag (numeric) args */
  1433. X
  1434. /*
  1435. X * If argument follows flag (immediately or as next parameter), return
  1436. X * pointer to it (and bump argv if necessary); else return NULL
  1437. X */
  1438. #define GETARG() (*(*argv + 2) ? *argv + 2 : \
  1439. X          (*(argv+1) && **(argv+1) != '-' ? *++argv : NULL))
  1440. X
  1441. /*
  1442. X * Must parse numeric parameters on both command-line passes: before reading
  1443. X * datefile (in order to set default year) and again after reading datefile
  1444. X * (in order to set the default first month to January if -w flag was set
  1445. X * inside the datefile)
  1446. X */
  1447. #define PARSE_NUM_PARAMS    (curr_pass == P_CMD1 || curr_pass == P_CMD2)
  1448. X
  1449. X    /* Walk argument list, ignoring first element (program name) */
  1450. X
  1451. X     while (opt = *++argv) {
  1452. X
  1453. X        /* Assume that any non-flag argument is a numeric argument */
  1454. X        if (*opt != '-') {
  1455. X                if (PARSE_NUM_PARAMS && nargs < MAXARGS) {
  1456. X                if (! IS_NUMERIC(opt))
  1457. X                    goto bad_par;
  1458. X                numargs[nargs++] = atoi(opt);
  1459. X            }
  1460. X            continue;
  1461. X        }
  1462. X
  1463. X        /* Check that flag is a) legal, and b) to be processed this pass */
  1464. X
  1465. X        if (! (pflag = get_flag(flag = opt[1])) )
  1466. X            goto bad_par;
  1467. X
  1468. X        /* get optional argument even if flag not processed this pass */
  1469. X
  1470. X        parg = pflag->has_arg ? GETARG() : NULL;
  1471. X
  1472. X        if (! (pflag->passes & curr_pass)) {    /* skip flag this pass? */
  1473. X            if (curr_pass == P_OPT)
  1474. X                FPR(stderr, E_FLAG_IGNORED, progname, flag,
  1475. X                    DATE_FILE, where);
  1476. X            continue;
  1477. X        }
  1478. X
  1479. X        switch (flag) {
  1480. X
  1481. X        case F_INITIALIZE:    /* reset all flags to defaults */
  1482. X
  1483. X            /* set up a command line to reset all of the
  1484. X             * flags; call get_args() recursively to parse it
  1485. X             * (note that some of the flags must be reset
  1486. X             * explicitly, as no command-line flags exist to
  1487. X             * reset them)
  1488. X             */
  1489. X
  1490. X            /* reset flags described above */
  1491. X            julian_dates  = JULIAN_DATES;
  1492. X            draw_moons    = DRAW_MOONS;
  1493. X            do_whole_year = FALSE;
  1494. X            blank_boxes   = FALSE;
  1495. X            num_style     = NUM_STYLE;
  1496. X
  1497. X            /* select program default for landscape/portrait
  1498. X             * mode and US/European date styles
  1499. X             */
  1500. X            sprintf(lbuf, "pcal -%c -%c",
  1501. #if (ROTATE == LANDSCAPE)
  1502. X                F_LANDSCAPE,
  1503. #else
  1504. X                F_PORTRAIT,
  1505. #endif
  1506. #if (DATE_STYLE == USA_DATES)
  1507. X                F_USA_DATES);
  1508. #else
  1509. X                F_EUR_DATES);
  1510. #endif
  1511. X            p = lbuf + strlen(lbuf);
  1512. X
  1513. X            /* all other flags take arguments and are reset
  1514. X             * by specifying the flag without an argument
  1515. X             */
  1516. X            for (pf = flag_tbl; pf->flag; pf++)
  1517. X                if ((pf->passes & curr_pass) && pf->has_arg) {
  1518. X                    sprintf(p, " -%c", pf->flag);
  1519. X                    p += strlen(p);
  1520. X                }
  1521. X
  1522. X            /* split new command line into words; parse it */
  1523. X            (void) loadwords();
  1524. X            (void) get_args(words, curr_pass, NULL);
  1525. X
  1526. X            break;
  1527. X
  1528. X        case F_BLACK_DAY:    /* print day in black or gray */
  1529. X        case F_GRAY_DAY:
  1530. X            if (parg)
  1531. X                set_color(parg, flag == F_BLACK_DAY ?
  1532. X                          BLACK : GRAY);
  1533. X            else
  1534. X                INIT_COLORS;    /* reset to defaults */
  1535. X            break;
  1536. X
  1537. X         case F_DAY_FONT:    /* specify alternate day font */
  1538. X            strcpy(dayfont, parg ? parg : DAYFONT);
  1539. X             break;
  1540. X
  1541. X        case F_NOTES_FONT:    /* specify alternate notes font */
  1542. X            strcpy(notesfont, parg ? parg : NOTESFONT);
  1543. X            break;
  1544. X
  1545. X         case F_TITLE_FONT:    /* specify alternate title font */
  1546. X            strcpy(titlefont, parg ? parg : TITLEFONT);
  1547. X             break;
  1548. X
  1549. X        case F_EMPTY_CAL:    /* generate empty calendar */
  1550. X            datefile_type = NO_DATEFILE;
  1551. X            strcpy(datefile, "");
  1552. X            break;
  1553. X
  1554. X        case F_DATE_FILE:    /* specify alternate date file */
  1555. X            datefile_type = parg ? USER_DATEFILE : SYS_DATEFILE;
  1556. X            strcpy(datefile, parg ? parg : "");
  1557. X            break;
  1558. X
  1559. X        case F_OUT_FILE:    /* specify alternate output file */
  1560. X            strcpy(outfile, parg ? parg : OUTFILE);
  1561. X            break;
  1562. X
  1563. X        case F_LANDSCAPE:    /* generate landscape calendar */
  1564. X             rotate = LANDSCAPE;
  1565. X            strcpy(xsval, XSVAL_L);
  1566. X            strcpy(ysval, YSVAL_L);
  1567. X            strcpy(xtval, XTVAL_L);
  1568. X            strcpy(ytval, YTVAL_L);
  1569. X             break;
  1570. X        case F_PORTRAIT:    /* generate portrait calendar */
  1571. X             rotate = PORTRAIT;
  1572. X            strcpy(xsval, XSVAL_P);
  1573. X            strcpy(ysval, YSVAL_P);
  1574. X            strcpy(xtval, XTVAL_P);
  1575. X            strcpy(ytval, YTVAL_P);
  1576. X             break;
  1577. X
  1578. X        case F_HELP:        /* request "help" message or version */
  1579. X        case F_VERSION:
  1580. X            FPR(stdout, "%s\n", VERSION_STRING + 4);
  1581. X            if (flag == F_HELP)
  1582. X                usage(stdout, TRUE);
  1583. X            exit(EXIT_SUCCESS);
  1584. X            break;
  1585. X
  1586. X        case F_MOON_4:        /* draw four moons */
  1587. X        case F_MOON_ALL:    /* draw a moon for each day */
  1588. X            draw_moons = flag == F_MOON_ALL ? ALL_MOONS : SOME_MOONS;
  1589. X            break;
  1590. X
  1591. X        case F_DEFINE:        /* define preprocessor symbol */
  1592. X            (void) do_define(parg);
  1593. X            break;
  1594. X
  1595. X        case F_UNDEF:        /* undef preprocessor symbol */
  1596. X            (void) do_undef(parg);
  1597. X            break;
  1598. X
  1599. X        case F_L_FOOT:        /* specify alternate left foot */
  1600. X            strcpy(lfoot, parg ? parg : LFOOT);
  1601. X            break;
  1602. X
  1603. X        case F_C_FOOT:        /* specify alternate center foot */
  1604. X            strcpy(cfoot, parg ? parg : CFOOT);
  1605. X            break;
  1606. X
  1607. X         case F_R_FOOT:        /* specify alternate right foot */
  1608. X            strcpy(rfoot, parg ? parg : RFOOT);
  1609. X            break;
  1610. X
  1611. X        case F_FIRST_DAY:    /* select starting day of week */
  1612. X            if (parg) {
  1613. X                if ((i = get_weekday(parg, FALSE)) != NOT_WEEKDAY)
  1614. X                    first_day_of_week = i;
  1615. X            }
  1616. X            else
  1617. X                first_day_of_week = FIRST_DAY;
  1618. X            break;
  1619. X
  1620. X        case F_USA_DATES:    /* select American date style */
  1621. X        case F_EUR_DATES:    /* select European date style */
  1622. X            date_style = flag == F_USA_DATES ? USA_DATES : EUR_DATES;
  1623. X            break;
  1624. X
  1625. X        case F_X_TRANS:        /* set x-axis translation factor */
  1626. X            strcpy(xtval, parg ? parg :
  1627. X                (rotate == LANDSCAPE ? XTVAL_L : XTVAL_P));
  1628. X            break;
  1629. X
  1630. X        case F_Y_TRANS:        /* set y-axis translation factor */
  1631. X            strcpy(ytval, parg ? parg :
  1632. X                (rotate == LANDSCAPE ? YTVAL_L : YTVAL_P));
  1633. X            break;
  1634. X
  1635. X        case F_X_SCALE:        /* set x-axis scaling factor */
  1636. X            strcpy(xsval, parg ? parg :
  1637. X                (rotate == LANDSCAPE ? XSVAL_L : XSVAL_P));
  1638. X            break;
  1639. X
  1640. X        case F_Y_SCALE:        /* set y-axis scaling factor */
  1641. X            strcpy(ysval, parg ? parg :
  1642. X                (rotate == LANDSCAPE ? YSVAL_L : YSVAL_P));
  1643. X            break;
  1644. X
  1645. X        case F_JULIAN:
  1646. X        case F_JULIAN_ALL:
  1647. X            julian_dates = flag == F_JULIAN_ALL ? ALL_JULIANS :
  1648. X                                  SOME_JULIANS;
  1649. X            break;
  1650. X
  1651. X        case F_WHOLE_YEAR:
  1652. X            do_whole_year = TRUE;
  1653. X            break;
  1654. X
  1655. X        case F_BLANK_BOXES:
  1656. X            blank_boxes = TRUE;
  1657. X            break;
  1658. X
  1659. X        case F_OUTLINE:
  1660. X        case F_OUTLINE_GRAY:
  1661. X            num_style = flag == F_OUTLINE ? OUTLINE_NUMS :
  1662. X                            FILLED_NUMS;
  1663. X            break;
  1664. X
  1665. X        case '-' :        /* accept - and -- as dummy flags */
  1666. X        case '\0':
  1667. X            break;
  1668. X
  1669. X        default:        /* missing case label if reached!!! */
  1670. X
  1671. bad_par:                /* unrecognized parameter */
  1672. X
  1673. X            FPR(stderr, E_ILL_OPT, progname, opt);
  1674. X            if (where)
  1675. X                FPR(stderr, E_ILL_OPT2,
  1676. X                    curr_pass == P_ENV ? ENV_VAR :
  1677. X                    curr_pass == P_OPT ? DATE_FILE : "",
  1678. X                    where);
  1679. X            FPR(stderr, "\n");
  1680. X            badopt = TRUE;
  1681. X            break;
  1682. X        }
  1683. X        }
  1684. X
  1685. X    if (! PARSE_NUM_PARAMS)
  1686. X        return !badopt;        /* return TRUE if OK, FALSE if error */
  1687. X
  1688. X    /* Validate non-flag (numeric) parameters */
  1689. X
  1690. X    switch (nargs) {
  1691. X    case 0:        /* no arguments - print current month and/or year */
  1692. X        time(&tmp);
  1693. X        p_tm = localtime(&tmp);
  1694. X        init_month = do_whole_year ? JAN : p_tm->tm_mon + 1;
  1695. X        init_year = p_tm->tm_year;
  1696. X        nmonths = 1;
  1697. X        break;            
  1698. X    case 1:        /* one argument - print entire year */
  1699. X        init_month = JAN;
  1700. X        init_year = numargs[0];
  1701. X        nmonths = 12;
  1702. X        break;
  1703. X    default:    /* two or three arguments - print one or more months */
  1704. X        init_month = numargs[0];
  1705. X        init_year = numargs[1];
  1706. X        nmonths = nargs > 2 ? numargs[2] : 1;
  1707. X        break;
  1708. X    }
  1709. X
  1710. X    if (nmonths < 1)        /* ensure at least one month */
  1711. X        nmonths = 1;
  1712. X
  1713. X    /* check range of month and year */
  1714. X
  1715. X    if (init_month < JAN || init_month > DEC) {
  1716. X        FPR(stderr, E_ILL_MONTH, progname, init_month, JAN, DEC);
  1717. X        badopt = TRUE;
  1718. X    }
  1719. X    
  1720. X    if (init_year > 0 && init_year < 100)    /* treat nn as 19nn */
  1721. X        init_year += CENTURY;
  1722. X    
  1723. X    if (init_year < MIN_YR || init_year > MAX_YR) {
  1724. X        FPR(stderr, E_ILL_YEAR, progname, init_year, MIN_YR, MAX_YR);
  1725. X        badopt = TRUE;
  1726. X    }
  1727. X
  1728. X    return !badopt;        /* return TRUE if OK, FALSE if error */
  1729. }
  1730. X
  1731. X
  1732. X
  1733. /*
  1734. X * color_msg - return character string explaining default day colors
  1735. X */
  1736. #ifdef PROTOS
  1737. char *color_msg(void)
  1738. #else
  1739. char *color_msg()
  1740. #endif
  1741. {
  1742. X    int i, ngray = 0, others;
  1743. X    static char msg[80];
  1744. X
  1745. X    for (i = SUN; i <= SAT; i++)    /* count gray weekdays */
  1746. X        if (default_color[i] == GRAY)
  1747. X            ngray++;
  1748. X
  1749. X    if (ngray == 0 || ngray == 7) {        /* all same color? */
  1750. X        sprintf(msg, COLOR_MSG_1, ngray ? W_GRAY : W_BLACK);
  1751. X        return msg;
  1752. X    }
  1753. X
  1754. X    others = ngray <= 3 ? BLACK : GRAY;    /* no - get predominant color */
  1755. X    msg[0] = '\0';
  1756. X    for (i = SUN; i <= SAT; i++)
  1757. X        if (default_color[i] != others) {
  1758. X            strncat(msg, days[i], MIN_DAY_LEN);
  1759. X            strcat(msg, "/");
  1760. X        }
  1761. X    LASTCHAR(msg) = ' ';
  1762. X
  1763. X    sprintf(msg + strlen(msg), COLOR_MSG_2,
  1764. X        others == BLACK ? W_GRAY : W_BLACK,
  1765. X                others == BLACK ? W_BLACK : W_GRAY);
  1766. X    return msg;
  1767. }
  1768. X
  1769. X
  1770. /*
  1771. X * usage - print message explaining correct usage of the command-line
  1772. X * arguments and flags.  If "fullmsg" is true, print associated text
  1773. X */
  1774. #ifdef PROTOS
  1775. void usage(FILE *fp,
  1776. X       int fullmsg)
  1777. #else
  1778. void usage(fp, fullmsg)
  1779. X    FILE *fp;    /* destination of usage message */
  1780. X    int fullmsg;    /* print complete message? */
  1781. #endif
  1782. {
  1783. X    FLAG_MSG *pflag;
  1784. X    PARAM_MSG *ppar;
  1785. X    DATE_MSG *pdate;
  1786. X    char buf[30], *p, flag, *meta;
  1787. X    int nchars, first, i, indent, n;
  1788. X
  1789. X    sprintf(buf, "%s: %s", W_USAGE, progname);
  1790. X    nchars = indent = strlen(buf);
  1791. X    first = TRUE;
  1792. X    meta = p = NULL;
  1793. X    FPR(fp, "\n%s", buf);
  1794. X
  1795. X    /* loop to print command-line syntax message (by group of flags) */
  1796. X
  1797. X    for (pflag = flag_msg; (flag = pflag->flag) != '\0'; pflag++) {
  1798. X        if (flag == '\n') {        /* end of group? */
  1799. X            if (p)
  1800. X                *p = '\0';
  1801. X            if (meta) {        /* append metavariable name */
  1802. X                strcat(buf, " ");
  1803. X                strcat(buf, meta);
  1804. X            }
  1805. X            strcat(buf, "]");
  1806. X            n = strlen(buf);
  1807. X            if (nchars + n > SCREENWIDTH) {    /* does it fit on line? */
  1808. X                FPR(fp, "\n");        /* no - start new one */
  1809. X                for (nchars = 0; nchars < indent; nchars++)
  1810. X                    FPR(fp, " ");
  1811. X            }
  1812. X            FPR(fp, "%s", buf);
  1813. X            nchars += n;
  1814. X            first = TRUE;
  1815. X        }
  1816. X        else if (flag != ' ') {        /* accumulate flags for group */
  1817. X            if (first) {
  1818. X                sprintf(buf, " [");
  1819. X                p = buf + strlen(buf);
  1820. X            }
  1821. X            else
  1822. X                *p++ = '|';
  1823. X            *p++ = '-';
  1824. X            *p++ = flag;
  1825. X            meta = pflag->meta;    /* save metavariable name */
  1826. X            first = FALSE;
  1827. X        }
  1828. X    }
  1829. X
  1830. X    /* loop to print selected numeric parameter descriptions */
  1831. X
  1832. X    for (i = 0; i < PARAM_MSGS; i++) {
  1833. X        sprintf(buf, " [%s]%s", param_msg[i].desc,
  1834. X            i < PARAM_MSGS - 1 ? " |" : "");
  1835. X        n = strlen(buf);
  1836. X        if (nchars + n > SCREENWIDTH) {    /* does it fit on line? */
  1837. X            FPR(fp, "\n");        /* no - start new one */
  1838. X            for (nchars = 0; nchars < indent; nchars++)
  1839. X                FPR(fp, " ");
  1840. X        }
  1841. X        FPR(fp, "%s", buf);
  1842. X        nchars += n;
  1843. X    }
  1844. X
  1845. X    FPR(fp, "\n\n");
  1846. X    if (! fullmsg) {
  1847. X        FPR(fp, USAGE_MSG, progname, F_HELP);
  1848. X        return;
  1849. X    }
  1850. X    
  1851. X    /* loop to print the full flag descriptions */
  1852. X
  1853. X    for (pflag = flag_msg; (flag = pflag->flag) != '\0'; pflag++) {
  1854. X        if (flag == '\n') {    /* newline?  print and quit */
  1855. X            FPR(fp, "\n");
  1856. X            continue;
  1857. X        }
  1858. X        p = buf;        /* copy flag and metavariable to buffer */
  1859. X        if (flag != ' ')
  1860. X            *p++ = '-';
  1861. X    /* special hack for VMS - surround upper-case flags in quotes */
  1862. #ifdef VMS
  1863. X        if (isupper(flag)) {
  1864. X            *p++ = '"';
  1865. X            *p++ = flag;
  1866. X            *p++ = '"';
  1867. X        }
  1868. X        else
  1869. X            *p++ = flag;
  1870. #else
  1871. X        *p++ = flag;
  1872. #endif
  1873. X        *p = '\0';
  1874. X        if (pflag->meta)
  1875. X            sprintf(p, " <%s>", pflag->meta);
  1876. X        FPR(fp, "\t%-16.16s", buf);
  1877. X        if (pflag->text)
  1878. X            FPR(fp, "%s", pflag->text);
  1879. X
  1880. X        /* print default value if specified */
  1881. X        if (pflag->def)
  1882. X            FPR(fp, " (%s: %s)", W_DEFAULT, pflag->def[0] ? pflag->def : "\"\"" );
  1883. X        FPR(fp, "\n");
  1884. X
  1885. X        /* special case - print color messages after F_GRAY_DAY */
  1886. X        if (flag == F_GRAY_DAY)
  1887. X            FPR(fp, "\t\t\t  (%s: %s)\n", W_DEFAULT, color_msg());
  1888. X
  1889. X    }
  1890. X    
  1891. X    /* now print the information about the numeric parameters */
  1892. X
  1893. X    for (ppar = param_msg; ppar->desc; ppar++)
  1894. X        FPR(fp, "\t%-16.16s%s\n", ppar->desc, ppar->text);
  1895. X    
  1896. X    /* print the date file syntax message */
  1897. X
  1898. X    FPR(fp, "\n");
  1899. X    for (pdate = date_msg; *pdate; pdate++)
  1900. X        FPR(fp, "\t%s\n", *pdate);
  1901. X
  1902. }
  1903. X
  1904. X
  1905. /*
  1906. X * alt_fopen - attempt to open a file in one of several paths in a
  1907. X * NULL-terminated path list.  If successful, return (opened) file pointer
  1908. X * and fill in full path name; if not, return NULL
  1909. X */
  1910. #ifdef PROTOS
  1911. FILE *alt_fopen(char *fullpath, char *name, char *pathlist[], char *access) 
  1912. #else
  1913. FILE *alt_fopen(fullpath, name, pathlist, access)
  1914. X    char *fullpath;        /* full path name (output) */
  1915. X    char *name;        /* base name (or full path spec) */
  1916. X    char *pathlist[];    /* NULL-terminated path list */
  1917. X    char *access;        /* permission requested */
  1918. #endif
  1919. {
  1920. X    char **path;
  1921. X    FILE *fp;
  1922. X
  1923. X    for (path = pathlist; *path; path++) {
  1924. X        mk_filespec(fullpath, *path, name);
  1925. X        if ((fp = fopen(fullpath, access)) != NULL)
  1926. X            return fp;
  1927. X        }
  1928. X
  1929. X    fullpath[0] = '\0';        /* file not found */
  1930. X    return NULL;
  1931. }
  1932. X
  1933. SHAR_EOF
  1934. chmod 0666 pcal.c ||
  1935. echo 'restore of pcal.c failed'
  1936. Wc_c="`wc -c < 'pcal.c'`"
  1937. test 36344 -eq "$Wc_c" ||
  1938.     echo 'pcal.c: original size 36344, current size' "$Wc_c"
  1939. fi
  1940. true || echo 'restore of pcal.man failed'
  1941. echo End of part 3, continue with part 4
  1942. exit 0
  1943.