tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

windtfmt.cpp (13207B)


      1 // © 2016 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html
      3 /*
      4 ********************************************************************************
      5 *   Copyright (C) 2005-2016, International Business Machines
      6 *   Corporation and others.  All Rights Reserved.
      7 ********************************************************************************
      8 *
      9 * File WINDTFMT.CPP
     10 *
     11 ********************************************************************************
     12 */
     13 
     14 #include "unicode/utypes.h"
     15 
     16 #if U_PLATFORM_USES_ONLY_WIN32_API
     17 
     18 #if !UCONFIG_NO_FORMATTING
     19 
     20 #include "unicode/ures.h"
     21 #include "unicode/format.h"
     22 #include "unicode/fmtable.h"
     23 #include "unicode/datefmt.h"
     24 #include "unicode/simpleformatter.h"
     25 #include "unicode/calendar.h"
     26 #include "unicode/gregocal.h"
     27 #include "unicode/locid.h"
     28 #include "unicode/unistr.h"
     29 #include "unicode/ustring.h"
     30 #include "unicode/timezone.h"
     31 #include "unicode/utmscale.h"
     32 
     33 #include "charstr.h"
     34 #include "cmemory.h"
     35 #include "ulocimp.h"
     36 #include "uresimp.h"
     37 #include "windtfmt.h"
     38 #include "wintzimpl.h"
     39 
     40 #ifndef WIN32_LEAN_AND_MEAN
     41 #   define WIN32_LEAN_AND_MEAN
     42 #endif
     43 #   define VC_EXTRALEAN
     44 #   define NOUSER
     45 #   define NOSERVICE
     46 #   define NOIME
     47 #   define NOMCX
     48 #include <windows.h>
     49 #include <typeinfo>
     50 
     51 U_NAMESPACE_BEGIN
     52 
     53 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Win32DateFormat)
     54 
     55 #define NEW_ARRAY(type,count) (type *) uprv_malloc((count) * sizeof(type))
     56 #define DELETE_ARRAY(array) uprv_free((void *) (array))
     57 
     58 #define STACK_BUFFER_SIZE 64
     59 
     60 UnicodeString* Win32DateFormat::getTimeDateFormat(const Calendar *cal, const Locale *locale, UErrorCode &status) const
     61 {
     62    UnicodeString *result = nullptr;
     63    const char *type = cal->getType();
     64    const char *base = locale->getBaseName();
     65    UResourceBundle *topBundle = ures_open((char *) 0, base, &status);
     66    UResourceBundle *calBundle = ures_getByKey(topBundle, "calendar", nullptr, &status);
     67    UResourceBundle *typBundle = ures_getByKeyWithFallback(calBundle, type, nullptr, &status);
     68    UResourceBundle *patBundle = ures_getByKeyWithFallback(typBundle, "DateTimePatterns", nullptr, &status);
     69 
     70    if (status == U_MISSING_RESOURCE_ERROR) {
     71        status = U_ZERO_ERROR;
     72        typBundle = ures_getByKeyWithFallback(calBundle, "gregorian", typBundle, &status);
     73        patBundle = ures_getByKeyWithFallback(typBundle, "DateTimePatterns", patBundle, &status);
     74    }
     75 
     76    if (U_FAILURE(status)) {
     77        static const char16_t defaultPattern[] = {0x007B, 0x0031, 0x007D, 0x0020, 0x007B, 0x0030, 0x007D, 0x0000}; // "{1} {0}"
     78        return new UnicodeString(defaultPattern, UPRV_LENGTHOF(defaultPattern));
     79    }
     80 
     81    int32_t resStrLen = 0;
     82    int32_t glueIndex = DateFormat::kDateTime;
     83    int32_t patSize = ures_getSize(patBundle);
     84    if (patSize >= (DateFormat::kDateTimeOffset + DateFormat::kShort + 1)) {
     85        // Get proper date time format
     86        glueIndex = (int32_t)(DateFormat::kDateTimeOffset + (fDateStyle - DateFormat::kDateOffset));
     87    }
     88    const char16_t *resStr = ures_getStringByIndex(patBundle, glueIndex, &resStrLen, &status);
     89 
     90    result = new UnicodeString(true, resStr, resStrLen);
     91 
     92    ures_close(patBundle);
     93    ures_close(typBundle);
     94    ures_close(calBundle);
     95    ures_close(topBundle);
     96 
     97    return result;
     98 }
     99 
    100 // TODO: This is copied in both winnmfmt.cpp and windtfmt.cpp, but really should
    101 // be factored out into a common helper for both.
    102 static UErrorCode GetEquivalentWindowsLocaleName(const Locale& locale, UnicodeString** buffer)
    103 {
    104    UErrorCode status = U_ZERO_ERROR;
    105 
    106    // Convert from names like "en_CA" and "de_DE@collation=phonebook" to "en-CA" and "de-DE-u-co-phonebk".
    107    CharString asciiBCP47Tag = ulocimp_toLanguageTag(locale.getName(), false, status);
    108 
    109    if (U_SUCCESS(status))
    110    {
    111        // Need it to be UTF-16, not 8-bit
    112        // TODO: This seems like a good thing for a helper
    113        wchar_t bcp47Tag[LOCALE_NAME_MAX_LENGTH] = {};
    114        int32_t i;
    115        for (i = 0; i < UPRV_LENGTHOF(bcp47Tag); i++)
    116        {
    117            if (asciiBCP47Tag[i] == '\0')
    118            {
    119                break;
    120            }
    121            else
    122            {
    123                // normally just copy the character
    124                bcp47Tag[i] = static_cast<wchar_t>(asciiBCP47Tag[i]);
    125            }
    126        }
    127 
    128        // Ensure it's null terminated
    129        if (i < (UPRV_LENGTHOF(bcp47Tag) - 1))
    130        {
    131            bcp47Tag[i] = L'\0';
    132        }
    133        else
    134        {
    135            // Ran out of room.
    136            bcp47Tag[UPRV_LENGTHOF(bcp47Tag) - 1] = L'\0';
    137        }
    138 
    139 
    140        wchar_t windowsLocaleName[LOCALE_NAME_MAX_LENGTH] = {};
    141 
    142        // Note: On Windows versions below 10, there is no support for locale name aliases.
    143        // This means that it will fail for locales where ICU has a completely different
    144        // name (like ku vs ckb), and it will also not work for alternate sort locale
    145        // names like "de-DE-u-co-phonebk".
    146        
    147        // TODO: We could add some sort of exception table for cases like ku vs ckb.
    148 
    149        int length = ResolveLocaleName(bcp47Tag, windowsLocaleName, UPRV_LENGTHOF(windowsLocaleName));
    150 
    151        if (length > 0)
    152        {
    153            *buffer = new UnicodeString(windowsLocaleName);
    154        }
    155        else
    156        {
    157            status = U_UNSUPPORTED_ERROR;
    158        }
    159    }
    160    return status;
    161 }
    162 
    163 // TODO: Range-check timeStyle, dateStyle
    164 Win32DateFormat::Win32DateFormat(DateFormat::EStyle timeStyle, DateFormat::EStyle dateStyle, const Locale &locale, UErrorCode &status)
    165  : DateFormat(), fDateTimeMsg(nullptr), fTimeStyle(timeStyle), fDateStyle(dateStyle), fLocale(locale), fZoneID(), fWindowsLocaleName(nullptr)
    166 {
    167    if (U_SUCCESS(status)) {
    168        GetEquivalentWindowsLocaleName(locale, &fWindowsLocaleName);
    169        // Note: In the previous code, it would look up the LCID for the locale, and if
    170        // the locale was not recognized then it would get an LCID of 0, which is a
    171        // synonym for LOCALE_USER_DEFAULT on Windows.
    172        // If the above method fails, then fWindowsLocaleName will remain as nullptr, and 
    173        // then we will pass nullptr to API GetLocaleInfoEx, which is the same as passing
    174        // LOCALE_USER_DEFAULT.
    175 
    176        fTZI = NEW_ARRAY(TIME_ZONE_INFORMATION, 1);
    177        uprv_memset(fTZI, 0, sizeof(TIME_ZONE_INFORMATION));
    178        adoptCalendar(Calendar::createInstance(locale, status));
    179    }
    180 }
    181 
    182 Win32DateFormat::Win32DateFormat(const Win32DateFormat &other)
    183  : DateFormat(other)
    184 {
    185    *this = other;
    186 }
    187 
    188 Win32DateFormat::~Win32DateFormat()
    189 {
    190 //    delete fCalendar;
    191    uprv_free(fTZI);
    192    delete fDateTimeMsg;
    193    delete fWindowsLocaleName;
    194 }
    195 
    196 Win32DateFormat &Win32DateFormat::operator=(const Win32DateFormat &other)
    197 {
    198    if (this == &other) { return *this; }  // self-assignment: no-op
    199    // The following handles fCalendar
    200    DateFormat::operator=(other);
    201 
    202 //    delete fCalendar;
    203 
    204    this->fDateTimeMsg = other.fDateTimeMsg == nullptr ? nullptr : new UnicodeString(*other.fDateTimeMsg);
    205    this->fTimeStyle   = other.fTimeStyle;
    206    this->fDateStyle   = other.fDateStyle;
    207    this->fLocale      = other.fLocale;
    208 //    this->fCalendar    = other.fCalendar->clone();
    209    this->fZoneID      = other.fZoneID;
    210 
    211    this->fTZI = NEW_ARRAY(TIME_ZONE_INFORMATION, 1);
    212    *this->fTZI = *other.fTZI;
    213 
    214    this->fWindowsLocaleName = other.fWindowsLocaleName == nullptr ? nullptr : new UnicodeString(*other.fWindowsLocaleName);
    215 
    216    return *this;
    217 }
    218 
    219 Win32DateFormat *Win32DateFormat::clone() const
    220 {
    221    return new Win32DateFormat(*this);
    222 }
    223 
    224 // TODO: Is just ignoring pos the right thing?
    225 UnicodeString &Win32DateFormat::format(Calendar &cal, UnicodeString &appendTo, FieldPosition & /* pos */) const
    226 {
    227    FILETIME ft;
    228    SYSTEMTIME st_gmt;
    229    SYSTEMTIME st_local;
    230    TIME_ZONE_INFORMATION tzi = *fTZI;
    231    UErrorCode status = U_ZERO_ERROR;
    232    const TimeZone &tz = cal.getTimeZone();
    233    int64_t uct, uft;
    234 
    235    setTimeZoneInfo(&tzi, tz);
    236 
    237    uct = utmscale_fromInt64((int64_t) cal.getTime(status), UDTS_ICU4C_TIME, &status);
    238    uft = utmscale_toInt64(uct, UDTS_WINDOWS_FILE_TIME, &status);
    239 
    240    ft.dwLowDateTime =  (DWORD) (uft & 0xFFFFFFFF);
    241    ft.dwHighDateTime = (DWORD) ((uft >> 32) & 0xFFFFFFFF);
    242 
    243    FileTimeToSystemTime(&ft, &st_gmt);
    244    SystemTimeToTzSpecificLocalTime(&tzi, &st_gmt, &st_local);
    245 
    246 
    247    if (fDateStyle != DateFormat::kNone && fTimeStyle != DateFormat::kNone) {
    248        UnicodeString date;
    249        UnicodeString time;
    250        UnicodeString *pattern = fDateTimeMsg;
    251 
    252        formatDate(&st_local, date);
    253        formatTime(&st_local, time);
    254 
    255        if (typeid(cal) != typeid(*fCalendar)) {
    256            pattern = getTimeDateFormat(&cal, &fLocale, status);
    257        }
    258 
    259        SimpleFormatter(*pattern, 2, 2, status).format(time, date, appendTo, status);
    260    } else if (fDateStyle != DateFormat::kNone) {
    261        formatDate(&st_local, appendTo);
    262    } else if (fTimeStyle != DateFormat::kNone) {
    263        formatTime(&st_local, appendTo);
    264    }
    265 
    266    return appendTo;
    267 }
    268 
    269 void Win32DateFormat::parse(const UnicodeString& /* text */, Calendar& /* cal */, ParsePosition& pos) const
    270 {
    271    pos.setErrorIndex(pos.getIndex());
    272 }
    273 
    274 void Win32DateFormat::adoptCalendar(Calendar *newCalendar)
    275 {
    276    if (fCalendar == nullptr || typeid(*fCalendar) != typeid(*newCalendar)) {
    277        UErrorCode status = U_ZERO_ERROR;
    278 
    279        if (fDateStyle != DateFormat::kNone && fTimeStyle != DateFormat::kNone) {
    280            delete fDateTimeMsg;
    281            fDateTimeMsg = getTimeDateFormat(newCalendar, &fLocale, status);
    282        }
    283    }
    284 
    285    delete fCalendar;
    286    fCalendar = newCalendar;
    287 
    288    fZoneID = setTimeZoneInfo(fTZI, fCalendar->getTimeZone());
    289 }
    290 
    291 void Win32DateFormat::setCalendar(const Calendar &newCalendar)
    292 {
    293    adoptCalendar(newCalendar.clone());
    294 }
    295 
    296 void Win32DateFormat::adoptTimeZone(TimeZone *zoneToAdopt)
    297 {
    298    fZoneID = setTimeZoneInfo(fTZI, *zoneToAdopt);
    299    fCalendar->adoptTimeZone(zoneToAdopt);
    300 }
    301 
    302 void Win32DateFormat::setTimeZone(const TimeZone& zone)
    303 {
    304    fZoneID = setTimeZoneInfo(fTZI, zone);
    305    fCalendar->setTimeZone(zone);
    306 }
    307 
    308 static const DWORD dfFlags[] = {DATE_LONGDATE, DATE_LONGDATE, DATE_SHORTDATE, DATE_SHORTDATE};
    309 
    310 void Win32DateFormat::formatDate(const SYSTEMTIME *st, UnicodeString &appendTo) const
    311 {
    312    int result=0;
    313    wchar_t stackBuffer[STACK_BUFFER_SIZE];
    314    wchar_t *buffer = stackBuffer;
    315    const wchar_t *localeName = nullptr;
    316 
    317    if (fWindowsLocaleName != nullptr)
    318    {
    319        localeName = reinterpret_cast<const wchar_t*>(toOldUCharPtr(fWindowsLocaleName->getTerminatedBuffer()));
    320    }
    321 
    322    result = GetDateFormatEx(localeName, dfFlags[fDateStyle - kDateOffset], st, nullptr, buffer, STACK_BUFFER_SIZE, nullptr);
    323 
    324    if (result == 0) {
    325        if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
    326            int newLength = GetDateFormatEx(localeName, dfFlags[fDateStyle - kDateOffset], st, nullptr, nullptr, 0, nullptr);
    327 
    328            buffer = NEW_ARRAY(wchar_t, newLength);
    329 
    330            GetDateFormatEx(localeName, dfFlags[fDateStyle - kDateOffset], st, nullptr, buffer, newLength, nullptr);
    331        }
    332    }
    333 
    334    appendTo.append((const char16_t *)buffer, (int32_t) wcslen(buffer));
    335 
    336    if (buffer != stackBuffer) {
    337        DELETE_ARRAY(buffer);
    338    }
    339 }
    340 
    341 static const DWORD tfFlags[] = {0, 0, 0, TIME_NOSECONDS};
    342 
    343 void Win32DateFormat::formatTime(const SYSTEMTIME *st, UnicodeString &appendTo) const
    344 {
    345    int result;
    346    wchar_t stackBuffer[STACK_BUFFER_SIZE];
    347    wchar_t *buffer = stackBuffer;
    348    const wchar_t *localeName = nullptr;
    349 
    350    if (fWindowsLocaleName != nullptr)
    351    {
    352        localeName = reinterpret_cast<const wchar_t*>(toOldUCharPtr(fWindowsLocaleName->getTerminatedBuffer()));
    353    }
    354 
    355    result = GetTimeFormatEx(localeName, tfFlags[fTimeStyle], st, nullptr, buffer, STACK_BUFFER_SIZE);
    356 
    357    if (result == 0) {
    358        if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
    359            int newLength = GetTimeFormatEx(localeName, tfFlags[fTimeStyle], st, nullptr, nullptr, 0);
    360 
    361            buffer = NEW_ARRAY(wchar_t, newLength);
    362 
    363            GetTimeFormatEx(localeName, tfFlags[fTimeStyle], st, nullptr, buffer, newLength);
    364        }
    365    }
    366 
    367    appendTo.append((const char16_t *)buffer, (int32_t) wcslen(buffer));
    368 
    369    if (buffer != stackBuffer) {
    370        DELETE_ARRAY(buffer);
    371    }
    372 }
    373 
    374 UnicodeString Win32DateFormat::setTimeZoneInfo(TIME_ZONE_INFORMATION *tzi, const TimeZone &zone) const
    375 {
    376    UnicodeString zoneID;
    377 
    378    zone.getID(zoneID);
    379 
    380    if (zoneID.compare(fZoneID) != 0) {
    381        UnicodeString icuid;
    382 
    383        zone.getID(icuid);
    384        if (! uprv_getWindowsTimeZoneInfo(tzi, icuid.getBuffer(), icuid.length())) {
    385            UBool found = false;
    386            int32_t ec = TimeZone::countEquivalentIDs(icuid);
    387 
    388            for (int z = 0; z < ec; z += 1) {
    389                UnicodeString equiv = TimeZone::getEquivalentID(icuid, z);
    390 
    391                found = uprv_getWindowsTimeZoneInfo(tzi, equiv.getBuffer(), equiv.length());
    392                if (found) {
    393                    break;
    394                }
    395            }
    396 
    397            if (! found) {
    398                GetTimeZoneInformation(tzi);
    399            }
    400        }
    401    }
    402 
    403    return zoneID;
    404 }
    405 
    406 U_NAMESPACE_END
    407 
    408 #endif /* #if !UCONFIG_NO_FORMATTING */
    409 
    410 #endif // U_PLATFORM_USES_ONLY_WIN32_API