From 3cf298b854443e7bbb2a96d976e8d79ffc4ddf02 Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 02:33:18 +0300 Subject: add string case conversion functions: lower and upper --- core/src/xmake/engine.c | 4 ++ core/src/xmake/string/case.c | 116 ++++++++++++++++++++++++++++++++++++++++++ tests/modules/string/test.lua | 7 +++ 3 files changed, 127 insertions(+) create mode 100644 core/src/xmake/string/case.c diff --git a/core/src/xmake/engine.c b/core/src/xmake/engine.c index 532be1adc..d81828f1f 100644 --- a/core/src/xmake/engine.c +++ b/core/src/xmake/engine.c @@ -286,6 +286,8 @@ tb_int_t xm_string_lastof(lua_State *lua); tb_int_t xm_string_convert(lua_State *lua); tb_int_t xm_string_endswith(lua_State *lua); tb_int_t xm_string_startswith(lua_State *lua); +tb_int_t xm_string_lower(lua_State *lua); +tb_int_t xm_string_upper(lua_State *lua); // the process functions tb_int_t xm_process_open(lua_State *lua); @@ -589,6 +591,8 @@ static luaL_Reg const g_string_functions[] = { { "convert", xm_string_convert }, { "endswith", xm_string_endswith }, { "startswith", xm_string_startswith }, + { "lower", xm_string_lower }, + { "upper", xm_string_upper }, { tb_null, tb_null }, }; diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c new file mode 100644 index 000000000..4c6fb54ca --- /dev/null +++ b/core/src/xmake/string/case.c @@ -0,0 +1,116 @@ +/*!A cross-platform build utility based on Lua + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (C) 2015-present, Xmake Open Source Community. + * + * @author ruki + * @file case.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * trace + */ +#define TB_TRACE_MODULE_NAME "case" +#define TB_TRACE_MODULE_DEBUG (0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" +#include "../utils/charset.h" +#include + +/* ////////////////////////////////////////////////////////////////////////////////////// + * helper + */ +static tb_int_t xm_string_case(lua_State* lua, tb_bool_t lower) { + + // check + tb_assert_and_check_return_val(lua, 0); + + // get the string + size_t size = 0; + tb_char_t const* str = luaL_checklstring(lua, 1, &size); + tb_check_return_val(str, 0); + + // empty? + if (!size) { + lua_pushstring(lua, ""); + return 1; + } + + // find charsets + xm_charset_entry_ref_t fcharset = xm_charset_find_by_name("utf8"); +#if defined(TB_CONFIG_OS_WINDOWS) + xm_charset_entry_ref_t tcharset = xm_charset_find_by_name("utf16"); +#else + xm_charset_entry_ref_t tcharset = xm_charset_find_by_name("utf32"); +#endif + tb_check_return_val(fcharset && tcharset, 0); + + // convert string + tb_long_t dst_size = 0; + tb_size_t dst_maxn = (tb_size_t)(size + 1) * 4; + tb_byte_t* dst_data = tb_malloc_bytes(dst_maxn); + if (dst_data && dst_maxn && + (dst_size = tb_charset_conv_data(fcharset->type, tcharset->type, (tb_byte_t const*)str, (tb_size_t)size, dst_data, dst_maxn)) >= 0 && + dst_size < dst_maxn) { + + // to lower/upper +#if defined(TB_CONFIG_OS_WINDOWS) + tb_uint16_t* p = (tb_uint16_t*)dst_data; + tb_size_t n = (tb_size_t)dst_size / 2; +#else + tb_uint32_t* p = (tb_uint32_t*)dst_data; + tb_size_t n = (tb_size_t)dst_size / 4; +#endif + while (n--) { + *p = (tb_wchar_t)(lower? towlower((wint_t)*p) : towupper((wint_t)*p)); + p++; + } + + // convert string back + tb_long_t res_size = 0; + tb_size_t res_maxn = dst_maxn * 2; + tb_byte_t* res_data = tb_malloc_bytes(res_maxn); + if (res_data && res_maxn && + (res_size = tb_charset_conv_data(tcharset->type, fcharset->type, dst_data, (tb_size_t)dst_size, res_data, res_maxn)) >= 0 && + res_size < res_maxn) { + lua_pushlstring(lua, (tb_char_t const*)res_data, res_size); + } else { + lua_pushnil(lua); + } + if (res_data) tb_free(res_data); + } else { + lua_pushnil(lua); + } + + // free data + if (dst_data) tb_free(dst_data); + + // ok + return 1; +} + +/* ////////////////////////////////////////////////////////////////////////////////////// + * implementation + */ +tb_int_t xm_string_lower(lua_State* lua) { + return xm_string_case(lua, tb_true); +} + +tb_int_t xm_string_upper(lua_State* lua) { + return xm_string_case(lua, tb_false); +} diff --git a/tests/modules/string/test.lua b/tests/modules/string/test.lua index 3f52dc593..6bb3b6f78 100644 --- a/tests/modules/string/test.lua +++ b/tests/modules/string/test.lua @@ -106,3 +106,10 @@ function test_replace(t) t:are_equal(("123$xyz456xyz789xyz"):replace("123$", "000", {plain = true}), "000xyz456xyz789xyz") t:are_equal(("123xyz$456xyz$789xyz$"):replace("xyz$", "000", {plain = true}), "123000456000789000") end + +function test_case(t) + t:are_equal(("Hello"):lower(), "hello") + t:are_equal(("Hello"):upper(), "HELLO") + t:are_equal(("Звезда Хэнсин"):lower(), "звезда хэнсин") + t:are_equal(("Звезда Хэнсин"):upper(), "ЗВЕЗДА ХЭНСИН") +end -- cgit v1.3.1 From 5af9fbbb34c797bfa626c6f490482751a56c95ad Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 02:56:38 +0300 Subject: windows issue? --- core/src/xmake/string/case.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index 4c6fb54ca..e8369f04b 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -31,6 +31,9 @@ #include "prefix.h" #include "../utils/charset.h" #include +#if defined(TB_CONFIG_OS_WINDOWS) +# include +#endif /* ////////////////////////////////////////////////////////////////////////////////////// * helper @@ -70,16 +73,16 @@ static tb_int_t xm_string_case(lua_State* lua, tb_bool_t lower) { // to lower/upper #if defined(TB_CONFIG_OS_WINDOWS) - tb_uint16_t* p = (tb_uint16_t*)dst_data; - tb_size_t n = (tb_size_t)dst_size / 2; + if (lower) CharLowerBuffW((LPWSTR)dst_data, (DWORD)(dst_size / 2)); + else CharUpperBuffW((LPWSTR)dst_data, (DWORD)(dst_size / 2)); #else tb_uint32_t* p = (tb_uint32_t*)dst_data; tb_size_t n = (tb_size_t)dst_size / 4; -#endif while (n--) { *p = (tb_wchar_t)(lower? towlower((wint_t)*p) : towupper((wint_t)*p)); p++; } +#endif // convert string back tb_long_t res_size = 0; -- cgit v1.3.1 From 424d7de3637812c82adbc2cd0419c842b1991be4 Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 03:26:15 +0300 Subject: Assume we can skip EMOJIS? --- core/src/xmake/string/case.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index e8369f04b..aeadc4e62 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -79,7 +79,8 @@ static tb_int_t xm_string_case(lua_State* lua, tb_bool_t lower) { tb_uint32_t* p = (tb_uint32_t*)dst_data; tb_size_t n = (tb_size_t)dst_size / 4; while (n--) { - *p = (tb_wchar_t)(lower? towlower((wint_t)*p) : towupper((wint_t)*p)); + if (*p < 0x10000) + *p = (lower? towlower((wint_t)*p) : towupper((wint_t)*p)); p++; } #endif -- cgit v1.3.1 From d83c306af108ef93fc08b9db1c42469c7f14372f Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 03:43:55 +0300 Subject: assume we check char --- core/src/xmake/string/case.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index aeadc4e62..858d4be08 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -79,8 +79,13 @@ static tb_int_t xm_string_case(lua_State* lua, tb_bool_t lower) { tb_uint32_t* p = (tb_uint32_t*)dst_data; tb_size_t n = (tb_size_t)dst_size / 4; while (n--) { - if (*p < 0x10000) - *p = (lower? towlower((wint_t)*p) : towupper((wint_t)*p)); + if (lower) { + if (iswupper((wint_t)*p)) + *p = towlower((wint_t)*p); + } else { + if (iswlower((wint_t)*p)) + *p = towupper((wint_t)*p); + } p++; } #endif -- cgit v1.3.1 From b5f93e02d0e28b694fe452ef868620fffbd7872b Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 04:00:16 +0300 Subject: try use setlocale --- core/src/xmake/string/case.c | 22 ++++++++++++++++------ tests/modules/string/test.lua | 2 ++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index 858d4be08..ee20b4a9b 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -34,6 +34,9 @@ #if defined(TB_CONFIG_OS_WINDOWS) # include #endif +#if defined(TB_CONFIG_LIBC_HAVE_SETLOCALE) +# include +#endif /* ////////////////////////////////////////////////////////////////////////////////////// * helper @@ -43,6 +46,11 @@ static tb_int_t xm_string_case(lua_State* lua, tb_bool_t lower) { // check tb_assert_and_check_return_val(lua, 0); +#if defined(TB_CONFIG_LIBC_HAVE_SETLOCALE) + // set locale to utf8 + setlocale(LC_ALL, ""); +#endif + // get the string size_t size = 0; tb_char_t const* str = luaL_checklstring(lua, 1, &size); @@ -79,12 +87,14 @@ static tb_int_t xm_string_case(lua_State* lua, tb_bool_t lower) { tb_uint32_t* p = (tb_uint32_t*)dst_data; tb_size_t n = (tb_size_t)dst_size / 4; while (n--) { - if (lower) { - if (iswupper((wint_t)*p)) - *p = towlower((wint_t)*p); - } else { - if (iswlower((wint_t)*p)) - *p = towupper((wint_t)*p); + if (*p < 0x10000) { + if (lower) { + if (iswupper((wint_t)*p)) + *p = towlower((wint_t)*p); + } else { + if (iswlower((wint_t)*p)) + *p = towupper((wint_t)*p); + } } p++; } diff --git a/tests/modules/string/test.lua b/tests/modules/string/test.lua index 6bb3b6f78..94b3c1a04 100644 --- a/tests/modules/string/test.lua +++ b/tests/modules/string/test.lua @@ -112,4 +112,6 @@ function test_case(t) t:are_equal(("Hello"):upper(), "HELLO") t:are_equal(("Звезда Хэнсин"):lower(), "звезда хэнсин") t:are_equal(("Звезда Хэнсин"):upper(), "ЗВЕЗДА ХЭНСИН") + t:are_equal(("Test 源文件🎆 Message"):lower(), "test 源文件🎆 message") + t:are_equal(("Test 源文件🎆 Message"):upper(), "TEST 源文件🎆 MESSAGE") end -- cgit v1.3.1 From e7ab18bef196961d784d0e98746bd41c3453a3ad Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 04:07:23 +0300 Subject: refactor: replace setlocale with tb_setlocale for locale initialization --- core/src/xmake/string/case.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index ee20b4a9b..1c2341cb1 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -34,9 +34,7 @@ #if defined(TB_CONFIG_OS_WINDOWS) # include #endif -#if defined(TB_CONFIG_LIBC_HAVE_SETLOCALE) -# include -#endif +#include "tbox/libc/stdlib/setlocale.h" /* ////////////////////////////////////////////////////////////////////////////////////// * helper @@ -46,10 +44,8 @@ static tb_int_t xm_string_case(lua_State* lua, tb_bool_t lower) { // check tb_assert_and_check_return_val(lua, 0); -#if defined(TB_CONFIG_LIBC_HAVE_SETLOCALE) - // set locale to utf8 - setlocale(LC_ALL, ""); -#endif + // init locale + tb_setlocale(); // get the string size_t size = 0; -- cgit v1.3.1 From 4fcbf73e87bfd1910ed87a43ad000ef1d128a395 Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 04:20:37 +0300 Subject: test macos --- core/src/xmake/string/case.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index 1c2341cb1..81cdeae1c 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -34,6 +34,9 @@ #if defined(TB_CONFIG_OS_WINDOWS) # include #endif +#if defined(TB_CONFIG_OS_MACOSX) || defined(TB_CONFIG_OS_IOS) +# include +#endif #include "tbox/libc/stdlib/setlocale.h" /* ////////////////////////////////////////////////////////////////////////////////////// @@ -58,6 +61,34 @@ static tb_int_t xm_string_case(lua_State* lua, tb_bool_t lower) { return 1; } +#if defined(TB_CONFIG_OS_MACOSX) || defined(TB_CONFIG_OS_IOS) + CFStringRef cf_str = CFStringCreateWithBytes(kCFAllocatorDefault, (tb_byte_t const*)str, size, kCFStringEncodingUTF8, false); + if (cf_str) { + CFMutableStringRef cf_mutable = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, cf_str); + CFRelease(cf_str); + if (cf_mutable) { + if (lower) CFStringLowercase(cf_mutable, NULL); + else CFStringUppercase(cf_mutable, NULL); + + CFIndex len = CFStringGetLength(cf_mutable); + CFIndex max_size = CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8) + 1; + tb_char_t* buffer = tb_malloc_bytes(max_size); + if (buffer) { + if (CFStringGetCString(cf_mutable, buffer, max_size, kCFStringEncodingUTF8)) { + lua_pushstring(lua, buffer); + } else { + lua_pushnil(lua); + } + tb_free(buffer); + } else { + lua_pushnil(lua); + } + CFRelease(cf_mutable); + return 1; + } + } +#endif + // find charsets xm_charset_entry_ref_t fcharset = xm_charset_find_by_name("utf8"); #if defined(TB_CONFIG_OS_WINDOWS) -- cgit v1.3.1 From 501ca613b96d0dde01b4f77bbf61dca1a58f64aa Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 04:35:12 +0300 Subject: fix: adjust character range check in xm_string_case function --- core/src/xmake/string/case.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index 81cdeae1c..d87b3e9a7 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -114,7 +114,7 @@ static tb_int_t xm_string_case(lua_State* lua, tb_bool_t lower) { tb_uint32_t* p = (tb_uint32_t*)dst_data; tb_size_t n = (tb_size_t)dst_size / 4; while (n--) { - if (*p < 0x10000) { + if (*p < 0x2000) { if (lower) { if (iswupper((wint_t)*p)) *p = towlower((wint_t)*p); -- cgit v1.3.1 From bd366e385b14cd0a3942918830c63b6f0760f4a8 Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 05:07:46 +0300 Subject: try use utf8proc --- core/src/xmake/string/case.c | 104 ++++++++----------------------------------- core/src/xmake/xmake.lua | 2 + core/xmake.lua | 2 + 3 files changed, 22 insertions(+), 86 deletions(-) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index d87b3e9a7..d83aa1731 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -31,13 +31,7 @@ #include "prefix.h" #include "../utils/charset.h" #include -#if defined(TB_CONFIG_OS_WINDOWS) -# include -#endif -#if defined(TB_CONFIG_OS_MACOSX) || defined(TB_CONFIG_OS_IOS) -# include -#endif -#include "tbox/libc/stdlib/setlocale.h" +#include /* ////////////////////////////////////////////////////////////////////////////////////// * helper @@ -47,9 +41,6 @@ static tb_int_t xm_string_case(lua_State* lua, tb_bool_t lower) { // check tb_assert_and_check_return_val(lua, 0); - // init locale - tb_setlocale(); - // get the string size_t size = 0; tb_char_t const* str = luaL_checklstring(lua, 1, &size); @@ -61,91 +52,32 @@ static tb_int_t xm_string_case(lua_State* lua, tb_bool_t lower) { return 1; } -#if defined(TB_CONFIG_OS_MACOSX) || defined(TB_CONFIG_OS_IOS) - CFStringRef cf_str = CFStringCreateWithBytes(kCFAllocatorDefault, (tb_byte_t const*)str, size, kCFStringEncodingUTF8, false); - if (cf_str) { - CFMutableStringRef cf_mutable = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, cf_str); - CFRelease(cf_str); - if (cf_mutable) { - if (lower) CFStringLowercase(cf_mutable, NULL); - else CFStringUppercase(cf_mutable, NULL); - - CFIndex len = CFStringGetLength(cf_mutable); - CFIndex max_size = CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8) + 1; - tb_char_t* buffer = tb_malloc_bytes(max_size); - if (buffer) { - if (CFStringGetCString(cf_mutable, buffer, max_size, kCFStringEncodingUTF8)) { - lua_pushstring(lua, buffer); - } else { - lua_pushnil(lua); - } - tb_free(buffer); - } else { - lua_pushnil(lua); - } - CFRelease(cf_mutable); - return 1; - } - } -#endif - - // find charsets - xm_charset_entry_ref_t fcharset = xm_charset_find_by_name("utf8"); -#if defined(TB_CONFIG_OS_WINDOWS) - xm_charset_entry_ref_t tcharset = xm_charset_find_by_name("utf16"); -#else - xm_charset_entry_ref_t tcharset = xm_charset_find_by_name("utf32"); -#endif - tb_check_return_val(fcharset && tcharset, 0); - // convert string - tb_long_t dst_size = 0; tb_size_t dst_maxn = (tb_size_t)(size + 1) * 4; tb_byte_t* dst_data = tb_malloc_bytes(dst_maxn); - if (dst_data && dst_maxn && - (dst_size = tb_charset_conv_data(fcharset->type, tcharset->type, (tb_byte_t const*)str, (tb_size_t)size, dst_data, dst_maxn)) >= 0 && - dst_size < dst_maxn) { - - // to lower/upper -#if defined(TB_CONFIG_OS_WINDOWS) - if (lower) CharLowerBuffW((LPWSTR)dst_data, (DWORD)(dst_size / 2)); - else CharUpperBuffW((LPWSTR)dst_data, (DWORD)(dst_size / 2)); -#else - tb_uint32_t* p = (tb_uint32_t*)dst_data; - tb_size_t n = (tb_size_t)dst_size / 4; - while (n--) { - if (*p < 0x2000) { - if (lower) { - if (iswupper((wint_t)*p)) - *p = towlower((wint_t)*p); - } else { - if (iswlower((wint_t)*p)) - *p = towupper((wint_t)*p); - } + if (dst_data) { + tb_byte_t* p = dst_data; + tb_byte_t const* s = (tb_byte_t const*)str; + tb_byte_t const* e = s + size; + while (s < e) { + utf8proc_int32_t codepoint; + utf8proc_ssize_t len = utf8proc_iterate((utf8proc_uint8_t const*)s, e - s, &codepoint); + if (len > 0) { + if (lower) codepoint = utf8proc_tolower(codepoint); + else codepoint = utf8proc_toupper(codepoint); + utf8proc_ssize_t wlen = utf8proc_encode_char(codepoint, (utf8proc_uint8_t*)p); + if (wlen > 0) p += wlen; + s += len; + } else { + *p++ = *s++; } - p++; } -#endif - - // convert string back - tb_long_t res_size = 0; - tb_size_t res_maxn = dst_maxn * 2; - tb_byte_t* res_data = tb_malloc_bytes(res_maxn); - if (res_data && res_maxn && - (res_size = tb_charset_conv_data(tcharset->type, fcharset->type, dst_data, (tb_size_t)dst_size, res_data, res_maxn)) >= 0 && - res_size < res_maxn) { - lua_pushlstring(lua, (tb_char_t const*)res_data, res_size); - } else { - lua_pushnil(lua); - } - if (res_data) tb_free(res_data); + lua_pushlstring(lua, (tb_char_t const*)dst_data, p - dst_data); + tb_free(dst_data); } else { lua_pushnil(lua); } - // free data - if (dst_data) tb_free(dst_data); - // ok return 1; } diff --git a/core/src/xmake/xmake.lua b/core/src/xmake/xmake.lua index 90f761b30..0c2e15991 100644 --- a/core/src/xmake/xmake.lua +++ b/core/src/xmake/xmake.lua @@ -20,6 +20,8 @@ target("xmake") add_deps("pdcurses") end + add_packages("utf8proc") + -- add definitions add_defines("__tb_prefix__=\"xmake\"") if is_mode("debug") then diff --git a/core/xmake.lua b/core/xmake.lua index 7b1ae6cbd..e160d000f 100644 --- a/core/xmake.lua +++ b/core/xmake.lua @@ -59,6 +59,8 @@ end -- use cosmocc toolchain option("cosmocc", {default = false, description = "Use cosmocc toolchain to build once and run anywhere."}) +add_requires("utf8proc") + -- embed all script files option("embed", {default = false, description = "Embed all script files."}) -- cgit v1.3.1 From 6be7cace6bf07c2f428ba1c42ab5e417e7243a11 Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 05:10:14 +0300 Subject: fixup --- core/src/xmake/xmake.lua | 2 +- core/xmake.lua | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/core/src/xmake/xmake.lua b/core/src/xmake/xmake.lua index 0c2e15991..eb3b3eeb6 100644 --- a/core/src/xmake/xmake.lua +++ b/core/src/xmake/xmake.lua @@ -20,7 +20,7 @@ target("xmake") add_deps("pdcurses") end - add_packages("utf8proc") + add_deps("utf8proc") -- add definitions add_defines("__tb_prefix__=\"xmake\"") diff --git a/core/xmake.lua b/core/xmake.lua index e160d000f..7b1ae6cbd 100644 --- a/core/xmake.lua +++ b/core/xmake.lua @@ -59,8 +59,6 @@ end -- use cosmocc toolchain option("cosmocc", {default = false, description = "Use cosmocc toolchain to build once and run anywhere."}) -add_requires("utf8proc") - -- embed all script files option("embed", {default = false, description = "Embed all script files."}) -- cgit v1.3.1 From 453362a7fd5df7e83bd7d33c9c9254edae7619b9 Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 05:21:20 +0300 Subject: feat: add support for fetching utf8proc compiler and linker flags --- core/xmake.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/core/xmake.sh b/core/xmake.sh index 28ef1d94a..e89547d74 100755 --- a/core/xmake.sh +++ b/core/xmake.sh @@ -18,6 +18,17 @@ if is_plat "solaris"; then add_defines "_XOPEN_SOURCE=600" fi +if [ -f "scripts/xrepo.sh" ]; then + xrepo_cflags=$(bash scripts/xrepo.sh fetch --cflags utf8proc 2>/dev/null) + xrepo_ldflags=$(bash scripts/xrepo.sh fetch --ldflags utf8proc 2>/dev/null) + if [ -n "$xrepo_cflags" ]; then + add_cxflags "${xrepo_cflags}" + fi + if [ -n "$xrepo_ldflags" ]; then + add_ldflags "${xrepo_ldflags}" + fi +fi + # disable some compiler errors if is_plat "macosx"; then add_cxflags "-Wno-error=deprecated-declarations" "-fno-strict-aliasing" "-Wno-error=nullability-completeness" "-Wno-error=parentheses-equality" -- cgit v1.3.1 From c570d73fd4e147b49d7bc302e62eee1529255e39 Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 05:39:59 +0300 Subject: add utf8proc submod --- .gitmodules | 3 +++ core/src/utf8proc/utf8proc | 1 + 2 files changed, 4 insertions(+) create mode 160000 core/src/utf8proc/utf8proc diff --git a/.gitmodules b/.gitmodules index 3f4327fcf..50058bdc1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,3 +19,6 @@ [submodule "core/src/lz4/lz4"] path = core/src/lz4/lz4 url = ../../xmake-io/xmake-core-lz4.git +[submodule "core/src/utf8proc/utf8proc"] + path = core/src/utf8proc/utf8proc + url = https://github.com/JuliaStrings/utf8proc diff --git a/core/src/utf8proc/utf8proc b/core/src/utf8proc/utf8proc new file mode 160000 index 000000000..e5e799221 --- /dev/null +++ b/core/src/utf8proc/utf8proc @@ -0,0 +1 @@ +Subproject commit e5e799221b45bbb90f5fdc5c69b6b8dfbf017e78 -- cgit v1.3.1 From 09a3ad64bb655f818533560d8cbbaceb63fe6b7e Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 05:40:05 +0300 Subject: Fixup --- core/src/utf8proc/xmake.lua | 5 +++++ core/src/utf8proc/xmake.sh | 9 +++++++++ core/src/xmake/xmake.sh | 2 +- core/xmake.lua | 2 +- core/xmake.sh | 11 +---------- 5 files changed, 17 insertions(+), 12 deletions(-) create mode 100644 core/src/utf8proc/xmake.lua create mode 100644 core/src/utf8proc/xmake.sh diff --git a/core/src/utf8proc/xmake.lua b/core/src/utf8proc/xmake.lua new file mode 100644 index 000000000..17d84f505 --- /dev/null +++ b/core/src/utf8proc/xmake.lua @@ -0,0 +1,5 @@ +target("utf8proc") + set_kind("static") + add_defines("UTF8PROC_STATIC") + add_includedirs("utf8proc", {public = true}) + add_files("utf8proc/utf8proc.c") diff --git a/core/src/utf8proc/xmake.sh b/core/src/utf8proc/xmake.sh new file mode 100644 index 000000000..56e0b0db6 --- /dev/null +++ b/core/src/utf8proc/xmake.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +target "utf8proc" + set_kind "static" + set_default false + add_defines "UTF8PROC_STATIC" + add_includedirs "utf8proc" "{public}" + add_files "utf8proc/utf8proc.c" + diff --git a/core/src/xmake/xmake.sh b/core/src/xmake/xmake.sh index 291001306..11f33ab25 100755 --- a/core/src/xmake/xmake.sh +++ b/core/src/xmake/xmake.sh @@ -22,7 +22,7 @@ target "xmake" fi fi else - local libs="lua_cjson lz4 sv tbox" + local libs="lua_cjson lz4 sv tbox utf8proc" for lib in $libs; do add_deps "$lib" done diff --git a/core/xmake.lua b/core/xmake.lua index 7b1ae6cbd..386dc9829 100644 --- a/core/xmake.lua +++ b/core/xmake.lua @@ -132,7 +132,7 @@ if is_plat("windows") then end -- add projects -includes("src/sv", "src/lz4", "src/xmake", "src/cli") +includes("src/sv", "src/lz4", "src/xmake", "src/cli", "src/utf8proc") if namespace then namespace("tbox", function () includes("src/tbox") diff --git a/core/xmake.sh b/core/xmake.sh index e89547d74..0ece092a5 100755 --- a/core/xmake.sh +++ b/core/xmake.sh @@ -18,16 +18,6 @@ if is_plat "solaris"; then add_defines "_XOPEN_SOURCE=600" fi -if [ -f "scripts/xrepo.sh" ]; then - xrepo_cflags=$(bash scripts/xrepo.sh fetch --cflags utf8proc 2>/dev/null) - xrepo_ldflags=$(bash scripts/xrepo.sh fetch --ldflags utf8proc 2>/dev/null) - if [ -n "$xrepo_cflags" ]; then - add_cxflags "${xrepo_cflags}" - fi - if [ -n "$xrepo_ldflags" ]; then - add_ldflags "${xrepo_ldflags}" - fi -fi # disable some compiler errors if is_plat "macosx"; then @@ -246,6 +236,7 @@ if ! has_config "external"; then includes "src/lz4" includes "src/sv" includes "src/tbox" + includes "src/utf8proc" fi includes "src/xmake" includes "src/cli" -- cgit v1.3.1 From 2d5b9f71e9fdee506f0422edee4666d035528663 Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 05:51:05 +0300 Subject: re --- core/src/utf8proc/xmake.lua | 2 +- core/src/utf8proc/xmake.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/utf8proc/xmake.lua b/core/src/utf8proc/xmake.lua index 17d84f505..2c57f0004 100644 --- a/core/src/utf8proc/xmake.lua +++ b/core/src/utf8proc/xmake.lua @@ -1,5 +1,5 @@ target("utf8proc") set_kind("static") - add_defines("UTF8PROC_STATIC") + add_defines("UTF8PROC_STATIC", {public = true}) add_includedirs("utf8proc", {public = true}) add_files("utf8proc/utf8proc.c") diff --git a/core/src/utf8proc/xmake.sh b/core/src/utf8proc/xmake.sh index 56e0b0db6..0002eafab 100644 --- a/core/src/utf8proc/xmake.sh +++ b/core/src/utf8proc/xmake.sh @@ -3,7 +3,7 @@ target "utf8proc" set_kind "static" set_default false - add_defines "UTF8PROC_STATIC" + add_defines "UTF8PROC_STATIC" "{public}" add_includedirs "utf8proc" "{public}" add_files "utf8proc/utf8proc.c" -- cgit v1.3.1 From 389070fe2a681a9e996ee562b8140bd56b727aa6 Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 06:30:32 +0300 Subject: cleaning --- core/src/xmake/string/case.c | 2 -- core/xmake.sh | 1 - 2 files changed, 3 deletions(-) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index d83aa1731..eeb459959 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -29,8 +29,6 @@ * includes */ #include "prefix.h" -#include "../utils/charset.h" -#include #include /* ////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/xmake.sh b/core/xmake.sh index 0ece092a5..5c68642ef 100755 --- a/core/xmake.sh +++ b/core/xmake.sh @@ -18,7 +18,6 @@ if is_plat "solaris"; then add_defines "_XOPEN_SOURCE=600" fi - # disable some compiler errors if is_plat "macosx"; then add_cxflags "-Wno-error=deprecated-declarations" "-fno-strict-aliasing" "-Wno-error=nullability-completeness" "-Wno-error=parentheses-equality" -- cgit v1.3.1 From 5e34b6a2e2f5f98b80481d864e84d5c016d6a7ab Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 07:36:54 +0300 Subject: cls --- .gitmodules | 3 --- core/src/utf8proc/utf8proc | 1 - 2 files changed, 4 deletions(-) delete mode 160000 core/src/utf8proc/utf8proc diff --git a/.gitmodules b/.gitmodules index 50058bdc1..3f4327fcf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,6 +19,3 @@ [submodule "core/src/lz4/lz4"] path = core/src/lz4/lz4 url = ../../xmake-io/xmake-core-lz4.git -[submodule "core/src/utf8proc/utf8proc"] - path = core/src/utf8proc/utf8proc - url = https://github.com/JuliaStrings/utf8proc diff --git a/core/src/utf8proc/utf8proc b/core/src/utf8proc/utf8proc deleted file mode 160000 index e5e799221..000000000 --- a/core/src/utf8proc/utf8proc +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e5e799221b45bbb90f5fdc5c69b6b8dfbf017e78 -- cgit v1.3.1 From 5f03e0684c22482c361586d64ca0e044a2f14f2b Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 07:37:08 +0300 Subject: cls --- core/src/utf8proc/xmake.lua | 5 ----- core/src/utf8proc/xmake.sh | 9 -------- core/src/xmake/string/case.c | 49 ++++++++++++++++++++++++++------------------ core/src/xmake/xmake.lua | 2 -- core/src/xmake/xmake.sh | 2 +- core/xmake.lua | 2 +- core/xmake.sh | 1 - 7 files changed, 31 insertions(+), 39 deletions(-) delete mode 100644 core/src/utf8proc/xmake.lua delete mode 100644 core/src/utf8proc/xmake.sh diff --git a/core/src/utf8proc/xmake.lua b/core/src/utf8proc/xmake.lua deleted file mode 100644 index 2c57f0004..000000000 --- a/core/src/utf8proc/xmake.lua +++ /dev/null @@ -1,5 +0,0 @@ -target("utf8proc") - set_kind("static") - add_defines("UTF8PROC_STATIC", {public = true}) - add_includedirs("utf8proc", {public = true}) - add_files("utf8proc/utf8proc.c") diff --git a/core/src/utf8proc/xmake.sh b/core/src/utf8proc/xmake.sh deleted file mode 100644 index 0002eafab..000000000 --- a/core/src/utf8proc/xmake.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -target "utf8proc" - set_kind "static" - set_default false - add_defines "UTF8PROC_STATIC" "{public}" - add_includedirs "utf8proc" "{public}" - add_files "utf8proc/utf8proc.c" - diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index eeb459959..5fa4c5786 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -29,7 +29,7 @@ * includes */ #include "prefix.h" -#include +#include /* ////////////////////////////////////////////////////////////////////////////////////// * helper @@ -50,28 +50,37 @@ static tb_int_t xm_string_case(lua_State* lua, tb_bool_t lower) { return 1; } - // convert string - tb_size_t dst_maxn = (tb_size_t)(size + 1) * 4; - tb_byte_t* dst_data = tb_malloc_bytes(dst_maxn); - if (dst_data) { - tb_byte_t* p = dst_data; - tb_byte_t const* s = (tb_byte_t const*)str; - tb_byte_t const* e = s + size; - while (s < e) { - utf8proc_int32_t codepoint; - utf8proc_ssize_t len = utf8proc_iterate((utf8proc_uint8_t const*)s, e - s, &codepoint); - if (len > 0) { - if (lower) codepoint = utf8proc_tolower(codepoint); - else codepoint = utf8proc_toupper(codepoint); - utf8proc_ssize_t wlen = utf8proc_encode_char(codepoint, (utf8proc_uint8_t*)p); - if (wlen > 0) p += wlen; - s += len; + // convert to wchar + tb_size_t wn = size + 1; + tb_wchar_t* wb = tb_nalloc_type(wn, tb_wchar_t); + if (wb) { + wn = tb_mbstowcs(wb, str, wn); + if (wn != -1) { + + // to case + tb_size_t i = 0; + for (i = 0; i < wn; i++) { + wb[i] = lower? towlower(wb[i]) : towupper(wb[i]); + } + + // convert to utf8 + tb_size_t un = (wn + 1) * 4; + tb_char_t* ub = (tb_char_t*)tb_malloc_bytes(un); + if (ub) { + tb_size_t n = tb_wcstombs(ub, wb, un); + if (n != -1) { + lua_pushlstring(lua, ub, n); + } else { + lua_pushnil(lua); + } + tb_free(ub); } else { - *p++ = *s++; + lua_pushnil(lua); } + } else { + lua_pushnil(lua); } - lua_pushlstring(lua, (tb_char_t const*)dst_data, p - dst_data); - tb_free(dst_data); + tb_free(wb); } else { lua_pushnil(lua); } diff --git a/core/src/xmake/xmake.lua b/core/src/xmake/xmake.lua index eb3b3eeb6..90f761b30 100644 --- a/core/src/xmake/xmake.lua +++ b/core/src/xmake/xmake.lua @@ -20,8 +20,6 @@ target("xmake") add_deps("pdcurses") end - add_deps("utf8proc") - -- add definitions add_defines("__tb_prefix__=\"xmake\"") if is_mode("debug") then diff --git a/core/src/xmake/xmake.sh b/core/src/xmake/xmake.sh index 11f33ab25..291001306 100755 --- a/core/src/xmake/xmake.sh +++ b/core/src/xmake/xmake.sh @@ -22,7 +22,7 @@ target "xmake" fi fi else - local libs="lua_cjson lz4 sv tbox utf8proc" + local libs="lua_cjson lz4 sv tbox" for lib in $libs; do add_deps "$lib" done diff --git a/core/xmake.lua b/core/xmake.lua index 386dc9829..7b1ae6cbd 100644 --- a/core/xmake.lua +++ b/core/xmake.lua @@ -132,7 +132,7 @@ if is_plat("windows") then end -- add projects -includes("src/sv", "src/lz4", "src/xmake", "src/cli", "src/utf8proc") +includes("src/sv", "src/lz4", "src/xmake", "src/cli") if namespace then namespace("tbox", function () includes("src/tbox") diff --git a/core/xmake.sh b/core/xmake.sh index 5c68642ef..28ef1d94a 100755 --- a/core/xmake.sh +++ b/core/xmake.sh @@ -235,7 +235,6 @@ if ! has_config "external"; then includes "src/lz4" includes "src/sv" includes "src/tbox" - includes "src/utf8proc" fi includes "src/xmake" includes "src/cli" -- cgit v1.3.1 From ee2f9eaac207c1b728a17c24287cd69076df74d9 Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 07:50:38 +0300 Subject: fixup --- core/src/xmake/string/case.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index 5fa4c5786..9206cafa2 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -71,18 +71,18 @@ static tb_int_t xm_string_case(lua_State* lua, tb_bool_t lower) { if (n != -1) { lua_pushlstring(lua, ub, n); } else { - lua_pushnil(lua); + lua_pushlstring(lua, str, size); } tb_free(ub); } else { - lua_pushnil(lua); + lua_pushlstring(lua, str, size); } } else { - lua_pushnil(lua); + lua_pushlstring(lua, str, size); } tb_free(wb); } else { - lua_pushnil(lua); + lua_pushlstring(lua, str, size); } // ok -- cgit v1.3.1 From c466d7ca16843055bca74d33e8f2fc24331d69b8 Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 08:19:05 +0300 Subject: try win --- core/src/xmake/string/case.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index 9206cafa2..762110bd9 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -30,6 +30,9 @@ */ #include "prefix.h" #include +#ifdef TB_CONFIG_OS_WINDOWS +# include +#endif /* ////////////////////////////////////////////////////////////////////////////////////// * helper @@ -58,10 +61,15 @@ static tb_int_t xm_string_case(lua_State* lua, tb_bool_t lower) { if (wn != -1) { // to case +#ifdef TB_CONFIG_OS_WINDOWS + if (lower) CharLowerBuffW((LPWSTR)wb, (DWORD)wn); + else CharUpperBuffW((LPWSTR)wb, (DWORD)wn); +#else tb_size_t i = 0; for (i = 0; i < wn; i++) { wb[i] = lower? towlower(wb[i]) : towupper(wb[i]); } +#endif // convert to utf8 tb_size_t un = (wn + 1) * 4; -- cgit v1.3.1 From 2ee21af12eaf85d32d36570c8189c581f59fb3c9 Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 09:07:23 +0300 Subject: add locale handling for string case conversion on non-Windows platforms --- core/src/xmake/string/case.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index 762110bd9..777148623 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -30,6 +30,7 @@ */ #include "prefix.h" #include +#include "tbox/libc/stdlib/setlocale.h" #ifdef TB_CONFIG_OS_WINDOWS # include #endif @@ -65,10 +66,23 @@ static tb_int_t xm_string_case(lua_State* lua, tb_bool_t lower) { if (lower) CharLowerBuffW((LPWSTR)wb, (DWORD)wn); else CharUpperBuffW((LPWSTR)wb, (DWORD)wn); #else + // attempt to set utf-8 locale for towlower/towupper + // we need this on some platforms like DragonFly BSD, because towlower/towupper depends on the current locale +#ifdef TB_CONFIG_LIBC_HAVE_SETLOCALE + tb_char_t* old_locale = tb_strdup(setlocale(LC_ALL, tb_null)); + tb_setlocale(); +#endif tb_size_t i = 0; for (i = 0; i < wn; i++) { wb[i] = lower? towlower(wb[i]) : towupper(wb[i]); } + +#ifdef TB_CONFIG_LIBC_HAVE_SETLOCALE + if (old_locale) { + setlocale(LC_ALL, old_locale); + tb_free(old_locale); + } +#endif #endif // convert to utf8 -- cgit v1.3.1 From 6971813c1ecfb51ecdd49a19ed6471c0680973e2 Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 09:56:27 +0300 Subject: wrap --- core/src/xmake/string/case.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index 777148623..28212a389 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -69,7 +69,8 @@ static tb_int_t xm_string_case(lua_State* lua, tb_bool_t lower) { // attempt to set utf-8 locale for towlower/towupper // we need this on some platforms like DragonFly BSD, because towlower/towupper depends on the current locale #ifdef TB_CONFIG_LIBC_HAVE_SETLOCALE - tb_char_t* old_locale = tb_strdup(setlocale(LC_ALL, tb_null)); + tb_char_t const* old_locale_str = setlocale(LC_ALL, tb_null); + tb_char_t* old_locale = old_locale_str? tb_strdup(old_locale_str) : tb_null; tb_setlocale(); #endif tb_size_t i = 0; -- cgit v1.3.1 From cd86127a0a961d1872ced3f578943741bbfa66ae Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 16:32:33 +0300 Subject: add spinlock to protect locale modification on non-Windows platforms --- core/src/xmake/string/case.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index 28212a389..18b54b6a6 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -31,10 +31,24 @@ #include "prefix.h" #include #include "tbox/libc/stdlib/setlocale.h" +/* Include Tbox platform header for spinlocks if not already in prefix.h */ +#include "tbox/platform/spinlock.h" + #ifdef TB_CONFIG_OS_WINDOWS # include #endif +/* ////////////////////////////////////////////////////////////////////////////////////// + * globals + */ + +/* + * Define a static Tbox spinlock to protect the non-thread-safe setlocale calls. + * This ensures that on non-Windows platforms, only one thread modifies the + * global locale at a time. + */ +static tb_spinlock_t g_locale_lock = TB_SPINLOCK_INIT; + /* ////////////////////////////////////////////////////////////////////////////////////// * helper */ @@ -66,6 +80,7 @@ static tb_int_t xm_string_case(lua_State* lua, tb_bool_t lower) { if (lower) CharLowerBuffW((LPWSTR)wb, (DWORD)wn); else CharUpperBuffW((LPWSTR)wb, (DWORD)wn); #else + tb_spinlock_enter(&g_locale_lock); // attempt to set utf-8 locale for towlower/towupper // we need this on some platforms like DragonFly BSD, because towlower/towupper depends on the current locale #ifdef TB_CONFIG_LIBC_HAVE_SETLOCALE @@ -84,6 +99,8 @@ static tb_int_t xm_string_case(lua_State* lua, tb_bool_t lower) { tb_free(old_locale); } #endif + /* Unlock after locale is restored */ + tb_spinlock_leave(&g_locale_lock); #endif // convert to utf8 -- cgit v1.3.1 From ef09020c7e5639701c167aa34fbfe4f3107cc015 Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 17:16:52 +0300 Subject: test --- core/src/xmake/string/case.c | 54 ++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index 18b54b6a6..7fd355519 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -36,19 +36,13 @@ #ifdef TB_CONFIG_OS_WINDOWS # include +#else +# include +# if TB_CONFIG_OS_MACOS || TB_CONFIG_OS_IOS || TB_CONFIG_OS_BSD +# include +# endif #endif -/* ////////////////////////////////////////////////////////////////////////////////////// - * globals - */ - -/* - * Define a static Tbox spinlock to protect the non-thread-safe setlocale calls. - * This ensures that on non-Windows platforms, only one thread modifies the - * global locale at a time. - */ -static tb_spinlock_t g_locale_lock = TB_SPINLOCK_INIT; - /* ////////////////////////////////////////////////////////////////////////////////////// * helper */ @@ -79,29 +73,35 @@ static tb_int_t xm_string_case(lua_State* lua, tb_bool_t lower) { #ifdef TB_CONFIG_OS_WINDOWS if (lower) CharLowerBuffW((LPWSTR)wb, (DWORD)wn); else CharUpperBuffW((LPWSTR)wb, (DWORD)wn); -#else - tb_spinlock_enter(&g_locale_lock); - // attempt to set utf-8 locale for towlower/towupper - // we need this on some platforms like DragonFly BSD, because towlower/towupper depends on the current locale -#ifdef TB_CONFIG_LIBC_HAVE_SETLOCALE - tb_char_t const* old_locale_str = setlocale(LC_ALL, tb_null); - tb_char_t* old_locale = old_locale_str? tb_strdup(old_locale_str) : tb_null; - tb_setlocale(); -#endif +#else + locale_t new_loc = (locale_t)0; + locale_t old_loc = (locale_t)0; + + // Create a temporary UTF-8 locale object + // LC_CTYPE_MASK: We only care about character classification/case + new_loc = newlocale(LC_CTYPE_MASK, "UTF-8", (locale_t)0); + if (!new_loc) { + // Fallback if system calls it en_US.UTF-8 + new_loc = newlocale(LC_CTYPE_MASK, "en_US.UTF-8", (locale_t)0); + } + + // Switch thread locale (No global lock needed!) + if (new_loc) { + old_loc = uselocale(new_loc); + } + + // Convert tb_size_t i = 0; for (i = 0; i < wn; i++) { wb[i] = lower? towlower(wb[i]) : towupper(wb[i]); } -#ifdef TB_CONFIG_LIBC_HAVE_SETLOCALE - if (old_locale) { - setlocale(LC_ALL, old_locale); - tb_free(old_locale); + // Restore thread locale and free object + if (new_loc) { + uselocale(old_loc); + freelocale(new_loc); } #endif - /* Unlock after locale is restored */ - tb_spinlock_leave(&g_locale_lock); -#endif // convert to utf8 tb_size_t un = (wn + 1) * 4; -- cgit v1.3.1 From e1e209f8e1c0f40b8cdbcfd647f0711455a6353a Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 17:21:56 +0300 Subject: cls --- core/src/xmake/string/case.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index 7fd355519..6f8e35522 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -30,9 +30,6 @@ */ #include "prefix.h" #include -#include "tbox/libc/stdlib/setlocale.h" -/* Include Tbox platform header for spinlocks if not already in prefix.h */ -#include "tbox/platform/spinlock.h" #ifdef TB_CONFIG_OS_WINDOWS # include -- cgit v1.3.1 From b8aeb70cb4636e746d3655b2f5ba38fe7fdd2bf9 Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 17:34:38 +0300 Subject: test --- core/src/xmake/string/case.c | 52 +++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index 6f8e35522..f66849597 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -35,7 +35,7 @@ # include #else # include -# if TB_CONFIG_OS_MACOS || TB_CONFIG_OS_IOS || TB_CONFIG_OS_BSD +# if defined(TB_CONFIG_OS_MACOS) || defined(TB_CONFIG_OS_IOS) || defined(TB_CONFIG_OS_BSD) # include # endif #endif @@ -59,70 +59,62 @@ static tb_int_t xm_string_case(lua_State* lua, tb_bool_t lower) { return 1; } - // convert to wchar + tb_bool_t ok = tb_false; tb_size_t wn = size + 1; tb_wchar_t* wb = tb_nalloc_type(wn, tb_wchar_t); + if (wb) { + // Convert input UTF-8 to Wide Char wn = tb_mbstowcs(wb, str, wn); if (wn != -1) { - // to case + // Perform Case Conversion #ifdef TB_CONFIG_OS_WINDOWS + // Windows API is naturally thread-safe for this operation if (lower) CharLowerBuffW((LPWSTR)wb, (DWORD)wn); else CharUpperBuffW((LPWSTR)wb, (DWORD)wn); #else - locale_t new_loc = (locale_t)0; - locale_t old_loc = (locale_t)0; - - // Create a temporary UTF-8 locale object - // LC_CTYPE_MASK: We only care about character classification/case - new_loc = newlocale(LC_CTYPE_MASK, "UTF-8", (locale_t)0); + // POSIX Thread-Safe Implementation + // We strictly only convert if we can get a valid UTF-8 thread locale. + // If newlocale fails, we skip conversion to avoid using an undefined global locale. + locale_t new_loc = newlocale(LC_CTYPE_MASK, "UTF-8", (locale_t)0); if (!new_loc) { - // Fallback if system calls it en_US.UTF-8 new_loc = newlocale(LC_CTYPE_MASK, "en_US.UTF-8", (locale_t)0); } - // Switch thread locale (No global lock needed!) if (new_loc) { - old_loc = uselocale(new_loc); - } - - // Convert - tb_size_t i = 0; - for (i = 0; i < wn; i++) { - wb[i] = lower? towlower(wb[i]) : towupper(wb[i]); - } + locale_t old_loc = uselocale(new_loc); + + tb_size_t i = 0; + for (i = 0; i < wn; i++) { + wb[i] = lower ? towlower(wb[i]) : towupper(wb[i]); + } - // Restore thread locale and free object - if (new_loc) { uselocale(old_loc); freelocale(new_loc); } #endif - // convert to utf8 + // Convert Wide Char back to UTF-8 tb_size_t un = (wn + 1) * 4; tb_char_t* ub = (tb_char_t*)tb_malloc_bytes(un); if (ub) { tb_size_t n = tb_wcstombs(ub, wb, un); if (n != -1) { lua_pushlstring(lua, ub, n); - } else { - lua_pushlstring(lua, str, size); + ok = tb_true; } tb_free(ub); - } else { - lua_pushlstring(lua, str, size); } - } else { - lua_pushlstring(lua, str, size); } tb_free(wb); - } else { + } + + // Fallback: If memory allocation failed or conversion failed, return original string + if (!ok) { lua_pushlstring(lua, str, size); } - // ok return 1; } -- cgit v1.3.1 From 48654429c7445533316365bfaaaa73f67d21ab5f Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 17:46:10 +0300 Subject: test --- core/src/xmake/string/case.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index f66849597..5310bdfb2 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -29,13 +29,13 @@ * includes */ #include "prefix.h" -#include #ifdef TB_CONFIG_OS_WINDOWS # include #else +# include # include -# if defined(TB_CONFIG_OS_MACOS) || defined(TB_CONFIG_OS_IOS) || defined(TB_CONFIG_OS_BSD) +# if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) # include # endif #endif -- cgit v1.3.1 From ba533f3e4f6bac865240411595bcd435d857140b Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 17:58:03 +0300 Subject: try check --- core/src/xmake/string/case.c | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index 5310bdfb2..69012fe99 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -67,16 +67,29 @@ static tb_int_t xm_string_case(lua_State* lua, tb_bool_t lower) { // Convert input UTF-8 to Wide Char wn = tb_mbstowcs(wb, str, wn); if (wn != -1) { - // Perform Case Conversion + tb_size_t i = 0; #ifdef TB_CONFIG_OS_WINDOWS - // Windows API is naturally thread-safe for this operation - if (lower) CharLowerBuffW((LPWSTR)wb, (DWORD)wn); - else CharUpperBuffW((LPWSTR)wb, (DWORD)wn); -#else + // Windows Implementation + // We iterate manually to check the state before converting + for (i = 0; i < wn; i++) { + tb_wchar_t wc = wb[i]; + if (lower) { + // Check if already lower; if not, convert + if (!IsCharLowerW(wc)) { + // CharLowerBuffW operates in-place on the buffer + CharLowerBuffW(&wb[i], 1); + } + } else { + // Check if already upper; if not, convert + if (!IsCharUpperW(wc)) { + CharUpperBuffW(&wb[i], 1); + } + } + } +#else // POSIX Thread-Safe Implementation // We strictly only convert if we can get a valid UTF-8 thread locale. - // If newlocale fails, we skip conversion to avoid using an undefined global locale. locale_t new_loc = newlocale(LC_CTYPE_MASK, "UTF-8", (locale_t)0); if (!new_loc) { new_loc = newlocale(LC_CTYPE_MASK, "en_US.UTF-8", (locale_t)0); @@ -84,10 +97,13 @@ static tb_int_t xm_string_case(lua_State* lua, tb_bool_t lower) { if (new_loc) { locale_t old_loc = uselocale(new_loc); - - tb_size_t i = 0; for (i = 0; i < wn; i++) { - wb[i] = lower ? towlower(wb[i]) : towupper(wb[i]); + tb_wchar_t wc = wb[i]; + if (lower) { + if (!iswlower(wc)) wb[i] = towlower(wc); + } else { + if (!iswupper(wc)) wb[i] = towupper(wc); + } } uselocale(old_loc); -- cgit v1.3.1 From 69c0e3587353d67ee6bbf933b89c8b476d719748 Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 18:24:20 +0300 Subject: Check ascii --- core/src/xmake/string/case.c | 189 ++++++++++++++++++++++++++++--------------- 1 file changed, 123 insertions(+), 66 deletions(-) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index 69012fe99..fc6d5f84d 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -29,6 +29,7 @@ * includes */ #include "prefix.h" +#include #ifdef TB_CONFIG_OS_WINDOWS # include @@ -40,6 +41,119 @@ # endif #endif +/* ////////////////////////////////////////////////////////////////////////////////////// + * private implementation + */ + +/* Check if string contains only ASCII characters (all bytes < 0x80) + */ +static tb_bool_t xm_string_is_ascii(tb_char_t const* str, tb_size_t size) { + tb_size_t i; + for (i = 0; i < size; i++) { + if ((tb_byte_t)str[i] >= 0x80) { + return tb_false; + } + } + return tb_true; +} + +/* Fast ASCII-only case conversion using tb_char_t + * Returns tb_true on success, tb_false on failure + */ +static tb_bool_t xm_string_case_ascii(lua_State* lua, tb_char_t const* str, tb_size_t size, tb_bool_t lower) { + tb_char_t* buf = (tb_char_t*)tb_malloc_bytes(size); + if (!buf) { + return tb_false; + } + + tb_size_t i; + for (i = 0; i < size; i++) { + tb_byte_t c = (tb_byte_t)str[i]; + if (lower) { + buf[i] = (tb_char_t)tolower(c); + } else { + buf[i] = (tb_char_t)toupper(c); + } + } + + lua_pushlstring(lua, buf, size); + tb_free(buf); + return tb_true; +} + +/* Unicode case conversion using wide characters + * Returns tb_true on success, tb_false on failure + */ +static tb_bool_t xm_string_case_unicode(lua_State* lua, tb_char_t const* str, tb_size_t size, tb_bool_t lower) { + tb_bool_t ok = tb_false; + tb_size_t wn = size + 1; + tb_wchar_t* wb = tb_nalloc_type(wn, tb_wchar_t); + + if (!wb) { + return tb_false; + } + + // Convert input UTF-8 to Wide Char + wn = tb_mbstowcs(wb, str, wn); + if (wn == (tb_size_t)-1) { + tb_free(wb); + return tb_false; + } + + // Perform Case Conversion + tb_size_t i; +#ifdef TB_CONFIG_OS_WINDOWS + // Windows Implementation + for (i = 0; i < wn; i++) { + tb_wchar_t wc = wb[i]; + if (lower) { + if (!IsCharLowerW(wc)) { + CharLowerBuffW(&wb[i], 1); + } + } else { + if (!IsCharUpperW(wc)) { + CharUpperBuffW(&wb[i], 1); + } + } + } +#else + // POSIX Thread-Safe Implementation + locale_t new_loc = newlocale(LC_CTYPE_MASK, "UTF-8", (locale_t)0); + if (!new_loc) { + new_loc = newlocale(LC_CTYPE_MASK, "en_US.UTF-8", (locale_t)0); + } + + if (new_loc) { + locale_t old_loc = uselocale(new_loc); + for (i = 0; i < wn; i++) { + tb_wchar_t wc = wb[i]; + if (lower) { + if (!iswlower(wc)) wb[i] = towlower(wc); + } else { + if (!iswupper(wc)) wb[i] = towupper(wc); + } + } + uselocale(old_loc); + freelocale(new_loc); + } +#endif + + // Convert Wide Char back to UTF-8 + tb_size_t un = (wn + 1) * 4; + tb_char_t* ub = (tb_char_t*)tb_malloc_bytes(un); + if (ub) { + tb_size_t n = tb_wcstombs(ub, wb, un); + if (n != (tb_size_t)-1) { + lua_pushlstring(lua, ub, n); + ok = tb_true; + } + tb_free(ub); + } + + tb_free(wb); + return ok; +} + /* ////////////////////////////////////////////////////////////////////////////////////// * helper */ @@ -59,78 +173,21 @@ static tb_int_t xm_string_case(lua_State* lua, tb_bool_t lower) { return 1; } - tb_bool_t ok = tb_false; - tb_size_t wn = size + 1; - tb_wchar_t* wb = tb_nalloc_type(wn, tb_wchar_t); - - if (wb) { - // Convert input UTF-8 to Wide Char - wn = tb_mbstowcs(wb, str, wn); - if (wn != -1) { - // Perform Case Conversion - tb_size_t i = 0; -#ifdef TB_CONFIG_OS_WINDOWS - // Windows Implementation - // We iterate manually to check the state before converting - for (i = 0; i < wn; i++) { - tb_wchar_t wc = wb[i]; - if (lower) { - // Check if already lower; if not, convert - if (!IsCharLowerW(wc)) { - // CharLowerBuffW operates in-place on the buffer - CharLowerBuffW(&wb[i], 1); - } - } else { - // Check if already upper; if not, convert - if (!IsCharUpperW(wc)) { - CharUpperBuffW(&wb[i], 1); - } - } - } -#else - // POSIX Thread-Safe Implementation - // We strictly only convert if we can get a valid UTF-8 thread locale. - locale_t new_loc = newlocale(LC_CTYPE_MASK, "UTF-8", (locale_t)0); - if (!new_loc) { - new_loc = newlocale(LC_CTYPE_MASK, "en_US.UTF-8", (locale_t)0); - } + tb_bool_t ok; - if (new_loc) { - locale_t old_loc = uselocale(new_loc); - for (i = 0; i < wn; i++) { - tb_wchar_t wc = wb[i]; - if (lower) { - if (!iswlower(wc)) wb[i] = towlower(wc); - } else { - if (!iswupper(wc)) wb[i] = towupper(wc); - } - } - - uselocale(old_loc); - freelocale(new_loc); - } -#endif - - // Convert Wide Char back to UTF-8 - tb_size_t un = (wn + 1) * 4; - tb_char_t* ub = (tb_char_t*)tb_malloc_bytes(un); - if (ub) { - tb_size_t n = tb_wcstombs(ub, wb, un); - if (n != -1) { - lua_pushlstring(lua, ub, n); - ok = tb_true; - } - tb_free(ub); - } - } - tb_free(wb); + // Fast path: ASCII-only strings don't need wide character conversion + if (xm_string_is_ascii(str, size)) { + ok = xm_string_case_ascii(lua, str, size, lower); + } else { + // Slow path: Unicode strings need wide character conversion + ok = xm_string_case_unicode(lua, str, size, lower); } - // Fallback: If memory allocation failed or conversion failed, return original string + // Fallback: return original string if conversion failed if (!ok) { lua_pushlstring(lua, str, size); } - + return 1; } -- cgit v1.3.1 From 6ace7f776cd21b91323e00d1bfc61f6722a7961d Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 18:48:12 +0300 Subject: Try to prioritize "en_US.UTF-8" --- core/src/xmake/string/case.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index fc6d5f84d..1bf1c022d 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -118,9 +118,9 @@ static tb_bool_t xm_string_case_unicode(lua_State* lua, tb_char_t const* str, tb } #else // POSIX Thread-Safe Implementation - locale_t new_loc = newlocale(LC_CTYPE_MASK, "UTF-8", (locale_t)0); + locale_t new_loc = newlocale(LC_CTYPE_MASK, "en_US.UTF-8", (locale_t)0); if (!new_loc) { - new_loc = newlocale(LC_CTYPE_MASK, "en_US.UTF-8", (locale_t)0); + new_loc = newlocale(LC_CTYPE_MASK, "UTF-8", (locale_t)0); } if (new_loc) { -- cgit v1.3.1 From 769cd6b401b4f33790d5361c2eb16aa3aac8fa1c Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 19:10:32 +0300 Subject: try build netbsd --- core/src/xmake/string/case.c | 54 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index 1bf1c022d..06b98de41 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -30,6 +30,8 @@ */ #include "prefix.h" #include +/* Include Tbox platform header for spinlocks */ +#include "tbox/platform/spinlock.h" #ifdef TB_CONFIG_OS_WINDOWS # include @@ -38,9 +40,21 @@ # include # if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) # include +# elif defined(__NetBSD__) +# include # endif #endif +/* ////////////////////////////////////////////////////////////////////////////////////// + * globals + */ + +/* + * Define a static Tbox spinlock. + * setlocale() is not thread-safe on Windows or Linux, so we protect it globally. + */ +static tb_spinlock_t g_locale_lock = TB_SPINLOCK_INIT; + /* ////////////////////////////////////////////////////////////////////////////////////// * private implementation */ @@ -70,9 +84,9 @@ static tb_bool_t xm_string_case_ascii(lua_State* lua, tb_char_t const* str, tb_s for (i = 0; i < size; i++) { tb_byte_t c = (tb_byte_t)str[i]; if (lower) { - buf[i] = (tb_char_t)tolower(c); + buf[i] = (tb_char_t)(isupper(c)? tolower(c) : c); } else { - buf[i] = (tb_char_t)toupper(c); + buf[i] = (tb_char_t)(islower(c)? toupper(c) : c); } } @@ -117,6 +131,41 @@ static tb_bool_t xm_string_case_unicode(lua_State* lua, tb_char_t const* str, tb } } #else +# if defined(__NetBSD__) + // NetBSD lacks of uselocale + tb_spinlock_enter(&g_locale_lock); + + char* saved_locale = tb_null; + char* current_locale = setlocale(LC_CTYPE, NULL); + + if (current_locale) { + size_t len = tb_strlen(current_locale); + saved_locale = (char*)tb_malloc_bytes(len + 1); + if (saved_locale) { + tb_memcpy(saved_locale, current_locale, len + 1); + } + } + + if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL) { + setlocale(LC_CTYPE, "UTF-8"); + } + + for (i = 0; i < wn; i++) { + tb_wchar_t wc = wb[i]; + if (lower) { + if (!iswlower(wc)) wb[i] = towlower(wc); + } else { + if (!iswupper(wc)) wb[i] = towupper(wc); + } + } + + if (saved_locale) { + setlocale(LC_CTYPE, saved_locale); + tb_free(saved_locale); + } + + tb_spinlock_leave(&g_locale_lock); +# else // POSIX Thread-Safe Implementation locale_t new_loc = newlocale(LC_CTYPE_MASK, "en_US.UTF-8", (locale_t)0); if (!new_loc) { @@ -136,6 +185,7 @@ static tb_bool_t xm_string_case_unicode(lua_State* lua, tb_char_t const* str, tb uselocale(old_loc); freelocale(new_loc); } +# endif #endif // Convert Wide Char back to UTF-8 -- cgit v1.3.1 From e075b6cd6c49b28cd595f7f0db8907c27fb0ef28 Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 19:15:51 +0300 Subject: fixup --- core/src/xmake/string/case.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index 06b98de41..0bdef51a3 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -42,9 +42,6 @@ # include # elif defined(__NetBSD__) # include -# endif -#endif - /* ////////////////////////////////////////////////////////////////////////////////////// * globals */ @@ -54,6 +51,9 @@ * setlocale() is not thread-safe on Windows or Linux, so we protect it globally. */ static tb_spinlock_t g_locale_lock = TB_SPINLOCK_INIT; +# endif +#endif + /* ////////////////////////////////////////////////////////////////////////////////////// * private implementation -- cgit v1.3.1 From 33bc0b8128a019a019378a08f789f8062b320fa1 Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 19:38:05 +0300 Subject: fixup --- core/src/xmake/string/case.c | 116 ++++++++++++++++++++----------------------- 1 file changed, 54 insertions(+), 62 deletions(-) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index 0bdef51a3..3bbee07b6 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -30,31 +30,34 @@ */ #include "prefix.h" #include -/* Include Tbox platform header for spinlocks */ +#include #include "tbox/platform/spinlock.h" -#ifdef TB_CONFIG_OS_WINDOWS -# include -#else -# include -# include -# if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) -# include -# elif defined(__NetBSD__) -# include +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) +# include +#endif + +/* ////////////////////////////////////////////////////////////////////////////////////// + * macros + */ + +/* Platforms that lack thread-safe uselocale() and must use setlocale() */ +#if defined(TB_CONFIG_OS_WINDOWS) || defined(__NetBSD__) +# include "tbox/libc/stdlib/setlocale.h" +# define XM_USE_SETLOCALE +#endif + /* ////////////////////////////////////////////////////////////////////////////////////// * globals */ -/* - * Define a static Tbox spinlock. - * setlocale() is not thread-safe on Windows or Linux, so we protect it globally. +#ifdef XM_USE_SETLOCALE +/* + * setlocale() is not thread-safe, so we protect it with a spinlock. */ static tb_spinlock_t g_locale_lock = TB_SPINLOCK_INIT; -# endif #endif - /* ////////////////////////////////////////////////////////////////////////////////////// * private implementation */ @@ -84,9 +87,9 @@ static tb_bool_t xm_string_case_ascii(lua_State* lua, tb_char_t const* str, tb_s for (i = 0; i < size; i++) { tb_byte_t c = (tb_byte_t)str[i]; if (lower) { - buf[i] = (tb_char_t)(isupper(c)? tolower(c) : c); + buf[i] = (tb_char_t)(isupper(c) ? tolower(c) : c); } else { - buf[i] = (tb_char_t)(islower(c)? toupper(c) : c); + buf[i] = (tb_char_t)(islower(c) ? toupper(c) : c); } } @@ -95,6 +98,20 @@ static tb_bool_t xm_string_case_ascii(lua_State* lua, tb_char_t const* str, tb_s return tb_true; } +/* Perform wide character case conversion in-place + */ +static tb_void_t xm_wchar_convert_case(tb_wchar_t* wb, tb_size_t wn, tb_bool_t lower) { + tb_size_t i; + for (i = 0; i < wn; i++) { + tb_wchar_t wc = wb[i]; + if (lower) { + if (!iswlower(wc)) wb[i] = towlower(wc); + } else { + if (!iswupper(wc)) wb[i] = towupper(wc); + } + } +} + /* Unicode case conversion using wide characters * Returns tb_true on success, tb_false on failure */ @@ -115,58 +132,38 @@ static tb_bool_t xm_string_case_unicode(lua_State* lua, tb_char_t const* str, tb } // Perform Case Conversion - tb_size_t i; -#ifdef TB_CONFIG_OS_WINDOWS - // Windows Implementation - for (i = 0; i < wn; i++) { - tb_wchar_t wc = wb[i]; - if (lower) { - if (!IsCharLowerW(wc)) { - CharLowerBuffW(&wb[i], 1); - } - } else { - if (!IsCharUpperW(wc)) { - CharUpperBuffW(&wb[i], 1); - } - } - } -#else -# if defined(__NetBSD__) - // NetBSD lacks of uselocale +#ifdef XM_USE_SETLOCALE + // Windows and NetBSD: Use setlocale (protected by spinlock) tb_spinlock_enter(&g_locale_lock); - char* saved_locale = tb_null; - char* current_locale = setlocale(LC_CTYPE, NULL); - + // Save current locale + char* saved_locale = tb_null; + char const* current_locale = setlocale(LC_CTYPE, tb_null); + if (current_locale) { - size_t len = tb_strlen(current_locale); + tb_size_t len = tb_strlen(current_locale); saved_locale = (char*)tb_malloc_bytes(len + 1); if (saved_locale) { tb_memcpy(saved_locale, current_locale, len + 1); } } - if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL) { - setlocale(LC_CTYPE, "UTF-8"); - } + // Force UTF-8 locale + tb_setlocale(); - for (i = 0; i < wn; i++) { - tb_wchar_t wc = wb[i]; - if (lower) { - if (!iswlower(wc)) wb[i] = towlower(wc); - } else { - if (!iswupper(wc)) wb[i] = towupper(wc); - } - } + // Convert case + xm_wchar_convert_case(wb, wn, lower); + // Restore original locale if (saved_locale) { setlocale(LC_CTYPE, saved_locale); tb_free(saved_locale); } - + tb_spinlock_leave(&g_locale_lock); -# else - // POSIX Thread-Safe Implementation + +#else + // POSIX Thread-Safe Implementation (Linux/macOS/FreeBSD/DragonFly) locale_t new_loc = newlocale(LC_CTYPE_MASK, "en_US.UTF-8", (locale_t)0); if (!new_loc) { new_loc = newlocale(LC_CTYPE_MASK, "UTF-8", (locale_t)0); @@ -174,18 +171,13 @@ static tb_bool_t xm_string_case_unicode(lua_State* lua, tb_char_t const* str, tb if (new_loc) { locale_t old_loc = uselocale(new_loc); - for (i = 0; i < wn; i++) { - tb_wchar_t wc = wb[i]; - if (lower) { - if (!iswlower(wc)) wb[i] = towlower(wc); - } else { - if (!iswupper(wc)) wb[i] = towupper(wc); - } - } + xm_wchar_convert_case(wb, wn, lower); uselocale(old_loc); freelocale(new_loc); + } else { + // Fallback: try without locale change + xm_wchar_convert_case(wb, wn, lower); } -# endif #endif // Convert Wide Char back to UTF-8 -- cgit v1.3.1 From a26410744069a9dfd1ffa6f6e362ed1633e2b650 Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 19:42:59 +0300 Subject: fixup --- core/src/xmake/string/case.c | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index 3bbee07b6..c6622145a 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -31,6 +31,7 @@ #include "prefix.h" #include #include +#include #include "tbox/platform/spinlock.h" #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) -- cgit v1.3.1 From 77c48ebd4820329582f9e41d26de648445c692a8 Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 20:07:35 +0300 Subject: re --- core/src/xmake/string/case.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index c6622145a..aee3647e0 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -166,6 +166,9 @@ static tb_bool_t xm_string_case_unicode(lua_State* lua, tb_char_t const* str, tb #else // POSIX Thread-Safe Implementation (Linux/macOS/FreeBSD/DragonFly) locale_t new_loc = newlocale(LC_CTYPE_MASK, "en_US.UTF-8", (locale_t)0); + if (!new_loc) { + new_loc = newlocale(LC_CTYPE_MASK, "C.UTF-8", (locale_t)0); + } if (!new_loc) { new_loc = newlocale(LC_CTYPE_MASK, "UTF-8", (locale_t)0); } -- cgit v1.3.1 From b11183e1222db0026c70d85e3582c4b216a49962 Mon Sep 17 00:00:00 2001 From: Saikari Date: Sat, 17 Jan 2026 20:12:18 +0300 Subject: fixup --- core/src/xmake/string/case.c | 85 +++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 45 deletions(-) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index aee3647e0..ea6917b50 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -63,41 +63,6 @@ static tb_spinlock_t g_locale_lock = TB_SPINLOCK_INIT; * private implementation */ -/* Check if string contains only ASCII characters (all bytes < 0x80) - */ -static tb_bool_t xm_string_is_ascii(tb_char_t const* str, tb_size_t size) { - tb_size_t i; - for (i = 0; i < size; i++) { - if ((tb_byte_t)str[i] >= 0x80) { - return tb_false; - } - } - return tb_true; -} - -/* Fast ASCII-only case conversion using tb_char_t - * Returns tb_true on success, tb_false on failure - */ -static tb_bool_t xm_string_case_ascii(lua_State* lua, tb_char_t const* str, tb_size_t size, tb_bool_t lower) { - tb_char_t* buf = (tb_char_t*)tb_malloc_bytes(size); - if (!buf) { - return tb_false; - } - - tb_size_t i; - for (i = 0; i < size; i++) { - tb_byte_t c = (tb_byte_t)str[i]; - if (lower) { - buf[i] = (tb_char_t)(isupper(c) ? tolower(c) : c); - } else { - buf[i] = (tb_char_t)(islower(c) ? toupper(c) : c); - } - } - - lua_pushlstring(lua, buf, size); - tb_free(buf); - return tb_true; -} /* Perform wide character case conversion in-place */ @@ -219,21 +184,51 @@ static tb_int_t xm_string_case(lua_State* lua, tb_bool_t lower) { return 1; } - tb_bool_t ok; + // allocate buffer for optimistic ascii conversion + tb_char_t* buf = (tb_char_t*)tb_malloc_bytes(size); + if (!buf) return 0; - // Fast path: ASCII-only strings don't need wide character conversion - if (xm_string_is_ascii(str, size)) { - ok = xm_string_case_ascii(lua, str, size, lower); - } else { - // Slow path: Unicode strings need wide character conversion - ok = xm_string_case_unicode(lua, str, size, lower); + tb_size_t i; + for (i = 0; i < size; i++) { + // Stop at first non-ascii char + if ((tb_byte_t)str[i] >= 0x80) break; + + // convert ascii + tb_byte_t c = (tb_byte_t)str[i]; + if (lower) { + buf[i] = (tb_char_t)(isupper(c) ? tolower(c) : c); + } else { + buf[i] = (tb_char_t)(islower(c) ? toupper(c) : c); + } + } + + // all ascii? + if (i == size) { + lua_pushlstring(lua, buf, size); + tb_free(buf); + return 1; } - // Fallback: return original string if conversion failed - if (!ok) { - lua_pushlstring(lua, str, size); + // push the converted ascii prefix + if (i > 0) { + lua_pushlstring(lua, buf, i); } + tb_free(buf); + // convert the remaining unicode suffix + if (xm_string_case_unicode(lua, str + i, size - i, lower)) { + // concat prefix and suffix: prefix (if any) + suffix + if (i > 0) { + lua_concat(lua, 2); + } + return 1; + } + + // failed? return original string + if (i > 0) { + lua_pop(lua, 1); + } + lua_pushlstring(lua, str, size); return 1; } -- cgit v1.3.1 From 3946b4ea50870d19e66df992520808eb74d1b1f7 Mon Sep 17 00:00:00 2001 From: Saikari Date: Sun, 18 Jan 2026 05:00:52 +0300 Subject: test commit --- core/src/tbox/tbox | 2 +- core/src/xmake/string/case.c | 98 ++------------------------------------------ 2 files changed, 5 insertions(+), 95 deletions(-) diff --git a/core/src/tbox/tbox b/core/src/tbox/tbox index dbe1674a4..73028bf91 160000 --- a/core/src/tbox/tbox +++ b/core/src/tbox/tbox @@ -1 +1 @@ -Subproject commit dbe1674a47dbf15e6f7b493acc848841d4f8d2b3 +Subproject commit 73028bf9165aece5ec9863124fda67500a005e24 diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index ea6917b50..6dd372d16 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -29,55 +29,13 @@ * includes */ #include "prefix.h" -#include -#include -#include -#include "tbox/platform/spinlock.h" -#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) -# include -#endif - -/* ////////////////////////////////////////////////////////////////////////////////////// - * macros - */ - -/* Platforms that lack thread-safe uselocale() and must use setlocale() */ -#if defined(TB_CONFIG_OS_WINDOWS) || defined(__NetBSD__) -# include "tbox/libc/stdlib/setlocale.h" -# define XM_USE_SETLOCALE -#endif - -/* ////////////////////////////////////////////////////////////////////////////////////// - * globals - */ - -#ifdef XM_USE_SETLOCALE -/* - * setlocale() is not thread-safe, so we protect it with a spinlock. - */ -static tb_spinlock_t g_locale_lock = TB_SPINLOCK_INIT; -#endif /* ////////////////////////////////////////////////////////////////////////////////////// * private implementation */ -/* Perform wide character case conversion in-place - */ -static tb_void_t xm_wchar_convert_case(tb_wchar_t* wb, tb_size_t wn, tb_bool_t lower) { - tb_size_t i; - for (i = 0; i < wn; i++) { - tb_wchar_t wc = wb[i]; - if (lower) { - if (!iswlower(wc)) wb[i] = towlower(wc); - } else { - if (!iswupper(wc)) wb[i] = towupper(wc); - } - } -} - /* Unicode case conversion using wide characters * Returns tb_true on success, tb_false on failure */ @@ -98,56 +56,8 @@ static tb_bool_t xm_string_case_unicode(lua_State* lua, tb_char_t const* str, tb } // Perform Case Conversion -#ifdef XM_USE_SETLOCALE - // Windows and NetBSD: Use setlocale (protected by spinlock) - tb_spinlock_enter(&g_locale_lock); - - // Save current locale - char* saved_locale = tb_null; - char const* current_locale = setlocale(LC_CTYPE, tb_null); - - if (current_locale) { - tb_size_t len = tb_strlen(current_locale); - saved_locale = (char*)tb_malloc_bytes(len + 1); - if (saved_locale) { - tb_memcpy(saved_locale, current_locale, len + 1); - } - } - - // Force UTF-8 locale - tb_setlocale(); - - // Convert case - xm_wchar_convert_case(wb, wn, lower); - - // Restore original locale - if (saved_locale) { - setlocale(LC_CTYPE, saved_locale); - tb_free(saved_locale); - } - - tb_spinlock_leave(&g_locale_lock); - -#else - // POSIX Thread-Safe Implementation (Linux/macOS/FreeBSD/DragonFly) - locale_t new_loc = newlocale(LC_CTYPE_MASK, "en_US.UTF-8", (locale_t)0); - if (!new_loc) { - new_loc = newlocale(LC_CTYPE_MASK, "C.UTF-8", (locale_t)0); - } - if (!new_loc) { - new_loc = newlocale(LC_CTYPE_MASK, "UTF-8", (locale_t)0); - } - - if (new_loc) { - locale_t old_loc = uselocale(new_loc); - xm_wchar_convert_case(wb, wn, lower); - uselocale(old_loc); - freelocale(new_loc); - } else { - // Fallback: try without locale change - xm_wchar_convert_case(wb, wn, lower); - } -#endif + if (lower) tb_wcslwr(wb); + else tb_wcsupr(wb); // Convert Wide Char back to UTF-8 tb_size_t un = (wn + 1) * 4; @@ -196,9 +106,9 @@ static tb_int_t xm_string_case(lua_State* lua, tb_bool_t lower) { // convert ascii tb_byte_t c = (tb_byte_t)str[i]; if (lower) { - buf[i] = (tb_char_t)(isupper(c) ? tolower(c) : c); + buf[i] = (tb_char_t)tb_tolower(c); } else { - buf[i] = (tb_char_t)(islower(c) ? toupper(c) : c); + buf[i] = (tb_char_t)tb_toupper(c); } } -- cgit v1.3.1 From 197a78f0f28b01710b45486fe589580e9b09ff2a Mon Sep 17 00:00:00 2001 From: Saikari Date: Sun, 18 Jan 2026 05:38:50 +0300 Subject: test --- core/src/xmake/string/case.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index 6dd372d16..7bde2da9a 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -29,6 +29,10 @@ * includes */ #include "prefix.h" +#if defined(TB_CONFIG_OS_LINUX) || defined(TB_CONFIG_OS_BSD) || defined(TB_CONFIG_OS_SOLARIS) +# include +# include +#endif /* ////////////////////////////////////////////////////////////////////////////////////// @@ -56,8 +60,28 @@ static tb_bool_t xm_string_case_unicode(lua_State* lua, tb_char_t const* str, tb } // Perform Case Conversion +#if defined(TB_CONFIG_OS_LINUX) || defined(TB_CONFIG_OS_BSD) || defined(TB_CONFIG_OS_SOLARIS) + locale_t loc = newlocale(LC_ALL_MASK, "C.UTF-8", (locale_t)0); + if (!loc) loc = newlocale(LC_ALL_MASK, "en_US.UTF-8", (locale_t)0); + if (!loc) loc = newlocale(LC_ALL_MASK, "C.utf8", (locale_t)0); + if (!loc) loc = newlocale(LC_ALL_MASK, "en_US.utf8", (locale_t)0); + if (loc) { + locale_t old = uselocale(loc); + tb_wchar_t* p = wb; + while (*p) { + *p = lower ? towlower(*p) : towupper(*p); + p++; + } + uselocale(old); + freelocale(loc); + } else { + if (lower) tb_wcslwr(wb); + else tb_wcsupr(wb); + } +#else if (lower) tb_wcslwr(wb); else tb_wcsupr(wb); +#endif // Convert Wide Char back to UTF-8 tb_size_t un = (wn + 1) * 4; -- cgit v1.3.1 From e50e95c8686457bf5dbfc95508e2b756132a2c6a Mon Sep 17 00:00:00 2001 From: Saikari Date: Sun, 18 Jan 2026 07:04:43 +0300 Subject: Update case.c --- core/src/xmake/string/case.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c index 7bde2da9a..498c32fcc 100644 --- a/core/src/xmake/string/case.c +++ b/core/src/xmake/string/case.c @@ -14,7 +14,7 @@ * * Copyright (C) 2015-present, Xmake Open Source Community. * - * @author ruki + * @author luadebug, ruki * @file case.c * */ -- cgit v1.3.1 From 92a5909e5b9466e8425c455383aaa242bb84e48f Mon Sep 17 00:00:00 2001 From: Saikari Date: Sun, 18 Jan 2026 07:29:36 +0300 Subject: try --- core/src/xmake/engine.c | 4 +- core/src/xmake/string/case.c | 178 ------------------------------------------ core/src/xmake/string/lower.c | 74 ++++++++++++++++++ core/src/xmake/string/upper.c | 74 ++++++++++++++++++ 4 files changed, 150 insertions(+), 180 deletions(-) delete mode 100644 core/src/xmake/string/case.c create mode 100644 core/src/xmake/string/lower.c create mode 100644 core/src/xmake/string/upper.c diff --git a/core/src/xmake/engine.c b/core/src/xmake/engine.c index d81828f1f..412b41661 100644 --- a/core/src/xmake/engine.c +++ b/core/src/xmake/engine.c @@ -591,8 +591,8 @@ static luaL_Reg const g_string_functions[] = { { "convert", xm_string_convert }, { "endswith", xm_string_endswith }, { "startswith", xm_string_startswith }, - { "lower", xm_string_lower }, - { "upper", xm_string_upper }, + { "lower", xm_string_tolower }, + { "upper", xm_string_toupper }, { tb_null, tb_null }, }; diff --git a/core/src/xmake/string/case.c b/core/src/xmake/string/case.c deleted file mode 100644 index 498c32fcc..000000000 --- a/core/src/xmake/string/case.c +++ /dev/null @@ -1,178 +0,0 @@ -/*!A cross-platform build utility based on Lua - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Copyright (C) 2015-present, Xmake Open Source Community. - * - * @author luadebug, ruki - * @file case.c - * - */ - -/* ////////////////////////////////////////////////////////////////////////////////////// - * trace - */ -#define TB_TRACE_MODULE_NAME "case" -#define TB_TRACE_MODULE_DEBUG (0) - -/* ////////////////////////////////////////////////////////////////////////////////////// - * includes - */ -#include "prefix.h" -#if defined(TB_CONFIG_OS_LINUX) || defined(TB_CONFIG_OS_BSD) || defined(TB_CONFIG_OS_SOLARIS) -# include -# include -#endif - - -/* ////////////////////////////////////////////////////////////////////////////////////// - * private implementation - */ - - -/* Unicode case conversion using wide characters - * Returns tb_true on success, tb_false on failure - */ -static tb_bool_t xm_string_case_unicode(lua_State* lua, tb_char_t const* str, tb_size_t size, tb_bool_t lower) { - tb_bool_t ok = tb_false; - tb_size_t wn = size + 1; - tb_wchar_t* wb = tb_nalloc_type(wn, tb_wchar_t); - - if (!wb) { - return tb_false; - } - - // Convert input UTF-8 to Wide Char - wn = tb_mbstowcs(wb, str, wn); - if (wn == (tb_size_t)-1) { - tb_free(wb); - return tb_false; - } - - // Perform Case Conversion -#if defined(TB_CONFIG_OS_LINUX) || defined(TB_CONFIG_OS_BSD) || defined(TB_CONFIG_OS_SOLARIS) - locale_t loc = newlocale(LC_ALL_MASK, "C.UTF-8", (locale_t)0); - if (!loc) loc = newlocale(LC_ALL_MASK, "en_US.UTF-8", (locale_t)0); - if (!loc) loc = newlocale(LC_ALL_MASK, "C.utf8", (locale_t)0); - if (!loc) loc = newlocale(LC_ALL_MASK, "en_US.utf8", (locale_t)0); - if (loc) { - locale_t old = uselocale(loc); - tb_wchar_t* p = wb; - while (*p) { - *p = lower ? towlower(*p) : towupper(*p); - p++; - } - uselocale(old); - freelocale(loc); - } else { - if (lower) tb_wcslwr(wb); - else tb_wcsupr(wb); - } -#else - if (lower) tb_wcslwr(wb); - else tb_wcsupr(wb); -#endif - - // Convert Wide Char back to UTF-8 - tb_size_t un = (wn + 1) * 4; - tb_char_t* ub = (tb_char_t*)tb_malloc_bytes(un); - if (ub) { - tb_size_t n = tb_wcstombs(ub, wb, un); - if (n != (tb_size_t)-1) { - lua_pushlstring(lua, ub, n); - ok = tb_true; - } - tb_free(ub); - } - - tb_free(wb); - return ok; -} - -/* ////////////////////////////////////////////////////////////////////////////////////// - * helper - */ -static tb_int_t xm_string_case(lua_State* lua, tb_bool_t lower) { - - // check - tb_assert_and_check_return_val(lua, 0); - - // get the string - size_t size = 0; - tb_char_t const* str = luaL_checklstring(lua, 1, &size); - tb_check_return_val(str, 0); - - // empty? - if (!size) { - lua_pushstring(lua, ""); - return 1; - } - - // allocate buffer for optimistic ascii conversion - tb_char_t* buf = (tb_char_t*)tb_malloc_bytes(size); - if (!buf) return 0; - - tb_size_t i; - for (i = 0; i < size; i++) { - // Stop at first non-ascii char - if ((tb_byte_t)str[i] >= 0x80) break; - - // convert ascii - tb_byte_t c = (tb_byte_t)str[i]; - if (lower) { - buf[i] = (tb_char_t)tb_tolower(c); - } else { - buf[i] = (tb_char_t)tb_toupper(c); - } - } - - // all ascii? - if (i == size) { - lua_pushlstring(lua, buf, size); - tb_free(buf); - return 1; - } - - // push the converted ascii prefix - if (i > 0) { - lua_pushlstring(lua, buf, i); - } - tb_free(buf); - - // convert the remaining unicode suffix - if (xm_string_case_unicode(lua, str + i, size - i, lower)) { - // concat prefix and suffix: prefix (if any) + suffix - if (i > 0) { - lua_concat(lua, 2); - } - return 1; - } - - // failed? return original string - if (i > 0) { - lua_pop(lua, 1); - } - lua_pushlstring(lua, str, size); - return 1; -} - -/* ////////////////////////////////////////////////////////////////////////////////////// - * implementation - */ -tb_int_t xm_string_lower(lua_State* lua) { - return xm_string_case(lua, tb_true); -} - -tb_int_t xm_string_upper(lua_State* lua) { - return xm_string_case(lua, tb_false); -} diff --git a/core/src/xmake/string/lower.c b/core/src/xmake/string/lower.c new file mode 100644 index 000000000..6d9648079 --- /dev/null +++ b/core/src/xmake/string/lower.c @@ -0,0 +1,74 @@ +/*!A cross-platform build utility based on Lua + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (C) 2015-present, Xmake Open Source Community. + * + * @author ruki + * @file lower.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * implementation + */ + +/* string.lower + * + * @param str the string + * + * @code + * local result = string.lower(str) + * @endcode + */ +tb_int_t xm_string_lower(lua_State *lua) { + // check + tb_assert_and_check_return_val(lua, 0); + + // get string + size_t size = 0; + tb_char_t const* cstr = luaL_checklstring(lua, 1, &size); + tb_check_return_val(cstr, 0); + + // empty? + if (!size) { + lua_pushstring(lua, ""); + return 1; + } + + // copy string to buffer + tb_char_t* buffer = tb_malloc_bytes(size + 1); + if (buffer) { + tb_memcpy(buffer, cstr, size); + buffer[size] = '\0'; + + // to lower + tb_long_t real_size = tb_charset_utf8_tolower(buffer, size); + + // push result + if (real_size >= 0) { + lua_pushlstring(lua, buffer, real_size); + } else { + lua_pushlstring(lua, cstr, size); + } + tb_free(buffer); + } else { + lua_pushnil(lua); + } + return 1; +} diff --git a/core/src/xmake/string/upper.c b/core/src/xmake/string/upper.c new file mode 100644 index 000000000..313e268a1 --- /dev/null +++ b/core/src/xmake/string/upper.c @@ -0,0 +1,74 @@ +/*!A cross-platform build utility based on Lua + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (C) 2015-present, Xmake Open Source Community. + * + * @author ruki + * @file upper.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * implementation + */ + +/* string.upper + * + * @param str the string + * + * @code + * local result = (str) + * @endcode + */ +tb_int_t xm_string_upper(lua_State *lua) { + // check + tb_assert_and_check_return_val(lua, 0); + + // get string + size_t size = 0; + tb_char_t const* cstr = luaL_checklstring(lua, 1, &size); + tb_check_return_val(cstr, 0); + + // empty? + if (!size) { + lua_pushstring(lua, ""); + return 1; + } + + // copy string to buffer + tb_char_t* buffer = tb_malloc_bytes(size + 1); + if (buffer) { + tb_memcpy(buffer, cstr, size); + buffer[size] = '\0'; + + // to upper + tb_long_t real_size = tb_charset_utf8_toupper(buffer, size); + + // push result + if (real_size >= 0) { + lua_pushlstring(lua, buffer, real_size); + } else { + lua_pushlstring(lua, cstr, size); + } + tb_free(buffer); + } else { + lua_pushnil(lua); + } + return 1; +} -- cgit v1.3.1 From 119b5258e159100e86c70e1c30432a7ab6e179ac Mon Sep 17 00:00:00 2001 From: Saikari Date: Sun, 18 Jan 2026 07:30:17 +0300 Subject: fixup --- core/src/xmake/string/lower.c | 2 +- core/src/xmake/string/upper.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/xmake/string/lower.c b/core/src/xmake/string/lower.c index 6d9648079..1837d81ec 100644 --- a/core/src/xmake/string/lower.c +++ b/core/src/xmake/string/lower.c @@ -14,7 +14,7 @@ * * Copyright (C) 2015-present, Xmake Open Source Community. * - * @author ruki + * @author luadebug, ruki * @file lower.c * */ diff --git a/core/src/xmake/string/upper.c b/core/src/xmake/string/upper.c index 313e268a1..ba625bde4 100644 --- a/core/src/xmake/string/upper.c +++ b/core/src/xmake/string/upper.c @@ -14,7 +14,7 @@ * * Copyright (C) 2015-present, Xmake Open Source Community. * - * @author ruki + * @author luadebug, ruki * @file upper.c * */ -- cgit v1.3.1 From 60b461f7af91ca0429c8a863dbe6c8e7be2a77ed Mon Sep 17 00:00:00 2001 From: Saikari Date: Sun, 18 Jan 2026 07:33:03 +0300 Subject: Update engine.c --- core/src/xmake/engine.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/xmake/engine.c b/core/src/xmake/engine.c index 412b41661..d81828f1f 100644 --- a/core/src/xmake/engine.c +++ b/core/src/xmake/engine.c @@ -591,8 +591,8 @@ static luaL_Reg const g_string_functions[] = { { "convert", xm_string_convert }, { "endswith", xm_string_endswith }, { "startswith", xm_string_startswith }, - { "lower", xm_string_tolower }, - { "upper", xm_string_toupper }, + { "lower", xm_string_lower }, + { "upper", xm_string_upper }, { tb_null, tb_null }, }; -- cgit v1.3.1 From a1c0eaed71793cd3761dbcf6b2a4cab6c6474434 Mon Sep 17 00:00:00 2001 From: Saikari Date: Sun, 18 Jan 2026 07:40:14 +0300 Subject: re --- core/src/xmake/string/lower.c | 2 +- core/src/xmake/string/upper.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/xmake/string/lower.c b/core/src/xmake/string/lower.c index 1837d81ec..ef43f2385 100644 --- a/core/src/xmake/string/lower.c +++ b/core/src/xmake/string/lower.c @@ -52,7 +52,7 @@ tb_int_t xm_string_lower(lua_State *lua) { } // copy string to buffer - tb_char_t* buffer = tb_malloc_bytes(size + 1); + tb_char_t* buffer = (tb_char_t*)tb_malloc_bytes(size + 1); if (buffer) { tb_memcpy(buffer, cstr, size); buffer[size] = '\0'; diff --git a/core/src/xmake/string/upper.c b/core/src/xmake/string/upper.c index ba625bde4..b9b71b349 100644 --- a/core/src/xmake/string/upper.c +++ b/core/src/xmake/string/upper.c @@ -52,7 +52,7 @@ tb_int_t xm_string_upper(lua_State *lua) { } // copy string to buffer - tb_char_t* buffer = tb_malloc_bytes(size + 1); + tb_char_t* buffer = (tb_char_t*)tb_malloc_bytes(size + 1); if (buffer) { tb_memcpy(buffer, cstr, size); buffer[size] = '\0'; -- cgit v1.3.1