This repository has been archived by the owner on Jul 7, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
/
minifs.h
4009 lines (3285 loc) · 114 KB
/
minifs.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
/*
File system library. Choice of public domain or MIT-0. See license statements at the end of this file.
minifs - v0.0.0 (unreleased) - TBD
David Reid - [email protected]
*/
#ifndef minifs_h
#define minifs_h
#ifdef __cplusplus
extern "C" {
#endif
/* Platform detection. */
#ifdef _WIN32
#define MFS_WIN32
#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PC_APP || WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
#define MFS_WIN32_UWP
#else
#define MFS_WIN32_DESKTOP
#endif
#else
#define MFS_POSIX
#ifdef __unix__
#define MFS_UNIX
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
#define MFS_BSD
#endif
#endif
#ifdef __linux__
#define MFS_LINUX
#endif
#ifdef __APPLE__
#define MFS_APPLE
#endif
#ifdef __ANDROID__
#define MFS_ANDROID
#endif
#ifdef __EMSCRIPTEN__
#define MFS_EMSCRIPTEN
#endif
#endif
#if defined(_MSC_VER)
typedef struct _stat64 mfs_stat_info;
#elif defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)
typedef struct stat64 mfs_stat_info;
#else
typedef struct stat mfs_stat_info;
#endif
/* Sized types. Prefer built-in types. Fall back to stdint. */
#include <stddef.h> /* For size_t. */
#ifdef _MSC_VER
#if defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wlanguage-extension-token"
#pragma GCC diagnostic ignored "-Wc++11-long-long"
#endif
typedef signed __int8 mfs_int8;
typedef unsigned __int8 mfs_uint8;
typedef signed __int16 mfs_int16;
typedef unsigned __int16 mfs_uint16;
typedef signed __int32 mfs_int32;
typedef unsigned __int32 mfs_uint32;
typedef signed __int64 mfs_int64;
typedef unsigned __int64 mfs_uint64;
#if defined(__clang__)
#pragma GCC diagnostic pop
#endif
#else
#define MFS_HAS_STDINT
#include <stdint.h>
typedef int8_t mfs_int8;
typedef uint8_t mfs_uint8;
typedef int16_t mfs_int16;
typedef uint16_t mfs_uint16;
typedef int32_t mfs_int32;
typedef uint32_t mfs_uint32;
typedef int64_t mfs_int64;
typedef uint64_t mfs_uint64;
#endif
#ifdef MFS_HAS_STDINT
typedef uintptr_t mfs_uintptr;
#else
#if defined(_WIN32)
#if defined(_WIN64)
typedef mfs_uint64 mfs_uintptr;
#else
typedef mfs_uint32 mfs_uintptr;
#endif
#elif defined(__GNUC__)
#if defined(__LP64__)
typedef mfs_uint64 mfs_uintptr;
#else
typedef mfs_uint32 mfs_uintptr;
#endif
#else
typedef mfs_uint64 mfs_uintptr; /* Fallback. */
#endif
#endif
typedef mfs_uint8 mfs_bool8;
typedef mfs_uint32 mfs_bool32;
#define MFS_TRUE 1
#define MFS_FALSE 0
typedef void* mfs_handle;
typedef void* mfs_ptr;
typedef void (* mfs_proc)(void);
/* Limits. */
#if defined(SIZE_MAX)
#define MFS_SIZE_MAX SIZE_MAX
#else
#define MFS_SIZE_MAX 0xFFFFFFFF /* When SIZE_MAX is not defined by the standard library just default to the maximum 32-bit unsigned integer. */
#endif
/* Define NULL for some compilers. */
#ifndef NULL
#define NULL 0
#endif
#include <stdio.h>
/* Standard result codes. */
typedef int mfs_result;
#define MFS_SUCCESS 0
#define MFS_ERROR -1
#define MFS_INVALID_ARGS -2
#define MFS_INVALID_OPERATION -3
#define MFS_OUT_OF_MEMORY -4
#define MFS_OUT_OF_RANGE -5
#define MFS_ACCESS_DENIED -6
#define MFS_DOES_NOT_EXIST -7
#define MFS_ALREADY_EXISTS -8
#define MFS_TOO_MANY_OPEN_FILES -9
#define MFS_INVALID_FILE -10
#define MFS_TOO_BIG -11
#define MFS_PATH_TOO_LONG -12
#define MFS_NAME_TOO_LONG -13
#define MFS_NOT_DIRECTORY -14
#define MFS_IS_DIRECTORY -15
#define MFS_DIRECTORY_NOT_EMPTY -16
#define MFS_END_OF_FILE -17
#define MFS_NO_SPACE -18
#define MFS_BUSY -19
#define MFS_IO_ERROR -20
#define MFS_INTERRUPT -21
#define MFS_UNAVAILABLE -22
#define MFS_ALREADY_IN_USE -23
#define MFS_BAD_ADDRESS -24
#define MFS_BAD_SEEK -25
#define MFS_BAD_PIPE -26
#define MFS_DEADLOCK -27
#define MFS_TOO_MANY_LINKS -28
#define MFS_NOT_IMPLEMENTED -29
#define MFS_NO_MESSAGE -30
#define MFS_BAD_MESSAGE -31
#define MFS_NO_DATA_AVAILABLE -32
#define MFS_INVALID_DATA -33
#define MFS_TIMEOUT -34
#define MFS_NO_NETWORK -35
#define MFS_NOT_UNIQUE -36
#define MFS_NOT_SOCKET -37
#define MFS_NO_ADDRESS -38
#define MFS_BAD_PROTOCOL -39
#define MFS_PROTOCOL_UNAVAILABLE -40
#define MFS_PROTOCOL_NOT_SUPPORTED -41
#define MFS_PROTOCOL_FAMILY_NOT_SUPPORTED -42
#define MFS_ADDRESS_FAMILY_NOT_SUPPORTED -43
#define MFS_SOCKET_NOT_SUPPORTED -44
#define MFS_CONNECTION_RESET -45
#define MFS_ALREADY_CONNECTED -46
#define MFS_NOT_CONNECTED -47
#define MFS_CONNECTION_REFUSED -48
#define MFS_NO_HOST -49
#define MFS_IN_PROGRESS -50
#define MFS_CANCELLED -51
#define MFS_MEMORY_ALREADY_MAPPED -52
#define MFS_AT_END -53
typedef struct
{
char pFileName[256];
mfs_uint64 sizeInBytes;
mfs_uint64 lastModifiedTime;
mfs_uint64 lastAccessTime;
mfs_bool32 isDirectory : 1;
mfs_bool32 isReadOnly : 1;
} mfs_file_info;
/*
File Reading
*/
/*
Opens a stdio FILE object.
*/
mfs_result mfs_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode);
mfs_result mfs_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode);
/*
Closes a stdio FILE object.
*/
mfs_result mfs_fclose(FILE* pFile);
/*
Reads data from a stdio FILE object.
*/
mfs_result mfs_fread(FILE* pFile, void* pData, size_t sizeInBytes, size_t* pBytesRead);
/*
Writes data to a stdio FILE object.
*/
mfs_result mfs_fwrite(FILE* pFile, const void* pData, size_t sizeInBytes, size_t* pBytesWritten);
/*
Seeks a stdio FILE object.
*/
mfs_result mfs_fseek(FILE* pFile, mfs_int64 offset, int origin);
/*
Retrieves the read/write point of a stdio FILE object.
*/
mfs_int64 mfs_ftell(FILE* pFile);
/*
Retrieves information about a stdio FILE object.
*/
mfs_result mfs_fstat(FILE* pFile, mfs_stat_info* info);
/*
High level API for opening and reading a file.
Free the file data with mfs_free().
*/
mfs_result mfs_open_and_read_file(const char* pFilePath, size_t* pFileSizeOut, void** ppFileData);
/*
High level API for opening and reading a text file.
Free the file data with mfs_free().
*/
mfs_result mfs_open_and_read_text_file(const char* pFilePath, size_t* pFileSizeOut, char** ppFileData);
/*
High level API for opening and writing a file.
*/
mfs_result mfs_open_and_write_file(const char* pFilePath, size_t fileSize, const void* pFileData);
/*
Directory Management
*/
/*
Sets the current directory.
*/
mfs_result mfs_set_current_directory(const char* pDirectoryPath);
/*
Retrieves the current directory.
If the output buffer is not large enough to store the entire result, it will be set to an empty string and MFS_OUT_OF_RANGE
will be returned.
*/
mfs_result mfs_get_current_directory(char* pDirectoryPath, size_t directoryPathSizeInBytes);
/*
Creates a directory.
*/
mfs_result mfs_mkdir(const char* pDirectory, mfs_bool32 recursive);
/*
Deletes a directory.
*/
mfs_result mfs_rmdir(const char* pDirectory, mfs_bool32 recursive);
/*
Recursively deletes the contents of a directory.
*/
mfs_result mfs_rmdir_content(const char* pDirectory);
/*
Checks if the given path refers to an existing directory.
*/
mfs_bool32 mfs_is_directory(const char* pPath);
/*
Checks if a file exists.
*/
mfs_bool32 mfs_file_exists(const char* pFilePath);
/*
Copies a file.
*/
mfs_result mfs_copy_file(const char* pSrcFilePath, const char* pDstFilePath, mfs_bool32 failIfExists);
/*
Moves a file.
*/
mfs_result mfs_move_file(const char* pSrcFilePath, const char* pDstFilePath, mfs_bool32 failIfExists);
/*
Deletes a file or folder.
*/
mfs_result mfs_delete_file(const char* pFilePath);
/*
Retrieves basic information about a file.
Returns MFS_SUCCESS on success. Otherwise returns an error code.
*/
mfs_result mfs_get_file_info(const char* pFilePath, mfs_file_info* pFileInfo);
/* Iteration */
typedef struct
{
#if defined(_WIN32)
struct
{
mfs_handle handle;
mfs_file_info fi; /* <-- For dancing around differences between the minifs API and the Win32 API. */
mfs_bool32 atEnd;
} win32;
#else
struct
{
/*DIR**/ void* dir;
char* pPath;
} posix;
#endif
} mfs_iterator;
/*
Initializes an iterator.
Iterators are used to iterate over each of the files in a directory.
*/
mfs_result mfs_iterator_init(const char* pDirectoryPath, mfs_iterator* pIterator);
/*
Uninitializes an iterator.
*/
void mfs_iterator_uninit(mfs_iterator* pIterator);
/*
Gets the next file in the iteration.
Returns MFS_SUCCESS on success. Returns MFS_AT_END if it's reached the end of iteration. Otherwise an error code is returned.
*/
mfs_result mfs_iterator_next(mfs_iterator* pIterator, mfs_file_info* pFileInfo);
/*
Paths
=====
The path API is basically just a string library where the input strings are just paths. These APIs follow the following convensions:
* All APIs generally follow the safe string API:
* Return values are error codes.
* First parameter is always the destination, second parameter is always the destination capacity and the third parameter is
always the source string.
* Passing in NULL for the source is an error as this would normally indicate an underlying error.
* Unlike the standard safe string APIs, the destination can be NULL in which case the API can be used to measure the output string.
* All APIs copy the string, unless they're named as "_in_place".
* All APIs that make a copy of the source string will have an optional parameter for receiving the length of the destination string.
*/
typedef struct
{
size_t offset;
size_t length;
} mfs_path_segment;
/*
Checks whether or not two path segments are equal.
*/
mfs_bool32 mfs_path_segments_equal(const char* s0Path, const mfs_path_segment s0, const char* s1Path, const mfs_path_segment s1);
typedef struct
{
const char* path;
mfs_path_segment segment;
} mfs_path_iterator;
/*
Initializes a path iterator, starting from the first segment.
*/
mfs_result mfs_path_first_segment(const char* path, mfs_path_iterator* pIterator);
/*
Initializes a path iterator, starting from the end segment.
*/
mfs_result mfs_path_last_segment(const char* path, mfs_path_iterator* pIterator);
/*
Moves a path iterator to the next segment.
*/
mfs_result mfs_path_next_segment(mfs_path_iterator* pIterator);
/*
Moves a path iterator to the previous segment.
*/
mfs_result mfs_path_prev_segment(mfs_path_iterator* pIterator);
/*
Determines if a path iterator is at the start.
*/
mfs_bool32 mfs_path_at_start(mfs_path_iterator iterator);
/*
Determines whether or not the iterator is at the end.
*/
mfs_bool32 mfs_path_at_end(mfs_path_iterator iterator);
/*
Checks if two iterators are equal.
*/
mfs_bool32 mfs_path_iterators_equal(const mfs_path_iterator i0, const mfs_path_iterator i1);
/*
Determines whether or not the given path refers to the root segment of a path.
*/
mfs_bool32 mfs_path_is_root(const char* path);
mfs_bool32 mfs_path_is_root_segment(const char* path, const mfs_path_segment segment);
/*
Determines whether or not the given path refers to a Unix style root directory ("/")
*/
mfs_bool32 mfs_path_is_unix_style_root(const char* path);
mfs_bool32 mfs_path_is_unix_style_root_segment(const char* path, const mfs_path_segment segment);
/*
Determines whether or not the given path refers to a Windows style root directory.
*/
mfs_bool32 mfs_path_is_win32_style_root(const char* path);
mfs_bool32 mfs_path_is_win32_style_root_segment(const char* path, const mfs_path_segment segment);
/*
Copies a path.
This is basically the same as strcpy_s(), except the length of the string is returned in [pDstLenOut].
*/
mfs_result mfs_path_copy(char* dst, size_t dstSizeInBytes, const char* src, size_t* pDstLenOut);
/*
Copies a path and normalizes the slashes to forward slashes.
*/
mfs_result mfs_path_to_forward_slashes(char* dst, size_t dstSizeInBytes, const char* src, size_t* pDstLenOut);
/*
Copies a path and normalizes the slashes to back slashes.
*/
mfs_result mfs_path_to_back_slashes(char* dst, size_t dstSizeInBytes, const char* src, size_t* pDstLenOut);
/*
Determines whether or not the given path is a decendant of another.
descendantAbsolutePath [in] The absolute path of the descendant.
parentAbsolutePath [in] The absolute path of the parent.
As an example, "C:/My/Folder" is a descendant of "C:/".
If either path contains "." or "..", clean it with mfs_path_clean() before calling this.
*/
mfs_bool32 mfs_path_is_descendant(const char* descendantAbsolutePath, const char* parentAbsolutePath);
/*
Determines whether or not the given path is a direct child of another.
childAbsolutePath [in] The absolute of the child.
parentAbsolutePath [in] The absolute path of the parent.
As an example, "C:/My/Folder" is NOT a child of "C:/" - it is a descendant. Alternatively, "C:/My" IS a child of "C:/".
If either path contains "." or "..", clean it with mfs_path_clean() before calling this.
*/
mfs_bool32 mfs_path_is_child(const char* childAbsolutePath, const char* parentAbsolutePath);
/*
Finds the file name portion of the path.
path [in] The path to search.
Returns a pointer to the beginning of the string containing the file name. If this is non-null, it will always be an offset of "path".
A path with a trailing slash will return an empty string.
*/
const char* mfs_path_file_name(const char* path);
/*
Finds the file extension of the given file path.
path [in] The path to search.
Returns a pointer to the beginning of the string containing the path's extension. If the path does not have an extension, an empty
string will be returned. The return value is always an offset of "path".
A path with a trailing slash will return an empty string.
On a path such as "filename.ext1.ext2" the returned string will be "ext2".
*/
const char* mfs_path_extension(const char* path);
/*
Determines whether or not the given path has an extension.
*/
mfs_bool32 mfs_path_has_extension(const char* path);
/*
Checks whether or not the two paths are equal.
path1 [in] The first path.
path2 [in] The second path.
Returns whether or not the operation is successful.
This is case-sensitive.
This is more than just a string comparison. Rather, this splits the path and compares each segment. The path "C:/My/Folder" is considered
equal to to "C:\\My\\Folder".
*/
mfs_bool32 mfs_path_equal(const char* path1, const char* path2);
/*
Checks if the extension of the given path is equal to the given extension.
This is not case sensitive. If you want this to be case sensitive, just do "strcmp(mfs_path_extension(path), extension)".
*/
mfs_bool32 mfs_path_extension_equal(const char* path, const char* extension);
/*
Determines whether or not the given path is relative.
path [in] The path to check.
*/
mfs_bool32 mfs_path_is_relative(const char* path);
/*
Determines whether or not the given path is absolute.
path [in] The path to check.
*/
mfs_bool32 mfs_path_is_absolute(const char* path);
/*
Retrieves the base path from the given path, not including the trailing slash.
dst [out] The destination buffer.
dstSizeInBytes [in] The size of the buffer pointed to by "pathOut", in bytes.
src [in] The input path.
Returns whether or not the operation is successful.
As an example, when "src" is "C:/MyFolder/MyFile", the output will be "C:/MyFolder". Note that there is no trailing slash.
If "src" is something like "/MyFolder", the return value will be an empty string.
*/
mfs_result mfs_path_base_path(char* dst, size_t dstSizeInBytes, const char* src, size_t* pDstLenOut);
/*
Retrieves the file name portion of the given path, without the extension.
dst [out] The destination buffer.
dstSizeInBytes [in] The size of the buffer pointed to by "pathOut", in bytes.
src [in] The input path.
*/
mfs_result mfs_path_file_name_without_extension(char* dst, size_t dstSizeInBytes, const char* src, size_t* pDstLenOut);
/*
Appends two paths together, and copies them to a separate buffer.
dst [out] The destination buffer.
dstSizeInBytes [in] The size of the buffer pointed to by "pathOut", in bytes.
base [in] The base directory.
other [in] The relative path to append to "base".
Returns whether or not the operation is successful.
This assumes both paths are well formed and "other" is a relative path.
"pathOut" and "base" are allowed to be the same pointer, in which case the other path is appended in-place.
*/
mfs_result mfs_path_append(char* dst, size_t dstSizeInBytes, const char* base, const char* other, size_t* pDstLenOut);
/*
Appends a base path and an iterator together, and copyies them to a separate buffer.
dst [out] The destination buffer.
dstSizeInBytes [in] The size of the buffer pointed to by "dst", in bytes.
base [in] The base directory.
iterator [in] The iterator to append.
Returns whether or not the operation is successful.
This assumes both paths are well formed and "i" is a valid iterator.
"pathOut" and "base" are allowed to be the same pointer, in which case the other path is appended in-place.
*/
mfs_result mfs_path_append_iterator(char* dst, size_t dstSizeInBytes, const char* base, mfs_path_iterator iterator, size_t* pDstLenOut);
/*
Appends an extension to the given base path and copies them to a separate buffer.
dst [out] The destination buffer.
dstSizeInBytes [in] The size of the buffer pointed to by "dst", in bytes.
base [in] The base directory.
extension [in] The relative path to append to "base".
Returns whether or not the operation is successful.
"pathOut" and "base" are allowed to be the same pointer, in which case the other path is appended in-place.
*/
mfs_result mfs_path_append_extension(char* dst, size_t dstSizeInBytes, const char* base, const char* extension, size_t* pDstLenOut);
/*
Cleans the path and resolves the ".." and "." segments.
dst [out] A pointer to the buffer that will receive the path.
dstSizeInBytes [in] The size of the buffer pointed to by pathOut, in bytes.
src [in] The path to clean.
Returns whether or not the operation is successful.
The output path will never be longer than the input path.
The output buffer should never overlap with the input path.
As an example, the path "my/messy/../path" will result in "my/path"
The path "my/messy/../../../path" (note how there are too many ".." segments) will return "path" (the extra ".." segments will be dropped.)
If an error occurs, such as an invalid input path, 0 will be returned.
*/
mfs_result mfs_path_clean(char* dst, size_t dstSizeInBytes, const char* src, size_t* pDstLenOut);
/*
Appends one path to the other and then cleans it.
*/
mfs_result mfs_path_append_and_clean(char* dst, size_t dstSizeInBytes, const char* base, const char* other, size_t* pDstLenOut);
/*
Removes the extension from a path.
If the path does not have an extension, the source string is copied straight into the destination without any changes and [pDstLenOut]
receives the length of the string.
*/
mfs_result mfs_path_remove_extension(char* dst, size_t dstSizeInBytes, const char* src, size_t* pDstLenOut);
mfs_result mfs_path_remove_extension_in_place(char* path, size_t* pDstLenOut);
/*
Creates a copy of the given string and removes the extension.
*/
mfs_result mfs_path_remove_file_name(char* dst, size_t dstSizeInBytes, const char* src, size_t* pDstLenOut);
mfs_result mfs_path_remove_file_name_in_place(char* path, size_t* pDstLenOut);
/*
Converts an absolute path to a relative path.
Returns whether or not the operation is successful.
This will normalize every slash to forward slashes.
*/
mfs_result mfs_path_to_relative(char* dst, size_t dstSizeInBytes, const char* absolutePathToMakeRelative, const char* absolutePathToMakeRelativeTo, size_t* pDstLenOut);
/*
Converts a relative path to an absolute path based on a base path.
Returns true if the conversion was successful; false if there was an error.
This is equivalent to an append followed by a clean. Slashes will be normalized to forward slashes.
*/
mfs_result mfs_path_to_absolute(char* dst, size_t dstSizeInBytes, const char* relativePathToMakeAbsolute, const char* basePath, size_t* pDstLenOut);
/*
Miscellaneous APIs.
*/
/*
Frees memory that was previously allocated by a public API, such as mfs_open_and_read_file().
*/
void mfs_free(void* p);
#ifdef __cplusplus
}
#endif
#endif /* minifs_h */
/************************************************************************************************************************************************************
*************************************************************************************************************************************************************
IMPLEMENTATION
*************************************************************************************************************************************************************
************************************************************************************************************************************************************/
#if defined(MINIFS_IMPLEMENTATION) || defined(MFS_IMPLEMENTATION)
#ifndef minifs_c
#define minifs_c
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <wchar.h>
#ifndef MFS_MALLOC
#ifdef MFS_WIN32
#define MFS_MALLOC(sz) HeapAlloc(GetProcessHeap(), 0, (sz))
#else
#define MFS_MALLOC(sz) malloc((sz))
#endif
#endif
#ifndef MFS_REALLOC
#ifdef MFS_WIN32
#define MFS_REALLOC(p, sz) (((sz) > 0) ? ((p) ? HeapReAlloc(GetProcessHeap(), 0, (p), (sz)) : HeapAlloc(GetProcessHeap(), 0, (sz))) : ((VOID*)(size_t)(HeapFree(GetProcessHeap(), 0, (p)) & 0)))
#else
#define MFS_REALLOC(p, sz) realloc((p), (sz))
#endif
#endif
#ifndef MFS_FREE
#ifdef MFS_WIN32
#define MFS_FREE(p) HeapFree(GetProcessHeap(), 0, (p))
#else
#define MFS_FREE(p) free((p))
#endif
#endif
#ifndef MFS_ZERO_MEMORY
#ifdef MFS_WIN32
#define MFS_ZERO_MEMORY(p, sz) ZeroMemory((p), (sz))
#else
#define MFS_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
#endif
#endif
#ifndef MFS_COPY_MEMORY
#ifdef MFS_WIN32
#define MFS_COPY_MEMORY(dst, src, sz) CopyMemory((dst), (src), (sz))
#else
#define MFS_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
#endif
#endif
#ifndef MFS_ASSERT
#ifdef MFS_WIN32
#define MFS_ASSERT(condition) assert(condition)
#else
#define MFS_ASSERT(condition) assert(condition)
#endif
#endif
#define MFS_ZERO_OBJECT(p) MFS_ZERO_MEMORY((p), sizeof(*(p)))
#if defined(MFS_WIN32)
#include <windows.h>
mfs_result mfs_result_from_GetLastError(DWORD error)
{
switch (error)
{
case ERROR_SUCCESS: return MFS_SUCCESS;
case ERROR_PATH_NOT_FOUND: return MFS_DOES_NOT_EXIST;
case ERROR_TOO_MANY_OPEN_FILES: return MFS_TOO_MANY_OPEN_FILES;
case ERROR_NOT_ENOUGH_MEMORY: return MFS_OUT_OF_MEMORY;
case ERROR_DISK_FULL: return MFS_NO_SPACE;
case ERROR_HANDLE_EOF: return MFS_END_OF_FILE;
case ERROR_NEGATIVE_SEEK: return MFS_BAD_SEEK;
case ERROR_INVALID_PARAMETER: return MFS_INVALID_ARGS;
case ERROR_ACCESS_DENIED: return MFS_ACCESS_DENIED;
case ERROR_SEM_TIMEOUT: return MFS_TIMEOUT;
case ERROR_FILE_NOT_FOUND: return MFS_DOES_NOT_EXIST;
default: break;
}
return MFS_ERROR;
}
#endif
#if defined(MFS_POSIX)
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h> /* For open() flags. */
#include <strings.h> /* For strcasecmp(). */
#endif
const char* mfs_result_description(mfs_result result)
{
switch (result)
{
case MFS_SUCCESS: return "No error";
case MFS_ERROR: return "Unknown error";
case MFS_INVALID_ARGS: return "Invalid argument";
case MFS_INVALID_OPERATION: return "Invalid operation";
case MFS_OUT_OF_MEMORY: return "Out of memory";
case MFS_OUT_OF_RANGE: return "Out of range";
case MFS_ACCESS_DENIED: return "Permission denied";
case MFS_DOES_NOT_EXIST: return "Resource does not exist";
case MFS_ALREADY_EXISTS: return "Resource already exists";
case MFS_TOO_MANY_OPEN_FILES: return "Too many open files";
case MFS_INVALID_FILE: return "Invalid file";
case MFS_TOO_BIG: return "Too large";
case MFS_PATH_TOO_LONG: return "Path too long";
case MFS_NAME_TOO_LONG: return "Name too long";
case MFS_NOT_DIRECTORY: return "Not a directory";
case MFS_IS_DIRECTORY: return "Is a directory";
case MFS_DIRECTORY_NOT_EMPTY: return "Directory not empty";
case MFS_END_OF_FILE: return "End of file";
case MFS_NO_SPACE: return "No space available";
case MFS_BUSY: return "Device or resource busy";
case MFS_IO_ERROR: return "Input/output error";
case MFS_INTERRUPT: return "Interrupted";
case MFS_UNAVAILABLE: return "Resource unavailable";
case MFS_ALREADY_IN_USE: return "Resource already in use";
case MFS_BAD_ADDRESS: return "Bad address";
case MFS_BAD_SEEK: return "Illegal seek";
case MFS_BAD_PIPE: return "Broken pipe";
case MFS_DEADLOCK: return "Deadlock";
case MFS_TOO_MANY_LINKS: return "Too many links";
case MFS_NOT_IMPLEMENTED: return "Not implemented";
case MFS_NO_MESSAGE: return "No message of desired type";
case MFS_BAD_MESSAGE: return "Invalid message";
case MFS_NO_DATA_AVAILABLE: return "No data available";
case MFS_INVALID_DATA: return "Invalid data";
case MFS_TIMEOUT: return "Timeout";
case MFS_NO_NETWORK: return "Network unavailable";
case MFS_NOT_UNIQUE: return "Not unique";
case MFS_NOT_SOCKET: return "Socket operation on non-socket";
case MFS_NO_ADDRESS: return "Destination address required";
case MFS_BAD_PROTOCOL: return "Protocol wrong type for socket";
case MFS_PROTOCOL_UNAVAILABLE: return "Protocol not available";
case MFS_PROTOCOL_NOT_SUPPORTED: return "Protocol not supported";
case MFS_PROTOCOL_FAMILY_NOT_SUPPORTED: return "Protocol family not supported";
case MFS_ADDRESS_FAMILY_NOT_SUPPORTED: return "Address family not supported";
case MFS_SOCKET_NOT_SUPPORTED: return "Socket type not supported";
case MFS_CONNECTION_RESET: return "Connection reset";
case MFS_ALREADY_CONNECTED: return "Already connected";
case MFS_NOT_CONNECTED: return "Not connected";
case MFS_CONNECTION_REFUSED: return "Connection refused";
case MFS_NO_HOST: return "No host";
case MFS_IN_PROGRESS: return "Operation in progress";
case MFS_CANCELLED: return "Operation cancelled";
case MFS_MEMORY_ALREADY_MAPPED: return "Memory already mapped";
case MFS_AT_END: return "Reached end of collection";
default: return "Unknown error";
}
}
mfs_result mfs_result_from_errno(int e)
{
switch (e)
{
case 0: return MFS_SUCCESS;
#ifdef EPERM
case EPERM: return MFS_INVALID_OPERATION;
#endif
#ifdef ENOENT
case ENOENT: return MFS_DOES_NOT_EXIST;
#endif
#ifdef ESRCH
case ESRCH: return MFS_DOES_NOT_EXIST;
#endif
#ifdef EINTR
case EINTR: return MFS_INTERRUPT;
#endif
#ifdef EIO
case EIO: return MFS_IO_ERROR;
#endif
#ifdef ENXIO
case ENXIO: return MFS_DOES_NOT_EXIST;
#endif
#ifdef E2BIG
case E2BIG: return MFS_INVALID_ARGS;
#endif
#ifdef ENOEXEC
case ENOEXEC: return MFS_INVALID_FILE;
#endif
#ifdef EBADF
case EBADF: return MFS_INVALID_FILE;
#endif
#ifdef ECHILD
case ECHILD: return MFS_ERROR;
#endif
#ifdef EAGAIN
case EAGAIN: return MFS_UNAVAILABLE;
#endif
#ifdef ENOMEM
case ENOMEM: return MFS_OUT_OF_MEMORY;
#endif
#ifdef EACCES
case EACCES: return MFS_ACCESS_DENIED;
#endif
#ifdef EFAULT
case EFAULT: return MFS_BAD_ADDRESS;
#endif
#ifdef ENOTBLK
case ENOTBLK: return MFS_ERROR;
#endif
#ifdef EBUSY
case EBUSY: return MFS_BUSY;
#endif
#ifdef EEXIST
case EEXIST: return MFS_ALREADY_EXISTS;
#endif
#ifdef EXDEV
case EXDEV: return MFS_ERROR;
#endif
#ifdef ENODEV
case ENODEV: return MFS_DOES_NOT_EXIST;
#endif
#ifdef ENOTDIR
case ENOTDIR: return MFS_NOT_DIRECTORY;
#endif
#ifdef EISDIR
case EISDIR: return MFS_IS_DIRECTORY;
#endif
#ifdef EINVAL
case EINVAL: return MFS_INVALID_ARGS;
#endif
#ifdef ENFILE
case ENFILE: return MFS_TOO_MANY_OPEN_FILES;
#endif
#ifdef EMFILE
case EMFILE: return MFS_TOO_MANY_OPEN_FILES;
#endif
#ifdef ENOTTY
case ENOTTY: return MFS_INVALID_OPERATION;
#endif
#ifdef ETXTBSY
case ETXTBSY: return MFS_BUSY;
#endif
#ifdef EFBIG
case EFBIG: return MFS_TOO_BIG;
#endif
#ifdef ENOSPC
case ENOSPC: return MFS_NO_SPACE;
#endif
#ifdef ESPIPE
case ESPIPE: return MFS_BAD_SEEK;
#endif
#ifdef EROFS
case EROFS: return MFS_ACCESS_DENIED;
#endif
#ifdef EMLINK
case EMLINK: return MFS_TOO_MANY_LINKS;
#endif
#ifdef EPIPE
case EPIPE: return MFS_BAD_PIPE;
#endif
#ifdef EDOM
case EDOM: return MFS_OUT_OF_RANGE;
#endif
#ifdef ERANGE
case ERANGE: return MFS_OUT_OF_RANGE;
#endif
#ifdef EDEADLK
case EDEADLK: return MFS_DEADLOCK;
#endif
#ifdef ENAMETOOLONG
case ENAMETOOLONG: return MFS_PATH_TOO_LONG;
#endif
#ifdef ENOLCK
case ENOLCK: return MFS_ERROR;
#endif
#ifdef ENOSYS
case ENOSYS: return MFS_NOT_IMPLEMENTED;
#endif
#ifdef ENOTEMPTY
case ENOTEMPTY: return MFS_DIRECTORY_NOT_EMPTY;
#endif