1 #include <cerrno>
2 #include <clocale>
3 #include <cmath>
4 #include <cstdio>
5 #include <cstdlib>
6 #include <cstring>
7 #include <ctime>
8 #include <iostream>
9 #include <sstream>
10 #include <string>
11 using namespace std;
12 
13 #include <unistd.h>
14 extern char **environ;
15 
16 #include "config.h"
17 
18 #ifdef HAVE_ICONV
19 # include <iconv.h>
20 # ifdef HAVE_LIBCHARSET
21 #  include <localcharset.h>
22 # else
23 #  ifdef HAVE_LANGINFO_H
24 #   include <langinfo.h>
25 #  else
26 #   define NO_ICONV
27 #  endif
28 # endif
29 #else
30 # define NO_ICONV
31 #endif
32 
33 #include "Options.h"
34 #include "xpUtil.h"
35 
36 void
xpExit(const string & message,const char * file,const int line)37 xpExit(const string &message, const char *file, const int line)
38 {
39     cerr << "Error: " << message;
40     cerr << "Exiting from " << file << " at line " << line << endl;
41 
42 #if 0
43     // force a segfault
44     const char *const ptr = NULL;
45     cout << ptr[5000] << endl;
46     cout.flush();
47 #endif
48 
49     exit(EXIT_FAILURE);
50 }
51 
52 void
xpWarn(const string & message,const char * file,const int line)53 xpWarn(const string &message, const char *file, const int line)
54 {
55     Options *options = Options::getInstance();
56     if (options->Verbosity() >= 0)
57     {
58         cerr << "Warning: " << message;
59         if (options->Verbosity() > 0)
60             cerr << "In " << file << " at line " << line << endl << endl;
61         cerr.flush();
62     }
63 }
64 
65 void
xpMsg(const string & message,const char * file,const int line)66 xpMsg(const string &message, const char *file, const int line)
67 {
68     Options *options = Options::getInstance();
69     if (options->Verbosity() > 0)
70     {
71         cout << message;
72         cout.flush();
73     }
74 }
75 
76 void
removeFromEnvironment(const char * name)77 removeFromEnvironment(const char *name)
78 {
79 #ifdef HAVE_UNSETENV
80     unsetenv(name);
81 #else
82     string badname = name;
83     badname += "=";
84 
85     // I found this useful code on groups.google.com.  It's based on
86     // sudo's code, where the environment is cleaned up before
87     // executing anything.
88     char **cur, **move;
89     for (cur = environ; *cur; cur++) {
90         if (strncmp(*cur, badname.c_str(), badname.length()) == 0)
91         {
92             /* Found variable; move subsequent variables over it */
93             for (move = cur; *move; move++)
94                 *move = *(move + 1);
95             cur--;
96         }
97     }
98 
99 #endif
100 }
101 
102 void
unlinkFile(const char * name)103 unlinkFile(const char *name)
104 {
105     if (unlink(name) == -1)
106     {
107         ostringstream errStr;
108         errStr << "Can't remove " << name << "\n";
109         xpWarn(errStr.str(), __FILE__, __LINE__);
110     }
111 }
112 
113 void
cross(const double a[3],const double b[3],double c[3])114 cross(const double a[3], const double b[3], double c[3])
115 {
116     c[0] = a[1]*b[2] - b[1]*a[2];
117     c[1] = a[2]*b[0] - b[2]*a[0];
118     c[2] = a[0]*b[1] - b[0]*a[1];
119 }
120 
121 double
dot(const double A0,const double A1,const double A2,const double B0,const double B1,const double B2)122 dot(const double A0, const double A1, const double A2,
123     const double B0, const double B1, const double B2)
124 {
125     return(A0 * B0 + A1 * B1 + A2 * B2);
126 }
127 
128 double
ndot(const double A0,const double A1,const double A2,const double B0,const double B1,const double B2)129 ndot(const double A0, const double A1, const double A2,
130      const double B0, const double B1, const double B2)
131 {
132     const double len_a = dot(A0, A1, A2, A0, A1, A2);
133     const double len_b = dot(B0, B1, B2, B0, B1, B2);
134     return(dot(A0, A1, A2, B0, B1, B2) / sqrt(len_a * len_b));
135 }
136 
137 double
dot(const double a[3],const double b[3])138 dot(const double a[3], const double b[3])
139 {
140     return(a[0] * b[0] + a[1] * b[1] + a[2] * b[2]);
141 }
142 
143 double
ndot(const double a[3],const double b[3])144 ndot(const double a[3], const double b[3])
145 {
146     const double len_a = dot(a, a);
147     const double len_b = dot(b, b);
148     return(dot(a, b) / sqrt(len_a * len_b));
149 }
150 
151 double
normalize(double a[3])152 normalize(double a[3])
153 {
154     const double length = dot(a, a);
155     if (length > 0)
156         for (int i = 0; i < 3; i++) a[i] /= length;
157 
158     return(length);
159 }
160 
161 void
invertMatrix(const double in[3][3],double out[3][3])162 invertMatrix(const double in[3][3], double out[3][3])
163 {
164     double a1 = in[0][0];
165     double a2 = in[0][1];
166     double a3 = in[0][2];
167 
168     double b1 = in[1][0];
169     double b2 = in[1][1];
170     double b3 = in[1][2];
171 
172     double c1 = in[2][0];
173     double c2 = in[2][1];
174     double c3 = in[2][2];
175 
176     double det = (a1*(b2*c3 - b3*c2) + a2*(b3*c1 - b1*c3)
177                   + a3*(b1*c2 - b2*c1));
178 
179     out[0][0] = (b2*c3 - b3*c2)/det;
180     out[0][1] = (a3*c2 - a2*c3)/det;
181     out[0][2] = (a2*b3 - a3*b2)/det;
182 
183     out[1][0] = (b3*c1 - b1*c3)/det;
184     out[1][1] = (a1*c3 - a3*c1)/det;
185     out[1][2] = (a3*b1 - a1*b3)/det;
186 
187     out[2][0] = (b1*c2 - b2*c1)/det;
188     out[2][1] = (a2*c1 - a1*c2)/det;
189     out[2][2] = (a1*b2 - a2*b1)/det;
190 #if 0
191     printf("in:\n");
192     for (int i = 0; i < 3; i++)
193     {
194         printf("[%14.8e, %14.8e, %14.8e],\n", in[i][0], in[i][1],
195                in[i][2]);
196     }
197 
198     printf("det = %14.8e\n", det);
199 
200     printf("out:\n");
201     for (int i = 0; i < 3; i++)
202     {
203         printf("[%14.8e, %14.8e, %14.8e],\n", out[i][0], out[i][1],
204                out[i][2]);
205     }
206 
207     printf("product:\n");
208     double prod[3][3];
209     for (int i = 0; i < 3; i++)
210     {
211         for (int j = 0; j < 3; j++)
212         {
213             prod[i][j] = 0;
214             for (int ii = 0; ii < 3; ii++)
215                 prod[i][j] += in[i][ii] * out[ii][j];
216         }
217         printf("[%14.8e, %14.8e, %14.8e],\n", prod[i][0], prod[i][1],
218                prod[i][2]);
219     }
220 
221 #endif
222 
223 }
224 
225 void
RADecToXYZ(double RA,double Dec,double & X,double & Y,double & Z)226 RADecToXYZ(double RA, double Dec, double &X, double &Y, double &Z)
227 {
228     RA *= 15;
229 
230     X = FAR_DISTANCE * cos(Dec) * cos(RA);
231     Y = FAR_DISTANCE * cos(Dec) * sin(RA);
232     Z = FAR_DISTANCE * sin(Dec);
233 }
234 
235 void
fromJulian(double jd,int & year,int & month,int & day,int & hour,int & min,double & sec)236 fromJulian(double jd, int &year, int &month, int &day,
237            int &hour, int &min, double &sec)
238 {
239     jd += 0.5;
240     int Z = (int) jd;
241     double F = jd - Z;
242 
243     int A = Z;
244     if (Z >= 2291161)
245     {
246         int alpha = (int) ((Z - 1867216.25)/36524.25);
247         A = Z + 1 + alpha - alpha/4;
248     }
249 
250     int B = A + 1524;
251     int C = (int) floor((B - 122.1) / 365.25);
252     int D = (int) floor(365.25 * C);
253     int E = (int) floor((B - D)/30.6001);
254 
255     double dday = B - D - (int) floor(30.6001 * E) + F;
256 
257     day = (int) floor(dday);
258 
259     month = E - 13;
260     if (E < 14) month = E - 1;
261 
262     year = C - 4715;
263     if (month > 2) year--;
264 
265     double dhour = 24 * (dday - day);
266     hour = (int) floor(dhour);
267 
268     double dmin = 60 * (dhour - hour);
269     min = (int) floor(dmin);
270     sec = 60 * (dmin - min);
271 }
272 
273 string
fromJulian(double date)274 fromJulian(double date)
275 {
276     char timeString[16];
277     int year, month, day, hour, min;
278     double sec;
279 
280     fromJulian(date, year, month, day, hour, min, sec);
281     snprintf(timeString, 16,
282              "%4.4d%2.2d%2.2d.%2.2d%2.2d%2.2d",
283              year, month, day,
284              hour, min, (int) floor(sec));
285 
286     return(string(timeString));
287 }
288 
289 time_t
get_tv_sec(double jd)290 get_tv_sec(double jd)
291 {
292     int year, month, day, hour, min;
293     double sec;
294     fromJulian(jd, year, month, day, hour, min, sec);
295 
296     tm tm_struct = { (int) floor(sec + 0.5), min, hour, day,
297                      month - 1, year - 1900, 0, 0, -1 };
298 
299 #ifdef HAVE_TIMEGM
300     time_t returnval = timegm(&tm_struct);
301 #else
302     string tz_save = "";
303     char *get_tz = getenv("TZ");
304     if (get_tz != NULL)
305     {
306         tz_save = "TZ=";
307         tz_save += get_tz;
308     }
309     putenv("TZ=UTC");
310     tzset();
311 
312     time_t returnval = mktime(&tm_struct);
313 
314     if (tz_save.empty())
315         removeFromEnvironment("TZ");
316     else
317         putenv((char *) tz_save.c_str());
318     tzset();
319 #endif
320     return(returnval);
321 }
322 
323 double
toJulian(int year,int month,int day,int hour,int min,int sec)324 toJulian(int year, int month, int day, int hour, int min, int sec)
325 {
326     // Gregorian calendar (after 1582 Oct 15)
327     const bool gregorian = (year > 1582
328                             || (year == 1582 && month > 10)
329                             || (year == 1582 && month == 10 && day >= 15));
330 
331     if(month < 3)
332     {
333         year -= 1;
334         month += 12;
335     }
336 
337     int a = year/100;
338     int b = 0;
339     if (gregorian) b = 2 - a + a/4;
340 
341     int c = (int) floor(365.25 * (year + 4716));
342     int d = (int) floor(30.6001 * (month + 1));
343     double e = day + ((sec/60. + min) / 60. + hour) / 24.;
344 
345     double jd = b + c + d + e - 1524.5;
346 
347     return(jd);
348 }
349 
350 double
deltaETpre1838(const double jd)351 deltaETpre1838(const double jd)
352 {
353     // From McCarthy & Babcock (1986)
354     const double j1800 = 2378496.5;
355     const double t = (jd - j1800)/36525;
356     const double delT = 5.156 + 13.3066 * (t - 0.19) * (t - 0.19);
357 
358     return(delT);
359 }
360 
361 double
deltaETpre1972(const double jd)362 deltaETpre1972(const double jd)
363 {
364     // Valid from 1825 to 2000, Montenbruck & Pfelger (2000), p 188
365     const double T = (jd - 2451545)/36525;
366     const int i = (int) floor(T/0.25);
367 
368     const double c[7][4] = {
369         { 10.4, -80.8,  413.9,  -572.3 },
370         {  6.6,  46.3, -358.4,    18.8 },
371         { -3.9, -10.8, -166.2,   867.4 },
372         { -2.6, 114.1,  327.5, -1467.4 },
373         { 24.2,  -6.3,   -8.2,   483.4 },
374         { 29.3,  32.5,   -3.8,   550.7 },
375         { 45.3, 130.5, -570.5,  1516.7 }
376     };
377 
378     double t = T - i * 0.25;
379 
380     int ii = i + 7;
381     if (ii < 0)
382     {
383         t = 0;
384         ii = 0;
385     }
386     else if (ii > 6)
387     {
388         ii = 6;
389         t = 0.25;
390     }
391 
392     const double delT = c[ii][0] + t * (c[ii][1]
393                                         + t * (c[ii][2]
394                                                + t * c[ii][3]));
395     return delT;
396 }
397 
398 double
deltaETpost1972(const double jd)399 deltaETpost1972(const double jd)
400 {
401     // based on JPL's leap seconds kernel file
402     const double delta_t_a = 32.184;
403     const double k = 1.657e-3;
404     const double eb = 1.671e-2;
405     const double m0 = 6.239996;
406     const double m1 = 1.99096871e-7;
407 
408     // leap seconds
409     int delta_at = 9;
410     if (jd >= toJulian(1972, 1, 1, 0, 0, 0)) delta_at++; // 10
411     if (jd >= toJulian(1972, 7, 1, 0, 0, 0)) delta_at++; // 11
412     if (jd >= toJulian(1973, 1, 1, 0, 0, 0)) delta_at++; // 12
413     if (jd >= toJulian(1974, 1, 1, 0, 0, 0)) delta_at++; // 13
414     if (jd >= toJulian(1975, 1, 1, 0, 0, 0)) delta_at++; // 14
415     if (jd >= toJulian(1976, 1, 1, 0, 0, 0)) delta_at++; // 15
416     if (jd >= toJulian(1977, 1, 1, 0, 0, 0)) delta_at++; // 16
417     if (jd >= toJulian(1978, 1, 1, 0, 0, 0)) delta_at++; // 17
418     if (jd >= toJulian(1979, 1, 1, 0, 0, 0)) delta_at++; // 18
419     if (jd >= toJulian(1980, 1, 1, 0, 0, 0)) delta_at++; // 19
420     if (jd >= toJulian(1981, 7, 1, 0, 0, 0)) delta_at++; // 20
421     if (jd >= toJulian(1982, 7, 1, 0, 0, 0)) delta_at++; // 21
422     if (jd >= toJulian(1983, 7, 1, 0, 0, 0)) delta_at++; // 22
423     if (jd >= toJulian(1985, 7, 1, 0, 0, 0)) delta_at++; // 23
424     if (jd >= toJulian(1988, 1, 1, 0, 0, 0)) delta_at++; // 24
425     if (jd >= toJulian(1990, 1, 1, 0, 0, 0)) delta_at++; // 25
426     if (jd >= toJulian(1991, 1, 1, 0, 0, 0)) delta_at++; // 26
427     if (jd >= toJulian(1992, 7, 1, 0, 0, 0)) delta_at++; // 27
428     if (jd >= toJulian(1993, 7, 1, 0, 0, 0)) delta_at++; // 28
429     if (jd >= toJulian(1994, 7, 1, 0, 0, 0)) delta_at++; // 29
430     if (jd >= toJulian(1996, 1, 1, 0, 0, 0)) delta_at++; // 30
431     if (jd >= toJulian(1997, 7, 1, 0, 0, 0)) delta_at++; // 31
432     if (jd >= toJulian(1999, 1, 1, 0, 0, 0)) delta_at++; // 32
433     if (jd >= toJulian(2006, 1, 1, 0, 0, 0)) delta_at++; // 33
434     if (jd >= toJulian(2009, 1, 1, 0, 0, 0)) delta_at++; // 34
435     if (jd >= toJulian(2012, 7, 1, 0, 0, 0)) delta_at++; // 35
436     if (jd >= toJulian(2015, 7, 1, 0, 0, 0)) delta_at++; // 36
437 
438     const double J2000 = toJulian(2000, 1, 1, 12, 0, 0);
439     const double m = m0 + m1 * (jd - J2000) * 86400;
440     const double e = m + eb * sin(m);
441 
442     const double delT = delta_t_a + k * sin(e) + delta_at;
443     return delT;
444 }
445 
446 // find the difference between universal and ephemeris time
447 // (delT = ET - UT)
448 double
delT(const double jd)449 delT(const double jd)
450 {
451     double delT;
452     if (jd < toJulian(1838, 1, 1, 0, 0, 0))
453     {
454         delT = deltaETpre1838(jd);
455     }
456     else
457     {
458         if (jd < toJulian(1972, 1, 1, 0, 0, 0))
459         {
460             delT = deltaETpre1972(jd);
461         }
462         else
463         {
464             delT = deltaETpost1972(jd);
465         }
466     }
467 
468 /*
469     int year, month, day, hour, min;
470     double sec;
471     fromJulian(jd, year, month, day, hour, min, sec);
472     double thisYear = year + (month-1)/12.;
473 
474     printf("%.1f %f %f %f %f\n", thisYear, deltaETpre1838(jd),
475            deltaETpre1972(jd), deltaETpost1972(jd), delT);
476 */
477     return delT;
478 }
479 
480 void
rotateX(double & X,double & Y,double & Z,const double theta)481 rotateX(double &X, double &Y, double &Z, const double theta)
482 {
483     const double st = sin(theta);
484     const double ct = cos(theta);
485     const double X0 = X;
486     const double Y0 = Y;
487     const double Z0 = Z;
488 
489     X = X0;
490     Y = Y0 * ct + Z0 * st;
491     Z = Z0 * ct - Y0 * st;
492 }
493 
494 void
rotateZ(double & X,double & Y,double & Z,const double theta)495 rotateZ(double &X, double &Y, double &Z, const double theta)
496 {
497     const double st = sin(theta);
498     const double ct = cos(theta);
499     const double X0 = X;
500     const double Y0 = Y;
501     const double Z0 = Z;
502 
503     X = X0 * ct + Y0 * st;
504     Y = Y0 * ct - X0 * st;
505     Z = Z0;
506 }
507 
508 /*
509   x = 0 passes through 0 and 2
510   y = 0 passes through 0 and 1
511 
512   Given a point (x, y), compute the area weighting of each pixel.
513 
514    --- ---
515   | 0 | 1 |
516    --- ---
517   | 2 | 3 |
518    --- ---
519 */
520 void
getWeights(const double t,const double u,double weights[4])521 getWeights(const double t, const double u, double weights[4])
522 {
523     // Weights are from Numerical Recipes, 2nd Edition
524     //        weight[0] = (1 - t) * u;
525     //        weight[2] = (1-t) * (1-u);
526     //        weight[3] = t * (1-u);
527     weights[1] = t * u;
528     weights[0] = u - weights[1];
529     weights[2] = 1 - t - u + weights[1];
530     weights[3] = t - weights[1];
531 }
532 
533 void
calcGreatArc(const double lat1,const double lon1,const double lat2,const double lon2,double & trueCourse,double & dist)534 calcGreatArc(const double lat1, const double lon1,
535              const double lat2, const double lon2,
536              double &trueCourse, double &dist)
537 {
538     /*
539      * Equations are from http://www.best.com/~williams/avform.html
540      * returned trueCourse is relative to latitude north
541      */
542     const double sin_lat1 = sin(lat1);
543     const double cos_lat1 = cos(lat1);
544     const double sin_lat2 = sin(lat2);
545     const double cos_lat2 = cos(lat2);
546     const double dlon = lon1 - lon2;
547 
548     // Arc length between points (in radians)
549     double arg = sin_lat1 * sin_lat2 + cos_lat1 * cos_lat2 * cos(dlon);
550     if (arg >= 1)
551         dist = 0;
552     else if (arg <= -1)
553         dist = M_PI;
554     else
555         dist = acos(arg);
556 
557     // True course
558     trueCourse = fmod(atan2(sin(dlon) * cos_lat2,
559                             cos_lat1 * sin_lat2
560                             - sin_lat1 * cos_lat2 * cos(dlon)), TWO_PI);
561 }
562 
563 double
kepler(const double e,double M)564 kepler(const double e, double M)
565 {
566     double E = M = fmod(M, TWO_PI);
567     double delta = 1;
568 
569     while (fabs(delta) > 1E-10)
570     {
571         delta = (M + e * sin(E) - E)/(1 - e * cos(E));
572         E += delta;
573     }
574     return(E);
575 }
576 
577 //  Precess rectangular coordinates in B1950 frame to J2000 using
578 //  Standish precession matrix from Lieske (1998)
579 void
precessB1950J2000(double & X,double & Y,double & Z)580 precessB1950J2000(double &X, double &Y, double &Z)
581 {
582     static const double p[3][3] =
583         { { 0.9999256791774783, -0.0111815116768724, -0.0048590038154553 },
584           { 0.0111815116959975,  0.9999374845751042, -0.0000271625775175 },
585           { 0.0048590037714450, -0.0000271704492210,  0.9999881946023742 } };
586 
587     double newX = p[0][0] * X + p[0][1] * Y + p[0][2] * Z;
588     double newY = p[1][0] * X + p[1][1] * Y + p[1][2] * Z;
589     double newZ = p[2][0] * X + p[2][1] * Y + p[2][2] * Z;
590 
591     X = newX;
592     Y = newY;
593     Z = newZ;
594 }
595 
596 // Return the shading function.  For Lambertian shading, the input
597 // value X is the cosine of the sun-surface-observer angle, and the
598 // return value is just X.  Using the sqrt of X brightens the image a
599 // bit.
600 double
photoFunction(const double x)601 photoFunction(const double x)
602 {
603     return(sqrt(x));
604 }
605 
606 static void
convertEncoding(const bool toNative,ICONV_CONST char * inBuf,char * outBuf)607 convertEncoding(const bool toNative, ICONV_CONST char *inBuf, char *outBuf)
608 {
609 #ifdef NO_ICONV
610     memcpy(outBuf, inBuf, MAX_LINE_LENGTH);
611 #else
612 #ifdef HAVE_LIBCHARSET
613     const char *encoding = locale_charset();
614 #else
615     const char *encoding = nl_langinfo(CODESET);
616 #endif
617     const char *fromCode = (toNative ? "UTF-8" : encoding);
618     const char *toCode =   (toNative ? encoding : "UTF-8");
619     iconv_t conv = iconv_open(toCode, fromCode);
620     if (conv != (iconv_t) -1)
621     {
622         size_t inbytesleft = strlen(inBuf);
623         size_t outbytesleft = MAX_LINE_LENGTH;
624         while (inbytesleft != (size_t) 0)
625         {
626             size_t retVal = iconv(conv, &inBuf, &inbytesleft,
627                                   &outBuf, &outbytesleft);
628             if (retVal == (size_t) -1)
629             {
630                 ostringstream errStr;
631                 errStr << "Can't convert sequence " << inBuf
632                        << " from encoding " << fromCode
633                        << " to encoding " << toCode << endl;
634                 switch (errno)
635                 {
636                 case EINVAL:
637                     errStr << "Incomplete character or shift sequence "
638                            << "(EINVAL)\n";
639                     break;
640                 case E2BIG:
641                     errStr << "Lack of space in output buffer (E2BIG)\n";
642                     break;
643                 case EILSEQ:
644                     errStr << "Illegal character or shift sequence "
645                            << "(EILSEQ)\n";
646                     break;
647                 case EBADF:
648                     errStr << "Invalid conversion descriptor (EBADF)\n";
649                     break;
650                 default:
651                     errStr << "Unknown iconv error\n";
652                 }
653                 xpWarn(errStr.str(), __FILE__, __LINE__);
654                 iconv_close(conv);
655                 return;
656             }
657         }
658         iconv_close(conv);
659     }
660     else
661     {
662         ostringstream errStr;
663         errStr << "iconv_open() failed, fromCode is " << fromCode
664                << ", toCode is " << toCode << "\n";
665         xpWarn(errStr.str(), __FILE__, __LINE__);
666     }
667 #endif
668 }
669 
670 void
strftimeUTF8(string & timeString)671 strftimeUTF8(string &timeString)
672 {
673     // This is the input string, with formatting characters
674     char buffer_UTF8[MAX_LINE_LENGTH];
675     memset(buffer_UTF8, 0, MAX_LINE_LENGTH);
676     strncpy(buffer_UTF8, timeString.c_str(), MAX_LINE_LENGTH);
677 
678     // Convert to native encoding
679     char buffer_native_format[MAX_LINE_LENGTH];
680     memset(buffer_native_format, 0, MAX_LINE_LENGTH);
681     convertEncoding(true, buffer_UTF8, buffer_native_format);
682 
683     // Run it through strftime()
684     char buffer_native[MAX_LINE_LENGTH];
685     Options *options = Options::getInstance();
686     time_t tv_sec = options->TVSec();
687     strftime(buffer_native, MAX_LINE_LENGTH,
688              buffer_native_format,
689              localtime((time_t *) &tv_sec));
690 
691     // Convert back to UTF8
692     convertEncoding(false, buffer_native, buffer_UTF8);
693     timeString.assign(buffer_UTF8);
694 }
695 
696 char *
checkLocale(const int category,const char * locale)697 checkLocale(const int category, const char *locale)
698 {
699     static bool showWarning = true;
700     char *returnVal = setlocale(category, locale);
701     if (locale != NULL)
702     {
703         if (returnVal == NULL)
704         {
705             ostringstream errMsg;
706             errMsg << "setlocale(";
707             switch (category)
708             {
709             case LC_CTYPE:
710                 errMsg << "LC_CTYPE";
711                 break;
712             case LC_COLLATE:
713                 errMsg << "LC_COLLATE";
714                 break;
715             case LC_TIME:
716                 errMsg << "LC_TIME";
717                 break;
718             case LC_NUMERIC:
719                 errMsg << "LC_NUMERIC";
720                 break;
721             case LC_MONETARY:
722                 errMsg << "LC_MONETARY";
723                 break;
724             case LC_MESSAGES:
725                 errMsg << "LC_MESSAGES";
726                 break;
727             case LC_ALL:
728                 errMsg << "LC_ALL";
729                 break;
730             default:
731                 errMsg << "UNKNOWN CATEGORY!";
732             }
733             errMsg << ", ";
734             if (strlen(locale) == 0)
735             {
736                 errMsg << "\"\"";
737             }
738             else
739             {
740                 errMsg << "\"" << locale << "\"";
741             }
742             errMsg << ") failed! ";
743 
744             if (strlen(locale) == 0)
745             {
746                 errMsg << "Check your LANG environment variable "
747                        << "(currently ";
748                 char *lang = getenv("LANG");
749                 if (lang == NULL)
750                 {
751                     errMsg << "NULL";
752                 }
753                 else
754                 {
755                     errMsg << "\"" << lang << "\"";
756                 }
757                 errMsg << "). Setting to \"C\".\n";
758 
759                 if (showWarning)
760                 {
761                     xpWarn(errMsg.str(), __FILE__, __LINE__);
762                     showWarning = false;
763                 }
764                 returnVal = setlocale(category, "C");
765             }
766             else
767             {
768                 errMsg << "Trying native ...\n";
769                 xpWarn(errMsg.str(), __FILE__, __LINE__);
770                 showWarning = true;
771                 returnVal = checkLocale(category, "");
772             }
773         }
774     }
775     return(returnVal);
776 }
777