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