windowpp 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/windowpp.js +86 -0
- package/cmake/embed_assets.py +144 -0
- package/framework/CMakeLists.txt +176 -0
- package/framework/include/windowpp/windowpp.h +704 -0
- package/framework/src/AppData/API/AppData.ts +137 -0
- package/framework/src/AppData/appdata_bridge.h +138 -0
- package/framework/src/AppData/appdata_manager.cpp +126 -0
- package/framework/src/AppData/appdata_manager.h +3 -0
- package/framework/src/FileSystem/API/FileSystem.ts +389 -0
- package/framework/src/FileSystem/Linux/filesearch.cpp +148 -0
- package/framework/src/FileSystem/Linux/readfile.cpp +79 -0
- package/framework/src/FileSystem/Linux/savefile.cpp +333 -0
- package/framework/src/FileSystem/MacOS/filesearch.cpp +149 -0
- package/framework/src/FileSystem/MacOS/readfile.cpp +80 -0
- package/framework/src/FileSystem/MacOS/savefile.cpp +264 -0
- package/framework/src/FileSystem/Windows/filesearch.cpp +195 -0
- package/framework/src/FileSystem/Windows/readfile.cpp +122 -0
- package/framework/src/FileSystem/Windows/savefile.cpp +290 -0
- package/framework/src/FileSystem/file_index_service.cpp +262 -0
- package/framework/src/FileSystem/file_index_service.h +55 -0
- package/framework/src/FileSystem/filesystem_bridge.h +243 -0
- package/framework/src/FileSystem/filesystem_handler.h +93 -0
- package/framework/src/FileSystem/filesystem_json.h +241 -0
- package/framework/src/FileSystem/filesystem_search_service.cpp +414 -0
- package/framework/src/FileSystem/filesystem_search_service.h +94 -0
- package/framework/src/Input/API/Input.ts +161 -0
- package/framework/src/Input/Linux/linux_key_utils.h +135 -0
- package/framework/src/Input/MacOS/macos_key_utils.h +137 -0
- package/framework/src/Input/Windows/win32_key_utils.h +199 -0
- package/framework/src/Input/input_bridge.h +192 -0
- package/framework/src/Input/input_service.cpp +584 -0
- package/framework/src/Input/input_service.h +21 -0
- package/framework/src/application.cpp +29 -0
- package/framework/src/common/hit_test.cpp +40 -0
- package/framework/src/common/image_loader.cpp +24 -0
- package/framework/src/common/paths.cpp +75 -0
- package/framework/src/filedrop/filedrop.cpp +316 -0
- package/framework/src/filedrop/filedrop.css +421 -0
- package/framework/src/filedrop/filedrop.hpp +92 -0
- package/framework/src/filedrop/filedrop.ts +183 -0
- package/framework/src/platform/API/App.ts +156 -0
- package/framework/src/platform/API/Window.ts +249 -0
- package/framework/src/platform/linux/app_linux.cpp +256 -0
- package/framework/src/platform/linux/app_linux.h +64 -0
- package/framework/src/platform/linux/linux_helpers.cpp +26 -0
- package/framework/src/platform/linux/linux_helpers.h +19 -0
- package/framework/src/platform/linux/tray_linux.cpp +21 -0
- package/framework/src/platform/linux/tray_linux.h +26 -0
- package/framework/src/platform/linux/window_linux.cpp +256 -0
- package/framework/src/platform/linux/window_linux.h +70 -0
- package/framework/src/platform/macos/app_macos.h +59 -0
- package/framework/src/platform/macos/app_macos.mm +223 -0
- package/framework/src/platform/macos/macos_helpers.h +21 -0
- package/framework/src/platform/macos/tray_macos.h +22 -0
- package/framework/src/platform/macos/tray_macos.mm +53 -0
- package/framework/src/platform/macos/window_macos.h +74 -0
- package/framework/src/platform/macos/window_macos.mm +318 -0
- package/framework/src/platform/platform_bridge.h +514 -0
- package/framework/src/platform/platform_factory.cpp +33 -0
- package/framework/src/platform/platform_factory.h +19 -0
- package/framework/src/platform/win32/app_win32.cpp +572 -0
- package/framework/src/platform/win32/app_win32.h +83 -0
- package/framework/src/platform/win32/tray_win32.cpp +57 -0
- package/framework/src/platform/win32/tray_win32.h +30 -0
- package/framework/src/platform/win32/win32_helpers.h +61 -0
- package/framework/src/platform/win32/window_win32.cpp +267 -0
- package/framework/src/platform/win32/window_win32.h +79 -0
- package/framework/src/renderer/webgpu.h +128 -0
- package/framework/src/renderer/webview/include/WebView2.h +48014 -0
- package/framework/src/renderer/webview/include/WebView2EnvironmentOptions.h +342 -0
- package/framework/src/renderer/webview/webview.h +13 -0
- package/framework/src/renderer/webview/webview_linux.cpp +392 -0
- package/framework/src/renderer/webview/webview_macos.mm +388 -0
- package/framework/src/renderer/webview/webview_win32.cpp +688 -0
- package/framework/src/renderer/webview/x64/WebView2Loader.dll +0 -0
- package/framework/src/renderer/webview/x64/WebView2Loader.lib +0 -0
- package/framework/src/renderer/webview/x64/WebView2LoaderStatic.lib +0 -0
- package/lib/build.js +112 -0
- package/lib/create.js +283 -0
- package/lib/dev.js +155 -0
- package/package.json +24 -0
- package/scripts/publish.js +67 -0
- package/scripts/sync-framework.js +73 -0
- package/templates/solid/CMakeLists.txt +56 -0
- package/templates/solid/frontend/package.json +22 -0
- package/templates/solid/frontend/vite.config.ts +25 -0
- package/templates/solid/main.cpp +72 -0
- package/templates/solid/package.json +12 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// FileSystem/Linux/savefile.cpp
|
|
3
|
+
// Handles: fs:writeFile, fs:writeBinaryFile, fs:appendFile,
|
|
4
|
+
// fs:copyFile, fs:moveFile, fs:deleteFile,
|
|
5
|
+
// fs:createDir, fs:removeDir
|
|
6
|
+
// ============================================================================
|
|
7
|
+
|
|
8
|
+
#include <fcntl.h>
|
|
9
|
+
#include <spawn.h>
|
|
10
|
+
#include <sys/types.h>
|
|
11
|
+
#include <unistd.h>
|
|
12
|
+
|
|
13
|
+
#include <cerrno>
|
|
14
|
+
#include <cstring>
|
|
15
|
+
#include <fstream>
|
|
16
|
+
#include <filesystem>
|
|
17
|
+
#include <set>
|
|
18
|
+
#include <stdexcept>
|
|
19
|
+
#include <string>
|
|
20
|
+
#include <vector>
|
|
21
|
+
|
|
22
|
+
#include "../filesystem_handler.h"
|
|
23
|
+
|
|
24
|
+
extern char **environ;
|
|
25
|
+
|
|
26
|
+
namespace wpp::fs {
|
|
27
|
+
|
|
28
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
static std::vector<uint8_t> base64Decode(const std::string& b64) {
|
|
31
|
+
static const int kDec[256] = {
|
|
32
|
+
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
33
|
+
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
34
|
+
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,
|
|
35
|
+
52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,
|
|
36
|
+
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
|
|
37
|
+
15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
|
|
38
|
+
-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
|
|
39
|
+
41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,
|
|
40
|
+
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
41
|
+
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
42
|
+
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
43
|
+
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
44
|
+
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
45
|
+
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
46
|
+
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
47
|
+
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
|
|
48
|
+
};
|
|
49
|
+
std::vector<uint8_t> out;
|
|
50
|
+
out.reserve(b64.size() * 3 / 4);
|
|
51
|
+
uint32_t buf = 0;
|
|
52
|
+
int bits = 0;
|
|
53
|
+
for (unsigned char c : b64) {
|
|
54
|
+
if (c == '=') break;
|
|
55
|
+
int v = kDec[c];
|
|
56
|
+
if (v < 0) continue;
|
|
57
|
+
buf = (buf << 6) | static_cast<uint32_t>(v);
|
|
58
|
+
bits += 6;
|
|
59
|
+
if (bits >= 8) {
|
|
60
|
+
bits -= 8;
|
|
61
|
+
out.push_back(static_cast<uint8_t>((buf >> bits) & 0xFF));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
static void writeData(const std::string& path,
|
|
68
|
+
const void* data, size_t size, bool append) {
|
|
69
|
+
int flags = O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC);
|
|
70
|
+
int fd = open(path.c_str(), flags, 0644);
|
|
71
|
+
if (fd < 0)
|
|
72
|
+
throw std::runtime_error("Cannot open '" + path + "' for writing: " +
|
|
73
|
+
std::string(strerror(errno)));
|
|
74
|
+
ssize_t n = write(fd, data, size);
|
|
75
|
+
close(fd);
|
|
76
|
+
if (n < 0 || static_cast<size_t>(n) != size)
|
|
77
|
+
throw std::runtime_error("Write failed for '" + path + "'");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
static void launchDetached(const std::vector<std::string>& args) {
|
|
81
|
+
if (args.empty()) {
|
|
82
|
+
throw std::runtime_error("launchDetached failed: no command provided");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
std::vector<char*> argv;
|
|
86
|
+
argv.reserve(args.size() + 1);
|
|
87
|
+
for (const auto& arg : args) {
|
|
88
|
+
argv.push_back(const_cast<char*>(arg.c_str()));
|
|
89
|
+
}
|
|
90
|
+
argv.push_back(nullptr);
|
|
91
|
+
|
|
92
|
+
pid_t pid = 0;
|
|
93
|
+
const int rc = posix_spawnp(&pid, argv[0], nullptr, nullptr, argv.data(), environ);
|
|
94
|
+
if (rc != 0) {
|
|
95
|
+
throw std::runtime_error("Failed to launch '" + args[0] + "': " + std::string(strerror(rc)));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
static bool tryLaunchDetached(const std::vector<std::string>& args) {
|
|
100
|
+
if (args.empty()) return false;
|
|
101
|
+
|
|
102
|
+
std::vector<char*> argv;
|
|
103
|
+
argv.reserve(args.size() + 1);
|
|
104
|
+
for (const auto& arg : args) {
|
|
105
|
+
argv.push_back(const_cast<char*>(arg.c_str()));
|
|
106
|
+
}
|
|
107
|
+
argv.push_back(nullptr);
|
|
108
|
+
|
|
109
|
+
pid_t pid = 0;
|
|
110
|
+
const int rc = posix_spawnp(&pid, argv[0], nullptr, nullptr, argv.data(), environ);
|
|
111
|
+
return rc == 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
static std::string trim(std::string value) {
|
|
115
|
+
const auto first = value.find_first_not_of(" \t\r\n");
|
|
116
|
+
if (first == std::string::npos) return {};
|
|
117
|
+
const auto last = value.find_last_not_of(" \t\r\n");
|
|
118
|
+
return value.substr(first, last - first + 1);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
static bool parseDesktopEntry(const std::string& path, ApplicationEntry& out) {
|
|
122
|
+
std::ifstream file(path);
|
|
123
|
+
if (!file.is_open()) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
bool in_desktop_entry = false;
|
|
128
|
+
bool hidden = false;
|
|
129
|
+
bool no_display = false;
|
|
130
|
+
bool is_application = false;
|
|
131
|
+
std::string name;
|
|
132
|
+
|
|
133
|
+
std::string line;
|
|
134
|
+
while (std::getline(file, line)) {
|
|
135
|
+
line = trim(line);
|
|
136
|
+
if (line.empty() || line[0] == '#') continue;
|
|
137
|
+
if (line.front() == '[' && line.back() == ']') {
|
|
138
|
+
in_desktop_entry = (line == "[Desktop Entry]");
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (!in_desktop_entry) continue;
|
|
142
|
+
|
|
143
|
+
const auto equals = line.find('=');
|
|
144
|
+
if (equals == std::string::npos) continue;
|
|
145
|
+
|
|
146
|
+
const std::string key = trim(line.substr(0, equals));
|
|
147
|
+
const std::string value = trim(line.substr(equals + 1));
|
|
148
|
+
if (key == "Name" && name.empty()) {
|
|
149
|
+
name = value;
|
|
150
|
+
} else if (key == "Hidden") {
|
|
151
|
+
hidden = value == "true" || value == "1";
|
|
152
|
+
} else if (key == "NoDisplay") {
|
|
153
|
+
no_display = value == "true" || value == "1";
|
|
154
|
+
} else if (key == "Type") {
|
|
155
|
+
is_application = value == "Application";
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (!is_application || hidden || no_display) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const std::filesystem::path fs_path(path);
|
|
164
|
+
out = ApplicationEntry{
|
|
165
|
+
fs_path.filename().string(),
|
|
166
|
+
name.empty() ? fs_path.stem().string() : name,
|
|
167
|
+
path,
|
|
168
|
+
path,
|
|
169
|
+
};
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ─── fs:writeFile ─────────────────────────────────────────────────────────────
|
|
174
|
+
|
|
175
|
+
void handleWriteFile(const std::string& path, const std::string& text) {
|
|
176
|
+
writeData(path, text.data(), text.size(), false);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// ─── fs:writeBinaryFile ───────────────────────────────────────────────────────
|
|
180
|
+
|
|
181
|
+
void handleWriteBinaryFile(const std::string& path, const std::string& b64) {
|
|
182
|
+
auto bytes = base64Decode(b64);
|
|
183
|
+
writeData(path, bytes.data(), bytes.size(), false);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ─── fs:appendFile ────────────────────────────────────────────────────────────
|
|
187
|
+
|
|
188
|
+
void handleAppendFile(const std::string& path, const std::string& text) {
|
|
189
|
+
writeData(path, text.data(), text.size(), true);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ─── fs:copyFile ─────────────────────────────────────────────────────────────
|
|
193
|
+
|
|
194
|
+
void handleCopyFile(const std::string& src, const std::string& dest) {
|
|
195
|
+
namespace fs = std::filesystem;
|
|
196
|
+
std::error_code ec;
|
|
197
|
+
fs::copy_file(src, dest, fs::copy_options::overwrite_existing, ec);
|
|
198
|
+
if (ec) throw std::runtime_error("copyFile failed: " + ec.message());
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// ─── fs:moveFile ─────────────────────────────────────────────────────────────
|
|
202
|
+
|
|
203
|
+
void handleMoveFile(const std::string& src, const std::string& dest) {
|
|
204
|
+
namespace fs = std::filesystem;
|
|
205
|
+
std::error_code ec;
|
|
206
|
+
fs::rename(src, dest, ec);
|
|
207
|
+
if (ec) throw std::runtime_error("moveFile failed: " + ec.message());
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ─── fs:deleteFile ────────────────────────────────────────────────────────────
|
|
211
|
+
|
|
212
|
+
void handleDeleteFile(const std::string& path) {
|
|
213
|
+
if (unlink(path.c_str()) != 0)
|
|
214
|
+
throw std::runtime_error("deleteFile failed for '" + path + "': " +
|
|
215
|
+
std::string(strerror(errno)));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ─── fs:createDir ────────────────────────────────────────────────────────────
|
|
219
|
+
|
|
220
|
+
void handleCreateDir(const std::string& path, bool recursive) {
|
|
221
|
+
namespace fs = std::filesystem;
|
|
222
|
+
std::error_code ec;
|
|
223
|
+
if (recursive)
|
|
224
|
+
fs::create_directories(path, ec);
|
|
225
|
+
else
|
|
226
|
+
fs::create_directory(path, ec);
|
|
227
|
+
if (ec) throw std::runtime_error("createDir failed: " + ec.message());
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// ─── fs:removeDir ────────────────────────────────────────────────────────────
|
|
231
|
+
|
|
232
|
+
void handleRemoveDir(const std::string& path, bool recursive) {
|
|
233
|
+
namespace fs = std::filesystem;
|
|
234
|
+
std::error_code ec;
|
|
235
|
+
if (recursive)
|
|
236
|
+
fs::remove_all(path, ec);
|
|
237
|
+
else
|
|
238
|
+
fs::remove(path, ec);
|
|
239
|
+
if (ec) throw std::runtime_error("removeDir failed: " + ec.message());
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
void handleOpenPath(const std::string& path) {
|
|
243
|
+
namespace fs = std::filesystem;
|
|
244
|
+
std::error_code ec;
|
|
245
|
+
if (!fs::exists(path, ec) || ec) {
|
|
246
|
+
throw std::runtime_error("openPath failed: path does not exist: " + path);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
launchDetached({"xdg-open", path});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
void handleRevealPath(const std::string& path) {
|
|
253
|
+
namespace fs = std::filesystem;
|
|
254
|
+
std::error_code ec;
|
|
255
|
+
fs::path target(path);
|
|
256
|
+
if (!fs::exists(target, ec) || ec) {
|
|
257
|
+
throw std::runtime_error("revealPath failed: path does not exist: " + path);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (fs::is_directory(target, ec)) {
|
|
261
|
+
launchDetached({"xdg-open", path});
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const fs::path parent = target.parent_path();
|
|
266
|
+
launchDetached({"xdg-open", parent.empty() ? std::string(".") : parent.string()});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
std::vector<ApplicationEntry> handleListApplications() {
|
|
270
|
+
namespace fs = std::filesystem;
|
|
271
|
+
|
|
272
|
+
std::vector<fs::path> roots = {
|
|
273
|
+
"/usr/share/applications",
|
|
274
|
+
"/usr/local/share/applications",
|
|
275
|
+
"/var/lib/flatpak/exports/share/applications",
|
|
276
|
+
};
|
|
277
|
+
if (const char* home = std::getenv("HOME")) {
|
|
278
|
+
roots.emplace_back(std::string(home) + "/.local/share/applications");
|
|
279
|
+
roots.emplace_back(std::string(home) + "/.local/share/flatpak/exports/share/applications");
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
std::vector<ApplicationEntry> apps;
|
|
283
|
+
std::set<std::string> seen;
|
|
284
|
+
for (const auto& root : roots) {
|
|
285
|
+
std::error_code ec;
|
|
286
|
+
if (!fs::exists(root, ec) || ec) {
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
fs::recursive_directory_iterator it(root, fs::directory_options::skip_permission_denied, ec);
|
|
291
|
+
fs::recursive_directory_iterator end;
|
|
292
|
+
for (; it != end; it.increment(ec)) {
|
|
293
|
+
if (ec) {
|
|
294
|
+
ec.clear();
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const auto& entry = *it;
|
|
299
|
+
if (!entry.is_regular_file(ec) || ec) {
|
|
300
|
+
ec.clear();
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
if (entry.path().extension() != ".desktop") {
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
ApplicationEntry app;
|
|
308
|
+
const std::string path = entry.path().string();
|
|
309
|
+
if (!parseDesktopEntry(path, app)) {
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
if (!seen.insert(app.id).second) {
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
apps.push_back(std::move(app));
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return apps;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
void handleLaunchApplication(const std::string& target) {
|
|
323
|
+
const std::string desktop_id = std::filesystem::path(target).filename().string();
|
|
324
|
+
if (tryLaunchDetached({"gio", "launch", target})) {
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
if (tryLaunchDetached({"gtk-launch", desktop_id})) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
launchDetached({"xdg-open", target});
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
} // namespace wpp::fs
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// FileSystem/MacOS/filesearch.cpp
|
|
3
|
+
// Handles: fs:exists, fs:stat, fs:readDir, fs:searchFiles
|
|
4
|
+
// ============================================================================
|
|
5
|
+
|
|
6
|
+
#include <dirent.h>
|
|
7
|
+
#include <fnmatch.h>
|
|
8
|
+
#include <sys/stat.h>
|
|
9
|
+
#include <unistd.h>
|
|
10
|
+
|
|
11
|
+
#include <cerrno>
|
|
12
|
+
#include <cstring>
|
|
13
|
+
#include <stdexcept>
|
|
14
|
+
#include <string>
|
|
15
|
+
#include <vector>
|
|
16
|
+
|
|
17
|
+
#include "../filesystem_handler.h"
|
|
18
|
+
|
|
19
|
+
namespace wpp::fs {
|
|
20
|
+
|
|
21
|
+
// ─── fs:exists ───────────────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
bool handleExists(const std::string& path) {
|
|
24
|
+
struct stat st{};
|
|
25
|
+
return stat(path.c_str(), &st) == 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ─── fs:stat ─────────────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
FileStat handleStat(const std::string& path) {
|
|
31
|
+
struct stat st{};
|
|
32
|
+
if (::stat(path.c_str(), &st) != 0)
|
|
33
|
+
throw std::runtime_error("stat failed for '" + path + "': " +
|
|
34
|
+
std::string(strerror(errno)));
|
|
35
|
+
|
|
36
|
+
// Extract file name from path
|
|
37
|
+
std::string name = path;
|
|
38
|
+
auto slash = path.rfind('/');
|
|
39
|
+
if (slash != std::string::npos) name = path.substr(slash + 1);
|
|
40
|
+
|
|
41
|
+
bool isDir = S_ISDIR(st.st_mode);
|
|
42
|
+
bool isLink = S_ISLNK(st.st_mode);
|
|
43
|
+
|
|
44
|
+
// macOS: st_birthtimespec holds creation time
|
|
45
|
+
int64_t createdMs = static_cast<int64_t>(st.st_birthtimespec.tv_sec) * 1000 +
|
|
46
|
+
st.st_birthtimespec.tv_nsec / 1000000;
|
|
47
|
+
int64_t modifiedMs = static_cast<int64_t>(st.st_mtimespec.tv_sec) * 1000 +
|
|
48
|
+
st.st_mtimespec.tv_nsec / 1000000;
|
|
49
|
+
|
|
50
|
+
return FileStat{
|
|
51
|
+
path,
|
|
52
|
+
name,
|
|
53
|
+
static_cast<int64_t>(st.st_size),
|
|
54
|
+
isDir,
|
|
55
|
+
isLink,
|
|
56
|
+
modifiedMs,
|
|
57
|
+
createdMs,
|
|
58
|
+
static_cast<int>(st.st_mode & 0777)
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ─── fs:readDir ──────────────────────────────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
std::vector<DirEntry> handleReadDir(const std::string& path) {
|
|
65
|
+
DIR* dir = opendir(path.c_str());
|
|
66
|
+
if (!dir)
|
|
67
|
+
throw std::runtime_error("readDir failed for '" + path + "': " +
|
|
68
|
+
std::string(strerror(errno)));
|
|
69
|
+
|
|
70
|
+
std::vector<DirEntry> entries;
|
|
71
|
+
struct dirent* ent;
|
|
72
|
+
while ((ent = readdir(dir)) != nullptr) {
|
|
73
|
+
std::string name = ent->d_name;
|
|
74
|
+
if (name == "." || name == "..") continue;
|
|
75
|
+
std::string full = path + "/" + name;
|
|
76
|
+
bool isDir = ent->d_type == DT_DIR;
|
|
77
|
+
bool isLink = ent->d_type == DT_LNK;
|
|
78
|
+
if (ent->d_type == DT_UNKNOWN) {
|
|
79
|
+
struct stat st{};
|
|
80
|
+
::stat(full.c_str(), &st);
|
|
81
|
+
isDir = S_ISDIR(st.st_mode);
|
|
82
|
+
isLink = S_ISLNK(st.st_mode);
|
|
83
|
+
}
|
|
84
|
+
entries.push_back(DirEntry{name, full, isDir, isLink});
|
|
85
|
+
}
|
|
86
|
+
closedir(dir);
|
|
87
|
+
return entries;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
std::vector<std::string> handleListRoots() {
|
|
91
|
+
return {"/"};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ─── fs:searchFiles ──────────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
static void searchRecursive(const std::string& dir,
|
|
97
|
+
const std::string& pattern,
|
|
98
|
+
bool recursive,
|
|
99
|
+
bool includeHidden,
|
|
100
|
+
bool includeDirectories,
|
|
101
|
+
size_t maxResults,
|
|
102
|
+
std::vector<std::string>& results) {
|
|
103
|
+
if (maxResults > 0 && results.size() >= maxResults) return;
|
|
104
|
+
|
|
105
|
+
DIR* d = opendir(dir.c_str());
|
|
106
|
+
if (!d) return;
|
|
107
|
+
|
|
108
|
+
struct dirent* ent;
|
|
109
|
+
while ((ent = readdir(d)) != nullptr) {
|
|
110
|
+
if (maxResults > 0 && results.size() >= maxResults) break;
|
|
111
|
+
std::string name = ent->d_name;
|
|
112
|
+
if (name == "." || name == "..") continue;
|
|
113
|
+
bool hidden = (!name.empty() && name[0] == '.');
|
|
114
|
+
if (hidden && !includeHidden) continue;
|
|
115
|
+
|
|
116
|
+
std::string full = dir + "/" + name;
|
|
117
|
+
bool isDir = ent->d_type == DT_DIR;
|
|
118
|
+
if (ent->d_type == DT_UNKNOWN) {
|
|
119
|
+
struct stat st{};
|
|
120
|
+
::stat(full.c_str(), &st);
|
|
121
|
+
isDir = S_ISDIR(st.st_mode);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (isDir) {
|
|
125
|
+
if (includeDirectories && fnmatch(pattern.c_str(), name.c_str(), FNM_CASEFOLD) == 0)
|
|
126
|
+
results.push_back(full);
|
|
127
|
+
if (recursive) {
|
|
128
|
+
searchRecursive(full, pattern, recursive,
|
|
129
|
+
includeHidden, includeDirectories, maxResults, results);
|
|
130
|
+
}
|
|
131
|
+
} else {
|
|
132
|
+
if (fnmatch(pattern.c_str(), name.c_str(), FNM_CASEFOLD) == 0)
|
|
133
|
+
results.push_back(full);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
closedir(d);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
std::vector<std::string> handleSearchFiles(const std::string& dir,
|
|
140
|
+
const SearchOptions& opts) {
|
|
141
|
+
std::string pat = opts.pattern.empty() ? "*" : opts.pattern;
|
|
142
|
+
std::vector<std::string> results;
|
|
143
|
+
searchRecursive(dir, pat, opts.recursive, opts.includeHidden,
|
|
144
|
+
opts.includeDirectories,
|
|
145
|
+
opts.maxResults, results);
|
|
146
|
+
return results;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
} // namespace wpp::fs
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// FileSystem/MacOS/readfile.cpp
|
|
3
|
+
// Handles: fs:readFile, fs:readBinaryFile
|
|
4
|
+
// ============================================================================
|
|
5
|
+
|
|
6
|
+
#include <fcntl.h>
|
|
7
|
+
#include <sys/stat.h>
|
|
8
|
+
#include <unistd.h>
|
|
9
|
+
|
|
10
|
+
#include <cerrno>
|
|
11
|
+
#include <cstring>
|
|
12
|
+
#include <stdexcept>
|
|
13
|
+
#include <string>
|
|
14
|
+
#include <vector>
|
|
15
|
+
|
|
16
|
+
#include "../filesystem_handler.h"
|
|
17
|
+
|
|
18
|
+
namespace wpp::fs {
|
|
19
|
+
|
|
20
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
static const std::string kB64Chars =
|
|
23
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
24
|
+
|
|
25
|
+
static std::string base64Encode(const std::vector<uint8_t>& bytes) {
|
|
26
|
+
std::string out;
|
|
27
|
+
out.reserve(((bytes.size() + 2) / 3) * 4);
|
|
28
|
+
size_t i = 0;
|
|
29
|
+
for (; i + 2 < bytes.size(); i += 3) {
|
|
30
|
+
uint32_t v = (bytes[i] << 16) | (bytes[i+1] << 8) | bytes[i+2];
|
|
31
|
+
out += kB64Chars[(v >> 18) & 0x3F];
|
|
32
|
+
out += kB64Chars[(v >> 12) & 0x3F];
|
|
33
|
+
out += kB64Chars[(v >> 6) & 0x3F];
|
|
34
|
+
out += kB64Chars[(v ) & 0x3F];
|
|
35
|
+
}
|
|
36
|
+
if (i < bytes.size()) {
|
|
37
|
+
uint32_t v = bytes[i] << 16;
|
|
38
|
+
if (i + 1 < bytes.size()) v |= bytes[i+1] << 8;
|
|
39
|
+
out += kB64Chars[(v >> 18) & 0x3F];
|
|
40
|
+
out += kB64Chars[(v >> 12) & 0x3F];
|
|
41
|
+
out += (i + 1 < bytes.size()) ? kB64Chars[(v >> 6) & 0x3F] : '=';
|
|
42
|
+
out += '=';
|
|
43
|
+
}
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
static std::vector<uint8_t> readAllBytes(const std::string& path) {
|
|
48
|
+
int fd = open(path.c_str(), O_RDONLY);
|
|
49
|
+
if (fd < 0) {
|
|
50
|
+
throw std::runtime_error("Cannot open '" + path + "': " +
|
|
51
|
+
std::string(strerror(errno)));
|
|
52
|
+
}
|
|
53
|
+
struct stat st{};
|
|
54
|
+
if (fstat(fd, &st) < 0) {
|
|
55
|
+
close(fd);
|
|
56
|
+
throw std::runtime_error("stat failed for '" + path + "'");
|
|
57
|
+
}
|
|
58
|
+
std::vector<uint8_t> buf(static_cast<size_t>(st.st_size));
|
|
59
|
+
ssize_t n = read(fd, buf.data(), buf.size());
|
|
60
|
+
close(fd);
|
|
61
|
+
if (n < 0)
|
|
62
|
+
throw std::runtime_error("Read failed for '" + path + "'");
|
|
63
|
+
buf.resize(static_cast<size_t>(n));
|
|
64
|
+
return buf;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ─── fs:readFile ─────────────────────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
std::string handleReadFile(const std::string& path) {
|
|
70
|
+
auto bytes = readAllBytes(path);
|
|
71
|
+
return std::string(bytes.begin(), bytes.end());
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ─── fs:readBinaryFile ───────────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
std::string handleReadBinaryFile(const std::string& path) {
|
|
77
|
+
return base64Encode(readAllBytes(path));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
} // namespace wpp::fs
|