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