/src/icu/source/common/putil.cpp
Line  | Count  | Source (jump to first uncovered line)  | 
1  |  | // © 2016 and later: Unicode, Inc. and others.  | 
2  |  | // License & terms of use: http://www.unicode.org/copyright.html  | 
3  |  | /*  | 
4  |  | ******************************************************************************  | 
5  |  | *  | 
6  |  | *   Copyright (C) 1997-2016, International Business Machines  | 
7  |  | *   Corporation and others.  All Rights Reserved.  | 
8  |  | *  | 
9  |  | ******************************************************************************  | 
10  |  | *  | 
11  |  | *  FILE NAME : putil.c (previously putil.cpp and ptypes.cpp)  | 
12  |  | *  | 
13  |  | *   Date        Name        Description  | 
14  |  | *   04/14/97    aliu        Creation.  | 
15  |  | *   04/24/97    aliu        Added getDefaultDataDirectory() and  | 
16  |  | *                            getDefaultLocaleID().  | 
17  |  | *   04/28/97    aliu        Rewritten to assume Unix and apply general methods  | 
18  |  | *                            for assumed case.  Non-UNIX platforms must be  | 
19  |  | *                            special-cased.  Rewrote numeric methods dealing  | 
20  |  | *                            with NaN and Infinity to be platform independent  | 
21  |  | *                             over all IEEE 754 platforms.  | 
22  |  | *   05/13/97    aliu        Restored sign of timezone  | 
23  |  | *                            (semantics are hours West of GMT)  | 
24  |  | *   06/16/98    erm         Added IEEE_754 stuff, cleaned up isInfinite, isNan,  | 
25  |  | *                             nextDouble..  | 
26  |  | *   07/22/98    stephen     Added remainder, max, min, trunc  | 
27  |  | *   08/13/98    stephen     Added isNegativeInfinity, isPositiveInfinity  | 
28  |  | *   08/24/98    stephen     Added longBitsFromDouble  | 
29  |  | *   09/08/98    stephen     Minor changes for Mac Port  | 
30  |  | *   03/02/99    stephen     Removed openFile().  Added AS400 support.  | 
31  |  | *                            Fixed EBCDIC tables  | 
32  |  | *   04/15/99    stephen     Converted to C.  | 
33  |  | *   06/28/99    stephen     Removed mutex locking in u_isBigEndian().  | 
34  |  | *   08/04/99    jeffrey R.  Added OS/2 changes  | 
35  |  | *   11/15/99    helena      Integrated S/390 IEEE support.  | 
36  |  | *   04/26/01    Barry N.    OS/400 support for uprv_getDefaultLocaleID  | 
37  |  | *   08/15/01    Steven H.   OS/400 support for uprv_getDefaultCodepage  | 
38  |  | *   01/03/08    Steven L.   Fake Time Support  | 
39  |  | ******************************************************************************  | 
40  |  | */  | 
41  |  |  | 
42  |  | // Defines _XOPEN_SOURCE for access to POSIX functions.  | 
43  |  | // Must be before any other #includes.  | 
44  |  | #include "uposixdefs.h"  | 
45  |  |  | 
46  |  | // First, the platform type. Need this for U_PLATFORM.  | 
47  |  | #include "unicode/platform.h"  | 
48  |  |  | 
49  |  | #if U_PLATFORM == U_PF_MINGW && defined __STRICT_ANSI__  | 
50  |  | /* tzset isn't defined in strict ANSI on MinGW. */  | 
51  |  | #undef __STRICT_ANSI__  | 
52  |  | #endif  | 
53  |  |  | 
54  |  | /*  | 
55  |  |  * Cygwin with GCC requires inclusion of time.h after the above disabling strict asci mode statement.  | 
56  |  |  */  | 
57  |  | #include <time.h>  | 
58  |  |  | 
59  |  | #if !U_PLATFORM_USES_ONLY_WIN32_API  | 
60  |  | #include <sys/time.h>  | 
61  |  | #endif  | 
62  |  |  | 
63  |  | /* include the rest of the ICU headers */  | 
64  |  | #include "unicode/putil.h"  | 
65  |  | #include "unicode/ustring.h"  | 
66  |  | #include "putilimp.h"  | 
67  |  | #include "uassert.h"  | 
68  |  | #include "umutex.h"  | 
69  |  | #include "cmemory.h"  | 
70  |  | #include "cstring.h"  | 
71  |  | #include "locmap.h"  | 
72  |  | #include "ucln_cmn.h"  | 
73  |  | #include "charstr.h"  | 
74  |  |  | 
75  |  | /* Include standard headers. */  | 
76  |  | #include <stdio.h>  | 
77  |  | #include <stdlib.h>  | 
78  |  | #include <string.h>  | 
79  |  | #include <math.h>  | 
80  |  | #include <locale.h>  | 
81  |  | #include <float.h>  | 
82  |  |  | 
83  |  | #ifndef U_COMMON_IMPLEMENTATION  | 
84  |  | #error U_COMMON_IMPLEMENTATION not set - must be set for all ICU source files in common/ - see https://unicode-org.github.io/icu/userguide/howtouseicu  | 
85  |  | #endif  | 
86  |  |  | 
87  |  |  | 
88  |  | /* include system headers */  | 
89  |  | #if U_PLATFORM_USES_ONLY_WIN32_API  | 
90  |  |     /*  | 
91  |  |      * TODO: U_PLATFORM_USES_ONLY_WIN32_API includes MinGW.  | 
92  |  |      * Should Cygwin be included as well (U_PLATFORM_HAS_WIN32_API)  | 
93  |  |      * to use native APIs as much as possible?  | 
94  |  |      */  | 
95  |  | #ifndef WIN32_LEAN_AND_MEAN  | 
96  |  | #   define WIN32_LEAN_AND_MEAN  | 
97  |  | #endif  | 
98  |  | #   define VC_EXTRALEAN  | 
99  |  | #   define NOUSER  | 
100  |  | #   define NOSERVICE  | 
101  |  | #   define NOIME  | 
102  |  | #   define NOMCX  | 
103  |  | #   include <windows.h>  | 
104  |  | #   include "unicode/uloc.h"  | 
105  |  | #   include "wintz.h"  | 
106  |  | #elif U_PLATFORM == U_PF_OS400  | 
107  |  | #   include <float.h>  | 
108  |  | #   include <qusec.h>       /* error code structure */  | 
109  |  | #   include <qusrjobi.h>  | 
110  |  | #   include <qliept.h>      /* EPT_CALL macro  - this include must be after all other "QSYSINCs" */  | 
111  |  | #   include <mih/testptr.h> /* For uprv_maximumPtr */  | 
112  |  | #elif U_PLATFORM == U_PF_OS390  | 
113  |  | #   include "unicode/ucnv.h"   /* Needed for UCNV_SWAP_LFNL_OPTION_STRING */  | 
114  |  | #elif U_PLATFORM_IS_DARWIN_BASED || U_PLATFORM_IS_LINUX_BASED || U_PLATFORM == U_PF_BSD || U_PLATFORM == U_PF_SOLARIS  | 
115  |  | #   include <limits.h>  | 
116  |  | #   include <unistd.h>  | 
117  |  | #   if U_PLATFORM == U_PF_SOLARIS  | 
118  |  | #       ifndef _XPG4_2  | 
119  |  | #           define _XPG4_2  | 
120  |  | #       endif  | 
121  |  | #   elif U_PLATFORM == U_PF_ANDROID  | 
122  |  | #       include <sys/system_properties.h>  | 
123  |  | #       include <dlfcn.h>  | 
124  |  | #   endif  | 
125  |  | #elif U_PLATFORM == U_PF_QNX  | 
126  |  | #   include <sys/neutrino.h>  | 
127  |  | #endif  | 
128  |  |  | 
129  |  |  | 
130  |  | /*  | 
131  |  |  * Only include langinfo.h if we have a way to get the codeset. If we later  | 
132  |  |  * depend on more feature, we can test on U_HAVE_NL_LANGINFO.  | 
133  |  |  *  | 
134  |  |  */  | 
135  |  |  | 
136  |  | #if U_HAVE_NL_LANGINFO_CODESET  | 
137  |  | #include <langinfo.h>  | 
138  |  | #endif  | 
139  |  |  | 
140  |  | /**  | 
141  |  |  * Simple things (presence of functions, etc) should just go in configure.in and be added to  | 
142  |  |  * icucfg.h via autoheader.  | 
143  |  |  */  | 
144  |  | #if U_PLATFORM_IMPLEMENTS_POSIX  | 
145  |  | #   if U_PLATFORM == U_PF_OS400  | 
146  |  | #    define HAVE_DLFCN_H 0  | 
147  |  | #    define HAVE_DLOPEN 0  | 
148  |  | #   else  | 
149  |  | #   ifndef HAVE_DLFCN_H  | 
150  |  | #    define HAVE_DLFCN_H 1  | 
151  |  | #   endif  | 
152  |  | #   ifndef HAVE_DLOPEN  | 
153  |  | #    define HAVE_DLOPEN 1  | 
154  |  | #   endif  | 
155  |  | #   endif  | 
156  |  | #   ifndef HAVE_GETTIMEOFDAY  | 
157  |  | #    define HAVE_GETTIMEOFDAY 1  | 
158  |  | #   endif  | 
159  |  | #else  | 
160  |  | #   define HAVE_DLFCN_H 0  | 
161  |  | #   define HAVE_DLOPEN 0  | 
162  |  | #   define HAVE_GETTIMEOFDAY 0  | 
163  |  | #endif  | 
164  |  |  | 
165  |  | U_NAMESPACE_USE  | 
166  |  |  | 
167  |  | /* Define the extension for data files, again... */  | 
168  |  | #define DATA_TYPE "dat"  | 
169  |  |  | 
170  |  | /* Leave this copyright notice here! */  | 
171  |  | static const char copyright[] = U_COPYRIGHT_STRING;  | 
172  |  |  | 
173  |  | /* floating point implementations ------------------------------------------- */  | 
174  |  |  | 
175  |  | /* We return QNAN rather than SNAN*/  | 
176  |  | #define SIGN 0x80000000U  | 
177  |  |  | 
178  |  | /* Make it easy to define certain types of constants */  | 
179  |  | typedef union { | 
180  |  |     int64_t i64; /* This must be defined first in order to allow the initialization to work. This is a C89 feature. */  | 
181  |  |     double d64;  | 
182  |  | } BitPatternConversion;  | 
183  |  | static const BitPatternConversion gNan = { (int64_t) INT64_C(0x7FF8000000000000) }; | 
184  |  | static const BitPatternConversion gInf = { (int64_t) INT64_C(0x7FF0000000000000) }; | 
185  |  |  | 
186  |  | /*---------------------------------------------------------------------------  | 
187  |  |   Platform utilities  | 
188  |  |   Our general strategy is to assume we're on a POSIX platform.  Platforms which  | 
189  |  |   are non-POSIX must declare themselves so.  The default POSIX implementation  | 
190  |  |   will sometimes work for non-POSIX platforms as well (e.g., the NaN-related  | 
191  |  |   functions).  | 
192  |  |   ---------------------------------------------------------------------------*/  | 
193  |  |  | 
194  |  | #if U_PLATFORM_USES_ONLY_WIN32_API || U_PLATFORM == U_PF_OS400  | 
195  |  | #   undef U_POSIX_LOCALE  | 
196  |  | #else  | 
197  |  | #   define U_POSIX_LOCALE    1  | 
198  |  | #endif  | 
199  |  |  | 
200  |  | /*  | 
201  |  |     WARNING! u_topNBytesOfDouble and u_bottomNBytesOfDouble  | 
202  |  |     can't be properly optimized by the gcc compiler sometimes (i.e. gcc 3.2).  | 
203  |  | */  | 
204  |  | #if !IEEE_754  | 
205  |  | static char*  | 
206  |  | u_topNBytesOfDouble(double* d, int n)  | 
207  |  | { | 
208  |  | #if U_IS_BIG_ENDIAN  | 
209  |  |     return (char*)d;  | 
210  |  | #else  | 
211  |  |     return (char*)(d + 1) - n;  | 
212  |  | #endif  | 
213  |  | }  | 
214  |  |  | 
215  |  | static char*  | 
216  |  | u_bottomNBytesOfDouble(double* d, int n)  | 
217  |  | { | 
218  |  | #if U_IS_BIG_ENDIAN  | 
219  |  |     return (char*)(d + 1) - n;  | 
220  |  | #else  | 
221  |  |     return (char*)d;  | 
222  |  | #endif  | 
223  |  | }  | 
224  |  | #endif   /* !IEEE_754 */  | 
225  |  |  | 
226  |  | #if IEEE_754  | 
227  |  | static UBool  | 
228  | 0  | u_signBit(double d) { | 
229  | 0  |     uint8_t hiByte;  | 
230  |  | #if U_IS_BIG_ENDIAN  | 
231  |  |     hiByte = *(uint8_t *)&d;  | 
232  |  | #else  | 
233  | 0  |     hiByte = *(((uint8_t *)&d) + sizeof(double) - 1);  | 
234  | 0  | #endif  | 
235  | 0  |     return (hiByte & 0x80) != 0;  | 
236  | 0  | }  | 
237  |  | #endif  | 
238  |  |  | 
239  |  |  | 
240  |  |  | 
241  |  | #if defined (U_DEBUG_FAKETIME)  | 
242  |  | /* Override the clock to test things without having to move the system clock.  | 
243  |  |  * Assumes POSIX gettimeofday() will function  | 
244  |  |  */  | 
245  |  | UDate fakeClock_t0 = 0; /** Time to start the clock from **/  | 
246  |  | UDate fakeClock_dt = 0; /** Offset (fake time - real time) **/  | 
247  |  | UBool fakeClock_set = FALSE; /** True if fake clock has spun up **/  | 
248  |  |  | 
249  |  | static UDate getUTCtime_real() { | 
250  |  |     struct timeval posixTime;  | 
251  |  |     gettimeofday(&posixTime, NULL);  | 
252  |  |     return (UDate)(((int64_t)posixTime.tv_sec * U_MILLIS_PER_SECOND) + (posixTime.tv_usec/1000));  | 
253  |  | }  | 
254  |  |  | 
255  |  | static UDate getUTCtime_fake() { | 
256  |  |     static UMutex fakeClockMutex;  | 
257  |  |     umtx_lock(&fakeClockMutex);  | 
258  |  |     if(!fakeClock_set) { | 
259  |  |         UDate real = getUTCtime_real();  | 
260  |  |         const char *fake_start = getenv("U_FAKETIME_START"); | 
261  |  |         if((fake_start!=NULL) && (fake_start[0]!=0)) { | 
262  |  |             sscanf(fake_start,"%lf",&fakeClock_t0);  | 
263  |  |             fakeClock_dt = fakeClock_t0 - real;  | 
264  |  |             fprintf(stderr,"U_DEBUG_FAKETIME was set at compile time, so the ICU clock will start at a preset value\n"  | 
265  |  |                     "env variable U_FAKETIME_START=%.0f (%s) for an offset of %.0f ms from the current time %.0f\n",  | 
266  |  |                     fakeClock_t0, fake_start, fakeClock_dt, real);  | 
267  |  |         } else { | 
268  |  |           fakeClock_dt = 0;  | 
269  |  |             fprintf(stderr,"U_DEBUG_FAKETIME was set at compile time, but U_FAKETIME_START was not set.\n"  | 
270  |  |                     "Set U_FAKETIME_START to the number of milliseconds since 1/1/1970 to set the ICU clock.\n");  | 
271  |  |         }  | 
272  |  |         fakeClock_set = TRUE;  | 
273  |  |     }  | 
274  |  |     umtx_unlock(&fakeClockMutex);  | 
275  |  |  | 
276  |  |     return getUTCtime_real() + fakeClock_dt;  | 
277  |  | }  | 
278  |  | #endif  | 
279  |  |  | 
280  |  | #if U_PLATFORM_USES_ONLY_WIN32_API  | 
281  |  | typedef union { | 
282  |  |     int64_t int64;  | 
283  |  |     FILETIME fileTime;  | 
284  |  | } FileTimeConversion;   /* This is like a ULARGE_INTEGER */  | 
285  |  |  | 
286  |  | /* Number of 100 nanoseconds from 1/1/1601 to 1/1/1970 */  | 
287  |  | #define EPOCH_BIAS  INT64_C(116444736000000000)  | 
288  |  | #define HECTONANOSECOND_PER_MILLISECOND   10000  | 
289  |  |  | 
290  |  | #endif  | 
291  |  |  | 
292  |  | /*---------------------------------------------------------------------------  | 
293  |  |   Universal Implementations  | 
294  |  |   These are designed to work on all platforms.  Try these, and if they  | 
295  |  |   don't work on your platform, then special case your platform with new  | 
296  |  |   implementations.  | 
297  |  | ---------------------------------------------------------------------------*/  | 
298  |  |  | 
299  |  | U_CAPI UDate U_EXPORT2  | 
300  |  | uprv_getUTCtime()  | 
301  | 0  | { | 
302  |  | #if defined(U_DEBUG_FAKETIME)  | 
303  |  |     return getUTCtime_fake(); /* Hook for overriding the clock */  | 
304  |  | #else  | 
305  | 0  |     return uprv_getRawUTCtime();  | 
306  | 0  | #endif  | 
307  | 0  | }  | 
308  |  |  | 
309  |  | /* Return UTC (GMT) time measured in milliseconds since 0:00 on 1/1/70.*/  | 
310  |  | U_CAPI UDate U_EXPORT2  | 
311  |  | uprv_getRawUTCtime()  | 
312  | 0  | { | 
313  |  | #if U_PLATFORM_USES_ONLY_WIN32_API  | 
314  |  |  | 
315  |  |     FileTimeConversion winTime;  | 
316  |  |     GetSystemTimeAsFileTime(&winTime.fileTime);  | 
317  |  |     return (UDate)((winTime.int64 - EPOCH_BIAS) / HECTONANOSECOND_PER_MILLISECOND);  | 
318  |  | #else  | 
319  |  | 
  | 
320  | 0  | #if HAVE_GETTIMEOFDAY  | 
321  | 0  |     struct timeval posixTime;  | 
322  | 0  |     gettimeofday(&posixTime, NULL);  | 
323  | 0  |     return (UDate)(((int64_t)posixTime.tv_sec * U_MILLIS_PER_SECOND) + (posixTime.tv_usec/1000));  | 
324  |  | #else  | 
325  |  |     time_t epochtime;  | 
326  |  |     time(&epochtime);  | 
327  |  |     return (UDate)epochtime * U_MILLIS_PER_SECOND;  | 
328  |  | #endif  | 
329  |  | 
  | 
330  | 0  | #endif  | 
331  | 0  | }  | 
332  |  |  | 
333  |  | /*-----------------------------------------------------------------------------  | 
334  |  |   IEEE 754  | 
335  |  |   These methods detect and return NaN and infinity values for doubles  | 
336  |  |   conforming to IEEE 754.  Platforms which support this standard include X86,  | 
337  |  |   Mac 680x0, Mac PowerPC, AIX RS/6000, and most others.  | 
338  |  |   If this doesn't work on your platform, you have non-IEEE floating-point, and  | 
339  |  |   will need to code your own versions.  A naive implementation is to return 0.0  | 
340  |  |   for getNaN and getInfinity, and false for isNaN and isInfinite.  | 
341  |  |   ---------------------------------------------------------------------------*/  | 
342  |  |  | 
343  |  | U_CAPI UBool U_EXPORT2  | 
344  |  | uprv_isNaN(double number)  | 
345  | 0  | { | 
346  | 0  | #if IEEE_754  | 
347  | 0  |     BitPatternConversion convertedNumber;  | 
348  | 0  |     convertedNumber.d64 = number;  | 
349  |  |     /* Infinity is 0x7FF0000000000000U. Anything greater than that is a NaN */  | 
350  | 0  |     return (UBool)((convertedNumber.i64 & U_INT64_MAX) > gInf.i64);  | 
351  |  | 
  | 
352  |  | #elif U_PLATFORM == U_PF_OS390  | 
353  |  |     uint32_t highBits = *(uint32_t*)u_topNBytesOfDouble(&number,  | 
354  |  |                         sizeof(uint32_t));  | 
355  |  |     uint32_t lowBits  = *(uint32_t*)u_bottomNBytesOfDouble(&number,  | 
356  |  |                         sizeof(uint32_t));  | 
357  |  |  | 
358  |  |     return ((highBits & 0x7F080000L) == 0x7F080000L) &&  | 
359  |  |       (lowBits == 0x00000000L);  | 
360  |  |  | 
361  |  | #else  | 
362  |  |     /* If your platform doesn't support IEEE 754 but *does* have an NaN value,*/  | 
363  |  |     /* you'll need to replace this default implementation with what's correct*/  | 
364  |  |     /* for your platform.*/  | 
365  |  |     return number != number;  | 
366  |  | #endif  | 
367  | 0  | }  | 
368  |  |  | 
369  |  | U_CAPI UBool U_EXPORT2  | 
370  |  | uprv_isInfinite(double number)  | 
371  | 0  | { | 
372  | 0  | #if IEEE_754  | 
373  | 0  |     BitPatternConversion convertedNumber;  | 
374  | 0  |     convertedNumber.d64 = number;  | 
375  |  |     /* Infinity is exactly 0x7FF0000000000000U. */  | 
376  | 0  |     return (UBool)((convertedNumber.i64 & U_INT64_MAX) == gInf.i64);  | 
377  |  | #elif U_PLATFORM == U_PF_OS390  | 
378  |  |     uint32_t highBits = *(uint32_t*)u_topNBytesOfDouble(&number,  | 
379  |  |                         sizeof(uint32_t));  | 
380  |  |     uint32_t lowBits  = *(uint32_t*)u_bottomNBytesOfDouble(&number,  | 
381  |  |                         sizeof(uint32_t));  | 
382  |  |  | 
383  |  |     return ((highBits  & ~SIGN) == 0x70FF0000L) && (lowBits == 0x00000000L);  | 
384  |  |  | 
385  |  | #else  | 
386  |  |     /* If your platform doesn't support IEEE 754 but *does* have an infinity*/  | 
387  |  |     /* value, you'll need to replace this default implementation with what's*/  | 
388  |  |     /* correct for your platform.*/  | 
389  |  |     return number == (2.0 * number);  | 
390  |  | #endif  | 
391  | 0  | }  | 
392  |  |  | 
393  |  | U_CAPI UBool U_EXPORT2  | 
394  |  | uprv_isPositiveInfinity(double number)  | 
395  | 0  | { | 
396  | 0  | #if IEEE_754 || U_PLATFORM == U_PF_OS390  | 
397  | 0  |     return (UBool)(number > 0 && uprv_isInfinite(number));  | 
398  |  | #else  | 
399  |  |     return uprv_isInfinite(number);  | 
400  |  | #endif  | 
401  | 0  | }  | 
402  |  |  | 
403  |  | U_CAPI UBool U_EXPORT2  | 
404  |  | uprv_isNegativeInfinity(double number)  | 
405  | 0  | { | 
406  | 0  | #if IEEE_754 || U_PLATFORM == U_PF_OS390  | 
407  | 0  |     return (UBool)(number < 0 && uprv_isInfinite(number));  | 
408  |  | 
  | 
409  |  | #else  | 
410  |  |     uint32_t highBits = *(uint32_t*)u_topNBytesOfDouble(&number,  | 
411  |  |                         sizeof(uint32_t));  | 
412  |  |     return((highBits & SIGN) && uprv_isInfinite(number));  | 
413  |  |  | 
414  |  | #endif  | 
415  | 0  | }  | 
416  |  |  | 
417  |  | U_CAPI double U_EXPORT2  | 
418  |  | uprv_getNaN()  | 
419  | 0  | { | 
420  | 0  | #if IEEE_754 || U_PLATFORM == U_PF_OS390  | 
421  | 0  |     return gNan.d64;  | 
422  |  | #else  | 
423  |  |     /* If your platform doesn't support IEEE 754 but *does* have an NaN value,*/  | 
424  |  |     /* you'll need to replace this default implementation with what's correct*/  | 
425  |  |     /* for your platform.*/  | 
426  |  |     return 0.0;  | 
427  |  | #endif  | 
428  | 0  | }  | 
429  |  |  | 
430  |  | U_CAPI double U_EXPORT2  | 
431  |  | uprv_getInfinity()  | 
432  | 0  | { | 
433  | 0  | #if IEEE_754 || U_PLATFORM == U_PF_OS390  | 
434  | 0  |     return gInf.d64;  | 
435  |  | #else  | 
436  |  |     /* If your platform doesn't support IEEE 754 but *does* have an infinity*/  | 
437  |  |     /* value, you'll need to replace this default implementation with what's*/  | 
438  |  |     /* correct for your platform.*/  | 
439  |  |     return 0.0;  | 
440  |  | #endif  | 
441  | 0  | }  | 
442  |  |  | 
443  |  | U_CAPI double U_EXPORT2  | 
444  |  | uprv_floor(double x)  | 
445  | 0  | { | 
446  | 0  |     return floor(x);  | 
447  | 0  | }  | 
448  |  |  | 
449  |  | U_CAPI double U_EXPORT2  | 
450  |  | uprv_ceil(double x)  | 
451  | 0  | { | 
452  | 0  |     return ceil(x);  | 
453  | 0  | }  | 
454  |  |  | 
455  |  | U_CAPI double U_EXPORT2  | 
456  |  | uprv_round(double x)  | 
457  | 0  | { | 
458  | 0  |     return uprv_floor(x + 0.5);  | 
459  | 0  | }  | 
460  |  |  | 
461  |  | U_CAPI double U_EXPORT2  | 
462  |  | uprv_fabs(double x)  | 
463  | 0  | { | 
464  | 0  |     return fabs(x);  | 
465  | 0  | }  | 
466  |  |  | 
467  |  | U_CAPI double U_EXPORT2  | 
468  |  | uprv_modf(double x, double* y)  | 
469  | 0  | { | 
470  | 0  |     return modf(x, y);  | 
471  | 0  | }  | 
472  |  |  | 
473  |  | U_CAPI double U_EXPORT2  | 
474  |  | uprv_fmod(double x, double y)  | 
475  | 0  | { | 
476  | 0  |     return fmod(x, y);  | 
477  | 0  | }  | 
478  |  |  | 
479  |  | U_CAPI double U_EXPORT2  | 
480  |  | uprv_pow(double x, double y)  | 
481  | 0  | { | 
482  |  |     /* This is declared as "double pow(double x, double y)" */  | 
483  | 0  |     return pow(x, y);  | 
484  | 0  | }  | 
485  |  |  | 
486  |  | U_CAPI double U_EXPORT2  | 
487  |  | uprv_pow10(int32_t x)  | 
488  | 0  | { | 
489  | 0  |     return pow(10.0, (double)x);  | 
490  | 0  | }  | 
491  |  |  | 
492  |  | U_CAPI double U_EXPORT2  | 
493  |  | uprv_fmax(double x, double y)  | 
494  | 0  | { | 
495  | 0  | #if IEEE_754  | 
496  |  |     /* first handle NaN*/  | 
497  | 0  |     if(uprv_isNaN(x) || uprv_isNaN(y))  | 
498  | 0  |         return uprv_getNaN();  | 
499  |  |  | 
500  |  |     /* check for -0 and 0*/  | 
501  | 0  |     if(x == 0.0 && y == 0.0 && u_signBit(x))  | 
502  | 0  |         return y;  | 
503  |  |  | 
504  | 0  | #endif  | 
505  |  |  | 
506  |  |     /* this should work for all flt point w/o NaN and Inf special cases */  | 
507  | 0  |     return (x > y ? x : y);  | 
508  | 0  | }  | 
509  |  |  | 
510  |  | U_CAPI double U_EXPORT2  | 
511  |  | uprv_fmin(double x, double y)  | 
512  | 0  | { | 
513  | 0  | #if IEEE_754  | 
514  |  |     /* first handle NaN*/  | 
515  | 0  |     if(uprv_isNaN(x) || uprv_isNaN(y))  | 
516  | 0  |         return uprv_getNaN();  | 
517  |  |  | 
518  |  |     /* check for -0 and 0*/  | 
519  | 0  |     if(x == 0.0 && y == 0.0 && u_signBit(y))  | 
520  | 0  |         return y;  | 
521  |  |  | 
522  | 0  | #endif  | 
523  |  |  | 
524  |  |     /* this should work for all flt point w/o NaN and Inf special cases */  | 
525  | 0  |     return (x > y ? y : x);  | 
526  | 0  | }  | 
527  |  |  | 
528  |  | U_CAPI UBool U_EXPORT2  | 
529  | 0  | uprv_add32_overflow(int32_t a, int32_t b, int32_t* res) { | 
530  |  |     // NOTE: Some compilers (GCC, Clang) have primitives available, like __builtin_add_overflow.  | 
531  |  |     // This function could be optimized by calling one of those primitives.  | 
532  | 0  |     auto a64 = static_cast<int64_t>(a);  | 
533  | 0  |     auto b64 = static_cast<int64_t>(b);  | 
534  | 0  |     int64_t res64 = a64 + b64;  | 
535  | 0  |     *res = static_cast<int32_t>(res64);  | 
536  | 0  |     return res64 != *res;  | 
537  | 0  | }  | 
538  |  |  | 
539  |  | U_CAPI UBool U_EXPORT2  | 
540  | 0  | uprv_mul32_overflow(int32_t a, int32_t b, int32_t* res) { | 
541  |  |     // NOTE: Some compilers (GCC, Clang) have primitives available, like __builtin_mul_overflow.  | 
542  |  |     // This function could be optimized by calling one of those primitives.  | 
543  | 0  |     auto a64 = static_cast<int64_t>(a);  | 
544  | 0  |     auto b64 = static_cast<int64_t>(b);  | 
545  | 0  |     int64_t res64 = a64 * b64;  | 
546  | 0  |     *res = static_cast<int32_t>(res64);  | 
547  | 0  |     return res64 != *res;  | 
548  | 0  | }  | 
549  |  |  | 
550  |  | /**  | 
551  |  |  * Truncates the given double.  | 
552  |  |  * trunc(3.3) = 3.0, trunc (-3.3) = -3.0  | 
553  |  |  * This is different than calling floor() or ceil():  | 
554  |  |  * floor(3.3) = 3, floor(-3.3) = -4  | 
555  |  |  * ceil(3.3) = 4, ceil(-3.3) = -3  | 
556  |  |  */  | 
557  |  | U_CAPI double U_EXPORT2  | 
558  |  | uprv_trunc(double d)  | 
559  | 0  | { | 
560  | 0  | #if IEEE_754  | 
561  |  |     /* handle error cases*/  | 
562  | 0  |     if(uprv_isNaN(d))  | 
563  | 0  |         return uprv_getNaN();  | 
564  | 0  |     if(uprv_isInfinite(d))  | 
565  | 0  |         return uprv_getInfinity();  | 
566  |  |  | 
567  | 0  |     if(u_signBit(d))    /* Signbit() picks up -0.0;  d<0 does not. */  | 
568  | 0  |         return ceil(d);  | 
569  | 0  |     else  | 
570  | 0  |         return floor(d);  | 
571  |  | 
  | 
572  |  | #else  | 
573  |  |     return d >= 0 ? floor(d) : ceil(d);  | 
574  |  |  | 
575  |  | #endif  | 
576  | 0  | }  | 
577  |  |  | 
578  |  | /**  | 
579  |  |  * Return the largest positive number that can be represented by an integer  | 
580  |  |  * type of arbitrary bit length.  | 
581  |  |  */  | 
582  |  | U_CAPI double U_EXPORT2  | 
583  |  | uprv_maxMantissa(void)  | 
584  | 0  | { | 
585  | 0  |     return pow(2.0, DBL_MANT_DIG + 1.0) - 1.0;  | 
586  | 0  | }  | 
587  |  |  | 
588  |  | U_CAPI double U_EXPORT2  | 
589  |  | uprv_log(double d)  | 
590  | 0  | { | 
591  | 0  |     return log(d);  | 
592  | 0  | }  | 
593  |  |  | 
594  |  | U_CAPI void * U_EXPORT2  | 
595  |  | uprv_maximumPtr(void * base)  | 
596  | 0  | { | 
597  |  | #if U_PLATFORM == U_PF_OS400  | 
598  |  |     /*  | 
599  |  |      * With the provided function we should never be out of range of a given segment  | 
600  |  |      * (a traditional/typical segment that is).  Our segments have 5 bytes for the  | 
601  |  |      * id and 3 bytes for the offset.  The key is that the casting takes care of  | 
602  |  |      * only retrieving the offset portion minus x1000.  Hence, the smallest offset  | 
603  |  |      * seen in a program is x001000 and when casted to an int would be 0.  | 
604  |  |      * That's why we can only add 0xffefff.  Otherwise, we would exceed the segment.  | 
605  |  |      *  | 
606  |  |      * Currently, 16MB is the current addressing limitation on i5/OS if the activation is  | 
607  |  |      * non-TERASPACE.  If it is TERASPACE it is 2GB - 4k(header information).  | 
608  |  |      * This function determines the activation based on the pointer that is passed in and  | 
609  |  |      * calculates the appropriate maximum available size for  | 
610  |  |      * each pointer type (TERASPACE and non-TERASPACE)  | 
611  |  |      *  | 
612  |  |      * Unlike other operating systems, the pointer model isn't determined at  | 
613  |  |      * compile time on i5/OS.  | 
614  |  |      */  | 
615  |  |     if ((base != NULL) && (_TESTPTR(base, _C_TERASPACE_CHECK))) { | 
616  |  |         /* if it is a TERASPACE pointer the max is 2GB - 4k */  | 
617  |  |         return ((void *)(((char *)base)-((uint32_t)(base))+((uint32_t)0x7fffefff)));  | 
618  |  |     }  | 
619  |  |     /* otherwise 16MB since NULL ptr is not checkable or the ptr is not TERASPACE */  | 
620  |  |     return ((void *)(((char *)base)-((uint32_t)(base))+((uint32_t)0xffefff)));  | 
621  |  |  | 
622  |  | #else  | 
623  | 0  |     return U_MAX_PTR(base);  | 
624  | 0  | #endif  | 
625  | 0  | }  | 
626  |  |  | 
627  |  | /*---------------------------------------------------------------------------  | 
628  |  |   Platform-specific Implementations  | 
629  |  |   Try these, and if they don't work on your platform, then special case your  | 
630  |  |   platform with new implementations.  | 
631  |  |   ---------------------------------------------------------------------------*/  | 
632  |  |  | 
633  |  | /* Generic time zone layer -------------------------------------------------- */  | 
634  |  |  | 
635  |  | /* Time zone utilities */  | 
636  |  | U_CAPI void U_EXPORT2  | 
637  |  | uprv_tzset()  | 
638  | 0  | { | 
639  | 0  | #if defined(U_TZSET)  | 
640  | 0  |     U_TZSET();  | 
641  |  | #else  | 
642  |  |     /* no initialization*/  | 
643  |  | #endif  | 
644  | 0  | }  | 
645  |  |  | 
646  |  | U_CAPI int32_t U_EXPORT2  | 
647  |  | uprv_timezone()  | 
648  | 0  | { | 
649  | 0  | #ifdef U_TIMEZONE  | 
650  | 0  |     return U_TIMEZONE;  | 
651  |  | #else  | 
652  |  |     time_t t, t1, t2;  | 
653  |  |     struct tm tmrec;  | 
654  |  |     int32_t tdiff = 0;  | 
655  |  |  | 
656  |  |     time(&t);  | 
657  |  |     uprv_memcpy( &tmrec, localtime(&t), sizeof(tmrec) );  | 
658  |  | #if U_PLATFORM != U_PF_IPHONE  | 
659  |  |     UBool dst_checked = (tmrec.tm_isdst != 0); /* daylight savings time is checked*/  | 
660  |  | #endif  | 
661  |  |     t1 = mktime(&tmrec);                 /* local time in seconds*/  | 
662  |  |     uprv_memcpy( &tmrec, gmtime(&t), sizeof(tmrec) );  | 
663  |  |     t2 = mktime(&tmrec);                 /* GMT (or UTC) in seconds*/  | 
664  |  |     tdiff = t2 - t1;  | 
665  |  |  | 
666  |  | #if U_PLATFORM != U_PF_IPHONE  | 
667  |  |     /* imitate NT behaviour, which returns same timezone offset to GMT for  | 
668  |  |        winter and summer.  | 
669  |  |        This does not work on all platforms. For instance, on glibc on Linux  | 
670  |  |        and on Mac OS 10.5, tdiff calculated above remains the same  | 
671  |  |        regardless of whether DST is in effect or not. iOS is another  | 
672  |  |        platform where this does not work. Linux + glibc and Mac OS 10.5  | 
673  |  |        have U_TIMEZONE defined so that this code is not reached.  | 
674  |  |     */  | 
675  |  |     if (dst_checked)  | 
676  |  |         tdiff += 3600;  | 
677  |  | #endif  | 
678  |  |     return tdiff;  | 
679  |  | #endif  | 
680  | 0  | }  | 
681  |  |  | 
682  |  | /* Note that U_TZNAME does *not* have to be tzname, but if it is,  | 
683  |  |    some platforms need to have it declared here. */  | 
684  |  |  | 
685  |  | #if defined(U_TZNAME) && (U_PLATFORM == U_PF_IRIX || U_PLATFORM_IS_DARWIN_BASED)  | 
686  |  | /* RS6000 and others reject char **tzname.  */  | 
687  |  | extern U_IMPORT char *U_TZNAME[];  | 
688  |  | #endif  | 
689  |  |  | 
690  |  | #if !UCONFIG_NO_FILE_IO && ((U_PLATFORM_IS_DARWIN_BASED && (U_PLATFORM != U_PF_IPHONE || defined(U_TIMEZONE))) || U_PLATFORM_IS_LINUX_BASED || U_PLATFORM == U_PF_BSD || U_PLATFORM == U_PF_SOLARIS)  | 
691  |  | /* These platforms are likely to use Olson timezone IDs. */  | 
692  |  | /* common targets of the symbolic link at TZDEFAULT are:  | 
693  |  |  * "/usr/share/zoneinfo/<olsonID>" default, older Linux distros, macOS to 10.12  | 
694  |  |  * "../usr/share/zoneinfo/<olsonID>" newer Linux distros: Red Hat Enterprise Linux 7, Ubuntu 16, SuSe Linux 12  | 
695  |  |  * "/usr/share/lib/zoneinfo/<olsonID>" Solaris  | 
696  |  |  * "../usr/share/lib/zoneinfo/<olsonID>" Solaris  | 
697  |  |  * "/var/db/timezone/zoneinfo/<olsonID>" macOS 10.13  | 
698  |  |  * To avoid checking lots of paths, just check that the target path  | 
699  |  |  * before the <olsonID> ends with "/zoneinfo/", and the <olsonID> is valid.  | 
700  |  |  */  | 
701  |  |  | 
702  |  | #define CHECK_LOCALTIME_LINK 1  | 
703  |  | #if U_PLATFORM_IS_DARWIN_BASED  | 
704  |  | #include <tzfile.h>  | 
705  |  | #define TZZONEINFO      (TZDIR "/")  | 
706  |  | #elif U_PLATFORM == U_PF_SOLARIS  | 
707  |  | #define TZDEFAULT       "/etc/localtime"  | 
708  |  | #define TZZONEINFO      "/usr/share/lib/zoneinfo/"  | 
709  |  | #define TZ_ENV_CHECK    "localtime"  | 
710  |  | #else  | 
711  | 0  | #define TZDEFAULT       "/etc/localtime"  | 
712  | 0  | #define TZZONEINFO      "/usr/share/zoneinfo/"  | 
713  |  | #endif  | 
714  |  | #define TZZONEINFOTAIL  "/zoneinfo/"  | 
715  |  | #if U_HAVE_DIRENT_H  | 
716  |  | #define TZFILE_SKIP     "posixrules" /* tz file to skip when searching. */  | 
717  |  | /* Some Linux distributions have 'localtime' in /usr/share/zoneinfo  | 
718  |  |    symlinked to /etc/localtime, which makes searchForTZFile return  | 
719  |  |    'localtime' when it's the first match. */  | 
720  |  | #define TZFILE_SKIP2    "localtime"  | 
721  |  | #define SEARCH_TZFILE  | 
722  |  | #include <dirent.h>  /* Needed to search through system timezone files */  | 
723  |  | #endif  | 
724  |  | static char gTimeZoneBuffer[PATH_MAX];  | 
725  |  | static char *gTimeZoneBufferPtr = NULL;  | 
726  |  | #endif  | 
727  |  |  | 
728  |  | #if !U_PLATFORM_USES_ONLY_WIN32_API  | 
729  | 0  | #define isNonDigit(ch) (ch < '0' || '9' < ch)  | 
730  | 0  | static UBool isValidOlsonID(const char *id) { | 
731  | 0  |     int32_t idx = 0;  | 
732  |  |  | 
733  |  |     /* Determine if this is something like Iceland (Olson ID)  | 
734  |  |     or AST4ADT (non-Olson ID) */  | 
735  | 0  |     while (id[idx] && isNonDigit(id[idx]) && id[idx] != ',') { | 
736  | 0  |         idx++;  | 
737  | 0  |     }  | 
738  |  |  | 
739  |  |     /* If we went through the whole string, then it might be okay.  | 
740  |  |     The timezone is sometimes set to "CST-7CDT", "CST6CDT5,J129,J131/19:30",  | 
741  |  |     "GRNLNDST3GRNLNDDT" or similar, so we cannot use it.  | 
742  |  |     The rest of the time it could be an Olson ID. George */  | 
743  | 0  |     return (UBool)(id[idx] == 0  | 
744  | 0  |         || uprv_strcmp(id, "PST8PDT") == 0  | 
745  | 0  |         || uprv_strcmp(id, "MST7MDT") == 0  | 
746  | 0  |         || uprv_strcmp(id, "CST6CDT") == 0  | 
747  | 0  |         || uprv_strcmp(id, "EST5EDT") == 0);  | 
748  | 0  | }  | 
749  |  |  | 
750  |  | /* On some Unix-like OS, 'posix' subdirectory in  | 
751  |  |    /usr/share/zoneinfo replicates the top-level contents. 'right'  | 
752  |  |    subdirectory has the same set of files, but individual files  | 
753  |  |    are different from those in the top-level directory or 'posix'  | 
754  |  |    because 'right' has files for TAI (Int'l Atomic Time) while 'posix'  | 
755  |  |    has files for UTC.  | 
756  |  |    When the first match for /etc/localtime is in either of them  | 
757  |  |    (usually in posix because 'right' has different file contents),  | 
758  |  |    or TZ environment variable points to one of them, createTimeZone  | 
759  |  |    fails because, say, 'posix/America/New_York' is not an Olson  | 
760  |  |    timezone id ('America/New_York' is). So, we have to skip | 
761  |  |    'posix/' and 'right/' at the beginning. */  | 
762  | 0  | static void skipZoneIDPrefix(const char** id) { | 
763  | 0  |     if (uprv_strncmp(*id, "posix/", 6) == 0  | 
764  | 0  |         || uprv_strncmp(*id, "right/", 6) == 0)  | 
765  | 0  |     { | 
766  | 0  |         *id += 6;  | 
767  | 0  |     }  | 
768  | 0  | }  | 
769  |  | #endif  | 
770  |  |  | 
771  |  | #if defined(U_TZNAME) && !U_PLATFORM_USES_ONLY_WIN32_API  | 
772  |  |  | 
773  |  | #define CONVERT_HOURS_TO_SECONDS(offset) (int32_t)(offset*3600)  | 
774  |  | typedef struct OffsetZoneMapping { | 
775  |  |     int32_t offsetSeconds;  | 
776  |  |     int32_t daylightType; /* 0=U_DAYLIGHT_NONE, 1=daylight in June-U_DAYLIGHT_JUNE, 2=daylight in December=U_DAYLIGHT_DECEMBER*/  | 
777  |  |     const char *stdID;  | 
778  |  |     const char *dstID;  | 
779  |  |     const char *olsonID;  | 
780  |  | } OffsetZoneMapping;  | 
781  |  |  | 
782  |  | enum { U_DAYLIGHT_NONE=0,U_DAYLIGHT_JUNE=1,U_DAYLIGHT_DECEMBER=2 }; | 
783  |  |  | 
784  |  | /*  | 
785  |  | This list tries to disambiguate a set of abbreviated timezone IDs and offsets  | 
786  |  | and maps it to an Olson ID.  | 
787  |  | Before adding anything to this list, take a look at  | 
788  |  | icu/source/tools/tzcode/tz.alias  | 
789  |  | Sometimes no daylight savings (0) is important to define due to aliases.  | 
790  |  | This list can be tested with icu/source/test/compat/tzone.pl  | 
791  |  | More values could be added to daylightType to increase precision.  | 
792  |  | */  | 
793  |  | static const struct OffsetZoneMapping OFFSET_ZONE_MAPPINGS[] = { | 
794  |  |     {-45900, 2, "CHAST", "CHADT", "Pacific/Chatham"}, | 
795  |  |     {-43200, 1, "PETT", "PETST", "Asia/Kamchatka"}, | 
796  |  |     {-43200, 2, "NZST", "NZDT", "Pacific/Auckland"}, | 
797  |  |     {-43200, 1, "ANAT", "ANAST", "Asia/Anadyr"}, | 
798  |  |     {-39600, 1, "MAGT", "MAGST", "Asia/Magadan"}, | 
799  |  |     {-37800, 2, "LHST", "LHST", "Australia/Lord_Howe"}, | 
800  |  |     {-36000, 2, "EST", "EST", "Australia/Sydney"}, | 
801  |  |     {-36000, 1, "SAKT", "SAKST", "Asia/Sakhalin"}, | 
802  |  |     {-36000, 1, "VLAT", "VLAST", "Asia/Vladivostok"}, | 
803  |  |     {-34200, 2, "CST", "CST", "Australia/South"}, | 
804  |  |     {-32400, 1, "YAKT", "YAKST", "Asia/Yakutsk"}, | 
805  |  |     {-32400, 1, "CHOT", "CHOST", "Asia/Choibalsan"}, | 
806  |  |     {-31500, 2, "CWST", "CWST", "Australia/Eucla"}, | 
807  |  |     {-28800, 1, "IRKT", "IRKST", "Asia/Irkutsk"}, | 
808  |  |     {-28800, 1, "ULAT", "ULAST", "Asia/Ulaanbaatar"}, | 
809  |  |     {-28800, 2, "WST", "WST", "Australia/West"}, | 
810  |  |     {-25200, 1, "HOVT", "HOVST", "Asia/Hovd"}, | 
811  |  |     {-25200, 1, "KRAT", "KRAST", "Asia/Krasnoyarsk"}, | 
812  |  |     {-21600, 1, "NOVT", "NOVST", "Asia/Novosibirsk"}, | 
813  |  |     {-21600, 1, "OMST", "OMSST", "Asia/Omsk"}, | 
814  |  |     {-18000, 1, "YEKT", "YEKST", "Asia/Yekaterinburg"}, | 
815  |  |     {-14400, 1, "SAMT", "SAMST", "Europe/Samara"}, | 
816  |  |     {-14400, 1, "AMT", "AMST", "Asia/Yerevan"}, | 
817  |  |     {-14400, 1, "AZT", "AZST", "Asia/Baku"}, | 
818  |  |     {-10800, 1, "AST", "ADT", "Asia/Baghdad"}, | 
819  |  |     {-10800, 1, "MSK", "MSD", "Europe/Moscow"}, | 
820  |  |     {-10800, 1, "VOLT", "VOLST", "Europe/Volgograd"}, | 
821  |  |     {-7200, 0, "EET", "CEST", "Africa/Tripoli"}, | 
822  |  |     {-7200, 1, "EET", "EEST", "Europe/Athens"}, /* Conflicts with Africa/Cairo */ | 
823  |  |     {-7200, 1, "IST", "IDT", "Asia/Jerusalem"}, | 
824  |  |     {-3600, 0, "CET", "WEST", "Africa/Algiers"}, | 
825  |  |     {-3600, 2, "WAT", "WAST", "Africa/Windhoek"}, | 
826  |  |     {0, 1, "GMT", "IST", "Europe/Dublin"}, | 
827  |  |     {0, 1, "GMT", "BST", "Europe/London"}, | 
828  |  |     {0, 0, "WET", "WEST", "Africa/Casablanca"}, | 
829  |  |     {0, 0, "WET", "WET", "Africa/El_Aaiun"}, | 
830  |  |     {3600, 1, "AZOT", "AZOST", "Atlantic/Azores"}, | 
831  |  |     {3600, 1, "EGT", "EGST", "America/Scoresbysund"}, | 
832  |  |     {10800, 1, "PMST", "PMDT", "America/Miquelon"}, | 
833  |  |     {10800, 2, "UYT", "UYST", "America/Montevideo"}, | 
834  |  |     {10800, 1, "WGT", "WGST", "America/Godthab"}, | 
835  |  |     {10800, 2, "BRT", "BRST", "Brazil/East"}, | 
836  |  |     {12600, 1, "NST", "NDT", "America/St_Johns"}, | 
837  |  |     {14400, 1, "AST", "ADT", "Canada/Atlantic"}, | 
838  |  |     {14400, 2, "AMT", "AMST", "America/Cuiaba"}, | 
839  |  |     {14400, 2, "CLT", "CLST", "Chile/Continental"}, | 
840  |  |     {14400, 2, "FKT", "FKST", "Atlantic/Stanley"}, | 
841  |  |     {14400, 2, "PYT", "PYST", "America/Asuncion"}, | 
842  |  |     {18000, 1, "CST", "CDT", "America/Havana"}, | 
843  |  |     {18000, 1, "EST", "EDT", "US/Eastern"}, /* Conflicts with America/Grand_Turk */ | 
844  |  |     {21600, 2, "EAST", "EASST", "Chile/EasterIsland"}, | 
845  |  |     {21600, 0, "CST", "MDT", "Canada/Saskatchewan"}, | 
846  |  |     {21600, 0, "CST", "CDT", "America/Guatemala"}, | 
847  |  |     {21600, 1, "CST", "CDT", "US/Central"}, /* Conflicts with Mexico/General */ | 
848  |  |     {25200, 1, "MST", "MDT", "US/Mountain"}, /* Conflicts with Mexico/BajaSur */ | 
849  |  |     {28800, 0, "PST", "PST", "Pacific/Pitcairn"}, | 
850  |  |     {28800, 1, "PST", "PDT", "US/Pacific"}, /* Conflicts with Mexico/BajaNorte */ | 
851  |  |     {32400, 1, "AKST", "AKDT", "US/Alaska"}, | 
852  |  |     {36000, 1, "HAST", "HADT", "US/Aleutian"} | 
853  |  | };  | 
854  |  |  | 
855  |  | /*#define DEBUG_TZNAME*/  | 
856  |  |  | 
857  |  | static const char* remapShortTimeZone(const char *stdID, const char *dstID, int32_t daylightType, int32_t offset)  | 
858  | 0  | { | 
859  | 0  |     int32_t idx;  | 
860  |  | #ifdef DEBUG_TZNAME  | 
861  |  |     fprintf(stderr, "TZ=%s std=%s dst=%s daylight=%d offset=%d\n", getenv("TZ"), stdID, dstID, daylightType, offset); | 
862  |  | #endif  | 
863  | 0  |     for (idx = 0; idx < UPRV_LENGTHOF(OFFSET_ZONE_MAPPINGS); idx++)  | 
864  | 0  |     { | 
865  | 0  |         if (offset == OFFSET_ZONE_MAPPINGS[idx].offsetSeconds  | 
866  | 0  |             && daylightType == OFFSET_ZONE_MAPPINGS[idx].daylightType  | 
867  | 0  |             && strcmp(OFFSET_ZONE_MAPPINGS[idx].stdID, stdID) == 0  | 
868  | 0  |             && strcmp(OFFSET_ZONE_MAPPINGS[idx].dstID, dstID) == 0)  | 
869  | 0  |         { | 
870  | 0  |             return OFFSET_ZONE_MAPPINGS[idx].olsonID;  | 
871  | 0  |         }  | 
872  | 0  |     }  | 
873  | 0  |     return NULL;  | 
874  | 0  | }  | 
875  |  | #endif  | 
876  |  |  | 
877  |  | #ifdef SEARCH_TZFILE  | 
878  | 0  | #define MAX_READ_SIZE 512  | 
879  |  |  | 
880  |  | typedef struct DefaultTZInfo { | 
881  |  |     char* defaultTZBuffer;  | 
882  |  |     int64_t defaultTZFileSize;  | 
883  |  |     FILE* defaultTZFilePtr;  | 
884  |  |     UBool defaultTZstatus;  | 
885  |  |     int32_t defaultTZPosition;  | 
886  |  | } DefaultTZInfo;  | 
887  |  |  | 
888  |  | /*  | 
889  |  |  * This method compares the two files given to see if they are a match.  | 
890  |  |  * It is currently use to compare two TZ files.  | 
891  |  |  */  | 
892  | 0  | static UBool compareBinaryFiles(const char* defaultTZFileName, const char* TZFileName, DefaultTZInfo* tzInfo) { | 
893  | 0  |     FILE* file;  | 
894  | 0  |     int64_t sizeFile;  | 
895  | 0  |     int64_t sizeFileLeft;  | 
896  | 0  |     int32_t sizeFileRead;  | 
897  | 0  |     int32_t sizeFileToRead;  | 
898  | 0  |     char bufferFile[MAX_READ_SIZE];  | 
899  | 0  |     UBool result = TRUE;  | 
900  |  | 
  | 
901  | 0  |     if (tzInfo->defaultTZFilePtr == NULL) { | 
902  | 0  |         tzInfo->defaultTZFilePtr = fopen(defaultTZFileName, "r");  | 
903  | 0  |     }  | 
904  | 0  |     file = fopen(TZFileName, "r");  | 
905  |  | 
  | 
906  | 0  |     tzInfo->defaultTZPosition = 0; /* reset position to begin search */  | 
907  |  | 
  | 
908  | 0  |     if (file != NULL && tzInfo->defaultTZFilePtr != NULL) { | 
909  |  |         /* First check that the file size are equal. */  | 
910  | 0  |         if (tzInfo->defaultTZFileSize == 0) { | 
911  | 0  |             fseek(tzInfo->defaultTZFilePtr, 0, SEEK_END);  | 
912  | 0  |             tzInfo->defaultTZFileSize = ftell(tzInfo->defaultTZFilePtr);  | 
913  | 0  |         }  | 
914  | 0  |         fseek(file, 0, SEEK_END);  | 
915  | 0  |         sizeFile = ftell(file);  | 
916  | 0  |         sizeFileLeft = sizeFile;  | 
917  |  | 
  | 
918  | 0  |         if (sizeFile != tzInfo->defaultTZFileSize) { | 
919  | 0  |             result = FALSE;  | 
920  | 0  |         } else { | 
921  |  |             /* Store the data from the files in separate buffers and  | 
922  |  |              * compare each byte to determine equality.  | 
923  |  |              */  | 
924  | 0  |             if (tzInfo->defaultTZBuffer == NULL) { | 
925  | 0  |                 rewind(tzInfo->defaultTZFilePtr);  | 
926  | 0  |                 tzInfo->defaultTZBuffer = (char*)uprv_malloc(sizeof(char) * tzInfo->defaultTZFileSize);  | 
927  | 0  |                 sizeFileRead = fread(tzInfo->defaultTZBuffer, 1, tzInfo->defaultTZFileSize, tzInfo->defaultTZFilePtr);  | 
928  | 0  |             }  | 
929  | 0  |             rewind(file);  | 
930  | 0  |             while(sizeFileLeft > 0) { | 
931  | 0  |                 uprv_memset(bufferFile, 0, MAX_READ_SIZE);  | 
932  | 0  |                 sizeFileToRead = sizeFileLeft < MAX_READ_SIZE ? sizeFileLeft : MAX_READ_SIZE;  | 
933  |  | 
  | 
934  | 0  |                 sizeFileRead = fread(bufferFile, 1, sizeFileToRead, file);  | 
935  | 0  |                 if (memcmp(tzInfo->defaultTZBuffer + tzInfo->defaultTZPosition, bufferFile, sizeFileRead) != 0) { | 
936  | 0  |                     result = FALSE;  | 
937  | 0  |                     break;  | 
938  | 0  |                 }  | 
939  | 0  |                 sizeFileLeft -= sizeFileRead;  | 
940  | 0  |                 tzInfo->defaultTZPosition += sizeFileRead;  | 
941  | 0  |             }  | 
942  | 0  |         }  | 
943  | 0  |     } else { | 
944  | 0  |         result = FALSE;  | 
945  | 0  |     }  | 
946  |  | 
  | 
947  | 0  |     if (file != NULL) { | 
948  | 0  |         fclose(file);  | 
949  | 0  |     }  | 
950  |  | 
  | 
951  | 0  |     return result;  | 
952  | 0  | }  | 
953  |  |  | 
954  |  |  | 
955  |  | /* dirent also lists two entries: "." and ".." that we can safely ignore. */  | 
956  |  | #define SKIP1 "."  | 
957  |  | #define SKIP2 ".."  | 
958  |  | static UBool U_CALLCONV putil_cleanup(void);  | 
959  |  | static CharString *gSearchTZFileResult = NULL;  | 
960  |  |  | 
961  |  | /*  | 
962  |  |  * This method recursively traverses the directory given for a matching TZ file and returns the first match.  | 
963  |  |  * This function is not thread safe - it uses a global, gSearchTZFileResult, to hold its results.  | 
964  |  |  */  | 
965  | 0  | static char* searchForTZFile(const char* path, DefaultTZInfo* tzInfo) { | 
966  | 0  |     DIR* dirp = NULL;  | 
967  | 0  |     struct dirent* dirEntry = NULL;  | 
968  | 0  |     char* result = NULL;  | 
969  | 0  |     UErrorCode status = U_ZERO_ERROR;  | 
970  |  |  | 
971  |  |     /* Save the current path */  | 
972  | 0  |     CharString curpath(path, -1, status);  | 
973  | 0  |     if (U_FAILURE(status)) { | 
974  | 0  |         goto cleanupAndReturn;  | 
975  | 0  |     }  | 
976  |  |  | 
977  | 0  |     dirp = opendir(path);  | 
978  | 0  |     if (dirp == NULL) { | 
979  | 0  |         goto cleanupAndReturn;  | 
980  | 0  |     }  | 
981  |  |  | 
982  | 0  |     if (gSearchTZFileResult == NULL) { | 
983  | 0  |         gSearchTZFileResult = new CharString;  | 
984  | 0  |         if (gSearchTZFileResult == NULL) { | 
985  | 0  |             goto cleanupAndReturn;  | 
986  | 0  |         }  | 
987  | 0  |         ucln_common_registerCleanup(UCLN_COMMON_PUTIL, putil_cleanup);  | 
988  | 0  |     }  | 
989  |  |  | 
990  |  |     /* Check each entry in the directory. */  | 
991  | 0  |     while((dirEntry = readdir(dirp)) != NULL) { | 
992  | 0  |         const char* dirName = dirEntry->d_name;  | 
993  | 0  |         if (uprv_strcmp(dirName, SKIP1) != 0 && uprv_strcmp(dirName, SKIP2) != 0  | 
994  | 0  |             && uprv_strcmp(TZFILE_SKIP, dirName) != 0 && uprv_strcmp(TZFILE_SKIP2, dirName) != 0) { | 
995  |  |             /* Create a newpath with the new entry to test each entry in the directory. */  | 
996  | 0  |             CharString newpath(curpath, status);  | 
997  | 0  |             newpath.append(dirName, -1, status);  | 
998  | 0  |             if (U_FAILURE(status)) { | 
999  | 0  |                 break;  | 
1000  | 0  |             }  | 
1001  |  |  | 
1002  | 0  |             DIR* subDirp = NULL;  | 
1003  | 0  |             if ((subDirp = opendir(newpath.data())) != NULL) { | 
1004  |  |                 /* If this new path is a directory, make a recursive call with the newpath. */  | 
1005  | 0  |                 closedir(subDirp);  | 
1006  | 0  |                 newpath.append('/', status); | 
1007  | 0  |                 if (U_FAILURE(status)) { | 
1008  | 0  |                     break;  | 
1009  | 0  |                 }  | 
1010  | 0  |                 result = searchForTZFile(newpath.data(), tzInfo);  | 
1011  |  |                 /*  | 
1012  |  |                  Have to get out here. Otherwise, we'd keep looking  | 
1013  |  |                  and return the first match in the top-level directory  | 
1014  |  |                  if there's a match in the top-level. If not, this function  | 
1015  |  |                  would return NULL and set gTimeZoneBufferPtr to NULL in initDefault().  | 
1016  |  |                  It worked without this in most cases because we have a fallback of calling  | 
1017  |  |                  localtime_r to figure out the default timezone.  | 
1018  |  |                 */  | 
1019  | 0  |                 if (result != NULL)  | 
1020  | 0  |                     break;  | 
1021  | 0  |             } else { | 
1022  | 0  |                 if(compareBinaryFiles(TZDEFAULT, newpath.data(), tzInfo)) { | 
1023  | 0  |                     int32_t amountToSkip = sizeof(TZZONEINFO) - 1;  | 
1024  | 0  |                     if (amountToSkip > newpath.length()) { | 
1025  | 0  |                         amountToSkip = newpath.length();  | 
1026  | 0  |                     }  | 
1027  | 0  |                     const char* zoneid = newpath.data() + amountToSkip;  | 
1028  | 0  |                     skipZoneIDPrefix(&zoneid);  | 
1029  | 0  |                     gSearchTZFileResult->clear();  | 
1030  | 0  |                     gSearchTZFileResult->append(zoneid, -1, status);  | 
1031  | 0  |                     if (U_FAILURE(status)) { | 
1032  | 0  |                         break;  | 
1033  | 0  |                     }  | 
1034  | 0  |                     result = gSearchTZFileResult->data();  | 
1035  |  |                     /* Get out after the first one found. */  | 
1036  | 0  |                     break;  | 
1037  | 0  |                 }  | 
1038  | 0  |             }  | 
1039  | 0  |         }  | 
1040  | 0  |     }  | 
1041  |  | 
  | 
1042  | 0  |   cleanupAndReturn:  | 
1043  | 0  |     if (dirp) { | 
1044  | 0  |         closedir(dirp);  | 
1045  | 0  |     }  | 
1046  | 0  |     return result;  | 
1047  | 0  | }  | 
1048  |  | #endif  | 
1049  |  |  | 
1050  |  | #if U_PLATFORM == U_PF_ANDROID  | 
1051  |  | typedef int(system_property_read_callback)(const prop_info* info,  | 
1052  |  |                                            void (*callback)(void* cookie,  | 
1053  |  |                                                             const char* name,  | 
1054  |  |                                                             const char* value,  | 
1055  |  |                                                             uint32_t serial),  | 
1056  |  |                                            void* cookie);  | 
1057  |  | typedef int(system_property_get)(const char*, char*);  | 
1058  |  |  | 
1059  |  | static char gAndroidTimeZone[PROP_VALUE_MAX] = { '\0' }; | 
1060  |  |  | 
1061  |  | static void u_property_read(void* cookie, const char* name, const char* value,  | 
1062  |  |                             uint32_t serial) { | 
1063  |  |     uprv_strcpy((char* )cookie, value);  | 
1064  |  | }  | 
1065  |  | #endif  | 
1066  |  |  | 
1067  |  | U_CAPI void U_EXPORT2  | 
1068  |  | uprv_tzname_clear_cache(void)  | 
1069  | 0  | { | 
1070  |  | #if U_PLATFORM == U_PF_ANDROID  | 
1071  |  |     /* Android's timezone is stored in system property. */  | 
1072  |  |     gAndroidTimeZone[0] = '\0';  | 
1073  |  |     void* libc = dlopen("libc.so", RTLD_NOLOAD); | 
1074  |  |     if (libc) { | 
1075  |  |         /* Android API 26+ has new API to get system property and old API  | 
1076  |  |          * (__system_property_get) is deprecated */  | 
1077  |  |         system_property_read_callback* property_read_callback =  | 
1078  |  |             (system_property_read_callback*)dlsym(  | 
1079  |  |                 libc, "__system_property_read_callback");  | 
1080  |  |         if (property_read_callback) { | 
1081  |  |             const prop_info* info =  | 
1082  |  |                 __system_property_find("persist.sys.timezone"); | 
1083  |  |             if (info) { | 
1084  |  |                 property_read_callback(info, &u_property_read, gAndroidTimeZone);  | 
1085  |  |             }  | 
1086  |  |         } else { | 
1087  |  |             system_property_get* property_get =  | 
1088  |  |                 (system_property_get*)dlsym(libc, "__system_property_get");  | 
1089  |  |             if (property_get) { | 
1090  |  |                 property_get("persist.sys.timezone", gAndroidTimeZone); | 
1091  |  |             }  | 
1092  |  |         }  | 
1093  |  |         dlclose(libc);  | 
1094  |  |     }  | 
1095  |  | #endif  | 
1096  |  | 
  | 
1097  | 0  | #if defined(CHECK_LOCALTIME_LINK) && !defined(DEBUG_SKIP_LOCALTIME_LINK)  | 
1098  | 0  |     gTimeZoneBufferPtr = NULL;  | 
1099  | 0  | #endif  | 
1100  | 0  | }  | 
1101  |  |  | 
1102  |  | U_CAPI const char* U_EXPORT2  | 
1103  |  | uprv_tzname(int n)  | 
1104  | 0  | { | 
1105  | 0  |     (void)n; // Avoid unreferenced parameter warning.  | 
1106  | 0  |     const char *tzid = NULL;  | 
1107  |  | #if U_PLATFORM_USES_ONLY_WIN32_API  | 
1108  |  |     tzid = uprv_detectWindowsTimeZone();  | 
1109  |  |  | 
1110  |  |     if (tzid != NULL) { | 
1111  |  |         return tzid;  | 
1112  |  |     }  | 
1113  |  |  | 
1114  |  | #ifndef U_TZNAME  | 
1115  |  |     // The return value is free'd in timezone.cpp on Windows because  | 
1116  |  |     // the other code path returns a pointer to a heap location.  | 
1117  |  |     // If we don't have a name already, then tzname wouldn't be any  | 
1118  |  |     // better, so just fall back.  | 
1119  |  |     return uprv_strdup(""); | 
1120  |  | #endif // !U_TZNAME  | 
1121  |  |  | 
1122  |  | #else  | 
1123  |  |  | 
1124  |  | /*#if U_PLATFORM_IS_DARWIN_BASED  | 
1125  |  |     int ret;  | 
1126  |  |  | 
1127  |  |     tzid = getenv("TZFILE"); | 
1128  |  |     if (tzid != NULL) { | 
1129  |  |         return tzid;  | 
1130  |  |     }  | 
1131  |  | #endif*/  | 
1132  |  |  | 
1133  |  | /* This code can be temporarily disabled to test tzname resolution later on. */  | 
1134  | 0  | #ifndef DEBUG_TZNAME  | 
1135  |  | #if U_PLATFORM == U_PF_ANDROID  | 
1136  |  |     tzid = gAndroidTimeZone;  | 
1137  |  | #else  | 
1138  | 0  |     tzid = getenv("TZ"); | 
1139  | 0  | #endif  | 
1140  | 0  |     if (tzid != NULL && isValidOlsonID(tzid)  | 
1141  |  | #if U_PLATFORM == U_PF_SOLARIS  | 
1142  |  |     /* Don't misinterpret TZ "localtime" on Solaris as a time zone name. */  | 
1143  |  |         && uprv_strcmp(tzid, TZ_ENV_CHECK) != 0  | 
1144  |  | #endif  | 
1145  | 0  |     ) { | 
1146  |  |         /* The colon forces tzset() to treat the remainder as zoneinfo path */  | 
1147  | 0  |         if (tzid[0] == ':') { | 
1148  | 0  |             tzid++;  | 
1149  | 0  |         }  | 
1150  |  |         /* This might be a good Olson ID. */  | 
1151  | 0  |         skipZoneIDPrefix(&tzid);  | 
1152  | 0  |         return tzid;  | 
1153  | 0  |     }  | 
1154  |  |     /* else U_TZNAME will give a better result. */  | 
1155  | 0  | #endif  | 
1156  |  |  | 
1157  | 0  | #if defined(CHECK_LOCALTIME_LINK) && !defined(DEBUG_SKIP_LOCALTIME_LINK)  | 
1158  |  |     /* Caller must handle threading issues */  | 
1159  | 0  |     if (gTimeZoneBufferPtr == NULL) { | 
1160  |  |         /*  | 
1161  |  |         This is a trick to look at the name of the link to get the Olson ID  | 
1162  |  |         because the tzfile contents is underspecified.  | 
1163  |  |         This isn't guaranteed to work because it may not be a symlink.  | 
1164  |  |         */  | 
1165  | 0  |         int32_t ret = (int32_t)readlink(TZDEFAULT, gTimeZoneBuffer, sizeof(gTimeZoneBuffer)-1);  | 
1166  | 0  |         if (0 < ret) { | 
1167  | 0  |             int32_t tzZoneInfoTailLen = uprv_strlen(TZZONEINFOTAIL);  | 
1168  | 0  |             gTimeZoneBuffer[ret] = 0;  | 
1169  | 0  |             char *  tzZoneInfoTailPtr = uprv_strstr(gTimeZoneBuffer, TZZONEINFOTAIL);  | 
1170  |  | 
  | 
1171  | 0  |             if (tzZoneInfoTailPtr != NULL  | 
1172  | 0  |                 && isValidOlsonID(tzZoneInfoTailPtr + tzZoneInfoTailLen))  | 
1173  | 0  |             { | 
1174  | 0  |                 return (gTimeZoneBufferPtr = tzZoneInfoTailPtr + tzZoneInfoTailLen);  | 
1175  | 0  |             }  | 
1176  | 0  |         } else { | 
1177  | 0  | #if defined(SEARCH_TZFILE)  | 
1178  | 0  |             DefaultTZInfo* tzInfo = (DefaultTZInfo*)uprv_malloc(sizeof(DefaultTZInfo));  | 
1179  | 0  |             if (tzInfo != NULL) { | 
1180  | 0  |                 tzInfo->defaultTZBuffer = NULL;  | 
1181  | 0  |                 tzInfo->defaultTZFileSize = 0;  | 
1182  | 0  |                 tzInfo->defaultTZFilePtr = NULL;  | 
1183  | 0  |                 tzInfo->defaultTZstatus = FALSE;  | 
1184  | 0  |                 tzInfo->defaultTZPosition = 0;  | 
1185  |  | 
  | 
1186  | 0  |                 gTimeZoneBufferPtr = searchForTZFile(TZZONEINFO, tzInfo);  | 
1187  |  |  | 
1188  |  |                 /* Free previously allocated memory */  | 
1189  | 0  |                 if (tzInfo->defaultTZBuffer != NULL) { | 
1190  | 0  |                     uprv_free(tzInfo->defaultTZBuffer);  | 
1191  | 0  |                 }  | 
1192  | 0  |                 if (tzInfo->defaultTZFilePtr != NULL) { | 
1193  | 0  |                     fclose(tzInfo->defaultTZFilePtr);  | 
1194  | 0  |                 }  | 
1195  | 0  |                 uprv_free(tzInfo);  | 
1196  | 0  |             }  | 
1197  |  | 
  | 
1198  | 0  |             if (gTimeZoneBufferPtr != NULL && isValidOlsonID(gTimeZoneBufferPtr)) { | 
1199  | 0  |                 return gTimeZoneBufferPtr;  | 
1200  | 0  |             }  | 
1201  | 0  | #endif  | 
1202  | 0  |         }  | 
1203  | 0  |     }  | 
1204  | 0  |     else { | 
1205  | 0  |         return gTimeZoneBufferPtr;  | 
1206  | 0  |     }  | 
1207  | 0  | #endif  | 
1208  | 0  | #endif  | 
1209  |  |  | 
1210  | 0  | #ifdef U_TZNAME  | 
1211  |  | #if U_PLATFORM_USES_ONLY_WIN32_API  | 
1212  |  |     /* The return value is free'd in timezone.cpp on Windows because  | 
1213  |  |      * the other code path returns a pointer to a heap location. */  | 
1214  |  |     return uprv_strdup(U_TZNAME[n]);  | 
1215  |  | #else  | 
1216  |  |     /*  | 
1217  |  |     U_TZNAME is usually a non-unique abbreviation, which isn't normally usable.  | 
1218  |  |     So we remap the abbreviation to an olson ID.  | 
1219  |  |  | 
1220  |  |     Since Windows exposes a little more timezone information,  | 
1221  |  |     we normally don't use this code on Windows because  | 
1222  |  |     uprv_detectWindowsTimeZone should have already given the correct answer.  | 
1223  |  |     */  | 
1224  | 0  |     { | 
1225  | 0  |         struct tm juneSol, decemberSol;  | 
1226  | 0  |         int daylightType;  | 
1227  | 0  |         static const time_t juneSolstice=1182478260; /*2007-06-21 18:11 UT*/  | 
1228  | 0  |         static const time_t decemberSolstice=1198332540; /*2007-12-22 06:09 UT*/  | 
1229  |  |  | 
1230  |  |         /* This probing will tell us when daylight savings occurs.  */  | 
1231  | 0  |         localtime_r(&juneSolstice, &juneSol);  | 
1232  | 0  |         localtime_r(&decemberSolstice, &decemberSol);  | 
1233  | 0  |         if(decemberSol.tm_isdst > 0) { | 
1234  | 0  |           daylightType = U_DAYLIGHT_DECEMBER;  | 
1235  | 0  |         } else if(juneSol.tm_isdst > 0) { | 
1236  | 0  |           daylightType = U_DAYLIGHT_JUNE;  | 
1237  | 0  |         } else { | 
1238  | 0  |           daylightType = U_DAYLIGHT_NONE;  | 
1239  | 0  |         }  | 
1240  | 0  |         tzid = remapShortTimeZone(U_TZNAME[0], U_TZNAME[1], daylightType, uprv_timezone());  | 
1241  | 0  |         if (tzid != NULL) { | 
1242  | 0  |             return tzid;  | 
1243  | 0  |         }  | 
1244  | 0  |     }  | 
1245  | 0  |     return U_TZNAME[n];  | 
1246  | 0  | #endif  | 
1247  |  | #else  | 
1248  |  |     return "";  | 
1249  |  | #endif  | 
1250  | 0  | }  | 
1251  |  |  | 
1252  |  | /* Get and set the ICU data directory --------------------------------------- */  | 
1253  |  |  | 
1254  |  | static icu::UInitOnce gDataDirInitOnce = U_INITONCE_INITIALIZER;  | 
1255  |  | static char *gDataDirectory = NULL;  | 
1256  |  |  | 
1257  |  | UInitOnce gTimeZoneFilesInitOnce = U_INITONCE_INITIALIZER;  | 
1258  |  | static CharString *gTimeZoneFilesDirectory = NULL;  | 
1259  |  |  | 
1260  |  | #if U_POSIX_LOCALE || U_PLATFORM_USES_ONLY_WIN32_API  | 
1261  |  |  static const char *gCorrectedPOSIXLocale = NULL; /* Sometimes heap allocated */  | 
1262  |  |  static bool gCorrectedPOSIXLocaleHeapAllocated = false;  | 
1263  |  | #endif  | 
1264  |  |  | 
1265  |  | static UBool U_CALLCONV putil_cleanup(void)  | 
1266  | 0  | { | 
1267  | 0  |     if (gDataDirectory && *gDataDirectory) { | 
1268  | 0  |         uprv_free(gDataDirectory);  | 
1269  | 0  |     }  | 
1270  | 0  |     gDataDirectory = NULL;  | 
1271  | 0  |     gDataDirInitOnce.reset();  | 
1272  |  | 
  | 
1273  | 0  |     delete gTimeZoneFilesDirectory;  | 
1274  | 0  |     gTimeZoneFilesDirectory = NULL;  | 
1275  | 0  |     gTimeZoneFilesInitOnce.reset();  | 
1276  |  | 
  | 
1277  | 0  | #ifdef SEARCH_TZFILE  | 
1278  | 0  |     delete gSearchTZFileResult;  | 
1279  | 0  |     gSearchTZFileResult = NULL;  | 
1280  | 0  | #endif  | 
1281  |  | 
  | 
1282  | 0  | #if U_POSIX_LOCALE || U_PLATFORM_USES_ONLY_WIN32_API  | 
1283  | 0  |     if (gCorrectedPOSIXLocale && gCorrectedPOSIXLocaleHeapAllocated) { | 
1284  | 0  |         uprv_free(const_cast<char *>(gCorrectedPOSIXLocale));  | 
1285  | 0  |         gCorrectedPOSIXLocale = NULL;  | 
1286  | 0  |         gCorrectedPOSIXLocaleHeapAllocated = false;  | 
1287  | 0  |     }  | 
1288  | 0  | #endif  | 
1289  | 0  |     return TRUE;  | 
1290  | 0  | }  | 
1291  |  |  | 
1292  |  | /*  | 
1293  |  |  * Set the data directory.  | 
1294  |  |  *    Make a copy of the passed string, and set the global data dir to point to it.  | 
1295  |  |  */  | 
1296  |  | U_CAPI void U_EXPORT2  | 
1297  | 0  | u_setDataDirectory(const char *directory) { | 
1298  | 0  |     char *newDataDir;  | 
1299  | 0  |     int32_t length;  | 
1300  |  | 
  | 
1301  | 0  |     if(directory==NULL || *directory==0) { | 
1302  |  |         /* A small optimization to prevent the malloc and copy when the  | 
1303  |  |         shared library is used, and this is a way to make sure that NULL  | 
1304  |  |         is never returned.  | 
1305  |  |         */  | 
1306  | 0  |         newDataDir = (char *)"";  | 
1307  | 0  |     }  | 
1308  | 0  |     else { | 
1309  | 0  |         length=(int32_t)uprv_strlen(directory);  | 
1310  | 0  |         newDataDir = (char *)uprv_malloc(length + 2);  | 
1311  |  |         /* Exit out if newDataDir could not be created. */  | 
1312  | 0  |         if (newDataDir == NULL) { | 
1313  | 0  |             return;  | 
1314  | 0  |         }  | 
1315  | 0  |         uprv_strcpy(newDataDir, directory);  | 
1316  |  | 
  | 
1317  |  | #if (U_FILE_SEP_CHAR != U_FILE_ALT_SEP_CHAR)  | 
1318  |  |         { | 
1319  |  |             char *p;  | 
1320  |  |             while((p = uprv_strchr(newDataDir, U_FILE_ALT_SEP_CHAR)) != NULL) { | 
1321  |  |                 *p = U_FILE_SEP_CHAR;  | 
1322  |  |             }  | 
1323  |  |         }  | 
1324  |  | #endif  | 
1325  | 0  |     }  | 
1326  |  |  | 
1327  | 0  |     if (gDataDirectory && *gDataDirectory) { | 
1328  | 0  |         uprv_free(gDataDirectory);  | 
1329  | 0  |     }  | 
1330  | 0  |     gDataDirectory = newDataDir;  | 
1331  | 0  |     ucln_common_registerCleanup(UCLN_COMMON_PUTIL, putil_cleanup);  | 
1332  | 0  | }  | 
1333  |  |  | 
1334  |  | U_CAPI UBool U_EXPORT2  | 
1335  |  | uprv_pathIsAbsolute(const char *path)  | 
1336  | 0  | { | 
1337  | 0  |   if(!path || !*path) { | 
1338  | 0  |     return FALSE;  | 
1339  | 0  |   }  | 
1340  |  |  | 
1341  | 0  |   if(*path == U_FILE_SEP_CHAR) { | 
1342  | 0  |     return TRUE;  | 
1343  | 0  |   }  | 
1344  |  |  | 
1345  |  | #if (U_FILE_SEP_CHAR != U_FILE_ALT_SEP_CHAR)  | 
1346  |  |   if(*path == U_FILE_ALT_SEP_CHAR) { | 
1347  |  |     return TRUE;  | 
1348  |  |   }  | 
1349  |  | #endif  | 
1350  |  |  | 
1351  |  | #if U_PLATFORM_USES_ONLY_WIN32_API  | 
1352  |  |   if( (((path[0] >= 'A') && (path[0] <= 'Z')) ||  | 
1353  |  |        ((path[0] >= 'a') && (path[0] <= 'z'))) &&  | 
1354  |  |       path[1] == ':' ) { | 
1355  |  |     return TRUE;  | 
1356  |  |   }  | 
1357  |  | #endif  | 
1358  |  |  | 
1359  | 0  |   return FALSE;  | 
1360  | 0  | }  | 
1361  |  |  | 
1362  |  | /* Backup setting of ICU_DATA_DIR_PREFIX_ENV_VAR  | 
1363  |  |    (needed for some Darwin ICU build environments) */  | 
1364  |  | #if U_PLATFORM_IS_DARWIN_BASED && defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR  | 
1365  |  | # if !defined(ICU_DATA_DIR_PREFIX_ENV_VAR)  | 
1366  |  | #  define ICU_DATA_DIR_PREFIX_ENV_VAR "IPHONE_SIMULATOR_ROOT"  | 
1367  |  | # endif  | 
1368  |  | #endif  | 
1369  |  |  | 
1370  |  | #if defined(ICU_DATA_DIR_WINDOWS)  | 
1371  |  | // Helper function to get the ICU Data Directory under the Windows directory location.  | 
1372  |  | static BOOL U_CALLCONV getIcuDataDirectoryUnderWindowsDirectory(char* directoryBuffer, UINT bufferLength)  | 
1373  |  | { | 
1374  |  |     wchar_t windowsPath[MAX_PATH];  | 
1375  |  |     char windowsPathUtf8[MAX_PATH];  | 
1376  |  |  | 
1377  |  |     UINT length = GetSystemWindowsDirectoryW(windowsPath, UPRV_LENGTHOF(windowsPath));  | 
1378  |  |     if ((length > 0) && (length < (UPRV_LENGTHOF(windowsPath) - 1))) { | 
1379  |  |         // Convert UTF-16 to a UTF-8 string.  | 
1380  |  |         UErrorCode status = U_ZERO_ERROR;  | 
1381  |  |         int32_t windowsPathUtf8Len = 0;  | 
1382  |  |         u_strToUTF8(windowsPathUtf8, static_cast<int32_t>(UPRV_LENGTHOF(windowsPathUtf8)),  | 
1383  |  |             &windowsPathUtf8Len, reinterpret_cast<const UChar*>(windowsPath), -1, &status);  | 
1384  |  |  | 
1385  |  |         if (U_SUCCESS(status) && (status != U_STRING_NOT_TERMINATED_WARNING) &&  | 
1386  |  |             (windowsPathUtf8Len < (UPRV_LENGTHOF(windowsPathUtf8) - 1))) { | 
1387  |  |             // Ensure it always has a separator, so we can append the ICU data path.  | 
1388  |  |             if (windowsPathUtf8[windowsPathUtf8Len - 1] != U_FILE_SEP_CHAR) { | 
1389  |  |                 windowsPathUtf8[windowsPathUtf8Len++] = U_FILE_SEP_CHAR;  | 
1390  |  |                 windowsPathUtf8[windowsPathUtf8Len] = '\0';  | 
1391  |  |             }  | 
1392  |  |             // Check if the concatenated string will fit.  | 
1393  |  |             if ((windowsPathUtf8Len + UPRV_LENGTHOF(ICU_DATA_DIR_WINDOWS)) < bufferLength) { | 
1394  |  |                 uprv_strcpy(directoryBuffer, windowsPathUtf8);  | 
1395  |  |                 uprv_strcat(directoryBuffer, ICU_DATA_DIR_WINDOWS);  | 
1396  |  |                 return TRUE;  | 
1397  |  |             }  | 
1398  |  |         }  | 
1399  |  |     }  | 
1400  |  |  | 
1401  |  |     return FALSE;  | 
1402  |  | }  | 
1403  |  | #endif  | 
1404  |  |  | 
1405  | 0  | static void U_CALLCONV dataDirectoryInitFn() { | 
1406  |  |     /* If we already have the directory, then return immediately. Will happen if user called  | 
1407  |  |      * u_setDataDirectory().  | 
1408  |  |      */  | 
1409  | 0  |     if (gDataDirectory) { | 
1410  | 0  |         return;  | 
1411  | 0  |     }  | 
1412  |  |  | 
1413  | 0  |     const char *path = NULL;  | 
1414  |  | #if defined(ICU_DATA_DIR_PREFIX_ENV_VAR)  | 
1415  |  |     char datadir_path_buffer[PATH_MAX];  | 
1416  |  | #endif  | 
1417  |  |  | 
1418  |  |     /*  | 
1419  |  |     When ICU_NO_USER_DATA_OVERRIDE is defined, users aren't allowed to  | 
1420  |  |     override ICU's data with the ICU_DATA environment variable. This prevents  | 
1421  |  |     problems where multiple custom copies of ICU's specific version of data  | 
1422  |  |     are installed on a system. Either the application must define the data  | 
1423  |  |     directory with u_setDataDirectory, define ICU_DATA_DIR when compiling  | 
1424  |  |     ICU, set the data with udata_setCommonData or trust that all of the  | 
1425  |  |     required data is contained in ICU's data library that contains  | 
1426  |  |     the entry point defined by U_ICUDATA_ENTRY_POINT.  | 
1427  |  |  | 
1428  |  |     There may also be some platforms where environment variables  | 
1429  |  |     are not allowed.  | 
1430  |  |     */  | 
1431  | 0  | #   if !defined(ICU_NO_USER_DATA_OVERRIDE) && !UCONFIG_NO_FILE_IO  | 
1432  |  |     /* First try to get the environment variable */  | 
1433  | 0  | #     if U_PLATFORM_HAS_WINUWP_API == 0  // Windows UWP does not support getenv  | 
1434  | 0  |         path=getenv("ICU_DATA"); | 
1435  | 0  | #     endif  | 
1436  | 0  | #   endif  | 
1437  |  |  | 
1438  |  |     /* ICU_DATA_DIR may be set as a compile option.  | 
1439  |  |      * U_ICU_DATA_DEFAULT_DIR is provided and is set by ICU at compile time  | 
1440  |  |      * and is used only when data is built in archive mode eliminating the need  | 
1441  |  |      * for ICU_DATA_DIR to be set. U_ICU_DATA_DEFAULT_DIR is set to the installation  | 
1442  |  |      * directory of the data dat file. Users should use ICU_DATA_DIR if they want to  | 
1443  |  |      * set their own path.  | 
1444  |  |      */  | 
1445  |  | #if defined(ICU_DATA_DIR) || defined(U_ICU_DATA_DEFAULT_DIR)  | 
1446  |  |     if(path==NULL || *path==0) { | 
1447  |  | # if defined(ICU_DATA_DIR_PREFIX_ENV_VAR)  | 
1448  |  |         const char *prefix = getenv(ICU_DATA_DIR_PREFIX_ENV_VAR);  | 
1449  |  | # endif  | 
1450  |  | # ifdef ICU_DATA_DIR  | 
1451  |  |         path=ICU_DATA_DIR;  | 
1452  |  | # else  | 
1453  |  |         path=U_ICU_DATA_DEFAULT_DIR;  | 
1454  |  | # endif  | 
1455  |  | # if defined(ICU_DATA_DIR_PREFIX_ENV_VAR)  | 
1456  |  |         if (prefix != NULL) { | 
1457  |  |             snprintf(datadir_path_buffer, PATH_MAX, "%s%s", prefix, path);  | 
1458  |  |             path=datadir_path_buffer;  | 
1459  |  |         }  | 
1460  |  | # endif  | 
1461  |  |     }  | 
1462  |  | #endif  | 
1463  |  | 
  | 
1464  |  | #if defined(ICU_DATA_DIR_WINDOWS)  | 
1465  |  |     char datadir_path_buffer[MAX_PATH];  | 
1466  |  |     if (getIcuDataDirectoryUnderWindowsDirectory(datadir_path_buffer, UPRV_LENGTHOF(datadir_path_buffer))) { | 
1467  |  |         path = datadir_path_buffer;  | 
1468  |  |     }  | 
1469  |  | #endif  | 
1470  |  | 
  | 
1471  | 0  |     if(path==NULL) { | 
1472  |  |         /* It looks really bad, set it to something. */  | 
1473  | 0  |         path = "";  | 
1474  | 0  |     }  | 
1475  |  | 
  | 
1476  | 0  |     u_setDataDirectory(path);  | 
1477  | 0  |     return;  | 
1478  | 0  | }  | 
1479  |  |  | 
1480  |  | U_CAPI const char * U_EXPORT2  | 
1481  | 0  | u_getDataDirectory(void) { | 
1482  | 0  |     umtx_initOnce(gDataDirInitOnce, &dataDirectoryInitFn);  | 
1483  | 0  |     return gDataDirectory;  | 
1484  | 0  | }  | 
1485  |  |  | 
1486  | 0  | static void setTimeZoneFilesDir(const char *path, UErrorCode &status) { | 
1487  | 0  |     if (U_FAILURE(status)) { | 
1488  | 0  |         return;  | 
1489  | 0  |     }  | 
1490  | 0  |     gTimeZoneFilesDirectory->clear();  | 
1491  | 0  |     gTimeZoneFilesDirectory->append(path, status);  | 
1492  |  | #if (U_FILE_SEP_CHAR != U_FILE_ALT_SEP_CHAR)  | 
1493  |  |     char *p = gTimeZoneFilesDirectory->data();  | 
1494  |  |     while ((p = uprv_strchr(p, U_FILE_ALT_SEP_CHAR)) != NULL) { | 
1495  |  |         *p = U_FILE_SEP_CHAR;  | 
1496  |  |     }  | 
1497  |  | #endif  | 
1498  | 0  | }  | 
1499  |  |  | 
1500  |  | #define TO_STRING(x) TO_STRING_2(x)  | 
1501  |  | #define TO_STRING_2(x) #x  | 
1502  |  |  | 
1503  | 0  | static void U_CALLCONV TimeZoneDataDirInitFn(UErrorCode &status) { | 
1504  | 0  |     U_ASSERT(gTimeZoneFilesDirectory == NULL);  | 
1505  | 0  |     ucln_common_registerCleanup(UCLN_COMMON_PUTIL, putil_cleanup);  | 
1506  | 0  |     gTimeZoneFilesDirectory = new CharString();  | 
1507  | 0  |     if (gTimeZoneFilesDirectory == NULL) { | 
1508  | 0  |         status = U_MEMORY_ALLOCATION_ERROR;  | 
1509  | 0  |         return;  | 
1510  | 0  |     }  | 
1511  |  |  | 
1512  | 0  |     const char *dir = "";  | 
1513  |  | 
  | 
1514  |  | #if defined(ICU_TIMEZONE_FILES_DIR_PREFIX_ENV_VAR)  | 
1515  |  |     char timezonefilesdir_path_buffer[PATH_MAX];  | 
1516  |  |     const char *prefix = getenv(ICU_TIMEZONE_FILES_DIR_PREFIX_ENV_VAR);  | 
1517  |  | #endif  | 
1518  |  | 
  | 
1519  |  | #if U_PLATFORM_HAS_WINUWP_API == 1  | 
1520  |  | // The UWP version does not support the environment variable setting.  | 
1521  |  |  | 
1522  |  | # if defined(ICU_DATA_DIR_WINDOWS)  | 
1523  |  |     // When using the Windows system data, we can possibly pick up time zone data from the Windows directory.  | 
1524  |  |     char datadir_path_buffer[MAX_PATH];  | 
1525  |  |     if (getIcuDataDirectoryUnderWindowsDirectory(datadir_path_buffer, UPRV_LENGTHOF(datadir_path_buffer))) { | 
1526  |  |         dir = datadir_path_buffer;  | 
1527  |  |     }  | 
1528  |  | # endif  | 
1529  |  |  | 
1530  |  | #else  | 
1531  | 0  |     dir = getenv("ICU_TIMEZONE_FILES_DIR"); | 
1532  | 0  | #endif // U_PLATFORM_HAS_WINUWP_API  | 
1533  |  | 
  | 
1534  |  | #if defined(U_TIMEZONE_FILES_DIR)  | 
1535  |  |     if (dir == NULL) { | 
1536  |  |         // Build time configuration setting.  | 
1537  |  |         dir = TO_STRING(U_TIMEZONE_FILES_DIR);  | 
1538  |  |     }  | 
1539  |  | #endif  | 
1540  |  | 
  | 
1541  | 0  |     if (dir == NULL) { | 
1542  | 0  |         dir = "";  | 
1543  | 0  |     }  | 
1544  |  | 
  | 
1545  |  | #if defined(ICU_TIMEZONE_FILES_DIR_PREFIX_ENV_VAR)  | 
1546  |  |     if (prefix != NULL) { | 
1547  |  |         snprintf(timezonefilesdir_path_buffer, PATH_MAX, "%s%s", prefix, dir);  | 
1548  |  |         dir = timezonefilesdir_path_buffer;  | 
1549  |  |     }  | 
1550  |  | #endif  | 
1551  |  | 
  | 
1552  | 0  |     setTimeZoneFilesDir(dir, status);  | 
1553  | 0  | }  | 
1554  |  |  | 
1555  |  |  | 
1556  |  | U_CAPI const char * U_EXPORT2  | 
1557  | 0  | u_getTimeZoneFilesDirectory(UErrorCode *status) { | 
1558  | 0  |     umtx_initOnce(gTimeZoneFilesInitOnce, &TimeZoneDataDirInitFn, *status);  | 
1559  | 0  |     return U_SUCCESS(*status) ? gTimeZoneFilesDirectory->data() : "";  | 
1560  | 0  | }  | 
1561  |  |  | 
1562  |  | U_CAPI void U_EXPORT2  | 
1563  | 0  | u_setTimeZoneFilesDirectory(const char *path, UErrorCode *status) { | 
1564  | 0  |     umtx_initOnce(gTimeZoneFilesInitOnce, &TimeZoneDataDirInitFn, *status);  | 
1565  | 0  |     setTimeZoneFilesDir(path, *status);  | 
1566  |  |  | 
1567  |  |     // Note: this function does some extra churn, first setting based on the  | 
1568  |  |     //       environment, then immediately replacing with the value passed in.  | 
1569  |  |     //       The logic is simpler that way, and performance shouldn't be an issue.  | 
1570  | 0  | }  | 
1571  |  |  | 
1572  |  |  | 
1573  |  | #if U_POSIX_LOCALE  | 
1574  |  | /* A helper function used by uprv_getPOSIXIDForDefaultLocale and  | 
1575  |  |  * uprv_getPOSIXIDForDefaultCodepage. Returns the posix locale id for  | 
1576  |  |  * LC_CTYPE and LC_MESSAGES. It doesn't support other locale categories.  | 
1577  |  |  */  | 
1578  |  | static const char *uprv_getPOSIXIDForCategory(int category)  | 
1579  | 0  | { | 
1580  | 0  |     const char* posixID = NULL;  | 
1581  | 0  |     if (category == LC_MESSAGES || category == LC_CTYPE) { | 
1582  |  |         /*  | 
1583  |  |         * On Solaris two different calls to setlocale can result in  | 
1584  |  |         * different values. Only get this value once.  | 
1585  |  |         *  | 
1586  |  |         * We must check this first because an application can set this.  | 
1587  |  |         *  | 
1588  |  |         * LC_ALL can't be used because it's platform dependent. The LANG  | 
1589  |  |         * environment variable seems to affect LC_CTYPE variable by default.  | 
1590  |  |         * Here is what setlocale(LC_ALL, NULL) can return.  | 
1591  |  |         * HPUX can return 'C C C C C C C'  | 
1592  |  |         * Solaris can return /en_US/C/C/C/C/C on the second try.  | 
1593  |  |         * Linux can return LC_CTYPE=C;LC_NUMERIC=C;...  | 
1594  |  |         *  | 
1595  |  |         * The default codepage detection also needs to use LC_CTYPE.  | 
1596  |  |         *  | 
1597  |  |         * Do not call setlocale(LC_*, "")! Using an empty string instead  | 
1598  |  |         * of NULL, will modify the libc behavior.  | 
1599  |  |         */  | 
1600  | 0  |         posixID = setlocale(category, NULL);  | 
1601  | 0  |         if ((posixID == 0)  | 
1602  | 0  |             || (uprv_strcmp("C", posixID) == 0) | 
1603  | 0  |             || (uprv_strcmp("POSIX", posixID) == 0)) | 
1604  | 0  |         { | 
1605  |  |             /* Maybe we got some garbage.  Try something more reasonable */  | 
1606  | 0  |             posixID = getenv("LC_ALL"); | 
1607  |  |             /* Solaris speaks POSIX -  See IEEE Std 1003.1-2008  | 
1608  |  |              * This is needed to properly handle empty env. variables  | 
1609  |  |              */  | 
1610  |  | #if U_PLATFORM == U_PF_SOLARIS  | 
1611  |  |             if ((posixID == 0) || (posixID[0] == '\0')) { | 
1612  |  |                 posixID = getenv(category == LC_MESSAGES ? "LC_MESSAGES" : "LC_CTYPE");  | 
1613  |  |                 if ((posixID == 0) || (posixID[0] == '\0')) { | 
1614  |  | #else  | 
1615  | 0  |             if (posixID == 0) { | 
1616  | 0  |                 posixID = getenv(category == LC_MESSAGES ? "LC_MESSAGES" : "LC_CTYPE");  | 
1617  | 0  |                 if (posixID == 0) { | 
1618  | 0  | #endif  | 
1619  | 0  |                     posixID = getenv("LANG"); | 
1620  | 0  |                 }  | 
1621  | 0  |             }  | 
1622  | 0  |         }  | 
1623  | 0  |     }  | 
1624  | 0  |     if ((posixID==0)  | 
1625  | 0  |         || (uprv_strcmp("C", posixID) == 0) | 
1626  | 0  |         || (uprv_strcmp("POSIX", posixID) == 0)) | 
1627  | 0  |     { | 
1628  |  |         /* Nothing worked.  Give it a nice POSIX default value. */  | 
1629  | 0  |         posixID = "en_US_POSIX";  | 
1630  |  |         // Note: this test will not catch 'C.UTF-8',  | 
1631  |  |         // that will be handled in uprv_getDefaultLocaleID().  | 
1632  |  |         // Leave this mapping here for the uprv_getPOSIXIDForDefaultCodepage()  | 
1633  |  |         // caller which expects to see "en_US_POSIX" in many branches.  | 
1634  | 0  |     }  | 
1635  | 0  |     return posixID;  | 
1636  | 0  | }  | 
1637  |  |  | 
1638  |  | /* Return just the POSIX id for the default locale, whatever happens to be in  | 
1639  |  |  * it. It gets the value from LC_MESSAGES and indirectly from LC_ALL and LANG.  | 
1640  |  |  */  | 
1641  |  | static const char *uprv_getPOSIXIDForDefaultLocale(void)  | 
1642  | 0  | { | 
1643  | 0  |     static const char* posixID = NULL;  | 
1644  | 0  |     if (posixID == 0) { | 
1645  | 0  |         posixID = uprv_getPOSIXIDForCategory(LC_MESSAGES);  | 
1646  | 0  |     }  | 
1647  | 0  |     return posixID;  | 
1648  | 0  | }  | 
1649  |  |  | 
1650  |  | #if !U_CHARSET_IS_UTF8  | 
1651  |  | /* Return just the POSIX id for the default codepage, whatever happens to be in  | 
1652  |  |  * it. It gets the value from LC_CTYPE and indirectly from LC_ALL and LANG.  | 
1653  |  |  */  | 
1654  |  | static const char *uprv_getPOSIXIDForDefaultCodepage(void)  | 
1655  |  | { | 
1656  |  |     static const char* posixID = NULL;  | 
1657  |  |     if (posixID == 0) { | 
1658  |  |         posixID = uprv_getPOSIXIDForCategory(LC_CTYPE);  | 
1659  |  |     }  | 
1660  |  |     return posixID;  | 
1661  |  | }  | 
1662  |  | #endif  | 
1663  |  | #endif  | 
1664  |  |  | 
1665  |  | /* NOTE: The caller should handle thread safety */  | 
1666  |  | U_CAPI const char* U_EXPORT2  | 
1667  |  | uprv_getDefaultLocaleID()  | 
1668  | 0  | { | 
1669  | 0  | #if U_POSIX_LOCALE  | 
1670  |  | /*  | 
1671  |  |   Note that:  (a '!' means the ID is improper somehow)  | 
1672  |  |      LC_ALL  ---->     default_loc          codepage  | 
1673  |  | --------------------------------------------------------  | 
1674  |  |      ab.CD             ab                   CD  | 
1675  |  |      ab@CD             ab__CD               -  | 
1676  |  |      ab@CD.EF          ab__CD               EF  | 
1677  |  |  | 
1678  |  |      ab_CD.EF@GH       ab_CD_GH             EF  | 
1679  |  |  | 
1680  |  | Some 'improper' ways to do the same as above:  | 
1681  |  |   !  ab_CD@GH.EF       ab_CD_GH             EF  | 
1682  |  |   !  ab_CD.EF@GH.IJ    ab_CD_GH             EF  | 
1683  |  |   !  ab_CD@ZZ.EF@GH.IJ ab_CD_GH             EF  | 
1684  |  |  | 
1685  |  |      _CD@GH            _CD_GH               -  | 
1686  |  |      _CD.EF@GH         _CD_GH               EF  | 
1687  |  |  | 
1688  |  | The variant cannot have dots in it.  | 
1689  |  | The 'rightmost' variant (@xxx) wins.  | 
1690  |  | The leftmost codepage (.xxx) wins.  | 
1691  |  | */  | 
1692  | 0  |     const char* posixID = uprv_getPOSIXIDForDefaultLocale();  | 
1693  |  |  | 
1694  |  |     /* Format: (no spaces)  | 
1695  |  |     ll [ _CC ] [ . MM ] [ @ VV]  | 
1696  |  |  | 
1697  |  |       l = lang, C = ctry, M = charmap, V = variant  | 
1698  |  |     */  | 
1699  |  | 
  | 
1700  | 0  |     if (gCorrectedPOSIXLocale != nullptr) { | 
1701  | 0  |         return gCorrectedPOSIXLocale;  | 
1702  | 0  |     }  | 
1703  |  |  | 
1704  |  |     // Copy the ID into owned memory.  | 
1705  |  |     // Over-allocate in case we replace "C" with "en_US_POSIX" (+10), + null termination  | 
1706  | 0  |     char *correctedPOSIXLocale = static_cast<char *>(uprv_malloc(uprv_strlen(posixID) + 10 + 1));  | 
1707  | 0  |     if (correctedPOSIXLocale == nullptr) { | 
1708  | 0  |         return nullptr;  | 
1709  | 0  |     }  | 
1710  | 0  |     uprv_strcpy(correctedPOSIXLocale, posixID);  | 
1711  |  | 
  | 
1712  | 0  |     char *limit;  | 
1713  | 0  |     if ((limit = uprv_strchr(correctedPOSIXLocale, '.')) != nullptr) { | 
1714  | 0  |         *limit = 0;  | 
1715  | 0  |     }  | 
1716  | 0  |     if ((limit = uprv_strchr(correctedPOSIXLocale, '@')) != nullptr) { | 
1717  | 0  |         *limit = 0;  | 
1718  | 0  |     }  | 
1719  |  | 
  | 
1720  | 0  |     if ((uprv_strcmp("C", correctedPOSIXLocale) == 0) // no @ variant | 
1721  | 0  |         || (uprv_strcmp("POSIX", correctedPOSIXLocale) == 0)) { | 
1722  |  |       // Raw input was C.* or POSIX.*, Give it a nice POSIX default value.  | 
1723  |  |       // (The "C"/"POSIX" case is handled in uprv_getPOSIXIDForCategory())  | 
1724  | 0  |       uprv_strcpy(correctedPOSIXLocale, "en_US_POSIX");  | 
1725  | 0  |     }  | 
1726  |  |  | 
1727  |  |     /* Note that we scan the *uncorrected* ID. */  | 
1728  | 0  |     const char *p;  | 
1729  | 0  |     if ((p = uprv_strrchr(posixID, '@')) != nullptr) { | 
1730  | 0  |         p++;  | 
1731  |  |  | 
1732  |  |         /* Take care of any special cases here.. */  | 
1733  | 0  |         if (!uprv_strcmp(p, "nynorsk")) { | 
1734  | 0  |             p = "NY";  | 
1735  |  |             /* Don't worry about no__NY. In practice, it won't appear. */  | 
1736  | 0  |         }  | 
1737  |  | 
  | 
1738  | 0  |         if (uprv_strchr(correctedPOSIXLocale,'_') == nullptr) { | 
1739  | 0  |             uprv_strcat(correctedPOSIXLocale, "__"); /* aa@b -> aa__b (note this can make the new locale 1 char longer) */  | 
1740  | 0  |         }  | 
1741  | 0  |         else { | 
1742  | 0  |             uprv_strcat(correctedPOSIXLocale, "_"); /* aa_CC@b -> aa_CC_b */  | 
1743  | 0  |         }  | 
1744  |  | 
  | 
1745  | 0  |         const char *q;  | 
1746  | 0  |         if ((q = uprv_strchr(p, '.')) != nullptr) { | 
1747  |  |             /* How big will the resulting string be? */  | 
1748  | 0  |             int32_t len = (int32_t)(uprv_strlen(correctedPOSIXLocale) + (q-p));  | 
1749  | 0  |             uprv_strncat(correctedPOSIXLocale, p, q-p); // do not include charset  | 
1750  | 0  |             correctedPOSIXLocale[len] = 0;  | 
1751  | 0  |         }  | 
1752  | 0  |         else { | 
1753  |  |             /* Anything following the @ sign */  | 
1754  | 0  |             uprv_strcat(correctedPOSIXLocale, p);  | 
1755  | 0  |         }  | 
1756  |  |  | 
1757  |  |         /* Should there be a map from 'no@nynorsk' -> no_NO_NY here?  | 
1758  |  |          * How about 'russian' -> 'ru'?  | 
1759  |  |          * Many of the other locales using ISO codes will be handled by the  | 
1760  |  |          * canonicalization functions in uloc_getDefault.  | 
1761  |  |          */  | 
1762  | 0  |     }  | 
1763  |  | 
  | 
1764  | 0  |     if (gCorrectedPOSIXLocale == nullptr) { | 
1765  | 0  |         gCorrectedPOSIXLocale = correctedPOSIXLocale;  | 
1766  | 0  |         gCorrectedPOSIXLocaleHeapAllocated = true;  | 
1767  | 0  |         ucln_common_registerCleanup(UCLN_COMMON_PUTIL, putil_cleanup);  | 
1768  | 0  |         correctedPOSIXLocale = nullptr;  | 
1769  | 0  |     }  | 
1770  | 0  |     posixID = gCorrectedPOSIXLocale;  | 
1771  |  | 
  | 
1772  | 0  |     if (correctedPOSIXLocale != nullptr) {  /* Was already set - clean up. */ | 
1773  | 0  |         uprv_free(correctedPOSIXLocale);  | 
1774  | 0  |     }  | 
1775  |  | 
  | 
1776  | 0  |     return posixID;  | 
1777  |  | 
  | 
1778  |  | #elif U_PLATFORM_USES_ONLY_WIN32_API  | 
1779  |  | #define POSIX_LOCALE_CAPACITY 64  | 
1780  |  |     UErrorCode status = U_ZERO_ERROR;  | 
1781  |  |     char *correctedPOSIXLocale = nullptr;  | 
1782  |  |  | 
1783  |  |     // If we have already figured this out just use the cached value  | 
1784  |  |     if (gCorrectedPOSIXLocale != nullptr) { | 
1785  |  |         return gCorrectedPOSIXLocale;  | 
1786  |  |     }  | 
1787  |  |  | 
1788  |  |     // No cached value, need to determine the current value  | 
1789  |  |     static WCHAR windowsLocale[LOCALE_NAME_MAX_LENGTH] = {}; | 
1790  |  |     int length = GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SNAME, windowsLocale, LOCALE_NAME_MAX_LENGTH);  | 
1791  |  |  | 
1792  |  |     // Now we should have a Windows locale name that needs converted to the POSIX style.  | 
1793  |  |     if (length > 0) // If length is 0, then the GetLocaleInfoEx failed.  | 
1794  |  |     { | 
1795  |  |         // First we need to go from UTF-16 to char (and also convert from _ to - while we're at it.)  | 
1796  |  |         char modifiedWindowsLocale[LOCALE_NAME_MAX_LENGTH] = {}; | 
1797  |  |  | 
1798  |  |         int32_t i;  | 
1799  |  |         for (i = 0; i < UPRV_LENGTHOF(modifiedWindowsLocale); i++)  | 
1800  |  |         { | 
1801  |  |             if (windowsLocale[i] == '_')  | 
1802  |  |             { | 
1803  |  |                 modifiedWindowsLocale[i] = '-';  | 
1804  |  |             }  | 
1805  |  |             else  | 
1806  |  |             { | 
1807  |  |                 modifiedWindowsLocale[i] = static_cast<char>(windowsLocale[i]);  | 
1808  |  |             }  | 
1809  |  |  | 
1810  |  |             if (modifiedWindowsLocale[i] == '\0')  | 
1811  |  |             { | 
1812  |  |                 break;  | 
1813  |  |             }  | 
1814  |  |         }  | 
1815  |  |  | 
1816  |  |         if (i >= UPRV_LENGTHOF(modifiedWindowsLocale))  | 
1817  |  |         { | 
1818  |  |             // Ran out of room, can't really happen, maybe we'll be lucky about a matching  | 
1819  |  |             // locale when tags are dropped  | 
1820  |  |             modifiedWindowsLocale[UPRV_LENGTHOF(modifiedWindowsLocale) - 1] = '\0';  | 
1821  |  |         }  | 
1822  |  |  | 
1823  |  |         // Now normalize the resulting name  | 
1824  |  |         correctedPOSIXLocale = static_cast<char *>(uprv_malloc(POSIX_LOCALE_CAPACITY + 1));  | 
1825  |  |         /* TODO: Should we just exit on memory allocation failure? */  | 
1826  |  |         if (correctedPOSIXLocale)  | 
1827  |  |         { | 
1828  |  |             int32_t posixLen = uloc_canonicalize(modifiedWindowsLocale, correctedPOSIXLocale, POSIX_LOCALE_CAPACITY, &status);  | 
1829  |  |             if (U_SUCCESS(status))  | 
1830  |  |             { | 
1831  |  |                 *(correctedPOSIXLocale + posixLen) = 0;  | 
1832  |  |                 gCorrectedPOSIXLocale = correctedPOSIXLocale;  | 
1833  |  |                 gCorrectedPOSIXLocaleHeapAllocated = true;  | 
1834  |  |                 ucln_common_registerCleanup(UCLN_COMMON_PUTIL, putil_cleanup);  | 
1835  |  |             }  | 
1836  |  |             else  | 
1837  |  |             { | 
1838  |  |                 uprv_free(correctedPOSIXLocale);  | 
1839  |  |             }  | 
1840  |  |         }  | 
1841  |  |     }  | 
1842  |  |  | 
1843  |  |     // If unable to find a locale we can agree upon, use en-US by default  | 
1844  |  |     if (gCorrectedPOSIXLocale == nullptr) { | 
1845  |  |         gCorrectedPOSIXLocale = "en_US";  | 
1846  |  |     }  | 
1847  |  |     return gCorrectedPOSIXLocale;  | 
1848  |  |  | 
1849  |  | #elif U_PLATFORM == U_PF_OS400  | 
1850  |  |     /* locales are process scoped and are by definition thread safe */  | 
1851  |  |     static char correctedLocale[64];  | 
1852  |  |     const  char *localeID = getenv("LC_ALL"); | 
1853  |  |            char *p;  | 
1854  |  |  | 
1855  |  |     if (localeID == NULL)  | 
1856  |  |         localeID = getenv("LANG"); | 
1857  |  |     if (localeID == NULL)  | 
1858  |  |         localeID = setlocale(LC_ALL, NULL);  | 
1859  |  |     /* Make sure we have something... */  | 
1860  |  |     if (localeID == NULL)  | 
1861  |  |         return "en_US_POSIX";  | 
1862  |  |  | 
1863  |  |     /* Extract the locale name from the path. */  | 
1864  |  |     if((p = uprv_strrchr(localeID, '/')) != NULL)  | 
1865  |  |     { | 
1866  |  |         /* Increment p to start of locale name. */  | 
1867  |  |         p++;  | 
1868  |  |         localeID = p;  | 
1869  |  |     }  | 
1870  |  |  | 
1871  |  |     /* Copy to work location. */  | 
1872  |  |     uprv_strcpy(correctedLocale, localeID);  | 
1873  |  |  | 
1874  |  |     /* Strip off the '.locale' extension. */  | 
1875  |  |     if((p = uprv_strchr(correctedLocale, '.')) != NULL) { | 
1876  |  |         *p = 0;  | 
1877  |  |     }  | 
1878  |  |  | 
1879  |  |     /* Upper case the locale name. */  | 
1880  |  |     T_CString_toUpperCase(correctedLocale);  | 
1881  |  |  | 
1882  |  |     /* See if we are using the POSIX locale.  Any of the  | 
1883  |  |     * following are equivalent and use the same QLGPGCMA  | 
1884  |  |     * (POSIX) locale.  | 
1885  |  |     * QLGPGCMA2 means UCS2  | 
1886  |  |     * QLGPGCMA_4 means UTF-32  | 
1887  |  |     * QLGPGCMA_8 means UTF-8  | 
1888  |  |     */  | 
1889  |  |     if ((uprv_strcmp("C", correctedLocale) == 0) || | 
1890  |  |         (uprv_strcmp("POSIX", correctedLocale) == 0) || | 
1891  |  |         (uprv_strncmp("QLGPGCMA", correctedLocale, 8) == 0)) | 
1892  |  |     { | 
1893  |  |         uprv_strcpy(correctedLocale, "en_US_POSIX");  | 
1894  |  |     }  | 
1895  |  |     else  | 
1896  |  |     { | 
1897  |  |         int16_t LocaleLen;  | 
1898  |  |  | 
1899  |  |         /* Lower case the lang portion. */  | 
1900  |  |         for(p = correctedLocale; *p != 0 && *p != '_'; p++)  | 
1901  |  |         { | 
1902  |  |             *p = uprv_tolower(*p);  | 
1903  |  |         }  | 
1904  |  |  | 
1905  |  |         /* Adjust for Euro.  After '_E' add 'URO'. */  | 
1906  |  |         LocaleLen = uprv_strlen(correctedLocale);  | 
1907  |  |         if (correctedLocale[LocaleLen - 2] == '_' &&  | 
1908  |  |             correctedLocale[LocaleLen - 1] == 'E')  | 
1909  |  |         { | 
1910  |  |             uprv_strcat(correctedLocale, "URO");  | 
1911  |  |         }  | 
1912  |  |  | 
1913  |  |         /* If using Lotus-based locale then convert to  | 
1914  |  |          * equivalent non Lotus.  | 
1915  |  |          */  | 
1916  |  |         else if (correctedLocale[LocaleLen - 2] == '_' &&  | 
1917  |  |             correctedLocale[LocaleLen - 1] == 'L')  | 
1918  |  |         { | 
1919  |  |             correctedLocale[LocaleLen - 2] = 0;  | 
1920  |  |         }  | 
1921  |  |  | 
1922  |  |         /* There are separate simplified and traditional  | 
1923  |  |          * locales called zh_HK_S and zh_HK_T.  | 
1924  |  |          */  | 
1925  |  |         else if (uprv_strncmp(correctedLocale, "zh_HK", 5) == 0)  | 
1926  |  |         { | 
1927  |  |             uprv_strcpy(correctedLocale, "zh_HK");  | 
1928  |  |         }  | 
1929  |  |  | 
1930  |  |         /* A special zh_CN_GBK locale...  | 
1931  |  |         */  | 
1932  |  |         else if (uprv_strcmp(correctedLocale, "zh_CN_GBK") == 0)  | 
1933  |  |         { | 
1934  |  |             uprv_strcpy(correctedLocale, "zh_CN");  | 
1935  |  |         }  | 
1936  |  |  | 
1937  |  |     }  | 
1938  |  |  | 
1939  |  |     return correctedLocale;  | 
1940  |  | #endif  | 
1941  |  | 
  | 
1942  | 0  | }  | 
1943  |  |  | 
1944  |  | #if !U_CHARSET_IS_UTF8  | 
1945  |  | #if U_POSIX_LOCALE  | 
1946  |  | /*  | 
1947  |  | Due to various platform differences, one platform may specify a charset,  | 
1948  |  | when they really mean a different charset. Remap the names so that they are  | 
1949  |  | compatible with ICU. Only conflicting/ambiguous aliases should be resolved  | 
1950  |  | here. Before adding anything to this function, please consider adding unique  | 
1951  |  | names to the ICU alias table in the data directory.  | 
1952  |  | */  | 
1953  |  | static const char*  | 
1954  |  | remapPlatformDependentCodepage(const char *locale, const char *name) { | 
1955  |  |     if (locale != NULL && *locale == 0) { | 
1956  |  |         /* Make sure that an empty locale is handled the same way. */  | 
1957  |  |         locale = NULL;  | 
1958  |  |     }  | 
1959  |  |     if (name == NULL) { | 
1960  |  |         return NULL;  | 
1961  |  |     }  | 
1962  |  | #if U_PLATFORM == U_PF_AIX  | 
1963  |  |     if (uprv_strcmp(name, "IBM-943") == 0) { | 
1964  |  |         /* Use the ASCII compatible ibm-943 */  | 
1965  |  |         name = "Shift-JIS";  | 
1966  |  |     }  | 
1967  |  |     else if (uprv_strcmp(name, "IBM-1252") == 0) { | 
1968  |  |         /* Use the windows-1252 that contains the Euro */  | 
1969  |  |         name = "IBM-5348";  | 
1970  |  |     }  | 
1971  |  | #elif U_PLATFORM == U_PF_SOLARIS  | 
1972  |  |     if (locale != NULL && uprv_strcmp(name, "EUC") == 0) { | 
1973  |  |         /* Solaris underspecifies the "EUC" name. */  | 
1974  |  |         if (uprv_strcmp(locale, "zh_CN") == 0) { | 
1975  |  |             name = "EUC-CN";  | 
1976  |  |         }  | 
1977  |  |         else if (uprv_strcmp(locale, "zh_TW") == 0) { | 
1978  |  |             name = "EUC-TW";  | 
1979  |  |         }  | 
1980  |  |         else if (uprv_strcmp(locale, "ko_KR") == 0) { | 
1981  |  |             name = "EUC-KR";  | 
1982  |  |         }  | 
1983  |  |     }  | 
1984  |  |     else if (uprv_strcmp(name, "eucJP") == 0) { | 
1985  |  |         /*  | 
1986  |  |         ibm-954 is the best match.  | 
1987  |  |         ibm-33722 is the default for eucJP (similar to Windows).  | 
1988  |  |         */  | 
1989  |  |         name = "eucjis";  | 
1990  |  |     }  | 
1991  |  |     else if (uprv_strcmp(name, "646") == 0) { | 
1992  |  |         /*  | 
1993  |  |          * The default codepage given by Solaris is 646 but the C library routines treat it as if it was  | 
1994  |  |          * ISO-8859-1 instead of US-ASCII(646).  | 
1995  |  |          */  | 
1996  |  |         name = "ISO-8859-1";  | 
1997  |  |     }  | 
1998  |  | #elif U_PLATFORM_IS_DARWIN_BASED  | 
1999  |  |     if (locale == NULL && *name == 0) { | 
2000  |  |         /*  | 
2001  |  |         No locale was specified, and an empty name was passed in.  | 
2002  |  |         This usually indicates that nl_langinfo didn't return valid information.  | 
2003  |  |         Mac OS X uses UTF-8 by default (especially the locale data and console).  | 
2004  |  |         */  | 
2005  |  |         name = "UTF-8";  | 
2006  |  |     }  | 
2007  |  |     else if (uprv_strcmp(name, "CP949") == 0) { | 
2008  |  |         /* Remap CP949 to a similar codepage to avoid issues with backslash and won symbol. */  | 
2009  |  |         name = "EUC-KR";  | 
2010  |  |     }  | 
2011  |  |     else if (locale != NULL && uprv_strcmp(locale, "en_US_POSIX") != 0 && uprv_strcmp(name, "US-ASCII") == 0) { | 
2012  |  |         /*  | 
2013  |  |          * For non C/POSIX locale, default the code page to UTF-8 instead of US-ASCII.  | 
2014  |  |          */  | 
2015  |  |         name = "UTF-8";  | 
2016  |  |     }  | 
2017  |  | #elif U_PLATFORM == U_PF_BSD  | 
2018  |  |     if (uprv_strcmp(name, "CP949") == 0) { | 
2019  |  |         /* Remap CP949 to a similar codepage to avoid issues with backslash and won symbol. */  | 
2020  |  |         name = "EUC-KR";  | 
2021  |  |     }  | 
2022  |  | #elif U_PLATFORM == U_PF_HPUX  | 
2023  |  |     if (locale != NULL && uprv_strcmp(locale, "zh_HK") == 0 && uprv_strcmp(name, "big5") == 0) { | 
2024  |  |         /* HP decided to extend big5 as hkbig5 even though it's not compatible :-( */  | 
2025  |  |         /* zh_TW.big5 is not the same charset as zh_HK.big5! */  | 
2026  |  |         name = "hkbig5";  | 
2027  |  |     }  | 
2028  |  |     else if (uprv_strcmp(name, "eucJP") == 0) { | 
2029  |  |         /*  | 
2030  |  |         ibm-1350 is the best match, but unavailable.  | 
2031  |  |         ibm-954 is mostly a superset of ibm-1350.  | 
2032  |  |         ibm-33722 is the default for eucJP (similar to Windows).  | 
2033  |  |         */  | 
2034  |  |         name = "eucjis";  | 
2035  |  |     }  | 
2036  |  | #elif U_PLATFORM == U_PF_LINUX  | 
2037  |  |     if (locale != NULL && uprv_strcmp(name, "euc") == 0) { | 
2038  |  |         /* Linux underspecifies the "EUC" name. */  | 
2039  |  |         if (uprv_strcmp(locale, "korean") == 0) { | 
2040  |  |             name = "EUC-KR";  | 
2041  |  |         }  | 
2042  |  |         else if (uprv_strcmp(locale, "japanese") == 0) { | 
2043  |  |             /* See comment below about eucJP */  | 
2044  |  |             name = "eucjis";  | 
2045  |  |         }  | 
2046  |  |     }  | 
2047  |  |     else if (uprv_strcmp(name, "eucjp") == 0) { | 
2048  |  |         /*  | 
2049  |  |         ibm-1350 is the best match, but unavailable.  | 
2050  |  |         ibm-954 is mostly a superset of ibm-1350.  | 
2051  |  |         ibm-33722 is the default for eucJP (similar to Windows).  | 
2052  |  |         */  | 
2053  |  |         name = "eucjis";  | 
2054  |  |     }  | 
2055  |  |     else if (locale != NULL && uprv_strcmp(locale, "en_US_POSIX") != 0 &&  | 
2056  |  |             (uprv_strcmp(name, "ANSI_X3.4-1968") == 0 || uprv_strcmp(name, "US-ASCII") == 0)) { | 
2057  |  |         /*  | 
2058  |  |          * For non C/POSIX locale, default the code page to UTF-8 instead of US-ASCII.  | 
2059  |  |          */  | 
2060  |  |         name = "UTF-8";  | 
2061  |  |     }  | 
2062  |  |     /*  | 
2063  |  |      * Linux returns ANSI_X3.4-1968 for C/POSIX, but the call site takes care of  | 
2064  |  |      * it by falling back to 'US-ASCII' when NULL is returned from this  | 
2065  |  |      * function. So, we don't have to worry about it here.  | 
2066  |  |      */  | 
2067  |  | #endif  | 
2068  |  |     /* return NULL when "" is passed in */  | 
2069  |  |     if (*name == 0) { | 
2070  |  |         name = NULL;  | 
2071  |  |     }  | 
2072  |  |     return name;  | 
2073  |  | }  | 
2074  |  |  | 
2075  |  | static const char*  | 
2076  |  | getCodepageFromPOSIXID(const char *localeName, char * buffer, int32_t buffCapacity)  | 
2077  |  | { | 
2078  |  |     char localeBuf[100];  | 
2079  |  |     const char *name = NULL;  | 
2080  |  |     char *variant = NULL;  | 
2081  |  |  | 
2082  |  |     if (localeName != NULL && (name = (uprv_strchr(localeName, '.'))) != NULL) { | 
2083  |  |         size_t localeCapacity = uprv_min(sizeof(localeBuf), (name-localeName)+1);  | 
2084  |  |         uprv_strncpy(localeBuf, localeName, localeCapacity);  | 
2085  |  |         localeBuf[localeCapacity-1] = 0; /* ensure NULL termination */  | 
2086  |  |         name = uprv_strncpy(buffer, name+1, buffCapacity);  | 
2087  |  |         buffer[buffCapacity-1] = 0; /* ensure NULL termination */  | 
2088  |  |         if ((variant = const_cast<char *>(uprv_strchr(name, '@'))) != NULL) { | 
2089  |  |             *variant = 0;  | 
2090  |  |         }  | 
2091  |  |         name = remapPlatformDependentCodepage(localeBuf, name);  | 
2092  |  |     }  | 
2093  |  |     return name;  | 
2094  |  | }  | 
2095  |  | #endif  | 
2096  |  |  | 
2097  |  | static const char*  | 
2098  |  | int_getDefaultCodepage()  | 
2099  |  | { | 
2100  |  | #if U_PLATFORM == U_PF_OS400  | 
2101  |  |     uint32_t ccsid = 37; /* Default to ibm-37 */  | 
2102  |  |     static char codepage[64];  | 
2103  |  |     Qwc_JOBI0400_t jobinfo;  | 
2104  |  |     Qus_EC_t error = { sizeof(Qus_EC_t) }; /* SPI error code */ | 
2105  |  |  | 
2106  |  |     EPT_CALL(QUSRJOBI)(&jobinfo, sizeof(jobinfo), "JOBI0400",  | 
2107  |  |         "*                         ", "                ", &error);  | 
2108  |  |  | 
2109  |  |     if (error.Bytes_Available == 0) { | 
2110  |  |         if (jobinfo.Coded_Char_Set_ID != 0xFFFF) { | 
2111  |  |             ccsid = (uint32_t)jobinfo.Coded_Char_Set_ID;  | 
2112  |  |         }  | 
2113  |  |         else if (jobinfo.Default_Coded_Char_Set_Id != 0xFFFF) { | 
2114  |  |             ccsid = (uint32_t)jobinfo.Default_Coded_Char_Set_Id;  | 
2115  |  |         }  | 
2116  |  |         /* else use the default */  | 
2117  |  |     }  | 
2118  |  |     sprintf(codepage,"ibm-%d", ccsid);  | 
2119  |  |     return codepage;  | 
2120  |  |  | 
2121  |  | #elif U_PLATFORM == U_PF_OS390  | 
2122  |  |     static char codepage[64];  | 
2123  |  |  | 
2124  |  |     strncpy(codepage, nl_langinfo(CODESET),63-strlen(UCNV_SWAP_LFNL_OPTION_STRING));  | 
2125  |  |     strcat(codepage,UCNV_SWAP_LFNL_OPTION_STRING);  | 
2126  |  |     codepage[63] = 0; /* NULL terminate */  | 
2127  |  |  | 
2128  |  |     return codepage;  | 
2129  |  |  | 
2130  |  | #elif U_PLATFORM_USES_ONLY_WIN32_API  | 
2131  |  |     static char codepage[64];  | 
2132  |  |     DWORD codepageNumber = 0;  | 
2133  |  |  | 
2134  |  | #if U_PLATFORM_HAS_WINUWP_API == 1  | 
2135  |  |     // UWP doesn't have a direct API to get the default ACP as Microsoft would rather  | 
2136  |  |     // have folks use Unicode than a "system" code page, however this is the same  | 
2137  |  |     // codepage as the system default locale codepage.  (FWIW, the system locale is  | 
2138  |  |     // ONLY used for codepage, it should never be used for anything else)  | 
2139  |  |     GetLocaleInfoEx(LOCALE_NAME_SYSTEM_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,  | 
2140  |  |         (LPWSTR)&codepageNumber, sizeof(codepageNumber) / sizeof(WCHAR));  | 
2141  |  | #else  | 
2142  |  |     // Win32 apps can call GetACP  | 
2143  |  |     codepageNumber = GetACP();  | 
2144  |  | #endif  | 
2145  |  |     // Special case for UTF-8  | 
2146  |  |     if (codepageNumber == 65001)  | 
2147  |  |     { | 
2148  |  |         return "UTF-8";  | 
2149  |  |     }  | 
2150  |  |     // Windows codepages can look like windows-1252, so format the found number  | 
2151  |  |     // the numbers are eclectic, however all valid system code pages, besides UTF-8  | 
2152  |  |     // are between 3 and 19999  | 
2153  |  |     if (codepageNumber > 0 && codepageNumber < 20000)  | 
2154  |  |     { | 
2155  |  |         sprintf(codepage, "windows-%ld", codepageNumber);  | 
2156  |  |         return codepage;  | 
2157  |  |     }  | 
2158  |  |     // If the codepage number call failed then return UTF-8  | 
2159  |  |     return "UTF-8";  | 
2160  |  |  | 
2161  |  | #elif U_POSIX_LOCALE  | 
2162  |  |     static char codesetName[100];  | 
2163  |  |     const char *localeName = NULL;  | 
2164  |  |     const char *name = NULL;  | 
2165  |  |  | 
2166  |  |     localeName = uprv_getPOSIXIDForDefaultCodepage();  | 
2167  |  |     uprv_memset(codesetName, 0, sizeof(codesetName));  | 
2168  |  |     /* On Solaris nl_langinfo returns C locale values unless setlocale  | 
2169  |  |      * was called earlier.  | 
2170  |  |      */  | 
2171  |  | #if (U_HAVE_NL_LANGINFO_CODESET && U_PLATFORM != U_PF_SOLARIS)  | 
2172  |  |     /* When available, check nl_langinfo first because it usually gives more  | 
2173  |  |        useful names. It depends on LC_CTYPE.  | 
2174  |  |        nl_langinfo may use the same buffer as setlocale. */  | 
2175  |  |     { | 
2176  |  |         const char *codeset = nl_langinfo(U_NL_LANGINFO_CODESET);  | 
2177  |  | #if U_PLATFORM_IS_DARWIN_BASED || U_PLATFORM_IS_LINUX_BASED  | 
2178  |  |         /*  | 
2179  |  |          * On Linux and MacOSX, ensure that default codepage for non C/POSIX locale is UTF-8  | 
2180  |  |          * instead of ASCII.  | 
2181  |  |          */  | 
2182  |  |         if (uprv_strcmp(localeName, "en_US_POSIX") != 0) { | 
2183  |  |             codeset = remapPlatformDependentCodepage(localeName, codeset);  | 
2184  |  |         } else  | 
2185  |  | #endif  | 
2186  |  |         { | 
2187  |  |             codeset = remapPlatformDependentCodepage(NULL, codeset);  | 
2188  |  |         }  | 
2189  |  |  | 
2190  |  |         if (codeset != NULL) { | 
2191  |  |             uprv_strncpy(codesetName, codeset, sizeof(codesetName));  | 
2192  |  |             codesetName[sizeof(codesetName)-1] = 0;  | 
2193  |  |             return codesetName;  | 
2194  |  |         }  | 
2195  |  |     }  | 
2196  |  | #endif  | 
2197  |  |  | 
2198  |  |     /* Use setlocale in a nice way, and then check some environment variables.  | 
2199  |  |        Maybe the application used setlocale already.  | 
2200  |  |     */  | 
2201  |  |     uprv_memset(codesetName, 0, sizeof(codesetName));  | 
2202  |  |     name = getCodepageFromPOSIXID(localeName, codesetName, sizeof(codesetName));  | 
2203  |  |     if (name) { | 
2204  |  |         /* if we can find the codeset name from setlocale, return that. */  | 
2205  |  |         return name;  | 
2206  |  |     }  | 
2207  |  |  | 
2208  |  |     if (*codesetName == 0)  | 
2209  |  |     { | 
2210  |  |         /* Everything failed. Return US ASCII (ISO 646). */  | 
2211  |  |         (void)uprv_strcpy(codesetName, "US-ASCII");  | 
2212  |  |     }  | 
2213  |  |     return codesetName;  | 
2214  |  | #else  | 
2215  |  |     return "US-ASCII";  | 
2216  |  | #endif  | 
2217  |  | }  | 
2218  |  |  | 
2219  |  |  | 
2220  |  | U_CAPI const char*  U_EXPORT2  | 
2221  |  | uprv_getDefaultCodepage()  | 
2222  |  | { | 
2223  |  |     static char const  *name = NULL;  | 
2224  |  |     umtx_lock(NULL);  | 
2225  |  |     if (name == NULL) { | 
2226  |  |         name = int_getDefaultCodepage();  | 
2227  |  |     }  | 
2228  |  |     umtx_unlock(NULL);  | 
2229  |  |     return name;  | 
2230  |  | }  | 
2231  |  | #endif  /* !U_CHARSET_IS_UTF8 */  | 
2232  |  |  | 
2233  |  |  | 
2234  |  | /* end of platform-specific implementation -------------- */  | 
2235  |  |  | 
2236  |  | /* version handling --------------------------------------------------------- */  | 
2237  |  |  | 
2238  |  | U_CAPI void U_EXPORT2  | 
2239  | 0  | u_versionFromString(UVersionInfo versionArray, const char *versionString) { | 
2240  | 0  |     char *end;  | 
2241  | 0  |     uint16_t part=0;  | 
2242  |  | 
  | 
2243  | 0  |     if(versionArray==NULL) { | 
2244  | 0  |         return;  | 
2245  | 0  |     }  | 
2246  |  |  | 
2247  | 0  |     if(versionString!=NULL) { | 
2248  | 0  |         for(;;) { | 
2249  | 0  |             versionArray[part]=(uint8_t)uprv_strtoul(versionString, &end, 10);  | 
2250  | 0  |             if(end==versionString || ++part==U_MAX_VERSION_LENGTH || *end!=U_VERSION_DELIMITER) { | 
2251  | 0  |                 break;  | 
2252  | 0  |             }  | 
2253  | 0  |             versionString=end+1;  | 
2254  | 0  |         }  | 
2255  | 0  |     }  | 
2256  |  | 
  | 
2257  | 0  |     while(part<U_MAX_VERSION_LENGTH) { | 
2258  | 0  |         versionArray[part++]=0;  | 
2259  | 0  |     }  | 
2260  | 0  | }  | 
2261  |  |  | 
2262  |  | U_CAPI void U_EXPORT2  | 
2263  | 0  | u_versionFromUString(UVersionInfo versionArray, const UChar *versionString) { | 
2264  | 0  |     if(versionArray!=NULL && versionString!=NULL) { | 
2265  | 0  |         char versionChars[U_MAX_VERSION_STRING_LENGTH+1];  | 
2266  | 0  |         int32_t len = u_strlen(versionString);  | 
2267  | 0  |         if(len>U_MAX_VERSION_STRING_LENGTH) { | 
2268  | 0  |             len = U_MAX_VERSION_STRING_LENGTH;  | 
2269  | 0  |         }  | 
2270  | 0  |         u_UCharsToChars(versionString, versionChars, len);  | 
2271  | 0  |         versionChars[len]=0;  | 
2272  | 0  |         u_versionFromString(versionArray, versionChars);  | 
2273  | 0  |     }  | 
2274  | 0  | }  | 
2275  |  |  | 
2276  |  | U_CAPI void U_EXPORT2  | 
2277  | 0  | u_versionToString(const UVersionInfo versionArray, char *versionString) { | 
2278  | 0  |     uint16_t count, part;  | 
2279  | 0  |     uint8_t field;  | 
2280  |  | 
  | 
2281  | 0  |     if(versionString==NULL) { | 
2282  | 0  |         return;  | 
2283  | 0  |     }  | 
2284  |  |  | 
2285  | 0  |     if(versionArray==NULL) { | 
2286  | 0  |         versionString[0]=0;  | 
2287  | 0  |         return;  | 
2288  | 0  |     }  | 
2289  |  |  | 
2290  |  |     /* count how many fields need to be written */  | 
2291  | 0  |     for(count=4; count>0 && versionArray[count-1]==0; --count) { | 
2292  | 0  |     }  | 
2293  |  | 
  | 
2294  | 0  |     if(count <= 1) { | 
2295  | 0  |         count = 2;  | 
2296  | 0  |     }  | 
2297  |  |  | 
2298  |  |     /* write the first part */  | 
2299  |  |     /* write the decimal field value */  | 
2300  | 0  |     field=versionArray[0];  | 
2301  | 0  |     if(field>=100) { | 
2302  | 0  |         *versionString++=(char)('0'+field/100); | 
2303  | 0  |         field%=100;  | 
2304  | 0  |     }  | 
2305  | 0  |     if(field>=10) { | 
2306  | 0  |         *versionString++=(char)('0'+field/10); | 
2307  | 0  |         field%=10;  | 
2308  | 0  |     }  | 
2309  | 0  |     *versionString++=(char)('0'+field); | 
2310  |  |  | 
2311  |  |     /* write the following parts */  | 
2312  | 0  |     for(part=1; part<count; ++part) { | 
2313  |  |         /* write a dot first */  | 
2314  | 0  |         *versionString++=U_VERSION_DELIMITER;  | 
2315  |  |  | 
2316  |  |         /* write the decimal field value */  | 
2317  | 0  |         field=versionArray[part];  | 
2318  | 0  |         if(field>=100) { | 
2319  | 0  |             *versionString++=(char)('0'+field/100); | 
2320  | 0  |             field%=100;  | 
2321  | 0  |         }  | 
2322  | 0  |         if(field>=10) { | 
2323  | 0  |             *versionString++=(char)('0'+field/10); | 
2324  | 0  |             field%=10;  | 
2325  | 0  |         }  | 
2326  | 0  |         *versionString++=(char)('0'+field); | 
2327  | 0  |     }  | 
2328  |  |  | 
2329  |  |     /* NUL-terminate */  | 
2330  | 0  |     *versionString=0;  | 
2331  | 0  | }  | 
2332  |  |  | 
2333  |  | U_CAPI void U_EXPORT2  | 
2334  | 0  | u_getVersion(UVersionInfo versionArray) { | 
2335  | 0  |     (void)copyright;   // Suppress unused variable warning from clang.  | 
2336  | 0  |     u_versionFromString(versionArray, U_ICU_VERSION);  | 
2337  | 0  | }  | 
2338  |  |  | 
2339  |  | /**  | 
2340  |  |  * icucfg.h dependent code  | 
2341  |  |  */  | 
2342  |  |  | 
2343  |  | #if U_ENABLE_DYLOAD && HAVE_DLOPEN && !U_PLATFORM_USES_ONLY_WIN32_API  | 
2344  |  |  | 
2345  |  | #if HAVE_DLFCN_H  | 
2346  |  | #ifdef __MVS__  | 
2347  |  | #ifndef __SUSV3  | 
2348  |  | #define __SUSV3 1  | 
2349  |  | #endif  | 
2350  |  | #endif  | 
2351  |  | #include <dlfcn.h>  | 
2352  |  | #endif /* HAVE_DLFCN_H */  | 
2353  |  |  | 
2354  |  | U_CAPI void * U_EXPORT2  | 
2355  | 0  | uprv_dl_open(const char *libName, UErrorCode *status) { | 
2356  | 0  |   void *ret = NULL;  | 
2357  | 0  |   if(U_FAILURE(*status)) return ret;  | 
2358  | 0  |   ret =  dlopen(libName, RTLD_NOW|RTLD_GLOBAL);  | 
2359  | 0  |   if(ret==NULL) { | 
2360  |  | #ifdef U_TRACE_DYLOAD  | 
2361  |  |     printf("dlerror on dlopen(%s): %s\n", libName, dlerror()); | 
2362  |  | #endif  | 
2363  | 0  |     *status = U_MISSING_RESOURCE_ERROR;  | 
2364  | 0  |   }  | 
2365  | 0  |   return ret;  | 
2366  | 0  | }  | 
2367  |  |  | 
2368  |  | U_CAPI void U_EXPORT2  | 
2369  | 0  | uprv_dl_close(void *lib, UErrorCode *status) { | 
2370  | 0  |   if(U_FAILURE(*status)) return;  | 
2371  | 0  |   dlclose(lib);  | 
2372  | 0  | }  | 
2373  |  |  | 
2374  |  | U_CAPI UVoidFunction* U_EXPORT2  | 
2375  | 0  | uprv_dlsym_func(void *lib, const char* sym, UErrorCode *status) { | 
2376  | 0  |   union { | 
2377  | 0  |       UVoidFunction *fp;  | 
2378  | 0  |       void *vp;  | 
2379  | 0  |   } uret;  | 
2380  | 0  |   uret.fp = NULL;  | 
2381  | 0  |   if(U_FAILURE(*status)) return uret.fp;  | 
2382  | 0  |   uret.vp = dlsym(lib, sym);  | 
2383  | 0  |   if(uret.vp == NULL) { | 
2384  |  | #ifdef U_TRACE_DYLOAD  | 
2385  |  |     printf("dlerror on dlsym(%p,%s): %s\n", lib,sym, dlerror()); | 
2386  |  | #endif  | 
2387  | 0  |     *status = U_MISSING_RESOURCE_ERROR;  | 
2388  | 0  |   }  | 
2389  | 0  |   return uret.fp;  | 
2390  | 0  | }  | 
2391  |  |  | 
2392  |  | #elif U_ENABLE_DYLOAD && U_PLATFORM_USES_ONLY_WIN32_API && !U_PLATFORM_HAS_WINUWP_API  | 
2393  |  |  | 
2394  |  | /* Windows API implementation. */  | 
2395  |  | // Note: UWP does not expose/allow these APIs, so the UWP version gets the null implementation. */  | 
2396  |  |  | 
2397  |  | U_CAPI void * U_EXPORT2  | 
2398  |  | uprv_dl_open(const char *libName, UErrorCode *status) { | 
2399  |  |   HMODULE lib = NULL;  | 
2400  |  |  | 
2401  |  |   if(U_FAILURE(*status)) return NULL;  | 
2402  |  |  | 
2403  |  |   lib = LoadLibraryA(libName);  | 
2404  |  |  | 
2405  |  |   if(lib==NULL) { | 
2406  |  |     *status = U_MISSING_RESOURCE_ERROR;  | 
2407  |  |   }  | 
2408  |  |  | 
2409  |  |   return (void*)lib;  | 
2410  |  | }  | 
2411  |  |  | 
2412  |  | U_CAPI void U_EXPORT2  | 
2413  |  | uprv_dl_close(void *lib, UErrorCode *status) { | 
2414  |  |   HMODULE handle = (HMODULE)lib;  | 
2415  |  |   if(U_FAILURE(*status)) return;  | 
2416  |  |  | 
2417  |  |   FreeLibrary(handle);  | 
2418  |  |  | 
2419  |  |   return;  | 
2420  |  | }  | 
2421  |  |  | 
2422  |  | U_CAPI UVoidFunction* U_EXPORT2  | 
2423  |  | uprv_dlsym_func(void *lib, const char* sym, UErrorCode *status) { | 
2424  |  |   HMODULE handle = (HMODULE)lib;  | 
2425  |  |   UVoidFunction* addr = NULL;  | 
2426  |  |  | 
2427  |  |   if(U_FAILURE(*status) || lib==NULL) return NULL;  | 
2428  |  |  | 
2429  |  |   addr = (UVoidFunction*)GetProcAddress(handle, sym);  | 
2430  |  |  | 
2431  |  |   if(addr==NULL) { | 
2432  |  |     DWORD lastError = GetLastError();  | 
2433  |  |     if(lastError == ERROR_PROC_NOT_FOUND) { | 
2434  |  |       *status = U_MISSING_RESOURCE_ERROR;  | 
2435  |  |     } else { | 
2436  |  |       *status = U_UNSUPPORTED_ERROR; /* other unknown error. */  | 
2437  |  |     }  | 
2438  |  |   }  | 
2439  |  |  | 
2440  |  |   return addr;  | 
2441  |  | }  | 
2442  |  |  | 
2443  |  | #else  | 
2444  |  |  | 
2445  |  | /* No dynamic loading, null (nonexistent) implementation. */  | 
2446  |  |  | 
2447  |  | U_CAPI void * U_EXPORT2  | 
2448  |  | uprv_dl_open(const char *libName, UErrorCode *status) { | 
2449  |  |     (void)libName;  | 
2450  |  |     if(U_FAILURE(*status)) return NULL;  | 
2451  |  |     *status = U_UNSUPPORTED_ERROR;  | 
2452  |  |     return NULL;  | 
2453  |  | }  | 
2454  |  |  | 
2455  |  | U_CAPI void U_EXPORT2  | 
2456  |  | uprv_dl_close(void *lib, UErrorCode *status) { | 
2457  |  |     (void)lib;  | 
2458  |  |     if(U_FAILURE(*status)) return;  | 
2459  |  |     *status = U_UNSUPPORTED_ERROR;  | 
2460  |  |     return;  | 
2461  |  | }  | 
2462  |  |  | 
2463  |  | U_CAPI UVoidFunction* U_EXPORT2  | 
2464  |  | uprv_dlsym_func(void *lib, const char* sym, UErrorCode *status) { | 
2465  |  |   (void)lib;  | 
2466  |  |   (void)sym;  | 
2467  |  |   if(U_SUCCESS(*status)) { | 
2468  |  |     *status = U_UNSUPPORTED_ERROR;  | 
2469  |  |   }  | 
2470  |  |   return (UVoidFunction*)NULL;  | 
2471  |  | }  | 
2472  |  |  | 
2473  |  | #endif  | 
2474  |  |  | 
2475  |  | /*  | 
2476  |  |  * Hey, Emacs, please set the following:  | 
2477  |  |  *  | 
2478  |  |  * Local Variables:  | 
2479  |  |  * indent-tabs-mode: nil  | 
2480  |  |  * End:  | 
2481  |  |  *  | 
2482  |  |  */  |