/* include the rest of the ICU headers */ #include"unicode/putil.h" #include"unicode/ustring.h" #include"putilimp.h" #include"uassert.h" #include"umutex.h" #include"cmemory.h" #include"cstring.h" #include"locmap.h" #include"ucln_cmn.h" #include"charstr.h"
/* Include standard headers. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include <locale.h> #include <float.h>
#ifndef U_COMMON_IMPLEMENTATION #error U_COMMON_IMPLEMENTATION not set - must be set for all ICU source files in common/ - see https://unicode-org.github.io/icu/userguide/icu/howtouseicu.html #endif
/* include system headers */ #if U_PLATFORM_USES_ONLY_WIN32_API /* *TODO:U_PLATFORM_USES_ONLY_WIN32_APIincludesMinGW. *ShouldCygwinbeincludedaswell(U_PLATFORM_HAS_WIN32_API) *tousenativeAPIsasmuchaspossible?
*/ #ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN #endif # define VC_EXTRALEAN # define NOUSER # define NOSERVICE # define NOIME # define NOMCX # include <windows.h> # include "unicode/uloc.h" # include "wintz.h" #elif U_PLATFORM == U_PF_OS400 # include <float.h> # include <qusec.h> /* error code structure */ # include <qusrjobi.h> # include <qliept.h> /* EPT_CALL macro - this include must be after all other "QSYSINCs" */ # include <mih/testptr.h> /* For uprv_maximumPtr */ #elif U_PLATFORM == U_PF_OS390 # include "unicode/ucnv.h"/* Needed for UCNV_SWAP_LFNL_OPTION_STRING */ #elif U_PLATFORM_IS_DARWIN_BASED || U_PLATFORM_IS_LINUX_BASED || U_PLATFORM == U_PF_BSD || U_PLATFORM == U_PF_SOLARIS # include <limits.h> # include <unistd.h> # if U_PLATFORM == U_PF_SOLARIS # ifndef _XPG4_2 # define _XPG4_2 # endif # elif U_PLATFORM == U_PF_ANDROID # include <sys/system_properties.h> # include <dlfcn.h> # endif #elif U_PLATFORM == U_PF_QNX # include <sys/neutrino.h> #endif
/* floating point implementations ------------------------------------------- */
/* We return QNAN rather than SNAN*/ #define SIGN 0x80000000U
/* Make it easy to define certain types of constants */ typedefunion {
int64_t i64; /* This must be defined first in order to allow the initialization to work. This is a C89 feature. */ double d64;
} BitPatternConversion; staticconst BitPatternConversion gNan = {static_cast<int64_t>(INT64_C(0x7FF8000000000000))}; staticconst BitPatternConversion gInf = {static_cast<int64_t>(INT64_C(0x7FF0000000000000))};
#ifdefined (U_DEBUG_FAKETIME) /* Override the clock to test things without having to move the system clock. *AssumesPOSIXgettimeofday()willfunction
*/
UDate fakeClock_t0 = 0; /** Time to start the clock from **/
UDate fakeClock_dt = 0; /** Offset (fake time - real time) **/
UBool fakeClock_set = false; /** True if fake clock has spun up **/
static UDate getUTCtime_fake() { static UMutex fakeClockMutex;
umtx_lock(&fakeClockMutex); if(!fakeClock_set) {
UDate real = getUTCtime_real(); constchar *fake_start = getenv("U_FAKETIME_START"); if((fake_start!=nullptr) && (fake_start[0]!=0)) {
sscanf(fake_start,"%lf",&fakeClock_t0);
fakeClock_dt = fakeClock_t0 - real;
fprintf(stderr,"U_DEBUG_FAKETIME was set at compile time, so the ICU clock will start at a preset value\n" "env variable U_FAKETIME_START=%.0f (%s) for an offset of %.0f ms from the current time %.0f\n",
fakeClock_t0, fake_start, fakeClock_dt, real);
} else {
fakeClock_dt = 0;
fprintf(stderr,"U_DEBUG_FAKETIME was set at compile time, but U_FAKETIME_START was not set.\n" "Set U_FAKETIME_START to the number of milliseconds since 1/1/1970 to set the ICU clock.\n");
}
fakeClock_set = true;
}
umtx_unlock(&fakeClockMutex);
return getUTCtime_real() + fakeClock_dt;
} #endif
#if U_PLATFORM_USES_ONLY_WIN32_API typedefunion {
int64_t int64;
FILETIME fileTime;
} FileTimeConversion; /* This is like a ULARGE_INTEGER */
/* Number of 100 nanoseconds from 1/1/1601 to 1/1/1970 */ #define EPOCH_BIAS INT64_C(116444736000000000) #define HECTONANOSECOND_PER_MILLISECOND 10000
U_CAPI UDate U_EXPORT2
uprv_getUTCtime()
{ #ifdefined(U_DEBUG_FAKETIME) return getUTCtime_fake(); /* Hook for overriding the clock */ #else return uprv_getRawUTCtime(); #endif
}
/* Return UTC (GMT) time measured in milliseconds since 0:00 on 1/1/70.*/
U_CAPI UDate U_EXPORT2
uprv_getRawUTCtime()
{ #if U_PLATFORM_USES_ONLY_WIN32_API
#else /* If your platform doesn't support IEEE 754 but *does* have an NaN value,*/ /* you'll need to replace this default implementation with what's correct*/ /* for your platform.*/ return number != number; #endif
}
#else /* If your platform doesn't support IEEE 754 but *does* have an infinity*/ /* value, you'll need to replace this default implementation with what's*/ /* correct for your platform.*/ return number == (2.0 * number); #endif
}
U_CAPI double U_EXPORT2
uprv_getNaN()
{ #if IEEE_754 || U_PLATFORM == U_PF_OS390 return gNan.d64; #else /* If your platform doesn't support IEEE 754 but *does* have an NaN value,*/ /* you'll need to replace this default implementation with what's correct*/ /* for your platform.*/ return0.0; #endif
}
U_CAPI double U_EXPORT2
uprv_getInfinity()
{ #if IEEE_754 || U_PLATFORM == U_PF_OS390 return gInf.d64; #else /* If your platform doesn't support IEEE 754 but *does* have an infinity*/ /* value, you'll need to replace this default implementation with what's*/ /* correct for your platform.*/ return0.0; #endif
}
/* check for -0 and 0*/ if(x == 0.0 && y == 0.0 && u_signBit(y)) return y;
#endif
/* this should work for all flt point w/o NaN and Inf special cases */ return (x > y ? y : x);
}
U_CAPI UBool U_EXPORT2
uprv_add32_overflow(int32_t a, int32_t b, int32_t* res) { // NOTE: Some compilers (GCC, Clang) have primitives available, like __builtin_add_overflow. // This function could be optimized by calling one of those primitives. auto a64 = static_cast<int64_t>(a); auto b64 = static_cast<int64_t>(b);
int64_t res64 = a64 + b64;
*res = static_cast<int32_t>(res64); return res64 != *res;
}
U_CAPI UBool U_EXPORT2
uprv_mul32_overflow(int32_t a, int32_t b, int32_t* res) { // NOTE: Some compilers (GCC, Clang) have primitives available, like __builtin_mul_overflow. // This function could be optimized by calling one of those primitives. auto a64 = static_cast<int64_t>(a); auto b64 = static_cast<int64_t>(b);
int64_t res64 = a64 * b64;
*res = static_cast<int32_t>(res64); return res64 != *res;
}
/* Determine if this is something like Iceland (Olson ID)
or AST4ADT (non-Olson ID) */ while (id[idx] && isNonDigit(id[idx]) && id[idx] != ',') {
idx++;
}
/* Allow at maximum 2 numbers at the end of the id to support zone id's
like GMT+11. */
idxMax = idx + 2; while (id[idx] && isDigit(id[idx]) && idx < idxMax) {
idx++;
}
/* If we went through the whole string, then it might be okay. Thetimezoneissometimessetto"CST-7CDT","CST6CDT5,J129,J131/19:30", "GRNLNDST3GRNLNDDT"orsimilar,sowecannotuseit.
The rest of the time it could be an Olson ID. George */ return id[idx] == 0
|| uprv_strcmp(id, "PST8PDT") == 0
|| uprv_strcmp(id, "MST7MDT") == 0
|| uprv_strcmp(id, "CST6CDT") == 0
|| uprv_strcmp(id, "EST5EDT") == 0;
}
/* On some Unix-like OS, 'posix' subdirectory in /usr/share/zoneinforeplicatesthetop-levelcontents.'right' subdirectoryhasthesamesetoffiles,butindividualfiles aredifferentfromthoseinthetop-leveldirectoryor'posix' because'right'hasfilesforTAI(Int'lAtomicTime)while'posix' hasfilesforUTC. Whenthefirstmatchfor/etc/localtimeisineitherofthem (usuallyinposixbecause'right'hasdifferentfilecontents), orTZenvironmentvariablepointstooneofthem,createTimeZone failsbecause,say,'posix/America/New_York'isnotanOlson timezoneid('America/New_York'is).So,wehavetoskip
'posix/' and 'right/' at the beginning. */ staticvoid skipZoneIDPrefix(constchar** id) { if (uprv_strncmp(*id, "posix/", 6) == 0
|| uprv_strncmp(*id, "right/", 6) == 0)
{
*id += 6;
}
} #endif
/* dirent also lists two entries: "." and ".." that we can safely ignore. */ #define SKIP1 "." #define SKIP2 ".." static UBool U_CALLCONV putil_cleanup(); static CharString *gSearchTZFileResult = nullptr;
if (gSearchTZFileResult == nullptr) {
gSearchTZFileResult = new CharString; if (gSearchTZFileResult == nullptr) { goto cleanupAndReturn;
}
ucln_common_registerCleanup(UCLN_COMMON_PUTIL, putil_cleanup);
}
/* Check each entry in the directory. */ while((dirEntry = readdir(dirp)) != nullptr) { constchar* dirName = dirEntry->d_name; if (uprv_strcmp(dirName, SKIP1) != 0 && uprv_strcmp(dirName, SKIP2) != 0
&& uprv_strcmp(TZFILE_SKIP, dirName) != 0 && uprv_strcmp(TZFILE_SKIP2, dirName) != 0) { /* Create a newpath with the new entry to test each entry in the directory. */
CharString newpath(curpath, status);
newpath.append(dirName, -1, status); if (U_FAILURE(status)) { break;
}
DIR* subDirp = nullptr; if ((subDirp = opendir(newpath.data())) != nullptr) { /* If this new path is a directory, make a recursive call with the newpath. */
closedir(subDirp);
newpath.append('/', status); if (U_FAILURE(status)) { break;
}
result = searchForTZFile(newpath.data(), tzInfo); /* Havetogetouthere.Otherwise,we'dkeeplooking andreturnthefirstmatchinthetop-leveldirectory ifthere'samatchinthetop-level.Ifnot,thisfunction wouldreturnnullptrandsetgTimeZoneBufferPtrtonullptrininitDefault(). Itworkedwithoutthisinmostcasesbecausewehaveafallbackofcalling localtime_rtofigureoutthedefaulttimezone.
*/ if (result != nullptr) break;
} else { if(compareBinaryFiles(TZDEFAULT, newpath.data(), tzInfo)) {
int32_t amountToSkip = sizeof(TZZONEINFO) - 1; if (amountToSkip > newpath.length()) {
amountToSkip = newpath.length();
} constchar* zoneid = newpath.data() + amountToSkip;
skipZoneIDPrefix(&zoneid);
gSearchTZFileResult->clear();
gSearchTZFileResult->append(zoneid, -1, status); if (U_FAILURE(status)) { break;
}
result = gSearchTZFileResult->data(); /* Get out after the first one found. */ break;
}
}
}
}
cleanupAndReturn: if (dirp) {
closedir(dirp);
} return result;
} #endif
U_CAPI void U_EXPORT2
uprv_tzname_clear_cache()
{ #if U_PLATFORM == U_PF_ANDROID /* Android's timezone is stored in system property. */
gAndroidTimeZone[0] = '\0'; void* libc = dlopen("libc.so", RTLD_NOLOAD); if (libc) { /* Android API 26+ has new API to get system property and old API
* (__system_property_get) is deprecated */
system_property_read_callback* property_read_callback =
(system_property_read_callback*)dlsym(
libc, "__system_property_read_callback"); if (property_read_callback) { const prop_info* info =
__system_property_find("persist.sys.timezone"); if (info) {
property_read_callback(info, &u_property_read, gAndroidTimeZone);
}
} else {
system_property_get* property_get =
(system_property_get*)dlsym(libc, "__system_property_get"); if (property_get) {
property_get("persist.sys.timezone", gAndroidTimeZone);
}
}
dlclose(libc);
} #endif
#ifndef U_TZNAME // The return value is free'd in timezone.cpp on Windows because // the other code path returns a pointer to a heap location. // If we don't have a name already, then tzname wouldn't be any // better, so just fall back. return uprv_strdup(""); #endif// !U_TZNAME
/* This code can be temporarily disabled to test tzname resolution later on. */ #ifndef DEBUG_TZNAME #if U_PLATFORM == U_PF_ANDROID
tzid = gAndroidTimeZone; #else
tzid = getenv("TZ"); #endif if (tzid != nullptr && isValidOlsonID(tzid) #if U_PLATFORM == U_PF_SOLARIS /* Don't misinterpret TZ "localtime" on Solaris as a time zone name. */
&& uprv_strcmp(tzid, TZ_ENV_CHECK) != 0 #endif
) { /* The colon forces tzset() to treat the remainder as zoneinfo path */ if (tzid[0] == ':') {
tzid++;
} /* This might be a good Olson ID. */
skipZoneIDPrefix(&tzid); return tzid;
} /* else U_TZNAME will give a better result. */ #endif
#ifdefined(CHECK_LOCALTIME_LINK) && !defined(DEBUG_SKIP_LOCALTIME_LINK) /* Caller must handle threading issues */ if (gTimeZoneBufferPtr == nullptr) { /* ThisisatricktolookatthenameofthelinktogettheOlsonID becausethetzfilecontentsisunderspecified. Thisisn'tguaranteedtoworkbecauseitmaynotbeasymlink.
*/ char *ret = realpath(TZDEFAULT, gTimeZoneBuffer); if (ret != nullptr && uprv_strcmp(TZDEFAULT, gTimeZoneBuffer) != 0) {
int32_t tzZoneInfoTailLen = uprv_strlen(TZZONEINFOTAIL); constchar *tzZoneInfoTailPtr = uprv_strstr(gTimeZoneBuffer, TZZONEINFOTAIL); // MacOS14 has the realpath as something like // /usr/share/zoneinfo.default/Australia/Melbourne // which will not have "/zoneinfo/" in the path. // Therefore if we fail, we fall back to read the link which is // /var/db/timezone/zoneinfo/Australia/Melbourne // We also fall back to reading the link if the realpath leads to something like // /usr/share/zoneinfo/posixrules if (tzZoneInfoTailPtr == nullptr ||
uprv_strcmp(tzZoneInfoTailPtr + tzZoneInfoTailLen, "posixrules") == 0) {
ssize_t size = readlink(TZDEFAULT, gTimeZoneBuffer, sizeof(gTimeZoneBuffer)-1); if (size > 0) {
gTimeZoneBuffer[size] = 0;
tzZoneInfoTailPtr = uprv_strstr(gTimeZoneBuffer, TZZONEINFOTAIL);
}
} if (tzZoneInfoTailPtr != nullptr) {
tzZoneInfoTailPtr += tzZoneInfoTailLen;
skipZoneIDPrefix(&tzZoneInfoTailPtr); if (isValidOlsonID(tzZoneInfoTailPtr)) { return (gTimeZoneBufferPtr = tzZoneInfoTailPtr);
}
}
} else { #ifdefined(SEARCH_TZFILE)
DefaultTZInfo* tzInfo = (DefaultTZInfo*)uprv_malloc(sizeof(DefaultTZInfo)); if (tzInfo != nullptr) {
tzInfo->defaultTZBuffer = nullptr;
tzInfo->defaultTZFileSize = 0;
tzInfo->defaultTZFilePtr = nullptr;
tzInfo->defaultTZstatus = false;
tzInfo->defaultTZPosition = 0;
#ifdef U_TZNAME #if U_PLATFORM_USES_ONLY_WIN32_API /* The return value is free'd in timezone.cpp on Windows because
* the other code path returns a pointer to a heap location. */ return uprv_strdup(U_TZNAME[n]); #else /* U_TZNAMEisusuallyanon-uniqueabbreviation,whichisn'tnormallyusable. SoweremaptheabbreviationtoanolsonID.
if(directory==nullptr || *directory==0) { /* A small optimization to prevent the malloc and copy when the sharedlibraryisused,andthisisawaytomakesurethatnullptr isneverreturned.
*/
newDataDir = (char *)"";
} else {
length=(int32_t)uprv_strlen(directory);
newDataDir = (char *)uprv_malloc(length + 2); /* Exit out if newDataDir could not be created. */ if (newDataDir == nullptr) { return;
}
uprv_strcpy(newDataDir, directory);
/* Backup setting of ICU_DATA_DIR_PREFIX_ENV_VAR
(needed for some Darwin ICU build environments) */ #if U_PLATFORM_IS_DARWIN_BASED && defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR # if !defined(ICU_DATA_DIR_PREFIX_ENV_VAR) # define ICU_DATA_DIR_PREFIX_ENV_VAR "IPHONE_SIMULATOR_ROOT" # endif #endif
#ifdefined(ICU_DATA_DIR_WINDOWS) // Helper function to get the ICU Data Directory under the Windows directory location. staticBOOL U_CALLCONV getIcuDataDirectoryUnderWindowsDirectory(char* directoryBuffer, UINT bufferLength)
{
wchar_t windowsPath[MAX_PATH]; char windowsPathUtf8[MAX_PATH];
UINT length = GetSystemWindowsDirectoryW(windowsPath, UPRV_LENGTHOF(windowsPath)); if ((length > 0) && (length < (UPRV_LENGTHOF(windowsPath) - 1))) { // Convert UTF-16 to a UTF-8 string.
UErrorCode status = U_ZERO_ERROR;
int32_t windowsPathUtf8Len = 0;
u_strToUTF8(windowsPathUtf8, static_cast<int32_t>(UPRV_LENGTHOF(windowsPathUtf8)),
&windowsPathUtf8Len, reinterpret_cast<const char16_t*>(windowsPath), -1, &status);
if (U_SUCCESS(status) && (status != U_STRING_NOT_TERMINATED_WARNING) &&
(windowsPathUtf8Len < (UPRV_LENGTHOF(windowsPathUtf8) - 1))) { // Ensure it always has a separator, so we can append the ICU data path. if (windowsPathUtf8[windowsPathUtf8Len - 1] != U_FILE_SEP_CHAR) {
windowsPathUtf8[windowsPathUtf8Len++] = U_FILE_SEP_CHAR;
windowsPathUtf8[windowsPathUtf8Len] = '\0';
} // Check if the concatenated string will fit. if ((windowsPathUtf8Len + UPRV_LENGTHOF(ICU_DATA_DIR_WINDOWS)) < bufferLength) {
uprv_strcpy(directoryBuffer, windowsPathUtf8);
uprv_strcat(directoryBuffer, ICU_DATA_DIR_WINDOWS); return true;
}
}
}
returnfalse;
} #endif
staticvoid U_CALLCONV dataDirectoryInitFn() { /* If we already have the directory, then return immediately. Will happen if user called *u_setDataDirectory().
*/ if (gDataDirectory) { return;
}
Theremayalsobesomeplatformswhereenvironmentvariables arenotallowed.
*/ # if !defined(ICU_NO_USER_DATA_OVERRIDE) && !UCONFIG_NO_FILE_IO /* First try to get the environment variable */ # if U_PLATFORM_HAS_WINUWP_API == 0// Windows UWP does not support getenv
path=getenv("ICU_DATA"); # endif # endif
/* ICU_DATA_DIR may be set as a compile option. *U_ICU_DATA_DEFAULT_DIRisprovidedandissetbyICUatcompiletime *andisusedonlywhendataisbuiltinarchivemodeeliminatingtheneed *forICU_DATA_DIRtobeset.U_ICU_DATA_DEFAULT_DIRissettotheinstallation *directoryofthedatadatfile.UsersshoulduseICU_DATA_DIRiftheywantto *settheirownpath.
*/ #ifdefined(ICU_DATA_DIR) || defined(U_ICU_DATA_DEFAULT_DIR) if(path==nullptr || *path==0) { # ifdefined(ICU_DATA_DIR_PREFIX_ENV_VAR) constchar *prefix = getenv(ICU_DATA_DIR_PREFIX_ENV_VAR); # endif # ifdef ICU_DATA_DIR
path=ICU_DATA_DIR; # else
path=U_ICU_DATA_DEFAULT_DIR; # endif # ifdefined(ICU_DATA_DIR_PREFIX_ENV_VAR) if (prefix != nullptr) {
snprintf(datadir_path_buffer, sizeof(datadir_path_buffer), "%s%s", prefix, path);
path=datadir_path_buffer;
} # endif
} #endif
#if U_PLATFORM_HAS_WINUWP_API == 1 // The UWP version does not support the environment variable setting.
# ifdefined(ICU_DATA_DIR_WINDOWS) // When using the Windows system data, we can possibly pick up time zone data from the Windows directory. char datadir_path_buffer[MAX_PATH]; if (getIcuDataDirectoryUnderWindowsDirectory(datadir_path_buffer, UPRV_LENGTHOF(datadir_path_buffer))) {
dir = datadir_path_buffer;
} # endif
#else
dir = getenv("ICU_TIMEZONE_FILES_DIR"); #endif// U_PLATFORM_HAS_WINUWP_API
#ifdefined(U_TIMEZONE_FILES_DIR) if (dir == nullptr) { // Build time configuration setting.
dir = TO_STRING(U_TIMEZONE_FILES_DIR);
} #endif
if (dir == nullptr) {
dir = "";
}
#ifdefined(ICU_TIMEZONE_FILES_DIR_PREFIX_ENV_VAR) if (prefix != nullptr) {
snprintf(timezonefilesdir_path_buffer, sizeof(timezonefilesdir_path_buffer), "%s%s", prefix, dir);
dir = timezonefilesdir_path_buffer;
} #endif
// Note: this function does some extra churn, first setting based on the // environment, then immediately replacing with the value passed in. // The logic is simpler that way, and performance shouldn't be an issue.
}
#if U_POSIX_LOCALE /* A helper function used by uprv_getPOSIXIDForDefaultLocale and *uprv_getPOSIXIDForDefaultCodepage.Returnstheposixlocaleidfor *LC_CTYPEandLC_MESSAGES.Itdoesn'tsupportotherlocalecategories.
*/ staticconstchar *uprv_getPOSIXIDForCategory(int category)
{ constchar* posixID = nullptr; if (category == LC_MESSAGES || category == LC_CTYPE) { /* *OnSolaristwodifferentcallstosetlocalecanresultin *differentvalues.Onlygetthisvalueonce. * *Wemustcheckthisfirstbecauseanapplicationcansetthis. * *LC_ALLcan'tbeusedbecauseit'splatformdependent.TheLANG *environmentvariableseemstoaffectLC_CTYPEvariablebydefault. *Hereiswhatsetlocale(LC_ALL,nullptr)canreturn. *HPUXcanreturn'CCCCCCC' *Solariscanreturn/en_US/C/C/C/C/Conthesecondtry. *LinuxcanreturnLC_CTYPE=C;LC_NUMERIC=C;... * *ThedefaultcodepagedetectionalsoneedstouseLC_CTYPE. * *Donotcallsetlocale(LC_*,"")!Usinganemptystringinstead *ofnullptr,willmodifythelibcbehavior.
*/
posixID = setlocale(category, nullptr); if ((posixID == nullptr)
|| (uprv_strcmp("C", posixID) == 0)
|| (uprv_strcmp("POSIX", posixID) == 0))
{ /* Maybe we got some garbage. Try something more reasonable */
posixID = getenv("LC_ALL"); /* Solaris speaks POSIX - See IEEE Std 1003.1-2008 *Thisisneededtoproperlyhandleemptyenv.variables
*/ #if U_PLATFORM == U_PF_SOLARIS if ((posixID == 0) || (posixID[0] == '\0')) {
posixID = getenv(category == LC_MESSAGES ? "LC_MESSAGES" : "LC_CTYPE"); if ((posixID == 0) || (posixID[0] == '\0')) { #else if (posixID == nullptr) {
posixID = getenv(category == LC_MESSAGES ? "LC_MESSAGES" : "LC_CTYPE"); if (posixID == nullptr) { #endif
posixID = getenv("LANG");
}
}
}
} if ((posixID == nullptr)
|| (uprv_strcmp("C", posixID) == 0)
|| (uprv_strcmp("POSIX", posixID) == 0))
{ /* Nothing worked. Give it a nice POSIX default value. */
posixID = "en_US_POSIX"; // Note: this test will not catch 'C.UTF-8', // that will be handled in uprv_getDefaultLocaleID(). // Leave this mapping here for the uprv_getPOSIXIDForDefaultCodepage() // caller which expects to see "en_US_POSIX" in many branches.
} return posixID;
}
/* Return just the POSIX id for the default locale, whatever happens to be in *it.ItgetsthevaluefromLC_MESSAGESandindirectlyfromLC_ALLandLANG.
*/ staticconstchar *uprv_getPOSIXIDForDefaultLocale()
{ staticconstchar* posixID = nullptr; if (posixID == nullptr) {
posixID = uprv_getPOSIXIDForCategory(LC_MESSAGES);
} return posixID;
}
#if !U_CHARSET_IS_UTF8 /* Return just the POSIX id for the default codepage, whatever happens to be in *it.ItgetsthevaluefromLC_CTYPEandindirectlyfromLC_ALLandLANG.
*/ staticconstchar *uprv_getPOSIXIDForDefaultCodepage()
{ staticconstchar* posixID = nullptr; if (posixID == 0) {
posixID = uprv_getPOSIXIDForCategory(LC_CTYPE);
} return posixID;
} #endif #endif
if (gCorrectedPOSIXLocale != nullptr) { return gCorrectedPOSIXLocale;
}
// Copy the ID into owned memory. // Over-allocate in case we replace "C" with "en_US_POSIX" (+10), + null termination char *correctedPOSIXLocale = static_cast<char *>(uprv_malloc(uprv_strlen(posixID) + 10 + 1)); if (correctedPOSIXLocale == nullptr) { return nullptr;
}
uprv_strcpy(correctedPOSIXLocale, posixID);
if ((uprv_strcmp("C", correctedPOSIXLocale) == 0) // no @ variant
|| (uprv_strcmp("POSIX", correctedPOSIXLocale) == 0)) { // Raw input was C.* or POSIX.*, Give it a nice POSIX default value. // (The "C"/"POSIX" case is handled in uprv_getPOSIXIDForCategory())
uprv_strcpy(correctedPOSIXLocale, "en_US_POSIX");
}
/* Note that we scan the *uncorrected* ID. */ constchar *p; if ((p = uprv_strrchr(posixID, '@')) != nullptr) {
p++;
/* Take care of any special cases here.. */ if (!uprv_strcmp(p, "nynorsk")) {
p = "NY"; /* Don't worry about no__NY. In practice, it won't appear. */
}
if (uprv_strchr(correctedPOSIXLocale,'_') == nullptr) {
uprv_strcat(correctedPOSIXLocale, "__"); /* aa@b -> aa__b (note this can make the new locale 1 char longer) */
} else {
uprv_strcat(correctedPOSIXLocale, "_"); /* aa_CC@b -> aa_CC_b */
}
constchar *q; if ((q = uprv_strchr(p, '.')) != nullptr) { /* How big will the resulting string be? */
int32_t len = (int32_t)(uprv_strlen(correctedPOSIXLocale) + (q-p));
uprv_strncat(correctedPOSIXLocale, p, q-p); // do not include charset
correctedPOSIXLocale[len] = 0;
} else { /* Anything following the @ sign */
uprv_strcat(correctedPOSIXLocale, p);
}
/* Should there be a map from 'no@nynorsk' -> no_NO_NY here? *Howabout'russian'->'ru'? *ManyoftheotherlocalesusingISOcodeswillbehandledbythe *canonicalizationfunctionsinuloc_getDefault.
*/
}
// If we have already figured this out just use the cached value if (gCorrectedPOSIXLocale != nullptr) { return gCorrectedPOSIXLocale;
}
// No cached value, need to determine the current value static WCHAR windowsLocale[LOCALE_NAME_MAX_LENGTH] = {}; int length = GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SNAME, windowsLocale, LOCALE_NAME_MAX_LENGTH);
// Now we should have a Windows locale name that needs converted to the POSIX style. if (length > 0) // If length is 0, then the GetLocaleInfoEx failed.
{ // First we need to go from UTF-16 to char (and also convert from _ to - while we're at it.) char modifiedWindowsLocale[LOCALE_NAME_MAX_LENGTH] = {};
int32_t i; for (i = 0; i < UPRV_LENGTHOF(modifiedWindowsLocale); i++)
{ if (windowsLocale[i] == '_')
{
modifiedWindowsLocale[i] = '-';
} else
{
modifiedWindowsLocale[i] = static_cast<char>(windowsLocale[i]);
}
if (modifiedWindowsLocale[i] == '\0')
{ break;
}
}
if (i >= UPRV_LENGTHOF(modifiedWindowsLocale))
{ // Ran out of room, can't really happen, maybe we'll be lucky about a matching // locale when tags are dropped
modifiedWindowsLocale[UPRV_LENGTHOF(modifiedWindowsLocale) - 1] = '\0';
}
// Now normalize the resulting name
correctedPOSIXLocale = static_cast<char *>(uprv_malloc(POSIX_LOCALE_CAPACITY + 1)); /* TODO: Should we just exit on memory allocation failure? */ if (correctedPOSIXLocale)
{
int32_t posixLen = uloc_canonicalize(modifiedWindowsLocale, correctedPOSIXLocale, POSIX_LOCALE_CAPACITY, &status); if (U_SUCCESS(status))
{
*(correctedPOSIXLocale + posixLen) = 0;
gCorrectedPOSIXLocale = correctedPOSIXLocale;
gCorrectedPOSIXLocaleHeapAllocated = true;
ucln_common_registerCleanup(UCLN_COMMON_PUTIL, putil_cleanup);
} else
{
uprv_free(correctedPOSIXLocale);
}
}
}
// If unable to find a locale we can agree upon, use en-US by default if (gCorrectedPOSIXLocale == nullptr) {
gCorrectedPOSIXLocale = "en_US";
} return gCorrectedPOSIXLocale;
#elif U_PLATFORM == U_PF_OS400 /* locales are process scoped and are by definition thread safe */ staticchar correctedLocale[64]; constchar *localeID = getenv("LC_ALL"); char *p;
if (localeID == nullptr)
localeID = getenv("LANG"); if (localeID == nullptr)
localeID = setlocale(LC_ALL, nullptr); /* Make sure we have something... */ if (localeID == nullptr) return"en_US_POSIX";
/* Extract the locale name from the path. */ if((p = uprv_strrchr(localeID, '/')) != nullptr)
{ /* Increment p to start of locale name. */
p++;
localeID = p;
}
/* Copy to work location. */
uprv_strcpy(correctedLocale, localeID);
/* Strip off the '.locale' extension. */ if((p = uprv_strchr(correctedLocale, '.')) != nullptr) {
*p = 0;
}
/* Upper case the locale name. */
T_CString_toUpperCase(correctedLocale);
/* See if we are using the POSIX locale. Any of the *followingareequivalentandusethesameQLGPGCMA *(POSIX)locale. *QLGPGCMA2meansUCS2 *QLGPGCMA_4meansUTF-32 *QLGPGCMA_8meansUTF-8
*/ if ((uprv_strcmp("C", correctedLocale) == 0) ||
(uprv_strcmp("POSIX", correctedLocale) == 0) ||
(uprv_strncmp("QLGPGCMA", correctedLocale, 8) == 0))
{
uprv_strcpy(correctedLocale, "en_US_POSIX");
} else
{
int16_t LocaleLen;
/* Lower case the lang portion. */ for(p = correctedLocale; *p != 0 && *p != '_'; p++)
{
*p = uprv_tolower(*p);
}
/* Adjust for Euro. After '_E' add 'URO'. */
LocaleLen = uprv_strlen(correctedLocale); if (correctedLocale[LocaleLen - 2] == '_' &&
correctedLocale[LocaleLen - 1] == 'E')
{
uprv_strcat(correctedLocale, "URO");
}
/* If using Lotus-based locale then convert to *equivalentnonLotus.
*/ elseif (correctedLocale[LocaleLen - 2] == '_' &&
correctedLocale[LocaleLen - 1] == 'L')
{
correctedLocale[LocaleLen - 2] = 0;
}
/* There are separate simplified and traditional *localescalledzh_HK_Sandzh_HK_T.
*/ elseif (uprv_strncmp(correctedLocale, "zh_HK", 5) == 0)
{
uprv_strcpy(correctedLocale, "zh_HK");
}
/* A special zh_CN_GBK locale...
*/ elseif (uprv_strcmp(correctedLocale, "zh_CN_GBK") == 0)
{
uprv_strcpy(correctedLocale, "zh_CN");
}
}
return correctedLocale; #endif
}
#if !U_CHARSET_IS_UTF8 #if U_POSIX_LOCALE /* Duetovariousplatformdifferences,oneplatformmayspecifyacharset, whentheyreallymeanadifferentcharset.Remapthenamessothattheyare compatiblewithICU.Onlyconflicting/ambiguousaliasesshouldberesolved here.Beforeaddinganythingtothisfunction,pleaseconsideraddingunique namestotheICUaliastableinthedatadirectory.
*/ staticconstchar*
remapPlatformDependentCodepage(constchar *locale, constchar *name) { if (locale != nullptr && *locale == 0) { /* Make sure that an empty locale is handled the same way. */
locale = nullptr;
} if (name == nullptr) { return nullptr;
} #if U_PLATFORM == U_PF_AIX if (uprv_strcmp(name, "IBM-943") == 0) { /* Use the ASCII compatible ibm-943 */
name = "Shift-JIS";
} elseif (uprv_strcmp(name, "IBM-1252") == 0) { /* Use the windows-1252 that contains the Euro */
name = "IBM-5348";
} #elif U_PLATFORM == U_PF_SOLARIS if (locale != nullptr && uprv_strcmp(name, "EUC") == 0) { /* Solaris underspecifies the "EUC" name. */ if (uprv_strcmp(locale, "zh_CN") == 0) {
name = "EUC-CN";
} elseif (uprv_strcmp(locale, "zh_TW") == 0) {
name = "EUC-TW";
} elseif (uprv_strcmp(locale, "ko_KR") == 0) {
name = "EUC-KR";
}
} elseif (uprv_strcmp(name, "eucJP") == 0) { /* ibm-954isthebestmatch. ibm-33722isthedefaultforeucJP(similartoWindows).
*/
name = "eucjis";
} elseif (uprv_strcmp(name, "646") == 0) { /* *ThedefaultcodepagegivenbySolarisis646buttheClibraryroutinestreatitasifitwas *ISO-8859-1insteadofUS-ASCII(646).
*/
name = "ISO-8859-1";
} #elif U_PLATFORM_IS_DARWIN_BASED if (locale == nullptr && *name == 0) { /* Nolocalewasspecified,andanemptynamewaspassedin. Thisusuallyindicatesthatnl_langinfodidn'treturnvalidinformation. MacOSXusesUTF-8bydefault(especiallythelocaledataandconsole).
*/
name = "UTF-8";
} elseif (uprv_strcmp(name, "CP949") == 0) { /* Remap CP949 to a similar codepage to avoid issues with backslash and won symbol. */
name = "EUC-KR";
} elseif (locale != nullptr && uprv_strcmp(locale, "en_US_POSIX") != 0 && uprv_strcmp(name, "US-ASCII") == 0) { /* *FornonC/POSIXlocale,defaultthecodepagetoUTF-8insteadofUS-ASCII.
*/
name = "UTF-8";
} #elif U_PLATFORM == U_PF_BSD if (uprv_strcmp(name, "CP949") == 0) { /* Remap CP949 to a similar codepage to avoid issues with backslash and won symbol. */
name = "EUC-KR";
} #elif U_PLATFORM == U_PF_HPUX if (locale != nullptr && uprv_strcmp(locale, "zh_HK") == 0 && uprv_strcmp(name, "big5") == 0) { /* HP decided to extend big5 as hkbig5 even though it's not compatible :-( */ /* zh_TW.big5 is not the same charset as zh_HK.big5! */
name = "hkbig5";
} elseif (uprv_strcmp(name, "eucJP") == 0) { /* ibm-1350isthebestmatch,butunavailable. ibm-954ismostlyasupersetofibm-1350. ibm-33722isthedefaultforeucJP(similartoWindows).
*/
name = "eucjis";
} #elif U_PLATFORM == U_PF_LINUX if (locale != nullptr && uprv_strcmp(name, "euc") == 0) { /* Linux underspecifies the "EUC" name. */ if (uprv_strcmp(locale, "korean") == 0) {
name = "EUC-KR";
} elseif (uprv_strcmp(locale, "japanese") == 0) { /* See comment below about eucJP */
name = "eucjis";
}
} elseif (uprv_strcmp(name, "eucjp") == 0) { /* ibm-1350isthebestmatch,butunavailable. ibm-954ismostlyasupersetofibm-1350. ibm-33722isthedefaultforeucJP(similartoWindows).
*/
name = "eucjis";
} elseif (locale != nullptr && uprv_strcmp(locale, "en_US_POSIX") != 0 &&
(uprv_strcmp(name, "ANSI_X3.4-1968") == 0 || uprv_strcmp(name, "US-ASCII") == 0)) { /* *FornonC/POSIXlocale,defaultthecodepagetoUTF-8insteadofUS-ASCII.
*/
name = "UTF-8";
} /* *LinuxreturnsANSI_X3.4-1968forC/POSIX,butthecallsitetakescareof *itbyfallingbackto'US-ASCII'whennullptrisreturnedfromthis *function.So,wedon'thavetoworryaboutithere.
*/ #endif /* return nullptr when "" is passed in */ if (*name == 0) {
name = nullptr;
} return name;
}
#if U_PLATFORM_HAS_WINUWP_API == 1 // UWP doesn't have a direct API to get the default ACP as Microsoft would rather // have folks use Unicode than a "system" code page, however this is the same // codepage as the system default locale codepage. (FWIW, the system locale is // ONLY used for codepage, it should never be used for anything else)
GetLocaleInfoEx(LOCALE_NAME_SYSTEM_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
(LPWSTR)&codepageNumber, sizeof(codepageNumber) / sizeof(WCHAR)); #else // Win32 apps can call GetACP
codepageNumber = GetACP(); #endif // Special case for UTF-8 if (codepageNumber == 65001)
{ return"UTF-8";
} // Windows codepages can look like windows-1252, so format the found number // the numbers are eclectic, however all valid system code pages, besides UTF-8 // are between 3 and 19999 if (codepageNumber > 0 && codepageNumber < 20000)
{
snprintf(codepage, sizeof(codepage), "windows-%ld", codepageNumber); return codepage;
} // If the codepage number call failed then return UTF-8 return"UTF-8";
/* Use setlocale in a nice way, and then check some environment variables. Maybetheapplicationusedsetlocalealready.
*/
uprv_memset(codesetName, 0, sizeof(codesetName));
name = getCodepageFromPOSIXID(localeName, codesetName, sizeof(codesetName)); if (name) { /* if we can find the codeset name from setlocale, return that. */ return name;
}
if (*codesetName == 0)
{ /* Everything failed. Return US ASCII (ISO 646). */
(void)uprv_strcpy(codesetName, "US-ASCII");
} return codesetName; #else return"US-ASCII"; #endif
}
/* count how many fields need to be written */ for(count=4; count>0 && versionArray[count-1]==0; --count) {
}
if(count <= 1) {
count = 2;
}
/* write the first part */ /* write the decimal field value */
field=versionArray[0]; if(field>=100) {
*versionString++=(char)('0'+field/100);
field%=100;
} if(field>=10) {
*versionString++=(char)('0'+field/10);
field%=10;
}
*versionString++=(char)('0'+field);
/* write the following parts */ for(part=1; part<count; ++part) { /* write a dot first */
*versionString++=U_VERSION_DELIMITER;
/* write the decimal field value */
field=versionArray[part]; if(field>=100) {
*versionString++=(char)('0'+field/100);
field%=100;
} if(field>=10) {
*versionString++=(char)('0'+field/10);
field%=10;
}
*versionString++=(char)('0'+field);
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.81Bemerkung:
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-08-26)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.