1 #include "astro.h"
2 #include "preferences.h"
3 
4 #include <stdio.h>
5 #include <math.h>
6 #include <ctype.h>
7 #include <stdlib.h>
8 #include <string.h>
9 
10 double ascii_strtod(const char *s00, char **se);  /* for PyEphem */
11 
12 /* sprint the variable a in sexagesimal format into out[].
13  * w is the number of spaces for the whole part.
14  * fracbase is the number of pieces a whole is to broken into; valid options:
15  *	360000:	<w>:mm:ss.ss
16  *	36000:	<w>:mm:ss.s
17  *	3600:	<w>:mm:ss
18  *	600:	<w>:mm.m
19  *	60:	<w>:mm
20  * return number of characters written to out, not counting final '\0'.
21  */
22 int
fs_sexa(char * out,double a,int w,int fracbase)23 fs_sexa (char *out, double a, int w, int fracbase)
24 {
25 	char *out0 = out;
26 	unsigned long n;
27 	int d;
28 	int f;
29 	int m;
30 	int s;
31 	int isneg;
32 
33 	/* save whether it's negative but do all the rest with a positive */
34 	isneg = (a < 0);
35 	if (isneg)
36 	    a = -a;
37 
38 	/* convert to an integral number of whole portions */
39 	n = (unsigned long)(a * fracbase + 0.5);
40 	d = n/fracbase;
41 	f = n%fracbase;
42 
43 	/* form the whole part; "negative 0" is a special case */
44 	if (isneg && d == 0)
45 	    out += sprintf (out, "%*s-0", w-2, "");
46 	else
47 	    out += sprintf (out, "%*d", w, isneg ? -d : d);
48 
49 	/* do the rest */
50 	switch (fracbase) {
51 	case 60:	/* dd:mm */
52 	    m = f/(fracbase/60);
53 	    out += sprintf (out, ":%02d", m);
54 	    break;
55 	case 600:	/* dd:mm.m */
56 	    out += sprintf (out, ":%02d.%1d", f/10, f%10);
57 	    break;
58 	case 3600:	/* dd:mm:ss */
59 	    m = f/(fracbase/60);
60 	    s = f%(fracbase/60);
61 	    out += sprintf (out, ":%02d:%02d", m, s);
62 	    break;
63 	case 36000:	/* dd:mm:ss.s*/
64 	    m = f/(fracbase/60);
65 	    s = f%(fracbase/60);
66 	    out += sprintf (out, ":%02d:%02d.%1d", m, s/10, s%10);
67 	    break;
68 	case 360000:	/* dd:mm:ss.ss */
69 	    m = f/(fracbase/60);
70 	    s = f%(fracbase/60);
71 	    out += sprintf (out, ":%02d:%02d.%02d", m, s/100, s%100);
72 	    break;
73 	default:
74 	    printf ("fs_sexa: unknown fracbase: %d\n", fracbase);
75 	    abort();
76 	}
77 
78 	return (out - out0);
79 }
80 
81 /* put the given modified Julian date, jd, in out[] according to the given
82  * preference format.
83  * return number of characters written to out, not counting final '\0'.
84  */
85 int
fs_date(char out[],int format,double jd)86 fs_date (char out[], int format, double jd)
87 {
88 	char *out0 = out;
89 	int m, y;
90 	double d;
91 
92 	mjd_cal (jd, &m, &d, &y);
93 	/* beware of %g rounding day up */
94 	if ((d < 1.0 && d - floor(d) >= .9999995)
95 				    || (d < 10.0 && d - floor(d) >= .999995)
96 				    || (d >= 10.0 && d - floor(d) >= .99995))
97 	    mjd_cal (mjd_day(jd+0.5), &m, &d, &y);
98 
99 	switch (format) {
100 	case PREF_YMD:
101 	    out += sprintf (out, "%4d/%02d/%02.6g", y, m, d);
102 	    break;
103 	case PREF_DMY:
104 	    out += sprintf (out, "%2.6g/%02d/%-4d", d, m, y);
105 	    break;
106 	case PREF_MDY:
107 	    out += sprintf (out, "%2d/%02.6g/%-4d", m, d, y);
108 	    break;
109 	default:
110 	    printf ("fs_date: bad date pref: %d\n", format);
111 	    abort();
112 	}
113 
114 	return (out - out0);
115 }
116 
117 
118 /* convert sexagesimal string str A:B:C to double.
119  *   Any missing A, B or C will be assumed 0.
120  *   optional - and + can be anywhere.
121  * return 0 if ok, -1 if can't find a thing or A, B or C are invalid numbers.
122  */
123 int
f_scansexa(const char * str0,double * dp)124 f_scansexa (
125 const char *str0,	/* input string */
126 double *dp)		/* cracked value, if return 0 */
127 {
128 	double a, b, c;
129 	char str[256];
130 	char *neg, *s, *end;
131 	int isneg, status;
132 
133 	/* copy str0 so we can play with it */
134 	strncpy (str, str0, sizeof(str)-1);
135 	str[sizeof(str)-1] = '\0';
136 
137 	/* note first negative (but not fooled by neg exponent) */
138 	isneg = 0;
139 	neg = strchr(str, '-');
140 	if (neg && (neg == str || (neg[-1] != 'E' && neg[-1] != 'e'))) {
141 	    *neg = ' ';
142 	    isneg = 1;
143 	}
144 
145         /* These three calls replace an old, locale-sensitive sscanf.
146            Note that, per the semantics of the strtod call, if we run
147            out of valid numbers to parse, then the last few values will
148            just get zero. */
149         status = 0;
150         s = str;
151         a = ascii_strtod(s, &end);
152         if (end == s) { /* since a will be -1 */
153              a = 0.0;
154              /* don't fail if A is an empty string */
155              if ((*end != ':') && (*end != '\0')) status = -1;
156         }
157         s = end;
158         if (*s == ':') s++;
159         b = ascii_strtod(s, &end);
160         if (end == s) { /* since b will be -1 */
161              b = 0.0;
162              /* don't fail if B is an empty string */
163              if ((*end != ':') && (*end != '\0')) status = -1;
164         }
165         s = end;
166         if (*s == ':') s++;
167         c = ascii_strtod(s, &end);
168         if (end == s) { /* since c will be -1 */
169              c = 0.0;
170              /* don't fail if C is an empty string */
171              if ((*end != ':') && (*end != '\0')) status = -1;
172         }
173 
174 	/* back to one value, restoring neg */
175 	*dp = a + b/60.0 + c/3600.0;
176 	if (isneg)
177 	    *dp *= -1;
178 	return status;
179 }
180 
181 /* crack a floating date string, bp, of the form X/Y/Z determined by the
182  *   PREF_DATE_FORMAT preference into its components. allow the day to be a
183  *   floating point number,
184  * the slashes may also be spaces or colons.
185  * a lone component with a decimal point is considered a year.
186  * Brandon Rhodes: 2011-11-24 supplemented this to allow dash separators.
187  */
188 void
f_sscandate(char * bp,int pref,int * m,double * d,int * y)189 f_sscandate (
190 char *bp,
191 int pref,       /* one of PREF_X for PREF_DATE_FORMAT */
192 int *m,
193 double *d,
194 int *y)
195 {
196         double X,Y,Z; /* the three components */
197         char *s, *end;
198 	int n;
199 
200 	X = Y = Z = 0.0;
201 
202         /* This replaces an old, locale-sensitive sscanf(). */
203         X = ascii_strtod(bp, &end);
204         if (bp == end) {
205              n = 0;
206              X = 0.0; /* X will be -1 */
207         } else {
208              s = end;
209              if (*s == '-' || *s == '/' || *s == ':') s++;
210              Y = ascii_strtod(s, &end);
211              if (s == end) {
212                   n = 1;
213                   Y = 0.0; /* Y will be -1 */
214              } else {
215                   s = end;
216                   if (*s == '-' || *s == '/' || *s == ':') s++;
217                   Z = ascii_strtod(s, &end);
218                   if (s == end) {
219                        n = 2;
220                        Z = 0.0; /* Z will be -1 */
221                   } else {
222                        n = 3;
223                   }
224              }
225         }
226 
227 
228 	if (n == 1 && (strchr(bp, '.')
229 			|| (pref == PREF_MDY && (X < 1 || X > 12))
230 			|| (pref == PREF_DMY && (X < 1 || X > 31)))) {
231 	    double Mjd;
232 	    year_mjd (X, &Mjd);
233 	    mjd_cal (Mjd, m, d, y);
234 	} else {
235 	    switch (pref) {
236 	    case PREF_MDY:
237 		if (n > 0 && X != 0)
238 		    *m = (int)X;
239 		if (n > 1 && Y != 0)
240 		    *d = Y;
241 		if (n > 2 && Z != 0)
242 		    *y = (int)Z;
243 		break;
244 	    case PREF_YMD:
245 		if (n > 0 && X != 0)
246 		    *y = (int)X;
247 		if (n > 1 && Y != 0)
248 		    *m = (int)Y;
249 		if (n > 2 && Z != 0)
250 		    *d = Z;
251 		break;
252 	    case PREF_DMY:
253 		if (n > 0 && X != 0)
254 		    *d = X;
255 		if (n > 1 && Y != 0)
256 		    *m = (int)Y;
257 		if (n > 2 && Z != 0)
258 		    *y = (int)Z;
259 		break;
260 	    }
261 	}
262 }
263 
264