-
Notifications
You must be signed in to change notification settings - Fork 2
/
cpath.h
2499 lines (2141 loc) · 65.1 KB
/
cpath.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
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
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
MIT License
Copyright (c) 2019 Braedon Wooding
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef CPATH_H
#define CPATH_H
#ifdef __cplusplus
extern "C" {
#endif
/*
Note this library also works with C++ via a C++ provided binding
which is available for free just by including this on a C++ compiler
or with __cplusplus defined
If you want:
- Unicode just add a #define CPATH_UNICODE or UNICODE or _UNICODE
- Custom Allocators just #define:
- CPATH_MALLOC and CPATH_FREE (make sure to define both)
*/
/*
Note on why this library offers no guarantees on reentrant systems
or no TOUTOC (and other race conditions):
- Often supporting this requires breaking a ton of compatability
and often the behaviour of each of the individual commands differs way
too much to make it consistent. Even when fully following it we can't
guarantee that you won't just misuse it and still cause the race conditions
- The majority (99%) of cases simply just don't care about it how often
are your files being written over...
- We have extra safety on things like opening directories and recursive
solutions to make sure that things like that don't happen.
- How do you want to handle it properly? You may want to just keep going
or you may want some way to unwind what you previously did, or just take
a snapshot of the system... It is too varied for us to offer a generalised
way.
- The majority of libraries don't offer it or they offer it to an extremely
limited number of commands with the others not having it, in my opinion
this is worse than just not offering it. Atleast we are consistent with
our guarantees
- File Systems are a mess and already are basically a gigantic global mess
trying to guarantee any sort of safety is not only a complexity mess but
also can give the wrong idea.
- You should be able to detect hard failures due to the change in a folder
and simply just re-call. Of course this could happen repeatedly but
come on... In reality it will occur once in a blue moon.
- Reloading files is easy it is just cpathLoadFiles
*/
/*
@TODO:
- Windows has lifted their max path so we could use the new functions
I don't think all functions have an alternative and it could be more messy
still look into it eventually.
*/
// custom allocators
#if !defined CPATH_MALLOC && !defined CPATH_FREE
#define CPATH_MALLOC(size) malloc(size)
#define CPATH_FREE(ptr) free(ptr)
#elif (defined CPATH_MALLOC && !defined CPATH_FREE) || \
(defined CPATH_FREE && !defined CPATH_MALLOC)
#error "Can't define only free or only malloc have to define both or neither"
#endif
// Support unicode
#if defined CPATH_UNICODE || (!defined UNICODE && defined _UNICODE)
#define UNICODE
#endif
#if defined CPATH_UNICODE || (defined UNICODE && !defined _UNICODE)
#define _UNICODE
#endif
#if !defined CPATH_UNICODE && (defined UNICODE || defined _UNICODE)
#define CPATH_UNICODE
#endif
#ifdef _MSC_VER
#define _CPATH_FUNC_ static __inline
#elif !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L
#define _CPATH_FUNC_ static __inline__
#else
#define _CPATH_FUNC_ static inline
#endif
/* == #includes == */
#if defined _MSC_VER || defined __MINGW32__
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#endif
#ifdef _MSC_VER
#include <tchar.h>
// Ignore all warnings to do with std functions not being 100% safe
// they are fine
#pragma warning(push)
#pragma warning (disable : 4996)
#else
#include <dirent.h>
#include <stddef.h>
#include <libgen.h>
#include <sys/stat.h>
#endif
// NOTE: This has to appear before defined BSD
// since sys/param.h defines BSD given environment
#if defined __unix__ || (defined __APPLE__ && defined __MACH__)
#include <sys/param.h>
#endif
#if defined __linux__ || defined BSD
#include <limits.h>
#endif
#ifdef __MINGW32__
#include <tchar.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <stdint.h>
// Linux has a max of 255 (+1 for \0) I couldn't find a max on windows
// But since 260 > 256 it is a reasonable value that should be crossplatform
#define CPATH_MAX_FILENAME_LEN (256)
/* Windows Unicode Support */
#if defined _MSC_VER || defined __MINGW32__
#define CPATH_STR(str) _TEXT(str)
#define cpath_str_length _tcslen
#define cpath_str_copy _tcscpy
#define cpath_strn_copy _tcsncpy
#define cpath_str_find_last_char _tcsrchr
#define cpath_str_compare _tcscmp
#define cpath_str_compare_safe _tcsncmp
#define cpath_fopen _tfopen
#define cpath_sprintf _stprintf
#define cpath_vsnprintf _vsntprintf
#else
#define CPATH_STR(str) str
#define cpath_str_length strlen
#define cpath_str_copy strcpy
#define cpath_strn_copy strncpy
#define cpath_str_find_last_char strrchr
#define cpath_str_compare strcmp
#define cpath_str_compare_safe strncmp
#define cpath_fopen fopen
#define cpath_sprintf sprintf
#define cpath_vsnprintf vsnprintf
#endif
#if defined _MSC_VER || defined __MINGW32__
#define CPATH_MAX_PATH_LEN MAX_PATH
#elif defined __linux__ || defined BSD
// @TODO: Do we add one? for \0
// I have found conflicting info
// we definitely don't for windows
#ifdef PATH_MAX
#define CPATH_MAX_PATH_LEN PATH_MAX
#endif
#endif
// Found this value online in a few other libraries
// so I'll just continue this tradition/standard despite not knowing why
// @A: This seems to be the max on Linux so I suppose its a reasonable value
#ifndef CPATH_MAX_PATH_LEN
#define CPATH_MAX_PATH_LEN (4096)
#endif
// on windows we need extra space for the \* mask that is required
// yes this is very weird... Maybe we can just chuck it onto the max path?
// I don't think we can because it seems that it is independent of the max path
// i.e. if you can have a max path of 256 characters this includes the extra
// in this case we could just subtract it from the max path
// but then again we may not always want to apply the mask (and we don't)
// so that isn't a great solution either...
// @TODO: Figure this stuff out.
#ifdef _MSC_VER
#define CPATH_PATH_EXTRA_CHARS (2)
#else
#define CPATH_PATH_EXTRA_CHARS (0)
#endif
/*
NOTE: This isn't the maximum number of drives (that would be 26)
This is the length of a drive prepath i.e. D:\ which is clearly 3
No such max exists on other systems to the best of my knowledge
By defining it like (static_assert(0, msg), 1) we make sure that we don't
have a compiler syntax error in it's typical usage we prefer having
the error from having the static assert rather than the error for having
an incorrect token.
@TODO: Do we actually need this???
*/
#if defined _MSC_VER
#define FILE_IS(f, flag) !!(f.dwFileAttributes & FILE_ATTRIBUTE_##flag)
#define FILE_IS_NOT(f, flag) !(f.dwFileAttributes & FILE_ATTRIBUTE_##flag)
#endif
#if defined _MSC_VER || defined __MINGW32__
#define CPATH_MAX_DRIVE_LEN (3)
#elif __cpp_static_assert
#define CPATH_MAX_DRIVE_LEN \
(static_assert(0, "You shouldn't use max drive length on non windows"), 1)
#elif __STDC_VERSION__ >= 201112L
#define CPATH_MAX_DRIVE_LEN \
(_Static_assert(0, "You shouldn't use max drive length on non windows"), 1)
#else
#define CPATH_MAX_DRIVE_LEN \
("You shouldn't use max drive length on non windows", )
#endif
#if defined _WIN32
#define _cpath_getcwd _getcwd
#else
#include <unistd.h> // for getcwd()
#define _cpath_getcwd getcwd
#endif
#if !defined _MSC_VER
#if defined __MINGW32__ && defined _UNICODE
#define _cpath_opendir _wopendir
#define _cpath_readdir _wreaddir
#define _cpath_closedir _wclosedir
#else
#define _cpath_opendir opendir
#define _cpath_readdir readdir
#define _cpath_closedir closedir
#endif
#endif
#if defined CPATH_FORCE_CONVERSION_SYSTEM
#if defined _MSC_VER || defined __MINGW32__
#define CPATH_SEP CPATH_STR('\\')
#define CPATH_OTHER_SEP CPATH_STR('/')
#else
#define CPATH_SEP CPATH_STR('/')
#define CPATH_OTHER_SEP CPATH_STR('\\')
#endif
#else
#define CPATH_SEP CPATH_STR('/')
#define CPATH_OTHER_SEP CPATH_STR('\\')
#endif
#if !defined CPATH_NO_CPP_BINDINGS && defined __cplusplus
namespace cpath { namespace internals {
#endif
// @NOTE: gdb/lldb can't print our strings
// for some reason they don't understand the typedefs
// however they will understand a #define cause they'll
// just see it as a char symbol.
// @BUG: @TODO: Make it so it can be a typedef...
// Currently I just have decided to make it default to this
// Worse case we introduce a #define
#if defined _MSC_VER || defined __MINGW32__
// todo
typedef cpath_offset_t;
typedef cpath_time_t;
typedef TCHAR cpath_char_t;
#else
typedef char cpath_char_t;
typedef off_t cpath_offset_t;
typedef time_t cpath_time_t;
#endif
#if !defined _MSC_VER
#if defined __MINGW32__ && defined _UNICODE
typedef _WDIR cpath_dirdata_t;
typedef struct _wdirent cpath_dirent_t;
#else
typedef DIR cpath_dirdata_t;
typedef struct dirent cpath_dirent_t;
#endif
#endif
typedef cpath_char_t *cpath_str;
typedef int(*cpath_err_handler)();
typedef int(*cpath_cmp)(const void*, const void*);
typedef struct cpath_t {
cpath_char_t buf[CPATH_MAX_PATH_LEN];
size_t len;
} cpath;
typedef struct cpath_file_t {
int isDir;
int isReg;
int isSym;
#if !defined _MSC_VER
#if defined __MINGW32__
struct _stat stat;
#else
struct stat stat;
#endif
#endif
int statLoaded;
cpath path;
cpath_char_t name[CPATH_MAX_FILENAME_LEN];
cpath_str extension;
} cpath_file;
typedef struct cpath_dir_t {
cpath_file *files;
size_t size;
#ifdef _MSC_VER
HANDLE_ handle;
WIN32_FIND_DATA findData;
#else
cpath_dirdata_t *dir;
cpath_dirent_t *dirent;
#endif
// This is set whenever you do an emplace
// This allows you to revert an emplace
struct cpath_dir_t *parent;
int hasNext;
cpath path;
} cpath_dir;
// This allows a forced type
typedef uint8_t CPathByteRep;
enum CPathByteRep_ {
BYTE_REP_JEDEC = 0, // KB = 1024, ...
BYTE_REP_DECIMAL = 1, // 1000 interval segments, B, kB, MB, GB, ...
BYTE_REP_IEC = 2, // KiB = 1024, ...
BYTE_REP_DECIMAL_UPPER = 3, // 1000 interval segments but B, KB, MB, GB, ...
BYTE_REP_DECIMAL_LOWER = 4, // 1000 interval segments but b, kb, mb, gb, ...
// representations
BYTE_REP_LONG = 0b10000000, // Represent as words i.e. kibibytes
BYTE_REP_BYTE_WORD = 0b01000000, // Just B as Byte (only bytes)
};
typedef void(*cpath_traverse_it)(
cpath_file *file, cpath_dir *parent, int depth,
void *data
);
/* == Declarations == */
/* == Path == */
/*
A clear path is always just '.'
When constructing a path it will always ignore extra / or \ at the end
and multiple in a row.
i.e. C://a\b\\c\ is just C:/a/b/c/
If you wish to force conversion to the system specific one
(/ on posix \ on windows) then just add
#define CPATH_FORCE_CONVERSION_SYSTEM
i.e. on Windows it will be C:\a\b\c on Posix it will be C:/a/b/c
*/
/*
Does a strcpy but converts all separators and ignores multiple in a row
Returns how many characters were copied.
*/
_CPATH_FUNC_
size_t cpathStrCpyConv(cpath_str dest, size_t len, const cpath_char_t *src);
/*
Trim all trailing / or \ from a path
*/
_CPATH_FUNC_
void cpathTrim(cpath *path);
/*
Construct a path from a utf8 string
*/
_CPATH_FUNC_
cpath cpathFromUtf8(const char *str);
/*
Copy a path
*/
_CPATH_FUNC_
void cpathCopy(cpath *out, const cpath *in);
/*
Construct a path from a system typed string.
*/
_CPATH_FUNC_
int cpathFromStr(cpath *out, const cpath_char_t *str);
/*
Concatenate a path with a literal
*/
#define CPATH_CONCAT_LIT(path, other) cpathConcatStr(path, CPATH_STR(other))
/*
Appends to a path with a literal, wont add a /
*/
#define CPATH_APPEND_LIT(path, other) cpathAppendStr(path, CPATH_STR(other))
/*
Append to the cpath buffer.
NOTE: Won't add a /
There is no concat version since it would be too inefficient
and thus encourage bad habits. Just add a / everytime you call this
*/
_CPATH_FUNC_
void cpathAppendSprintf(cpath *out, const cpath_char_t *fmt, ...);
/*
Appends to the path (not adding /)
*/
_CPATH_FUNC_
int cpathAppendStrn(cpath *out, const cpath_char_t *other, size_t len);
/*
Appends to the path (not adding /)
*/
_CPATH_FUNC_
int cpathAppendStr(cpath *out, const cpath_char_t *other);
/*
Appends to the path (not adding /)
*/
_CPATH_FUNC_
int cpathAppend(cpath *out, const cpath *other);
/*
Concatenate a path (adds / for you) from a system typed string
*/
_CPATH_FUNC_
int cpathConcatStrn(cpath *out, const cpath_char_t *other, size_t len);
/*
Concatenate a path (adds / for you) from a system typed string
*/
_CPATH_FUNC_
int cpathConcatStr(cpath *out, const cpath_char_t *other);
/*
Concat a path with another.
*/
_CPATH_FUNC_
int cpathConcat(cpath *out, const cpath *other);
/*
Does the path exist.
*/
_CPATH_FUNC_
int cpathExists(const cpath *path);
/*
Attempts to canonicalise without system calls
Will fail in cases where it can't predict the path
i.e. C:/a/../b will be just b
*/
_CPATH_FUNC_
int cpathCanonicaliseNoSysCall(cpath *out, cpath *path);
/*
Resolves the path. Involves system calls.
*/
_CPATH_FUNC_
int cpathCanonicalise(cpath *out, cpath *path);
/*
Tries to canonicalise without any system calls
then resorts to the system call version if it fails
*/
_CPATH_FUNC_
int cpathCanonicaliseAvoidSysCall(cpath *out, cpath *path);
/*
Clears a given path.
*/
_CPATH_FUNC_
void cpathClear(cpath *path);
/*
Iterate through every component. Doesn't include the \
i.e. \usr\local\bin\myfile.so will be '' 'usr' 'local' 'bin' 'myfile.so'
If you wish to iterate from the beginning set index to 0. Else set index
to whatever index you wish to iterate from. It'll restore the previous
segments as it goes meaning on completion the path will be fully restored.
NOTE: Modifies the path buffer so you shouldn't use the path for anything
Until this returns NULL (indicating no more path segments)
OR you use cpathItRefRestore() note that on the last path segment
it obviously won't have to add an '\0' so it'll also work if you bail
out then. It is hard to detect that though.
You can strdup it yourself or do a memcpy if you care about editing it
*/
_CPATH_FUNC_
const cpath_char_t *cpathItRef(cpath *path, int *index);
/*
Restores the path to a usable state.
ONLY needs to be called if the path is wanted to be usable and you haven't
iterated through all the references.
If the index isn't valid it'll cycle through the path fixing it up.
This is just for the sake of it, it'll make things easier and less prone
to breaking.
Do note that this will update index so that you can use it again with
it ref to get the resumed behaviour you would expect (effectively just a ++)
*/
_CPATH_FUNC_
void cpathItRefRestore(cpath *path, int *index);
/*
Go up a directory effectively going back by one /
i.e. C:/D/E/F/g.c => C:/D/E/F and so on...
Returns true if it succeeded
Could possibly involve a canonicalise if the path is a symlink
i.e. ./ has to be resolved and so does ~/
*/
_CPATH_FUNC_
int cpathUpDir(cpath *path);
/*
Converts all separators to the given separator
Note: will only convert / and \
*/
_CPATH_FUNC_
void cpathConvertSepCustom(cpath *path, cpath_char_t sep);
/*
Converts all seperators to the default one.
*/
_CPATH_FUNC_
void cpathConvertSep(cpath *path) { cpathConvertSepCustom(path, CPATH_SEP); }
/* == File System == */
/*
Get the current working directory allocating the space
*/
_CPATH_FUNC_
cpath_char_t *cpathGetCwdAlloc();
/*
Get the current working directory passing in a buffer
*/
_CPATH_FUNC_
cpath_char_t *cpathGetCwdBuf(cpath_char_t *buf, size_t size);
/*
Write the cwd to a path buffer.
*/
_CPATH_FUNC_
void cpathWriteCwd(cpath *path);
/*
Get the cwd as a path.
*/
_CPATH_FUNC_
cpath cpathGetCwd();
/*
Opens a directory placing information into the given directory buffer.
*/
_CPATH_FUNC_
int cpathOpenDir(cpath_dir *dir, const cpath *path);
/*
Restarts the given directory iterator.
*/
_CPATH_FUNC_
int cpathRestartDir(cpath_dir *dir);
/*
Clear directory data.
If closing parents then will recurse through all previous emplaces.
*/
_CPATH_FUNC_
void cpathCloseDir(cpath_dir *dir);
/*
Move to the next file in the iterator.
*/
_CPATH_FUNC_
int cpathMoveNextFile(cpath_dir *dir);
/*
Load stat. Sets statLoaded.
*/
_CPATH_FUNC_
int cpathGetFileInfo(cpath_file *file);
/*
Load file flags such as isDir, isReg, isSym
Attempts to not use stat since that is slow
*/
_CPATH_FUNC_
int cpathLoadFlags(cpath_dir *dir, cpath_file *file, void *data);
/*
Peeks the next file but doesn't move the iterator along.
*/
_CPATH_FUNC_
int cpathPeekNextFile(cpath_dir *dir, cpath_file *file);
/*
Get the next file inside the directory, acts like an iterator.
i.e. while (cpathGetNextFile(...)) can use dir->hasNext to verify
that there are indeed a file but it is safe!
File may be null if you don't wish to actually get a copy
*/
_CPATH_FUNC_
int cpathGetNextFile(cpath_dir *dir, cpath_file *file);
/*
Is the file . or ..
*/
_CPATH_FUNC_
int cpathFileIsSpecialHardLink(const cpath_file *file);
/*
Preload all files in a directory. Will be called automatically in some
function calls such as cpathGetFile()
*/
_CPATH_FUNC_
int cpathLoadAllFiles(cpath_dir *dir);
/*
More of a helper function, checks if we have space for n
and will preload any files if required.
*/
_CPATH_FUNC_
int cpathCheckGetN(cpath_dir *dir, size_t n);
/*
Get the nth file inside the directory, this is independent of the iterator
above.
Note calling this will require the reading of the entire directory.
if you want to prevent all these allocations then I recommend using
getNextFile(...)
*/
_CPATH_FUNC_
int cpathGetFile(cpath_dir *dir, cpath_file *file, size_t n);
/*
Get a const reference to a file, this is more performant than GetFile
but you can't modify anything about the object.
*/
_CPATH_FUNC_
int cpathGetFileConst(cpath_dir *dir, const cpath_file **file, size_t n);
/*
Opens the next sub directory into this
Saves the old directory into the parent if given saveDir
*/
_CPATH_FUNC_
int cpathOpenSubFileEmplace(cpath_dir *dir, const cpath_file *file, int saveDir);
/*
Opens the n'th sub directory into this directory
Saves the old directory into the parent if given saveDir
*/
_CPATH_FUNC_
int cpathOpenSubDirEmplace(cpath_dir *dir, size_t n, int saveDir);
/*
Opens the n'th sub directory into other given directory
*/
_CPATH_FUNC_
int cpathOpenSubDir(cpath_dir *out, cpath_dir *dir, size_t n);
/*
Opens the next sub directory into other given directory
*/
_CPATH_FUNC_
int cpathOpenNextSubDir(cpath_dir *out, cpath_dir *dir);
/*
Peeks the sub directory.
*/
_CPATH_FUNC_
int cpathOpenNextSubDir(cpath_dir *out, cpath_dir *dir);
/*
Opens the next sub directory into this
Saves the old directory into the parent if given saveDir
*/
_CPATH_FUNC_
int cpathOpenNextSubDirEmplace(cpath_dir *dir, int saveDir);
/*
Peeks the sub directory
*/
_CPATH_FUNC_
int cpathOpenCurrentSubDirEmplace(cpath_dir *dir, int saveDir);
/*
Revert an emplace and go back to the parent.
Note: This can occur multiple times.
Returns true if it went back to the parent.
This form is for a pointer variant where the storage
of the first directory is external.
*/
_CPATH_FUNC_
int cpathRevertEmplace(cpath_dir **dir);
/*
Revert an emplace and go back to the parent.
Note: This can occur multiple times.
Returns true if it went back to the parent.
This form is for a directory that is stored
internally to the function.
*/
_CPATH_FUNC_
int cpathRevertEmplaceCopy(cpath_dir *dir);
/*
Opens the given path as a file.
*/
_CPATH_FUNC_
int cpathOpenFile(cpath_file *file, const cpath *path);
/*
Converts a given file to a directory.
Note: Fails to convert if the file isn't a directory.
*/
_CPATH_FUNC_
int cpathFileToDir(cpath_dir *dir, const cpath_file *file);
/*
Get the extension for a file.
Note: You can't access a file's extension BEFORE you call this
It will be null prior.
Returns NULL if no extension or if it is a directory.
*/
_CPATH_FUNC_
cpath_str cpathGetExtension(cpath_file *file);
/*
Get the time of last access
*/
_CPATH_FUNC_
cpath_time_t cpathGetLastAccess(cpath_file *file);
/*
Get the time of last modification
*/
_CPATH_FUNC_
cpath_time_t cpathGetLastModification(cpath_file *file);
/*
Get the file size in bytes of this file.
*/
_CPATH_FUNC_
cpath_offset_t cpathGetFileSize(cpath_file *file);
/*
Standard compare function for files
*/
_CPATH_FUNC_
void cpathSort(cpath_dir *dir, cpath_cmp cmp);
/*
Get the file size in decimal form
*/
_CPATH_FUNC_
double cpathGetFileSizeDec(cpath_file *file, int interval);
/*
Get the file size prefix
*/
_CPATH_FUNC_
const cpath_char_t *cpathGetFileSizeSuffix(cpath_file *file, CPathByteRep rep);
/*
Create the given directory
*/
_CPATH_FUNC_
int cpathMkdir(const cpath *path);
/*
Open a given file
*/
_CPATH_FUNC_
FILE *cpathOpen(const cpath *path, const cpath_char_t *mode);
/* == Definitions == */
/* == Path == */
_CPATH_FUNC_
size_t cpathStrCpyConv(cpath_str dest, size_t len, const cpath_char_t *src) {
int lastWasSep = 0;
cpath_str startDest = dest;
for (size_t i = 0; i < len + 1; i++) {
int isSep = src[i] == CPATH_OTHER_SEP || src[i] == CPATH_SEP;
if (isSep && !lastWasSep) {
*dest++ = CPATH_SEP;
} else if (!isSep || !lastWasSep) {
*dest++ = src[i];
}
lastWasSep = isSep;
}
return dest - startDest - 1;
}
_CPATH_FUNC_
void cpathTrim(cpath *path) {
/* trim all the terminating / and \ */
/* We don't want to trim // into empty string
We will trim it to just /
*/
while (path->len > 1 && (path->buf[path->len - 1] == CPATH_SEP ||
path->buf[path->len - 1] == CPATH_OTHER_SEP)) {
path->len--;
}
path->buf[path->len] = CPATH_STR('\0');
}
_CPATH_FUNC_
cpath cpathFromUtf8(const char *str) {
cpath path;
if (str[0] == CPATH_STR('\0')) {
// empty string which is just '.'
path.len = 1;
path.buf[0] = CPATH_STR('.');
path.buf[1] = CPATH_STR('\0');
return path;
}
path.len = 0;
path.buf[0] = CPATH_STR('\0');
size_t len = cpath_str_length(str);
// NOTE: max path len includes the \0 where as str length doesn't!
if (len >= CPATH_MAX_PATH_LEN) {
errno = ENAMETOOLONG;
return path;
}
#if defined CPATH_UNICODE && defined _MSC_VER
mbstowcs_s(&path.len, path.buf, len + 1, str, CPATH_MAX_PATH_LEN);
cpathConvertSep(&path);
#else
path.len = cpathStrCpyConv(path.buf, len, str);
#endif
cpathTrim(&path);
return path;
}
_CPATH_FUNC_
void cpathCopy(cpath *out, const cpath *in) {
cpath_str_copy(out->buf, in->buf);
out->len = in->len;
}
_CPATH_FUNC_
int cpathFromStr(cpath *out, const cpath_char_t *str) {
size_t len = cpath_str_length(str);
if (len >= CPATH_MAX_PATH_LEN) return 0;
if (len == 0) {
out->len = 1;
out->buf[0] = CPATH_STR('.');
out->buf[1] = CPATH_STR('\0');
return 1;
}
out->len = cpathStrCpyConv(out->buf, len, str);
cpathTrim(out);
return 1;
}
_CPATH_FUNC_
void cpathAppendSprintf(cpath *out, const cpath_char_t *fmt, ...) {
va_list list;
va_start(list, fmt);
out->len += cpath_vsnprintf(out->buf + out->len,
CPATH_MAX_PATH_LEN - out->len, fmt, list);
va_end(list);
}
_CPATH_FUNC_
int cpathAppendStrn(cpath *out, const cpath_char_t *other, size_t len) {
if (len + out->len >= CPATH_MAX_PATH_LEN) {
// path too long, >= cause max path includes CPATH_STR('\0')
errno = ENAMETOOLONG;
return 0;
}
// This is more efficient than a strcat since it doesn't have to get
// the length again it also handles all the conversions well and doesn't
// have to retraverse the out->buf which we know is fine.
out->len += cpathStrCpyConv(out->buf + out->len, len, other);
cpathTrim(out);
return 1;
}
_CPATH_FUNC_
int cpathAppendStr(cpath *out, const cpath_char_t *other) {
return cpathAppendStrn(out, other, cpath_str_length(other));
}
_CPATH_FUNC_
int cpathAppend(cpath *out, const cpath *other) {
return cpathAppendStrn(out, other->buf, other->len);
}
_CPATH_FUNC_
int cpathConcatStrn(cpath *out, const cpath_char_t *str, size_t len) {
if (len + out->len >= CPATH_MAX_PATH_LEN) {
// path too long, >= cause max path includes CPATH_STR('\0')
errno = ENAMETOOLONG;
return 0;
}
if (str[0] != CPATH_SEP && out->buf[out->len - 1] != CPATH_SEP &&
str[0] != CPATH_OTHER_SEP && out->buf[out->len - 1] != CPATH_OTHER_SEP) {
out->buf[out->len++] = CPATH_SEP;
out->buf[out->len] = CPATH_STR('\0');
}
// This is more efficient than a strcat since it doesn't have to get
// the length again it also handles all the conversions well and doesn't
// have to retraverse the out->buf which we know is fine.
out->len += cpathStrCpyConv(out->buf + out->len, len, str);
cpathTrim(out);
return 1;
}
_CPATH_FUNC_
int cpathConcatStr(cpath *out, const cpath_char_t *other) {
return cpathConcatStrn(out, other, cpath_str_length(other));
}
_CPATH_FUNC_
int cpathConcat(cpath *out, const cpath *other) {
return cpathConcatStrn(out, other->buf, other->len);
}
_CPATH_FUNC_
int cpathExists(const cpath *path) {
#if defined _MSC_VER || defined __MINGW32__
DWORD res = GetFileAttributes(path->buf);
return res != INVALID_FILE_ATTRIBUTES;
#else
/*
We can either try to use stat or just access, access is more efficient
when not checking permissions.
struct stat tmp;
return stat(path->buf, &tmp) == 0;
*/
return access(path->buf, F_OK) != -1;
#endif
}
_CPATH_FUNC_
int cpathCanonicaliseNoSysCall(cpath *out, cpath *path) {
/*
NOTE: This should work even if out == path
It just has been written that way.
*/
if (path == NULL) {
errno = EINVAL;