Skip to content
Snippets Groups Projects
Glob.cxx 12.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
       file Copyright.txt or https://cmake.org/licensing#kwsys for details.  */
    
    #include "kwsysPrivate.h"
    #include KWSYS_HEADER(Glob.hxx)
    
    #include KWSYS_HEADER(Configure.hxx)
    
    #include KWSYS_HEADER(RegularExpression.hxx)
    #include KWSYS_HEADER(SystemTools.hxx)
    #include KWSYS_HEADER(Directory.hxx)
    
    // Work-around CMake dependency scanning limitation.  This must
    // duplicate the above list of headers.
    #if 0
    
    #include "Configure.hxx.in"
    #include "Directory.hxx.in"
    #include "Glob.hxx.in"
    #include "RegularExpression.hxx.in"
    #include "SystemTools.hxx.in"
    
    #include <string>
    #include <vector>
    
    
    #include <ctype.h>
    #include <stdio.h>
    
    #include <string.h>
    
    namespace KWSYS_NAMESPACE {
    
    #if defined(_WIN32) || defined(__APPLE__) || defined(__CYGWIN__)
    
    // On Windows and apple, no difference between lower and upper case
    
    #define KWSYS_GLOB_CASE_INDEPENDENT
    
    #if defined(_WIN32) || defined(__CYGWIN__)
    // Handle network paths
    
    #define KWSYS_GLOB_SUPPORT_NETWORK_PATHS
    
    #endif
    
    class GlobInternals
    {
    public:
    
      std::vector<std::string> Files;
      std::vector<kwsys::RegularExpression> Expressions;
    
    };
    
    Glob::Glob()
    {
    
      this->Internals = new GlobInternals;
      this->Recurse = false;
      this->Relative = "";
    
      // RecurseThroughSymlinks is true by default for backwards compatibility,
      // not because it's a good idea...
    
    
      // Keep separate variables for directory listing for back compatibility
      this->ListDirs = true;
      this->RecurseListDirs = false;
    
    }
    
    Glob::~Glob()
    {
    
      delete this->Internals;
    
    std::vector<std::string>& Glob::GetFiles()
    
      return this->Internals->Files;
    
    std::string Glob::PatternToRegex(const std::string& pattern,
    
                                     bool require_whole_string, bool preserve_case)
    
      // Incrementally build the regular expression from the pattern.
    
      std::string regex = require_whole_string ? "^" : "";
    
      std::string::const_iterator pattern_first = pattern.begin();
      std::string::const_iterator pattern_last = pattern.end();
    
      for (std::string::const_iterator i = pattern_first; i != pattern_last; ++i) {
    
        if (c == '*') {
    
          // A '*' (not between brackets) matches any string.
    
          // We modify this to not match slashes since the original glob
    
          // pattern documentation was meant for matching file name
          // components separated by slashes.
          regex += "[^/]*";
    
        } else if (c == '?') {
    
          // A '?' (not between brackets) matches any single character.
    
          // We modify this to not match slashes since the original glob
    
          // pattern documentation was meant for matching file name
          // components separated by slashes.
          regex += "[^/]";
    
        } else if (c == '[') {
    
          // Parse out the bracket expression.  It begins just after the
          // opening character.
    
          std::string::const_iterator bracket_first = i + 1;
    
          std::string::const_iterator bracket_last = bracket_first;
    
    
          // The first character may be complementation '!' or '^'.
    
          if (bracket_last != pattern_last &&
              (*bracket_last == '!' || *bracket_last == '^')) {
    
    
          // If the next character is a ']' it is included in the brackets
          // because the bracket string may not be empty.
    
          if (bracket_last != pattern_last && *bracket_last == ']') {
    
          while (bracket_last != pattern_last && *bracket_last != ']') {
    
    
          // Check whether we have a complete bracket string.
    
          if (bracket_last == pattern_last) {
    
            // The bracket string did not end, so it was opened simply by
            // a '[' that is supposed to be matched literally.
            regex += "\\[";
    
            // Convert the bracket string to its regex equivalent.
    
            std::string::const_iterator k = bracket_first;
    
    
            // Open the regex block.
            regex += "[";
    
            // A regex range complement uses '^' instead of '!'.
    
            if (k != bracket_last && *k == '!') {
    
            for (; k != bracket_last; ++k) {
    
              if (*k == '\\') {
    
    
            // Close the regex block.
            regex += "]";
    
            // Jump to the end of the bracket string.
            i = bracket_last;
    
          // A single character matches itself.
          int ch = c;
    
          if (!(('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') ||
                ('0' <= ch && ch <= '9'))) {
    
            // Escape the non-alphanumeric character.
            regex += "\\";
    
    #if defined(KWSYS_GLOB_CASE_INDEPENDENT)
    
            // On case-insensitive systems file names are converted to lower
            // case before matching.
    
            if (!preserve_case) {
    
          // Store the character.
          regex.append(1, static_cast<char>(ch));
    
      if (require_whole_string) {
    
    bool Glob::RecurseDirectory(std::string::size_type start,
    
                                const std::string& dir, GlobMessages* messages)
    
      if (!d.Load(dir)) {
    
      unsigned long cc;
    
      std::string realname;
      std::string fname;
    
      for (cc = 0; cc < d.GetNumberOfFiles(); cc++) {
    
        fname = d.GetFile(cc);
    
        if (fname == "." || fname == "..") {
    
          continue;
    
        if (start == 0) {
    
          realname = dir + fname;
    
          realname = dir + "/" + fname;
    
    #if defined(KWSYS_GLOB_CASE_INDEPENDENT)
    
        // On Windows and apple, no difference between lower and upper case
    
        fname = kwsys::SystemTools::LowerCase(fname);
    
        bool isDir = kwsys::SystemTools::FileIsDirectory(realname);
        bool isSymLink = kwsys::SystemTools::FileIsSymlink(realname);
    
        if (isDir && (!isSymLink || this->RecurseThroughSymlinks)) {
          if (isSymLink) {
    
            ++this->FollowedSymlinkCount;
    
            std::string realPathErrorMessage;
    
            std::string canonicalPath(
              SystemTools::GetRealPath(dir, &realPathErrorMessage));
    
            if (!realPathErrorMessage.empty()) {
              if (messages) {
    
                messages->push_back(Message(
    
                  Glob::error, "Canonical path generation from path '" + dir +
                    "' failed! Reason: '" + realPathErrorMessage + "'"));
    
            if (std::find(this->VisitedSymlinks.begin(),
                          this->VisitedSymlinks.end(),
                          canonicalPath) == this->VisitedSymlinks.end()) {
              if (this->RecurseListDirs) {
    
                // symlinks are treated as directories
                this->AddFile(this->Internals->Files, realname);
    
              this->VisitedSymlinks.push_back(canonicalPath);
    
              if (!this->RecurseDirectory(start + 1, realname, messages)) {
    
                this->VisitedSymlinks.pop_back();
    
                return false;
              }
    
              this->VisitedSymlinks.pop_back();
            }
    
            // else we have already visited this symlink - prevent cyclic recursion
    
            else if (messages) {
    
              std::string message;
    
              for (std::vector<std::string>::const_iterator pathIt =
                     std::find(this->VisitedSymlinks.begin(),
                               this->VisitedSymlinks.end(), canonicalPath);
                   pathIt != this->VisitedSymlinks.end(); ++pathIt) {
    
                message += *pathIt + "\n";
    
              message += canonicalPath + "/" + fname;
              messages->push_back(Message(Glob::cyclicRecursion, message));
            }
    
          } else {
            if (this->RecurseListDirs) {
    
              this->AddFile(this->Internals->Files, realname);
    
            }
            if (!this->RecurseDirectory(start + 1, realname, messages)) {
    
        } else {
          if (!this->Internals->Expressions.empty() &&
              this->Internals->Expressions.rbegin()->find(fname)) {
    
            this->AddFile(this->Internals->Files, realname);
    
    void Glob::ProcessDirectory(std::string::size_type start,
    
                                const std::string& dir, GlobMessages* messages)
    
      // std::cout << "ProcessDirectory: " << dir << std::endl;
      bool last = (start == this->Internals->Expressions.size() - 1);
      if (last && this->Recurse) {
    
        this->RecurseDirectory(start, dir, messages);
    
        return;
    
    Alexander Neundorf's avatar
     
    Alexander Neundorf committed
    
    
      if (start >= this->Internals->Expressions.size()) {
    
    Alexander Neundorf's avatar
     
    Alexander Neundorf committed
        return;
    
    Alexander Neundorf's avatar
     
    Alexander Neundorf committed
    
    
      if (!d.Load(dir)) {
    
        return;
    
      unsigned long cc;
    
      std::string realname;
      std::string fname;
    
      for (cc = 0; cc < d.GetNumberOfFiles(); cc++) {
    
        fname = d.GetFile(cc);
    
        if (fname == "." || fname == "..") {
    
          continue;
    
        if (start == 0) {
    
          realname = dir + fname;
    
          realname = dir + "/" + fname;
    
    #if defined(KWSYS_GLOB_CASE_INDEPENDENT)
        // On case-insensitive file systems convert to lower case for matching.
    
        fname = kwsys::SystemTools::LowerCase(fname);
    
        // std::cout << "Look at file: " << fname << std::endl;
        // std::cout << "Match: "
    
        // << this->Internals->TextExpressions[start].c_str() << std::endl;
    
        // std::cout << "Real name: " << realname << std::endl;
    
        if ((!last && !kwsys::SystemTools::FileIsDirectory(realname)) ||
            (!this->ListDirs && last &&
             kwsys::SystemTools::FileIsDirectory(realname))) {
    
          continue;
    
        if (this->Internals->Expressions[start].find(fname)) {
          if (last) {
    
            this->AddFile(this->Internals->Files, realname);
    
          } else {
            this->ProcessDirectory(start + 1, realname, messages);
    
    bool Glob::FindFiles(const std::string& inexpr, GlobMessages* messages)
    
      std::string cexpr;
      std::string::size_type cc;
      std::string expr = inexpr;
    
      this->Internals->Expressions.clear();
      this->Internals->Files.clear();
    
      if (!kwsys::SystemTools::FileIsFullPath(expr)) {
    
        expr = kwsys::SystemTools::GetCurrentWorkingDirectory();
    
        expr += "/" + inexpr;
    
      std::string fexpr = expr;
    
      std::string::size_type skip = 0;
      std::string::size_type last_slash = 0;
    
      for (cc = 0; cc < expr.size(); cc++) {
        if (cc > 0 && expr[cc] == '/' && expr[cc - 1] != '\\') {
    
    Francois Bertel's avatar
    Francois Bertel committed
          last_slash = cc;
    
        }
        if (cc > 0 && (expr[cc] == '[' || expr[cc] == '?' || expr[cc] == '*') &&
            expr[cc - 1] != '\\') {
    
      }
      if (last_slash > 0) {
        // std::cout << "I can skip: " << fexpr.substr(0, last_slash)
    
        skip = last_slash;
    
      }
      if (skip == 0) {
    #if defined(KWSYS_GLOB_SUPPORT_NETWORK_PATHS)
    
        // Handle network paths
    
        if (expr[0] == '/' && expr[1] == '/') {
    
          int cnt = 0;
    
          for (cc = 2; cc < expr.size(); cc++) {
            if (expr[cc] == '/') {
              cnt++;
              if (cnt == 2) {
    
          skip = int(cc + 1);
        } else
    
    #endif
          // Handle drive letters on Windows
    
          if (expr[1] == ':' && expr[0] != '/') {
          skip = 2;
    
        expr = expr.substr(skip);
    
    
      cexpr = "";
    
      for (cc = 0; cc < expr.size(); cc++) {
    
        int ch = expr[cc];
    
        if (ch == '/') {
          if (!cexpr.empty()) {
    
            this->AddExpression(cexpr);
    
          cexpr = "";
        } else {
    
          cexpr.append(1, static_cast<char>(ch));
    
      }
      if (!cexpr.empty()) {
    
        this->AddExpression(cexpr);
    
    
      // Handle network paths
    
        this->ProcessDirectory(0, fexpr.substr(0, skip) + "/", messages);
    
        this->ProcessDirectory(0, "/", messages);
    
      return true;
    }
    
    
    void Glob::AddExpression(const std::string& expr)
    
      this->Internals->Expressions.push_back(
    
        kwsys::RegularExpression(this->PatternToRegex(expr)));
    
    }
    
    void Glob::SetRelative(const char* dir)
    {
    
        this->Relative = "";
        return;
    
      this->Relative = dir;
    }
    
    const char* Glob::GetRelative()
    {
    
      if (this->Relative.empty()) {
    
      return this->Relative.c_str();
    }
    
    
    void Glob::AddFile(std::vector<std::string>& files, const std::string& file)
    
      if (!this->Relative.empty()) {
    
        files.push_back(kwsys::SystemTools::RelativePath(this->Relative, file));
    
        files.push_back(file);
    
    }
    
    } // namespace KWSYS_NAMESPACE