1 /*============================================================================
2 KWSys - Kitware System Library
3 Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
5 Distributed under the OSI-approved BSD License (the "License");
6 see accompanying file Copyright.txt for details.
8 This software is distributed WITHOUT ANY WARRANTY; without even the
9 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 See the License for more information.
11 ============================================================================*/
12 #include "kwsysPrivate.h"
13 #include KWSYS_HEADER(Glob.hxx)
15 #include KWSYS_HEADER(Configure.hxx)
17 #include KWSYS_HEADER(RegularExpression.hxx)
18 #include KWSYS_HEADER(SystemTools.hxx)
19 #include KWSYS_HEADER(Directory.hxx)
20 #include KWSYS_HEADER(stl/string)
21 #include KWSYS_HEADER(stl/vector)
23 // Work-around CMake dependency scanning limitation. This must
24 // duplicate the above list of headers.
26 # include "Glob.hxx.in"
27 # include "Directory.hxx.in"
28 # include "Configure.hxx.in"
29 # include "RegularExpression.hxx.in"
30 # include "SystemTools.hxx.in"
31 # include "kwsys_stl.hxx.in"
32 # include "kwsys_stl_string.hxx.in"
38 namespace KWSYS_NAMESPACE
40 #if defined(_WIN32) || defined(__APPLE__) || defined(__CYGWIN__)
41 // On Windows and apple, no difference between lower and upper case
42 # define KWSYS_GLOB_CASE_INDEPENDENT
45 #if defined(_WIN32) || defined(__CYGWIN__)
46 // Handle network paths
47 # define KWSYS_GLOB_SUPPORT_NETWORK_PATHS
50 //----------------------------------------------------------------------------
54 kwsys_stl::vector<kwsys_stl::string> Files;
55 kwsys_stl::vector<kwsys::RegularExpression> Expressions;
58 //----------------------------------------------------------------------------
61 this->Internals = new GlobInternals;
62 this->Recurse = false;
65 this->RecurseThroughSymlinks = true;
66 // RecurseThroughSymlinks is true by default for backwards compatibility,
67 // not because it's a good idea...
68 this->FollowedSymlinkCount = 0;
71 //----------------------------------------------------------------------------
74 delete this->Internals;
77 //----------------------------------------------------------------------------
78 kwsys_stl::vector<kwsys_stl::string>& Glob::GetFiles()
80 return this->Internals->Files;
83 //----------------------------------------------------------------------------
84 kwsys_stl::string Glob::PatternToRegex(const kwsys_stl::string& pattern,
85 bool require_whole_string,
88 // Incrementally build the regular expression from the pattern.
89 kwsys_stl::string regex = require_whole_string? "^" : "";
90 kwsys_stl::string::const_iterator pattern_first = pattern.begin();
91 kwsys_stl::string::const_iterator pattern_last = pattern.end();
92 for(kwsys_stl::string::const_iterator i = pattern_first;
93 i != pattern_last; ++i)
98 // A '*' (not between brackets) matches any string.
99 // We modify this to not match slashes since the orignal glob
100 // pattern documentation was meant for matching file name
101 // components separated by slashes.
106 // A '?' (not between brackets) matches any single character.
107 // We modify this to not match slashes since the orignal glob
108 // pattern documentation was meant for matching file name
109 // components separated by slashes.
114 // Parse out the bracket expression. It begins just after the
115 // opening character.
116 kwsys_stl::string::const_iterator bracket_first = i+1;
117 kwsys_stl::string::const_iterator bracket_last = bracket_first;
119 // The first character may be complementation '!' or '^'.
120 if(bracket_last != pattern_last &&
121 (*bracket_last == '!' || *bracket_last == '^'))
126 // If the next character is a ']' it is included in the brackets
127 // because the bracket string may not be empty.
128 if(bracket_last != pattern_last && *bracket_last == ']')
133 // Search for the closing ']'.
134 while(bracket_last != pattern_last && *bracket_last != ']')
139 // Check whether we have a complete bracket string.
140 if(bracket_last == pattern_last)
142 // The bracket string did not end, so it was opened simply by
143 // a '[' that is supposed to be matched literally.
148 // Convert the bracket string to its regex equivalent.
149 kwsys_stl::string::const_iterator k = bracket_first;
151 // Open the regex block.
154 // A regex range complement uses '^' instead of '!'.
155 if(k != bracket_last && *k == '!')
161 // Convert the remaining characters.
162 for(; k != bracket_last; ++k)
164 // Backslashes must be escaped.
170 // Store this character.
174 // Close the regex block.
177 // Jump to the end of the bracket string.
183 // A single character matches itself.
185 if(!(('a' <= ch && ch <= 'z') ||
186 ('A' <= ch && ch <= 'Z') ||
187 ('0' <= ch && ch <= '9')))
189 // Escape the non-alphanumeric character.
192 #if defined(KWSYS_GLOB_CASE_INDEPENDENT)
195 // On case-insensitive systems file names are converted to lower
196 // case before matching.
204 // Store the character.
205 regex.append(1, static_cast<char>(ch));
209 if(require_whole_string)
216 //----------------------------------------------------------------------------
217 void Glob::RecurseDirectory(kwsys_stl::string::size_type start,
218 const kwsys_stl::string& dir)
221 if ( !d.Load(dir.c_str()) )
226 kwsys_stl::string fullname;
227 kwsys_stl::string realname;
228 kwsys_stl::string fname;
229 for ( cc = 0; cc < d.GetNumberOfFiles(); cc ++ )
231 fname = d.GetFile(cc);
232 if ( strcmp(fname.c_str(), ".") == 0 ||
233 strcmp(fname.c_str(), "..") == 0 )
240 realname = dir + fname;
244 realname = dir + "/" + fname;
247 #if defined( KWSYS_GLOB_CASE_INDEPENDENT )
248 // On Windows and apple, no difference between lower and upper case
249 fname = kwsys::SystemTools::LowerCase(fname);
254 fullname = dir + fname;
258 fullname = dir + "/" + fname;
261 bool isDir = kwsys::SystemTools::FileIsDirectory(realname.c_str());
262 bool isSymLink = kwsys::SystemTools::FileIsSymlink(realname.c_str());
264 if ( isDir && (!isSymLink || this->RecurseThroughSymlinks) )
268 ++this->FollowedSymlinkCount;
270 this->RecurseDirectory(start+1, realname);
274 if ( (this->Internals->Expressions.size() > 0) &&
275 this->Internals->Expressions[
276 this->Internals->Expressions.size()-1].find(fname.c_str()) )
278 this->AddFile(this->Internals->Files, realname.c_str());
284 //----------------------------------------------------------------------------
285 void Glob::ProcessDirectory(kwsys_stl::string::size_type start,
286 const kwsys_stl::string& dir)
288 //kwsys_ios::cout << "ProcessDirectory: " << dir << kwsys_ios::endl;
289 bool last = ( start == this->Internals->Expressions.size()-1 );
290 if ( last && this->Recurse )
292 this->RecurseDirectory(start, dir);
296 if ( start >= this->Internals->Expressions.size() )
302 if ( !d.Load(dir.c_str()) )
307 kwsys_stl::string fullname;
308 kwsys_stl::string realname;
309 kwsys_stl::string fname;
310 for ( cc = 0; cc < d.GetNumberOfFiles(); cc ++ )
312 fname = d.GetFile(cc);
313 if ( strcmp(fname.c_str(), ".") == 0 ||
314 strcmp(fname.c_str(), "..") == 0 )
321 realname = dir + fname;
325 realname = dir + "/" + fname;
328 #if defined(KWSYS_GLOB_CASE_INDEPENDENT)
329 // On case-insensitive file systems convert to lower case for matching.
330 fname = kwsys::SystemTools::LowerCase(fname);
335 fullname = dir + fname;
339 fullname = dir + "/" + fname;
342 //kwsys_ios::cout << "Look at file: " << fname << kwsys_ios::endl;
343 //kwsys_ios::cout << "Match: "
344 // << this->Internals->TextExpressions[start].c_str() << kwsys_ios::endl;
345 //kwsys_ios::cout << "Full name: " << fullname << kwsys_ios::endl;
348 !kwsys::SystemTools::FileIsDirectory(realname.c_str()) )
353 if ( this->Internals->Expressions[start].find(fname.c_str()) )
357 this->AddFile(this->Internals->Files, realname.c_str());
361 this->ProcessDirectory(start+1, realname + "/");
367 //----------------------------------------------------------------------------
368 bool Glob::FindFiles(const kwsys_stl::string& inexpr)
370 kwsys_stl::string cexpr;
371 kwsys_stl::string::size_type cc;
372 kwsys_stl::string expr = inexpr;
374 this->Internals->Expressions.clear();
375 this->Internals->Files.clear();
377 if ( !kwsys::SystemTools::FileIsFullPath(expr.c_str()) )
379 expr = kwsys::SystemTools::GetCurrentWorkingDirectory();
380 expr += "/" + inexpr;
382 kwsys_stl::string fexpr = expr;
384 kwsys_stl::string::size_type skip = 0;
385 kwsys_stl::string::size_type last_slash = 0;
386 for ( cc = 0; cc < expr.size(); cc ++ )
388 if ( cc > 0 && expr[cc] == '/' && expr[cc-1] != '\\' )
393 (expr[cc] == '[' || expr[cc] == '?' || expr[cc] == '*') &&
399 if ( last_slash > 0 )
401 //kwsys_ios::cout << "I can skip: " << fexpr.substr(0, last_slash)
402 // << kwsys_ios::endl;
407 #if defined( KWSYS_GLOB_SUPPORT_NETWORK_PATHS )
408 // Handle network paths
409 if ( expr[0] == '/' && expr[1] == '/' )
412 for ( cc = 2; cc < expr.size(); cc ++ )
414 if ( expr[cc] == '/' )
427 // Handle drive letters on Windows
428 if ( expr[1] == ':' && expr[0] != '/' )
436 expr = expr.substr(skip);
440 for ( cc = 0; cc < expr.size(); cc ++ )
445 if ( cexpr.size() > 0 )
447 this->AddExpression(cexpr.c_str());
453 cexpr.append(1, static_cast<char>(ch));
456 if ( cexpr.size() > 0 )
458 this->AddExpression(cexpr.c_str());
461 // Handle network paths
464 this->ProcessDirectory(0, fexpr.substr(0, skip) + "/");
468 this->ProcessDirectory(0, "/");
473 //----------------------------------------------------------------------------
474 void Glob::AddExpression(const char* expr)
476 this->Internals->Expressions.push_back(
477 kwsys::RegularExpression(
478 this->PatternToRegex(expr).c_str()));
481 //----------------------------------------------------------------------------
482 void Glob::SetRelative(const char* dir)
489 this->Relative = dir;
492 //----------------------------------------------------------------------------
493 const char* Glob::GetRelative()
495 if ( this->Relative.empty() )
499 return this->Relative.c_str();
502 //----------------------------------------------------------------------------
503 void Glob::AddFile(kwsys_stl::vector<kwsys_stl::string>& files, const char* file)
505 if ( !this->Relative.empty() )
507 files.push_back(kwsys::SystemTools::RelativePath(this->Relative.c_str(), file));
511 files.push_back(file);
515 } // namespace KWSYS_NAMESPACE