Glob.cxx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. /*=========================================================================
  2. Program: KWSys - Kitware System Library
  3. Module: $RCSfile$
  4. Copyright (c) Kitware, Inc., Insight Consortium. All rights reserved.
  5. See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
  6. This software is distributed WITHOUT ANY WARRANTY; without even
  7. the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  8. PURPOSE. See the above copyright notices for more information.
  9. =========================================================================*/
  10. #include "kwsysPrivate.h"
  11. #include KWSYS_HEADER(Glob.hxx)
  12. #include KWSYS_HEADER(Configure.hxx)
  13. #include KWSYS_HEADER(RegularExpression.hxx)
  14. #include KWSYS_HEADER(SystemTools.hxx)
  15. #include KWSYS_HEADER(Directory.hxx)
  16. #include KWSYS_HEADER(stl/string)
  17. #include KWSYS_HEADER(stl/vector)
  18. // Work-around CMake dependency scanning limitation. This must
  19. // duplicate the above list of headers.
  20. #if 0
  21. # include "Glob.hxx.in"
  22. # include "Directory.hxx.in"
  23. # include "Configure.hxx.in"
  24. # include "RegularExpression.hxx.in"
  25. # include "SystemTools.hxx.in"
  26. # include "kwsys_stl.hxx.in"
  27. # include "kwsys_stl_string.hxx.in"
  28. #endif
  29. #include <ctype.h>
  30. #include <stdio.h>
  31. #include <string.h>
  32. namespace KWSYS_NAMESPACE
  33. {
  34. #if defined(_WIN32) || defined(__APPLE__) || defined(__CYGWIN__)
  35. // On Windows and apple, no difference between lower and upper case
  36. # define KWSYS_GLOB_CASE_INDEPENDENT
  37. #endif
  38. #if defined(_WIN32) || defined(__CYGWIN__)
  39. // Handle network paths
  40. # define KWSYS_GLOB_SUPPORT_NETWORK_PATHS
  41. #endif
  42. //----------------------------------------------------------------------------
  43. class GlobInternals
  44. {
  45. public:
  46. kwsys_stl::vector<kwsys_stl::string> Files;
  47. kwsys_stl::vector<kwsys::RegularExpression> Expressions;
  48. };
  49. //----------------------------------------------------------------------------
  50. Glob::Glob()
  51. {
  52. this->Internals = new GlobInternals;
  53. this->Recurse = false;
  54. this->Relative = "";
  55. }
  56. //----------------------------------------------------------------------------
  57. Glob::~Glob()
  58. {
  59. delete this->Internals;
  60. }
  61. //----------------------------------------------------------------------------
  62. kwsys_stl::vector<kwsys_stl::string>& Glob::GetFiles()
  63. {
  64. return this->Internals->Files;
  65. }
  66. //----------------------------------------------------------------------------
  67. kwsys_stl::string Glob::PatternToRegex(const kwsys_stl::string& pattern,
  68. bool require_whole_string)
  69. {
  70. // Incrementally build the regular expression from the pattern.
  71. kwsys_stl::string regex = require_whole_string? "^" : "";
  72. kwsys_stl::string::const_iterator pattern_first = pattern.begin();
  73. kwsys_stl::string::const_iterator pattern_last = pattern.end();
  74. for(kwsys_stl::string::const_iterator i = pattern_first;
  75. i != pattern_last; ++i)
  76. {
  77. int c = *i;
  78. if(c == '*')
  79. {
  80. // A '*' (not between brackets) matches any string.
  81. // We modify this to not match slashes since the orignal glob
  82. // pattern documentation was meant for matching file name
  83. // components separated by slashes.
  84. regex += "[^/]*";
  85. }
  86. else if(c == '?')
  87. {
  88. // A '?' (not between brackets) matches any single character.
  89. // We modify this to not match slashes since the orignal glob
  90. // pattern documentation was meant for matching file name
  91. // components separated by slashes.
  92. regex += "[^/]";
  93. }
  94. else if(c == '[')
  95. {
  96. // Parse out the bracket expression. It begins just after the
  97. // opening character.
  98. kwsys_stl::string::const_iterator bracket_first = i+1;
  99. kwsys_stl::string::const_iterator bracket_last = bracket_first;
  100. // The first character may be complementation '!' or '^'.
  101. if(bracket_last != pattern_last &&
  102. (*bracket_last == '!' || *bracket_last == '^'))
  103. {
  104. ++bracket_last;
  105. }
  106. // If the next character is a ']' it is included in the brackets
  107. // because the bracket string may not be empty.
  108. if(bracket_last != pattern_last && *bracket_last == ']')
  109. {
  110. ++bracket_last;
  111. }
  112. // Search for the closing ']'.
  113. while(bracket_last != pattern_last && *bracket_last != ']')
  114. {
  115. ++bracket_last;
  116. }
  117. // Check whether we have a complete bracket string.
  118. if(bracket_last == pattern_last)
  119. {
  120. // The bracket string did not end, so it was opened simply by
  121. // a '[' that is supposed to be matched literally.
  122. regex += "\\[";
  123. }
  124. else
  125. {
  126. // Convert the bracket string to its regex equivalent.
  127. kwsys_stl::string::const_iterator k = bracket_first;
  128. // Open the regex block.
  129. regex += "[";
  130. // A regex range complement uses '^' instead of '!'.
  131. if(k != bracket_last && *k == '!')
  132. {
  133. regex += "^";
  134. ++k;
  135. }
  136. // Convert the remaining characters.
  137. for(; k != bracket_last; ++k)
  138. {
  139. // Backslashes must be escaped.
  140. if(*k == '\\')
  141. {
  142. regex += "\\";
  143. }
  144. // Store this character.
  145. regex += *k;
  146. }
  147. // Close the regex block.
  148. regex += "]";
  149. // Jump to the end of the bracket string.
  150. i = bracket_last;
  151. }
  152. }
  153. else
  154. {
  155. // A single character matches itself.
  156. int ch = c;
  157. if(!(('a' <= ch && ch <= 'z') ||
  158. ('A' <= ch && ch <= 'Z') ||
  159. ('0' <= ch && ch <= '9')))
  160. {
  161. // Escape the non-alphanumeric character.
  162. regex += "\\";
  163. }
  164. #if defined(KWSYS_GLOB_CASE_INDEPENDENT)
  165. else
  166. {
  167. // On case-insensitive systems file names are converted to lower
  168. // case before matching.
  169. ch = tolower(ch);
  170. }
  171. #endif
  172. // Store the character.
  173. regex.append(1, static_cast<char>(ch));
  174. }
  175. }
  176. if(require_whole_string)
  177. {
  178. regex += "$";
  179. }
  180. return regex;
  181. }
  182. //----------------------------------------------------------------------------
  183. void Glob::RecurseDirectory(kwsys_stl::string::size_type start,
  184. const kwsys_stl::string& dir, bool dir_only)
  185. {
  186. kwsys::Directory d;
  187. if ( !d.Load(dir.c_str()) )
  188. {
  189. return;
  190. }
  191. unsigned long cc;
  192. kwsys_stl::string fullname;
  193. kwsys_stl::string realname;
  194. kwsys_stl::string fname;
  195. for ( cc = 0; cc < d.GetNumberOfFiles(); cc ++ )
  196. {
  197. fname = d.GetFile(cc);
  198. if ( strcmp(fname.c_str(), ".") == 0 ||
  199. strcmp(fname.c_str(), "..") == 0 )
  200. {
  201. continue;
  202. }
  203. if ( start == 0 )
  204. {
  205. realname = dir + fname;
  206. }
  207. else
  208. {
  209. realname = dir + "/" + fname;
  210. }
  211. #if defined( KWSYS_GLOB_CASE_INDEPENDENT )
  212. // On Windows and apple, no difference between lower and upper case
  213. fname = kwsys::SystemTools::LowerCase(fname);
  214. #endif
  215. if ( start == 0 )
  216. {
  217. fullname = dir + fname;
  218. }
  219. else
  220. {
  221. fullname = dir + "/" + fname;
  222. }
  223. if ( !dir_only || !kwsys::SystemTools::FileIsDirectory(realname.c_str()) )
  224. {
  225. if ( this->Internals->Expressions[
  226. this->Internals->Expressions.size()-1].find(fname.c_str()) )
  227. {
  228. this->AddFile(this->Internals->Files, realname.c_str());
  229. }
  230. }
  231. if ( kwsys::SystemTools::FileIsDirectory(realname.c_str()) )
  232. {
  233. this->RecurseDirectory(start+1, realname, dir_only);
  234. }
  235. }
  236. }
  237. //----------------------------------------------------------------------------
  238. void Glob::ProcessDirectory(kwsys_stl::string::size_type start,
  239. const kwsys_stl::string& dir, bool dir_only)
  240. {
  241. //kwsys_ios::cout << "ProcessDirectory: " << dir << kwsys_ios::endl;
  242. bool last = ( start == this->Internals->Expressions.size()-1 );
  243. if ( last && this->Recurse )
  244. {
  245. this->RecurseDirectory(start, dir, dir_only);
  246. return;
  247. }
  248. kwsys::Directory d;
  249. if ( !d.Load(dir.c_str()) )
  250. {
  251. return;
  252. }
  253. unsigned long cc;
  254. kwsys_stl::string fullname;
  255. kwsys_stl::string realname;
  256. kwsys_stl::string fname;
  257. for ( cc = 0; cc < d.GetNumberOfFiles(); cc ++ )
  258. {
  259. fname = d.GetFile(cc);
  260. if ( strcmp(fname.c_str(), ".") == 0 ||
  261. strcmp(fname.c_str(), "..") == 0 )
  262. {
  263. continue;
  264. }
  265. if ( start == 0 )
  266. {
  267. realname = dir + fname;
  268. }
  269. else
  270. {
  271. realname = dir + "/" + fname;
  272. }
  273. #if defined(KWSYS_GLOB_CASE_INDEPENDENT)
  274. // On case-insensitive file systems convert to lower case for matching.
  275. fname = kwsys::SystemTools::LowerCase(fname);
  276. #endif
  277. if ( start == 0 )
  278. {
  279. fullname = dir + fname;
  280. }
  281. else
  282. {
  283. fullname = dir + "/" + fname;
  284. }
  285. //kwsys_ios::cout << "Look at file: " << fname << kwsys_ios::endl;
  286. //kwsys_ios::cout << "Match: "
  287. // << this->Internals->TextExpressions[start].c_str() << kwsys_ios::endl;
  288. //kwsys_ios::cout << "Full name: " << fullname << kwsys_ios::endl;
  289. if ( (!dir_only || !last) &&
  290. !kwsys::SystemTools::FileIsDirectory(realname.c_str()) )
  291. {
  292. continue;
  293. }
  294. if ( this->Internals->Expressions[start].find(fname.c_str()) )
  295. {
  296. if ( last )
  297. {
  298. this->AddFile(this->Internals->Files, realname.c_str());
  299. }
  300. else
  301. {
  302. this->ProcessDirectory(start+1, realname + "/", dir_only);
  303. }
  304. }
  305. }
  306. }
  307. //----------------------------------------------------------------------------
  308. bool Glob::FindFiles(const kwsys_stl::string& inexpr)
  309. {
  310. kwsys_stl::string cexpr;
  311. kwsys_stl::string::size_type cc;
  312. kwsys_stl::string expr = inexpr;
  313. this->Internals->Expressions.clear();
  314. this->Internals->Files.clear();
  315. if ( !kwsys::SystemTools::FileIsFullPath(expr.c_str()) )
  316. {
  317. expr = kwsys::SystemTools::GetCurrentWorkingDirectory();
  318. expr += "/" + inexpr;
  319. }
  320. kwsys_stl::string fexpr = expr;
  321. int skip = 0;
  322. int last_slash = 0;
  323. for ( cc = 0; cc < expr.size(); cc ++ )
  324. {
  325. if ( cc > 0 && expr[cc] == '/' && expr[cc-1] != '\\' )
  326. {
  327. last_slash = static_cast<int>(cc);
  328. }
  329. if ( cc > 0 &&
  330. (expr[cc] == '[' || expr[cc] == '?' || expr[cc] == '*') &&
  331. expr[cc-1] != '\\' )
  332. {
  333. break;
  334. }
  335. }
  336. if ( last_slash > 0 )
  337. {
  338. //kwsys_ios::cout << "I can skip: " << fexpr.substr(0, last_slash)
  339. //<< kwsys_ios::endl;
  340. skip = last_slash;
  341. }
  342. if ( skip == 0 )
  343. {
  344. #if defined( KWSYS_GLOB_SUPPORT_NETWORK_PATHS )
  345. // Handle network paths
  346. if ( expr[0] == '/' && expr[1] == '/' )
  347. {
  348. int cnt = 0;
  349. for ( cc = 2; cc < expr.size(); cc ++ )
  350. {
  351. if ( expr[cc] == '/' )
  352. {
  353. cnt ++;
  354. if ( cnt == 2 )
  355. {
  356. break;
  357. }
  358. }
  359. }
  360. skip = int(cc + 1);
  361. }
  362. else
  363. #endif
  364. // Handle drive letters on Windows
  365. if ( expr[1] == ':' && expr[0] != '/' )
  366. {
  367. skip = 2;
  368. }
  369. }
  370. if ( skip > 0 )
  371. {
  372. expr = expr.substr(skip);
  373. }
  374. cexpr = "";
  375. for ( cc = 0; cc < expr.size(); cc ++ )
  376. {
  377. int ch = expr[cc];
  378. if ( ch == '/' )
  379. {
  380. if ( cexpr.size() > 0 )
  381. {
  382. this->AddExpression(cexpr.c_str());
  383. }
  384. cexpr = "";
  385. }
  386. else
  387. {
  388. cexpr.append(1, static_cast<char>(ch));
  389. }
  390. }
  391. if ( cexpr.size() > 0 )
  392. {
  393. this->AddExpression(cexpr.c_str());
  394. }
  395. // Handle network paths
  396. if ( skip > 0 )
  397. {
  398. this->ProcessDirectory(0, fexpr.substr(0, skip) + "/",
  399. true);
  400. }
  401. else
  402. {
  403. this->ProcessDirectory(0, "/", true);
  404. }
  405. return true;
  406. }
  407. //----------------------------------------------------------------------------
  408. void Glob::AddExpression(const char* expr)
  409. {
  410. this->Internals->Expressions.push_back(
  411. kwsys::RegularExpression(
  412. this->PatternToRegex(expr).c_str()));
  413. }
  414. //----------------------------------------------------------------------------
  415. void Glob::SetRelative(const char* dir)
  416. {
  417. if ( !dir )
  418. {
  419. this->Relative = "";
  420. return;
  421. }
  422. this->Relative = dir;
  423. }
  424. //----------------------------------------------------------------------------
  425. const char* Glob::GetRelative()
  426. {
  427. if ( this->Relative.empty() )
  428. {
  429. return 0;
  430. }
  431. return this->Relative.c_str();
  432. }
  433. //----------------------------------------------------------------------------
  434. void Glob::AddFile(kwsys_stl::vector<kwsys_stl::string>& files, const char* file)
  435. {
  436. if ( !this->Relative.empty() )
  437. {
  438. files.push_back(kwsys::SystemTools::RelativePath(this->Relative.c_str(), file));
  439. }
  440. else
  441. {
  442. files.push_back(file);
  443. }
  444. }
  445. } // namespace KWSYS_NAMESPACE