|
25 | 25 | #ifdef PHP_WIN32 |
26 | 26 | #include "win32/php_registry.h" |
27 | 27 | #include "win32/winutil.h" |
| 28 | +#include "win32/codepage.h" |
28 | 29 | #endif |
29 | 30 |
|
30 | 31 | #if defined(HAVE_SCANDIR) && defined(HAVE_ALPHASORT) && defined(HAVE_DIRENT_H) |
@@ -307,6 +308,191 @@ static void php_ini_parser_cb(zval *arg1, zval *arg2, zval *arg3, int callback_t |
307 | 308 | } |
308 | 309 | /* }}} */ |
309 | 310 |
|
| 311 | +#ifdef PHP_WIN32 |
| 312 | +typedef struct { |
| 313 | + zval args[3]; |
| 314 | + int callback_type; |
| 315 | +} php_ini_utf8_entry; |
| 316 | + |
| 317 | +typedef struct { |
| 318 | + const struct php_win32_cp *cp; |
| 319 | + const struct php_win32_cp *utf8; |
| 320 | + HashTable values; |
| 321 | + zend_llist entries; |
| 322 | + bool failed; |
| 323 | +} php_ini_utf8_context; |
| 324 | + |
| 325 | +static php_ini_utf8_context *php_ini_utf8; |
| 326 | + |
| 327 | +static const struct php_win32_cp *php_ini_utf8_codepage(HashTable *hash) |
| 328 | +{ |
| 329 | + const char *names[] = {"internal_encoding", "default_charset"}; |
| 330 | + for (int i = 0; i < 2; i++) { |
| 331 | + zval *value = zend_hash_str_find(hash, names[i], strlen(names[i])); |
| 332 | + if (!value) { |
| 333 | + value = zend_hash_str_find(&configuration_hash, names[i], strlen(names[i])); |
| 334 | + } |
| 335 | + if (value && Z_TYPE_P(value) == IS_PTR) { |
| 336 | + value = Z_PTR_P(value); |
| 337 | + } |
| 338 | + if (value && Z_TYPE_P(value) == IS_STRING && Z_STRLEN_P(value)) { |
| 339 | + return php_win32_cp_get_by_enc(Z_STRVAL_P(value)); |
| 340 | + } |
| 341 | + } |
| 342 | + return php_win32_cp_get_by_id(CP_UTF8); |
| 343 | +} |
| 344 | + |
| 345 | +static void php_ini_utf8_convert(php_ini_utf8_context *ctx, zval *value, const struct php_win32_cp *from, const struct php_win32_cp *to) |
| 346 | +{ |
| 347 | + if (ctx->failed || Z_TYPE_P(value) != IS_STRING || from == to) { |
| 348 | + return; |
| 349 | + } |
| 350 | + size_t length, wide_len; |
| 351 | + wchar_t *wide = php_win32_cp_conv_to_w(from->id, from->to_w_fl, Z_STRVAL_P(value), Z_STRLEN_P(value), &wide_len); |
| 352 | + char *bytes = NULL; |
| 353 | + if (wide) { |
| 354 | + bytes = php_win32_cp_conv_from_w(to->id, to->from_w_fl, wide, wide_len, &length); |
| 355 | + free(wide); |
| 356 | + } |
| 357 | + if (!bytes) { |
| 358 | + ctx->failed = true; |
| 359 | + return; |
| 360 | + } |
| 361 | + zend_string_release(Z_STR_P(value)); |
| 362 | + ZVAL_STR(value, zend_string_init(bytes, length, true)); |
| 363 | + free(bytes); |
| 364 | +} |
| 365 | + |
| 366 | +static zval *php_ini_utf8_get_config(zend_string *name) |
| 367 | +{ |
| 368 | + php_ini_utf8_context *ctx = php_ini_utf8; |
| 369 | + zval *value = zend_hash_find(&ctx->values, name); |
| 370 | + if (!value || Z_TYPE_P(value) != IS_PTR) { |
| 371 | + return value; |
| 372 | + } |
| 373 | + value = Z_PTR_P(value); |
| 374 | + if (value && Z_TYPE_P(value) == IS_STRING) { |
| 375 | + zval converted; |
| 376 | + ZVAL_STR(&converted, zend_string_copy(Z_STR_P(value))); |
| 377 | + php_ini_utf8_convert(ctx, &converted, ctx->cp, ctx->utf8); |
| 378 | + return zend_hash_update(&ctx->values, name, &converted); |
| 379 | + } |
| 380 | + return value; |
| 381 | +} |
| 382 | + |
| 383 | +static char *php_ini_utf8_getenv(const char *name, size_t name_len) |
| 384 | +{ |
| 385 | + wchar_t *wide_name = php_win32_cp_conv_utf8_to_w(name, name_len, PHP_WIN32_CP_IGNORE_LEN_P); |
| 386 | + if (!wide_name) { |
| 387 | + php_ini_utf8->failed = true; |
| 388 | + return estrdup(""); |
| 389 | + } |
| 390 | + const wchar_t *value = _wgetenv(wide_name); |
| 391 | + free(wide_name); |
| 392 | + if (!value) { |
| 393 | + return NULL; |
| 394 | + } |
| 395 | + char *bytes = php_win32_cp_w_to_utf8(value); |
| 396 | + if (!bytes) { |
| 397 | + php_ini_utf8->failed = true; |
| 398 | + return estrdup(""); |
| 399 | + } |
| 400 | + char *result = estrdup(bytes); |
| 401 | + free(bytes); |
| 402 | + return result; |
| 403 | +} |
| 404 | + |
| 405 | +static void php_ini_utf8_entry_dtor(void *data) |
| 406 | +{ |
| 407 | + php_ini_utf8_entry *entry = data; |
| 408 | + for (int i = 0; i < 3; i++) { |
| 409 | + zval_internal_ptr_dtor(&entry->args[i]); |
| 410 | + } |
| 411 | +} |
| 412 | + |
| 413 | +static void php_ini_utf8_parser_cb(zval *arg1, zval *arg2, zval *arg3, int callback_type, void *data) |
| 414 | +{ |
| 415 | + php_ini_utf8_context *ctx = data; |
| 416 | + php_ini_utf8_entry entry; |
| 417 | + zval *args[3] = {arg1, arg2, arg3}; |
| 418 | + for (int i = 0; i < 3; i++) { |
| 419 | + if (args[i]) { |
| 420 | + ZVAL_STR(&entry.args[i], zend_string_dup(Z_STR_P(args[i]), true)); |
| 421 | + } else { |
| 422 | + ZVAL_UNDEF(&entry.args[i]); |
| 423 | + } |
| 424 | + } |
| 425 | + entry.callback_type = callback_type; |
| 426 | + zend_llist_add_element(&ctx->entries, &entry); |
| 427 | + /* Keep earlier -d values available for expansion, without queuing extensions twice. */ |
| 428 | + if (callback_type == ZEND_INI_PARSER_ENTRY && !is_special_section |
| 429 | + && (zend_string_equals_literal_ci(Z_STR_P(arg1), PHP_EXTENSION_TOKEN) |
| 430 | + || zend_string_equals_literal_ci(Z_STR_P(arg1), ZEND_EXTENSION_TOKEN))) { |
| 431 | + return; |
| 432 | + } |
| 433 | + php_ini_parser_cb(arg1, arg2, arg3, callback_type, &ctx->values); |
| 434 | +} |
| 435 | + |
| 436 | +static zend_result php_ini_parse_utf8(const char *entries) |
| 437 | +{ |
| 438 | + php_ini_utf8_context ctx = {0}; |
| 439 | + char *(*getenv_save)(const char *, size_t) = zend_getenv; |
| 440 | + zend_hash_init(&ctx.values, 8, NULL, config_zval_dtor, true); |
| 441 | + zend_llist_init(&ctx.entries, sizeof(php_ini_utf8_entry), php_ini_utf8_entry_dtor, true); |
| 442 | + ctx.cp = php_ini_utf8_codepage(&ctx.values); |
| 443 | + ctx.utf8 = php_win32_cp_get_by_id(CP_UTF8); |
| 444 | + if (!ctx.cp || !ctx.utf8) { |
| 445 | + ctx.failed = true; |
| 446 | + goto cleanup; |
| 447 | + } |
| 448 | + /* Borrow php.ini scalars; section arrays only need their structure while parsing. */ |
| 449 | + zend_string *key; |
| 450 | + zval *value; |
| 451 | + ZEND_HASH_FOREACH_STR_KEY_VAL(&configuration_hash, key, value) { |
| 452 | + if (key) { |
| 453 | + zval name, borrowed; |
| 454 | + ZVAL_STR(&name, zend_string_copy(key)); |
| 455 | + php_ini_utf8_convert(&ctx, &name, ctx.cp, ctx.utf8); |
| 456 | + if (Z_TYPE_P(value) == IS_ARRAY) { |
| 457 | + ZVAL_NEW_PERSISTENT_ARR(&borrowed); |
| 458 | + zend_hash_init(Z_ARRVAL(borrowed), 0, NULL, config_zval_dtor, true); |
| 459 | + } else { |
| 460 | + ZVAL_PTR(&borrowed, value); |
| 461 | + } |
| 462 | + zend_hash_update(&ctx.values, Z_STR(name), &borrowed); |
| 463 | + zend_string_release(Z_STR(name)); |
| 464 | + } |
| 465 | + } ZEND_HASH_FOREACH_END(); |
| 466 | + php_ini_utf8 = &ctx; |
| 467 | + zend_getenv = php_ini_utf8_getenv; |
| 468 | + /* Parse in UTF-8, then apply the values after resolving the final code page. */ |
| 469 | + zend_parse_ini_string(entries, true, ZEND_INI_SCANNER_NORMAL, php_ini_utf8_parser_cb, &ctx); |
| 470 | + zend_getenv = getenv_save; |
| 471 | + php_ini_utf8 = NULL; |
| 472 | + RESET_ACTIVE_INI_HASH(); |
| 473 | + ctx.cp = php_ini_utf8_codepage(&ctx.values); |
| 474 | + ctx.failed |= !ctx.cp; |
| 475 | + for (zend_llist_element *element = ctx.entries.head; element && !ctx.failed; element = element->next) { |
| 476 | + php_ini_utf8_entry *entry = (php_ini_utf8_entry *) element->data; |
| 477 | + for (int i = 0; i < 3; i++) { |
| 478 | + php_ini_utf8_convert(&ctx, &entry->args[i], ctx.utf8, ctx.cp); |
| 479 | + } |
| 480 | + if (!ctx.failed) { |
| 481 | + php_ini_parser_cb(&entry->args[0], Z_ISUNDEF(entry->args[1]) ? NULL : &entry->args[1], |
| 482 | + Z_ISUNDEF(entry->args[2]) ? NULL : &entry->args[2], entry->callback_type, &configuration_hash); |
| 483 | + } |
| 484 | + } |
| 485 | +cleanup: |
| 486 | + zend_llist_destroy(&ctx.entries); |
| 487 | + zend_hash_destroy(&ctx.values); |
| 488 | + if (ctx.failed) { |
| 489 | + php_error(E_CORE_WARNING, "Could not convert command line INI values to the configured Windows code page"); |
| 490 | + return FAILURE; |
| 491 | + } |
| 492 | + return SUCCESS; |
| 493 | +} |
| 494 | +#endif |
| 495 | + |
310 | 496 | /* {{{ php_load_php_extension_cb */ |
311 | 497 | static void php_load_php_extension_cb(void *arg) |
312 | 498 | { |
@@ -404,7 +590,7 @@ static void append_ini_path(char *php_ini_search_path, size_t search_path_size, |
404 | 590 | } |
405 | 591 |
|
406 | 592 | /* {{{ php_init_config */ |
407 | | -void php_init_config(void) |
| 593 | +zend_result php_init_config(void) |
408 | 594 | { |
409 | 595 | char *php_ini_file_name = NULL; |
410 | 596 | char *php_ini_search_path = NULL; |
@@ -716,8 +902,15 @@ void php_init_config(void) |
716 | 902 | if (sapi_module.ini_entries) { |
717 | 903 | /* Reset active ini section */ |
718 | 904 | RESET_ACTIVE_INI_HASH(); |
| 905 | + |
| 906 | +#ifdef PHP_WIN32 |
| 907 | + if (sapi_module.ini_entries_utf8) { |
| 908 | + return php_ini_parse_utf8(sapi_module.ini_entries); |
| 909 | + } |
| 910 | +#endif |
719 | 911 | zend_parse_ini_string(sapi_module.ini_entries, true, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t) php_ini_parser_cb, &configuration_hash); |
720 | 912 | } |
| 913 | + return SUCCESS; |
721 | 914 | } |
722 | 915 | /* }}} */ |
723 | 916 |
|
@@ -876,6 +1069,11 @@ PHPAPI void php_ini_activate_per_host_config(const char *host, size_t host_len) |
876 | 1069 | /* {{{ cfg_get_entry */ |
877 | 1070 | PHPAPI zval *cfg_get_entry_ex(zend_string *name) |
878 | 1071 | { |
| 1072 | +#ifdef PHP_WIN32 |
| 1073 | + if (php_ini_utf8) { |
| 1074 | + return php_ini_utf8_get_config(name); |
| 1075 | + } |
| 1076 | +#endif |
879 | 1077 | return zend_hash_find(&configuration_hash, name); |
880 | 1078 | } |
881 | 1079 | /* }}} */ |
|
0 commit comments