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