tor-browser

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

winnmfmt.cpp (14809B)


      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 WINNMFMT.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 "winnmfmt.h"
     21 
     22 #include "unicode/format.h"
     23 #include "unicode/numfmt.h"
     24 #include "unicode/locid.h"
     25 #include "unicode/ustring.h"
     26 
     27 #include "charstr.h"
     28 #include "cmemory.h"
     29 #include "uassert.h"
     30 #include "ulocimp.h"
     31 #include "locmap.h"
     32 
     33 #ifndef WIN32_LEAN_AND_MEAN
     34 #   define WIN32_LEAN_AND_MEAN
     35 #endif
     36 #   define VC_EXTRALEAN
     37 #   define NOUSER
     38 #   define NOSERVICE
     39 #   define NOIME
     40 #   define NOMCX
     41 #include <windows.h>
     42 #include <stdio.h>
     43 
     44 U_NAMESPACE_BEGIN
     45 
     46 union FormatInfo
     47 {
     48    NUMBERFMTW   number;
     49    CURRENCYFMTW currency;
     50 };
     51 
     52 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Win32NumberFormat)
     53 
     54 #define NEW_ARRAY(type,count) (type *) uprv_malloc((count) * sizeof(type))
     55 #define DELETE_ARRAY(array) uprv_free((void *) (array))
     56 
     57 #define STACK_BUFFER_SIZE 32
     58 
     59 /*
     60 * Turns a string of the form "3;2;0" into the grouping UINT
     61 * needed for NUMBERFMT and CURRENCYFMT. If the string does not
     62 * end in ";0" then the return value should be multiplied by 10.
     63 * (e.g. "3" => 30, "3;2" => 320)
     64 */
     65 static UINT getGrouping(const wchar_t *grouping)
     66 {
     67    UINT g = 0;
     68    const wchar_t *s;
     69 
     70    for (s = grouping; *s != L'\0'; s += 1) {
     71        if (*s > L'0' && *s < L'9') {
     72            g = g * 10 + (*s - L'0');
     73        } else if (*s != L';') {
     74            break;
     75        }
     76    }
     77 
     78    if (*s != L'0') {
     79        g *= 10;
     80    }
     81 
     82    return g;
     83 }
     84 
     85 static void getNumberFormat(NUMBERFMTW *fmt, const wchar_t *windowsLocaleName)
     86 {
     87    wchar_t buf[10];
     88 
     89    GetLocaleInfoEx(windowsLocaleName, LOCALE_RETURN_NUMBER|LOCALE_IDIGITS, (LPWSTR) &fmt->NumDigits, sizeof(UINT));
     90    GetLocaleInfoEx(windowsLocaleName, LOCALE_RETURN_NUMBER|LOCALE_ILZERO,  (LPWSTR) &fmt->LeadingZero, sizeof(UINT));
     91 
     92    GetLocaleInfoEx(windowsLocaleName, LOCALE_SGROUPING, (LPWSTR)buf, 10);
     93    fmt->Grouping = getGrouping(buf);
     94 
     95    fmt->lpDecimalSep = NEW_ARRAY(wchar_t, 6);
     96    GetLocaleInfoEx(windowsLocaleName, LOCALE_SDECIMAL,  fmt->lpDecimalSep,  6);
     97 
     98    fmt->lpThousandSep = NEW_ARRAY(wchar_t, 6);
     99    GetLocaleInfoEx(windowsLocaleName, LOCALE_STHOUSAND, fmt->lpThousandSep, 6);
    100 
    101    GetLocaleInfoEx(windowsLocaleName, LOCALE_RETURN_NUMBER|LOCALE_INEGNUMBER, (LPWSTR) &fmt->NegativeOrder, sizeof(UINT));
    102 }
    103 
    104 static void freeNumberFormat(NUMBERFMTW *fmt)
    105 {
    106    if (fmt != nullptr) {
    107        DELETE_ARRAY(fmt->lpThousandSep);
    108        DELETE_ARRAY(fmt->lpDecimalSep);
    109    }
    110 }
    111 
    112 static void getCurrencyFormat(CURRENCYFMTW *fmt, const wchar_t *windowsLocaleName)
    113 {
    114    wchar_t buf[10];
    115 
    116    GetLocaleInfoEx(windowsLocaleName, LOCALE_RETURN_NUMBER|LOCALE_ICURRDIGITS, (LPWSTR) &fmt->NumDigits, sizeof(UINT));
    117    GetLocaleInfoEx(windowsLocaleName, LOCALE_RETURN_NUMBER|LOCALE_ILZERO, (LPWSTR) &fmt->LeadingZero, sizeof(UINT));
    118 
    119    GetLocaleInfoEx(windowsLocaleName, LOCALE_SMONGROUPING, (LPWSTR)buf, sizeof(buf));
    120    fmt->Grouping = getGrouping(buf);
    121 
    122    fmt->lpDecimalSep = NEW_ARRAY(wchar_t, 6);
    123    GetLocaleInfoEx(windowsLocaleName, LOCALE_SMONDECIMALSEP,  fmt->lpDecimalSep,  6);
    124 
    125    fmt->lpThousandSep = NEW_ARRAY(wchar_t, 6);
    126    GetLocaleInfoEx(windowsLocaleName, LOCALE_SMONTHOUSANDSEP, fmt->lpThousandSep, 6);
    127 
    128    GetLocaleInfoEx(windowsLocaleName, LOCALE_RETURN_NUMBER|LOCALE_INEGCURR,  (LPWSTR) &fmt->NegativeOrder, sizeof(UINT));
    129    GetLocaleInfoEx(windowsLocaleName, LOCALE_RETURN_NUMBER|LOCALE_ICURRENCY, (LPWSTR) &fmt->PositiveOrder, sizeof(UINT));
    130 
    131    fmt->lpCurrencySymbol = NEW_ARRAY(wchar_t, 8);
    132    GetLocaleInfoEx(windowsLocaleName, LOCALE_SCURRENCY, (LPWSTR) fmt->lpCurrencySymbol, 8);
    133 }
    134 
    135 static void freeCurrencyFormat(CURRENCYFMTW *fmt)
    136 {
    137    if (fmt != nullptr) {
    138        DELETE_ARRAY(fmt->lpCurrencySymbol);
    139        DELETE_ARRAY(fmt->lpThousandSep);
    140        DELETE_ARRAY(fmt->lpDecimalSep);
    141    }
    142 }
    143 
    144 // TODO: This is copied in both winnmfmt.cpp and windtfmt.cpp, but really should
    145 // be factored out into a common helper for both.
    146 static UErrorCode GetEquivalentWindowsLocaleName(const Locale& locale, UnicodeString** buffer)
    147 {
    148    UErrorCode status = U_ZERO_ERROR;
    149 
    150    // Convert from names like "en_CA" and "de_DE@collation=phonebook" to "en-CA" and "de-DE-u-co-phonebk".
    151    CharString asciiBCP47Tag = ulocimp_toLanguageTag(locale.getName(), false, status);
    152 
    153    if (U_SUCCESS(status))
    154    {
    155        // Need it to be UTF-16, not 8-bit
    156        // TODO: This seems like a good thing for a helper
    157        wchar_t bcp47Tag[LOCALE_NAME_MAX_LENGTH] = {};
    158        int32_t i;
    159        for (i = 0; i < UPRV_LENGTHOF(bcp47Tag); i++)
    160        {
    161            if (asciiBCP47Tag[i] == '\0')
    162            {
    163                break;
    164            }
    165            else
    166            {
    167                // normally just copy the character
    168                bcp47Tag[i] = static_cast<wchar_t>(asciiBCP47Tag[i]);
    169            }
    170        }
    171 
    172        // Ensure it's null terminated
    173        if (i < (UPRV_LENGTHOF(bcp47Tag) - 1))
    174        {
    175            bcp47Tag[i] = L'\0';
    176        }
    177        else
    178        {
    179            // Ran out of room.
    180            bcp47Tag[UPRV_LENGTHOF(bcp47Tag) - 1] = L'\0';
    181        }
    182 
    183 
    184        wchar_t windowsLocaleName[LOCALE_NAME_MAX_LENGTH] = {};
    185 
    186        // Note: On Windows versions below 10, there is no support for locale name aliases.
    187        // This means that it will fail for locales where ICU has a completely different
    188        // name (like ku vs ckb), and it will also not work for alternate sort locale
    189        // names like "de-DE-u-co-phonebk".
    190 
    191        // TODO: We could add some sort of exception table for cases like ku vs ckb.
    192 
    193        int length = ResolveLocaleName(bcp47Tag, windowsLocaleName, UPRV_LENGTHOF(windowsLocaleName));
    194 
    195        if (length > 0)
    196        {
    197            *buffer = new UnicodeString(windowsLocaleName);
    198        }
    199        else
    200        {
    201            status = U_UNSUPPORTED_ERROR;
    202        }
    203    }
    204    return status;
    205 }
    206 
    207 Win32NumberFormat::Win32NumberFormat(const Locale &locale, UBool currency, UErrorCode &status)
    208  : NumberFormat(), fCurrency(currency), fFormatInfo(nullptr), fFractionDigitsSet(false), fWindowsLocaleName(nullptr)
    209 {
    210    if (!U_FAILURE(status)) {
    211        fLCID = locale.getLCID();
    212 
    213        GetEquivalentWindowsLocaleName(locale, &fWindowsLocaleName);
    214        // Note: In the previous code, it would look up the LCID for the locale, and if
    215        // the locale was not recognized then it would get an LCID of 0, which is a
    216        // synonym for LOCALE_USER_DEFAULT on Windows.
    217        // If the above method fails, then fWindowsLocaleName will remain as nullptr, and 
    218        // then we will pass nullptr to API GetLocaleInfoEx, which is the same as passing
    219        // LOCALE_USER_DEFAULT.
    220 
    221        // Resolve actual locale to be used later
    222        UErrorCode tmpsts = U_ZERO_ERROR;
    223        char tmpLocID[ULOC_FULLNAME_CAPACITY];
    224        int32_t len = uloc_getLocaleForLCID(fLCID, tmpLocID, UPRV_LENGTHOF(tmpLocID) - 1, &tmpsts);
    225        if (U_SUCCESS(tmpsts)) {
    226            tmpLocID[len] = 0;
    227            fLocale = Locale((const char*)tmpLocID);
    228        }
    229 
    230        const wchar_t *localeName = nullptr;
    231 
    232        if (fWindowsLocaleName != nullptr)
    233        {
    234            localeName = reinterpret_cast<const wchar_t*>(toOldUCharPtr(fWindowsLocaleName->getTerminatedBuffer()));
    235        }
    236 
    237        fFormatInfo = (FormatInfo*)uprv_malloc(sizeof(FormatInfo));
    238 
    239        if (fCurrency) {
    240            getCurrencyFormat(&fFormatInfo->currency, localeName);
    241        } else {
    242            getNumberFormat(&fFormatInfo->number, localeName);
    243        }
    244    }
    245 }
    246 
    247 Win32NumberFormat::Win32NumberFormat(const Win32NumberFormat &other)
    248  : NumberFormat(other), fFormatInfo((FormatInfo*)uprv_malloc(sizeof(FormatInfo)))
    249 {
    250    if (fFormatInfo != nullptr) {
    251        uprv_memset(fFormatInfo, 0, sizeof(*fFormatInfo));
    252    }
    253    *this = other;
    254 }
    255 
    256 Win32NumberFormat::~Win32NumberFormat()
    257 {
    258    if (fFormatInfo != nullptr) {
    259        if (fCurrency) {
    260            freeCurrencyFormat(&fFormatInfo->currency);
    261        } else {
    262            freeNumberFormat(&fFormatInfo->number);
    263        }
    264 
    265        uprv_free(fFormatInfo);
    266    }
    267    delete fWindowsLocaleName;
    268 }
    269 
    270 Win32NumberFormat &Win32NumberFormat::operator=(const Win32NumberFormat &other)
    271 {
    272    if (this == &other) { return *this; }  // self-assignment: no-op
    273    NumberFormat::operator=(other);
    274 
    275    this->fCurrency          = other.fCurrency;
    276    this->fLocale            = other.fLocale;
    277    this->fLCID              = other.fLCID;
    278    this->fFractionDigitsSet = other.fFractionDigitsSet;
    279    this->fWindowsLocaleName = other.fWindowsLocaleName == nullptr ? nullptr : new UnicodeString(*other.fWindowsLocaleName);
    280    
    281    const wchar_t *localeName = nullptr;
    282 
    283    if (fWindowsLocaleName != nullptr)
    284    {
    285        localeName = reinterpret_cast<const wchar_t*>(toOldUCharPtr(fWindowsLocaleName->getTerminatedBuffer()));
    286    }
    287 
    288    if (fCurrency) {
    289        freeCurrencyFormat(&fFormatInfo->currency);
    290        getCurrencyFormat(&fFormatInfo->currency, localeName);
    291    } else {
    292        freeNumberFormat(&fFormatInfo->number);
    293        getNumberFormat(&fFormatInfo->number, localeName);
    294    }
    295 
    296    return *this;
    297 }
    298 
    299 Win32NumberFormat *Win32NumberFormat::clone() const
    300 {
    301    return new Win32NumberFormat(*this);
    302 }
    303 
    304 UnicodeString& Win32NumberFormat::format(double number, UnicodeString& appendTo, FieldPosition& /* pos */) const
    305 {
    306    return format(getMaximumFractionDigits(), appendTo, L"%.16f", number);
    307 }
    308 
    309 UnicodeString& Win32NumberFormat::format(int32_t number, UnicodeString& appendTo, FieldPosition& /* pos */) const
    310 {
    311    return format(getMinimumFractionDigits(), appendTo, L"%I32d", number);
    312 }
    313 
    314 UnicodeString& Win32NumberFormat::format(int64_t number, UnicodeString& appendTo, FieldPosition& /* pos */) const
    315 {
    316    return format(getMinimumFractionDigits(), appendTo, L"%I64d", number);
    317 }
    318 
    319 void Win32NumberFormat::parse(const UnicodeString& text, Formattable& result, ParsePosition& parsePosition) const
    320 {
    321    UErrorCode status = U_ZERO_ERROR;
    322    NumberFormat *nf = fCurrency? NumberFormat::createCurrencyInstance(fLocale, status) : NumberFormat::createInstance(fLocale, status);
    323 
    324    nf->parse(text, result, parsePosition);
    325    delete nf;
    326 }
    327 void Win32NumberFormat::setMaximumFractionDigits(int32_t newValue)
    328 {
    329    fFractionDigitsSet = true;
    330    NumberFormat::setMaximumFractionDigits(newValue);
    331 }
    332 
    333 void Win32NumberFormat::setMinimumFractionDigits(int32_t newValue)
    334 {
    335    fFractionDigitsSet = true;
    336    NumberFormat::setMinimumFractionDigits(newValue);
    337 }
    338 
    339 UnicodeString &Win32NumberFormat::format(int32_t numDigits, UnicodeString &appendTo, const wchar_t *fmt, ...) const
    340 {
    341    wchar_t nStackBuffer[STACK_BUFFER_SIZE];
    342    wchar_t *nBuffer = nStackBuffer;
    343    va_list args;
    344    int result;
    345 
    346    nBuffer[0] = 0x0000;
    347 
    348    /* Due to the arguments causing a result to be <= 23 characters (+2 for nullptr and minus),
    349    we don't need to reallocate the buffer. */
    350    va_start(args, fmt);
    351    result = _vsnwprintf(nBuffer, STACK_BUFFER_SIZE, fmt, args);
    352    va_end(args);
    353 
    354    /* Just to make sure of the above statement, we add this assert */
    355    U_ASSERT(result >=0);
    356    // The following code is not used because _vscwprintf isn't available on MinGW at the moment.
    357    /*if (result < 0) {
    358        int newLength;
    359 
    360        va_start(args, fmt);
    361        newLength = _vscwprintf(fmt, args);
    362        va_end(args);
    363 
    364        nBuffer = NEW_ARRAY(char16_t, newLength + 1);
    365 
    366        va_start(args, fmt);
    367        result = _vsnwprintf(nBuffer, newLength + 1, fmt, args);
    368        va_end(args);
    369    }*/
    370 
    371    // vswprintf is sensitive to the locale set by setlocale. For some locales
    372    // it doesn't use "." as the decimal separator, which is what GetNumberFormatW
    373    // and GetCurrencyFormatW both expect to see.
    374    //
    375    // To fix this, we scan over the string and replace the first non-digits, except
    376    // for a leading "-", with a "."
    377    //
    378    // Note: (nBuffer[0] == L'-') will evaluate to 1 if there is a leading '-' in the
    379    // number, and 0 otherwise.
    380    for (wchar_t *p = &nBuffer[nBuffer[0] == L'-']; *p != L'\0'; p += 1) {
    381        if (*p < L'0' || *p > L'9') {
    382            *p = L'.';
    383            break;
    384        }
    385    }
    386 
    387    wchar_t stackBuffer[STACK_BUFFER_SIZE];
    388    wchar_t *buffer = stackBuffer;
    389    FormatInfo formatInfo;
    390 
    391    formatInfo = *fFormatInfo;
    392    buffer[0] = 0x0000;
    393 
    394    const wchar_t *localeName = nullptr;
    395 
    396    if (fWindowsLocaleName != nullptr)
    397    {
    398        localeName = reinterpret_cast<const wchar_t*>(toOldUCharPtr(fWindowsLocaleName->getTerminatedBuffer()));
    399    }
    400 
    401    if (fCurrency) {
    402        if (fFractionDigitsSet) {
    403            formatInfo.currency.NumDigits = (UINT) numDigits;
    404        }
    405 
    406        if (!isGroupingUsed()) {
    407            formatInfo.currency.Grouping = 0;
    408        }
    409 
    410        result = GetCurrencyFormatEx(localeName, 0, nBuffer, &formatInfo.currency, buffer, STACK_BUFFER_SIZE);
    411 
    412        if (result == 0) {
    413            DWORD lastError = GetLastError();
    414 
    415            if (lastError == ERROR_INSUFFICIENT_BUFFER) {
    416                int newLength = GetCurrencyFormatEx(localeName, 0, nBuffer, &formatInfo.currency, nullptr, 0);
    417 
    418                buffer = NEW_ARRAY(wchar_t, newLength);
    419                buffer[0] = 0x0000;
    420                GetCurrencyFormatEx(localeName, 0, nBuffer,  &formatInfo.currency, buffer, newLength);
    421            }
    422        }
    423    } else {
    424        if (fFractionDigitsSet) {
    425            formatInfo.number.NumDigits = (UINT) numDigits;
    426        }
    427 
    428        if (!isGroupingUsed()) {
    429            formatInfo.number.Grouping = 0;
    430        }
    431 
    432        result = GetNumberFormatEx(localeName, 0, nBuffer, &formatInfo.number, buffer, STACK_BUFFER_SIZE);
    433 
    434        if (result == 0) {
    435            if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
    436                int newLength = GetNumberFormatEx(localeName, 0, nBuffer, &formatInfo.number, nullptr, 0);
    437 
    438                buffer = NEW_ARRAY(wchar_t, newLength);
    439                buffer[0] = 0x0000;
    440                GetNumberFormatEx(localeName, 0, nBuffer, &formatInfo.number, buffer, newLength);
    441            }
    442        }
    443    }
    444 
    445    appendTo.append((char16_t *)buffer, (int32_t) wcslen(buffer));
    446 
    447    if (buffer != stackBuffer) {
    448        DELETE_ARRAY(buffer);
    449    }
    450 
    451    /*if (nBuffer != nStackBuffer) {
    452        DELETE_ARRAY(nBuffer);
    453    }*/
    454 
    455    return appendTo;
    456 }
    457 
    458 U_NAMESPACE_END
    459 
    460 #endif /* #if !UCONFIG_NO_FORMATTING */
    461 
    462 #endif // U_PLATFORM_USES_ONLY_WIN32_API