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