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