PageRenderTime 37ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/indra/llvfs/lldiriterator.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 228 lines | 158 code | 31 blank | 39 comment | 15 complexity | 7710f26467df4dce8a9e6c349c04855e MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file lldiriterator.cpp
  3. * @brief Iterator through directory entries matching the search pattern.
  4. *
  5. * $LicenseInfo:firstyear=2010&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include "lldiriterator.h"
  27. #include <boost/filesystem.hpp>
  28. #include <boost/regex.hpp>
  29. namespace fs = boost::filesystem;
  30. static std::string glob_to_regex(const std::string& glob);
  31. class LLDirIterator::Impl
  32. {
  33. public:
  34. Impl(const std::string &dirname, const std::string &mask);
  35. ~Impl();
  36. bool next(std::string &fname);
  37. private:
  38. boost::regex mFilterExp;
  39. fs::directory_iterator mIter;
  40. bool mIsValid;
  41. };
  42. LLDirIterator::Impl::Impl(const std::string &dirname, const std::string &mask)
  43. : mIsValid(false)
  44. {
  45. fs::path dir_path(dirname);
  46. bool is_dir = false;
  47. // Check if path is a directory.
  48. try
  49. {
  50. is_dir = fs::is_directory(dir_path);
  51. }
  52. catch (fs::basic_filesystem_error<fs::path>& e)
  53. {
  54. llwarns << e.what() << llendl;
  55. return;
  56. }
  57. if (!is_dir)
  58. {
  59. llwarns << "Invalid path: \"" << dir_path.string() << "\"" << llendl;
  60. return;
  61. }
  62. // Initialize the directory iterator for the given path.
  63. try
  64. {
  65. mIter = fs::directory_iterator(dir_path);
  66. }
  67. catch (fs::basic_filesystem_error<fs::path>& e)
  68. {
  69. llwarns << e.what() << llendl;
  70. return;
  71. }
  72. // Convert the glob mask to a regular expression
  73. std::string exp = glob_to_regex(mask);
  74. // Initialize boost::regex with the expression converted from
  75. // the glob mask.
  76. // An exception is thrown if the expression is not valid.
  77. try
  78. {
  79. mFilterExp.assign(exp);
  80. }
  81. catch (boost::regex_error& e)
  82. {
  83. llwarns << "\"" << exp << "\" is not a valid regular expression: "
  84. << e.what() << llendl;
  85. return;
  86. }
  87. mIsValid = true;
  88. }
  89. LLDirIterator::Impl::~Impl()
  90. {
  91. }
  92. bool LLDirIterator::Impl::next(std::string &fname)
  93. {
  94. fname = "";
  95. if (!mIsValid)
  96. {
  97. llwarns << "The iterator is not correctly initialized." << llendl;
  98. return false;
  99. }
  100. fs::directory_iterator end_itr; // default construction yields past-the-end
  101. bool found = false;
  102. while (mIter != end_itr && !found)
  103. {
  104. boost::smatch match;
  105. std::string name = mIter->path().filename();
  106. if (found = boost::regex_match(name, match, mFilterExp))
  107. {
  108. fname = name;
  109. }
  110. ++mIter;
  111. }
  112. return found;
  113. }
  114. /**
  115. Converts the incoming glob into a regex. This involves
  116. converting incoming glob expressions to regex equivilents and
  117. at the same time, escaping any regex meaningful characters which
  118. do not have glob meaning, i.e.
  119. .()+|^$
  120. in the input.
  121. */
  122. std::string glob_to_regex(const std::string& glob)
  123. {
  124. std::string regex;
  125. regex.reserve(glob.size()<<1);
  126. S32 braces = 0;
  127. bool escaped = false;
  128. bool square_brace_open = false;
  129. for (std::string::const_iterator i = glob.begin(); i != glob.end(); ++i)
  130. {
  131. char c = *i;
  132. switch (c)
  133. {
  134. case '*':
  135. if (glob.begin() == i)
  136. {
  137. regex+="[^.].*";
  138. }
  139. else
  140. {
  141. regex+= escaped ? "*" : ".*";
  142. }
  143. break;
  144. case '?':
  145. regex+= escaped ? '?' : '.';
  146. break;
  147. case '{':
  148. braces++;
  149. regex+='(';
  150. break;
  151. case '}':
  152. if (!braces)
  153. {
  154. llerrs << "glob_to_regex: Closing brace without an equivalent opening brace: " << glob << llendl;
  155. }
  156. regex+=')';
  157. braces--;
  158. break;
  159. case ',':
  160. regex+= braces ? '|' : c;
  161. break;
  162. case '!':
  163. regex+= square_brace_open ? '^' : c;
  164. break;
  165. case '.': // This collection have different regex meaning
  166. case '^': // and so need escaping.
  167. case '(':
  168. case ')':
  169. case '+':
  170. case '|':
  171. case '$':
  172. regex += '\\';
  173. default:
  174. regex += c;
  175. break;
  176. }
  177. escaped = ('\\' == c);
  178. square_brace_open = ('[' == c);
  179. }
  180. if (braces)
  181. {
  182. llerrs << "glob_to_regex: Unterminated brace expression: " << glob << llendl;
  183. }
  184. return regex;
  185. }
  186. LLDirIterator::LLDirIterator(const std::string &dirname, const std::string &mask)
  187. {
  188. mImpl = new Impl(dirname, mask);
  189. }
  190. LLDirIterator::~LLDirIterator()
  191. {
  192. delete mImpl;
  193. }
  194. bool LLDirIterator::next(std::string &fname)
  195. {
  196. return mImpl->next(fname);
  197. }