]> granicus.if.org Git - php/commitdiff
Clean up setlocale implementation
authorNikita Popov <nikita.ppv@gmail.com>
Mon, 24 Aug 2020 15:09:33 +0000 (17:09 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Mon, 24 Aug 2020 15:09:33 +0000 (17:09 +0200)
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.

ext/standard/string.c

index 9c5830623fb11053f2ad9858cac960df870f33d6..c61d9911128d5d593a8dada0f14cbbc64c6fa2e3 100644 (file)
@@ -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);
+                       }
                }
        }