1/*
2 * Copyright (c) 2007 2008
3 * Francois Dumont
4 *
5 * This material is provided "as is", with absolutely no warranty expressed
6 * or implied. Any use is at your own risk.
7 *
8 * Permission to use or copy this software for any purpose is hereby granted
9 * without fee, provided the above notices are retained on all copies.
10 * Permission to modify the code and to distribute modified code is granted,
11 * provided the above notices are retained, and a notice that the code was
12 * modified is included with the above copyright notice.
13 *
14 */
15
16#if defined (_STLP_USE_SAFE_STRING_FUNCTIONS)
17#  define _STLP_WCSNCPY(D, DS, S, C) wcsncpy_s(D, DS, S, C)
18#else
19#  define _STLP_WCSNCPY(D, DS, S, C) wcsncpy(D, S, C)
20#endif
21
22static const wchar_t* __wtrue_name = L"true";
23static const wchar_t* __wfalse_name = L"false";
24
25typedef struct _Locale_codecvt {
26  _Locale_lcid_t lc;
27  UINT cp;
28  unsigned char cleads[256 / CHAR_BIT];
29  unsigned char max_char_size;
30  DWORD mbtowc_flags;
31  DWORD wctomb_flags;
32} _Locale_codecvt_t;
33
34/* Ctype */
35_Locale_mask_t _WLocale_ctype(_Locale_ctype_t* ltype, wint_t c,
36                              _Locale_mask_t which_bits) {
37  wchar_t buf[2];
38  WORD out[2];
39  buf[0] = c; buf[1] = 0;
40  GetStringTypeW(CT_CTYPE1, buf, -1, out);
41  _STLP_MARK_PARAMETER_AS_UNUSED(ltype)
42  return (_Locale_mask_t)(MapCtypeMask(out[0]) & which_bits);
43}
44
45wint_t _WLocale_tolower(_Locale_ctype_t* ltype, wint_t c) {
46  wchar_t in_c = c;
47  wchar_t res;
48
49  LCMapStringW(ltype->lc.id, LCMAP_LOWERCASE, &in_c, 1, &res, 1);
50  return res;
51}
52
53wint_t _WLocale_toupper(_Locale_ctype_t* ltype, wint_t c) {
54  wchar_t in_c = c;
55  wchar_t res;
56
57  LCMapStringW(ltype->lc.id, LCMAP_UPPERCASE, &in_c, 1, &res, 1);
58  return res;
59}
60
61_Locale_codecvt_t* _Locale_codecvt_create(const char * name, _Locale_lcid_t* lc_hint, int *__err_code) {
62  char cp_name[MAX_CP_LEN + 1];
63  unsigned char *ptr;
64  CPINFO CPInfo;
65  int i;
66
67  _Locale_codecvt_t *lcodecvt = (_Locale_codecvt_t*)malloc(sizeof(_Locale_codecvt_t));
68
69  if (!lcodecvt) { *__err_code = _STLP_LOC_NO_MEMORY; return lcodecvt; }
70  memset(lcodecvt, 0, sizeof(_Locale_codecvt_t));
71
72  if (__GetLCIDFromName(name, &lcodecvt->lc.id, cp_name, lc_hint) == -1)
73  { free(lcodecvt); *__err_code = _STLP_LOC_UNKNOWN_NAME; return NULL; }
74
75  lcodecvt->cp = atoi(cp_name);
76  if (!GetCPInfo(lcodecvt->cp, &CPInfo)) { free(lcodecvt); return NULL; }
77
78  if (lcodecvt->cp != CP_UTF7 && lcodecvt->cp != CP_UTF8) {
79    lcodecvt->mbtowc_flags = MB_PRECOMPOSED;
80    lcodecvt->wctomb_flags = WC_COMPOSITECHECK | WC_SEPCHARS;
81  }
82  lcodecvt->max_char_size = CPInfo.MaxCharSize;
83
84  if (CPInfo.MaxCharSize > 1) {
85    for (ptr = (unsigned char*)CPInfo.LeadByte; *ptr && *(ptr + 1); ptr += 2)
86      for (i = *ptr; i <= *(ptr + 1); ++i) lcodecvt->cleads[i / CHAR_BIT] |= (0x01 << i % CHAR_BIT);
87  }
88
89  return lcodecvt;
90}
91
92char const* _Locale_codecvt_name(const _Locale_codecvt_t* lcodecvt, char* buf) {
93  char cp_buf[MAX_CP_LEN + 1];
94  my_ltoa(lcodecvt->cp, cp_buf);
95  return __GetLocaleName(lcodecvt->lc.id, cp_buf, buf);
96}
97
98void _Locale_codecvt_destroy(_Locale_codecvt_t* lcodecvt) {
99  if (!lcodecvt) return;
100
101  free(lcodecvt);
102}
103
104int _WLocale_mb_cur_max (_Locale_codecvt_t * lcodecvt)
105{ return lcodecvt->max_char_size; }
106
107int _WLocale_mb_cur_min (_Locale_codecvt_t *lcodecvt) {
108  _STLP_MARK_PARAMETER_AS_UNUSED(lcodecvt)
109  return 1;
110}
111
112int _WLocale_is_stateless (_Locale_codecvt_t * lcodecvt)
113{ return (lcodecvt->max_char_size == 1) ? 1 : 0; }
114
115static int __isleadbyte(int i, unsigned char *ctable) {
116  unsigned char c = (unsigned char)i;
117  return (ctable[c / CHAR_BIT] & (0x01 << c % CHAR_BIT));
118}
119
120static int __mbtowc(_Locale_codecvt_t *l, wchar_t *dst, const char *from, unsigned int count) {
121  int result;
122
123  if (l->cp == CP_UTF7 || l->cp == CP_UTF8) {
124    result = MultiByteToWideChar(l->cp, l->mbtowc_flags, from, count, dst, 1);
125    if (result == 0) {
126      switch (GetLastError()) {
127        case ERROR_NO_UNICODE_TRANSLATION:
128          return -2;
129        default:
130          return -1;
131      }
132    }
133  }
134  else {
135    if (count == 1 && __isleadbyte(*from, l->cleads)) return (size_t)-2;
136    result = MultiByteToWideChar(l->cp, l->mbtowc_flags, from, count, dst, 1);
137    if (result == 0) return -1;
138  }
139
140  return result;
141}
142
143size_t _WLocale_mbtowc(_Locale_codecvt_t *lcodecvt, wchar_t *to,
144                       const char *from, size_t n, mbstate_t *shift_state) {
145  int result;
146  _STLP_MARK_PARAMETER_AS_UNUSED(shift_state)
147  if (lcodecvt->max_char_size == 1) { /* Single byte encoding. */
148    result = MultiByteToWideChar(lcodecvt->cp, lcodecvt->mbtowc_flags, from, 1, to, 1);
149    if (result == 0) return (size_t)-1;
150    return result;
151  }
152  else { /* Multi byte encoding. */
153    int retval;
154    unsigned int count = 1;
155    while (n--) {
156      retval = __mbtowc(lcodecvt, to, from, count);
157      if (retval == -2)
158      { if (++count > ((unsigned int)lcodecvt->max_char_size)) return (size_t)-1; }
159      else if (retval == -1)
160      { return (size_t)-1; }
161      else
162      { return count; }
163    }
164    return (size_t)-2;
165  }
166}
167
168size_t _WLocale_wctomb(_Locale_codecvt_t *lcodecvt, char *to, size_t n,
169                       const wchar_t c, mbstate_t *shift_state) {
170  int size = WideCharToMultiByte(lcodecvt->cp, lcodecvt->wctomb_flags, &c, 1, NULL, 0, NULL, NULL);
171
172  if (!size) return (size_t)-1;
173  if ((size_t)size > n) return (size_t)-2;
174
175  if (n > INT_MAX)
176    /* Limiting the output buf size to INT_MAX seems like reasonable to transform a single wchar_t. */
177    n = INT_MAX;
178
179  WideCharToMultiByte(lcodecvt->cp,  lcodecvt->wctomb_flags, &c, 1, to, (int)n, NULL, NULL);
180
181  _STLP_MARK_PARAMETER_AS_UNUSED(shift_state)
182  return (size_t)size;
183}
184
185size_t _WLocale_unshift(_Locale_codecvt_t *lcodecvt, mbstate_t *st,
186                        char *buf, size_t n, char **next) {
187  /* _WLocale_wctomb do not even touch to st, there is nothing to unshift in this localization implementation. */
188  _STLP_MARK_PARAMETER_AS_UNUSED(lcodecvt)
189  _STLP_MARK_PARAMETER_AS_UNUSED(st)
190  _STLP_MARK_PARAMETER_AS_UNUSED(&n)
191  *next = buf;
192  return 0;
193}
194
195/* Collate */
196/* This function takes care of the potential size_t DWORD different size. */
197static int _WLocale_strcmp_aux(_Locale_collate_t* lcol,
198                               const wchar_t* s1, size_t n1,
199                               const wchar_t* s2, size_t n2) {
200  int result = CSTR_EQUAL;
201  while (n1 > 0 || n2 > 0) {
202    DWORD size1 = trim_size_t_to_DWORD(n1);
203    DWORD size2 = trim_size_t_to_DWORD(n2);
204    result = CompareStringW(lcol->lc.id, 0, s1, size1, s2, size2);
205    if (result != CSTR_EQUAL)
206      break;
207    n1 -= size1;
208    n2 -= size2;
209  }
210  return result;
211}
212
213int _WLocale_strcmp(_Locale_collate_t* lcol,
214                    const wchar_t* s1, size_t n1,
215                    const wchar_t* s2, size_t n2) {
216  int result;
217  result = _WLocale_strcmp_aux(lcol, s1, n1, s2, n2);
218  return (result == CSTR_EQUAL) ? 0 : (result == CSTR_LESS_THAN) ? -1 : 1;
219}
220
221size_t _WLocale_strxfrm(_Locale_collate_t* lcol,
222                        wchar_t* dst, size_t dst_size,
223                        const wchar_t* src, size_t src_size) {
224  int result, i;
225
226  /* see _Locale_strxfrm: */
227  if (src_size > INT_MAX) {
228    if (dst != 0) {
229      _STLP_WCSNCPY(dst, dst_size, src, src_size);
230    }
231    return src_size;
232  }
233  if (dst_size > INT_MAX) {
234    dst_size = INT_MAX;
235  }
236  result = LCMapStringW(lcol->lc.id, LCMAP_SORTKEY, src, (int)src_size, dst, (int)dst_size);
237  if (result != 0 && dst != 0) {
238    for (i = result - 1; i >= 0; --i) {
239      dst[i] = ((unsigned char*)dst)[i];
240    }
241  }
242  return result != 0 ? result - 1 : 0;
243}
244
245/* Numeric */
246wchar_t _WLocale_decimal_point(_Locale_numeric_t* lnum) {
247  wchar_t buf[4];
248  GetLocaleInfoW(lnum->lc.id, LOCALE_SDECIMAL, buf, 4);
249  return buf[0];
250}
251
252wchar_t _WLocale_thousands_sep(_Locale_numeric_t* lnum) {
253  wchar_t buf[4];
254  GetLocaleInfoW(lnum->lc.id, LOCALE_STHOUSAND, buf, 4);
255  return buf[0];
256}
257
258const wchar_t * _WLocale_true(_Locale_numeric_t* lnum, wchar_t* buf, size_t bufSize) {
259  _STLP_MARK_PARAMETER_AS_UNUSED(lnum)
260  _STLP_MARK_PARAMETER_AS_UNUSED(buf)
261  _STLP_MARK_PARAMETER_AS_UNUSED(&bufSize)
262  return __wtrue_name;
263}
264
265const wchar_t * _WLocale_false(_Locale_numeric_t* lnum, wchar_t* buf, size_t bufSize) {
266  _STLP_MARK_PARAMETER_AS_UNUSED(lnum)
267  _STLP_MARK_PARAMETER_AS_UNUSED(buf)
268  _STLP_MARK_PARAMETER_AS_UNUSED(&bufSize)
269  return __wfalse_name;
270}
271
272/* Monetary */
273const wchar_t* _WLocale_int_curr_symbol(_Locale_monetary_t * lmon, wchar_t* buf, size_t bufSize)
274{ GetLocaleInfoW(lmon->lc.id, LOCALE_SINTLSYMBOL, buf, (int)bufSize); return buf; }
275
276const wchar_t* _WLocale_currency_symbol(_Locale_monetary_t * lmon, wchar_t* buf, size_t bufSize)
277{ GetLocaleInfoW(lmon->lc.id, LOCALE_SCURRENCY, buf, (int)bufSize); return buf; }
278
279wchar_t _WLocale_mon_decimal_point(_Locale_monetary_t * lmon)
280{ return lmon->decimal_point[0]; }
281
282wchar_t _WLocale_mon_thousands_sep(_Locale_monetary_t * lmon)
283{ return lmon->thousands_sep[0]; }
284
285const wchar_t* _WLocale_positive_sign(_Locale_monetary_t * lmon, wchar_t* buf, size_t bufSize)
286{ GetLocaleInfoW(lmon->lc.id, LOCALE_SPOSITIVESIGN, buf, (int)bufSize); return buf; }
287
288const wchar_t* _WLocale_negative_sign(_Locale_monetary_t * lmon, wchar_t* buf, size_t bufSize)
289{ GetLocaleInfoW(lmon->lc.id, LOCALE_SNEGATIVESIGN, buf, (int)bufSize); return buf; }
290
291/* Time */
292const wchar_t * _WLocale_full_monthname(_Locale_time_t * ltime, int month,
293                                        wchar_t* buf, size_t bufSize)
294{ GetLocaleInfoW(ltime->lc.id, LOCALE_SMONTHNAME1 + month, buf, (int)bufSize); return buf; }
295
296const wchar_t * _WLocale_abbrev_monthname(_Locale_time_t * ltime, int month,
297                                          wchar_t* buf, size_t bufSize)
298{ GetLocaleInfoW(ltime->lc.id, LOCALE_SABBREVMONTHNAME1 + month, buf, (int)bufSize); return buf; }
299
300const wchar_t * _WLocale_full_dayofweek(_Locale_time_t * ltime, int day,
301                                        wchar_t* buf, size_t bufSize)
302{ GetLocaleInfoW(ltime->lc.id, LOCALE_SDAYNAME1 + day, buf, (int)bufSize); return buf; }
303
304const wchar_t * _WLocale_abbrev_dayofweek(_Locale_time_t * ltime, int day,
305                                          wchar_t* buf, size_t bufSize)
306{ GetLocaleInfoW(ltime->lc.id, LOCALE_SABBREVDAYNAME1 + day, buf, (int)bufSize); return buf; }
307
308const wchar_t* _WLocale_am_str(_Locale_time_t* ltime,
309                               wchar_t* buf, size_t bufSize)
310{ GetLocaleInfoW(ltime->lc.id, LOCALE_S1159, buf, (int)bufSize); return buf; }
311
312const wchar_t* _WLocale_pm_str(_Locale_time_t* ltime,
313                               wchar_t* buf, size_t bufSize)
314{ GetLocaleInfoW(ltime->lc.id, LOCALE_S2359, buf, (int)bufSize); return buf; }
315