diff --git a/CMakeLists.txt b/CMakeLists.txt index ee76015..dbb42a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,54 +1,94 @@ -cmake_minimum_required(VERSION 3.15) -project(PathEditor LANGUAGES C RC) +cmake_minimum_required(VERSION 3.10) +project(PathEditor VERSION 3.0 LANGUAGES C) -set(IUP_DIR "${CMAKE_SOURCE_DIR}/libs/iup-3.31_Win64_dllw6_lib") +# 启用资源编译器以处理 .rc 文件 +enable_language(RC) -add_executable(PathEditor +# 设置 C 标准 +set(CMAKE_C_STANDARD 17) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS OFF) # 禁用特定编译器的扩展(如 gnu17),强制使用标准 C17 + +# 定义源文件 +set(SOURCES src/main.c - src/utils.c - src/registry.c - src/callbacks.c + src/utils/string_ext.c + src/utils/os_env.c + src/utils/safe_string.c + src/utils/logger.c + src/ui/ui_utils.c + src/ui/dialogs.c + src/ui/main_window.c + src/core/registry_service.c + src/core/path_manager.c + src/core/app_context.c + src/core/lua_config.c + src/core/import_export.c + src/controller/callbacks.c ico/resources.rc ) -target_include_directories(PathEditor PRIVATE - ${IUP_DIR}/include - include +# 创建 GUI 可执行文件(WIN32 属性会自动添加 -mwindows 参数) +add_executable(${PROJECT_NAME} WIN32 ${SOURCES}) + +# 添加宏定义 +target_compile_definitions(${PROJECT_NAME} PRIVATE + _WIN32 + UNICODE + _UNICODE ) -target_link_directories(PathEditor PRIVATE ${IUP_DIR}) +# 添加编译选项 +if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") + target_compile_options(${PROJECT_NAME} PRIVATE + -Wall + -O2 + -fexec-charset=UTF-8 + ) +endif() -target_link_libraries(PathEditor - iup iupcd gdi32 comdlg32 comctl32 uuid ole32 advapi32 +# 设置头文件搜索路径 +target_include_directories(${PROJECT_NAME} PRIVATE + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/include/core + ${CMAKE_SOURCE_DIR}/include/ui + ${CMAKE_SOURCE_DIR}/include/controller + ${CMAKE_SOURCE_DIR}/include/utils + ${CMAKE_SOURCE_DIR}/libs/IUP/include + ${CMAKE_SOURCE_DIR}/libs/lua/include ) -target_compile_definitions(PathEditor PRIVATE UNICODE _UNICODE) - -target_compile_options(PathEditor PRIVATE -Wall -O2 -fexec-charset=UTF-8) - -target_link_options(PathEditor PRIVATE -mwindows) - -set_target_properties(PathEditor PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/bin" +# 设置库文件搜索路径 +target_link_directories(${PROJECT_NAME} PRIVATE + ${CMAKE_SOURCE_DIR}/libs/IUP + ${CMAKE_SOURCE_DIR}/libs/lua ) -add_custom_command(TARGET PathEditor POST_BUILD +# 链接所需库 +target_link_libraries(${PROJECT_NAME} PRIVATE + lua55 + iup + iupcd + gdi32 + comdlg32 + comctl32 + uuid + ole32 + advapi32 +) + +# 编译完成后,复制程序实际需要的核心 DLL 文件到构建输出目录 +set(IUP_REQUIRED_DLLS "${CMAKE_CURRENT_SOURCE_DIR}/libs/IUP/iup.dll") +set(LUA_REQUIRED_DLLS "${CMAKE_CURRENT_SOURCE_DIR}/libs/lua/lua55.dll") +add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different - "${IUP_DIR}/iup.dll" - "${IUP_DIR}/iupcd.dll" - "${IUP_DIR}/freetype6.dll" - "${IUP_DIR}/ftgl.dll" - "${IUP_DIR}/zlib1.dll" - "${IUP_DIR}/iupcontrols.dll" - "${IUP_DIR}/iupim.dll" - "${IUP_DIR}/iupimglib.dll" - "${IUP_DIR}/iup_mglplot.dll" - "${IUP_DIR}/iup_plot.dll" - "${IUP_DIR}/iup_scintilla.dll" - "${IUP_DIR}/iupgl.dll" - "${IUP_DIR}/iupglcontrols.dll" - "${IUP_DIR}/iupole.dll" - "${IUP_DIR}/iuptuio.dll" - "${CMAKE_SOURCE_DIR}/bin/" - COMMENT "Copying IUP runtime DLLs to bin/" + ${IUP_REQUIRED_DLLS} + "$" + COMMENT "Copying required DLLs to build directory..." +) +add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${LUA_REQUIRED_DLLS} + "$" + COMMENT "Copying Lua DLL to build directory..." ) diff --git a/Makefile b/Makefile deleted file mode 100644 index 870ecc1..0000000 --- a/Makefile +++ /dev/null @@ -1,54 +0,0 @@ -CC = gcc -WINDRES = windres - -# Paths - specific to user environment -IUP_DIR = libs/iup-3.31_Win64_dllw6_lib -INCLUDE_DIR = $(IUP_DIR)/include -LIB_DIR = $(IUP_DIR) -LOCAL_INCLUDE_DIR = include - -# Output Directories -OBJ_DIR = obj -BIN_DIR = bin - -# Flags -# -mwindows: Create GUI app (no console) -# -DUNICODE -D_UNICODE: Use Wide Character API -CFLAGS = -Wall -O2 -I$(INCLUDE_DIR) -I$(LOCAL_INCLUDE_DIR) -D_WIN32 -DUNICODE -D_UNICODE -fexec-charset=UTF-8 -LDFLAGS = -L$(LIB_DIR) -liup -liupcd -lgdi32 -lcomdlg32 -lcomctl32 -luuid -lole32 -ladvapi32 -mwindows - -# Source -SRC = src/main.c src/utils.c src/registry.c src/callbacks.c -RES = ico/resources.rc -OBJ = $(OBJ_DIR)/main.o $(OBJ_DIR)/utils.o $(OBJ_DIR)/registry.o $(OBJ_DIR)/callbacks.o $(OBJ_DIR)/resources.o -EXE = $(BIN_DIR)/PathEditor.exe - -all: $(BIN_DIR) $(OBJ_DIR) $(EXE) - -$(BIN_DIR): - if not exist $(BIN_DIR) mkdir $(BIN_DIR) - -$(OBJ_DIR): - if not exist $(OBJ_DIR) mkdir $(OBJ_DIR) - -$(EXE): $(OBJ) - $(CC) -o $@ $^ $(LDFLAGS) - -$(OBJ_DIR)/main.o: src/main.c - $(CC) $(CFLAGS) -c -o $@ $< - -$(OBJ_DIR)/utils.o: src/utils.c - $(CC) $(CFLAGS) -c -o $@ $< - -$(OBJ_DIR)/registry.o: src/registry.c - $(CC) $(CFLAGS) -c -o $@ $< - -$(OBJ_DIR)/callbacks.o: src/callbacks.c - $(CC) $(CFLAGS) -c -o $@ $< - -$(OBJ_DIR)/resources.o: ico/resources.rc - $(WINDRES) -i $< -o $@ - -clean: - if exist $(OBJ_DIR)\*.o del /Q $(OBJ_DIR)\*.o - if exist $(BIN_DIR)\*.exe del /Q $(BIN_DIR)\*.exe \ No newline at end of file diff --git a/README.md b/README.md index dbd5102..4004784 100644 --- a/README.md +++ b/README.md @@ -1,122 +1,135 @@ -# Path Editor — Windows 系统环境变量编辑器 +# Path Editor (系统环境变量编辑器) -一个轻量级的 Windows 系统环境变量(PATH)编辑器,使用 C 语言和 [IUP](https://www.tecgraf.puc-rio.br/iup/) 图形库开发。 +* Path Editor 是一个专为 Windows 用户设计的系统环境变量(PATH)管理工具。它基于原生 C 语言和 IUP 图形库开发,旨在替代 Windows 自带的简陋编辑界面。 +* 相比系统自带的编辑器,Path Editor 提供了更加直观的双视图(系统/用户变量)界面、智能的路径有效性检测、自动备份机制以及便捷的拖拽操作,让环境变量的管理变得安全、高效且轻松。无论您是开发者还是系统管理员,它都是您配置开发环境的得力助手。 -## 项目结构 +## ✨ 功能特点 -``` -PathEditor/ -├── src/ -│ ├── main.c # 程序入口,构建 UI 布局 -│ ├── callbacks.c # 按钮回调 + 自定义输入对话框 -│ ├── registry.c # 注册表读写操作 -│ └── utils.c # 编码转换 + 权限检测 + 斑马纹样式 -├── include/ -│ ├── globals.h # 全局控件句柄与常量 -│ ├── callbacks.h -│ ├── registry.h -│ └── utils.h -├── libs/ # IUP 3.31 库文件(预编译) -├── ico/ # 应用图标与资源文件 -├── dist/ -│ └── installer.iss # Inno Setup 安装包脚本 -├── ManagePath.bat # 备用的命令行 PATH 管理脚本 -├── CMakeLists.txt # CMake 构建配置 -└── CLAUDE.md # Claude Code 项目指南 -``` +* **🛡️ 安全第一**: + * **自动备份**:每次保存前自动备份注册表,防止意外。 + * **只读模式**:非管理员运行时自动切换到只读模式,防止误操作。 + * **权限检测**:智能检测当前运行权限。 -## 功能特点 +* **📑 双视图管理**: + * 完美支持 **System (系统变量)** 和 **User (用户变量)** 的分离查看与编辑。 + * 清晰的 Tab 标签页切换。 -- **可视化编辑** — 以列表形式直观查看和管理系统 PATH 变量 -- **增删改查** — 新建、编辑、删除条目,支持从文件管理器直接选择目录 -- **拖拽排序** — 上移/下移按钮调整路径优先级 -- **权限检测** — 非管理员模式自动切换为只读,防止误操作 -- **自定义输入框** — 支持超长路径的编辑(80 字符可见宽度) -- **斑马纹列表** — 交替行背景色,方便阅读 -- **双击编辑** — 双击列表项直接编辑对应路径 -- **即时广播** — 保存后通过 `WM_SETTINGCHANGE` 通知系统 +* **🔴 智能诊断与维护**: + * **无效路径高亮**:自动检测路径是否存在,不存在的显示为红色。 + * **重复路径高亮**:自动检测重复项,重复的显示为橙色。 + * **一键清理**:智能移除所有无效和重复的路径,保持环境整洁。 -## 工作原理 +* **📂 高效交互**: + * **拖拽支持**:直接将文件夹拖入窗口即可添加(支持管理员模式下的 UIPI 穿透)。 + * **实时搜索**:顶部搜索框支持不区分大小写的实时过滤查找。 + * **快捷键**:支持 Delete 键快速删除选中项。 -程序直接读写 Windows 注册表键: +* **🔄 导入导出**: + * **导出备份**:将 PATH 导出为 JSON 文件,方便备份和迁移。 + * **导入恢复**:从 JSON 文件导入路径配置。 + * **格式兼容**:支持旧版 TXT 格式导入。 -``` -HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\Path -``` +* **便捷管理**: + * ➕ **新建**:添加新路径到列表。 + * 📂 **浏览**:直接从文件资源管理器选择目录添加。 + * ✏️ **编辑**:双击或点击按钮修改现有路径。 + * 🗑️ **删除**:移除不需要的路径。 + * ⬆️⬇️ **排序**:上移/下移调整路径优先级。 -PATH 值使用 `REG_EXPAND_SZ` 类型,支持 `%SystemRoot%` 等环境变量展开。 +* **轻量级**:原生 C 语言编写,无臃肿依赖,运行速度极快。 -**编码转换**:IUP 控件使用 UTF-8,Windows 注册表 API 使用 UTF-16,`utils.c` 提供 `wide_to_utf8()` 和 `utf8_to_wide()` 完成双向转换。 +## 🛠️ 架构与二次开发 -## 下载与安装 +本项目注重代码的模块化和可维护性,采用了经典的 **MVC 分层架构**,非常适合作为 C 语言桌面程序开发的参考: -从 [Releases](https://github.com/LHY0125/PathEditor/releases) 页面下载 `PathEditorSetup.exe` 安装包。 +* **分层设计**: + * `src/core/` (Model): 核心数据与业务逻辑,完全脱离 UI 框架(无任何 `` 依赖)。 + * `src/ui/` (View): 负责界面布局与组件的纯视觉展示。 + * `src/controller/` (Controller): 负责连接用户交互与底层数据。 + * `src/utils/` (Utils): 纯粹的底层工具类封装(系统级调用、字符串处理)。 +* **热配置系统**:所有 UI 参数(窗口大小、按钮文本、布局间距等)均通过 `lua/config.lua` 配置,修改无需重新编译即可生效。 +* **清晰的应用状态**:摒弃了脆弱的全局变量模式,采用 `AppContext` 统一管理应用运行时的上下文状态,通过指针传递,安全可靠。 +* **开发工具库**: + * 统一错误码系统 (`utils/error_code.h`) + * 安全字符串函数 (`utils/safe_string.h`) + * 日志系统 (`utils/logger.h`) -> **注意:** 安装后必须以管理员身份运行,否则只能查看,无法保存更改。 +## 📦 下载与安装 -## 从源码构建 +您可以从 [Releases](https://github.com/LHY0125/PathEditor/releases) 页面下载最新的安装包 (`PathEditorSetup.exe`)。 + +安装完成后,请**以管理员身份运行**程序以确保能够保存对系统环境变量的修改。 + +## 🛠️ 构建指南 + +如果您想从源码构建本项目,请按照以下步骤操作: ### 环境要求 -| 工具 | 说明 | -|------|------| -| Windows 操作系统 | 需 Windows API 支持 | -| MinGW-w64 (GCC) | 编译器,确保 `gcc` 在 PATH 中 | -| CMake 3.15+ | 构建工具 | -| IUP 3.31 | GUI 库(已包含在 `libs/` 中) | +* Windows 操作系统 +* GCC 编译器 (推荐 MinGW-w64) +* CMake 工具 (推荐使用 CMake 构建) +* IUP 库 (已包含在 `libs` 目录下) +* Inno Setup 6 (仅打包需要) -### 编译 +### 编译步骤 (推荐使用 CMake) -```bash -git clone https://github.com/LHY0125/PathEditor.git -cd PathEditor +本项目已迁移至 CMake 构建系统,支持生成更标准的构建文件并集成到各大 IDE。 -# 配置(MinGW Makefiles 生成器) -cmake -B build -G "MinGW Makefiles" +1. 克隆仓库: -# 编译 -cmake --build build -``` + ```bash + git clone https://github.com/LHY0125/PathEditor.git + cd PathEditor + ``` -编译产出在 `bin/PathEditor.exe`。 +2. 使用 CMake 配置和编译: -**其他 CMake 选项:** + ```bash + # 生成构建系统 (以 MinGW 为例) + cmake -B build -G "MinGW Makefiles" + + # 编译项目 + cmake --build build + ``` -```bash -# Debug 模式(带调试符号) -cmake -B build -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Debug -cmake --build build +3. 运行: + 编译成功后,可执行文件位于 `build/PathEditor.exe`。 -# Release 优化 -cmake -B build -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release -cmake --build build -``` +### 打包 (可选) -### 打包安装程序 +本项目使用 Inno Setup 生成安装包。 -使用 [Inno Setup 6](https://jrsoftware.org/isdl.php) 生成 `.exe` 安装包: +1. 确保已安装 [Inno Setup 6](https://jrsoftware.org/isdl.php)。 +2. 运行根目录下的 `build_installer.bat` 脚本。 +3. 生成的安装包将位于 `dist/dist/PathEditorSetup.exe`。 -1. 确保已安装 Inno Setup 6 -2. 先编译项目生成 `bin/PathEditor.exe` 及所需 DLL -3. 用 Inno Setup 编译 `dist/installer.iss` -4. 安装包输出至 `dist/dist/PathEditorSetup.exe` +## 📝 使用说明 -## 使用说明 +1. **启动**:右键点击程序图标,选择“以管理员身份运行”。 +2. **查看**:程序启动后会自动加载当前的系统 PATH 变量。 + * **红色**条目表示路径不存在。 + * **橙色**条目表示路径重复。 +3. **搜索**:在顶部输入关键词可快速筛选。 +4. **修改**: + * 拖拽文件夹到列表可直接添加。 + * 使用右侧按钮栏进行常规操作。 + * 点击“一键清理”可自动删除无效和重复项。 +5. **保存**:操作完成后,务必点击底部的【确定】按钮保存更改。 +6. **生效**:保存后,某些正在运行的程序可能需要重启才能识别新的环境变量。CMD 或 PowerShell 窗口需要重新打开。 -1. **启动** — 右键程序图标,选择「以管理员身份运行」 -2. **查看** — 程序启动后自动加载当前系统 PATH 到列表 -3. **新建** — 点击「新建」按钮,输入路径后确认,新条目追加到列表末尾 -4. **编辑** — 选中条目后点击「编辑」,或直接双击条目 -5. **浏览** — 点击「浏览」打开文件夹选择器,选中目录后自动添加 -6. **删除** — 选中条目后点击「删除」 -7. **排序** — 选中条目后点击「上移」或「下移」调整顺序 -8. **保存** — 操作完成后点击「确定」写入注册表并广播变更 -9. **生效** — CMD / PowerShell 窗口需要重新打开才能识别新变量;部分程序可能需要重启 +## 👤 作者信息 -**命令行备选方案:** 项目附带 `ManagePath.bat` 脚本,提供导出、导入和备份 PATH 的功能,无需 GUI 也可操作。 +* **作者**:LHY +* **邮箱**:<3364451258@qq.com> +* **GitHub**:[https://github.com/LHY0125/PathEditor](https://github.com/LHY0125/PathEditor) -## 许可证 +如果您觉得这个工具对您有帮助,请给我的 GitHub 仓库点个 Star ⭐️! -本项目基于 [MIT License](LICENSE) 开源。 +## 📄 许可证 -Copyright (c) 2026 LHY +本项目基于 MIT 许可证开源,您可以在遵守许可证条款的前提下自由使用、修改和分发本项目的代码。 + +详细信息请参阅 [LICENSE](LICENSE) 文件。 + +Copyright © 2026 LHY. All Rights Reserved. diff --git a/bin/PathEditor.exe b/bin/PathEditor.exe deleted file mode 100644 index 011da74..0000000 Binary files a/bin/PathEditor.exe and /dev/null differ diff --git a/build_installer.bat b/build_installer.bat new file mode 100644 index 0000000..538e7d4 --- /dev/null +++ b/build_installer.bat @@ -0,0 +1,12 @@ +@echo off + +echo Building Installer... +"D:\Program Files (x86)\Inno Setup 6\ISCC.exe" "dist\installer.iss" + +if %ERRORLEVEL% NEQ 0 ( + echo Installer build failed! + exit /b %ERRORLEVEL% +) + +echo Done! Installer is in dist\dist\ +pause \ No newline at end of file diff --git a/dist/installer.iss b/dist/installer.iss index 512f122..e2b4605 100644 --- a/dist/installer.iss +++ b/dist/installer.iss @@ -37,8 +37,8 @@ Name: "english"; MessagesFile: "compiler:Default.isl" Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked [Files] -Source: "d:\Code\doing_exercises\programs\PathEditor\bin\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion -Source: "d:\Code\doing_exercises\programs\PathEditor\bin\*.dll"; DestDir: "{app}"; Flags: ignoreversion +Source: "d:\Code\doing_exercises\programs\PathEditor\build\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion +Source: "d:\Code\doing_exercises\programs\PathEditor\build\*.dll"; DestDir: "{app}"; Flags: ignoreversion ; NOTE: Don't use "Flags: ignoreversion" on any shared system files [Icons] diff --git a/include/callbacks.h b/include/callbacks.h deleted file mode 100644 index ea7dcc3..0000000 --- a/include/callbacks.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef CALLBACKS_H -#define CALLBACKS_H - -#include - -// 按钮回调 -int btn_new_cb(Ihandle* self); -int btn_edit_cb(Ihandle* self); -int btn_browse_cb(Ihandle* self); -int btn_del_cb(Ihandle* self); -int btn_up_cb(Ihandle* self); -int btn_down_cb(Ihandle* self); -int btn_ok_cb(Ihandle* self); -int btn_cancel_cb(Ihandle* self); -int btn_help_cb(Ihandle* self); - -// 双击回调 -int list_dblclick_cb(Ihandle* self, int item, char* text); - -#endif // CALLBACKS_H \ No newline at end of file diff --git a/include/controller/callbacks.h b/include/controller/callbacks.h new file mode 100644 index 0000000..6c8c98f --- /dev/null +++ b/include/controller/callbacks.h @@ -0,0 +1,35 @@ +#ifndef CALLBACKS_H +#define CALLBACKS_H + +#include + +// 按钮回调 +int btn_new_cb(Ihandle *self); +int btn_edit_cb(Ihandle *self); +int btn_browse_cb(Ihandle *self); +int btn_del_cb(Ihandle *self); +int btn_up_cb(Ihandle *self); +int btn_down_cb(Ihandle *self); +int btn_clean_cb(Ihandle *self); +int btn_import_cb(Ihandle *self); +int btn_export_cb(Ihandle *self); +int btn_ok_cb(Ihandle *self); +int btn_cancel_cb(Ihandle *self); +int btn_help_cb(Ihandle *self); + +// 搜索回调 +int txt_search_cb(Ihandle *self); + +// 双击回调 +int list_dblclick_cb(Ihandle *self, int item, char *text); + +// 拖拽回调 +int list_dropfiles_cb(Ihandle *self, const char *filename, int num, int x, int y); + +// 键盘按键回调 +int list_k_any_cb(Ihandle *self, int c); + +// 载入数据与更新UI +void load_all_paths(void); + +#endif // CALLBACKS_H \ No newline at end of file diff --git a/include/core/app_context.h b/include/core/app_context.h new file mode 100644 index 0000000..c77beee --- /dev/null +++ b/include/core/app_context.h @@ -0,0 +1,22 @@ +#ifndef APP_CONTEXT_H +#define APP_CONTEXT_H + +#include "utils/string_ext.h" +#include + +// 应用上下文结构体,用于存储应用运行时的状态 +typedef struct { + StringList sys_paths; + StringList user_paths; +} AppContext; + +// 创建应用上下文 +AppContext* create_app_context(void); + +// 销毁应用上下文 +void destroy_app_context(AppContext* ctx); + +// 获取应用上下文 +AppContext* get_app_context(Ihandle *ih); + +#endif // APP_CONTEXT_H diff --git a/include/core/import_export.h b/include/core/import_export.h new file mode 100644 index 0000000..e4cc9a4 --- /dev/null +++ b/include/core/import_export.h @@ -0,0 +1,20 @@ +#ifndef IMPORT_EXPORT_H +#define IMPORT_EXPORT_H + +#include "utils/string_ext.h" +#include "utils/error_code.h" + +#define EXPORT_VERSION "1.0" + +typedef struct { + StringList system; + StringList user; +} ExportData; + +// 导出 PATH 到文件 +ErrorCode export_paths_to_file(const ExportData *data, const char *filepath); + +// 从文件导入 PATH +ErrorCode import_paths_from_file(const char *filepath, ExportData *data); + +#endif // IMPORT_EXPORT_H \ No newline at end of file diff --git a/include/core/lua_config.h b/include/core/lua_config.h new file mode 100644 index 0000000..9e638a9 --- /dev/null +++ b/include/core/lua_config.h @@ -0,0 +1,34 @@ +#ifndef LUA_CONFIG_H +#define LUA_CONFIG_H + +#include + +// 初始化 Lua 配置系统 +// 返回值: 0 成功, -1 失败 +int lua_config_init(void); + +// 销毁 Lua 配置系统 +void lua_config_destroy(void); + +// 获取字符串配置值 +// section: 配置章节名 (如 "app", "dialog", "button") +// key: 配置键名 (如 "name", "size", "rastersize") +// 返回值: 配置值字符串, 失败时返回 NULL +const char* lua_config_get_string(const char* section, const char* key); + +// 获取整型配置值 +// section: 配置章节名 +// key: 配置键名 +// default_value: 默认值 (当配置不存在或转换失败时返回) +// 返回值: 配置值或默认值 +int lua_config_get_int(const char* section, const char* key, int default_value); + +// 重新加载配置文件 +// 返回值: 0 成功, -1 失败 +int lua_config_reload(void); + +// 获取配置加载状态 +// 返回值: 1 已加载, 0 未加载 +int lua_config_is_loaded(void); + +#endif // LUA_CONFIG_H \ No newline at end of file diff --git a/include/core/path_manager.h b/include/core/path_manager.h new file mode 100644 index 0000000..dcdb0e4 --- /dev/null +++ b/include/core/path_manager.h @@ -0,0 +1,19 @@ +#ifndef PATH_MANAGER_H +#define PATH_MANAGER_H + +#include "utils/string_ext.h" +#include "utils/error_code.h" + +// 移除列表中指定索引的项 +ErrorCode path_manager_remove_at(StringList *list, int index); + +// 上移指定索引的项 +ErrorCode path_manager_move_up(StringList *list, int index); + +// 下移指定索引的项 +ErrorCode path_manager_move_down(StringList *list, int index); + +// 清理无效和重复的路径 +ErrorCode path_manager_clean(StringList *list); + +#endif // PATH_MANAGER_H diff --git a/include/core/registry_service.h b/include/core/registry_service.h new file mode 100644 index 0000000..c2c0f7b --- /dev/null +++ b/include/core/registry_service.h @@ -0,0 +1,15 @@ +#ifndef REGISTRY_SERVICE_H +#define REGISTRY_SERVICE_H + +#include "utils/string_ext.h" +#include "utils/error_code.h" + +// 加载系统变量和用户变量到字符串列表 +ErrorCode load_system_paths(StringList *list); +ErrorCode load_user_paths(StringList *list); + +// 从字符串列表保存系统变量和用户变量 +ErrorCode save_system_paths(const StringList *list); +ErrorCode save_user_paths(const StringList *list); + +#endif // REGISTRY_SERVICE_H diff --git a/include/globals.h b/include/globals.h deleted file mode 100644 index ba69969..0000000 --- a/include/globals.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef GLOBALS_H -#define GLOBALS_H - -#include - -// 注册表路径常量 -#define REG_PATH L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment" -#define REG_VALUE L"Path" - -// 全局控件句柄声明 -extern Ihandle *dlg; // 主对话框句柄 -extern Ihandle *list_path; // 路径列表控件句柄 -extern Ihandle *lbl_status; // 状态标签句柄 -extern Ihandle *btn_new; // 新增按钮句柄 -extern Ihandle *btn_edit; // 编辑按钮句柄 -extern Ihandle *btn_browse; // 浏览按钮句柄 -extern Ihandle *btn_del; // 删除按钮句柄 -extern Ihandle *btn_up; // 上移按钮句柄 -extern Ihandle *btn_down; // 下移按钮句柄 -extern Ihandle *btn_ok; // 确认按钮句柄 -extern Ihandle *btn_cancel; // 取消按钮句柄 -extern Ihandle *btn_help; // 帮助按钮句柄 - -#endif // GLOBALS_H \ No newline at end of file diff --git a/include/registry.h b/include/registry.h deleted file mode 100644 index ac27727..0000000 --- a/include/registry.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef REGISTRY_H -#define REGISTRY_H - -// 从注册表加载PATH到列表控件 -void load_path(); - -// 将列表控件中的PATH保存回注册表 -void save_path(); - -#endif // REGISTRY_H \ No newline at end of file diff --git a/include/ui/dialogs.h b/include/ui/dialogs.h new file mode 100644 index 0000000..6a25dc5 --- /dev/null +++ b/include/ui/dialogs.h @@ -0,0 +1,8 @@ +#ifndef DIALOGS_H +#define DIALOGS_H + +// 自定义输入对话框 +// 返回值:0-取消,1-确认 +int custom_input_dialog(const char *title, const char *label_text, char *buffer, int buffer_size); + +#endif // DIALOGS_H diff --git a/include/ui/main_window.h b/include/ui/main_window.h new file mode 100644 index 0000000..b2f3629 --- /dev/null +++ b/include/ui/main_window.h @@ -0,0 +1,9 @@ +#ifndef MAIN_WINDOW_H +#define MAIN_WINDOW_H + +#include + +// 创建主窗口 +Ihandle* create_main_window(void); + +#endif // MAIN_WINDOW_H diff --git a/include/ui/ui_utils.h b/include/ui/ui_utils.h new file mode 100644 index 0000000..edaebbe --- /dev/null +++ b/include/ui/ui_utils.h @@ -0,0 +1,13 @@ +#ifndef UI_UTILS_H +#define UI_UTILS_H + +#include +#include "utils/string_ext.h" + +// 刷新单个列表框样式 +void refresh_single_list_style(Ihandle *list); + +// 同步字符串列表到 UI 列表框 +void sync_string_list_to_ui(Ihandle *list_ui, const StringList *str_list); + +#endif // UI_UTILS_H diff --git a/include/utils.h b/include/utils.h deleted file mode 100644 index dd775a6..0000000 --- a/include/utils.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef UTILS_H -#define UTILS_H - -#include -#include - -// 宽字符转UTF-8 -char* wide_to_utf8(const wchar_t* wstr); - -// UTF-8转宽字符 -wchar_t* utf8_to_wide(const char* str); - -// 检查管理员权限 -int check_admin(); - -// 刷新列表样式(斑马纹) -void refresh_list_style(); - -#endif // UTILS_H \ No newline at end of file diff --git a/include/utils/error_code.h b/include/utils/error_code.h new file mode 100644 index 0000000..1d0bc79 --- /dev/null +++ b/include/utils/error_code.h @@ -0,0 +1,19 @@ +#ifndef ERROR_CODE_H +#define ERROR_CODE_H + +typedef enum { + ERR_OK = 0, // 成功 + ERR_FAILED = -1, // 失败 + ERR_NULL_PTR = -2, // 空指针 + ERR_OUT_OF_MEMORY = -3, // 内存不足 + ERR_FILE_NOT_FOUND = -4, // 文件不存在 + ERR_PERMISSION_DENIED = -5, // 权限拒绝 + ERR_INVALID_FORMAT = -6, // 无效格式 + ERR_REGISTRY_FAILED = -7, // 注册表操作失败 + ERR_NOT_FOUND = -8, // 未找到 + ERR_EXISTS = -9 // 已存在 +} ErrorCode; + +const char* error_code_to_string(ErrorCode code); + +#endif // ERROR_CODE_H diff --git a/include/utils/logger.h b/include/utils/logger.h new file mode 100644 index 0000000..9246bd1 --- /dev/null +++ b/include/utils/logger.h @@ -0,0 +1,33 @@ +#ifndef LOGGER_H +#define LOGGER_H + +// 日志级别 +typedef enum { + LOG_LEVEL_DEBUG, // 调试日志级别 + LOG_LEVEL_INFO, // 信息日志级别 + LOG_LEVEL_WARN, // 警告日志级别 + LOG_LEVEL_ERROR // 错误日志级别 +} LogLevel; + +// 初始化日志系统 +void log_init(const char *log_file, LogLevel level); + +// 销毁日志系统 +void log_destroy(void); + +// 日志函数 +void log_debug(const char *fmt, ...); + +// 信息日志函数 +void log_info(const char *fmt, ...); + +// 警告日志函数 +void log_warn(const char *fmt, ...); + +// 错误日志函数 +void log_error(const char *fmt, ...); + +// 设置日志级别 +void log_set_level(LogLevel level); + +#endif // LOGGER_H \ No newline at end of file diff --git a/include/utils/os_env.h b/include/utils/os_env.h new file mode 100644 index 0000000..039f892 --- /dev/null +++ b/include/utils/os_env.h @@ -0,0 +1,13 @@ +#ifndef OS_ENV_H +#define OS_ENV_H + +// 检查是否以管理员权限运行 +int check_admin(void); + +// 检查路径是否有效 +int is_path_valid(const char *path); + +// 备份注册表 +void backup_registry(void); + +#endif // OS_ENV_H diff --git a/include/utils/safe_string.h b/include/utils/safe_string.h new file mode 100644 index 0000000..3e204b0 --- /dev/null +++ b/include/utils/safe_string.h @@ -0,0 +1,15 @@ +#ifndef SAFE_STRING_H +#define SAFE_STRING_H + +#include + +// 安全字符串操作函数 +char* safe_strcpy(char *dst, size_t dst_size, const char *src); + +// 安全字符串拼接函数 +char* safe_strcat(char *dst, size_t dst_size, const char *src); + +// 安全字符串复制函数 +char* safe_strdup(const char *src); + +#endif // SAFE_STRING_H diff --git a/include/utils/string_ext.h b/include/utils/string_ext.h new file mode 100644 index 0000000..96fbd47 --- /dev/null +++ b/include/utils/string_ext.h @@ -0,0 +1,24 @@ +#ifndef STRING_EXT_H +#define STRING_EXT_H + +#include + +// 简单字符串列表结构 +typedef struct +{ + char **items; + int count; + int capacity; +} StringList; + +// 字符串列表 +void init_string_list(StringList *list); +void add_string_list(StringList *list, const char *str); +void clear_string_list(StringList *list); + +// 字符串转换函数 +char *wide_to_utf8(const wchar_t *wstr); +wchar_t *utf8_to_wide(const char *str); +char *stristr(const char *haystack, const char *needle); + +#endif // STRING_EXT_H diff --git a/bin/freetype6.dll b/libs/IUP/freetype6.dll similarity index 100% rename from bin/freetype6.dll rename to libs/IUP/freetype6.dll diff --git a/bin/ftgl.dll b/libs/IUP/ftgl.dll similarity index 100% rename from bin/ftgl.dll rename to libs/IUP/ftgl.dll diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iup.h b/libs/IUP/include/iup.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iup.h rename to libs/IUP/include/iup.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iup_class_cbs.hpp b/libs/IUP/include/iup_class_cbs.hpp similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iup_class_cbs.hpp rename to libs/IUP/include/iup_class_cbs.hpp diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iup_config.h b/libs/IUP/include/iup_config.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iup_config.h rename to libs/IUP/include/iup_config.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iup_export.h b/libs/IUP/include/iup_export.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iup_export.h rename to libs/IUP/include/iup_export.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iup_mglplot.h b/libs/IUP/include/iup_mglplot.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iup_mglplot.h rename to libs/IUP/include/iup_mglplot.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iup_plot.h b/libs/IUP/include/iup_plot.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iup_plot.h rename to libs/IUP/include/iup_plot.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iup_plus.h b/libs/IUP/include/iup_plus.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iup_plus.h rename to libs/IUP/include/iup_plus.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iup_scintilla.h b/libs/IUP/include/iup_scintilla.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iup_scintilla.h rename to libs/IUP/include/iup_scintilla.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iup_varg.h b/libs/IUP/include/iup_varg.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iup_varg.h rename to libs/IUP/include/iup_varg.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iupcbs.h b/libs/IUP/include/iupcbs.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iupcbs.h rename to libs/IUP/include/iupcbs.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iupcontrols.h b/libs/IUP/include/iupcontrols.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iupcontrols.h rename to libs/IUP/include/iupcontrols.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iupdef.h b/libs/IUP/include/iupdef.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iupdef.h rename to libs/IUP/include/iupdef.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iupdraw.h b/libs/IUP/include/iupdraw.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iupdraw.h rename to libs/IUP/include/iupdraw.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iupdraw_cd.h b/libs/IUP/include/iupdraw_cd.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iupdraw_cd.h rename to libs/IUP/include/iupdraw_cd.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iupfiledlg.h b/libs/IUP/include/iupfiledlg.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iupfiledlg.h rename to libs/IUP/include/iupfiledlg.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iupgl.h b/libs/IUP/include/iupgl.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iupgl.h rename to libs/IUP/include/iupgl.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iupglcontrols.h b/libs/IUP/include/iupglcontrols.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iupglcontrols.h rename to libs/IUP/include/iupglcontrols.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iupim.h b/libs/IUP/include/iupim.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iupim.h rename to libs/IUP/include/iupim.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iupkey.h b/libs/IUP/include/iupkey.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iupkey.h rename to libs/IUP/include/iupkey.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iuplua.h b/libs/IUP/include/iuplua.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iuplua.h rename to libs/IUP/include/iuplua.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iuplua_mglplot.h b/libs/IUP/include/iuplua_mglplot.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iuplua_mglplot.h rename to libs/IUP/include/iuplua_mglplot.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iuplua_plot.h b/libs/IUP/include/iuplua_plot.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iuplua_plot.h rename to libs/IUP/include/iuplua_plot.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iuplua_scintilla.h b/libs/IUP/include/iuplua_scintilla.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iuplua_scintilla.h rename to libs/IUP/include/iuplua_scintilla.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iupluacontrols.h b/libs/IUP/include/iupluacontrols.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iupluacontrols.h rename to libs/IUP/include/iupluacontrols.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iupluafiledlg.h b/libs/IUP/include/iupluafiledlg.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iupluafiledlg.h rename to libs/IUP/include/iupluafiledlg.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iupluagl.h b/libs/IUP/include/iupluagl.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iupluagl.h rename to libs/IUP/include/iupluagl.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iupluaglcontrols.h b/libs/IUP/include/iupluaglcontrols.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iupluaglcontrols.h rename to libs/IUP/include/iupluaglcontrols.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iupluaim.h b/libs/IUP/include/iupluaim.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iupluaim.h rename to libs/IUP/include/iupluaim.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iupluaole.h b/libs/IUP/include/iupluaole.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iupluaole.h rename to libs/IUP/include/iupluaole.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iupluascripterdlg.h b/libs/IUP/include/iupluascripterdlg.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iupluascripterdlg.h rename to libs/IUP/include/iupluascripterdlg.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iupluatuio.h b/libs/IUP/include/iupluatuio.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iupluatuio.h rename to libs/IUP/include/iupluatuio.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iupluaweb.h b/libs/IUP/include/iupluaweb.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iupluaweb.h rename to libs/IUP/include/iupluaweb.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iupole.h b/libs/IUP/include/iupole.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iupole.h rename to libs/IUP/include/iupole.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iuptuio.h b/libs/IUP/include/iuptuio.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iuptuio.h rename to libs/IUP/include/iuptuio.h diff --git a/libs/iup-3.31_Win64_dllw6_lib/include/iupweb.h b/libs/IUP/include/iupweb.h similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/include/iupweb.h rename to libs/IUP/include/iupweb.h diff --git a/bin/iup.dll b/libs/IUP/iup.dll similarity index 100% rename from bin/iup.dll rename to libs/IUP/iup.dll diff --git a/bin/iup_mglplot.dll b/libs/IUP/iup_mglplot.dll similarity index 100% rename from bin/iup_mglplot.dll rename to libs/IUP/iup_mglplot.dll diff --git a/bin/iup_plot.dll b/libs/IUP/iup_plot.dll similarity index 100% rename from bin/iup_plot.dll rename to libs/IUP/iup_plot.dll diff --git a/bin/iup_scintilla.dll b/libs/IUP/iup_scintilla.dll similarity index 100% rename from bin/iup_scintilla.dll rename to libs/IUP/iup_scintilla.dll diff --git a/bin/iupcd.dll b/libs/IUP/iupcd.dll similarity index 100% rename from bin/iupcd.dll rename to libs/IUP/iupcd.dll diff --git a/bin/iupcontrols.dll b/libs/IUP/iupcontrols.dll similarity index 100% rename from bin/iupcontrols.dll rename to libs/IUP/iupcontrols.dll diff --git a/bin/iupgl.dll b/libs/IUP/iupgl.dll similarity index 100% rename from bin/iupgl.dll rename to libs/IUP/iupgl.dll diff --git a/bin/iupglcontrols.dll b/libs/IUP/iupglcontrols.dll similarity index 100% rename from bin/iupglcontrols.dll rename to libs/IUP/iupglcontrols.dll diff --git a/bin/iupim.dll b/libs/IUP/iupim.dll similarity index 100% rename from bin/iupim.dll rename to libs/IUP/iupim.dll diff --git a/bin/iupimglib.dll b/libs/IUP/iupimglib.dll similarity index 100% rename from bin/iupimglib.dll rename to libs/IUP/iupimglib.dll diff --git a/bin/iupole.dll b/libs/IUP/iupole.dll similarity index 100% rename from bin/iupole.dll rename to libs/IUP/iupole.dll diff --git a/bin/iuptuio.dll b/libs/IUP/iuptuio.dll similarity index 100% rename from bin/iuptuio.dll rename to libs/IUP/iuptuio.dll diff --git a/libs/iup-3.31_Win64_dllw6_lib/libfreetype6.a b/libs/IUP/libfreetype6.a similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/libfreetype6.a rename to libs/IUP/libfreetype6.a diff --git a/libs/iup-3.31_Win64_dllw6_lib/libftgl.a b/libs/IUP/libftgl.a similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/libftgl.a rename to libs/IUP/libftgl.a diff --git a/libs/iup-3.31_Win64_dllw6_lib/libiup.a b/libs/IUP/libiup.a similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/libiup.a rename to libs/IUP/libiup.a diff --git a/libs/iup-3.31_Win64_dllw6_lib/libiup_mglplot.a b/libs/IUP/libiup_mglplot.a similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/libiup_mglplot.a rename to libs/IUP/libiup_mglplot.a diff --git a/libs/iup-3.31_Win64_dllw6_lib/libiup_plot.a b/libs/IUP/libiup_plot.a similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/libiup_plot.a rename to libs/IUP/libiup_plot.a diff --git a/libs/iup-3.31_Win64_dllw6_lib/libiup_scintilla.a b/libs/IUP/libiup_scintilla.a similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/libiup_scintilla.a rename to libs/IUP/libiup_scintilla.a diff --git a/libs/iup-3.31_Win64_dllw6_lib/libiupcd.a b/libs/IUP/libiupcd.a similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/libiupcd.a rename to libs/IUP/libiupcd.a diff --git a/libs/iup-3.31_Win64_dllw6_lib/libiupcontrols.a b/libs/IUP/libiupcontrols.a similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/libiupcontrols.a rename to libs/IUP/libiupcontrols.a diff --git a/libs/iup-3.31_Win64_dllw6_lib/libiupgl.a b/libs/IUP/libiupgl.a similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/libiupgl.a rename to libs/IUP/libiupgl.a diff --git a/libs/iup-3.31_Win64_dllw6_lib/libiupglcontrols.a b/libs/IUP/libiupglcontrols.a similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/libiupglcontrols.a rename to libs/IUP/libiupglcontrols.a diff --git a/libs/iup-3.31_Win64_dllw6_lib/libiupim.a b/libs/IUP/libiupim.a similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/libiupim.a rename to libs/IUP/libiupim.a diff --git a/libs/iup-3.31_Win64_dllw6_lib/libiupimglib.a b/libs/IUP/libiupimglib.a similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/libiupimglib.a rename to libs/IUP/libiupimglib.a diff --git a/libs/iup-3.31_Win64_dllw6_lib/libiupole.a b/libs/IUP/libiupole.a similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/libiupole.a rename to libs/IUP/libiupole.a diff --git a/libs/iup-3.31_Win64_dllw6_lib/libiuptuio.a b/libs/IUP/libiuptuio.a similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/libiuptuio.a rename to libs/IUP/libiuptuio.a diff --git a/libs/iup-3.31_Win64_dllw6_lib/libzlib1.a b/libs/IUP/libzlib1.a similarity index 100% rename from libs/iup-3.31_Win64_dllw6_lib/libzlib1.a rename to libs/IUP/libzlib1.a diff --git a/bin/zlib1.dll b/libs/IUP/zlib1.dll similarity index 100% rename from bin/zlib1.dll rename to libs/IUP/zlib1.dll diff --git a/libs/iup-3.31_Win64_dllw6_lib/freetype6.dll b/libs/iup-3.31_Win64_dllw6_lib/freetype6.dll deleted file mode 100644 index e8c3c21..0000000 Binary files a/libs/iup-3.31_Win64_dllw6_lib/freetype6.dll and /dev/null differ diff --git a/libs/iup-3.31_Win64_dllw6_lib/ftgl.dll b/libs/iup-3.31_Win64_dllw6_lib/ftgl.dll deleted file mode 100644 index 97fc44d..0000000 Binary files a/libs/iup-3.31_Win64_dllw6_lib/ftgl.dll and /dev/null differ diff --git a/libs/iup-3.31_Win64_dllw6_lib/iup.dll b/libs/iup-3.31_Win64_dllw6_lib/iup.dll deleted file mode 100644 index aceb679..0000000 Binary files a/libs/iup-3.31_Win64_dllw6_lib/iup.dll and /dev/null differ diff --git a/libs/iup-3.31_Win64_dllw6_lib/iup_mglplot.dll b/libs/iup-3.31_Win64_dllw6_lib/iup_mglplot.dll deleted file mode 100644 index 81afca5..0000000 Binary files a/libs/iup-3.31_Win64_dllw6_lib/iup_mglplot.dll and /dev/null differ diff --git a/libs/iup-3.31_Win64_dllw6_lib/iup_plot.dll b/libs/iup-3.31_Win64_dllw6_lib/iup_plot.dll deleted file mode 100644 index 3bc153e..0000000 Binary files a/libs/iup-3.31_Win64_dllw6_lib/iup_plot.dll and /dev/null differ diff --git a/libs/iup-3.31_Win64_dllw6_lib/iup_scintilla.dll b/libs/iup-3.31_Win64_dllw6_lib/iup_scintilla.dll deleted file mode 100644 index 96a09b8..0000000 Binary files a/libs/iup-3.31_Win64_dllw6_lib/iup_scintilla.dll and /dev/null differ diff --git a/libs/iup-3.31_Win64_dllw6_lib/iupcd.dll b/libs/iup-3.31_Win64_dllw6_lib/iupcd.dll deleted file mode 100644 index a0c78a0..0000000 Binary files a/libs/iup-3.31_Win64_dllw6_lib/iupcd.dll and /dev/null differ diff --git a/libs/iup-3.31_Win64_dllw6_lib/iupcontrols.dll b/libs/iup-3.31_Win64_dllw6_lib/iupcontrols.dll deleted file mode 100644 index 89a2aef..0000000 Binary files a/libs/iup-3.31_Win64_dllw6_lib/iupcontrols.dll and /dev/null differ diff --git a/libs/iup-3.31_Win64_dllw6_lib/iupgl.dll b/libs/iup-3.31_Win64_dllw6_lib/iupgl.dll deleted file mode 100644 index daeb402..0000000 Binary files a/libs/iup-3.31_Win64_dllw6_lib/iupgl.dll and /dev/null differ diff --git a/libs/iup-3.31_Win64_dllw6_lib/iupglcontrols.dll b/libs/iup-3.31_Win64_dllw6_lib/iupglcontrols.dll deleted file mode 100644 index 43b6a94..0000000 Binary files a/libs/iup-3.31_Win64_dllw6_lib/iupglcontrols.dll and /dev/null differ diff --git a/libs/iup-3.31_Win64_dllw6_lib/iupim.dll b/libs/iup-3.31_Win64_dllw6_lib/iupim.dll deleted file mode 100644 index 34909fe..0000000 Binary files a/libs/iup-3.31_Win64_dllw6_lib/iupim.dll and /dev/null differ diff --git a/libs/iup-3.31_Win64_dllw6_lib/iupimglib.dll b/libs/iup-3.31_Win64_dllw6_lib/iupimglib.dll deleted file mode 100644 index 221ff24..0000000 Binary files a/libs/iup-3.31_Win64_dllw6_lib/iupimglib.dll and /dev/null differ diff --git a/libs/iup-3.31_Win64_dllw6_lib/iupole.dll b/libs/iup-3.31_Win64_dllw6_lib/iupole.dll deleted file mode 100644 index 47719a5..0000000 Binary files a/libs/iup-3.31_Win64_dllw6_lib/iupole.dll and /dev/null differ diff --git a/libs/iup-3.31_Win64_dllw6_lib/iuptuio.dll b/libs/iup-3.31_Win64_dllw6_lib/iuptuio.dll deleted file mode 100644 index 1b56326..0000000 Binary files a/libs/iup-3.31_Win64_dllw6_lib/iuptuio.dll and /dev/null differ diff --git a/libs/iup-3.31_Win64_dllw6_lib/zlib1.dll b/libs/iup-3.31_Win64_dllw6_lib/zlib1.dll deleted file mode 100644 index 4f435eb..0000000 Binary files a/libs/iup-3.31_Win64_dllw6_lib/zlib1.dll and /dev/null differ diff --git a/libs/lua/include/lauxlib.h b/libs/lua/include/lauxlib.h new file mode 100644 index 0000000..7f1d3ca --- /dev/null +++ b/libs/lua/include/lauxlib.h @@ -0,0 +1,271 @@ +/* +** $Id: lauxlib.h $ +** Auxiliary functions for building Lua libraries +** See Copyright Notice in lua.h +*/ + + +#ifndef lauxlib_h +#define lauxlib_h + + +#include +#include + +#include "luaconf.h" +#include "lua.h" + + +/* global table */ +#define LUA_GNAME "_G" + + +typedef struct luaL_Buffer luaL_Buffer; + + +/* extra error code for 'luaL_loadfilex' */ +#define LUA_ERRFILE (LUA_ERRERR+1) + + +/* key, in the registry, for table of loaded modules */ +#define LUA_LOADED_TABLE "_LOADED" + + +/* key, in the registry, for table of preloaded loaders */ +#define LUA_PRELOAD_TABLE "_PRELOAD" + + +typedef struct luaL_Reg { + const char *name; + lua_CFunction func; +} luaL_Reg; + + +#define LUAL_NUMSIZES (sizeof(lua_Integer)*16 + sizeof(lua_Number)) + +LUALIB_API void (luaL_checkversion_) (lua_State *L, lua_Number ver, size_t sz); +#define luaL_checkversion(L) \ + luaL_checkversion_(L, LUA_VERSION_NUM, LUAL_NUMSIZES) + +LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e); +LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e); +LUALIB_API const char *(luaL_tolstring) (lua_State *L, int idx, size_t *len); +LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg); +LUALIB_API int (luaL_typeerror) (lua_State *L, int arg, const char *tname); +LUALIB_API const char *(luaL_checklstring) (lua_State *L, int arg, + size_t *l); +LUALIB_API const char *(luaL_optlstring) (lua_State *L, int arg, + const char *def, size_t *l); +LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int arg); +LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int arg, lua_Number def); + +LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int arg); +LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int arg, + lua_Integer def); + +LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg); +LUALIB_API void (luaL_checktype) (lua_State *L, int arg, int t); +LUALIB_API void (luaL_checkany) (lua_State *L, int arg); + +LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname); +LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname); +LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname); +LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname); + +LUALIB_API void (luaL_where) (lua_State *L, int lvl); +LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...); + +LUALIB_API int (luaL_checkoption) (lua_State *L, int arg, const char *def, + const char *const lst[]); + +LUALIB_API int (luaL_fileresult) (lua_State *L, int stat, const char *fname); +LUALIB_API int (luaL_execresult) (lua_State *L, int stat); + +LUALIB_API void *luaL_alloc (void *ud, void *ptr, size_t osize, + size_t nsize); + + +/* predefined references */ +#define LUA_NOREF (-2) +#define LUA_REFNIL (-1) + +LUALIB_API int (luaL_ref) (lua_State *L, int t); +LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref); + +LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename, + const char *mode); + +#define luaL_loadfile(L,f) luaL_loadfilex(L,f,NULL) + +LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz, + const char *name, const char *mode); +LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s); + +LUALIB_API lua_State *(luaL_newstate) (void); + +LUALIB_API unsigned luaL_makeseed (lua_State *L); + +LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx); + +LUALIB_API void (luaL_addgsub) (luaL_Buffer *b, const char *s, + const char *p, const char *r); +LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, + const char *p, const char *r); + +LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup); + +LUALIB_API int (luaL_getsubtable) (lua_State *L, int idx, const char *fname); + +LUALIB_API void (luaL_traceback) (lua_State *L, lua_State *L1, + const char *msg, int level); + +LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname, + lua_CFunction openf, int glb); + +/* +** =============================================================== +** some useful macros +** =============================================================== +*/ + + +#define luaL_newlibtable(L,l) \ + lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1) + +#define luaL_newlib(L,l) \ + (luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0)) + +#define luaL_argcheck(L, cond,arg,extramsg) \ + ((void)(luai_likely(cond) || luaL_argerror(L, (arg), (extramsg)))) + +#define luaL_argexpected(L,cond,arg,tname) \ + ((void)(luai_likely(cond) || luaL_typeerror(L, (arg), (tname)))) + +#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL)) +#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL)) + +#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i))) + +#define luaL_dofile(L, fn) \ + (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0)) + +#define luaL_dostring(L, s) \ + (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0)) + +#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n))) + +#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n))) + +#define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL) + + +/* +** Perform arithmetic operations on lua_Integer values with wrap-around +** semantics, as the Lua core does. +*/ +#define luaL_intop(op,v1,v2) \ + ((lua_Integer)((lua_Unsigned)(v1) op (lua_Unsigned)(v2))) + + +/* push the value used to represent failure/error */ +#if defined(LUA_FAILISFALSE) +#define luaL_pushfail(L) lua_pushboolean(L, 0) +#else +#define luaL_pushfail(L) lua_pushnil(L) +#endif + + + +/* +** {====================================================== +** Generic Buffer manipulation +** ======================================================= +*/ + +struct luaL_Buffer { + char *b; /* buffer address */ + size_t size; /* buffer size */ + size_t n; /* number of characters in buffer */ + lua_State *L; + union { + LUAI_MAXALIGN; /* ensure maximum alignment for buffer */ + char b[LUAL_BUFFERSIZE]; /* initial buffer */ + } init; +}; + + +#define luaL_bufflen(bf) ((bf)->n) +#define luaL_buffaddr(bf) ((bf)->b) + + +#define luaL_addchar(B,c) \ + ((void)((B)->n < (B)->size || luaL_prepbuffsize((B), 1)), \ + ((B)->b[(B)->n++] = (c))) + +#define luaL_addsize(B,s) ((B)->n += (s)) + +#define luaL_buffsub(B,s) ((B)->n -= (s)) + +LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B); +LUALIB_API char *(luaL_prepbuffsize) (luaL_Buffer *B, size_t sz); +LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l); +LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s); +LUALIB_API void (luaL_addvalue) (luaL_Buffer *B); +LUALIB_API void (luaL_pushresult) (luaL_Buffer *B); +LUALIB_API void (luaL_pushresultsize) (luaL_Buffer *B, size_t sz); +LUALIB_API char *(luaL_buffinitsize) (lua_State *L, luaL_Buffer *B, size_t sz); + +#define luaL_prepbuffer(B) luaL_prepbuffsize(B, LUAL_BUFFERSIZE) + +/* }====================================================== */ + + + +/* +** {====================================================== +** File handles for IO library +** ======================================================= +*/ + +/* +** A file handle is a userdata with metatable 'LUA_FILEHANDLE' and +** initial structure 'luaL_Stream' (it may contain other fields +** after that initial structure). +*/ + +#define LUA_FILEHANDLE "FILE*" + + +typedef struct luaL_Stream { + FILE *f; /* stream (NULL for incompletely created streams) */ + lua_CFunction closef; /* to close stream (NULL for closed streams) */ +} luaL_Stream; + +/* }====================================================== */ + + +/* +** {============================================================ +** Compatibility with deprecated conversions +** ============================================================= +*/ +#if defined(LUA_COMPAT_APIINTCASTS) + +#define luaL_checkunsigned(L,a) ((lua_Unsigned)luaL_checkinteger(L,a)) +#define luaL_optunsigned(L,a,d) \ + ((lua_Unsigned)luaL_optinteger(L,a,(lua_Integer)(d))) + +#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n))) +#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d))) + +#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n))) +#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d))) + +#endif +/* }============================================================ */ + + + +#endif + + diff --git a/libs/lua/include/lua.h b/libs/lua/include/lua.h new file mode 100644 index 0000000..ab473dc --- /dev/null +++ b/libs/lua/include/lua.h @@ -0,0 +1,547 @@ +/* +** $Id: lua.h $ +** Lua - A Scripting Language +** Lua.org, PUC-Rio, Brazil (www.lua.org) +** See Copyright Notice at the end of this file +*/ + + +#ifndef lua_h +#define lua_h + +#include +#include + + +#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2025 Lua.org, PUC-Rio" +#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" + + +#define LUA_VERSION_MAJOR_N 5 +#define LUA_VERSION_MINOR_N 5 +#define LUA_VERSION_RELEASE_N 0 + +#define LUA_VERSION_NUM (LUA_VERSION_MAJOR_N * 100 + LUA_VERSION_MINOR_N) +#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + LUA_VERSION_RELEASE_N) + + +#include "luaconf.h" + + +/* mark for precompiled code ('Lua') */ +#define LUA_SIGNATURE "\x1bLua" + +/* option for multiple returns in 'lua_pcall' and 'lua_call' */ +#define LUA_MULTRET (-1) + + +/* +** Pseudo-indices +** (The stack size is limited to INT_MAX/2; we keep some free empty +** space after that to help overflow detection.) +*/ +#define LUA_REGISTRYINDEX (-(INT_MAX/2 + 1000)) +#define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i)) + + +/* thread status */ +#define LUA_OK 0 +#define LUA_YIELD 1 +#define LUA_ERRRUN 2 +#define LUA_ERRSYNTAX 3 +#define LUA_ERRMEM 4 +#define LUA_ERRERR 5 + + +typedef struct lua_State lua_State; + + +/* +** basic types +*/ +#define LUA_TNONE (-1) + +#define LUA_TNIL 0 +#define LUA_TBOOLEAN 1 +#define LUA_TLIGHTUSERDATA 2 +#define LUA_TNUMBER 3 +#define LUA_TSTRING 4 +#define LUA_TTABLE 5 +#define LUA_TFUNCTION 6 +#define LUA_TUSERDATA 7 +#define LUA_TTHREAD 8 + +#define LUA_NUMTYPES 9 + + + +/* minimum Lua stack available to a C function */ +#define LUA_MINSTACK 20 + + +/* predefined values in the registry */ +/* index 1 is reserved for the reference mechanism */ +#define LUA_RIDX_GLOBALS 2 +#define LUA_RIDX_MAINTHREAD 3 +#define LUA_RIDX_LAST 3 + + +/* type of numbers in Lua */ +typedef LUA_NUMBER lua_Number; + + +/* type for integer functions */ +typedef LUA_INTEGER lua_Integer; + +/* unsigned integer type */ +typedef LUA_UNSIGNED lua_Unsigned; + +/* type for continuation-function contexts */ +typedef LUA_KCONTEXT lua_KContext; + + +/* +** Type for C functions registered with Lua +*/ +typedef int (*lua_CFunction) (lua_State *L); + +/* +** Type for continuation functions +*/ +typedef int (*lua_KFunction) (lua_State *L, int status, lua_KContext ctx); + + +/* +** Type for functions that read/write blocks when loading/dumping Lua chunks +*/ +typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz); + +typedef int (*lua_Writer) (lua_State *L, const void *p, size_t sz, void *ud); + + +/* +** Type for memory-allocation functions +*/ +typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); + + +/* +** Type for warning functions +*/ +typedef void (*lua_WarnFunction) (void *ud, const char *msg, int tocont); + + +/* +** Type used by the debug API to collect debug information +*/ +typedef struct lua_Debug lua_Debug; + + +/* +** Functions to be called by the debugger in specific events +*/ +typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar); + + +/* +** generic extra include file +*/ +#if defined(LUA_USER_H) +#include LUA_USER_H +#endif + + +/* +** RCS ident string +*/ +extern const char lua_ident[]; + + +/* +** state manipulation +*/ +LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud, unsigned seed); +LUA_API void (lua_close) (lua_State *L); +LUA_API lua_State *(lua_newthread) (lua_State *L); +LUA_API int (lua_closethread) (lua_State *L, lua_State *from); + +LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); + + +LUA_API lua_Number (lua_version) (lua_State *L); + + +/* +** basic stack manipulation +*/ +LUA_API int (lua_absindex) (lua_State *L, int idx); +LUA_API int (lua_gettop) (lua_State *L); +LUA_API void (lua_settop) (lua_State *L, int idx); +LUA_API void (lua_pushvalue) (lua_State *L, int idx); +LUA_API void (lua_rotate) (lua_State *L, int idx, int n); +LUA_API void (lua_copy) (lua_State *L, int fromidx, int toidx); +LUA_API int (lua_checkstack) (lua_State *L, int n); + +LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n); + + +/* +** access functions (stack -> C) +*/ + +LUA_API int (lua_isnumber) (lua_State *L, int idx); +LUA_API int (lua_isstring) (lua_State *L, int idx); +LUA_API int (lua_iscfunction) (lua_State *L, int idx); +LUA_API int (lua_isinteger) (lua_State *L, int idx); +LUA_API int (lua_isuserdata) (lua_State *L, int idx); +LUA_API int (lua_type) (lua_State *L, int idx); +LUA_API const char *(lua_typename) (lua_State *L, int tp); + +LUA_API lua_Number (lua_tonumberx) (lua_State *L, int idx, int *isnum); +LUA_API lua_Integer (lua_tointegerx) (lua_State *L, int idx, int *isnum); +LUA_API int (lua_toboolean) (lua_State *L, int idx); +LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len); +LUA_API lua_Unsigned (lua_rawlen) (lua_State *L, int idx); +LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx); +LUA_API void *(lua_touserdata) (lua_State *L, int idx); +LUA_API lua_State *(lua_tothread) (lua_State *L, int idx); +LUA_API const void *(lua_topointer) (lua_State *L, int idx); + + +/* +** Comparison and arithmetic functions +*/ + +#define LUA_OPADD 0 /* ORDER TM, ORDER OP */ +#define LUA_OPSUB 1 +#define LUA_OPMUL 2 +#define LUA_OPMOD 3 +#define LUA_OPPOW 4 +#define LUA_OPDIV 5 +#define LUA_OPIDIV 6 +#define LUA_OPBAND 7 +#define LUA_OPBOR 8 +#define LUA_OPBXOR 9 +#define LUA_OPSHL 10 +#define LUA_OPSHR 11 +#define LUA_OPUNM 12 +#define LUA_OPBNOT 13 + +LUA_API void (lua_arith) (lua_State *L, int op); + +#define LUA_OPEQ 0 +#define LUA_OPLT 1 +#define LUA_OPLE 2 + +LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2); +LUA_API int (lua_compare) (lua_State *L, int idx1, int idx2, int op); + + +/* +** push functions (C -> stack) +*/ +LUA_API void (lua_pushnil) (lua_State *L); +LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n); +LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n); +LUA_API const char *(lua_pushlstring) (lua_State *L, const char *s, size_t len); +LUA_API const char *(lua_pushexternalstring) (lua_State *L, + const char *s, size_t len, lua_Alloc falloc, void *ud); +LUA_API const char *(lua_pushstring) (lua_State *L, const char *s); +LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt, + va_list argp); +LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...); +LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n); +LUA_API void (lua_pushboolean) (lua_State *L, int b); +LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p); +LUA_API int (lua_pushthread) (lua_State *L); + + +/* +** get functions (Lua -> stack) +*/ +LUA_API int (lua_getglobal) (lua_State *L, const char *name); +LUA_API int (lua_gettable) (lua_State *L, int idx); +LUA_API int (lua_getfield) (lua_State *L, int idx, const char *k); +LUA_API int (lua_geti) (lua_State *L, int idx, lua_Integer n); +LUA_API int (lua_rawget) (lua_State *L, int idx); +LUA_API int (lua_rawgeti) (lua_State *L, int idx, lua_Integer n); +LUA_API int (lua_rawgetp) (lua_State *L, int idx, const void *p); + +LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec); +LUA_API void *(lua_newuserdatauv) (lua_State *L, size_t sz, int nuvalue); +LUA_API int (lua_getmetatable) (lua_State *L, int objindex); +LUA_API int (lua_getiuservalue) (lua_State *L, int idx, int n); + + +/* +** set functions (stack -> Lua) +*/ +LUA_API void (lua_setglobal) (lua_State *L, const char *name); +LUA_API void (lua_settable) (lua_State *L, int idx); +LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k); +LUA_API void (lua_seti) (lua_State *L, int idx, lua_Integer n); +LUA_API void (lua_rawset) (lua_State *L, int idx); +LUA_API void (lua_rawseti) (lua_State *L, int idx, lua_Integer n); +LUA_API void (lua_rawsetp) (lua_State *L, int idx, const void *p); +LUA_API int (lua_setmetatable) (lua_State *L, int objindex); +LUA_API int (lua_setiuservalue) (lua_State *L, int idx, int n); + + +/* +** 'load' and 'call' functions (load and run Lua code) +*/ +LUA_API void (lua_callk) (lua_State *L, int nargs, int nresults, + lua_KContext ctx, lua_KFunction k); +#define lua_call(L,n,r) lua_callk(L, (n), (r), 0, NULL) + +LUA_API int (lua_pcallk) (lua_State *L, int nargs, int nresults, int errfunc, + lua_KContext ctx, lua_KFunction k); +#define lua_pcall(L,n,r,f) lua_pcallk(L, (n), (r), (f), 0, NULL) + +LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt, + const char *chunkname, const char *mode); + +LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data, int strip); + + +/* +** coroutine functions +*/ +LUA_API int (lua_yieldk) (lua_State *L, int nresults, lua_KContext ctx, + lua_KFunction k); +LUA_API int (lua_resume) (lua_State *L, lua_State *from, int narg, + int *nres); +LUA_API int (lua_status) (lua_State *L); +LUA_API int (lua_isyieldable) (lua_State *L); + +#define lua_yield(L,n) lua_yieldk(L, (n), 0, NULL) + + +/* +** Warning-related functions +*/ +LUA_API void (lua_setwarnf) (lua_State *L, lua_WarnFunction f, void *ud); +LUA_API void (lua_warning) (lua_State *L, const char *msg, int tocont); + + +/* +** garbage-collection options +*/ + +#define LUA_GCSTOP 0 +#define LUA_GCRESTART 1 +#define LUA_GCCOLLECT 2 +#define LUA_GCCOUNT 3 +#define LUA_GCCOUNTB 4 +#define LUA_GCSTEP 5 +#define LUA_GCISRUNNING 6 +#define LUA_GCGEN 7 +#define LUA_GCINC 8 +#define LUA_GCPARAM 9 + + +/* +** garbage-collection parameters +*/ +/* parameters for generational mode */ +#define LUA_GCPMINORMUL 0 /* control minor collections */ +#define LUA_GCPMAJORMINOR 1 /* control shift major->minor */ +#define LUA_GCPMINORMAJOR 2 /* control shift minor->major */ + +/* parameters for incremental mode */ +#define LUA_GCPPAUSE 3 /* size of pause between successive GCs */ +#define LUA_GCPSTEPMUL 4 /* GC "speed" */ +#define LUA_GCPSTEPSIZE 5 /* GC granularity */ + +/* number of parameters */ +#define LUA_GCPN 6 + + +LUA_API int (lua_gc) (lua_State *L, int what, ...); + + +/* +** miscellaneous functions +*/ + +LUA_API int (lua_error) (lua_State *L); + +LUA_API int (lua_next) (lua_State *L, int idx); + +LUA_API void (lua_concat) (lua_State *L, int n); +LUA_API void (lua_len) (lua_State *L, int idx); + +#define LUA_N2SBUFFSZ 64 +LUA_API unsigned (lua_numbertocstring) (lua_State *L, int idx, char *buff); +LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s); + +LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); +LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud); + +LUA_API void (lua_toclose) (lua_State *L, int idx); +LUA_API void (lua_closeslot) (lua_State *L, int idx); + + +/* +** {============================================================== +** some useful macros +** =============================================================== +*/ + +#define lua_getextraspace(L) ((void *)((char *)(L) - LUA_EXTRASPACE)) + +#define lua_tonumber(L,i) lua_tonumberx(L,(i),NULL) +#define lua_tointeger(L,i) lua_tointegerx(L,(i),NULL) + +#define lua_pop(L,n) lua_settop(L, -(n)-1) + +#define lua_newtable(L) lua_createtable(L, 0, 0) + +#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) + +#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0) + +#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION) +#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE) +#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA) +#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL) +#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN) +#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD) +#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE) +#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0) + +#define lua_pushliteral(L, s) lua_pushstring(L, "" s) + +#define lua_pushglobaltable(L) \ + ((void)lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)) + +#define lua_tostring(L,i) lua_tolstring(L, (i), NULL) + + +#define lua_insert(L,idx) lua_rotate(L, (idx), 1) + +#define lua_remove(L,idx) (lua_rotate(L, (idx), -1), lua_pop(L, 1)) + +#define lua_replace(L,idx) (lua_copy(L, -1, (idx)), lua_pop(L, 1)) + +/* }============================================================== */ + + +/* +** {============================================================== +** compatibility macros +** =============================================================== +*/ + +#define lua_newuserdata(L,s) lua_newuserdatauv(L,s,1) +#define lua_getuservalue(L,idx) lua_getiuservalue(L,idx,1) +#define lua_setuservalue(L,idx) lua_setiuservalue(L,idx,1) + +#define lua_resetthread(L) lua_closethread(L,NULL) + +/* }============================================================== */ + +/* +** {====================================================================== +** Debug API +** ======================================================================= +*/ + + +/* +** Event codes +*/ +#define LUA_HOOKCALL 0 +#define LUA_HOOKRET 1 +#define LUA_HOOKLINE 2 +#define LUA_HOOKCOUNT 3 +#define LUA_HOOKTAILCALL 4 + + +/* +** Event masks +*/ +#define LUA_MASKCALL (1 << LUA_HOOKCALL) +#define LUA_MASKRET (1 << LUA_HOOKRET) +#define LUA_MASKLINE (1 << LUA_HOOKLINE) +#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT) + + +LUA_API int (lua_getstack) (lua_State *L, int level, lua_Debug *ar); +LUA_API int (lua_getinfo) (lua_State *L, const char *what, lua_Debug *ar); +LUA_API const char *(lua_getlocal) (lua_State *L, const lua_Debug *ar, int n); +LUA_API const char *(lua_setlocal) (lua_State *L, const lua_Debug *ar, int n); +LUA_API const char *(lua_getupvalue) (lua_State *L, int funcindex, int n); +LUA_API const char *(lua_setupvalue) (lua_State *L, int funcindex, int n); + +LUA_API void *(lua_upvalueid) (lua_State *L, int fidx, int n); +LUA_API void (lua_upvaluejoin) (lua_State *L, int fidx1, int n1, + int fidx2, int n2); + +LUA_API void (lua_sethook) (lua_State *L, lua_Hook func, int mask, int count); +LUA_API lua_Hook (lua_gethook) (lua_State *L); +LUA_API int (lua_gethookmask) (lua_State *L); +LUA_API int (lua_gethookcount) (lua_State *L); + + +struct lua_Debug { + int event; + const char *name; /* (n) */ + const char *namewhat; /* (n) 'global', 'local', 'field', 'method' */ + const char *what; /* (S) 'Lua', 'C', 'main', 'tail' */ + const char *source; /* (S) */ + size_t srclen; /* (S) */ + int currentline; /* (l) */ + int linedefined; /* (S) */ + int lastlinedefined; /* (S) */ + unsigned char nups; /* (u) number of upvalues */ + unsigned char nparams;/* (u) number of parameters */ + char isvararg; /* (u) */ + unsigned char extraargs; /* (t) number of extra arguments */ + char istailcall; /* (t) */ + int ftransfer; /* (r) index of first value transferred */ + int ntransfer; /* (r) number of transferred values */ + char short_src[LUA_IDSIZE]; /* (S) */ + /* private part */ + struct CallInfo *i_ci; /* active function */ +}; + +/* }====================================================================== */ + + +#define LUAI_TOSTRAUX(x) #x +#define LUAI_TOSTR(x) LUAI_TOSTRAUX(x) + +#define LUA_VERSION_MAJOR LUAI_TOSTR(LUA_VERSION_MAJOR_N) +#define LUA_VERSION_MINOR LUAI_TOSTR(LUA_VERSION_MINOR_N) +#define LUA_VERSION_RELEASE LUAI_TOSTR(LUA_VERSION_RELEASE_N) + +#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR +#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE + + +/****************************************************************************** +* Copyright (C) 1994-2025 Lua.org, PUC-Rio. +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +******************************************************************************/ + + +#endif diff --git a/libs/lua/include/lua.hpp b/libs/lua/include/lua.hpp new file mode 100644 index 0000000..2853364 --- /dev/null +++ b/libs/lua/include/lua.hpp @@ -0,0 +1,10 @@ +// lua.hpp +// Lua header files for C++ +// 'extern "C" not supplied automatically in lua.h and other headers +// because Lua also compiles as C++ + +extern "C" { +#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +} diff --git a/libs/lua/include/luaconf.h b/libs/lua/include/luaconf.h new file mode 100644 index 0000000..96a7780 --- /dev/null +++ b/libs/lua/include/luaconf.h @@ -0,0 +1,745 @@ +/* +** $Id: luaconf.h $ +** Configuration file for Lua +** See Copyright Notice in lua.h +*/ + + +#ifndef luaconf_h +#define luaconf_h + +#include +#include + + +/* +** =================================================================== +** General Configuration File for Lua +** +** Some definitions here can be changed externally, through the compiler +** (e.g., with '-D' options): They are commented out or protected +** by '#if !defined' guards. However, several other definitions +** should be changed directly here, either because they affect the +** Lua ABI (by making the changes here, you ensure that all software +** connected to Lua, such as C libraries, will be compiled with the same +** configuration); or because they are seldom changed. +** +** Search for "@@" to find all configurable definitions. +** =================================================================== +*/ + + +/* +** {==================================================================== +** System Configuration: macros to adapt (if needed) Lua to some +** particular platform, for instance restricting it to C89. +** ===================================================================== +*/ + +/* +@@ LUA_USE_C89 controls the use of non-ISO-C89 features. +** Define it if you want Lua to avoid the use of a few C99 features +** or Windows-specific features on Windows. +*/ +/* #define LUA_USE_C89 */ + + +/* +** By default, Lua on Windows use (some) specific Windows features +*/ +#if !defined(LUA_USE_C89) && defined(_WIN32) && !defined(_WIN32_WCE) +#define LUA_USE_WINDOWS /* enable goodies for regular Windows */ +#endif + + +#if defined(LUA_USE_WINDOWS) +#define LUA_DL_DLL /* enable support for DLL */ +#define LUA_USE_C89 /* broadly, Windows is C89 */ +#endif + + +/* +** When POSIX DLL ('LUA_USE_DLOPEN') is enabled, the Lua stand-alone +** application will try to dynamically link a 'readline' facility +** for its REPL. In that case, LUA_READLINELIB is the name of the +** library it will look for those facilities. If lua.c cannot open +** the specified library, it will generate a warning and then run +** without 'readline'. If that macro is not defined, lua.c will not +** use 'readline'. +*/ +#if defined(LUA_USE_LINUX) +#define LUA_USE_POSIX +#define LUA_USE_DLOPEN /* needs an extra library: -ldl */ +#define LUA_READLINELIB "libreadline.so" +#endif + + +#if defined(LUA_USE_MACOSX) +#define LUA_USE_POSIX +#define LUA_USE_DLOPEN /* macOS does not need -ldl */ +#define LUA_READLINELIB "libedit.dylib" +#endif + + +#if defined(LUA_USE_IOS) +#define LUA_USE_POSIX +#define LUA_USE_DLOPEN +#endif + + +#if defined(LUA_USE_C89) && defined(LUA_USE_POSIX) +#error "POSIX is not compatible with C89" +#endif + + +/* +@@ LUAI_IS32INT is true iff 'int' has (at least) 32 bits. +*/ +#define LUAI_IS32INT ((UINT_MAX >> 30) >= 3) + +/* }================================================================== */ + + + +/* +** {================================================================== +** Configuration for Number types. These options should not be +** set externally, because any other code connected to Lua must +** use the same configuration. +** =================================================================== +*/ + +/* +@@ LUA_INT_TYPE defines the type for Lua integers. +@@ LUA_FLOAT_TYPE defines the type for Lua floats. +** Lua should work fine with any mix of these options supported +** by your C compiler. The usual configurations are 64-bit integers +** and 'double' (the default), 32-bit integers and 'float' (for +** restricted platforms), and 'long'/'double' (for C compilers not +** compliant with C99, which may not have support for 'long long'). +*/ + +/* predefined options for LUA_INT_TYPE */ +#define LUA_INT_INT 1 +#define LUA_INT_LONG 2 +#define LUA_INT_LONGLONG 3 + +/* predefined options for LUA_FLOAT_TYPE */ +#define LUA_FLOAT_FLOAT 1 +#define LUA_FLOAT_DOUBLE 2 +#define LUA_FLOAT_LONGDOUBLE 3 + + +/* Default configuration ('long long' and 'double', for 64-bit Lua) */ +#define LUA_INT_DEFAULT LUA_INT_LONGLONG +#define LUA_FLOAT_DEFAULT LUA_FLOAT_DOUBLE + + +/* +@@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats. +*/ +/* #define LUA_32BITS */ + + +/* +@@ LUA_C89_NUMBERS ensures that Lua uses the largest types available for +** C89 ('long' and 'double'); Windows always has '__int64', so it does +** not need to use this case. +*/ +#if defined(LUA_USE_C89) && !defined(LUA_USE_WINDOWS) +#define LUA_C89_NUMBERS 1 +#else +#define LUA_C89_NUMBERS 0 +#endif + + +#if defined(LUA_32BITS) /* { */ +/* +** 32-bit integers and 'float' +*/ +#if LUAI_IS32INT /* use 'int' if big enough */ +#define LUA_INT_TYPE LUA_INT_INT +#else /* otherwise use 'long' */ +#define LUA_INT_TYPE LUA_INT_LONG +#endif +#define LUA_FLOAT_TYPE LUA_FLOAT_FLOAT + +#elif LUA_C89_NUMBERS /* }{ */ +/* +** largest types available for C89 ('long' and 'double') +*/ +#define LUA_INT_TYPE LUA_INT_LONG +#define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE + +#else /* }{ */ +/* use defaults */ + +#define LUA_INT_TYPE LUA_INT_DEFAULT +#define LUA_FLOAT_TYPE LUA_FLOAT_DEFAULT + +#endif /* } */ + + +/* }================================================================== */ + + + +/* +** {================================================================== +** Configuration for Paths. +** =================================================================== +*/ + +/* +** LUA_PATH_SEP is the character that separates templates in a path. +** LUA_PATH_MARK is the string that marks the substitution points in a +** template. +** LUA_EXEC_DIR in a Windows path is replaced by the executable's +** directory. +*/ +#define LUA_PATH_SEP ";" +#define LUA_PATH_MARK "?" +#define LUA_EXEC_DIR "!" + + +/* +@@ LUA_PATH_DEFAULT is the default path that Lua uses to look for +** Lua libraries. +@@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for +** C libraries. +** CHANGE them if your machine has a non-conventional directory +** hierarchy or if you want to install your libraries in +** non-conventional directories. +*/ + +#define LUA_VDIR LUA_VERSION_MAJOR "." LUA_VERSION_MINOR +#if defined(_WIN32) /* { */ +/* +** In Windows, any exclamation mark ('!') in the path is replaced by the +** path of the directory of the executable file of the current process. +*/ +#define LUA_LDIR "!\\lua\\" +#define LUA_CDIR "!\\" +#define LUA_SHRDIR "!\\..\\share\\lua\\" LUA_VDIR "\\" + +#if !defined(LUA_PATH_DEFAULT) +#define LUA_PATH_DEFAULT \ + LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \ + LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua;" \ + LUA_SHRDIR"?.lua;" LUA_SHRDIR"?\\init.lua;" \ + ".\\?.lua;" ".\\?\\init.lua" +#endif + +#if !defined(LUA_CPATH_DEFAULT) +#define LUA_CPATH_DEFAULT \ + LUA_CDIR"?.dll;" \ + LUA_CDIR"..\\lib\\lua\\" LUA_VDIR "\\?.dll;" \ + LUA_CDIR"loadall.dll;" ".\\?.dll" +#endif + +#else /* }{ */ + +#define LUA_ROOT "/usr/local/" +#define LUA_LDIR LUA_ROOT "share/lua/" LUA_VDIR "/" +#define LUA_CDIR LUA_ROOT "lib/lua/" LUA_VDIR "/" + +#if !defined(LUA_PATH_DEFAULT) +#define LUA_PATH_DEFAULT \ + LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \ + LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" \ + "./?.lua;" "./?/init.lua" +#endif + +#if !defined(LUA_CPATH_DEFAULT) +#define LUA_CPATH_DEFAULT \ + LUA_CDIR"?.so;" LUA_CDIR"loadall.so;" "./?.so" +#endif + +#endif /* } */ + + +/* +@@ LUA_DIRSEP is the directory separator (for submodules). +** CHANGE it if your machine does not use "/" as the directory separator +** and is not Windows. (On Windows Lua automatically uses "\".) +*/ +#if !defined(LUA_DIRSEP) + +#if defined(_WIN32) +#define LUA_DIRSEP "\\" +#else +#define LUA_DIRSEP "/" +#endif + +#endif + + +/* +** LUA_IGMARK is a mark to ignore all after it when building the +** module name (e.g., used to build the luaopen_ function name). +** Typically, the suffix after the mark is the module version, +** as in "mod-v1.2.so". +*/ +#define LUA_IGMARK "-" + +/* }================================================================== */ + + +/* +** {================================================================== +** Marks for exported symbols in the C code +** =================================================================== +*/ + +/* +@@ LUA_API is a mark for all core API functions. +@@ LUALIB_API is a mark for all auxiliary library functions. +@@ LUAMOD_API is a mark for all standard library opening functions. +** CHANGE them if you need to define those functions in some special way. +** For instance, if you want to create one Windows DLL with the core and +** the libraries, you may want to use the following definition (define +** LUA_BUILD_AS_DLL to get it). +*/ +#if defined(LUA_BUILD_AS_DLL) /* { */ + +#if defined(LUA_CORE) || defined(LUA_LIB) /* { */ +#define LUA_API __declspec(dllexport) +#else /* }{ */ +#define LUA_API __declspec(dllimport) +#endif /* } */ + +#else /* }{ */ + +#define LUA_API extern + +#endif /* } */ + + +/* +** More often than not the libs go together with the core. +*/ +#define LUALIB_API LUA_API + +#if defined(__cplusplus) +/* Lua uses the "C name" when calling open functions */ +#define LUAMOD_API extern "C" +#else +#define LUAMOD_API LUA_API +#endif + +/* }================================================================== */ + + +/* +** {================================================================== +** Compatibility with previous versions +** =================================================================== +*/ + +/* +@@ LUA_COMPAT_GLOBAL avoids 'global' being a reserved word +*/ +#define LUA_COMPAT_GLOBAL + + +/* +@@ LUA_COMPAT_MATHLIB controls the presence of several deprecated +** functions in the mathematical library. +** (These functions were already officially removed in 5.3; +** nevertheless they are still available here.) +*/ +/* #define LUA_COMPAT_MATHLIB */ + + +/* +@@ The following macros supply trivial compatibility for some +** changes in the API. The macros themselves document how to +** change your code to avoid using them. +** (Once more, these macros were officially removed in 5.3, but they are +** still available here.) +*/ +#define lua_strlen(L,i) lua_rawlen(L, (i)) + +#define lua_objlen(L,i) lua_rawlen(L, (i)) + +#define lua_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ) +#define lua_lessthan(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPLT) + +/* }================================================================== */ + + + +/* +** {================================================================== +** Configuration for Numbers (low-level part). +** Change these definitions if no predefined LUA_FLOAT_* / LUA_INT_* +** satisfy your needs. +** =================================================================== +*/ + +/* +@@ LUAI_UACNUMBER is the result of a 'default argument promotion' +@@ over a floating number. +@@ l_floatatt(x) corrects float attribute 'x' to the proper float type +** by prefixing it with one of FLT/DBL/LDBL. +@@ LUA_NUMBER_FRMLEN is the length modifier for writing floats. +@@ LUA_NUMBER_FMT is the format for writing floats with the maximum +** number of digits that respects tostring(tonumber(numeral)) == numeral. +** (That would be floor(log10(2^n)), where n is the number of bits in +** the float mantissa.) +@@ LUA_NUMBER_FMT_N is the format for writing floats with the minimum +** number of digits that ensures tonumber(tostring(number)) == number. +** (That would be LUA_NUMBER_FMT+2.) +@@ l_mathop allows the addition of an 'l' or 'f' to all math operations. +@@ l_floor takes the floor of a float. +@@ lua_str2number converts a decimal numeral to a number. +*/ + + +/* The following definition is good for most cases here */ + +#define l_floor(x) (l_mathop(floor)(x)) + + +/* now the variable definitions */ + +#if LUA_FLOAT_TYPE == LUA_FLOAT_FLOAT /* { single float */ + +#define LUA_NUMBER float + +#define l_floatatt(n) (FLT_##n) + +#define LUAI_UACNUMBER double + +#define LUA_NUMBER_FRMLEN "" +#define LUA_NUMBER_FMT "%.7g" +#define LUA_NUMBER_FMT_N "%.9g" + +#define l_mathop(op) op##f + +#define lua_str2number(s,p) strtof((s), (p)) + + +#elif LUA_FLOAT_TYPE == LUA_FLOAT_LONGDOUBLE /* }{ long double */ + +#define LUA_NUMBER long double + +#define l_floatatt(n) (LDBL_##n) + +#define LUAI_UACNUMBER long double + +#define LUA_NUMBER_FRMLEN "L" +#define LUA_NUMBER_FMT "%.19Lg" +#define LUA_NUMBER_FMT_N "%.21Lg" + +#define l_mathop(op) op##l + +#define lua_str2number(s,p) strtold((s), (p)) + +#elif LUA_FLOAT_TYPE == LUA_FLOAT_DOUBLE /* }{ double */ + +#define LUA_NUMBER double + +#define l_floatatt(n) (DBL_##n) + +#define LUAI_UACNUMBER double + +#define LUA_NUMBER_FRMLEN "" +#define LUA_NUMBER_FMT "%.15g" +#define LUA_NUMBER_FMT_N "%.17g" + +#define l_mathop(op) op + +#define lua_str2number(s,p) strtod((s), (p)) + +#else /* }{ */ + +#error "numeric float type not defined" + +#endif /* } */ + + + +/* +@@ LUA_UNSIGNED is the unsigned version of LUA_INTEGER. +@@ LUAI_UACINT is the result of a 'default argument promotion' +@@ over a LUA_INTEGER. +@@ LUA_INTEGER_FRMLEN is the length modifier for reading/writing integers. +@@ LUA_INTEGER_FMT is the format for writing integers. +@@ LUA_MAXINTEGER is the maximum value for a LUA_INTEGER. +@@ LUA_MININTEGER is the minimum value for a LUA_INTEGER. +@@ LUA_MAXUNSIGNED is the maximum value for a LUA_UNSIGNED. +@@ lua_integer2str converts an integer to a string. +*/ + + +/* The following definitions are good for most cases here */ + +#define LUA_INTEGER_FMT "%" LUA_INTEGER_FRMLEN "d" + +#define LUAI_UACINT LUA_INTEGER + +#define lua_integer2str(s,sz,n) \ + l_sprintf((s), sz, LUA_INTEGER_FMT, (LUAI_UACINT)(n)) + +/* +** use LUAI_UACINT here to avoid problems with promotions (which +** can turn a comparison between unsigneds into a signed comparison) +*/ +#define LUA_UNSIGNED unsigned LUAI_UACINT + + +/* now the variable definitions */ + +#if LUA_INT_TYPE == LUA_INT_INT /* { int */ + +#define LUA_INTEGER int +#define LUA_INTEGER_FRMLEN "" + +#define LUA_MAXINTEGER INT_MAX +#define LUA_MININTEGER INT_MIN + +#define LUA_MAXUNSIGNED UINT_MAX + +#elif LUA_INT_TYPE == LUA_INT_LONG /* }{ long */ + +#define LUA_INTEGER long +#define LUA_INTEGER_FRMLEN "l" + +#define LUA_MAXINTEGER LONG_MAX +#define LUA_MININTEGER LONG_MIN + +#define LUA_MAXUNSIGNED ULONG_MAX + +#elif LUA_INT_TYPE == LUA_INT_LONGLONG /* }{ long long */ + +/* use presence of macro LLONG_MAX as proxy for C99 compliance */ +#if defined(LLONG_MAX) /* { */ +/* use ISO C99 stuff */ + +#define LUA_INTEGER long long +#define LUA_INTEGER_FRMLEN "ll" + +#define LUA_MAXINTEGER LLONG_MAX +#define LUA_MININTEGER LLONG_MIN + +#define LUA_MAXUNSIGNED ULLONG_MAX + +#elif defined(LUA_USE_WINDOWS) /* }{ */ +/* in Windows, can use specific Windows types */ + +#define LUA_INTEGER __int64 +#define LUA_INTEGER_FRMLEN "I64" + +#define LUA_MAXINTEGER _I64_MAX +#define LUA_MININTEGER _I64_MIN + +#define LUA_MAXUNSIGNED _UI64_MAX + +#else /* }{ */ + +#error "Compiler does not support 'long long'. Use option '-DLUA_32BITS' \ + or '-DLUA_C89_NUMBERS' (see file 'luaconf.h' for details)" + +#endif /* } */ + +#else /* }{ */ + +#error "numeric integer type not defined" + +#endif /* } */ + +/* }================================================================== */ + + +/* +** {================================================================== +** Dependencies with C99 and other C details +** =================================================================== +*/ + +/* +@@ l_sprintf is equivalent to 'snprintf' or 'sprintf' in C89. +** (All uses in Lua have only one format item.) +*/ +#if !defined(LUA_USE_C89) +#define l_sprintf(s,sz,f,i) snprintf(s,sz,f,i) +#else +#define l_sprintf(s,sz,f,i) ((void)(sz), sprintf(s,f,i)) +#endif + + +/* +@@ lua_strx2number converts a hexadecimal numeral to a number. +** In C99, 'strtod' does that conversion. Otherwise, you can +** leave 'lua_strx2number' undefined and Lua will provide its own +** implementation. +*/ +#if !defined(LUA_USE_C89) +#define lua_strx2number(s,p) lua_str2number(s,p) +#endif + + +/* +@@ lua_pointer2str converts a pointer to a readable string in a +** non-specified way. +*/ +#define lua_pointer2str(buff,sz,p) l_sprintf(buff,sz,"%p",p) + + +/* +@@ lua_number2strx converts a float to a hexadecimal numeral. +** In C99, 'sprintf' (with format specifiers '%a'/'%A') does that. +** Otherwise, you can leave 'lua_number2strx' undefined and Lua will +** provide its own implementation. +*/ +#if !defined(LUA_USE_C89) +#define lua_number2strx(L,b,sz,f,n) \ + ((void)L, l_sprintf(b,sz,f,(LUAI_UACNUMBER)(n))) +#endif + + +/* +** 'strtof' and 'opf' variants for math functions are not valid in +** C89. Otherwise, the macro 'HUGE_VALF' is a good proxy for testing the +** availability of these variants. ('math.h' is already included in +** all files that use these macros.) +*/ +#if defined(LUA_USE_C89) || (defined(HUGE_VAL) && !defined(HUGE_VALF)) +#undef l_mathop /* variants not available */ +#undef lua_str2number +#define l_mathop(op) (lua_Number)op /* no variant */ +#define lua_str2number(s,p) ((lua_Number)strtod((s), (p))) +#endif + + +/* +@@ LUA_KCONTEXT is the type of the context ('ctx') for continuation +** functions. It must be a numerical type; Lua will use 'intptr_t' if +** available, otherwise it will use 'ptrdiff_t' (the nearest thing to +** 'intptr_t' in C89) +*/ +#define LUA_KCONTEXT ptrdiff_t + +#if !defined(LUA_USE_C89) && defined(__STDC_VERSION__) && \ + __STDC_VERSION__ >= 199901L +#include +#if defined(INTPTR_MAX) /* even in C99 this type is optional */ +#undef LUA_KCONTEXT +#define LUA_KCONTEXT intptr_t +#endif +#endif + + +/* +@@ lua_getlocaledecpoint gets the locale "radix character" (decimal point). +** Change that if you do not want to use C locales. (Code using this +** macro must include the header 'locale.h'.) +*/ +#if !defined(lua_getlocaledecpoint) +#define lua_getlocaledecpoint() (localeconv()->decimal_point[0]) +#endif + + +/* +** macros to improve jump prediction, used mostly for error handling +** and debug facilities. (Some macros in the Lua API use these macros. +** Define LUA_NOBUILTIN if you do not want '__builtin_expect' in your +** code.) +*/ +#if !defined(luai_likely) + +#if defined(__GNUC__) && !defined(LUA_NOBUILTIN) +#define luai_likely(x) (__builtin_expect(((x) != 0), 1)) +#define luai_unlikely(x) (__builtin_expect(((x) != 0), 0)) +#else +#define luai_likely(x) (x) +#define luai_unlikely(x) (x) +#endif + +#endif + + + +/* }================================================================== */ + + +/* +** {================================================================== +** Language Variations +** ===================================================================== +*/ + +/* +@@ LUA_NOCVTN2S/LUA_NOCVTS2N control how Lua performs some +** coercions. Define LUA_NOCVTN2S to turn off automatic coercion from +** numbers to strings. Define LUA_NOCVTS2N to turn off automatic +** coercion from strings to numbers. +*/ +/* #define LUA_NOCVTN2S */ +/* #define LUA_NOCVTS2N */ + + +/* +@@ LUA_USE_APICHECK turns on several consistency checks on the C API. +** Define it as a help when debugging C code. +*/ +/* #define LUA_USE_APICHECK */ + +/* }================================================================== */ + + +/* +** {================================================================== +** Macros that affect the API and must be stable (that is, must be the +** same when you compile Lua and when you compile code that links to +** Lua). +** ===================================================================== +*/ + +/* +@@ LUA_EXTRASPACE defines the size of a raw memory area associated with +** a Lua state with very fast access. +** CHANGE it if you need a different size. +*/ +#define LUA_EXTRASPACE (sizeof(void *)) + + +/* +@@ LUA_IDSIZE gives the maximum size for the description of the source +** of a function in debug information. +** CHANGE it if you want a different size. +*/ +#define LUA_IDSIZE 60 + + +/* +@@ LUAL_BUFFERSIZE is the initial buffer size used by the lauxlib +** buffer system. +*/ +#define LUAL_BUFFERSIZE ((int)(16 * sizeof(void*) * sizeof(lua_Number))) + + +/* +@@ LUAI_MAXALIGN defines fields that, when used in a union, ensure +** maximum alignment for the other items in that union. +*/ +#define LUAI_MAXALIGN lua_Number n; double u; void *s; lua_Integer i; long l + +/* }================================================================== */ + + + + + +/* =================================================================== */ + +/* +** Local configuration. You can use this space to add your redefinitions +** without modifying the main part of the file. +*/ + + + +#endif + diff --git a/libs/lua/include/lualib.h b/libs/lua/include/lualib.h new file mode 100644 index 0000000..068f60a --- /dev/null +++ b/libs/lua/include/lualib.h @@ -0,0 +1,65 @@ +/* +** $Id: lualib.h $ +** Lua standard libraries +** See Copyright Notice in lua.h +*/ + + +#ifndef lualib_h +#define lualib_h + +#include "lua.h" + + +/* version suffix for environment variable names */ +#define LUA_VERSUFFIX "_" LUA_VERSION_MAJOR "_" LUA_VERSION_MINOR + +#define LUA_GLIBK 1 +LUAMOD_API int (luaopen_base) (lua_State *L); + +#define LUA_LOADLIBNAME "package" +#define LUA_LOADLIBK (LUA_GLIBK << 1) +LUAMOD_API int (luaopen_package) (lua_State *L); + + +#define LUA_COLIBNAME "coroutine" +#define LUA_COLIBK (LUA_LOADLIBK << 1) +LUAMOD_API int (luaopen_coroutine) (lua_State *L); + +#define LUA_DBLIBNAME "debug" +#define LUA_DBLIBK (LUA_COLIBK << 1) +LUAMOD_API int (luaopen_debug) (lua_State *L); + +#define LUA_IOLIBNAME "io" +#define LUA_IOLIBK (LUA_DBLIBK << 1) +LUAMOD_API int (luaopen_io) (lua_State *L); + +#define LUA_MATHLIBNAME "math" +#define LUA_MATHLIBK (LUA_IOLIBK << 1) +LUAMOD_API int (luaopen_math) (lua_State *L); + +#define LUA_OSLIBNAME "os" +#define LUA_OSLIBK (LUA_MATHLIBK << 1) +LUAMOD_API int (luaopen_os) (lua_State *L); + +#define LUA_STRLIBNAME "string" +#define LUA_STRLIBK (LUA_OSLIBK << 1) +LUAMOD_API int (luaopen_string) (lua_State *L); + +#define LUA_TABLIBNAME "table" +#define LUA_TABLIBK (LUA_STRLIBK << 1) +LUAMOD_API int (luaopen_table) (lua_State *L); + +#define LUA_UTF8LIBNAME "utf8" +#define LUA_UTF8LIBK (LUA_TABLIBK << 1) +LUAMOD_API int (luaopen_utf8) (lua_State *L); + + +/* open selected libraries */ +LUALIB_API void (luaL_openselectedlibs) (lua_State *L, int load, int preload); + +/* open all libraries */ +#define luaL_openlibs(L) luaL_openselectedlibs(L, ~0, 0) + + +#endif diff --git a/libs/lua/liblua55.a b/libs/lua/liblua55.a new file mode 100644 index 0000000..d6ae770 Binary files /dev/null and b/libs/lua/liblua55.a differ diff --git a/libs/lua/lua55.dll b/libs/lua/lua55.dll new file mode 100644 index 0000000..ff62671 Binary files /dev/null and b/libs/lua/lua55.dll differ diff --git a/lua/config.lua b/lua/config.lua new file mode 100644 index 0000000..562945b --- /dev/null +++ b/lua/config.lua @@ -0,0 +1,76 @@ +-- PathEditor 配置文件 +-- 用于热更新 UI 参数,无需重新编译即可调整界面 + +local config = { + -- 应用程序信息 + app = { + name = "PathEditor", + name_readonly = "PathEditor (只读模式)" + }, + + -- 对话框设置 + dialog = { + size = "800x800", + minsize = "800x800", + select_dir = "选择目录" + }, + + -- 列表控件设置 + list = { + item_padding = "5x5", + backcolor = "255 255 255" + }, + + -- 按钮设置 + button = { + rastersize = "100x32", + new = "新建(N)", + edit = "编辑(E)", + browse = "浏览(B)...", + del = "删除(D)", + up = "上移(U)", + down = "下移(O)", + clean = "一键清理", + import = "导入(I)", + export = "导出(E)", + ok = "确定", + cancel = "取消", + help = "帮助(?)" + }, + + -- 标签文本 + label = { + title = "环境变量编辑器:", + search_placeholder = "输入关键词搜索...", + tab_sys = "系统变量 (System)", + tab_user = "用户变量 (User)", + export_title = "导出 PATH", + import_title = "导入 PATH" + }, + + -- 布局设置 + layout = { + vbox_gap = "5", + vbox_margin = "0x0", + vbox_all_margin = "10x10", + vbox_all_gap = "5", + hbox_gap = "10", + hbox_margin = "10x10", + hbox_alignment = "ACENTER" + }, + + -- 状态栏 + status = { + normal = "状态: 就绪", + readonly = "状态: ⚠️ 只读模式 (无管理员权限)", + saving = "状态: 保存中...", + saved = "状态: ✓ 保存成功", + error = "状态: ✗ 保存失败", + deleted = "状态: 已删除选中项", + loaded = "状态: 已加载系统和用户变量", + drag_folder_only = "提示: 只能拖拽文件夹添加到 PATH", + admin_warning = "未检测到管理员权限,只能查看和导出 PATH,无法保存更改。" + } +} + +return config \ No newline at end of file diff --git a/src/callbacks.c b/src/callbacks.c deleted file mode 100644 index 63605ef..0000000 --- a/src/callbacks.c +++ /dev/null @@ -1,327 +0,0 @@ -#include "callbacks.h" -#include "globals.h" -#include "registry.h" -#include "utils.h" -#include -#include - -// 简单的自定义输入对话框,支持更宽的输入框 -// 返回 1 表示确定,0 表示取消 -int show_custom_input_dialog(const char *title, const char *label_text, char *buffer, int buffer_size) -{ - Ihandle *text = IupText(NULL); - IupSetAttribute(text, "VALUE", buffer); - IupSetAttribute(text, "EXPAND", "HORIZONTAL"); - IupSetAttribute(text, "RASTERSIZE", "600x"); // 设置宽度为600像素 - IupSetAttribute(text, "VISIBLECOLUMNS", "80"); // 设置可见列数 - - // 如果是编辑模式,全选文本 - if (strlen(buffer) > 0) - { - IupSetAttribute(text, "SELECTION", "ALL"); - } - - Ihandle *dlg = IupDialog( - IupVbox( - IupLabel(label_text), - text, - IupHbox( - IupFill(), - IupButton("确定", "1"), // "1" will be returned by IupPopup - IupButton("取消", "0"), // "0" will be returned by IupPopup - NULL), - NULL)); - - // 布局设置 - IupSetAttribute(dlg, "TITLE", title); - IupSetAttribute(dlg, "MINBOX", "NO"); - IupSetAttribute(dlg, "MAXBOX", "NO"); - IupSetAttribute(dlg, "RESIZE", "NO"); - IupSetAttribute(dlg, "MARGIN", "10x10"); - IupSetAttribute(dlg, "GAP", "10"); - - // 按钮响应 - // 这是一个简单的 hack:IupPopup 的参数 x, y 如果是 IUP_CENTER 等,它是一个模态循环。 - // 但是标准的 IupPopup 不返回按钮值。 - // 我们使用 IupAlarm 类似的逻辑,或者使用 IUP 提供的 IupGetParam。 - // 为了最简单实现,我们使用 IUP 的 IupGetParam,但是它很难调整宽度。 - // 所以还是手动构建对话框。 - - // 为了获取返回值,我们需要设置按钮回调。 - // 但为了避免定义额外的全局函数,我们可以使用 IupPopup 阻塞特性。 - // 我们需要定义两个简单的回调函数。 - // 为了简化,这里定义两个静态辅助函数。 - - // 由于 C 语言闭包限制,我们需要用全局或静态变量传递状态,或者使用 Dialog 的 Attribute。 - IupSetAttribute(dlg, "MY_STATUS", "0"); - - // 注册回调 (使用 IupSetCallback 注册 lambda 类似的逻辑比较难,这里用名字) - // 必须定义全局函数。为了避免污染,我们在文件顶部定义静态函数。 - // 见下文的 static int on_dialog_ok... - - // 由于不能在函数内部定义函数,我们需要调整代码结构。 - // 见下文重构。 - - return 0; // 占位 -} - -// 静态辅助函数:对话框确定 -static int on_dialog_ok(Ihandle *self) -{ - Ihandle *dlg = IupGetDialog(self); - IupSetAttribute(dlg, "MY_STATUS", "1"); - return IUP_CLOSE; -} - -// 静态辅助函数:对话框取消 -static int on_dialog_cancel(Ihandle *self) -{ - Ihandle *dlg = IupGetDialog(self); - IupSetAttribute(dlg, "MY_STATUS", "0"); - return IUP_CLOSE; -} - -// 真正的实现函数 -int custom_input_dialog(const char *title, const char *label_text, char *buffer, int buffer_size) -{ - Ihandle *text = IupText(NULL); - IupSetAttribute(text, "VALUE", buffer); - IupSetAttribute(text, "EXPAND", "HORIZONTAL"); - IupSetAttribute(text, "RASTERSIZE", "500x"); - IupSetAttribute(text, "NAME", "INPUT_TEXT"); - - Ihandle *btn_ok = IupButton("确定", NULL); - IupSetCallback(btn_ok, "ACTION", on_dialog_ok); - IupSetAttribute(btn_ok, "RASTERSIZE", "100x32"); - - Ihandle *btn_cancel = IupButton("取消", NULL); - IupSetCallback(btn_cancel, "ACTION", on_dialog_cancel); - IupSetAttribute(btn_cancel, "RASTERSIZE", "100x32"); - - Ihandle *vbox = IupVbox( - IupLabel(label_text), - text, - IupHbox(IupFill(), btn_ok, btn_cancel, NULL), - NULL); - IupSetAttribute(vbox, "MARGIN", "15x15"); - IupSetAttribute(vbox, "GAP", "10"); - - Ihandle *dlg = IupDialog(vbox); - IupSetAttribute(dlg, "TITLE", title); - IupSetAttribute(dlg, "MINBOX", "NO"); - IupSetAttribute(dlg, "MAXBOX", "NO"); - IupSetAttribute(dlg, "RESIZE", "NO"); - - IupSetAttributeHandle(dlg, "DEFAULTENTER", btn_ok); - IupSetAttributeHandle(dlg, "DEFAULTESC", btn_cancel); - - IupPopup(dlg, IUP_CENTER, IUP_CENTER); - - int result = IupGetInt(dlg, "MY_STATUS"); - if (result == 1) - { - char *val = IupGetAttribute(text, "VALUE"); - if (val) - { - strncpy(buffer, val, buffer_size); - buffer[buffer_size - 1] = '\0'; - } - } - - IupDestroy(dlg); - return result; -} - -// 按钮回调:新建 -int btn_new_cb(Ihandle *self) -{ - char buffer[1024] = ""; - if (custom_input_dialog("新建环境变量", "请输入路径:", buffer, sizeof(buffer))) - { - if (strlen(buffer) > 0) - { - int count = IupGetInt(list_path, "COUNT"); - count++; - IupSetAttributeId(list_path, "", count, buffer); - IupSetInt(list_path, "COUNT", count); - IupSetInt(list_path, "VALUE", count); - - refresh_list_style(); - } - } - return IUP_DEFAULT; -} - -// 按钮回调:编辑 -int btn_edit_cb(Ihandle *self) -{ - int selected = IupGetInt(list_path, "VALUE"); - if (selected == 0) - return IUP_DEFAULT; - - char *current_val = IupGetAttributeId(list_path, "", selected); - char buffer[4096]; // 假设单个路径不超过4096 - if (current_val) - { - strncpy(buffer, current_val, 4096); - buffer[4095] = '\0'; - } - else - { - buffer[0] = '\0'; - } - - if (custom_input_dialog("编辑环境变量", "编辑路径:", buffer, sizeof(buffer))) - { - if (strlen(buffer) > 0) - { - IupSetAttributeId(list_path, "", selected, buffer); - refresh_list_style(); - } - } - return IUP_DEFAULT; -} - -// 双击回调 -int list_dblclick_cb(Ihandle *self, int item, char *text) -{ - // 这里的 item 是点击的行号 - if (item > 0) - { - // 选中该行 - IupSetInt(list_path, "VALUE", item); - // 调用编辑逻辑 - btn_edit_cb(NULL); - } - return IUP_DEFAULT; -} - -// 按钮回调:浏览 -int btn_browse_cb(Ihandle *self) -{ - Ihandle *filedlg = IupFileDlg(); - IupSetAttribute(filedlg, "DIALOGTYPE", "DIR"); - IupSetAttribute(filedlg, "TITLE", "选择目录"); - - IupPopup(filedlg, IUP_CENTER, IUP_CENTER); - - if (IupGetInt(filedlg, "STATUS") != -1) - { - char *value = IupGetAttribute(filedlg, "VALUE"); - if (value) - { - int count = IupGetInt(list_path, "COUNT"); - count++; - IupSetAttributeId(list_path, "", count, value); - IupSetInt(list_path, "COUNT", count); - IupSetInt(list_path, "VALUE", count); - - refresh_list_style(); - } - } - IupDestroy(filedlg); - return IUP_DEFAULT; -} - -// 按钮回调:删除 -int btn_del_cb(Ihandle *self) -{ - int selected = IupGetInt(list_path, "VALUE"); - if (selected == 0) - return IUP_DEFAULT; - - IupSetAttribute(list_path, "REMOVEITEM", "SELECTED"); - - // 重新刷新,因为删除了中间项,后面的奇偶性变了 - refresh_list_style(); - return IUP_DEFAULT; -} - -// 按钮回调:上移 -int btn_up_cb(Ihandle *self) -{ - int selected = IupGetInt(list_path, "VALUE"); - if (selected <= 1) - return IUP_DEFAULT; // 已经是第一个或未选中 - - char *current = IupGetAttributeId(list_path, "", selected); - char *prev = IupGetAttributeId(list_path, "", selected - 1); - - // 交换内容 - char buf_curr[4096], buf_prev[4096]; - strncpy(buf_curr, current, 4096); - buf_curr[4095] = '\0'; - strncpy(buf_prev, prev, 4096); - buf_prev[4095] = '\0'; - - IupSetAttributeId(list_path, "", selected, buf_prev); - IupSetAttributeId(list_path, "", selected - 1, buf_curr); - - IupSetInt(list_path, "VALUE", selected - 1); - - // 刷新样式(虽然颜色不需要变,但为了保险) - refresh_list_style(); - return IUP_DEFAULT; -} - -// 按钮回调:下移 -int btn_down_cb(Ihandle *self) -{ - int selected = IupGetInt(list_path, "VALUE"); - int count = IupGetInt(list_path, "COUNT"); - if (selected == 0 || selected >= count) - return IUP_DEFAULT; - - char *current = IupGetAttributeId(list_path, "", selected); - char *next = IupGetAttributeId(list_path, "", selected + 1); - - char buf_curr[4096], buf_next[4096]; - strncpy(buf_curr, current, 4096); - buf_curr[4095] = '\0'; - strncpy(buf_next, next, 4096); - buf_next[4095] = '\0'; - - IupSetAttributeId(list_path, "", selected, buf_next); - IupSetAttributeId(list_path, "", selected + 1, buf_curr); - - IupSetInt(list_path, "VALUE", selected + 1); - - refresh_list_style(); - return IUP_DEFAULT; -} - -// 按钮回调:确定 -int btn_ok_cb(Ihandle *self) -{ - save_path(); - return IUP_DEFAULT; -} - -// 按钮回调:取消 -int btn_cancel_cb(Ihandle *self) -{ - IupExitLoop(); - return IUP_DEFAULT; -} - -// 按钮回调:帮助 -int btn_help_cb(Ihandle *self) -{ - IupMessage("使用说明", - "1. 本程序用于编辑系统环境变量 PATH。\n" - "2. 必须以【管理员身份】运行才能保存更改。\n" - "3. 操作说明:\n" - " - 新建:添加新路径到列表末尾。\n" - " - 编辑:修改选中的路径。\n" - " - 浏览:从文件系统选择目录添加。\n" - " - 删除:移除选中的路径。\n" - " - 上移/下移:调整路径优先级。\n" - "4. 点击【确定】保存更改并生效。\n" - "5. 注意:某些正在运行的程序可能需要重启才能识别新的环境变量。\n\n" - "--------------------------------------------------\n" - "作者:LHY\n" - "邮箱:3364451258@qq.com\n" - "GitHub:https://github.com/LHY0125/PathEditor\n" - "记得给我的项目点个star!"); - return IUP_DEFAULT; -} \ No newline at end of file diff --git a/src/controller/callbacks.c b/src/controller/callbacks.c new file mode 100644 index 0000000..a9bf9e5 --- /dev/null +++ b/src/controller/callbacks.c @@ -0,0 +1,599 @@ +#include "controller/callbacks.h" +#include "core/app_context.h" +#include "core/registry_service.h" +#include "core/path_manager.h" +#include "core/lua_config.h" +#include "core/import_export.h" +#include "utils/string_ext.h" +#include "utils/os_env.h" +#include "utils/error_code.h" +#include "utils/safe_string.h" +#include "utils/logger.h" +#include "ui/ui_utils.h" +#include "ui/dialogs.h" +#include +#include +#include +#include + +// 辅助函数:获取主对话框 +static Ihandle *get_main_dlg() +{ + // 在实际情况中,可以通过 IupGetHandle 注册名字,或者通过某个全局/静态缓存 + // 但如果想彻底不用全局,我们可以在 IupSetHandle 里面把主窗口存下来 + return IupGetHandle("MAIN_DIALOG"); +} + +// 获取当前的缓存数据列表 +static StringList *get_current_raw_data(Ihandle *dlg) +{ + AppContext *ctx = get_app_context(dlg); + if (!ctx) + return NULL; + + Ihandle *tabs_main = IupGetDialogChild(dlg, "TABS_MAIN"); + int pos = IupGetInt(tabs_main, "VALUEPOS"); + if (pos == 0) + return &ctx->sys_paths; + if (pos == 1) + return &ctx->user_paths; + return &ctx->sys_paths; +} + +// 辅助函数:获取当前选中的列表UI控件 +static Ihandle *get_current_list(Ihandle *dlg) +{ + Ihandle *tabs_main = IupGetDialogChild(dlg, "TABS_MAIN"); + int pos = IupGetInt(tabs_main, "VALUEPOS"); + if (pos == 0) + return IupGetDialogChild(dlg, "LIST_SYS"); + if (pos == 1) + return IupGetDialogChild(dlg, "LIST_USER"); + return IupGetDialogChild(dlg, "LIST_SYS"); +} + +// 按钮回调:新建 +int btn_new_cb(Ihandle *self) +{ + Ihandle *dlg = IupGetDialog(self); + char buffer[1024] = ""; + if (custom_input_dialog("新建环境变量", "请输入路径:", buffer, sizeof(buffer))) + { + if (strlen(buffer) > 0) + { + StringList *raw_data = get_current_raw_data(dlg); + add_string_list(raw_data, buffer); + + Ihandle *current_list = get_current_list(dlg); + sync_string_list_to_ui(current_list, raw_data); + + int count = IupGetInt(current_list, "COUNT"); + IupSetInt(current_list, "VALUE", count); + } + } + return IUP_DEFAULT; +} + +// 按钮回调:编辑 +int btn_edit_cb(Ihandle *self) +{ + Ihandle *dlg = IupGetDialog(self); + Ihandle *current_list = get_current_list(dlg); + int selected = IupGetInt(current_list, "VALUE"); + if (selected == 0) + return IUP_DEFAULT; + + StringList *raw_data = get_current_raw_data(dlg); + if (selected - 1 >= raw_data->count) + return IUP_DEFAULT; + + char buffer[4096]; + safe_strcpy(buffer, sizeof(buffer), raw_data->items[selected - 1]); + + if (custom_input_dialog("编辑环境变量", "编辑路径:", buffer, sizeof(buffer))) + { + if (strlen(buffer) > 0) + { + free(raw_data->items[selected - 1]); + raw_data->items[selected - 1] = _strdup(buffer); + + sync_string_list_to_ui(current_list, raw_data); + IupSetInt(current_list, "VALUE", selected); + } + } + return IUP_DEFAULT; +} + +// 双击回调 +int list_dblclick_cb(Ihandle *self, int item, char *text) +{ + if (item > 0) + { + IupSetInt(self, "VALUE", item); + btn_edit_cb(self); + } + return IUP_DEFAULT; +} + +// 按钮回调:浏览 +int btn_browse_cb(Ihandle *self) +{ + Ihandle *dlg = IupGetDialog(self); + Ihandle *filedlg = IupFileDlg(); + IupSetAttribute(filedlg, "DIALOGTYPE", "DIR"); + IupSetAttribute(filedlg, "TITLE", lua_config_get_string("dialog", "select_dir")); + + IupPopup(filedlg, IUP_CENTER, IUP_CENTER); + + if (IupGetInt(filedlg, "STATUS") != -1) + { + char *value = IupGetAttribute(filedlg, "VALUE"); + if (value) + { + StringList *raw_data = get_current_raw_data(dlg); + add_string_list(raw_data, value); + + Ihandle *current_list = get_current_list(dlg); + sync_string_list_to_ui(current_list, raw_data); + + int count = IupGetInt(current_list, "COUNT"); + IupSetInt(current_list, "VALUE", count); + } + } + IupDestroy(filedlg); + return IUP_DEFAULT; +} + +// 按钮回调:删除 +int btn_del_cb(Ihandle *self) +{ + Ihandle *dlg = IupGetDialog(self); + Ihandle *current_list = get_current_list(dlg); + int selected = IupGetInt(current_list, "VALUE"); + + if (selected == 0) + { + IupMessage("提示", "请先选择要删除的项"); + return IUP_DEFAULT; + } + + StringList *raw_data = get_current_raw_data(dlg); + ErrorCode result = path_manager_remove_at(raw_data, selected - 1); + if (result != ERR_OK) + { + log_error("Failed to remove path at index %d", selected - 1); + } + + sync_string_list_to_ui(current_list, raw_data); + + Ihandle *lbl_status = IupGetDialogChild(dlg, "LBL_STATUS"); + if (lbl_status) + IupSetAttribute(lbl_status, "TITLE", lua_config_get_string("status", "deleted")); + + return IUP_DEFAULT; +} + +// 按钮回调:上移 +int btn_up_cb(Ihandle *self) +{ + Ihandle *dlg = IupGetDialog(self); + Ihandle *current_list = get_current_list(dlg); + int selected = IupGetInt(current_list, "VALUE"); + if (selected <= 1) + return IUP_DEFAULT; + + StringList *raw_data = get_current_raw_data(dlg); + ErrorCode result = path_manager_move_up(raw_data, selected - 1); + if (result != ERR_OK) + { + log_error("Failed to move path up at index %d", selected - 1); + } + + sync_string_list_to_ui(current_list, raw_data); + IupSetInt(current_list, "VALUE", selected - 1); + + return IUP_DEFAULT; +} + +// 按钮回调:下移 +int btn_down_cb(Ihandle *self) +{ + Ihandle *dlg = IupGetDialog(self); + Ihandle *current_list = get_current_list(dlg); + int selected = IupGetInt(current_list, "VALUE"); + StringList *raw_data = get_current_raw_data(dlg); + + if (selected == 0 || selected >= raw_data->count) + return IUP_DEFAULT; + + ErrorCode result = path_manager_move_down(raw_data, selected - 1); + if (result != ERR_OK) + { + log_error("Failed to move path down at index %d", selected - 1); + } + + sync_string_list_to_ui(current_list, raw_data); + IupSetInt(current_list, "VALUE", selected + 1); + + return IUP_DEFAULT; +} + +// 按钮回调:一键清理 +int btn_clean_cb(Ihandle *self) +{ + Ihandle *dlg = IupGetDialog(self); + StringList *raw_data = get_current_raw_data(dlg); + if (!raw_data || raw_data->count == 0) + return IUP_DEFAULT; + + if (IupAlarm("确认清理", "此操作将移除当前列表中所有【无效路径】和【重复路径】。\n确定要继续吗?", "确定", "取消", NULL) != 1) + { + return IUP_DEFAULT; + } + + int before_count = raw_data->count; + path_manager_clean(raw_data); + int removed = before_count - raw_data->count; + + Ihandle *current_list = get_current_list(dlg); + sync_string_list_to_ui(current_list, raw_data); + + char msg[128]; + snprintf(msg, sizeof(msg), "清理完成!共移除了 %d 个无效或重复路径。", removed); + IupMessage("提示", msg); + return IUP_DEFAULT; +} + +// 搜索回调 +int txt_search_cb(Ihandle *self) +{ + Ihandle *dlg = IupGetDialog(self); + char *filter = IupGetAttribute(self, "VALUE"); + if (!filter) + return IUP_DEFAULT; + + Ihandle *current_list = get_current_list(dlg); + StringList *raw_data = get_current_raw_data(dlg); + + IupSetAttribute(current_list, "REMOVEITEM", "ALL"); + + int count = 0; + for (int i = 0; i < raw_data->count; i++) + { + if (strlen(filter) == 0 || stristr(raw_data->items[i], filter) != NULL) + { + count++; + IupSetAttributeId(current_list, "", count, raw_data->items[i]); + } + } + + IupSetInt(current_list, "COUNT", count); + refresh_single_list_style(current_list); + + return IUP_DEFAULT; +} + +// 拖拽回调 +int list_dropfiles_cb(Ihandle *self, const char *filename, int num, int x, int y) +{ + Ihandle *dlg = IupGetDialog(self); + Ihandle *current_list = self; + AppContext *ctx = get_app_context(dlg); + if (!ctx) + return IUP_DEFAULT; + + StringList *raw_data = NULL; + if (self == IupGetDialogChild(dlg, "LIST_SYS")) + raw_data = &ctx->sys_paths; + else if (self == IupGetDialogChild(dlg, "LIST_USER")) + raw_data = &ctx->user_paths; + else + return IUP_DEFAULT; + + DWORD attr = GetFileAttributesA(filename); + if (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY)) + { + Ihandle *txt_search = IupGetDialogChild(dlg, "TXT_SEARCH"); + if (txt_search) + IupSetAttribute(txt_search, "VALUE", ""); + + add_string_list(raw_data, filename); + sync_string_list_to_ui(current_list, raw_data); + + IupSetInt(current_list, "VALUE", raw_data->count); + } + else + { + Ihandle *lbl_status = IupGetDialogChild(dlg, "LBL_STATUS"); + if (lbl_status) + IupSetAttribute(lbl_status, "TITLE", lua_config_get_string("status", "drag_folder_only")); + } + + return IUP_DEFAULT; +} + +// 键盘按键回调 +int list_k_any_cb(Ihandle *self, int c) +{ + if (c == K_DEL) + { + btn_del_cb(self); + return IUP_IGNORE; + } + return IUP_DEFAULT; +} + +// 按钮回调:确定 (保存所有) +int btn_ok_cb(Ihandle *self) +{ + Ihandle *dlg = IupGetDialog(self); + AppContext *ctx = get_app_context(dlg); + if (!ctx) + return IUP_DEFAULT; + + if (!check_admin()) + { + IupMessage("错误", "需要管理员权限才能保存更改!"); + return IUP_DEFAULT; + } + + backup_registry(); + + ErrorCode sys_ok = save_system_paths(&ctx->sys_paths); + ErrorCode user_ok = save_user_paths(&ctx->user_paths); + + Ihandle *lbl_status = IupGetDialogChild(dlg, "LBL_STATUS"); + + if (sys_ok == ERR_OK && user_ok == ERR_OK) + { + log_info("Saved system paths: %d, user paths: %d", ctx->sys_paths.count, ctx->user_paths.count); + SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)L"Environment", SMTO_ABORTIFHUNG, 5000, NULL); + IupMessage("成功", "系统和用户 PATH 环境变量均已更新!"); + if (lbl_status) + IupSetAttribute(lbl_status, "TITLE", lua_config_get_string("status", "saved")); + } + else if (sys_ok == ERR_OK) + { + IupMessage("提示", "系统变量保存成功,但用户变量保存失败。"); + } + else if (user_ok == ERR_OK) + { + IupMessage("提示", "用户变量保存成功,但系统变量保存失败。"); + } + else + { + log_error("Failed to save paths: sys=%d, user=%d", sys_ok, user_ok); + IupMessage("错误", "保存失败!"); + if (lbl_status) + IupSetAttribute(lbl_status, "TITLE", lua_config_get_string("status", "error")); + } + return IUP_DEFAULT; +} + +// 按钮回调:取消 +int btn_cancel_cb(Ihandle *self) +{ + IupExitLoop(); + return IUP_DEFAULT; +} + +// 按钮回调:导入 +int btn_import_cb(Ihandle *self) +{ + Ihandle *dlg = IupGetDialog(self); + AppContext *ctx = get_app_context(dlg); + if (!ctx) + return IUP_DEFAULT; + + if (!check_admin()) + { + IupMessage("错误", "需要管理员权限才能导入 PATH!"); + return IUP_DEFAULT; + } + + Ihandle *filedlg = IupFileDlg(); + IupSetAttribute(filedlg, "DIALOGTYPE", "OPEN"); + IupSetAttribute(filedlg, "TITLE", lua_config_get_string("label", "import_title")); + IupSetAttribute(filedlg, "FILTER", "json"); + IupSetAttribute(filedlg, "EXTFILTER", "JSON 文件 (*.json)|*.json|文本文件 (*.txt)|*.txt|所有文件 (*.*)|*.*"); + + IupPopup(filedlg, IUP_CENTER, IUP_CENTER); + + if (IupGetInt(filedlg, "STATUS") != -1) + { + char *filepath = IupGetAttribute(filedlg, "VALUE"); + if (filepath) + { + ExportData imported; + ErrorCode import_result = import_paths_from_file(filepath, &imported); + if (import_result == ERR_OK) + { + int has_system = imported.system.count > 0; + int has_user = imported.user.count > 0; + + if (!has_system && !has_user) + { + IupMessage("错误", "文件中没有找到有效的路径!"); + return IUP_DEFAULT; + } + + int choice = 0; + if (has_system && has_user) + { + choice = IupAlarm("导入选项", "请选择导入目标:", + "仅系统变量", "仅用户变量", "全部导入"); + } + else if (has_system) + { + choice = 3; + } + else + { + choice = 2; + } + + int total_imported = 0; + + if (choice == 1 || choice == 3) + { + clear_string_list(&ctx->sys_paths); + for (int i = 0; i < imported.system.count; i++) + { + add_string_list(&ctx->sys_paths, imported.system.items[i]); + } + Ihandle *list_sys = IupGetDialogChild(dlg, "LIST_SYS"); + sync_string_list_to_ui(list_sys, &ctx->sys_paths); + total_imported += imported.system.count; + } + + if (choice == 2 || choice == 3) + { + clear_string_list(&ctx->user_paths); + for (int i = 0; i < imported.user.count; i++) + { + add_string_list(&ctx->user_paths, imported.user.items[i]); + } + Ihandle *list_user = IupGetDialogChild(dlg, "LIST_USER"); + sync_string_list_to_ui(list_user, &ctx->user_paths); + total_imported += imported.user.count; + } + + char msg[256]; + snprintf(msg, sizeof(msg), "成功导入 %d 个路径!", total_imported); + IupMessage("导入成功", msg); + + Ihandle *lbl_status = IupGetDialogChild(dlg, "LBL_STATUS"); + if (lbl_status) + IupSetAttribute(lbl_status, "TITLE", lua_config_get_string("status", "loaded")); + } + else + { + log_error("Import failed: error code %d", import_result); + IupMessage("错误", "导入失败,请检查文件格式是否正确!"); + } + } + } + IupDestroy(filedlg); + return IUP_DEFAULT; +} + +// 按钮回调:导出 +int btn_export_cb(Ihandle *self) +{ + Ihandle *dlg = IupGetDialog(self); + AppContext *ctx = get_app_context(dlg); + if (!ctx) + return IUP_DEFAULT; + + ExportData data; + data.system = ctx->sys_paths; + data.user = ctx->user_paths; + + Ihandle *filedlg = IupFileDlg(); + IupSetAttribute(filedlg, "DIALOGTYPE", "SAVE"); + IupSetAttribute(filedlg, "TITLE", lua_config_get_string("label", "export_title")); + IupSetAttribute(filedlg, "FILTER", "json"); + IupSetAttribute(filedlg, "EXTFILTER", "JSON 文件 (*.json)|*.json"); + IupSetAttribute(filedlg, "DEFAULTEXT", "json"); + + char default_name[64]; + snprintf(default_name, sizeof(default_name), "path_all.json"); + IupSetAttribute(filedlg, "VALUE", default_name); + + IupPopup(filedlg, IUP_CENTER, IUP_CENTER); + + if (IupGetInt(filedlg, "STATUS") != -1) + { + char *filepath = IupGetAttribute(filedlg, "VALUE"); + if (filepath) + { + char final_path[MAX_PATH]; + if (strchr(filepath, '.') == NULL) + { + snprintf(final_path, sizeof(final_path), "%s.json", filepath); + } + else + { + safe_strcpy(final_path, sizeof(final_path), filepath); + } + filepath = final_path; + + ErrorCode export_result = export_paths_to_file(&data, filepath); + if (export_result == ERR_OK) + { + char msg[512]; + snprintf(msg, sizeof(msg), "成功导出!\n系统变量: %d 个\n用户变量: %d 个\n\n保存位置: %s", + data.system.count, data.user.count, filepath); + IupMessage("导出成功", msg); + } + else + { + log_error("Export failed: error code %d", export_result); + IupMessage("错误", "导出失败!"); + } + } + } + IupDestroy(filedlg); + return IUP_DEFAULT; +} + +// 载入所有路径 +void load_all_paths(void) +{ + Ihandle *dlg = get_main_dlg(); + if (!dlg) + return; + AppContext *ctx = get_app_context(dlg); + if (!ctx) + return; + + if (load_system_paths(&ctx->sys_paths) != ERR_OK) + { + log_error("Failed to load system paths"); + IupMessage("错误", "无法打开系统环境变量注册表键,请尝试以管理员身份运行。"); + } + else + { + log_info("Loaded system paths: %d", ctx->sys_paths.count); + } + + ErrorCode user_result = load_user_paths(&ctx->user_paths); + if (user_result == ERR_OK) + { + log_info("Loaded user paths: %d", ctx->user_paths.count); + } + + Ihandle *list_sys = IupGetDialogChild(dlg, "LIST_SYS"); + Ihandle *list_user = IupGetDialogChild(dlg, "LIST_USER"); + + sync_string_list_to_ui(list_sys, &ctx->sys_paths); + sync_string_list_to_ui(list_user, &ctx->user_paths); + + Ihandle *lbl_status = IupGetDialogChild(dlg, "LBL_STATUS"); + if (lbl_status) + IupSetAttribute(lbl_status, "TITLE", lua_config_get_string("status", "loaded")); +} + +// 按钮回调:帮助 +int btn_help_cb(Ihandle *self) +{ + IupMessage("使用说明", + "1. 本程序用于编辑系统环境变量 PATH。\n" + "2. 必须以【管理员身份】运行才能保存更改。\n" + "3. 操作说明:\n" + " - 新建:添加新路径到列表末尾。\n" + " - 编辑:修改选中的路径。\n" + " - 浏览:从文件系统选择目录添加。\n" + " - 删除:移除选中的路径。\n" + " - 上移/下移:调整路径优先级。\n" + " - 导入/导出:备份和恢复 PATH 配置。\n" + "4. 点击【确定】保存更改并生效。\n" + "5. 注意:某些正在运行的程序可能需要重启才能识别新的环境变量。\n\n" + "--------------------------------------------------\n" + "作者:LHY\n" + "邮箱:3364451258@qq.com\n" + "GitHub:https://github.com/LHY0125/PathEditor\n" + "记得给我的项目点个star!"); + + return IUP_DEFAULT; +} \ No newline at end of file diff --git a/src/core/app_context.c b/src/core/app_context.c new file mode 100644 index 0000000..d2f0bd3 --- /dev/null +++ b/src/core/app_context.c @@ -0,0 +1,36 @@ +#include "core/app_context.h" +#include + +// 创建应用上下文 +AppContext *create_app_context(void) +{ + AppContext *ctx = (AppContext *)malloc(sizeof(AppContext)); + if (ctx) + { + init_string_list(&ctx->sys_paths); + init_string_list(&ctx->user_paths); + } + return ctx; +} + +// 销毁应用上下文 +void destroy_app_context(AppContext *ctx) +{ + if (ctx) + { + clear_string_list(&ctx->sys_paths); + clear_string_list(&ctx->user_paths); + free(ctx); + } +} + +// 获取应用上下文 +AppContext *get_app_context(Ihandle *ih) +{ + if (!ih) + return NULL; + Ihandle *dlg = IupGetDialog(ih); + if (!dlg) + return NULL; + return (AppContext *)IupGetAttribute(dlg, "APP_CONTEXT"); +} \ No newline at end of file diff --git a/src/core/import_export.c b/src/core/import_export.c new file mode 100644 index 0000000..7770b21 --- /dev/null +++ b/src/core/import_export.c @@ -0,0 +1,312 @@ +#include "core/import_export.h" +#include "utils/os_env.h" +#include "utils/error_code.h" +#include "utils/logger.h" +#include +#include +#include +#include + +// 获取当前日期时间 +static void get_current_datetime(char *buffer, int size) +{ + time_t now = time(NULL); + struct tm *tm_info = localtime(&now); + strftime(buffer, size, "%Y-%m-%d %H:%M:%S", tm_info); +} + +// 转义 JSON 字符串中的特殊字符 +static char *escape_json_string(const char *str) +{ + if (!str) + return NULL; + + int len = strlen(str); + char *result = (char *)malloc(len * 2 + 1); + if (!result) + return NULL; + + char *p = result; + for (int i = 0; i < len; i++) + { + switch (str[i]) + { + case '\\': + *p++ = '\\'; + *p++ = '\\'; + break; + case '"': + *p++ = '\\'; + *p++ = '"'; + break; + case '\n': + *p++ = '\\'; + *p++ = 'n'; + break; + case '\r': + *p++ = '\\'; + *p++ = 'r'; + break; + case '\t': + *p++ = '\\'; + *p++ = 't'; + break; + default: + *p++ = str[i]; + break; + } + } + *p = '\0'; + return result; +} + +// 导出路径数据到 JSON 文件 +ErrorCode export_paths_to_file(const ExportData *data, const char *filepath) +{ + if (!data || !filepath) + return ERR_NULL_PTR; + + FILE *fp = fopen(filepath, "w"); + if (!fp) + { + log_error("Failed to open file for export: %s", filepath); + return ERR_FILE_NOT_FOUND; + } + + fprintf(fp, "\xEF\xBB\xBF"); + + char datetime[64]; + get_current_datetime(datetime, sizeof(datetime)); + + fprintf(fp, "{\n"); + fprintf(fp, " \"version\": \"%s\",\n", EXPORT_VERSION); + fprintf(fp, " \"type\": \"ALL\",\n"); + fprintf(fp, " \"exported\": \"%s\",\n", datetime); + + fprintf(fp, " \"system\": [\n"); + for (int i = 0; i < data->system.count; i++) + { + if (data->system.items[i]) + { + char *escaped = escape_json_string(data->system.items[i]); + if (escaped) + { + fprintf(fp, " \"%s\"%s\n", escaped, (i < data->system.count - 1) ? "," : ""); + free(escaped); + } + } + } + fprintf(fp, " ],\n"); + + fprintf(fp, " \"user\": [\n"); + for (int i = 0; i < data->user.count; i++) + { + if (data->user.items[i]) + { + char *escaped = escape_json_string(data->user.items[i]); + if (escaped) + { + fprintf(fp, " \"%s\"%s\n", escaped, (i < data->user.count - 1) ? "," : ""); + free(escaped); + } + } + } + fprintf(fp, " ]\n"); + + fprintf(fp, "}\n"); + + fclose(fp); + log_info("Exported paths to file: sys=%d, user=%d, file=%s", + data->system.count, data->user.count, filepath); + return ERR_OK; +} + +// 移除字符串首尾的空格、制表符、换行符和回车符 +static void trim_whitespace(char *str) +{ + char *start = str; + while (*start == ' ' || *start == '\t') + start++; + + char *end = str + strlen(str) - 1; + while (end > start && (*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r')) + *end-- = '\0'; + + if (start != str) + memmove(str, start, strlen(start) + 1); +} + +// 检查字符串是否为注释行或空行 +static int is_comment_or_empty(const char *line) +{ + while (*line == ' ' || *line == '\t') + line++; + + if (*line == '#' || *line == '\0') + return 1; + + return 0; +} + +// 检查文件是否为 JSON 格式 +static int is_json_file(const char *filepath) +{ + const char *ext = strrchr(filepath, '.'); + return ext && strcasecmp(ext, ".json") == 0; +} + +// 从文件导入 PATH +ErrorCode import_paths_from_file(const char *filepath, ExportData *data) +{ + if (!filepath || !data) + return ERR_NULL_PTR; + + init_string_list(&data->system); + init_string_list(&data->user); + + if (!is_json_file(filepath)) + { + FILE *fp = fopen(filepath, "rb"); + if (!fp) + { + log_error("Failed to open file for import: %s", filepath); + return ERR_FILE_NOT_FOUND; + } + + StringList list; + init_string_list(&list); + + char line[4096]; + while (fgets(line, sizeof(line), fp)) + { + trim_whitespace(line); + if (is_comment_or_empty(line)) + continue; + add_string_list(&list, line); + } + + fclose(fp); + + data->system = list; + log_info("Imported paths from TXT file: %d paths, file=%s", list.count, filepath); + return ERR_OK; + } + + FILE *fp = fopen(filepath, "rb"); + if (!fp) + { + log_error("Failed to open file for import: %s", filepath); + return ERR_FILE_NOT_FOUND; + } + + char buffer[8192]; + int in_system = 0; + int in_user = 0; + int depth = 0; + int in_string = 0; + char path_buffer[4096]; + int path_len = 0; + + while (fgets(buffer, sizeof(buffer), fp)) + { + char *p = buffer; + while (*p) + { + if (*p == '"' && (p == buffer || *(p - 1) != '\\')) + { + in_string = !in_string; + } + else if (in_string && *p == '\\') + { + p++; + if (*p) + { + if (*p == 'n') + *p = '\n'; + else if (*p == 'r') + *p = '\r'; + else if (*p == 't') + *p = '\t'; + } + } + else if (!in_string) + { + if (*p == '{' || *p == '[') + depth++; + else if (*p == '}' || *p == ']') + depth--; + else if (depth == 1 && *p == '"') + { + if (strncmp(p, "\"system\"", 8) == 0) + { + in_system = 1; + in_user = 0; + } + else if (strncmp(p, "\"user\"", 6) == 0) + { + in_user = 1; + in_system = 0; + } + } + else if (in_system && depth == 2 && *p == '"') + { + path_len = 0; + p++; + while (*p && path_len < (int)sizeof(path_buffer) - 1) + { + if (*p == '"' && *(p - 1) != '\\') + break; + if (*p == '\\' && *(p + 1)) + { + p++; + if (*p == 'n') + *p = '\n'; + else if (*p == 'r') + *p = '\r'; + else if (*p == 't') + *p = '\t'; + } + path_buffer[path_len++] = *p++; + } + if (path_len > 0) + { + path_buffer[path_len] = '\0'; + add_string_list(&data->system, path_buffer); + } + } + else if (in_user && depth == 2 && *p == '"') + { + path_len = 0; + p++; + while (*p && path_len < (int)sizeof(path_buffer) - 1) + { + if (*p == '"' && *(p - 1) != '\\') + break; + if (*p == '\\' && *(p + 1)) + { + p++; + if (*p == 'n') + *p = '\n'; + else if (*p == 'r') + *p = '\r'; + else if (*p == 't') + *p = '\t'; + } + path_buffer[path_len++] = *p++; + } + if (path_len > 0) + { + path_buffer[path_len] = '\0'; + add_string_list(&data->user, path_buffer); + } + } + } + p++; + } + } + + fclose(fp); + log_info("Imported paths from JSON file: sys=%d, user=%d, file=%s", + data->system.count, data->user.count, filepath); + return ERR_OK; +} \ No newline at end of file diff --git a/src/core/lua_config.c b/src/core/lua_config.c new file mode 100644 index 0000000..ed4ac69 --- /dev/null +++ b/src/core/lua_config.c @@ -0,0 +1,247 @@ +#include "core/lua_config.h" +#include +#include +#include +#include +#include +#include + +static lua_State *G_L = NULL; +static int G_loaded = 0; +static const char *G_config_path = "lua/config.lua"; + +static const char *get_string_default(const char *section, const char *key) +{ + if (strcmp(section, "app") == 0) + { + if (strcmp(key, "name") == 0) + return "PathEditor"; + if (strcmp(key, "name_readonly") == 0) + return "PathEditor (只读模式)"; + } + else if (strcmp(section, "dialog") == 0) + { + if (strcmp(key, "size") == 0) + return "800x800"; + if (strcmp(key, "minsize") == 0) + return "800x800"; + if (strcmp(key, "select_dir") == 0) + return "选择目录"; + } + else if (strcmp(section, "list") == 0) + { + if (strcmp(key, "item_padding") == 0) + return "5x5"; + if (strcmp(key, "backcolor") == 0) + return "255 255 255"; + } + else if (strcmp(section, "button") == 0) + { + if (strcmp(key, "rastersize") == 0) + return "100x32"; + if (strcmp(key, "new") == 0) + return "新建(N)"; + if (strcmp(key, "edit") == 0) + return "编辑(E)"; + if (strcmp(key, "browse") == 0) + return "浏览(B)..."; + if (strcmp(key, "del") == 0) + return "删除(D)"; + if (strcmp(key, "up") == 0) + return "上移(U)"; + if (strcmp(key, "down") == 0) + return "下移(O)"; + if (strcmp(key, "clean") == 0) + return "一键清理"; + if (strcmp(key, "import") == 0) + return "导入(I)"; + if (strcmp(key, "export") == 0) + return "导出(E)"; + if (strcmp(key, "ok") == 0) + return "确定"; + if (strcmp(key, "cancel") == 0) + return "取消"; + if (strcmp(key, "help") == 0) + return "帮助(?)"; + } + else if (strcmp(section, "label") == 0) + { + if (strcmp(key, "title") == 0) + return "环境变量编辑器:"; + if (strcmp(key, "search_placeholder") == 0) + return "输入关键词搜索..."; + if (strcmp(key, "tab_sys") == 0) + return "系统变量 (System)"; + if (strcmp(key, "tab_user") == 0) + return "用户变量 (User)"; + if (strcmp(key, "export_title") == 0) + return "导出 PATH"; + if (strcmp(key, "import_title") == 0) + return "导入 PATH"; + } + else if (strcmp(section, "layout") == 0) + { + if (strcmp(key, "vbox_gap") == 0) + return "5"; + if (strcmp(key, "vbox_margin") == 0) + return "0x0"; + if (strcmp(key, "vbox_all_margin") == 0) + return "10x10"; + if (strcmp(key, "vbox_all_gap") == 0) + return "5"; + if (strcmp(key, "hbox_gap") == 0) + return "10"; + if (strcmp(key, "hbox_margin") == 0) + return "10x10"; + if (strcmp(key, "hbox_alignment") == 0) + return "ACENTER"; + } + else if (strcmp(section, "status") == 0) + { + if (strcmp(key, "normal") == 0) + return "状态: 就绪"; + if (strcmp(key, "readonly") == 0) + return "状态: ⚠️ 只读模式 (无管理员权限)"; + if (strcmp(key, "saving") == 0) + return "状态: 保存中..."; + if (strcmp(key, "saved") == 0) + return "状态: ✓ 保存成功"; + if (strcmp(key, "error") == 0) + return "状态: ✗ 保存失败"; + if (strcmp(key, "deleted") == 0) + return "状态: 已删除选中项"; + if (strcmp(key, "loaded") == 0) + return "状态: 已加载系统和用户变量"; + if (strcmp(key, "drag_folder_only") == 0) + return "提示: 只能拖拽文件夹添加到 PATH"; + if (strcmp(key, "admin_warning") == 0) + return "未检测到管理员权限,只能查看和导出 PATH,无法保存更改。"; + } + return ""; +} + +int lua_config_init(void) +{ + if (G_L != NULL) + { + return 0; + } + + G_L = luaL_newstate(); + if (G_L == NULL) + { + return -1; + } + + luaL_openlibs(G_L); + + if (luaL_dofile(G_L, G_config_path) != LUA_OK) + { + const char *err = lua_tostring(G_L, -1); + if (err) + { + fprintf(stderr, "[Lua Config] 加载配置文件失败: %s\n", err); + } + lua_settop(G_L, 0); + G_loaded = 0; + return 0; + } + + lua_settop(G_L, 0); + G_loaded = 1; + return 0; +} + +void lua_config_destroy(void) +{ + if (G_L != NULL) + { + lua_close(G_L); + G_L = NULL; + } + G_loaded = 0; +} + +const char *lua_config_get_string(const char *section, const char *key) +{ + if (G_L == NULL || section == NULL || key == NULL) + { + return get_string_default(section, key); + } + + lua_getglobal(G_L, "config"); + if (!lua_istable(G_L, -1)) + { + lua_settop(G_L, 0); + return get_string_default(section, key); + } + + lua_getfield(G_L, -1, section); + if (!lua_istable(G_L, -1)) + { + lua_settop(G_L, 0); + return get_string_default(section, key); + } + + lua_getfield(G_L, -1, key); + if (!lua_isstring(G_L, -1)) + { + lua_settop(G_L, 0); + return get_string_default(section, key); + } + + const char *value = lua_tostring(G_L, -1); + lua_settop(G_L, 0); + + return value ? value : get_string_default(section, key); +} + +int lua_config_get_int(const char *section, const char *key, int default_value) +{ + if (G_L == NULL || section == NULL || key == NULL) + { + return default_value; + } + + lua_getglobal(G_L, "config"); + if (!lua_istable(G_L, -1)) + { + lua_settop(G_L, 0); + return default_value; + } + + lua_getfield(G_L, -1, section); + if (!lua_istable(G_L, -1)) + { + lua_settop(G_L, 0); + return default_value; + } + + lua_getfield(G_L, -1, key); + if (!lua_isnumber(G_L, -1)) + { + lua_settop(G_L, 0); + return default_value; + } + + int value = (int)lua_tointeger(G_L, -1); + lua_settop(G_L, 0); + + return value; +} + +int lua_config_reload(void) +{ + if (G_L != NULL) + { + lua_close(G_L); + G_L = NULL; + } + + return lua_config_init(); +} + +int lua_config_is_loaded(void) +{ + return G_loaded; +} \ No newline at end of file diff --git a/src/core/path_manager.c b/src/core/path_manager.c new file mode 100644 index 0000000..abaddd3 --- /dev/null +++ b/src/core/path_manager.c @@ -0,0 +1,89 @@ +#include "core/path_manager.h" +#include "utils/os_env.h" +#include "utils/error_code.h" +#include "utils/logger.h" +#include +#include + +// 删除指定索引的路径项 +ErrorCode path_manager_remove_at(StringList *list, int index) +{ + if (!list || index < 0 || index >= list->count) + return ERR_NULL_PTR; + + free(list->items[index]); + for (int i = index; i < list->count - 1; i++) + { + list->items[i] = list->items[i + 1]; + } + list->items[list->count - 1] = NULL; + list->count--; + return ERR_OK; +} + +// 向上移动路径项 +ErrorCode path_manager_move_up(StringList *list, int index) +{ + if (!list || index <= 0 || index >= list->count) + return ERR_NULL_PTR; + + char *temp = list->items[index]; + list->items[index] = list->items[index - 1]; + list->items[index - 1] = temp; + return ERR_OK; +} + +// 向下移动路径项 +ErrorCode path_manager_move_down(StringList *list, int index) +{ + if (!list || index < 0 || index >= list->count - 1) + return ERR_NULL_PTR; + + char *temp = list->items[index]; + list->items[index] = list->items[index + 1]; + list->items[index + 1] = temp; + return ERR_OK; +} + +// 清理无效路径项 +ErrorCode path_manager_clean(StringList *list) +{ + if (!list) return ERR_NULL_PTR; + + int removed_count = 0; + + for (int i = list->count - 1; i >= 0; i--) + { + char *item = list->items[i]; + if (!item) continue; + + int should_remove = 0; + + if (!is_path_valid(item)) + { + should_remove = 1; + } + else + { + for (int j = 0; j < i; j++) + { + char *prev_item = list->items[j]; + if (prev_item && _stricmp(item, prev_item) == 0) + { + should_remove = 1; + break; + } + } + } + + if (should_remove) + { + path_manager_remove_at(list, i); + removed_count++; + } + } + + log_info("Cleaned paths: removed %d invalid/duplicate paths, remaining %d", + removed_count, list->count); + return ERR_OK; +} \ No newline at end of file diff --git a/src/core/registry_service.c b/src/core/registry_service.c new file mode 100644 index 0000000..96f7b5a --- /dev/null +++ b/src/core/registry_service.c @@ -0,0 +1,146 @@ +#include "core/registry_service.h" +#include "utils/string_ext.h" +#include "utils/error_code.h" +#include +#include +#include + +#define REG_PATH_SYS L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment" +#define REG_PATH_USER L"Environment" +#define REG_VALUE L"Path" + +// 内部辅助函数:加载单个列表 +static ErrorCode load_single_path(HKEY hKeyRoot, const wchar_t *regPath, StringList *list) +{ + clear_string_list(list); + + HKEY hKey; + LONG res = RegOpenKeyExW(hKeyRoot, regPath, 0, KEY_READ, &hKey); + if (res != ERROR_SUCCESS) + { + return ERR_REGISTRY_FAILED; + } + + DWORD type, size; + res = RegQueryValueExW(hKey, REG_VALUE, NULL, &type, NULL, &size); + if (res == ERROR_SUCCESS) + { + wchar_t *buffer = (wchar_t *)malloc(size + 2); + if (buffer) + { + memset(buffer, 0, size + 2); + if (RegQueryValueExW(hKey, REG_VALUE, NULL, &type, (LPBYTE)buffer, &size) == ERROR_SUCCESS) + { + wchar_t *current = buffer; + wchar_t *next_semicolon = NULL; + + while (*current) + { + next_semicolon = wcschr(current, L';'); + if (next_semicolon) + *next_semicolon = L'\0'; + + if (wcslen(current) > 0) + { + char *utf8_str = wide_to_utf8(current); + if (utf8_str) + { + add_string_list(list, utf8_str); + free(utf8_str); + } + } + + if (next_semicolon) + current = next_semicolon + 1; + else + break; + } + } + free(buffer); + } + } + RegCloseKey(hKey); + return ERR_OK; +} + +// 加载系统环境变量路径 +ErrorCode load_system_paths(StringList *list) +{ + return load_single_path(HKEY_LOCAL_MACHINE, REG_PATH_SYS, list); +} + +// 加载用户环境变量路径 +ErrorCode load_user_paths(StringList *list) +{ + return load_single_path(HKEY_CURRENT_USER, REG_PATH_USER, list); +} + +// 内部辅助函数:保存单个列表 +static ErrorCode save_single_path(HKEY hKeyRoot, const wchar_t *regPath, const StringList *list) +{ + if (!list) + return ERR_NULL_PTR; + + // 计算大小 + size_t total_len = 0; + for (int i = 0; i < list->count; i++) + { + if (list->items[i]) + { + wchar_t *witem = utf8_to_wide(list->items[i]); + if (witem) + { + total_len += wcslen(witem) + 1; + free(witem); + } + } + } + total_len += 1; + + wchar_t *buffer = (wchar_t *)malloc(total_len * sizeof(wchar_t)); + if (!buffer) + return ERR_OUT_OF_MEMORY; + + buffer[0] = L'\0'; + for (int i = 0; i < list->count; i++) + { + if (list->items[i]) + { + wchar_t *witem = utf8_to_wide(list->items[i]); + if (witem) + { + wcscat(buffer, witem); + if (i < list->count - 1) + wcscat(buffer, L";"); + free(witem); + } + } + } + + HKEY hKey; + ErrorCode result = ERR_PERMISSION_DENIED; + if (RegOpenKeyExW(hKeyRoot, regPath, 0, KEY_WRITE, &hKey) == ERROR_SUCCESS) + { + DWORD size = (DWORD)((wcslen(buffer) + 1) * sizeof(wchar_t)); + if (RegSetValueExW(hKey, REG_VALUE, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size) == ERROR_SUCCESS) + { + result = ERR_OK; + } + RegCloseKey(hKey); + } + + free(buffer); + return result; +} + +// 保存系统环境变量路径 +ErrorCode save_system_paths(const StringList *list) +{ + return save_single_path(HKEY_LOCAL_MACHINE, REG_PATH_SYS, list); +} + +// 保存用户环境变量路径 +ErrorCode save_user_paths(const StringList *list) +{ + return save_single_path(HKEY_CURRENT_USER, REG_PATH_USER, list); +} \ No newline at end of file diff --git a/src/main.c b/src/main.c index 77ef73c..65a635e 100644 --- a/src/main.c +++ b/src/main.c @@ -3,128 +3,147 @@ #include #include #include -#include "globals.h" -#include "utils.h" -#include "registry.h" -#include "callbacks.h" +#include "core/app_context.h" +#include "core/lua_config.h" +#include "utils/string_ext.h" +#include "utils/os_env.h" +#include "utils/logger.h" +#include "controller/callbacks.h" +#include "ui/main_window.h" -// 全局控件定义 -Ihandle *dlg, *list_path, *lbl_status; -Ihandle *btn_new, *btn_edit, *btn_browse, *btn_del, *btn_up, *btn_down; -Ihandle *btn_ok, *btn_cancel, *btn_help; +/* +!编译命令: +cmake -B build -G "MinGW Makefiles" +cmake --build build +!打包命令: +build_installer.bat +!运行命令: +powershell -Command "Start-Process 'build\\PathEditor.exe' -Verb RunAs" +*/ + +// 定义 Windows 消息常量 +#ifndef WM_COPYGLOBALDATA +#define WM_COPYGLOBALDATA 0x0049 +#endif + +// 消息过滤器常量 +#ifndef MSGFLT_ADD +#define MSGFLT_ADD 1 +#endif // 主函数 int main(int argc, char **argv) { + // 初始化日志系统 + log_init(NULL, LOG_LEVEL_INFO); + log_info("PathEditor starting..."); + // 强制设置 UTF8MODE 环境变量,必须在 IupOpen 之前 putenv("IUP_UTF8MODE=YES"); IupOpen(&argc, &argv); IupSetGlobal("UTF8MODE", "YES"); - // 创建列表控件 - list_path = IupFlatList(); - IupSetAttribute(list_path, "EXPAND", "YES"); - IupSetAttribute(list_path, "ITEMPADDING", "5x5"); - IupSetAttribute(list_path, "BACKCOLOR", "255 255 255"); - IupSetAttribute(list_path, "BORDER", "YES"); - IupSetAttribute(list_path, "CANFOCUS", "YES"); - IupSetAttribute(list_path, "HLINE", "NO"); // 禁用横线,使用斑马纹 - // IupFlatList 不支持 VISIBLELINES,高度由 EXPAND 和布局决定 + if (lua_config_init() != 0) + { + IupMessage("警告", "Lua 配置系统初始化失败,将使用默认值"); + } - // 创建右侧按钮 - btn_new = IupButton("新建(N)", NULL); - btn_edit = IupButton("编辑(E)", NULL); - btn_browse = IupButton("浏览(B)...", NULL); - btn_del = IupButton("删除(D)", NULL); - btn_up = IupButton("上移(U)", NULL); - btn_down = IupButton("下移(O)", NULL); + log_info("Lua config initialized"); - // 设置按钮回调 - IupSetCallback(btn_new, "ACTION", (Icallback)btn_new_cb); - IupSetCallback(btn_edit, "ACTION", (Icallback)btn_edit_cb); - IupSetCallback(btn_browse, "ACTION", (Icallback)btn_browse_cb); - IupSetCallback(btn_del, "ACTION", (Icallback)btn_del_cb); - IupSetCallback(btn_up, "ACTION", (Icallback)btn_up_cb); - IupSetCallback(btn_down, "ACTION", (Icallback)btn_down_cb); + // 在管理员模式下,解决无法拖拽文件到列表框的问题 (UIPI) + // 需要加载 User32.dll 获取 ChangeWindowMessageFilter 函数 + HMODULE hUser32 = LoadLibraryW(L"user32.dll"); + if (hUser32) + { + typedef BOOL(WINAPI * ChangeWindowMessageFilterProc)(UINT, DWORD); + ChangeWindowMessageFilterProc pChangeWindowMessageFilter = + (ChangeWindowMessageFilterProc)GetProcAddress(hUser32, "ChangeWindowMessageFilter"); - // 设置双击回调 - IupSetCallback(list_path, "DBLCLICK_CB", (Icallback)list_dblclick_cb); + if (pChangeWindowMessageFilter) + { + pChangeWindowMessageFilter(WM_DROPFILES, MSGFLT_ADD); + pChangeWindowMessageFilter(WM_COPYDATA, MSGFLT_ADD); + pChangeWindowMessageFilter(WM_COPYGLOBALDATA, MSGFLT_ADD); + } + FreeLibrary(hUser32); + } - // 设置按钮大小 (宽度和高度都增加约1/4) - IupSetAttribute(btn_new, "RASTERSIZE", "100x32"); - IupSetAttribute(btn_edit, "RASTERSIZE", "100x32"); - IupSetAttribute(btn_browse, "RASTERSIZE", "100x32"); - IupSetAttribute(btn_del, "RASTERSIZE", "100x32"); - IupSetAttribute(btn_up, "RASTERSIZE", "100x32"); - IupSetAttribute(btn_down, "RASTERSIZE", "100x32"); + // 禁用默认的全局按键处理 + IupSetGlobal("INPUTCALLBACKS", "NO"); - Ihandle *vbox_btns = IupVbox( - btn_new, btn_edit, btn_browse, btn_del, - IupFill(), // 间隔 - btn_up, btn_down, - NULL); - IupSetAttribute(vbox_btns, "GAP", "5"); - IupSetAttribute(vbox_btns, "MARGIN", "0x0"); + // 创建应用上下文 + AppContext *ctx = create_app_context(); + if (!ctx) + { + IupMessage("错误", "无法分配内存创建应用上下文"); + IupClose(); + return 1; + } - // 上部布局:列表 + 按钮 - Ihandle *hbox_main = IupHbox(list_path, vbox_btns, NULL); - IupSetAttribute(hbox_main, "GAP", "10"); - IupSetAttribute(hbox_main, "MARGIN", "10x10"); + Ihandle *dlg = create_main_window(); - // 状态标签 - lbl_status = IupLabel("状态: 就绪"); - IupSetAttribute(lbl_status, "EXPAND", "HORIZONTAL"); + // 绑定上下文到对话框 + IupSetAttribute(dlg, "APP_CONTEXT", (char *)ctx); + // 注册主窗口句柄,方便其他地方获取 + IupSetHandle("MAIN_DIALOG", dlg); - // 底部按钮 - btn_ok = IupButton("确定", NULL); - btn_cancel = IupButton("取消", NULL); - btn_help = IupButton("帮助(?)", NULL); - - IupSetCallback(btn_ok, "ACTION", (Icallback)btn_ok_cb); - IupSetCallback(btn_cancel, "ACTION", (Icallback)btn_cancel_cb); - IupSetCallback(btn_help, "ACTION", (Icallback)btn_help_cb); - - IupSetAttribute(btn_ok, "RASTERSIZE", "100x32"); - IupSetAttribute(btn_cancel, "RASTERSIZE", "100x32"); - IupSetAttribute(btn_help, "RASTERSIZE", "100x32"); - - Ihandle *hbox_bottom = IupHbox(lbl_status, IupFill(), btn_help, btn_ok, btn_cancel, NULL); - IupSetAttribute(hbox_bottom, "GAP", "10"); - IupSetAttribute(hbox_bottom, "MARGIN", "10x10"); - IupSetAttribute(hbox_bottom, "ALIGNMENT", "ACENTER"); - - // 总体布局 - Ihandle *vbox_all = IupVbox( - IupLabel("系统变量 Path:"), - hbox_main, - hbox_bottom, - NULL); - IupSetAttribute(vbox_all, "MARGIN", "10x10"); - IupSetAttribute(vbox_all, "GAP", "5"); - - // 创建对话框 - dlg = IupDialog(vbox_all); - IupSetAttribute(dlg, "TITLE", "编辑环境变量 (IUP版)"); - IupSetAttribute(dlg, "SIZE", "450x350"); - IupSetAttribute(dlg, "MINBOX", "NO"); - IupSetAttribute(dlg, "MAXBOX", "NO"); - - // 加载数据 + // 检查管理员权限 if (!check_admin()) { - IupMessage("警告", "程序未以管理员身份运行,您只能查看,无法保存更改!"); - IupSetAttribute(dlg, "TITLE", "编辑环境变量 (只读模式)"); - IupSetAttribute(lbl_status, "TITLE", "状态: 只读模式 (权限不足)"); + IupMessage("警告", lua_config_get_string("status", "admin_warning")); + + Ihandle *lbl_status = IupGetDialogChild(dlg, "LBL_STATUS"); + if (lbl_status) + IupSetAttribute(lbl_status, "TITLE", lua_config_get_string("status", "readonly")); + + Ihandle *btn_new = IupGetDialogChild(dlg, "BTN_NEW"); + Ihandle *btn_edit = IupGetDialogChild(dlg, "BTN_EDIT"); + Ihandle *btn_browse = IupGetDialogChild(dlg, "BTN_BROWSE"); + Ihandle *btn_del = IupGetDialogChild(dlg, "BTN_DEL"); + Ihandle *btn_up = IupGetDialogChild(dlg, "BTN_UP"); + Ihandle *btn_down = IupGetDialogChild(dlg, "BTN_DOWN"); + Ihandle *btn_clean = IupGetDialogChild(dlg, "BTN_CLEAN"); + Ihandle *btn_ok = IupGetDialogChild(dlg, "BTN_OK"); + + if (btn_new) + IupSetAttribute(btn_new, "ACTIVE", "NO"); + if (btn_edit) + IupSetAttribute(btn_edit, "ACTIVE", "NO"); + if (btn_browse) + IupSetAttribute(btn_browse, "ACTIVE", "NO"); + if (btn_del) + IupSetAttribute(btn_del, "ACTIVE", "NO"); + if (btn_up) + IupSetAttribute(btn_up, "ACTIVE", "NO"); + if (btn_down) + IupSetAttribute(btn_down, "ACTIVE", "NO"); + if (btn_clean) + IupSetAttribute(btn_clean, "ACTIVE", "NO"); + if (btn_ok) + IupSetAttribute(btn_ok, "ACTIVE", "NO"); + + Ihandle *btn_import = IupGetDialogChild(dlg, "BTN_IMPORT"); + Ihandle *btn_export = IupGetDialogChild(dlg, "BTN_EXPORT"); + if (btn_import) + IupSetAttribute(btn_import, "ACTIVE", "NO"); + if (btn_export) + IupSetAttribute(btn_export, "ACTIVE", "NO"); } IupShowXY(dlg, IUP_CENTER, IUP_CENTER); // IUP List APPEND 属性需要在控件 Map 之后才能生效 // IupShowXY 会触发 Map - load_path(); + load_all_paths(); IupMainLoop(); + + log_info("PathEditor exiting..."); + destroy_app_context(ctx); + lua_config_destroy(); + log_destroy(); IupClose(); + return 0; } \ No newline at end of file diff --git a/src/registry.c b/src/registry.c deleted file mode 100644 index 1b9f6bb..0000000 --- a/src/registry.c +++ /dev/null @@ -1,195 +0,0 @@ -#include "registry.h" -#include "globals.h" -#include "utils.h" -#include -#include -#include -#include - -// 从注册表加载PATH -void load_path() -{ - HKEY hKey; - LONG res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, REG_PATH, 0, KEY_READ, &hKey); - if (res != ERROR_SUCCESS) - { - char msg[512]; - snprintf(msg, sizeof(msg), "无法打开注册表键 (HKLM)。\n路径: %ls\n错误码: %ld\n\n请尝试右键点击程序 -> '以管理员身份运行'。", REG_PATH, res); - IupMessage("错误", msg); - IupSetAttribute(lbl_status, "TITLE", "状态: 注册表读取失败"); - return; - } - - DWORD type, size; - res = RegQueryValueExW(hKey, REG_VALUE, NULL, &type, NULL, &size); - if (res == ERROR_SUCCESS) - { - // 安全分配内存:size 是字节数,多分配 2 个字节给 null 终止符 - wchar_t *buffer = (wchar_t *)malloc(size + 2); - if (!buffer) - { - IupMessage("错误", "内存分配失败!"); - RegCloseKey(hKey); - return; - } - - // 初始化内存 - memset(buffer, 0, size + 2); - - if (RegQueryValueExW(hKey, REG_VALUE, NULL, &type, (LPBYTE)buffer, &size) == ERROR_SUCCESS) - { - // 重新实现分割逻辑,避免 wcstok 的兼容性问题 - wchar_t *current = buffer; - wchar_t *next_semicolon = NULL; - int count = 0; - - // 清空列表 - IupSetAttribute(list_path, "REMOVEITEM", "ALL"); - - // 检查内容是否为空 - if (wcslen(buffer) == 0) - { - IupMessage("提示", "读取到的 PATH 变量为空!"); - } - - while (*current) - { - // 查找下一个分号 - next_semicolon = wcschr(current, L';'); - if (next_semicolon) - { - *next_semicolon = L'\0'; // 临时截断 - } - - // 跳过空项 - if (wcslen(current) > 0) - { - char *utf8_str = wide_to_utf8(current); - - count++; - IupSetAttributeId(list_path, "", count, utf8_str); - - free(utf8_str); - } - - if (next_semicolon) - { - current = next_semicolon + 1; - } - else - { - break; - } - } - - IupSetInt(list_path, "COUNT", count); // 显式设置列表项数量 - IupSetInt(list_path, "VALUE", 1); // 选中第一项 - - // 刷新斑马纹样式 - refresh_list_style(); - - char status_msg[100]; - sprintf(status_msg, "状态: 已加载 %d 个条目", count); - IupSetAttribute(lbl_status, "TITLE", status_msg); - } - else - { - IupMessage("错误", "读取 PATH 值内容失败!"); - IupSetAttribute(lbl_status, "TITLE", "状态: 读取值内容失败"); - } - free(buffer); - } - else - { - char msg[256]; - sprintf(msg, "查询 PATH 值大小失败。错误码: %ld", res); - IupMessage("错误", msg); - IupSetAttribute(lbl_status, "TITLE", "状态: 查询值失败"); - } - RegCloseKey(hKey); -} - -// 保存PATH到注册表 -void save_path() -{ - if (!check_admin()) - { - IupMessage("错误", "需要管理员权限才能保存更改!\n请重新以管理员身份运行程序。"); - return; - } - - int count = IupGetInt(list_path, "COUNT"); - if (count == 0) - { - // 警告:清空PATH是很危险的 - if (IupAlarm("警告", "PATH 为空!这可能导致系统命令无法使用。\n确定要保存吗?", "确定", "取消", NULL) != 1) - { - return; - } - } - - // 计算所需缓冲区大小 - size_t total_len = 0; - for (int i = 1; i <= count; i++) - { - char *item = IupGetAttributeId(list_path, "", i); - if (item) - { - wchar_t *witem = utf8_to_wide(item); - total_len += wcslen(witem) + 1; // +1 for ';' - free(witem); - } - } - total_len += 1; // null terminator - - wchar_t *buffer = (wchar_t *)malloc(total_len * sizeof(wchar_t)); - if (!buffer) - { - IupMessage("错误", "内存分配失败 (保存时)!"); - return; - } - buffer[0] = L'\0'; - - for (int i = 1; i <= count; i++) - { - char *item = IupGetAttributeId(list_path, "", i); - if (item) - { - wchar_t *witem = utf8_to_wide(item); - wcscat(buffer, witem); - if (i < count) - { - wcscat(buffer, L";"); - } - free(witem); - } - } - - // 写入注册表 - HKEY hKey; - if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, REG_PATH, 0, KEY_WRITE, &hKey) == ERROR_SUCCESS) - { - // 使用 REG_EXPAND_SZ 类型,因为 PATH 可能包含 %SystemRoot% - DWORD size = (wcslen(buffer) + 1) * sizeof(wchar_t); - if (RegSetValueExW(hKey, REG_VALUE, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size) == ERROR_SUCCESS) - { - // 发送系统广播通知环境变量已更改 - SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)L"Environment", SMTO_ABORTIFHUNG, 5000, NULL); - IupMessage("成功", "PATH 环境变量已更新!"); - IupSetAttribute(lbl_status, "TITLE", "状态: 保存成功"); - } - else - { - IupMessage("错误", "写入注册表失败!"); - IupSetAttribute(lbl_status, "TITLE", "状态: 保存失败"); - } - RegCloseKey(hKey); - } - else - { - IupMessage("错误", "无法打开注册表进行写入。请检查权限!"); - IupSetAttribute(lbl_status, "TITLE", "状态: 打开注册表失败"); - } - - free(buffer); -} \ No newline at end of file diff --git a/src/ui/dialogs.c b/src/ui/dialogs.c new file mode 100644 index 0000000..5846345 --- /dev/null +++ b/src/ui/dialogs.c @@ -0,0 +1,71 @@ +#include "ui/dialogs.h" +#include "core/lua_config.h" +#include "utils/safe_string.h" +#include +#include + +// 静态辅助函数:对话框确定 +static int on_dialog_ok(Ihandle *self) +{ + Ihandle *dlg = IupGetDialog(self); + IupSetAttribute(dlg, "MY_STATUS", "1"); + return IUP_CLOSE; +} + +// 静态辅助函数:对话框取消 +static int on_dialog_cancel(Ihandle *self) +{ + Ihandle *dlg = IupGetDialog(self); + IupSetAttribute(dlg, "MY_STATUS", "0"); + return IUP_CLOSE; +} + +// 真正的实现函数 +int custom_input_dialog(const char *title, const char *label_text, char *buffer, int buffer_size) +{ + Ihandle *text = IupText(NULL); + IupSetAttribute(text, "VALUE", buffer); + IupSetAttribute(text, "EXPAND", "HORIZONTAL"); + IupSetAttribute(text, "RASTERSIZE", "500x"); + IupSetAttribute(text, "NAME", "INPUT_TEXT"); + + Ihandle *btn_ok = IupButton(lua_config_get_string("button", "ok"), NULL); + IupSetCallback(btn_ok, "ACTION", on_dialog_ok); + IupSetAttribute(btn_ok, "RASTERSIZE", lua_config_get_string("button", "rastersize")); + + Ihandle *btn_cancel = IupButton(lua_config_get_string("button", "cancel"), NULL); + IupSetCallback(btn_cancel, "ACTION", on_dialog_cancel); + IupSetAttribute(btn_cancel, "RASTERSIZE", lua_config_get_string("button", "rastersize")); + + Ihandle *vbox = IupVbox( + IupLabel(label_text), + text, + IupHbox(IupFill(), btn_ok, btn_cancel, NULL), + NULL); + IupSetAttribute(vbox, "MARGIN", "15x15"); + IupSetAttribute(vbox, "GAP", "10"); + + Ihandle *dlg = IupDialog(vbox); + IupSetAttribute(dlg, "TITLE", title); + IupSetAttribute(dlg, "MINBOX", "NO"); + IupSetAttribute(dlg, "MAXBOX", "NO"); + IupSetAttribute(dlg, "RESIZE", "NO"); + + IupSetAttributeHandle(dlg, "DEFAULTENTER", btn_ok); + IupSetAttributeHandle(dlg, "DEFAULTESC", btn_cancel); + + IupPopup(dlg, IUP_CENTER, IUP_CENTER); + + int result = IupGetInt(dlg, "MY_STATUS"); + if (result == 1) + { + char *val = IupGetAttribute(text, "VALUE"); + if (val) + { + safe_strcpy(buffer, buffer_size, val); + } + } + + IupDestroy(dlg); + return result; +} \ No newline at end of file diff --git a/src/ui/main_window.c b/src/ui/main_window.c new file mode 100644 index 0000000..1d6a008 --- /dev/null +++ b/src/ui/main_window.c @@ -0,0 +1,157 @@ +#include "ui/main_window.h" +#include "controller/callbacks.h" +#include "core/lua_config.h" +#include + +// 创建路径列表控件 +static Ihandle *create_path_list(const char *name) +{ + Ihandle *list = IupFlatList(); + IupSetAttribute(list, "NAME", name); + IupSetAttribute(list, "EXPAND", "YES"); + IupSetAttribute(list, "ITEMPADDING", lua_config_get_string("list", "item_padding")); + IupSetAttribute(list, "BACKCOLOR", lua_config_get_string("list", "backcolor")); + IupSetAttribute(list, "BORDER", "YES"); + IupSetAttribute(list, "CANFOCUS", "YES"); + IupSetAttribute(list, "HLINE", "NO"); + IupSetCallback(list, "DBLCLICK_CB", (Icallback)list_dblclick_cb); + IupSetCallback(list, "DROPFILES_CB", (Icallback)list_dropfiles_cb); + IupSetCallback(list, "K_ANY", (Icallback)list_k_any_cb); + return list; +} + +// 创建主窗口 +Ihandle *create_main_window(void) +{ + // 创建系统路径列表 + Ihandle *list_sys = create_path_list("LIST_SYS"); + // 创建用户路径列表 + Ihandle *list_user = create_path_list("LIST_USER"); + + // 创建搜索框 + Ihandle *txt_search = IupText(NULL); + IupSetAttribute(txt_search, "NAME", "TXT_SEARCH"); + IupSetAttribute(txt_search, "EXPAND", "HORIZONTAL"); + IupSetAttribute(txt_search, "CUEBANNER", lua_config_get_string("label", "search_placeholder")); + IupSetCallback(txt_search, "VALUECHANGED_CB", (Icallback)txt_search_cb); + + // 创建选项卡 + Ihandle *tabs_main = IupTabs( + IupVbox(list_sys, NULL), + IupVbox(list_user, NULL), + NULL); + IupSetAttribute(tabs_main, "NAME", "TABS_MAIN"); + IupSetAttribute(tabs_main, "TABTITLE0", lua_config_get_string("label", "tab_sys")); + IupSetAttribute(tabs_main, "TABTITLE1", lua_config_get_string("label", "tab_user")); + IupSetAttribute(tabs_main, "TABTYPE", "TOP"); + + // 创建操作按钮 + Ihandle *btn_new = IupButton(lua_config_get_string("button", "new"), NULL); + IupSetAttribute(btn_new, "NAME", "BTN_NEW"); + Ihandle *btn_edit = IupButton(lua_config_get_string("button", "edit"), NULL); + IupSetAttribute(btn_edit, "NAME", "BTN_EDIT"); + Ihandle *btn_browse = IupButton(lua_config_get_string("button", "browse"), NULL); + IupSetAttribute(btn_browse, "NAME", "BTN_BROWSE"); + Ihandle *btn_del = IupButton(lua_config_get_string("button", "del"), NULL); + IupSetAttribute(btn_del, "NAME", "BTN_DEL"); + Ihandle *btn_up = IupButton(lua_config_get_string("button", "up"), NULL); + IupSetAttribute(btn_up, "NAME", "BTN_UP"); + Ihandle *btn_down = IupButton(lua_config_get_string("button", "down"), NULL); + IupSetAttribute(btn_down, "NAME", "BTN_DOWN"); + Ihandle *btn_clean = IupButton(lua_config_get_string("button", "clean"), NULL); + IupSetAttribute(btn_clean, "NAME", "BTN_CLEAN"); + + Ihandle *btn_import = IupButton(lua_config_get_string("button", "import"), NULL); + IupSetAttribute(btn_import, "NAME", "BTN_IMPORT"); + Ihandle *btn_export = IupButton(lua_config_get_string("button", "export"), NULL); + IupSetAttribute(btn_export, "NAME", "BTN_EXPORT"); + + // 设置按钮回调 + IupSetCallback(btn_new, "ACTION", (Icallback)btn_new_cb); + IupSetCallback(btn_edit, "ACTION", (Icallback)btn_edit_cb); + IupSetCallback(btn_browse, "ACTION", (Icallback)btn_browse_cb); + IupSetCallback(btn_del, "ACTION", (Icallback)btn_del_cb); + IupSetCallback(btn_up, "ACTION", (Icallback)btn_up_cb); + IupSetCallback(btn_down, "ACTION", (Icallback)btn_down_cb); + IupSetCallback(btn_clean, "ACTION", (Icallback)btn_clean_cb); + IupSetCallback(btn_import, "ACTION", (Icallback)btn_import_cb); + IupSetCallback(btn_export, "ACTION", (Icallback)btn_export_cb); + + // 设置按钮大小 + const char *btn_size = lua_config_get_string("button", "rastersize"); + IupSetAttribute(btn_new, "RASTERSIZE", btn_size); + IupSetAttribute(btn_edit, "RASTERSIZE", btn_size); + IupSetAttribute(btn_browse, "RASTERSIZE", btn_size); + IupSetAttribute(btn_del, "RASTERSIZE", btn_size); + IupSetAttribute(btn_up, "RASTERSIZE", btn_size); + IupSetAttribute(btn_down, "RASTERSIZE", btn_size); + IupSetAttribute(btn_clean, "RASTERSIZE", btn_size); + IupSetAttribute(btn_import, "RASTERSIZE", btn_size); + IupSetAttribute(btn_export, "RASTERSIZE", btn_size); + + // 创建操作按钮垂直布局 + Ihandle *vbox_btns = IupVbox( + btn_new, btn_edit, btn_browse, btn_del, + IupFill(), + btn_clean, + IupFill(), + btn_import, btn_export, + btn_up, btn_down, + NULL); + IupSetAttribute(vbox_btns, "GAP", lua_config_get_string("layout", "vbox_gap")); + IupSetAttribute(vbox_btns, "MARGIN", lua_config_get_string("layout", "vbox_margin")); + + // 创建主窗口水平布局 + Ihandle *hbox_main = IupHbox(tabs_main, vbox_btns, NULL); + IupSetAttribute(hbox_main, "GAP", lua_config_get_string("layout", "hbox_gap")); + IupSetAttribute(hbox_main, "MARGIN", lua_config_get_string("layout", "hbox_margin")); + + // 创建状态标签 + Ihandle *lbl_status = IupLabel(lua_config_get_string("status", "normal")); + IupSetAttribute(lbl_status, "NAME", "LBL_STATUS"); + IupSetAttribute(lbl_status, "EXPAND", "HORIZONTAL"); + + // 创建底部按钮 + Ihandle *btn_ok = IupButton(lua_config_get_string("button", "ok"), NULL); + IupSetAttribute(btn_ok, "NAME", "BTN_OK"); + Ihandle *btn_cancel = IupButton(lua_config_get_string("button", "cancel"), NULL); + IupSetAttribute(btn_cancel, "NAME", "BTN_CANCEL"); + Ihandle *btn_help = IupButton(lua_config_get_string("button", "help"), NULL); + IupSetAttribute(btn_help, "NAME", "BTN_HELP"); + + // 设置底部按钮回调 + IupSetCallback(btn_ok, "ACTION", (Icallback)btn_ok_cb); + IupSetCallback(btn_cancel, "ACTION", (Icallback)btn_cancel_cb); + IupSetCallback(btn_help, "ACTION", (Icallback)btn_help_cb); + + // 设置底部按钮大小 + IupSetAttribute(btn_ok, "RASTERSIZE", btn_size); + IupSetAttribute(btn_cancel, "RASTERSIZE", btn_size); + IupSetAttribute(btn_help, "RASTERSIZE", btn_size); + + // 创建底部按钮水平布局 + Ihandle *hbox_bottom = IupHbox(lbl_status, IupFill(), btn_help, btn_ok, btn_cancel, NULL); + IupSetAttribute(hbox_bottom, "GAP", lua_config_get_string("layout", "hbox_gap")); + IupSetAttribute(hbox_bottom, "MARGIN", lua_config_get_string("layout", "hbox_margin")); + IupSetAttribute(hbox_bottom, "ALIGNMENT", lua_config_get_string("layout", "hbox_alignment")); + + // 创建主窗口垂直布局 + Ihandle *vbox_all = IupVbox( + IupLabel(lua_config_get_string("label", "title")), + txt_search, + hbox_main, + hbox_bottom, + NULL); + IupSetAttribute(vbox_all, "MARGIN", lua_config_get_string("layout", "vbox_all_margin")); + IupSetAttribute(vbox_all, "GAP", lua_config_get_string("layout", "vbox_all_gap")); + + // 创建主窗口对话框 + Ihandle *dlg = IupDialog(vbox_all); + IupSetAttribute(dlg, "TITLE", lua_config_get_string("app", "name")); + IupSetAttribute(dlg, "RASTERSIZE", lua_config_get_string("dialog", "size")); + IupSetAttribute(dlg, "MINSIZE", lua_config_get_string("dialog", "minsize")); + IupSetAttribute(dlg, "MINBOX", "NO"); + IupSetAttribute(dlg, "MAXBOX", "NO"); + + return dlg; +} \ No newline at end of file diff --git a/src/ui/ui_utils.c b/src/ui/ui_utils.c new file mode 100644 index 0000000..c1075e6 --- /dev/null +++ b/src/ui/ui_utils.c @@ -0,0 +1,69 @@ +#include "ui/ui_utils.h" +#include "utils/os_env.h" +#include +#include + +// 刷新列表样式(斑马纹 + 有效性检查) +void refresh_single_list_style(Ihandle *list) +{ + if (!list) + return; + int count = IupGetInt(list, "COUNT"); + + for (int i = 1; i <= count; i++) + { + char *item = IupGetAttributeId(list, "", i); + if (!item) + continue; + + // 默认颜色:黑字 + char fg_color[32] = "0 0 0"; + + // 1. 检查有效性 + if (!is_path_valid(item)) + { + // 无效路径:红色 + sprintf(fg_color, "255 0 0"); + } + + // 2. 检查重复 (只检查当前项之前的项,如果之前出现过,当前项标橙) + for (int j = 1; j < i; j++) + { + char *prev_item = IupGetAttributeId(list, "", j); + if (prev_item && _stricmp(item, prev_item) == 0) // Windows 路径不区分大小写 + { + // 重复路径:橙色 + sprintf(fg_color, "255 128 0"); + break; + } + } + + IupSetAttributeId(list, "ITEMFGCOLOR", i, fg_color); + + // 斑马纹背景 + if (i % 2 == 0) + { + IupSetAttributeId(list, "ITEMBGCOLOR", i, "245 245 245"); + } + else + { + IupSetAttributeId(list, "ITEMBGCOLOR", i, "255 255 255"); + } + } +} + +// 同步字符串列表到 UI 列表 +void sync_string_list_to_ui(Ihandle *list_ui, const StringList *str_list) +{ + if (!list_ui || !str_list) return; + + IupSetAttribute(list_ui, "REMOVEITEM", "ALL"); + + for (int i = 0; i < str_list->count; i++) + { + IupSetAttributeId(list_ui, "", i + 1, str_list->items[i]); + } + IupSetInt(list_ui, "COUNT", str_list->count); + + refresh_single_list_style(list_ui); +} \ No newline at end of file diff --git a/src/utils.c b/src/utils.c deleted file mode 100644 index 8514d69..0000000 --- a/src/utils.c +++ /dev/null @@ -1,61 +0,0 @@ -#include "utils.h" -#include "globals.h" -#include -#include -#include - -// 宽字符转UTF-8 (用于IUP显示) -char *wide_to_utf8(const wchar_t *wstr) -{ - if (!wstr) - return NULL; - int size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL); - char *str = (char *)malloc(size_needed); - WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, size_needed, NULL, NULL); - return str; -} - -// UTF-8转宽字符 (用于Windows API) -wchar_t *utf8_to_wide(const char *str) -{ - if (!str) - return NULL; - int size_needed = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); - wchar_t *wstr = (wchar_t *)malloc(size_needed * sizeof(wchar_t)); - MultiByteToWideChar(CP_UTF8, 0, str, -1, wstr, size_needed); - return wstr; -} - -// 检查管理员权限 -int check_admin() -{ - HKEY hKey; - LONG result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", 0, KEY_WRITE, &hKey); - if (result == ERROR_SUCCESS) - { - RegCloseKey(hKey); - return 1; - } - return 0; -} - -// 刷新列表样式(斑马纹) -void refresh_list_style() -{ - if (!list_path) - return; - int count = IupGetInt(list_path, "COUNT"); - for (int i = 1; i <= count; i++) - { - // 奇数行:白色 - // 偶数行:极浅灰色 (245 245 245) - if (i % 2 == 0) - { - IupSetAttributeId(list_path, "ITEMBGCOLOR", i, "245 245 245"); - } - else - { - IupSetAttributeId(list_path, "ITEMBGCOLOR", i, "255 255 255"); - } - } -} \ No newline at end of file diff --git a/src/utils/logger.c b/src/utils/logger.c new file mode 100644 index 0000000..96a9f0b --- /dev/null +++ b/src/utils/logger.c @@ -0,0 +1,126 @@ +#include "utils/logger.h" +#include +#include +#include +#include +#include + +// 全局日志文件指针 +static FILE *G_log_file = NULL; +// 全局日志级别 +static LogLevel G_log_level = LOG_LEVEL_INFO; + +// 将日志级别转换为字符串 +static const char *level_to_string(LogLevel level) +{ + switch (level) + { + case LOG_LEVEL_DEBUG: + return "DEBUG"; + case LOG_LEVEL_INFO: + return "INFO "; + case LOG_LEVEL_WARN: + return "WARN "; + case LOG_LEVEL_ERROR: + return "ERROR"; + default: + return "UNKN "; + } +} + +// 获取当前时间戳 +static void get_timestamp(char *buffer, size_t size) +{ + time_t now = time(NULL); + struct tm *tm_info = localtime(&now); + strftime(buffer, size, "%Y-%m-%d %H:%M:%S", tm_info); +} + +// 写入日志 +static void log_write(LogLevel level, const char *fmt, va_list args) +{ + if (level < G_log_level) + return; + + char timestamp[64]; + get_timestamp(timestamp, sizeof(timestamp)); + + char message[1024]; + vsnprintf(message, sizeof(message), fmt, args); + + const char *level_str = level_to_string(level); + + if (G_log_file) + { + fprintf(G_log_file, "[%s] [%s] %s\n", timestamp, level_str, message); + fflush(G_log_file); + } + + fprintf(stdout, "[%s] [%s] %s\n", timestamp, level_str, message); +} + +// 初始化日志系统 +void log_init(const char *log_file, LogLevel level) +{ + if (log_file) + { + G_log_file = fopen(log_file, "a"); + if (!G_log_file) + { + fprintf(stderr, "Failed to open log file: %s\n", log_file); + } + } + G_log_level = level; +} + +// 销毁日志系统 +void log_destroy(void) +{ + if (G_log_file) + { + fclose(G_log_file); + G_log_file = NULL; + } +} + +// 设置日志级别 +void log_set_level(LogLevel level) +{ + G_log_level = level; +} + +// 调试日志 +void log_debug(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + log_write(LOG_LEVEL_DEBUG, fmt, args); + va_end(args); +} + +// 信息日志 +void log_info(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + log_write(LOG_LEVEL_INFO, fmt, args); + va_end(args); +} + +// 警告日志 +void log_warn(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + log_write(LOG_LEVEL_WARN, fmt, args); + va_end(args); +} + +// 错误日志 +void log_error(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + log_write(LOG_LEVEL_ERROR, fmt, args); + va_end(args); +} \ No newline at end of file diff --git a/src/utils/os_env.c b/src/utils/os_env.c new file mode 100644 index 0000000..ae5f55c --- /dev/null +++ b/src/utils/os_env.c @@ -0,0 +1,90 @@ +#include "utils/os_env.h" +#include "utils/string_ext.h" +#include +#include +#include +#include +#include + +// 检查管理员权限 +int check_admin(void) +{ + HKEY hKey; + LONG result = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", 0, KEY_WRITE, &hKey); + if (result == ERROR_SUCCESS) + { + RegCloseKey(hKey); + return 1; + } + return 0; +} + +// 内部:检查路径是否存在 +static int path_exists(const char *path) +{ + // 如果包含 %,说明是变量,无法直接检查存在性,默认视为有效 + if (strchr(path, '%')) + return 1; + + wchar_t *wpath = utf8_to_wide(path); + if (!wpath) + return 0; + + DWORD attr = GetFileAttributesW(wpath); + free(wpath); + + if (attr == INVALID_FILE_ATTRIBUTES) + return 0; + return (attr & FILE_ATTRIBUTE_DIRECTORY); // 必须是目录 +} + +// 检查路径是否存在(公开给外部使用) +int is_path_valid(const char *path) +{ + return path_exists(path); +} + +// 备份注册表 +void backup_registry(void) +{ + // 创建 records 目录 + if (_mkdir("records") == -1) + { + // 目录可能已存在,忽略错误 + } + + // 获取当前时间 + time_t t = time(NULL); + struct tm *tm_info = localtime(&t); + char time_str[64]; + strftime(time_str, sizeof(time_str), "%Y%m%d_%H%M%S", tm_info); + + // 构造备份文件名 + char filename[256]; + snprintf(filename, sizeof(filename), "records\\backup_%s.reg", time_str); + + // 构造 reg export 命令 + char full_cmd[1024]; + snprintf(full_cmd, sizeof(full_cmd), "reg.exe export \"HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment\" \"%s\" /y", filename); + + // 使用 CreateProcess 隐藏窗口执行 + STARTUPINFOA si; + PROCESS_INFORMATION pi; + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_HIDE; // 隐藏窗口 + memset(&pi, 0, sizeof(pi)); + + if (CreateProcessA(NULL, full_cmd, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) + { + // 等待进程结束 + WaitForSingleObject(pi.hProcess, INFINITE); + + DWORD exit_code = 0; + GetExitCodeProcess(pi.hProcess, &exit_code); + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } +} \ No newline at end of file diff --git a/src/utils/safe_string.c b/src/utils/safe_string.c new file mode 100644 index 0000000..a983de4 --- /dev/null +++ b/src/utils/safe_string.c @@ -0,0 +1,49 @@ +#include "utils/safe_string.h" +#include +#include + +// 安全字符串复制 +char *safe_strcpy(char *dst, size_t dst_size, const char *src) +{ + if (!dst || !src || dst_size == 0) + return NULL; + + size_t len = strlen(src); + if (len >= dst_size) + { + memcpy(dst, src, dst_size - 1); + dst[dst_size - 1] = '\0'; + } + else + { + strcpy(dst, src); + } + return dst; +} + +// 安全字符串拼接 +char *safe_strcat(char *dst, size_t dst_size, const char *src) +{ + if (!dst || !src || dst_size == 0) + return NULL; + + size_t dst_len = strlen(dst); + if (dst_len >= dst_size) + return NULL; + + return safe_strcpy(dst + dst_len, dst_size - dst_len, src); +} + +// 安全字符串复制(返回新分配的内存) +char *safe_strdup(const char *src) +{ + if (!src) + return NULL; + + size_t len = strlen(src); + char *result = (char *)malloc(len + 1); + if (result) + strcpy(result, src); + + return result; +} \ No newline at end of file diff --git a/src/utils/string_ext.c b/src/utils/string_ext.c new file mode 100644 index 0000000..6ea5080 --- /dev/null +++ b/src/utils/string_ext.c @@ -0,0 +1,101 @@ +#include "utils/string_ext.h" +#include +#include +#include +#include +#include + +// 宽字符转UTF-8 (用于IUP显示) +char *wide_to_utf8(const wchar_t *wstr) +{ + if (!wstr) + return NULL; + int size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL); + char *str = (char *)malloc(size_needed); + if (str) + { + WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, size_needed, NULL, NULL); + } + return str; +} + +// UTF-8转宽字符 (用于Windows API) +wchar_t *utf8_to_wide(const char *str) +{ + if (!str) + return NULL; + int size_needed = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); + wchar_t *wstr = (wchar_t *)malloc(size_needed * sizeof(wchar_t)); + if (wstr) + { + MultiByteToWideChar(CP_UTF8, 0, str, -1, wstr, size_needed); + } + return wstr; +} + +// 初始化字符串列表 +void init_string_list(StringList *list) +{ + if (!list) + return; + list->items = NULL; + list->count = 0; + list->capacity = 0; +} + +// 不区分大小写的字符串查找 (简单实现) +char *stristr(const char *haystack, const char *needle) +{ + if (!haystack || !needle) + return NULL; + if (*needle == '\0') + return (char *)haystack; + + const char *h = haystack; + const char *n = needle; + const char *h_start = haystack; + + while (*h) + { + h_start = h; + n = needle; + while (*h && *n && (tolower((unsigned char)*h) == tolower((unsigned char)*n))) + { + h++; + n++; + } + if (*n == '\0') + return (char *)h_start; + h = h_start + 1; + } + return NULL; +} + +// 添加字符串到列表 +void add_string_list(StringList *list, const char *str) +{ + if (!list || !str) + return; + if (list->count >= list->capacity) + { + list->capacity = (list->capacity == 0) ? 16 : list->capacity * 2; + list->items = (char **)realloc(list->items, list->capacity * sizeof(char *)); + } + list->items[list->count] = _strdup(str); // 复制字符串 + list->count++; +} + +// 清空字符串列表 +void clear_string_list(StringList *list) +{ + if (!list) + return; + for (int i = 0; i < list->count; i++) + { + free(list->items[i]); + } + free(list->items); + list->items = NULL; + list->count = 0; + list->capacity = 0; +} \ No newline at end of file