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,264 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// FileSystem/MacOS/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 <unistd.h>
|
|
11
|
+
#include <stdio.h> // rename
|
|
12
|
+
#include <sys/stat.h>
|
|
13
|
+
#include <sys/types.h>
|
|
14
|
+
|
|
15
|
+
#include <cerrno>
|
|
16
|
+
#include <cstring>
|
|
17
|
+
#include <cstdlib>
|
|
18
|
+
#include <set>
|
|
19
|
+
#include <algorithm>
|
|
20
|
+
#include <filesystem>
|
|
21
|
+
#include <stdexcept>
|
|
22
|
+
#include <string>
|
|
23
|
+
#include <vector>
|
|
24
|
+
|
|
25
|
+
#include "../filesystem_handler.h"
|
|
26
|
+
|
|
27
|
+
extern char **environ;
|
|
28
|
+
|
|
29
|
+
namespace wpp::fs {
|
|
30
|
+
|
|
31
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
static std::vector<uint8_t> base64Decode(const std::string& b64) {
|
|
34
|
+
static const int kDec[256] = {
|
|
35
|
+
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
36
|
+
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
37
|
+
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,
|
|
38
|
+
52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,
|
|
39
|
+
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
|
|
40
|
+
15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
|
|
41
|
+
-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
|
|
42
|
+
41,42,43,44,45,46,47,48,49,50,51,-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
|
+
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
49
|
+
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
|
|
50
|
+
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
|
|
51
|
+
};
|
|
52
|
+
std::vector<uint8_t> out;
|
|
53
|
+
out.reserve(b64.size() * 3 / 4);
|
|
54
|
+
uint32_t buf = 0;
|
|
55
|
+
int bits = 0;
|
|
56
|
+
for (unsigned char c : b64) {
|
|
57
|
+
if (c == '=') break;
|
|
58
|
+
int v = kDec[c];
|
|
59
|
+
if (v < 0) continue;
|
|
60
|
+
buf = (buf << 6) | static_cast<uint32_t>(v);
|
|
61
|
+
bits += 6;
|
|
62
|
+
if (bits >= 8) {
|
|
63
|
+
bits -= 8;
|
|
64
|
+
out.push_back(static_cast<uint8_t>((buf >> bits) & 0xFF));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static void writeData(const std::string& path,
|
|
71
|
+
const void* data, size_t size, bool append) {
|
|
72
|
+
int flags = O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC);
|
|
73
|
+
int fd = open(path.c_str(), flags, 0644);
|
|
74
|
+
if (fd < 0)
|
|
75
|
+
throw std::runtime_error("Cannot open '" + path + "' for writing: " +
|
|
76
|
+
std::string(strerror(errno)));
|
|
77
|
+
ssize_t n = write(fd, data, size);
|
|
78
|
+
close(fd);
|
|
79
|
+
if (n < 0 || static_cast<size_t>(n) != size)
|
|
80
|
+
throw std::runtime_error("Write failed for '" + path + "'");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
static void launchDetached(const std::vector<std::string>& args) {
|
|
84
|
+
if (args.empty()) {
|
|
85
|
+
throw std::runtime_error("launchDetached failed: no command provided");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
std::vector<char*> argv;
|
|
89
|
+
argv.reserve(args.size() + 1);
|
|
90
|
+
for (const auto& arg : args) {
|
|
91
|
+
argv.push_back(const_cast<char*>(arg.c_str()));
|
|
92
|
+
}
|
|
93
|
+
argv.push_back(nullptr);
|
|
94
|
+
|
|
95
|
+
pid_t pid = 0;
|
|
96
|
+
const int rc = posix_spawnp(&pid, argv[0], nullptr, nullptr, argv.data(), environ);
|
|
97
|
+
if (rc != 0) {
|
|
98
|
+
throw std::runtime_error("Failed to launch '" + args[0] + "': " + std::string(strerror(rc)));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
static std::string toLowerAscii(std::string value) {
|
|
103
|
+
std::transform(value.begin(), value.end(), value.begin(), [](unsigned char c) {
|
|
104
|
+
return static_cast<char>(std::tolower(c));
|
|
105
|
+
});
|
|
106
|
+
return value;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ─── fs:writeFile ─────────────────────────────────────────────────────────────
|
|
110
|
+
|
|
111
|
+
void handleWriteFile(const std::string& path, const std::string& text) {
|
|
112
|
+
writeData(path, text.data(), text.size(), false);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ─── fs:writeBinaryFile ───────────────────────────────────────────────────────
|
|
116
|
+
|
|
117
|
+
void handleWriteBinaryFile(const std::string& path, const std::string& b64) {
|
|
118
|
+
auto bytes = base64Decode(b64);
|
|
119
|
+
writeData(path, bytes.data(), bytes.size(), false);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ─── fs:appendFile ────────────────────────────────────────────────────────────
|
|
123
|
+
|
|
124
|
+
void handleAppendFile(const std::string& path, const std::string& text) {
|
|
125
|
+
writeData(path, text.data(), text.size(), true);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ─── fs:copyFile ─────────────────────────────────────────────────────────────
|
|
129
|
+
|
|
130
|
+
void handleCopyFile(const std::string& src, const std::string& dest) {
|
|
131
|
+
namespace fs = std::filesystem;
|
|
132
|
+
std::error_code ec;
|
|
133
|
+
fs::copy_file(src, dest, fs::copy_options::overwrite_existing, ec);
|
|
134
|
+
if (ec) throw std::runtime_error("copyFile failed: " + ec.message());
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ─── fs:moveFile ─────────────────────────────────────────────────────────────
|
|
138
|
+
|
|
139
|
+
void handleMoveFile(const std::string& src, const std::string& dest) {
|
|
140
|
+
namespace fs = std::filesystem;
|
|
141
|
+
std::error_code ec;
|
|
142
|
+
fs::rename(src, dest, ec);
|
|
143
|
+
if (ec) throw std::runtime_error("moveFile failed: " + ec.message());
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ─── fs:deleteFile ────────────────────────────────────────────────────────────
|
|
147
|
+
|
|
148
|
+
void handleDeleteFile(const std::string& path) {
|
|
149
|
+
if (unlink(path.c_str()) != 0)
|
|
150
|
+
throw std::runtime_error("deleteFile failed for '" + path + "': " +
|
|
151
|
+
std::string(strerror(errno)));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ─── fs:createDir ────────────────────────────────────────────────────────────
|
|
155
|
+
|
|
156
|
+
void handleCreateDir(const std::string& path, bool recursive) {
|
|
157
|
+
namespace fs = std::filesystem;
|
|
158
|
+
std::error_code ec;
|
|
159
|
+
if (recursive)
|
|
160
|
+
fs::create_directories(path, ec);
|
|
161
|
+
else
|
|
162
|
+
fs::create_directory(path, ec);
|
|
163
|
+
if (ec) throw std::runtime_error("createDir failed: " + ec.message());
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ─── fs:removeDir ────────────────────────────────────────────────────────────
|
|
167
|
+
|
|
168
|
+
void handleRemoveDir(const std::string& path, bool recursive) {
|
|
169
|
+
namespace fs = std::filesystem;
|
|
170
|
+
std::error_code ec;
|
|
171
|
+
if (recursive)
|
|
172
|
+
fs::remove_all(path, ec);
|
|
173
|
+
else
|
|
174
|
+
fs::remove(path, ec);
|
|
175
|
+
if (ec) throw std::runtime_error("removeDir failed: " + ec.message());
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
void handleOpenPath(const std::string& path) {
|
|
179
|
+
namespace fs = std::filesystem;
|
|
180
|
+
std::error_code ec;
|
|
181
|
+
if (!fs::exists(path, ec) || ec) {
|
|
182
|
+
throw std::runtime_error("openPath failed: path does not exist: " + path);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
launchDetached({"open", path});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
void handleRevealPath(const std::string& path) {
|
|
189
|
+
namespace fs = std::filesystem;
|
|
190
|
+
std::error_code ec;
|
|
191
|
+
fs::path target(path);
|
|
192
|
+
if (!fs::exists(target, ec) || ec) {
|
|
193
|
+
throw std::runtime_error("revealPath failed: path does not exist: " + path);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (fs::is_directory(target, ec)) {
|
|
197
|
+
launchDetached({"open", path});
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
launchDetached({"open", "-R", path});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
std::vector<ApplicationEntry> handleListApplications() {
|
|
205
|
+
namespace fs = std::filesystem;
|
|
206
|
+
|
|
207
|
+
std::vector<fs::path> roots = {
|
|
208
|
+
"/Applications",
|
|
209
|
+
"/System/Applications",
|
|
210
|
+
"/System/Applications/Utilities",
|
|
211
|
+
};
|
|
212
|
+
if (const char* home = std::getenv("HOME")) {
|
|
213
|
+
roots.emplace_back(std::string(home) + "/Applications");
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
std::vector<ApplicationEntry> apps;
|
|
217
|
+
std::set<std::string> seen;
|
|
218
|
+
for (const auto& root : roots) {
|
|
219
|
+
std::error_code ec;
|
|
220
|
+
if (!fs::exists(root, ec) || ec) {
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
fs::recursive_directory_iterator it(root, fs::directory_options::skip_permission_denied, ec);
|
|
225
|
+
fs::recursive_directory_iterator end;
|
|
226
|
+
for (; it != end; it.increment(ec)) {
|
|
227
|
+
if (ec) {
|
|
228
|
+
ec.clear();
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const auto& entry = *it;
|
|
233
|
+
if (!entry.is_directory(ec) || ec) {
|
|
234
|
+
ec.clear();
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (toLowerAscii(entry.path().extension().string()) != ".app") {
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
it.disable_recursion_pending();
|
|
243
|
+
const std::string path = entry.path().string();
|
|
244
|
+
if (!seen.insert(path).second) {
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
apps.push_back(ApplicationEntry{
|
|
249
|
+
path,
|
|
250
|
+
entry.path().stem().string(),
|
|
251
|
+
path,
|
|
252
|
+
path,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return apps;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
void handleLaunchApplication(const std::string& target) {
|
|
261
|
+
launchDetached({"open", target});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
} // namespace wpp::fs
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// FileSystem/Windows/filesearch.cpp
|
|
3
|
+
// Handles: fs:exists, fs:stat, fs:readDir, fs:searchFiles
|
|
4
|
+
// ============================================================================
|
|
5
|
+
|
|
6
|
+
#define WIN32_LEAN_AND_MEAN
|
|
7
|
+
#define NOMINMAX
|
|
8
|
+
#include <windows.h>
|
|
9
|
+
|
|
10
|
+
#include <filesystem>
|
|
11
|
+
#include <stdexcept>
|
|
12
|
+
#include <string>
|
|
13
|
+
#include <vector>
|
|
14
|
+
|
|
15
|
+
// PathMatchSpecW is in shlwapi
|
|
16
|
+
#pragma comment(lib, "shlwapi.lib")
|
|
17
|
+
#include <shlwapi.h>
|
|
18
|
+
|
|
19
|
+
#include "../filesystem_handler.h"
|
|
20
|
+
|
|
21
|
+
namespace wpp::fs {
|
|
22
|
+
|
|
23
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
static std::wstring toWide(const std::string& utf8) {
|
|
26
|
+
if (utf8.empty()) return {};
|
|
27
|
+
int len = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, nullptr, 0);
|
|
28
|
+
std::wstring ws(len, L'\0');
|
|
29
|
+
MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, ws.data(), len);
|
|
30
|
+
if (!ws.empty() && ws.back() == L'\0') ws.pop_back();
|
|
31
|
+
return ws;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static std::string toUtf8(const std::wstring& ws) {
|
|
35
|
+
if (ws.empty()) return {};
|
|
36
|
+
int len = WideCharToMultiByte(CP_UTF8, 0, ws.c_str(), -1,
|
|
37
|
+
nullptr, 0, nullptr, nullptr);
|
|
38
|
+
std::string s(len, '\0');
|
|
39
|
+
WideCharToMultiByte(CP_UTF8, 0, ws.c_str(), -1,
|
|
40
|
+
s.data(), len, nullptr, nullptr);
|
|
41
|
+
if (!s.empty() && s.back() == '\0') s.pop_back();
|
|
42
|
+
return s;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static int64_t filetimeToUnixMs(const FILETIME& ft) {
|
|
46
|
+
// FILETIME: 100-ns intervals since 1601-01-01
|
|
47
|
+
uint64_t ticks = (static_cast<uint64_t>(ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
|
|
48
|
+
// Offset from 1601-01-01 to 1970-01-01 in 100-ns intervals
|
|
49
|
+
constexpr uint64_t kEpochOffset = 116444736000000000ULL;
|
|
50
|
+
if (ticks < kEpochOffset) return 0;
|
|
51
|
+
return static_cast<int64_t>((ticks - kEpochOffset) / 10000);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ─── fs:exists ───────────────────────────────────────────────────────────────
|
|
55
|
+
|
|
56
|
+
bool handleExists(const std::string& path) {
|
|
57
|
+
DWORD attr = GetFileAttributesW(toWide(path).c_str());
|
|
58
|
+
return attr != INVALID_FILE_ATTRIBUTES;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ─── fs:stat ─────────────────────────────────────────────────────────────────
|
|
62
|
+
|
|
63
|
+
FileStat handleStat(const std::string& path) {
|
|
64
|
+
std::wstring wpath = toWide(path);
|
|
65
|
+
WIN32_FILE_ATTRIBUTE_DATA info{};
|
|
66
|
+
if (!GetFileAttributesExW(wpath.c_str(), GetFileExInfoStandard, &info)) {
|
|
67
|
+
DWORD err = GetLastError();
|
|
68
|
+
throw std::runtime_error("stat failed for '" + path +
|
|
69
|
+
"' (Win32 error " + std::to_string(err) + ")");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Extract file name
|
|
73
|
+
auto fsPath = std::filesystem::path(wpath);
|
|
74
|
+
std::string name = toUtf8(fsPath.filename().wstring());
|
|
75
|
+
|
|
76
|
+
uint64_t size =
|
|
77
|
+
(static_cast<uint64_t>(info.nFileSizeHigh) << 32) | info.nFileSizeLow;
|
|
78
|
+
bool isDir = (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
|
79
|
+
bool isLink = (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
|
|
80
|
+
|
|
81
|
+
return FileStat{
|
|
82
|
+
path,
|
|
83
|
+
name,
|
|
84
|
+
static_cast<int64_t>(size),
|
|
85
|
+
isDir,
|
|
86
|
+
isLink,
|
|
87
|
+
filetimeToUnixMs(info.ftLastWriteTime),
|
|
88
|
+
filetimeToUnixMs(info.ftCreationTime),
|
|
89
|
+
-1 // mode not available on Windows
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ─── fs:readDir ──────────────────────────────────────────────────────────────
|
|
94
|
+
|
|
95
|
+
std::vector<DirEntry> handleReadDir(const std::string& path) {
|
|
96
|
+
std::wstring wpattern = toWide(path) + L"\\*";
|
|
97
|
+
WIN32_FIND_DATAW ffd{};
|
|
98
|
+
HANDLE hFind = FindFirstFileW(wpattern.c_str(), &ffd);
|
|
99
|
+
if (hFind == INVALID_HANDLE_VALUE) {
|
|
100
|
+
DWORD err = GetLastError();
|
|
101
|
+
if (err == ERROR_FILE_NOT_FOUND) return {};
|
|
102
|
+
throw std::runtime_error("readDir failed for '" + path +
|
|
103
|
+
"' (Win32 error " + std::to_string(err) + ")");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
std::vector<DirEntry> entries;
|
|
107
|
+
do {
|
|
108
|
+
std::wstring wname = ffd.cFileName;
|
|
109
|
+
if (wname == L"." || wname == L"..") continue;
|
|
110
|
+
bool isDir = (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
|
111
|
+
bool isLink = (ffd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
|
|
112
|
+
std::string name = toUtf8(wname);
|
|
113
|
+
entries.push_back(DirEntry{name, path + "\\" + name, isDir, isLink});
|
|
114
|
+
} while (FindNextFileW(hFind, &ffd));
|
|
115
|
+
FindClose(hFind);
|
|
116
|
+
return entries;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
std::vector<std::string> handleListRoots() {
|
|
120
|
+
DWORD length = GetLogicalDriveStringsW(0, nullptr);
|
|
121
|
+
if (length == 0) {
|
|
122
|
+
return {};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
std::wstring buffer(length, L'\0');
|
|
126
|
+
GetLogicalDriveStringsW(length, buffer.data());
|
|
127
|
+
|
|
128
|
+
std::vector<std::string> roots;
|
|
129
|
+
const wchar_t* current = buffer.c_str();
|
|
130
|
+
while (*current) {
|
|
131
|
+
roots.push_back(toUtf8(current));
|
|
132
|
+
current += wcslen(current) + 1;
|
|
133
|
+
}
|
|
134
|
+
return roots;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ─── fs:searchFiles ──────────────────────────────────────────────────────────
|
|
138
|
+
|
|
139
|
+
static void searchRecursive(const std::wstring& dir,
|
|
140
|
+
const std::wstring& wpattern,
|
|
141
|
+
bool recursive,
|
|
142
|
+
bool includeHidden,
|
|
143
|
+
bool includeDirectories,
|
|
144
|
+
size_t maxResults,
|
|
145
|
+
std::vector<std::string>& results) {
|
|
146
|
+
if (maxResults > 0 && results.size() >= maxResults) return;
|
|
147
|
+
|
|
148
|
+
std::wstring query = dir + L"\\*";
|
|
149
|
+
WIN32_FIND_DATAW ffd{};
|
|
150
|
+
HANDLE hFind = FindFirstFileW(query.c_str(), &ffd);
|
|
151
|
+
if (hFind == INVALID_HANDLE_VALUE) return;
|
|
152
|
+
|
|
153
|
+
do {
|
|
154
|
+
if (maxResults > 0 && results.size() >= maxResults) break;
|
|
155
|
+
|
|
156
|
+
std::wstring wname = ffd.cFileName;
|
|
157
|
+
if (wname == L"." || wname == L"..") continue;
|
|
158
|
+
|
|
159
|
+
bool hidden = (!wname.empty() && wname[0] == L'.') ||
|
|
160
|
+
(ffd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0;
|
|
161
|
+
if (hidden && !includeHidden) continue;
|
|
162
|
+
|
|
163
|
+
bool isDir = (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
|
164
|
+
std::wstring fullW = dir + L"\\" + wname;
|
|
165
|
+
|
|
166
|
+
if (isDir) {
|
|
167
|
+
if (includeDirectories && PathMatchSpecW(wname.c_str(), wpattern.c_str())) {
|
|
168
|
+
results.push_back(toUtf8(fullW));
|
|
169
|
+
}
|
|
170
|
+
if (recursive) {
|
|
171
|
+
searchRecursive(fullW, wpattern, recursive,
|
|
172
|
+
includeHidden, includeDirectories, maxResults, results);
|
|
173
|
+
}
|
|
174
|
+
} else {
|
|
175
|
+
// Match filename against glob pattern
|
|
176
|
+
if (PathMatchSpecW(wname.c_str(), wpattern.c_str())) {
|
|
177
|
+
results.push_back(toUtf8(fullW));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
} while (FindNextFileW(hFind, &ffd));
|
|
181
|
+
FindClose(hFind);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
std::vector<std::string> handleSearchFiles(const std::string& dir,
|
|
185
|
+
const SearchOptions& opts) {
|
|
186
|
+
std::wstring wdir = toWide(dir);
|
|
187
|
+
std::wstring wpattern = toWide(opts.pattern.empty() ? "*" : opts.pattern);
|
|
188
|
+
std::vector<std::string> results;
|
|
189
|
+
searchRecursive(wdir, wpattern, opts.recursive, opts.includeHidden,
|
|
190
|
+
opts.includeDirectories,
|
|
191
|
+
opts.maxResults, results);
|
|
192
|
+
return results;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
} // namespace wpp::fs
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// FileSystem/Windows/readfile.cpp
|
|
3
|
+
// Handles: fs:readFile, fs:readBinaryFile
|
|
4
|
+
// ============================================================================
|
|
5
|
+
|
|
6
|
+
#define WIN32_LEAN_AND_MEAN
|
|
7
|
+
#define NOMINMAX
|
|
8
|
+
#include <windows.h>
|
|
9
|
+
|
|
10
|
+
#include <fstream>
|
|
11
|
+
#include <sstream>
|
|
12
|
+
#include <string>
|
|
13
|
+
#include <vector>
|
|
14
|
+
#include <stdexcept>
|
|
15
|
+
|
|
16
|
+
#include "../filesystem_handler.h"
|
|
17
|
+
|
|
18
|
+
namespace wpp::fs {
|
|
19
|
+
|
|
20
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
static std::wstring toWide(const std::string& utf8) {
|
|
23
|
+
if (utf8.empty()) return {};
|
|
24
|
+
int len = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, nullptr, 0);
|
|
25
|
+
std::wstring ws(len, L'\0');
|
|
26
|
+
MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, ws.data(), len);
|
|
27
|
+
// Remove the null terminator added by MultiByteToWideChar
|
|
28
|
+
if (!ws.empty() && ws.back() == L'\0') ws.pop_back();
|
|
29
|
+
return ws;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static const std::string kB64Chars =
|
|
33
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
34
|
+
|
|
35
|
+
static std::string base64Encode(const std::vector<uint8_t>& bytes) {
|
|
36
|
+
std::string out;
|
|
37
|
+
out.reserve(((bytes.size() + 2) / 3) * 4);
|
|
38
|
+
size_t i = 0;
|
|
39
|
+
for (; i + 2 < bytes.size(); i += 3) {
|
|
40
|
+
uint32_t v = (bytes[i] << 16) | (bytes[i+1] << 8) | bytes[i+2];
|
|
41
|
+
out += kB64Chars[(v >> 18) & 0x3F];
|
|
42
|
+
out += kB64Chars[(v >> 12) & 0x3F];
|
|
43
|
+
out += kB64Chars[(v >> 6) & 0x3F];
|
|
44
|
+
out += kB64Chars[(v ) & 0x3F];
|
|
45
|
+
}
|
|
46
|
+
if (i < bytes.size()) {
|
|
47
|
+
uint32_t v = bytes[i] << 16;
|
|
48
|
+
if (i + 1 < bytes.size()) v |= bytes[i+1] << 8;
|
|
49
|
+
out += kB64Chars[(v >> 18) & 0x3F];
|
|
50
|
+
out += kB64Chars[(v >> 12) & 0x3F];
|
|
51
|
+
out += (i + 1 < bytes.size()) ? kB64Chars[(v >> 6) & 0x3F] : '=';
|
|
52
|
+
out += '=';
|
|
53
|
+
}
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ─── fs:readFile ─────────────────────────────────────────────────────────────
|
|
58
|
+
|
|
59
|
+
std::string handleReadFile(const std::string& path) {
|
|
60
|
+
std::wstring wpath = toWide(path);
|
|
61
|
+
HANDLE hFile = CreateFileW(
|
|
62
|
+
wpath.c_str(), GENERIC_READ, FILE_SHARE_READ,
|
|
63
|
+
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
|
64
|
+
|
|
65
|
+
if (hFile == INVALID_HANDLE_VALUE) {
|
|
66
|
+
DWORD err = GetLastError();
|
|
67
|
+
throw std::runtime_error("Cannot open file '" + path +
|
|
68
|
+
"' (Win32 error " + std::to_string(err) + ")");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
LARGE_INTEGER fileSize{};
|
|
72
|
+
if (!GetFileSizeEx(hFile, &fileSize)) {
|
|
73
|
+
CloseHandle(hFile);
|
|
74
|
+
throw std::runtime_error("Cannot get size of '" + path + "'");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
std::string text(static_cast<size_t>(fileSize.QuadPart), '\0');
|
|
78
|
+
DWORD bytesRead = 0;
|
|
79
|
+
if (!ReadFile(hFile, text.data(), static_cast<DWORD>(text.size()),
|
|
80
|
+
&bytesRead, nullptr)) {
|
|
81
|
+
CloseHandle(hFile);
|
|
82
|
+
throw std::runtime_error("Read failed for '" + path + "'");
|
|
83
|
+
}
|
|
84
|
+
CloseHandle(hFile);
|
|
85
|
+
text.resize(bytesRead);
|
|
86
|
+
return text;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ─── fs:readBinaryFile ───────────────────────────────────────────────────────
|
|
90
|
+
|
|
91
|
+
// Returns the file bytes as a Base64-encoded string.
|
|
92
|
+
std::string handleReadBinaryFile(const std::string& path) {
|
|
93
|
+
std::wstring wpath = toWide(path);
|
|
94
|
+
HANDLE hFile = CreateFileW(
|
|
95
|
+
wpath.c_str(), GENERIC_READ, FILE_SHARE_READ,
|
|
96
|
+
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
|
97
|
+
|
|
98
|
+
if (hFile == INVALID_HANDLE_VALUE) {
|
|
99
|
+
DWORD err = GetLastError();
|
|
100
|
+
throw std::runtime_error("Cannot open file '" + path +
|
|
101
|
+
"' (Win32 error " + std::to_string(err) + ")");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
LARGE_INTEGER fileSize{};
|
|
105
|
+
if (!GetFileSizeEx(hFile, &fileSize)) {
|
|
106
|
+
CloseHandle(hFile);
|
|
107
|
+
throw std::runtime_error("Cannot get size of '" + path + "'");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
std::vector<uint8_t> bytes(static_cast<size_t>(fileSize.QuadPart));
|
|
111
|
+
DWORD bytesRead = 0;
|
|
112
|
+
if (!ReadFile(hFile, bytes.data(), static_cast<DWORD>(bytes.size()),
|
|
113
|
+
&bytesRead, nullptr)) {
|
|
114
|
+
CloseHandle(hFile);
|
|
115
|
+
throw std::runtime_error("Read failed for '" + path + "'");
|
|
116
|
+
}
|
|
117
|
+
CloseHandle(hFile);
|
|
118
|
+
bytes.resize(bytesRead);
|
|
119
|
+
return base64Encode(bytes);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
} // namespace wpp::fs
|