codepage.c 16 KB

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