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