1 /* 2 * Copyright (c) 2012-2014, Bruno Levy 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 * If you modify this software, you should include a notice giving the 30 * name of the person performing the modification, the date of modification, 31 * and the reason for such modification. 32 * 33 * Contact: Bruno Levy 34 * 35 * Bruno.Levy@inria.fr 36 * http://www.loria.fr/~levy 37 * 38 * ALICE Project 39 * LORIA, INRIA Lorraine, 40 * Campus Scientifique, BP 239 41 * 54506 VANDOEUVRE LES NANCY CEDEX 42 * FRANCE 43 * 44 */ 45 46 #ifndef GEOGRAM_BASIC_FILE_SYSTEM 47 #define GEOGRAM_BASIC_FILE_SYSTEM 48 49 #include <geogram/basic/common.h> 50 #include <geogram/basic/numeric.h> 51 #include <geogram/basic/counted.h> 52 #include <geogram/basic/smart_pointer.h> 53 54 #include <string> 55 #include <vector> 56 #include <map> 57 58 /** 59 * \file geogram/basic/file_system.h 60 * \brief Functions and times for filesystem manipulation 61 */ 62 63 namespace GEO { 64 65 /** 66 * \brief Abstraction layer for file-system management. 67 */ 68 namespace FileSystem { 69 70 /** 71 * \brief A Node in a FileSystem. 72 * \details This class abstracts a FileSystem and 73 * operations on it. 74 */ 75 class GEOGRAM_API Node : public Counted { 76 public: 77 /** 78 * \brief Node constructor. 79 */ 80 Node(); 81 82 /** 83 * \brief Node destructor. 84 */ 85 ~Node() override; 86 87 /************************** OS-dependent **************************/ 88 89 /** 90 * \brief Checks if a path is a regular file. 91 * \param[in] path system path to verify. 92 * \retval true if \p path is a regular file. 93 * \retval false otherwise. 94 */ 95 virtual bool is_file(const std::string& path); 96 97 /** 98 * \brief Checks if a path is a directory. 99 * \param[in] path system path to verify. 100 * \retval true if \p path is a directory. 101 * \retval false otherwise. 102 */ 103 virtual bool is_directory(const std::string& path); 104 105 /** 106 * \brief Creates a directory 107 * \details This recursively creates a new directory given by its \b 108 * absolute path \p path, creating any missing intermediate 109 * directories on the fly. 110 * \param[in] path absolute path to the directory to be created. 111 * \retval true if the directory was successfully created. 112 * \retval false otherwise. 113 */ 114 virtual bool create_directory(const std::string& path); 115 116 /** 117 * \brief Deletes a directory 118 * \details This deletes the directory specified by path \p path. 119 * The path must specify an empty directory. 120 * \param[in] path the path of the directory to be removed. 121 * \retval true if the directory was successfully deleted. 122 * \retval false otherwise. 123 */ 124 virtual bool delete_directory(const std::string& path); 125 126 /** 127 * \brief Deletes a file 128 * \param[in] path the path of the file to be deleted. 129 * \retval true if the file path was successfully deleted 130 * \retval false otherwise 131 */ 132 virtual bool delete_file(const std::string& path); 133 134 /** 135 * \brief Lists directory contents 136 * \details Lists all the files and sub-directories in the directory 137 * specified by \p path, and stores the list in \p result. Special 138 * entries "." and ".." are not stored in \p result. 139 * \param[in] path the path to the directory to list. 140 * \param[in] result output vector of files and sub-directories. 141 * \retval true if \p path specifies a readable directory. 142 * \retval false otherwise. 143 */ 144 virtual bool get_directory_entries( 145 const std::string& path, std::vector<std::string>& result 146 ); 147 148 /** 149 * \brief Gets the current working directory. 150 * \return The absolute path to the current directory. 151 */ 152 virtual std::string get_current_working_directory(); 153 154 /** 155 * \brief Sets the working directory. 156 * \param[in] path path to the new working directory. 157 * \retval true if the current directory could be 158 * changed to \p path. 159 * \retval false otherwise. 160 */ 161 virtual bool set_current_working_directory( 162 const std::string& path 163 ); 164 165 /** 166 * \brief Renames or moves a file. 167 * \details This renames the existing file or directory specified by 168 * path \p old_name to the new path \p new_name. The new name 169 * must not be the name of an existing file or directory. 170 * If \p old_name and \p new_name are not in the same directory, 171 * \p old_name is moved to the \p new_name. 172 * \param[in] old_name path of the file or directory to be renamed. 173 * \param[in] new_name new path of the file or directory. 174 * \retval true if the file was renamed successfully. 175 * \retval false otherwise. 176 */ 177 virtual bool rename_file( 178 const std::string& old_name, const std::string& new_name 179 ); 180 181 /** 182 * \brief Gets a file last modification time. 183 * \param[in] path the path to an existing file or directory. 184 * \return the last modification time in seconds 185 */ 186 virtual Numeric::uint64 get_time_stamp(const std::string& path); 187 188 /** 189 * \brief Marks a filename as executable. 190 * \details On unix, it chmods the file, on Windows, does nothing. 191 * \param[in] filename name of the file to be made executable 192 * \retval true on success. 193 * \retval false otherwise. 194 */ 195 virtual bool set_executable_flag(const std::string& filename); 196 197 198 /** 199 * \brief Modifies the last modification time of a file. 200 * \param[in] filename name of the file. 201 * \retval true on success. 202 * \retval false otherwise. 203 */ 204 virtual bool touch(const std::string& filename); 205 206 /** 207 * \brief Normalizes a path. 208 * \details A path is normalized if it is absolute and it does not 209 * contain any "../" component. 210 * \param[in] path the path to be normalized. The path can have 211 * components that do not exist. 212 * \return the normalized path 213 */ 214 virtual std::string normalized_path(const std::string& path); 215 216 217 /** 218 * \brief Gets the current user's home directory. 219 * \return The path to the current user's home directory 220 * as a string. 221 */ 222 virtual std::string home_directory(); 223 224 /** 225 * \brief Gets the current user's home directory. 226 * \details Under unix, it returns the content of the HOME 227 * environment 228 * variable. Under Windows, it returns the "My Documents" 229 * directory. 230 * \return The path to the current user's home directory 231 * as a string. 232 */ 233 virtual std::string documents_directory(); 234 235 236 /** 237 * \brief Load file contents in a string. 238 * \param[in] path the path to the file 239 * \return a string with the contents of the file. 240 */ 241 virtual std::string load_file_as_string(const std::string& path); 242 243 /************************ OS-independent **************************/ 244 245 /** 246 * \brief Gets a path extension 247 * \details Extracts the extension from the path \p path, 248 * that is any character that appear after the last dot (.) 249 * and after any 250 * directory separator character. If \p path has no extension, the 251 * empty string is returned. 252 * 253 * Examples 254 * - extension("/dir/file.cpp") -> "cpp" 255 * - extension("file") -> "" 256 * - extension("/dir.ext/file") -> "" 257 * 258 * \param[in] path the path to a file or directory 259 * \return the path's extension (without the dot), or the empty 260 * string if none. 261 */ 262 virtual std::string extension(const std::string& path); 263 264 /** 265 * \brief Gets a path base name 266 * \details Extracts the base name from the path \p path, 267 * that is any 268 * character that appear after the last directory separator. If 269 * parameter \p remove_extension is \c true (the default), the 270 * extension is removed from the base name, otherwise is it kept. If 271 * the path does not contain any directory separator, the whole path 272 * is returned. 273 * 274 * Examples 275 * - base_name("/dir/file.cpp") -> "file" 276 * - base_name("/dir/file.cpp", false) -> "file.cpp" 277 * - base_name("file") -> "file" 278 * 279 * \param[in] path the path to a file or directory 280 * \param[in] remove_extension whether to remove the extension from 281 * the base name or not. 282 */ 283 virtual std::string base_name( 284 const std::string& path, bool remove_extension = true 285 ); 286 287 /** 288 * \brief Gets a path directory 289 * \details Extracts the directory from the path \p path, 290 * that is any character that appear before the last directory 291 * separator. If the path does not contain any directory 292 * separator, string "." is returned. 293 * 294 * Examples 295 * - dir_name("/dir/file.cpp") -> "dir" 296 * - dir_name("file") -> "." 297 * - dir_name("/") -> "/" 298 * 299 * \param[in] path the path to a file or directory 300 * \return the path directory or "." if none 301 */ 302 virtual std::string dir_name(const std::string& path); 303 304 /** 305 * \brief Lists directory contents 306 * \details Lists all the files and sub-directories in the directory 307 * specified by \p path, and stores the list in \p result. Special 308 * entries "." and ".." are not stored in \p result. If parameter 309 * recursive is set to \c true, \p result will include the entries 310 * of all sub-directories in \p path recursively. 311 * \param[in] path the path to an existing directory 312 * \param[in] result output vector of entries in \p path 313 * \param[in] recursive recursively traverses all sub-directories in 314 * \p path 315 */ 316 virtual void get_directory_entries( 317 const std::string& path, 318 std::vector<std::string>& result, bool recursive 319 ); 320 321 /** 322 * \brief Lists files in a directory 323 * \details Lists all the files in the directory specified by 324 * \p path, and stores the list in \p result. Special entries "." 325 * and ".." are not stored in \p result. If parameter recursive 326 * is set to \c true, \p result will include the entries of all 327 * sub-directories in \p path recursively. 328 * \param[in] path the path to an existing directory 329 * \param[in] result output vector of files in \p path 330 * \param[in] recursive recursively traverses all sub-directories in 331 * \p path 332 * \see get_directory_entries() 333 */ 334 virtual void get_files( 335 const std::string& path, 336 std::vector<std::string>& result, bool recursive = false 337 ); 338 339 /** 340 * \brief Lists sub-directories in a directory 341 * \details Lists all the sub-directories in the directory specified 342 * by \p path, and stores the list in \p result. Special entries "." 343 * and ".." are not stored in \p result. If parameter recursive 344 * is set to \c true, \p result will include the entries of all 345 * sub-directories in \p path recursively. 346 * \param[in] path the path to an existing directory 347 * \param[in] result output vector of sub-directories in \p path 348 * \param[in] recursive recursively traverses all sub-directories in 349 * \p path 350 * \see get_directory_entries() 351 */ 352 virtual void get_subdirectories( 353 const std::string& path, 354 std::vector<std::string>& result, bool recursive = false 355 ); 356 357 /** 358 * \brief Converts a path to Unix format 359 * \details It changes all Windows "\" directory separators into 360 * Unix "/" directory separators. 361 * \param[in,out] path the path to be converted 362 */ 363 virtual void flip_slashes(std::string& path); 364 365 /** 366 * \brief Copies a file 367 * \param[in] from name of the file to be copied 368 * \param[out] to name of the copy 369 * \retval true if the copy was successful 370 * \retval false otherwise 371 */ 372 virtual bool copy_file( 373 const std::string& from, const std::string& to 374 ); 375 }; 376 377 /** 378 * \brief Implementation of a file system stored in memory. 379 */ 380 class GEOGRAM_API MemoryNode : public Node { 381 public: 382 383 /** 384 * \brief MemoryNode constructor. 385 * \param[in] path full path to this node. 386 */ path_(path)387 MemoryNode(const std::string& path="/") : path_(path) { 388 } 389 390 /** \copydoc Node::copy_file() */ 391 bool copy_file( 392 const std::string& from, const std::string& to 393 ) override ; 394 395 /** \copydoc Node::load_file_as_string() */ 396 std::string load_file_as_string(const std::string& path) override; 397 398 /** \copydoc Node::is_file() */ 399 virtual bool is_file(const std::string& path) override; 400 401 /** \copydoc Node::is_directory() */ 402 virtual bool is_directory(const std::string& path) override; 403 404 /** \copydoc Node::create_directory() */ 405 virtual bool create_directory(const std::string& path) override; 406 407 /** \copydoc Node::delete_directory() */ 408 virtual bool delete_directory(const std::string& path) override; 409 410 /** \copydoc Node::delete_file() */ 411 virtual bool delete_file(const std::string& path) override; 412 413 /** \copydoc Node::get_directory_entries() */ 414 bool get_directory_entries( 415 const std::string& path, std::vector<std::string>& result 416 ) override; 417 418 419 /** \copydoc Node::rename_file() */ 420 bool rename_file( 421 const std::string& old_name, const std::string& new_name 422 ) override; 423 424 /** 425 * \brief Gets the contents of a file. 426 * \param[in] path the path to the file. 427 * \return a const pointer to the contents of the file. 428 */ 429 const char* get_file_contents(const std::string& path); 430 431 /** 432 * \brief Creates a file. 433 * \param[in] path the path to the file 434 * \param[in] content a const pointer to the contents of the file 435 */ 436 bool create_file(const std::string& path, const char* content); 437 438 protected: 439 /** 440 * \brief Splits a path. 441 * \param[in] path the path 442 * \param[out] leadingsubdir the leading subdirectory or the 443 * empty string 444 * \param[out] rest the rest of the path 445 */ 446 static void split_path( 447 const std::string& path, std::string& leadingsubdir, 448 std::string& rest 449 ); 450 451 private: 452 std::string path_; 453 std::map<std::string, SmartPointer<MemoryNode> > subnodes_; 454 std::map<std::string, const char*> files_; 455 }; 456 457 typedef SmartPointer<Node> Node_var; 458 459 /**********************************************************/ 460 461 /** 462 * \brief Initializes the FileSystem library. 463 * \details This function is automatically called during 464 * Geogram startup. It should not be called by client 465 * code. 466 */ 467 void GEOGRAM_API initialize(); 468 469 /** 470 * \brief Terminates the FileSystem library. 471 * \details This function is automatically called during 472 * Geogram shutdown. It should not be called by client 473 * code. 474 */ 475 void GEOGRAM_API terminate(); 476 477 /** \copydoc FileSystem::Node::is_file() */ 478 bool GEOGRAM_API is_file(const std::string& path); 479 480 /** \copydoc FileSystem::Node::is_directory() */ 481 bool GEOGRAM_API is_directory(const std::string& path); 482 483 /** \copydoc FileSystem::Node::create_directory() */ 484 bool GEOGRAM_API create_directory(const std::string& path); 485 486 /** \copydoc FileSystem::Node::delete_directory() */ 487 bool GEOGRAM_API delete_directory(const std::string& path); 488 489 /** \copydoc FileSystem::Node::delete_file() */ 490 bool GEOGRAM_API delete_file(const std::string& path); 491 492 /** \copydoc FileSystem::Node::get_directory_entries() */ 493 bool GEOGRAM_API get_directory_entries( 494 const std::string& path, std::vector<std::string>& result 495 ); 496 497 /** \copydoc FileSystem::Node::get_current_working_directory() */ 498 std::string GEOGRAM_API get_current_working_directory(); 499 bool GEOGRAM_API set_current_working_directory( 500 const std::string& path 501 ); 502 503 /** \copydoc FileSystem::Node::rename_file() */ 504 bool GEOGRAM_API rename_file( 505 const std::string& old_name, const std::string& new_name 506 ); 507 508 /** \copydoc FileSystem::Node::get_time_stamp() */ 509 Numeric::uint64 GEOGRAM_API get_time_stamp( 510 const std::string& path 511 ); 512 513 /** \copydoc FileSystem::Node::extension() */ 514 std::string GEOGRAM_API extension(const std::string& path); 515 516 /** \copydoc FileSystem::Node::base_name() */ 517 std::string GEOGRAM_API base_name( 518 const std::string& path, bool remove_extension = true 519 ); 520 521 /** \copydoc FileSystem::Node::dir_name() */ 522 std::string GEOGRAM_API dir_name(const std::string& path); 523 524 /** \copydoc FileSystem::Node::get_directory_entries() */ 525 void GEOGRAM_API get_directory_entries( 526 const std::string& path, 527 std::vector<std::string>& result, bool recursive 528 ); 529 530 /** \copydoc FileSystem::Node::get_files() */ 531 void GEOGRAM_API get_files( 532 const std::string& path, 533 std::vector<std::string>& result, bool recursive = false 534 ); 535 536 /** \copydoc FileSystem::Node::get_subdirectories() */ 537 void GEOGRAM_API get_subdirectories( 538 const std::string& path, 539 std::vector<std::string>& result, bool recursive = false 540 ); 541 542 /** \copydoc FileSystem::Node::flip_slashes() */ 543 void GEOGRAM_API flip_slashes(std::string& path); 544 545 /** \copydoc FileSystem::Node::copy_file() */ 546 bool GEOGRAM_API copy_file( 547 const std::string& from, const std::string& to 548 ); 549 550 /** \copydoc FileSystem::Node::set_executable_flag() */ 551 bool GEOGRAM_API set_executable_flag(const std::string& filename); 552 553 /** \copydoc FileSystem::Node::touch() */ 554 bool GEOGRAM_API touch(const std::string& filename); 555 556 /** \copydoc FileSystem::Node::normalized_path() */ 557 std::string GEOGRAM_API normalized_path(const std::string& path); 558 559 /** \copydoc FileSystem::Node::home_directory() */ 560 std::string GEOGRAM_API home_directory(); 561 562 /** \copydoc FileSystem::Node::documents_directory() */ 563 std::string GEOGRAM_API documents_directory(); 564 565 /** 566 * \brief Gets the root of the file system. 567 * \param[out] root a pointer to the root of 568 * the FileSystem. 569 */ 570 void GEOGRAM_API get_root(Node*& root); 571 572 #ifdef GEO_OS_EMSCRIPTEN 573 /** 574 * \brief Declares a function to be called whenever the file system 575 * changes. 576 * \details The function will be called when the user loads a file 577 * using the button in the webpage. 578 * \param[in] callback the function to be called. 579 */ 580 void set_file_system_changed_callback(void(*callback)()); 581 #endif 582 583 } 584 } 585 586 #endif 587 588