tinydir.h 18 KB

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