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