tinydir.h 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  1. /*
  2. Copyright (c) 2013-2016, 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. dir->_h = FindFirstFileEx(path_buf, FindExInfoStandard, &dir->_f, FindExSearchNameMatch, NULL, 0);
  262. if (dir->_h == INVALID_HANDLE_VALUE)
  263. {
  264. errno = ENOENT;
  265. #else
  266. dir->_d = _tinydir_opendir(path);
  267. if (dir->_d == NULL)
  268. {
  269. #endif
  270. goto bail;
  271. }
  272. /* read first file */
  273. dir->has_next = 1;
  274. #ifndef _MSC_VER
  275. #ifdef _TINYDIR_USE_READDIR
  276. dir->_e = _tinydir_readdir(dir->_d);
  277. #else
  278. /* allocate dirent buffer for readdir_r */
  279. size = _tinydir_dirent_buf_size(dir->_d); /* conversion to int */
  280. if (size == -1) return -1;
  281. dir->_ep = (struct _tinydir_dirent*)_TINYDIR_MALLOC(size);
  282. if (dir->_ep == NULL) return -1;
  283. error = readdir_r(dir->_d, dir->_ep, &dir->_e);
  284. if (error != 0) return -1;
  285. #endif
  286. if (dir->_e == NULL)
  287. {
  288. dir->has_next = 0;
  289. }
  290. #endif
  291. return 0;
  292. bail:
  293. tinydir_close(dir);
  294. return -1;
  295. }
  296. _TINYDIR_FUNC
  297. int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path)
  298. {
  299. /* Count the number of files first, to pre-allocate the files array */
  300. size_t n_files = 0;
  301. if (tinydir_open(dir, path) == -1)
  302. {
  303. return -1;
  304. }
  305. while (dir->has_next)
  306. {
  307. n_files++;
  308. if (tinydir_next(dir) == -1)
  309. {
  310. goto bail;
  311. }
  312. }
  313. tinydir_close(dir);
  314. if (tinydir_open(dir, path) == -1)
  315. {
  316. return -1;
  317. }
  318. dir->n_files = 0;
  319. dir->_files = (tinydir_file *)_TINYDIR_MALLOC(sizeof *dir->_files * n_files);
  320. if (dir->_files == NULL)
  321. {
  322. goto bail;
  323. }
  324. while (dir->has_next)
  325. {
  326. tinydir_file *p_file;
  327. dir->n_files++;
  328. p_file = &dir->_files[dir->n_files - 1];
  329. if (tinydir_readfile(dir, p_file) == -1)
  330. {
  331. goto bail;
  332. }
  333. if (tinydir_next(dir) == -1)
  334. {
  335. goto bail;
  336. }
  337. /* Just in case the number of files has changed between the first and
  338. second reads, terminate without writing into unallocated memory */
  339. if (dir->n_files == n_files)
  340. {
  341. break;
  342. }
  343. }
  344. qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp);
  345. return 0;
  346. bail:
  347. tinydir_close(dir);
  348. return -1;
  349. }
  350. _TINYDIR_FUNC
  351. void tinydir_close(tinydir_dir *dir)
  352. {
  353. if (dir == NULL)
  354. {
  355. return;
  356. }
  357. memset(dir->path, 0, sizeof(dir->path));
  358. dir->has_next = 0;
  359. dir->n_files = 0;
  360. _TINYDIR_FREE(dir->_files);
  361. dir->_files = NULL;
  362. #ifdef _MSC_VER
  363. if (dir->_h != INVALID_HANDLE_VALUE)
  364. {
  365. FindClose(dir->_h);
  366. }
  367. dir->_h = INVALID_HANDLE_VALUE;
  368. #else
  369. if (dir->_d)
  370. {
  371. _tinydir_closedir(dir->_d);
  372. }
  373. dir->_d = NULL;
  374. dir->_e = NULL;
  375. #ifndef _TINYDIR_USE_READDIR
  376. _TINYDIR_FREE(dir->_ep);
  377. dir->_ep = NULL;
  378. #endif
  379. #endif
  380. }
  381. _TINYDIR_FUNC
  382. int tinydir_next(tinydir_dir *dir)
  383. {
  384. if (dir == NULL)
  385. {
  386. errno = EINVAL;
  387. return -1;
  388. }
  389. if (!dir->has_next)
  390. {
  391. errno = ENOENT;
  392. return -1;
  393. }
  394. #ifdef _MSC_VER
  395. if (FindNextFile(dir->_h, &dir->_f) == 0)
  396. #else
  397. #ifdef _TINYDIR_USE_READDIR
  398. dir->_e = _tinydir_readdir(dir->_d);
  399. #else
  400. if (dir->_ep == NULL)
  401. {
  402. return -1;
  403. }
  404. if (readdir_r(dir->_d, dir->_ep, &dir->_e) != 0)
  405. {
  406. return -1;
  407. }
  408. #endif
  409. if (dir->_e == NULL)
  410. #endif
  411. {
  412. dir->has_next = 0;
  413. #ifdef _MSC_VER
  414. if (GetLastError() != ERROR_SUCCESS &&
  415. GetLastError() != ERROR_NO_MORE_FILES)
  416. {
  417. tinydir_close(dir);
  418. errno = EIO;
  419. return -1;
  420. }
  421. #endif
  422. }
  423. return 0;
  424. }
  425. _TINYDIR_FUNC
  426. int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file)
  427. {
  428. if (dir == NULL || file == NULL)
  429. {
  430. errno = EINVAL;
  431. return -1;
  432. }
  433. #ifdef _MSC_VER
  434. if (dir->_h == INVALID_HANDLE_VALUE)
  435. #else
  436. if (dir->_e == NULL)
  437. #endif
  438. {
  439. errno = ENOENT;
  440. return -1;
  441. }
  442. if (_tinydir_strlen(dir->path) +
  443. _tinydir_strlen(
  444. #ifdef _MSC_VER
  445. dir->_f.cFileName
  446. #else
  447. dir->_e->d_name
  448. #endif
  449. ) + 1 + _TINYDIR_PATH_EXTRA >=
  450. _TINYDIR_PATH_MAX)
  451. {
  452. /* the path for the file will be too long */
  453. errno = ENAMETOOLONG;
  454. return -1;
  455. }
  456. if (_tinydir_strlen(
  457. #ifdef _MSC_VER
  458. dir->_f.cFileName
  459. #else
  460. dir->_e->d_name
  461. #endif
  462. ) >= _TINYDIR_FILENAME_MAX)
  463. {
  464. errno = ENAMETOOLONG;
  465. return -1;
  466. }
  467. _tinydir_strcpy(file->path, dir->path);
  468. _tinydir_strcat(file->path, TINYDIR_STRING("/"));
  469. _tinydir_strcpy(file->name,
  470. #ifdef _MSC_VER
  471. dir->_f.cFileName
  472. #else
  473. dir->_e->d_name
  474. #endif
  475. );
  476. _tinydir_strcat(file->path, file->name);
  477. #ifndef _MSC_VER
  478. #ifdef __MINGW32__
  479. if (_tstat(
  480. #else
  481. if (stat(
  482. #endif
  483. file->path, &file->_s) == -1)
  484. {
  485. return -1;
  486. }
  487. #endif
  488. _tinydir_get_ext(file);
  489. file->is_dir =
  490. #ifdef _MSC_VER
  491. !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
  492. #else
  493. S_ISDIR(file->_s.st_mode);
  494. #endif
  495. file->is_reg =
  496. #ifdef _MSC_VER
  497. !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) ||
  498. (
  499. !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) &&
  500. !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
  501. !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) &&
  502. #ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM
  503. !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) &&
  504. #endif
  505. #ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA
  506. !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) &&
  507. #endif
  508. !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) &&
  509. !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY));
  510. #else
  511. S_ISREG(file->_s.st_mode);
  512. #endif
  513. return 0;
  514. }
  515. _TINYDIR_FUNC
  516. int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i)
  517. {
  518. if (dir == NULL || file == NULL)
  519. {
  520. errno = EINVAL;
  521. return -1;
  522. }
  523. if (i >= dir->n_files)
  524. {
  525. errno = ENOENT;
  526. return -1;
  527. }
  528. memcpy(file, &dir->_files[i], sizeof(tinydir_file));
  529. _tinydir_get_ext(file);
  530. return 0;
  531. }
  532. _TINYDIR_FUNC
  533. int tinydir_open_subdir_n(tinydir_dir *dir, size_t i)
  534. {
  535. _tinydir_char_t path[_TINYDIR_PATH_MAX];
  536. if (dir == NULL)
  537. {
  538. errno = EINVAL;
  539. return -1;
  540. }
  541. if (i >= dir->n_files || !dir->_files[i].is_dir)
  542. {
  543. errno = ENOENT;
  544. return -1;
  545. }
  546. _tinydir_strcpy(path, dir->_files[i].path);
  547. tinydir_close(dir);
  548. if (tinydir_open_sorted(dir, path) == -1)
  549. {
  550. return -1;
  551. }
  552. return 0;
  553. }
  554. /* Open a single file given its path */
  555. _TINYDIR_FUNC
  556. int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path)
  557. {
  558. tinydir_dir dir;
  559. int result = 0;
  560. int found = 0;
  561. _tinydir_char_t dir_name_buf[_TINYDIR_PATH_MAX];
  562. _tinydir_char_t file_name_buf[_TINYDIR_FILENAME_MAX];
  563. _tinydir_char_t *dir_name;
  564. _tinydir_char_t *base_name;
  565. #if (defined _MSC_VER || defined __MINGW32__)
  566. _tinydir_char_t drive_buf[_TINYDIR_DRIVE_MAX];
  567. _tinydir_char_t ext_buf[_TINYDIR_FILENAME_MAX];
  568. #endif
  569. if (file == NULL || path == NULL || _tinydir_strlen(path) == 0)
  570. {
  571. errno = EINVAL;
  572. return -1;
  573. }
  574. if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
  575. {
  576. errno = ENAMETOOLONG;
  577. return -1;
  578. }
  579. /* Get the parent path */
  580. #if (defined _MSC_VER || defined __MINGW32__)
  581. #if ((defined _MSC_VER) && (_MSC_VER >= 1400))
  582. _tsplitpath_s(
  583. path,
  584. drive_buf, _TINYDIR_DRIVE_MAX,
  585. dir_name_buf, _TINYDIR_FILENAME_MAX,
  586. file_name_buf, _TINYDIR_FILENAME_MAX,
  587. ext_buf, _TINYDIR_FILENAME_MAX);
  588. #else
  589. _tsplitpath(
  590. path,
  591. drive_buf,
  592. dir_name_buf,
  593. file_name_buf,
  594. ext_buf);
  595. #endif
  596. /* _splitpath_s not work fine with only filename and widechar support */
  597. #ifdef _UNICODE
  598. if (drive_buf[0] == L'\xFEFE')
  599. drive_buf[0] = '\0';
  600. if (dir_name_buf[0] == L'\xFEFE')
  601. dir_name_buf[0] = '\0';
  602. #endif
  603. if (errno)
  604. {
  605. errno = EINVAL;
  606. return -1;
  607. }
  608. /* Emulate the behavior of dirname by returning "." for dir name if it's
  609. empty */
  610. if (drive_buf[0] == '\0' && dir_name_buf[0] == '\0')
  611. {
  612. _tinydir_strcpy(dir_name_buf, TINYDIR_STRING("."));
  613. }
  614. /* Concatenate the drive letter and dir name to form full dir name */
  615. _tinydir_strcat(drive_buf, dir_name_buf);
  616. dir_name = drive_buf;
  617. /* Concatenate the file name and extension to form base name */
  618. _tinydir_strcat(file_name_buf, ext_buf);
  619. base_name = file_name_buf;
  620. #else
  621. _tinydir_strcpy(dir_name_buf, path);
  622. dir_name = dirname(dir_name_buf);
  623. _tinydir_strcpy(file_name_buf, path);
  624. base_name =basename(file_name_buf);
  625. #endif
  626. /* Open the parent directory */
  627. if (tinydir_open(&dir, dir_name) == -1)
  628. {
  629. return -1;
  630. }
  631. /* Read through the parent directory and look for the file */
  632. while (dir.has_next)
  633. {
  634. if (tinydir_readfile(&dir, file) == -1)
  635. {
  636. result = -1;
  637. goto bail;
  638. }
  639. if (_tinydir_strcmp(file->name, base_name) == 0)
  640. {
  641. /* File found */
  642. found = 1;
  643. break;
  644. }
  645. tinydir_next(&dir);
  646. }
  647. if (!found)
  648. {
  649. result = -1;
  650. errno = ENOENT;
  651. }
  652. bail:
  653. tinydir_close(&dir);
  654. return result;
  655. }
  656. _TINYDIR_FUNC
  657. void _tinydir_get_ext(tinydir_file *file)
  658. {
  659. _tinydir_char_t *period = _tinydir_strrchr(file->name, TINYDIR_STRING('.'));
  660. if (period == NULL)
  661. {
  662. file->extension = &(file->name[_tinydir_strlen(file->name)]);
  663. }
  664. else
  665. {
  666. file->extension = period + 1;
  667. }
  668. }
  669. _TINYDIR_FUNC
  670. int _tinydir_file_cmp(const void *a, const void *b)
  671. {
  672. const tinydir_file *fa = (const tinydir_file *)a;
  673. const tinydir_file *fb = (const tinydir_file *)b;
  674. if (fa->is_dir != fb->is_dir)
  675. {
  676. return -(fa->is_dir - fb->is_dir);
  677. }
  678. return _tinydir_strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX);
  679. }
  680. #ifndef _MSC_VER
  681. #ifndef _TINYDIR_USE_READDIR
  682. /*
  683. The following authored by Ben Hutchings <ben@decadent.org.uk>
  684. from https://womble.decadent.org.uk/readdir_r-advisory.html
  685. */
  686. /* Calculate the required buffer size (in bytes) for directory *
  687. * entries read from the given directory handle. Return -1 if this *
  688. * this cannot be done. *
  689. * *
  690. * This code does not trust values of NAME_MAX that are less than *
  691. * 255, since some systems (including at least HP-UX) incorrectly *
  692. * define it to be a smaller value. */
  693. _TINYDIR_FUNC
  694. size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp)
  695. {
  696. long name_max;
  697. size_t name_end;
  698. /* parameter may be unused */
  699. (void)dirp;
  700. #if defined _TINYDIR_USE_FPATHCONF
  701. name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
  702. if (name_max == -1)
  703. #if defined(NAME_MAX)
  704. name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
  705. #else
  706. return (size_t)(-1);
  707. #endif
  708. #elif defined(NAME_MAX)
  709. name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
  710. #else
  711. #error "buffer size for readdir_r cannot be determined"
  712. #endif
  713. name_end = (size_t)offsetof(struct _tinydir_dirent, d_name) + name_max + 1;
  714. return (name_end > sizeof(struct _tinydir_dirent) ?
  715. name_end : sizeof(struct _tinydir_dirent));
  716. }
  717. #endif
  718. #endif
  719. #ifdef __cplusplus
  720. }
  721. #endif
  722. # if defined (_MSC_VER)
  723. # pragma warning(pop)
  724. # endif
  725. #endif