-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathpaths.h
More file actions
826 lines (736 loc) · 31.3 KB
/
paths.h
File metadata and controls
826 lines (736 loc) · 31.3 KB
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
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
#pragma once
/**
* Cross platform file utilities, Copyright (c) 2014-2018, Jorma Rebane
* Distributed under MIT Software License
*
* @note This module predates C++17 filesystem and offers a different
* set of convenient API's for dealing with every-day File I/O Tasks.
*/
#if _MSC_VER
# pragma warning(disable: 4251)
#endif
#include "config.h"
#include "strview.h"
#include <ctime> // time_t
#include <vector>
#if __has_include("sprint.h")
# include "sprint.h"
#endif
namespace rpp /* ReCpp */
{
////////////////////////////////////////////////////////////////////////////////
/**
* @return TRUE if the file exists, arg ex: "dir/file.ext"
*/
RPPAPI bool file_exists(strview filename) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI bool file_exists(ustrview filename) noexcept;
#endif
/**
* @return TRUE if the file is a symlink
*/
RPPAPI bool is_symlink(strview filename) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI bool is_symlink(ustrview filename) noexcept;
#endif
/**
* @return TRUE if the folder exists, arg ex: "root/dir" or "root/dir/"
*/
RPPAPI bool folder_exists(strview folder) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI bool folder_exists(ustrview filename) noexcept;
#endif
/**
* @return TRUE if either a file or a folder exists at the given path
*/
RPPAPI bool file_or_folder_exists(strview fileOrFolder) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI bool file_or_folder_exists(ustrview fileOrFolder) noexcept;
#endif
/**
* @brief Creates a symbolic link to a file or folder
* @param target Destination where the link will point to
* @param link Name of the link to create
*/
RPPAPI bool create_symlink(strview target, strview link) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI bool create_symlink(ustrview target, ustrview link) noexcept;
#endif
/**
* @brief Gets basic information of a file
* @param filename Name of the file, ex: "dir/file.ext"
* @param filesize (optional) If not null, writes the long size of the file
* @param created (optional) If not null, writes the file creation date
* @param accessed (optional) If not null, writes the last file access date
* @param modified (optional) If not null, writes the last file modification date
* @return TRUE if the file exists and required data was retrieved from the OS
*/
RPPAPI bool file_info(strview filename, int64* filesize, time_t* created,
time_t* accessed, time_t* modified) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI bool file_info(ustrview filename, int64* filesize, time_t* created,
time_t* accessed, time_t* modified) noexcept;
#endif // RPP_ENABLE_UNICODE
/**
* @brief Gets basic information of a file
* @param fd File handle, either Winapi HANDLE or POSIX fd
* @param filesize (optional) If not null, writes the long size of the file
* @param created (optional) If not null, writes the file creation date
* @param accessed (optional) If not null, writes the last file access date
* @param modified (optional) If not null, writes the last file modification date
* @return TRUE if the file exists and required data was retrieved from the OS
*/
RPPAPI bool file_info(intptr_t fd, int64* filesize, time_t* created,
time_t* accessed, time_t* modified) noexcept;
/**
* @return Short size of a file
*/
RPPAPI int file_size(strview filename) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI int file_size(ustrview filename) noexcept;
#endif
/**
* @return Long size of a file
*/
RPPAPI int64 file_sizel(strview filename) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI int64 file_sizel(ustrview filename) noexcept;
#endif
/**
* @return File creation date
*/
RPPAPI time_t file_created(strview filename) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI time_t file_created(ustrview filename) noexcept;
#endif
/**
* @return Last file access date
*/
RPPAPI time_t file_accessed(strview filename) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI time_t file_accessed(ustrview filename) noexcept;
#endif
/**
* @return Last file modification date
*/
RPPAPI time_t file_modified(strview filename) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI time_t file_modified(ustrview filename) noexcept;
#endif
/**
* @brief Deletes a single file, ex: "root/dir/file.ext"
* @return TRUE if the file was actually deleted (can fail due to file locks or access rights)
*/
RPPAPI bool delete_file(strview filename) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI bool delete_file(ustrview filename) noexcept;
#endif
/**
* @brief Renames oldPath to newPath, overwriting newPath if it exists.
* @note Falls back to copy + delete when moving across filesystem boundaries.
* @return TRUE if the file was renamed successfully
*/
RPPAPI bool rename_file(strview oldPath, strview newPath) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI bool rename_file(ustrview oldPath, ustrview newPath) noexcept;
#endif
inline bool move_file(strview oldPath, strview newPath) noexcept { return rename_file(oldPath, newPath); }
#if RPP_ENABLE_UNICODE
inline bool move_file(ustrview oldPath, ustrview newPath) noexcept { return rename_file(oldPath, newPath); }
#endif
/**
* @brief Copies sourceFile to destinationFile, overwriting the previous file!
* @note File access rights are also copied
* @return TRUE if sourceFile was opened && destinationFile was created && copied successfully
*/
RPPAPI bool copy_file(strview sourceFile, strview destinationFile) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI bool copy_file(ustrview sourceFile, ustrview destinationFile) noexcept;
#endif
/**
* @brief Copies file access rights from sourceFile to destinationFile
* @return TRUE if file access rights were copied successfully
*/
RPPAPI bool copy_file_mode(strview sourceFile, strview destinationFile) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI bool copy_file_mode(ustrview sourceFile, ustrview destinationFile) noexcept;
#endif
/**
* @brief Copies sourceFile to destinationFile IF destinationFile doesn't exist
* @return TRUE if destinationFile exists or copy_file return true
*/
RPPAPI bool copy_file_if_needed(strview sourceFile, strview destinationFile) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI bool copy_file_if_needed(ustrview sourceFile, ustrview destinationFile) noexcept;
#endif
/**
* @brief Copies sourceFile into destinationFolder, overwriting the previous file!
* @return TRUE if sourceFile was opened && destinationFolder was created && copied successfully
*/
RPPAPI bool copy_file_into_folder(strview sourceFile, strview destinationFolder) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI bool copy_file_into_folder(ustrview sourceFile, ustrview destinationFolder) noexcept;
#endif
/**
* Creates a folder, recursively creating folders that do not exist
* @param foldername Relative or Absolute path
* @return TRUE if the final folder was actually created (can fail due to access rights)
*/
RPPAPI bool create_folder(strview foldername) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI bool create_folder(ustrview foldername) noexcept;
#endif
enum class delete_mode
{
non_recursive,
recursive,
};
/**
* Deletes a folder, by default only if it's empty.
* @param foldername Relative or Absolute path
* @param mode If delete_mode::recursive, all subdirectories and files will also be deleted (permanently)
* @return TRUE if the folder was deleted
*/
RPPAPI bool delete_folder(strview foldername, delete_mode mode = delete_mode::non_recursive) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI bool delete_folder(ustrview foldername, delete_mode mode = delete_mode::non_recursive) noexcept;
#endif
/**
* @brief Resolves a relative path to a full path name using filesystem path resolution
* @result "path" ==> "C:\Projects\Test\path"
*/
RPPAPI string full_path(strview path) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI ustring full_path(ustrview path) noexcept;
#endif
/**
* @brief Merges all ../ inside of a path
* @result ../lib/../bin/file.txt ==> ../bin/file.txt
*/
RPPAPI string merge_dirups(strview path) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI ustring merge_dirups(ustrview path) noexcept;
#endif
/**
* @brief Extract the filename (no extension) from a file path
*
* @result /root/dir/file.ext ==> file
* @result /root/dir/file ==> file
* @result /root/dir/ ==>
* @result file.ext ==> file
* @result /.git/f.reallylong ==> f.reallylong
* @result /.git/filewnoext ==> filewnoext
*/
RPPAPI strview file_name(strview path) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI ustrview file_name(ustrview path) noexcept;
#endif
/**
* @brief Extract the file part (with ext) from a file path
* @result /root/dir/file.ext ==> file.ext
* @result /root/dir/file ==> file
* @result /root/dir/ ==>
* @result file.ext ==> file.ext
*/
RPPAPI strview file_nameext(strview path) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI ustrview file_nameext(ustrview path) noexcept;
#endif
/**
* @brief Extract the extension from a file path
* @result /root/dir/file.ext ==> ext
* @result /root/dir/file ==>
* @result /root/dir/ ==>
* @result file.ext ==> ext
* @result /.git/f.reallylong ==>
* @result /.git/filewnoext ==>
*/
RPPAPI strview file_ext(strview path) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI ustrview file_ext(ustrview path) noexcept;
#endif
/**
* @brief Replaces the current file path extension
* Usage: file_replace_ext("dir/file.old", "new");
* @result /dir/file.old ==> /dir/file.new
* @result /dir/file ==> /dir/file.new
* @result /dir/ ==> /dir/
* @result file.old ==> file.new
*/
RPPAPI string file_replace_ext(strview path, strview ext) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI ustring file_replace_ext(ustrview path, ustrview ext) noexcept;
#endif
/**
* @brief Changes only the file name by appending a string, leaving directory and extension untouched
* @result /dir/file.txt ==> /dir/fileadd.txt
* @result /dir/file ==> /dir/fileadd
* @result /dir/ ==> /dir/
* @result file.txt ==> fileadd.txt
*/
RPPAPI string file_name_append(strview path, strview add) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI ustring file_name_append(ustrview path, ustrview add) noexcept;
#endif
/**
* @brief Replaces only the file name of the path, leaving directory and extension untouched
* @result /dir/file.txt ==> /dir/replaced.txt
* @result /dir/file ==> /dir/replaced
* @result /dir/ ==> /dir/
* @result file.txt ==> replaced.txt
*/
RPPAPI string file_name_replace(strview path, strview newFileName) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI ustring file_name_replace(ustrview path, ustrview newFileName) noexcept;
#endif
/**
* @brief Replaces the file name and extension, leaving directory untouched
* @result /dir/file.txt ==> /dir/replaced.bin
* @result /dir/file ==> /dir/replaced
* @result /dir/ ==> /dir/
* @result file.txt ==> replaced.bin
*/
RPPAPI string file_nameext_replace(strview path, strview newFileNameAndExt) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI ustring file_nameext_replace(ustrview path, ustrview newFileNameAndExt) noexcept;
#endif
/**
* @brief Extract the foldername from a path name
* @result /root/dir/file.ext ==> dir
* @result /root/dir/file ==> dir
* @result /root/dir/ ==> dir
* @result dir/ ==> dir
* @result file.ext ==>
*/
RPPAPI strview folder_name(strview path) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI ustrview folder_name(ustrview path) noexcept;
#endif
/**
* @brief Extracts the full folder path from a file path.
* Will preserve / and assume input is always a filePath
* @result /root/dir/file.ext ==> /root/dir/
* @result /root/dir/file ==> /root/dir/
* @result /root/dir/ ==> /root/dir/
* @result dir/ ==> dir/
* @result file.ext ==>
*/
RPPAPI strview folder_path(strview path) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI ustrview folder_path(ustrview path) noexcept;
#endif
/**
* @brief Normalizes the path string to use a specific type of slash
* @note This does not perform full path expansion.
* @note The string is modified in-place !careful!
*
* @result \root/dir\\file.ext ==> /root/dir/file.ext
*/
RPPAPI string& normalize(string& path, char sep = '/') noexcept;
RPPAPI char* normalize(char* path, char sep = '/') noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI ustring& normalize(ustring& path, char16_t sep = u'/') noexcept;
RPPAPI char16_t* normalize(char16_t* path, char16_t sep = u'/') noexcept;
#endif
/**
* @brief Normalizes the path string to use a specific type of slash
* @note A copy of the string is made
*/
RPPAPI string normalized(strview path, char sep = '/') noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI ustring normalized(ustrview path, char16_t sep = u'/') noexcept;
#endif
/**
* @brief Efficiently combines path strings, removing any repeated / or \
* @result path_combine("tmp", "file.txt") ==> "tmp/file.txt"
* @result path_combine("tmp/", "file.txt") ==> "tmp/file.txt"
* @result path_combine("tmp/", "/file.txt") ==> "tmp/file.txt"
* @result path_combine("tmp/", "/folder//") ==> "tmp/folder"
*/
RPPAPI string path_combine(strview path1, strview path2) noexcept;
RPPAPI string path_combine(strview path1, strview path2, strview path3) noexcept;
RPPAPI string path_combine(strview path1, strview path2, strview path3, strview path4) noexcept;
#if RPP_ENABLE_UNICODE
/**
* @brief Efficiently combines two WideChar path strings, removing any repeated / or \
*/
RPPAPI ustring path_combine(ustrview path1, ustrview path2) noexcept;
RPPAPI ustring path_combine(ustrview path1, ustrview path2, ustrview path3) noexcept;
RPPAPI ustring path_combine(ustrview path1, ustrview path2, ustrview path3, ustrview path4) noexcept;
#endif
////////////////////////////////////////////////////////////////////////////////
using string_list = std::vector<string>;
#if RPP_ENABLE_UNICODE
using ustring_list = std::vector<ustring>;
#endif
// common base class which doesn't rely on specific string type
class RPPAPI dir_iter_base
{
public:
struct impl;
struct state {
#if _WIN32
void* hFind;
char ffd[592]; // WIN32_FIND_DATAW
#else
void* d;
void* e;
string dirname;
#endif
impl* operator->() noexcept;
const impl* operator->() const noexcept;
};
state s; // iterator state
dir_iter_base(strview dir) noexcept;
#if RPP_ENABLE_UNICODE
dir_iter_base(ustrview dir) noexcept;
#endif
~dir_iter_base() noexcept;
explicit operator bool() const noexcept;
bool next() noexcept; // find next directory entry
bool is_dir() const noexcept; // if current entry is a dir
bool is_file() const noexcept; // if current entry is a file
bool is_symlink() const noexcept; // if current entry is a symbolic link
bool is_device() const noexcept; // if current entry is a block device, character device, named pipe or socket
// gets specific type of string, performing necessary string conversions
template<StringViewType T>
struct name_util
{
static auto get(const dir_iter_base* it) noexcept -> typename T::string_t;
};
};
/**
* Basic and minimal directory iterator.
* @note This is not recursive!
* Example usage:
* @code
* for (dir_entry e : dir_iterator { dir })
* if (e.is_dir) e.add_path_to(out);
* @endcode
*/
template<StringViewType T>
class RPPAPI directory_iter : public dir_iter_base
{
public:
using view_t = T;
using char_t = typename T::char_t;
using string_t = typename T::string_t;
private:
const string_t dir; // original path used to construct this dir_iterator
mutable string_t fulldir; // full path to directory we're iterating
public:
explicit directory_iter(view_t dir) noexcept
: dir_iter_base{dir}
, dir{dir.to_string()}
{
}
explicit directory_iter(const string_t& dir) noexcept
: dir_iter_base{dir}
, dir{dir}
{
}
explicit directory_iter(string_t&& dir) noexcept
: dir_iter_base{view_t{dir}}
, dir{std::move(dir)}
{
}
~directory_iter() noexcept = default;
directory_iter(directory_iter&& it) = delete;
directory_iter(const directory_iter&) = delete;
directory_iter& operator=(directory_iter&& it) = delete;
directory_iter& operator=(const directory_iter&) = delete;
struct entry
{
const directory_iter* it;
const string_t name; // name of the entry, file, dir, etc.
entry(const directory_iter* it) noexcept
: it{it}, name{dir_iter_base::name_util<view_t>::get(it)}
{}
/** @returns "{input_dir}/{entry.name}" */
string_t path() const noexcept { return rpp::path_combine(it->path(), name); }
/** @returns absolute path to "{input_dir}/{entry.name}" */
string_t full_path() const noexcept { return rpp::path_combine(it->full_path(), name); }
/** @returns TRUE if current iterator state points to a valid FILE or DIR */
bool is_valid() const noexcept { return (is_file() || is_dir()); }
/** @returns TRUE if this is a directory */
bool is_dir() const noexcept { return it && it->is_dir(); }
/** @returns TRUE if this is a regular file */
bool is_file() const noexcept { return it && it->is_file(); }
/** @returns TRUE if this is a symbolic link */
bool is_symlink() const noexcept { return it && it->is_symlink(); }
/** @returns TRUE if this is a block device, character device, named pipe or socket */
bool is_device() const noexcept { return it && it->is_device(); }
bool add_path_to(std::vector<string_t>& out) const noexcept {
if (is_valid()) {
out.emplace_back(path());
return true;
}
return false;
}
bool add_full_path_to(std::vector<string_t>& out) const noexcept {
if (is_valid()) {
out.emplace_back(full_path());
return true;
}
return false;
}
};
struct iter { // bare minimum for basic looping
directory_iter* it;
bool operator==(const iter& other) const noexcept { return it == other.it; }
bool operator!=(const iter& other) const noexcept { return it != other.it; }
entry operator*() const noexcept { return { it }; }
iter& operator++() noexcept {
if (!it || !it->next()) it = nullptr;
return *this;
}
};
entry current() const noexcept { return { this }; }
iter begin() noexcept { return { this->operator bool() ? this : nullptr }; }
iter end() const noexcept { return { nullptr }; }
/** @returns Current entry name, can be file, dir, etc */
string_t name() const noexcept
{
return dir_iter_base::name_util<view_t>::get(this);
}
/** @returns original path used to construct this dir_iterator */
const string_t& path() const noexcept { return dir; }
/** @returns full path to directory we're iterating */
const string_t& full_path() const noexcept
{
return fulldir.empty() ? (fulldir = rpp::full_path(dir)) : fulldir;
}
}; // directory_iter<T>
template<StringViewType T>
using directory_entry = typename directory_iter<T>::entry;
// ascii version of the directory iterator
using dir_iterator = directory_iter<strview>;
using dir_entry = directory_entry<strview>;
#if RPP_ENABLE_UNICODE
// unicode version of the directory iterator
using udir_iterator = directory_iter<ustrview>;
using udir_entry = directory_entry<ustrview>;
#endif
////////////////////////////////////////////////////////////////////////////////
enum list_dir_flags
{
dir_current = 0, // default: only current directory
dir_relpath = 0, // default: relative paths
dir_relpath_current = 0,
dir_recursive = (1 << 1), // recursively lists through directories
dir_relpath_recursive = dir_relpath | dir_recursive, // recursive and relpaths
dir_fullpath = (1 << 2), // emits fullpath instead of relative paths
dir_fullpath_recursive = dir_recursive | dir_fullpath, // both recursive and fullpath
dir_relpath_combine = (1 << 3), // lists relpath, but outputs: path_combine(input_dir, relpath)
dir_relpath_combine_recursive = dir_relpath_combine | dir_recursive,
};
inline list_dir_flags operator|(list_dir_flags a, list_dir_flags b) noexcept { return list_dir_flags(int(a) | int(b)); }
/**
* Lists all folders inside this directory
* @param out Destination vector for result folder names (not full folder paths!)
* @param dir Relative or full path of this directory
* @param flags enum list_dir_flags:
* dir_recursive - will perform a recursive search
* dir_fullpath - returned paths will be fullpaths instead of relative
* dir_relpath_combine - combines input_dir with relpaths
* @return Number of folders found
*
* @code
* @example [dir_recursive=false] [dir_fullpath=false] vector<string> {
* "bin",
* "src",
* "lib",
* };
* @example [dir_recursive=true] [dir_fullpath=false] vector<string> {
* "bin",
* "bin/data",
* "bin/data/models",
* "src",
* "src/mesh",
* "lib",
* };
* @example [dir_recursive=false] [dir_fullpath=true] vector<string> {
* "/projects/ReCpp/bin",
* "/projects/ReCpp/src",
* "/projects/ReCpp/lib",
* };
* @example [dir_recursive=true] [dir_fullpath=true] vector<string> {
* "/projects/ReCpp/bin",
* "/projects/ReCpp/bin/data",
* "/projects/ReCpp/bin/data/models",
* "/projects/ReCpp/src",
* "/projects/ReCpp/src/mesh",
* "/projects/ReCpp/lib",
* };
* @endcode
*/
RPPAPI int list_dirs( string_list& out, strview dir, list_dir_flags flags = {}) noexcept;
inline string_list list_dirs( strview dir, list_dir_flags flags = {}) noexcept { string_list out; list_dirs(out, dir, flags); return out; }
#if RPP_ENABLE_UNICODE
RPPAPI int list_dirs(ustring_list& out, ustrview dir, list_dir_flags flags = {}) noexcept;
inline ustring_list list_dirs(ustrview dir, list_dir_flags flags = {}) noexcept { ustring_list out; list_dirs(out, dir, flags); return out; }
#endif
/**
* Lists all files inside this directory that have the specified extension (default: all files)
* @param out Destination vector for result file names.
* @param dir Relative or full path of this directory
* @param suffix Filter files by suffix, ex: ".txt" or "_mask.jpg", default ("") lists all files
* @param flags enum list_dir_flags:
* dir_recursive - will perform a recursive search
* dir_fullpath - returned paths will be fullpaths instead of relative
* dir_relpath_combine - combines input_dir with relpaths
* @return Number of files found that match the extension
* Example:
* @code
* vector<string> relativePaths = list_files(folder);
* vector<string> recursiveRelativePaths = list_files_recursive(folder);
* @endcode
* @code
* @example [dir_recursive=false] [dir_fullpath=false] vector<string> {
* "main.cpp",
* "file_io.h",
* "file_io.cpp",
* };
* @example [dir_recursive=false] [dir_fullpath=true] vector<string> {
* "/projects/ReCpp/main.cpp",
* "/projects/ReCpp/file_io.h",
* "/projects/ReCpp/file_io.cpp",
* };
* @example [dir_recursive=true] [dir_fullpath=false] vector<string> {
* "main.cpp",
* "file_io.h",
* "file_io.cpp",
* "mesh/mesh.h",
* "mesh/mesh.cpp",
* "mesh/mesh_obj.cpp",
* "mesh/mesh_fbx.cpp",
* };
* @example [dir_recursive=true] [dir_fullpath=true] vector<string> {
* "/projects/ReCpp/main.cpp",
* "/projects/ReCpp/file_io.h",
* "/projects/ReCpp/file_io.cpp",
* "/projects/ReCpp/mesh/mesh.h",
* "/projects/ReCpp/mesh/mesh.cpp",
* "/projects/ReCpp/mesh/mesh_obj.cpp",
* "/projects/ReCpp/mesh/mesh_fbx.cpp",
* };
* @endcode
*/
RPPAPI int list_files( string_list& out, strview dir, strview suffix, list_dir_flags flags = {}) noexcept;
inline string_list list_files(strview dir, strview suffix, list_dir_flags flags = {}) noexcept { string_list out; list_files(out, dir, suffix, flags); return out; }
#if RPP_ENABLE_UNICODE
RPPAPI int list_files(ustring_list& out, ustrview dir, ustrview suffix, list_dir_flags flags = {}) noexcept;
inline ustring_list list_files(ustrview dir, ustrview suffix, list_dir_flags flags = {}) noexcept { ustring_list out; list_files(out, dir, suffix, flags); return out; }
#endif
/**
* Recursively lists all files under this directory and its subdirectories that match the list of suffixes.
* @param dir Relative or full path of root directory
* @param suffixes Filter files by suffixes, ex: {"txt","_old.cfg","_mask.jpg"}
* @param flags enum list_dir_flags:
* dir_recursive - will perform a recursive search
* dir_fullpath - returned paths will be fullpaths instead of relative
* dir_relpath_combine - combines input_dir with relpaths
* @return vector of resulting relative file paths
*/
RPPAPI int list_files(string_list& out, strview dir, const std::vector<strview>& suffixes, list_dir_flags flags = {}) noexcept;
inline string_list list_files( strview dir, const std::vector<strview>& suffixes, list_dir_flags flags = {}) noexcept { string_list out; list_files(out, dir, suffixes, flags); return out; }
#if RPP_ENABLE_UNICODE
RPPAPI int list_files(ustring_list& out, ustrview dir, const std::vector<ustrview>& suffixes, list_dir_flags flags = {}) noexcept;
inline ustring_list list_files(ustrview dir, const std::vector<ustrview>& suffixes, list_dir_flags flags = {}) noexcept { ustring_list out; list_files(out, dir, suffixes, flags); return out; }
#endif
/**
* Lists all files and folders inside a dir.
* @param outDirs All found directories relative to input dir
* @param outFiles All found files
* @param dir Directory to search in
* @param flags enum list_dir_flags:
* dir_recursive - will perform a recursive search
* dir_fullpath - returned paths will be fullpaths instead of relative
* dir_relpath_combine - combines input_dir with relpaths
* @return Number of files and folders found that match the extension
* Example:
* @code
* string dir = "C:/Projects/ReCpp";
* vector<string> dirs, files;
* list_alldir(dirs, files, dir, true, false); // recursive, relative paths
* // dirs: { ".git", ".git/logs", ... }
* // files: { ".git/config", ..., ".gitignore", ... }
* @endcode
*/
RPPAPI int list_alldir( string_list& outDirs, string_list& outFiles, strview dir, list_dir_flags flags = {}) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI int list_alldir(ustring_list& outDirs, ustring_list& outFiles, ustrview dir, list_dir_flags flags = {}) noexcept;
#endif
/**
* @return The current working directory of the application.
* An extra slash is always appended.
* Path is always normalized to forward slashes /
* @example Linux: "/home/jorma/Projects/ReCpp/"
* @example Windows: "C:/Projects/ReCpp/"
*/
RPPAPI string working_dir() noexcept;
#if RPP_ENABLE_UNICODE
inline ustring working_diru() noexcept { return to_ustring(working_dir()); }
#endif
/**
* @return Directory where the current module in which Rpp was linked to is located.
* For DLL-s which link RPP statically, this is the folder where the DLL is located.
* @param moduleObject [null] If RPP is a DLL then a platform specific
* object must be passed for identification to work.
* This is only if the dynamic module is not in the same binary
* as RPP, such as external dynamic libaries.
*
* An extra slash is always appended.
* Path is always normalized to forward slashes /
*
* @code
* string modulePath = rpp::module_dir(); // "/path/to/project/bin/"
* @endcode
*
* Specifying external dynamic modules:
* @code
* // macOS/iOS
* string modulePath = rpp::module_dir(MyLibraryObject.class);
*
* // Win32
* string modulePath = rpp::module_dir(GetModuleHandle("fbxsdk"));
* @endcode
*/
RPPAPI string module_dir(void* moduleObject = nullptr) noexcept;
/**
* Identical to `module_dir` but also includes the DLL/EXE name,
* resulting in a full path to enclosing binary.
* Path is always normalized to forward slashes /
*/
RPPAPI string module_path(void* moduleObject = nullptr) noexcept;
/**
* Calls chdir() to set the working directory of the application to a new value
* @return TRUE if chdir() is successful
*/
RPPAPI bool change_dir(strview new_wd) noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI bool change_dir(ustrview new_wd) noexcept;
#endif
/**
* @return The system temporary directory for storing misc files
* @note For windows this is: %USERPROFILE%/AppData/Local/Temp/
* @note for iOS this is $TMPDIR: %APPSTORAGE%/tmp/
* A trailing slash is always appended to the path
* Path is always normalized to forward slashes /
*/
RPPAPI string temp_dir() noexcept;
#if RPP_ENABLE_UNICODE
inline ustring temp_diru() noexcept { return to_ustring(temp_dir()); }
#endif
/**
* @return The system home directory for this user
* @note ENV $HOME is used
* A trailing slash is always appended to the path.
* Path is always normalized to forward slashes /
*/
RPPAPI string home_dir() noexcept;
#if RPP_ENABLE_UNICODE
RPPAPI ustring home_diru() noexcept;
#endif
////////////////////////////////////////////////////////////////////////////////
} // namespace rpp