Skip to content

Commit

Permalink
Clean up setlocale implementation
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nikic committed Aug 24, 2020
1 parent 8c3574b commit 9feb988
Showing 1 changed file with 92 additions and 96 deletions.
188 changes: 92 additions & 96 deletions ext/standard/string.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}

Expand Down

0 comments on commit 9feb988

Please sign in to comment.