home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / Apps / Astro / astrolog / Source / general.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-12  |  26.4 KB  |  964 lines

  1. /*
  2. ** Astrolog (Version 4.10) File: general.c
  3. **
  4. ** IMPORTANT NOTICE: the graphics database and chart display routines
  5. ** used in this program are Copyright (C) 1991-1994 by Walter D. Pullen
  6. ** (cruiser1@stein.u.washington.edu). Permission is granted to freely
  7. ** use and distribute these routines provided one doesn't sell,
  8. ** restrict, or profit from them in any way. Modification is allowed
  9. ** provided these notices remain with any altered or edited versions of
  10. ** the program.
  11. **
  12. ** The main planetary calculation routines used in this program have
  13. ** been Copyrighted and the core of this program is basically a
  14. ** conversion to C of the routines created by James Neely as listed in
  15. ** Michael Erlewine's 'Manual of Computer Programming for Astrologers',
  16. ** available from Matrix Software. The copyright gives us permission to
  17. ** use the routines for personal use but not to sell them or profit from
  18. ** them in any way.
  19. **
  20. ** The PostScript code within the core graphics routines are programmed
  21. ** and Copyright (C) 1992-1993 by Brian D. Willoughby
  22. ** (brianw@sounds.wa.com). Conditions are identical to those above.
  23. **
  24. ** The extended accurate ephemeris databases and formulas are from the
  25. ** calculation routines in the program "Placalc" and are programmed and
  26. ** Copyright (C) 1989,1991,1993 by Astrodienst AG and Alois Treindl
  27. ** (alois@azur.ch). The use of that source code is subject to
  28. ** regulations made by Astrodienst Zurich, and the code is not in the
  29. ** public domain. This copyright notice must not be changed or removed
  30. ** by any user of this program.
  31. **
  32. ** Initial programming 8/28,30, 9/10,13,16,20,23, 10/3,6,7, 11/7,10,21/1991.
  33. ** X Window graphics initially programmed 10/23-29/1991.
  34. ** PostScript graphics initially programmed 11/29-30/1992.
  35. ** Last code change made 3/19/1994.
  36. */
  37.  
  38. #include "astrolog.h"
  39.  
  40.  
  41. /*
  42. ******************************************************************************
  43. ** General Functions.
  44. ******************************************************************************
  45. */
  46.  
  47. /* Swap two real floating point values. */
  48.  
  49. void SwapReal(d1, d2)
  50. real *d1, *d2;
  51. {
  52.   real temp;
  53.  
  54.   temp = *d1; *d1 = *d2; *d2 = temp;
  55. }
  56.  
  57.  
  58. /* Return the length of a string. */
  59.  
  60. int StringLen(line)
  61. char *line;
  62. {
  63.   int i;
  64.  
  65.   for (i = 0; *line++; i++)
  66.     ;
  67.   return i;
  68. }
  69.  
  70.  
  71. /* Return whether one string is greater than another. */
  72.  
  73. int StringCmp(s1, s2)
  74. char *s1, *s2;
  75. {
  76.   while (*s1 && *s1 == *s2)
  77.     s1++, s2++;
  78.   return *s1 - *s2;
  79. }
  80.  
  81.  
  82. /* Determine the sign of a number: -1 if value negative, +1 if value */
  83. /* positive, and 0 if it's zero.                                     */
  84.  
  85. real Sgn(d)
  86. real d;
  87. {
  88.   return d == 0.0 ? 0.0 : SGN2(d);
  89. }
  90.  
  91.  
  92. /* Given an x and y coordinate, return the angle formed by a line from the */
  93. /* origin to this coordinate. This is just converting from rectangular to  */
  94. /* polar coordinates; however, we don't determine the radius here.         */
  95.  
  96. real Angle(x, y)
  97. real x, y;
  98. {
  99.   real a;
  100.  
  101.   if (x != 0.0)
  102.     a = ATAN(y/x);
  103.   else
  104.     a = Sgn(y)*PI/2.0;
  105.   if (a < 0.0)
  106.     a += PI;
  107.   if (y < 0.0)
  108.     a += PI;
  109.   return a;
  110. }
  111.  
  112.  
  113. /* Modulus function for floating point values. The modulus value itself */
  114. /* has been specified earlier: it is usually either 360.0 or PI/2.0.    */
  115.  
  116. real Mod(d)
  117. real d;
  118. {
  119.   if (d >= modulus)        /* In most cases, our value is only slightly */
  120.     d -= modulus;          /* out of range, so we can test for it and   */
  121.   else if (d < 0.0)        /* avoid the more complicated arithmetic.    */
  122.     d += modulus;
  123.   if (d >= 0 && d < modulus)
  124.     return d;
  125.   return (d - floor(d/modulus)*modulus);
  126. }
  127.  
  128.  
  129. /* Integer division - like the "/" operator but always rounds result down. */
  130.  
  131. long Dvd(x, y)
  132. long x, y;
  133. {
  134.   long z;
  135.  
  136.   if (y == 0)
  137.     return x;
  138.   z = x / y;
  139.   if (((x >= 0) == (y >= 0)) || x-z*y == 0)
  140.     return z;
  141.   return z - 1;
  142. }
  143.  
  144.  
  145. /*
  146. ******************************************************************************
  147. ** General Astrology Functions.
  148. ******************************************************************************
  149. */
  150.  
  151. /* A similar modulus function: convert an integer to value from 1..12. */
  152.  
  153. int Mod12(i)
  154. int i;
  155. {
  156.   while (i > SIGNS)
  157.     i -= SIGNS;
  158.   while (i < 1)
  159.     i += SIGNS;
  160.   return i;
  161. }
  162.  
  163.  
  164. /* Convert an inputed fractional degrees/minutes value to a true decimal   */
  165. /* degree quantity. For example, the user enters the decimal value "10.30" */
  166. /* to mean 10 degrees and 30 minutes; this will return 10.5, i.e. 10       */
  167. /* degrees and 30 minutes expressed as a floating point degree value.      */
  168.  
  169. real DecToDeg(d)
  170. real d;
  171. {
  172.   return Sgn(d)*(floor(dabs(d))+FRACT(dabs(d))*100.0/60.0);
  173. }
  174.  
  175.  
  176. /* This is the inverse of the above function. Given a true decimal value */
  177. /* for a zodiac degree, adjust it so the degrees are in the integer part */
  178. /* and the minute expressed as hundredths, e.g. 10.5 degrees -> 10.30    */
  179.  
  180. real DegToDec(d)
  181. real d;
  182. {
  183.   return Sgn(d)*(floor(dabs(d))+FRACT(dabs(d))*60.0/100.0);
  184. }
  185.  
  186.  
  187. /* Return the shortest distance between two degrees in the zodiac. This is  */
  188. /* normally their difference, but we have to check if near the Aries point. */
  189.  
  190. real MinDistance(deg1, deg2)
  191. real deg1, deg2;
  192. {
  193.   real i;
  194.  
  195.   i = dabs(deg1-deg2);
  196.   return i < DEGHALF ? i : DEGREES - i;
  197. }
  198.  
  199.  
  200. /* This is just like the above routine, except the min distance value  */
  201. /* returned will either be positive or negative based on whether the   */
  202. /* second value is ahead or behind the first one in a circular zodiac. */
  203.  
  204. real MinDifference(deg1, deg2)
  205. real deg1, deg2;
  206. {
  207.   real i;
  208.  
  209.   i = deg2 - deg1;
  210.   if (dabs(i) < DEGHALF)
  211.     return i;
  212.   return Sgn(i)*(dabs(i) - DEGREES);
  213. }
  214.  
  215.  
  216. /* Return the degree of the midpoint between two zodiac positions, making */
  217. /* sure we return the true midpoint closest to the positions in question. */
  218.  
  219. real Midpoint(deg1, deg2)
  220. real deg1, deg2;
  221. {
  222.   real mid;
  223.  
  224.   mid = (deg1+deg2)/2.0;
  225.   return MinDistance(deg1, mid) < DEGQUAD ? mid : Mod(mid+DEGHALF);
  226. }
  227.  
  228.  
  229. /* Given a planet and sign, determine whether: The planet rules the sign, */
  230. /* the planet has its fall in the sign, the planet exalts in the sign, or */
  231. /* is debilitated in the sign; and return an appropriate character.       */
  232.  
  233. char Dignify(body, sign)
  234. int body, sign;
  235. {
  236.   if (body > U_HI)
  237.     return ' ';
  238.   if (ruler1[body] == sign || ruler2[body] == sign)
  239.     return 'R';
  240.   if (ruler1[body] == Mod12(sign+6) || ruler2[body] == Mod12(sign+6))
  241.     return 'F';
  242.   if (exalt[body] == sign)
  243.     return 'e';
  244.   if (exalt[body] == Mod12(sign+6))
  245.     return 'd';
  246.   return '-';
  247. }
  248.  
  249.  
  250. /* Determine the number of days in a particular month. The year is needed, */
  251. /* too, because we have to check for leap years in the case of February.   */
  252.  
  253. int DayInMonth(month, year)
  254. int month, year;
  255. {
  256.   int d;
  257.  
  258.   if (month == _SEP || month == _APR || month == _JUN || month == _NOV)
  259.     d = 30;
  260.   else if (month != _FEB)
  261.     d = 31;
  262.   else {
  263.     d = 28;
  264.     if (year % 4 == 0 &&
  265.       (year % 100 != 0 || year % 400 == 0 || year <= G2JYEA))
  266.       d++;
  267.   }
  268.   return d;
  269. }
  270.  
  271.  
  272. /* Return the actual number of days in a particular month. Normally, this  */
  273. /* is the same as the above routine which determines the index of the last */
  274. /* day of the month, but the values can differ when changing between       */
  275. /* calendar systems (Julian to Gregorian) in which one can jump over days. */
  276.  
  277. int DaysInMonth(month, year)
  278. int month, year;
  279. {
  280.   int d;
  281.  
  282.   d = DayInMonth(month, year);
  283.   if (year == G2JYEA && month == G2JMON)
  284.     d -= (G2JDAY2 - G2JDAY1 - 1);
  285.   return d;
  286. }
  287.  
  288.  
  289. /* Return the day of the week (Sunday is 0) of the specified given date. */
  290.  
  291. int DayOfWeek(month, day, year)
  292. int month, day, year;
  293. {
  294.   int d;
  295.  
  296.   d = (int)((MdyToJulian(month, day, year) + 1) % 7);
  297.   return d < 0 ? d+7 : d;
  298. }
  299.  
  300.  
  301. /* Given a day, and the month and year it falls in, add a number of days    */
  302. /* to it and return the new day index. As month changes are not checked for */
  303. /* here, this is mostly just adding the offset to the day; however we need  */
  304. /* to check for calendar changes for when days in a month may be skipped.   */
  305.  
  306. /* We do not check for month crossings. only for G2J. */
  307.  
  308. int AddDay(month, day, year, delta)
  309. int month, day, year, delta;
  310. {
  311.   int d;
  312.  
  313.   d = day + delta;
  314.   if (year == G2JYEA && month == G2JMON) {
  315.     if (d > G2JDAY1 && d < G2JDAY2)
  316.       d += SGN(delta)*(G2JDAY2-G2JDAY1-1);
  317.   }
  318.   return d;
  319. }
  320.  
  321.  
  322. /* Given an aspect and two objects making that aspect with each other,   */
  323. /* return the maximum orb allowed for such an aspect. Normally this only */
  324. /* depends on the aspect itself, but some objects require narrow orbs,   */
  325. /* and some allow wider orbs, so check for these cases.                  */
  326.  
  327. real Orb(body1, body2, aspect)
  328. int body1, body2, aspect;
  329. {
  330.   real orb, i;
  331.  
  332.   orb = aspectorb[aspect];
  333.   i = body1 > BASE ? 2.0 : planetorb[body1];
  334.   orb = MIN(orb, i);
  335.   i = body2 > BASE ? 2.0 : planetorb[body2];
  336.   orb = MIN(orb, i);
  337.   if (body1 <= BASE)
  338.     orb += planetadd[body1];
  339.   if (body2 <= BASE)
  340.     orb += planetadd[body2];
  341.   return orb;
  342. }
  343.  
  344.  
  345. /*
  346. ******************************************************************************
  347. ** File IO Routines.
  348. ******************************************************************************
  349. */
  350.  
  351. /* Exit the program, and do any cleanup necessary. Note that if we had     */
  352. /* a non-fatal error, and we are in the -Q loop mode, then we won't        */
  353. /* actually terminate the program, but drop back to the command line loop. */
  354.  
  355. void Terminate(value)
  356. int value;
  357. {
  358.   if (value == _FORCE) {
  359.     AnsiColor(WHITE);
  360.     fprintf(stdout, "\n%s terminated.\n", appname);
  361.   }
  362.   if (value == _ERROR && (operation & DASHQ) > 0)
  363.     return;
  364.   if (ansi)
  365.     fprintf(S, "%c[0m", ESCAPE);    /* Get out of any Ansi color mode. */
  366.   exit(abs(value));
  367. }
  368.  
  369.  
  370. /* Print a warning message given a string. This is called in non-fatal  */
  371. /* cases where we return to normal execution after printing the string. */
  372.  
  373. void PrintWarning(string)
  374. char *string;
  375. {
  376.   AnsiColor(RED);
  377.   fprintf(stderr, "%s\n", string);
  378.   AnsiColor(DEFAULT); 
  379. }
  380.  
  381.  
  382. /* Print an error message. This is called in more serious cases which halt */
  383. /* running of the current chart sequence, which can terminate the program  */
  384. /* but isn't a fatal error in that we can still fall back to the -Q loop.  */
  385.  
  386. void PrintError(string)
  387. char *string;
  388. {
  389.   AnsiColor(RED);
  390.   fprintf(stderr, "%s: %s\n", appname, string);
  391.   Terminate(_ERROR);
  392.   AnsiColor(DEFAULT); 
  393. }
  394.  
  395.  
  396. /* Simplification for a commonly printed error message. */
  397.  
  398. void TooFew(option)
  399. char *option;
  400. {
  401.   char string[STRING];
  402.  
  403.   sprintf(string, "Too few options to switch -%s", option);
  404.   PrintError(string);
  405. }
  406.  
  407.  
  408. /* Another simplification for a commonly printed error message. */
  409.  
  410. void BadVal(option, value)
  411. char *option;
  412. int value;
  413. {
  414.   char string[STRING];
  415.  
  416.   sprintf(string, "Value %d passed to switch -%s out of range.\n",
  417.     value, option);
  418.   PrintError(string);
  419. }
  420.  
  421.  
  422. /* A simple procedure used throughout Astrolog: Print a particular */
  423. /* character on the screen 'n' times.                              */
  424.  
  425. void PrintTab(chr, count)
  426. char chr;
  427. int count;
  428. {
  429.   int i;
  430.  
  431.   for (i = 0; i < count; i++)
  432.     printc(chr);
  433. }
  434.  
  435.  
  436. /* Set an Ansi text color. */
  437.  
  438. void AnsiColor(col)
  439. int col;
  440. {
  441.   /* Special case: If we are passed the value REVERSE, and ansi is not    */
  442.   /* only on but set to a value > 1, then we'll enter reverse video mode. */
  443.  
  444.   if (!ansi || (col == REVERSE && ansi < 2))
  445.     return;
  446.   fprintf(S, "%c[", ESCAPE);
  447.   if (col == DEFAULT)
  448.     printc('0');
  449.   else if (col == REVERSE) {
  450.     printc('7');
  451.   } else
  452.     fprintf(S, "%c;%d", col > 7 ? '1' : '0', 30 + (col & 7));
  453.   printc('m');
  454. }
  455.  
  456.  
  457. /* Print a zodiac position on the screen. This basically just prints the  */
  458. /* string returned from CharZodiac() below, except we take care of color. */
  459.  
  460. void PrintZodiac(deg)
  461. real deg;
  462. {
  463.   AnsiColor(elemansi[(int) (deg / 30.0) & 3]);
  464.   fprintf(S, "%s", CharZodiac(deg));
  465.   AnsiColor(DEFAULT);
  466. }
  467.  
  468.  
  469. /* Given a zodiac position, return a string containing it as it's */
  470. /* formatted for display to the user.                             */
  471.  
  472. char *CharZodiac(deg)
  473. real deg;
  474. {
  475.   static char zod[11];
  476.   int sign, d, m;
  477.   real s;
  478.  
  479.   if (!(operation & DASHs0)) {
  480.  
  481.     /* Normally, we format the position in degrees/sign/minutes format: */
  482.  
  483.     deg = Mod(deg + (seconds < 0 ? 1.0/60.0/60.0/2.0 : 1.0/60.0/2.0));
  484.     sign = (int) (deg / 30.0);
  485.     d = (int) deg - sign*30;
  486.     m = (int) (FRACT(deg)*60.0);
  487.     sprintf(zod, "%2d%c%c%c%02d", d, SIGNAM(sign + 1), m);
  488.     if (seconds < 0) {
  489.       s = FRACT(deg)*60.0; s = FRACT(s)*60.0;
  490.       sprintf(zod, "%s'%02d\"", zod, (int)s);
  491.     }
  492.   } else {
  493.  
  494.     /* However, if -s0 switch in effect, get position in hours/minutes: */
  495.  
  496.     deg = Mod(deg + (seconds < 0 ? 1.0/4.0/60.0/2.0 : 1.0/4.0/2.0));
  497.     d = (int) (deg / 15.0);
  498.     m = (int) ((deg - (real)d*15.0)*60.0/24.0);
  499.     sprintf(zod, "%2dh,%02dm", d, m);
  500.     if (seconds < 0) {
  501.       s = FRACT(deg)*4.0; s = FRACT(s)*60.0;
  502.       sprintf(zod, "%s,%02ds", zod, (int)s);
  503.     }
  504.   }
  505.   return zod;
  506. }
  507.  
  508.  
  509. /* This is similar to formatting a zodiac degree, but here we return a */
  510. /* string of a (signed) declination value in degrees and minutes.      */
  511.  
  512. char *CharAltitude(deg)
  513. real deg;
  514. {
  515.   static char alt[8];
  516.   int d, m, s;
  517.  
  518.   while (deg > DEGQUAD)    /* Ensure declination is from -90..+90 degrees. */
  519.     deg -= DEGHALF;
  520.   while (deg < -DEGQUAD)
  521.     deg += DEGHALF;
  522.   s = deg < 0.0;
  523.   deg = dabs(deg) + 1.0/60.0/2.0;
  524.   d = (int) deg;
  525.   m = (int) (FRACT(deg)*60.0);
  526.   sprintf(alt, "%c%2d%c%02d'", s ? '-' : '+', d, DEGR1, m);
  527.   return alt;
  528. }
  529.  
  530.  
  531. /* Another string formatter, here we return a date string given a month,    */
  532. /* day, and year. We format with the day or month first based on whether    */
  533. /* the "European" date variable is set or not. The routine also takes a     */
  534. /* parameter to indicate how much the string should be abbreviated, if any. */
  535.  
  536. char *CharDate(mon, day, yea, full)
  537. int mon, day, yea, full;
  538. {
  539.   static char dat[20];
  540.  
  541.   if (full > FALSE) {
  542.     if (eurodate) {
  543.       if (full > TRUE)
  544.         sprintf(dat, "%2d %c%c%c%5d", day, MONNAM(mon), yea);
  545.       else
  546.         sprintf(dat, "%d %s %d", day, monthname[mon], yea);
  547.     } else {
  548.       if (full > TRUE)
  549.         sprintf(dat, "%c%c%c %2d%5d", MONNAM(mon), day, yea);
  550.       else
  551.         sprintf(dat, "%s %d, %d", monthname[mon], day, yea);
  552.     }
  553.   } else {
  554.     if (eurodate) {
  555.       if (full)
  556.         sprintf(dat, "%2d-%2d-%2d", day, mon, yea%100);
  557.       else
  558.         sprintf(dat, "%2d-%2d-%4d", day, mon, yea);
  559.     } else {
  560.       if (full)
  561.         sprintf(dat, "%2d/%2d/%2d", mon, day, yea%100);
  562.       else
  563.         sprintf(dat, "%2d/%2d/%4d", mon, day, yea);
  564.     }
  565.   }
  566.   return dat;
  567. }
  568.  
  569.  
  570. /* Return a string containing the given time expressed as an hour and */
  571. /* minute quantity. This is formatted in 24 hour or am/pm time based  */
  572. /* on whether the "European" time format flag is set or not.          */
  573.  
  574. char *CharTime(hr, min)
  575. int hr, min;
  576. {
  577.   static char tim[8];
  578.  
  579.   if (eurotime)
  580.     sprintf(tim, "%2d:%02d", hr, min);
  581.   else
  582.     sprintf(tim, "%2d:%02d%cm", Mod12(hr), min, hr < 12 ? 'a' : 'p');
  583.   return tim;
  584. }
  585.  
  586.  
  587. /* Return a string containing the given time zone, given as a real value     */
  588. /* having the hours before GMT in the integer part and minutes fractionally. */
  589.  
  590. char *CharZone(zon)
  591. real zon;
  592. {
  593.   static char tim[7];
  594.  
  595.   sprintf(tim, "%c%d:%02d", Zon > 0.0 ? '-' : '+', (int)dabs(Zon),
  596.     (int)(FRACT(dabs(Zon))*100.0+ROUND/60.0));
  597.   return tim;
  598. }
  599.  
  600.  
  601. /* Nicely format the given longitude and latitude locations and return    */
  602. /* them in a string. Various parts of the program display a chart header, */
  603. /* and this allows the similar computations to be coded only once.        */
  604.  
  605. char *CharLocation(lon, lat, norm)
  606. real lon, lat, norm;
  607. {
  608.   static char loc[14];
  609.   int i, j;
  610.  
  611.   i = (int) (FRACT(dabs(lon))*norm+ROUND);
  612.   j = (int) (FRACT(dabs(lat))*norm+ROUND);
  613.   sprintf(loc, "%3.0f%c%02d%c%3.0f%c%02d%c",
  614.     floor(dabs(lon)), DEGR1, i, lon < 0.0 ? 'E' : 'W',
  615.     floor(dabs(lat)), DEGR1, j, lat < 0.0 ? 'S' : 'N');
  616.   return loc;
  617. }
  618.  
  619.  
  620. #ifdef TIME
  621. /* Compute the date and time it is right now as the program is running      */
  622. /* using the computer's internal clock. We do this by getting the number    */
  623. /* of seconds which have passed since January 1, 1970 and going from there. */
  624. /* The time return value filled is expressed in the given zone parameter.   */
  625.  
  626. void GetTimeNow(Mon, Day, Yea, Tim, Zon)
  627. int *Mon, *Day, *Yea;
  628. real *Tim, Zon;
  629. {
  630.   dword curtimer;
  631.   int min, sec;
  632.   real hr;
  633.  
  634.   time(&curtimer);
  635.   sec = (int) (curtimer % 60);
  636.   curtimer /= 60;
  637.   min = (int) (curtimer % 60);
  638.   curtimer /= 60;
  639.   hr = (real) (curtimer % 24) - Zon;
  640.   curtimer /= 24;
  641.   while (hr < 0.0) {
  642.     curtimer--;
  643.     hr += 24.0;
  644.   }
  645.   while (hr >= 24.0) {
  646.     curtimer++;
  647.     hr -= 24.0;
  648.   }
  649. #ifdef PC
  650.   curtimer += 2415020L;  /* Number of days between 1/1/1970 and 1/1/4713 BC. */
  651. #else
  652.   curtimer += 2440588L;  /* Number of days in 70 years different than above. */
  653. #endif
  654.   JulianToMdy((real)curtimer, Mon, Day, Yea);
  655.   *Tim = hr + (real) min / 100.0 + (real) sec / 6000.0;
  656. }
  657. #endif
  658.  
  659.  
  660. /* Stop and wait for the user to enter a line of text given a prompt to */
  661. /* display and a string buffer to fill with it.                         */
  662.  
  663. void InputString(prompt, string)
  664. char *prompt, *string;
  665. {
  666.   FILE *data;
  667.  
  668.   data = S; S = stdout;
  669.   fprintf(S, "%s", prompt);
  670.   AnsiColor(YELLOW);
  671.   fprintf(S, " > ");
  672.   AnsiColor(DEFAULT);
  673.   if (gets(string) == NULL)    /* Pressing control-D will terminate the */
  674.     Terminate(_FORCE);         /* program (at least on some machines.)  */
  675.   S = data;
  676. }
  677.  
  678.  
  679. /* Prompt the user for a floating point value, and make sure it conforms  */
  680. /* to the specified bounds before returning it. If a non-numeric value is */
  681. /* entered, then assume it's the name of a month, and try to convert it   */
  682. /* to the appropriate number from 1 to 12; and also check for an "AM" or  */
  683. /* "PM" suffix to hour values, and adjust the number appropriately.       */
  684.  
  685. real Input(prompt, low, high)
  686. char *prompt;
  687. real low, high;
  688. {
  689.   char line[STRING], c;
  690.   real x;
  691.   int i, j;
  692.  
  693.   loop {
  694.     InputString(prompt, line);
  695.     i = StringLen(line);
  696.     for (j = 0; j < i; j++)
  697.       if (line[j] == ':')      /* Convert all colons in the */
  698.         line[j] = '.';         /* entered line to periods.  */
  699.     c = CAP(line[0]);
  700.  
  701.     /* If they entered a string, then check to see if it's a month name. */
  702.  
  703.     if (c >= 'A' && c <= 'Z') {
  704.       switch (c) {
  705.       case 'J': x = CAP(line[1]) == 'U' ?                   /* January,     */
  706.         (CAP(line[2]) == 'L' ? 7.0 : 6.0) : 1.0; break;     /* June,July    */
  707.       case 'F': x = 2.0; break;                             /* February     */
  708.       case 'M': x = CAP(line[2]) == 'Y' ? 5.0 : 3.0; break; /* March,May    */
  709.       case 'A': x = CAP(line[1]) == 'U' ? 8.0 : 4.0; break; /* April,August */
  710.       case 'S': x = 9.0; break;                             /* September    */
  711.       case 'O': x = 10.0; break;                            /* October      */
  712.       case 'N': x = 11.0; break;                            /* November     */
  713.       case 'D': x = 12.0; break;                            /* December     */
  714.       default: x = 0.0;
  715.       }
  716.     } else {
  717.       sscanf(line, "%lf", &x);    /* Convert entered line to number. */
  718.       i = StringLen(line)-1;
  719.       if (i > 0 && CAP(line[i]) == 'M')
  720.         i--;
  721.       if (i > 0) {
  722.         c = CAP(line[i]);
  723.         if (c == 'A')                    /* Adjust value appropriately */
  724.           x = x >= 12.0 ? x-12.0 : x;    /* if 'AM' or 'PM' suffix.    */
  725.         else if (c == 'P')
  726.           x = x >= 12.0 ? x : x+12.0;
  727.       }
  728.     }
  729.     if (x >= low && x <= high)
  730.       return x;
  731.     sprintf(line, "Value out of range of from %.0f to %.0f.", low, high);
  732.     PrintWarning(line);
  733.   }
  734. }
  735.  
  736.  
  737. /* Given a string representing the complete pathname to a file, strip off    */
  738. /* all the path information leaving just the filename itself. This is called */
  739. /* by the main program to determine the name of the Astrolog executable.     */
  740.  
  741. char *ProcessProgname(name)
  742. char *name;
  743. {
  744.   char *b, *c, *e;
  745.  
  746.   b = c = name;
  747.   while (*c) {
  748. #ifdef PC
  749.     *c = UNCAP(*c);    /* Because DOS filenames are case insensitive. */
  750. #endif
  751.     c++;
  752.   }
  753.   e = c;
  754.   while (c > b && *c != '.')
  755.     c--;
  756.   if (c > b)
  757.     *c = 0;
  758.   else
  759.     c = e;
  760.   while (c > b && *c != DIR_SEP)
  761.     c--;
  762.   if (c > b)
  763.     name = c+1;
  764.   return name;
  765. }
  766.  
  767.  
  768. /* This important procedure gets all the parameters defining the chart that  */
  769. /* will be worked with later. Given a "filename", it gets from it all the    */
  770. /* pertinent chart information. This is more than just reading from a file - */
  771. /* the procedure also takes care of the cases of prompting the user for the  */
  772. /* information and using the time functions to determine the date now - the  */
  773. /* program considers these cases "virtual" files. Furthermore, when reading  */
  774. /* from a real file, we have to check if it was written in the -o0 format.   */
  775.  
  776. bool InputData(filename)
  777. char *filename;
  778. {
  779.   FILE *data;
  780.   char name[STRING], c;
  781.   int i;
  782.   real k, l, m;
  783.  
  784.   /* If we are to read from the virtual file "set" then that means use a   */
  785.   /* particular set of chart information generated earlier in the program. */
  786.  
  787.   if (StringCmp(filename, "set") == 0) {
  788.     autom = 1;
  789.     SetCore(MonX, DayX, YeaX, TimX, ZonX, LonX, LatX);
  790.     return TRUE;
  791.   }
  792.  
  793. #ifdef TIME
  794.   /* If we are to read from the file "now" then that means use the time */
  795.   /* functions to calculate the present date and time.                  */
  796.  
  797.   if (StringCmp(filename, "now") == 0) {
  798.     autom = 1;
  799.     ZZ = defzone; OO = deflong; AA = deflat;
  800.     GetTimeNow(&MM, &DD, &YY, &TT, ZZ);
  801.     return TRUE;
  802.   }
  803. #endif
  804.  
  805.   /* If we are to read from the file "tty" then that means prompt the user */
  806.   /* for all the chart information.                                        */
  807.  
  808.   if (StringCmp(filename, "tty") == 0) {
  809.     if (!noswitches) {
  810.       /* Temporarily disable and internal redirection of output to a file */
  811.       /* because we always want user headers and prompts to be displayed. */
  812.       data = S; S = stdout;
  813.       AnsiColor(WHITE);
  814.       fprintf(S, "** %s version %s ", appname, VERSION);
  815.       fprintf(S, "(See '%cHc' switch for copyrights and credits.) **\n", DASH);
  816.       AnsiColor(DEFAULT);
  817.       fprintf(S, "   Invoke as '%s %cH' for list of command line options.\n",
  818.         ProcessProgname(progname), DASH);
  819.       S = data;
  820.     }
  821.     MM = (int)Input("Enter month of birth (e.g. '3', 'Mar')", 1.0, 12.0);
  822.     DD = (int)Input("Enter day   of birth (e.g. '1', '31') ", 1.0,
  823.       (real) DayInMonth(MM, 0));
  824.     YY = (int)Input("Enter year  of birth (e.g. '1994')    ", -5000.0, 5000.0);
  825.     if (YY >= 0 && YY <= 99) {
  826.       sprintf(name,
  827.         "Assuming first century A.D. is really meant instead of %d.",
  828.         1900 + YY);
  829.       PrintWarning(name);
  830.     }
  831.     fprintf(stdout, "Subtract one hour if Daylight Saving time in effect.\n");
  832.     TT = Input("Enter time  of birth (e.g. '18:30' '6:30pm')", -2.0, 24.0);
  833.     fprintf(stdout,
  834.       "Negative values indicate time zones east of Greenwich.\n");
  835.     ZZ = Input("Time zone in hours before GMT (5=Eastern, 8=Pacific)",
  836.       -24.0, 24.0);
  837.     fprintf(stdout,
  838.       "Negative values indicate eastern and southern locations.\n");
  839.     OO = Input("Longitude west of place (i.e. DEG:MIN)", -DEGHALF, DEGHALF);
  840.     AA = Input("Latitude north of place (i.e. DEG:MIN)", -DEGQUAD, DEGQUAD);
  841.     printl();
  842.     return TRUE;
  843.   }
  844.  
  845.   /* Now that the special cases are taken care of, we can assume we are */
  846.   /* to read from a real file.                                          */
  847.  
  848.   autom = 1;
  849.   data = OpenFile(filename, 1);
  850.  
  851.   /* Read the chart parameters from a normal file. */
  852.  
  853.   if ((c = getc(data)) != 'S') {
  854.     ungetc(c, data);
  855.     fscanf(data, "%d%d%d", &MM, &DD, &YY);
  856.     fscanf(data, "%lf%lf%lf%lf", &TT, &ZZ, &OO, &AA);
  857.  
  858.   /* Read the actual chart positions from a file produced with the -o0. */
  859.  
  860.   } else {
  861.  
  862.     /* Hack: A negative month value means the chart parameters are invalid, */
  863.     /* hence -o0 is in effect and we can assume the chart positions are     */
  864.     /* already in memory so we don't have to calculate them later.          */
  865.  
  866.     MM = -1;
  867.     for (i = 1; i <= BASE; i++) {
  868.       fscanf(data, "%s%lf%lf%lf", name, &k, &l, &m);
  869.       planet[i] = (l-1.0)*30.0+k+m/60.0;
  870.       fscanf(data, "%s%lf%lf", name, &k, &l);
  871.       planetalt[i] = k+l/60.0;
  872.       ret[i] = DTOR(name[1] == 'D' ? 1.0 : -1.0);
  873.  
  874.       /* -o0 files from version 3.05 and before don't have the uranians in   */
  875.       /* them. Be prepared to skip over them in old files for compatibility. */
  876.  
  877.       if (i == OBJECTS) {
  878.         while (getc(data) >= ' ')
  879.           ;
  880.         if ((c = getc(data)) != 'H')
  881.           i = C_HI;
  882.         else
  883.           i = total;
  884.       }
  885.     }
  886.     for (i = 1; i <= SIGNS/2; i++) {
  887.       fscanf(data, "%s%lf%lf%lf", name, &k, &l, &m);
  888.       house[i+6] = Mod((house[i] = Mod((l-1.0)*30.0+k+m/60.0))+DEGHALF);
  889.     }
  890.   }
  891.   fclose(data);
  892.   return TRUE;
  893. }
  894.  
  895.  
  896. /* Open the file indicated by the given string and return the file's stream */
  897. /* pointer, or NULL if the file couldn't be found or opened. All parts of   */
  898. /* the program which open files to read call this routine. We look in       */
  899. /* several various locations and directories for the file before giving up. */
  900.  
  901. FILE *OpenFile(filename, filemode)
  902. char *filename;
  903. int filemode;
  904. {
  905.   FILE *data;
  906.   char name[STRING], mode[3];
  907. #ifdef ENVIRON
  908.   char *env;
  909. #endif
  910.  
  911.   /* Some file types we want to open as binary instead of Ascii. */
  912.   sprintf(mode, "r%s", filemode == 2 ? "b" : "");
  913.  
  914.   /* First look for the file in the current directory. */
  915.   data = fopen(filename, mode);
  916.   if (data != NULL)
  917.     return data;
  918.  
  919. #ifdef ENVIRON
  920.   /* Next look for the file in the directory indicated by the version */
  921.   /* specific system environment variable.                            */
  922.   sprintf(name, "%s%s", ENVIRONVER, VERSION);
  923.   env = getenv(name);
  924.   if (env && *env) {
  925.     sprintf(name, "%s%c%s", env, DIR_SEP, filename);
  926.     data = fopen(name, mode);
  927.     if (data != NULL)
  928.       return data;
  929.   }
  930.  
  931.   /* Next look in the directory in the general environment variable. */
  932.   env = getenv(ENVIRONALL);
  933.   if (env && *env) {
  934.     sprintf(name, "%s%c%s", env, DIR_SEP, filename);
  935.     data = fopen(name, mode);
  936.     if (data != NULL)
  937.       return data;
  938.   }
  939.  
  940.   /* Next look in the directory in the version prefix environment variable. */
  941.   env = getenv(ENVIRONVER);
  942.   if (env && *env) {
  943.     sprintf(name, "%s%c%s", env, DIR_SEP, filename);
  944.     data = fopen(name, mode);
  945.     if (data != NULL)
  946.       return data;
  947.   }
  948. #endif
  949.  
  950.   /* Finally look in one of several directories specified at compile time. */
  951.   sprintf(name, "%s%c%s", filemode == 0 ? DEFAULT_DIR :
  952.     (filemode == 1 ? CHART_DIR : EPHE_DIR), DIR_SEP, filename);
  953.   data = fopen(name, mode);
  954.   if (data == NULL && filemode == 1) {
  955.     /* If the file was never found, print an error (unless we were looking */
  956.     /* for a certain file type, e.g. the optional astrolog.dat file).      */
  957.     sprintf(name, "File '%s' not found.", filename);
  958.     PrintError(name);
  959.   }
  960.   return data;
  961. }
  962.  
  963. /* general.c */
  964.