1 // Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
2 //
3 // Permission to use, copy, modify, and distribute this software for any
4 // purpose with or without fee is hereby granted, provided that the above
5 // copyright notice and this permission notice appear in all copies.
6 //
7 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13 // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 //
15 // Aegisub Project http://www.aegisub.org/
16 
17 #include <libaegisub/exception.h>
18 #include <libaegisub/fs_fwd.h>
19 
20 #include <boost/filesystem/path.hpp>
21 #include <cstdint>
22 #include <ctime>
23 #include <iterator>
24 #include <memory>
25 #include <string>
26 
27 #undef CreateDirectory
28 
29 namespace agi {
30 	namespace fs {
31 		/// Define a filesystem error which takes a path or a string
32 #define DEFINE_FS_EXCEPTION(type, base, message) \
33 		struct type : public base { \
34 			type(path const& p) : base(message + p.string()) { } \
35 			type(std::string const& s) : base(s) { } \
36 			const char *GetName() const { return ""; } \
37 			Exception *Copy() const { return new type(*this); } \
38 		}
39 
40 		/// @class agi::FileSystemError
41 		/// @extends agi::Exception
42 		/// @brief Base class for errors related to the file system
43 		///
44 		/// This base class can not be instantiated.
45 		/// File system errors do not support inner exceptions, as they
46 		/// are always originating causes for errors.
47 		DEFINE_EXCEPTION(FileSystemError, Exception);
48 
49 		/// A file can't be accessed for some reason
50 		DEFINE_FS_EXCEPTION(FileNotAccessible, FileSystemError, "File is not accessible: ");
51 
52 		/// A file can't be accessed because there's no file by the given name
53 		DEFINE_FS_EXCEPTION(FileNotFound, FileNotAccessible, "File not found: ");
54 
55 		/// An error of some unknown type has occured
56 		DEFINE_EXCEPTION(FileSystemUnknownError, FileSystemError);;
57 
58 		/// The path exists, but isn't a file
59 		DEFINE_FS_EXCEPTION(NotAFile, FileNotAccessible, "Path is not a file (and should be): ");
60 
61 		/// The path exists, but isn't a directory
62 		DEFINE_FS_EXCEPTION(NotADirectory, FileNotAccessible, "Path is not a directory (and should be): ");
63 
64 		/// The given path is too long for the filesystem
65 		DEFINE_FS_EXCEPTION(PathTooLog, FileSystemError, "Path is too long: ");
66 
67 		/// Insufficient free space to complete operation
68 		DEFINE_FS_EXCEPTION(DriveFull, FileSystemError, "Insufficient free space to write file: ");
69 
70 		/// Base class for access denied errors
71 		DEFINE_FS_EXCEPTION(AccessDenied, FileNotAccessible, "Access denied to path: ");
72 
73 		/// Trying to read the file gave an access denied error
74 		DEFINE_FS_EXCEPTION(ReadDenied, AccessDenied, "Access denied when trying to read: ");
75 
76 		/// Trying to write the file gave an access denied error
77 		DEFINE_FS_EXCEPTION(WriteDenied, AccessDenied, "Access denied when trying to write: ");
78 
79 		/// File exists and cannot be overwritten due to being read-only
80 		DEFINE_FS_EXCEPTION(ReadOnlyFile, WriteDenied, "File is read-only: ");
81 
82 		bool Exists(path const& p);
83 		bool FileExists(path const& file);
84 		bool DirectoryExists(path const& dir);
85 
86 		/// Get the local-charset encoded shortname for a file
87 		///
88 		/// This is purely for compatibility with external libraries which do
89 		/// not support unicode filenames on Windows. On all other platforms,
90 		/// it is a no-op.
91 		std::string ShortName(path const& file_path);
92 
93 		/// Check for amount of free space on a path
94 		uintmax_t FreeSpace(path const& dir_path);
95 
96 		/// Get the size in bytes of the file at path
97 		///
98 		/// @throws agi::FileNotFound if path does not exist
99 		/// @throws agi::acs::NotAFile if path is a directory
100 		/// @throws agi::acs::Read if path exists but could not be read
101 		uintmax_t Size(path const& file_path);
102 
103 		/// Get the modification time of the file at path
104 		///
105 		/// @throws agi::FileNotFound if path does not exist
106 		/// @throws agi::acs::NotAFile if path is a directory
107 		/// @throws agi::acs::Read if path exists but could not be read
108 		time_t ModifiedTime(path const& file_path);
109 
110 		/// Create a directory and all required intermediate directories
111 		/// @throws agi::acs::Write if the directory could not be created.
112 		///
113 		/// Trying to create a directory which already exists is not an error.
114 		bool CreateDirectory(path const& dir_path);
115 
116 		/// Touch the given path
117 		///
118 		/// Creates the file if it does not exist, or updates the modified
119 		/// time if it does
120 		void Touch(path const& file_path);
121 
122 		/// Rename a file or directory
123 		/// @param from Source path
124 		/// @param to   Destination path
125 		void Rename(path const& from, path const& to);
126 
127 		/// Copy a file
128 		/// @param from Source path
129 		/// @param to   Destination path
130 		///
131 		/// The destination path will be created if it does not exist.
132 		void Copy(path const& from, path const& to);
133 
134 		/// Delete a file
135 		/// @param path Path to file to delete
136 		/// @throws agi::FileNotAccessibleError if file exists but could not be deleted
137 		bool Remove(path const& file);
138 
139 		/// Check if the file has the given extension
140 		/// @param p Path to check
141 		/// @param ext Case-insensitive extension, without leading dot
142 		bool HasExtension(path const& p, std::string const& ext);
143 
144 		agi::fs::path Canonicalize(agi::fs::path const& path);
145 
146 		class DirectoryIterator {
147 			struct PrivData;
148 			std::shared_ptr<PrivData> privdata;
149 			std::string value;
150 		public:
151 			typedef path value_type;
152 			typedef path* pointer;
153 			typedef path& reference;
154 			typedef size_t difference_type;
155 			typedef std::forward_iterator_tag iterator_category;
156 
157 			bool operator==(DirectoryIterator const&) const;
158 			bool operator!=(DirectoryIterator const& rhs) const { return !(*this == rhs); }
159 			DirectoryIterator& operator++();
160 			std::string const& operator*() const { return value; }
161 
162 			DirectoryIterator(path const& p, std::string const& filter);
163 			DirectoryIterator();
164 			~DirectoryIterator();
165 
166 			template<typename T> void GetAll(T& cont);
167 		};
168 
begin(DirectoryIterator & it)169 		inline DirectoryIterator& begin(DirectoryIterator &it) { return it; }
end(DirectoryIterator &)170 		inline DirectoryIterator end(DirectoryIterator &) { return DirectoryIterator(); }
171 
172 		template<typename T>
GetAll(T & cont)173 		inline void DirectoryIterator::GetAll(T& cont) {
174 			copy(*this, end(*this), std::back_inserter(cont));
175 		}
176 	}
177 }
178