incorporate GlobPattern

This commit is contained in:
David Rose 2002-05-23 02:18:04 +00:00
parent 9db8ebaac7
commit e4a4bc3034
9 changed files with 500 additions and 48 deletions

View File

@ -5,6 +5,7 @@ ppremake_SOURCES = \
dSearchPath.I dSearchPath.cxx dSearchPath.h \
executionEnvironment.cxx executionEnvironment.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 \
ppCommandFile.cxx ppCommandFile.h ppDependableFile.cxx \
ppDependableFile.h ppDirectory.cxx \

View File

@ -40,9 +40,6 @@
/* Define if you have the <minmax.h> header file. */
/* #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. */
/* #undef HAVE_DIRENT_H */

View File

@ -42,7 +42,7 @@ AC_SUBST(libm)
dnl Checks for header files.
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.

View File

@ -1065,7 +1065,13 @@ scan_directory(vector_string &contents) const {
#if defined(HAVE_DIRENT_H)
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) {
return false;
}

90
ppremake/globPattern.I Normal file
View File

@ -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
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// 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
ppremake/globPattern.cxx Normal file
View File

@ -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
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#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
ppremake/globPattern.h Normal file
View File

@ -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
// panda3d@yahoogroups.com .
//
////////////////////////////////////////////////////////////////////
#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

View File

@ -14,10 +14,7 @@
#include "tokenize.h"
#include "filename.h"
#include "dSearchPath.h"
#ifdef HAVE_GLOB_H
#include <glob.h>
#endif
#include "globPattern.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
@ -3102,49 +3099,19 @@ p_find_map_variable(const string &varname) {
////////////////////////////////////////////////////////////////////
void PPScope::
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"));
bool changed_dir = false;
if (!dirname.empty()) {
if (chdir(dirname.c_str()) < 0) {
perror("chdir");
} else {
changed_dir = true;
}
}
vector<string> words;
tokenize_whitespace(str, words);
vector<string>::const_iterator wi;
glob_t pglob;
memset(&pglob, 0, sizeof(pglob));
int flags = 0;
for (wi = words.begin(); wi != words.end(); ++wi) {
glob((*wi).c_str(), flags, NULL, &pglob);
flags |= GLOB_APPEND;
GlobPattern glob(*wi);
glob.match_files(results, dirname);
}
for (int i = 0; i < (int)pglob.gl_pathc; i++) {
results.push_back(string(pglob.gl_pathv[i]));
}
globfree(&pglob);
// Sort the results into alphabetical order.
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
}

View File

@ -76,16 +76,17 @@ extern bool dry_run;
extern bool verbose_dry_run;
#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
Panda-like. */
#define PUBLISHED public
#define INLINE inline
#define EXPCL_DTOOL
#define EXPCL_PANDA
#endif