fennec
Loading...
Searching...
No Matches
path.h
1// =====================================================================================================================
2// fennec, a free and open source game engine
3// Copyright © 2025 Medusa Slockbower
4//
5// This program is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program. If not, see <https://www.gnu.org/licenses/>.
17// =====================================================================================================================
18
19#ifndef FENNEC_FILESYSTEM_PATH_H
20#define FENNEC_FILESYSTEM_PATH_H
21
22#include <fennec/filesystem/path.h>
23#include <fennec/string/string.h>
24
25namespace fennec
26{
27
33struct path
34{
35public:
36// Definitions =========================================================================================================
37
38 class iterator;
39 friend iterator;
40
41
42// Static Functions ====================================================================================================
43
46 static path current();
47
51 static path current(const path& path);
52
53
54// Constructors ========================================================================================================
55
58 path() : _str("/") { }
59
63 path(const cstring& str)
64 : _str(str) {
65 if (str.size() > 2 && str[str.size() - 1] == '/') {
66 _str = _str.substring(0, str.size() - 1);
67 }
68 }
69
73 path(const string& str)
74 : _str(str) {
75 if (str.size() > 2 && str[str.size() - 1] == '/') {
76 _str = _str.substring(0, str.size() - 1);
77 }
78 }
79
83 path(const path& p)
84 : _str(p._str) {
85 }
86
90 path(path&& p) noexcept : _str(move(p._str)) { }
91
92
93// Assignment Operators ================================================================================================
94
99 template<size_t n>
100 path& operator=(const char (&str)[n]) {
101 _str = str;
102 return *this;
103 }
104
109 path& operator=(const cstring& p) {
110 _str = p;
111 return *this;
112 }
113
118 path& operator=(const string& p) {
119 _str = p;
120 return *this;
121 }
122
127 path& operator=(const path& p) {
128 _str = p._str;
129 return *this;
130 }
131
136 path& operator=(path&& p) noexcept {
137 _str = move(p._str);
138 return *this;
139 }
140
141
142// Append Operators ====================================================================================================
143
148 path operator/(const cstring& str) const {
149 return path(_str + '/' + str);
150 }
151
152 path operator/(const string& str) const {
153 return path(_str + '/' + str);
154 }
155
156 path operator/(const path& p) const {
157 return path(_str + '/' + p._str);
158 }
159
160 bool operator==(const path& p) const {
161 return _str == p._str;
162 }
163
164 string filename() const {
165 size_t i = _str.rfind('/');
166 return _str.substring(i + 1);
167 }
168
169 const string& str() const { return _str; }
170 const char* cstr() const { return _str.cstr(); }
171
172 bool empty() {
173 size_t size = _str.size();
174 if (size == 0) return true;
175#if FENNEC_PLATFORM_WINDOWS
176 return (_str[1] == ':' && size == 3);
177#else
178 return (_str[0] == '/' && size == 1);
179#endif
180 }
181
182 path parent() const {
183#ifdef FENNEC_PLATFORM_WINDOWS
184 size_t start = _str.size() - 1;
185 start = _str[start] == '/' || _str[start] == '\\' ? start - 1 : start;
186
187 size_t r = _str.rfind('/', start);
188 size_t l = _str.rfind('\\', start);
189 if (r == _str.size()) {
190 start = l;
191 }
192 else if (l == _str.size()) {
193 start = r;
194 }
195 else {
196 start = max(r, l);
197 }
198 return _str.substring(0, start);
199#else
200 size_t start = _str.size() - 1;
201 start = _str[start] == '/' ? start - 1 : start;
202 return path(_str.substring(0, _str.rfind('/', start)));
203#endif
204 }
205
206 path absolute() const {
207 path parse = *this;
208 path working; working._str.resize(0);
209
210// Check if this is a rooted path;
211#ifdef FENNEC_PLATFORM_WINDOWS
212 if (_str[1] != ':') {
213#else
214 if (_str[0] != '/') {
215#endif
216 working = current();
217 }
218
219 while (not parse.empty()) {
220 // Handle dots
221 while (not parse.empty() && parse._str[0] == '.') {
222 // Check for ".."
223 if (parse._str[1] == '.') {
224 // ".."
225 if (parse._str.size() == 2) {
226 parse = path();
227 working = working.parent();
228 }
229 // "../"
230 else if (parse._str[2] == '/') {
231 working = working.parent();
232 parse._str = parse._str.substring(3);
233 }
234 }
235 // "./"
236 else if (parse._str[1] == '/') {
237 parse._str = parse._str.substring(2);
238 }
239 }
240
241 if (parse.empty()) break;
242
243 // Push the path
244 const size_t loc = parse._str.find('/');
245 working._str += '/';
246 working._str += parse._str.substring(0, loc);
247 parse._str = parse._str.substring(loc + 1);
248 }
249
250 return working;
251 }
252
253
254// Iterator ============================================================================================================
255
256 iterator begin() const {
257 return iterator(this, 0);
258 }
259
260 iterator end() const {
261 return iterator(this, _str.size());
262 }
263
264 class iterator {
265 public:
266 constexpr iterator(const path* path, size_t p)
267 : _str(&path->_str)
268 , _pos(p) {
269
270 // Handle end()
271 if (p == _str->size()) {
272 return;
273 }
274
275 // Handle rooted paths
276#ifdef FENNEC_PLATFORM_WINDOWS
277 if ((*_str)[1] == ':') {
278 _pos = max(_pos, size_t(3));
279 }
280#else
281 if ((*_str)[0] == '/') {
282 _pos = max(_pos, size_t(1));
283 }
284#endif
285
286 // Ensure we are at the start of a directory/file name
287 if (_pos != 0 && (*_str)[_pos - 1] != '/') {
288 _pos = _str->find('/', _pos) + 1;
289 }
290 }
291
292 constexpr iterator(const iterator&) = default;
293 constexpr iterator(iterator&&) noexcept = default;
294
295 constexpr string operator*() const {
296 if ((*_str)[_pos] == '/') {
297 return string("");
298 }
299
300 size_t e = _str->find('/', _pos);
301 return _str->substring(_pos, e - _pos);
302 }
303
304 constexpr iterator& operator++() {
305 _pos = min(_str->find('/', _pos) + 1, _str->size());
306 return *this;
307 }
308
309 constexpr iterator operator++(int) {
310 iterator it = *this;
311 this->operator++();
312 return it;
313 }
314
315 constexpr bool operator==(const iterator& rhs) const {
316 return _str == rhs._str and _pos == rhs._pos;
317 }
318
319 constexpr bool operator!=(const iterator& rhs) const {
320 return _str != rhs._str or _pos != rhs._pos;
321 }
322
323 private:
324 const string* _str;
325 size_t _pos;
326 };
327
328private:
329 string _str;
330};
331
332}
333
334#endif // FENNEC_FILESYSTEM_PATH_H
constexpr genType min(genType x, genType y)
Returns if otherwise it returns .
Definition common.h:688
constexpr genType max(genType x, genType y)
Returns if , otherwise it returns .
Definition common.h:705
constexpr genType e()
Definition constants.h:635
constexpr size_t rfind(char c, size_t i=npos) const
Finds the index of the last occurrence of c in the string.
Definition string.h:291
constexpr size_t size() const
Definition string.h:150
constexpr _string substring(size_t i, size_t n=npos) const
Retrieve a substring of a string.
Definition string.h:351
This struct wraps c-style strings.
Definition cstring.h:64
constexpr size_t size() const
Definition cstring.h:187
struct for handling file paths
Definition path.h:34
static path current()
Get the current working directory.
Definition path.cpp:40
path & operator=(const char(&str)[n])
C-String Assignment Operator.
Definition path.h:100
path & operator=(path &&p) noexcept
Path Move Assignment Operator.
Definition path.h:136
path & operator=(const cstring &p)
C-String Assignment Operator.
Definition path.h:109
path(path &&p) noexcept
Path Move Constructor.
Definition path.h:90
path operator/(const cstring &str) const
Definition path.h:148
path(const cstring &str)
C-String Conversion Constructor.
Definition path.h:63
path(const string &str)
String Conversion Constructor.
Definition path.h:73
path & operator=(const string &p)
String Assignment Operator.
Definition path.h:118
path()
Default Constructor, returns the root of the current working directory.
Definition path.h:58
path(const path &p)
Path Copy Constructor.
Definition path.h:83
path & operator=(const path &p)
Path Copy Assignment Operator.
Definition path.h:127
constexpr remove_reference_t< T > && move(T &&x) noexcept
produces an x-value type to indicate x may be "moved"
Definition utility.h:92