From 257ff7608eecaf6ce4a7f181a36e984f1432b874 Mon Sep 17 00:00:00 2001 From: MeowOS Date: Sun, 30 Aug 2026 23:01:31 +0800 Subject: [PATCH] =?UTF-8?q?feat(sdk):=20meow=5Fconfig=5F*=20App=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E8=AF=BB=E5=86=99API(agent-158)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 6 API: meow_config_dir/set/get/int/bool/remove; 落 TF MeowData//data/.cfg(env MEOW_DATA_DIR, 回退/tmp/meowcfg_) - 与系统设置 meow_settings_*(板载settings.conf) 区分; key清洗[A-Za-z0-9._-]→_ 防路径穿越; 值上限4096; 降级不崩(pthread_once/mutex) - MEOW_CFG_* 错误码(避开 meow_ipc.h 枚举命名空间, 防宏展开冲突); 数值与 MEOWOS_API/meow_settings.h 同值 - meow_ui.a=92e61fb8(新增config成员) hello=5e31d919不变(0回归, 静态链未拉入新成员); build_sdk 0warning; 宿主test_meow_ui_config ALL PASS - 未重链launcher(launcher未用config); 遗留真机验证MEOW_DATA_DIR落盘/无卡降级 --- build_sdk.sh | 2 +- meow_ui.h | 35 +++++++ meow_ui_config.c | 226 ++++++++++++++++++++++++++++++++++++++++++ test_meow_ui_config.c | 126 +++++++++++++++++++++++ 4 files changed, 388 insertions(+), 1 deletion(-) create mode 100644 meow_ui_config.c create mode 100644 test_meow_ui_config.c diff --git a/build_sdk.sh b/build_sdk.sh index cecf0c5..9994d4d 100755 --- a/build_sdk.sh +++ b/build_sdk.sh @@ -10,7 +10,7 @@ AR="${AR:-/usr/bin/ar}" # 系统 GNU ar 即可归档 mipsel ELF 对象(架 rm -f meow_ui.a *.o # -I../meow_launcherd:meow_notify_ipc.c 复用共享协议头 meow_ipc.h(同一份、杜绝两端漂移) -OBJS="meow_ui_gfx meow_ui_log meow_ui_notify meow_notify_ipc meow_ui_img meow_ui_widget meow_ui_input meow_ui_theme meow_ui_ime meow_ui_sys meow_ui_vol meow_ui_activity meow_ui_global meow_ui_dialog stb_image" +OBJS="meow_ui_gfx meow_ui_log meow_ui_notify meow_notify_ipc meow_ui_img meow_ui_widget meow_ui_input meow_ui_theme meow_ui_ime meow_ui_sys meow_ui_vol meow_ui_activity meow_ui_global meow_ui_dialog meow_ui_config stb_image" CFLAGS="-Os -static -I$ROOT/patch_v2/meow_launcherd" for f in $OBJS; do echo "== cc $f ==" diff --git a/meow_ui.h b/meow_ui.h index a64dd49..6b16ef0 100644 --- a/meow_ui.h +++ b/meow_ui.h @@ -21,6 +21,17 @@ #include #include +/* meow_config_*(§9.2)专用返回码。数值与 MEOWOS_API.md §1 的同名错误码一致, + * 但用 meow_cfg 前缀——因为 MEOW_OK/MEOW_ERR_* 这个命名空间已被共享协议头 + * meow_ipc.h(枚举)与 launcher meow_settings.h(宏)占用,SDK 的 meow_notify_ipc.c + * 同 TU include meow_ui.h + meow_ipc.h,若在此定义同名宏会导致「宏展开枚举标识符」冲突。 */ +#define MEOW_CFG_OK 0 /* 成功 */ +#define MEOW_CFG_ERR_INVAL -1 /* 参数/取值非法(空 key、NULL out、outsz<=0 等) */ +#define MEOW_CFG_ERR_NOENT -2 /* 键未设/文件不存在 */ +#define MEOW_CFG_ERR_IO -5 /* 读写(存储/文件)失败 */ +#define MEOW_CFG_ERR_NOMEM -6 /* 路径超长 */ +#define MEOW_CFG_ERR_NODEV -10 /* 无数据目录(卡不在/只读/不可建) */ + #ifdef __cplusplus extern "C" { #endif @@ -611,6 +622,30 @@ int meow_notify_has_active(void); /* 1=尚有活动通知,0=无 * const char *meow_notify_urgency_str(meow_notify_urgency u); const char *meow_notify_category_str(meow_notify_category cat); +/* ============================================================ */ +/* §9.2 应用配置读写(meow_config_*,M2.x 最小可用版) */ +/* ============================================================ */ +/* App 自有配置读写:与系统设置 meow_settings_*(板载 settings.conf、系统级、带 schema) + * 明确区分——这是单个 App 自己的键值偏好,落 TF 数据目录,不占板载、无 schema。 + * + * - 数据目录:优先 env MEOW_DATA_DIR(launcher 注入 /usr/data/mnt/sd_0/MeowData//data, + * 见 app_launcher.c launch_app);无 env(手动跑/宿主测/无卡)回退 /tmp/meowcfg_ + * (非持久但仍可写)。meow_config_dir() 返回最终目录;空/不可建返回 NULL。 + * - 文件布局:每键一小文件 /.cfg,内容=值字符串(空串=0 字节文件)。 + * key 只允许 [A-Za-z0-9._-],其余字符替换为 '_'(杜绝路径穿越);key 上限 64。 + * - 轻量:每次 get 读文件(App 配置小),不做大内存缓存;pthread_once 惰性初始化目录。 + * - 失败降级:无数据目录/写失败 → 返回负错误码(见上 MEOW_CFG_ERR_*),不崩不阻塞。 + * + * 返回码:MEOW_CFG_OK(0) 成功;(set) 负=失败;(get) MEOW_CFG_ERR_NOENT(-2)=键未设/文件不存在; + * meow_config_int/bool 对任何非 MEOW_CFG_OK 一律返回 def。 + * 错误码数值同 MEOWOS_API.md §1 / meow_settings_*(MEOW_ERR_*)一致,便于 App 习惯。 */ +const char *meow_config_dir(void); /* 当前 App 数据目录(data);空/不可建 NULL */ +int meow_config_set(const char *key, const char *val);/* 写 /.cfg;成功 MEOW_CFG_OK(0) */ +int meow_config_get(const char *key, char *out, int outsz); /* 读;未设/不存在返回 MEOW_CFG_ERR_NOENT */ +int meow_config_int(const char *key, int def); /* 便捷 int;未设/非法返回 def */ +int meow_config_bool(const char *key, int def); /* 便捷 bool;未设/非法返回 def */ +void meow_config_remove(const char *key); /* 删键(无该键/无目录则静默) */ + /* ============================================================ */ /* §5.7 应用可读 TF 卡字体列表(M2.58:字体索引 / 查列表 / 丢失降级默认) */ /* ============================================================ */ diff --git a/meow_ui_config.c b/meow_ui_config.c new file mode 100644 index 0000000..ce99fa7 --- /dev/null +++ b/meow_ui_config.c @@ -0,0 +1,226 @@ +/* + * meow_ui_config.c — MeowOS 应用配置读写 API(M2.x,§9.2)。 + * + * 背景:第三/系统 App 需要读写「自己的配置」(播放列表偏好、UI 选项、最近列表等)。 + * 此前 SDK 无统一接口,App 只能自己造轮子(自读 env + snprintf + fopen/fprintf), + * 重复、易错、无失败降级。本文件实现 SDK 统一 meow_config_*: + * + * - app 一行 meow_config_set/get 读写键值,SDK 管「数据目录解析 / 文件布局 / 降级」; + * - 数据目录:优先 env MEOW_DATA_DIR(launcher 注入 /usr/data/mnt/sd_0/MeowData//data, + * 见 app_launcher.c launch_app,exFAT 无 symlink → env 传址);无 env(手动跑/宿主测/无卡) + * 回退 /tmp/meowcfg_(非持久但仍可写); + * - 文件布局:每键一小文件 /.cfg,内容=值字符串(空串=0 字节文件); + * key 只允许 [A-Za-z0-9._-],其余字符(含 '/')替换为 '_',杜绝路径穿越/子目录注入; + * - 轻量:每次 get 读文件(App 配置小),不做大内存缓存;pthread_once 惰性初始化目录; + * - 线程安全:pthread_mutex 串行库内单键操作(不同 key 独立文件,无共享读写竞态); + * - 失败降级:无数据目录/建目录失败/读写失败 → 返回负错误码(MEOW_CFG_ERR_*),不崩不阻塞。 + * + * 与 meow_settings_*(系统设置)的界限:settings 是「系统/MeowOS 设置」(板载 + * /usr/data/meow/settings.conf、带 schema/取值域/枚举/listener),config 是「App 自有 KV」 + * (TF 数据目录、raw 字符串、无 schema)。二者命名/落点/语义不同层;本文件**不 import** + * meow_settings,纯 libc 完成,可宿主 gcc 直接编译单测。 + * + * 约定:与 meow_ui_log.c 同风格(M2.x 中文注释 + pthread_once 惰性 init);纯 libc,零依赖。 + */ +#include "meow_ui.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 内部常量(轻量键值小文件,见头注释) */ +#define MEOW_CFG_DIR_MAX 320 /* 数据目录绝对路径上限 */ +#define MEOW_CFG_KEY_MAX 64 /* key 上限(清洗后) */ +#define MEOW_CFG_VAL_MAX 4096 /* 值上限(set 截断;get 按 outsz-1 读) */ +#define MEOW_CFG_PATH_MAX (MEOW_CFG_DIR_MAX + MEOW_CFG_KEY_MAX + 8) /* /.cfg + NUL */ + +/* 内部状态(各字段约定见头注释) */ +static pthread_mutex_t g_cfg_mu = PTHREAD_MUTEX_INITIALIZER; +static pthread_once_t g_cfg_once = PTHREAD_ONCE_INIT; + +static char g_cfg_dir[MEOW_CFG_DIR_MAX] = ""; /* 解析后的数据目录(env 或 /tmp 回退) */ +static int g_cfg_ready = 0; /* 1=目录已解析且 mkdir 成功 */ +static int g_cfg_env = 0; /* 1=来自 MEOW_DATA_DIR(持久),0=/tmp 回退 */ + +/* mkdir -p:逐分量 mkdir(已存在 EEXIST 视为成功)。返回 0=成功,-1=失败。 + * 仿 meow_ui_log.c log_mkdir_p;用于数据目录不存在时补齐(语义幂等)。 */ +static int cfg_mkdir_p(const char *path) +{ + char tmp[MEOW_CFG_DIR_MAX]; + size_t len, i; + if (!path || !path[0] || strlen(path) >= sizeof tmp) return -1; + len = strlen(path); + memcpy(tmp, path, len + 1); + if (tmp[len - 1] == '/') tmp[len - 1] = '\0'; /* 去尾斜杠 */ + for (i = 1; i <= strlen(tmp); i++) { + if (tmp[i] == '/' || tmp[i] == '\0') { + char ch = tmp[i]; + tmp[i] = '\0'; + if (tmp[0] && mkdir(tmp, 0755) != 0 && errno != EEXIST) return -1; + tmp[i] = ch; + } + } + return 0; +} + +/* 一次性初始化(pthread_once 保证仅一次):解析数据目录 + mkdir -p + 置 ready。 */ +static void cfg_init(void) +{ + const char *env = getenv("MEOW_DATA_DIR"); + + /* 层序:1) env MEOW_DATA_DIR(launcher 注入,持久);2) 回退 /tmp/meowcfg_。 */ + if (env && env[0]) { + snprintf(g_cfg_dir, sizeof g_cfg_dir, "%s", env); + g_cfg_env = 1; + } else { + snprintf(g_cfg_dir, sizeof g_cfg_dir, "/tmp/meowcfg_%d", (int)getpid()); + g_cfg_env = 0; + } + + /* 去尾斜杠(统一 /.cfg 拼接) */ + size_t n = strlen(g_cfg_dir); + while (n > 1 && g_cfg_dir[n - 1] == '/') g_cfg_dir[--n] = '\0'; + + /* mkdir 幂等;失败只降级(g_cfg_ready=0 → meow_config_dir 返回 NULL),不崩。 + * 注:env 指向 /data 时 launcher 已建该目录;卡不在/只读 → 此处失败降级。 */ + if (cfg_mkdir_p(g_cfg_dir) == 0) g_cfg_ready = 1; +} + +/* key 清洗:只保留 [A-Za-z0-9._-],其余(含 '/'、空白、'..')逐字符替换为 '_'。 + * 返回 0=成功(写入 out),-1=空 key/全非法。清洗后无路径分隔,杜绝路径穿越。 */ +static int cfg_sanitize_key(const char *key, char *out, int n) +{ + int len = 0; + const unsigned char *p; + if (!key || n <= 0) return -1; + for (p = (const unsigned char *)key; *p && len < n - 1; p++) { + unsigned char c = *p; + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || c == '.' || c == '_' || c == '-') + out[len++] = (char)c; + else + out[len++] = '_'; + } + if (len == 0) return -1; /* 全非法(如纯 '/')→ 拒绝 */ + out[len] = '\0'; + return 0; +} + +/* 拼 /.cfg 到 path。返回 0=成功,-1=路径超长。 */ +static int cfg_path(const char *key, char *path, int n) +{ + const char *d = meow_config_dir(); + if (!d) return -1; + if (snprintf(path, (size_t)n, "%s/%s.cfg", d, key) >= n) return -1; + return 0; +} + +const char *meow_config_dir(void) +{ + /* 不取 g_cfg_mu:pthread_once 保证 cfg_init 只跑一次,且其完成后 g_cfg_dir/g_cfg_ready + * 已是稳定快照(once 自带 happens-before/内存序)。set/get/remove 持锁后经 cfg_path 也调 + * 本函数——若在此加锁会自我死锁(非递归互斥)。故仅返回只读快照。 */ + pthread_once(&g_cfg_once, cfg_init); + return g_cfg_ready ? g_cfg_dir : NULL; +} + +int meow_config_set(const char *key, const char *val) +{ + char k[MEOW_CFG_KEY_MAX + 1]; + char path[MEOW_CFG_PATH_MAX]; + const char *v = val ? val : ""; /* NULL 视为空串(=清空该键) */ + size_t vlen, towrite; + int fd, rc = MEOW_CFG_OK, s; + + if (cfg_sanitize_key(key, k, sizeof k) != 0) return MEOW_CFG_ERR_INVAL; + vlen = strlen(v); + towrite = vlen > MEOW_CFG_VAL_MAX ? MEOW_CFG_VAL_MAX : vlen; /* 值上限截断 */ + + pthread_mutex_lock(&g_cfg_mu); + s = cfg_path(k, path, sizeof path); + if (s != 0) { pthread_mutex_unlock(&g_cfg_mu); return MEOW_CFG_ERR_NOMEM; } + + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) { + if (!g_cfg_env) fprintf(stderr, "meow_config: set '%s' open %s: %s\n", k, path, strerror(errno)); + pthread_mutex_unlock(&g_cfg_mu); + return MEOW_CFG_ERR_IO; + } + /* 循环写完(防短写;配置小,实际单次 write 即完) */ + size_t off = 0; + while (off < towrite) { + ssize_t w = write(fd, v + off, towrite - off); + if (w < 0) { rc = MEOW_CFG_ERR_IO; break; } + if (w == 0) { rc = MEOW_CFG_ERR_IO; break; } + off += (size_t)w; + } + close(fd); + pthread_mutex_unlock(&g_cfg_mu); + return rc; +} + +int meow_config_get(const char *key, char *out, int outsz) +{ + char k[MEOW_CFG_KEY_MAX + 1]; + char path[MEOW_CFG_PATH_MAX]; + ssize_t n; + + if (!out || outsz <= 0) return MEOW_CFG_ERR_INVAL; + out[0] = '\0'; + if (cfg_sanitize_key(key, k, sizeof k) != 0) return MEOW_CFG_ERR_INVAL; + + pthread_mutex_lock(&g_cfg_mu); + if (cfg_path(k, path, sizeof path) != 0) { pthread_mutex_unlock(&g_cfg_mu); return MEOW_CFG_ERR_NOMEM; } + + int fd = open(path, O_RDONLY); + if (fd < 0) { pthread_mutex_unlock(&g_cfg_mu); return MEOW_CFG_ERR_NOENT; } /* 未设/不存在 */ + + n = read(fd, out, (size_t)outsz - 1); + if (n < 0) n = 0; + close(fd); + pthread_mutex_unlock(&g_cfg_mu); + + out[n] = '\0'; /* 截断/短读均安全 NUL 终止 */ + return MEOW_CFG_OK; +} + +int meow_config_int(const char *key, int def) +{ + char buf[32]; + char *end = NULL; + long v; + if (meow_config_get(key, buf, sizeof buf) != MEOW_CFG_OK) return def; + v = strtol(buf, &end, 10); + if (end == buf) return def; /* 非数字 → def */ + return (int)v; +} + +int meow_config_bool(const char *key, int def) +{ + char buf[16]; + if (meow_config_get(key, buf, sizeof buf) != MEOW_CFG_OK) return def; + if (!strcmp(buf, "1") || !strcmp(buf, "true") || !strcmp(buf, "yes") || !strcmp(buf, "on")) + return 1; + if (!strcmp(buf, "0") || !strcmp(buf, "false") || !strcmp(buf, "no") || !strcmp(buf, "off")) + return 0; + return def; +} + +void meow_config_remove(const char *key) +{ + char k[MEOW_CFG_KEY_MAX + 1]; + char path[MEOW_CFG_PATH_MAX]; + + if (cfg_sanitize_key(key, k, sizeof k) != 0) return; /* 非法 key 静默 */ + + pthread_mutex_lock(&g_cfg_mu); + if (cfg_path(k, path, sizeof path) == 0) + unlink(path); /* 无目录 / 文件不存在:unlink 失败即静默(void 语义) */ + pthread_mutex_unlock(&g_cfg_mu); +} diff --git a/test_meow_ui_config.c b/test_meow_ui_config.c new file mode 100644 index 0000000..ff6b733 --- /dev/null +++ b/test_meow_ui_config.c @@ -0,0 +1,126 @@ +/* + * test_meow_ui_config.c — meow_config_* 宿主单测(2026-08-30)。 + * + * 编译(宿主,config 为纯 libc,无需 SDK 其它对象): + * gcc -Wall -Wextra test_meow_ui_config.c meow_ui_config.c -o test_meow_ui_config -lpthread + * + * 两种模式(meow_config_dir 经 pthread_once 缓存,目录只在首次调用确定,故分两次跑): + * ./test_meow_ui_config env → 设 MEOW_DATA_DIR=,验证持久目录读写 + * ./test_meow_ui_config tmp → unsetenv MEOW_DATA_DIR,验证 /tmp/meowcfg_ 回退 + * + * 任一断言失败 → 打印 FAIL 并以非零退出码结束。 + */ +#include "meow_ui.h" + +#include +#include +#include +#include + +static int g_fail = 0; +#define CHECK(cond, msg) do { \ + if (cond) { printf(" PASS: %s\n", msg); } \ + else { printf(" FAIL: %s\n", msg); g_fail = 1; } \ +} while (0) + +int main(int argc, char **argv) +{ + const char *mode = (argc > 1) ? argv[1] : "env"; + char *tmpdir = NULL; + + if (strcmp(mode, "tmp") == 0) { + unsetenv("MEOW_DATA_DIR"); + } else { + tmpdir = strdup("/tmp/meowcfgtestXXXXXX"); + if (!mkdtemp(tmpdir)) { printf("FATAL: mkdtemp failed\n"); return 2; } + setenv("MEOW_DATA_DIR", tmpdir, 1); + } + + printf("== meow_config test [%s] ==\n", mode); + + const char *dir = meow_config_dir(); + CHECK(dir != NULL, "meow_config_dir() 非 NULL"); + printf(" dir = %s\n", dir ? dir : "(null)"); + if (strcmp(mode, "tmp") == 0) + CHECK(dir && strstr(dir, "/tmp/meowcfg_") != NULL, "tmp 回退目录为 /tmp/meowcfg_"); + else + CHECK(dir && strcmp(dir, tmpdir) == 0, "env 目录 = MEOW_DATA_DIR"); + + /* --- set/get 往返 --- */ + CHECK(meow_config_set("k1", "hello") == MEOW_CFG_OK, "set k1=hello -> MEOW_CFG_OK"); + char buf[128]; + CHECK(meow_config_get("k1", buf, sizeof buf) == MEOW_CFG_OK, "get k1 -> MEOW_CFG_OK"); + CHECK(strcmp(buf, "hello") == 0, "get k1 值 = hello"); + + /* --- 全数字 int / bool 解析 --- */ + meow_config_set("num", "42"); + CHECK(meow_config_int("num", -1) == 42, "int(num)=42"); + meow_config_set("truth", "true"); + CHECK(meow_config_bool("truth", 0) == 1, "bool(true)=1"); + meow_config_set("lie", "off"); + CHECK(meow_config_bool("lie", 1) == 0, "bool(off)=0"); + CHECK(meow_config_int("nonnum", 7) == 7, "int 非数字 -> def"); + CHECK(meow_config_bool("nonbool", 3) == 3, "bool 非法 -> def"); + + /* --- 未设键 --- */ + CHECK(meow_config_get("absent", buf, sizeof buf) == MEOW_CFG_ERR_NOENT, "get 未设键 -> MEOW_CFG_ERR_NOENT"); + CHECK(meow_config_int("absent", 9) == 9, "int 未设键 -> def"); + CHECK(meow_config_bool("absent", 1) == 1, "bool 未设键 -> def"); + + /* --- int 便捷写(经 set)再读 --- */ + char nbuf[16]; + meow_config_get("num", nbuf, sizeof nbuf); + CHECK(strcmp(nbuf, "42") == 0, "int 存储为十进制 '42'"); + + /* --- 空值(NULL=清空为 0 字节文件,与「未设」区分)--- */ + meow_config_set("empty", ""); + CHECK(meow_config_get("empty", buf, sizeof buf) == MEOW_CFG_OK, "get 空值键 -> MEOW_CFG_OK"); + CHECK(buf[0] == '\0', "空值键读回空串"); + CHECK(meow_config_get("empty", buf, sizeof buf) != MEOW_CFG_ERR_NOENT, "空值键 ≠ 未设"); + + /* --- remove --- */ + meow_config_remove("k1"); + CHECK(meow_config_get("k1", buf, sizeof buf) == MEOW_CFG_ERR_NOENT, "remove 后 get -> NOENT"); + + /* --- key 清洗:'/' 等非法字符替换为 '_',杜绝路径穿越 --- */ + meow_config_set("a/b", "slash"); + CHECK(meow_config_get("a_b", buf, sizeof buf) == MEOW_CFG_OK && strcmp(buf, "slash") == 0, + "key 'a/b' 清洗为 'a_b'"); + /* 路径穿越尝试:key 含 '/' 的非法串,不得逃出数据目录('/'→'_','.' 保留为字面量)。 */ + meow_config_set("../../etc/passwd", "evil"); + CHECK(meow_config_get(".._.._etc_passwd", buf, sizeof buf) == MEOW_CFG_OK && + strcmp(buf, "evil") == 0, "key '../../etc/passwd' 清洗为 '.._.._etc_passwd'('/'→'_',无穿越)"); + + /* --- 参数校验 --- */ + CHECK(meow_config_get(NULL, buf, sizeof buf) == MEOW_CFG_ERR_INVAL, "get(NULL) -> INVAL"); + CHECK(meow_config_get("x", NULL, 10) == MEOW_CFG_ERR_INVAL, "get(out=NULL) -> INVAL"); + CHECK(meow_config_get("x", buf, 0) == MEOW_CFG_ERR_INVAL, "get(outsz=0) -> INVAL"); + CHECK(meow_config_set("", "v") == MEOW_CFG_ERR_INVAL, "set(空 key) -> INVAL"); + + /* --- 多键独立 --- */ + meow_config_set("ind.a", "1"); + meow_config_set("ind.b", "2"); + meow_config_get("ind.a", buf, sizeof buf); + CHECK(strcmp(buf, "1") == 0, "多键独立 a"); + meow_config_get("ind.b", buf, sizeof buf); + CHECK(strcmp(buf, "2") == 0, "多键独立 b"); + meow_config_remove("ind.a"); + meow_config_get("ind.b", buf, sizeof buf); + CHECK(strcmp(buf, "2") == 0, "删除 a 不影响 b"); + + /* --- 值截断(>MEOW_CFG_VAL_MAX 4096 截断;读合法)--- */ + { + char big[4096 * 2]; + char rbuf[4100]; + memset(big, 'A', sizeof big - 1); + big[sizeof big - 1] = '\0'; + meow_config_set("big", big); + meow_config_get("big", rbuf, sizeof rbuf); + CHECK(strlen(rbuf) == 4096, "值 >4096 截断为 4096"); + } + + if (tmpdir) free(tmpdir); + if (g_fail) { printf("\nRESULT: FAIL (%d)\n", g_fail); return 1; } + printf("\nRESULT: ALL PASS\n"); + return 0; +}