xref: /PHP-7.3/win32/codepage.c (revision 27f168cf)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2018 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 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 #if PHP_DEBUG
107 	size_t save_in_len = in_len;
108 #endif
109 
110 	assert(in && in_len ? in[in_len] == '\0' : 1);
111 
112 	if (!in) {
113 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
114 		return NULL;
115 	} else if (0 == in_len) {
116 		/* Not binary safe. */
117 		in_len = strlen(in);
118 	}
119 
120 	end = in + in_len;
121 
122 	if (in_len > 15) {
123 		const char *aidx = (const char *)ZEND_SLIDE_TO_ALIGNED16(in);
124 
125 		/* Process unaligned chunk. */
126 		while (idx < aidx) {
127 			if (!__isascii(*idx) && '\0' != *idx) {
128 				ASCII_FAIL_RETURN()
129 			}
130 			idx++;
131 		}
132 
133 		/* Process aligned chunk. */
134 		while (end - idx > 15) {
135 			const __m128i block = _mm_load_si128((__m128i *)idx);
136 			if (_mm_movemask_epi8(block)) {
137 				ASCII_FAIL_RETURN()
138 			}
139 			idx += 16;
140 		}
141 	}
142 
143 	/* Process the trailing part, or otherwise process string < 16 bytes. */
144 	while (idx < end) {
145 		if (!__isascii(*idx) && '\0' != *idx) {
146 			ASCII_FAIL_RETURN()
147 		}
148 		idx++;
149 	}
150 
151 	ret = malloc((in_len+1)*sizeof(wchar_t));
152 	if (!ret) {
153 		SET_ERRNO_FROM_WIN32_CODE(ERROR_OUTOFMEMORY);
154 		return NULL;
155 	}
156 
157 	ret_idx = ret;
158 	idx = in;
159 
160 	/* Check and conversion could be merged. This however would
161 		be more expencive, if a non ASCII string was passed.
162 		TODO check whether the impact is acceptable. */
163 	if (in_len > 15) {
164 		const char *aidx = (const char *)ZEND_SLIDE_TO_ALIGNED16(in);
165 
166 		/* Process unaligned chunk. */
167 		while (idx < aidx) {
168 			*ret_idx++ = (wchar_t)*idx++;
169 		}
170 
171 		/* Process aligned chunk. */
172 		if (end - idx > 15) {
173 			const __m128i mask = _mm_set1_epi32(0);
174 			while (end - idx > 15) {
175 				const __m128i block = _mm_load_si128((__m128i *)idx);
176 
177 				{
178 					const __m128i lo = _mm_unpacklo_epi8(block, mask);
179 					_mm_storeu_si128((__m128i *)ret_idx, lo);
180 				}
181 
182 				ret_idx += 8;
183 				{
184 					const __m128i hi = _mm_unpackhi_epi8(block, mask);
185 					_mm_storeu_si128((__m128i *)ret_idx, hi);
186 				}
187 
188 				idx += 16;
189 				ret_idx += 8;
190 			}
191 		}
192 	}
193 
194 	/* Process the trailing part, or otherwise process string < 16 bytes. */
195 	while (idx < end) {
196 		*ret_idx++ = (wchar_t)*idx++;
197 	}
198 
199 	ret[in_len] = L'\0';
200 
201 	assert(ret && !save_in_len ? wcslen(ret) == in_len : 1);
202 
203 	if (PHP_WIN32_CP_IGNORE_LEN_P != out_len) {
204 		*out_len = in_len;
205 	}
206 
207 	return ret;
208 }/*}}}*/
209 #undef ASCII_FAIL_RETURN
210 
php_win32_cp_from_w_int(const wchar_t * in,size_t in_len,size_t * out_len,UINT cp,DWORD flags)211 __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)
212 {/*{{{*/
213 	int r;
214 	int target_len, tmp_len;
215 	char* target;
216 
217 	if (!in || in_len > INT_MAX) {
218 		SET_ERRNO_FROM_WIN32_CODE(ERROR_INVALID_PARAMETER);
219 		return NULL;
220 	}
221 	assert(in_len ? in[in_len] == '\0' : 1);
222 
223 	tmp_len = !in_len ? -1 : (int)in_len + 1;
224 
225 	target_len = WideCharToMultiByte(cp, flags, in, tmp_len, NULL, 0, NULL, NULL);
226 	if (target_len == 0) {
227 		SET_ERRNO_FROM_WIN32_CODE(GetLastError());
228 		return NULL;
229 	}
230 
231 	target = malloc(target_len);
232 	if (target == NULL) {
233 		SET_ERRNO_FROM_WIN32_CODE(ERROR_OUTOFMEMORY);
234 		return NULL;
235 	}
236 
237 	r = WideCharToMultiByte(cp, flags, in, tmp_len, target, target_len, NULL, NULL);
238 	if (r == 0) {
239 		free(target);
240 		SET_ERRNO_FROM_WIN32_CODE(GetLastError());
241 		return NULL;
242 	}
243 
244 	assert(target ? r == target_len : 1);
245 	assert(target && !in_len ? strlen(target) == target_len - 1 : 1);
246 
247 	target[target_len-1] = '\0';
248 
249 	if (PHP_WIN32_CP_IGNORE_LEN_P != out_len) {
250 		*out_len = target_len - 1;
251 	}
252 
253 	return target;
254 }/*}}}*/
255 
php_win32_cp_conv_w_to_utf8(const wchar_t * in,size_t in_len,size_t * out_len)256 PW32CP char *php_win32_cp_conv_w_to_utf8(const wchar_t* in, size_t in_len, size_t *out_len)
257 {/*{{{*/
258 	return php_win32_cp_from_w_int(in, in_len, out_len, CP_UTF8, WC_ERR_INVALID_CHARS);
259 }/*}}}*/
260 
php_win32_cp_conv_w_to_cur(const wchar_t * in,size_t in_len,size_t * out_len)261 PW32CP char *php_win32_cp_conv_w_to_cur(const wchar_t* in, size_t in_len, size_t *out_len)
262 {/*{{{*/
263 	char *ret;
264 
265 	ret = php_win32_cp_from_w_int(in, in_len, out_len, cur_cp->id, cur_cp->from_w_fl);
266 
267 	return ret;
268 }/*}}}*/
269 
php_win32_cp_conv_from_w(DWORD cp,DWORD flags,const wchar_t * in,size_t in_len,size_t * out_len)270 PW32CP char *php_win32_cp_conv_from_w(DWORD cp, DWORD flags, const wchar_t* in, size_t in_len, size_t *out_len)
271 {/*{{{*/
272 	return php_win32_cp_from_w_int(in, in_len, out_len, cp, flags);
273 }/*}}}*/
274 
275 /* This is only usable after the startup phase*/
php_win32_cp_get_enc(void)276 __forceinline static char *php_win32_cp_get_enc(void)
277 {/*{{{*/
278 	char *enc = NULL;
279 	const zend_encoding *zenc;
280 
281 	if (PG(internal_encoding) && PG(internal_encoding)[0]) {
282 		enc = PG(internal_encoding);
283 	} else if (SG(default_charset) && SG(default_charset)[0] ) {
284 		enc = SG(default_charset);
285 	} else {
286 		zenc = zend_multibyte_get_internal_encoding();
287 		if (zenc) {
288 			enc = (char *)zend_multibyte_get_encoding_name(zenc);
289 		}
290 	}
291 
292 	return enc;
293 }/*}}}*/
294 
php_win32_cp_is_cli_sapi()295 __forceinline static BOOL php_win32_cp_is_cli_sapi()
296 {/*{{{*/
297 	return strlen(sapi_module.name) >= sizeof("cli") - 1 && !strncmp(sapi_module.name, "cli", sizeof("cli") - 1);
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_cp_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_cp_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_cp_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 		convert_to_string(z_in_cp);
661 
662 		in_cp = php_win32_cp_get_by_enc(Z_STRVAL_P(z_in_cp));
663 		if (!in_cp) {
664 			php_error_docref(NULL, E_WARNING, "Invalid charset %s", Z_STRVAL_P(z_in_cp));
665 			RETURN_NULL();
666 		}
667 	}
668 
669 	if (IS_LONG == Z_TYPE_P(z_out_cp)) {
670 		if (ZEND_LONG_UINT_OVFL(Z_LVAL_P(z_out_cp))) {
671 			php_error_docref(NULL, E_WARNING, "Argument %d is out of range", Z_LVAL_P(z_out_cp));
672 			RETURN_NULL();
673 		}
674 
675 		out_cp = php_win32_cp_get_by_id((DWORD)Z_LVAL_P(z_out_cp));
676 		if (!out_cp) {
677 			php_error_docref(NULL, E_WARNING, "Invalid codepage %d", Z_LVAL_P(z_out_cp));
678 			RETURN_NULL();
679 		}
680 	} else {
681 		convert_to_string(z_out_cp);
682 
683 		out_cp = php_win32_cp_get_by_enc(Z_STRVAL_P(z_out_cp));
684 		if (!out_cp) {
685 			php_error_docref(NULL, E_WARNING, "Invalid charset %s", Z_STRVAL_P(z_out_cp));
686 			RETURN_NULL();
687 		}
688 	}
689 
690 	tmpw = php_win32_cp_conv_to_w(in_cp->id, in_cp->to_w_fl, subj, subj_len, &tmpw_len);
691 	if (!tmpw) {
692 		php_error_docref(NULL, E_WARNING, "Wide char conversion failed");
693 		RETURN_NULL();
694 	}
695 
696 	ret = php_win32_cp_conv_from_w(out_cp->id, out_cp->from_w_fl, tmpw, tmpw_len, &ret_len);
697 	if (!ret) {
698 		free(tmpw);
699 		php_error_docref(NULL, E_WARNING, "Wide char conversion failed");
700 		RETURN_NULL();
701 	}
702 
703 	RETVAL_STRINGL(ret, ret_len);
704 
705 	free(tmpw);
706 	free(ret);
707 }
708 /* }}} */
709 
710 /* }}} */
711 /*
712  * Local variables:
713  * tab-width: 4
714  * c-basic-offset: 4
715  * End:
716  * vim600: sw=4 ts=4 fdm=marker
717  * vim<600: sw=4 ts=4
718  */
719