blob: a12659b685dfb8a90e2d7c2336b92ae1b9fd733b [file] [log] [blame]
/*
* Copyright (c) 2007 2008
* Francois Dumont
*
* This material is provided "as is", with absolutely no warranty expressed
* or implied. Any use is at your own risk.
*
* Permission to use or copy this software for any purpose is hereby granted
* without fee, provided the above notices are retained on all copies.
* Permission to modify the code and to distribute modified code is granted,
* provided the above notices are retained, and a notice that the code was
* modified is included with the above copyright notice.
*
*/
#if defined (_STLP_USE_SAFE_STRING_FUNCTIONS)
# define _STLP_WCSNCPY(D, DS, S, C) wcsncpy_s(D, DS, S, C)
#else
# define _STLP_WCSNCPY(D, DS, S, C) wcsncpy(D, S, C)
#endif
static const wchar_t* __wtrue_name = L"true";
static const wchar_t* __wfalse_name = L"false";
typedef struct _Locale_codecvt {
_Locale_lcid_t lc;
UINT cp;
unsigned char cleads[256 / CHAR_BIT];
unsigned char max_char_size;
DWORD mbtowc_flags;
DWORD wctomb_flags;
} _Locale_codecvt_t;
/* Ctype */
_Locale_mask_t _WLocale_ctype(_Locale_ctype_t* ltype, wint_t c,
_Locale_mask_t which_bits) {
wchar_t buf[2];
WORD out[2];
buf[0] = c; buf[1] = 0;
GetStringTypeW(CT_CTYPE1, buf, -1, out);
_STLP_MARK_PARAMETER_AS_UNUSED(ltype)
return (_Locale_mask_t)(MapCtypeMask(out[0]) & which_bits);
}
wint_t _WLocale_tolower(_Locale_ctype_t* ltype, wint_t c) {
wchar_t in_c = c;
wchar_t res;
LCMapStringW(ltype->lc.id, LCMAP_LOWERCASE, &in_c, 1, &res, 1);
return res;
}
wint_t _WLocale_toupper(_Locale_ctype_t* ltype, wint_t c) {
wchar_t in_c = c;
wchar_t res;
LCMapStringW(ltype->lc.id, LCMAP_UPPERCASE, &in_c, 1, &res, 1);
return res;
}
_Locale_codecvt_t* _Locale_codecvt_create(const char * name, _Locale_lcid_t* lc_hint, int *__err_code) {
char cp_name[MAX_CP_LEN + 1];
unsigned char *ptr;
CPINFO CPInfo;
int i;
_Locale_codecvt_t *lcodecvt = (_Locale_codecvt_t*)malloc(sizeof(_Locale_codecvt_t));
if (!lcodecvt) { *__err_code = _STLP_LOC_NO_MEMORY; return lcodecvt; }
memset(lcodecvt, 0, sizeof(_Locale_codecvt_t));
if (__GetLCIDFromName(name, &lcodecvt->lc.id, cp_name, lc_hint) == -1)
{ free(lcodecvt); *__err_code = _STLP_LOC_UNKNOWN_NAME; return NULL; }
lcodecvt->cp = atoi(cp_name);
if (!GetCPInfo(lcodecvt->cp, &CPInfo)) { free(lcodecvt); return NULL; }
if (lcodecvt->cp != CP_UTF7 && lcodecvt->cp != CP_UTF8) {
lcodecvt->mbtowc_flags = MB_PRECOMPOSED;
lcodecvt->wctomb_flags = WC_COMPOSITECHECK | WC_SEPCHARS;
}
lcodecvt->max_char_size = CPInfo.MaxCharSize;
if (CPInfo.MaxCharSize > 1) {
for (ptr = (unsigned char*)CPInfo.LeadByte; *ptr && *(ptr + 1); ptr += 2)
for (i = *ptr; i <= *(ptr + 1); ++i) lcodecvt->cleads[i / CHAR_BIT] |= (0x01 << i % CHAR_BIT);
}
return lcodecvt;
}
char const* _Locale_codecvt_name(const _Locale_codecvt_t* lcodecvt, char* buf) {
char cp_buf[MAX_CP_LEN + 1];
my_ltoa(lcodecvt->cp, cp_buf);
return __GetLocaleName(lcodecvt->lc.id, cp_buf, buf);
}
void _Locale_codecvt_destroy(_Locale_codecvt_t* lcodecvt) {
if (!lcodecvt) return;
free(lcodecvt);
}
int _WLocale_mb_cur_max (_Locale_codecvt_t * lcodecvt)
{ return lcodecvt->max_char_size; }
int _WLocale_mb_cur_min (_Locale_codecvt_t *lcodecvt) {
_STLP_MARK_PARAMETER_AS_UNUSED(lcodecvt)
return 1;
}
int _WLocale_is_stateless (_Locale_codecvt_t * lcodecvt)
{ return (lcodecvt->max_char_size == 1) ? 1 : 0; }
static int __isleadbyte(int i, unsigned char *ctable) {
unsigned char c = (unsigned char)i;
return (ctable[c / CHAR_BIT] & (0x01 << c % CHAR_BIT));
}
static int __mbtowc(_Locale_codecvt_t *l, wchar_t *dst, const char *from, unsigned int count) {
int result;
if (l->cp == CP_UTF7 || l->cp == CP_UTF8) {
result = MultiByteToWideChar(l->cp, l->mbtowc_flags, from, count, dst, 1);
if (result == 0) {
switch (GetLastError()) {
case ERROR_NO_UNICODE_TRANSLATION:
return -2;
default:
return -1;
}
}
}
else {
if (count == 1 && __isleadbyte(*from, l->cleads)) return (size_t)-2;
result = MultiByteToWideChar(l->cp, l->mbtowc_flags, from, count, dst, 1);
if (result == 0) return -1;
}
return result;
}
size_t _WLocale_mbtowc(_Locale_codecvt_t *lcodecvt, wchar_t *to,
const char *from, size_t n, mbstate_t *shift_state) {
int result;
_STLP_MARK_PARAMETER_AS_UNUSED(shift_state)
if (lcodecvt->max_char_size == 1) { /* Single byte encoding. */
result = MultiByteToWideChar(lcodecvt->cp, lcodecvt->mbtowc_flags, from, 1, to, 1);
if (result == 0) return (size_t)-1;
return result;
}
else { /* Multi byte encoding. */
int retval;
unsigned int count = 1;
while (n--) {
retval = __mbtowc(lcodecvt, to, from, count);
if (retval == -2)
{ if (++count > ((unsigned int)lcodecvt->max_char_size)) return (size_t)-1; }
else if (retval == -1)
{ return (size_t)-1; }
else
{ return count; }
}
return (size_t)-2;
}
}
size_t _WLocale_wctomb(_Locale_codecvt_t *lcodecvt, char *to, size_t n,
const wchar_t c, mbstate_t *shift_state) {
int size = WideCharToMultiByte(lcodecvt->cp, lcodecvt->wctomb_flags, &c, 1, NULL, 0, NULL, NULL);
if (!size) return (size_t)-1;
if ((size_t)size > n) return (size_t)-2;
if (n > INT_MAX)
/* Limiting the output buf size to INT_MAX seems like reasonable to transform a single wchar_t. */
n = INT_MAX;
WideCharToMultiByte(lcodecvt->cp, lcodecvt->wctomb_flags, &c, 1, to, (int)n, NULL, NULL);
_STLP_MARK_PARAMETER_AS_UNUSED(shift_state)
return (size_t)size;
}
size_t _WLocale_unshift(_Locale_codecvt_t *lcodecvt, mbstate_t *st,
char *buf, size_t n, char **next) {
/* _WLocale_wctomb do not even touch to st, there is nothing to unshift in this localization implementation. */
_STLP_MARK_PARAMETER_AS_UNUSED(lcodecvt)
_STLP_MARK_PARAMETER_AS_UNUSED(st)
_STLP_MARK_PARAMETER_AS_UNUSED(&n)
*next = buf;
return 0;
}
/* Collate */
/* This function takes care of the potential size_t DWORD different size. */
static int _WLocale_strcmp_aux(_Locale_collate_t* lcol,
const wchar_t* s1, size_t n1,
const wchar_t* s2, size_t n2) {
int result = CSTR_EQUAL;
while (n1 > 0 || n2 > 0) {
DWORD size1 = trim_size_t_to_DWORD(n1);
DWORD size2 = trim_size_t_to_DWORD(n2);
result = CompareStringW(lcol->lc.id, 0, s1, size1, s2, size2);
if (result != CSTR_EQUAL)
break;
n1 -= size1;
n2 -= size2;
}
return result;
}
int _WLocale_strcmp(_Locale_collate_t* lcol,
const wchar_t* s1, size_t n1,
const wchar_t* s2, size_t n2) {
int result;
result = _WLocale_strcmp_aux(lcol, s1, n1, s2, n2);
return (result == CSTR_EQUAL) ? 0 : (result == CSTR_LESS_THAN) ? -1 : 1;
}
size_t _WLocale_strxfrm(_Locale_collate_t* lcol,
wchar_t* dst, size_t dst_size,
const wchar_t* src, size_t src_size) {
int result, i;
/* see _Locale_strxfrm: */
if (src_size > INT_MAX) {
if (dst != 0) {
_STLP_WCSNCPY(dst, dst_size, src, src_size);
}
return src_size;
}
if (dst_size > INT_MAX) {
dst_size = INT_MAX;
}
result = LCMapStringW(lcol->lc.id, LCMAP_SORTKEY, src, (int)src_size, dst, (int)dst_size);
if (result != 0 && dst != 0) {
for (i = result - 1; i >= 0; --i) {
dst[i] = ((unsigned char*)dst)[i];
}
}
return result != 0 ? result - 1 : 0;
}
/* Numeric */
wchar_t _WLocale_decimal_point(_Locale_numeric_t* lnum) {
wchar_t buf[4];
GetLocaleInfoW(lnum->lc.id, LOCALE_SDECIMAL, buf, 4);
return buf[0];
}
wchar_t _WLocale_thousands_sep(_Locale_numeric_t* lnum) {
wchar_t buf[4];
GetLocaleInfoW(lnum->lc.id, LOCALE_STHOUSAND, buf, 4);
return buf[0];
}
const wchar_t * _WLocale_true(_Locale_numeric_t* lnum, wchar_t* buf, size_t bufSize) {
_STLP_MARK_PARAMETER_AS_UNUSED(lnum)
_STLP_MARK_PARAMETER_AS_UNUSED(buf)
_STLP_MARK_PARAMETER_AS_UNUSED(&bufSize)
return __wtrue_name;
}
const wchar_t * _WLocale_false(_Locale_numeric_t* lnum, wchar_t* buf, size_t bufSize) {
_STLP_MARK_PARAMETER_AS_UNUSED(lnum)
_STLP_MARK_PARAMETER_AS_UNUSED(buf)
_STLP_MARK_PARAMETER_AS_UNUSED(&bufSize)
return __wfalse_name;
}
/* Monetary */
const wchar_t* _WLocale_int_curr_symbol(_Locale_monetary_t * lmon, wchar_t* buf, size_t bufSize)
{ GetLocaleInfoW(lmon->lc.id, LOCALE_SINTLSYMBOL, buf, (int)bufSize); return buf; }
const wchar_t* _WLocale_currency_symbol(_Locale_monetary_t * lmon, wchar_t* buf, size_t bufSize)
{ GetLocaleInfoW(lmon->lc.id, LOCALE_SCURRENCY, buf, (int)bufSize); return buf; }
wchar_t _WLocale_mon_decimal_point(_Locale_monetary_t * lmon)
{ return lmon->decimal_point[0]; }
wchar_t _WLocale_mon_thousands_sep(_Locale_monetary_t * lmon)
{ return lmon->thousands_sep[0]; }
const wchar_t* _WLocale_positive_sign(_Locale_monetary_t * lmon, wchar_t* buf, size_t bufSize)
{ GetLocaleInfoW(lmon->lc.id, LOCALE_SPOSITIVESIGN, buf, (int)bufSize); return buf; }
const wchar_t* _WLocale_negative_sign(_Locale_monetary_t * lmon, wchar_t* buf, size_t bufSize)
{ GetLocaleInfoW(lmon->lc.id, LOCALE_SNEGATIVESIGN, buf, (int)bufSize); return buf; }
/* Time */
const wchar_t * _WLocale_full_monthname(_Locale_time_t * ltime, int month,
wchar_t* buf, size_t bufSize)
{ GetLocaleInfoW(ltime->lc.id, LOCALE_SMONTHNAME1 + month, buf, (int)bufSize); return buf; }
const wchar_t * _WLocale_abbrev_monthname(_Locale_time_t * ltime, int month,
wchar_t* buf, size_t bufSize)
{ GetLocaleInfoW(ltime->lc.id, LOCALE_SABBREVMONTHNAME1 + month, buf, (int)bufSize); return buf; }
const wchar_t * _WLocale_full_dayofweek(_Locale_time_t * ltime, int day,
wchar_t* buf, size_t bufSize)
{ GetLocaleInfoW(ltime->lc.id, LOCALE_SDAYNAME1 + day, buf, (int)bufSize); return buf; }
const wchar_t * _WLocale_abbrev_dayofweek(_Locale_time_t * ltime, int day,
wchar_t* buf, size_t bufSize)
{ GetLocaleInfoW(ltime->lc.id, LOCALE_SABBREVDAYNAME1 + day, buf, (int)bufSize); return buf; }
const wchar_t* _WLocale_am_str(_Locale_time_t* ltime,
wchar_t* buf, size_t bufSize)
{ GetLocaleInfoW(ltime->lc.id, LOCALE_S1159, buf, (int)bufSize); return buf; }
const wchar_t* _WLocale_pm_str(_Locale_time_t* ltime,
wchar_t* buf, size_t bufSize)
{ GetLocaleInfoW(ltime->lc.id, LOCALE_S2359, buf, (int)bufSize); return buf; }