From: Nikita Popov Date: Mon, 24 Aug 2020 15:09:33 +0000 (+0200) Subject: Clean up setlocale implementation X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=9feb98859f12a119b7292bfc3ce9dd58f3c338e7;p=php Clean up setlocale implementation Factor out the core logic into a separate function and drop the "clever" code that combines iteration through variadic arguments and arrays. This fixes bug #79829 as a side effect. --- diff --git a/ext/standard/string.c b/ext/standard/string.c index 9c5830623f..c61d991112 100644 --- a/ext/standard/string.c +++ b/ext/standard/string.c @@ -4600,116 +4600,112 @@ PHP_FUNCTION(strip_tags) } /* }}} */ -/* {{{ Set locale information */ -PHP_FUNCTION(setlocale) -{ - zval *args = NULL; - zval *plocale; - zend_string *loc; +static zend_string *try_setlocale_str(zend_long cat, zend_string *loc) { const char *retval; - zend_long cat; - int num_args, i = 0; - uint32_t idx; - ZEND_PARSE_PARAMETERS_START(2, -1) - Z_PARAM_LONG(cat) - Z_PARAM_VARIADIC('+', args, num_args) - ZEND_PARSE_PARAMETERS_END(); - - idx = 0; - while (1) { - if (Z_TYPE(args[0]) == IS_ARRAY) { - while (idx < Z_ARRVAL(args[0])->nNumUsed) { - plocale = &Z_ARRVAL(args[0])->arData[idx].val; - if (Z_TYPE_P(plocale) != IS_UNDEF) { - break; - } - idx++; - } - if (idx >= Z_ARRVAL(args[0])->nNumUsed) { - break; - } - } else { - plocale = &args[i]; - } - - loc = zval_try_get_string(plocale); - if (UNEXPECTED(!loc)) { - return; - } - - if (!strcmp("0", ZSTR_VAL(loc))) { - zend_string_release_ex(loc, 0); - loc = NULL; - } else { - if (ZSTR_LEN(loc) >= 255) { - php_error_docref(NULL, E_WARNING, "Specified locale name is too long"); - zend_string_release_ex(loc, 0); - break; - } + if (!strcmp("0", ZSTR_VAL(loc))) { + loc = NULL; + } else { + if (ZSTR_LEN(loc) >= 255) { + php_error_docref(NULL, E_WARNING, "Specified locale name is too long"); + return NULL; } + } # ifndef PHP_WIN32 - retval = setlocale(cat, loc ? ZSTR_VAL(loc) : NULL); + retval = setlocale(cat, loc ? ZSTR_VAL(loc) : NULL); # else - if (loc) { - /* BC: don't try /^[a-z]{2}_[A-Z]{2}($|\..*)/ except for /^u[ks]_U[KS]$/ */ - char *locp = ZSTR_VAL(loc); - if (ZSTR_LEN(loc) >= 5 && locp[2] == '_' - && locp[0] >= 'a' && locp[0] <= 'z' && locp[1] >= 'a' && locp[1] <= 'z' - && locp[3] >= 'A' && locp[3] <= 'Z' && locp[4] >= 'A' && locp[4] <= 'Z' - && (locp[5] == '\0' || locp[5] == '.') - && !(locp[0] == 'u' && (locp[1] == 'k' || locp[1] == 's') - && locp[3] == 'U' && (locp[4] == 'K' || locp[4] == 'S') - && locp[5] == '\0') - ) { - retval = NULL; - } else { - retval = setlocale(cat, ZSTR_VAL(loc)); - } + if (loc) { + /* BC: don't try /^[a-z]{2}_[A-Z]{2}($|\..*)/ except for /^u[ks]_U[KS]$/ */ + char *locp = ZSTR_VAL(loc); + if (ZSTR_LEN(loc) >= 5 && locp[2] == '_' + && locp[0] >= 'a' && locp[0] <= 'z' && locp[1] >= 'a' && locp[1] <= 'z' + && locp[3] >= 'A' && locp[3] <= 'Z' && locp[4] >= 'A' && locp[4] <= 'Z' + && (locp[5] == '\0' || locp[5] == '.') + && !(locp[0] == 'u' && (locp[1] == 'k' || locp[1] == 's') + && locp[3] == 'U' && (locp[4] == 'K' || locp[4] == 'S') + && locp[5] == '\0') + ) { + retval = NULL; } else { - retval = setlocale(cat, NULL); + retval = setlocale(cat, ZSTR_VAL(loc)); } + } else { + retval = setlocale(cat, NULL); + } # endif - zend_update_current_locale(); - if (retval) { - if (loc) { - /* Remember if locale was changed */ - size_t len = strlen(retval); - - BG(locale_changed) = 1; - if (cat == LC_CTYPE || cat == LC_ALL) { - if (BG(ctype_string)) { - zend_string_release_ex(BG(ctype_string), 0); - } - if (len == 1 && *retval == 'C') { - /* C locale is represented as NULL. */ - BG(ctype_string) = NULL; - zend_string_release_ex(loc, 0); - RETURN_CHAR('C'); - } else if (len == ZSTR_LEN(loc) && !memcmp(ZSTR_VAL(loc), retval, len)) { - BG(ctype_string) = zend_string_copy(loc); - RETURN_STR(BG(ctype_string)); - } else { - BG(ctype_string) = zend_string_init(retval, len, 0); - zend_string_release_ex(loc, 0); - RETURN_STR_COPY(BG(ctype_string)); - } - } else if (len == ZSTR_LEN(loc) && !memcmp(ZSTR_VAL(loc), retval, len)) { - RETURN_STR(loc); - } + zend_update_current_locale(); + if (!retval) { + return NULL; + } + + if (loc) { + /* Remember if locale was changed */ + size_t len = strlen(retval); + + BG(locale_changed) = 1; + if (cat == LC_CTYPE || cat == LC_ALL) { + if (BG(ctype_string)) { + zend_string_release_ex(BG(ctype_string), 0); + } + if (len == 1 && *retval == 'C') { + /* C locale is represented as NULL. */ + BG(ctype_string) = NULL; zend_string_release_ex(loc, 0); + return ZSTR_CHAR('C'); + } else if (len == ZSTR_LEN(loc) && !memcmp(ZSTR_VAL(loc), retval, len)) { + BG(ctype_string) = zend_string_copy(loc); + return zend_string_copy(BG(ctype_string)); + } else { + BG(ctype_string) = zend_string_init(retval, len, 0); + return zend_string_copy(BG(ctype_string)); } - RETURN_STRING(retval); - } - if (loc) { - zend_string_release_ex(loc, 0); + } else if (len == ZSTR_LEN(loc) && !memcmp(ZSTR_VAL(loc), retval, len)) { + return zend_string_copy(loc); } + } + return zend_string_init(retval, strlen(retval), 0); +} + +static zend_string *try_setlocale_zval(zend_long cat, zval *loc_zv) { + zend_string *loc_str = zval_try_get_string(loc_zv); + zend_string *result = try_setlocale_str(cat, loc_str); + zend_string_release_ex(loc_str, 0); + return result; +} + +/* {{{ Set locale information */ +PHP_FUNCTION(setlocale) +{ + zend_long cat; + zval *args = NULL; + int num_args; + + ZEND_PARSE_PARAMETERS_START(2, -1) + Z_PARAM_LONG(cat) + Z_PARAM_VARIADIC('+', args, num_args) + ZEND_PARSE_PARAMETERS_END(); - if (Z_TYPE(args[0]) == IS_ARRAY) { - idx++; + for (uint32_t i = 0; i < num_args; i++) { + if (Z_TYPE(args[i]) == IS_ARRAY) { + zval *elem; + ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL(args[i]), elem) { + zend_string *result = try_setlocale_zval(cat, elem); + if (EG(exception)) { + RETURN_THROWS(); + } + if (result) { + RETURN_STR(result); + } + } ZEND_HASH_FOREACH_END(); } else { - if (++i >= num_args) break; + zend_string *result = try_setlocale_zval(cat, &args[i]); + if (EG(exception)) { + RETURN_THROWS(); + } + if (result) { + RETURN_STR(result); + } } }