Skip to content

Promote a few remaining errors in ext/standard #6110

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Zend/tests/bug41026.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ echo "Done\n";
--EXPECT--
Done

Warning: (Registered shutdown functions) Unable to call self::on_shutdown() - function does not exist in Unknown on line 0
Fatal error: Registered shutdown function self::on_shutdown() cannot be called, function does not exist in Unknown on line 0
6 changes: 4 additions & 2 deletions ext/mbstring/mbstring.c
Original file line number Diff line number Diff line change
Expand Up @@ -2233,8 +2233,7 @@ PHP_FUNCTION(mb_strcut)
}

if (from > string.len) {
// TODO Out of bounds ValueError
RETURN_FALSE;
RETURN_EMPTY_STRING();
}

ret = mbfl_strcut(&string, &result, from, len);
Expand Down Expand Up @@ -3500,6 +3499,9 @@ PHP_FUNCTION(mb_send_mail)
str_headers = php_trim(str_headers, NULL, 0, 2);
} else if (headers_ht) {
str_headers = php_mail_build_headers(headers_ht);
if (EG(exception)) {
RETURN_THROWS();
}
}

zend_hash_init(&ht_headers, 0, NULL, ZVAL_PTR_DTOR, 0);
Expand Down
2 changes: 1 addition & 1 deletion ext/mbstring/mbstring.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function mb_substr_count(string $haystack, string $needle, ?string $encoding = n

function mb_substr(string $str, int $start, ?int $length = null, ?string $encoding = null): string {}

function mb_strcut(string $str, int $start, ?int $length = null, ?string $encoding = null): string|false {}
function mb_strcut(string $str, int $start, ?int $length = null, ?string $encoding = null): string {}

function mb_strwidth(string $str, ?string $encoding = null): int {}

Expand Down
9 changes: 2 additions & 7 deletions ext/mbstring/mbstring_arginfo.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: 442b9dc473714c91663fcd530214935ba74302e4 */
* Stub hash: e02a0588d1f46fa96452558e35ea904907b8bdf2 */

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_mb_language, 0, 0, MAY_BE_STRING|MAY_BE_BOOL)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, language, IS_STRING, 1, "null")
Expand Down Expand Up @@ -87,12 +87,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_mb_substr, 0, 2, IS_STRING, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, encoding, IS_STRING, 1, "null")
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_mb_strcut, 0, 2, MAY_BE_STRING|MAY_BE_FALSE)
ZEND_ARG_TYPE_INFO(0, str, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, length, IS_LONG, 1, "null")
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, encoding, IS_STRING, 1, "null")
ZEND_END_ARG_INFO()
#define arginfo_mb_strcut arginfo_mb_substr

#define arginfo_mb_strwidth arginfo_mb_strlen

Expand Down
3 changes: 2 additions & 1 deletion ext/mbstring/tests/bug49354.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ var_dump(mb_strcut($crap, 2, 100, 'UTF-8'));
var_dump(mb_strcut($crap, 3, 100, 'UTF-8'));
var_dump(mb_strcut($crap, 12, 100, 'UTF-8'));
var_dump(mb_strcut($crap, 13, 100, 'UTF-8'));

?>
--EXPECT--
string(12) "AåBäCöDü"
string(11) "åBäCöDü"
string(11) "åBäCöDü"
string(9) "BäCöDü"
string(0) ""
bool(false)
string(0) ""
2 changes: 1 addition & 1 deletion ext/mbstring/tests/mb_strcut.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ print MBStringChars(mb_strcut($euc_jp, 5, 5,'EUC-JP'), 'EUC-JP') . "\n";
print MBStringChars(mb_strcut($euc_jp, 0, 100,'EUC-JP'), 'EUC-JP') . "\n";

$str = mb_strcut($euc_jp, 100, 10,'EUC-JP');
($str === false) ? print "OK\n" : print "No good\n";
($str === "") ? print "OK\n" : print "No good\n";

$str = mb_strcut($euc_jp, -100, 10,'EUC-JP');
($str !== "") ? print "OK\n" : print "No good\n";
Expand Down
2 changes: 1 addition & 1 deletion ext/sodium/libsodium.c
Original file line number Diff line number Diff line change
Expand Up @@ -3067,7 +3067,7 @@ PHP_FUNCTION(sodium_unpad)
RETURN_THROWS();
}
if (padded_len < blocksize) {
zend_argument_error(sodium_exception_ce, 1, "must not be shorter than the block size");
zend_argument_error(sodium_exception_ce, 1, "must be at least as long as the block size");
RETURN_THROWS();
}

Expand Down
18 changes: 1 addition & 17 deletions ext/standard/array.c
Original file line number Diff line number Diff line change
Expand Up @@ -962,22 +962,6 @@ static int php_array_user_compare(Bucket *a, Bucket *b) /* {{{ */
}
/* }}} */

/* check if comparison function is valid */
#define PHP_ARRAY_CMP_FUNC_CHECK(func_name) \
if (!zend_is_callable(*func_name, 0, NULL)) { \
php_error_docref(NULL, E_WARNING, "Invalid comparison function"); \
BG(user_compare_fci) = old_user_compare_fci; \
BG(user_compare_fci_cache) = old_user_compare_fci_cache; \
RETURN_FALSE; \
} \

/* Clear FCI cache otherwise : for example the same or other array with
* (partly) the same key values has been sorted with uasort() or
* other sorting function the comparison is cached, however the name
* of the function for comparison is not respected. see bug #28739 AND #33295
*
* Following defines will assist in backup / restore values. */

#define PHP_ARRAY_CMP_FUNC_VARS \
zend_fcall_info old_user_compare_fci; \
zend_fcall_info_cache old_user_compare_fci_cache \
Expand Down Expand Up @@ -2567,7 +2551,7 @@ static void php_compact_var(HashTable *eg_active_symbol_table, zval *return_valu
zend_hash_update(Z_ARRVAL_P(return_value), Z_STR_P(entry), &data);
}
} else {
php_error_docref(NULL, E_NOTICE, "Undefined variable $%s", ZSTR_VAL(Z_STR_P(entry)));
php_error_docref(NULL, E_WARNING, "Undefined variable $%s", ZSTR_VAL(Z_STR_P(entry)));
}
} else if (Z_TYPE_P(entry) == IS_ARRAY) {
if (Z_REFCOUNTED_P(entry)) {
Expand Down
62 changes: 24 additions & 38 deletions ext/standard/basic_functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -1310,7 +1310,7 @@ PHP_FUNCTION(time_sleep_until)
target_ns = (uint64_t) (target_secs * ns_per_sec);
current_ns = ((uint64_t) tm.tv_sec) * ns_per_sec + ((uint64_t) tm.tv_usec) * 1000;
if (target_ns < current_ns) {
php_error_docref(NULL, E_WARNING, "Sleep until to time is less than current time");
php_error_docref(NULL, E_WARNING, "Argument #1 ($timestamp) must be greater than or equal to the current time");
RETURN_FALSE;
}

Expand Down Expand Up @@ -1468,9 +1468,8 @@ PHPAPI int _php_error_log_ex(int opt_err, const char *message, size_t message_le
break;

case 2: /*send to an address */
php_error_docref(NULL, E_WARNING, "TCP/IP option not available!");
zend_value_error("TCP/IP option is not available for error logging");
return FAILURE;
break;

case 3: /*save to a file */
stream = php_stream_open_wrapper(opt, "a", IGNORE_URL_WIN | REPORT_ERRORS, NULL);
Expand Down Expand Up @@ -1684,10 +1683,9 @@ static int user_shutdown_function_call(zval *zv) /* {{{ */
zval retval;

if (!zend_is_callable(&shutdown_function_entry->arguments[0], 0, NULL)) {
zend_string *function_name
= zend_get_callable_name(&shutdown_function_entry->arguments[0]);
php_error(E_WARNING, "(Registered shutdown functions) Unable to call %s() - function does not exist", ZSTR_VAL(function_name));
zend_string_release_ex(function_name, 0);
zend_string *function_name = zend_get_callable_name(&shutdown_function_entry->arguments[0]);
zend_throw_error(NULL, "Registered shutdown function %s() cannot be called, function does not exist", ZSTR_VAL(function_name));
zend_string_release(function_name);
return 0;
}

Expand Down Expand Up @@ -1719,21 +1717,10 @@ static void user_tick_function_call(user_tick_function_entry *tick_fe) /* {{{ */
tick_fe->arguments + 1
) == SUCCESS) {
zval_ptr_dtor(&retval);

} else {
zval *obj, *method;

if (Z_TYPE_P(function) == IS_STRING) {
php_error_docref(NULL, E_WARNING, "Unable to call %s() - function does not exist", Z_STRVAL_P(function));
} else if ( Z_TYPE_P(function) == IS_ARRAY
&& (obj = zend_hash_index_find(Z_ARRVAL_P(function), 0)) != NULL
&& (method = zend_hash_index_find(Z_ARRVAL_P(function), 1)) != NULL
&& Z_TYPE_P(obj) == IS_OBJECT
&& Z_TYPE_P(method) == IS_STRING) {
php_error_docref(NULL, E_WARNING, "Unable to call %s::%s() - function does not exist", ZSTR_VAL(Z_OBJCE_P(obj)->name), Z_STRVAL_P(method));
} else {
php_error_docref(NULL, E_WARNING, "Unable to call tick function");
}
zend_string *function_name = zend_get_callable_name(function);
zend_throw_error(NULL, "Registered tick function %s() cannot be called, function does not exist", ZSTR_VAL(function_name));
zend_string_release(function_name);
}

tick_fe->calling = 0;
Expand Down Expand Up @@ -1764,7 +1751,7 @@ static int user_tick_function_compare(user_tick_function_entry * tick_fe1, user_
}

if (ret && tick_fe1->calling) {
php_error_docref(NULL, E_WARNING, "Unable to delete tick function executed at the moment");
zend_throw_error(NULL, "Registered tick function cannot be unregistered while it is being executed");
return 0;
}
return ret;
Expand Down Expand Up @@ -1818,23 +1805,22 @@ PHP_FUNCTION(register_shutdown_function)

/* Prevent entering of anything but valid callback (syntax check only!) */
if (!zend_is_callable(&shutdown_function_entry.arguments[0], 0, NULL)) {
zend_string *callback_name
= zend_get_callable_name(&shutdown_function_entry.arguments[0]);
php_error_docref(NULL, E_WARNING, "Invalid shutdown callback '%s' passed", ZSTR_VAL(callback_name));
zend_string *callback_name = zend_get_callable_name(&shutdown_function_entry.arguments[0]);
zend_argument_type_error(1, "must be a valid callback, function \"%s\" not found or invalid function name", ZSTR_VAL(callback_name));
efree(shutdown_function_entry.arguments);
zend_string_release_ex(callback_name, 0);
RETVAL_FALSE;
} else {
if (!BG(user_shutdown_function_names)) {
ALLOC_HASHTABLE(BG(user_shutdown_function_names));
zend_hash_init(BG(user_shutdown_function_names), 0, NULL, user_shutdown_function_dtor, 0);
}
zend_string_release(callback_name);
RETURN_THROWS();
}

for (i = 0; i < shutdown_function_entry.arg_count; i++) {
Z_TRY_ADDREF(shutdown_function_entry.arguments[i]);
}
zend_hash_next_index_insert_mem(BG(user_shutdown_function_names), &shutdown_function_entry, sizeof(php_shutdown_function_entry));
if (!BG(user_shutdown_function_names)) {
ALLOC_HASHTABLE(BG(user_shutdown_function_names));
zend_hash_init(BG(user_shutdown_function_names), 0, NULL, user_shutdown_function_dtor, 0);
}

for (i = 0; i < shutdown_function_entry.arg_count; i++) {
Z_TRY_ADDREF(shutdown_function_entry.arguments[i]);
}
zend_hash_next_index_insert_mem(BG(user_shutdown_function_names), &shutdown_function_entry, sizeof(php_shutdown_function_entry));
}
/* }}} */

Expand Down Expand Up @@ -2057,7 +2043,7 @@ PHP_FUNCTION(ini_get_all)

if (extname) {
if ((module = zend_hash_str_find_ptr(&module_registry, extname, extname_len)) == NULL) {
php_error_docref(NULL, E_WARNING, "Unable to find extension '%s'", extname);
php_error_docref(NULL, E_WARNING, "Extension \"%s\" cannot be found", extname);
RETURN_FALSE;
}
module_number = module->module_number;
Expand Down Expand Up @@ -2542,7 +2528,7 @@ PHP_FUNCTION(move_uploaded_file)
if (successful) {
zend_hash_str_del(SG(rfc1867_uploaded_files), path, path_len);
} else {
php_error_docref(NULL, E_WARNING, "Unable to move '%s' to '%s'", path, new_path);
php_error_docref(NULL, E_WARNING, "Unable to move \"%s\" to \"%s\"", path, new_path);
}

RETURN_BOOL(successful);
Expand Down
2 changes: 1 addition & 1 deletion ext/standard/basic_functions.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -672,7 +672,7 @@ function localeconv(): array {}

function strnatcasecmp(string $s1, string $s2): int {}

function substr_count(string $haystack, string $needle, int $offset = 0, ?int $length = null): int|false {}
function substr_count(string $haystack, string $needle, int $offset = 0, ?int $length = null): int {}

function str_pad(string $input, int $pad_length, string $pad_string = " ", int $pad_type = STR_PAD_RIGHT): string {}

Expand Down
14 changes: 7 additions & 7 deletions ext/standard/basic_functions_arginfo.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: 26683100d272e35a6318e0eaece65dc3761b7b03 */
* Stub hash: 0224dc521c4a8bd49fbcfd26cddb01a2e571cf74 */

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0)
ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0)
Expand Down Expand Up @@ -743,7 +743,7 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_setrawcookie, 0, 1, _IS_BOOL, 0)
ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, value, IS_STRING, 0, "\'\'")
ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, expires_or_options, "0")
ZEND_ARG_TYPE_MASK(0, expires_or_options, MAY_BE_ARRAY|MAY_BE_LONG, "0")
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, path, IS_STRING, 0, "\'\'")
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, domain, IS_STRING, 0, "\'\'")
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, secure, _IS_BOOL, 0, "false")
Expand Down Expand Up @@ -932,9 +932,9 @@ ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_substr_replace, 0, 3, MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_FALSE)
ZEND_ARG_TYPE_MASK(0, str, MAY_BE_ARRAY|MAY_BE_STRING, NULL)
ZEND_ARG_TYPE_MASK(0, replace, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
ZEND_ARG_INFO(0, start)
ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, length, "null")
ZEND_ARG_TYPE_MASK(0, replace, MAY_BE_ARRAY|MAY_BE_STRING, NULL)
ZEND_ARG_TYPE_MASK(0, start, MAY_BE_ARRAY|MAY_BE_LONG, NULL)
ZEND_ARG_TYPE_MASK(0, length, MAY_BE_ARRAY|MAY_BE_LONG|MAY_BE_NULL, "null")
ZEND_END_ARG_INFO()

#define arginfo_quotemeta arginfo_base64_encode
Expand Down Expand Up @@ -1042,7 +1042,7 @@ ZEND_END_ARG_INFO()

#define arginfo_strnatcasecmp arginfo_strnatcmp

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_substr_count, 0, 2, MAY_BE_LONG|MAY_BE_FALSE)
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_substr_count, 0, 2, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, haystack, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, needle, IS_STRING, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, offset, IS_LONG, 0, "0")
Expand Down Expand Up @@ -1770,7 +1770,7 @@ ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_password_needs_rehash, 0, 2, _IS_BOOL, 0)
ZEND_ARG_TYPE_INFO(0, hash, IS_STRING, 0)
ZEND_ARG_TYPE_MASK(0, algo, MAY_BE_STRING|MAY_BE_LONG, NULL)
ZEND_ARG_TYPE_MASK(0, algo, MAY_BE_STRING|MAY_BE_LONG|MAY_BE_NULL, NULL)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
ZEND_END_ARG_INFO()

Expand Down
2 changes: 1 addition & 1 deletion ext/standard/browscap.c
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ static int browscap_read_file(char *filename, browser_data *browdata, int persis

zend_stream_init_fp(&fh, VCWD_FOPEN(filename, "r"), filename);
if (!fh.handle.fp) {
zend_error(E_CORE_WARNING, "Cannot open '%s' for reading", filename);
zend_error(E_CORE_WARNING, "Cannot open \"%s\" for reading", filename);
return FAILURE;
}

Expand Down
2 changes: 1 addition & 1 deletion ext/standard/dl.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ PHPAPI PHP_FUNCTION(dl)
}

if (filename_len >= MAXPATHLEN) {
php_error_docref(NULL, E_WARNING, "File name exceeds the maximum allowed length of %d characters", MAXPATHLEN);
php_error_docref(NULL, E_WARNING, "Filename exceeds the maximum allowed length of %d characters", MAXPATHLEN);
RETURN_FALSE;
}

Expand Down
23 changes: 13 additions & 10 deletions ext/standard/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -942,14 +942,17 @@ PHP_FUNCTION(popen)
mode_len--;
}
}
#endif

/* Musl only partially validates the mode. Manually check it to ensure consistent behavior. */
if (mode_len != 1 || (*posix_mode != 'r' && *posix_mode != 'w')) {
php_error_docref2(NULL, command, posix_mode, E_WARNING, "Invalid mode");
if (mode_len > 2 ||
(mode_len == 1 && (*posix_mode != 'r' && *posix_mode != 'w')) ||
(mode_len == 2 && (memcmp(posix_mode, "rb", 2) && memcmp(posix_mode, "wb", 2)))
) {
zend_argument_value_error(2, "must be one of \"r\", \"rb\", \"w\", or \"wb\"");
efree(posix_mode);
RETURN_FALSE;
RETURN_THROWS();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this now throws ValueError, we should make it consistent between Linux & Windows. I.e. move this outside the ifndef, but also allow b (I think we may require that first char is r/w and only allow b as second).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully I managed to find the condition you imagined ^^

}
#endif

fp = VCWD_POPEN(command, posix_mode);
if (!fp) {
Expand Down Expand Up @@ -1816,7 +1819,7 @@ PHP_FUNCTION(fputcsv)
zend_argument_value_error(3, "must be a single character");
RETURN_THROWS();
} else if (delimiter_str_len > 1) {
php_error_docref(NULL, E_NOTICE, "delimiter must be a single character");
php_error_docref(NULL, E_WARNING, "Argument #3 ($delimiter) must be a single character");
}

/* use first character from string */
Expand All @@ -1828,15 +1831,15 @@ PHP_FUNCTION(fputcsv)
zend_argument_value_error(4, "must be a single character");
RETURN_THROWS();
} else if (enclosure_str_len > 1) {
php_error_docref(NULL, E_NOTICE, "enclosure must be a single character");
php_error_docref(NULL, E_WARNING, "Argument #4 ($enclosure) must be a single character");
}
/* use first character from string */
enclosure = *enclosure_str;
}

if (escape_str != NULL) {
if (escape_str_len > 1) {
php_error_docref(NULL, E_NOTICE, "escape must be empty or a single character");
php_error_docref(NULL, E_WARNING, "Argument #5 ($escape) must be empty or a single character");
}
if (escape_str_len < 1) {
escape_char = PHP_CSV_NO_ESCAPE;
Expand Down Expand Up @@ -1954,7 +1957,7 @@ PHP_FUNCTION(fgetcsv)
zend_argument_value_error(3, "must be a single character");
RETURN_THROWS();
} else if (delimiter_str_len > 1) {
php_error_docref(NULL, E_NOTICE, "delimiter must be a single character");
php_error_docref(NULL, E_WARNING, "Argument #3 ($delimiter) must be a single character");
}

/* use first character from string */
Expand All @@ -1966,7 +1969,7 @@ PHP_FUNCTION(fgetcsv)
zend_argument_value_error(4, "must be a single character");
RETURN_THROWS();
} else if (enclosure_str_len > 1) {
php_error_docref(NULL, E_NOTICE, "enclosure must be a single character");
php_error_docref(NULL, E_WARNING, "Argument #4 ($enclosure) must be a single character");
}

/* use first character from string */
Expand All @@ -1975,7 +1978,7 @@ PHP_FUNCTION(fgetcsv)

if (escape_str != NULL) {
if (escape_str_len > 1) {
php_error_docref(NULL, E_NOTICE, "escape must be empty or a single character");
php_error_docref(NULL, E_WARNING, "Argument #5 ($enclosure) must be empty or a single character");
}

if (escape_str_len < 1) {
Expand Down
Loading