From 236988050bfe9f51ebe6b72c6e3d760bb2e349ef Mon Sep 17 00:00:00 2001 From: ruki Date: Sun, 18 Aug 2019 23:29:31 +0800 Subject: rewrite io --- core/src/xmake/io/file.h | 147 ----------------- core/src/xmake/io/file___len.c | 56 ------- core/src/xmake/io/file___tostring.c | 51 ------ core/src/xmake/io/file_close.c | 92 +++++++++++ core/src/xmake/io/file_close___gc.c | 124 -------------- core/src/xmake/io/file_flush.c | 28 ++-- core/src/xmake/io/file_isatty.c | 13 +- core/src/xmake/io/file_open.c | 298 +++++++++++++++++++++++++++++++++ core/src/xmake/io/file_path.c | 55 ------- core/src/xmake/io/file_read.c | 81 ++++----- core/src/xmake/io/file_seek.c | 32 ++-- core/src/xmake/io/file_size.c | 60 +++++++ core/src/xmake/io/file_write.c | 35 ++-- core/src/xmake/io/open.c | 320 ------------------------------------ core/src/xmake/io/prefix.h | 66 ++++++++ core/src/xmake/io/std.c | 87 ++++------ core/src/xmake/machine.c | 43 ++--- core/src/xmake/makefile | 10 +- xmake/core/base/io.lua | 151 +++++++++++++++-- xmake/core/base/process.lua | 3 +- xmake/core/sandbox/modules/io.lua | 155 +++++++++++------ 21 files changed, 902 insertions(+), 1005 deletions(-) delete mode 100644 core/src/xmake/io/file.h delete mode 100644 core/src/xmake/io/file___len.c delete mode 100644 core/src/xmake/io/file___tostring.c create mode 100644 core/src/xmake/io/file_close.c delete mode 100644 core/src/xmake/io/file_close___gc.c create mode 100644 core/src/xmake/io/file_open.c delete mode 100644 core/src/xmake/io/file_path.c create mode 100644 core/src/xmake/io/file_size.c delete mode 100644 core/src/xmake/io/open.c diff --git a/core/src/xmake/io/file.h b/core/src/xmake/io/file.h deleted file mode 100644 index 69555b393..000000000 --- a/core/src/xmake/io/file.h +++ /dev/null @@ -1,147 +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 - 2019, TBOOX Open Source Group. - * - * @author OpportunityLiu - * @file file.h - * - */ -#ifndef XM_IO_FILE_H -#define XM_IO_FILE_H - -/* ////////////////////////////////////////////////////////////////////////////////////// - * includes - */ -#include "prefix.h" - -/* ////////////////////////////////////////////////////////////////////////////////////// - * macros - */ -#define xm_io_file_is_file(file) ((file)->type == XM_IO_FILE_TYPE_FILE) -#define xm_io_file_is_std(file) ((file)->type != XM_IO_FILE_TYPE_FILE) - -#define xm_io_file_is_closed_file(file) (xm_io_file_is_file(file) && !((file)->file_ref)) -#define xm_io_file_is_closed_std(file) (xm_io_file_is_std(file) && !((file)->std_ref)) - -#define xm_io_file_is_tty(file) (!!((file)->type & XM_IO_FILE_FLAG_TTY)) -#define xm_io_file_is_closed(file) (xm_io_file_is_closed_file(file) || xm_io_file_is_closed_std(file)) - -// the file udata type -#define xm_io_file_udata "io._file*" - -// return file success -#define xm_io_file_return_success() do { return 1; } while (0) - -// return file error with reason -#define xm_io_file_return_error(lua, file, reason) \ - do \ - { \ - lua_pushnil(lua); \ - lua_pushfstring(lua, "error: %s (%s)", reason, file->name); \ - return 2; \ - } while (0) - -// return file closed error -#define xm_io_file_return_error_closed(lua) \ - do \ - { \ - lua_pushnil(lua); \ - lua_pushliteral(lua, "error: file has been closed"); \ - return 2; \ - } while (0) - -/* ////////////////////////////////////////////////////////////////////////////////////// - * types - */ -typedef enum __xm_io_file_type_e -{ - XM_IO_FILE_TYPE_FILE = 0 //!< disk file - -, XM_IO_FILE_TYPE_STDIN = 1 -, XM_IO_FILE_TYPE_STDOUT = 2 -, XM_IO_FILE_TYPE_STDERR = 3 - -, XM_IO_FILE_FLAG_TTY = 0x10 //!< mark tty std stream - -} xm_io_file_type_e; - -/* use negetive numbers for this enum, its a extension for tb_charset_type_e - * before adding new values, make sure they have not conflicts with values in tb_charset_type_e - */ -typedef enum __xm_io_file_encoding_e -{ - XM_IO_FILE_ENCODING_UNKNOWN = -1 -, XM_IO_FILE_ENCODING_BINARY = -2 - -} xm_io_file_encoding_e; - -// the file type -typedef struct __xm_io_file_t -{ - union - { - /* the normal file for XM_IO_FILE_TYPE_FILE - * - * direct: file_ref -> stream -> file - * transcode: file_ref -> fstream -> stream -> file - */ - tb_stream_ref_t file_ref; - - // the standard io file - tb_stdfile_ref_t std_ref; - }; - - tb_stream_ref_t stream; // the file stream for XM_IO_FILE_TYPE_FILE - tb_stream_ref_t fstream; // the file charset stream filter - tb_size_t mode; // tb_file_mode_t - tb_size_t type; // xm_io_file_type_e - tb_size_t encoding; // value of xm_io_file_encoding_e or tb_charset_type_e - tb_char_t name[64]; - tb_char_t const* path; - tb_buffer_t rcache; // the read line cache buffer - tb_buffer_t wcache; // the write line cache buffer -} xm_io_file_t; - -/* ////////////////////////////////////////////////////////////////////////////////////// - * interfaces - */ -static __tb_inline__ xm_io_file_t* xm_io_newfile(lua_State* lua) -{ - // check - tb_assert_and_check_return_val(lua, tb_null); - - // new file - xm_io_file_t* file = (xm_io_file_t*)lua_newuserdata(lua, sizeof(xm_io_file_t)); - tb_assert_and_check_return_val(file, tb_null); - - // init file - luaL_getmetatable(lua, xm_io_file_udata); - lua_setmetatable(lua, -2); - tb_memset(file, 0, sizeof(xm_io_file_t)); - return file; -} - -static __tb_inline__ xm_io_file_t* xm_io_getfile(lua_State* lua) -{ - // check - tb_assert_and_check_return_val(lua, tb_null); - - // get file - xm_io_file_t* file = (xm_io_file_t*)luaL_checkudata(lua, 1, xm_io_file_udata); - tb_assert(file); - return file; -} - -#endif diff --git a/core/src/xmake/io/file___len.c b/core/src/xmake/io/file___len.c deleted file mode 100644 index d244edfd4..000000000 --- a/core/src/xmake/io/file___len.c +++ /dev/null @@ -1,56 +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 - 2019, TBOOX Open Source Group. - * - * @author OpportunityLiu - * @file file___len.c - * - */ - -/* ////////////////////////////////////////////////////////////////////////////////////// - * trace - */ -#define TB_TRACE_MODULE_NAME "file___len" -#define TB_TRACE_MODULE_DEBUG (0) - -/* ////////////////////////////////////////////////////////////////////////////////////// - * includes - */ -#include "file.h" -#include "prefix.h" - -/* ////////////////////////////////////////////////////////////////////////////////////// - * implementation - */ - -/* get file length, #file - */ -tb_int_t xm_io_file___len(lua_State* lua) -{ - // check - tb_assert_and_check_return_val(lua, 0); - - xm_io_file_t* file = xm_io_getfile(lua); - if (xm_io_file_is_closed(file)) - xm_io_file_return_error_closed(lua); - else if (xm_io_file_is_file(file)) - { - // get size from raw file stream, because we cannot get size from fstream - tb_assert(file->stream); - lua_pushnumber(lua, (lua_Number)tb_stream_size(file->stream)); - xm_io_file_return_success(); - } - else xm_io_file_return_error(lua, file, "getting file size for this file is invalid"); -} diff --git a/core/src/xmake/io/file___tostring.c b/core/src/xmake/io/file___tostring.c deleted file mode 100644 index 55a5e9167..000000000 --- a/core/src/xmake/io/file___tostring.c +++ /dev/null @@ -1,51 +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 - 2019, TBOOX Open Source Group. - * - * @author OpportunityLiu - * @file file___tostring.c - * - */ - -/* ////////////////////////////////////////////////////////////////////////////////////// - * trace - */ -#define TB_TRACE_MODULE_NAME "file___tostring" -#define TB_TRACE_MODULE_DEBUG (0) - -/* ////////////////////////////////////////////////////////////////////////////////////// - * includes - */ -#include "file.h" -#include "prefix.h" - -/* ////////////////////////////////////////////////////////////////////////////////////// - * implementation - */ - -/* - * tostring(file) - */ -tb_int_t xm_io_file___tostring(lua_State* lua) -{ - // check - tb_assert_and_check_return_val(lua, 0); - - // get file name as string - xm_io_file_t* file = xm_io_getfile(lua); - lua_pushstring(lua, file->name); - return 1; -} - diff --git a/core/src/xmake/io/file_close.c b/core/src/xmake/io/file_close.c new file mode 100644 index 000000000..b808d3e44 --- /dev/null +++ b/core/src/xmake/io/file_close.c @@ -0,0 +1,92 @@ +/*!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 - 2019, TBOOX Open Source Group. + * + * @author OpportunityLiu, ruki + * @file file_close.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * trace + */ +#define TB_TRACE_MODULE_NAME "file_close" +#define TB_TRACE_MODULE_DEBUG (0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * interfaces + */ + +// io.file_close(file) +tb_int_t xm_io_file_close(lua_State* lua) +{ + // check + tb_assert_and_check_return_val(lua, 0); + + // is user data? + if (!lua_isuserdata(lua, 1)) + return 0; + + // get file + xm_io_file_t* file = (xm_io_file_t*)lua_touserdata(lua, 1); + tb_check_return_val(file, 0); + + // close file + if (xm_io_file_is_file(file)) + { + // check + tb_assert(file->file_ref); + +#ifdef TB_CONFIG_OS_WINDOWS + // write cached data first + tb_byte_t const* odata = tb_buffer_data(&file->wcache); + tb_size_t osize = tb_buffer_size(&file->wcache); + if (odata && osize) + { + if (!tb_stream_bwrit(file->file_ref, odata, osize)) return tb_false; + tb_buffer_clear(&file->wcache); + } +#endif + + // close file + if (!tb_stream_clos(file->file_ref)) + xm_io_file_return_error(lua, "failed to close file"); + file->file_ref = tb_null; + + // exit fstream + if (file->fstream) tb_stream_exit(file->fstream); + file->fstream = tb_null; + + // exit stream + if (file->stream) tb_stream_exit(file->stream); + file->stream = tb_null; + + // exit the line cache buffer + tb_buffer_exit(&file->rcache); + tb_buffer_exit(&file->wcache); + + // exit file + tb_free(file); + } + + lua_pushboolean(lua, tb_true); + return 1; +} + diff --git a/core/src/xmake/io/file_close___gc.c b/core/src/xmake/io/file_close___gc.c deleted file mode 100644 index 1c59ff3ea..000000000 --- a/core/src/xmake/io/file_close___gc.c +++ /dev/null @@ -1,124 +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 - 2019, TBOOX Open Source Group. - * - * @author OpportunityLiu - * @file file_close___gc.c - * - */ - -/* ////////////////////////////////////////////////////////////////////////////////////// - * trace - */ -#define TB_TRACE_MODULE_NAME "file_close___gc" -#define TB_TRACE_MODULE_DEBUG (0) - -/* ////////////////////////////////////////////////////////////////////////////////////// - * includes - */ -#include "file.h" -#include "prefix.h" - -/* ////////////////////////////////////////////////////////////////////////////////////// - * implementation - */ - -static tb_int_t xm_io_file_close_impl(lua_State* lua, tb_bool_t allow_closed_file) -{ - // check - tb_assert_and_check_return_val(lua, 0); - - // close file - xm_io_file_t* file = xm_io_getfile(lua); - if (xm_io_file_is_closed(file)) - { - if (allow_closed_file) - { - lua_pushboolean(lua, tb_true); - xm_io_file_return_success(); - } - else xm_io_file_return_error_closed(lua); - } - if (xm_io_file_is_file(file)) - { - // check - tb_assert(file->file_ref); - -#ifdef TB_CONFIG_OS_WINDOWS - // write cached data first - tb_byte_t const* odata = tb_buffer_data(&file->wcache); - tb_size_t osize = tb_buffer_size(&file->wcache); - if (odata && osize) - { - if (!tb_stream_bwrit(file->file_ref, odata, osize)) return tb_false; - tb_buffer_clear(&file->wcache); - } -#endif - - // close file - if (!tb_stream_clos(file->file_ref)) - xm_io_file_return_error(lua, file, "failed to close file"); - file->file_ref = tb_null; - - // exit fstream - if (file->fstream) tb_stream_exit(file->fstream); - file->fstream = tb_null; - - // exit stream - if (file->stream) tb_stream_exit(file->stream); - file->stream = tb_null; - - // exit the line cache buffer - tb_buffer_exit(&file->rcache); - tb_buffer_exit(&file->wcache); - - // free file path - if (file->path) - { - tb_free(file->path); - file->path = tb_null; - } - - // mark this file as closed - tb_strlcpy(file->name, "file: (closed file)", tb_arrayn(file->name)); - lua_pushboolean(lua, tb_true); - xm_io_file_return_success(); - } - else - { - lua_pushboolean(lua, tb_true); - xm_io_file_return_success(); - } -} - -/* ////////////////////////////////////////////////////////////////////////////////////// - * interfaces - */ - -/* - * file:close() - */ -tb_int_t xm_io_file_close(lua_State* lua) -{ - return xm_io_file_close_impl(lua, tb_false); -} - -/* - * file:close() - */ -tb_int_t xm_io_file___gc(lua_State* lua) -{ - return xm_io_file_close_impl(lua, tb_true); -} diff --git a/core/src/xmake/io/file_flush.c b/core/src/xmake/io/file_flush.c index e63a0952a..1c1939db1 100644 --- a/core/src/xmake/io/file_flush.c +++ b/core/src/xmake/io/file_flush.c @@ -14,7 +14,7 @@ * * Copyright (C) 2015 - 2019, TBOOX Open Source Group. * - * @author OpportunityLiu + * @author OpportunityLiu, ruki * @file file_flush.c * */ @@ -28,7 +28,6 @@ /* ////////////////////////////////////////////////////////////////////////////////////// * includes */ -#include "file.h" #include "prefix.h" /* ////////////////////////////////////////////////////////////////////////////////////// @@ -36,14 +35,14 @@ */ static tb_bool_t xm_io_std_flush_impl(xm_io_file_t* file) { - tb_assert_and_check_return_val(xm_io_file_is_std(file) && !xm_io_file_is_closed(file), tb_false); + tb_assert_and_check_return_val(xm_io_file_is_std(file), tb_false); return (file->std_ref != tb_stdfile_input())? tb_stdfile_flush(file->std_ref) : tb_false; } static tb_bool_t xm_io_file_flush_impl(xm_io_file_t* file) { // check - tb_assert_and_check_return_val(xm_io_file_is_file(file) && !xm_io_file_is_closed(file), tb_false); + tb_assert_and_check_return_val(xm_io_file_is_file(file), tb_false); #ifdef TB_CONFIG_OS_WINDOWS // write cached data first @@ -62,25 +61,26 @@ static tb_bool_t xm_io_file_flush_impl(xm_io_file_t* file) * interfaces */ -/* - * file:flush() - */ +// io.file_flush(file) tb_int_t xm_io_file_flush(lua_State* lua) { // check tb_assert_and_check_return_val(lua, 0); - // file has been closed? - xm_io_file_t* file = xm_io_getfile(lua); - if (xm_io_file_is_closed(file)) - xm_io_file_return_error_closed(lua); + // is user data? + if (!lua_isuserdata(lua, 1)) + return 0; + + // get file + xm_io_file_t* file = (xm_io_file_t*)lua_touserdata(lua, 1); + tb_check_return_val(file, 0); // flush file - tb_bool_t ok = xm_io_file_is_file(file) ? xm_io_file_flush_impl(file) : xm_io_std_flush_impl(file); + tb_bool_t ok = xm_io_file_is_file(file)? xm_io_file_flush_impl(file) : xm_io_std_flush_impl(file); if (ok) { lua_pushboolean(lua, tb_true); - xm_io_file_return_success(); + return 1; } - else xm_io_file_return_error(lua, file, "failed to flush file"); + else xm_io_file_return_error(lua, "failed to flush file"); } diff --git a/core/src/xmake/io/file_isatty.c b/core/src/xmake/io/file_isatty.c index ca44ce0a0..548fc8b2b 100644 --- a/core/src/xmake/io/file_isatty.c +++ b/core/src/xmake/io/file_isatty.c @@ -28,22 +28,27 @@ /* ////////////////////////////////////////////////////////////////////////////////////// * includes */ -#include "file.h" #include "prefix.h" /* ////////////////////////////////////////////////////////////////////////////////////// * implementation */ -/* file:isatty() - */ +// io.file_isatty(file) tb_int_t xm_io_file_isatty(lua_State* lua) { // check tb_assert_and_check_return_val(lua, 0); + // is user data? + if (!lua_isuserdata(lua, 1)) + return 0; + + // get file + xm_io_file_t* file = (xm_io_file_t*)lua_touserdata(lua, 1); + tb_check_return_val(file, 0); + // is tty? - xm_io_file_t* file = xm_io_getfile(lua); lua_pushboolean(lua, xm_io_file_is_tty(file)); return 1; } diff --git a/core/src/xmake/io/file_open.c b/core/src/xmake/io/file_open.c new file mode 100644 index 000000000..3f1f0e9e2 --- /dev/null +++ b/core/src/xmake/io/file_open.c @@ -0,0 +1,298 @@ +/*!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 - 2019, TBOOX Open Source Group. + * + * @author OpportunityLiu, ruki + * @file file_open.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * trace + */ +#define TB_TRACE_MODULE_NAME "file_open" +#define TB_TRACE_MODULE_DEBUG (0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * macros + */ + +// num of bytes read to guess encoding +#define CHECK_SIZE (1024) + +// is utf-8 tail character +#define IS_UTF8_TAIL(c) (c >= 0x80 && c < 0xc0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * private implementation + */ +static tb_size_t xm_io_file_detect_charset(tb_byte_t const** data_ptr, tb_long_t size) +{ + // check + tb_assert(data_ptr && *data_ptr); + + tb_byte_t const* data = *data_ptr; + tb_size_t charset = XM_IO_FILE_ENCODING_BINARY; + do + { + if (size >= 3 && data[0] == 239 && data[1] == 187 && data[2] == 191) // utf-8 with bom + { + charset = TB_CHARSET_TYPE_UTF8; + data += 3; // skip bom + break; + } + if (size >= 2) + { + if (data[0] == 254 && data[1] == 255) // utf16be + { + charset = TB_CHARSET_TYPE_UTF16 | TB_CHARSET_TYPE_BE; + data += 2; // skip bom + break; + } + else if (data[0] == 255 && data[1] == 254) // utf16le + { + charset = TB_CHARSET_TYPE_UTF16 | TB_CHARSET_TYPE_LE; + data += 2; // skip bom + break; + } + } + + tb_sint16_t utf16be_conf = 0; + tb_sint16_t utf16le_conf = 0; + tb_sint16_t utf8_conf = 0; + tb_sint16_t ascii_conf = 0; + tb_sint16_t zero_count = 0; + for (tb_long_t i = 0; i < (size - 4) && i < CHECK_SIZE; i++) + { + if (data[i] == 0) zero_count++; + + if (data[i] < 0x80) + ascii_conf++; + else + ascii_conf = TB_MINS16; + + if (i % 2 == 0) + { + if (data[i] == 0) utf16be_conf++; + if (data[i + 1] == 0) utf16le_conf++; + } + + if (IS_UTF8_TAIL(data[i])) + ; + else if (data[i] < 0x80) + utf8_conf++; + else if (data[i] >= 0xc0 && data[i] < 0xe0 && IS_UTF8_TAIL(data[i + 1])) + utf8_conf++; + else if (data[i] >= 0xe0 && data[i] < 0xf0 && IS_UTF8_TAIL(data[i + 1]) && IS_UTF8_TAIL(data[i + 2])) + utf8_conf++; + else if (data[i] >= 0xf0 && data[i] < 0xf8 && IS_UTF8_TAIL(data[i + 1]) && IS_UTF8_TAIL(data[i + 2]) && IS_UTF8_TAIL(data[i + 3])) + utf8_conf++; + else + utf8_conf = TB_MINS16; + } + + if (ascii_conf > 0 && zero_count <= 1) + { + charset = TB_CHARSET_TYPE_UTF8; + break; + } + if (utf8_conf > 0 && zero_count <= 1) + { + charset = TB_CHARSET_TYPE_UTF8; + break; + } + if (utf16be_conf > 0 && utf16be_conf > utf16le_conf) + { + charset = TB_CHARSET_TYPE_UTF16 | TB_CHARSET_TYPE_BE; + break; + } + if (utf16le_conf > 0 && utf16le_conf >= utf16be_conf) + { + charset = TB_CHARSET_TYPE_UTF16 | TB_CHARSET_TYPE_LE; + break; + } + if (utf8_conf > 0) + { + charset = TB_CHARSET_TYPE_UTF8; + break; + } + +#ifdef TB_CONFIG_OS_WINDOWS + charset = TB_CHARSET_TYPE_ANSI; +#endif + + } while (0); + + *data_ptr = data; + return charset; +} +static tb_size_t xm_io_file_detect_encoding(tb_stream_ref_t stream, tb_long_t* pbomoff) +{ + // check + tb_assert_and_check_return_val(stream && pbomoff, XM_IO_FILE_ENCODING_BINARY); + + // detect encoding + tb_byte_t* data = tb_null; + tb_size_t encoding = XM_IO_FILE_ENCODING_BINARY; + tb_long_t size = tb_stream_peek(stream, &data, CHECK_SIZE); + if (size > 0) + { + tb_byte_t const* p = data; + encoding = xm_io_file_detect_charset(&p, size); + *pbomoff = p - data; + } + return encoding; +} + +/* ////////////////////////////////////////////////////////////////////////////////////// + * implementation + */ + +// io.file_open(path, modestr) +tb_int_t xm_io_file_open(lua_State* lua) +{ + // check + tb_assert_and_check_return_val(lua, 0); + + // get file path and mode + tb_char_t const* path = luaL_checkstring(lua, 1); + tb_char_t const* modestr = luaL_optstring(lua, 2, "r"); + tb_assert_and_check_return_val(path && modestr, 0); + + // get file mode value + tb_size_t mode; + switch (modestr[0]) + { + case 'w': mode = TB_FILE_MODE_RW | TB_FILE_MODE_CREAT | TB_FILE_MODE_TRUNC; break; + case 'a': mode = TB_FILE_MODE_RW | TB_FILE_MODE_APPEND | TB_FILE_MODE_CREAT; break; + case 'r': default: mode = TB_FILE_MODE_RO; break; + } + + // get file encoding + tb_long_t bomoff = 0; + tb_stream_ref_t stream = tb_null; + tb_bool_t update = !!tb_strchr(modestr, '+'); + tb_size_t encoding = XM_IO_FILE_ENCODING_UNKNOWN; + if (modestr[1] == 'b' || (update && modestr[2] == 'b')) + encoding = XM_IO_FILE_ENCODING_BINARY; + else if (tb_strstr(modestr, "utf8") || tb_strstr(modestr, "utf-8")) + encoding = TB_CHARSET_TYPE_UTF8; + else if (tb_strstr(modestr, "utf16le") || tb_strstr(modestr, "utf-16le")) + encoding = TB_CHARSET_TYPE_UTF16 | TB_CHARSET_TYPE_LE; + else if (tb_strstr(modestr, "utf16be") || tb_strstr(modestr, "utf-16be")) + encoding = TB_CHARSET_TYPE_UTF16 | TB_CHARSET_TYPE_BE; + else if (tb_strstr(modestr, "utf16") || tb_strstr(modestr, "utf-16")) + encoding = TB_CHARSET_TYPE_UTF16 | TB_CHARSET_TYPE_NE; + else if (modestr[0] == 'w' || modestr[0] == 'a') // set to utf-8 if not specified for the writing mode + encoding = TB_CHARSET_TYPE_UTF8; + else if (modestr[0] == 'r') // detect encoding if not specified for the reading mode + { + stream = tb_stream_init_from_file(path, mode); + if (stream && tb_stream_open(stream)) + encoding = xm_io_file_detect_encoding(stream, &bomoff); + else + { + if (stream) tb_stream_exit(stream); + lua_pushnil(lua); + lua_pushliteral(lua, "file not found!"); + return 2; + } + } + else + { + lua_pushnil(lua); + lua_pushliteral(lua, "invalid open mode!"); + return 2; + } + tb_assert_and_check_return_val(encoding != XM_IO_FILE_ENCODING_UNKNOWN, 0); + + // open file + tb_bool_t open_ok = tb_false; + tb_stream_ref_t file_ref = tb_null; + tb_stream_ref_t fstream = tb_null; + do + { + // init stream from file + stream = stream? stream : tb_stream_init_from_file(path, mode); + tb_assert_and_check_break(stream); + + // is transcode? + tb_bool_t is_transcode = encoding != TB_CHARSET_TYPE_UTF8 && encoding != XM_IO_FILE_ENCODING_BINARY; + if (is_transcode) + { + if (modestr[0] == 'r') + fstream = tb_stream_init_filter_from_charset(stream, encoding, TB_CHARSET_TYPE_UTF8); + else + fstream = tb_stream_init_filter_from_charset(stream, TB_CHARSET_TYPE_UTF8, encoding); + tb_assert_and_check_break(fstream); + + // use fstream as file + file_ref = fstream; + } + else file_ref = stream; + + // open file stream + if (!tb_stream_open(file_ref)) break; + + // skip bom characters if exists + if (bomoff > 0 && !tb_stream_seek(stream, bomoff)) break; + + // ok + open_ok = tb_true; + + } while (0); + + // open failed? + if (!open_ok) + { + // exit stream + if (stream) tb_stream_exit(stream); + stream = tb_null; + + // exit charset stream filter + if (fstream) tb_stream_exit(fstream); + fstream = tb_null; + + // return errors + lua_pushnil(lua); + lua_pushliteral(lua, "failed to open file."); + return 2; + } + + // make file + xm_io_file_t* file = tb_malloc0_type(xm_io_file_t); + tb_assert_and_check_return_val(file, 0); + + // init file + file->file_ref = file_ref; + file->stream = stream; + file->fstream = fstream; + file->mode = mode; + file->type = XM_IO_FILE_TYPE_FILE; + file->encoding = encoding; + + // init the read/write line cache buffer + tb_buffer_init(&file->rcache); + tb_buffer_init(&file->wcache); + + // ok + lua_pushlightuserdata(lua, (tb_pointer_t)file); + return 1; +} diff --git a/core/src/xmake/io/file_path.c b/core/src/xmake/io/file_path.c deleted file mode 100644 index fab2b156b..000000000 --- a/core/src/xmake/io/file_path.c +++ /dev/null @@ -1,55 +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 - 2019, TBOOX Open Source Group. - * - * @author OpportunityLiu - * @file file_path.c - * - */ - -/* ////////////////////////////////////////////////////////////////////////////////////// - * trace - */ -#define TB_TRACE_MODULE_NAME "file_path" -#define TB_TRACE_MODULE_DEBUG (0) - -/* ////////////////////////////////////////////////////////////////////////////////////// - * includes - */ -#include "file.h" -#include "prefix.h" - -/* ////////////////////////////////////////////////////////////////////////////////////// - * implementation - */ - -/* file:path() - */ -tb_int_t xm_io_file_path(lua_State* lua) -{ - // check - tb_assert_and_check_return_val(lua, 0); - - // this file has been closed? - xm_io_file_t* file = xm_io_getfile(lua); - if (xm_io_file_is_closed(file)) - xm_io_file_return_error_closed(lua); - else - { - // return file path - lua_pushstring(lua, file->path); - xm_io_file_return_success(); - } -} diff --git a/core/src/xmake/io/file_read.c b/core/src/xmake/io/file_read.c index 4e264d87a..d183c0011 100644 --- a/core/src/xmake/io/file_read.c +++ b/core/src/xmake/io/file_read.c @@ -14,7 +14,7 @@ * * Copyright (C) 2015 - 2019, TBOOX Open Source Group. * - * @author OpportunityLiu + * @author OpportunityLiu, ruki * @file file_read.c * */ @@ -29,7 +29,6 @@ * includes */ #include "prefix.h" -#include "file.h" /* ////////////////////////////////////////////////////////////////////////////////////// * types @@ -99,7 +98,7 @@ static tb_long_t xm_io_file_buffer_readline(tb_stream_ref_t stream, tb_buffer_re static tb_int_t xm_io_file_buffer_pushline(tb_buffer_ref_t buf, xm_io_file_t* file, tb_char_t const* continuation, tb_bool_t keep_crlf) { // check - tb_assert(buf && file && continuation && xm_io_file_is_file(file) && !xm_io_file_is_closed(file)); + tb_assert(buf && file && continuation && xm_io_file_is_file(file) && file->file_ref); // is binary? tb_bool_t is_binary = file->encoding == XM_IO_FILE_ENCODING_BINARY; @@ -178,12 +177,12 @@ static tb_int_t xm_io_file_buffer_pushline(tb_buffer_ref_t buf, xm_io_file_t* fi static tb_int_t xm_io_file_read_all_directly(lua_State* lua, xm_io_file_t* file) { // check - tb_assert(lua && file && xm_io_file_is_file(file) && !xm_io_file_is_closed(file)); + tb_assert(lua && file && xm_io_file_is_file(file) && file->file_ref); // init buffer tb_buffer_t buf; if (!tb_buffer_init(&buf)) - xm_io_file_return_error(lua, file, "init buffer failed!"); + xm_io_file_return_error(lua, "init buffer failed!"); // read all tb_byte_t data[TB_STREAM_BLOCK_MAXN]; @@ -210,7 +209,7 @@ static tb_int_t xm_io_file_read_all_directly(lua_State* lua, xm_io_file_t* file) static tb_int_t xm_io_file_read_all(lua_State* lua, xm_io_file_t* file, tb_char_t const* continuation) { // check - tb_assert(lua && file && continuation && xm_io_file_is_file(file) && !xm_io_file_is_closed(file)); + tb_assert(lua && file && continuation && xm_io_file_is_file(file) && file->file_ref); // is binary? read all directly tb_bool_t is_binary = file->encoding == XM_IO_FILE_ENCODING_BINARY; @@ -220,7 +219,7 @@ static tb_int_t xm_io_file_read_all(lua_State* lua, xm_io_file_t* file, tb_char_ // init buffer tb_buffer_t buf; if (!tb_buffer_init(&buf)) - xm_io_file_return_error(lua, file, "init buffer failed!"); + xm_io_file_return_error(lua, "init buffer failed!"); // read all tb_bool_t has_content = tb_false; @@ -240,7 +239,7 @@ static tb_int_t xm_io_file_read_all(lua_State* lua, xm_io_file_t* file, tb_char_ case PL_FAIL: default: tb_buffer_exit(&buf); - xm_io_file_return_error(lua, file, "failed to read all"); + xm_io_file_return_error(lua, "failed to read all"); break; } } @@ -249,12 +248,12 @@ static tb_int_t xm_io_file_read_all(lua_State* lua, xm_io_file_t* file, tb_char_ static tb_int_t xm_io_file_read_line(lua_State* lua, xm_io_file_t* file, tb_char_t const* continuation, tb_bool_t keep_crlf) { // check - tb_assert(lua && file && continuation && xm_io_file_is_file(file) && !xm_io_file_is_closed(file)); + tb_assert(lua && file && continuation && xm_io_file_is_file(file) && file->file_ref); // init buffer tb_buffer_t buf; if (!tb_buffer_init(&buf)) - xm_io_file_return_error(lua, file, "init buffer failed!"); + xm_io_file_return_error(lua, "init buffer failed!"); // read line tb_bool_t has_content = tb_false; @@ -277,7 +276,7 @@ static tb_int_t xm_io_file_read_line(lua_State* lua, xm_io_file_t* file, tb_char case PL_FAIL: default: tb_buffer_exit(&buf); - xm_io_file_return_error(lua, file, "failed to readline"); + xm_io_file_return_error(lua, "failed to readline"); break; } } @@ -286,15 +285,15 @@ static tb_int_t xm_io_file_read_line(lua_State* lua, xm_io_file_t* file, tb_char static tb_int_t xm_io_file_read_n(lua_State* lua, xm_io_file_t* file, tb_char_t const* continuation, tb_long_t n) { // check - tb_assert(lua && file && continuation && xm_io_file_is_file(file) && !xm_io_file_is_closed(file)); + tb_assert(lua && file && continuation && xm_io_file_is_file(file) && file->file_ref); // check continuation if (*continuation != '\0') - xm_io_file_return_error(lua, file, "continuation is not supported for read number of bytes"); + xm_io_file_return_error(lua, "continuation is not supported for read number of bytes"); // check encoding if (file->encoding != XM_IO_FILE_ENCODING_BINARY) - xm_io_file_return_error(lua, file, "read number of bytes only allows binary file, reopen with 'rb' and try again"); + xm_io_file_return_error(lua, "read number of bytes only allows binary file, reopen with 'rb' and try again"); tb_bool_t ok = tb_false; if (n == 0) @@ -325,7 +324,7 @@ static tb_int_t xm_io_file_read_n(lua_State* lua, xm_io_file_t* file, tb_char_t static tb_size_t xm_io_file_std_buffer_pushline(tb_buffer_ref_t buf, xm_io_file_t* file, tb_char_t const* continuation, tb_bool_t keep_crlf) { // check - tb_assert(buf && file && continuation && xm_io_file_is_std(file) && !xm_io_file_is_closed(file)); + tb_assert(buf && file && continuation && xm_io_file_is_std(file)); // get input buffer tb_char_t strbuf[8192]; @@ -377,12 +376,12 @@ static tb_size_t xm_io_file_std_buffer_pushline(tb_buffer_ref_t buf, xm_io_file_ static tb_int_t xm_io_file_std_read_line(lua_State* lua, xm_io_file_t* file, tb_char_t const* continuation, tb_bool_t keep_crlf) { // check - tb_assert(lua && file && continuation && xm_io_file_is_std(file) && !xm_io_file_is_closed(file)); + tb_assert(lua && file && continuation && xm_io_file_is_std(file)); // init buffer tb_buffer_t buf; if (!tb_buffer_init(&buf)) - xm_io_file_return_error(lua, file, "init buffer failed!"); + xm_io_file_return_error(lua, "init buffer failed!"); // read line tb_bool_t has_content = tb_false; @@ -405,7 +404,7 @@ static tb_int_t xm_io_file_std_read_line(lua_State* lua, xm_io_file_t* file, tb_ case PL_FAIL: default: tb_buffer_exit(&buf); - xm_io_file_return_error(lua, file, "failed to readline"); + xm_io_file_return_error(lua, "failed to readline"); break; } } @@ -414,12 +413,12 @@ static tb_int_t xm_io_file_std_read_line(lua_State* lua, xm_io_file_t* file, tb_ static tb_int_t xm_io_file_std_read_all(lua_State* lua, xm_io_file_t* file, tb_char_t const* continuation) { // check - tb_assert(lua && file && continuation && xm_io_file_is_std(file) && !xm_io_file_is_closed(file)); + tb_assert(lua && file && continuation && xm_io_file_is_std(file)); // init buffer tb_buffer_t buf; if (!tb_buffer_init(&buf)) - xm_io_file_return_error(lua, file, "init buffer failed!"); + xm_io_file_return_error(lua, "init buffer failed!"); // read all tb_bool_t has_content = tb_false; @@ -439,7 +438,7 @@ static tb_int_t xm_io_file_std_read_all(lua_State* lua, xm_io_file_t* file, tb_c case PL_FAIL: default: tb_buffer_exit(&buf); - xm_io_file_return_error(lua, file, "failed to readline"); + xm_io_file_return_error(lua, "failed to readline"); break; } } @@ -448,11 +447,11 @@ static tb_int_t xm_io_file_std_read_all(lua_State* lua, xm_io_file_t* file, tb_c static tb_int_t xm_io_file_std_read_n(lua_State* lua, xm_io_file_t* file, tb_char_t const* continuation, tb_long_t n) { // check - tb_assert(lua && file && continuation && xm_io_file_is_std(file) && !xm_io_file_is_closed(file)); + tb_assert(lua && file && continuation && xm_io_file_is_std(file)); // check continuation if (*continuation != '\0') - xm_io_file_return_error(lua, file, "continuation is not supported for std streams"); + xm_io_file_return_error(lua, "continuation is not supported for std streams"); // io.read(0) if (n == 0) @@ -479,11 +478,11 @@ static tb_int_t xm_io_file_std_read_n(lua_State* lua, xm_io_file_t* file, tb_cha static tb_int_t xm_io_file_std_read_num(lua_State* lua, xm_io_file_t* file, tb_char_t const* continuation) { // check - tb_assert(lua && file && continuation && xm_io_file_is_std(file) && !xm_io_file_is_closed(file)); + tb_assert(lua && file && continuation && xm_io_file_is_std(file)); // check continuation if (*continuation != '\0') - xm_io_file_return_error(lua, file, "continuation is not supported for std streams"); + xm_io_file_return_error(lua, "continuation is not supported for std streams"); // read number tb_char_t strbuf[512]; @@ -493,19 +492,25 @@ static tb_int_t xm_io_file_std_read_num(lua_State* lua, xm_io_file_t* file, tb_c return 1; } -/* - * file:read([mode, [continuation]]) - * file:read("all", "\\") - * file:read("L") - * file:read(10) +/* io.file_read(file, [mode, [continuation]]) + * io.file_read(file, "all", "\\") + * io.file_read(file, "L") + * io.file_read(file, 10) */ tb_int_t xm_io_file_read(lua_State* lua) { // check tb_assert_and_check_return_val(lua, 0); - // get file and arguments - xm_io_file_t* file = xm_io_getfile(lua); + // is user data? + if (!lua_isuserdata(lua, 1)) + return 0; + + // get file + xm_io_file_t* file = (xm_io_file_t*)lua_touserdata(lua, 1); + tb_check_return_val(file, 0); + + // get arguments tb_char_t const* mode = luaL_optstring(lua, 2, "l"); tb_char_t const* continuation = luaL_optstring(lua, 3, ""); tb_assert_and_check_return_val(mode && continuation, 0); @@ -514,26 +519,22 @@ tb_int_t xm_io_file_read(lua_State* lua) if (lua_isnumber(lua, 2)) { count = (tb_long_t)lua_tointeger(lua, 2); - if (count < 0) xm_io_file_return_error(lua, file, "invalid read size, must be positive nubmber or 0"); + if (count < 0) xm_io_file_return_error(lua, "invalid read size, must be positive nubmber or 0"); } else if (*mode == '*') mode++; if (xm_io_file_is_file(file)) { - // this file has been closed? - if (xm_io_file_is_closed_file(file)) - xm_io_file_return_error_closed(lua); - if (count >= 0) return xm_io_file_read_n(lua, file, continuation, count); switch (*mode) { case 'a': return xm_io_file_read_all(lua, file, continuation); case 'L': return xm_io_file_read_line(lua, file, continuation, tb_true); - case 'n': xm_io_file_return_error(lua, file, "read number is not implemented"); + case 'n': xm_io_file_return_error(lua, "read number is not implemented"); case 'l': return xm_io_file_read_line(lua, file, continuation, tb_false); default: - xm_io_file_return_error(lua, file, "unknonwn read mode"); + xm_io_file_return_error(lua, "unknonwn read mode"); return 0; } } @@ -547,7 +548,7 @@ tb_int_t xm_io_file_read(lua_State* lua) case 'n': return xm_io_file_std_read_num(lua, file, continuation); case 'l': return xm_io_file_std_read_line(lua, file, continuation, tb_false); default: - xm_io_file_return_error(lua, file, "unknonwn read mode"); + xm_io_file_return_error(lua, "unknonwn read mode"); return 0; } } diff --git a/core/src/xmake/io/file_seek.c b/core/src/xmake/io/file_seek.c index 54e0d4026..8387a6083 100644 --- a/core/src/xmake/io/file_seek.c +++ b/core/src/xmake/io/file_seek.c @@ -14,7 +14,7 @@ * * Copyright (C) 2015 - 2019, TBOOX Open Source Group. * - * @author OpportunityLiu + * @author OpportunityLiu, ruki * @file file_seek.c * */ @@ -28,31 +28,35 @@ /* ////////////////////////////////////////////////////////////////////////////////////// * includes */ -#include "file.h" #include "prefix.h" /* ////////////////////////////////////////////////////////////////////////////////////// * implementation */ -/* - * file:seek([whence [, offset]]) - */ +// io.file_seek(file, [whence [, offset]]) tb_int_t xm_io_file_seek(lua_State* lua) { // check tb_assert_and_check_return_val(lua, 0); - xm_io_file_t* file = xm_io_getfile(lua); + // is user data? + if (!lua_isuserdata(lua, 1)) + return 0; + + // get file + xm_io_file_t* file = (xm_io_file_t*)lua_touserdata(lua, 1); + tb_check_return_val(file, 0); + + // get whence and offset tb_char_t const* whence = luaL_optstring(lua, 2, "cur"); tb_hong_t offset = (tb_hong_t)luaL_optnumber(lua, 3, 0); - tb_assert_and_check_return_val(file && whence, 0); + tb_assert_and_check_return_val(whence, 0); + // seek file if (xm_io_file_is_file(file)) { - if (xm_io_file_is_closed_file(file)) - xm_io_file_return_error_closed(lua); - + tb_assert(file->file_ref); switch (*whence) { case 's': // "set" @@ -62,7 +66,7 @@ tb_int_t xm_io_file_seek(lua_State* lua) tb_hong_t size = tb_stream_size(file->file_ref); if (size > 0 && size + offset <= size) offset = size + offset; - else xm_io_file_return_error(lua, file, "seek failed, invalid offset!"); + else xm_io_file_return_error(lua, "seek failed, invalid offset!"); } break; default: // "cur" @@ -73,9 +77,9 @@ tb_int_t xm_io_file_seek(lua_State* lua) if (tb_stream_seek(file->file_ref, offset)) { lua_pushnumber(lua, (lua_Number)offset); - xm_io_file_return_success(); + return 1; } - else xm_io_file_return_error(lua, file, "seek failed!"); + else xm_io_file_return_error(lua, "seek failed!"); } - else xm_io_file_return_error(lua, file, "seek is not supported on this file"); + else xm_io_file_return_error(lua, "seek is not supported on this file"); } diff --git a/core/src/xmake/io/file_size.c b/core/src/xmake/io/file_size.c new file mode 100644 index 000000000..ca0343e8a --- /dev/null +++ b/core/src/xmake/io/file_size.c @@ -0,0 +1,60 @@ +/*!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 - 2019, TBOOX Open Source Group. + * + * @author OpportunityLiu, ruki + * @file file_size.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * trace + */ +#define TB_TRACE_MODULE_NAME "file_size" +#define TB_TRACE_MODULE_DEBUG (0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * implementation + */ + +// io.file_size(file) +tb_int_t xm_io_file_size(lua_State* lua) +{ + // check + tb_assert_and_check_return_val(lua, 0); + + // is user data? + if (!lua_isuserdata(lua, 1)) + return 0; + + // get file + xm_io_file_t* file = (xm_io_file_t*)lua_touserdata(lua, 1); + tb_check_return_val(file, 0); + + // get file length + if (xm_io_file_is_file(file)) + { + // get size from raw file stream, because we cannot get size from fstream + tb_assert(file->stream); + lua_pushnumber(lua, (lua_Number)tb_stream_size(file->stream)); + return 1; + } + else xm_io_file_return_error(lua, "getting file size for this file is invalid"); +} diff --git a/core/src/xmake/io/file_write.c b/core/src/xmake/io/file_write.c index d0c371516..7dc38cdce 100644 --- a/core/src/xmake/io/file_write.c +++ b/core/src/xmake/io/file_write.c @@ -14,7 +14,7 @@ * * Copyright (C) 2015 - 2019, TBOOX Open Source Group. * - * @author OpportunityLiu + * @author OpportunityLiu, ruki * @file file_write.c * */ @@ -29,7 +29,6 @@ * includes */ #include "prefix.h" -#include "file.h" /* ////////////////////////////////////////////////////////////////////////////////////// * private implementation @@ -37,7 +36,7 @@ static tb_void_t xm_io_file_write_file_directly(xm_io_file_t* file, tb_char_t const* data, tb_size_t size) { // check - tb_assert(file && data && xm_io_file_is_file(file) && !xm_io_file_is_closed(file)); + tb_assert(file && data && xm_io_file_is_file(file) && file->file_ref); // write data to file tb_stream_bwrit(file->file_ref, (tb_byte_t const*)data, size); @@ -45,7 +44,7 @@ static tb_void_t xm_io_file_write_file_directly(xm_io_file_t* file, tb_char_t co static tb_void_t xm_io_file_write_file_transcrlf(xm_io_file_t* file, tb_char_t const* data, tb_size_t size) { // check - tb_assert(file && data && xm_io_file_is_file(file) && !xm_io_file_is_closed(file)); + tb_assert(file && data && xm_io_file_is_file(file) && file->file_ref); #ifdef TB_CONFIG_OS_WINDOWS @@ -95,7 +94,7 @@ static tb_void_t xm_io_file_write_file_transcrlf(xm_io_file_t* file, tb_char_t c static tb_void_t xm_io_file_write_std(xm_io_file_t* file, tb_char_t const* data, tb_size_t size) { // check - tb_assert(file && data && xm_io_file_is_std(file) && !xm_io_file_is_closed(file)); + tb_assert(file && data && xm_io_file_is_std(file)); // check type tb_size_t type = (file->type & ~XM_IO_FILE_FLAG_TTY); @@ -105,22 +104,26 @@ static tb_void_t xm_io_file_write_std(xm_io_file_t* file, tb_char_t const* data, tb_stdfile_writ(file->std_ref, (tb_byte_t const*)data, size); } -/* - * file:write(...) +/* ////////////////////////////////////////////////////////////////////////////////////// + * implementation */ + +// io.file_write(file, ...) tb_int_t xm_io_file_write(lua_State* lua) { // check tb_assert_and_check_return_val(lua, 0); - // get file - xm_io_file_t* file = xm_io_getfile(lua); - tb_int_t narg = lua_gettop(lua); + // is user data? + if (!lua_isuserdata(lua, 1)) + return 0; - // this file has been closed? - if (xm_io_file_is_closed(file)) - xm_io_file_return_error_closed(lua); + // get file + xm_io_file_t* file = (xm_io_file_t*)lua_touserdata(lua, 1); + tb_check_return_val(file, 0); + // write file data + tb_int_t narg = lua_gettop(lua); if (narg > 1) { tb_bool_t is_binary = file->encoding == XM_IO_FILE_ENCODING_BINARY; @@ -141,7 +144,7 @@ tb_int_t xm_io_file_write(lua_State* lua) xm_io_file_write_file_transcrlf(file, data, (tb_size_t)datasize); } } - - lua_settop(lua, 1); - xm_io_file_return_success(); + lua_settop(lua, 1); + lua_pushboolean(lua, tb_true); + return 1; } diff --git a/core/src/xmake/io/open.c b/core/src/xmake/io/open.c deleted file mode 100644 index b71daf7df..000000000 --- a/core/src/xmake/io/open.c +++ /dev/null @@ -1,320 +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 - 2019, TBOOX Open Source Group. - * - * @author OpportunityLiu - * @file open.c - * - */ - -/* ////////////////////////////////////////////////////////////////////////////////////// - * trace - */ -#define TB_TRACE_MODULE_NAME "open" -#define TB_TRACE_MODULE_DEBUG (0) - -/* ////////////////////////////////////////////////////////////////////////////////////// - * includes - */ -#include "file.h" -#include "prefix.h" - -/* ////////////////////////////////////////////////////////////////////////////////////// - * macros - */ - -// num of bytes read to guess encoding -#define CHECK_SIZE (1024) - -// is utf-8 tail character -#define IS_UTF8_TAIL(c) (c >= 0x80 && c < 0xc0) - -/* ////////////////////////////////////////////////////////////////////////////////////// - * private implementation - */ -static tb_size_t xm_io_file_detect_charset(tb_byte_t const** data_ptr, tb_long_t size) -{ - // check - tb_assert(data_ptr && *data_ptr); - - tb_byte_t const* data = *data_ptr; - tb_size_t charset = XM_IO_FILE_ENCODING_BINARY; - do - { - if (size >= 3 && data[0] == 239 && data[1] == 187 && data[2] == 191) // utf-8 with bom - { - charset = TB_CHARSET_TYPE_UTF8; - data += 3; // skip bom - break; - } - if (size >= 2) - { - if (data[0] == 254 && data[1] == 255) // utf16be - { - charset = TB_CHARSET_TYPE_UTF16 | TB_CHARSET_TYPE_BE; - data += 2; // skip bom - break; - } - else if (data[0] == 255 && data[1] == 254) // utf16le - { - charset = TB_CHARSET_TYPE_UTF16 | TB_CHARSET_TYPE_LE; - data += 2; // skip bom - break; - } - } - - tb_sint16_t utf16be_conf = 0; - tb_sint16_t utf16le_conf = 0; - tb_sint16_t utf8_conf = 0; - tb_sint16_t ascii_conf = 0; - tb_sint16_t zero_count = 0; - for (tb_long_t i = 0; i < (size - 4) && i < CHECK_SIZE; i++) - { - if (data[i] == 0) zero_count++; - - if (data[i] < 0x80) - ascii_conf++; - else - ascii_conf = TB_MINS16; - - if (i % 2 == 0) - { - if (data[i] == 0) utf16be_conf++; - if (data[i + 1] == 0) utf16le_conf++; - } - - if (IS_UTF8_TAIL(data[i])) - ; - else if (data[i] < 0x80) - utf8_conf++; - else if (data[i] >= 0xc0 && data[i] < 0xe0 && IS_UTF8_TAIL(data[i + 1])) - utf8_conf++; - else if (data[i] >= 0xe0 && data[i] < 0xf0 && IS_UTF8_TAIL(data[i + 1]) && IS_UTF8_TAIL(data[i + 2])) - utf8_conf++; - else if (data[i] >= 0xf0 && data[i] < 0xf8 && IS_UTF8_TAIL(data[i + 1]) && IS_UTF8_TAIL(data[i + 2]) && IS_UTF8_TAIL(data[i + 3])) - utf8_conf++; - else - utf8_conf = TB_MINS16; - } - - if (ascii_conf > 0 && zero_count <= 1) - { - charset = TB_CHARSET_TYPE_UTF8; - break; - } - if (utf8_conf > 0 && zero_count <= 1) - { - charset = TB_CHARSET_TYPE_UTF8; - break; - } - if (utf16be_conf > 0 && utf16be_conf > utf16le_conf) - { - charset = TB_CHARSET_TYPE_UTF16 | TB_CHARSET_TYPE_BE; - break; - } - if (utf16le_conf > 0 && utf16le_conf >= utf16be_conf) - { - charset = TB_CHARSET_TYPE_UTF16 | TB_CHARSET_TYPE_LE; - break; - } - if (utf8_conf > 0) - { - charset = TB_CHARSET_TYPE_UTF8; - break; - } - -#ifdef TB_CONFIG_OS_WINDOWS - charset = TB_CHARSET_TYPE_ANSI; -#endif - - } while (0); - - *data_ptr = data; - return charset; -} -static tb_size_t xm_io_file_detect_encoding(tb_stream_ref_t stream, tb_long_t* pbomoff) -{ - // check - tb_assert_and_check_return_val(stream && pbomoff, XM_IO_FILE_ENCODING_BINARY); - - // detect encoding - tb_byte_t* data = tb_null; - tb_size_t encoding = XM_IO_FILE_ENCODING_BINARY; - tb_long_t size = tb_stream_peek(stream, &data, CHECK_SIZE); - if (size > 0) - { - tb_byte_t const* p = data; - encoding = xm_io_file_detect_charset(&p, size); - *pbomoff = p - data; - } - return encoding; -} - -/* ////////////////////////////////////////////////////////////////////////////////////// - * implementation - */ - -/* - * io.open(path, modestr) - */ -tb_int_t xm_io_open(lua_State* lua) -{ - // check - tb_assert_and_check_return_val(lua, 0); - - // get file path and mode - tb_char_t const* path = luaL_checkstring(lua, 1); - tb_char_t const* modestr = luaL_optstring(lua, 2, "r"); - tb_assert_and_check_return_val(path && modestr, 0); - - // get file mode value - tb_size_t mode; - switch (modestr[0]) - { - case 'w': mode = TB_FILE_MODE_RW | TB_FILE_MODE_CREAT | TB_FILE_MODE_TRUNC; break; - case 'a': mode = TB_FILE_MODE_RW | TB_FILE_MODE_APPEND | TB_FILE_MODE_CREAT; break; - case 'r': default: mode = TB_FILE_MODE_RO; break; - } - - // get file encoding - tb_long_t bomoff = 0; - tb_stream_ref_t stream = tb_null; - tb_bool_t update = !!tb_strchr(modestr, '+'); - tb_size_t encoding = XM_IO_FILE_ENCODING_UNKNOWN; - if (modestr[1] == 'b' || (update && modestr[2] == 'b')) - encoding = XM_IO_FILE_ENCODING_BINARY; - else if (tb_strstr(modestr, "utf8") || tb_strstr(modestr, "utf-8")) - encoding = TB_CHARSET_TYPE_UTF8; - else if (tb_strstr(modestr, "utf16le") || tb_strstr(modestr, "utf-16le")) - encoding = TB_CHARSET_TYPE_UTF16 | TB_CHARSET_TYPE_LE; - else if (tb_strstr(modestr, "utf16be") || tb_strstr(modestr, "utf-16be")) - encoding = TB_CHARSET_TYPE_UTF16 | TB_CHARSET_TYPE_BE; - else if (tb_strstr(modestr, "utf16") || tb_strstr(modestr, "utf-16")) - encoding = TB_CHARSET_TYPE_UTF16 | TB_CHARSET_TYPE_NE; - else if (modestr[0] == 'w' || modestr[0] == 'a') // set to utf-8 if not specified for the writing mode - encoding = TB_CHARSET_TYPE_UTF8; - else if (modestr[0] == 'r') // detect encoding if not specified for the reading mode - { - stream = tb_stream_init_from_file(path, mode); - if (stream && tb_stream_open(stream)) - encoding = xm_io_file_detect_encoding(stream, &bomoff); - else - { - if (stream) tb_stream_exit(stream); - lua_pushnil(lua); - lua_pushliteral(lua, "file not found!"); - return 2; - } - } - else - { - lua_pushnil(lua); - lua_pushliteral(lua, "invalid open mode!"); - return 2; - } - tb_assert_and_check_return_val(encoding != XM_IO_FILE_ENCODING_UNKNOWN, 0); - - // open file - tb_bool_t open_ok = tb_false; - tb_stream_ref_t file_ref = tb_null; - tb_stream_ref_t fstream = tb_null; - do - { - // init stream from file - stream = stream? stream : tb_stream_init_from_file(path, mode); - tb_assert_and_check_break(stream); - - // is transcode? - tb_bool_t is_transcode = encoding != TB_CHARSET_TYPE_UTF8 && encoding != XM_IO_FILE_ENCODING_BINARY; - if (is_transcode) - { - if (modestr[0] == 'r') - fstream = tb_stream_init_filter_from_charset(stream, encoding, TB_CHARSET_TYPE_UTF8); - else - fstream = tb_stream_init_filter_from_charset(stream, TB_CHARSET_TYPE_UTF8, encoding); - tb_assert_and_check_break(fstream); - - // use fstream as file - file_ref = fstream; - } - else file_ref = stream; - - // open file stream - if (!tb_stream_open(file_ref)) break; - - // skip bom characters if exists - if (bomoff > 0 && !tb_stream_seek(stream, bomoff)) break; - - // ok - open_ok = tb_true; - - } while (0); - - // open failed? - if (!open_ok) - { - // exit stream - if (stream) tb_stream_exit(stream); - stream = tb_null; - - // exit charset stream filter - if (fstream) tb_stream_exit(fstream); - fstream = tb_null; - - // return errors - lua_pushnil(lua); - lua_pushliteral(lua, "failed to open file."); - return 2; - } - - // get absolute file path - tb_char_t full[TB_PATH_MAXN] = {0}; - path = tb_path_absolute(path, full, TB_PATH_MAXN); - tb_assert_and_check_return_val(path, 0); - - // new file - xm_io_file_t* xm_file = xm_io_newfile(lua); - xm_file->file_ref = file_ref; - xm_file->stream = stream; - xm_file->fstream = fstream; - xm_file->mode = mode; - xm_file->type = XM_IO_FILE_TYPE_FILE; - xm_file->encoding = encoding; - - // init the read/write line cache buffer - tb_buffer_init(&xm_file->rcache); - tb_buffer_init(&xm_file->wcache); - - // save file path - tb_size_t pathlen = tb_strlen(path); - xm_file->path = tb_malloc_cstr(pathlen + 1); - if (xm_file->path) - { - tb_strncpy((tb_char_t*)xm_file->path, path, pathlen); - ((tb_char_t*)xm_file->path)[pathlen] = '\0'; - } - - // save file name - tb_size_t name_maxn = tb_arrayn(xm_file->name); - tb_strlcpy(xm_file->name, "file: ", name_maxn); - if (pathlen < name_maxn - tb_arrayn("file: ")) - tb_strcat(xm_file->name, path); - else - { - tb_strcat(xm_file->name, "..."); - tb_strcat(xm_file->name, path + (pathlen - name_maxn + tb_arrayn("file: ") + tb_arrayn("..."))); - } - return 1; -} diff --git a/core/src/xmake/io/prefix.h b/core/src/xmake/io/prefix.h index fc61e8829..761d4cdf8 100644 --- a/core/src/xmake/io/prefix.h +++ b/core/src/xmake/io/prefix.h @@ -26,6 +26,72 @@ */ #include "../prefix.h" +/* ////////////////////////////////////////////////////////////////////////////////////// + * macros + */ +#define xm_io_file_is_file(file) ((file)->type == XM_IO_FILE_TYPE_FILE) +#define xm_io_file_is_std(file) ((file)->type != XM_IO_FILE_TYPE_FILE) +#define xm_io_file_is_tty(file) (!!((file)->type & XM_IO_FILE_FLAG_TTY)) + +// return file error +#define xm_io_file_return_error(lua, error) \ + do \ + { \ + lua_pushnil(lua); \ + lua_pushstring(lua, error); \ + return 2; \ + } while (0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * types + */ +typedef enum __xm_io_file_type_e +{ + XM_IO_FILE_TYPE_FILE = 0 //!< disk file + +, XM_IO_FILE_TYPE_STDIN = 1 +, XM_IO_FILE_TYPE_STDOUT = 2 +, XM_IO_FILE_TYPE_STDERR = 3 + +, XM_IO_FILE_FLAG_TTY = 0x10 //!< mark tty std stream + +} xm_io_file_type_e; + +/* use negetive numbers for this enum, its a extension for tb_charset_type_e + * before adding new values, make sure they have not conflicts with values in tb_charset_type_e + */ +typedef enum __xm_io_file_encoding_e +{ + XM_IO_FILE_ENCODING_UNKNOWN = -1 +, XM_IO_FILE_ENCODING_BINARY = -2 + +} xm_io_file_encoding_e; + +// the file type +typedef struct __xm_io_file_t +{ + union + { + /* the normal file for XM_IO_FILE_TYPE_FILE + * + * direct: file_ref -> stream -> file + * transcode: file_ref -> fstream -> stream -> file + */ + tb_stream_ref_t file_ref; + + // the standard io file + tb_stdfile_ref_t std_ref; + }; + + tb_stream_ref_t stream; // the file stream for XM_IO_FILE_TYPE_FILE + tb_stream_ref_t fstream; // the file charset stream filter + tb_size_t mode; // tb_file_mode_t + tb_size_t type; // xm_io_file_type_e + tb_size_t encoding; // value of xm_io_file_encoding_e or tb_charset_type_e + tb_buffer_t rcache; // the read line cache buffer + tb_buffer_t wcache; // the write line cache buffer + +} xm_io_file_t; #endif diff --git a/core/src/xmake/io/std.c b/core/src/xmake/io/std.c index 2ed46a341..b549f3da7 100644 --- a/core/src/xmake/io/std.c +++ b/core/src/xmake/io/std.c @@ -14,31 +14,24 @@ * * Copyright (C) 2015 - 2019, TBOOX Open Source Group. * - * @author OpportunityLiu - * @file std.c + * @author OpportunityLiu, ruki + * @file file_std.c * */ /* ////////////////////////////////////////////////////////////////////////////////////// * trace */ -#define TB_TRACE_MODULE_NAME "io_std" +#define TB_TRACE_MODULE_NAME "std" #define TB_TRACE_MODULE_DEBUG (0) /* ////////////////////////////////////////////////////////////////////////////////////// * includes */ -#include "file.h" #include "prefix.h" -#include -#ifdef TB_CONFIG_OS_WINDOWS -# include -#else -# include -#endif /* ////////////////////////////////////////////////////////////////////////////////////// - * implementation + * private implementation */ static tb_size_t xm_io_std_isatty(tb_size_t type) { @@ -66,73 +59,49 @@ static tb_size_t xm_io_std_isatty(tb_size_t type) return type; } -static tb_void_t xm_io_std_init(lua_State* lua, tb_size_t type) +/* ////////////////////////////////////////////////////////////////////////////////////// + * interfaces + */ + +// io.std(stdin: 1, stdout: 2, stderr: 3) +tb_int_t xm_io_std(lua_State* lua) { // check - tb_assert_and_check_return(lua); + tb_assert_and_check_return_val(lua, 0); - tb_char_t const* name = tb_null; - tb_char_t const* path = tb_null; - tb_stdfile_ref_t fp = tb_null; + // get std type + tB_int_t type = lua_tointeger(lua, 1); + tb_stdfile_ref_t fp = tb_null; switch (type) { case XM_IO_FILE_TYPE_STDIN: - name = "stdin"; fp = tb_stdfile_input(); break; case XM_IO_FILE_TYPE_STDOUT: - name = "stdout"; fp = tb_stdfile_output(); break; case XM_IO_FILE_TYPE_STDERR: - name = "stderr"; fp = tb_stdfile_error(); break; } -#ifdef TB_CONFIG_OS_WINDOWS - path = "CON"; // console device -#else - switch (type) - { - case XM_IO_FILE_TYPE_STDIN: path = "/dev/stdin"; break; - case XM_IO_FILE_TYPE_STDOUT: path = "/dev/stdout"; break; - case XM_IO_FILE_TYPE_STDERR: path = "/dev/stderr"; break; - } -#endif - tb_assert_and_check_return(name && path && fp); - // new file - xm_io_file_t* file = xm_io_newfile(lua); - tb_assert_and_check_return(file); - lua_setfield(lua, -2, name); + // make file + xm_io_file_t* file = tb_malloc0_type(xm_io_file_t); + tb_assert_and_check_return_val(file, 0); // init file - file->encoding = TB_CHARSET_TYPE_UTF8; - file->type = xm_io_std_isatty(type); - file->path = path; - file->std_ref = fp; - file->stream = tb_null; - file->fstream = tb_null; + file->std_ref = fp; + file->stream = tb_null; + file->fstream = tb_null; + file->type = xm_io_std_isatty(type); + file->encoding = TB_CHARSET_TYPE_UTF8; - // init name - tb_char_t const* info = xm_io_file_is_tty(file) ? "" : " redirected"; - tb_snprintf(file->name, tb_arrayn(file->name), "file: (%s%s)", name, info); + // init the read/write line cache buffer + tb_buffer_init(&file->rcache); + tb_buffer_init(&file->wcache); - // init the line cache buffer - tb_buffer_init(&file->rcache); - tb_buffer_init(&file->wcache); + // ok + lua_pushlightuserdata(lua, (tb_pointer_t)file); + return 1; } -tb_int_t xm_io_std(lua_State* lua) -{ - // check - tb_assert_and_check_return_val(lua, 0); - - // init io.stdin, io.stdout and io.stderr - lua_getglobal(lua, "io"); - xm_io_std_init(lua, XM_IO_FILE_TYPE_STDIN); - xm_io_std_init(lua, XM_IO_FILE_TYPE_STDOUT); - xm_io_std_init(lua, XM_IO_FILE_TYPE_STDERR); - lua_pop(lua, 1); - return 0; -} diff --git a/core/src/xmake/machine.c b/core/src/xmake/machine.c index 53f781031..83b218760 100644 --- a/core/src/xmake/machine.c +++ b/core/src/xmake/machine.c @@ -89,21 +89,16 @@ tb_int_t xm_os_gid(lua_State* lua); tb_int_t xm_os_getown(lua_State* lua); #endif -// the io functions -tb_int_t xm_io_std(lua_State* lua); -tb_int_t xm_io_open(lua_State* lua); - // the io/file functions +tb_int_t xm_io_std(lua_State* lua); +tb_int_t xm_io_file_open(lua_State* lua); tb_int_t xm_io_file_read(lua_State* lua); tb_int_t xm_io_file_seek(lua_State* lua); +tb_int_t xm_io_file_size(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_isatty(lua_State* lua); -tb_int_t xm_io_file_path(lua_State* lua); -tb_int_t xm_io_file___len(lua_State* lua); -tb_int_t xm_io_file___tostring(lua_State* lua); -tb_int_t xm_io_file___gc(lua_State* lua); // the io/filelock functions tb_int_t xm_io_filelock_open(lua_State* lua); @@ -228,7 +223,15 @@ static luaL_Reg const g_winos_functions[] = // the io functions static luaL_Reg const g_io_functions[] = { - { "open", xm_io_open } + { "std", xm_io_std } +, { "file_open", xm_io_file_open } +, { "file_read", xm_io_file_read } +, { "file_seek", xm_io_file_seek } +, { "file_size", xm_io_file_size } +, { "file_write", xm_io_file_write } +, { "file_flush", xm_io_file_flush } +, { "file_isatty", xm_io_file_isatty } +, { "file_close", xm_io_file_close } , { "filelock_open", xm_io_filelock_open } , { "filelock_lock", xm_io_filelock_lock } , { "filelock_trylock", xm_io_filelock_trylock } @@ -237,22 +240,6 @@ static luaL_Reg const g_io_functions[] = , { tb_null, tb_null } }; -// the io/file functions -static luaL_Reg const g_io_file_functions[] = -{ - { "read", xm_io_file_read } -, { "seek", xm_io_file_seek } -, { "write", xm_io_file_write } -, { "flush", xm_io_file_flush } -, { "isatty", xm_io_file_isatty } -, { "path", xm_io_file_path } -, { "close", xm_io_file_close } -, { "__gc", xm_io_file___gc } -, { "__len", xm_io_file___len } -, { "__tostring", xm_io_file___tostring } -, { tb_null, tb_null } -}; - // the path functions static luaL_Reg const g_path_functions[] = { @@ -614,12 +601,6 @@ xm_machine_ref_t xm_machine_init() // bind io functions luaL_register(machine->lua, "io", g_io_functions); - // bind io._file (metatable) functions - xm_machine_register_metatable(machine, "io", "_file", "io._file*", g_io_file_functions); - - // add stdin, stdout, stderr to io - xm_io_std(machine->lua); - // bind path functions luaL_register(machine->lua, "path", g_path_functions); diff --git a/core/src/xmake/makefile b/core/src/xmake/makefile index 1568a8b45..9385211cb 100644 --- a/core/src/xmake/makefile +++ b/core/src/xmake/makefile @@ -44,12 +44,12 @@ xmake_C_FILES += \ os/uid \ os/gid \ os/getown \ - io/file___len \ - io/file___tostring \ - io/file_close___gc \ + io/std \ + io/file_size \ + io/file_close \ io/file_flush \ io/file_isatty \ - io/file_path \ + io/file_open \ io/file_read \ io/file_seek \ io/file_write \ @@ -58,8 +58,6 @@ xmake_C_FILES += \ io/filelock_unlock \ io/filelock_trylock \ io/filelock_close \ - io/open \ - io/std \ path/relative \ path/absolute \ path/translate \ diff --git a/xmake/core/base/io.lua b/xmake/core/base/io.lua index cbb2a409b..b86d4dd1a 100644 --- a/xmake/core/base/io.lua +++ b/xmake/core/base/io.lua @@ -20,7 +20,7 @@ -- define module local io = io or {} -local _file = _file or io._file or {} +local _file = _file or {} local _filelock = _filelock or {} -- load modules @@ -28,15 +28,130 @@ local path = require("base/path") local table = require("base/table") local string = require("base/string") --- save original apis -io._open = io._open or io.open +-- save metatable +io._file = _file io._filelock = _filelock -_file._read = _file._read or _file.read + +-- new an file +function _file.new(filepath, file) + local file = table.inherit(_file) + file._NAME = path.filename(filepath) + file._PATH = filepath + file._FILE = file + setmetatable(file, _file) + return file +end + +-- get the file name +function _file:name() + return self._NAME +end + +-- get the file path +function _file:path() + return self._PATH +end + +-- close file +function _file:close() + if not self._FILE then + return false, string.format("file(%s) has been closed!", self:name()) + end + local ok, errors = io.file_close(self._FILE) + if ok then + self._FILE = nil + end + return ok, errors +end + +-- tostring(file) +function _file:__tostring() + return "file: " .. self:name() +end + +-- gc(file) +function _file:__gc() + if self._FILE and io.file_close(self._FILE) then + self._FILE = nil + end +end + +-- get file length +function _file:__len() + return self:size() +end + +-- get file size +function _file:size() + if not self._FILE then + return false, string.format("file(%s) has been closed!", self:name()) + end + local result, errors = io.file_size(self._FILE) + if not result and errors then + errors = string.format("file(%s): %s", self:name(), errors) + end + return result, errors +end -- read data from file function _file:read(fmt, opt) + if not self._FILE then + return false, string.format("file(%s) has been closed!", self:name()) + end opt = opt or {} - return self:_read(fmt, opt.continuation) + local result, errors = io.file_read(self._FILE, fmt, opt.continuation) + if not result then + errors = string.format("file(%s): %s", self:name(), errors) + end + return result, errors +end + +-- write data to file +function _file:write(...) + if not self._FILE then + return false, string.format("file(%s) has been closed!", self:name()) + end + local ok, errors = io.file_write(self._FILE, ...) + if not ok and errors then + errors = string.format("file(%s): %s", self:name(), errors) + end + return ok, errors +end + +-- seek offset at file +function _file:seek(whence, offset) + if not self._FILE then + return false, string.format("file(%s) has been closed!", self:name()) + end + local result, errors = io.file_seek(self._FILE, whence, offset) + if not result and errors then + errors = string.format("file(%s): %s", self:name(), errors) + end + return result, errors +end + +-- flush data to file +function _file:flush() + if not self._FILE then + return false, string.format("file(%s) has been closed!", self:name()) + end + local ok, errors = io.file_flush(self._FILE) + if not ok and errors then + errors = string.format("file(%s): %s", self:name(), errors) + end + return ok, errors +end + +-- this file is a tty? +function _file:isatty() + if not self._FILE then + return false, string.format("file(%s) has been closed!", self:name()) + end + local ok, errors = io.file_isatty(self._FILE) + if not ok and errors then + errors = string.format("file(%s): %s", self:name(), errors) + end + return ok, errors end -- iterator of lines @@ -66,11 +181,11 @@ end -- save object function _file:save(object, opt) local str, errors = string.serialize(object, opt) - if not errors then - self:write(str) - return str + if errors then + return false, errors + else + return self:write(str) end - return str, errors end -- load object @@ -183,8 +298,7 @@ end -- gc(filelock) function _filelock:__gc() - local ok = self._LOCK and io.filelock_close(self._LOCK) or false - if ok then + if self._LOCK and io.filelock_close(self._LOCK) then self._LOCK = nil self._LOCKED_NUM = 0 end @@ -236,7 +350,7 @@ function io.read(fmt, opt) end function io.write(...) - io.stdout:write(...) + return io.stdout:write(...) end function io.print(...) @@ -274,9 +388,9 @@ function io.writefile(filepath, data, opt) end -- isatty -function io.isatty(fd) - fd = fd or io.stdout - return fd:isatty() +function io.isatty(file) + file = file or io.stdout + return file:isatty() end -- replace the original open interface @@ -290,11 +404,12 @@ function io.open(filepath, mode, opt) mode = mode or "r" -- open it - local file = io._open(filepath, mode .. (opt.encoding or "")) - if not file then + local file = io.file_open(filepath, mode .. (opt.encoding or "")) + if file then + return _file.new(filepath, file) + else return nil, string.format("failed to open file: %s", filepath) end - return file end -- open a filelock diff --git a/xmake/core/base/process.lua b/xmake/core/base/process.lua index cf5f3744e..1e4fe5434 100644 --- a/xmake/core/base/process.lua +++ b/xmake/core/base/process.lua @@ -84,8 +84,7 @@ end -- gc(subprocess) function _subprocess:__gc() - local ok = self._PROC and process._close(self._PROC) or false - if ok then + if self._PROC and process._close(self._PROC) then self._PROC = nil end end diff --git a/xmake/core/sandbox/modules/io.lua b/xmake/core/sandbox/modules/io.lua index ca6f29ee4..752026e78 100644 --- a/xmake/core/sandbox/modules/io.lua +++ b/xmake/core/sandbox/modules/io.lua @@ -27,57 +27,105 @@ local vformat = require("sandbox/modules/vformat") -- define module local sandbox_io = sandbox_io or {} -local sandbox_io_file = sandbox_io._file or {} +local sandbox_io_file = sandbox_io_file or {} local sandbox_io_filelock = sandbox_io_filelock or {} -sandbox_io._file = sandbox_io_file +sandbox_io._file = sandbox_io._file or io._file sandbox_io._filelock = sandbox_io._filelock or io._filelock --- inherit some builtin interfaces -sandbox_io.lines = io.lines -sandbox_io.read = io.read -sandbox_io.isatty = io.isatty - --- inherit matatable of file -if sandbox_io_file.__index ~= sandbox_io_file then - sandbox_io_file.__index = sandbox_io_file - for k, v in pairs(io._file) do - if type(v) == "function" then - sandbox_io_file[k] = function(s, ...) - local result, err = v(s._FILE, ...) - if result == nil and err ~= nil then - raise(err) - end - -- wrap to sandbox_file again - if result == s._FILE then - result = s - end - return result - end - end +-- get file size +function sandbox_io_file.size(file) + local result, errors = file:_size() + if not result then + raise(errors) end - -- file:lines does not use its second return value for error - sandbox_io_file.lines = io._file.lines + return result end --- get file size -function sandbox_io_file:size() - -- __len on tables is scheduled to be supported in 5.2. - return sandbox_io_file.__len(self) +-- close file +function sandbox_io_file.close(file) + local ok, errors = file:_close() + if not ok then + raise(errors) + end + return ok +end + +-- flush file +function sandbox_io_file.flush(file) + local ok, errors = file:_flush() + if not ok then + raise(errors) + end + return ok +end + +-- this file is a tty? +function sandbox_io_file.isatty(file) + local ok, errors = file:_isatty() + if not ok then + raise(errors) + end + return ok +end + +-- seek offset at file +function sandbox_io_file.seek(file, whence, offset) + local result, errors = file:_seek(whence, offset) + if not result then + raise(errors) + end + return result +end + +-- read data from file +function sandbox_io_file.read(file, fmt, opt) + local result, errors = file:_read(fmt, opt) + if not result then + raise(errors) + end + return result +end + +-- write data to file +function sandbox_io_file.write(file, ...) + local ok, errors = file:_write(...) + if not ok then + raise(errors) + end + return ok end -- print file -function sandbox_io_file:print(...) - return self:write(vformat(...), "\n") +function sandbox_io_file.print(file, ...) + return sandbox_io_file.write(file, vformat(...), "\n") end -- printf file -function sandbox_io_file:printf(...) - return self:write(vformat(...)) +function sandbox_io_file.printf(file, ...) + return sandbox_io_file.write(file, vformat(...)) end --- writef file -function sandbox_io_file:writef(...) - return self:write(string.format(...)) +-- writef file (without value filter) +function sandbox_io_file.writef(file, ...) + return sandbox_io_file.write(file, string.format(...)) +end + +-- load object from file +function sandbox_io_file.load(file) + local result, errors = file:_load() + if errors then + raise(errors) + end + return result +end + +-- save object to file +function sandbox_io_file.save(file, object, opt) + local ok, errors = file:_save(object, opt) + if not ok then + raise(errors) + end + return ok end -- lock filelock @@ -146,9 +194,17 @@ function sandbox_io.open(filepath, mode, opt) raise(errors) end - -- bind metatable - file = { _FILE = file } - setmetatable(file, sandbox_io_file); + -- hook file interfaces + local hooked = {} + for name, func in pairs(sandbox_io_file) do + if not name:startswith("_") and type(func) == "function" then + hooked["_" .. name] = file["_" .. name] or file[name] + hooked[name] = func + end + end + for name, func in pairs(hooked) do + file[name] = func + end return file end @@ -235,6 +291,11 @@ function sandbox_io.readfile(filepath, opt) return result end +--- direct read from stdin +function sandbox_io.read(fmt, opt) + return sandbox_io.stdin:read(fmt, opt) +end + --- direct write to stdout function sandbox_io.write(...) sandbox_io.stdout:write(...) @@ -245,6 +306,12 @@ function sandbox_io.flush(file) return (file or sandbox_io.stdout):flush() end +-- isatty +function sandbox_io.isatty(file) + file = file or sandbox_io.stdout + return file:isatty() +end + -- write all data to file function sandbox_io.writefile(filepath, data, opt) @@ -297,14 +364,6 @@ function sandbox_io.tail(filepath, linecount, opt) io.tail(filepath, linecount, opt) end --- wrap std files -sandbox_io.stdin = { _FILE = io.stdin } -sandbox_io.stderr = { _FILE = io.stderr } -sandbox_io.stdout = { _FILE = io.stdout } -setmetatable(sandbox_io.stdin, sandbox_io_file); -setmetatable(sandbox_io.stderr, sandbox_io_file); -setmetatable(sandbox_io.stdout, sandbox_io_file); - -- return module return sandbox_io -- cgit v1.3.1