Project Alice
Loading...
Searching...
No Matches
simple_fs_nix.cpp
Go to the documentation of this file.
1#include <dirent.h>
2#include <fcntl.h>
3#include <sys/mman.h>
4#include <sys/stat.h>
5#include <unistd.h>
6
7#include <codecvt>
8#include <locale>
9
10#include "simple_fs.hpp"
11#include "text.hpp"
12
13namespace simple_fs {
15#if defined(_GNU_SOURCE) || defined(_DEFAULT_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE)
16 if(mapping_handle) {
17 if(munmap(mapping_handle, content.file_size) == -1) {
18 // error
19 }
20 }
21#else
22 if(file_buffer) {
23 free(file_buffer);
24 }
25#endif
26 if(file_descriptor != -1) {
27 close(file_descriptor);
28 }
29}
30
31file::file(file&& other) noexcept {
32 file_descriptor = other.file_descriptor;
33#if defined(_GNU_SOURCE) || defined(_DEFAULT_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE)
34 mapping_handle = other.mapping_handle;
35#else
36 file_buffer = other.file_buffer;
37#endif
38 content = other.content;
39 absolute_path = std::move(other.absolute_path);
40
41 other.file_descriptor = -1;
42#if defined(_GNU_SOURCE) || defined(_DEFAULT_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE)
43 other.mapping_handle = nullptr;
44#else
45 other.file_buffer = nullptr;
46#endif
47}
48void file::operator=(file&& other) noexcept {
49 file_descriptor = other.file_descriptor;
50#if defined(_GNU_SOURCE) || defined(_DEFAULT_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE)
51 mapping_handle = other.mapping_handle;
52#else
53 file_buffer = other.file_buffer;
54#endif
55 content = other.content;
56 absolute_path = std::move(other.absolute_path);
57
58 other.file_descriptor = -1;
59#if defined(_GNU_SOURCE) || defined(_DEFAULT_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE)
60 other.mapping_handle = nullptr;
61#else
62 other.file_buffer = nullptr;
63#endif
64}
65
66file::file(native_string const& full_path) {
67 file_descriptor = open(full_path.c_str(), O_RDONLY | O_NONBLOCK);
68 if(file_descriptor != -1) {
69 absolute_path = full_path;
70 struct stat sb;
71 if(fstat(file_descriptor, &sb) != -1) {
72 content.file_size = sb.st_size;
73#if _POSIX_C_SOURCE >= 200112L
74 posix_fadvise(file_descriptor, 0, static_cast<off_t>(content.file_size), POSIX_FADV_WILLNEED);
75#endif
76#if defined(_GNU_SOURCE) || defined(_DEFAULT_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE)
77 mapping_handle = mmap(0, content.file_size, PROT_READ, MAP_PRIVATE, file_descriptor, 0);
78 if(mapping_handle == MAP_FAILED) {
79 // error
80 }
81 content.data = static_cast<char*>(mapping_handle);
82#else
83 file_buffer = malloc(content.file_size);
84 read(file_descriptor, file_buffer, content.file_size);
85 content.data = static_cast<char*>(file_buffer);
86#endif
87 }
88 }
89}
90file::file(int file_descriptor, native_string const& full_path) : file_descriptor(file_descriptor) {
91 absolute_path = full_path;
92 struct stat sb;
93 if(fstat(file_descriptor, &sb) != -1) {
94 content.file_size = sb.st_size;
95#if defined(_GNU_SOURCE) || defined(_DEFAULT_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE)
96 mapping_handle = mmap(0, content.file_size, PROT_READ, MAP_PRIVATE, file_descriptor, 0);
97 if(mapping_handle == MAP_FAILED) {
98 // error
99 }
100 content.data = static_cast<char*>(mapping_handle);
101#else
102 file_buffer = malloc(content.file_size);
103 read(file_descriptor, file_buffer, content.file_size);
104 content.data = static_cast<char*>(file_buffer);
105#endif
106 }
107}
108
109std::optional<file> open_file(unopened_file const& f) {
110 std::optional<file> result(file{f.absolute_path});
111 if(!result->content.data) {
112 result = std::optional<file>{};
113 }
114 return result;
115}
116
118 fs.ordered_roots.clear();
119 fs.ignored_paths.clear();
120}
121
123 fs.ordered_roots.emplace_back(root_path);
124}
125
127 char module_name[1024];
128 ssize_t path_used = -1;
129 if((path_used = readlink("/proc/self/exe", module_name, sizeof(module_name) - 1)) != -1) {
130 while(path_used >= 0 && module_name[path_used] != '/') {
131 module_name[path_used] = '\0';
132 --path_used;
133 }
134 } else {
135 char tmpbuf[512];
136 snprintf(tmpbuf, sizeof(tmpbuf), "/proc/%u/exe", unsigned(getpid()));
137 if((path_used = readlink(tmpbuf, module_name, sizeof(module_name) - 1)) != -1) {
138 while(path_used >= 0 && module_name[path_used] != '/') {
139 module_name[path_used] = '\0';
140 --path_used;
141 }
142 } else {
143 // error
144 std::abort();
145 }
146 }
147
148 fs.ordered_roots.push_back(native_string(module_name) + native_string(root_path));
149}
150
152 return directory(&fs, NATIVE(""));
153}
154
156 native_string result;
157 for(auto const& str : fs.ordered_roots) {
158 result += NATIVE(";") + str;
159 }
160 result += NATIVE("?");
161 for(auto const& replace_path : fs.ignored_paths) {
162 result += replace_path + NATIVE(";");
163 }
164 return result;
165}
166
169 auto break_position = std::find(data.data(), data.data() + data.length(), NATIVE('?'));
170 // Parse ordered roots
171 {
172 auto position = data.data() + 1;
173 auto end = break_position;
174 while(position < end) {
175 auto next_semicolon = std::find(position, end, NATIVE(';'));
176 fs.ordered_roots.emplace_back(position, next_semicolon);
177 position = next_semicolon + 1;
178 }
179 }
180 // Replaced paths
181 {
182 auto position = break_position + 1;
183 auto end = data.data() + data.length();
184 while(position < end) {
185 auto next_semicolon = std::find(position, end, NATIVE(';'));
186 fs.ignored_paths.emplace_back(position, next_semicolon);
187 position = next_semicolon + 1;
188 }
189 }
190}
191
192namespace impl {
194 for(auto c = str; *c != 0; ++c) {
195 if(int32_t(*c) > 127 || int32_t(*c) < 0)
196 return true;
197 }
198 return false;
199}
200} // namespace impl
201
202std::vector<unopened_file> list_files(directory const& dir, native_char const* extension) {
203 std::vector<unopened_file> accumulated_results;
204 if(dir.parent_system) {
205 for(size_t i = dir.parent_system->ordered_roots.size(); i-- > 0;) {
206 auto const appended_path = dir.parent_system->ordered_roots[i] + dir.relative_path;
207 if(simple_fs::is_ignored_path(*dir.parent_system, appended_path + NATIVE("/"))) {
208 continue;
209 }
210
211 DIR* d = opendir(appended_path.c_str());
212 if(d) {
213 struct dirent* dir_ent = nullptr;
214 while((dir_ent = readdir(d)) != nullptr) {
215 // Check if it's a file. Not POSIX standard but included in Linux
216 if(dir_ent->d_type != DT_REG)
217 continue;
218
219 // Check if the file is of the right extension
220 if(extension && extension[0] != 0) {
221 char* dot = strrchr(dir_ent->d_name, '.');
222 if(!dot || dot == dir_ent->d_name)
223 continue;
224 if(strcmp(dot, extension))
225 continue;
226 }
227
228 if(impl::contains_non_ascii(dir_ent->d_name))
229 continue;
230
231 auto search_result = std::find_if(accumulated_results.begin(), accumulated_results.end(),
232 [n = dir_ent->d_name](auto const& f) { return f.file_name.compare(n) == 0; });
233 if(search_result == accumulated_results.end()) {
234 accumulated_results.emplace_back(
235 dir.parent_system->ordered_roots[i] + dir.relative_path + NATIVE("/") + dir_ent->d_name, dir_ent->d_name);
236 }
237 }
238 closedir(d);
239 }
240 }
241 } else {
242 auto const appended_path = dir.relative_path;
243 DIR* d = opendir(appended_path.c_str());
244 if(d) {
245 struct dirent* dir_ent = nullptr;
246 while((dir_ent = readdir(d)) != nullptr) {
247 // Check if it's a file. Not POSIX standard but included in Linux
248 if(dir_ent->d_type != DT_REG)
249 continue;
250
251 // Check if the file is of the right extension
252 if(extension && extension[0] != 0) {
253 char* dot = strrchr(dir_ent->d_name, '.');
254 if(!dot || dot == dir_ent->d_name)
255 continue;
256 if(strcmp(dot, extension))
257 continue;
258 }
259
260 if(impl::contains_non_ascii(dir_ent->d_name))
261 continue;
262
263 accumulated_results.emplace_back(dir.relative_path + NATIVE("/") + dir_ent->d_name, dir_ent->d_name);
264 }
265 closedir(d);
266 }
267 }
268 std::sort(accumulated_results.begin(), accumulated_results.end(), [](unopened_file const& a, unopened_file const& b) {
269 return std::lexicographical_compare(std::begin(a.file_name), std::end(a.file_name), std::begin(b.file_name),
270 std::end(b.file_name),
271 [](native_char const& char1, native_char const& char2) { return tolower(char1) < tolower(char2); });
272 });
273 return accumulated_results;
274}
275std::vector<directory> list_subdirectories(directory const& dir) {
276 std::vector<directory> accumulated_results;
277 if(dir.parent_system) {
278 for(size_t i = dir.parent_system->ordered_roots.size(); i-- > 0;) {
279 auto const appended_path = dir.parent_system->ordered_roots[i] + dir.relative_path;
280 if(simple_fs::is_ignored_path(*dir.parent_system, appended_path + NATIVE("/"))) {
281 continue;
282 }
283 DIR* d = opendir(appended_path.c_str());
284 if(d) {
285 struct dirent* dir_ent = nullptr;
286 while((dir_ent = readdir(d)) != nullptr) {
287 // Check if it's a directory. Not POSIX standard but included in Linux
288 if(dir_ent->d_type != DT_DIR)
289 continue;
290
291 if(impl::contains_non_ascii(dir_ent->d_name))
292 continue;
293
294 native_string const rel_name = dir.relative_path + NATIVE("/") + dir_ent->d_name;
295 if(dir_ent->d_name[0] != NATIVE('.')) {
296 auto search_result = std::find_if(accumulated_results.begin(), accumulated_results.end(),
297 [&rel_name](auto const& s) { return s.relative_path.compare(rel_name) == 0; });
298 if(search_result == accumulated_results.end()) {
299 accumulated_results.emplace_back(dir.parent_system, rel_name);
300 }
301 }
302 }
303 closedir(d);
304 }
305 }
306 } else {
307 auto const appended_path = dir.relative_path;
308 DIR* d = opendir(appended_path.c_str());
309 if(d) {
310 struct dirent* dir_ent = nullptr;
311 while((dir_ent = readdir(d)) != nullptr) {
312 // Check if it's a directory. Not POSIX standard but included in Linux
313 if(dir_ent->d_type != DT_DIR)
314 continue;
315
316 if(impl::contains_non_ascii(dir_ent->d_name))
317 continue;
318
319 native_string const rel_name = dir.relative_path + NATIVE("/") + dir_ent->d_name;
320 if(dir_ent->d_name[0] != NATIVE('.')) {
321 accumulated_results.emplace_back(nullptr, rel_name);
322 }
323 }
324 closedir(d);
325 }
326 }
327 std::sort(accumulated_results.begin(), accumulated_results.end(), [](directory const& a, directory const& b) {
328 return std::lexicographical_compare(std::begin(a.relative_path), std::end(a.relative_path), std::begin(b.relative_path),
329 std::end(b.relative_path),
330 [](native_char const& char1, native_char const& char2) { return tolower(char1) < tolower(char2); });
331 });
332
333 return accumulated_results;
334}
335
337 return directory(dir.parent_system, dir.relative_path + NATIVE('/') + native_string(directory_name));
338}
339
340std::optional<file> open_file(directory const& dir, native_string_view file_name) {
341 if(dir.parent_system) {
342 for(size_t i = dir.parent_system->ordered_roots.size(); i-- > 0;) {
343 native_string dir_path = dir.parent_system->ordered_roots[i] + dir.relative_path;
344 native_string full_path = dir_path + NATIVE('/') + native_string(file_name);
345 if(simple_fs::is_ignored_path(*dir.parent_system, full_path)) {
346 continue;
347 }
348 int file_descriptor = open(full_path.c_str(), O_RDONLY | O_NONBLOCK);
349 if(file_descriptor != -1) {
350 return std::optional<file>(file(file_descriptor, full_path));
351 }
352 }
353 } else {
354 native_string full_path = dir.relative_path + NATIVE('/') + native_string(file_name);
355 int file_descriptor = open(full_path.c_str(), O_RDONLY | O_NONBLOCK);
356 if(file_descriptor != -1) {
357 return std::optional<file>(file(file_descriptor, full_path));
358 }
359 }
360 return std::optional<file>{};
361}
362
363std::optional<unopened_file> peek_file(directory const& dir, native_string_view file_name) {
364 if(dir.parent_system) {
365 for(size_t i = dir.parent_system->ordered_roots.size(); i-- > 0;) {
366 native_string full_path = dir.parent_system->ordered_roots[i] + dir.relative_path + NATIVE('/') + native_string(file_name);
367 if(simple_fs::is_ignored_path(*dir.parent_system, full_path)) {
368 continue;
369 }
370 struct stat stat_buf;
371 int result = stat(full_path.c_str(), &stat_buf);
372 if(result != -1 && S_ISREG(stat_buf.st_mode)) {
373 return std::optional<unopened_file>(unopened_file(full_path, file_name));
374 }
375 }
376 } else {
377 native_string full_path = dir.relative_path + NATIVE('/') + native_string(file_name);
378 struct stat stat_buf;
379 int result = stat(full_path.c_str(), &stat_buf);
380 if(result != -1 && S_ISREG(stat_buf.st_mode)) {
381 return std::optional<unopened_file>(unopened_file(full_path, file_name));
382 }
383 }
384 return std::optional<unopened_file>{};
385}
386
388 fs.ignored_paths.emplace_back(replaced_path);
389}
390
391std::vector<native_string> list_roots(file_system const& fs) {
392 return fs.ordered_roots;
393}
394
396 for(auto const& replace_path : fs.ignored_paths) {
397 if(path.starts_with(replace_path))
398 return true;
399 }
400 return false;
401}
402
404 return f.absolute_path;
405}
406
408 return f.file_name;
409}
410
412 return f.absolute_path;
413}
414
416 return dir.relative_path;
417}
418
419void write_file(directory const& dir, native_string_view file_name, char const* file_data, uint32_t file_size) {
420 if(dir.parent_system)
421 std::abort();
422
423 native_string full_path = dir.relative_path + NATIVE('/') + native_string(file_name);
424
425 mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
426 int file_handle = open(full_path.c_str(), O_RDWR | O_CREAT | O_TRUNC, mode);
427 if(file_handle != -1) {
428 ssize_t written = 0;
429 int64_t size_remaining = file_size;
430 do {
431 written = write(file_handle, file_data, size_t(size_remaining));
432 file_data += written;
433 size_remaining -= written;
434 } while(written >= 0 && size_remaining > 0);
435
436 fsync(file_handle);
437 close(file_handle);
438 }
439}
440
442 return f.content;
443}
444
446 char current_path[FILENAME_MAX];
447 mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
448
449 strcpy(current_path, path.c_str());
450
451 for(char* p = strchr(current_path + 1, '/'); p; p = strchr(p + 1, '/')) {
452 *p = '\0';
453 int status = mkdir(current_path, mode);
454 if(status == -1) {
455 // error
456 }
457 *p = '/';
458 }
459}
460
462 native_string path = native_string(getenv("HOME")) + "/.local/share/Alice/settings/";
463 make_directories(path);
464
465 return directory(nullptr, path);
466}
467
469 native_string path = native_string(getenv("HOME")) + "/.local/share/Alice/saves/";
470 make_directories(path);
471
472 return directory(nullptr, path);
473}
474
476 native_string path = native_string(getenv("HOME")) + "/.local/share/Alice/";
477 make_directories(path);
478
479 return directory(nullptr, path);
480}
481
483 native_string path = native_string(getenv("HOME")) + "/.local/share/Alice/templates/";
484 make_directories(path);
485
486 return directory(nullptr, path);
487}
488
490 native_string path = native_string(getenv("HOME")) + "/.local/share/Alice/oos/";
491 make_directories(path);
492
493 return directory(nullptr, path);
494}
495
497 native_string path = native_string(getenv("HOME")) + "/.local/share/Alice/data_dumps/";
498 make_directories(path);
499
500 return directory(nullptr, path);
501}
502
504 native_string path = native_string(getenv("HOME")) + "/.local/share/Alice/scenarios/";
505 make_directories(path);
506
507 return directory(nullptr, path);
508}
509
510native_string win1250_to_native(std::string_view data_in) {
511 std::string result;
512 std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter;
513 for(auto ch : data_in) {
514 if(ch >= 0)
515 result += ch;
516 else
517 result += converter.to_bytes(text::win1250toUTF16(ch));
518 }
519 return result;
520}
521
522native_string utf8_to_native(std::string_view str) {
523 return std::string(str);
524}
525
527 return std::string(str);
528}
529
530std::string remove_double_backslashes(std::string_view data_in) {
531 std::string res;
532 res.reserve(data_in.size());
533 for(uint32_t i = 0; i < data_in.size(); ++i) {
534 if(data_in[i] == '\\') {
535 res += '/';
536 if(i + 1 < data_in.size() && data_in[i + 1] == '\\')
537 ++i;
538 } else {
539 res += data_in[i];
540 }
541 }
542 return res;
543}
544
546 std::string res;
547 res.reserve(path.size());
548 for(size_t i = 0; i < path.size(); i++) {
549 res += path[i] == '\\' ? '/' : path[i];
550 }
551 return res;
552}
553
554} // namespace simple_fs
void operator=(file const &other)=delete
bool contains_non_ascii(native_char const *str)
std::vector< unopened_file > list_files(directory const &dir, native_char const *extension)
void add_root(file_system &fs, native_string_view root_path)
void reset(file_system &fs)
native_string win1250_to_native(std::string_view data_in)
void add_relative_root(file_system &fs, native_string_view root_path)
std::vector< directory > list_subdirectories(directory const &dir)
directory open_directory(directory const &dir, native_string_view directory_name)
void add_ignore_path(file_system &fs, native_string_view replaced_path)
native_string utf8_to_native(std::string_view data_in)
native_string extract_state(file_system const &fs)
void make_directories(native_string const &path)
directory get_root(file_system const &fs)
void restore_state(file_system &fs, native_string_view data)
void write_file(directory const &dir, native_string_view file_name, char const *file_data, uint32_t file_size)
directory get_or_create_oos_directory()
std::vector< native_string > list_roots(file_system const &fs)
directory get_or_create_templates_directory()
std::string remove_double_backslashes(std::string_view data_in)
directory get_or_create_settings_directory()
native_string get_full_name(directory const &f)
native_string correct_slashes(native_string_view path)
directory get_or_create_save_game_directory()
std::optional< file > open_file(directory const &dir, native_string_view file_name)
directory get_or_create_root_documents()
directory get_or_create_data_dumps_directory()
bool is_ignored_path(file_system const &fs, native_string_view path)
std::optional< unopened_file > peek_file(directory const &dir, native_string_view file_name)
std::string native_to_utf8(native_string_view data_in)
file_contents view_contents(file const &f)
native_string get_file_name(unopened_file const &f)
directory get_or_create_scenario_directory()
char16_t win1250toUTF16(char in)
Definition: text.cpp:587
char native_char
#define NATIVE(X)
std::string_view native_string_view
std::string native_string
uint uint32_t
#define ssize_t
#define snprintf
#define getpid