diff --git a/ppremake/executionEnvironment.cxx b/ppremake/executionEnvironment.cxx index 056f28f4a1..bbe01d6be4 100644 --- a/ppremake/executionEnvironment.cxx +++ b/ppremake/executionEnvironment.cxx @@ -3,3 +3,131 @@ // //////////////////////////////////////////////////////////////////// // +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "executionEnvironment.h" + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include // for perror + +#ifdef WIN32_VC +// Windows requires this for getcwd(). +#include +#define getcwd _getcwd +#endif + +//////////////////////////////////////////////////////////////////// +// Function: ExecutionEnvironment::get_environment_variable +// Access: Public, Static +// Description: Returns the definition of the indicated environment +// variable, or the empty string if the variable is +// undefined. The nonstatic implementation. +//////////////////////////////////////////////////////////////////// +string ExecutionEnvironment:: +get_environment_variable(const string &var) { + const char *def = getenv(var.c_str()); + if (def != (char *)NULL) { + return def; + } + return string(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ExecutionEnviroment::expand_string +// Access: Public, Static +// Description: Reads the string, looking for environment variable +// names marked by a $. Expands all such variable +// names. A repeated dollar sign ($$) is mapped to a +// single dollar sign. +// +// Returns the expanded string. +//////////////////////////////////////////////////////////////////// +string ExecutionEnvironment:: +expand_string(const string &str) { + string result; + + size_t last = 0; + size_t dollar = str.find('$'); + while (dollar != string::npos && dollar + 1 < str.length()) { + size_t start = dollar + 1; + + if (str[start] == '$') { + // A double dollar sign maps to a single dollar sign. + result += str.substr(last, start - last); + last = start + 1; + + } else { + string varname; + size_t end = start; + + if (str[start] == '{') { + // Curly braces delimit the variable name explicitly. + end = str.find('}', start + 1); + if (end != string::npos) { + varname = str.substr(start + 1, end - (start + 1)); + end++; + } + } + + if (end == start) { + // Scan for the end of the variable name. + while (end < str.length() && (isalnum(str[end]) || str[end] == '_')) { + end++; + } + varname = str.substr(start, end - start); + } + + string subst = + result += str.substr(last, dollar - last); + result += get_environment_variable(varname); + last = end; + } + + dollar = str.find('$', last); + } + + result += str.substr(last); + + return result; +} + +//////////////////////////////////////////////////////////////////// +// Function: ExecutionEnviroment::get_cwd +// Access: Public, Static +// Description: Returns the name of the current working directory. +//////////////////////////////////////////////////////////////////// +Filename ExecutionEnvironment:: +get_cwd() { + // getcwd() requires us to allocate a dynamic buffer and grow it on + // demand. + static size_t bufsize = 1024; + static char *buffer = NULL; + + if (buffer == (char *)NULL) { + buffer = new char[bufsize]; + } + + while (getcwd(buffer, bufsize) == (char *)NULL) { + if (errno != ERANGE) { + perror("getcwd"); + return string(); + } + delete[] buffer; + bufsize = bufsize * 2; + buffer = new char[bufsize]; + assert(buffer != (char *)NULL); + } + + return Filename::from_os_specific(buffer); +} diff --git a/ppremake/executionEnvironment.h b/ppremake/executionEnvironment.h index 55ef742120..1874053635 100644 --- a/ppremake/executionEnvironment.h +++ b/ppremake/executionEnvironment.h @@ -3,3 +3,32 @@ // //////////////////////////////////////////////////////////////////// // +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef EXECUTIONENVIRONMENT_H +#define EXECUTIONENVIRONMENT_H + +#include "ppremake.h" +#include "filename.h" + +//////////////////////////////////////////////////////////////////// +// Class : ExecutionEnvironment +// Description : This class is borrowed from dtool/src/dtoolutil, and +// stripped down to just the bare minimum that Filename +// needs; and also modified to build outside of Panda. +//////////////////////////////////////////////////////////////////// +class ExecutionEnvironment { +public: + static string get_environment_variable(const string &var); + static string expand_string(const string &str); + static Filename get_cwd(); +}; + +#endif diff --git a/ppremake/globPattern.I b/ppremake/globPattern.I index 066521c3e8..fd4f4a8fb5 100644 --- a/ppremake/globPattern.I +++ b/ppremake/globPattern.I @@ -3,3 +3,84 @@ // //////////////////////////////////////////////////////////////////// // +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// 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 ©) : _pattern(copy._pattern) { +} + +//////////////////////////////////////////////////////////////////// +// Function: GlobPattern::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void GlobPattern:: +operator = (const GlobPattern ©) { + _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; +} diff --git a/ppremake/globPattern.cxx b/ppremake/globPattern.cxx index 526627dcfc..48e6974317 100644 --- a/ppremake/globPattern.cxx +++ b/ppremake/globPattern.cxx @@ -3,3 +3,303 @@ // //////////////////////////////////////////////////////////////////// // +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#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; +} + + + diff --git a/ppremake/globPattern.h b/ppremake/globPattern.h index 1fd65005bb..eddac8fdc6 100644 --- a/ppremake/globPattern.h +++ b/ppremake/globPattern.h @@ -3,3 +3,75 @@ // //////////////////////////////////////////////////////////////////// // +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#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 ©); + INLINE void operator = (const GlobPattern ©); + + 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 diff --git a/ppremake/ppremake.h b/ppremake/ppremake.h index fa18a77242..e484ae479a 100644 --- a/ppremake/ppremake.h +++ b/ppremake/ppremake.h @@ -3,6 +3,15 @@ // Created by: drose (25Sep00) // //////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// */ #ifndef PPREMAKE_H diff --git a/ppremake/vector_string.h b/ppremake/vector_string.h index 4cf02da613..4918c0432b 100644 --- a/ppremake/vector_string.h +++ b/ppremake/vector_string.h @@ -3,3 +3,29 @@ // //////////////////////////////////////////////////////////////////// // +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef VECTOR_STRING_H +#define VECTOR_STRING_H + +#include "ppremake.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Class : vector_string +// Description : This typedef is borrowed from dtool/src/dtoolutil, and +// stripped down to just the bare minimum that Filename +// needs; and also modified to build outside of Panda. +//////////////////////////////////////////////////////////////////// + +typedef vector vector_string; + +#endif