From 499a0cbdfa4322832d23d173e1256fb6769d021c Mon Sep 17 00:00:00 2001 From: Max Qian Date: Fri, 27 Oct 2023 19:24:47 +0800 Subject: [PATCH] update pre --- cmake_modules/compiler_options.cmake | 2 + locale/lithium.pot | 16 +- locale/po/en_US.UTF-8/lithium.po | 14 +- src/core/base/hydrogencom.cpp | 11 +- src/modules/system/dirw.cpp | 236 ++++++++++++++++++++ src/modules/system/dirw.hpp | 117 ++++++++++ src/modules/system/filew.cpp | 312 +++++++++++++++++++++++++++ src/modules/system/filew.hpp | 159 ++++++++++++++ src/modules/system/process.cpp | 61 ++++++ src/modules/system/process.hpp | 4 + src/modules/utils/switch.cpp | 77 +++++++ src/modules/utils/switch.hpp | 95 ++++++++ 12 files changed, 1085 insertions(+), 19 deletions(-) create mode 100644 src/modules/system/dirw.cpp create mode 100644 src/modules/system/dirw.hpp create mode 100644 src/modules/system/filew.cpp create mode 100644 src/modules/system/filew.hpp create mode 100644 src/modules/utils/switch.cpp create mode 100644 src/modules/utils/switch.hpp diff --git a/cmake_modules/compiler_options.cmake b/cmake_modules/compiler_options.cmake index 691d41f2..5b01fb0c 100644 --- a/cmake_modules/compiler_options.cmake +++ b/cmake_modules/compiler_options.cmake @@ -53,6 +53,8 @@ if (APPLE) endif () endif () +add_compile_options(-fsanitize=address -Wall) + # set build architecture for non-Apple platforms if (NOT APPLE) set(CMAKE_OSX_ARCHITECTURES x86_64 CACHE STRING "build architecture for non-Apple platforms" FORCE) diff --git a/locale/lithium.pot b/locale/lithium.pot index 597c3aee..4799227b 100644 --- a/locale/lithium.pot +++ b/locale/lithium.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Lithium \n" "Report-Msgid-Bugs-To: astro_air@126.com\n" -"POT-Creation-Date: 2023-10-20 23:53+0800\n" +"POT-Creation-Date: 2023-10-27 18:56+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -238,30 +238,30 @@ msgstr "" msgid "Set server port to %d" msgstr "" -#: E:/msys64/home/Qrm/OpenAPT/src/LithiumApp.cpp:70 +#: E:/msys64/home/Qrm/OpenAPT/src/LithiumApp.cpp:81 msgid "Failed to load Lithium App , error : {}" msgstr "" -#: E:/msys64/home/Qrm/OpenAPT/src/LithiumApp.cpp:82 +#: E:/msys64/home/Qrm/OpenAPT/src/LithiumApp.cpp:93 msgid "Get config value: {}" msgstr "" -#: E:/msys64/home/Qrm/OpenAPT/src/LithiumApp.cpp:88 +#: E:/msys64/home/Qrm/OpenAPT/src/LithiumApp.cpp:99 msgid "Set {} to {}" msgstr "" -#: E:/msys64/home/Qrm/OpenAPT/src/LithiumApp.cpp:275 +#: E:/msys64/home/Qrm/OpenAPT/src/LithiumApp.cpp:286 msgid "Failed to run chai command : {}" msgstr "" -#: E:/msys64/home/Qrm/OpenAPT/src/LithiumApp.cpp:293 +#: E:/msys64/home/Qrm/OpenAPT/src/LithiumApp.cpp:304 msgid "Failed to run chai multi command {}" msgstr "" -#: E:/msys64/home/Qrm/OpenAPT/src/LithiumApp.cpp:306 +#: E:/msys64/home/Qrm/OpenAPT/src/LithiumApp.cpp:317 msgid "Failed to load chaiscript file {}" msgstr "" -#: E:/msys64/home/Qrm/OpenAPT/src/LithiumApp.cpp:319 +#: E:/msys64/home/Qrm/OpenAPT/src/LithiumApp.cpp:330 msgid "Failed to run chai script {}" msgstr "" diff --git a/locale/po/en_US.UTF-8/lithium.po b/locale/po/en_US.UTF-8/lithium.po index 7bf095a3..95eb5d2b 100644 --- a/locale/po/en_US.UTF-8/lithium.po +++ b/locale/po/en_US.UTF-8/lithium.po @@ -219,30 +219,30 @@ msgstr "" msgid "Set server port to %d" msgstr "" -#: E:/msys64/home/Qrm/OpenAPT/src/LithiumApp.cpp:70 +#: E:/msys64/home/Qrm/OpenAPT/src/LithiumApp.cpp:81 msgid "Failed to load Lithium App , error : {}" msgstr "" -#: E:/msys64/home/Qrm/OpenAPT/src/LithiumApp.cpp:82 +#: E:/msys64/home/Qrm/OpenAPT/src/LithiumApp.cpp:93 msgid "Get config value: {}" msgstr "" -#: E:/msys64/home/Qrm/OpenAPT/src/LithiumApp.cpp:88 +#: E:/msys64/home/Qrm/OpenAPT/src/LithiumApp.cpp:99 msgid "Set {} to {}" msgstr "" -#: E:/msys64/home/Qrm/OpenAPT/src/LithiumApp.cpp:275 +#: E:/msys64/home/Qrm/OpenAPT/src/LithiumApp.cpp:286 msgid "Failed to run chai command : {}" msgstr "" -#: E:/msys64/home/Qrm/OpenAPT/src/LithiumApp.cpp:293 +#: E:/msys64/home/Qrm/OpenAPT/src/LithiumApp.cpp:304 msgid "Failed to run chai multi command {}" msgstr "" -#: E:/msys64/home/Qrm/OpenAPT/src/LithiumApp.cpp:306 +#: E:/msys64/home/Qrm/OpenAPT/src/LithiumApp.cpp:317 msgid "Failed to load chaiscript file {}" msgstr "" -#: E:/msys64/home/Qrm/OpenAPT/src/LithiumApp.cpp:319 +#: E:/msys64/home/Qrm/OpenAPT/src/LithiumApp.cpp:330 msgid "Failed to run chai script {}" msgstr "" diff --git a/src/core/base/hydrogencom.cpp b/src/core/base/hydrogencom.cpp index 8cfe3725..ac4ff932 100644 --- a/src/core/base/hydrogencom.cpp +++ b/src/core/base/hydrogencom.cpp @@ -361,6 +361,9 @@ void IDLog(const char *fmt, ...) double time_ns() { struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 0; + #if defined(HAVE_TIMESPEC_GET) timespec_get(&ts, TIME_UTC); #elif defined(HAVE_CLOCK_GETTIME) @@ -557,9 +560,9 @@ int tty_read_expanded(int fd, char *buf, int nbytes, long timeout_seconds, long if (tty_debug) { - IDLog("%d bytes read and %d bytes remaining...\n", bytesRead, numBytesToRead - bytesRead); + IDLog("%lu bytes read and %lu bytes remaining...\n", bytesRead, numBytesToRead - bytesRead); int i = 0; - for (i = *nbytes_read; i < (*nbytes_read + bytesRead); i++) + for (i = *nbytes_read; static_cast(i) < static_cast((*nbytes_read + bytesRead)); i++) IDLog("%s: buffer[%d]=%#X (%c)\n", __FUNCTION__, i, (unsigned char)buf[i], buf[i]); } @@ -704,7 +707,7 @@ int tty_read_section_expanded(int fd, char *buf, char stop_char, long timeout_se return tty_read_section_expanded(fd, buf, stop_char, timeout_seconds, timeout_microseconds, nbytes_read); } - for (int index = 8; index < bytesRead; index++) + for (int index = 8; static_cast(index) < static_cast(bytesRead); index++) { (*nbytes_read)++; @@ -726,7 +729,7 @@ int tty_read_section_expanded(int fd, char *buf, char stop_char, long timeout_se if (bytesRead < 0) return TTY_READ_ERROR; - for (int index = 0; index < bytesRead; index++) + for (int index = 0; static_cast(index) < static_cast(bytesRead); index++) { (*nbytes_read)++; diff --git a/src/modules/system/dirw.cpp b/src/modules/system/dirw.cpp new file mode 100644 index 00000000..da4af59e --- /dev/null +++ b/src/modules/system/dirw.cpp @@ -0,0 +1,236 @@ +/* + * dirw.cpp + * + * Copyright (C) 2023 Max Qian + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/************************************************* + +Copyright: 2023 Max Qian. All rights reserved + +Author: Max Qian + +E-mail: astro_air@126.com + +Date: 2023-10-27 + +Description: Folder Watcher + +**************************************************/ + +#include "dirw.hpp" + +#include "loguru/loguru.hpp" + +FolderMonitor::FolderMonitor(const std::string &folderPath) + : m_folderPath(folderPath), m_isMonitoring(false) {} + +void FolderMonitor::StartMonitoring() +{ + if (m_isMonitoring) + { + LOG_F(ERROR, "Folder monitor is already running."); + return; + } + + m_isMonitoring = true; + + // 创建监视线程 + m_monitorThread = std::thread([this]() + { + while (m_isMonitoring) { + MonitorFolderChanges(); + std::this_thread::sleep_for(std::chrono::seconds(1)); + } }); +} + +void FolderMonitor::StopMonitoring() +{ + if (!m_isMonitoring) + { + LOG_F(WARNING, "Folder monitor is not running."); + return; + } + + m_isMonitoring = false; + + // 等待监视线程退出 + if (m_monitorThread.joinable()) + { + m_monitorThread.join(); + } +} + +void FolderMonitor::RegisterFileChangeEventCallback(FileChangeEventCallback callback) +{ + m_fileChangeEventCallback = callback; +} + +void FolderMonitor::MonitorFolderChanges() +{ +#ifdef _WIN32 + // Windows平台下的文件夹监视器实现 + HANDLE hDir = CreateFile(m_folderPath.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + + if (hDir == INVALID_HANDLE_VALUE) + { + LOG_F(ERROR, "Failed to open folder: {}", m_folderPath); + return; + } + + DWORD dwBytesReturned; + FILE_NOTIFY_INFORMATION buffer[1024]; + + BOOL result = ReadDirectoryChangesW(hDir, &buffer, sizeof(buffer), TRUE, + FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME, &dwBytesReturned, NULL, NULL); + + if (!result) + { + LOG_F(ERROR, "Failed to start monitoring folder: {}", m_folderPath); + CloseHandle(hDir); + return; + } + + while (result && m_isMonitoring) + { + DWORD offset = 0; + FILE_NOTIFY_INFORMATION *pInfo = nullptr; + + do + { + pInfo = reinterpret_cast(reinterpret_cast(&buffer) + offset); + + std::wstring fileName(pInfo->FileName, pInfo->FileNameLength / sizeof(wchar_t)); + std::string utf8FileName(fileName.begin(), fileName.end()); + + std::string filePath = m_folderPath + "\\" + utf8FileName; + + if (pInfo->Action == FILE_ACTION_MODIFIED) + { + if (m_fileChangeEventCallback) + { + m_fileChangeEventCallback(filePath); + } + } + else if (pInfo->Action == FILE_ACTION_ADDED || pInfo->Action == FILE_ACTION_RENAMED_NEW_NAME) + { + if (m_fileChangeEventCallback) + { + m_fileChangeEventCallback(filePath); + } + } + else if (pInfo->Action == FILE_ACTION_REMOVED || pInfo->Action == FILE_ACTION_RENAMED_OLD_NAME) + { + // 文件被删除或重命名 + // 可以在此处执行相应的处理操作 + } + + offset += pInfo->NextEntryOffset; + } while (pInfo->NextEntryOffset != 0 && m_isMonitoring); + + result = ReadDirectoryChangesW(hDir, &buffer, sizeof(buffer), TRUE, + FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME, &dwBytesReturned, NULL, NULL); + } + + CloseHandle(hDir); +#else + // Linux平台下的文件夹监视器实现 + int fd = inotify_init(); + if (fd == -1) + { + LOG_F(ERROR, "Failed to initialize inotify."); + return; + } + + int wd = inotify_add_watch(fd, m_folderPath.c_str(), + IN_MODIFY | IN_CREATE | IN_DELETE | IN_MOVE | IN_MOVED_FROM | IN_MOVED_TO); + + if (wd == -1) + { + LOG_F(ERROR, "Failed to add watch for folder: ", m_folderPath); + close(fd); + return; + } + + char buffer[4096]; + + while (m_isMonitoring) + { + ssize_t len = read(fd, buffer, sizeof(buffer)); + if (len == -1) + { + LOG_F(ERROR, "Failed to read events from inotify."); + break; + } + + const struct inotify_event *event = nullptr; + for (char *ptr = buffer; ptr < buffer + len; ptr += sizeof(struct inotify_event) + event->len) + { + event = reinterpret_cast(ptr); + + std::string fileName(event->name); + std::string filePath = m_folderPath + "/" + fileName; + + if (event->mask & IN_MODIFY) + { + if (m_fileChangeEventCallback) + { + m_fileChangeEventCallback(filePath); + } + } + else if (event->mask & (IN_CREATE | IN_MOVED_TO)) + { + if (m_fileChangeEventCallback) + { + m_fileChangeEventCallback(filePath); + } + } + else if (event->mask & (IN_DELETE | IN_MOVED_FROM)) + { + // 文件被删除或移动 + // 可以在此处执行相应的处理操作 + } + } + } + + inotify_rm_watch(fd, wd); + close(fd); +#endif +} + +/* + +// 示例用法 +int main() +{ + FolderMonitor folderMonitor("test"); + + // 注册文件变更事件的处理函数 + folderMonitor.RegisterFileChangeEventCallback([](const std::string &filePath) + { std::cout << "File changed: " << filePath << std::endl; }); + + // 启动监视器 + folderMonitor.StartMonitoring(); + + // 持续运行一段时间 + std::this_thread::sleep_for(std::chrono::seconds(60)); + + // 停止监视器 + folderMonitor.StopMonitoring(); + + return 0; +} + +*/ diff --git a/src/modules/system/dirw.hpp b/src/modules/system/dirw.hpp new file mode 100644 index 00000000..2cdba6f4 --- /dev/null +++ b/src/modules/system/dirw.hpp @@ -0,0 +1,117 @@ +/* + * dirw.hpp + * + * Copyright (C) 2023 Max Qian + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/************************************************* + +Copyright: 2023 Max Qian. All rights reserved + +Author: Max Qian + +E-mail: astro_air@126.com + +Date: 2023-10-27 + +Description: Folder Watcher + +**************************************************/ + +#pragma once + +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#include +#endif + + +/** + * @class FolderMonitor + * @brief 文件夹监视器类,用于监视指定文件夹的变化并触发回调函数。 + */ +class FolderMonitor +{ +public: + /** + * @brief 定义文件变化事件回调函数类型。 + * @param filePath 变化的文件路径。 + */ + using FileChangeEventCallback = std::function; + + /** + * @brief 构造函数。 + * @param folderPath 要监视的文件夹路径。 + */ + FolderMonitor(const std::string &folderPath); + + /** + * @brief 开始监视文件夹变化。 + */ + void StartMonitoring(); + + /** + * @brief 停止监视文件夹变化。 + */ + void StopMonitoring(); + + /** + * @brief 注册文件变化事件回调函数。 + * @param callback 文件变化事件回调函数。 + */ + void RegisterFileChangeEventCallback(FileChangeEventCallback callback); + +private: + std::string m_folderPath; /**< 要监视的文件夹路径 */ + bool m_isMonitoring; /**< 是否正在监视文件夹变化 */ + std::thread m_monitorThread; /**< 监视线程 */ + FileChangeEventCallback m_fileChangeEventCallback; /**< 文件变化事件回调函数 */ + + /** + * @brief 监视文件夹变化的内部方法。 + */ + void MonitorFolderChanges(); +}; + +/* + +// 示例用法 +int main() +{ + FolderMonitor folderMonitor("test"); + + // 注册文件变更事件的处理函数 + folderMonitor.RegisterFileChangeEventCallback([](const std::string &filePath) + { std::cout << "File changed: " << filePath << std::endl; }); + + // 启动监视器 + folderMonitor.StartMonitoring(); + + // 持续运行一段时间 + std::this_thread::sleep_for(std::chrono::seconds(60)); + + // 停止监视器 + folderMonitor.StopMonitoring(); + + return 0; +} + +*/ diff --git a/src/modules/system/filew.cpp b/src/modules/system/filew.cpp new file mode 100644 index 00000000..501b2c07 --- /dev/null +++ b/src/modules/system/filew.cpp @@ -0,0 +1,312 @@ +/* + * filww.cpp + * + * Copyright (C) 2023 Max Qian + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/************************************************* + +Copyright: 2023 Max Qian. All rights reserved + +Author: Max Qian + +E-mail: astro_air@126.com + +Date: 2023-10-27 + +Description: File Watcher + +**************************************************/ + +#include "filew.hpp" + +#ifdef _WIN32 +#include +#include +#else +#include +#endif + +#include "loguru/loguru.hpp" + +enum class FileEventType +{ + kCreated, + kModified, + kDeleted, +}; + +struct FileEvent +{ + std::string path; + FileEventType type; +}; + +using FileEventHandler = std::function; + +struct FileMonitor::WatchInfo +{ + std::string path; + FileEventHandler handler; + WatchHandle handle; + +#ifdef _WIN32 + std::vector buffer; +#endif +}; + +FileMonitor::FileMonitor() : m_running(true) +{ +} + +FileMonitor::~FileMonitor() +{ + for (auto &watch_info : m_watches) + { +#ifdef _WIN32 + UnregisterWait(watch_info.handle); + CloseHandle(watch_info.handle); +#else + DestroyWatch(watch_info.handle); +#endif + } +} + +bool FileMonitor::AddWatch(const std::string &path, const FileEventHandler &handler) +{ + auto handle = CreateWatch(path); + if (handle == INVALID_HANDLE_VALUE) + { + return false; + } + + WatchInfo watch_info; + watch_info.path = path; + watch_info.handler = handler; + watch_info.handle = handle; + + m_watches.push_back(watch_info); + m_handles[handle] = &m_watches.back(); + + return true; +} + +bool FileMonitor::RemoveWatch(const std::string &path) +{ + auto it = std::find_if(m_watches.begin(), m_watches.end(), + [&](const WatchInfo &watch_info) + { return watch_info.path == path; }); + + if (it == m_watches.end()) + { + return false; + } + +#ifdef _WIN32 + UnregisterWait(it->handle); + CloseHandle(it->handle); +#else + DestroyWatch(it->handle); +#endif + + m_handles.erase(it->handle); + m_watches.erase(it); + + return true; +} + +FileMonitor::WatchHandle FileMonitor::CreateWatch(const std::string &path) +{ +#ifdef _WIN32 + int size = MultiByteToWideChar(CP_UTF8, 0, path.c_str(), -1, NULL, 0); + std::vector buffer(size); + MultiByteToWideChar(CP_UTF8, 0, path.c_str(), -1, buffer.data(), size); + LPCWSTR lpPath = buffer.data(); + auto handle = FindFirstChangeNotificationW( + lpPath, + FALSE, + FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_FILE_NAME); + + if (handle == INVALID_HANDLE_VALUE) + { + return INVALID_HANDLE_VALUE; + } + + RegisterWaitForSingleObject( + &handle, + handle, + &FileMonitor::Win32Callback, + &m_handles[handle], + INFINITE, + WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE); + + return handle; +#else + auto handle = inotify_init(); + if (handle == -1) + { + return -1; + } + + auto watch_descriptor = inotify_add_watch( + handle, + path.c_str(), + IN_CREATE | IN_MOVED_TO | IN_MODIFY | IN_DELETE | IN_MOVED_FROM); + + if (watch_descriptor == -1) + { + close(handle); + return -1; + } + + pthread_t thread_id; + pthread_create(&thread_id, nullptr, &FileMonitor::LinuxThreadFunc, &m_handles[watch_descriptor]); + pthread_detach(thread_id); + + return watch_descriptor; +#endif +} + +void FileMonitor::DestroyWatch(WatchHandle handle) +{ +#ifdef _WIN32 + FindCloseChangeNotification(handle); +#else + inotify_rm_watch(handle, IN_ALL_EVENTS); + close(handle); +#endif +} + +void FileMonitor::MonitorLoop() +{ + while (m_running) + { + // Do nothing. + } +} + +#ifdef _WIN32 +void CALLBACK FileMonitor::Win32Callback(PVOID lpParameter, BOOLEAN TimerOrWaitFired) +{ + auto watch_info = static_cast(lpParameter); + + DWORD bytes_returned; + FILE_NOTIFY_INFORMATION *info; + + while (ReadDirectoryChangesW( + watch_info->handle, + &watch_info->buffer[0], + static_cast(watch_info->buffer.size()), + FALSE, + FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_FILE_NAME, + &bytes_returned, + nullptr, + nullptr)) + { + info = reinterpret_cast(&watch_info->buffer[0]); + + do + { + std::wstring file_name(info->FileName, info->FileNameLength / 2); + + FileEvent event; + event.path = std::string(watch_info->path.begin(), watch_info->path.end()) + "\\" + std::string(file_name.begin(), file_name.end()); + + switch (info->Action) + { + case FILE_ACTION_ADDED: + case FILE_ACTION_RENAMED_NEW_NAME: + event.type = FileEventType::kCreated; + break; + case FILE_ACTION_MODIFIED: + event.type = FileEventType::kModified; + break; + case FILE_ACTION_REMOVED: + case FILE_ACTION_RENAMED_OLD_NAME: + event.type = FileEventType::kDeleted; + break; + default: + continue; + } + + watch_info->handler(event); + } while ((info = reinterpret_cast( + reinterpret_cast(info) + info->NextEntryOffset))); + } +} +#else +void *FileMonitor::LinuxThreadFunc(void *arg) +{ + auto watch_info = static_cast(arg); + + char buffer[1024]; + ssize_t len; + + while ((len = read(watch_info->handle, buffer, sizeof(buffer))) > 0) + { + char *ptr = buffer; + while (ptr < buffer + len) + { + auto event = reinterpret_cast(ptr); + + FileEvent file_event; + file_event.path = watch_info->path + "/" + event->name; + + switch (event->mask & (IN_CREATE | IN_MOVED_TO | IN_MODIFY | IN_DELETE | IN_MOVED_FROM)) + { + case IN_CREATE: + case IN_MOVED_TO: + file_event.type = FileEventType::kCreated; + break; + case IN_MODIFY: + file_event.type = FileEventType::kModified; + break; + case IN_DELETE: + case IN_MOVED_FROM: + file_event.type = FileEventType::kDeleted; + break; + default: + continue; + } + + watch_info->handler(file_event); + + ptr += sizeof(inotify_event) + event->len; + } + } + + return nullptr; +} +#endif + +int main() +{ + FileMonitor monitor; + + if (!monitor.AddWatch(".", OnFileEvent)) + { + std::cerr << "Failed to add watch." << std::endl; + return 1; + } + + std::cin.get(); + + if (!monitor.RemoveWatch(".")) + { + std::cerr << "Failed to remove watch." << std::endl; + return 1; + } + + return 0; +} diff --git a/src/modules/system/filew.hpp b/src/modules/system/filew.hpp new file mode 100644 index 00000000..e3f5b093 --- /dev/null +++ b/src/modules/system/filew.hpp @@ -0,0 +1,159 @@ +/* + * filww.hpp + * + * Copyright (C) 2023 Max Qian + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/************************************************* + +Copyright: 2023 Max Qian. All rights reserved + +Author: Max Qian + +E-mail: astro_air@126.com + +Date: 2023-10-27 + +Description: File Watcher + +**************************************************/ + +#pragma once + +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#include +#endif + +/** + * @brief Enumerates the types of file events that can be monitored. + */ +enum class FileEventType +{ + kCreated, /**< A new file was created. */ + kModified, /**< An existing file was modified. */ + kDeleted /**< A file was deleted. */ +}; + +/** + * @brief Represents a file event that occurred. + */ +struct FileEvent +{ + std::string path; /**< The path of the file that triggered the event. */ + FileEventType type; /**< The type of the event that occurred. */ +}; + +/** + * @brief Defines a callback function that is invoked when a file event occurs. + */ +using FileEventHandler = std::function; + +/** + * @brief Monitors files and directories for changes. + */ +class FileMonitor +{ +public: + /** + * @brief Constructor for the FileMonitor class. + */ + FileMonitor(); + + /** + * @brief Destructor for the FileMonitor class. + */ + ~FileMonitor(); + + /** + * @brief Adds a watch for a file or directory. + * + * @param path The path to the file or directory to watch. + * @param handler The callback function to invoke when a file event occurs. + * + * @return True if the watch was successfully added, false otherwise. + */ + bool AddWatch(const std::string &path, const FileEventHandler &handler); + + /** + * @brief Removes a watch for a file or directory. + * + * @param path The path to the file or directory to unwatch. + * + * @return True if the watch was successfully removed, false otherwise. + */ + bool RemoveWatch(const std::string &path); + +private: + /** + * @brief Contains information about a watch. + */ + struct WatchInfo; + + /** + * @brief The type of handle used to identify a watch. + */ + using WatchHandle = decltype(CreateWatch("")); + + /** + * @brief Creates a new watch for a file or directory. + * + * @param path The path to the file or directory to watch. + * + * @return The handle to the new watch, or INVALID_HANDLE_VALUE on failure. + */ + WatchHandle CreateWatch(const std::string &path); + + /** + * @brief Destroys a watch. + * + * @param handle The handle to the watch to destroy. + */ + void DestroyWatch(WatchHandle handle); + + /** + * @brief The main monitoring loop. + */ + void MonitorLoop(); + +#ifdef _WIN32 + /** + * @brief The callback function for the Windows implementation. + * + * @param lpParameter A pointer to the WatchInfo structure associated with the watch that triggered the event. + * @param TimerOrWaitFired Not used. + */ + static void CALLBACK Win32Callback(PVOID lpParameter, BOOLEAN TimerOrWaitFired); +#else + /** + * @brief The thread function for the Linux implementation. + * + * @param arg A pointer to the WatchInfo structure associated with the watch. + * + * @return Always returns nullptr. + */ + static void *LinuxThreadFunc(void *arg); +#endif + + std::vector m_watches; /**< The list of watches currently being monitored. */ + std::unordered_map m_handles; /**< A map of watch handles to their associated WatchInfo structures. */ + bool m_running; /**< Indicates whether the monitoring loop is running or not. */ +}; diff --git a/src/modules/system/process.cpp b/src/modules/system/process.cpp index 261a4e1a..1687b95d 100644 --- a/src/modules/system/process.cpp +++ b/src/modules/system/process.cpp @@ -408,4 +408,65 @@ namespace Lithium::Process #error "Unsupported operating system" #endif + Process GetSelfProcessInfo() + { + Process info; + + // 获取进程ID +#ifdef _WIN32 + DWORD pid = GetCurrentProcessId(); +#else + pid_t pid = getpid(); +#endif + info.pid = pid; + + // 获取进程位置 +#ifdef _WIN32 + char path[MAX_PATH]; + GetModuleFileName(NULL, path, MAX_PATH); +#else + char path[PATH_MAX]; + ssize_t count = readlink("/proc/self/exe", path, PATH_MAX); + if (count != -1) + { + path[count] = '\0'; + } +#endif + info.path = path; + + // 获取进程状态 +#ifdef _WIN32 + HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); + if (hProcess) + { + DWORD exitCode; + if (GetExitCodeProcess(hProcess, &exitCode)) + { + info.status = "Running"; + } + else + { + info.status = "Unknown"; + } + CloseHandle(hProcess); + } + else + { + info.status = "Unknown"; + } +#else + struct stat statBuf; + if (stat(path, &statBuf) == 0) + { + info.status = "Running"; + } + else + { + info.status = "Unknown"; + } +#endif + + return info; + } + } diff --git a/src/modules/system/process.hpp b/src/modules/system/process.hpp index 0368aa9e..e44d226e 100644 --- a/src/modules/system/process.hpp +++ b/src/modules/system/process.hpp @@ -57,6 +57,8 @@ namespace Lithium::Process pid_t pid; std::string name; std::string output; + std::string path; + std::string status; }; class ProcessManager @@ -123,4 +125,6 @@ namespace Lithium::Process std::vector> GetAllProcesses(); + Process GetSelfProcessInfo(); + } diff --git a/src/modules/utils/switch.cpp b/src/modules/utils/switch.cpp new file mode 100644 index 00000000..0cce0dfa --- /dev/null +++ b/src/modules/utils/switch.cpp @@ -0,0 +1,77 @@ +/* + * switch.cpp + * + * Copyright (C) 2023 Max Qian + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/************************************************* + +Copyright: 2023 Max Qian. All rights reserved + +Author: Max Qian + +E-mail: astro_air@126.com + +Date: 2023-10-27 + +Description: Smart Switch just like javascript + +**************************************************/ + +#include "switch.hpp" + +/** + * @brief Registers a case with the given string and function. + * + * @param str The string to match against. + * @param func The function to call if the string matches. + */ +void StringSwitch::registerCase(const std::string &str, Func func) +{ + cases()[str] = std::move(func); +} + +/** + * @brief Sets the default function to be called if no match is found. + * + * @param func The function to call for the default case. + */ +void StringSwitch::setDefault(DefaultFunc func) +{ + defaultFunc = std::move(func); +} + +std::unordered_map &StringSwitch::cases() +{ + static std::unordered_map cases; + return cases; +} + +StringSwitch::DefaultFunc StringSwitch::defaultFunc; + +/* + +int main() +{ + StringSwitch::registerCase("hello", [](int i) + { std::cout << "Hello " << i << std::endl; }); + + StringSwitch::setDefault([]() + { std::cout << "Default case" << std::endl; }); + + StringSwitch::match("hello", 42); + StringSwitch::match("world"); +} + +*/ \ No newline at end of file diff --git a/src/modules/utils/switch.hpp b/src/modules/utils/switch.hpp new file mode 100644 index 00000000..20ae2990 --- /dev/null +++ b/src/modules/utils/switch.hpp @@ -0,0 +1,95 @@ +/* + * switch.hpp + * + * Copyright (C) 2023 Max Qian + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/************************************************* + +Copyright: 2023 Max Qian. All rights reserved + +Author: Max Qian + +E-mail: astro_air@126.com + +Date: 2023-10-27 + +Description: Smart Switch just like javascript + +**************************************************/ + +#pragma once + +#include +#include +#include + +/** + * @brief A class for implementing a string switch statement. + */ +class StringSwitch +{ +public: + using Func = std::function; /**< The function type for handling a case. */ + using DefaultFunc = std::function; /**< The function type for handling the default case. */ + + /** + * @brief Registers a case with the given string and function. + * + * @param str The string to match against. + * @param func The function to call if the string matches. + */ + static void registerCase(const std::string &str, Func func); + + /** + * @brief Matches the given string against the registered cases. + * + * @tparam Args The types of the arguments to pass to the function. + * @param str The string to match against. + * @param args The arguments to pass to the function. + * @return true if a match was found, false otherwise. + */ + template + static bool match(const std::string &str, Args &&...args) + { + auto iter = cases().find(str); + if (iter != cases().end()) + { + if constexpr (sizeof...(args) > 0) + { + iter->second(std::forward(args)...); + } + return true; + } + + if (defaultFunc) + { + defaultFunc(); + return true; + } + + return false; + } + + /** + * @brief Sets the default function to be called if no match is found. + * + * @param func The function to call for the default case. + */ + static void setDefault(DefaultFunc func); + +private: + static std::unordered_map &cases(); /**< Returns the map of registered cases. */ + static DefaultFunc defaultFunc; /**< The default function to call if no match is found. */ +}; \ No newline at end of file