You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

640 lines
23 KiB

  1. /* /////////////////////////////////////////////////////////////////////////
  2. * File: unixstl/filesystem/directory_functions.hpp
  3. *
  4. * Purpose: Functions for manipulating directories.
  5. *
  6. * Created: 7th February 2002
  7. * Updated: 13th February 2011
  8. *
  9. * Home: http://stlsoft.org/
  10. *
  11. * Copyright (c) 2002-2011, Matthew Wilson and Synesis Software
  12. * All rights reserved.
  13. *
  14. * Redistribution and use in source and binary forms, with or without
  15. * modification, are permitted provided that the following conditions are met:
  16. *
  17. * - Redistributions of source code must retain the above copyright notice, this
  18. * list of conditions and the following disclaimer.
  19. * - Redistributions in binary form must reproduce the above copyright notice,
  20. * this list of conditions and the following disclaimer in the documentation
  21. * and/or other materials provided with the distribution.
  22. * - Neither the name(s) of Matthew Wilson and Synesis Software nor the names of
  23. * any contributors may be used to endorse or promote products derived from
  24. * this software without specific prior written permission.
  25. *
  26. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  27. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  28. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  29. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  30. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  31. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  32. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  33. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  34. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  35. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  36. * POSSIBILITY OF SUCH DAMAGE.
  37. *
  38. * ////////////////////////////////////////////////////////////////////// */
  39. /** \file unixstl/filesystem/directory_functions.hpp
  40. *
  41. * \brief [C++ only] Functions for manipulating directories
  42. * (\ref group__library__filesystem "File System" Library).
  43. */
  44. #ifndef UNIXSTL_INCL_UNIXSTL_FILESYSTEM_HPP_DIRECTORY_FUNCTIONS
  45. #define UNIXSTL_INCL_UNIXSTL_FILESYSTEM_HPP_DIRECTORY_FUNCTIONS
  46. #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
  47. # define UNIXSTL_VER_UNIXSTL_FILESYSTEM_HPP_DIRECTORY_FUNCTIONS_MAJOR 3
  48. # define UNIXSTL_VER_UNIXSTL_FILESYSTEM_HPP_DIRECTORY_FUNCTIONS_MINOR 0
  49. # define UNIXSTL_VER_UNIXSTL_FILESYSTEM_HPP_DIRECTORY_FUNCTIONS_REVISION 6
  50. # define UNIXSTL_VER_UNIXSTL_FILESYSTEM_HPP_DIRECTORY_FUNCTIONS_EDIT 43
  51. #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
  52. /* /////////////////////////////////////////////////////////////////////////
  53. * Compatibility
  54. */
  55. /*
  56. [Incompatibilies-start]
  57. STLSOFT_COMPILER_IS_MSVC: _MSC_VER<1200
  58. [Incompatibilies-end]
  59. */
  60. /* /////////////////////////////////////////////////////////////////////////
  61. * Includes
  62. */
  63. #ifndef UNIXSTL_INCL_UNIXSTL_H_UNIXSTL
  64. # include <unixstl/unixstl.h>
  65. #endif /* !UNIXSTL_INCL_UNIXSTL_H_UNIXSTL */
  66. #ifndef UNIXSTL_INCL_UNIXSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS
  67. # include <unixstl/filesystem/filesystem_traits.hpp>
  68. #endif /* !UNIXSTL_INCL_UNIXSTL_FILESYSTEM_HPP_FILESYSTEM_TRAITS */
  69. #ifndef UNIXSTL_INCL_UNIXSTL_FILESYSTEM_HPP_FILE_PATH_BUFFER
  70. # include <unixstl/filesystem/file_path_buffer.hpp>
  71. #endif /* !UNIXSTL_INCL_UNIXSTL_FILESYSTEM_HPP_FILE_PATH_BUFFER */
  72. /* /////////////////////////////////////////////////////////////////////////
  73. * Namespace
  74. */
  75. #ifndef _UNIXSTL_NO_NAMESPACE
  76. # if defined(_STLSOFT_NO_NAMESPACE) || \
  77. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  78. /* There is no stlsoft namespace, so must define ::unixstl */
  79. namespace unixstl
  80. {
  81. # else
  82. /* Define stlsoft::unixstl_project */
  83. namespace stlsoft
  84. {
  85. namespace unixstl_project
  86. {
  87. # endif /* _STLSOFT_NO_NAMESPACE */
  88. #endif /* !_UNIXSTL_NO_NAMESPACE */
  89. /* /////////////////////////////////////////////////////////////////////////
  90. * Helper functions
  91. */
  92. template <ss_typename_param_k C>
  93. inline C* find_last_path_name_separator_(C const* s)
  94. {
  95. typedef filesystem_traits<C> traits_t;
  96. ss_typename_type_k traits_t::char_type const* slash = traits_t::str_rchr(s, '/');
  97. #ifdef _WIN32
  98. ss_typename_type_k traits_t::char_type const* bslash = traits_t::str_rchr(s, '\\');
  99. if(NULL == slash)
  100. {
  101. slash = bslash;
  102. }
  103. else if(NULL != bslash)
  104. {
  105. if(slash < bslash)
  106. {
  107. slash = bslash;
  108. }
  109. }
  110. #endif /* _WIN32 */
  111. return const_cast<C*>(slash);
  112. }
  113. template <ss_typename_param_k C>
  114. inline
  115. us_bool_t
  116. create_directory_recurse_impl(
  117. C const* dir
  118. , unsigned short mode
  119. )
  120. {
  121. typedef C char_type;
  122. typedef filesystem_traits<C> traits_t;
  123. typedef basic_file_path_buffer<char_type> file_path_buffer_t;
  124. us_bool_t bRet;
  125. if( NULL == dir ||
  126. '\0' == *dir)
  127. {
  128. traits_t::set_last_error(ENOTDIR);
  129. bRet = false;
  130. }
  131. else
  132. {
  133. if(traits_t::file_exists(dir))
  134. {
  135. if(traits_t::is_directory(dir))
  136. {
  137. traits_t::set_last_error(EISDIR);
  138. bRet = true;
  139. }
  140. else
  141. {
  142. traits_t::set_last_error(EEXIST);
  143. bRet = false;
  144. }
  145. }
  146. else
  147. {
  148. file_path_buffer_t sz;
  149. file_path_buffer_t szParent;
  150. // May be being compiled absent exception support, so need to check the
  151. // file path buffers. (This _could_ be done with a compile-time #ifdef,
  152. // but it's best not, since some translators support exceptions but yet
  153. // don't throw on mem exhaustion, and in any case a user could change
  154. // ::new)
  155. if( 0 == sz.size() ||
  156. 0 == szParent.size())
  157. {
  158. bRet = false;
  159. }
  160. else
  161. {
  162. us_size_t const dirLen = traits_t::str_len(dir);
  163. if((dirLen + 1) > sz.size())
  164. {
  165. traits_t::set_last_error(EINVAL);
  166. bRet = false;
  167. }
  168. else
  169. {
  170. traits_t::char_copy(&sz[0], dir, dirLen + 1);
  171. traits_t::remove_dir_end(&sz[0]);
  172. if( traits_t::create_directory(sz.c_str(), mode) ||
  173. EEXIST == traits_t::get_last_error())
  174. {
  175. traits_t::set_last_error(0);
  176. bRet = true;
  177. }
  178. else
  179. {
  180. // Trim previous directory
  181. traits_t::char_copy(&szParent[0], sz.c_str(), dirLen + 1);
  182. char_type* pszSlash = find_last_path_name_separator_(szParent.c_str());
  183. if(pszSlash == NULL)
  184. {
  185. traits_t::set_last_error(ENOTDIR);
  186. bRet = false;
  187. }
  188. else
  189. {
  190. *pszSlash = '\0'; // Will always have enough room for two bytes
  191. // If second character is ':', and total lengths is less than four,
  192. // or the recurse create fails, then return false;
  193. if( ( szParent[1] == ':' &&
  194. (traits_t::set_last_error(EACCES), traits_t::str_len(szParent.c_str()) < 4)) ||
  195. !create_directory_recurse_impl(szParent.c_str(), mode))
  196. {
  197. bRet = false;
  198. }
  199. else
  200. {
  201. bRet = traits_t::create_directory(sz.c_str(), mode) || EEXIST == traits_t::get_last_error();
  202. }
  203. }
  204. }
  205. }
  206. }
  207. }
  208. }
  209. return bRet;
  210. }
  211. template<
  212. ss_typename_param_k C
  213. , ss_typename_param_k FD // This is need because VC++6 cannot deduce filesystem_traits<C>::find_data_type
  214. >
  215. inline
  216. us_int_t
  217. remove_directory_recurse_impl(
  218. C const* dir
  219. , us_int_t (*pfn)(void* param, C const* subDir, FD const* st, struct dirent const* de, int err)
  220. , void* param
  221. )
  222. {
  223. typedef C char_type;
  224. typedef filesystem_traits<C> traits_t;
  225. typedef basic_file_path_buffer<char_type> file_path_buffer_t;
  226. us_int_t dwRet;
  227. if(NULL != pfn)
  228. {
  229. (void)(*pfn)(param, dir, NULL, NULL, ~0); // Entering
  230. }
  231. if( NULL == dir ||
  232. '\0' == *dir)
  233. {
  234. dwRet = ENOTDIR;
  235. if(NULL != pfn)
  236. {
  237. (void)(*pfn)(param, dir, NULL, NULL, dwRet);
  238. }
  239. }
  240. else
  241. {
  242. if(!traits_t::file_exists(dir))
  243. {
  244. // The given path does not exist, so this is treated as success, but
  245. // reporting ENOENT
  246. dwRet = ENOENT;
  247. if(NULL != pfn)
  248. {
  249. (void)(*pfn)(param, dir, NULL, NULL, dwRet);
  250. }
  251. }
  252. else
  253. {
  254. if(traits_t::is_file(dir))
  255. {
  256. // The given path exists as a file. This is failure
  257. dwRet = EEXIST;
  258. if(NULL != pfn)
  259. {
  260. (void)(*pfn)(param, dir, NULL, NULL, dwRet);
  261. }
  262. }
  263. else
  264. {
  265. // Otherwise, we attempt to remove it
  266. if(traits_t::remove_directory(dir))
  267. {
  268. dwRet = 0;
  269. if(NULL != pfn)
  270. {
  271. (void)(*pfn)(param, dir, NULL, NULL, dwRet); // Deleted
  272. }
  273. }
  274. else
  275. {
  276. const int removeError = traits_t::get_last_error();
  277. if(ENOTEMPTY != removeError)
  278. {
  279. dwRet = removeError;
  280. if(NULL != pfn)
  281. {
  282. (void)(*pfn)(param, dir, NULL, NULL, dwRet);
  283. }
  284. }
  285. else
  286. {
  287. // It has some contents, so we need to remove them
  288. file_path_buffer_t sz;
  289. DIR* hSrch;
  290. us_size_t n;
  291. us_size_t const dirLen = traits_t::str_len(dir);
  292. traits_t::char_copy(&sz[0], dir, dirLen + 1);
  293. traits_t::ensure_dir_end(&sz[0]);
  294. n = traits_t::str_len(sz.c_str());
  295. hSrch = traits_t::open_dir(sz.c_str());
  296. if(NULL == hSrch)
  297. {
  298. dwRet = traits_t::get_last_error();
  299. }
  300. else
  301. {
  302. dwRet = 0;
  303. for(struct dirent const* de; 0 == dwRet && NULL != (de = traits_t::read_dir(hSrch)); )
  304. {
  305. if(!traits_t::is_dots(de->d_name))
  306. {
  307. ss_typename_type_k traits_t::stat_data_type st;
  308. us_size_t const denameLen = traits_t::str_len(de->d_name);
  309. traits_t::char_copy(&sz[0] + n, de->d_name, denameLen + 1);
  310. if(!traits_t::stat(sz.c_str(), &st))
  311. {
  312. dwRet = traits_t::get_last_error();
  313. if(NULL != pfn)
  314. {
  315. (void)(*pfn)(param, dir, NULL, de, dwRet);
  316. }
  317. }
  318. else
  319. {
  320. if(traits_t::is_file(&st))
  321. {
  322. // If it's a file, the pfn must be consulted, otherwise
  323. // it's an automatic failure
  324. if( NULL == pfn ||
  325. !(*pfn)(param, dir, &st, de, 0))
  326. {
  327. dwRet = ENOTEMPTY;
  328. if(NULL != pfn)
  329. {
  330. (void)(*pfn)(param, dir, &st, de, dwRet);
  331. }
  332. break;
  333. }
  334. else
  335. {
  336. if(!traits_t::delete_file(sz.c_str()))
  337. {
  338. dwRet = traits_t::get_last_error();
  339. if(NULL != pfn)
  340. {
  341. (void)(*pfn)(param, dir, &st, de, dwRet);
  342. }
  343. break;
  344. }
  345. }
  346. }
  347. else
  348. {
  349. // If it's a directory, then pfn is consulted, otherwise
  350. // it's an automatic attempt to recursively delete
  351. if( NULL != pfn &&
  352. !(*pfn)(param, dir, &st, de, 0))
  353. {
  354. dwRet = ENOTEMPTY;
  355. if(NULL != pfn)
  356. {
  357. (void)(*pfn)(param, dir, &st, de, dwRet);
  358. }
  359. break;
  360. }
  361. else
  362. {
  363. dwRet = remove_directory_recurse_impl(sz.c_str(), pfn, param);
  364. }
  365. }
  366. }
  367. }
  368. }
  369. traits_t::close_dir(hSrch);
  370. if(0 == dwRet)
  371. {
  372. if(traits_t::remove_directory(dir))
  373. {
  374. if(NULL != pfn)
  375. {
  376. (void)(*pfn)(param, dir, NULL, NULL, 0); // Deleted
  377. }
  378. }
  379. else
  380. {
  381. dwRet = traits_t::get_last_error();
  382. }
  383. }
  384. }
  385. }
  386. }
  387. }
  388. }
  389. }
  390. return dwRet;
  391. }
  392. /* /////////////////////////////////////////////////////////////////////////
  393. * Functions
  394. */
  395. /** \brief Creates the given directory, including all its parent directories, applying
  396. * the given mode.
  397. *
  398. * \ingroup group__library__filesystem
  399. *
  400. * \param dir The path of the directory to create
  401. * \param mode The permissions with which each directory is to be created
  402. */
  403. inline us_bool_t create_directory_recurse(us_char_a_t const* dir, unsigned short mode = 0755)
  404. {
  405. return create_directory_recurse_impl(dir, mode);
  406. }
  407. #if 0
  408. /** \brief Creates the given directory, including all its parent directories, applying
  409. * the given mode.
  410. *
  411. * \ingroup group__library__filesystem
  412. *
  413. * \param dir The path of the directory to create
  414. * \param mode The permissions with which each directory is to be created
  415. */
  416. inline us_bool_t create_directory_recurse(us_char_w_t const* dir, unsigned short mode = 0755)
  417. {
  418. return create_directory_recurse_impl(dir, mode);
  419. }
  420. #endif /* 0 */
  421. /** \brief Creates the given directory, including all its parent directories, applying
  422. * the given mode.
  423. *
  424. * \ingroup group__library__filesystem
  425. *
  426. * \param dir The path of the directory to create
  427. * \param mode The permissions with which each directory is to be created
  428. */
  429. template <ss_typename_param_k S>
  430. inline us_bool_t create_directory_recurse(S const& dir, unsigned short mode = 0755)
  431. {
  432. return create_directory_recurse(stlsoft_ns_qual(c_str_ptr)(dir), mode);
  433. }
  434. /** \brief Removes the given directory, and all its subdirectories.
  435. *
  436. * \ingroup group__library__filesystem
  437. *
  438. * \param dir The path of the directory to remove.
  439. * \param pfn Pointer to a callback function, which will receive
  440. * notifications and requests for file/directory deletion. The semantics
  441. * of the parameters are specified in the note below.
  442. * \param param Caller-supplied parameter, always passed through to the
  443. * callback function \c pfn.
  444. *
  445. * \note If no callback function is specified, then the function will remove
  446. * only empty subdirectories, i.e. no files will be removed. To remove
  447. * files, a function must be supplied, and may take additional measures
  448. * (such as changing file attributes) before the deletion is attempted by
  449. * <code>remove_directory_recurse()</code>. Do not delete the file in the
  450. * callback, otherwise the attempt within
  451. * <code>remove_directory_recurse()</code> will fail, and the function
  452. * will report overall failure of the operation.
  453. *
  454. * \note The semantics of the callback function's parameters are as follows:
  455. * \li If the err param is ~0 (-1 on UNIX), then the \c dir param specifies
  456. * the name of the current directory being traversed. All other params are
  457. * unspecified (except \c param). The return value is ignored.
  458. * \li If the err param is 0 and the \c st param is NULL, then \c dir
  459. * specifies the name of a directory that has been successfully removed.
  460. * All other params are unspecified (except \c param). The return value is
  461. * ignored.
  462. * \li If the err param is 0 and the \c st param is not NULL, then \c dir
  463. * specifies the name of the currently traversing directory, \c st
  464. * specifies the stat information for the entry to be deleted, and \c de
  465. * specifies the name of the entry within directory \c dir that is a
  466. * candidate for removal. Return true to enable removal of this entry, or
  467. * false to prevent removal (and cancel the overall operation). All other
  468. * params are unspecified (except \c param). The return value is ignored.
  469. * \li If the err param is any other value, and the \c st param is NULL,
  470. * then the \c dir param specifies the name of a directory that could not
  471. * be deleted and err specifies the errno value associated with the
  472. * failure. All other params are unspecified (except \c param). The return
  473. * value is ignored.
  474. * \li If the err param is any other value, and the \c st param is not NULL,
  475. * then the \c dir param specifies the name of a directory within which an
  476. * entry could not be deleted, \c st specifies the stat information of the
  477. * entry that could not be deleted, \c de specifies the name of the entry
  478. * that could not be deleted, and err specifies the errno value associated
  479. * with the failure. All other params are unspecified (except \c param).
  480. * The return value is ignored.
  481. */
  482. inline us_bool_t remove_directory_recurse(
  483. us_char_a_t const* dir
  484. , us_int_t (*pfn)(void* param, us_char_a_t const* subDir, struct stat const* st, struct dirent const* de, int err)
  485. , void* param
  486. )
  487. {
  488. typedef filesystem_traits<us_char_a_t> traits_t;
  489. us_int_t dwRet = remove_directory_recurse_impl<us_char_a_t, struct stat>(dir, pfn, param);
  490. traits_t::set_last_error(dwRet);
  491. return 0 == dwRet;
  492. }
  493. /** \brief Removes the given directory, and all its subdirectories.
  494. *
  495. * \ingroup group__library__filesystem
  496. */
  497. inline us_bool_t remove_directory_recurse(us_char_a_t const* dir)
  498. {
  499. return remove_directory_recurse(dir, NULL, NULL);
  500. }
  501. #if 0
  502. /** \brief Removes the given directory, and all its subdirectories.
  503. *
  504. * \ingroup group__library__filesystem
  505. */
  506. inline us_bool_t remove_directory_recurse(
  507. us_char_w_t const* dir
  508. , us_int_t (*pfn)(void* param, us_char_w_t const* subDir, struct stat const* st, struct dirent const* de, int err)
  509. , void* param
  510. )
  511. {
  512. typedef filesystem_traits<us_char_w_t> traits_t;
  513. us_int_t dwRet = remove_directory_recurse_impl<us_char_w_t, struct stat>(dir, pfn, param);
  514. traits_t::set_last_error(dwRet);
  515. return 0 == dwRet;
  516. }
  517. /** \brief Removes the given directory, and all its subdirectories.
  518. *
  519. * \ingroup group__library__filesystem
  520. */
  521. inline us_bool_t remove_directory_recurse(us_char_w_t const* dir)
  522. {
  523. return remove_directory_recurse(dir, NULL, NULL);
  524. }
  525. #endif /* 0 */
  526. /** \brief Removes the given directory, and all its subdirectories.
  527. *
  528. * \ingroup group__library__filesystem
  529. */
  530. template <ss_typename_param_k S>
  531. inline us_bool_t remove_directory_recurse(S const& dir)
  532. {
  533. typedef filesystem_traits<us_char_a_t> traits_t;
  534. us_int_t dwRet = remove_directory_recurse(stlsoft_ns_qual(c_str_ptr)(dir), NULL, NULL);
  535. traits_t::set_last_error(dwRet);
  536. return 0 == dwRet;
  537. }
  538. /* /////////////////////////////////////////////////////////////////////////
  539. * Unit-testing
  540. */
  541. #ifdef STLSOFT_UNITTEST
  542. # include "./unittest/directory_functions_unittest_.h"
  543. #endif /* STLSOFT_UNITTEST */
  544. /* ////////////////////////////////////////////////////////////////////// */
  545. #ifndef _UNIXSTL_NO_NAMESPACE
  546. # if defined(_STLSOFT_NO_NAMESPACE) || \
  547. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  548. } // namespace unixstl
  549. # else
  550. } // namespace unixstl_project
  551. } // namespace stlsoft
  552. # endif /* _STLSOFT_NO_NAMESPACE */
  553. #endif /* !_UNIXSTL_NO_NAMESPACE */
  554. /* ////////////////////////////////////////////////////////////////////// */
  555. #endif /* UNIXSTL_INCL_UNIXSTL_FILESYSTEM_HPP_DIRECTORY_FUNCTIONS */
  556. /* ///////////////////////////// end of file //////////////////////////// */