forked from urho3d/urho3d
/
Path.h
304 lines (255 loc) · 12.5 KB
/
Path.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
#pragma once
#include "../Container/Str.h"
namespace Urho3D {
/// Class that represents a path to a file.
class URHO3D_API Path
{
public:
/// Construct empty path.
Path()=default;
/// Copy construct.
Path(const Path& other)=default;
/// Copy.
Path& operator =(const Path& other)=default;
/// Move construct.
Path(Path&& other): path_(std::move(other.path_)) {}
/// Construct from the specified path string using the specified base path to resolve relative directories as needed.
Path(const String& path, const Path& basePath = Path::EMPTY): path_(MakePathStringInternal(path)) { ResolveRelativeTo(basePath); }
/// Construct from the specified path string using the specified base path to resolve relative directories as needed.
Path(const char* path, const Path& basePath = Path::EMPTY): path_(MakePathStringInternal(path)) { ResolveRelativeTo(basePath); }
/// Construct from the specified wide path string using the specified base path to resolve relative directories as needed.
Path(const wchar_t* path, const Path& basePath = Path::EMPTY): path_(MakePathStringInternal(String(path))) { ResolveRelativeTo(basePath); }
/// Construct from the specified path string when the string is known not to contain self-resolvable relative components ("./" or "resolvable/../") and known to be in the internal representation
static Path CreateFromResolvedInternalString(const String& path) { Path p; p.path_ = path; return p; }
//TODO: ^ Consider renaming the above CreateFromRawString or the like so it's shorter
/// Raw appending to this path with the other path. "A/B" += "C/D.file" --> "A/BC/D.file"
Path& operator +=(const Path& rhs)
{
path_ += rhs.path_;
return *this;
}
/// Smart appending to this path with the other path. "A/B" /= "C/D.file" --> "A/B/C/D.file" ; "A/B/" / "C/D.file" --> "A/B/C/D.file"
Path& operator /=(const Path& rhs)
{
path_ += "/" + rhs.path_;
path_.Replace("//","/");
return *this;
}
/// Raw concatenation to this path with the other path. "A/B" + "C/D.file" --> "A/BC/D.file"
Path operator +(const Path& rhs) const
{
Path p{*this};
p+=rhs;
return p;
}
/// Smart concatenation to this path with the other path. "A/B.ext" / "C/D.file" --> "A/B.ext/C/D.file" ; "A/B/" / "C/D.file" --> "A/B/C/D.file"
Path operator /(const Path& rhs) const
{
Path p{*this};
p/=rhs;
return p;
}
/// Raw concatenation to this path with the other path. "A/B" + "C/D.file" --> "A/BC/D.file"
friend Path operator +(const String& lhs, const Path& rhs)
{
Path p{rhs};
p.path_ = lhs + p.path_;
return p;
}
/// Smart concatenation to this path with the other path. "A/B.ext" / "C/D.file" --> "A/B.ext/C/D.file" ; "A/B/" / "C/D.file" --> "A/B/C/D.file"
friend Path operator /(const String& lhs, const Path& rhs)
{
return Path{lhs} / rhs;
}
/// Case-sensitive equality test with the other past based on the internal path format.
bool operator ==(const Path& rhs) const
{
return path_ == rhs.path_;
}
/// Case-sensitive inequality test with the other past based on the internal path format.
bool operator !=(const Path& rhs) const
{
return path_ != rhs.path_;
}
/// Return whether a path is absolute (starts with "/" on Unix or "[letter]:" on Windows)
bool IsAbsolute() const;
/// Returns true if the path would go up a directory ("../" or "..")
bool IsRequestingParentDirectory() const;
/// Returns true if the path is explicitly relative (starts with "./" or "../")
bool IsExplicitlyRelative() const;
/// Returns true if the path is empty
bool Empty() const
{
return path_.Empty();
}
/// Returns the length of the string
unsigned Length() const
{
return path_.Length();
}
/// Makes the path empty.
void Clear()
{
path_.Clear();
}
/// Return comparison result between two paths, optionally case insensitive.
int Compare(const Path& rhs, bool caseSensitive = true) const
{
return path_.Compare(rhs.path_, caseSensitive);
}
// /// Returns the number of segments in the path separated by "/". Trailing empty string is not counted.
// unsigned NumberOfSegments
// {
// return path_.Count('/') - HasTrailingSlash();
// }
/// Splits the given path using the specified separator character, optionally preserving empty paths.
static Vector<Path> SplitPathsStringStatic(const String& path, char separator = ';', bool keepEmptyPaths = false);
/// Splits the path using the specified separator character, optionally preserving empty paths.
Vector<Path> SplitPaths(char separator = ';', bool keepEmptyPaths = false) const
{
return SplitPathsStringStatic(path_);
}
/// Resolves relative paths agains the base path. The base path may be a file, whose name will be trimmed, or a directory that must end in '/'.
/// If requireRelativePrefix is true relative paths will only be resolved if they begin with './' or '../'.
void ResolveRelativeTo(const Path& basePath, bool requireRelativePrefix = true);
/// Split a full path to path, filename and extension. The extension will be converted to lowercase by default.
void Split(Path& pathName, String& fileName, String& extension, bool lowercaseExtension = true) const;
/// Return the path from a full path.
Path GetDirectoryPath() const;
/// Return the filename from a full path.
String GetFileName() const;
/// Return the extension from a full path, converted to lowercase by default.
String GetExtension(bool lowercaseExtension = true) const;
/// Return the filename and extension from a full path. The case of the extension is preserved by default, so that the file can be opened in case-sensitive operating systems.
String GetFileNameAndExtension(bool lowercaseExtension = false) const;
/// Replace the extension of a file name with another.
void ReplaceExtension(const String& newExtension);
/// Copy and replace the extension of a file name with another.
Path WithReplacedExtension(const String& newExtension) const;
/// Returns true if the path has a traling slash.
bool HasTrailingSlash() const;
/// Add a slash at the end of the path if missing. Does not add a slash to an empty path.
void AddTrailingSlash();
/// Remove the slash from the end of a path if it exists.
void RemoveTrailingSlash();
/// Copy and add a slash at the end of the path if missing and convert to internal format (use slashes).
Path WithTrailingSlash() const;
/// Copy and remove the slash from the end of a path if exists and convert to internal format (use slashes).
Path WithoutTrailingSlash() const;
/// Return the parent path, or the path itself if not available.
Path GetParentPath() const;
/// Convert a path to internal format (use slashes).
const String& GetInternalPathString() const
{
return path_;
}
//Question: Why doesn't GetNativePathString() just return a WString on Windows?
// I believe the only time we don't just convert to a WString after that on Windows is in
// int DoSystemRun(const String& fileName, const Vector<String>& arguments)
// in which I would be fine with having to move fixedFileName into the #ifdef code
// One more in bool LuaScript::LoadRawFile(const String& fileName)
// which should probably be re-written using luaL_loadbuffer instead of luaL_loadfile
// Another couple for Image:
// return stbi_write_tga(GetNativePath(fileName).CString(), width_, height_, components_, data_.Get()) != 0;
// return stbi_write_jpg(GetNativePath(fileName).CString(), width_, height_, components_, data_.Get(), quality) != 0;
// As such I support switching to a [W]String GetNativePathString()
// String GetNativePathStringNarrow() that can hopefully be removed in the future.
// This may sound unreasonable, but if you check stb_image_write.h, we might as well just make it use a wide character string instead
// static int stbi__start_write_file(stbi__write_context *s, const char *filename)
// {
// FILE *f;
// // Urho3D: proper UTF8 handling for Windows, requires Urho3D WString class
// #ifndef _WIN32
// f = fopen(filename, "wb");
// #else
// Urho3D::WString wstr(filename);
// #ifdef STBI_MSC_SECURE_CRT
// if (_wfopen_s(&f, wstr.CString(), L"wb") // NOTE: This line doesn't even match parentheses
// f = NULL;
// #else
// f = _wfopen(wstr.CString(), L"wb");
// #endif
// It may be better to preserve the stb API, though, in case the user has other code that use it
/// Convert a path to the wide character string format required by the operating system.
WString GetWideNativePathString() const;
/// Convert a path to the string format required by the operating system.
String GetNativePathString() const;
// TODO: Evaluate AutoNativePathString
#ifdef _WIN32
using NativeString = WString;
#else
using NativeString = const String&;
#endif
/// Convert a path to the string format required by the operating system with platform-dependent character width.
NativeString GetAutoNativePathString() const;
/// Convert a path to a string.
const String& ToString() const { return GetInternalPathString(); }
/// Return hash value for HashSet & HashMap.
unsigned ToHash() const { return GetInternalPathString().ToHash(); }
#ifdef URHO3D_PATH_BACKWARDS_COMPATABILITY
/// DEPRECATED: Returns the char* representation of the path. Provided only for backwards compatibility.
URHO3D_DEPRECATED
const char* CString() const { return path_.CString(); }
/// DEPRECATED: Implicit conversion to string
URHO3D_DEPRECATED
operator String() const { return path_; }
/// DEPRECATED: Removal of operator String() should eliminate the need for this. Raw concatenation to this path with a string. "A/B" + "../C/D.file" --> "A/B../C/D.file"
URHO3D_DEPRECATED Path operator +(const String& rhs) const
{
return *this + CreateFromResolvedInternalString(rhs);
}
/// DEPRECATED: Removal of operator String() should eliminate the need for this. Raw String to this path with the other path. "A/B" += ../C/D.file" --> "A/B../C/D.file"
URHO3D_DEPRECATED Path& operator +=(const String& rhs)
{
path_ += rhs;
return *this;
}
/// DEPRECATED: Removal of operator String() should eliminate the need for this. Raw concatenation to this path with a string. "A/B" + "../C/D.file" --> "A/B../C/D.file"
URHO3D_DEPRECATED Path operator +(const char* rhs) const
{
return *this + CreateFromResolvedInternalString(rhs);
}
/// DEPRECATED: Removal of operator String() should eliminate the need for this. Raw String to this path with the other path. "A/B" += ../C/D.file" --> "A/B../C/D.file"
URHO3D_DEPRECATED Path& operator +=(const char* rhs)
{
path_ += rhs;
return *this;
}
/// DEPRECATED: Removal of operator String() should eliminate the need for this. Raw concatenation to this path with the other path. "A/B" + "C/D.file" --> "A/BC/D.file"
URHO3D_DEPRECATED friend Path operator +(const char* lhs, const Path& rhs)
{
Path p{rhs};
p.path_ = lhs + p.path_;
return p;
}
/// DEPRECATED: Removal of operator String() should eliminate the need for this. Smart concatenation to this path with the other path. "A/B.ext" / "C/D.file" --> "A/B.ext/C/D.file" ; "A/B/" / "C/D.file" --> "A/B/C/D.file"
URHO3D_DEPRECATED friend Path operator /(const char* lhs, const Path& rhs)
{
return Path{lhs} / rhs;
}
#endif
/// Empty path.
static const Path EMPTY;
private:
/// Moves all relative path segments to the front of the path (e.g. "A/../../" --> "../"
void CompressRelativePath();
/// Trims and converts the string to the internal format (forward slashes).
static String MakePathStringInternal(const String& str);
/// The path.
String path_;
// TODO: UniquePtr Memo of the split path file extension and such, if such info was requested?
// Would mean that sizeof(Path) != sizeof(String)
};
/// Enum controlling the behavior of the GLOB Matching
enum GLOB_STAR_MODE
{
/// "*" will match "/"
GS_REQUIRE_SINGLE_STAR,
/// "**" will match "/", "*" will not
GS_REQUIRE_DOUBLE_STAR,
/// "/" will not be matched
GS_DISABLE_RECURSION
};
/// Test a path against the specified GLOB pattern path. Returns true for a match.
URHO3D_API bool GLOBMatches(const Path& globPattern, const Path& path, GLOB_STAR_MODE mode = GLOB_STAR_MODE::GS_REQUIRE_DOUBLE_STAR);
}