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