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