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