Coverage Report

Created: 2025-08-24 06:24

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