diff options
| author | ruki <[email protected]> | 2026-01-15 11:20:12 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-01-15 11:20:12 +0800 |
| commit | b8c02f4d81b0aceb976401f3ce20605f4ee775cb (patch) | |
| tree | 21acfd3dc03d8ab4c5ed8e911340f0390b3ba3f1 | |
| parent | 6a282204b4d2d27f987ff6b9c2ebf7a680ab7971 (diff) | |
| parent | 5e6df0bcfe9768e84545cd94466973435268aef1 (diff) | |
Merge pull request #7219 from xmake-io/iconv
Add cli.iconv module
| -rw-r--r-- | core/src/xmake/engine.c | 2 | ||||
| -rw-r--r-- | core/src/xmake/io/file_convert.c | 169 | ||||
| -rw-r--r-- | core/src/xmake/string/convert.c | 58 | ||||
| -rw-r--r-- | core/src/xmake/utils/charset.c | 82 | ||||
| -rw-r--r-- | core/src/xmake/utils/charset.h | 54 | ||||
| -rw-r--r-- | core/src/xmake/utils/prefix.h | 29 | ||||
| -rwxr-xr-x | core/src/xmake/xmake.sh | 1 | ||||
| -rw-r--r-- | tests/modules/io/test.lua | 66 | ||||
| -rw-r--r-- | xmake/core/base/io.lua | 27 | ||||
| -rw-r--r-- | xmake/core/sandbox/modules/io.lua | 11 | ||||
| -rw-r--r-- | xmake/modules/cli/iconv.lua | 69 |
11 files changed, 513 insertions, 55 deletions
diff --git a/core/src/xmake/engine.c b/core/src/xmake/engine.c index d46e30a42..532be1adc 100644 --- a/core/src/xmake/engine.c +++ b/core/src/xmake/engine.c @@ -174,6 +174,7 @@ tb_int_t xm_io_file_rawfd(lua_State *lua); tb_int_t xm_io_file_write(lua_State *lua); tb_int_t xm_io_file_flush(lua_State *lua); tb_int_t xm_io_file_close(lua_State *lua); +tb_int_t xm_io_file_convert(lua_State *lua); tb_int_t xm_io_file_isatty(lua_State *lua); // the io/filelock functions @@ -480,6 +481,7 @@ static luaL_Reg const g_io_functions[] = { { "file_flush", xm_io_file_flush }, { "file_isatty", xm_io_file_isatty }, { "file_close", xm_io_file_close }, + { "file_convert", xm_io_file_convert }, { "file_rawfd", xm_io_file_rawfd }, { "filelock_open", xm_io_filelock_open }, { "filelock_lock", xm_io_filelock_lock }, diff --git a/core/src/xmake/io/file_convert.c b/core/src/xmake/io/file_convert.c new file mode 100644 index 000000000..6901a9a08 --- /dev/null +++ b/core/src/xmake/io/file_convert.c @@ -0,0 +1,169 @@ +/*!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 file_convert.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * trace + */ +#define TB_TRACE_MODULE_NAME "file_convert" +#define TB_TRACE_MODULE_DEBUG (0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" +#include "../utils/charset.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * implementation + */ + +/* convert file + * + * @param srcpath the srcpath + * @param dstpath the dstpath + * @param ftype the from-charset type, e.g. ascii, gb2312, gbk, ios8859, ucs2, ucs4, utf8, utf16, utf32 + * @param ttype the to-charset type + * + * @code + * io.convert(srcpath, dstpath, "gbk", "utf8") + * io.convert(srcpath, dstpath, "utf8", "gb2312") + * @endcode + */ +tb_int_t xm_io_file_convert(lua_State *lua) { + // check + tb_assert_and_check_return_val(lua, 0); + +#ifdef TB_CONFIG_MODULE_HAVE_CHARSET + // get arguments + tb_char_t const *srcpath = luaL_checkstring(lua, 1); + tb_char_t const *dstpath = luaL_checkstring(lua, 2); + tb_char_t const *fname = luaL_checkstring(lua, 3); + tb_char_t const *tname = luaL_checkstring(lua, 4); + tb_check_return_val(srcpath && dstpath && fname && tname, 0); + + // get charset type + tb_size_t ftype = TB_CHARSET_TYPE_UTF8; + tb_size_t ttype = TB_CHARSET_TYPE_UTF8; + xm_charset_entry_ref_t fentry = xm_charset_find_by_name(fname); + if (fentry) ftype = fentry->type; + else { + lua_pushfstring(lua, "invalid charset: %s", fname); + lua_error(lua); + } + xm_charset_entry_ref_t tentry = xm_charset_find_by_name(tname); + if (tentry) ttype = tentry->type; + else { + lua_pushfstring(lua, "invalid charset: %s", tname); + lua_error(lua); + } + + // done + tb_bool_t ok = tb_false; + tb_stream_ref_t istream = tb_null; + tb_stream_ref_t ostream = tb_null; + tb_stream_ref_t fstream = tb_null; + do { + // init istream + istream = tb_stream_init_from_file(srcpath, TB_FILE_MODE_RO); + tb_assert_and_check_break(istream); + + // open istream + if (!tb_stream_open(istream)) break; + + // skip bom + tb_size_t skip = 0; + tb_byte_t* bom_data = tb_null; + tb_long_t bom_size = tb_stream_peek(istream, &bom_data, 3); + if (bom_size >= 3 && ftype == TB_CHARSET_TYPE_UTF8 && bom_data[0] == 0xef && bom_data[1] == 0xbb && bom_data[2] == 0xbf) { + skip = 3; + } else if (bom_size >= 2 && (ftype & TB_CHARSET_TYPE_UTF16)) { + if (bom_data[0] == 0xff && bom_data[1] == 0xfe) skip = 2; + else if (bom_data[0] == 0xfe && bom_data[1] == 0xff) skip = 2; + } + if (skip > 0 && !tb_stream_skip(istream, skip)) break; + + // init ostream + ostream = tb_stream_init_from_file(dstpath, TB_FILE_MODE_RW | TB_FILE_MODE_CREAT | TB_FILE_MODE_TRUNC); + tb_assert_and_check_break(ostream); + + // open ostream + if (!tb_stream_open(ostream)) break; + + // write bom + if (!tb_strcmp(tname, "utf8bom")) { + static tb_byte_t const k_bom[] = {0xef, 0xbb, 0xbf}; + if (!tb_stream_bwrit(ostream, k_bom, 3)) break; + } else if (!tb_strcmp(tname, "utf16lebom")) { + static tb_byte_t const k_bom[] = {0xff, 0xfe}; + if (!tb_stream_bwrit(ostream, k_bom, 2)) break; + } else if (!tb_strcmp(tname, "utf16bom")) { +#ifndef TB_WORDS_BIGENDIAN + static tb_byte_t const k_bom[] = {0xff, 0xfe}; +#else + static tb_byte_t const k_bom[] = {0xfe, 0xff}; +#endif + if (!tb_stream_bwrit(ostream, k_bom, 2)) break; + } + + // init fstream + fstream = tb_stream_init_filter_from_charset(istream, ftype, ttype); + tb_assert_and_check_break(fstream); + + // open fstream + if (!tb_stream_open(fstream)) break; + + // transfer + tb_byte_t data[TB_STREAM_BLOCK_MAXN]; + while (1) { + tb_long_t real = tb_stream_read(fstream, data, sizeof(data)); + if (real > 0) { + if (!tb_stream_bwrit(ostream, data, real)) break; + } else if (real == 0) { + tb_long_t wait = tb_stream_wait(fstream, TB_STREAM_WAIT_READ, tb_stream_timeout(fstream)); + if (wait <= 0) break; + } else break; + } + + // ok + ok = tb_true; + + } while (0); + + // exit fstream + if (fstream) tb_stream_exit(fstream); + fstream = tb_null; + + // exit istream + if (istream) tb_stream_exit(istream); + istream = tb_null; + + // exit ostream + if (ostream) tb_stream_exit(ostream); + ostream = tb_null; + + // ok? + lua_pushboolean(lua, ok); + return 1; +#else + lua_pushboolean(lua, tb_false); + return 1; +#endif +} diff --git a/core/src/xmake/string/convert.c b/core/src/xmake/string/convert.c index b3adaf8b8..3b67cda30 100644 --- a/core/src/xmake/string/convert.c +++ b/core/src/xmake/string/convert.c @@ -29,59 +29,7 @@ * includes */ #include "prefix.h" - -/* ////////////////////////////////////////////////////////////////////////////////////// - * globals - */ - -// the charset entry type -typedef struct __xm_charset_entry_t { - // the charset type - tb_size_t type; - - // the charset name - tb_char_t const *name; -} xm_charset_entry_t, *xm_charset_entry_ref_t; - -// the charsets, @note: type & name is sorted -static xm_charset_entry_t g_charsets[] = { - { TB_CHARSET_TYPE_ANSI, "ansi" }, - { TB_CHARSET_TYPE_ASCII, "ascii" }, - { TB_CHARSET_TYPE_GB2312, "gb2312" }, - { TB_CHARSET_TYPE_GBK, "gbk" }, - { TB_CHARSET_TYPE_ISO8859, "iso8859" }, - { TB_CHARSET_TYPE_UCS2 | TB_CHARSET_TYPE_NE, "ucs2" }, - { TB_CHARSET_TYPE_UCS4 | TB_CHARSET_TYPE_NE, "ucs4" }, - { TB_CHARSET_TYPE_UTF16 | TB_CHARSET_TYPE_NE, "utf16" }, - { TB_CHARSET_TYPE_UTF16 | TB_CHARSET_TYPE_BE, "utf16be" }, - { TB_CHARSET_TYPE_UTF16 | TB_CHARSET_TYPE_LE, "utf16le" }, - { TB_CHARSET_TYPE_UTF32 | TB_CHARSET_TYPE_NE, "utf32" }, - { TB_CHARSET_TYPE_UTF32 | TB_CHARSET_TYPE_BE, "utf32be" }, - { TB_CHARSET_TYPE_UTF32 | TB_CHARSET_TYPE_LE, "utf32le" }, - { TB_CHARSET_TYPE_UTF8, "utf8" }, -}; - -/* ////////////////////////////////////////////////////////////////////////////////////// - * finder - */ -static tb_long_t xm_string_charset_comp_by_name(tb_iterator_ref_t iterator, tb_cpointer_t item, tb_cpointer_t name) { - return tb_stricmp(((xm_charset_entry_ref_t)item)->name, (tb_char_t const *)name); -} -static xm_charset_entry_ref_t xm_string_charset_find_by_name(tb_char_t const *name) { - // make iterator - tb_array_iterator_t array_iterator; - tb_iterator_ref_t iterator = - tb_array_iterator_init_mem(&array_iterator, g_charsets, tb_arrayn(g_charsets), sizeof(xm_charset_entry_t)); - tb_assert_and_check_return_val(iterator, tb_null); - - // find it by the binary search - tb_size_t itor = tb_binary_find_all_if(iterator, xm_string_charset_comp_by_name, name); - if (itor != tb_iterator_tail(iterator)) { - return (xm_charset_entry_ref_t)tb_iterator_item(iterator, itor); - } else { - return tb_null; - } -} +#include "../utils/charset.h" /* ////////////////////////////////////////////////////////////////////////////////////// * implementation @@ -109,8 +57,8 @@ tb_int_t xm_string_convert(lua_State *lua) { tb_check_return_val(src_cstr && ftype_cstr && ttype_cstr, 0); // find charsets - xm_charset_entry_ref_t fcharset = xm_string_charset_find_by_name(ftype_cstr); - xm_charset_entry_ref_t tcharset = xm_string_charset_find_by_name(ttype_cstr); + xm_charset_entry_ref_t fcharset = xm_charset_find_by_name(ftype_cstr); + xm_charset_entry_ref_t tcharset = xm_charset_find_by_name(ttype_cstr); luaL_argcheck(lua, fcharset, 2, "charset not found"); luaL_argcheck(lua, tcharset, 3, "charset not found"); tb_check_return_val(fcharset && tcharset, 0); diff --git a/core/src/xmake/utils/charset.c b/core/src/xmake/utils/charset.c new file mode 100644 index 000000000..0a93d9c9a --- /dev/null +++ b/core/src/xmake/utils/charset.c @@ -0,0 +1,82 @@ +/*!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 charset.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * trace + */ +#define TB_TRACE_MODULE_NAME "charset" +#define TB_TRACE_MODULE_DEBUG (0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "charset.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * globals + */ + +// the charsets, @note: type & name is sorted +static xm_charset_entry_t g_charsets[] = { + { TB_CHARSET_TYPE_ANSI, "ansi" }, + { TB_CHARSET_TYPE_ASCII, "ascii" }, + { TB_CHARSET_TYPE_GB2312, "gb2312" }, + { TB_CHARSET_TYPE_GBK, "gbk" }, + { TB_CHARSET_TYPE_ISO8859, "iso8859" }, + { TB_CHARSET_TYPE_UCS2 | TB_CHARSET_TYPE_NE, "ucs2" }, + { TB_CHARSET_TYPE_UCS4 | TB_CHARSET_TYPE_NE, "ucs4" }, + { TB_CHARSET_TYPE_UTF16 | TB_CHARSET_TYPE_NE, "utf16" }, + { TB_CHARSET_TYPE_UTF16 | TB_CHARSET_TYPE_BE, "utf16be" }, + { TB_CHARSET_TYPE_UTF16 | TB_CHARSET_TYPE_NE, "utf16bom" }, + { TB_CHARSET_TYPE_UTF16 | TB_CHARSET_TYPE_LE, "utf16le" }, + { TB_CHARSET_TYPE_UTF16 | TB_CHARSET_TYPE_LE, "utf16lebom" }, + { TB_CHARSET_TYPE_UTF32 | TB_CHARSET_TYPE_NE, "utf32" }, + { TB_CHARSET_TYPE_UTF32 | TB_CHARSET_TYPE_BE, "utf32be" }, + { TB_CHARSET_TYPE_UTF32 | TB_CHARSET_TYPE_LE, "utf32le" }, + { TB_CHARSET_TYPE_UTF8, "utf8" }, + { TB_CHARSET_TYPE_UTF8, "utf8bom" }, +}; + +/* ////////////////////////////////////////////////////////////////////////////////////// + * finder + */ +static tb_long_t xm_charset_comp_by_name(tb_iterator_ref_t iterator, tb_cpointer_t item, tb_cpointer_t name) { + return tb_stricmp(((xm_charset_entry_ref_t)item)->name, (tb_char_t const *)name); +} + +/* ////////////////////////////////////////////////////////////////////////////////////// + * implementation + */ +xm_charset_entry_ref_t xm_charset_find_by_name(tb_char_t const *name) { + // make iterator + tb_array_iterator_t array_iterator; + tb_iterator_ref_t iterator = + tb_array_iterator_init_mem(&array_iterator, g_charsets, tb_arrayn(g_charsets), sizeof(xm_charset_entry_t)); + tb_assert_and_check_return_val(iterator, tb_null); + + // find it by the binary search + tb_size_t itor = tb_binary_find_all_if(iterator, xm_charset_comp_by_name, name); + if (itor != tb_iterator_tail(iterator)) { + return (xm_charset_entry_ref_t)tb_iterator_item(iterator, itor); + } else { + return tb_null; + } +} diff --git a/core/src/xmake/utils/charset.h b/core/src/xmake/utils/charset.h new file mode 100644 index 000000000..f23febb94 --- /dev/null +++ b/core/src/xmake/utils/charset.h @@ -0,0 +1,54 @@ +/*!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 charset.h + * + */ +#ifndef XM_UTILS_CHARSET_H +#define XM_UTILS_CHARSET_H + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * types + */ + +// the charset entry type +typedef struct __xm_charset_entry_t { + // the charset type + tb_size_t type; + + // the charset name + tb_char_t const *name; +} xm_charset_entry_t, *xm_charset_entry_ref_t; + +/* ////////////////////////////////////////////////////////////////////////////////////// + * interfaces + */ + +/* find charset by name + * + * @param name the charset name + * + * @return the charset entry + */ +xm_charset_entry_ref_t xm_charset_find_by_name(tb_char_t const *name); + +#endif diff --git a/core/src/xmake/utils/prefix.h b/core/src/xmake/utils/prefix.h new file mode 100644 index 000000000..a9f509e72 --- /dev/null +++ b/core/src/xmake/utils/prefix.h @@ -0,0 +1,29 @@ +/*!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 prefix.h + * + */ +#ifndef XM_UTILS_PREFIX_H +#define XM_UTILS_PREFIX_H + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "../prefix.h" + +#endif diff --git a/core/src/xmake/xmake.sh b/core/src/xmake/xmake.sh index 8a8f1224c..291001306 100755 --- a/core/src/xmake/xmake.sh +++ b/core/src/xmake/xmake.sh @@ -70,6 +70,7 @@ target "xmake" add_files "sandbox/*.c" add_files "semver/*.c" add_files "string/*.c" + add_files "utils/*.c" add_files "tty/*.c" add_files "binutils/*.c" add_files "binutils/coff/*.c" diff --git a/tests/modules/io/test.lua b/tests/modules/io/test.lua index e4c6f23e1..2b8bad8bf 100644 --- a/tests/modules/io/test.lua +++ b/tests/modules/io/test.lua @@ -94,3 +94,69 @@ function test_write(t) os.tryrm("temp") end + +function test_convert(t) + local src = "files/utf8bom-lf-eleof" + local dst = "temp/convert_test.txt" + os.mkdir("temp") + + -- utf8 to gbk (strip bom) + io.convert(src, dst, {from = "utf8", to = "gbk"}) + local content = io.readfile(dst, {encoding = "binary"}) + t:are_equal(content, "123\\\n456\n789\n") + + -- gbk to utf8 + local src_gbk = dst + local dst_utf8 = "temp/convert_test_utf8.txt" + io.convert(src_gbk, dst_utf8, {from = "gbk", to = "utf8"}) + content = io.readfile(dst_utf8, {encoding = "binary"}) + t:are_equal(content, "123\\\n456\n789\n") + + -- utf8 to utf8bom + local dst_utf8bom = "temp/convert_test_utf8bom.txt" + io.convert(src, dst_utf8bom, {from = "utf8", to = "utf8bom"}) + content = io.readfile(dst_utf8bom, {encoding = "binary"}) + t:are_equal(content:sub(1, 3), "\239\187\191") + t:are_equal(content:sub(4), "123\\\n456\n789\n") + + -- utf16le to utf8 + local src_utf16le = "files/utf16le-crlf-neleof" + local dst_utf16le_to_utf8 = "temp/convert_test_utf16le_to_utf8.txt" + io.convert(src_utf16le, dst_utf16le_to_utf8, {from = "utf16le", to = "utf8"}) + content = io.readfile(dst_utf16le_to_utf8, {encoding = "binary"}) + t:are_equal(content, "123\\\r\n456\r\n789") + + -- utf16be to utf8 + local src_utf16be = "files/utf16be-lf-eleof" + local dst_utf16be_to_utf8 = "temp/convert_test_utf16be_to_utf8.txt" + io.convert(src_utf16be, dst_utf16be_to_utf8, {from = "utf16be", to = "utf8"}) + content = io.readfile(dst_utf16be_to_utf8, {encoding = "binary"}) + t:are_equal(content, "123\\\n456\n789\n") + + -- utf8 to utf16le + local dst_utf8_to_utf16le = "temp/convert_test_utf8_to_utf16le.txt" + io.convert(src, dst_utf8_to_utf16le, {from = "utf8", to = "utf16le"}) + content = io.readfile(dst_utf8_to_utf16le, {encoding = "binary"}) + t:are_equal(content:sub(1, 2), "1\0") + + -- utf8 to utf16be + local dst_utf8_to_utf16be = "temp/convert_test_utf8_to_utf16be.txt" + io.convert(src, dst_utf8_to_utf16be, {from = "utf8", to = "utf16be"}) + content = io.readfile(dst_utf8_to_utf16be, {encoding = "binary"}) + t:are_equal(content:sub(1, 2), "\0001") + + -- utf8 to utf16lebom + local dst_utf8_to_utf16lebom = "temp/convert_test_utf8_to_utf16lebom.txt" + io.convert(src, dst_utf8_to_utf16lebom, {from = "utf8", to = "utf16lebom"}) + content = io.readfile(dst_utf8_to_utf16lebom, {encoding = "binary"}) + t:are_equal(content:sub(1, 2), "\255\254") + + -- utf8 to utf16bom + local dst_utf8_to_utf16bom = "temp/convert_test_utf8_to_utf16bom.txt" + io.convert(src, dst_utf8_to_utf16bom, {from = "utf8", to = "utf16bom"}) + content = io.readfile(dst_utf8_to_utf16bom, {encoding = "binary"}) + local bom = content:sub(1, 2) + t:require(bom == "\255\254" or bom == "\254\255") + + os.tryrm("temp") +end diff --git a/xmake/core/base/io.lua b/xmake/core/base/io.lua index f35c01ab3..3afd187d8 100644 --- a/xmake/core/base/io.lua +++ b/xmake/core/base/io.lua @@ -598,6 +598,33 @@ function io.close(file) return (file or io.stdout):close() end +-- convert file encoding +-- +-- @param inputfile the input file path +-- @param outputfile the output file path +-- @param opt the options +-- - from: the input encoding, e.g. utf8, utf8bom, utf16, utf16le, utf16lebom, utf16be, gb2312, gbk, iso8859, ucs2, ucs4, utf32 .. (default: utf8) +-- - to: the output encoding, e.g. utf8, utf8bom, utf16, utf16le, utf16lebom, utf16be, gb2312, gbk, iso8859, ucs2, ucs4, utf32 .. (default: utf8) +function io.convert(inputfile, outputfile, opt) + opt = opt or {} + inputfile = tostring(inputfile) + outputfile = tostring(outputfile) + local from = opt.from or "utf8" + local to = opt.to or "utf8" + + -- try using c implementation first + if io.file_convert and io.file_convert(inputfile, outputfile, from, to) then + return true + end + + -- fallback to lua implementation (slow but works) + local content, errors = io.readfile(inputfile, {encoding = from}) + if not content then + return false, errors + end + return io.writefile(outputfile, content, {encoding = to}) +end + -- save object the the given filepath function io.save(filepath, object, opt) opt = opt or {} diff --git a/xmake/core/sandbox/modules/io.lua b/xmake/core/sandbox/modules/io.lua index 1b15d4495..0cafaa5f2 100644 --- a/xmake/core/sandbox/modules/io.lua +++ b/xmake/core/sandbox/modules/io.lua @@ -201,6 +201,17 @@ function sandbox_io.insert(filepath, lineidx, text, opt) return data end +-- convert file encoding +function sandbox_io.convert(inputfile, outputfile, opt) + assert(inputfile and outputfile) + inputfile = vformat(inputfile) + outputfile = vformat(outputfile) + local ok, errors = io.convert(inputfile, outputfile, opt) + if not ok then + raise(errors) + end +end + -- get std file function sandbox_io.stdfile(filepath) assert(filepath) diff --git a/xmake/modules/cli/iconv.lua b/xmake/modules/cli/iconv.lua new file mode 100644 index 000000000..a494155ed --- /dev/null +++ b/xmake/modules/cli/iconv.lua @@ -0,0 +1,69 @@ +--!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 iconv.lua +-- + +-- imports +import("core.base.option") + +-- supported encodings +local encodings = {"ansi", "ascii", "gb2312", "gbk", "iso8859", "ucs2", "ucs4", "utf16", "utf16be", "utf16bom", "utf16le", "utf16lebom", "utf32", "utf32be", "utf32le", "utf8", "utf8bom"} + +-- the options +local options = { + {'f', "from", "kv", "utf8", "The source encoding.", values = encodings}, + {'t', "to", "kv", "utf8", "The target encoding.", values = encodings}, + {'l', "list", "k", nil, "List supported encodings."}, + {'o', "outputfile", "kv", nil, "The output file."}, + {nil, "inputfile", "v", nil, "The input file."} +} + +-- main entry +function main(...) + + -- parse arguments + local argv = table.pack(...) + local args = option.parse(argv, options, "Convert encoding of a file.", + "", + "Usage: xmake l cli.iconv [options] [inputfile]") + + -- list encodings + if args.list then + print(table.concat(encodings, " ")) + return + end + + -- get arguments + local input_file = assert(args.inputfile, "input file required!") + local output_file = args.outputfile + local from_code = args.from + local to_code = args.to + + -- convert encoding + if output_file then + io.convert(input_file, output_file, {from = from_code, to = to_code}) + else + local tmpfile = os.tmpfile() + io.convert(input_file, tmpfile, {from = from_code, to = to_code}) + local data = io.readfile(tmpfile, {encoding = "binary"}) + if data then + io.write(data) + end + os.rm(tmpfile) + end +end |
