From 976b5fca05bf0e8d28a00196ca30eaa096c557e1 Mon Sep 17 00:00:00 2001 From: Lizongdi <1210855344@qq.com> Date: Sun, 20 Sep 2026 14:14:50 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E4=B8=BB=E7=BA=BF=E5=BE=85=E5=90=88?= =?UTF-8?q?=E3=80=91=E5=A2=9E=E5=8A=A0tail=E5=91=BD=E4=BB=A4=EF=BC=8C?= =?UTF-8?q?=E8=A7=A3=E5=86=B3lfs=E5=88=9D=E5=A7=8B=E5=8C=96=E9=97=AE?= =?UTF-8?q?=E9=A2=98=EF=BC=8C=E4=BF=AE=E5=A4=8DRd=5FMgets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- library/common/rd_stdio.c | 37 ++++++++++++++--- lua/lua.c | 86 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 6 deletions(-) diff --git a/library/common/rd_stdio.c b/library/common/rd_stdio.c index 65a83a3..471ac06 100644 --- a/library/common/rd_stdio.c +++ b/library/common/rd_stdio.c @@ -56,12 +56,14 @@ lfs_t g_lfs; lfs_flashbd_t g_bd; struct lfs_flashbd_config g_bd_cfg; struct lfs_config g_cfg; +static uint8_t g_lfs_ready = 0; /* mount 成功后才允许读写,防止 mount 失败后继续写坏盘 */ #endif #ifndef WIN32 WEAK void *Rd_Fopen(const char *path, const char *mode) { #ifdef USE_W25QXX + if (!g_lfs_ready) return NULL; #if LV_USE_FS_LITTLEFS lv_fs_mode_t lv_mode; if (strchr(mode, 'r') != NULL) lv_mode = LV_FS_MODE_RD; @@ -144,6 +146,7 @@ WEAK ssize_t Rd_Mread(void *buf, size_t count, void *file) WEAK ssize_t Rd_Mwrite(const void *buf, size_t count, void *file) { #ifdef USE_W25QXX + if (!g_lfs_ready) return RD_FAILURE; #if LV_USE_FS_LITTLEFS uint32_t size = 0; lv_fs_file_t *fp = (lv_fs_file_t *)file; @@ -244,7 +247,7 @@ WEAK char *Rd_Mgets(char *buf, int size, void *file) while (i < size - 1) { ssize_t n = Rd_Mread(&c, 1, file); - if (n != 0) + if (n != 1) { // 读到 EOF 或 错误(Rd_Mread 返回 0 表示读满请求的字节数) if (i == 0) return NULL; // 什么都没读到 @@ -334,6 +337,7 @@ WEAK int Rd_fflush(void *file) WEAK int Rd_remove(const char *filename) { #ifdef USE_W25QXX + if (!g_lfs_ready) return RD_FAILURE; const char *p = filename; if (strlen(filename) >= 2 && filename[1] == ':') p = filename + 2; int err = lfs_remove(&g_lfs, p); @@ -347,6 +351,7 @@ WEAK int Rd_remove(const char *filename) WEAK int Rd_rename(const char *oldpath, const char *newpath) { #ifdef USE_W25QXX + if (!g_lfs_ready) return RD_FAILURE; const char *o = oldpath, *n = newpath; if (strlen(oldpath) >= 2 && oldpath[1] == ':') o = oldpath + 2; if (strlen(newpath) >= 2 && newpath[1] == ':') n = newpath + 2; @@ -375,14 +380,34 @@ WEAK void Rd_LittleFSInit(void) lfs_flashbd_default_config(&g_cfg, &g_bd, &g_bd_cfg, 1 * 1024 * 1024, 12 * 256, NULL, NULL, NULL); int err = lfs_mount(&g_lfs, &g_cfg); - if (err) + if (err == LFS_ERR_CORRUPT) { - printf("mount error format...\n"); - (void)Rd_Lfs_Mount(); + /* 只有真正的“无文件系统/损坏”才格式化;IO/内存错误格式化只会越弄越糟 */ + printf("lfs: no fs / corrupt, formatting...\n"); + err = lfs_format(&g_lfs, &g_cfg); + if (err == LFS_ERR_OK) + { + err = lfs_mount(&g_lfs, &g_cfg); + } + else + { + printf("lfs: format failed, err=%d\n", err); + } } - return; + else if (err != LFS_ERR_OK) + { + /* LFS_ERR_IO / LFS_ERR_NOMEM 等:不格式化,避免破坏已有数据 */ + printf("lfs: mount failed, err=%d (not formatted)\n", err); + } + + if (err != LFS_ERR_OK) + { + g_lfs_ready = 0; + return; + } + g_lfs_ready = 1; #else - return; + return; #endif } diff --git a/lua/lua.c b/lua/lua.c index 039e13f..97f2977 100644 --- a/lua/lua.c +++ b/lua/lua.c @@ -483,6 +483,91 @@ static int l_cat(lua_State *L) return 0; /* 直接打印到终端,不再向 Lua 返回字符串 */ } +static int l_tail(lua_State *L) +{ + const char *path = luaL_optstring(L, 1, "C:rd.log"); + long n = luaL_optinteger(L, 2, 15); + + long start_off = 0; /* 实际输出的起始偏移 */ + long lines = 0; /* 文件总行数 */ + + if (n > 0) + { + /* 环形缓冲:只保留最近最多 n 行的行首偏移 */ + long *off = (long *)RD_MALLOC(sizeof(long) * (size_t)n); + if (off == NULL) + { + lua_print("tail: out of memory\n"); + return 0; + } + + void *fp = Rd_Fopen(path, "r"); + if (fp == NULL) + { + RD_FREE(off); + lua_print("tail: cannot open '%s'\n", path); + return 0; + } + + /* 第一遍:像 l_cat 一样用 Rd_Mread 分块读取, + * 手动按字节跟踪偏移并记录每行行首偏移(避免使用有问题的 Rd_Mgets) */ + long pos = 0; /* 当前已读到的文件偏移 */ + int at_line_start = 1; /* 当前位置是否是一行的开头 */ + char buf[128]; + ssize_t m; + while ((m = Rd_Mread(buf, sizeof(buf), fp)) > 0) + { + int i; + for (i = 0; i < (int)m; i++) + { + if (at_line_start) + { + off[lines % n] = pos; /* 记录本行行首偏移 */ + lines++; + at_line_start = 0; + } + pos++; + if (buf[i] == '\n') + at_line_start = 1; + } + } + Rd_Mclose(fp); + + if (lines > 0) + { + long first = (lines <= n) ? 0 : (lines - n); + start_off = off[first % n]; + } + RD_FREE(off); + } + else + { + return 0; /* n <= 0 时不输出 */ + } + + /* 第二遍:从 start_off 重新打开并打印到文件末尾 */ + void *fp = Rd_Fopen(path, "r"); + if (fp == NULL) + { + lua_print("tail: cannot open '%s'\n", path); + return 0; + } + if (Rd_Mseek(fp, start_off, SEEK_SET) < 0) /* Rd_Mseek 失败返回 -1 */ + { + Rd_Mclose(fp); + return 0; + } + + char buf[128]; + ssize_t m; + while ((m = Rd_Mread(buf, sizeof(buf), fp)) > 0) + { + lua_print("%.*s", (int)m, buf); + } + Rd_Mclose(fp); + return 0; +} + static int l_mv(lua_State *L) { size_t len; @@ -582,6 +667,7 @@ static int luaopen_default(lua_State *L) lua_register_help(L, "name", l_name, "Show build time"); lua_register_help(L, "echo", l_echo, "eg: echo(\"Hello\", \"C:a.txt\")"); lua_register_help(L, "cat", l_cat, "eg: cat(\"C:a.txt\")"); + lua_register_help(L, "tail", l_tail, "eg: tail(\"C:rd.log\") tail(\"C:rd.log\", 30)"); lua_register_help(L, "mv", l_mv, "eg: mv(\"C:a.txt\", \"C:b.txt\")"); lua_register_help(L, "rm", l_rm, "eg: rm(\"C:a.txt\")"); lua_register_help(L, "ls", l_ls, "eg: ls()");