summaryrefslogtreecommitdiff
path: root/src/contrib/tinydir-1.2.4
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2024-05-04 16:51:29 -0700
committer3gg <3gg@shellblade.net>2024-05-04 16:51:29 -0700
commit8222bfe56d4dabe8d92fc4b25ea1b0163b16f3e1 (patch)
tree763389e42276035ac134d94eb1dc32293b88d807 /src/contrib/tinydir-1.2.4
Initial commit.
Diffstat (limited to 'src/contrib/tinydir-1.2.4')
-rw-r--r--src/contrib/tinydir-1.2.4/CMakeLists.txt15
-rw-r--r--src/contrib/tinydir-1.2.4/COPYING26
-rw-r--r--src/contrib/tinydir-1.2.4/tinydir.h787
3 files changed, 828 insertions, 0 deletions
diff --git a/src/contrib/tinydir-1.2.4/CMakeLists.txt b/src/contrib/tinydir-1.2.4/CMakeLists.txt
new file mode 100644
index 0000000..7d83854
--- /dev/null
+++ b/src/contrib/tinydir-1.2.4/CMakeLists.txt
@@ -0,0 +1,15 @@
1cmake_minimum_required(VERSION 3.0)
2
3project(tinydir)
4
5set(CMAKE_C_STANDARD 17)
6set(CMAKE_C_STANDARD_REQUIRED On)
7set(CMAKE_C_EXTENSIONS Off)
8
9add_library(tinydir INTERFACE
10 tinydir.h)
11
12target_include_directories(tinydir INTERFACE
13 .)
14
15target_compile_options(tinydir INTERFACE -Wall -Wextra -Wpedantic)
diff --git a/src/contrib/tinydir-1.2.4/COPYING b/src/contrib/tinydir-1.2.4/COPYING
new file mode 100644
index 0000000..a10aa53
--- /dev/null
+++ b/src/contrib/tinydir-1.2.4/COPYING
@@ -0,0 +1,26 @@
1Copyright (c) 2013-2016, tinydir authors:
2- Cong Xu
3- Lautis Sun
4- Baudouin Feildel
5- Andargor <andargor@yahoo.com>
6All rights reserved.
7
8Redistribution and use in source and binary forms, with or without
9modification, are permitted provided that the following conditions are met:
10
111. Redistributions of source code must retain the above copyright notice, this
12 list of conditions and the following disclaimer.
132. Redistributions in binary form must reproduce the above copyright notice,
14 this list of conditions and the following disclaimer in the documentation
15 and/or other materials provided with the distribution.
16
17THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
21ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/src/contrib/tinydir-1.2.4/tinydir.h b/src/contrib/tinydir-1.2.4/tinydir.h
new file mode 100644
index 0000000..89be46f
--- /dev/null
+++ b/src/contrib/tinydir-1.2.4/tinydir.h
@@ -0,0 +1,787 @@
1/*
2Copyright (c) 2013-2021, tinydir authors:
3- Cong Xu
4- Lautis Sun
5- Baudouin Feildel
6- Andargor <andargor@yahoo.com>
7All rights reserved.
8
9Redistribution and use in source and binary forms, with or without
10 modification, are
11permitted provided that the following conditions are met:
12
13 1. Redistributions of source code must retain the above copyright notice,
14this list of conditions and the following disclaimer.
15 2.
16Redistributions in binary form must reproduce the above copyright notice, this
17list of conditions and the following disclaimer in the documentation and/or
18other materials provided with the distribution.
19
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
24FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
28TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31#ifndef TINYDIR_H
32#define TINYDIR_H
33
34#ifdef __cplusplus
35extern "C" {
36#endif
37
38#if ((defined _UNICODE) && !(defined UNICODE))
39#define UNICODE
40#endif
41
42#if ((defined UNICODE) && !(defined _UNICODE))
43#define _UNICODE
44#endif
45
46#include <errno.h>
47#include <stdlib.h>
48#include <string.h>
49#ifdef _MSC_VER
50#ifndef WIN32_LEAN_AND_MEAN
51#define WIN32_LEAN_AND_MEAN
52#endif
53#include <tchar.h>
54#include <windows.h>
55#pragma warning(push)
56#pragma warning(disable : 4996)
57#else
58#include <dirent.h>
59#include <libgen.h>
60#include <stddef.h>
61#include <sys/stat.h>
62#endif
63#ifdef __MINGW32__
64#include <tchar.h>
65#endif
66
67/* types */
68
69/* Windows UNICODE wide character support */
70#if defined _MSC_VER || defined __MINGW32__
71#define _tinydir_char_t TCHAR
72#define TINYDIR_STRING(s) _TEXT(s)
73#define _tinydir_strlen _tcslen
74#define _tinydir_strcpy _tcscpy
75#define _tinydir_strcat _tcscat
76#define _tinydir_strcmp _tcscmp
77#define _tinydir_strrchr _tcsrchr
78#define _tinydir_strncmp _tcsncmp
79#else
80#define _tinydir_char_t char
81#define TINYDIR_STRING(s) s
82#define _tinydir_strlen strlen
83#define _tinydir_strcpy strcpy
84#define _tinydir_strcat strcat
85#define _tinydir_strcmp strcmp
86#define _tinydir_strrchr strrchr
87#define _tinydir_strncmp strncmp
88#endif
89
90#if (defined _MSC_VER || defined __MINGW32__)
91#include <windows.h>
92#define _TINYDIR_PATH_MAX MAX_PATH
93#elif defined __linux__
94#include <limits.h>
95#ifdef PATH_MAX
96#define _TINYDIR_PATH_MAX PATH_MAX
97#endif
98#elif defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
99#include <sys/param.h>
100#if defined(BSD)
101#include <limits.h>
102#ifdef PATH_MAX
103#define _TINYDIR_PATH_MAX PATH_MAX
104#endif
105#endif
106#endif
107
108#ifndef _TINYDIR_PATH_MAX
109#define _TINYDIR_PATH_MAX 4096
110#endif
111
112#ifdef _MSC_VER
113/* extra chars for the "\\*" mask */
114#define _TINYDIR_PATH_EXTRA 2
115#else
116#define _TINYDIR_PATH_EXTRA 0
117#endif
118
119#define _TINYDIR_FILENAME_MAX 256
120
121#if (defined _MSC_VER || defined __MINGW32__)
122#define _TINYDIR_DRIVE_MAX 3
123#endif
124
125#ifdef _MSC_VER
126#define _TINYDIR_FUNC static __inline
127#elif !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L
128#define _TINYDIR_FUNC static __inline__
129#elif defined(__cplusplus)
130#define _TINYDIR_FUNC static inline
131#elif defined(__GNUC__)
132/* Suppress unused function warning */
133#define _TINYDIR_FUNC __attribute__((unused)) static
134#else
135#define _TINYDIR_FUNC static
136#endif
137
138#if defined(i386) || defined(__i386__) || defined(__i386) || defined(_M_IX86)
139#ifdef _MSC_VER
140#define _TINYDIR_CDECL __cdecl
141#else
142#define _TINYDIR_CDECL __attribute__((cdecl))
143#endif
144#else
145#define _TINYDIR_CDECL
146#endif
147
148/* readdir_r usage; define TINYDIR_USE_READDIR_R to use it (if supported) */
149#ifdef TINYDIR_USE_READDIR_R
150
151/* readdir_r is a POSIX-only function, and may not be available under various
152 * environments/settings, e.g. MinGW. Use readdir fallback */
153#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE || \
154 _POSIX_SOURCE
155#define _TINYDIR_HAS_READDIR_R
156#endif
157#if _POSIX_C_SOURCE >= 200112L
158#define _TINYDIR_HAS_FPATHCONF
159#include <unistd.h>
160#endif
161#if _BSD_SOURCE || _SVID_SOURCE || \
162 (_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
163#define _TINYDIR_HAS_DIRFD
164#include <sys/types.h>
165#endif
166#if defined _TINYDIR_HAS_FPATHCONF && defined _TINYDIR_HAS_DIRFD && \
167 defined _PC_NAME_MAX
168#define _TINYDIR_USE_FPATHCONF
169#endif
170#if defined __MINGW32__ || !defined _TINYDIR_HAS_READDIR_R || \
171 !(defined _TINYDIR_USE_FPATHCONF || defined NAME_MAX)
172#define _TINYDIR_USE_READDIR
173#endif
174
175/* Use readdir by default */
176#else
177#define _TINYDIR_USE_READDIR
178#endif
179
180/* MINGW32 has two versions of dirent, ASCII and UNICODE*/
181#ifndef _MSC_VER
182#if (defined __MINGW32__) && (defined _UNICODE)
183#define _TINYDIR_DIR _WDIR
184#define _tinydir_dirent _wdirent
185#define _tinydir_opendir _wopendir
186#define _tinydir_readdir _wreaddir
187#define _tinydir_closedir _wclosedir
188#else
189#define _TINYDIR_DIR DIR
190#define _tinydir_dirent dirent
191#define _tinydir_opendir opendir
192#define _tinydir_readdir readdir
193#define _tinydir_closedir closedir
194#endif
195#endif
196
197/* Allow user to use a custom allocator by defining _TINYDIR_MALLOC and
198 * _TINYDIR_FREE. */
199#if defined(_TINYDIR_MALLOC) && defined(_TINYDIR_FREE)
200#elif !defined(_TINYDIR_MALLOC) && !defined(_TINYDIR_FREE)
201#else
202#error "Either define both alloc and free or none of them!"
203#endif
204
205#if !defined(_TINYDIR_MALLOC)
206#define _TINYDIR_MALLOC(_size) malloc(_size)
207#define _TINYDIR_FREE(_ptr) free(_ptr)
208#endif /* !defined(_TINYDIR_MALLOC) */
209
210typedef struct tinydir_file {
211 _tinydir_char_t path[_TINYDIR_PATH_MAX];
212 _tinydir_char_t name[_TINYDIR_FILENAME_MAX];
213 _tinydir_char_t* extension;
214 int is_dir;
215 int is_reg;
216
217#ifndef _MSC_VER
218#ifdef __MINGW32__
219 struct _stat _s;
220#else
221 struct stat _s;
222#endif
223#endif
224} tinydir_file;
225
226typedef struct tinydir_dir {
227 _tinydir_char_t path[_TINYDIR_PATH_MAX];
228 int has_next;
229 size_t n_files;
230
231 tinydir_file* _files;
232#ifdef _MSC_VER
233 HANDLE _h;
234 WIN32_FIND_DATA _f;
235#else
236 _TINYDIR_DIR* _d;
237 struct _tinydir_dirent* _e;
238#ifndef _TINYDIR_USE_READDIR
239 struct _tinydir_dirent* _ep;
240#endif
241#endif
242} tinydir_dir;
243
244/* declarations */
245
246_TINYDIR_FUNC
247int tinydir_open(tinydir_dir* dir, const _tinydir_char_t* path);
248_TINYDIR_FUNC
249int tinydir_open_sorted(tinydir_dir* dir, const _tinydir_char_t* path);
250_TINYDIR_FUNC
251void tinydir_close(tinydir_dir* dir);
252
253_TINYDIR_FUNC
254int tinydir_next(tinydir_dir* dir);
255_TINYDIR_FUNC
256int tinydir_readfile(const tinydir_dir* dir, tinydir_file* file);
257_TINYDIR_FUNC
258int tinydir_readfile_n(const tinydir_dir* dir, tinydir_file* file, size_t i);
259_TINYDIR_FUNC
260int tinydir_open_subdir_n(tinydir_dir* dir, size_t i);
261
262_TINYDIR_FUNC
263int tinydir_file_open(tinydir_file* file, const _tinydir_char_t* path);
264_TINYDIR_FUNC
265void _tinydir_get_ext(tinydir_file* file);
266_TINYDIR_FUNC
267int _TINYDIR_CDECL _tinydir_file_cmp(const void* a, const void* b);
268#ifndef _MSC_VER
269#ifndef _TINYDIR_USE_READDIR
270_TINYDIR_FUNC
271size_t _tinydir_dirent_buf_size(_TINYDIR_DIR* dirp);
272#endif
273#endif
274
275/* definitions*/
276
277_TINYDIR_FUNC
278int tinydir_open(tinydir_dir* dir, const _tinydir_char_t* path) {
279#ifndef _MSC_VER
280#ifndef _TINYDIR_USE_READDIR
281 int error;
282 int size; /* using int size */
283#endif
284#else
285 _tinydir_char_t path_buf[_TINYDIR_PATH_MAX];
286#endif
287 _tinydir_char_t* pathp;
288
289 if (dir == NULL || path == NULL || _tinydir_strlen(path) == 0) {
290 errno = EINVAL;
291 return -1;
292 }
293 if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX) {
294 errno = ENAMETOOLONG;
295 return -1;
296 }
297
298 /* initialise dir */
299 dir->_files = NULL;
300#ifdef _MSC_VER
301 dir->_h = INVALID_HANDLE_VALUE;
302#else
303 dir->_d = NULL;
304#ifndef _TINYDIR_USE_READDIR
305 dir->_ep = NULL;
306#endif
307#endif
308 tinydir_close(dir);
309
310 _tinydir_strcpy(dir->path, path);
311 /* Remove trailing slashes */
312 pathp = &dir->path[_tinydir_strlen(dir->path) - 1];
313 while (pathp != dir->path &&
314 (*pathp == TINYDIR_STRING('\\') || *pathp == TINYDIR_STRING('/'))) {
315 *pathp = TINYDIR_STRING('\0');
316 pathp++;
317 }
318#ifdef _MSC_VER
319 _tinydir_strcpy(path_buf, dir->path);
320 _tinydir_strcat(path_buf, TINYDIR_STRING("\\*"));
321#if (defined WINAPI_FAMILY) && (WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP)
322 dir->_h = FindFirstFileEx(
323 path_buf, FindExInfoStandard, &dir->_f, FindExSearchNameMatch, NULL, 0);
324#else
325 dir->_h = FindFirstFile(path_buf, &dir->_f);
326#endif
327 if (dir->_h == INVALID_HANDLE_VALUE) {
328 errno = ENOENT;
329#else
330 dir->_d = _tinydir_opendir(path);
331 if (dir->_d == NULL) {
332#endif
333 goto bail;
334 }
335
336 /* read first file */
337 dir->has_next = 1;
338#ifndef _MSC_VER
339#ifdef _TINYDIR_USE_READDIR
340 dir->_e = _tinydir_readdir(dir->_d);
341#else
342 /* allocate dirent buffer for readdir_r */
343 size = _tinydir_dirent_buf_size(dir->_d); /* conversion to int */
344 if (size == -1)
345 return -1;
346 dir->_ep = (struct _tinydir_dirent*)_TINYDIR_MALLOC(size);
347 if (dir->_ep == NULL)
348 return -1;
349
350 error = readdir_r(dir->_d, dir->_ep, &dir->_e);
351 if (error != 0)
352 return -1;
353#endif
354 if (dir->_e == NULL) {
355 dir->has_next = 0;
356 }
357#endif
358
359 return 0;
360
361bail:
362 tinydir_close(dir);
363 return -1;
364}
365
366_TINYDIR_FUNC
367int tinydir_open_sorted(tinydir_dir* dir, const _tinydir_char_t* path) {
368 /* Count the number of files first, to pre-allocate the files array */
369 size_t n_files = 0;
370 if (tinydir_open(dir, path) == -1) {
371 return -1;
372 }
373 while (dir->has_next) {
374 n_files++;
375 if (tinydir_next(dir) == -1) {
376 goto bail;
377 }
378 }
379 tinydir_close(dir);
380
381 if (n_files == 0 || tinydir_open(dir, path) == -1) {
382 return -1;
383 }
384
385 dir->n_files = 0;
386 dir->_files = (tinydir_file*)_TINYDIR_MALLOC(sizeof *dir->_files * n_files);
387 if (dir->_files == NULL) {
388 goto bail;
389 }
390 while (dir->has_next) {
391 tinydir_file* p_file;
392 dir->n_files++;
393
394 p_file = &dir->_files[dir->n_files - 1];
395 if (tinydir_readfile(dir, p_file) == -1) {
396 goto bail;
397 }
398
399 if (tinydir_next(dir) == -1) {
400 goto bail;
401 }
402
403 /* Just in case the number of files has changed between the first and
404 second reads, terminate without writing into unallocated memory */
405 if (dir->n_files == n_files) {
406 break;
407 }
408 }
409
410 qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp);
411
412 return 0;
413
414bail:
415 tinydir_close(dir);
416 return -1;
417}
418
419_TINYDIR_FUNC
420void tinydir_close(tinydir_dir* dir) {
421 if (dir == NULL) {
422 return;
423 }
424
425 memset(dir->path, 0, sizeof(dir->path));
426 dir->has_next = 0;
427 dir->n_files = 0;
428 _TINYDIR_FREE(dir->_files);
429 dir->_files = NULL;
430#ifdef _MSC_VER
431 if (dir->_h != INVALID_HANDLE_VALUE) {
432 FindClose(dir->_h);
433 }
434 dir->_h = INVALID_HANDLE_VALUE;
435#else
436 if (dir->_d) {
437 _tinydir_closedir(dir->_d);
438 }
439 dir->_d = NULL;
440 dir->_e = NULL;
441#ifndef _TINYDIR_USE_READDIR
442 _TINYDIR_FREE(dir->_ep);
443 dir->_ep = NULL;
444#endif
445#endif
446}
447
448_TINYDIR_FUNC
449int tinydir_next(tinydir_dir* dir) {
450 if (dir == NULL) {
451 errno = EINVAL;
452 return -1;
453 }
454 if (!dir->has_next) {
455 errno = ENOENT;
456 return -1;
457 }
458
459#ifdef _MSC_VER
460 if (FindNextFile(dir->_h, &dir->_f) == 0)
461#else
462#ifdef _TINYDIR_USE_READDIR
463 dir->_e = _tinydir_readdir(dir->_d);
464#else
465 if (dir->_ep == NULL) {
466 return -1;
467 }
468 if (readdir_r(dir->_d, dir->_ep, &dir->_e) != 0) {
469 return -1;
470 }
471#endif
472 if (dir->_e == NULL)
473#endif
474 {
475 dir->has_next = 0;
476#ifdef _MSC_VER
477 if (GetLastError() != ERROR_SUCCESS &&
478 GetLastError() != ERROR_NO_MORE_FILES) {
479 tinydir_close(dir);
480 errno = EIO;
481 return -1;
482 }
483#endif
484 }
485
486 return 0;
487}
488
489_TINYDIR_FUNC
490int tinydir_readfile(const tinydir_dir* dir, tinydir_file* file) {
491 const _tinydir_char_t* filename;
492 if (dir == NULL || file == NULL) {
493 errno = EINVAL;
494 return -1;
495 }
496#ifdef _MSC_VER
497 if (dir->_h == INVALID_HANDLE_VALUE)
498#else
499 if (dir->_e == NULL)
500#endif
501 {
502 errno = ENOENT;
503 return -1;
504 }
505 filename =
506#ifdef _MSC_VER
507 dir->_f.cFileName;
508#else
509 dir->_e->d_name;
510#endif
511 if (_tinydir_strlen(dir->path) + _tinydir_strlen(filename) + 1 +
512 _TINYDIR_PATH_EXTRA >=
513 _TINYDIR_PATH_MAX) {
514 /* the path for the file will be too long */
515 errno = ENAMETOOLONG;
516 return -1;
517 }
518 if (_tinydir_strlen(filename) >= _TINYDIR_FILENAME_MAX) {
519 errno = ENAMETOOLONG;
520 return -1;
521 }
522
523 _tinydir_strcpy(file->path, dir->path);
524 if (_tinydir_strcmp(dir->path, TINYDIR_STRING("/")) != 0)
525 _tinydir_strcat(file->path, TINYDIR_STRING("/"));
526 _tinydir_strcpy(file->name, filename);
527 _tinydir_strcat(file->path, filename);
528#ifndef _MSC_VER
529#ifdef __MINGW32__
530 if (_tstat(
531#elif (defined _BSD_SOURCE) || (defined _DEFAULT_SOURCE) || \
532 ((defined _XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500)) || \
533 ((defined _POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L)) || \
534 ((defined __APPLE__) && (defined __MACH__)) || (defined BSD)
535 if (lstat(
536#else
537 if (stat(
538#endif
539 file->path, &file->_s) == -1) {
540 return -1;
541 }
542#endif
543 _tinydir_get_ext(file);
544
545 file->is_dir =
546#ifdef _MSC_VER
547 !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
548#else
549 S_ISDIR(file->_s.st_mode);
550#endif
551 file->is_reg =
552#ifdef _MSC_VER
553 !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) ||
554 (!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) &&
555 !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
556 !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) &&
557#ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM
558 !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) &&
559#endif
560#ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA
561 !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) &&
562#endif
563 !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) &&
564 !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY));
565#else
566 S_ISREG(file->_s.st_mode);
567#endif
568
569 return 0;
570}
571
572_TINYDIR_FUNC
573int tinydir_readfile_n(const tinydir_dir* dir, tinydir_file* file, size_t i) {
574 if (dir == NULL || file == NULL) {
575 errno = EINVAL;
576 return -1;
577 }
578 if (i >= dir->n_files) {
579 errno = ENOENT;
580 return -1;
581 }
582
583 memcpy(file, &dir->_files[i], sizeof(tinydir_file));
584 _tinydir_get_ext(file);
585
586 return 0;
587}
588
589_TINYDIR_FUNC
590int tinydir_open_subdir_n(tinydir_dir* dir, size_t i) {
591 _tinydir_char_t path[_TINYDIR_PATH_MAX];
592 if (dir == NULL) {
593 errno = EINVAL;
594 return -1;
595 }
596 if (i >= dir->n_files || !dir->_files[i].is_dir) {
597 errno = ENOENT;
598 return -1;
599 }
600
601 _tinydir_strcpy(path, dir->_files[i].path);
602 tinydir_close(dir);
603 if (tinydir_open_sorted(dir, path) == -1) {
604 return -1;
605 }
606
607 return 0;
608}
609
610/* Open a single file given its path */
611_TINYDIR_FUNC
612int tinydir_file_open(tinydir_file* file, const _tinydir_char_t* path) {
613 tinydir_dir dir;
614 int result = 0;
615 int found = 0;
616 _tinydir_char_t dir_name_buf[_TINYDIR_PATH_MAX];
617 _tinydir_char_t file_name_buf[_TINYDIR_PATH_MAX];
618 _tinydir_char_t* dir_name;
619 _tinydir_char_t* base_name;
620#if (defined _MSC_VER || defined __MINGW32__)
621 _tinydir_char_t drive_buf[_TINYDIR_PATH_MAX];
622 _tinydir_char_t ext_buf[_TINYDIR_FILENAME_MAX];
623#endif
624
625 if (file == NULL || path == NULL || _tinydir_strlen(path) == 0) {
626 errno = EINVAL;
627 return -1;
628 }
629 if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX) {
630 errno = ENAMETOOLONG;
631 return -1;
632 }
633
634 /* Get the parent path */
635#if (defined _MSC_VER || defined __MINGW32__)
636#if ((defined _MSC_VER) && (_MSC_VER >= 1400))
637 errno = _tsplitpath_s(
638 path, drive_buf, _TINYDIR_DRIVE_MAX, dir_name_buf, _TINYDIR_FILENAME_MAX,
639 file_name_buf, _TINYDIR_FILENAME_MAX, ext_buf, _TINYDIR_FILENAME_MAX);
640#else
641 _tsplitpath(path, drive_buf, dir_name_buf, file_name_buf, ext_buf);
642#endif
643
644 if (errno) {
645 return -1;
646 }
647
648/* _splitpath_s not work fine with only filename and widechar support */
649#ifdef _UNICODE
650 if (drive_buf[0] == L'\xFEFE')
651 drive_buf[0] = '\0';
652 if (dir_name_buf[0] == L'\xFEFE')
653 dir_name_buf[0] = '\0';
654#endif
655
656 /* Emulate the behavior of dirname by returning "." for dir name if it's
657 empty */
658 if (drive_buf[0] == '\0' && dir_name_buf[0] == '\0') {
659 _tinydir_strcpy(dir_name_buf, TINYDIR_STRING("."));
660 }
661 /* Concatenate the drive letter and dir name to form full dir name */
662 _tinydir_strcat(drive_buf, dir_name_buf);
663 dir_name = drive_buf;
664 /* Concatenate the file name and extension to form base name */
665 _tinydir_strcat(file_name_buf, ext_buf);
666 base_name = file_name_buf;
667#else
668 _tinydir_strcpy(dir_name_buf, path);
669 dir_name = dirname(dir_name_buf);
670 _tinydir_strcpy(file_name_buf, path);
671 base_name = basename(file_name_buf);
672#endif
673
674 /* Special case: if the path is a root dir, open the parent dir as the file */
675#if (defined _MSC_VER || defined __MINGW32__)
676 if (_tinydir_strlen(base_name) == 0)
677#else
678 if ((_tinydir_strcmp(base_name, TINYDIR_STRING("/"))) == 0)
679#endif
680 {
681 memset(file, 0, sizeof *file);
682 file->is_dir = 1;
683 file->is_reg = 0;
684 _tinydir_strcpy(file->path, dir_name);
685 file->extension = file->path + _tinydir_strlen(file->path);
686 return 0;
687 }
688
689 /* Open the parent directory */
690 if (tinydir_open(&dir, dir_name) == -1) {
691 return -1;
692 }
693
694 /* Read through the parent directory and look for the file */
695 while (dir.has_next) {
696 if (tinydir_readfile(&dir, file) == -1) {
697 result = -1;
698 goto bail;
699 }
700 if (_tinydir_strcmp(file->name, base_name) == 0) {
701 /* File found */
702 found = 1;
703 break;
704 }
705 tinydir_next(&dir);
706 }
707 if (!found) {
708 result = -1;
709 errno = ENOENT;
710 }
711
712bail:
713 tinydir_close(&dir);
714 return result;
715}
716
717_TINYDIR_FUNC
718void _tinydir_get_ext(tinydir_file* file) {
719 _tinydir_char_t* period = _tinydir_strrchr(file->name, TINYDIR_STRING('.'));
720 if (period == NULL) {
721 file->extension = &(file->name[_tinydir_strlen(file->name)]);
722 } else {
723 file->extension = period + 1;
724 }
725}
726
727_TINYDIR_FUNC
728int _TINYDIR_CDECL _tinydir_file_cmp(const void* a, const void* b) {
729 const tinydir_file* fa = (const tinydir_file*)a;
730 const tinydir_file* fb = (const tinydir_file*)b;
731 if (fa->is_dir != fb->is_dir) {
732 return -(fa->is_dir - fb->is_dir);
733 }
734 return _tinydir_strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX);
735}
736
737#ifndef _MSC_VER
738#ifndef _TINYDIR_USE_READDIR
739/*
740The following authored by Ben Hutchings <ben@decadent.org.uk>
741from https://womble.decadent.org.uk/readdir_r-advisory.html
742*/
743/* Calculate the required buffer size (in bytes) for directory *
744 * entries read from the given directory handle. Return -1 if this *
745 * this cannot be done. *
746 * *
747 * This code does not trust values of NAME_MAX that are less than *
748 * 255, since some systems (including at least HP-UX) incorrectly *
749 * define it to be a smaller value. */
750_TINYDIR_FUNC
751size_t _tinydir_dirent_buf_size(_TINYDIR_DIR* dirp) {
752 long name_max;
753 size_t name_end;
754 /* parameter may be unused */
755 (void)dirp;
756
757#if defined _TINYDIR_USE_FPATHCONF
758 name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
759 if (name_max == -1)
760#if defined(NAME_MAX)
761 name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
762#else
763 return (size_t)(-1);
764#endif
765#elif defined(NAME_MAX)
766 name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
767#else
768#error "buffer size for readdir_r cannot be determined"
769#endif
770 name_end = (size_t)offsetof(struct _tinydir_dirent, d_name) + name_max + 1;
771 return (
772 name_end > sizeof(struct _tinydir_dirent)
773 ? name_end
774 : sizeof(struct _tinydir_dirent));
775}
776#endif
777#endif
778
779#ifdef __cplusplus
780}
781#endif
782
783#if defined(_MSC_VER)
784#pragma warning(pop)
785#endif
786
787#endif