xref: /PHP-8.1/win32/codepage.c (revision aff36587)
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 #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 	zend_string *kind = NULL;
591 
592 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|S", &kind) == FAILURE) {
593 		RETURN_THROWS();
594 	}
595 
596 	if (!kind) {
597 		const struct php_win32_cp *cp = php_win32_cp_get_current();
598 		RETURN_LONG(cp->id);
599 	} else if (zend_string_equals_literal_ci(kind, "ansi")) {
600 		RETURN_LONG(GetACP());
601 	} else if (zend_string_equals_literal_ci(kind, "oem")) {
602 		RETURN_LONG(GetOEMCP());
603 	} else {
604 		/* TODO Warn/ValueError for invalid kind? */
605 		const struct php_win32_cp *cp = php_win32_cp_get_current();
606 		RETURN_LONG(cp->id);
607 	}
608 }
609 /* }}} */
610 
611 
612 /* {{{ Indicates whether the codepage is UTF-8 compatible. */
PHP_FUNCTION(sapi_windows_cp_is_utf8)613 PHP_FUNCTION(sapi_windows_cp_is_utf8)
614 {
615 	if (zend_parse_parameters_none() == FAILURE) {
616 		RETURN_THROWS();
617 	}
618 
619 	RETURN_BOOL(php_win32_cp_use_unicode());
620 }
621 /* }}} */
622 
623 /* {{{ Convert string from one codepage to another. */
PHP_FUNCTION(sapi_windows_cp_conv)624 PHP_FUNCTION(sapi_windows_cp_conv)
625 {
626 	char *ret;
627 	size_t ret_len, tmpw_len;
628 	wchar_t *tmpw;
629 	const struct php_win32_cp *in_cp, *out_cp;
630 	zend_string *string_in_codepage = NULL;
631 	zend_long int_in_codepage = 0;
632 	zend_string *string_out_codepage = NULL;
633 	zend_long int_out_codepage = 0;
634 	zend_string *subject;
635 
636 	ZEND_PARSE_PARAMETERS_START(3, 3)
637 		Z_PARAM_STR_OR_LONG(string_in_codepage, int_in_codepage)
638 		Z_PARAM_STR_OR_LONG(string_out_codepage, int_out_codepage)
639 		Z_PARAM_STR(subject)
640 	ZEND_PARSE_PARAMETERS_END();
641 
642 	if (ZEND_SIZE_T_INT_OVFL(ZSTR_LEN(subject))) {
643 		zend_argument_value_error(1, "is too long");
644 		RETURN_THROWS();
645 	}
646 
647 	if (string_in_codepage != NULL) {
648 		in_cp = php_win32_cp_get_by_enc(ZSTR_VAL(string_in_codepage));
649 		if (!in_cp) {
650 			zend_argument_value_error(1, "must be a valid charset");
651 			RETURN_THROWS();
652 		}
653 	} else {
654 		if (ZEND_LONG_UINT_OVFL(int_in_codepage)) {
655 			zend_argument_value_error(1, "must be between 0 and %u", UINT_MAX);
656 			RETURN_THROWS();
657 		}
658 
659 		in_cp = php_win32_cp_get_by_id((DWORD)int_in_codepage);
660 		if (!in_cp) {
661 			zend_argument_value_error(1, "must be a valid codepage");
662 			RETURN_THROWS();
663 		}
664 	}
665 
666 	if (string_out_codepage != NULL) {
667 		out_cp = php_win32_cp_get_by_enc(ZSTR_VAL(string_out_codepage));
668 		if (!out_cp) {
669 			zend_argument_value_error(2, "must be a valid charset");
670 			RETURN_THROWS();
671 		}
672 	} else {
673 		if (ZEND_LONG_UINT_OVFL(int_out_codepage)) {
674 			zend_argument_value_error(2, "must be between 0 and %u", UINT_MAX);
675 			RETURN_THROWS();
676 		}
677 
678 		out_cp = php_win32_cp_get_by_id((DWORD)int_out_codepage);
679 		if (!out_cp) {
680 			zend_argument_value_error(2, "must be a valid codepage");
681 			RETURN_THROWS();
682 		}
683 	}
684 
685 	tmpw = php_win32_cp_conv_to_w(in_cp->id, in_cp->to_w_fl, ZSTR_VAL(subject), ZSTR_LEN(subject), &tmpw_len);
686 	if (!tmpw) {
687 		php_error_docref(NULL, E_WARNING, "Wide char conversion failed");
688 		RETURN_NULL();
689 	}
690 
691 	ret = php_win32_cp_conv_from_w(out_cp->id, out_cp->from_w_fl, tmpw, tmpw_len, &ret_len);
692 	if (!ret) {
693 		free(tmpw);
694 		php_error_docref(NULL, E_WARNING, "Wide char conversion failed");
695 		RETURN_NULL();
696 	}
697 
698 	RETVAL_STRINGL(ret, ret_len);
699 
700 	free(tmpw);
701 	free(ret);
702 }
703 /* }}} */
704 
705 /* }}} */
706