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