tinydir.h 18 KB

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