1 /*=========================================================================
3 Program: KWSys - Kitware System Library
4 Module: $RCSfile: Glob.cxx,v $
6 Copyright (c) Kitware, Inc., Insight Consortium. All rights reserved.
7 See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 This software is distributed WITHOUT ANY WARRANTY; without even
10 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11 PURPOSE. See the above copyright notices for more information.
13 =========================================================================*/
14 #include "kwsysPrivate.h"
15 #include KWSYS_HEADER(Glob.hxx)
17 #include KWSYS_HEADER(Configure.hxx)
19 #include KWSYS_HEADER(RegularExpression.hxx)
20 #include KWSYS_HEADER(SystemTools.hxx)
21 #include KWSYS_HEADER(Directory.hxx)
22 #include KWSYS_HEADER(stl/string)
23 #include KWSYS_HEADER(stl/vector)
25 // Work-around CMake dependency scanning limitation. This must
26 // duplicate the above list of headers.
28 # include "Glob.hxx.in"
29 # include "Directory.hxx.in"
30 # include "Configure.hxx.in"
31 # include "RegularExpression.hxx.in"
32 # include "SystemTools.hxx.in"
33 # include "kwsys_stl.hxx.in"
34 # include "kwsys_stl_string.hxx.in"
40 namespace KWSYS_NAMESPACE
42 #if defined(_WIN32) || defined(__APPLE__) || defined(__CYGWIN__)
43 // On Windows and apple, no difference between lower and upper case
44 # define KWSYS_GLOB_CASE_INDEPENDENT
47 #if defined(_WIN32) || defined(__CYGWIN__)
48 // Handle network paths
49 # define KWSYS_GLOB_SUPPORT_NETWORK_PATHS
52 //----------------------------------------------------------------------------
56 kwsys_stl::vector
<kwsys_stl::string
> Files
;
57 kwsys_stl::vector
<kwsys::RegularExpression
> Expressions
;
60 //----------------------------------------------------------------------------
63 this->Internals
= new GlobInternals
;
64 this->Recurse
= false;
67 this->RecurseThroughSymlinks
= true;
68 // RecurseThroughSymlinks is true by default for backwards compatibility,
69 // not because it's a good idea...
70 this->FollowedSymlinkCount
= 0;
73 //----------------------------------------------------------------------------
76 delete this->Internals
;
79 //----------------------------------------------------------------------------
80 kwsys_stl::vector
<kwsys_stl::string
>& Glob::GetFiles()
82 return this->Internals
->Files
;
85 //----------------------------------------------------------------------------
86 kwsys_stl::string
Glob::PatternToRegex(const kwsys_stl::string
& pattern
,
87 bool require_whole_string
)
89 // Incrementally build the regular expression from the pattern.
90 kwsys_stl::string regex
= require_whole_string
? "^" : "";
91 kwsys_stl::string::const_iterator pattern_first
= pattern
.begin();
92 kwsys_stl::string::const_iterator pattern_last
= pattern
.end();
93 for(kwsys_stl::string::const_iterator i
= pattern_first
;
94 i
!= pattern_last
; ++i
)
99 // A '*' (not between brackets) matches any string.
100 // We modify this to not match slashes since the orignal glob
101 // pattern documentation was meant for matching file name
102 // components separated by slashes.
107 // A '?' (not between brackets) matches any single character.
108 // We modify this to not match slashes since the orignal glob
109 // pattern documentation was meant for matching file name
110 // components separated by slashes.
115 // Parse out the bracket expression. It begins just after the
116 // opening character.
117 kwsys_stl::string::const_iterator bracket_first
= i
+1;
118 kwsys_stl::string::const_iterator bracket_last
= bracket_first
;
120 // The first character may be complementation '!' or '^'.
121 if(bracket_last
!= pattern_last
&&
122 (*bracket_last
== '!' || *bracket_last
== '^'))
127 // If the next character is a ']' it is included in the brackets
128 // because the bracket string may not be empty.
129 if(bracket_last
!= pattern_last
&& *bracket_last
== ']')
134 // Search for the closing ']'.
135 while(bracket_last
!= pattern_last
&& *bracket_last
!= ']')
140 // Check whether we have a complete bracket string.
141 if(bracket_last
== pattern_last
)
143 // The bracket string did not end, so it was opened simply by
144 // a '[' that is supposed to be matched literally.
149 // Convert the bracket string to its regex equivalent.
150 kwsys_stl::string::const_iterator k
= bracket_first
;
152 // Open the regex block.
155 // A regex range complement uses '^' instead of '!'.
156 if(k
!= bracket_last
&& *k
== '!')
162 // Convert the remaining characters.
163 for(; k
!= bracket_last
; ++k
)
165 // Backslashes must be escaped.
171 // Store this character.
175 // Close the regex block.
178 // Jump to the end of the bracket string.
184 // A single character matches itself.
186 if(!(('a' <= ch
&& ch
<= 'z') ||
187 ('A' <= ch
&& ch
<= 'Z') ||
188 ('0' <= ch
&& ch
<= '9')))
190 // Escape the non-alphanumeric character.
193 #if defined(KWSYS_GLOB_CASE_INDEPENDENT)
196 // On case-insensitive systems file names are converted to lower
197 // case before matching.
202 // Store the character.
203 regex
.append(1, static_cast<char>(ch
));
207 if(require_whole_string
)
214 //----------------------------------------------------------------------------
215 void Glob::RecurseDirectory(kwsys_stl::string::size_type start
,
216 const kwsys_stl::string
& dir
, bool dir_only
)
219 if ( !d
.Load(dir
.c_str()) )
224 kwsys_stl::string fullname
;
225 kwsys_stl::string realname
;
226 kwsys_stl::string fname
;
227 for ( cc
= 0; cc
< d
.GetNumberOfFiles(); cc
++ )
229 fname
= d
.GetFile(cc
);
230 if ( strcmp(fname
.c_str(), ".") == 0 ||
231 strcmp(fname
.c_str(), "..") == 0 )
238 realname
= dir
+ fname
;
242 realname
= dir
+ "/" + fname
;
245 #if defined( KWSYS_GLOB_CASE_INDEPENDENT )
246 // On Windows and apple, no difference between lower and upper case
247 fname
= kwsys::SystemTools::LowerCase(fname
);
252 fullname
= dir
+ fname
;
256 fullname
= dir
+ "/" + fname
;
259 if ( !dir_only
|| !kwsys::SystemTools::FileIsDirectory(realname
.c_str()) )
261 if ( (this->Internals
->Expressions
.size() > 0) &&
262 this->Internals
->Expressions
[
263 this->Internals
->Expressions
.size()-1].find(fname
.c_str()) )
265 this->AddFile(this->Internals
->Files
, realname
.c_str());
268 if ( kwsys::SystemTools::FileIsDirectory(realname
.c_str()) )
270 bool isSymLink
= kwsys::SystemTools::FileIsSymlink(realname
.c_str());
271 if (!isSymLink
|| this->RecurseThroughSymlinks
)
275 ++this->FollowedSymlinkCount
;
277 this->RecurseDirectory(start
+1, realname
, dir_only
);
283 //----------------------------------------------------------------------------
284 void Glob::ProcessDirectory(kwsys_stl::string::size_type start
,
285 const kwsys_stl::string
& dir
, bool dir_only
)
287 //kwsys_ios::cout << "ProcessDirectory: " << dir << kwsys_ios::endl;
288 bool last
= ( start
== this->Internals
->Expressions
.size()-1 );
289 if ( last
&& this->Recurse
)
291 this->RecurseDirectory(start
, dir
, dir_only
);
295 if ( start
>= this->Internals
->Expressions
.size() )
301 if ( !d
.Load(dir
.c_str()) )
306 kwsys_stl::string fullname
;
307 kwsys_stl::string realname
;
308 kwsys_stl::string fname
;
309 for ( cc
= 0; cc
< d
.GetNumberOfFiles(); cc
++ )
311 fname
= d
.GetFile(cc
);
312 if ( strcmp(fname
.c_str(), ".") == 0 ||
313 strcmp(fname
.c_str(), "..") == 0 )
320 realname
= dir
+ fname
;
324 realname
= dir
+ "/" + fname
;
327 #if defined(KWSYS_GLOB_CASE_INDEPENDENT)
328 // On case-insensitive file systems convert to lower case for matching.
329 fname
= kwsys::SystemTools::LowerCase(fname
);
334 fullname
= dir
+ fname
;
338 fullname
= dir
+ "/" + fname
;
341 //kwsys_ios::cout << "Look at file: " << fname << kwsys_ios::endl;
342 //kwsys_ios::cout << "Match: "
343 // << this->Internals->TextExpressions[start].c_str() << kwsys_ios::endl;
344 //kwsys_ios::cout << "Full name: " << fullname << kwsys_ios::endl;
346 if ( (!dir_only
|| !last
) &&
347 !kwsys::SystemTools::FileIsDirectory(realname
.c_str()) )
352 if ( this->Internals
->Expressions
[start
].find(fname
.c_str()) )
356 this->AddFile(this->Internals
->Files
, realname
.c_str());
360 this->ProcessDirectory(start
+1, realname
+ "/", dir_only
);
366 //----------------------------------------------------------------------------
367 bool Glob::FindFiles(const kwsys_stl::string
& inexpr
)
369 kwsys_stl::string cexpr
;
370 kwsys_stl::string::size_type cc
;
371 kwsys_stl::string expr
= inexpr
;
373 this->Internals
->Expressions
.clear();
374 this->Internals
->Files
.clear();
376 if ( !kwsys::SystemTools::FileIsFullPath(expr
.c_str()) )
378 expr
= kwsys::SystemTools::GetCurrentWorkingDirectory();
379 expr
+= "/" + inexpr
;
381 kwsys_stl::string fexpr
= expr
;
383 kwsys_stl::string::size_type skip
= 0;
384 kwsys_stl::string::size_type last_slash
= 0;
385 for ( cc
= 0; cc
< expr
.size(); cc
++ )
387 if ( cc
> 0 && expr
[cc
] == '/' && expr
[cc
-1] != '\\' )
392 (expr
[cc
] == '[' || expr
[cc
] == '?' || expr
[cc
] == '*') &&
398 if ( last_slash
> 0 )
400 //kwsys_ios::cout << "I can skip: " << fexpr.substr(0, last_slash)
401 //<< kwsys_ios::endl;
406 #if defined( KWSYS_GLOB_SUPPORT_NETWORK_PATHS )
407 // Handle network paths
408 if ( expr
[0] == '/' && expr
[1] == '/' )
411 for ( cc
= 2; cc
< expr
.size(); cc
++ )
413 if ( expr
[cc
] == '/' )
426 // Handle drive letters on Windows
427 if ( expr
[1] == ':' && expr
[0] != '/' )
435 expr
= expr
.substr(skip
);
439 for ( cc
= 0; cc
< expr
.size(); cc
++ )
444 if ( cexpr
.size() > 0 )
446 this->AddExpression(cexpr
.c_str());
452 cexpr
.append(1, static_cast<char>(ch
));
455 if ( cexpr
.size() > 0 )
457 this->AddExpression(cexpr
.c_str());
460 // Handle network paths
463 this->ProcessDirectory(0, fexpr
.substr(0, skip
) + "/",
468 this->ProcessDirectory(0, "/", true);
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