| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Copyright (c) 2000-2022 Inria | ||
| 3 | * All rights reserved. | ||
| 4 | * | ||
| 5 | * Redistribution and use in source and binary forms, with or without | ||
| 6 | * modification, are permitted provided that the following conditions are met: | ||
| 7 | * | ||
| 8 | * * Redistributions of source code must retain the above copyright notice, | ||
| 9 | * this list of conditions and the following disclaimer. | ||
| 10 | * * Redistributions in binary form must reproduce the above copyright notice, | ||
| 11 | * this list of conditions and the following disclaimer in the documentation | ||
| 12 | * and/or other materials provided with the distribution. | ||
| 13 | * * Neither the name of the ALICE Project-Team nor the names of its | ||
| 14 | * contributors may be used to endorse or promote products derived from this | ||
| 15 | * software without specific prior written permission. | ||
| 16 | * | ||
| 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
| 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
| 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | ||
| 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
| 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
| 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
| 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
| 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| 27 | * POSSIBILITY OF SUCH DAMAGE. | ||
| 28 | * | ||
| 29 | * Contact: Bruno Levy | ||
| 30 | * | ||
| 31 | * https://www.inria.fr/fr/bruno-levy | ||
| 32 | * | ||
| 33 | * Inria, | ||
| 34 | * Domaine de Voluceau, | ||
| 35 | * 78150 Le Chesnay - Rocquencourt | ||
| 36 | * FRANCE | ||
| 37 | * | ||
| 38 | */ | ||
| 39 | |||
| 40 | #include <geogram/basic/file_system.h> | ||
| 41 | #include <geogram/basic/line_stream.h> | ||
| 42 | #include <geogram/basic/logger.h> | ||
| 43 | #include <geogram/basic/string.h> | ||
| 44 | |||
| 45 | #include <iostream> | ||
| 46 | #include <fstream> | ||
| 47 | #include <assert.h> | ||
| 48 | |||
| 49 | #include <sys/stat.h> | ||
| 50 | |||
| 51 | #ifdef GEO_OS_WINDOWS | ||
| 52 | #include <windows.h> | ||
| 53 | #include <io.h> | ||
| 54 | #include <shlobj.h> | ||
| 55 | constexpr char SEPARATOR = '\\'; | ||
| 56 | #else | ||
| 57 | #include <sys/types.h> | ||
| 58 | #include <sys/stat.h> | ||
| 59 | #include <fcntl.h> | ||
| 60 | #include <dirent.h> | ||
| 61 | #include <unistd.h> | ||
| 62 | #include <stdio.h> | ||
| 63 | constexpr char SEPARATOR = '/'; | ||
| 64 | #endif | ||
| 65 | |||
| 66 | #ifdef GEO_OS_EMSCRIPTEN | ||
| 67 | #include <emscripten.h> | ||
| 68 | #endif | ||
| 69 | |||
| 70 | namespace { | ||
| 71 | using namespace GEO; | ||
| 72 | |||
| 73 | /******************* Windows file system implementation **********************/ | ||
| 74 | #ifdef GEO_OS_WINDOWS | ||
| 75 | |||
| 76 | class FileSystemRootNode : public FileSystem::Node { | ||
| 77 | public: | ||
| 78 | bool is_file(const std::string& path) override { | ||
| 79 | WIN32_FIND_DATAA file; | ||
| 80 | HANDLE file_handle = FindFirstFileA(path.c_str(), &file); | ||
| 81 | if(file_handle == INVALID_HANDLE_VALUE) { | ||
| 82 | return false; | ||
| 83 | } | ||
| 84 | FindClose(file_handle); | ||
| 85 | return (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0; | ||
| 86 | } | ||
| 87 | |||
| 88 | bool is_directory(const std::string& path) override { | ||
| 89 | WIN32_FIND_DATAA file; | ||
| 90 | HANDLE file_handle = FindFirstFileA(path.c_str(), &file); | ||
| 91 | if(file_handle == INVALID_HANDLE_VALUE) { | ||
| 92 | return false; | ||
| 93 | } | ||
| 94 | FindClose(file_handle); | ||
| 95 | return (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; | ||
| 96 | } | ||
| 97 | |||
| 98 | bool create_directory(const std::string& path_in) override { | ||
| 99 | std::vector<std::string> path; | ||
| 100 | String::split_string(path_in, '/', path); | ||
| 101 | std::string current; | ||
| 102 | int start_at = 0; | ||
| 103 | if(path_in.at(1) == ':') { | ||
| 104 | current += path_in.at(0); | ||
| 105 | current += path_in.at(1); | ||
| 106 | start_at = 1; | ||
| 107 | } | ||
| 108 | else if(path_in.at(0) != '/' && path_in.at(0) != '\\') { | ||
| 109 | current += get_current_working_directory(); | ||
| 110 | } | ||
| 111 | for(size_t i = size_t(start_at); i < path.size(); i++) { | ||
| 112 | current += "/"; | ||
| 113 | current += path[i]; | ||
| 114 | if(path[i].at(0) == '.' && | ||
| 115 | path[i].at(1) == '.' && | ||
| 116 | path[i].length() == 2 | ||
| 117 | ) { | ||
| 118 | continue; | ||
| 119 | } | ||
| 120 | if(!is_directory(current)) { | ||
| 121 | if(!::CreateDirectoryA(current.c_str(), nullptr)) { | ||
| 122 | Logger::err("OS") | ||
| 123 | << "Could not create directory " | ||
| 124 | << current << std::endl; | ||
| 125 | return false; | ||
| 126 | } | ||
| 127 | } | ||
| 128 | } | ||
| 129 | return true; | ||
| 130 | } | ||
| 131 | |||
| 132 | bool delete_directory(const std::string& path) override { | ||
| 133 | return ::RemoveDirectoryA(path.c_str()) != FALSE; | ||
| 134 | } | ||
| 135 | |||
| 136 | bool delete_file(const std::string& path) override { | ||
| 137 | return ::DeleteFileA(path.c_str()) != FALSE; | ||
| 138 | } | ||
| 139 | |||
| 140 | bool get_directory_entries( | ||
| 141 | const std::string& path, std::vector<std::string>& result | ||
| 142 | ) override { | ||
| 143 | std::string dirname = path; | ||
| 144 | if(dirname.at(dirname.size() - 1) != '/' && | ||
| 145 | dirname.at(dirname.size() - 1) != '\\' | ||
| 146 | ) { | ||
| 147 | dirname += '/'; | ||
| 148 | } | ||
| 149 | |||
| 150 | std::string current_directory = get_current_working_directory(); | ||
| 151 | |||
| 152 | bool dir_found = set_current_working_directory(dirname); | ||
| 153 | if(!dir_found) { | ||
| 154 | return false; | ||
| 155 | } | ||
| 156 | |||
| 157 | WIN32_FIND_DATAA file; | ||
| 158 | HANDLE file_handle = FindFirstFileA("*.*", &file); | ||
| 159 | if(file_handle != INVALID_HANDLE_VALUE) { | ||
| 160 | do { | ||
| 161 | std::string file_name = file.cFileName; | ||
| 162 | if(file_name != "." && file_name != "..") { | ||
| 163 | file_name = dirname + file_name; | ||
| 164 | flip_slashes(file_name); | ||
| 165 | result.push_back(file_name); | ||
| 166 | } | ||
| 167 | } while(FindNextFileA(file_handle, &file)); | ||
| 168 | FindClose(file_handle); | ||
| 169 | } | ||
| 170 | set_current_working_directory(current_directory); | ||
| 171 | return true; | ||
| 172 | } | ||
| 173 | |||
| 174 | std::string get_current_working_directory() override { | ||
| 175 | char buf[2048]; | ||
| 176 | std::string result = ""; | ||
| 177 | if(GetCurrentDirectoryA(sizeof(buf), buf)) { | ||
| 178 | result = buf; | ||
| 179 | flip_slashes(result); | ||
| 180 | } | ||
| 181 | return result; | ||
| 182 | } | ||
| 183 | |||
| 184 | bool set_current_working_directory( | ||
| 185 | const std::string& path_in | ||
| 186 | ) override { | ||
| 187 | std::string path = path_in; | ||
| 188 | if( | ||
| 189 | path.at(path.size() - 1) != '/' && | ||
| 190 | path.at(path.size() - 1) != '\\') { | ||
| 191 | path += "/"; | ||
| 192 | } | ||
| 193 | return SetCurrentDirectoryA(path.c_str()) != -1; | ||
| 194 | } | ||
| 195 | |||
| 196 | bool rename_file( | ||
| 197 | const std::string& old_name, const std::string& new_name | ||
| 198 | ) override { | ||
| 199 | return ::rename(old_name.c_str(), new_name.c_str()) != -1; | ||
| 200 | } | ||
| 201 | |||
| 202 | Numeric::uint64 get_time_stamp(const std::string& path) override { | ||
| 203 | WIN32_FILE_ATTRIBUTE_DATA infos; | ||
| 204 | if(!GetFileAttributesExA( | ||
| 205 | path.c_str(), GetFileExInfoStandard, &infos) | ||
| 206 | ) { | ||
| 207 | return 0; | ||
| 208 | } | ||
| 209 | return infos.ftLastWriteTime.dwLowDateTime; | ||
| 210 | } | ||
| 211 | |||
| 212 | bool set_executable_flag(const std::string& filename) override { | ||
| 213 | geo_argused(filename); | ||
| 214 | return false; | ||
| 215 | } | ||
| 216 | |||
| 217 | bool touch(const std::string& filename) override { | ||
| 218 | HANDLE hfile = CreateFileA( | ||
| 219 | filename.c_str(), | ||
| 220 | GENERIC_READ | GENERIC_WRITE, | ||
| 221 | FILE_SHARE_READ | FILE_SHARE_WRITE, | ||
| 222 | nullptr, | ||
| 223 | OPEN_EXISTING, | ||
| 224 | FILE_ATTRIBUTE_NORMAL, | ||
| 225 | nullptr | ||
| 226 | ); | ||
| 227 | if(hfile == INVALID_HANDLE_VALUE) { | ||
| 228 | Logger::err("FileSystem") | ||
| 229 | << "Could not touch file:" | ||
| 230 | << filename | ||
| 231 | << std::endl; | ||
| 232 | return false; | ||
| 233 | } | ||
| 234 | SYSTEMTIME now_system; | ||
| 235 | FILETIME now_file; | ||
| 236 | GetSystemTime(&now_system); | ||
| 237 | SystemTimeToFileTime(&now_system, &now_file); | ||
| 238 | SetFileTime(hfile,nullptr,&now_file,&now_file); | ||
| 239 | CloseHandle(hfile); | ||
| 240 | return true; | ||
| 241 | } | ||
| 242 | |||
| 243 | std::string normalized_path(const std::string& path_in) override { | ||
| 244 | if(path_in == "") { | ||
| 245 | return ""; | ||
| 246 | } | ||
| 247 | std::string path = path_in; | ||
| 248 | std::string result; | ||
| 249 | char buffer[MAX_PATH]; | ||
| 250 | GetFullPathNameA(path.c_str(), MAX_PATH, buffer, nullptr); | ||
| 251 | result = std::string(buffer); | ||
| 252 | flip_slashes(result); | ||
| 253 | return result; | ||
| 254 | } | ||
| 255 | |||
| 256 | |||
| 257 | std::string home_directory() override { | ||
| 258 | std::string home; | ||
| 259 | wchar_t folder[MAX_PATH+1]; | ||
| 260 | HRESULT hr = SHGetFolderPathW(0, CSIDL_PROFILE, 0, 0, folder); | ||
| 261 | if (SUCCEEDED(hr)) { | ||
| 262 | char result[MAX_PATH+1]; | ||
| 263 | wcstombs(result, folder, MAX_PATH); | ||
| 264 | home=std::string(result); | ||
| 265 | flip_slashes(home); | ||
| 266 | } | ||
| 267 | return home; | ||
| 268 | } | ||
| 269 | |||
| 270 | std::string documents_directory() override { | ||
| 271 | std::string home; | ||
| 272 | wchar_t folder[MAX_PATH+1]; | ||
| 273 | HRESULT hr = SHGetFolderPathW(0, CSIDL_MYDOCUMENTS, 0, 0, folder); | ||
| 274 | if (SUCCEEDED(hr)) { | ||
| 275 | char result[MAX_PATH+1]; | ||
| 276 | wcstombs(result, folder, MAX_PATH); | ||
| 277 | home=std::string(result); | ||
| 278 | flip_slashes(home); | ||
| 279 | } | ||
| 280 | return home; | ||
| 281 | } | ||
| 282 | }; | ||
| 283 | |||
| 284 | #else | ||
| 285 | |||
| 286 | /***** Unix/Mac/Android/Emscripten file system implementation ****************/ | ||
| 287 | |||
| 288 |
1/2✓ Branch 1 taken 251 times.
✗ Branch 2 not taken.
|
251 | class FileSystemRootNode : public FileSystem::Node { |
| 289 | public: | ||
| 290 | 251 | bool is_file(const std::string& path) override { | |
| 291 | // We use 'stat' and not 'lstat' since | ||
| 292 | // we want to be able to follow symbolic | ||
| 293 | // links (required for instance when testing | ||
| 294 | // input path in GOMGEN, there can be some | ||
| 295 | // symlinks in system includes) | ||
| 296 | struct stat buff; | ||
| 297 |
1/6✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 251 times.
|
251 | if(stat(path.c_str(), &buff)) { |
| 298 | return false; | ||
| 299 | } | ||
| 300 | ✗ | return S_ISREG(buff.st_mode); | |
| 301 | } | ||
| 302 | |||
| 303 | ✗ | bool is_directory(const std::string& path) override { | |
| 304 | // We use 'stat' and not 'lstat' since | ||
| 305 | // we want to be able to follow symbolic | ||
| 306 | // links (required for instance when testing | ||
| 307 | // input path in GOMGEN, there can be some | ||
| 308 | // symlinks in system includes) | ||
| 309 | struct stat buff; | ||
| 310 |
1/6✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1057 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
|
1057 | if(stat(path.c_str(), &buff)) { |
| 311 | return false; | ||
| 312 | } | ||
| 313 | ✗ | return S_ISDIR(buff.st_mode); | |
| 314 | } | ||
| 315 | |||
| 316 |
1/2✓ Branch 1 taken 151 times.
✗ Branch 2 not taken.
|
151 | bool create_directory(const std::string& path_in) override { |
| 317 | std::vector<std::string> path; | ||
| 318 |
1/2✓ Branch 1 taken 151 times.
✗ Branch 2 not taken.
|
151 | String::split_string(path_in, '/', path); |
| 319 | std::string current; | ||
| 320 |
2/2✓ Branch 0 taken 1057 times.
✓ Branch 1 taken 151 times.
|
2416 | for(size_t i = 0; i < path.size(); i++) { |
| 321 | current += "/"; | ||
| 322 | current += path[i]; | ||
| 323 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1057 times.
|
1057 | if(!is_directory(current)) { |
| 324 | ✗ | if(mkdir(current.c_str(), 0755) != 0) { | |
| 325 | ✗ | Logger::err("OS") | |
| 326 | << "Could not create directory " | ||
| 327 | << current << std::endl; | ||
| 328 | ✗ | return false; | |
| 329 | } | ||
| 330 | } | ||
| 331 | } | ||
| 332 | return true; | ||
| 333 | 151 | } | |
| 334 | |||
| 335 | ✗ | bool delete_directory(const std::string& path) override { | |
| 336 | ✗ | return rmdir(path.c_str()) == 0; | |
| 337 | } | ||
| 338 | |||
| 339 | ✗ | bool delete_file(const std::string& path) override { | |
| 340 | ✗ | return unlink(path.c_str()) == 0; | |
| 341 | } | ||
| 342 | |||
| 343 | ✗ | bool get_directory_entries( | |
| 344 | const std::string& path, std::vector<std::string>& result | ||
| 345 | ) override { | ||
| 346 | std::string dirname = path; | ||
| 347 | ✗ | if(dirname[dirname.length() - 1] != '/') { | |
| 348 | dirname += "/"; | ||
| 349 | } | ||
| 350 | ✗ | DIR* dir = opendir(dirname.c_str()); | |
| 351 | ✗ | if(dir == nullptr) { | |
| 352 | ✗ | Logger::err("OS") | |
| 353 | << "Could not open directory " << dirname | ||
| 354 | << std::endl; | ||
| 355 | #ifdef GEO_OS_ANDROID | ||
| 356 | static bool first_time = true; | ||
| 357 | if(first_time) { | ||
| 358 | Logger::err("OS") | ||
| 359 | << "You may need to grant the \"Storage\" permission" | ||
| 360 | << std::endl | ||
| 361 | << "to this application" | ||
| 362 | << std::endl; | ||
| 363 | Logger::err("OS") | ||
| 364 | << "(see Parameters/Applications" | ||
| 365 | << std::endl | ||
| 366 | << "in Android perferences)" | ||
| 367 | << std::endl; | ||
| 368 | first_time = false; | ||
| 369 | } | ||
| 370 | #endif | ||
| 371 | ✗ | return false; | |
| 372 | } | ||
| 373 | ✗ | struct dirent* entry = readdir(dir); | |
| 374 | ✗ | while(entry != nullptr) { | |
| 375 | ✗ | std::string current = std::string(entry->d_name); | |
| 376 | // Ignore . and .. | ||
| 377 | ✗ | if(current != "." && current != "..") { | |
| 378 | ✗ | if(dirname != "./") { | |
| 379 | ✗ | current = dirname + current; | |
| 380 | } | ||
| 381 | // Ignore symbolic links and other special Unix stuff | ||
| 382 | ✗ | if(is_file(current) || is_directory(current)) { | |
| 383 | ✗ | result.push_back(current); | |
| 384 | } | ||
| 385 | } | ||
| 386 | ✗ | entry = readdir(dir); | |
| 387 | } | ||
| 388 | ✗ | closedir(dir); | |
| 389 | return true; | ||
| 390 | } | ||
| 391 | |||
| 392 | 151 | std::string get_current_working_directory() override { | |
| 393 | char buff[4096]; | ||
| 394 | 151 | return std::string(getcwd(buff, 4096)); | |
| 395 | } | ||
| 396 | |||
| 397 | ✗ | bool set_current_working_directory(const std::string& path) override { | |
| 398 | ✗ | return chdir(path.c_str()) == 0; | |
| 399 | } | ||
| 400 | |||
| 401 | ✗ | bool rename_file( | |
| 402 | const std::string& old_name, const std::string& new_name | ||
| 403 | ) override { | ||
| 404 | ✗ | if(is_file(new_name)) { | |
| 405 | return false; | ||
| 406 | } | ||
| 407 | ✗ | return ::rename(old_name.c_str(), new_name.c_str()) == 0; | |
| 408 | } | ||
| 409 | |||
| 410 | ✗ | Numeric::uint64 get_time_stamp(const std::string& path) override { | |
| 411 | struct stat buffer; | ||
| 412 | ✗ | if(!stat(path.c_str(), &buffer)) { | |
| 413 | ✗ | return Numeric::uint64(buffer.st_mtime); | |
| 414 | } | ||
| 415 | return 0; | ||
| 416 | } | ||
| 417 | |||
| 418 | ✗ | bool set_executable_flag(const std::string& filename) override { | |
| 419 | geo_argused(filename); | ||
| 420 | ✗ | if(::chmod(filename.c_str(), 0755) != 0) { | |
| 421 | ✗ | Logger::err("FileSyst") | |
| 422 | << "Could not change file permissions for:" | ||
| 423 | << filename << std::endl; | ||
| 424 | ✗ | return false; | |
| 425 | } | ||
| 426 | return true; | ||
| 427 | } | ||
| 428 | |||
| 429 | ✗ | bool touch(const std::string& filename) override { | |
| 430 | #ifdef GEO_OS_APPLE | ||
| 431 | { | ||
| 432 | struct stat buff; | ||
| 433 | int rc = stat(filename.c_str(), &buff); | ||
| 434 | if(rc != 0) { // FABIEN NOT SURE WE GET THE TOUCH | ||
| 435 | Logger::err("FileSystem") | ||
| 436 | << "Could not touch file:" | ||
| 437 | << filename | ||
| 438 | << std::endl; | ||
| 439 | return false; | ||
| 440 | } | ||
| 441 | return true; | ||
| 442 | } | ||
| 443 | #else | ||
| 444 | { | ||
| 445 | ✗ | int rc = utimensat( | |
| 446 | AT_FDCWD, | ||
| 447 | filename.c_str(), | ||
| 448 | nullptr, | ||
| 449 | 0 | ||
| 450 | ); | ||
| 451 | ✗ | if(rc != 0) { | |
| 452 | ✗ | Logger::err("FileSystem") | |
| 453 | << "Could not touch file:" | ||
| 454 | << filename | ||
| 455 | << std::endl; | ||
| 456 | ✗ | return false; | |
| 457 | } | ||
| 458 | return true; | ||
| 459 | } | ||
| 460 | #endif | ||
| 461 | } | ||
| 462 | |||
| 463 | 302 | std::string normalized_path(const std::string& path_in) override { | |
| 464 | |||
| 465 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 302 times.
|
302 | if(path_in == "") { |
| 466 | ✗ | return ""; | |
| 467 | } | ||
| 468 | |||
| 469 | std::string path = path_in; | ||
| 470 | std::string result; | ||
| 471 | |||
| 472 | // If this is a relative path, prepend "./" | ||
| 473 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 302 times.
|
302 | if(path[0] != '/') { |
| 474 | ✗ | path = "./" + path; | |
| 475 | } | ||
| 476 | |||
| 477 | char buffer[PATH_MAX]; | ||
| 478 | char* p = realpath(path.c_str(), buffer); | ||
| 479 |
2/2✓ Branch 0 taken 172 times.
✓ Branch 1 taken 130 times.
|
302 | if(p != nullptr) { |
| 480 |
1/4✓ Branch 1 taken 172 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
344 | result = std::string(p); |
| 481 | } else { | ||
| 482 | // realpath() only works for existing paths and existing file, | ||
| 483 | // therefore we attempt calling it on the input path by adding | ||
| 484 | // one component at a time. | ||
| 485 | size_t pos = 1; | ||
| 486 |
1/2✓ Branch 0 taken 1044 times.
✗ Branch 1 not taken.
|
1044 | while(pos != std::string::npos) { |
| 487 | 1044 | pos = path.find('/',pos); | |
| 488 |
2/2✓ Branch 0 taken 914 times.
✓ Branch 1 taken 130 times.
|
1044 | if(pos != std::string::npos) { |
| 489 |
1/2✓ Branch 1 taken 914 times.
✗ Branch 2 not taken.
|
914 | std::string path_part = path.substr(0,pos); |
| 490 | p = realpath(path_part.c_str(), buffer); | ||
| 491 |
1/2✓ Branch 0 taken 914 times.
✗ Branch 1 not taken.
|
914 | if(p == nullptr) { |
| 492 | break; | ||
| 493 | } else { | ||
| 494 |
3/6✓ Branch 1 taken 914 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 132 times.
✓ Branch 4 taken 782 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
1828 | result = std::string(p) + |
| 495 |
2/6✓ Branch 1 taken 914 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 914 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
|
1828 | path.substr(pos, path.length()-pos); |
| 496 | } | ||
| 497 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 914 times.
|
914 | ++pos; |
| 498 |
1/2✓ Branch 0 taken 914 times.
✗ Branch 1 not taken.
|
914 | if(pos == path.length()) { |
| 499 | break; | ||
| 500 | } | ||
| 501 | } | ||
| 502 | } | ||
| 503 | } | ||
| 504 |
1/2✓ Branch 1 taken 302 times.
✗ Branch 2 not taken.
|
302 | flip_slashes(result); |
| 505 | 302 | return result; | |
| 506 | } | ||
| 507 | |||
| 508 | |||
| 509 | 502 | std::string home_directory() override { | |
| 510 | std::string home; | ||
| 511 | #if defined GEO_OS_EMSCRIPTEN | ||
| 512 | home="/"; | ||
| 513 | #else | ||
| 514 | 502 | char* result = getenv("HOME"); | |
| 515 |
1/2✓ Branch 0 taken 502 times.
✗ Branch 1 not taken.
|
502 | if(result != nullptr) { |
| 516 | home=result; | ||
| 517 | } | ||
| 518 | #endif | ||
| 519 | 502 | return home; | |
| 520 | } | ||
| 521 | |||
| 522 | ✗ | std::string documents_directory() override { | |
| 523 | std::string home; | ||
| 524 | #if defined GEO_OS_EMSCRIPTEN | ||
| 525 | home="/"; | ||
| 526 | #elif defined GEO_OS_ANDROID | ||
| 527 | char* result = getenv("EXTERNAL_STORAGE"); | ||
| 528 | if(result != nullptr) { | ||
| 529 | home=result; | ||
| 530 | } | ||
| 531 | #else | ||
| 532 | ✗ | char* result = getenv("HOME"); | |
| 533 | ✗ | if(result != nullptr) { | |
| 534 | home=result; | ||
| 535 | } | ||
| 536 | #endif | ||
| 537 | ✗ | return home; | |
| 538 | } | ||
| 539 | |||
| 540 | }; | ||
| 541 | #endif | ||
| 542 | |||
| 543 | FileSystem::Node_var root_; | ||
| 544 | } | ||
| 545 | |||
| 546 | |||
| 547 | namespace GEO { | ||
| 548 | |||
| 549 | namespace FileSystem { | ||
| 550 | |||
| 551 | 251 | Node::Node() { | |
| 552 | 251 | } | |
| 553 | |||
| 554 | 502 | Node::~Node() { | |
| 555 | 502 | } | |
| 556 | |||
| 557 | /******* OS-independent functions *************************************/ | ||
| 558 | |||
| 559 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1043 times.
|
1043 | std::string Node::extension(const std::string& path) { |
| 560 | size_t len = path.length(); | ||
| 561 |
1/2✓ Branch 0 taken 1043 times.
✗ Branch 1 not taken.
|
1043 | if(len != 0) { |
| 562 |
1/2✓ Branch 0 taken 4979 times.
✗ Branch 1 not taken.
|
4979 | for(size_t i = len - 1; i != 0; i--) { |
| 563 |
2/4✓ Branch 0 taken 4979 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4979 times.
✗ Branch 3 not taken.
|
4979 | if(path[i] == '/' || path[i] == '\\') { |
| 564 | break; | ||
| 565 | } | ||
| 566 |
2/2✓ Branch 0 taken 1043 times.
✓ Branch 1 taken 3936 times.
|
4979 | if(path[i] == '.') { |
| 567 |
1/2✓ Branch 2 taken 1043 times.
✗ Branch 3 not taken.
|
2086 | return String::to_lowercase(path.substr(i + 1)); |
| 568 | } | ||
| 569 | } | ||
| 570 | } | ||
| 571 | return std::string(); | ||
| 572 | } | ||
| 573 | |||
| 574 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 508 times.
|
508 | std::string Node::base_name( |
| 575 | const std::string& path, bool remove_extension | ||
| 576 | ) { | ||
| 577 | 508 | long int len = (long int)(path.length()); | |
| 578 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 508 times.
|
508 | if(len == 0) { |
| 579 | return std::string(); | ||
| 580 | } | ||
| 581 | long int dot_pos = len; | ||
| 582 | long int i; | ||
| 583 |
2/2✓ Branch 0 taken 6176 times.
✓ Branch 1 taken 4 times.
|
6180 | for(i = len - 1; i >= 0; i--) { |
| 584 |
3/4✓ Branch 0 taken 5672 times.
✓ Branch 1 taken 504 times.
✓ Branch 2 taken 5672 times.
✗ Branch 3 not taken.
|
6176 | if(path[size_t(i)] == '/' || path[size_t(i)] == '\\') { |
| 585 | break; | ||
| 586 | } | ||
| 587 |
3/4✓ Branch 0 taken 5672 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5668 times.
✓ Branch 3 taken 4 times.
|
5672 | if(remove_extension && path[size_t(i)] == '.') { |
| 588 | dot_pos = i; | ||
| 589 | } | ||
| 590 | } | ||
| 591 | 508 | return path.substr(size_t(i + 1), size_t(dot_pos - i - 1)); | |
| 592 | } | ||
| 593 | |||
| 594 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 155 times.
|
155 | std::string Node::dir_name(const std::string& path) { |
| 595 | size_t len = path.length(); | ||
| 596 |
1/2✓ Branch 0 taken 155 times.
✗ Branch 1 not taken.
|
155 | if(len != 0) { |
| 597 |
2/2✓ Branch 0 taken 1641 times.
✓ Branch 1 taken 4 times.
|
1645 | for(size_t i = len - 1; i != 0; i--) { |
| 598 |
3/4✓ Branch 0 taken 1490 times.
✓ Branch 1 taken 151 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1490 times.
|
1641 | if(path[i] == '/' || path[i] == '\\') { |
| 599 | 151 | return path.substr(0, i); | |
| 600 | } | ||
| 601 | } | ||
| 602 | } | ||
| 603 | 4 | return "."; | |
| 604 | } | ||
| 605 | |||
| 606 | ✗ | void Node::get_directory_entries_recursive( | |
| 607 | const std::string& path, | ||
| 608 | std::vector<std::string>& result, bool recursive | ||
| 609 | ) { | ||
| 610 | // TODO: seems to be bugged, enters infinite recursion... | ||
| 611 | ✗ | get_directory_entries(path, result); | |
| 612 | ✗ | if(recursive) { | |
| 613 | ✗ | for(size_t i = 0; i < result.size(); i++) { | |
| 614 | ✗ | if(is_directory(result[i])) { | |
| 615 | ✗ | get_directory_entries_recursive(result[i], result, true); | |
| 616 | } | ||
| 617 | } | ||
| 618 | } | ||
| 619 | ✗ | } | |
| 620 | |||
| 621 | ✗ | void Node::get_files( | |
| 622 | const std::string& path, | ||
| 623 | std::vector<std::string>& result, bool recursive | ||
| 624 | ) { | ||
| 625 | std::vector<std::string> entries; | ||
| 626 | ✗ | get_directory_entries_recursive(path, entries, recursive); | |
| 627 | ✗ | for(size_t i = 0; i < entries.size(); i++) { | |
| 628 | ✗ | if(is_file(entries[i])) { | |
| 629 | ✗ | result.push_back(entries[i]); | |
| 630 | } | ||
| 631 | } | ||
| 632 | ✗ | } | |
| 633 | |||
| 634 | ✗ | void Node::get_subdirectories_recursive( | |
| 635 | const std::string& path, | ||
| 636 | std::vector<std::string>& result, bool recursive | ||
| 637 | ) { | ||
| 638 | std::vector<std::string> entries; | ||
| 639 | ✗ | get_directory_entries_recursive(path, entries, recursive); | |
| 640 | ✗ | for(size_t i = 0; i < entries.size(); i++) { | |
| 641 | ✗ | if(is_directory(entries[i])) { | |
| 642 | ✗ | result.push_back(entries[i]); | |
| 643 | } | ||
| 644 | } | ||
| 645 | ✗ | } | |
| 646 | |||
| 647 | 302 | void Node::flip_slashes(std::string& s) { | |
| 648 |
2/2✓ Branch 0 taken 19291 times.
✓ Branch 1 taken 302 times.
|
39186 | for(size_t i = 0; i < s.length(); i++) { |
| 649 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 19291 times.
|
19291 | if(s[i] == '\\') { |
| 650 | ✗ | s[i] = '/'; | |
| 651 | } | ||
| 652 | } | ||
| 653 | 302 | } | |
| 654 | |||
| 655 | ✗ | bool Node::copy_file(const std::string& from, const std::string& to) { | |
| 656 | ✗ | FILE* fromf = fopen(from.c_str(), "rb"); | |
| 657 | ✗ | if(fromf == nullptr) { | |
| 658 | ✗ | Logger::err("FileSyst") | |
| 659 | << "Could not open source file:" << from << std::endl; | ||
| 660 | ✗ | return false; | |
| 661 | } | ||
| 662 | ✗ | FILE* tof = fopen(to.c_str(),"wb"); | |
| 663 | ✗ | if(tof == nullptr) { | |
| 664 | ✗ | Logger::err("FileSyst") | |
| 665 | << "Could not create file:" << to << std::endl; | ||
| 666 | ✗ | fclose(fromf); | |
| 667 | ✗ | return false; | |
| 668 | } | ||
| 669 | |||
| 670 | bool result = true; | ||
| 671 | const size_t buff_size = 4096; | ||
| 672 | char buff[buff_size]; | ||
| 673 | size_t rdsize = 0; | ||
| 674 | do { | ||
| 675 | rdsize = fread(buff, 1, buff_size, fromf); | ||
| 676 | ✗ | if(fwrite(buff, 1, rdsize, tof) != rdsize) { | |
| 677 | ✗ | Logger::err("FileSyst") << "I/O error when writing to file:" | |
| 678 | << to << std::endl; | ||
| 679 | result = false; | ||
| 680 | ✗ | break; | |
| 681 | } | ||
| 682 | ✗ | } while(rdsize == 4096); | |
| 683 | |||
| 684 | ✗ | fclose(fromf); | |
| 685 | ✗ | fclose(tof); | |
| 686 | return result; | ||
| 687 | } | ||
| 688 | |||
| 689 | /*************** OS-dependent functions *******************************/ | ||
| 690 | |||
| 691 | ✗ | bool Node::is_file(const std::string& path) { | |
| 692 | geo_argused(path); | ||
| 693 | ✗ | return false; | |
| 694 | } | ||
| 695 | |||
| 696 | ✗ | bool Node::is_directory(const std::string& path) { | |
| 697 | geo_argused(path); | ||
| 698 | ✗ | return false; | |
| 699 | } | ||
| 700 | |||
| 701 | ✗ | bool Node::create_directory(const std::string& path) { | |
| 702 | geo_argused(path); | ||
| 703 | ✗ | return false; | |
| 704 | } | ||
| 705 | |||
| 706 | ✗ | bool Node::delete_directory(const std::string& path) { | |
| 707 | geo_argused(path); | ||
| 708 | ✗ | return false; | |
| 709 | } | ||
| 710 | |||
| 711 | ✗ | bool Node::delete_file(const std::string& path) { | |
| 712 | geo_argused(path); | ||
| 713 | ✗ | return false; | |
| 714 | } | ||
| 715 | |||
| 716 | ✗ | bool Node::get_directory_entries( | |
| 717 | const std::string& path, std::vector<std::string>& result | ||
| 718 | ) { | ||
| 719 | geo_argused(path); | ||
| 720 | geo_argused(result); | ||
| 721 | ✗ | return false; | |
| 722 | } | ||
| 723 | |||
| 724 | ✗ | std::string Node::get_current_working_directory() { | |
| 725 | ✗ | return "/"; | |
| 726 | } | ||
| 727 | |||
| 728 | ✗ | bool Node::set_current_working_directory(const std::string& path) { | |
| 729 | geo_argused(path); | ||
| 730 | ✗ | return false; | |
| 731 | } | ||
| 732 | |||
| 733 | ✗ | bool Node::rename_file( | |
| 734 | const std::string& old_name, const std::string& new_name | ||
| 735 | ) { | ||
| 736 | geo_argused(old_name); | ||
| 737 | geo_argused(new_name); | ||
| 738 | ✗ | return false; | |
| 739 | } | ||
| 740 | |||
| 741 | ✗ | Numeric::uint64 Node::get_time_stamp(const std::string& path) { | |
| 742 | geo_argused(path); | ||
| 743 | ✗ | return 0; | |
| 744 | } | ||
| 745 | |||
| 746 | ✗ | bool Node::set_executable_flag(const std::string& filename) { | |
| 747 | geo_argused(filename); | ||
| 748 | ✗ | return false; | |
| 749 | } | ||
| 750 | |||
| 751 | ✗ | bool Node::touch(const std::string& filename) { | |
| 752 | geo_argused(filename); | ||
| 753 | ✗ | return false; | |
| 754 | } | ||
| 755 | |||
| 756 | ✗ | std::string Node::normalized_path(const std::string& path) { | |
| 757 | std::vector<std::string> components; | ||
| 758 | ✗ | String::split_string(path, '/', components); | |
| 759 | std::vector<std::string> new_components; | ||
| 760 | ✗ | for(auto c: components) { | |
| 761 | ✗ | if(c == ".") { | |
| 762 | ✗ | } else if(c == "..") { | |
| 763 | ✗ | if(new_components.size() != 0) { | |
| 764 | ✗ | new_components.pop_back(); | |
| 765 | } | ||
| 766 | } else { | ||
| 767 | ✗ | new_components.push_back(c); | |
| 768 | } | ||
| 769 | } | ||
| 770 | std::string result; | ||
| 771 | ✗ | for(auto c: new_components) { | |
| 772 | result += "/"; | ||
| 773 | result += c; | ||
| 774 | } | ||
| 775 | ✗ | return result; | |
| 776 | ✗ | } | |
| 777 | |||
| 778 | ✗ | std::string Node::home_directory() { | |
| 779 | ✗ | return "/"; | |
| 780 | } | ||
| 781 | |||
| 782 | ✗ | std::string Node::documents_directory() { | |
| 783 | ✗ | return "/"; | |
| 784 | } | ||
| 785 | |||
| 786 | ✗ | std::string Node::load_file_as_string(const std::string& path) { | |
| 787 | std::string result; | ||
| 788 | ✗ | FILE* f = fopen(path.c_str(),"rb"); | |
| 789 | ✗ | if(f != nullptr) { | |
| 790 | // Get file length | ||
| 791 | ✗ | fseek(f, 0L, SEEK_END); | |
| 792 | ✗ | size_t length = size_t(ftell(f)); | |
| 793 | ✗ | fseek(f, 0L, SEEK_SET); | |
| 794 | ✗ | if(length != 0) { | |
| 795 | result.resize(length); | ||
| 796 | size_t read_length = fread(&result[0], 1, length, f); | ||
| 797 | ✗ | if(read_length != length) { | |
| 798 | ✗ | Logger::warn("FileSystem") | |
| 799 | << "Problem occured when reading " | ||
| 800 | << path | ||
| 801 | << std::endl; | ||
| 802 | |||
| 803 | } | ||
| 804 | } | ||
| 805 | ✗ | fclose(f); | |
| 806 | } | ||
| 807 | ✗ | return result; | |
| 808 | } | ||
| 809 | |||
| 810 | /*********************************************************************/ | ||
| 811 | |||
| 812 | ✗ | bool MemoryNode::copy_file( | |
| 813 | const std::string& from, const std::string& to | ||
| 814 | ) { | ||
| 815 | ✗ | const char* contents = get_file_contents(from); | |
| 816 | ✗ | if(contents == nullptr) { | |
| 817 | return false; | ||
| 818 | } | ||
| 819 | ✗ | return create_file(to, contents); | |
| 820 | } | ||
| 821 | |||
| 822 | ✗ | std::string MemoryNode::load_file_as_string(const std::string& path) { | |
| 823 | std::string result; | ||
| 824 | std::string subdir; | ||
| 825 | std::string rest; | ||
| 826 | ✗ | split_path(path, subdir, rest); | |
| 827 | ✗ | if(subdir == "") { | |
| 828 | auto it = files_.find(rest); | ||
| 829 | ✗ | if(it != files_.end()) { | |
| 830 | ✗ | result = std::string(it->second); | |
| 831 | } | ||
| 832 | } else { | ||
| 833 | auto it = subnodes_.find(subdir); | ||
| 834 | ✗ | if(it != subnodes_.end()) { | |
| 835 | ✗ | result = it->second->load_file_as_string(rest); | |
| 836 | } | ||
| 837 | } | ||
| 838 | ✗ | return result; | |
| 839 | } | ||
| 840 | |||
| 841 | ✗ | bool MemoryNode::is_file(const std::string& path) { | |
| 842 | std::string result; | ||
| 843 | std::string subdir; | ||
| 844 | std::string rest; | ||
| 845 | ✗ | split_path(path, subdir, rest); | |
| 846 | ✗ | if(subdir == "") { | |
| 847 | return(files_.find(rest) != files_.end()); | ||
| 848 | } else { | ||
| 849 | auto it = subnodes_.find(subdir); | ||
| 850 | ✗ | if(it == subnodes_.end()) { | |
| 851 | return false; | ||
| 852 | } | ||
| 853 | ✗ | return it->second->is_file(rest); | |
| 854 | } | ||
| 855 | } | ||
| 856 | |||
| 857 | ✗ | bool MemoryNode::is_directory(const std::string& path) { | |
| 858 | std::string result; | ||
| 859 | std::string subdir; | ||
| 860 | std::string rest; | ||
| 861 | ✗ | split_path(path, subdir, rest); | |
| 862 | ✗ | if(subdir == "") { | |
| 863 | return(subnodes_.find(rest) != subnodes_.end()); | ||
| 864 | } else { | ||
| 865 | auto it = subnodes_.find(subdir); | ||
| 866 | ✗ | if(it == subnodes_.end()) { | |
| 867 | return false; | ||
| 868 | } | ||
| 869 | ✗ | return it->second->is_directory(rest); | |
| 870 | } | ||
| 871 | } | ||
| 872 | |||
| 873 | ✗ | bool MemoryNode::create_directory(const std::string& path) { | |
| 874 | std::string result; | ||
| 875 | std::string subdir; | ||
| 876 | std::string rest; | ||
| 877 | ✗ | split_path(path, subdir, rest); | |
| 878 | ✗ | if(subdir == "") { | |
| 879 | ✗ | if(subnodes_.find(path) != subnodes_.end()) { | |
| 880 | return false; | ||
| 881 | } | ||
| 882 | ✗ | subnodes_[path] = new MemoryNode(path_ + path + "/"); | |
| 883 | ✗ | return true; | |
| 884 | } else { | ||
| 885 | auto it = subnodes_.find(subdir); | ||
| 886 | ✗ | if(it == subnodes_.end()) { | |
| 887 | ✗ | subnodes_[subdir] = new MemoryNode(path_ + subdir + "/"); | |
| 888 | } | ||
| 889 | ✗ | return it->second->create_directory(rest); | |
| 890 | } | ||
| 891 | } | ||
| 892 | |||
| 893 | ✗ | bool MemoryNode::delete_directory(const std::string& path) { | |
| 894 | std::string result; | ||
| 895 | std::string subdir; | ||
| 896 | std::string rest; | ||
| 897 | ✗ | split_path(path, subdir, rest); | |
| 898 | ✗ | if(subdir == "") { | |
| 899 | auto it = subnodes_.find(rest); | ||
| 900 | ✗ | if(it == subnodes_.end()) { | |
| 901 | return false; | ||
| 902 | } | ||
| 903 | subnodes_.erase(it); | ||
| 904 | ✗ | return true; | |
| 905 | } else { | ||
| 906 | auto it = subnodes_.find(subdir); | ||
| 907 | ✗ | if(it == subnodes_.end()) { | |
| 908 | return false; | ||
| 909 | } | ||
| 910 | ✗ | return it->second->delete_directory(rest); | |
| 911 | } | ||
| 912 | } | ||
| 913 | |||
| 914 | ✗ | bool MemoryNode::delete_file(const std::string& path) { | |
| 915 | std::string result; | ||
| 916 | std::string subdir; | ||
| 917 | std::string rest; | ||
| 918 | ✗ | split_path(path, subdir, rest); | |
| 919 | ✗ | if(subdir == "") { | |
| 920 | auto it = files_.find(rest); | ||
| 921 | ✗ | if(it == files_.end()) { | |
| 922 | return false; | ||
| 923 | } | ||
| 924 | files_.erase(it); | ||
| 925 | ✗ | return true; | |
| 926 | } else { | ||
| 927 | auto it = subnodes_.find(subdir); | ||
| 928 | ✗ | if(it == subnodes_.end()) { | |
| 929 | return false; | ||
| 930 | } | ||
| 931 | ✗ | return it->second->delete_file(rest); | |
| 932 | } | ||
| 933 | } | ||
| 934 | |||
| 935 | ✗ | bool MemoryNode::get_directory_entries( | |
| 936 | const std::string& path, std::vector<std::string>& result | ||
| 937 | ) { | ||
| 938 | std::string subdir; | ||
| 939 | std::string rest; | ||
| 940 | ✗ | split_path(path, subdir, rest); | |
| 941 | ✗ | if(subdir == "" && rest == "") { | |
| 942 | result.clear(); | ||
| 943 | ✗ | for(auto it : subnodes_) { | |
| 944 | ✗ | result.push_back(path_ + it.first); | |
| 945 | ✗ | } | |
| 946 | ✗ | for(auto it : files_) { | |
| 947 | ✗ | result.push_back(path_ + it.first); | |
| 948 | } | ||
| 949 | ✗ | return true; | |
| 950 | } else { | ||
| 951 | ✗ | if(subdir == "") { | |
| 952 | subdir = rest; | ||
| 953 | rest = ""; | ||
| 954 | } | ||
| 955 | auto it = subnodes_.find(subdir); | ||
| 956 | ✗ | if(it == subnodes_.end()) { | |
| 957 | return false; | ||
| 958 | } | ||
| 959 | ✗ | return it->second->get_directory_entries(rest, result); | |
| 960 | } | ||
| 961 | } | ||
| 962 | |||
| 963 | ✗ | bool MemoryNode::rename_file( | |
| 964 | const std::string& from, const std::string& to | ||
| 965 | ) { | ||
| 966 | ✗ | const char* contents = get_file_contents(from); | |
| 967 | ✗ | if(contents == nullptr) { | |
| 968 | return false; | ||
| 969 | } | ||
| 970 | ✗ | if(!delete_file(from)) { | |
| 971 | return false; | ||
| 972 | } | ||
| 973 | ✗ | return create_file(to, contents); | |
| 974 | } | ||
| 975 | |||
| 976 | ✗ | const char* MemoryNode::get_file_contents(const std::string& path) { | |
| 977 | std::string subdir; | ||
| 978 | std::string rest; | ||
| 979 | ✗ | split_path(path, subdir, rest); | |
| 980 | const char* result = nullptr; | ||
| 981 | ✗ | if(subdir == "") { | |
| 982 | auto it = files_.find(rest); | ||
| 983 | ✗ | if(it != files_.end()) { | |
| 984 | ✗ | result = it->second; | |
| 985 | } | ||
| 986 | } else { | ||
| 987 | auto it = subnodes_.find(subdir); | ||
| 988 | ✗ | if(it != subnodes_.end()) { | |
| 989 | ✗ | result = it->second->get_file_contents(rest); | |
| 990 | } | ||
| 991 | } | ||
| 992 | ✗ | return result; | |
| 993 | } | ||
| 994 | |||
| 995 | ✗ | bool MemoryNode::create_file( | |
| 996 | const std::string& path, const char* content | ||
| 997 | ) { | ||
| 998 | std::string subdir; | ||
| 999 | std::string rest; | ||
| 1000 | ✗ | split_path(path, subdir, rest); | |
| 1001 | ✗ | if(subdir == "") { | |
| 1002 | ✗ | if(files_.find(rest) != files_.end()) { | |
| 1003 | return false; | ||
| 1004 | } | ||
| 1005 | ✗ | files_[rest] = content; | |
| 1006 | ✗ | return true; | |
| 1007 | } else { | ||
| 1008 | ✗ | SmartPointer<MemoryNode>& n = subnodes_[subdir]; | |
| 1009 | ✗ | if(n.is_null()) { | |
| 1010 | ✗ | n = new MemoryNode(path_ + subdir + "/"); | |
| 1011 | } | ||
| 1012 | ✗ | return n->create_file(rest, content); | |
| 1013 | } | ||
| 1014 | } | ||
| 1015 | |||
| 1016 | ✗ | void MemoryNode::split_path( | |
| 1017 | const std::string& path, std::string& leadingsubdir, | ||
| 1018 | std::string& rest | ||
| 1019 | ) { | ||
| 1020 | leadingsubdir = ""; | ||
| 1021 | rest = ""; | ||
| 1022 | std::vector<std::string> components; | ||
| 1023 | ✗ | String::split_string(path, '/', components); | |
| 1024 | ✗ | if(components.size() == 0) { | |
| 1025 | return; | ||
| 1026 | ✗ | } else if(components.size() == 1) { | |
| 1027 | leadingsubdir = ""; | ||
| 1028 | rest = components[0]; | ||
| 1029 | } else { | ||
| 1030 | leadingsubdir = components[0]; | ||
| 1031 | ✗ | for(size_t i=1; i<components.size(); ++i) { | |
| 1032 | ✗ | if(i != 1) { | |
| 1033 | rest += "/"; | ||
| 1034 | } | ||
| 1035 | rest += components[i]; | ||
| 1036 | } | ||
| 1037 | } | ||
| 1038 | ✗ | } | |
| 1039 | |||
| 1040 | /*********************************************************************/ | ||
| 1041 | |||
| 1042 | 251 | void initialize() { | |
| 1043 | 502 | root_ = new FileSystemRootNode; | |
| 1044 | 251 | } | |
| 1045 | |||
| 1046 |
1/2✓ Branch 0 taken 251 times.
✗ Branch 1 not taken.
|
251 | void terminate() { |
| 1047 | root_.reset(); | ||
| 1048 | 251 | } | |
| 1049 | |||
| 1050 | 251 | bool is_file(const std::string& path) { | |
| 1051 | 251 | return root_->is_file(path); | |
| 1052 | } | ||
| 1053 | |||
| 1054 | ✗ | bool is_directory(const std::string& path) { | |
| 1055 | ✗ | return root_->is_directory(path); | |
| 1056 | } | ||
| 1057 | |||
| 1058 | |||
| 1059 | ✗ | bool can_read_directory(const std::string& path) { | |
| 1060 | ✗ | const std::string abs_path = absolute_path(path); | |
| 1061 | #ifdef GEO_OS_WINDOWS | ||
| 1062 | return is_directory(abs_path); // TODO: check permissions | ||
| 1063 | #else | ||
| 1064 | struct stat stats; | ||
| 1065 | ✗ | if( ::stat(abs_path.c_str(), &stats) == 0 ) { | |
| 1066 | ✗ | return (stats.st_mode & S_IRUSR) != 0; | |
| 1067 | } | ||
| 1068 | return false; | ||
| 1069 | #endif | ||
| 1070 | } | ||
| 1071 | |||
| 1072 | 151 | bool can_write_directory( | |
| 1073 | const std::string& path, bool create_missing_directories | ||
| 1074 | ) { | ||
| 1075 | 151 | std::string abs_path = absolute_path(path); | |
| 1076 |
1/2✓ Branch 0 taken 151 times.
✗ Branch 1 not taken.
|
151 | if( create_missing_directories ) { |
| 1077 |
2/4✓ Branch 1 taken 151 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 151 times.
|
151 | if( !FileSystem::create_directory(abs_path) ) { |
| 1078 | return false; | ||
| 1079 | } | ||
| 1080 | } | ||
| 1081 | #ifdef GEO_OS_WINDOWS | ||
| 1082 | return is_directory(abs_path); // TODO: check permissions | ||
| 1083 | #else | ||
| 1084 | struct stat stats; | ||
| 1085 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 151 times.
|
151 | if( ::stat(abs_path.c_str(), &stats) == 0 ) { |
| 1086 | 151 | return (stats.st_mode & S_IWUSR) != 0; | |
| 1087 | } | ||
| 1088 | return false; | ||
| 1089 | #endif | ||
| 1090 | } | ||
| 1091 | |||
| 1092 | 151 | bool create_directory(const std::string& path) { | |
| 1093 | 151 | return root_->create_directory(path); | |
| 1094 | } | ||
| 1095 | |||
| 1096 | ✗ | bool delete_directory(const std::string& path) { | |
| 1097 | ✗ | return root_->delete_directory(path); | |
| 1098 | } | ||
| 1099 | |||
| 1100 | ✗ | bool delete_file(const std::string& path) { | |
| 1101 | ✗ | return root_->delete_file(path); | |
| 1102 | } | ||
| 1103 | |||
| 1104 | ✗ | bool get_directory_entries( | |
| 1105 | const std::string& path, std::vector<std::string>& result | ||
| 1106 | ) { | ||
| 1107 | ✗ | return root_->get_directory_entries(path, result); | |
| 1108 | } | ||
| 1109 | |||
| 1110 | 151 | std::string get_current_working_directory() { | |
| 1111 | 151 | return root_->get_current_working_directory(); | |
| 1112 | } | ||
| 1113 | |||
| 1114 | ✗ | bool set_current_working_directory( | |
| 1115 | const std::string& path | ||
| 1116 | ) { | ||
| 1117 | ✗ | return root_->set_current_working_directory(path); | |
| 1118 | } | ||
| 1119 | |||
| 1120 | ✗ | bool rename_file( | |
| 1121 | const std::string& old_name, const std::string& new_name | ||
| 1122 | ) { | ||
| 1123 | ✗ | return root_->rename_file(old_name, new_name); | |
| 1124 | } | ||
| 1125 | |||
| 1126 | ✗ | Numeric::uint64 get_time_stamp(const std::string& path) { | |
| 1127 | ✗ | return root_->get_time_stamp(path); | |
| 1128 | } | ||
| 1129 | |||
| 1130 | 1043 | std::string extension(const std::string& path) { | |
| 1131 | 1043 | return root_->extension(path); | |
| 1132 | } | ||
| 1133 | |||
| 1134 | 508 | std::string base_name( | |
| 1135 | const std::string& path, bool remove_extension | ||
| 1136 | ) { | ||
| 1137 | 508 | return root_->base_name(path, remove_extension); | |
| 1138 | } | ||
| 1139 | |||
| 1140 | 155 | std::string dir_name(const std::string& path) { | |
| 1141 | 155 | return root_->dir_name(path); | |
| 1142 | } | ||
| 1143 | |||
| 1144 | ✗ | void get_directory_entries_recursive( | |
| 1145 | const std::string& path, | ||
| 1146 | std::vector<std::string>& result, bool recursive | ||
| 1147 | ) { | ||
| 1148 | ✗ | return root_->get_directory_entries_recursive( | |
| 1149 | path, result, recursive | ||
| 1150 | ✗ | ); | |
| 1151 | } | ||
| 1152 | |||
| 1153 | ✗ | void get_files( | |
| 1154 | const std::string& path, | ||
| 1155 | std::vector<std::string>& result, bool recursive | ||
| 1156 | ) { | ||
| 1157 | ✗ | return root_->get_files(path, result, recursive); | |
| 1158 | } | ||
| 1159 | |||
| 1160 | ✗ | void get_subdirectories( | |
| 1161 | const std::string& path, | ||
| 1162 | std::vector<std::string>& result, bool recursive | ||
| 1163 | ) { | ||
| 1164 | ✗ | return root_->get_subdirectories_recursive(path, result, recursive); | |
| 1165 | } | ||
| 1166 | |||
| 1167 | ✗ | void flip_slashes(std::string& path) { | |
| 1168 | ✗ | return root_->flip_slashes(path); | |
| 1169 | } | ||
| 1170 | |||
| 1171 | ✗ | bool copy_file( | |
| 1172 | const std::string& from, const std::string& to | ||
| 1173 | ) { | ||
| 1174 | ✗ | return root_->copy_file(from, to); | |
| 1175 | } | ||
| 1176 | |||
| 1177 | ✗ | bool set_executable_flag(const std::string& filename) { | |
| 1178 | ✗ | return root_->set_executable_flag(filename); | |
| 1179 | } | ||
| 1180 | |||
| 1181 | ✗ | bool touch(const std::string& filename) { | |
| 1182 | ✗ | return root_->touch(filename); | |
| 1183 | } | ||
| 1184 | |||
| 1185 | 302 | std::string normalized_path(const std::string& path) { | |
| 1186 | 302 | return root_->normalized_path(path); | |
| 1187 | } | ||
| 1188 | |||
| 1189 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 302 times.
|
302 | std::string absolute_path(const std::string& path) { |
| 1190 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 302 times.
|
302 | if( path.empty() ) { |
| 1191 | return path; | ||
| 1192 | } | ||
| 1193 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 298 times.
|
302 | if( path[0] == '.' ) { |
| 1194 | return FileSystem::normalized_path( | ||
| 1195 |
2/4✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
|
8 | FileSystem::get_current_working_directory() + '/' + path |
| 1196 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | ); |
| 1197 | } | ||
| 1198 |
2/2✓ Branch 0 taken 151 times.
✓ Branch 1 taken 147 times.
|
298 | if( path[0] == '/' ) { |
| 1199 | 151 | return FileSystem::normalized_path(path); | |
| 1200 | } | ||
| 1201 | #ifdef GEO_OS_WINDOWS | ||
| 1202 | if( | ||
| 1203 | path.size() > 2 && path[1] == ':' && | ||
| 1204 | (path[2] == '\\' || path[2] == '/') | ||
| 1205 | ) { | ||
| 1206 | return FileSystem::normalized_path(path); | ||
| 1207 | } | ||
| 1208 | #endif | ||
| 1209 | // relative path | ||
| 1210 | return FileSystem::normalized_path( | ||
| 1211 |
2/4✓ Branch 2 taken 147 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 147 times.
✗ Branch 6 not taken.
|
294 | FileSystem::get_current_working_directory() + SEPARATOR + path |
| 1212 |
1/2✓ Branch 1 taken 147 times.
✗ Branch 2 not taken.
|
147 | ); |
| 1213 | } | ||
| 1214 | |||
| 1215 | 502 | std::string home_directory() { | |
| 1216 | 502 | return root_->home_directory(); | |
| 1217 | } | ||
| 1218 | |||
| 1219 | ✗ | std::string documents_directory() { | |
| 1220 | ✗ | return root_->documents_directory(); | |
| 1221 | } | ||
| 1222 | |||
| 1223 | ✗ | void get_root(Node*& root) { | |
| 1224 | ✗ | root = root_; | |
| 1225 | ✗ | } | |
| 1226 | } // end namespace FileSystem | ||
| 1227 | } // end namespace GEO | ||
| 1228 | |||
| 1229 | |||
| 1230 | |||
| 1231 | #ifdef GEO_OS_EMSCRIPTEN | ||
| 1232 | |||
| 1233 | namespace GEO { | ||
| 1234 | namespace FileSystem { | ||
| 1235 | static void (*file_system_changed_callback_)() = nullptr; | ||
| 1236 | void set_file_system_changed_callback(void(*callback)()) { | ||
| 1237 | file_system_changed_callback_ = callback; | ||
| 1238 | } | ||
| 1239 | } | ||
| 1240 | } | ||
| 1241 | |||
| 1242 | extern "C" { | ||
| 1243 | void file_system_changed_callback(); | ||
| 1244 | } | ||
| 1245 | |||
| 1246 | EMSCRIPTEN_KEEPALIVE void file_system_changed_callback() { | ||
| 1247 | if(GEO::FileSystem::file_system_changed_callback_ != nullptr) { | ||
| 1248 | (*GEO::FileSystem::file_system_changed_callback_)(); | ||
| 1249 | } | ||
| 1250 | } | ||
| 1251 | |||
| 1252 | #endif | ||
| 1253 |