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;
68 //----------------------------------------------------------------------------
71 delete this->Internals
;
74 //----------------------------------------------------------------------------
75 kwsys_stl::vector
<kwsys_stl::string
>& Glob::GetFiles()
77 return this->Internals
->Files
;
80 //----------------------------------------------------------------------------
81 kwsys_stl::string
Glob::PatternToRegex(const kwsys_stl::string
& pattern
,
82 bool require_whole_string
)
84 // Incrementally build the regular expression from the pattern.
85 kwsys_stl::string regex
= require_whole_string
? "^" : "";
86 kwsys_stl::string::const_iterator pattern_first
= pattern
.begin();
87 kwsys_stl::string::const_iterator pattern_last
= pattern
.end();
88 for(kwsys_stl::string::const_iterator i
= pattern_first
;
89 i
!= pattern_last
; ++i
)
94 // A '*' (not between brackets) matches any string.
95 // We modify this to not match slashes since the orignal glob
96 // pattern documentation was meant for matching file name
97 // components separated by slashes.
102 // A '?' (not between brackets) matches any single character.
103 // We modify this to not match slashes since the orignal glob
104 // pattern documentation was meant for matching file name
105 // components separated by slashes.
110 // Parse out the bracket expression. It begins just after the
111 // opening character.
112 kwsys_stl::string::const_iterator bracket_first
= i
+1;
113 kwsys_stl::string::const_iterator bracket_last
= bracket_first
;
115 // The first character may be complementation '!' or '^'.
116 if(bracket_last
!= pattern_last
&&
117 (*bracket_last
== '!' || *bracket_last
== '^'))
122 // If the next character is a ']' it is included in the brackets
123 // because the bracket string may not be empty.
124 if(bracket_last
!= pattern_last
&& *bracket_last
== ']')
129 // Search for the closing ']'.
130 while(bracket_last
!= pattern_last
&& *bracket_last
!= ']')
135 // Check whether we have a complete bracket string.
136 if(bracket_last
== pattern_last
)
138 // The bracket string did not end, so it was opened simply by
139 // a '[' that is supposed to be matched literally.
144 // Convert the bracket string to its regex equivalent.
145 kwsys_stl::string::const_iterator k
= bracket_first
;
147 // Open the regex block.
150 // A regex range complement uses '^' instead of '!'.
151 if(k
!= bracket_last
&& *k
== '!')
157 // Convert the remaining characters.
158 for(; k
!= bracket_last
; ++k
)
160 // Backslashes must be escaped.
166 // Store this character.
170 // Close the regex block.
173 // Jump to the end of the bracket string.
179 // A single character matches itself.
181 if(!(('a' <= ch
&& ch
<= 'z') ||
182 ('A' <= ch
&& ch
<= 'Z') ||
183 ('0' <= ch
&& ch
<= '9')))
185 // Escape the non-alphanumeric character.
188 #if defined(KWSYS_GLOB_CASE_INDEPENDENT)
191 // On case-insensitive systems file names are converted to lower
192 // case before matching.
197 // Store the character.
198 regex
.append(1, static_cast<char>(ch
));
202 if(require_whole_string
)
209 //----------------------------------------------------------------------------
210 void Glob::RecurseDirectory(kwsys_stl::string::size_type start
,
211 const kwsys_stl::string
& dir
, bool dir_only
)
214 if ( !d
.Load(dir
.c_str()) )
219 kwsys_stl::string fullname
;
220 kwsys_stl::string realname
;
221 kwsys_stl::string fname
;
222 for ( cc
= 0; cc
< d
.GetNumberOfFiles(); cc
++ )
224 fname
= d
.GetFile(cc
);
225 if ( strcmp(fname
.c_str(), ".") == 0 ||
226 strcmp(fname
.c_str(), "..") == 0 )
233 realname
= dir
+ fname
;
237 realname
= dir
+ "/" + fname
;
240 #if defined( KWSYS_GLOB_CASE_INDEPENDENT )
241 // On Windows and apple, no difference between lower and upper case
242 fname
= kwsys::SystemTools::LowerCase(fname
);
247 fullname
= dir
+ fname
;
251 fullname
= dir
+ "/" + fname
;
254 if ( !dir_only
|| !kwsys::SystemTools::FileIsDirectory(realname
.c_str()) )
256 if ( (this->Internals
->Expressions
.size() > 0) &&
257 this->Internals
->Expressions
[
258 this->Internals
->Expressions
.size()-1].find(fname
.c_str()) )
260 this->AddFile(this->Internals
->Files
, realname
.c_str());
263 if ( kwsys::SystemTools::FileIsDirectory(realname
.c_str()) )
265 this->RecurseDirectory(start
+1, realname
, dir_only
);
270 //----------------------------------------------------------------------------
271 void Glob::ProcessDirectory(kwsys_stl::string::size_type start
,
272 const kwsys_stl::string
& dir
, bool dir_only
)
274 //kwsys_ios::cout << "ProcessDirectory: " << dir << kwsys_ios::endl;
275 bool last
= ( start
== this->Internals
->Expressions
.size()-1 );
276 if ( last
&& this->Recurse
)
278 this->RecurseDirectory(start
, dir
, dir_only
);
282 if ( start
>= this->Internals
->Expressions
.size() )
288 if ( !d
.Load(dir
.c_str()) )
293 kwsys_stl::string fullname
;
294 kwsys_stl::string realname
;
295 kwsys_stl::string fname
;
296 for ( cc
= 0; cc
< d
.GetNumberOfFiles(); cc
++ )
298 fname
= d
.GetFile(cc
);
299 if ( strcmp(fname
.c_str(), ".") == 0 ||
300 strcmp(fname
.c_str(), "..") == 0 )
307 realname
= dir
+ fname
;
311 realname
= dir
+ "/" + fname
;
314 #if defined(KWSYS_GLOB_CASE_INDEPENDENT)
315 // On case-insensitive file systems convert to lower case for matching.
316 fname
= kwsys::SystemTools::LowerCase(fname
);
321 fullname
= dir
+ fname
;
325 fullname
= dir
+ "/" + fname
;
328 //kwsys_ios::cout << "Look at file: " << fname << kwsys_ios::endl;
329 //kwsys_ios::cout << "Match: "
330 // << this->Internals->TextExpressions[start].c_str() << kwsys_ios::endl;
331 //kwsys_ios::cout << "Full name: " << fullname << kwsys_ios::endl;
333 if ( (!dir_only
|| !last
) &&
334 !kwsys::SystemTools::FileIsDirectory(realname
.c_str()) )
339 if ( this->Internals
->Expressions
[start
].find(fname
.c_str()) )
343 this->AddFile(this->Internals
->Files
, realname
.c_str());
347 this->ProcessDirectory(start
+1, realname
+ "/", dir_only
);
353 //----------------------------------------------------------------------------
354 bool Glob::FindFiles(const kwsys_stl::string
& inexpr
)
356 kwsys_stl::string cexpr
;
357 kwsys_stl::string::size_type cc
;
358 kwsys_stl::string expr
= inexpr
;
360 this->Internals
->Expressions
.clear();
361 this->Internals
->Files
.clear();
363 if ( !kwsys::SystemTools::FileIsFullPath(expr
.c_str()) )
365 expr
= kwsys::SystemTools::GetCurrentWorkingDirectory();
366 expr
+= "/" + inexpr
;
368 kwsys_stl::string fexpr
= expr
;
372 for ( cc
= 0; cc
< expr
.size(); cc
++ )
374 if ( cc
> 0 && expr
[cc
] == '/' && expr
[cc
-1] != '\\' )
376 last_slash
= static_cast<int>(cc
);
379 (expr
[cc
] == '[' || expr
[cc
] == '?' || expr
[cc
] == '*') &&
385 if ( last_slash
> 0 )
387 //kwsys_ios::cout << "I can skip: " << fexpr.substr(0, last_slash)
388 //<< kwsys_ios::endl;
393 #if defined( KWSYS_GLOB_SUPPORT_NETWORK_PATHS )
394 // Handle network paths
395 if ( expr
[0] == '/' && expr
[1] == '/' )
398 for ( cc
= 2; cc
< expr
.size(); cc
++ )
400 if ( expr
[cc
] == '/' )
413 // Handle drive letters on Windows
414 if ( expr
[1] == ':' && expr
[0] != '/' )
422 expr
= expr
.substr(skip
);
426 for ( cc
= 0; cc
< expr
.size(); cc
++ )
431 if ( cexpr
.size() > 0 )
433 this->AddExpression(cexpr
.c_str());
439 cexpr
.append(1, static_cast<char>(ch
));
442 if ( cexpr
.size() > 0 )
444 this->AddExpression(cexpr
.c_str());
447 // Handle network paths
450 this->ProcessDirectory(0, fexpr
.substr(0, skip
) + "/",
455 this->ProcessDirectory(0, "/", true);
460 //----------------------------------------------------------------------------
461 void Glob::AddExpression(const char* expr
)
463 this->Internals
->Expressions
.push_back(
464 kwsys::RegularExpression(
465 this->PatternToRegex(expr
).c_str()));
468 //----------------------------------------------------------------------------
469 void Glob::SetRelative(const char* dir
)
476 this->Relative
= dir
;
479 //----------------------------------------------------------------------------
480 const char* Glob::GetRelative()
482 if ( this->Relative
.empty() )
486 return this->Relative
.c_str();
489 //----------------------------------------------------------------------------
490 void Glob::AddFile(kwsys_stl::vector
<kwsys_stl::string
>& files
, const char* file
)
492 if ( !this->Relative
.empty() )
494 files
.push_back(kwsys::SystemTools::RelativePath(this->Relative
.c_str(), file
));
498 files
.push_back(file
);
502 } // namespace KWSYS_NAMESPACE