Imported Upstream version 2.8.9
[platform/upstream/cmake.git] / Source / kwsys / Glob.cxx
1 /*============================================================================
2   KWSys - Kitware System Library
3   Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
4
5   Distributed under the OSI-approved BSD License (the "License");
6   see accompanying file Copyright.txt for details.
7
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)
14
15 #include KWSYS_HEADER(Configure.hxx)
16
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)
22
23 // Work-around CMake dependency scanning limitation.  This must
24 // duplicate the above list of headers.
25 #if 0
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"
33 #endif
34
35 #include <ctype.h>
36 #include <stdio.h>
37 #include <string.h>
38 namespace KWSYS_NAMESPACE
39 {
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
43 #endif
44
45 #if defined(_WIN32) || defined(__CYGWIN__)
46 // Handle network paths
47 # define KWSYS_GLOB_SUPPORT_NETWORK_PATHS
48 #endif
49
50 //----------------------------------------------------------------------------
51 class GlobInternals
52 {
53 public:
54   kwsys_stl::vector<kwsys_stl::string> Files;
55   kwsys_stl::vector<kwsys::RegularExpression> Expressions;
56 };
57
58 //----------------------------------------------------------------------------
59 Glob::Glob()
60 {
61   this->Internals = new GlobInternals;
62   this->Recurse = false;
63   this->Relative = "";
64
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;
69 }
70
71 //----------------------------------------------------------------------------
72 Glob::~Glob()
73 {
74   delete this->Internals;
75 }
76
77 //----------------------------------------------------------------------------
78 kwsys_stl::vector<kwsys_stl::string>& Glob::GetFiles()
79 {
80   return this->Internals->Files;
81 }
82
83 //----------------------------------------------------------------------------
84 kwsys_stl::string Glob::PatternToRegex(const kwsys_stl::string& pattern,
85                                        bool require_whole_string,
86                                        bool preserve_case)
87 {
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)
94     {
95     int c = *i;
96     if(c == '*')
97       {
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.
102       regex += "[^/]*";
103       }
104     else if(c == '?')
105       {
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.
110       regex += "[^/]";
111       }
112     else if(c == '[')
113       {
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;
118
119       // The first character may be complementation '!' or '^'.
120       if(bracket_last != pattern_last &&
121          (*bracket_last == '!' || *bracket_last == '^'))
122         {
123         ++bracket_last;
124         }
125
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 == ']')
129         {
130         ++bracket_last;
131         }
132
133       // Search for the closing ']'.
134       while(bracket_last != pattern_last && *bracket_last != ']')
135         {
136         ++bracket_last;
137         }
138
139       // Check whether we have a complete bracket string.
140       if(bracket_last == pattern_last)
141         {
142         // The bracket string did not end, so it was opened simply by
143         // a '[' that is supposed to be matched literally.
144         regex += "\\[";
145         }
146       else
147         {
148         // Convert the bracket string to its regex equivalent.
149         kwsys_stl::string::const_iterator k = bracket_first;
150
151         // Open the regex block.
152         regex += "[";
153
154         // A regex range complement uses '^' instead of '!'.
155         if(k != bracket_last && *k == '!')
156           {
157           regex += "^";
158           ++k;
159           }
160
161         // Convert the remaining characters.
162         for(; k != bracket_last; ++k)
163           {
164           // Backslashes must be escaped.
165           if(*k == '\\')
166             {
167             regex += "\\";
168             }
169
170           // Store this character.
171           regex += *k;
172           }
173
174         // Close the regex block.
175         regex += "]";
176
177         // Jump to the end of the bracket string.
178         i = bracket_last;
179         }
180       }
181     else
182       {
183       // A single character matches itself.
184       int ch = c;
185       if(!(('a' <= ch && ch <= 'z') ||
186            ('A' <= ch && ch <= 'Z') ||
187            ('0' <= ch && ch <= '9')))
188         {
189         // Escape the non-alphanumeric character.
190         regex += "\\";
191         }
192 #if defined(KWSYS_GLOB_CASE_INDEPENDENT)
193       else
194         {
195         // On case-insensitive systems file names are converted to lower
196         // case before matching.
197         if(!preserve_case)
198           {
199           ch = tolower(ch);
200           }
201         }
202 #endif
203       (void)preserve_case;
204       // Store the character.
205       regex.append(1, static_cast<char>(ch));
206       }
207     }
208
209   if(require_whole_string)
210     {
211     regex += "$";
212     }
213   return regex;
214 }
215
216 //----------------------------------------------------------------------------
217 void Glob::RecurseDirectory(kwsys_stl::string::size_type start,
218   const kwsys_stl::string& dir)
219 {
220   kwsys::Directory d;
221   if ( !d.Load(dir.c_str()) )
222     {
223     return;
224     }
225   unsigned long cc;
226   kwsys_stl::string fullname;
227   kwsys_stl::string realname;
228   kwsys_stl::string fname;
229   for ( cc = 0; cc < d.GetNumberOfFiles(); cc ++ )
230     {
231     fname = d.GetFile(cc);
232     if ( strcmp(fname.c_str(), ".") == 0 ||
233       strcmp(fname.c_str(), "..") == 0  )
234       {
235       continue;
236       }
237
238     if ( start == 0 )
239       {
240       realname = dir + fname;
241       }
242     else
243       {
244       realname = dir + "/" + fname;
245       }
246
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);
250 #endif
251
252     if ( start == 0 )
253       {
254       fullname = dir + fname;
255       }
256     else
257       {
258       fullname = dir + "/" + fname;
259       }
260
261     bool isDir = kwsys::SystemTools::FileIsDirectory(realname.c_str());
262     bool isSymLink = kwsys::SystemTools::FileIsSymlink(realname.c_str());
263
264     if ( isDir && (!isSymLink || this->RecurseThroughSymlinks) )
265       {
266       if (isSymLink)
267         {
268         ++this->FollowedSymlinkCount;
269         }
270       this->RecurseDirectory(start+1, realname);
271       }
272     else
273       {
274       if ( (this->Internals->Expressions.size() > 0) &&
275            this->Internals->Expressions[
276              this->Internals->Expressions.size()-1].find(fname.c_str()) )
277         {
278         this->AddFile(this->Internals->Files, realname.c_str());
279         }
280       }
281     }
282 }
283
284 //----------------------------------------------------------------------------
285 void Glob::ProcessDirectory(kwsys_stl::string::size_type start,
286   const kwsys_stl::string& dir)
287 {
288   //kwsys_ios::cout << "ProcessDirectory: " << dir << kwsys_ios::endl;
289   bool last = ( start == this->Internals->Expressions.size()-1 );
290   if ( last && this->Recurse )
291     {
292     this->RecurseDirectory(start, dir);
293     return;
294     }
295
296   if ( start >= this->Internals->Expressions.size() )
297     {
298     return;
299     }
300
301   kwsys::Directory d;
302   if ( !d.Load(dir.c_str()) )
303     {
304     return;
305     }
306   unsigned long cc;
307   kwsys_stl::string fullname;
308   kwsys_stl::string realname;
309   kwsys_stl::string fname;
310   for ( cc = 0; cc < d.GetNumberOfFiles(); cc ++ )
311     {
312     fname = d.GetFile(cc);
313     if ( strcmp(fname.c_str(), ".") == 0 ||
314       strcmp(fname.c_str(), "..") == 0  )
315       {
316       continue;
317       }
318
319     if ( start == 0 )
320       {
321       realname = dir + fname;
322       }
323     else
324       {
325       realname = dir + "/" + fname;
326       }
327
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);
331 #endif
332
333     if ( start == 0 )
334       {
335       fullname = dir + fname;
336       }
337     else
338       {
339       fullname = dir + "/" + fname;
340       }
341
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;
346
347     if ( !last &&
348       !kwsys::SystemTools::FileIsDirectory(realname.c_str()) )
349       {
350       continue;
351       }
352
353     if ( this->Internals->Expressions[start].find(fname.c_str()) )
354       {
355       if ( last )
356         {
357         this->AddFile(this->Internals->Files, realname.c_str());
358         }
359       else
360         {
361         this->ProcessDirectory(start+1, realname + "/");
362         }
363       }
364     }
365 }
366
367 //----------------------------------------------------------------------------
368 bool Glob::FindFiles(const kwsys_stl::string& inexpr)
369 {
370   kwsys_stl::string cexpr;
371   kwsys_stl::string::size_type cc;
372   kwsys_stl::string expr = inexpr;
373
374   this->Internals->Expressions.clear();
375   this->Internals->Files.clear();
376
377   if ( !kwsys::SystemTools::FileIsFullPath(expr.c_str()) )
378     {
379     expr = kwsys::SystemTools::GetCurrentWorkingDirectory();
380     expr += "/" + inexpr;
381     }
382   kwsys_stl::string fexpr = expr;
383
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 ++ )
387     {
388     if ( cc > 0 && expr[cc] == '/' && expr[cc-1] != '\\' )
389       {
390       last_slash = cc;
391       }
392     if ( cc > 0 &&
393       (expr[cc] == '[' || expr[cc] == '?' || expr[cc] == '*') &&
394       expr[cc-1] != '\\' )
395       {
396       break;
397       }
398     }
399   if ( last_slash > 0 )
400     {
401     //kwsys_ios::cout << "I can skip: " << fexpr.substr(0, last_slash)
402     //<< kwsys_ios::endl;
403     skip = last_slash;
404     }
405   if ( skip == 0 )
406     {
407 #if defined( KWSYS_GLOB_SUPPORT_NETWORK_PATHS )
408     // Handle network paths
409     if ( expr[0] == '/' && expr[1] == '/' )
410       {
411       int cnt = 0;
412       for ( cc = 2; cc < expr.size(); cc ++ )
413         {
414         if ( expr[cc] == '/' )
415           {
416           cnt ++;
417           if ( cnt == 2 )
418             {
419             break;
420             }
421           }
422         }
423       skip = int(cc + 1);
424       }
425     else
426 #endif
427       // Handle drive letters on Windows
428       if ( expr[1] == ':' && expr[0] != '/' )
429         {
430         skip = 2;
431         }
432     }
433
434   if ( skip > 0 )
435     {
436     expr = expr.substr(skip);
437     }
438
439   cexpr = "";
440   for ( cc = 0; cc < expr.size(); cc ++ )
441     {
442     int ch = expr[cc];
443     if ( ch == '/' )
444       {
445       if ( cexpr.size() > 0 )
446         {
447         this->AddExpression(cexpr.c_str());
448         }
449       cexpr = "";
450       }
451     else
452       {
453       cexpr.append(1, static_cast<char>(ch));
454       }
455     }
456   if ( cexpr.size() > 0 )
457     {
458     this->AddExpression(cexpr.c_str());
459     }
460
461   // Handle network paths
462   if ( skip > 0 )
463     {
464     this->ProcessDirectory(0, fexpr.substr(0, skip) + "/");
465     }
466   else
467     {
468     this->ProcessDirectory(0, "/");
469     }
470   return true;
471 }
472
473 //----------------------------------------------------------------------------
474 void Glob::AddExpression(const char* expr)
475 {
476   this->Internals->Expressions.push_back(
477     kwsys::RegularExpression(
478       this->PatternToRegex(expr).c_str()));
479 }
480
481 //----------------------------------------------------------------------------
482 void Glob::SetRelative(const char* dir)
483 {
484   if ( !dir )
485     {
486     this->Relative = "";
487     return;
488     }
489   this->Relative = dir;
490 }
491
492 //----------------------------------------------------------------------------
493 const char* Glob::GetRelative()
494 {
495   if ( this->Relative.empty() )
496     {
497     return 0;
498     }
499   return this->Relative.c_str();
500 }
501
502 //----------------------------------------------------------------------------
503 void Glob::AddFile(kwsys_stl::vector<kwsys_stl::string>& files, const char* file)
504 {
505   if ( !this->Relative.empty() )
506     {
507     files.push_back(kwsys::SystemTools::RelativePath(this->Relative.c_str(), file));
508     }
509   else
510     {
511     files.push_back(file);
512     }
513 }
514
515 } // namespace KWSYS_NAMESPACE
516