Browse Source

incorporate GlobPattern

David Rose 23 years ago
parent
commit
e4a4bc3034

+ 1 - 0
ppremake/Makefile.am

@@ -5,6 +5,7 @@ ppremake_SOURCES =							\
     dSearchPath.I dSearchPath.cxx dSearchPath.h				\
     dSearchPath.I dSearchPath.cxx dSearchPath.h				\
     executionEnvironment.cxx executionEnvironment.h			\
     executionEnvironment.cxx executionEnvironment.h			\
     filename.I filename.cxx filename.h					\
     filename.I filename.cxx filename.h					\
+    globPattern.I globPattern.cxx globPattern.h				\
     gnu_getopt.c gnu_getopt.h gnu_regex.c gnu_regex.h			\
     gnu_getopt.c gnu_getopt.h gnu_regex.c gnu_regex.h			\
     ppCommandFile.cxx ppCommandFile.h ppDependableFile.cxx		\
     ppCommandFile.cxx ppCommandFile.h ppDependableFile.cxx		\
     ppDependableFile.h ppDirectory.cxx					\
     ppDependableFile.h ppDirectory.cxx					\

+ 0 - 3
ppremake/config_msvc.h

@@ -40,9 +40,6 @@
 /* Define if you have the <minmax.h> header file. */
 /* Define if you have the <minmax.h> header file. */
 /* #undef HAVE_MINMAX_H */
 /* #undef HAVE_MINMAX_H */
 
 
-/* Define if you have the <glob.h> header file. */
-/* #undef HAVE_GLOB_H */
-
 /* Define if you have the <dirent.h> header file. */
 /* Define if you have the <dirent.h> header file. */
 /* #undef HAVE_DIRENT_H */
 /* #undef HAVE_DIRENT_H */
 
 

+ 1 - 1
ppremake/configure.in

@@ -42,7 +42,7 @@ AC_SUBST(libm)
 
 
 dnl Checks for header files.
 dnl Checks for header files.
 AC_HEADER_STDC
 AC_HEADER_STDC
-AC_CHECK_HEADERS(malloc.h alloca.h unistd.h utime.h io.h minmax.h glob.h dirent.h sys/types.h sys/time.h sys/utime.h sys/wait.h string.h strstream.h regex.h)
+AC_CHECK_HEADERS(malloc.h alloca.h unistd.h utime.h io.h minmax.h dirent.h sys/types.h sys/time.h sys/utime.h sys/wait.h string.h strstream.h regex.h)
 
 
 dnl Checks for typedefs, structures, and compiler characteristics.
 dnl Checks for typedefs, structures, and compiler characteristics.
 
 

+ 7 - 1
ppremake/filename.cxx

@@ -1065,7 +1065,13 @@ scan_directory(vector_string &contents) const {
 #if defined(HAVE_DIRENT_H)
 #if defined(HAVE_DIRENT_H)
   size_t orig_size = contents.size();
   size_t orig_size = contents.size();
 
 
-  DIR *root = opendir(_filename.c_str());
+  string dirname;
+  if (empty()) {
+    dirname = ".";
+  } else {
+    dirname = _filename;
+  }
+  DIR *root = opendir(dirname.c_str());
   if (root == (DIR *)NULL) {
   if (root == (DIR *)NULL) {
     return false;
     return false;
   }
   }

+ 90 - 0
ppremake/globPattern.I

@@ -0,0 +1,90 @@
+// Filename: globPattern.I
+// Created by:  drose (30May00)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: GlobPattern::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE GlobPattern::
+GlobPattern(const string &pattern) : _pattern(pattern) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GlobPattern::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE GlobPattern::
+GlobPattern(const GlobPattern &copy) : _pattern(copy._pattern) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GlobPattern::Copy Assignment Operator
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void GlobPattern::
+operator = (const GlobPattern &copy) {
+  _pattern = copy._pattern;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GlobPattern::set_pattern
+//       Access: Public
+//  Description: Changes the pattern string that the GlobPattern
+//               object matches.
+////////////////////////////////////////////////////////////////////
+INLINE void GlobPattern::
+set_pattern(const string &pattern) {
+  _pattern = pattern;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GlobPattern::get_pattern
+//       Access: Public
+//  Description: Returns the pattern string that the GlobPattern
+//               object matches.
+////////////////////////////////////////////////////////////////////
+INLINE const string &GlobPattern::
+get_pattern() const {
+  return _pattern;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GlobPattern::matches
+//       Access: Public
+//  Description: Returns true if the candidate string matches the
+//               pattern, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool GlobPattern::
+matches(const string &candidate) const {
+  return matches_substr(_pattern.begin(), _pattern.end(),
+                        candidate.begin(), candidate.end());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GlobPattern::output
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void GlobPattern::
+output(ostream &out) const {
+  out << _pattern;
+}

+ 309 - 0
ppremake/globPattern.cxx

@@ -0,0 +1,309 @@
+// Filename: globPattern.cxx
+// Created by:  drose (30May00)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "globPattern.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: GlobPattern::has_glob_characters
+//       Access: Public
+//  Description: Returns true if the pattern includes any special
+//               globbing characters, or false if it is just a literal
+//               string.
+////////////////////////////////////////////////////////////////////
+bool GlobPattern::
+has_glob_characters() const {
+  string::const_iterator pi;
+  pi = _pattern.begin();
+  while (pi != _pattern.end()) {
+    switch (*pi) {
+    case '*':
+    case '?':
+    case '[':
+      return true;
+
+    case '\\':
+      ++pi;
+      if (pi == _pattern.end()) {
+        return false;
+      }
+    }
+    ++pi;
+  }
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GlobPattern::match_files
+//       Access: Public
+//  Description: Treats the GlobPattern as a filename pattern, and
+//               returns a list of any actual files that match the
+//               pattern.  This is the behavior of the standard Posix
+//               glob() function.  Any part of the filename may
+//               contain glob characters, including intermediate
+//               directory names.
+//
+//               If cwd is specified, it is the directory that
+//               relative filenames are taken to be relative to;
+//               otherwise, the actual current working directory is
+//               assumed.
+//
+//               The return value is the number of files matched,
+//               which are added to the results vector.
+////////////////////////////////////////////////////////////////////
+int GlobPattern::
+match_files(vector_string &results, const Filename &cwd) {
+  string prefix, pattern, suffix;
+
+  string source = _pattern;
+  if (!source.empty() && source[0] == '/') {
+    // If the first character is a slash, that becomes the prefix.
+    prefix = "/";
+    source = source.substr(1);
+  }
+
+  size_t slash = source.find('/');
+  if (slash == string::npos) {
+    pattern = source;
+  } else {
+    pattern = source.substr(0, slash);
+    suffix = source.substr(slash + 1);
+  }
+  
+  GlobPattern glob(pattern);
+  return glob.r_match_files(prefix, suffix, results, cwd);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GlobPattern::r_match_files
+//       Access: Private
+//  Description: The recursive implementation of match_files().
+////////////////////////////////////////////////////////////////////
+int GlobPattern::
+r_match_files(const Filename &prefix, const string &suffix,
+              vector_string &results, const Filename &cwd) {
+  string next_pattern, next_suffix;
+
+  size_t slash = suffix.find('/');
+  if (slash == string::npos) {
+    next_pattern = suffix;
+  } else {
+    next_pattern = suffix.substr(0, slash);
+    next_suffix = suffix.substr(slash + 1);
+  }
+
+  Filename parent_dir;
+  if (prefix.is_local() && !cwd.empty()) {
+    parent_dir = Filename(cwd, prefix);
+  } else {
+    parent_dir = prefix;
+  }
+
+  GlobPattern next_glob(next_pattern);
+
+  if (!has_glob_characters()) {
+    // If there are no special characters in the pattern, it's a
+    // literal match.
+    if (suffix.empty()) {
+      // Time to stop.
+      Filename single_filename(parent_dir, _pattern);
+      if (single_filename.exists()) {
+        results.push_back(Filename(prefix, _pattern));
+        return 1;
+      }
+      return 0;
+    }
+
+    return next_glob.r_match_files(Filename(prefix, _pattern),
+                                   next_suffix, results, cwd);
+
+  } 
+
+  // If there *are* special glob characters, we must attempt to
+  // match the pattern against the files in this directory.
+  
+  vector_string dir_files;
+  if (!parent_dir.scan_directory(dir_files)) {
+    // Not a directory, or unable to read directory; stop here.
+    return 0;
+  }
+  
+  // Now go through each file in the directory looking for one that
+  // matches the pattern.
+  int num_matched = 0;
+  
+  vector_string::const_iterator fi;
+  for (fi = dir_files.begin(); fi != dir_files.end(); ++fi) {
+    const string &local_file = (*fi);
+    if (_pattern[0] == '.' || (local_file.empty() || local_file[0] != '.')) {
+      if (matches(local_file)) {
+        // We have a match; continue.
+        if (suffix.empty()) {
+          results.push_back(Filename(prefix, local_file));
+          num_matched++; 
+        } else {
+          num_matched += next_glob.r_match_files(Filename(prefix, local_file),
+                                                 next_suffix, results, cwd);
+        }
+      }
+    }
+  }
+  
+  return num_matched;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GlobPattern::matches_substr
+//       Access: Private
+//  Description: The recursive implementation of matches().  This
+//               returns true if the pattern substring [pi, pend)
+//               matches the candidate substring [ci, cend), false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+bool GlobPattern::
+matches_substr(string::const_iterator pi, string::const_iterator pend,
+               string::const_iterator ci, string::const_iterator cend) const {
+  // If we run out of pattern or candidate string, it's a match only
+  // if they both ran out at the same time.
+  if (pi == pend || ci == cend) {
+    // A special exception: we allow ci to reach the end before pi,
+    // only if pi is one character before the end and that last
+    // character is '*'.
+    if ((ci == cend) && (pi + 1 == pend) && (*pi) == '*') {
+      return true;
+    }
+    return (pi == pend && ci == cend);
+  }
+
+  switch (*pi) {
+
+  case '*':
+    // A '*' in the pattern string means to match any sequence of zero
+    // or more characters in the candidate string.  This means we have
+    // to recurse twice: either consume one character of the candidate
+    // string and continue to try matching the *, or stop trying to
+    // match the * here.
+    return
+      matches_substr(pi, pend, ci + 1, cend) ||
+      matches_substr(pi + 1, pend, ci, cend);
+
+  case '?':
+    // A '?' in the pattern string means to match exactly one
+    // character in the candidate string.  That's easy.
+    return matches_substr(pi + 1, pend, ci + 1, cend);
+
+  case '[':
+    // An open square bracket begins a set.
+    ++pi;
+    if ((*pi) == '!') {
+      ++pi;
+      if (matches_set(pi, pend, *ci)) {
+        return false;
+      }
+    } else {
+      if (!matches_set(pi, pend, *ci)) {
+        return false;
+      }
+    }
+    if (pi == pend) {
+      // Oops, there wasn't a closing square bracket.
+      return false;
+    }
+    return matches_substr(pi + 1, pend, ci + 1, cend);
+
+  case '\\':
+    // A backslash escapes the next special character.
+    ++pi;
+    if (pi == pend) {
+      return false;
+    }
+    // fall through.
+
+  default:
+    // Anything else means to match exactly that.
+    if ((*pi) != (*ci)) {
+      return false;
+    }
+    return matches_substr(pi + 1, pend, ci + 1, cend);
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: GlobPattern::matches_set
+//       Access: Private
+//  Description: Called when an unescaped open square bracked is
+//               scanned, this is called with pi positioned after the
+//               opening square bracket, scans the set sequence,
+//               leaving pi positioned on the closing square bracket,
+//               and returns true if the indicated character matches
+//               the set of characters indicated, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool GlobPattern::
+matches_set(string::const_iterator &pi, string::const_iterator pend,
+            char ch) const {
+  bool matched = false;
+
+  while (pi != pend && (*pi) != ']') {
+    if ((*pi) == '\\') {
+      // Backslash escapes the next character.
+      ++pi;
+      if (pi == pend) {
+        return false;
+      }
+    }
+
+    if (ch == (*pi)) {
+      matched = true;
+    }
+
+    // Maybe it's an a-z style range?
+    char start = (*pi);
+    ++pi;
+    if (pi != pend && (*pi) == '-') {
+      ++pi;
+      if (pi != pend && (*pi) != ']') {
+        // Yes, we have a range: start-end.
+
+        if ((*pi) == '\\') {
+          // Backslash escapes.
+          ++pi;
+          if (pi == pend) {
+            return false;
+          }
+        }
+
+        char end = (*pi);
+        ++pi;
+
+        if (ch >= start && ch <= end) {
+          matched = true;
+        }
+      } else {
+        // This was a - at the end of the string.
+        if (ch == '-') {
+          matched = true;
+        }
+      }
+    }
+  }
+
+  return matched;
+}
+
+
+

+ 81 - 0
ppremake/globPattern.h

@@ -0,0 +1,81 @@
+// Filename: globPattern.h
+// Created by:  drose (30May00)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef GLOBPATTERN_H
+#define GLOBPATTERN_H
+
+#include "ppremake.h"
+#include "filename.h"
+#include "vector_string.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : GlobPattern
+// Description : This class can be used to test for string matches
+//               against standard Unix-shell filename globbing
+//               conventions.  It serves as a portable standin for the
+//               Posix fnmatch() call.
+//
+//               A GlobPattern is given a pattern string, which can
+//               contain operators like *, ?, and [].  Then it can be
+//               tested against any number of candidate strings; for
+//               each candidate, it will indicate whether the string
+//               matches the pattern or not.  It can be used, for
+//               example, to scan a directory for all files matching a
+//               particular pattern.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA GlobPattern {
+public:
+  INLINE GlobPattern(const string &pattern = string());
+  INLINE GlobPattern(const GlobPattern &copy);
+  INLINE void operator = (const GlobPattern &copy);
+
+  INLINE void set_pattern(const string &pattern);
+  INLINE const string &get_pattern() const;
+
+  INLINE bool matches(const string &candidate) const;
+
+  INLINE void output(ostream &out) const;
+
+  bool has_glob_characters() const;
+  int match_files(vector_string &results, const Filename &cwd = Filename());
+
+private:
+  bool matches_substr(string::const_iterator pi,
+                      string::const_iterator pend,
+                      string::const_iterator ci,
+                      string::const_iterator cend) const;
+
+  bool matches_set(string::const_iterator &pi,
+                   string::const_iterator pend,
+                   char ch) const;
+
+  int r_match_files(const Filename &prefix, const string &suffix,
+                    vector_string &results, const Filename &cwd);
+
+  string _pattern;
+};
+
+INLINE ostream &operator << (ostream &out, const GlobPattern &glob) {
+  glob.output(out);
+  return out;
+}
+
+
+#include "globPattern.I"
+
+#endif

+ 5 - 38
ppremake/ppScope.cxx

@@ -14,10 +14,7 @@
 #include "tokenize.h"
 #include "tokenize.h"
 #include "filename.h"
 #include "filename.h"
 #include "dSearchPath.h"
 #include "dSearchPath.h"
-
-#ifdef HAVE_GLOB_H
-#include <glob.h>
-#endif
+#include "globPattern.h"
 
 
 #ifdef HAVE_UNISTD_H
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #include <unistd.h>
@@ -3102,49 +3099,19 @@ p_find_map_variable(const string &varname) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PPScope::
 void PPScope::
 glob_string(const string &str, vector<string> &results) {
 glob_string(const string &str, vector<string> &results) {
-#ifdef WIN32_VC
-  cerr << "glob temporarily unsupported in Win32 without Cygwin.\n";
-
-#else  // WIN32_VC
-  // We run glob_string() within the directory indicated by
-  // $[THISDIRPREFIX].  This way, local filenames will be expanded the
-  // way we expect.
+  // The globbing is relative to THISDIRPREFIX, not necessarily the
+  // current directory.
   string dirname = trim_blanks(expand_variable("THISDIRPREFIX"));
   string dirname = trim_blanks(expand_variable("THISDIRPREFIX"));
-  bool changed_dir = false;
-  if (!dirname.empty()) {
-    if (chdir(dirname.c_str()) < 0) {
-      perror("chdir");
-    } else {
-      changed_dir = true;
-    }
-  }
 
 
   vector<string> words;
   vector<string> words;
   tokenize_whitespace(str, words);
   tokenize_whitespace(str, words);
 
 
   vector<string>::const_iterator wi;
   vector<string>::const_iterator wi;
-
-  glob_t pglob;
-  memset(&pglob, 0, sizeof(pglob));
-
-  int flags = 0;
   for (wi = words.begin(); wi != words.end(); ++wi) {
   for (wi = words.begin(); wi != words.end(); ++wi) {
-    glob((*wi).c_str(), flags, NULL, &pglob);
-    flags |= GLOB_APPEND;
-  }
-
-  for (int i = 0; i < (int)pglob.gl_pathc; i++) {
-    results.push_back(string(pglob.gl_pathv[i]));
+    GlobPattern glob(*wi);
+    glob.match_files(results, dirname);
   }
   }
 
 
-  globfree(&pglob);
-
   // Sort the results into alphabetical order.
   // Sort the results into alphabetical order.
   sort(results.begin(), results.end());
   sort(results.begin(), results.end());
-
-  if (changed_dir) {
-    // Now restore the current directory back to where it should be.
-    PPMain::chdir_root();
-  }
-#endif  // WIN32_VC
 }
 }

+ 6 - 5
ppremake/ppremake.h

@@ -76,16 +76,17 @@ extern bool dry_run;
 extern bool verbose_dry_run;
 extern bool verbose_dry_run;
 #endif
 #endif
 
 
-/* These are defined so that we may build Filename and DSearchPath,
-   which are copied from dtool.  We have to copy these files from
-   dtool since ppremake must be built first, and stands outside of
-   Panda; but we want to minimize the changes we must make to these
-   files so that we can easily recopy them at need. 
+/* These are defined so that we may build Filename, DSearchPath, and
+   GlobPattern, which are copied from dtool and panda.  We have to
+   copy these files in since ppremake must be built first, and stands
+   outside of Panda; but we want to minimize the changes we must make
+   to these files so that we can easily recopy them at need.
 
 
    These symbols just make the build environment a bit more
    These symbols just make the build environment a bit more
    Panda-like. */
    Panda-like. */
 #define PUBLISHED public
 #define PUBLISHED public
 #define INLINE inline
 #define INLINE inline
 #define EXPCL_DTOOL
 #define EXPCL_DTOOL
+#define EXPCL_PANDA
 
 
 #endif
 #endif