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