1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | http://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Anatol Belski <ab@php.net> |
14 +----------------------------------------------------------------------+
15 */
16
17 #include <assert.h>
18
19 #include "php.h"
20 #include "SAPI.h"
21 #include <emmintrin.h>
22
23 #include "win32/console.h"
24
25 ZEND_TLS const struct php_win32_cp *cur_cp = NULL;
26 ZEND_TLS const struct php_win32_cp *orig_cp = NULL;
27 ZEND_TLS const struct php_win32_cp *cur_out_cp = NULL;
28 ZEND_TLS const struct php_win32_cp *orig_out_cp = NULL;
29 ZEND_TLS const struct php_win32_cp *cur_in_cp = NULL;
30 ZEND_TLS const struct php_win32_cp *orig_in_cp = NULL;
31
32 #include "cp_enc_map.c"
33
php_win32_cp_to_w_int(const char * in,size_t in_len,size_t * out_len,UINT cp,DWORD flags)34 __forceinline static wchar_t *php_win32_cp_to_w_int(const char* in, size_t in_len, size_t *out_len, UINT cp, DWORD flags)
35 {/*{{{*/
36 wchar_t *ret;
37 int ret_len, tmp_len;
38
39 if (!in || in_len > (size_t)INT_MAX) {
40 SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
41 return NULL;
42 }
43 assert(in_len ? (in[in_len] == L'\0') : 1);
44
45 tmp_len = !in_len ? -1 : (int)in_len + 1;
46
47 ret_len = MultiByteToWideChar(cp, flags, in, tmp_len, NULL, 0);
48 if (ret_len == 0) {
49 SET_ERRNO_FROM_WIN32_CODE(GetLastError());
50 return NULL;
51 }
52
53 ret = malloc(ret_len * sizeof(wchar_t));
54 if (!ret) {
55 SET_ERRNO_FROM_WIN32_CODE(ERROR_OUTOFMEMORY);
56 return NULL;
57 }
58
59 tmp_len = MultiByteToWideChar(cp, flags, in, tmp_len, ret, ret_len);
60 if (tmp_len == 0) {
61 free(ret);
62 SET_ERRNO_FROM_WIN32_CODE(GetLastError());
63 return NULL;
64 }
65
66 assert(ret ? tmp_len == ret_len : 1);
67 assert(ret && !in_len ? wcslen(ret) == ret_len - 1 : 1);
68
69 ret[ret_len-1] = L'\0';
70
71 if (PHP_WIN32_CP_IGNORE_LEN_P != out_len) {
72 *out_len = ret_len - 1;
73 }
74
75 return ret;
76 }/*}}}*/
77
php_win32_cp_conv_utf8_to_w(const char * in,size_t in_len,size_t * out_len)78 PW32CP wchar_t *php_win32_cp_conv_utf8_to_w(const char* in, size_t in_len, size_t *out_len)
79 {/*{{{*/
80 return php_win32_cp_to_w_int(in, in_len, out_len, CP_UTF8, MB_ERR_INVALID_CHARS);
81 }/*}}}*/
82
php_win32_cp_conv_cur_to_w(const char * in,size_t in_len,size_t * out_len)83 PW32CP wchar_t *php_win32_cp_conv_cur_to_w(const char* in, size_t in_len, size_t *out_len)
84 {/*{{{*/
85 wchar_t *ret;
86
87 ret = php_win32_cp_to_w_int(in, in_len, out_len, cur_cp->id, cur_cp->from_w_fl);
88
89 return ret;
90 }/*}}}*/
91
php_win32_cp_conv_to_w(DWORD cp,DWORD flags,const char * in,size_t in_len,size_t * out_len)92 PW32CP wchar_t *php_win32_cp_conv_to_w(DWORD cp, DWORD flags, const char* in, size_t in_len, size_t *out_len)
93 {/*{{{*/
94 return php_win32_cp_to_w_int(in, in_len, out_len, cp, flags);
95 }/*}}}*/
96
97 #define ASCII_FAIL_RETURN() \
98 if (PHP_WIN32_CP_IGNORE_LEN_P != out_len) { \
99 *out_len = 0; \
100 } \
101 return NULL;
php_win32_cp_conv_ascii_to_w(const char * in,size_t in_len,size_t * out_len)102 PW32CP wchar_t *php_win32_cp_conv_ascii_to_w(const char* in, size_t in_len, size_t *out_len)
103 {/*{{{*/
104 wchar_t *ret, *ret_idx;
105 const char *idx = in, *end;
106 char ch_err = 0;
107
108 #if PHP_DEBUG
109 size_t save_in_len = in_len;
110 #endif
111
112 assert(in && in_len ? in[in_len] == '\0' : 1);
113
114 if (!in) {
115 SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
116 return NULL;
117 } else if (0 == in_len) {
118 /* Not binary safe. */
119 in_len = strlen(in);
120 }
121
122 end = in + in_len;
123
124 if (in_len > 15) {
125 const char *aidx = (const char *)ZEND_SLIDE_TO_ALIGNED16(in);
126
127 /* Process unaligned chunk. */
128 while (idx < aidx) {
129 ch_err |= *idx;
130 idx++;
131 }
132 if (ch_err & 0x80) {
133 ASCII_FAIL_RETURN()
134 }
135
136 /* Process aligned chunk. */
137 __m128i vec_err = _mm_setzero_si128();
138 while (end - idx > 15) {
139 const __m128i block = _mm_load_si128((__m128i *)idx);
140 vec_err = _mm_or_si128(vec_err, block);
141 idx += 16;
142 }
143 if (_mm_movemask_epi8(vec_err)) {
144 ASCII_FAIL_RETURN()
145 }
146 }
147
148 /* Process the trailing part, or otherwise process string < 16 bytes. */
149 while (idx < end) {
150 ch_err |= *idx;
151 idx++;
152 }
153 if (ch_err & 0x80) {
154 ASCII_FAIL_RETURN()
155 }
156
157 ret = malloc((in_len+1)*sizeof(wchar_t));
158 if (!ret) {
159 SET_ERRNO_FROM_WIN32_CODE(ERROR_OUTOFMEMORY);
160 return NULL;
161 }
162
163 ret_idx = ret;
164 idx = in;
165
166 if (in_len > 15) {
167 const char *aidx = (const char *)ZEND_SLIDE_TO_ALIGNED16(in);
168
169 /* Process unaligned chunk. */
170 while (idx < aidx) {
171 *ret_idx++ = (wchar_t)*idx++;
172 }
173
174 /* Process aligned chunk. */
175 if (end - idx > 15) {
176 const __m128i mask = _mm_set1_epi32(0);
177 while (end - idx > 15) {
178 const __m128i block = _mm_load_si128((__m128i *)idx);
179
180 const __m128i lo = _mm_unpacklo_epi8(block, mask);
181 _mm_storeu_si128((__m128i *)ret_idx, lo);
182
183 ret_idx += 8;
184 const __m128i hi = _mm_unpackhi_epi8(block, mask);
185 _mm_storeu_si128((__m128i *)ret_idx, hi);
186
187 idx += 16;
188 ret_idx += 8;
189 }
190 }
191 }
192
193 /* Process the trailing part, or otherwise process string < 16 bytes. */
194 while (idx < end) {
195 *ret_idx++ = (wchar_t)*idx++;
196 }
197
198 ret[in_len] = L'\0';
199
200 assert(ret && !save_in_len ? wcslen(ret) == in_len : 1);
201
202 if (PHP_WIN32_CP_IGNORE_LEN_P != out_len) {
203 *out_len = in_len;
204 }
205
206 return ret;
207 }/*}}}*/
208 #undef ASCII_FAIL_RETURN
209
php_win32_cp_from_w_int(const wchar_t * in,size_t in_len,size_t * out_len,UINT cp,DWORD flags)210 __forceinline static char *php_win32_cp_from_w_int(const wchar_t* in, size_t in_len, size_t *out_len, UINT cp, DWORD flags)
211 {/*{{{*/
212 int r;
213 int target_len, tmp_len;
214 char* target;
215
216 if (!in || in_len > INT_MAX) {
217 SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
218 return NULL;
219 }
220 assert(in_len ? in[in_len] == '\0' : 1);
221
222 tmp_len = !in_len ? -1 : (int)in_len + 1;
223
224 target_len = WideCharToMultiByte(cp, flags, in, tmp_len, NULL, 0, NULL, NULL);
225 if (target_len == 0) {
226 SET_ERRNO_FROM_WIN32_CODE(GetLastError());
227 return NULL;
228 }
229
230 target = malloc(target_len);
231 if (target == NULL) {
232 SET_ERRNO_FROM_WIN32_CODE(ERROR_OUTOFMEMORY);
233 return NULL;
234 }
235
236 r = WideCharToMultiByte(cp, flags, in, tmp_len, target, target_len, NULL, NULL);
237 if (r == 0) {
238 free(target);
239 SET_ERRNO_FROM_WIN32_CODE(GetLastError());
240 return NULL;
241 }
242
243 assert(target ? r == target_len : 1);
244 assert(target && !in_len ? strlen(target) == target_len - 1 : 1);
245
246 target[target_len-1] = '\0';
247
248 if (PHP_WIN32_CP_IGNORE_LEN_P != out_len) {
249 *out_len = target_len - 1;
250 }
251
252 return target;
253 }/*}}}*/
254
php_win32_cp_conv_w_to_utf8(const wchar_t * in,size_t in_len,size_t * out_len)255 PW32CP char *php_win32_cp_conv_w_to_utf8(const wchar_t* in, size_t in_len, size_t *out_len)
256 {/*{{{*/
257 return php_win32_cp_from_w_int(in, in_len, out_len, CP_UTF8, WC_ERR_INVALID_CHARS);
258 }/*}}}*/
259
php_win32_cp_conv_w_to_cur(const wchar_t * in,size_t in_len,size_t * out_len)260 PW32CP char *php_win32_cp_conv_w_to_cur(const wchar_t* in, size_t in_len, size_t *out_len)
261 {/*{{{*/
262 char *ret;
263
264 ret = php_win32_cp_from_w_int(in, in_len, out_len, cur_cp->id, cur_cp->from_w_fl);
265
266 return ret;
267 }/*}}}*/
268
php_win32_cp_conv_from_w(DWORD cp,DWORD flags,const wchar_t * in,size_t in_len,size_t * out_len)269 PW32CP char *php_win32_cp_conv_from_w(DWORD cp, DWORD flags, const wchar_t* in, size_t in_len, size_t *out_len)
270 {/*{{{*/
271 return php_win32_cp_from_w_int(in, in_len, out_len, cp, flags);
272 }/*}}}*/
273
274 /* This is only usable after the startup phase*/
php_win32_cp_get_enc(void)275 __forceinline static char *php_win32_cp_get_enc(void)
276 {/*{{{*/
277 char *enc = NULL;
278 const zend_encoding *zenc;
279
280 if (PG(internal_encoding) && PG(internal_encoding)[0]) {
281 enc = PG(internal_encoding);
282 } else if (SG(default_charset) && SG(default_charset)[0] ) {
283 enc = SG(default_charset);
284 } else {
285 zenc = zend_multibyte_get_internal_encoding();
286 if (zenc) {
287 enc = (char *)zend_multibyte_get_encoding_name(zenc);
288 }
289 }
290
291 return enc;
292 }/*}}}*/
293
php_win32_cp_get_current(void)294 PW32CP const struct php_win32_cp *php_win32_cp_get_current(void)
295 {/*{{{*/
296 return cur_cp;
297 }/*}}}*/
298
php_win32_cp_get_orig(void)299 PW32CP const struct php_win32_cp *php_win32_cp_get_orig(void)
300 {/*{{{*/
301 return orig_cp;
302 }/*}}}*/
303
php_win32_cp_get_by_id(DWORD id)304 PW32CP const struct php_win32_cp *php_win32_cp_get_by_id(DWORD id)
305 {/*{{{*/
306 size_t i;
307
308 if (id < php_win32_cp_map[0].id) {
309 switch (id) {
310 case CP_ACP:
311 id = GetACP();
312 break;
313 case CP_OEMCP:
314 id = GetOEMCP();
315 break;
316 }
317 }
318
319 for (i = 0; i < sizeof(php_win32_cp_map)/sizeof(struct php_win32_cp); i++) {
320 if (php_win32_cp_map[i].id == id) {
321 return &php_win32_cp_map[i];
322 }
323 }
324
325 SET_ERRNO_FROM_WIN32_CODE(ERROR_NOT_FOUND);
326
327 return NULL;
328 }/*}}}*/
329
php_win32_cp_get_by_enc(const char * enc)330 PW32CP const struct php_win32_cp *php_win32_cp_get_by_enc(const char *enc)
331 {/*{{{*/
332 size_t enc_len = 0, i;
333
334 if (!enc || !enc[0]) {
335 return php_win32_cp_get_by_id(65001U);
336 }
337
338 enc_len = strlen(enc);
339
340 for (i = 0; i < sizeof(php_win32_cp_map)/sizeof(struct php_win32_cp); i++) {
341 const struct php_win32_cp *cp = &php_win32_cp_map[i];
342
343 if (!cp->name || !cp->name[0]) {
344 continue;
345 }
346
347 if (0 == zend_binary_strcasecmp(enc, enc_len, cp->name, strlen(cp->name))) {
348 return cp;
349 }
350
351 if (cp->enc) {
352 char *start = cp->enc, *idx;
353
354 idx = strpbrk(start, "|");
355
356 while (NULL != idx) {
357 if (0 == zend_binary_strcasecmp(enc, enc_len, start, idx - start)) {
358 return cp;
359 }
360 start = idx + 1;
361 idx = strpbrk(start, "|");
362 }
363 /* Last in the list, or single charset specified. */
364 if (0 == zend_binary_strcasecmp(enc, enc_len, start, strlen(start))) {
365 return cp;
366 }
367 }
368 }
369
370 return php_win32_cp_get_by_id(GetACP());
371 }/*}}}*/
372
php_win32_cp_set_by_id(DWORD id)373 PW32CP const struct php_win32_cp *php_win32_cp_set_by_id(DWORD id)
374 {/*{{{*/
375 const struct php_win32_cp *tmp;
376 if (!IsValidCodePage(id)) {
377 SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
378 return NULL;
379 }
380
381 tmp = php_win32_cp_get_by_id(id);
382 if (tmp) {
383 cur_cp = tmp;
384 }
385
386 return cur_cp;
387 }/*}}}*/
388
php_win32_cp_use_unicode(void)389 PW32CP BOOL php_win32_cp_use_unicode(void)
390 {/*{{{*/
391 return 65001 == cur_cp->id;
392 }/*}}}*/
393
php_win32_cp_env_any_to_w(const char * env)394 PW32CP wchar_t *php_win32_cp_env_any_to_w(const char* env)
395 {/*{{{*/
396 wchar_t *envw = NULL, ew[32760];
397 char *cur = (char *)env, *prev;
398 size_t bin_len = 0;
399
400 if (!env) {
401 SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
402 return NULL;
403 }
404
405 do {
406 wchar_t *tmp;
407
408 tmp = php_win32_cp_any_to_w(cur);
409 if (tmp) {
410 size_t tmp_len = wcslen(tmp) + 1;
411 memmove(ew + bin_len, tmp, tmp_len * sizeof(wchar_t));
412 free(tmp);
413
414 bin_len += tmp_len;
415 }
416
417 prev = cur;
418
419 } while (NULL != (cur = strchr(prev, '\0')) && cur++ && *cur && bin_len + (cur - prev) < 32760);
420
421 envw = (wchar_t *) malloc((bin_len + 3) * sizeof(wchar_t));
422 if (!envw) {
423 SET_ERRNO_FROM_WIN32_CODE(ERROR_OUTOFMEMORY);
424 return NULL;
425 }
426 memmove(envw, ew, bin_len * sizeof(wchar_t));
427 envw[bin_len] = L'\0';
428 envw[bin_len + 1] = L'\0';
429 envw[bin_len + 2] = L'\0';
430
431 return envw;
432 }/*}}}*/
433
php_win32_cp_cli_io_setup(void)434 static BOOL php_win32_cp_cli_io_setup(void)
435 {/*{{{*/
436 BOOL ret = TRUE;
437
438 if (PG(input_encoding) && PG(input_encoding)[0]) {
439 cur_in_cp = php_win32_cp_get_by_enc(PG(input_encoding));
440 if (!cur_in_cp) {
441 cur_in_cp = cur_cp;
442 }
443 } else {
444 cur_in_cp = cur_cp;
445 }
446
447 if (PG(output_encoding) && PG(output_encoding)[0]) {
448 cur_out_cp = php_win32_cp_get_by_enc(PG(output_encoding));
449 if (!cur_out_cp) {
450 cur_out_cp = cur_cp;
451 }
452 } else {
453 cur_out_cp = cur_cp;
454 }
455
456 if(php_get_module_initialized()) {
457 ret = SetConsoleCP(cur_in_cp->id) && SetConsoleOutputCP(cur_out_cp->id);
458 }
459
460 return ret;
461 }/*}}}*/
462
php_win32_cp_do_setup(const char * enc)463 PW32CP const struct php_win32_cp *php_win32_cp_do_setup(const char *enc)
464 {/*{{{*/
465 if (!enc) {
466 enc = php_win32_cp_get_enc();
467 }
468
469 cur_cp = php_win32_cp_get_by_enc(enc);
470 if (!orig_cp) {
471 orig_cp = php_win32_cp_get_by_id(GetACP());
472 }
473 if (php_win32_console_is_cli_sapi()) {
474 if (!orig_in_cp) {
475 orig_in_cp = php_win32_cp_get_by_id(GetConsoleCP());
476 if (!orig_in_cp) {
477 orig_in_cp = orig_cp;
478 }
479 }
480 if (!orig_out_cp) {
481 orig_out_cp = php_win32_cp_get_by_id(GetConsoleOutputCP());
482 if (!orig_out_cp) {
483 orig_out_cp = orig_cp;
484 }
485 }
486 php_win32_cp_cli_io_setup();
487 }
488
489 return cur_cp;
490 }/*}}}*/
491
php_win32_cp_do_update(const char * enc)492 PW32CP const struct php_win32_cp *php_win32_cp_do_update(const char *enc)
493 {/*{{{*/
494 if (!enc) {
495 enc = php_win32_cp_get_enc();
496 }
497 cur_cp = php_win32_cp_get_by_enc(enc);
498
499 if (php_win32_console_is_cli_sapi()) {
500 php_win32_cp_cli_do_setup(cur_cp->id);
501 }
502
503 return cur_cp;
504 }/*}}}*/
505
php_win32_cp_shutdown(void)506 PW32CP const struct php_win32_cp *php_win32_cp_shutdown(void)
507 {/*{{{*/
508 return orig_cp;
509 }/*}}}*/
510
511 /* php_win32_cp_setup() needs to have run before! */
php_win32_cp_cli_do_setup(DWORD id)512 PW32CP const struct php_win32_cp *php_win32_cp_cli_do_setup(DWORD id)
513 {/*{{{*/
514 const struct php_win32_cp *cp;
515
516 if (!orig_cp) {
517 php_win32_cp_setup();
518 }
519
520 if (id) {
521 cp = php_win32_cp_set_by_id(id);
522 } else {
523 cp = cur_cp;
524 }
525
526 if (!cp) {
527 return NULL;
528 }
529
530 if (php_win32_cp_cli_io_setup()) {
531 return cp;
532 }
533
534 return cp;
535 }/*}}}*/
536
php_win32_cp_cli_do_restore(DWORD id)537 PW32CP const struct php_win32_cp *php_win32_cp_cli_do_restore(DWORD id)
538 {/*{{{*/
539 BOOL cli_io_restored = TRUE;
540
541 if (orig_in_cp) {
542 cli_io_restored = cli_io_restored && SetConsoleCP(orig_in_cp->id);
543 }
544
545 if (orig_out_cp) {
546 cli_io_restored = cli_io_restored && SetConsoleOutputCP(orig_out_cp->id);
547 }
548
549 if (cli_io_restored && id) {
550 return php_win32_cp_set_by_id(id);
551 }
552
553 return NULL;
554 }/*}}}*/
555
556 /* Userspace functions, see basic_functions.* for arginfo and decls. */
557
558 /* {{{ Set process codepage. */
PHP_FUNCTION(sapi_windows_cp_set)559 PHP_FUNCTION(sapi_windows_cp_set)
560 {
561 zend_long id;
562 const struct php_win32_cp *cp;
563
564 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &id) == FAILURE) {
565 RETURN_THROWS();
566 }
567
568 if (ZEND_LONG_UINT_OVFL(id)) {
569 zend_argument_value_error(1, "must be between 0 and %u", UINT_MAX);
570 RETURN_THROWS();
571 }
572
573 if (php_win32_console_is_cli_sapi()) {
574 cp = php_win32_cp_cli_do_setup((DWORD)id);
575 } else {
576 cp = php_win32_cp_set_by_id((DWORD)id);
577 }
578 if (!cp) {
579 php_error_docref(NULL, E_WARNING, "Failed to switch to codepage %d", id);
580 RETURN_FALSE;
581 }
582
583 RETURN_BOOL(cp->id == id);
584 }
585 /* }}} */
586
587 /* {{{ Get process codepage. */
PHP_FUNCTION(sapi_windows_cp_get)588 PHP_FUNCTION(sapi_windows_cp_get)
589 {
590 char *kind;
591 size_t kind_len = 0;
592
593 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s", &kind, &kind_len) == FAILURE) {
594 RETURN_THROWS();
595 }
596
597 if (kind_len == sizeof("ansi")-1 && !strncasecmp(kind, "ansi", kind_len)) {
598 RETURN_LONG(GetACP());
599 } else if (kind_len == sizeof("oem")-1 && !strncasecmp(kind, "oem", kind_len)) {
600 RETURN_LONG(GetOEMCP());
601 } else {
602 const struct php_win32_cp *cp = php_win32_cp_get_current();
603 RETURN_LONG(cp->id);
604 }
605 }
606 /* }}} */
607
608
609 /* {{{ Indicates whether the codepage is UTF-8 compatible. */
PHP_FUNCTION(sapi_windows_cp_is_utf8)610 PHP_FUNCTION(sapi_windows_cp_is_utf8)
611 {
612 if (zend_parse_parameters_none() == FAILURE) {
613 RETURN_THROWS();
614 }
615
616 RETURN_BOOL(php_win32_cp_use_unicode());
617 }
618 /* }}} */
619
620 /* {{{ Convert string from one codepage to another. */
PHP_FUNCTION(sapi_windows_cp_conv)621 PHP_FUNCTION(sapi_windows_cp_conv)
622 {
623 char *ret;
624 size_t ret_len, tmpw_len;
625 wchar_t *tmpw;
626 const struct php_win32_cp *in_cp, *out_cp;
627 zend_string *string_in_codepage = NULL;
628 zend_long int_in_codepage = 0;
629 zend_string *string_out_codepage = NULL;
630 zend_long int_out_codepage = 0;
631 zend_string *subject;
632
633 ZEND_PARSE_PARAMETERS_START(3, 3)
634 Z_PARAM_STR_OR_LONG(string_in_codepage, int_in_codepage)
635 Z_PARAM_STR_OR_LONG(string_out_codepage, int_out_codepage)
636 Z_PARAM_STR(subject)
637 ZEND_PARSE_PARAMETERS_END();
638
639 if (ZEND_SIZE_T_INT_OVFL(ZSTR_LEN(subject))) {
640 zend_argument_value_error(1, "is too long");
641 RETURN_THROWS();
642 }
643
644 if (string_in_codepage != NULL) {
645 in_cp = php_win32_cp_get_by_enc(ZSTR_VAL(string_in_codepage));
646 if (!in_cp) {
647 zend_argument_value_error(1, "must be a valid charset");
648 RETURN_THROWS();
649 }
650 } else {
651 if (ZEND_LONG_UINT_OVFL(int_in_codepage)) {
652 zend_argument_value_error(1, "must be between 0 and %u", UINT_MAX);
653 RETURN_THROWS();
654 }
655
656 in_cp = php_win32_cp_get_by_id((DWORD)int_in_codepage);
657 if (!in_cp) {
658 zend_argument_value_error(1, "must be a valid codepage");
659 RETURN_THROWS();
660 }
661 }
662
663 if (string_out_codepage != NULL) {
664 out_cp = php_win32_cp_get_by_enc(ZSTR_VAL(string_out_codepage));
665 if (!out_cp) {
666 zend_argument_value_error(2, "must be a valid charset");
667 RETURN_THROWS();
668 }
669 } else {
670 if (ZEND_LONG_UINT_OVFL(int_out_codepage)) {
671 zend_argument_value_error(2, "must be between 0 and %u", UINT_MAX);
672 RETURN_THROWS();
673 }
674
675 out_cp = php_win32_cp_get_by_id((DWORD)int_out_codepage);
676 if (!out_cp) {
677 zend_argument_value_error(2, "must be a valid codepage");
678 RETURN_THROWS();
679 }
680 }
681
682 tmpw = php_win32_cp_conv_to_w(in_cp->id, in_cp->to_w_fl, ZSTR_VAL(subject), ZSTR_LEN(subject), &tmpw_len);
683 if (!tmpw) {
684 php_error_docref(NULL, E_WARNING, "Wide char conversion failed");
685 RETURN_NULL();
686 }
687
688 ret = php_win32_cp_conv_from_w(out_cp->id, out_cp->from_w_fl, tmpw, tmpw_len, &ret_len);
689 if (!ret) {
690 free(tmpw);
691 php_error_docref(NULL, E_WARNING, "Wide char conversion failed");
692 RETURN_NULL();
693 }
694
695 RETVAL_STRINGL(ret, ret_len);
696
697 free(tmpw);
698 free(ret);
699 }
700 /* }}} */
701
702 /* }}} */
703