home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Applications / MacTide 1.3.3 / tidelib.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-01-04  |  50.4 KB  |  1,886 lines  |  [TEXT/MPS ]

  1. /*  Tidelib  Common functions used by XTide and Tide.
  2.     Last modified 1/4/96
  3.  
  4.     This program uses the harmonic method to compute tide levels.
  5.     All of the data and constants are read in from the harmonics file.
  6.     Please refer to ReadMe Mac and README for more information.
  7.  
  8.  
  9.     Copiright (C) 1996 Mikhail Fridberg
  10.     Copyright (C) 1995  David Flater.
  11.     Also starring:  Jef Poskanzer; Rob Miracle; Geoff Kuenning;
  12.     Dale DePriest.
  13.  
  14.     This program is free software; you can redistribute it and/or modify
  15.     it under the terms of the GNU General Public License as published by
  16.     the Free Software Foundation; either version 2 of the License, or
  17.     (at your option) any later version.
  18.  
  19.  
  20.     This program is distributed in the hope that it will be useful,
  21.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  22.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  23.     GNU General Public License for more details.
  24.  
  25.     You should have received a copy of the GNU General Public License
  26.     along with this program; if not, write to the Free Software
  27.     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  28.  
  29.  
  30.     The tide prediction algorithm used in this program was developed
  31.     with United States Government funding, so no proprietary rights
  32.     can be attached to it.  For more information, refer to the
  33.     following publications:
  34.  
  35.     Manual of Harmonic Analysis and Prediction of Tides.  Special
  36.     Publication No. 98, Revised (1940) Edition.  United States
  37.     Government Printing Office, 1941.
  38.  
  39.     Computer Applications to Tides in the National Ocean Survey.
  40.     Supplement to Manual of Harmonic Analysis and Prediction of Tides
  41.     (Special Publication No. 98).  National Ocean Service, National
  42.     Oceanic and Atmospheric Administration, U.S. Department of
  43.     Commerce, January 1982.
  44.  
  45. */
  46.  
  47. #include <stdio.h>
  48. #include <math.h>
  49. #include <stdlib.h>
  50. #include <signal.h>
  51. #include "unistd.h"
  52. #include <unix.h>
  53. #include <time.h>
  54. #include <assert.h>
  55. #include <string.h>
  56. #include "sys/time.h"
  57. #include <ctype.h>
  58. #include <Script.h>
  59.  
  60. #include "tidelib.h"
  61. #include "arglib.h"
  62. #include "glyphs.h"
  63. #include <GestaltEqu.h>
  64. #include <Traps.h>
  65. #include <OSUtils.h>
  66. /* Compatibility by Dale DePriest */
  67.  
  68. #ifdef OS2
  69. #include <float.h>
  70. #endif /* OS2 */
  71.  
  72. /* Typo prevention */
  73. #define DAYSECONDS 86400
  74. #define HOURSECONDS 3600
  75.  
  76. /* Exported variables */
  77. int    gHasFPU;
  78. int    gTimezone=0;
  79. char hfile_name[MAXARGLEN+1], location[MAXARGLEN+1], *ppm = NULL,
  80.   tzfile[MAXARGLEN+1];
  81. char next_ht_text[13], next_ht_date[11], next_lt_text[13],
  82.   next_lt_date[11], *geometry = NULL;
  83. char *fgrise_color_arg = NULL, *fgfall_color_arg = NULL,
  84.   *bg_color_arg = NULL, *fgtext_color_arg = NULL, *fgmark_color_arg = NULL,
  85.   *fgmllw_color_arg = NULL, *fgmiddle_color_arg = NULL;
  86. int tadjust = 0, utc = 0, list = 0, checkyear = 0, text = 0, skinny = 0,
  87.   now = 0, graphmode = 0, httimeoff = 0, lttimeoff = 0, tstep = 180,
  88.   middle = 0, mark = 0, mllw = 0, lines = 1, PPMWIDTH = 960,
  89.   PPMHEIGHT = 300, hinc = 0, tinc = 0, loctz = 0, iscurrent = 0,
  90.   curonly = 0, toplines = 0, hincmagic = 0, calendar = 0, banner = 0;
  91. time_t next_ht = 0, prev_ht = 0, faketime = 0, epoch = 0;
  92. double amplitude, htleveloff = 0.0, ltleveloff = 0.0, DATUM, marklev;
  93. Cursor            waitCursor;
  94. CursHandle        hCurs;                
  95. /* Local variables */
  96.  
  97. static int year = 0, meridian = 0;
  98.  
  99. /* Tide-prediction storage -- cst == constituent */
  100.  
  101. static int num_csts = 0, num_nodes = 0, num_epochs = 0, first_year = 0;
  102. static double *cst_speeds, **cst_epochs, **cst_nodes, *loc_amp, *loc_epoch,
  103.   *work;
  104.  
  105. /* Exit without core dump on assertion failures. */
  106. void
  107. sigabort ()
  108. {
  109.   exit (-1);
  110. }
  111.  
  112. /* User-friendly error messages */
  113. void
  114. barf (enum tideerr err)
  115. {
  116.   fprintf (stderr, "Tidelib Fatal Error:  ");
  117.   switch (err) {
  118.   case BADCOLORSPEC:
  119.     fprintf (stderr, "BADCOLORSPEC\n");
  120.     fprintf (stderr,
  121. "One of your specified colors is not understood.  This may be because it is\n\
  122. not in rgb.txt, or because you used the wrong syntax.  In PPM mode, colors\n\
  123. must be specified as rgb:hh/hh/hh, where hh is a 2-digit hexadecimal number.\n\
  124. Other formats are possible under X; type 'man X' for an explanation of them.\n");
  125.     break;
  126.   case BADGEOMETRY:
  127.     fprintf (stderr, "BADGEOMETRY\n");
  128.     fprintf (stderr,
  129. "In PPM mode, geometry must be specified as WxH, where W and H are width and\n\
  130. height in pixels respectively.  Other formats are possible under X; type\n\
  131. 'man X' for an explanation of them.  This error is also generated if the\n\
  132. geometry that you specify is too small or too large in one or both directions.\n");
  133.     break;
  134.   case CANTOPENDISPLAY:
  135.     fprintf (stderr, "CANTOPENDISPLAY\n");
  136.     fprintf (stderr,
  137. "Can't open the X display.  Check the setting of the DISPLAY environment\n\
  138. variable or the value supplied with the -display switch.  You may also need\n\
  139. to fix your X authorities with xhost or xauth.\n");
  140.     break;
  141.   case WANTMOREARGS:
  142.     fprintf (stderr, "WANTMOREARGS\n");
  143.     fprintf (stderr,
  144.       "Your last switch wants an argument that you did not provide.\n");
  145.     break;
  146.   case CONFIGFAIL:
  147.     fprintf (stderr, "CONFIGFAIL\n");
  148.     perror ("fopen");
  149.     fprintf (stderr,
  150.       "Could not open the config file that you specified.\n");
  151.     break;
  152.   case CANTOPENFILE:
  153.     fprintf (stderr, "CANTOPENFILE\n");
  154.     perror ("fopen");
  155.     fprintf (stderr,
  156.       "Could not open a file.\n");
  157.     break;
  158.   case BADINTEGER:
  159.     fprintf (stderr, "BADINTEGER\n");
  160.     fprintf (stderr,
  161.       "One of your switches wants an integer but didn't get one.\n");
  162.     break;
  163.   case BADFLOAT:
  164.     fprintf (stderr, "BADFLOAT\n");
  165.     fprintf (stderr,
  166.   "One of your switches wants a floating point number but didn't get one.\n");
  167.     break;
  168.   case BADHHMM:
  169.     fprintf (stderr, "BADHHMM\n");
  170.     fprintf (stderr,
  171. "Offset must be of the form [-]HH:MM where HH is hours and MM is minutes.\n"
  172.     );
  173.     break;
  174.   case STRETCHTOOSMALL:
  175.     fprintf (stderr, "STRETCHTOOSMALL\n");
  176.     fprintf (stderr, "-gstretch needs a positive value.\n");
  177.     break;
  178.   case STRETCHTOOBIG:
  179.     fprintf (stderr, "STRETCHTOOBIG\n");
  180.     fprintf (stderr, "-gstretch wants a smaller number.\n");
  181.     break;
  182.   case BADTIMESTAMP:
  183.     fprintf (stderr, "BADTIMESTAMP\n");
  184.     fprintf (stderr,
  185. "-gstart wants a timestamp of the form YYYY:MM:DD:HH:MM.  Example -- half past\n\
  186. midnight, June 1, 1995:  1995:06:01:00:30.  This error may also be raised if\n\
  187. you specify a non-existent time, such as February 31st or the hour that is\n\
  188. skipped because of Daylight Savings Time, or go outside the acceptable range\n\
  189. of dates (i.e. before 1970 or after 2037 on most Unix machines).\n");
  190.     break;
  191.   case MISSINGYEAR:
  192.     fprintf (stderr, "MISSINGYEAR\n");
  193.     fprintf (stderr,
  194. "In order to calculate tides for a given year, the node factors and\n\
  195. equilibrium arguments for that year must be in the harmonics file.  The\n\
  196. harmonics file that you are using only supports the following years:\n");
  197.     if (num_nodes < num_epochs)
  198.       num_epochs = num_nodes;
  199.     fprintf (stderr, "    %d through %d\n", first_year,
  200.       first_year+num_epochs-1);
  201.     break;
  202.   case OFFSETSTEXTONLY:
  203.     fprintf (stderr, "OFFSETSTEXTONLY\n");
  204.     fprintf (stderr,
  205. "-hloff, -htoff, -lloff, and -ltoff are only supported in text mode and cannot\n\
  206. be used with -graph, -ppm, -mark, or -banner.\n");
  207.     break;
  208.   default:
  209.     assert (0);
  210.   }
  211.   exit (-1);
  212. }
  213.  
  214. /* Allocate tide-prediction storage except for node factors and epochs. */
  215. static void
  216. allocate_cst ()
  217. {
  218.   assert (num_csts);
  219.   assert (cst_speeds = (double *) malloc (num_csts * sizeof (double)));
  220.   assert (loc_amp = (double *) malloc (num_csts * sizeof (double)));
  221.   assert (loc_epoch = (double *) malloc (num_csts * sizeof (double)));
  222.   assert (work = (double *) malloc (num_csts * sizeof (double)));
  223. }
  224.  
  225. /* Allocate cst_nodes. */
  226. static void
  227. allocate_nodes ()
  228. {
  229.   int a;
  230.   assert (num_csts);
  231.   assert (num_nodes);
  232.   assert (cst_nodes = (double **) malloc (num_csts * sizeof (double *)));
  233.   for (a=0;a<num_csts;a++)
  234.     assert (cst_nodes[a] = (double *) malloc (num_nodes * sizeof (double)));
  235. }
  236.  
  237. /* Allocate cst_epochs. */
  238. static void
  239. allocate_epochs ()
  240. {
  241.   int a;
  242.   int temp_epochs,temp_csts;
  243.   
  244.   assert (num_csts);
  245.   assert (num_epochs);
  246.   temp_epochs = num_epochs * sizeof (double);
  247.   temp_csts = num_csts * sizeof (double);
  248.   assert (cst_epochs = (double **) malloc (temp_csts));
  249.   for (a=0;a<num_csts;a++){
  250.     cst_epochs[a] = (double *) malloc (temp_epochs);
  251.     assert (cst_epochs[a]);
  252.     }
  253. }
  254.  
  255. /* Turn a time displacement of the form [-]HH:MM into the number of seconds. */
  256. int
  257. hhmm2seconds (char *hhmm)
  258. {
  259.   long h, m;
  260.   char s;
  261.   if (sscanf (hhmm, "%d:%d", &h, &m) != 2)
  262.     barf (BADHHMM);
  263.   if (sscanf (hhmm, "%c", &s) != 1)
  264.     barf (BADHHMM);
  265.   if (h < 0 || s == '-')
  266.     m = -m;
  267.   return h*(long)HOURSECONDS + m*60;
  268. }
  269.  
  270. /* Get rid of trailing garbage in linrec */
  271. static char *
  272. nojunk (char *linrec)
  273. {
  274.   char *a;
  275.   a = &(linrec[strlen(linrec)]);
  276.   while (a > linrec)
  277.     if (*(a-1) == '\n' || *(a-1) == '\r' || *(a-1) == ' ')
  278.       *(--a) = '\0';
  279.     else
  280.       break;
  281.   return linrec;
  282. }
  283.  
  284. /* Slackful strcmp; 0 = match.  It's case-insensitive and accepts a
  285.    prefix instead of the entire string.  The second argument is the
  286.    one that can be inter. */
  287. static int
  288. slackcmp (char *a, char *b)
  289. {
  290.   int c, cmp, n;
  291.   n = strlen (b);
  292.   if ((int)(strlen (a)) < n)
  293.     return 1;
  294.   for (c=0;c<n;c++)
  295.   {
  296.     cmp = ((a[c] >= 'A' && a[c] <= 'Z') ? a[c] - 'A' + 'a' : a[c])
  297.             -
  298.           ((b[c] >= 'A' && b[c] <= 'Z') ? b[c] - 'A' + 'a' : b[c]);
  299.     if (cmp)
  300.       return cmp;
  301.   }
  302.   return 0;
  303. }
  304.  
  305. /* Read a line from the harmonics file, skipping comment lines */
  306. static int
  307. next_line (FILE *fp, char linrec[linelen], int end_ok)
  308. {
  309.   do {
  310.     if (!fgets (linrec, linelen, fp)) {
  311.       if (end_ok)
  312.         return 0;
  313.       else {
  314.         fprintf (stderr, "Unexpected end of harmonics file '%s'\n",
  315.         hfile_name);
  316.         exit (-1);
  317.       }
  318.     }
  319.   } while (linrec[0] == '#');
  320.   return 1;
  321. }
  322.  
  323. /* Convert a struct tm in GMT back to a time_t. */
  324. time_t
  325. tm2gmt (struct tm *ht)
  326. {
  327.   time_t temp;
  328.   
  329. #if defined(sun) && !defined(SVR4) && !defined(__svr4__)
  330.   /* Old SunOS */
  331.   temp = timegm (ht);
  332.   /* {
  333.     struct tm *sunos;
  334.     temp = timelocal (ht);
  335.     sunos = localtime (&temp);
  336.     temp += sunos->tm_gmtoff;
  337.   } */
  338. #else
  339.   assert ((temp = mktime (ht)) != -1);
  340.   /* Determine the local time zone offset and correct for it */
  341.   /* Taking timezone from gettimeofday didn't work (always zero) */
  342. #ifdef ultrix
  343.  {
  344.   /* Ultrix compatibility courtesy of Rob Miracle */
  345.   struct tm *tzinfo;
  346.   tzinfo = localtime (&temp);
  347.   temp += tzinfo->tm_gmtoff;
  348.  }
  349. #else
  350.   localtime(&temp);
  351.   temp += gTimezone;
  352.  
  353. #endif
  354. #endif
  355.   return temp;
  356. }
  357.  
  358. /* Calculate time_t of the epoch. */
  359. static void
  360. set_epoch ()
  361. {
  362.   struct tm ht;
  363.   if (year < first_year || year >= first_year + num_epochs) {
  364.     fprintf (stderr, "Tidelib:  Don't have equilibrium arguments for %d\n",
  365.       year);
  366.     barf (MISSINGYEAR);
  367.   }
  368.   ht.tm_year = year - 1900;
  369.   ht.tm_sec = ht.tm_min = ht.tm_hour = ht.tm_mon = 0;
  370.   ht.tm_mday = 1;
  371.   ht.tm_isdst = 0;
  372.   epoch = tm2gmt (&ht);
  373. }
  374.  
  375. /* Attempt to load up the local time zone of the location.  Moof! */
  376. void
  377. change_time_zone (char *tz)
  378. {
  379.   static char env_string[MAXARGLEN+1];
  380. #ifdef BROKEN_ZONEINFO
  381.  
  382.   /* This is not a comprehensive time zone translation since many time
  383.      zones are not represented in the harmonics file.  Additional
  384.      translations will be added as needed in future versions of
  385.      XTide. */
  386.  
  387.   /* I have not tried to encode the different systems for DST.  If you
  388.      have an encoding that is known to be correct, please send it.  It
  389.      should look something like EST5EDT4,116/2:00:00,298/2:00:00. */
  390.  
  391.   /* United States.  YST is Yukon Standard Time, sometimes written as
  392.      AKST (Alaska Standard Time).  Hawaii doesn't do DST. */
  393.  
  394.   if (!strcmp (tz, ":US/Eastern"))
  395.     strcpy (tz, "EST5EDT");
  396.   else if (!strcmp (tz, ":US/Central"))
  397.     strcpy (tz, "CST6CDT");
  398.   else if (!strcmp (tz, ":US/Mountain"))
  399.     strcpy (tz, "MST7MDT");
  400.   else if (!strncmp (tz, ":US/Pacific", 11))
  401.     strcpy (tz, "PST8PDT");
  402.   else if (!strcmp (tz, ":US/Alaska"))
  403.     strcpy (tz, "YST9YDT");
  404.   else if (!strcmp (tz, ":US/Hawaii"))
  405.     strcpy (tz, "HST10");
  406.  
  407.   /* Canada */
  408.  
  409.   else if (!strcmp (tz, ":Canada/Pacific"))
  410.     strcpy (tz, "PST8PDT");
  411.   else if (!strcmp (tz, ":Canada/Eastern"))
  412.     strcpy (tz, "EST5EDT");
  413.   else if (!strcmp (tz, ":Canada/Atlantic"))
  414.     strcpy (tz, "AST4ADT");
  415.   else if (!strcmp (tz, ":Canada/Newfoundland"))
  416.     strcpy (tz, "NST3:30NDT");
  417.  
  418.   /* Mexico */
  419.  
  420.   else if (!strcmp (tz, ":Mexico/BajaNorte"))
  421.     strcpy (tz, "PST8PDT");
  422.   else if (!strcmp (tz, ":Mexico/BajaSur"))
  423.     strcpy (tz, "MST7");
  424.   else if (!strcmp (tz, ":Mexico/General"))
  425.     strcpy (tz, "CST6");
  426.  
  427.   /* South America */
  428.   /* These will be really screwed up since DST is in December */
  429.  
  430.   else if (!strcmp (tz, ":Chile/EasterIsland"))
  431.     strcpy (tz, "LST6");
  432.   else if (!strcmp (tz, ":Brazil/East"))
  433.     strcpy (tz, "LST3");
  434.  
  435.   /* Asia */
  436.  
  437.   else if (!strcmp (tz, ":PRC"))
  438.     strcpy (tz, "CST-8");
  439.   else if (!strcmp (tz, ":Hongkong"))
  440.     strcpy (tz, "HKT-8");
  441.   else if (!strcmp (tz, ":Singapore"))
  442.     strcpy (tz, "SST-8");
  443.   else if (!strcmp (tz, ":Japan"))
  444.     strcpy (tz, "JST-9");
  445.  
  446.   /* The Rest Of Us */
  447.   /* DST will be screwed up for these */
  448.   /* The weird syntax for WET is from Dale's file; I did MET the same way. */
  449.  
  450.   else if (!strcmp (tz, ":WET"))
  451.     strcpy (tz, "WET0WETDST");
  452.   else if (!strcmp (tz, ":MET"))
  453.     strcpy (tz, "MET-1METDST");
  454.   else if (!strcmp (tz, ":Iceland"))
  455.     strcpy (tz, "WET0");
  456.   else if (!strcmp (tz, ":Australia/Queensland"))
  457.     strcpy (tz, "EST-10");
  458.   else if (!strcmp (tz, ":GB-Eire"))
  459.     strcpy (tz, "GMT0BST");
  460.  
  461.   /* GMT and offsets therefrom, which will be called LST for Local
  462.      Standard Time. */
  463.  
  464.   else if (!strcmp (tz, ":GMT"))
  465.     strcpy (tz, "GMT0");
  466.   else if (!strcmp (tz, ":UTC"))
  467.     strcpy (tz, "GMT0");
  468.   else if (!strncmp (tz, ":GMT", 4)) {
  469.     int a;
  470.     assert (sscanf (tz+4, "%d", &a) == 1);
  471.     sprintf (tz, "LST%d", -a);
  472.   }
  473.  
  474. #endif
  475.   /* assert (setenv ("TZ", tz, 1) == 0); */
  476.   /* According to the SYSV man page, I can't alter env_string ever again. */
  477.   sprintf (env_string, "TZ=%s", tz);
  478. // This is part that makes it possible to use time in local time zone.
  479. // It doesn't work on Mac, so it's commented out
  480. //  assert (putenv (env_string) == 0);
  481. //  tzset ();
  482.  
  483. }
  484.  
  485. /* Load harmonics data */
  486. void
  487. load_data ()
  488. {
  489.   FILE *fp;
  490.   char linrec[linelen], junk[80];
  491.   int a, b;
  492.   if (!(fp = fopen (hfile_name, "r"))) {
  493.     fprintf (stderr, "Could not open harmonics file '%s'\n", hfile_name);
  494.     barf (CANTOPENFILE);
  495.   }
  496.   next_line (fp, linrec, 0);
  497.   assert (sscanf (linrec, "%d", &num_csts) == 1);
  498.   allocate_cst ();
  499.   /* Load constituent speeds */
  500.   for (a=0;a<num_csts;a++) {
  501.     next_line (fp, linrec, 0);
  502.     assert (sscanf (linrec, "%s %lf", junk, &(cst_speeds[a])) == 2);
  503.     cst_speeds[a] /= 3600.0;  /* Convert to degrees per second */
  504.   }
  505.   /* Get first year for nodes and epochs */
  506.   next_line (fp, linrec, 0);
  507.   assert (sscanf (linrec, "%d", &first_year) == 1);
  508.  
  509.   /* Load epoch table */
  510.   next_line (fp, linrec, 0);
  511.   assert (sscanf (linrec, "%d", &num_epochs) == 1);
  512.   allocate_epochs ();
  513.   for (a=0;a<num_csts;a++) {
  514.     assert (fscanf (fp, "%s", linrec) == 1);
  515.     for (b=0;b<num_epochs;b++)
  516.       assert (fscanf (fp, "%lf", &(cst_epochs[a][b])) == 1);
  517.   }
  518.   /* Sanity check */
  519.   assert (fscanf (fp, "%s", linrec) == 1);
  520.   assert (!strcmp (linrec, "*END*"));
  521.  
  522.   /* Load node factor table */
  523.   next_line (fp, linrec, 0);  /* Skip lingering CR from *END* */
  524.   next_line (fp, linrec, 0);
  525.   assert (sscanf (linrec, "%d", &num_nodes) == 1);
  526.   allocate_nodes ();
  527.   for (a=0;a<num_csts;a++) {
  528.     assert (fscanf (fp, "%s", linrec) == 1);
  529.     for (b=0;b<num_nodes;b++)
  530.       assert (fscanf (fp, "%lf", &(cst_nodes[a][b])) == 1);
  531.   }
  532.   /* Sanity check */
  533.   assert (fscanf (fp, "%s", linrec) == 1);
  534.   assert (!strcmp (linrec, "*END*"));
  535.  
  536.   /* List locations and exit? */
  537.   if (list) {
  538.     next_line (fp, linrec, 0);  /* Skip lingering CR from *END* */
  539.     while (next_line (fp, linrec, 1)) {
  540.       nojunk (linrec);
  541.       if (curonly)
  542.         if (!strstr (linrec, "Current"))
  543.           continue;
  544.       printf ("%s", linrec);
  545.       next_line (fp, linrec, 0);
  546.       if (list == 2)
  547.         printf ("\t%s\n", nojunk(linrec));
  548.       else
  549.         printf ("\n");
  550.       for (a=0;a<num_csts+1;a++)
  551.         next_line (fp, linrec, 0);
  552.     }
  553.     fclose (fp);
  554.     exit (0);
  555.   }
  556.  
  557.   /* Load harmonic constants for desired location */
  558.   while (next_line (fp, linrec, 1)) {
  559.     nojunk (linrec);
  560.     if (curonly)
  561.       if (!strstr (linrec, "Current"))
  562.         continue;
  563.     if (slackcmp (linrec, location))
  564.       continue;
  565.     strcpy (location, linrec);
  566.     /* Is it a current? */
  567.     if (strstr (location, "Current"))
  568.       iscurrent = 1;
  569.     /* Get meridian */
  570.     next_line (fp, linrec, 0);
  571.     meridian = hhmm2seconds (linrec);
  572.     /* Get tzfile, if present */
  573.     if (sscanf (nojunk(linrec), "%s %s", junk, tzfile) < 2)
  574.       strcpy (tzfile, ":UTC");
  575.     /* Get DATUM */
  576.     next_line (fp, linrec, 0);
  577.     assert (sscanf (linrec, "%lf", &DATUM) == 1);
  578.     /* Get constituents */
  579.     for (a=0;a<num_csts;a++) {
  580.       char junk[80];
  581.       next_line (fp, linrec, 0);
  582.       assert (sscanf (linrec, "%s %lf %lf", junk, &(loc_amp[a]),
  583.         &(loc_epoch[a])) == 3);
  584.       assert (loc_amp[a] >= 0.0);
  585.     }
  586.     fclose (fp);
  587.     return;
  588.   }
  589.   if (curonly)
  590.     fprintf (stderr, "Could not find current set '%s' in harmonics file\n",
  591.       location);
  592.   else
  593.     fprintf (stderr, "Could not find location '%s' in harmonics file\n",
  594.       location);
  595.   exit (-1);
  596. }
  597.  
  598. /* Check for errors in equilibrium arguments by inferring values from
  599.    neighboring years and comparing with the stored values.  This
  600.    function assumes that the values are normalized [0..360). */
  601. void
  602. check_epoch ()
  603. {
  604.   double inferred, stored, error;
  605.   int a, final_year;
  606.   time_t prev_epoch, next_epoch;
  607.   final_year = first_year + num_epochs - 1;
  608.   if (checkyear < first_year || checkyear > final_year) {
  609.     fprintf (stderr, "Tidelib:  Don't have equilibrium arguments for %d\n",
  610.       checkyear);
  611.     barf (MISSINGYEAR);
  612.   }
  613.   if (checkyear != first_year) {
  614.     year = checkyear - 1;
  615.     set_epoch ();
  616.     prev_epoch = epoch;
  617.   }
  618.   if (checkyear != final_year) {
  619.     year = checkyear + 1;
  620.     set_epoch ();
  621.     next_epoch = epoch;
  622.   }
  623.   year = checkyear;
  624.   set_epoch ();
  625.   puts ("#  Stored  Inferred  Error");
  626.   for (a=0;a<num_csts;a++) {
  627.     if (checkyear == first_year)
  628.       inferred = fmod (cst_speeds[a] * (-(int)(next_epoch - epoch)) +
  629.       cst_epochs[a][1], 360.0);
  630.     else if (checkyear == final_year)
  631.       inferred = fmod (cst_speeds[a] * (epoch - prev_epoch) +
  632.       cst_epochs[a][final_year-1-first_year], 360.0);
  633.     else {
  634.       double t1, t2;
  635.       t1 = fmod (cst_speeds[a] * (-(int)(next_epoch - epoch)) +
  636.         cst_epochs[a][checkyear+1-first_year], 360.0);
  637.       t2 = fmod (cst_speeds[a] * (epoch - prev_epoch) +
  638.         cst_epochs[a][checkyear-1-first_year], 360.0);
  639.       if (t1 < 0.0)
  640.         t1 += 360.0;
  641.       if (t2 < 0.0)
  642.         t2 += 360.0;
  643.       inferred = (t1 + t2) / 2.0;
  644.       if (fabs (inferred - t1) > 90.0 || fabs (inferred - t2) > 90.0)
  645.         inferred = fmod (inferred + 180.0, 360.0);
  646.     }
  647.     stored = cst_epochs[a][checkyear-first_year];
  648.     if (inferred < 0.0)
  649.       inferred += 360.0;
  650.     error = fabs (stored - inferred);
  651.     if (fabs (stored + 360.0 - inferred) < error)
  652.       error = fabs (stored + 360.0 - inferred);
  653.     if (fabs (stored - inferred - 360.0) < error)
  654.       error = fabs (stored - inferred - 360.0);
  655.     printf ("%d  %010.6f  %010.6f  %f\n", a, stored, inferred, error);
  656.   }
  657. }
  658.  
  659. /* Convert YYYY:MM:DD:HH:MM to time_t.  Don't know what happens to DST. */
  660. time_t
  661. parse_time_string (char *time_string)
  662. {
  663.   struct tm ht;
  664.   time_t temp;
  665.   if (sscanf (time_string, "%d:%d:%d:%d:%d",
  666.     &(ht.tm_year), &(ht.tm_mon), &(ht.tm_mday), &(ht.tm_hour),
  667.     &(ht.tm_min)) != 5)
  668.     barf (BADTIMESTAMP);
  669.   ht.tm_sec = 0;
  670.   (ht.tm_mon)--;
  671.   ht.tm_year -= 1900;
  672.   ht.tm_isdst = -1;
  673.   if ((temp = mktime (&ht)) == -1)
  674.     barf (BADTIMESTAMP);
  675.   return temp;
  676. }
  677.  
  678. /* Return the year for a time_t.  This function gets called a lot and might
  679.    be a performance bottleneck if gmtime is slow. */
  680. int
  681. yearoftimet (time_t t)
  682. {
  683.  
  684. //Since there no gmtime() in THINK C 5.0.4 ANSI library, I'm just going to take 
  685. //localtime() with t as argument, adding gTimezone to it.
  686. /*  return ((gmtime (&t))->tm_year) + 1900;
  687. */
  688.     static    time_t    utc_time;
  689.     utc_time =t+gTimezone;
  690.     return (localtime(&utc_time))->tm_year+1900;
  691.  
  692. }
  693.  
  694.  
  695. /* Make a reasonable choice for hinc for a given amplitude. */
  696. void
  697. pick_hinc ()
  698. {
  699.   if (amplitude > 30.0)
  700.     hinc = 10;
  701.   else if (amplitude > 15.0)
  702.     hinc = 5;
  703.   else if (amplitude > 8.0)
  704.     hinc = 2;
  705.   else
  706.     hinc = 1;
  707. }
  708.  
  709. /* Figure out normalized multipliers for constituents for a particular
  710.    year.  Save amplitude for drawing unit lines. */
  711. static void
  712. figure_multipliers ()
  713. {
  714.   int a;
  715.   amplitude = 0.0;
  716.   if (year < first_year || year >= first_year + num_nodes) {
  717.     fprintf (stderr, "Tidelib:  Don't have node factors for %d\n", year);
  718.     barf (MISSINGYEAR);
  719.   }
  720.   for (a=0;a<num_csts;a++)
  721.     amplitude += (work[a] = loc_amp[a] * cst_nodes[a][year-first_year]);
  722.   assert (amplitude != 0.0);
  723.   for (a=0;a<num_csts;a++)
  724.     work[a] /= amplitude;
  725.   if (hincmagic)
  726.     pick_hinc ();
  727. }
  728.  
  729. /* Doh...  Forgot to convert to radians. */
  730. static double
  731. cos_degrees (double x)
  732. {
  733. //This will speed things up a bit...
  734. //  return cos (x * M_PI / 180.0);
  735. return cos (x * 0.01745329252);
  736. }
  737.  
  738. //This is same routine as cos_degrees(), but compiled to use FPU. I can't
  739. //test it, since I don't have Mac with FPU.
  740.  
  741. #pragma options mc68881
  742. static double
  743. cos_degrees_fpu (double x)
  744. {
  745. return cos (x * 0.01745329252);
  746. }
  747. #pragma options !mc68881
  748.  
  749. /* Re-initialize for a different year */
  750. void
  751. happy_new_year (int new_year)
  752. {
  753.   year = new_year;
  754.   figure_multipliers ();
  755.   set_epoch ();
  756. }
  757.  
  758. /* Calculate the normalized tide (-1.0 .. 1.0) for a given time. */
  759. double
  760. time2tide (time_t t)
  761. {
  762.   double tide = 0.0;
  763.   int a, sleepyTime, new_year = yearoftimet (t);
  764.   EventRecord        event;
  765.   
  766.       sleepyTime=1;
  767.     WaitNextEvent(everyEvent, &event, sleepyTime, NULL);
  768.   if (new_year != year)
  769.     happy_new_year (new_year);
  770.  if (gHasFPU){
  771.   for (a=0;a<num_csts;a++)
  772.     tide += work[a] *
  773.       cos_degrees_fpu (cst_speeds[a] * ((int)(t - epoch) + meridian) +
  774.       cst_epochs[a][year-first_year] - loc_epoch[a]);
  775.  } else
  776.   {
  777.   for (a=0;a<num_csts;a++)
  778.     tide += work[a] *
  779.       cos_degrees (cst_speeds[a] * ((int)(t - epoch) + meridian) +
  780.       cst_epochs[a][year-first_year] - loc_epoch[a]);
  781.   }
  782. /* cos((cst_speeds[a] * ((int)(t - epoch) + meridian) +
  783.       cst_epochs[a][year-first_year] - loc_epoch[a])*0.01745329252);*/
  784.   return tide;
  785. }
  786.  
  787. /* Calculate the denormalized tide. */
  788. double
  789. time2atide (time_t t)
  790. {
  791.   return time2tide (t) * amplitude + DATUM;
  792. }
  793.  
  794. /* Next high tide, low tide, transition of the mark level, or some
  795.    combination.
  796.        Bit      Meaning
  797.         0       low tide
  798.         1       high tide
  799.         2       falling transition
  800.         3       rising transition
  801. */
  802. int
  803. next_big_event (time_t *tm)
  804. {
  805.   double p, q;
  806.   int flags = 0, slope = 0;
  807.   p = time2atide (*tm);
  808.   *tm += 60;
  809.   q = time2atide (*tm);
  810.   *tm += 60;
  811.   if (p < q)
  812.     slope = 1;
  813.   while (1) {
  814.     if ((slope == 1 && q < p) || (slope == 0 && p < q)) {
  815.       /* Tide event */
  816.       flags |= (1 << slope);
  817.     }
  818.     /* Modes in which to return mark transitions: */
  819.     /*    -text (no -graph)   */
  820.     /*    -graph (no -text)   */
  821.     /*    -ppm                */
  822.     if (mark && ((text && !graphmode) || (!text && graphmode)
  823.     || ppm))
  824.       if ((p > marklev && q <= marklev) || (p < marklev && q >= marklev)) {
  825.         /* Transition event */
  826.         if (p < q)
  827.           flags |= 8;
  828.         else
  829.           flags |= 4;
  830.         if (!(flags & 3)) {
  831.           /* If we're incredibly unlucky, we could miss a tide event if we
  832.              don't check for it here:
  833.  
  834.                              . <----   Value that would be returned
  835.                         -----------    Mark level
  836.                       .           .   
  837.           */
  838.           p = q;
  839.           q = time2atide (*tm);
  840.           if ((slope == 1 && q < p) || (slope == 0 && p < q)) {
  841.             /* Tide event */
  842.             flags |= (1 << slope);
  843.           }
  844.         }
  845.       }
  846.     if (flags) {
  847.       *tm -= 60;
  848.       /* Don't back up over a transition event, but do back up to where the
  849.          tide changed if possible.  If they happen at the same time, then
  850.          we're off by a minute on the tide, but if we back it up it will
  851.          get snagged on the transition event over and over. */
  852.       if (flags < 4)
  853.         *tm -= 60;
  854.       return flags;
  855.     }
  856.     p = q;
  857.     q = time2atide (*tm);
  858.     *tm += 60;
  859.   }
  860. }
  861.  
  862. /* Used by do_timestamp. */
  863. static int
  864. is_ascii (char a)
  865. {
  866.   if (a >= ' ' && a <= '~')
  867.     return 1;
  868.   return 0;
  869. }
  870.  
  871. /* Generate a timestamp in the appropriate style. */
  872. void
  873. do_timestamp (char buf[13], struct tm *t)
  874. {
  875.   int a;
  876.   if (skinny)
  877.     strftime (buf, 13, "%H%M", t);
  878.   else if (tadjust)
  879.     strftime (buf, 13, "%I:%M %p", t);
  880.   else if (utc)
  881.     strftime (buf, 13, "%H:%M UTC", t);
  882.   else
  883.     strftime (buf, 13, "%I:%M %p %Z", t);
  884.   if (!skinny && buf[0] == '0') {
  885.     if (text && !ppm && !banner)
  886.       buf[0] = ' ';
  887.     else
  888. #if defined(sun) && !defined(SVR4) && !defined(__svr4__)
  889.     /* Old SunOS */
  890.     {
  891.       for (a=0;a<12;a++)
  892.         buf[a] = buf[a+1];
  893.     }
  894. #else
  895.       memmove (buf, buf+1, 12);
  896. #endif
  897.   }
  898.  
  899.   /* Some time zone files don't fill in the time zone properly.  We
  900.      can get blanks, or worse yet, unterminated binary garbage. */
  901.   buf[12] = '\0';
  902.   for (a=11;a>=8;a--)
  903.     if (!is_ascii (buf[a]))
  904.       buf[a] = '\0';
  905.   nojunk (buf);
  906. }
  907.  
  908. /* Convert a genuine time_t to a specially mangled struct tm. */
  909. struct tm *
  910. tmtime (time_t t)
  911. {
  912.     static    time_t    utc_time;
  913.     utc_time =t+gTimezone;
  914.   if (tadjust) {
  915.     t += tadjust;
  916.     utc_time =t+gTimezone;
  917.  
  918.       return localtime(&utc_time);
  919.   } else if (utc)
  920.       return localtime(&utc_time);
  921.     else
  922.       return localtime (&t);
  923. }
  924.  
  925. /* Find the previous hour-transition from a specified time. */
  926. time_t
  927. prev_hour (time_t t)
  928. {
  929.   struct tm ttm;
  930.   time_t temp;
  931.   static    time_t    utc_time;
  932.   utc_time =t+gTimezone;
  933.  
  934.   if (utc || tadjust) {
  935.     ttm = *(localtime(&utc_time));
  936.  
  937.     ttm.tm_sec = ttm.tm_min = 0;
  938.     temp = tm2gmt (&ttm) - (tadjust % (long)HOURSECONDS);
  939.   } else {
  940.     ttm = *(localtime (&t));
  941.     ttm.tm_sec = ttm.tm_min = 0;
  942.     temp = mktime (&ttm);
  943.   }
  944.   if (temp <= t - (long)HOURSECONDS)
  945.     temp += (long)HOURSECONDS;
  946.   if (temp > t)
  947.     temp -= (long)HOURSECONDS;
  948.   assert (temp > t - (long)HOURSECONDS && temp <= t);
  949.   return temp;
  950. }
  951.  
  952. /* Find the previous day-transition from a specified time. */
  953. time_t
  954. prev_day (time_t t)
  955. {
  956.   struct tm ttm;
  957.   time_t temp;
  958.   static    time_t    utc_time;
  959.   utc_time =t+gTimezone;
  960.  
  961.   if (utc || tadjust) {
  962.     ttm = *(localtime(&utc_time));
  963.     ttm.tm_sec = ttm.tm_min = ttm.tm_hour = 0;
  964.     temp = tm2gmt (&ttm) - tadjust;
  965.   } else {
  966.     ttm = *(localtime (&t));
  967.     ttm.tm_sec = ttm.tm_min = ttm.tm_hour = 0;
  968.     temp = mktime (&ttm);
  969.   }
  970.   if (temp <= t - (long)DAYSECONDS)
  971.     temp += (long)DAYSECONDS;
  972.   if (temp > t)
  973.     temp -= (long)DAYSECONDS;
  974.   assert (temp > t - (long)DAYSECONDS && temp <= t);
  975.  
  976.   /* If there has been a DST change, the time that we have calculated
  977.      can be wrong.  Test for the error, and correct it when found.  It
  978.      is assumed that the DST change is by an integer number of hours
  979.      and by no more than 2 hours. */
  980.   if (!(utc || tadjust)) {
  981.     if (localtime(&temp)->tm_hour) {
  982.       int day;
  983.       temp += 3 * (long)HOURSECONDS;
  984.       day = localtime(&temp)->tm_yday;
  985.       while (localtime(&temp)->tm_yday == day)
  986.         temp -= (long)HOURSECONDS;
  987.       temp += (long)HOURSECONDS;
  988.     }
  989.   }
  990.  
  991.   assert (temp > t - (long)HOURSECONDS * 26 && temp <= t);
  992.   return temp;
  993. }
  994.  
  995. /* Find the Sunday on which to start a calendar. */
  996. time_t
  997. sunday_month (time_t t)
  998. {
  999.   struct tm ttm;
  1000.   int almost_done = 0;
  1001.   ttm = *(tmtime(t));
  1002.   if (ttm.tm_mday == 1)
  1003.     almost_done = 1;
  1004.   while (ttm.tm_wday || !almost_done) {
  1005.     t = prev_day (t-1);
  1006.     ttm = *(tmtime(t));
  1007.     if (ttm.tm_mday == 1)
  1008.       almost_done = 1;
  1009.   }
  1010.   return t;
  1011. }
  1012.  
  1013. /* Find the next day-transition from a specified time, ASSUMING that the
  1014.    parameter passed in is ALREADY on a DAY BOUNDARY. */
  1015. time_t
  1016. increment_day (time_t t)
  1017. {
  1018.   time_t iday;
  1019.   int day;
  1020.   struct tm *ttm;
  1021.  
  1022.   /* These are easy because there's no DST to worry about */
  1023.   if (utc || tadjust)
  1024.     return t + (long)DAYSECONDS;
  1025.  
  1026.   /* Local time is the troublesome case!  The following code will work
  1027.      provided that the DST change is by an integer number of hours
  1028.      and by no more than 2 hours. */
  1029.  
  1030.   day = localtime(&t)->tm_yday;
  1031.   iday = t + (long)HOURSECONDS * 22;
  1032.   ttm = localtime(&iday);
  1033.   while (ttm->tm_yday == day) {
  1034.     iday += (long)HOURSECONDS;
  1035.     ttm = localtime(&iday);
  1036.   }
  1037.  
  1038.   /* Insure that we were not caught by a non-integer DST change. */
  1039.   assert (!(ttm->tm_min));
  1040.  
  1041.   return iday;
  1042. }
  1043.  
  1044. /* Determine next big event and set up text string for write_high_tide.
  1045.    Returns are same as next_big_event. */
  1046. int
  1047. update_high_tide ()
  1048. {
  1049.   struct tm *t;
  1050.   char *tidedate, *tidetime;
  1051.   int event_type;
  1052.   prev_ht = next_ht;
  1053.   event_type = next_big_event (&next_ht);
  1054.   if ((event_type & 2) || graphmode || text || ppm || banner) {
  1055.     tidedate = next_ht_date;
  1056.     tidetime = next_ht_text;
  1057.   } else {
  1058.     tidedate = next_lt_date;
  1059.     tidetime = next_lt_text;
  1060.   }
  1061.   if (event_type & 2)
  1062.     t = tmtime (next_ht + httimeoff);
  1063.   else
  1064.     t = tmtime (next_ht + lttimeoff);
  1065.   do_timestamp (tidetime, t);
  1066.   if (skinny)
  1067.     strftime (tidedate, 11, "%m-%d", t);
  1068.   else
  1069.     strftime (tidedate, 11, "%Y-%m-%d", t);
  1070.   return event_type;
  1071. }
  1072.  
  1073.  
  1074. /**********************************************************************/
  1075.  
  1076. /* Calendar and plain text mode */
  1077.  
  1078. #define MAXTIDESPERDAY 25
  1079. #define DAYWIDTH 7
  1080. /* static char week[DAYWIDTH*7][MAXTIDESPERDAY*2]; */
  1081. static char **week = NULL;
  1082.  
  1083. /* Set one tide in the calendar week being constructed */
  1084. static void
  1085. do_one_calendar_tide (int dayofweek, int tidenumber, int event_type)
  1086. {
  1087.   int x;
  1088.   char desc, temp[DAYWIDTH];
  1089.   double adjust = 0.0;
  1090.   assert (tidenumber < MAXTIDESPERDAY - 1);  /* Leave room for dates */
  1091.   assert (dayofweek < 7);
  1092.   if (iscurrent) {
  1093.     switch (event_type) {
  1094.     case 1:
  1095.       desc = 'E';
  1096.       break;
  1097.     case 2:
  1098.       desc = 'F';
  1099.       break;
  1100.     case 4:
  1101.     case 8:
  1102.       desc = 'S';
  1103.       break;
  1104.     default:
  1105.       assert (0);
  1106.     }
  1107.   } else {
  1108.     switch (event_type) {
  1109.     case 1:
  1110.       desc = 'L';
  1111.       adjust = ltleveloff;
  1112.       break;
  1113.     case 2:
  1114.       desc = 'H';
  1115.       adjust = htleveloff;
  1116.       break;
  1117.     case 4:
  1118.       desc = 'F';
  1119.       break;
  1120.     case 8:
  1121.       desc = 'R';
  1122.       break;
  1123.     default:
  1124.       assert (0);
  1125.     }
  1126.   }
  1127.   week[dayofweek*DAYWIDTH][2+tidenumber*2] = desc;
  1128.   for (x=0;x<(int)(strlen(next_ht_text));x++)
  1129.     week[dayofweek*DAYWIDTH+x+1][2+tidenumber*2] = next_ht_text[x];
  1130.   /* This will die horribly if the amplitude is too big. */
  1131.   sprintf (temp, "%0.2f", adjust + time2atide (next_ht));
  1132.   for (x=0;x<(int)(strlen(temp));x++)
  1133.     week[dayofweek*DAYWIDTH+x][3+tidenumber*2] = temp[x];
  1134. }
  1135.  
  1136. /* Do one day for a calendar. */
  1137. static void
  1138. do_calendar_day (time_t start, time_t stop, int dayofweek, int *maxline)
  1139. {
  1140.   int tidenumber = 0, event_type, x;
  1141.   next_ht = start - 180; /* Don't miss tides that happen at midnight */
  1142.   while (1) {
  1143.     event_type = update_high_tide ();
  1144.     if (next_ht < start)
  1145.       continue;
  1146.     if (next_ht >= stop)
  1147.       break;
  1148.  
  1149.     /* Add date stamp from first tide (hope we get one) */
  1150.     if (tidenumber == 0)
  1151.       for (x=0;x<(int)(strlen(next_ht_date));x++)
  1152.         week[dayofweek*DAYWIDTH+x][0] = next_ht_date[x];
  1153.  
  1154.     /* Refer to list_tides to understand why this is like this. */
  1155.     if ((event_type & 4) && (event_type & 1))
  1156.       do_one_calendar_tide (dayofweek, tidenumber++, 4);
  1157.     if ((event_type & 8) && (event_type & 2))
  1158.       do_one_calendar_tide (dayofweek, tidenumber++, 8);
  1159.     if (event_type & 1)
  1160.       do_one_calendar_tide (dayofweek, tidenumber++, 1);
  1161.     if (event_type & 2)
  1162.       do_one_calendar_tide (dayofweek, tidenumber++, 2);
  1163.     if ((event_type & 4) && !(event_type & 1))
  1164.       do_one_calendar_tide (dayofweek, tidenumber++, 4);
  1165.     if ((event_type & 8) && !(event_type & 2))
  1166.       do_one_calendar_tide (dayofweek, tidenumber++, 8);
  1167.  
  1168.     if (3+(tidenumber-1)*2 > *maxline)
  1169.       *maxline = 3+(tidenumber-1)*2;
  1170.   }
  1171. }
  1172.  
  1173. /* Do one week for a calendar. */
  1174. static time_t
  1175. do_calendar_week (time_t start)
  1176. {
  1177.   int x, y, maxline = 0, dayofweek;
  1178.   time_t stop;
  1179.   for (x=0;x<DAYWIDTH*7;x++)
  1180.     for (y=0;y<MAXTIDESPERDAY*2;y++)
  1181.       week[x][y] = ' ';
  1182.   start = prev_day (start);
  1183.   stop = increment_day (start);
  1184.   for (dayofweek=0;dayofweek<7;dayofweek++) {
  1185.     do_calendar_day (start, stop, dayofweek, &maxline);
  1186.     start = stop;
  1187.     stop = increment_day (stop);
  1188.   }
  1189.   for (y=0;y<=maxline;y++) {
  1190.     for (x=0;x<DAYWIDTH*7;x++)
  1191.       putchar (week[x][y]);
  1192.     printf ("\n");
  1193.   }
  1194.   return start;
  1195. }
  1196.  
  1197. /* Do a calendar. */
  1198. void
  1199. do_calendar ()
  1200. {
  1201.   time_t start = sunday_month (faketime);
  1202.   int month = tmtime(faketime)->tm_mon, looper;
  1203.  
  1204.   if (!week) {
  1205.     /* Allocate storage */
  1206.     assert (week = (char **) malloc (DAYWIDTH*7 * sizeof (char *)));
  1207.     for (looper=0;looper<DAYWIDTH*7;looper++) {
  1208.      assert (week[looper] = (char *) malloc (MAXTIDESPERDAY*2*sizeof(char)));
  1209.     }
  1210.   }
  1211.  
  1212.   puts (location);
  1213.   if (mark && !iscurrent)
  1214.     printf ("Mark level:  %f\n", marklev);
  1215.   printf ("\nSunday Monday Tuesda Wednes Thursd Friday Saturday\n");
  1216.   do {
  1217.     printf ("\n");
  1218.     start = do_calendar_week (start);
  1219.   } while (tmtime(start)->tm_mon != (month+1)%12);
  1220. }
  1221.  
  1222. /* List several days of high and low tides to standard output.  This
  1223. got ugly because of the need to make things come out in the right order
  1224. with the enhanced mark code. */
  1225. void
  1226. list_tides (void)
  1227. {
  1228.   int a, event_type;
  1229.   char *high, *low, *rise, *fall;
  1230.   next_ht = faketime;
  1231.   if (iscurrent) {
  1232.     high =        "  Max Flood";
  1233.     low =         "    Max Ebb";
  1234.     rise = fall = "Slack Water";
  1235.   } else {
  1236.     high =        "  High Tide";
  1237.     low =         "   Low Tide";
  1238.     rise =        "     Rising";
  1239.     fall =        "    Falling";
  1240.   }
  1241.   puts (location);
  1242.   if (mark && !iscurrent)
  1243.     printf ("Mark level:  %f\n", marklev);
  1244.   if (httimeoff || lttimeoff || htleveloff != 0.0 || ltleveloff != 0.0)
  1245.     printf ("Offsets:  hloff=%f  htoff=%dmin  lloff=%f  ltoff=%dmin\n",
  1246.     htleveloff, httimeoff/60, ltleveloff, lttimeoff/60);
  1247.   if (text < 1)
  1248.     text = 21;
  1249.   for (a=0;a<text;a++) {
  1250.     event_type = update_high_tide ();
  1251.     if ((event_type & 4) && (event_type & 1)) {
  1252.       printf ("%s:  %s %s  %0.2f\n", fall, next_ht_date, next_ht_text,
  1253.       time2atide (next_ht));
  1254.     }
  1255.     if ((event_type & 8) && (event_type & 2)) {
  1256.       printf ("%s:  %s %s  %0.2f\n", rise, next_ht_date, next_ht_text,
  1257.       time2atide (next_ht));
  1258.     }
  1259.     if (event_type & 1) {
  1260.       printf ("%s:  %s %s  %0.2f\n", low, next_ht_date, next_ht_text,
  1261.       ltleveloff + time2atide (next_ht));
  1262.     }
  1263.     if (event_type & 2) {
  1264.       printf ("%s:  %s %s  %0.2f\n", high, next_ht_date, next_ht_text,
  1265.       htleveloff + time2atide (next_ht));
  1266.     }
  1267.     if ((event_type & 4) && !(event_type & 1)) {
  1268.       printf ("%s:  %s %s  %0.2f\n", fall, next_ht_date, next_ht_text,
  1269.       time2atide (next_ht));
  1270.     }
  1271.     if ((event_type & 8) && !(event_type & 2)) {
  1272.       printf ("%s:  %s %s  %0.2f\n", rise, next_ht_date, next_ht_text,
  1273.       time2atide (next_ht));
  1274.     }
  1275.   }
  1276. }
  1277.  
  1278.  
  1279. /**********************************************************************/
  1280.  
  1281.  
  1282. /* Banner mode */
  1283. #define BANNERWIDTH 68
  1284. #define BANNERSTART 11
  1285. void
  1286. do_banner ()
  1287. {
  1288.   int a, b, c, markchar, middlechar, mllwchar;
  1289.   struct tm *t;
  1290.   time_t curtime;
  1291.   char bdate[6], btime[5], buf[80], tbuf[80];
  1292.   if (text < 1)
  1293.     text = 21;
  1294.   curtime = next_ht = faketime;
  1295.   puts (location);
  1296.   /* Initialize the amplitude. */
  1297.   happy_new_year (yearoftimet (faketime));
  1298.   if (mark) {
  1299.     if (!iscurrent)
  1300.       printf ("Mark level:  %f\n", marklev);
  1301.     markchar = (int)((double)BANNERWIDTH * (((marklev - DATUM) / amplitude)
  1302.            * 0.5 + 0.5)) + BANNERSTART;
  1303.     if (markchar < BANNERSTART || markchar > 78)
  1304.       mark = 0;
  1305.   }
  1306.   if (middle)
  1307.     middlechar = BANNERWIDTH / 2 + BANNERSTART;
  1308.   if (mllw) {
  1309.     mllwchar =  (int)((double)BANNERWIDTH * (((-DATUM) / amplitude)
  1310.            * 0.5 + 0.5)) + BANNERSTART;
  1311.     if (mllwchar < BANNERSTART || mllwchar > 78)
  1312.       mllw = 0;
  1313.   }
  1314.   buf[79] = '\0';
  1315.   update_high_tide ();
  1316.   for (a=0;a<text;a++) {
  1317.     t = tmtime (curtime);
  1318.     strftime (btime, 5, "%H%M", t);
  1319.     strftime (bdate, 6, "%m-%d", t);
  1320.     b = (int)((double)BANNERWIDTH * (time2tide(curtime) * 0.5 + 0.5))
  1321.       + BANNERSTART;
  1322.     sprintf (buf, "%s %s ", bdate, btime);
  1323.     if (iscurrent) {
  1324.       for (c=BANNERSTART;c<79;c++)
  1325.         buf[c] = ' ';
  1326.       if (b < markchar)
  1327.         for (c=b;c<markchar;c++)
  1328.           buf[c] = '*';
  1329.       else
  1330.         for (c=markchar+1;c<=b;c++)
  1331.           buf[c] = '*';
  1332.     } else {
  1333.       for (c=BANNERSTART;c<=b;c++)
  1334.         buf[c] = '*';
  1335.       for (c=b+1;c<79;c++)
  1336.         buf[c] = ' ';
  1337.     }
  1338.     if (mark)
  1339.       buf[markchar] = '|';
  1340.     if (middle)
  1341.       buf[middlechar] = '|';
  1342.     if (mllw)
  1343.       buf[mllwchar] = '|';
  1344.     if (curtime+(tstep>>1) >= next_ht) {
  1345.       sprintf (tbuf, "%s %0.2f", next_ht_text, time2atide (next_ht));
  1346.       strncpy (buf+BANNERSTART+1, tbuf, strlen (tbuf));
  1347.       while (curtime+(tstep>>1) >= next_ht)
  1348.         update_high_tide ();
  1349.     }
  1350.     puts (buf);
  1351.     curtime += tstep;
  1352.   }
  1353. }
  1354.  
  1355.  
  1356. /**********************************************************************/
  1357.  
  1358.  
  1359. /* ASCII graph mode was done by Jef Poskanzer. */
  1360. /* Generate an ASCII graph of the tides. */
  1361. #define TEXTHEIGHT 14
  1362. #define TTEXTHEIGHT 10
  1363. static char textmap[TEXTWIDTH*TEXTHEIGHT];
  1364. static void t_setchr (int x, int y, char chr) {
  1365.   if (x < 0 || y < 0 || x >= TEXTWIDTH || y >= TEXTHEIGHT)
  1366.     return;
  1367.   textmap[y*TEXTWIDTH+x] = chr;
  1368. }
  1369. static char t_getchr (int x, int y) {
  1370.   if (x < 0 || y < 0 || x >= TEXTWIDTH || y >= TEXTHEIGHT)
  1371.     return '\0';
  1372.   return textmap[y*TEXTWIDTH+x];
  1373. }
  1374. static double t_tide2wl (double tide) {
  1375.   return ((double)TTEXTHEIGHT) * (-tide) * 0.5 + (double)TTEXTHEIGHT * 0.5;
  1376. }
  1377. static double t_wl2tide (int y) {
  1378.   return -2.0*(y - TTEXTHEIGHT * 0.5) / ((double)TTEXTHEIGHT);
  1379. }
  1380. static void t_putstr (int x, int y, char *s) {
  1381.   int a;
  1382.   for (a=0;a<(int)(strlen(s));a++)
  1383.     t_setchr (x+a, y, s[a]);
  1384. }
  1385. static void t_center_text (int x, int y, char *text)
  1386. {
  1387.   int l = strlen (text);
  1388.   t_putstr (x - l / 2, y, text);
  1389. }
  1390. static void t_write_tide (int slope, int x)
  1391. {
  1392.   if (slope) {
  1393.     t_center_text (x, TTEXTHEIGHT, next_ht_date);
  1394.     t_center_text (x, TTEXTHEIGHT+1, next_ht_text);
  1395.   } else {
  1396.     t_center_text (x, TTEXTHEIGHT+2, next_ht_date);
  1397.     t_center_text (x, TTEXTHEIGHT+3, next_ht_text);
  1398.   }
  1399. }
  1400. void
  1401. tide2ascii (void)
  1402. {
  1403.   int x, y, event_type, midwl;
  1404.   time_t start = faketime, this_time = time(NULL);
  1405.  
  1406.   puts (location);
  1407.   if (mark && !iscurrent)
  1408.     printf ("Mark level:  %f\n", marklev);
  1409.  
  1410.   /* Set the background. */
  1411.   for (x=0;x<TEXTWIDTH;x++)
  1412.     for (y=0;y<TEXTHEIGHT;y++)
  1413.       t_setchr (x, y, ' ');
  1414.  
  1415.   /* Initialize the amplitude. */
  1416.   happy_new_year (yearoftimet (faketime));
  1417.  
  1418.   /* Draw day separators. */
  1419.   if (lines) {
  1420.     time_t day;
  1421.     for (day=prev_day(start);day<start+TEXTWIDTH*tstep;
  1422.                              day=increment_day(day)) {
  1423.       x = (int)((day - start) / tstep + 0.5);
  1424.       for (y=0;y<TEXTHEIGHT;y++)
  1425.         t_setchr (x, y, '|');
  1426.     }
  1427.   }
  1428.  
  1429.   /* Draw the tides. */
  1430.   midwl = (int)(t_tide2wl (-DATUM / amplitude) + 0.5);
  1431.   for (x=0;x<TEXTWIDTH;x++) {
  1432.     int yy = (int)(t_tide2wl (time2tide (faketime)) + 0.5);
  1433.     if (iscurrent) {
  1434.       if (yy < midwl)
  1435.         for (y=midwl-1;y>=yy;y--)
  1436.           t_setchr (x, y, '*');
  1437.       else
  1438.         for (y=midwl+1;y<=yy;y++)
  1439.           t_setchr (x, y, '*');
  1440.     } else {
  1441.       for (y=TTEXTHEIGHT-1;y>=yy;y--)
  1442.         t_setchr (x, y, '*');
  1443.     }
  1444.     faketime += tstep;
  1445.   }
  1446.  
  1447.   /* Extra lines */
  1448.   if (mark) {
  1449.     y = (int) (t_tide2wl ((marklev - DATUM) / amplitude) + 0.5);
  1450.     for (x=0;x<TEXTWIDTH;x++)
  1451.       if (t_getchr (x, y) == '*' || iscurrent)
  1452.         t_setchr (x, y, '-');
  1453.   }
  1454.   if (middle) {
  1455.     y = (int) (t_tide2wl (0.0) + 0.5);
  1456.     for (x=0;x<TEXTWIDTH;x++)
  1457.       if (t_getchr (x, y) == '*')
  1458.         t_setchr (x, y, '-');
  1459.   }
  1460.   if (mllw) {
  1461.     y = (int) (t_tide2wl (-DATUM / amplitude) + 0.5);
  1462.     for (x=0;x<TEXTWIDTH;x++)
  1463.       if (t_getchr (x, y) == '*')
  1464.         t_setchr (x, y, '-');
  1465.   }
  1466.  
  1467.   if (now) {
  1468.     /* X marks the spot */
  1469.     x = (int)((this_time - start) / tstep);
  1470.     y = (int) (t_tide2wl (time2tide (this_time)) + 0.5);
  1471.     t_setchr (x, y, '+');
  1472.   }
  1473.  
  1474.   /* Scrawl timestamps */
  1475.   next_ht = start;
  1476.   event_type = update_high_tide ();
  1477.   while (next_ht < start + TEXTWIDTH*tstep) {
  1478.     t_write_tide ((event_type & 2), (int)((next_ht - start) / tstep + 0.5));
  1479.     event_type = update_high_tide ();
  1480.   }
  1481.  
  1482.   /* Write output. */
  1483.   for (y=TTEXTHEIGHT;y<TTEXTHEIGHT+2;y++) {
  1484.     printf("[");
  1485.     for (x=0;x<TEXTWIDTH;x++)
  1486.       putchar(t_getchr(x,y));
  1487.     printf("]\n");
  1488.   }
  1489.   for (y=0;y<TTEXTHEIGHT;y++) {
  1490.     double temp;
  1491.     printf("[");
  1492.     for (x=0;x<TEXTWIDTH;x++)
  1493.       putchar(t_getchr(x,y));
  1494.     temp = t_wl2tide(y) * amplitude + DATUM;
  1495.     /* Chop off minus sign for super-low tides to save space */
  1496.     if (temp <= -10.0)
  1497.       temp = -temp;
  1498.     printf("]%0.1f\n", temp);
  1499.   }
  1500.   for (y=TTEXTHEIGHT+2;y<TTEXTHEIGHT+4;y++) {
  1501.     printf("[");
  1502.     for (x=0;x<TEXTWIDTH;x++)
  1503.       putchar(t_getchr(x,y));
  1504.     printf("]\n");
  1505.   }
  1506. }
  1507.  
  1508.  
  1509.  
  1510. /**********************************************************************/
  1511.  
  1512.  
  1513. /* Generate a PPM graph of the tides.  While we're at it, re-invent the
  1514.    wheel.
  1515. */
  1516. static int BGR = 135;
  1517. static int BGG = 206;
  1518. static int BGB = 235;
  1519. static int FGR = 0;
  1520. static int FGG = 0;
  1521. static int FGB = 255;
  1522. static int EBR = 46;
  1523. static int EBG = 139;
  1524. static int EBB = 87;
  1525. static int TXR = 0;
  1526. static int TXG = 0;
  1527. static int TXB = 0;
  1528. static int MKR = 255;
  1529. static int MKG = 0;
  1530. static int MKB = 0;
  1531. static int MDR = 255;
  1532. static int MDG = 255;
  1533. static int MDB = 0;
  1534. static int MLR = 255;
  1535. static int MLG = 255;
  1536. static int MLB = 255;
  1537. #define margin 0.05
  1538. unsigned char *pixmap;
  1539. /* Colors must be specified in the form rgb:hh/hh/hh, where hh is a 2-byte
  1540.    hexidecimal number. */
  1541. static void p_parsecolor (int *r, int *g, int *b, char *col) {
  1542.   if (col) {
  1543.     if (sscanf (col, "rgb:%x/%x/%x", r, g, b) != 3)
  1544.       barf (BADCOLORSPEC);
  1545.   }
  1546.   if (!((*r >= 0 && *r <= 255)&&(*g >= 0 && *g <= 255)&&
  1547.   (*b >= 0 && *b <= 255)))
  1548.     barf (BADCOLORSPEC);
  1549. }
  1550. static void p_parseallcolors ()
  1551. {
  1552.   p_parsecolor (&FGR, &FGG, &FGB, fgrise_color_arg);
  1553.   p_parsecolor (&EBR, &EBG, &EBB, fgfall_color_arg);
  1554.   p_parsecolor (&TXR, &TXG, &TXB, fgtext_color_arg);
  1555.   p_parsecolor (&MKR, &MKG, &MKB, fgmark_color_arg);
  1556.   p_parsecolor (&MLR, &MLG, &MLB, fgmllw_color_arg);
  1557.   p_parsecolor (&MDR, &MDG, &MDB, fgmiddle_color_arg);
  1558.   p_parsecolor (&BGR, &BGG, &BGB, bg_color_arg);
  1559. }
  1560. static void p_setval (int x, int y, int c, int val) {
  1561.   if (x < 0 || y < -12 || x >= PPMWIDTH || y >= PPMHEIGHT)
  1562.     return;
  1563.   pixmap[((y+12)*PPMWIDTH+x)*3+c] = (unsigned char)val;
  1564. }
  1565. static int p_getval (int x, int y, int c) {
  1566.   if (x < 0 || y < -12 || x >= PPMWIDTH || y >= PPMHEIGHT)
  1567.     return 0;
  1568.   return (int) (pixmap[((y+12)*PPMWIDTH+x)*3+c]);
  1569. }
  1570. static void p_setpix (int x, int y, int r, int g, int b) {
  1571.   p_setval (x, y, 0, r);
  1572.   p_setval (x, y, 1, g);
  1573.   p_setval (x, y, 2, b);
  1574. }
  1575. static int p_interp (int a, int b, double x) {
  1576.   return (int)((double)a + x * (double)(b-a) + 0.5);
  1577. }
  1578. static double p_tide2wl (double tide) {
  1579.   return ((double)PPMHEIGHT * (1.0 - 2.0 * margin)) * (-tide) * 0.5
  1580.   + (double)PPMHEIGHT * 0.5;
  1581. }
  1582. static double p_wl2tide (int y) {
  1583.   return -2.0*(y - PPMHEIGHT * 0.5) / ((double)PPMHEIGHT *
  1584.   (1.0 - 2.0 * margin));
  1585. }
  1586. static void p_putglyph (int x, int y, char glyph) {
  1587.   int base = (int)(glyph - ' '), xl, yl;
  1588.   assert (base >= 0 && base < 95);
  1589.   base *= 7;
  1590.   for (yl=0;yl<11;yl++)
  1591.     for (xl=0;xl<7;xl++)
  1592.       if (glyphs[yl][base+xl])
  1593.     p_setpix (x+xl, y+yl, TXR, TXG, TXB);
  1594. }
  1595. static void p_putstr (int x, int y, char *s) {
  1596.   int a;
  1597.   for (a=0;a<(int)(strlen(s));a++)
  1598.     p_putglyph (x+a*7, y, s[a]);
  1599. }
  1600. static void p_center_text (int x, int y, char *text)
  1601. {
  1602.   int l = strlen (text);
  1603.   p_putstr (x - 7 * l / 2, y, text);
  1604. }
  1605. static void p_write_high_tide (int x)
  1606. {
  1607.   p_center_text (x, 1, next_ht_date);
  1608.   p_center_text (x, 13, next_ht_text);
  1609. }
  1610. static void p_write_trans_time (int x)
  1611. {
  1612.   int bottom = PPMHEIGHT - 19;
  1613.   if (tinc)
  1614.     bottom -= 12;
  1615.   p_center_text (x, bottom-12, next_ht_date);
  1616.   p_center_text (x, bottom, next_ht_text);
  1617. }
  1618. static void p_draw_depth_lines (int *tmax, int *tmin)
  1619. {
  1620.   int a, x, y;
  1621.   if (lines) {
  1622.     *tmin = 30;
  1623.     *tmax = PPMHEIGHT - 14;
  1624.     if (mark)
  1625.       *tmax -= 24;
  1626.     if (tinc)
  1627.       *tmax -= 12;
  1628.     *tmin = (int) (p_wl2tide (*tmin) * amplitude + DATUM);
  1629.     *tmax = (int) (ceil (p_wl2tide (*tmax) * amplitude + DATUM));
  1630.     for (a=*tmax;a<=*tmin;a++) {
  1631.       if (amplitude > 30.0 && (a % 10))
  1632.         continue;
  1633.       y = (int) p_tide2wl (((double)a - DATUM) / amplitude);
  1634.       if (hinc) {
  1635.         if (!(a % hinc)) {
  1636.           char temp[5];
  1637.           int l;
  1638.           sprintf (temp, "%d", abs(a));
  1639.           l = strlen (temp);
  1640.           for (x=0;x<PPMWIDTH-7*l-2;x++)
  1641.             p_setpix (x, y, TXR, TXG, TXB);
  1642.           continue;
  1643.         }
  1644.       }
  1645.       for (x=0;x<PPMWIDTH;x++)
  1646.         p_setpix (x, y, TXR, TXG, TXB);
  1647.     }
  1648.   }
  1649. }
  1650. void
  1651. tide2ppm (char *outfile)
  1652. {
  1653.   FILE *fp;
  1654.   int x, y, event_type, a, tmax, tmin, midwl;
  1655.   time_t start = faketime, this_time = time(NULL);
  1656.  
  1657.   assert (sizeof (unsigned char) == 1);
  1658.   assert (pixmap = (unsigned char *) malloc (3 * PPMWIDTH *
  1659.     (PPMHEIGHT + 12)));
  1660.  
  1661.   p_parseallcolors ();
  1662.  
  1663.   /* Set the background. */
  1664.   for (x=0;x<PPMWIDTH;x++)
  1665.     for (y=-12;y<PPMHEIGHT;y++)
  1666.       p_setpix (x, y, BGR, BGG, BGB);
  1667.  
  1668.   /* Initialize the amplitude before drawing the depth lines. */
  1669.   happy_new_year (yearoftimet (faketime));
  1670.  
  1671.   if (!toplines)
  1672.     p_draw_depth_lines (&tmax, &tmin);
  1673.  
  1674.   /* Draw the tides. */
  1675.   midwl = (int)(p_tide2wl (-DATUM / amplitude));
  1676.   for (x=0;x<PPMWIDTH;x++) {
  1677.     double i, yy = p_tide2wl (time2tide (faketime));
  1678.     if (iscurrent) {
  1679.       if ((int)yy < midwl) {
  1680.         for (y=midwl-1;y>(int)yy;y--)
  1681.         p_setpix (x, y, FGR, FGG, FGB);
  1682.         /* Anti-aliasing */
  1683.         i = yy - floor (yy);
  1684.         p_setval (x, (int)yy, 0, p_interp (FGR, p_getval (x, (int)yy, 0), i));
  1685.         p_setval (x, (int)yy, 1, p_interp (FGG, p_getval (x, (int)yy, 1), i));
  1686.         p_setval (x, (int)yy, 2, p_interp (FGB, p_getval (x, (int)yy, 2), i));
  1687.       } else {
  1688.         for (y=midwl+1;y<=(int)yy;y++)
  1689.         p_setpix (x, y, EBR, EBG, EBB);
  1690.         /* Anti-aliasing */
  1691.         i = yy - floor (yy);
  1692.         p_setval (x, (int)yy+1, 0, p_interp (p_getval (x, (int)yy+1, 0), EBR,
  1693.         i));
  1694.         p_setval (x, (int)yy+1, 1, p_interp (p_getval (x, (int)yy+1, 1), EBG,
  1695.         i));
  1696.         p_setval (x, (int)yy+1, 2, p_interp (p_getval (x, (int)yy+1, 2), EBB,
  1697.         i));
  1698.       }
  1699.     } else {
  1700.       for (y=PPMHEIGHT-1;y>(int)yy;y--)
  1701.     p_setpix (x, y, FGR, FGG, FGB);
  1702.       /* Anti-aliasing */
  1703.       i = yy - floor (yy);
  1704.       p_setval (x, (int)yy, 0, p_interp (FGR, p_getval (x, (int)yy, 0), i));
  1705.       p_setval (x, (int)yy, 1, p_interp (FGG, p_getval (x, (int)yy, 1), i));
  1706.       p_setval (x, (int)yy, 2, p_interp (FGB, p_getval (x, (int)yy, 2), i));
  1707.     }
  1708.     faketime += tstep;
  1709.   }
  1710.  
  1711.   if (toplines)
  1712.     p_draw_depth_lines (&tmax, &tmin);
  1713.  
  1714.   /* Label unit lines */
  1715.   if (hinc) {
  1716.     for (a=tmax;a<=tmin;a++) {
  1717.       if (!(a % hinc)) {
  1718.         char temp[5];
  1719.         int l;
  1720.         y = (int) p_tide2wl (((double)a - DATUM) / amplitude);
  1721.         sprintf (temp, "%d", abs(a));
  1722.         l = strlen (temp);
  1723.         p_putstr (PPMWIDTH - 7 * l, y-4, temp);
  1724.       }
  1725.     }
  1726.   }
  1727.  
  1728.   /* Extra lines */
  1729.   if (mark) {
  1730.     y = (int) p_tide2wl ((marklev - DATUM) / amplitude);
  1731.     for (x=0;x<PPMWIDTH;x++)
  1732.       p_setpix (x, y, MKR, MKG, MKB);
  1733.   }
  1734.   if (middle) {
  1735.     y = (int) p_tide2wl (0.0);
  1736.     for (x=0;x<PPMWIDTH;x++)
  1737.       p_setpix (x, y, MDR, MDG, MDB);
  1738.   }
  1739.   if (mllw) {
  1740.     y = (int) p_tide2wl (-DATUM / amplitude);
  1741.     for (x=0;x<PPMWIDTH;x++)
  1742.       p_setpix (x, y, MLR, MLG, MLB);
  1743.   }
  1744.  
  1745.   /* Draw tick marks */
  1746.   if (lines) {
  1747.     time_t hour, prevday;
  1748.     prevday = prev_day (start);
  1749.     for (hour=prev_hour(start);hour<=start+PPMWIDTH*tstep+(long)HOURSECONDS;
  1750.     hour+=(long)HOURSECONDS) {
  1751.       x = (hour - start) / tstep;
  1752.       for (y=PPMHEIGHT-1;y>PPMHEIGHT-8;y--)
  1753.         p_setpix (x, y, TXR, TXG, TXB);
  1754.       if (tinc) {
  1755.         if (!(((hour - prevday) / 3600) % tinc)) {
  1756.           char buf[13];
  1757.           do_timestamp (buf, tmtime (hour));
  1758.           if (buf[1] == ':')
  1759.             buf[1] = '\0';
  1760.           else
  1761.             buf[2] = '\0';
  1762.           p_center_text (x, PPMHEIGHT-19, buf);
  1763.         }
  1764.       }
  1765.     }
  1766.     for (hour=prevday;hour<=start+PPMWIDTH*tstep+(long)HOURSECONDS;
  1767.                               hour=increment_day(hour)) {
  1768.       x = (hour - start) / tstep;
  1769.       for (y=PPMHEIGHT-1;y>PPMHEIGHT-8;y--) {
  1770.         p_setpix (x-1, y, TXR, TXG, TXB);
  1771.         p_setpix (x+1, y, TXR, TXG, TXB);
  1772.       }
  1773.     }
  1774.   }
  1775.  
  1776.   if (now) {
  1777.     int looper;
  1778.     /* X marks the spot */
  1779.     x = (int)((this_time - start) / tstep);
  1780.     y = (int) p_tide2wl (time2tide (this_time));
  1781.     for (looper=-4;looper<=4;looper++) {
  1782.       p_setpix (x+looper, y, TXR, TXG, TXB);
  1783.       p_setpix (x, y+looper, TXR, TXG, TXB);
  1784.     }
  1785.   }
  1786.  
  1787.   /* Scrawl timestamps */
  1788.   next_ht = start;
  1789.   event_type = update_high_tide ();
  1790.   while (next_ht < start + PPMWIDTH*tstep) {
  1791.     x = (int)((next_ht - start) / tstep);
  1792.     if (event_type & 3)
  1793.       p_write_high_tide (x);
  1794.     if (event_type & 12) {
  1795.       p_write_trans_time (x);
  1796.       for (y=PPMHEIGHT-1;y>=PPMHEIGHT-7;y--)
  1797.         p_setpix (x, y, MKR, MKG, MKB);
  1798.     }
  1799.     event_type = update_high_tide ();
  1800.   }
  1801.  
  1802.   /* Identify the pixmap */
  1803.   p_center_text (PPMWIDTH>>1, -11, location);
  1804.  
  1805.   /* Write output. */
  1806.   if (strcmp (outfile, "-")) {
  1807. #ifdef OS2
  1808.     if (!(fp = fopen (outfile, "wb")))
  1809. #else
  1810.     if (!(fp = fopen (outfile, "w")))
  1811. #endif
  1812.       barf (CANTOPENFILE);
  1813.   }
  1814.   else
  1815.     fp = stdout;
  1816.   fprintf (fp, "P6\n%d %d\n255\n", PPMWIDTH, PPMHEIGHT+12);
  1817.   fwrite (pixmap, 3, PPMWIDTH*(PPMHEIGHT+12), fp);
  1818.   fclose (fp);
  1819.   free (pixmap);
  1820. }
  1821.  
  1822. //Following routines are exclusively for Mac
  1823.  
  1824. short NumToolboxTraps( void )
  1825. {
  1826.     if (NGetTrapAddress(_InitGraf, ToolTrap) ==
  1827.             NGetTrapAddress(0xAA6E, ToolTrap))
  1828.         return(0x0200);
  1829.     else
  1830.         return(0x0400);
  1831. }
  1832.  
  1833. TrapType GetTrapType(short theTrap)
  1834. {
  1835.  
  1836.     if ((theTrap & TrapMask) > 0)
  1837.         return(ToolTrap);
  1838.     else
  1839.         return(OSTrap);
  1840.  
  1841. }
  1842.  
  1843.  
  1844. Boolean TrapAvailable(short theTrap)
  1845. {
  1846.  
  1847.     TrapType    tType;
  1848.  
  1849.     tType = GetTrapType(theTrap);
  1850.     if (tType == ToolTrap)
  1851.     theTrap = theTrap & 0x07FF;
  1852.     if (theTrap >= NumToolboxTraps())
  1853.         theTrap = _Unimplemented;
  1854.  
  1855.     return (NGetTrapAddress(theTrap, tType) !=
  1856.             NGetTrapAddress(_Unimplemented, ToolTrap));
  1857. }
  1858.  
  1859.  
  1860. Boolean WNEAvailable(void)
  1861. {
  1862.     return TrapAvailable(_WaitNextEvent);
  1863. }
  1864.  
  1865.  
  1866. void FindIfHasFPU(Boolean answer)
  1867. {
  1868. long    response;
  1869.  
  1870. if (TrapAvailable(_Gestalt)){
  1871.  
  1872.     assert(!Gestalt(gestaltFPUType, &response));
  1873.     if (response == gestaltNoFPU) answer=FALSE;
  1874.         else answer=TRUE;
  1875.     } else {
  1876.         answer=FALSE;
  1877.     }
  1878. }
  1879.  
  1880. void FindTimeZone(void)
  1881. {
  1882.   MachineLocation    loc;
  1883.  
  1884.   ReadLocation(&loc);
  1885.   gTimezone = (short)(loc.gmtFlags.gmtDelta & 0x00FFFFFF);
  1886. }