2 ******************************************************************************
4 * Copyright (C) 1997-2010, International Business Machines
5 * Corporation and others. All Rights Reserved.
7 ******************************************************************************
9 * FILE NAME : putil.c (previously putil.cpp and ptypes.cpp)
11 * Date Name Description
12 * 04/14/97 aliu Creation.
13 * 04/24/97 aliu Added getDefaultDataDirectory() and
14 * getDefaultLocaleID().
15 * 04/28/97 aliu Rewritten to assume Unix and apply general methods
16 * for assumed case. Non-UNIX platforms must be
17 * special-cased. Rewrote numeric methods dealing
18 * with NaN and Infinity to be platform independent
19 * over all IEEE 754 platforms.
20 * 05/13/97 aliu Restored sign of timezone
21 * (semantics are hours West of GMT)
22 * 06/16/98 erm Added IEEE_754 stuff, cleaned up isInfinite, isNan,
24 * 07/22/98 stephen Added remainder, max, min, trunc
25 * 08/13/98 stephen Added isNegativeInfinity, isPositiveInfinity
26 * 08/24/98 stephen Added longBitsFromDouble
27 * 09/08/98 stephen Minor changes for Mac Port
28 * 03/02/99 stephen Removed openFile(). Added AS400 support.
30 * 04/15/99 stephen Converted to C.
31 * 06/28/99 stephen Removed mutex locking in u_isBigEndian().
32 * 08/04/99 jeffrey R. Added OS/2 changes
33 * 11/15/99 helena Integrated S/390 IEEE support.
34 * 04/26/01 Barry N. OS/400 support for uprv_getDefaultLocaleID
35 * 08/15/01 Steven H. OS/400 support for uprv_getDefaultCodepage
36 * 01/03/08 Steven L. Fake Time Support
37 ******************************************************************************
40 /* Define _XOPEN_SOURCE for access to POSIX functions. */
42 /* Use the predefined value. */
46 * The Open Group Base Specifications Issue 6 (IEEE Std 1003.1, 2004 Edition)
48 * SUSv3 = Open Group Single UNIX Specification, Version 3 (UNIX03)
50 # define _XOPEN_SOURCE 600
53 /* Make sure things like readlink and such functions work.
54 Poorly upgraded Solaris machines can't have this defined.
55 Cleanly installed Solaris can use this #define.
57 #if !defined(_XOPEN_SOURCE_EXTENDED) && ((!defined(__STDC_VERSION__) || __STDC_VERSION__ >= 199901L) || defined(__xlc__))
58 #define _XOPEN_SOURCE_EXTENDED 1
61 /* include ICU headers */
62 #include "unicode/utypes.h"
63 #include "unicode/putil.h"
64 #include "unicode/ustring.h"
73 /* Include standard headers. */
82 /* include system headers */
84 # define WIN32_LEAN_AND_MEAN
92 #elif defined(U_CYGWIN) && defined(__STRICT_ANSI__)
93 /* tzset isn't defined in strict ANSI on Cygwin. */
94 # undef __STRICT_ANSI__
97 # include <qusec.h> /* error code structure */
98 # include <qusrjobi.h>
99 # include <qliept.h> /* EPT_CALL macro - this include must be after all other "QSYSINCs" */
100 # include <mih/testptr.h> /* For uprv_maximumPtr */
101 #elif defined(XP_MAC)
103 # include <IntlResources.h>
105 # include <Folders.h>
106 # include <MacTypes.h>
107 # include <TextUtils.h>
108 # define ICU_NO_USER_DATA_OVERRIDE 1
110 #include "unicode/ucnv.h" /* Needed for UCNV_SWAP_LFNL_OPTION_STRING */
111 #elif defined(U_DARWIN) || defined(U_LINUX) || defined(U_BSD)
115 #include <sys/neutrino.h>
116 #elif defined(U_SOLARIS)
123 #if defined(U_DARWIN)
124 #include <TargetConditionals.h>
128 #include <sys/time.h>
132 * Only include langinfo.h if we have a way to get the codeset. If we later
133 * depend on more feature, we can test on U_HAVE_NL_LANGINFO.
137 #if U_HAVE_NL_LANGINFO_CODESET
138 #include <langinfo.h>
142 * Simple things (presence of functions, etc) should just go in configure.in and be added to
143 * icucfg.h via autoheader.
145 #if defined(HAVE_CONFIG_H)
149 /* Define the extension for data files, again... */
150 #define DATA_TYPE "dat"
152 /* Leave this copyright notice here! */
153 static const char copyright[] = U_COPYRIGHT_STRING;
155 /* floating point implementations ------------------------------------------- */
157 /* We return QNAN rather than SNAN*/
158 #define SIGN 0x80000000U
160 /* Make it easy to define certain types of constants */
162 int64_t i64; /* This must be defined first in order to allow the initialization to work. This is a C89 feature. */
164 } BitPatternConversion;
165 static const BitPatternConversion gNan = { (int64_t) INT64_C(0x7FF8000000000000) };
166 static const BitPatternConversion gInf = { (int64_t) INT64_C(0x7FF0000000000000) };
168 /*---------------------------------------------------------------------------
170 Our general strategy is to assume we're on a POSIX platform. Platforms which
171 are non-POSIX must declare themselves so. The default POSIX implementation
172 will sometimes work for non-POSIX platforms as well (e.g., the NaN-related
174 ---------------------------------------------------------------------------*/
176 #if defined(U_WINDOWS) || defined(XP_MAC) || defined(OS400)
177 # undef U_POSIX_LOCALE
179 # define U_POSIX_LOCALE 1
183 WARNING! u_topNBytesOfDouble and u_bottomNBytesOfDouble
184 can't be properly optimized by the gcc compiler sometimes (i.e. gcc 3.2).
188 u_topNBytesOfDouble(double* d, int n)
193 return (char*)(d + 1) - n;
198 u_bottomNBytesOfDouble(double* d, int n)
201 return (char*)(d + 1) - n;
206 #endif /* !IEEE_754 */
210 u_signBit(double d) {
213 hiByte = *(uint8_t *)&d;
215 hiByte = *(((uint8_t *)&d) + sizeof(double) - 1);
217 return (hiByte & 0x80) != 0;
223 #if defined (U_DEBUG_FAKETIME)
224 /* Override the clock to test things without having to move the system clock.
225 * Assumes POSIX gettimeofday() will function
227 UDate fakeClock_t0 = 0; /** Time to start the clock from **/
228 UDate fakeClock_dt = 0; /** Offset (fake time - real time) **/
229 UBool fakeClock_set = FALSE; /** True if fake clock has spun up **/
230 static UMTX fakeClockMutex = NULL;
232 static UDate getUTCtime_real() {
233 struct timeval posixTime;
234 gettimeofday(&posixTime, NULL);
235 return (UDate)(((int64_t)posixTime.tv_sec * U_MILLIS_PER_SECOND) + (posixTime.tv_usec/1000));
238 static UDate getUTCtime_fake() {
239 umtx_lock(&fakeClockMutex);
241 UDate real = getUTCtime_real();
242 const char *fake_start = getenv("U_FAKETIME_START");
243 if((fake_start!=NULL) && (fake_start[0]!=0)) {
244 sscanf(fake_start,"%lf",&fakeClock_t0);
245 fakeClock_dt = fakeClock_t0 - real;
246 fprintf(stderr,"U_DEBUG_FAKETIME was set at compile time, so the ICU clock will start at a preset value\n"
247 "env variable U_FAKETIME_START=%.0f (%s) for an offset of %.0f ms from the current time %.0f\n",
248 fakeClock_t0, fake_start, fakeClock_dt, real);
251 fprintf(stderr,"U_DEBUG_FAKETIME was set at compile time, but U_FAKETIME_START was not set.\n"
252 "Set U_FAKETIME_START to the number of milliseconds since 1/1/1970 to set the ICU clock.\n");
254 fakeClock_set = TRUE;
256 umtx_unlock(&fakeClockMutex);
258 return getUTCtime_real() + fakeClock_dt;
262 #if defined(U_WINDOWS)
266 } FileTimeConversion; /* This is like a ULARGE_INTEGER */
268 /* Number of 100 nanoseconds from 1/1/1601 to 1/1/1970 */
269 #define EPOCH_BIAS INT64_C(116444736000000000)
270 #define HECTONANOSECOND_PER_MILLISECOND 10000
274 /*---------------------------------------------------------------------------
275 Universal Implementations
276 These are designed to work on all platforms. Try these, and if they
277 don't work on your platform, then special case your platform with new
279 ---------------------------------------------------------------------------*/
281 U_CAPI UDate U_EXPORT2
284 #if defined(U_DEBUG_FAKETIME)
285 return getUTCtime_fake(); /* Hook for overriding the clock */
287 return uprv_getRawUTCtime();
291 /* Return UTC (GMT) time measured in milliseconds since 0:00 on 1/1/70.*/
292 U_CAPI UDate U_EXPORT2
299 uprv_memset( &tmrec, 0, sizeof(tmrec) );
303 t1 = mktime(&tmrec); /* seconds of 1/1/1970*/
306 uprv_memcpy( &tmrec, gmtime(&t), sizeof(tmrec) );
307 t2 = mktime(&tmrec); /* seconds of current GMT*/
308 return (UDate)(t2 - t1) * U_MILLIS_PER_SECOND; /* GMT (or UTC) in seconds since 1970*/
309 #elif defined(U_WINDOWS)
311 FileTimeConversion winTime;
312 GetSystemTimeAsFileTime(&winTime.fileTime);
313 return (UDate)((winTime.int64 - EPOCH_BIAS) / HECTONANOSECOND_PER_MILLISECOND);
316 #if defined(HAVE_GETTIMEOFDAY)
317 struct timeval posixTime;
318 gettimeofday(&posixTime, NULL);
319 return (UDate)(((int64_t)posixTime.tv_sec * U_MILLIS_PER_SECOND) + (posixTime.tv_usec/1000));
323 return (UDate)epochtime * U_MILLIS_PER_SECOND;
329 /*-----------------------------------------------------------------------------
331 These methods detect and return NaN and infinity values for doubles
332 conforming to IEEE 754. Platforms which support this standard include X86,
333 Mac 680x0, Mac PowerPC, AIX RS/6000, and most others.
334 If this doesn't work on your platform, you have non-IEEE floating-point, and
335 will need to code your own versions. A naive implementation is to return 0.0
336 for getNaN and getInfinity, and false for isNaN and isInfinite.
337 ---------------------------------------------------------------------------*/
339 U_CAPI UBool U_EXPORT2
340 uprv_isNaN(double number)
343 BitPatternConversion convertedNumber;
344 convertedNumber.d64 = number;
345 /* Infinity is 0x7FF0000000000000U. Anything greater than that is a NaN */
346 return (UBool)((convertedNumber.i64 & U_INT64_MAX) > gInf.i64);
349 uint32_t highBits = *(uint32_t*)u_topNBytesOfDouble(&number,
351 uint32_t lowBits = *(uint32_t*)u_bottomNBytesOfDouble(&number,
354 return ((highBits & 0x7F080000L) == 0x7F080000L) &&
355 (lowBits == 0x00000000L);
358 /* If your platform doesn't support IEEE 754 but *does* have an NaN value,*/
359 /* you'll need to replace this default implementation with what's correct*/
360 /* for your platform.*/
361 return number != number;
365 U_CAPI UBool U_EXPORT2
366 uprv_isInfinite(double number)
369 BitPatternConversion convertedNumber;
370 convertedNumber.d64 = number;
371 /* Infinity is exactly 0x7FF0000000000000U. */
372 return (UBool)((convertedNumber.i64 & U_INT64_MAX) == gInf.i64);
374 uint32_t highBits = *(uint32_t*)u_topNBytesOfDouble(&number,
376 uint32_t lowBits = *(uint32_t*)u_bottomNBytesOfDouble(&number,
379 return ((highBits & ~SIGN) == 0x70FF0000L) && (lowBits == 0x00000000L);
382 /* If your platform doesn't support IEEE 754 but *does* have an infinity*/
383 /* value, you'll need to replace this default implementation with what's*/
384 /* correct for your platform.*/
385 return number == (2.0 * number);
389 U_CAPI UBool U_EXPORT2
390 uprv_isPositiveInfinity(double number)
392 #if IEEE_754 || defined(OS390)
393 return (UBool)(number > 0 && uprv_isInfinite(number));
395 return uprv_isInfinite(number);
399 U_CAPI UBool U_EXPORT2
400 uprv_isNegativeInfinity(double number)
402 #if IEEE_754 || defined(OS390)
403 return (UBool)(number < 0 && uprv_isInfinite(number));
406 uint32_t highBits = *(uint32_t*)u_topNBytesOfDouble(&number,
408 return((highBits & SIGN) && uprv_isInfinite(number));
413 U_CAPI double U_EXPORT2
416 #if IEEE_754 || defined(OS390)
419 /* If your platform doesn't support IEEE 754 but *does* have an NaN value,*/
420 /* you'll need to replace this default implementation with what's correct*/
421 /* for your platform.*/
426 U_CAPI double U_EXPORT2
429 #if IEEE_754 || defined(OS390)
432 /* If your platform doesn't support IEEE 754 but *does* have an infinity*/
433 /* value, you'll need to replace this default implementation with what's*/
434 /* correct for your platform.*/
439 U_CAPI double U_EXPORT2
445 U_CAPI double U_EXPORT2
451 U_CAPI double U_EXPORT2
454 return uprv_floor(x + 0.5);
457 U_CAPI double U_EXPORT2
463 U_CAPI double U_EXPORT2
464 uprv_modf(double x, double* y)
469 U_CAPI double U_EXPORT2
470 uprv_fmod(double x, double y)
475 U_CAPI double U_EXPORT2
476 uprv_pow(double x, double y)
478 /* This is declared as "double pow(double x, double y)" */
482 U_CAPI double U_EXPORT2
483 uprv_pow10(int32_t x)
485 return pow(10.0, (double)x);
488 U_CAPI double U_EXPORT2
489 uprv_fmax(double x, double y)
492 /* first handle NaN*/
493 if(uprv_isNaN(x) || uprv_isNaN(y))
494 return uprv_getNaN();
496 /* check for -0 and 0*/
497 if(x == 0.0 && y == 0.0 && u_signBit(x))
502 /* this should work for all flt point w/o NaN and Inf special cases */
503 return (x > y ? x : y);
506 U_CAPI double U_EXPORT2
507 uprv_fmin(double x, double y)
510 /* first handle NaN*/
511 if(uprv_isNaN(x) || uprv_isNaN(y))
512 return uprv_getNaN();
514 /* check for -0 and 0*/
515 if(x == 0.0 && y == 0.0 && u_signBit(y))
520 /* this should work for all flt point w/o NaN and Inf special cases */
521 return (x > y ? y : x);
525 * Truncates the given double.
526 * trunc(3.3) = 3.0, trunc (-3.3) = -3.0
527 * This is different than calling floor() or ceil():
528 * floor(3.3) = 3, floor(-3.3) = -4
529 * ceil(3.3) = 4, ceil(-3.3) = -3
531 U_CAPI double U_EXPORT2
535 /* handle error cases*/
537 return uprv_getNaN();
538 if(uprv_isInfinite(d))
539 return uprv_getInfinity();
541 if(u_signBit(d)) /* Signbit() picks up -0.0; d<0 does not. */
547 return d >= 0 ? floor(d) : ceil(d);
553 * Return the largest positive number that can be represented by an integer
554 * type of arbitrary bit length.
556 U_CAPI double U_EXPORT2
557 uprv_maxMantissa(void)
559 return pow(2.0, DBL_MANT_DIG + 1.0) - 1.0;
562 U_CAPI double U_EXPORT2
568 U_CAPI void * U_EXPORT2
569 uprv_maximumPtr(void * base)
573 * With the provided function we should never be out of range of a given segment
574 * (a traditional/typical segment that is). Our segments have 5 bytes for the
575 * id and 3 bytes for the offset. The key is that the casting takes care of
576 * only retrieving the offset portion minus x1000. Hence, the smallest offset
577 * seen in a program is x001000 and when casted to an int would be 0.
578 * That's why we can only add 0xffefff. Otherwise, we would exceed the segment.
580 * Currently, 16MB is the current addressing limitation on i5/OS if the activation is
581 * non-TERASPACE. If it is TERASPACE it is 2GB - 4k(header information).
582 * This function determines the activation based on the pointer that is passed in and
583 * calculates the appropriate maximum available size for
584 * each pointer type (TERASPACE and non-TERASPACE)
586 * Unlike other operating systems, the pointer model isn't determined at
587 * compile time on i5/OS.
589 if ((base != NULL) && (_TESTPTR(base, _C_TERASPACE_CHECK))) {
590 /* if it is a TERASPACE pointer the max is 2GB - 4k */
591 return ((void *)(((char *)base)-((uint32_t)(base))+((uint32_t)0x7fffefff)));
593 /* otherwise 16MB since NULL ptr is not checkable or the ptr is not TERASPACE */
594 return ((void *)(((char *)base)-((uint32_t)(base))+((uint32_t)0xffefff)));
597 return U_MAX_PTR(base);
601 /*---------------------------------------------------------------------------
602 Platform-specific Implementations
603 Try these, and if they don't work on your platform, then special case your
604 platform with new implementations.
605 ---------------------------------------------------------------------------*/
607 /* Generic time zone layer -------------------------------------------------- */
609 /* Time zone utilities */
610 U_CAPI void U_EXPORT2
616 /* no initialization*/
620 U_CAPI int32_t U_EXPORT2
634 uprv_memcpy( &tmrec, localtime(&t), sizeof(tmrec) );
636 dst_checked = (tmrec.tm_isdst != 0); /* daylight savings time is checked*/
638 t1 = mktime(&tmrec); /* local time in seconds*/
639 uprv_memcpy( &tmrec, gmtime(&t), sizeof(tmrec) );
640 t2 = mktime(&tmrec); /* GMT (or UTC) in seconds*/
643 /* On iOS the calculated tdiff is correct so and doesn't need this dst
645 /* imitate NT behaviour, which returns same timezone offset to GMT for
654 /* Note that U_TZNAME does *not* have to be tzname, but if it is,
655 some platforms need to have it declared here. */
657 #if defined(U_TZNAME) && (defined(U_IRIX) || defined(U_DARWIN) || defined(U_CYGWIN))
658 /* RS6000 and others reject char **tzname. */
659 extern U_IMPORT char *U_TZNAME[];
662 #if !UCONFIG_NO_FILE_IO && ((defined(U_DARWIN) && !defined(U_IOS)) || defined(U_LINUX) || defined(U_BSD))
663 /* These platforms are likely to use Olson timezone IDs. */
664 #define CHECK_LOCALTIME_LINK 1
665 #if defined(U_DARWIN)
667 #define TZZONEINFO (TZDIR "/")
669 #define TZDEFAULT "/etc/localtime"
670 #define TZZONEINFO "/usr/share/zoneinfo/"
673 #define TZFILE_SKIP "posixrules" /* tz file to skip when searching. */
674 /* Some Linux distributions have 'localtime' in /usr/share/zoneinfo
675 symlinked to /etc/localtime, which makes searchForTZFile return
676 'localtime' when it's the first match. */
677 #define TZFILE_SKIP2 "localtime"
678 #define SEARCH_TZFILE
679 #include <dirent.h> /* Needed to search through system timezone files */
681 static char gTimeZoneBuffer[PATH_MAX];
682 static char *gTimeZoneBufferPtr = NULL;
686 #define isNonDigit(ch) (ch < '0' || '9' < ch)
687 static UBool isValidOlsonID(const char *id) {
690 /* Determine if this is something like Iceland (Olson ID)
691 or AST4ADT (non-Olson ID) */
692 while (id[idx] && isNonDigit(id[idx]) && id[idx] != ',') {
696 /* If we went through the whole string, then it might be okay.
697 The timezone is sometimes set to "CST-7CDT", "CST6CDT5,J129,J131/19:30",
698 "GRNLNDST3GRNLNDDT" or similar, so we cannot use it.
699 The rest of the time it could be an Olson ID. George */
700 return (UBool)(id[idx] == 0
701 || uprv_strcmp(id, "PST8PDT") == 0
702 || uprv_strcmp(id, "MST7MDT") == 0
703 || uprv_strcmp(id, "CST6CDT") == 0
704 || uprv_strcmp(id, "EST5EDT") == 0);
707 /* On some Unix-like OS, 'posix' subdirectory in
708 /usr/share/zoneinfo replicates the top-level contents. 'right'
709 subdirectory has the same set of files, but individual files
710 are different from those in the top-level directory or 'posix'
711 because 'right' has files for TAI (Int'l Atomic Time) while 'posix'
713 When the first match for /etc/localtime is in either of them
714 (usually in posix because 'right' has different file contents),
715 or TZ environment variable points to one of them, createTimeZone
716 fails because, say, 'posix/America/New_York' is not an Olson
717 timezone id ('America/New_York' is). So, we have to skip
718 'posix/' and 'right/' at the beginning. */
719 static void skipZoneIDPrefix(const char** id) {
720 if (uprv_strncmp(*id, "posix/", 6) == 0
721 || uprv_strncmp(*id, "right/", 6) == 0)
728 #if defined(U_TZNAME) && !defined(U_WINDOWS)
730 #define CONVERT_HOURS_TO_SECONDS(offset) (int32_t)(offset*3600)
731 typedef struct OffsetZoneMapping {
732 int32_t offsetSeconds;
733 int32_t daylightType; /* 1=daylight in June, 2=daylight in December*/
740 This list tries to disambiguate a set of abbreviated timezone IDs and offsets
741 and maps it to an Olson ID.
742 Before adding anything to this list, take a look at
743 icu/source/tools/tzcode/tz.alias
744 Sometimes no daylight savings (0) is important to define due to aliases.
745 This list can be tested with icu/source/test/compat/tzone.pl
746 More values could be added to daylightType to increase precision.
748 static const struct OffsetZoneMapping OFFSET_ZONE_MAPPINGS[] = {
749 {-45900, 2, "CHAST", "CHADT", "Pacific/Chatham"},
750 {-43200, 1, "PETT", "PETST", "Asia/Kamchatka"},
751 {-43200, 2, "NZST", "NZDT", "Pacific/Auckland"},
752 {-43200, 1, "ANAT", "ANAST", "Asia/Anadyr"},
753 {-39600, 1, "MAGT", "MAGST", "Asia/Magadan"},
754 {-37800, 2, "LHST", "LHST", "Australia/Lord_Howe"},
755 {-36000, 2, "EST", "EST", "Australia/Sydney"},
756 {-36000, 1, "SAKT", "SAKST", "Asia/Sakhalin"},
757 {-36000, 1, "VLAT", "VLAST", "Asia/Vladivostok"},
758 {-34200, 2, "CST", "CST", "Australia/South"},
759 {-32400, 1, "YAKT", "YAKST", "Asia/Yakutsk"},
760 {-32400, 1, "CHOT", "CHOST", "Asia/Choibalsan"},
761 {-31500, 2, "CWST", "CWST", "Australia/Eucla"},
762 {-28800, 1, "IRKT", "IRKST", "Asia/Irkutsk"},
763 {-28800, 1, "ULAT", "ULAST", "Asia/Ulaanbaatar"},
764 {-28800, 2, "WST", "WST", "Australia/West"},
765 {-25200, 1, "HOVT", "HOVST", "Asia/Hovd"},
766 {-25200, 1, "KRAT", "KRAST", "Asia/Krasnoyarsk"},
767 {-21600, 1, "NOVT", "NOVST", "Asia/Novosibirsk"},
768 {-21600, 1, "OMST", "OMSST", "Asia/Omsk"},
769 {-18000, 1, "YEKT", "YEKST", "Asia/Yekaterinburg"},
770 {-14400, 1, "SAMT", "SAMST", "Europe/Samara"},
771 {-14400, 1, "AMT", "AMST", "Asia/Yerevan"},
772 {-14400, 1, "AZT", "AZST", "Asia/Baku"},
773 {-10800, 1, "AST", "ADT", "Asia/Baghdad"},
774 {-10800, 1, "MSK", "MSD", "Europe/Moscow"},
775 {-10800, 1, "VOLT", "VOLST", "Europe/Volgograd"},
776 {-7200, 0, "EET", "CEST", "Africa/Tripoli"},
777 {-7200, 1, "EET", "EEST", "Europe/Athens"}, /* Conflicts with Africa/Cairo */
778 {-7200, 1, "IST", "IDT", "Asia/Jerusalem"},
779 {-3600, 0, "CET", "WEST", "Africa/Algiers"},
780 {-3600, 2, "WAT", "WAST", "Africa/Windhoek"},
781 {0, 1, "GMT", "IST", "Europe/Dublin"},
782 {0, 1, "GMT", "BST", "Europe/London"},
783 {0, 0, "WET", "WEST", "Africa/Casablanca"},
784 {0, 0, "WET", "WET", "Africa/El_Aaiun"},
785 {3600, 1, "AZOT", "AZOST", "Atlantic/Azores"},
786 {3600, 1, "EGT", "EGST", "America/Scoresbysund"},
787 {10800, 1, "PMST", "PMDT", "America/Miquelon"},
788 {10800, 2, "UYT", "UYST", "America/Montevideo"},
789 {10800, 1, "WGT", "WGST", "America/Godthab"},
790 {10800, 2, "BRT", "BRST", "Brazil/East"},
791 {12600, 1, "NST", "NDT", "America/St_Johns"},
792 {14400, 1, "AST", "ADT", "Canada/Atlantic"},
793 {14400, 2, "AMT", "AMST", "America/Cuiaba"},
794 {14400, 2, "CLT", "CLST", "Chile/Continental"},
795 {14400, 2, "FKT", "FKST", "Atlantic/Stanley"},
796 {14400, 2, "PYT", "PYST", "America/Asuncion"},
797 {18000, 1, "CST", "CDT", "America/Havana"},
798 {18000, 1, "EST", "EDT", "US/Eastern"}, /* Conflicts with America/Grand_Turk */
799 {21600, 2, "EAST", "EASST", "Chile/EasterIsland"},
800 {21600, 0, "CST", "MDT", "Canada/Saskatchewan"},
801 {21600, 0, "CST", "CDT", "America/Guatemala"},
802 {21600, 1, "CST", "CDT", "US/Central"}, /* Conflicts with Mexico/General */
803 {25200, 1, "MST", "MDT", "US/Mountain"}, /* Conflicts with Mexico/BajaSur */
804 {28800, 0, "PST", "PST", "Pacific/Pitcairn"},
805 {28800, 1, "PST", "PDT", "US/Pacific"}, /* Conflicts with Mexico/BajaNorte */
806 {32400, 1, "AKST", "AKDT", "US/Alaska"},
807 {36000, 1, "HAST", "HADT", "US/Aleutian"}
810 /*#define DEBUG_TZNAME*/
812 static const char* remapShortTimeZone(const char *stdID, const char *dstID, int32_t daylightType, int32_t offset)
816 fprintf(stderr, "TZ=%s std=%s dst=%s daylight=%d offset=%d\n", getenv("TZ"), stdID, dstID, daylightType, offset);
818 for (idx = 0; idx < (int32_t)sizeof(OFFSET_ZONE_MAPPINGS)/sizeof(OFFSET_ZONE_MAPPINGS[0]); idx++)
820 if (offset == OFFSET_ZONE_MAPPINGS[idx].offsetSeconds
821 && daylightType == OFFSET_ZONE_MAPPINGS[idx].daylightType
822 && strcmp(OFFSET_ZONE_MAPPINGS[idx].stdID, stdID) == 0
823 && strcmp(OFFSET_ZONE_MAPPINGS[idx].dstID, dstID) == 0)
825 return OFFSET_ZONE_MAPPINGS[idx].olsonID;
833 #define MAX_PATH_SIZE PATH_MAX /* Set the limit for the size of the path. */
834 #define MAX_READ_SIZE 512
836 typedef struct DefaultTZInfo {
837 char* defaultTZBuffer;
838 int64_t defaultTZFileSize;
839 FILE* defaultTZFilePtr;
840 UBool defaultTZstatus;
841 int32_t defaultTZPosition;
845 * This method compares the two files given to see if they are a match.
846 * It is currently use to compare two TZ files.
848 static UBool compareBinaryFiles(const char* defaultTZFileName, const char* TZFileName, DefaultTZInfo* tzInfo) {
851 int64_t sizeFileLeft;
852 int32_t sizeFileRead;
853 int32_t sizeFileToRead;
854 char bufferFile[MAX_READ_SIZE];
857 if (tzInfo->defaultTZFilePtr == NULL) {
858 tzInfo->defaultTZFilePtr = fopen(defaultTZFileName, "r");
860 file = fopen(TZFileName, "r");
862 tzInfo->defaultTZPosition = 0; /* reset position to begin search */
864 if (file != NULL && tzInfo->defaultTZFilePtr != NULL) {
865 /* First check that the file size are equal. */
866 if (tzInfo->defaultTZFileSize == 0) {
867 fseek(tzInfo->defaultTZFilePtr, 0, SEEK_END);
868 tzInfo->defaultTZFileSize = ftell(tzInfo->defaultTZFilePtr);
870 fseek(file, 0, SEEK_END);
871 sizeFile = ftell(file);
872 sizeFileLeft = sizeFile;
874 if (sizeFile != tzInfo->defaultTZFileSize) {
877 /* Store the data from the files in seperate buffers and
878 * compare each byte to determine equality.
880 if (tzInfo->defaultTZBuffer == NULL) {
881 rewind(tzInfo->defaultTZFilePtr);
882 tzInfo->defaultTZBuffer = (char*)uprv_malloc(sizeof(char) * tzInfo->defaultTZFileSize);
883 fread(tzInfo->defaultTZBuffer, 1, tzInfo->defaultTZFileSize, tzInfo->defaultTZFilePtr);
886 while(sizeFileLeft > 0) {
887 uprv_memset(bufferFile, 0, MAX_READ_SIZE);
888 sizeFileToRead = sizeFileLeft < MAX_READ_SIZE ? sizeFileLeft : MAX_READ_SIZE;
890 sizeFileRead = fread(bufferFile, 1, sizeFileToRead, file);
891 if (memcmp(tzInfo->defaultTZBuffer + tzInfo->defaultTZPosition, bufferFile, sizeFileRead) != 0) {
895 sizeFileLeft -= sizeFileRead;
896 tzInfo->defaultTZPosition += sizeFileRead;
910 * This method recursively traverses the directory given for a matching TZ file and returns the first match.
912 /* dirent also lists two entries: "." and ".." that we can safely ignore. */
915 static char SEARCH_TZFILE_RESULT[MAX_PATH_SIZE] = "";
916 static char* searchForTZFile(const char* path, DefaultTZInfo* tzInfo) {
917 char curpath[MAX_PATH_SIZE];
918 DIR* dirp = opendir(path);
920 struct dirent* dirEntry = NULL;
927 /* Save the current path */
928 uprv_memset(curpath, 0, MAX_PATH_SIZE);
929 uprv_strcpy(curpath, path);
931 /* Check each entry in the directory. */
932 while((dirEntry = readdir(dirp)) != NULL) {
933 const char* dirName = dirEntry->d_name;
934 if (uprv_strcmp(dirName, SKIP1) != 0 && uprv_strcmp(dirName, SKIP2) != 0) {
935 /* Create a newpath with the new entry to test each entry in the directory. */
936 char newpath[MAX_PATH_SIZE];
937 uprv_strcpy(newpath, curpath);
938 uprv_strcat(newpath, dirName);
940 if ((subDirp = opendir(newpath)) != NULL) {
941 /* If this new path is a directory, make a recursive call with the newpath. */
943 uprv_strcat(newpath, "/");
944 result = searchForTZFile(newpath, tzInfo);
946 Have to get out here. Otherwise, we'd keep looking
947 and return the first match in the top-level directory
948 if there's a match in the top-level. If not, this function
949 would return NULL and set gTimeZoneBufferPtr to NULL in initDefault().
950 It worked without this in most cases because we have a fallback of calling
951 localtime_r to figure out the default timezone.
955 } else if (uprv_strcmp(TZFILE_SKIP, dirName) != 0 && uprv_strcmp(TZFILE_SKIP2, dirName) != 0) {
956 if(compareBinaryFiles(TZDEFAULT, newpath, tzInfo)) {
957 const char* zoneid = newpath + (sizeof(TZZONEINFO)) - 1;
958 skipZoneIDPrefix(&zoneid);
959 uprv_strcpy(SEARCH_TZFILE_RESULT, zoneid);
960 result = SEARCH_TZFILE_RESULT;
961 /* Get out after the first one found. */
971 U_CAPI const char* U_EXPORT2
974 const char *tzid = NULL;
976 tzid = uprv_detectWindowsTimeZone();
983 /*#if defined(U_DARWIN)
986 tzid = getenv("TZFILE");
992 /* This code can be temporarily disabled to test tzname resolution later on. */
995 if (tzid != NULL && isValidOlsonID(tzid))
997 /* This might be a good Olson ID. */
998 skipZoneIDPrefix(&tzid);
1001 /* else U_TZNAME will give a better result. */
1004 #if defined(CHECK_LOCALTIME_LINK)
1005 /* Caller must handle threading issues */
1006 if (gTimeZoneBufferPtr == NULL) {
1008 This is a trick to look at the name of the link to get the Olson ID
1009 because the tzfile contents is underspecified.
1010 This isn't guaranteed to work because it may not be a symlink.
1012 int32_t ret = (int32_t)readlink(TZDEFAULT, gTimeZoneBuffer, sizeof(gTimeZoneBuffer));
1014 int32_t tzZoneInfoLen = uprv_strlen(TZZONEINFO);
1015 gTimeZoneBuffer[ret] = 0;
1016 if (uprv_strncmp(gTimeZoneBuffer, TZZONEINFO, tzZoneInfoLen) == 0
1017 && isValidOlsonID(gTimeZoneBuffer + tzZoneInfoLen))
1019 return (gTimeZoneBufferPtr = gTimeZoneBuffer + tzZoneInfoLen);
1022 #if defined(SEARCH_TZFILE)
1023 DefaultTZInfo* tzInfo = (DefaultTZInfo*)uprv_malloc(sizeof(DefaultTZInfo));
1024 if (tzInfo != NULL) {
1025 tzInfo->defaultTZBuffer = NULL;
1026 tzInfo->defaultTZFileSize = 0;
1027 tzInfo->defaultTZFilePtr = NULL;
1028 tzInfo->defaultTZstatus = FALSE;
1029 tzInfo->defaultTZPosition = 0;
1031 gTimeZoneBufferPtr = searchForTZFile(TZZONEINFO, tzInfo);
1033 /* Free previously allocated memory */
1034 if (tzInfo->defaultTZBuffer != NULL) {
1035 uprv_free(tzInfo->defaultTZBuffer);
1037 if (tzInfo->defaultTZFilePtr != NULL) {
1038 fclose(tzInfo->defaultTZFilePtr);
1043 if (gTimeZoneBufferPtr != NULL && isValidOlsonID(gTimeZoneBufferPtr)) {
1044 return gTimeZoneBufferPtr;
1050 return gTimeZoneBufferPtr;
1057 /* The return value is free'd in timezone.cpp on Windows because
1058 * the other code path returns a pointer to a heap location. */
1059 return uprv_strdup(U_TZNAME[n]);
1062 U_TZNAME is usually a non-unique abbreviation, which isn't normally usable.
1063 So we remap the abbreviation to an olson ID.
1065 Since Windows exposes a little more timezone information,
1066 we normally don't use this code on Windows because
1067 uprv_detectWindowsTimeZone should have already given the correct answer.
1070 struct tm juneSol, decemberSol;
1072 static const time_t juneSolstice=1182478260; /*2007-06-21 18:11 UT*/
1073 static const time_t decemberSolstice=1198332540; /*2007-12-22 06:09 UT*/
1075 /* This probing will tell us when daylight savings occurs. */
1076 localtime_r(&juneSolstice, &juneSol);
1077 localtime_r(&decemberSolstice, &decemberSol);
1078 daylightType = ((decemberSol.tm_isdst > 0) << 1) | (juneSol.tm_isdst > 0);
1079 tzid = remapShortTimeZone(U_TZNAME[0], U_TZNAME[1], daylightType, uprv_timezone());
1091 /* Get and set the ICU data directory --------------------------------------- */
1093 static char *gDataDirectory = NULL;
1095 static char *gCorrectedPOSIXLocale = NULL; /* Heap allocated */
1098 static UBool U_CALLCONV putil_cleanup(void)
1100 if (gDataDirectory && *gDataDirectory) {
1101 uprv_free(gDataDirectory);
1103 gDataDirectory = NULL;
1105 if (gCorrectedPOSIXLocale) {
1106 uprv_free(gCorrectedPOSIXLocale);
1107 gCorrectedPOSIXLocale = NULL;
1114 * Set the data directory.
1115 * Make a copy of the passed string, and set the global data dir to point to it.
1116 * TODO: see bug #2849, regarding thread safety.
1118 U_CAPI void U_EXPORT2
1119 u_setDataDirectory(const char *directory) {
1123 if(directory==NULL || *directory==0) {
1124 /* A small optimization to prevent the malloc and copy when the
1125 shared library is used, and this is a way to make sure that NULL
1128 newDataDir = (char *)"";
1131 length=(int32_t)uprv_strlen(directory);
1132 newDataDir = (char *)uprv_malloc(length + 2);
1133 /* Exit out if newDataDir could not be created. */
1134 if (newDataDir == NULL) {
1137 uprv_strcpy(newDataDir, directory);
1139 #if (U_FILE_SEP_CHAR != U_FILE_ALT_SEP_CHAR)
1142 while(p = uprv_strchr(newDataDir, U_FILE_ALT_SEP_CHAR)) {
1143 *p = U_FILE_SEP_CHAR;
1150 if (gDataDirectory && *gDataDirectory) {
1151 uprv_free(gDataDirectory);
1153 gDataDirectory = newDataDir;
1154 ucln_common_registerCleanup(UCLN_COMMON_PUTIL, putil_cleanup);
1158 U_CAPI UBool U_EXPORT2
1159 uprv_pathIsAbsolute(const char *path)
1161 if(!path || !*path) {
1165 if(*path == U_FILE_SEP_CHAR) {
1169 #if (U_FILE_SEP_CHAR != U_FILE_ALT_SEP_CHAR)
1170 if(*path == U_FILE_ALT_SEP_CHAR) {
1175 #if defined(U_WINDOWS)
1176 if( (((path[0] >= 'A') && (path[0] <= 'Z')) ||
1177 ((path[0] >= 'a') && (path[0] <= 'z'))) &&
1186 /* Temporary backup setting of ICU_DATA_DIR_PREFIX_ENV_VAR
1187 until some client wrapper makefiles are updated */
1188 #if defined(U_DARWIN) && TARGET_IPHONE_SIMULATOR
1189 # if !defined(ICU_DATA_DIR_PREFIX_ENV_VAR)
1190 # define ICU_DATA_DIR_PREFIX_ENV_VAR "IPHONE_SIMULATOR_ROOT"
1194 U_CAPI const char * U_EXPORT2
1195 u_getDataDirectory(void) {
1196 const char *path = NULL;
1197 #if defined(ICU_DATA_DIR_PREFIX_ENV_VAR)
1198 char datadir_path_buffer[PATH_MAX];
1201 /* if we have the directory, then return it immediately */
1202 UMTX_CHECK(NULL, gDataDirectory, path);
1209 When ICU_NO_USER_DATA_OVERRIDE is defined, users aren't allowed to
1210 override ICU's data with the ICU_DATA environment variable. This prevents
1211 problems where multiple custom copies of ICU's specific version of data
1212 are installed on a system. Either the application must define the data
1213 directory with u_setDataDirectory, define ICU_DATA_DIR when compiling
1214 ICU, set the data with udata_setCommonData or trust that all of the
1215 required data is contained in ICU's data library that contains
1216 the entry point defined by U_ICUDATA_ENTRY_POINT.
1218 There may also be some platforms where environment variables
1221 # if !defined(ICU_NO_USER_DATA_OVERRIDE) && !UCONFIG_NO_FILE_IO
1222 /* First try to get the environment variable */
1223 path=getenv("ICU_DATA");
1226 /* ICU_DATA_DIR may be set as a compile option.
1227 * U_ICU_DATA_DEFAULT_DIR is provided and is set by ICU at compile time
1228 * and is used only when data is built in archive mode eliminating the need
1229 * for ICU_DATA_DIR to be set. U_ICU_DATA_DEFAULT_DIR is set to the installation
1230 * directory of the data dat file. Users should use ICU_DATA_DIR if they want to
1231 * set their own path.
1233 #if defined(ICU_DATA_DIR) || defined(U_ICU_DATA_DEFAULT_DIR)
1234 if(path==NULL || *path==0) {
1235 # if defined(ICU_DATA_DIR_PREFIX_ENV_VAR)
1236 const char *prefix = getenv(ICU_DATA_DIR_PREFIX_ENV_VAR);
1238 # ifdef ICU_DATA_DIR
1241 path=U_ICU_DATA_DEFAULT_DIR;
1243 # if defined(ICU_DATA_DIR_PREFIX_ENV_VAR)
1244 if (prefix != NULL) {
1245 snprintf(datadir_path_buffer, PATH_MAX, "%s%s", prefix, path);
1246 path=datadir_path_buffer;
1253 /* It looks really bad, set it to something. */
1257 u_setDataDirectory(path);
1258 return gDataDirectory;
1265 /* Macintosh-specific locale information ------------------------------------ */
1272 int32_t date_region;
1273 const char* posixID;
1276 /* Todo: This will be updated with a newer version from www.unicode.org web
1277 page when it's available.*/
1278 #define MAC_LC_MAGIC_NUMBER -5
1279 #define MAC_LC_INIT_NUMBER -9
1281 static const mac_lc_rec mac_lc_recs[] = {
1282 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 0, "en_US",
1284 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 1, "fr_FR",
1286 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 2, "en_GB",
1288 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 3, "de_DE",
1290 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 4, "it_IT",
1292 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 5, "nl_NL",
1294 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 6, "fr_BE",
1295 /* French for Belgium or Lxembourg*/
1296 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 7, "sv_SE",
1298 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 9, "da_DK",
1300 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 10, "pt_PT",
1302 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 11, "fr_CA",
1304 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 13, "is_IS",
1306 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 14, "ja_JP",
1308 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 15, "en_AU",
1310 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 16, "ar_AE",
1311 /* the Arabic world (?)*/
1312 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 17, "fi_FI",
1314 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 18, "fr_CH",
1315 /* French for Switzerland*/
1316 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 19, "de_CH",
1317 /* German for Switzerland*/
1318 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 20, "el_GR",
1320 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 21, "is_IS",
1322 /*MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 22, "",*/
1324 /*MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 23, "",*/
1326 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 24, "tr_TR",
1328 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 25, "sh_YU",
1329 /* Croatian system for Yugoslavia*/
1330 /*MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 33, "",*/
1331 /* Hindi system for India*/
1332 /*MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 34, "",*/
1334 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 41, "lt_LT",
1336 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 42, "pl_PL",
1338 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 43, "hu_HU",
1340 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 44, "et_EE",
1342 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 45, "lv_LV",
1344 /*MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 46, "",*/
1345 /* Lapland [Ask Rich for the data. HS]*/
1346 /*MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 47, "",*/
1348 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 48, "fa_IR",
1350 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 49, "ru_RU",
1352 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 50, "en_IE",
1354 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 51, "ko_KR",
1356 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 52, "zh_CN",
1357 /* People's Republic of China*/
1358 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 53, "zh_TW",
1360 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, 54, "th_TH",
1363 /* fallback is en_US*/
1364 MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER, MAC_LC_MAGIC_NUMBER,
1365 MAC_LC_MAGIC_NUMBER, "en_US"
1371 /* A helper function used by uprv_getPOSIXIDForDefaultLocale and
1372 * uprv_getPOSIXIDForDefaultCodepage. Returns the posix locale id for
1373 * LC_CTYPE and LC_MESSAGES. It doesn't support other locale categories.
1375 static const char *uprv_getPOSIXIDForCategory(int category)
1377 const char* posixID = NULL;
1378 if (category == LC_MESSAGES || category == LC_CTYPE) {
1380 * On Solaris two different calls to setlocale can result in
1381 * different values. Only get this value once.
1383 * We must check this first because an application can set this.
1385 * LC_ALL can't be used because it's platform dependent. The LANG
1386 * environment variable seems to affect LC_CTYPE variable by default.
1387 * Here is what setlocale(LC_ALL, NULL) can return.
1388 * HPUX can return 'C C C C C C C'
1389 * Solaris can return /en_US/C/C/C/C/C on the second try.
1390 * Linux can return LC_CTYPE=C;LC_NUMERIC=C;...
1392 * The default codepage detection also needs to use LC_CTYPE.
1394 * Do not call setlocale(LC_*, "")! Using an empty string instead
1395 * of NULL, will modify the libc behavior.
1397 posixID = setlocale(category, NULL);
1399 || (uprv_strcmp("C", posixID) == 0)
1400 || (uprv_strcmp("POSIX", posixID) == 0))
1402 /* Maybe we got some garbage. Try something more reasonable */
1403 posixID = getenv("LC_ALL");
1405 posixID = getenv(category == LC_MESSAGES ? "LC_MESSAGES" : "LC_CTYPE");
1407 posixID = getenv("LANG");
1413 || (uprv_strcmp("C", posixID) == 0)
1414 || (uprv_strcmp("POSIX", posixID) == 0))
1416 /* Nothing worked. Give it a nice POSIX default value. */
1417 posixID = "en_US_POSIX";
1422 /* Return just the POSIX id for the default locale, whatever happens to be in
1423 * it. It gets the value from LC_MESSAGES and indirectly from LC_ALL and LANG.
1425 static const char *uprv_getPOSIXIDForDefaultLocale(void)
1427 static const char* posixID = NULL;
1429 posixID = uprv_getPOSIXIDForCategory(LC_MESSAGES);
1434 /* Return just the POSIX id for the default codepage, whatever happens to be in
1435 * it. It gets the value from LC_CTYPE and indirectly from LC_ALL and LANG.
1437 static const char *uprv_getPOSIXIDForDefaultCodepage(void)
1439 static const char* posixID = NULL;
1441 posixID = uprv_getPOSIXIDForCategory(LC_CTYPE);
1447 /* NOTE: The caller should handle thread safety */
1448 U_CAPI const char* U_EXPORT2
1449 uprv_getDefaultLocaleID()
1453 Note that: (a '!' means the ID is improper somehow)
1454 LC_ALL ----> default_loc codepage
1455 --------------------------------------------------------
1460 ab_CD.EF@GH ab_CD_GH EF
1462 Some 'improper' ways to do the same as above:
1463 ! ab_CD@GH.EF ab_CD_GH EF
1464 ! ab_CD.EF@GH.IJ ab_CD_GH EF
1465 ! ab_CD@ZZ.EF@GH.IJ ab_CD_GH EF
1470 The variant cannot have dots in it.
1471 The 'rightmost' variant (@xxx) wins.
1472 The leftmost codepage (.xxx) wins.
1474 char *correctedPOSIXLocale = 0;
1475 const char* posixID = uprv_getPOSIXIDForDefaultLocale();
1480 /* Format: (no spaces)
1481 ll [ _CC ] [ . MM ] [ @ VV]
1483 l = lang, C = ctry, M = charmap, V = variant
1486 if (gCorrectedPOSIXLocale != NULL) {
1487 return gCorrectedPOSIXLocale;
1490 if ((p = uprv_strchr(posixID, '.')) != NULL) {
1491 /* assume new locale can't be larger than old one? */
1492 correctedPOSIXLocale = uprv_malloc(uprv_strlen(posixID)+1);
1493 /* Exit on memory allocation error. */
1494 if (correctedPOSIXLocale == NULL) {
1497 uprv_strncpy(correctedPOSIXLocale, posixID, p-posixID);
1498 correctedPOSIXLocale[p-posixID] = 0;
1500 /* do not copy after the @ */
1501 if ((p = uprv_strchr(correctedPOSIXLocale, '@')) != NULL) {
1502 correctedPOSIXLocale[p-correctedPOSIXLocale] = 0;
1506 /* Note that we scan the *uncorrected* ID. */
1507 if ((p = uprv_strrchr(posixID, '@')) != NULL) {
1508 if (correctedPOSIXLocale == NULL) {
1509 correctedPOSIXLocale = uprv_malloc(uprv_strlen(posixID)+1);
1510 /* Exit on memory allocation error. */
1511 if (correctedPOSIXLocale == NULL) {
1514 uprv_strncpy(correctedPOSIXLocale, posixID, p-posixID);
1515 correctedPOSIXLocale[p-posixID] = 0;
1519 /* Take care of any special cases here.. */
1520 if (!uprv_strcmp(p, "nynorsk")) {
1522 /* Don't worry about no__NY. In practice, it won't appear. */
1525 if (uprv_strchr(correctedPOSIXLocale,'_') == NULL) {
1526 uprv_strcat(correctedPOSIXLocale, "__"); /* aa@b -> aa__b */
1529 uprv_strcat(correctedPOSIXLocale, "_"); /* aa_CC@b -> aa_CC_b */
1532 if ((q = uprv_strchr(p, '.')) != NULL) {
1533 /* How big will the resulting string be? */
1534 len = (int32_t)(uprv_strlen(correctedPOSIXLocale) + (q-p));
1535 uprv_strncat(correctedPOSIXLocale, p, q-p);
1536 correctedPOSIXLocale[len] = 0;
1539 /* Anything following the @ sign */
1540 uprv_strcat(correctedPOSIXLocale, p);
1543 /* Should there be a map from 'no@nynorsk' -> no_NO_NY here?
1544 * How about 'russian' -> 'ru'?
1545 * Many of the other locales using ISO codes will be handled by the
1546 * canonicalization functions in uloc_getDefault.
1550 /* Was a correction made? */
1551 if (correctedPOSIXLocale != NULL) {
1552 posixID = correctedPOSIXLocale;
1555 /* copy it, just in case the original pointer goes away. See j2395 */
1556 correctedPOSIXLocale = (char *)uprv_malloc(uprv_strlen(posixID) + 1);
1557 /* Exit on memory allocation error. */
1558 if (correctedPOSIXLocale == NULL) {
1561 posixID = uprv_strcpy(correctedPOSIXLocale, posixID);
1564 if (gCorrectedPOSIXLocale == NULL) {
1565 gCorrectedPOSIXLocale = correctedPOSIXLocale;
1566 ucln_common_registerCleanup(UCLN_COMMON_PUTIL, putil_cleanup);
1567 correctedPOSIXLocale = NULL;
1570 if (correctedPOSIXLocale != NULL) { /* Was already set - clean up. */
1571 uprv_free(correctedPOSIXLocale);
1576 #elif defined(U_WINDOWS)
1577 UErrorCode status = U_ZERO_ERROR;
1578 LCID id = GetThreadLocale();
1579 const char* locID = uprv_convertToPosix(id, &status);
1581 if (U_FAILURE(status)) {
1586 #elif defined(XP_MAC)
1587 int32_t script = MAC_LC_INIT_NUMBER;
1588 /* = IntlScript(); or GetScriptManagerVariable(smSysScript);*/
1589 int32_t region = MAC_LC_INIT_NUMBER;
1590 /* = GetScriptManagerVariable(smRegionCode);*/
1591 int32_t lang = MAC_LC_INIT_NUMBER;
1592 /* = GetScriptManagerVariable(smScriptLang);*/
1593 int32_t date_region = MAC_LC_INIT_NUMBER;
1594 const char* posixID = 0;
1595 int32_t count = sizeof(mac_lc_recs) / sizeof(mac_lc_rec);
1599 ih = (Intl1Hndl) GetIntlResource(1);
1601 date_region = ((uint16_t)(*ih)->intl1Vers) >> 8;
1603 for (i = 0; i < count; i++) {
1604 if ( ((mac_lc_recs[i].script == MAC_LC_MAGIC_NUMBER)
1605 || (mac_lc_recs[i].script == script))
1606 && ((mac_lc_recs[i].region == MAC_LC_MAGIC_NUMBER)
1607 || (mac_lc_recs[i].region == region))
1608 && ((mac_lc_recs[i].lang == MAC_LC_MAGIC_NUMBER)
1609 || (mac_lc_recs[i].lang == lang))
1610 && ((mac_lc_recs[i].date_region == MAC_LC_MAGIC_NUMBER)
1611 || (mac_lc_recs[i].date_region == date_region))
1614 posixID = mac_lc_recs[i].posixID;
1621 #elif defined(OS400)
1622 /* locales are process scoped and are by definition thread safe */
1623 static char correctedLocale[64];
1624 const char *localeID = getenv("LC_ALL");
1627 if (localeID == NULL)
1628 localeID = getenv("LANG");
1629 if (localeID == NULL)
1630 localeID = setlocale(LC_ALL, NULL);
1631 /* Make sure we have something... */
1632 if (localeID == NULL)
1633 return "en_US_POSIX";
1635 /* Extract the locale name from the path. */
1636 if((p = uprv_strrchr(localeID, '/')) != NULL)
1638 /* Increment p to start of locale name. */
1643 /* Copy to work location. */
1644 uprv_strcpy(correctedLocale, localeID);
1646 /* Strip off the '.locale' extension. */
1647 if((p = uprv_strchr(correctedLocale, '.')) != NULL) {
1651 /* Upper case the locale name. */
1652 T_CString_toUpperCase(correctedLocale);
1654 /* See if we are using the POSIX locale. Any of the
1655 * following are equivalent and use the same QLGPGCMA
1657 * QLGPGCMA2 means UCS2
1658 * QLGPGCMA_4 means UTF-32
1659 * QLGPGCMA_8 means UTF-8
1661 if ((uprv_strcmp("C", correctedLocale) == 0) ||
1662 (uprv_strcmp("POSIX", correctedLocale) == 0) ||
1663 (uprv_strncmp("QLGPGCMA", correctedLocale, 8) == 0))
1665 uprv_strcpy(correctedLocale, "en_US_POSIX");
1671 /* Lower case the lang portion. */
1672 for(p = correctedLocale; *p != 0 && *p != '_'; p++)
1674 *p = uprv_tolower(*p);
1677 /* Adjust for Euro. After '_E' add 'URO'. */
1678 LocaleLen = uprv_strlen(correctedLocale);
1679 if (correctedLocale[LocaleLen - 2] == '_' &&
1680 correctedLocale[LocaleLen - 1] == 'E')
1682 uprv_strcat(correctedLocale, "URO");
1685 /* If using Lotus-based locale then convert to
1686 * equivalent non Lotus.
1688 else if (correctedLocale[LocaleLen - 2] == '_' &&
1689 correctedLocale[LocaleLen - 1] == 'L')
1691 correctedLocale[LocaleLen - 2] = 0;
1694 /* There are separate simplified and traditional
1695 * locales called zh_HK_S and zh_HK_T.
1697 else if (uprv_strncmp(correctedLocale, "zh_HK", 5) == 0)
1699 uprv_strcpy(correctedLocale, "zh_HK");
1702 /* A special zh_CN_GBK locale...
1704 else if (uprv_strcmp(correctedLocale, "zh_CN_GBK") == 0)
1706 uprv_strcpy(correctedLocale, "zh_CN");
1711 return correctedLocale;
1716 #if !U_CHARSET_IS_UTF8
1719 Due to various platform differences, one platform may specify a charset,
1720 when they really mean a different charset. Remap the names so that they are
1721 compatible with ICU. Only conflicting/ambiguous aliases should be resolved
1722 here. Before adding anything to this function, please consider adding unique
1723 names to the ICU alias table in the data directory.
1726 remapPlatformDependentCodepage(const char *locale, const char *name) {
1727 if (locale != NULL && *locale == 0) {
1728 /* Make sure that an empty locale is handled the same way. */
1735 if (uprv_strcmp(name, "IBM-943") == 0) {
1736 /* Use the ASCII compatible ibm-943 */
1739 else if (uprv_strcmp(name, "IBM-1252") == 0) {
1740 /* Use the windows-1252 that contains the Euro */
1743 #elif defined(U_SOLARIS)
1744 if (locale != NULL && uprv_strcmp(name, "EUC") == 0) {
1745 /* Solaris underspecifies the "EUC" name. */
1746 if (uprv_strcmp(locale, "zh_CN") == 0) {
1749 else if (uprv_strcmp(locale, "zh_TW") == 0) {
1752 else if (uprv_strcmp(locale, "ko_KR") == 0) {
1756 else if (uprv_strcmp(name, "eucJP") == 0) {
1758 ibm-954 is the best match.
1759 ibm-33722 is the default for eucJP (similar to Windows).
1763 else if (uprv_strcmp(name, "646") == 0) {
1765 * The default codepage given by Solaris is 646 but the C library routines treat it as if it was
1766 * ISO-8859-1 instead of US-ASCII(646).
1768 name = "ISO-8859-1";
1770 #elif defined(U_DARWIN)
1771 if (locale == NULL && *name == 0) {
1773 No locale was specified, and an empty name was passed in.
1774 This usually indicates that nl_langinfo didn't return valid information.
1775 Mac OS X uses UTF-8 by default (especially the locale data and console).
1779 else if (uprv_strcmp(name, "CP949") == 0) {
1780 /* Remap CP949 to a similar codepage to avoid issues with backslash and won symbol. */
1783 else if (locale != NULL && uprv_strcmp(locale, "en_US_POSIX") != 0 && uprv_strcmp(name, "US-ASCII") == 0) {
1785 * For non C/POSIX locale, default the code page to UTF-8 instead of US-ASCII.
1789 #elif defined(U_BSD)
1790 if (uprv_strcmp(name, "CP949") == 0) {
1791 /* Remap CP949 to a similar codepage to avoid issues with backslash and won symbol. */
1794 #elif defined(U_HPUX)
1795 if (locale != NULL && uprv_strcmp(locale, "zh_HK") == 0 && uprv_strcmp(name, "big5") == 0) {
1796 /* HP decided to extend big5 as hkbig5 even though it's not compatible :-( */
1797 /* zh_TW.big5 is not the same charset as zh_HK.big5! */
1800 else if (uprv_strcmp(name, "eucJP") == 0) {
1802 ibm-1350 is the best match, but unavailable.
1803 ibm-954 is mostly a superset of ibm-1350.
1804 ibm-33722 is the default for eucJP (similar to Windows).
1808 #elif defined(U_LINUX)
1809 if (locale != NULL && uprv_strcmp(name, "euc") == 0) {
1810 /* Linux underspecifies the "EUC" name. */
1811 if (uprv_strcmp(locale, "korean") == 0) {
1814 else if (uprv_strcmp(locale, "japanese") == 0) {
1815 /* See comment below about eucJP */
1819 else if (uprv_strcmp(name, "eucjp") == 0) {
1821 ibm-1350 is the best match, but unavailable.
1822 ibm-954 is mostly a superset of ibm-1350.
1823 ibm-33722 is the default for eucJP (similar to Windows).
1827 else if (locale != NULL && uprv_strcmp(locale, "en_US_POSIX") != 0 &&
1828 (uprv_strcmp(name, "ANSI_X3.4-1968") == 0 || uprv_strcmp(name, "US-ASCII") == 0)) {
1830 * For non C/POSIX locale, default the code page to UTF-8 instead of US-ASCII.
1835 * Linux returns ANSI_X3.4-1968 for C/POSIX, but the call site takes care of
1836 * it by falling back to 'US-ASCII' when NULL is returned from this
1837 * function. So, we don't have to worry about it here.
1840 /* return NULL when "" is passed in */
1848 getCodepageFromPOSIXID(const char *localeName, char * buffer, int32_t buffCapacity)
1850 char localeBuf[100];
1851 const char *name = NULL;
1852 char *variant = NULL;
1854 if (localeName != NULL && (name = (uprv_strchr(localeName, '.'))) != NULL) {
1855 size_t localeCapacity = uprv_min(sizeof(localeBuf), (name-localeName)+1);
1856 uprv_strncpy(localeBuf, localeName, localeCapacity);
1857 localeBuf[localeCapacity-1] = 0; /* ensure NULL termination */
1858 name = uprv_strncpy(buffer, name+1, buffCapacity);
1859 buffer[buffCapacity-1] = 0; /* ensure NULL termination */
1860 if ((variant = (uprv_strchr(name, '@'))) != NULL) {
1863 name = remapPlatformDependentCodepage(localeBuf, name);
1870 int_getDefaultCodepage()
1873 uint32_t ccsid = 37; /* Default to ibm-37 */
1874 static char codepage[64];
1875 Qwc_JOBI0400_t jobinfo;
1876 Qus_EC_t error = { sizeof(Qus_EC_t) }; /* SPI error code */
1878 EPT_CALL(QUSRJOBI)(&jobinfo, sizeof(jobinfo), "JOBI0400",
1881 if (error.Bytes_Available == 0) {
1882 if (jobinfo.Coded_Char_Set_ID != 0xFFFF) {
1883 ccsid = (uint32_t)jobinfo.Coded_Char_Set_ID;
1885 else if (jobinfo.Default_Coded_Char_Set_Id != 0xFFFF) {
1886 ccsid = (uint32_t)jobinfo.Default_Coded_Char_Set_Id;
1888 /* else use the default */
1890 sprintf(codepage,"ibm-%d", ccsid);
1893 #elif defined(OS390)
1894 static char codepage[64];
1896 strncpy(codepage, nl_langinfo(CODESET),63-strlen(UCNV_SWAP_LFNL_OPTION_STRING));
1897 strcat(codepage,UCNV_SWAP_LFNL_OPTION_STRING);
1898 codepage[63] = 0; /* NULL terminate */
1902 #elif defined(XP_MAC)
1903 return "macintosh"; /* TODO: Macintosh Roman. There must be a better way. fixme! */
1905 #elif defined(U_WINDOWS)
1906 static char codepage[64];
1907 sprintf(codepage, "windows-%d", GetACP());
1910 #elif U_POSIX_LOCALE
1911 static char codesetName[100];
1912 const char *localeName = NULL;
1913 const char *name = NULL;
1915 localeName = uprv_getPOSIXIDForDefaultCodepage();
1916 uprv_memset(codesetName, 0, sizeof(codesetName));
1917 #if U_HAVE_NL_LANGINFO_CODESET
1918 /* When available, check nl_langinfo first because it usually gives more
1919 useful names. It depends on LC_CTYPE.
1920 nl_langinfo may use the same buffer as setlocale. */
1922 const char *codeset = nl_langinfo(U_NL_LANGINFO_CODESET);
1923 #if defined(U_DARWIN) || defined(U_LINUX)
1925 * On Linux and MacOSX, ensure that default codepage for non C/POSIX locale is UTF-8
1928 if (uprv_strcmp(localeName, "en_US_POSIX") != 0) {
1929 codeset = remapPlatformDependentCodepage(localeName, codeset);
1933 codeset = remapPlatformDependentCodepage(NULL, codeset);
1936 if (codeset != NULL) {
1937 uprv_strncpy(codesetName, codeset, sizeof(codesetName));
1938 codesetName[sizeof(codesetName)-1] = 0;
1944 /* Use setlocale in a nice way, and then check some environment variables.
1945 Maybe the application used setlocale already.
1947 uprv_memset(codesetName, 0, sizeof(codesetName));
1948 name = getCodepageFromPOSIXID(localeName, codesetName, sizeof(codesetName));
1950 /* if we can find the codeset name from setlocale, return that. */
1954 if (*codesetName == 0)
1956 /* Everything failed. Return US ASCII (ISO 646). */
1957 (void)uprv_strcpy(codesetName, "US-ASCII");
1966 U_CAPI const char* U_EXPORT2
1967 uprv_getDefaultCodepage()
1969 static char const *name = NULL;
1972 name = int_getDefaultCodepage();
1977 #endif /* !U_CHARSET_IS_UTF8 */
1980 /* end of platform-specific implementation -------------- */
1982 /* version handling --------------------------------------------------------- */
1984 U_CAPI void U_EXPORT2
1985 u_versionFromString(UVersionInfo versionArray, const char *versionString) {
1989 if(versionArray==NULL) {
1993 if(versionString!=NULL) {
1995 versionArray[part]=(uint8_t)uprv_strtoul(versionString, &end, 10);
1996 if(end==versionString || ++part==U_MAX_VERSION_LENGTH || *end!=U_VERSION_DELIMITER) {
1999 versionString=end+1;
2003 while(part<U_MAX_VERSION_LENGTH) {
2004 versionArray[part++]=0;
2008 U_CAPI void U_EXPORT2
2009 u_versionFromUString(UVersionInfo versionArray, const UChar *versionString) {
2010 if(versionArray!=NULL && versionString!=NULL) {
2011 char versionChars[U_MAX_VERSION_STRING_LENGTH+1];
2012 int32_t len = u_strlen(versionString);
2013 if(len>U_MAX_VERSION_STRING_LENGTH) {
2014 len = U_MAX_VERSION_STRING_LENGTH;
2016 u_UCharsToChars(versionString, versionChars, len);
2017 versionChars[len]=0;
2018 u_versionFromString(versionArray, versionChars);
2022 U_CAPI void U_EXPORT2
2023 u_versionToString(UVersionInfo versionArray, char *versionString) {
2024 uint16_t count, part;
2027 if(versionString==NULL) {
2031 if(versionArray==NULL) {
2036 /* count how many fields need to be written */
2037 for(count=4; count>0 && versionArray[count-1]==0; --count) {
2044 /* write the first part */
2045 /* write the decimal field value */
2046 field=versionArray[0];
2048 *versionString++=(char)('0'+field/100);
2052 *versionString++=(char)('0'+field/10);
2055 *versionString++=(char)('0'+field);
2057 /* write the following parts */
2058 for(part=1; part<count; ++part) {
2059 /* write a dot first */
2060 *versionString++=U_VERSION_DELIMITER;
2062 /* write the decimal field value */
2063 field=versionArray[part];
2065 *versionString++=(char)('0'+field/100);
2069 *versionString++=(char)('0'+field/10);
2072 *versionString++=(char)('0'+field);
2079 U_CAPI void U_EXPORT2
2080 u_getVersion(UVersionInfo versionArray) {
2081 u_versionFromString(versionArray, U_ICU_VERSION);
2085 * icucfg.h dependent code
2090 #if defined(U_CHECK_DYLOAD)
2092 #if defined(HAVE_DLOPEN)
2103 U_INTERNAL void * U_EXPORT2
2104 uprv_dl_open(const char *libName, UErrorCode *status) {
2106 if(U_FAILURE(*status)) return ret;
2107 ret = dlopen(libName, RTLD_NOW|RTLD_GLOBAL);
2109 #ifndef U_TRACE_DYLOAD
2112 *status = U_MISSING_RESOURCE_ERROR;
2117 U_INTERNAL void U_EXPORT2
2118 uprv_dl_close(void *lib, UErrorCode *status) {
2119 if(U_FAILURE(*status)) return;
2123 U_INTERNAL void* U_EXPORT2
2124 uprv_dl_sym(void *lib, const char* sym, UErrorCode *status) {
2126 if(U_FAILURE(*status)) return ret;
2127 ret = dlsym(lib, sym);
2129 *status = U_MISSING_RESOURCE_ERROR;
2136 /* null (nonexistent) implementation. */
2138 U_INTERNAL void * U_EXPORT2
2139 uprv_dl_open(const char *libName, UErrorCode *status) {
2140 if(U_FAILURE(*status)) return NULL;
2141 *status = U_UNSUPPORTED_ERROR;
2145 U_INTERNAL void U_EXPORT2
2146 uprv_dl_close(void *lib, UErrorCode *status) {
2147 if(U_FAILURE(*status)) return;
2148 *status = U_UNSUPPORTED_ERROR;
2153 U_INTERNAL void* U_EXPORT2
2154 uprv_dl_sym(void *lib, const char* sym, UErrorCode *status) {
2155 if(U_FAILURE(*status)) return NULL;
2156 *status = U_UNSUPPORTED_ERROR;
2164 #elif defined U_WINDOWS
2166 U_INTERNAL void * U_EXPORT2
2167 uprv_dl_open(const char *libName, UErrorCode *status) {
2170 if(U_FAILURE(*status)) return NULL;
2172 lib = LoadLibraryA(libName);
2175 *status = U_MISSING_RESOURCE_ERROR;
2181 U_INTERNAL void U_EXPORT2
2182 uprv_dl_close(void *lib, UErrorCode *status) {
2183 HMODULE handle = (HMODULE)lib;
2184 if(U_FAILURE(*status)) return;
2186 FreeLibrary(handle);
2192 U_INTERNAL void* U_EXPORT2
2193 uprv_dl_sym(void *lib, const char* sym, UErrorCode *status) {
2194 HMODULE handle = (HMODULE)lib;
2197 if(U_FAILURE(*status) || lib==NULL) return NULL;
2199 addr = GetProcAddress(handle, sym);
2202 DWORD lastError = GetLastError();
2203 if(lastError == ERROR_PROC_NOT_FOUND) {
2204 *status = U_MISSING_RESOURCE_ERROR;
2206 *status = U_UNSUPPORTED_ERROR; /* other unknown error. */
2216 /* No dynamic loading set. */
2218 U_INTERNAL void * U_EXPORT2
2219 uprv_dl_open(const char *libName, UErrorCode *status) {
2220 if(U_FAILURE(*status)) return NULL;
2221 *status = U_UNSUPPORTED_ERROR;
2225 U_INTERNAL void U_EXPORT2
2226 uprv_dl_close(void *lib, UErrorCode *status) {
2227 if(U_FAILURE(*status)) return;
2228 *status = U_UNSUPPORTED_ERROR;
2233 U_INTERNAL void* U_EXPORT2
2234 uprv_dl_sym(void *lib, const char* sym, UErrorCode *status) {
2235 if(U_FAILURE(*status)) return NULL;
2236 *status = U_UNSUPPORTED_ERROR;
2243 #endif /* U_ENABLE_DYLOAD */
2246 * Hey, Emacs, please set the following:
2249 * indent-tabs-mode: nil