mirror of
https://github.com/Sneed-Group/Poodletooth-iLand
synced 2024-12-26 21:22:27 -06:00
3470 lines
110 KiB
C++
3470 lines
110 KiB
C++
|
// Filename: ppScope.cxx
|
||
|
// Created by: drose (25Sep00)
|
||
|
//
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#include "ppremake.h"
|
||
|
#include "ppScope.h"
|
||
|
#include "ppNamedScopes.h"
|
||
|
#include "ppFilenamePattern.h"
|
||
|
#include "ppDirectory.h"
|
||
|
#include "ppSubroutine.h"
|
||
|
#include "ppCommandFile.h"
|
||
|
#include "ppDependableFile.h"
|
||
|
#include "ppMain.h"
|
||
|
#include "tokenize.h"
|
||
|
#include "filename.h"
|
||
|
#include "dSearchPath.h"
|
||
|
#include "globPattern.h"
|
||
|
#include "md5.h"
|
||
|
|
||
|
#ifdef HAVE_UNISTD_H
|
||
|
#include <unistd.h>
|
||
|
#endif
|
||
|
|
||
|
#ifdef HAVE_SYS_TYPES_H
|
||
|
#include <sys/types.h>
|
||
|
#endif
|
||
|
|
||
|
#ifdef HAVE_SYS_WAIT_H
|
||
|
#include <sys/wait.h>
|
||
|
#endif
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
#include <algorithm>
|
||
|
#include <ctype.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <stdio.h> // for perror() and sprintf().
|
||
|
#include <errno.h>
|
||
|
#include <signal.h>
|
||
|
#include <assert.h>
|
||
|
|
||
|
#ifdef WIN32_VC
|
||
|
#include <windows.h> // for GetFileAttributes()
|
||
|
#endif // WIN32_VC
|
||
|
|
||
|
static const string variable_patsubst(VARIABLE_PATSUBST);
|
||
|
|
||
|
PPScope::MapVariableDefinition PPScope::_null_map_def;
|
||
|
|
||
|
PPScope::ScopeStack PPScope::_scope_stack;
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::Constructor
|
||
|
// Access: Public
|
||
|
// Description:
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
PPScope::
|
||
|
PPScope(PPNamedScopes *named_scopes) :
|
||
|
_named_scopes(named_scopes)
|
||
|
{
|
||
|
_directory = (PPDirectory *)NULL;
|
||
|
_parent_scope = (PPScope *)NULL;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::get_named_scopes
|
||
|
// Access: Public
|
||
|
// Description: Returns a pointer to the PPNamedScopes collection
|
||
|
// associated with this scope. This pointer could be
|
||
|
// NULL.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
PPNamedScopes *PPScope::
|
||
|
get_named_scopes() {
|
||
|
return _named_scopes;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::set_parent
|
||
|
// Access: Public
|
||
|
// Description: Sets a static parent scope to this scope. When a
|
||
|
// variable reference is undefined in this scope, it
|
||
|
// will search first up the static parent chain before
|
||
|
// it searches the dynamic scope stack.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
void PPScope::
|
||
|
set_parent(PPScope *parent) {
|
||
|
_parent_scope = parent;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::get_parent
|
||
|
// Access: Public
|
||
|
// Description: Returns the static parent scope to this scope, if
|
||
|
// any, or NULL if the static parent has not been set.
|
||
|
// See set_parent().
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
PPScope *PPScope::
|
||
|
get_parent() {
|
||
|
return _parent_scope;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::define_variable
|
||
|
// Access: Public
|
||
|
// Description: Makes a new variable definition. If the variable
|
||
|
// does not already exist in this scope, a new variable
|
||
|
// is created, possibly shadowing a variable declaration
|
||
|
// in some parent scope.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
void PPScope::
|
||
|
define_variable(const string &varname, const string &definition) {
|
||
|
_variables[varname] = definition;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::set_variable
|
||
|
// Access: Public
|
||
|
// Description: Changes the definition of an already-existing
|
||
|
// variable. The variable is changed in whichever scope
|
||
|
// it is defined. Returns false if the variable has not
|
||
|
// been defined.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
bool PPScope::
|
||
|
set_variable(const string &varname, const string &definition) {
|
||
|
if (p_set_variable(varname, definition)) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Check the scopes on the stack for the variable definition.
|
||
|
ScopeStack::reverse_iterator si;
|
||
|
for (si = _scope_stack.rbegin(); si != _scope_stack.rend(); ++si) {
|
||
|
if ((*si)->p_set_variable(varname, definition)) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If the variable isn't defined, we check the environment.
|
||
|
const char *env = getenv(varname.c_str());
|
||
|
if (env != (const char *)NULL) {
|
||
|
// It is defined in the environment; thus, it is implicitly
|
||
|
// defined here at the global scope: the bottom of the stack.
|
||
|
PPScope *bottom = this;
|
||
|
if (!_scope_stack.empty()) {
|
||
|
bottom = _scope_stack.front();
|
||
|
}
|
||
|
bottom->define_variable(varname, definition);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// The variable isn't defined anywhere. Too bad.
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::define_map_variable
|
||
|
// Access: Public
|
||
|
// Description: Makes a new map variable definition. This defines a
|
||
|
// new variable that can be used as a function to
|
||
|
// retrieve variables from within a named scope, based
|
||
|
// on a particular key variable.
|
||
|
//
|
||
|
// In this variant of define_map_variable(), the
|
||
|
// definition is a string of the form
|
||
|
// key_varname(scope_names).
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
void PPScope::
|
||
|
define_map_variable(const string &varname, const string &definition) {
|
||
|
size_t p = definition.find(VARIABLE_OPEN_NESTED);
|
||
|
if (p != string::npos && definition[definition.length() - 1] == VARIABLE_CLOSE_NESTED) {
|
||
|
size_t q = definition.length() - 1;
|
||
|
string scope_names = definition.substr(p + 1, q - (p + 1));
|
||
|
string key_varname = definition.substr(0, p);
|
||
|
define_map_variable(varname, key_varname, scope_names);
|
||
|
} else {
|
||
|
// No scoping; not really a map variable.
|
||
|
define_map_variable(varname, definition, "");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::define_map_variable
|
||
|
// Access: Public
|
||
|
// Description: Makes a new map variable definition. This defines a
|
||
|
// new variable that can be used as a function to
|
||
|
// retrieve variables from within a named scope, based
|
||
|
// on a particular key variable.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
void PPScope::
|
||
|
define_map_variable(const string &varname, const string &key_varname,
|
||
|
const string &scope_names) {
|
||
|
MapVariableDefinition &def = _map_variables[varname];
|
||
|
def.clear();
|
||
|
define_variable(varname, "");
|
||
|
|
||
|
if (_named_scopes == (PPNamedScopes *)NULL) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (key_varname.empty()) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
vector<string> names;
|
||
|
tokenize_whitespace(scope_names, names);
|
||
|
|
||
|
// Get all of the named scopes.
|
||
|
PPNamedScopes::Scopes scopes;
|
||
|
|
||
|
vector<string>::const_iterator ni;
|
||
|
for (ni = names.begin(); ni != names.end(); ++ni) {
|
||
|
const string &name = (*ni);
|
||
|
_named_scopes->get_scopes(name, scopes);
|
||
|
}
|
||
|
|
||
|
if (scopes.empty()) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Now go through the scopes and build up the results.
|
||
|
vector<string> results;
|
||
|
|
||
|
PPNamedScopes::Scopes::const_iterator si;
|
||
|
for (si = scopes.begin(); si != scopes.end(); ++si) {
|
||
|
PPScope *scope = (*si);
|
||
|
string key_string = scope->expand_variable(key_varname);
|
||
|
vector<string> keys;
|
||
|
tokenize_whitespace(key_string, keys);
|
||
|
|
||
|
if (!keys.empty()) {
|
||
|
vector<string>::const_iterator ki;
|
||
|
results.insert(results.end(), keys.begin(), keys.end());
|
||
|
for (ki = keys.begin(); ki != keys.end(); ++ki) {
|
||
|
def[*ki] = scope;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Also define a traditional variable along with the map variable.
|
||
|
define_variable(varname, repaste(results, " "));
|
||
|
}
|
||
|
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::add_to_map_variable
|
||
|
// Access: Public
|
||
|
// Description: Adds a new key/scope pair to a previous map variable
|
||
|
// definition.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
void PPScope::
|
||
|
add_to_map_variable(const string &varname, const string &key,
|
||
|
PPScope *scope) {
|
||
|
MapVariableDefinition &def = find_map_variable(varname);
|
||
|
if (&def == &_null_map_def) {
|
||
|
cerr << "Warning: undefined map variable: " << varname << "\n";
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
def[key] = scope;
|
||
|
|
||
|
// We need to do all this work to define the traditional expansion.
|
||
|
// Maybe not a great idea.
|
||
|
vector<string> results;
|
||
|
MapVariableDefinition::const_iterator di;
|
||
|
for (di = def.begin(); di != def.end(); ++di) {
|
||
|
results.push_back((*di).first);
|
||
|
}
|
||
|
|
||
|
set_variable(varname, repaste(results, " "));
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::define_formals
|
||
|
// Access: Public
|
||
|
// Description: Supplies values to a slew of variables at once,
|
||
|
// typically to define actual values for a list of
|
||
|
// formal parameters to a user-defined subroutine or
|
||
|
// function.
|
||
|
//
|
||
|
// Formals is a vector of variable names to be defined,
|
||
|
// and actuals is a comma-separated list of expressions
|
||
|
// to be substituted in, one-per-one. The
|
||
|
// subroutine_name is used only for error reporting.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
void PPScope::
|
||
|
define_formals(const string &subroutine_name,
|
||
|
const vector<string> &formals, const string &actuals) {
|
||
|
vector<string> actual_words;
|
||
|
tokenize_params(actuals, actual_words, true);
|
||
|
|
||
|
if (actual_words.size() < formals.size()) {
|
||
|
cerr << "Warning: not all parameters defined for " << subroutine_name
|
||
|
<< ": " << actuals << "\n";
|
||
|
} else if (actual_words.size() > formals.size()) {
|
||
|
cerr << "Warning: more parameters defined for " << subroutine_name
|
||
|
<< " than actually exist: " << actuals << "\n";
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < (int)formals.size(); i++) {
|
||
|
if (i < (int)actual_words.size()) {
|
||
|
define_variable(formals[i], actual_words[i]);
|
||
|
} else {
|
||
|
define_variable(formals[i], string());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::get_variable
|
||
|
// Access: Public
|
||
|
// Description: Returns the variable definition associated with the
|
||
|
// indicated variable name.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
get_variable(const string &varname) {
|
||
|
// Is it a user-defined function?
|
||
|
const PPSubroutine *sub = PPSubroutine::get_func(varname);
|
||
|
if (sub != (const PPSubroutine *)NULL) {
|
||
|
return expand_function(varname, sub, string());
|
||
|
}
|
||
|
|
||
|
// cerr << "getvar arg is: '" << varname << "'" << endl;
|
||
|
|
||
|
string result;
|
||
|
if (p_get_variable(varname, result)) {
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
// Check the scopes on the stack for the variable definition.
|
||
|
ScopeStack::reverse_iterator si;
|
||
|
for (si = _scope_stack.rbegin(); si != _scope_stack.rend(); ++si) {
|
||
|
if ((*si)->p_get_variable(varname, result)) {
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If the variable isn't defined, we check the environment.
|
||
|
const char *env = getenv(varname.c_str());
|
||
|
if (env != (const char *)NULL) {
|
||
|
return env;
|
||
|
}
|
||
|
|
||
|
// It's not defined anywhere, so it's implicitly empty.
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_defined
|
||
|
// Access: Private
|
||
|
// Description: Expands the "defined" function variable. Code mimics get_variable()
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_defined(const string ¶ms) {
|
||
|
// Split the string up into tokens based on the commas.
|
||
|
vector<string> tokens;
|
||
|
tokenize_params(params, tokens, true);
|
||
|
|
||
|
if (tokens.size() != 1) {
|
||
|
cerr << "error: defined requires one parameter.\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
string varname = tokens[0];
|
||
|
string falsestr;
|
||
|
string truestr = "1";
|
||
|
|
||
|
// Is it a user-defined function?
|
||
|
const PPSubroutine *sub = PPSubroutine::get_func(varname);
|
||
|
|
||
|
string nullstr;
|
||
|
|
||
|
if (sub != (const PPSubroutine *)NULL) {
|
||
|
if(nullstr != expand_function(varname, sub, string())) {
|
||
|
return truestr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
string result;
|
||
|
|
||
|
if (p_get_variable(varname, result)) {
|
||
|
return truestr;
|
||
|
}
|
||
|
|
||
|
// Check the scopes on the stack for the variable definition.
|
||
|
ScopeStack::reverse_iterator si;
|
||
|
for (si = _scope_stack.rbegin(); si != _scope_stack.rend(); ++si) {
|
||
|
if ((*si)->p_get_variable(varname, result)) {
|
||
|
return truestr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If the variable isn't defined, we check the environment.
|
||
|
const char *env = getenv(varname.c_str());
|
||
|
if (env != (const char *)NULL) {
|
||
|
return truestr;
|
||
|
}
|
||
|
|
||
|
// It's not defined anywhere, so it's implicitly empty.
|
||
|
return falsestr;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_variable
|
||
|
// Access: Public
|
||
|
// Description: Similar to get_variable(), except the variable
|
||
|
// definition is in turn expanded.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_variable(const string &varname) {
|
||
|
return expand_string(get_variable(varname));
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::find_map_variable
|
||
|
// Access: Public
|
||
|
// Description: Looks for the map variable definition in this scope
|
||
|
// or some ancestor scope. Returns the map variable
|
||
|
// definition if it is found, or _null_map_def if it is
|
||
|
// not.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
PPScope::MapVariableDefinition &PPScope::
|
||
|
find_map_variable(const string &varname) {
|
||
|
MapVariableDefinition &def = p_find_map_variable(varname);
|
||
|
if (&def != &_null_map_def) {
|
||
|
return def;
|
||
|
}
|
||
|
|
||
|
// No such map variable. Check the stack.
|
||
|
ScopeStack::reverse_iterator si;
|
||
|
for (si = _scope_stack.rbegin(); si != _scope_stack.rend(); ++si) {
|
||
|
MapVariableDefinition &def = (*si)->p_find_map_variable(varname);
|
||
|
if (&def != &_null_map_def) {
|
||
|
return def;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Nada.
|
||
|
return _null_map_def;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::get_directory
|
||
|
// Access: Public
|
||
|
// Description: Returns the directory level associated with this
|
||
|
// scope, if any, or with the nearest parent to this
|
||
|
// scope.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
PPDirectory *PPScope::
|
||
|
get_directory() {
|
||
|
if (_directory != (PPDirectory *)NULL) {
|
||
|
return _directory;
|
||
|
}
|
||
|
|
||
|
// Check the stack.
|
||
|
ScopeStack::reverse_iterator si;
|
||
|
for (si = _scope_stack.rbegin(); si != _scope_stack.rend(); ++si) {
|
||
|
if ((*si)->_directory != (PPDirectory *)NULL) {
|
||
|
return (*si)->_directory;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return (PPDirectory *)NULL;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::set_directory
|
||
|
// Access: Public
|
||
|
// Description: Associates this scope with the indicated directory
|
||
|
// level. Typically this is done when definition a
|
||
|
// scope for a particular source file which exists at a
|
||
|
// known directory level.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
void PPScope::
|
||
|
set_directory(PPDirectory *directory) {
|
||
|
_directory = directory;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_string
|
||
|
// Access: Public
|
||
|
// Description: Expands out all the variable references in the given
|
||
|
// string. Variables are expanded recursively; that is,
|
||
|
// if a variable expansion includes a reference to
|
||
|
// another variable name, the second variable name is
|
||
|
// expanded. However, cyclical references are not
|
||
|
// expanded.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_string(const string &str) {
|
||
|
string result = r_expand_string(str, (ExpandedVariable *)NULL);
|
||
|
|
||
|
if (debug_expansions > 0 && str != result) {
|
||
|
// Look for the str in our table--how many times has this
|
||
|
// particular string been expanded?
|
||
|
ExpandResultCount &result_count = debug_expand[str];
|
||
|
|
||
|
// Then, how many times has it expanded to this same result?
|
||
|
// First, assuming this is the first time it has expanded to this
|
||
|
// result, try to insert the result string with an initial count
|
||
|
// of 1.
|
||
|
pair<ExpandResultCount::iterator, bool> r =
|
||
|
result_count.insert(ExpandResultCount::value_type(result, 1));
|
||
|
|
||
|
if (!r.second) {
|
||
|
// If the result string was not successfully inserted into the
|
||
|
// map, it was already there--so increment the count.
|
||
|
ExpandResultCount::iterator rci = r.first;
|
||
|
(*rci).second++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_self_reference
|
||
|
// Access: Public
|
||
|
// Description: Similar to expand_string(), except that only simple
|
||
|
// references to the named variable are expanded--other
|
||
|
// variable references are left unchanged. This allows
|
||
|
// us to define a variable in terms of its previous
|
||
|
// definition.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_self_reference(const string &str, const string &varname) {
|
||
|
// Look for a simple reference to the named variable. A more
|
||
|
// complex reference, like a computed variable name or something
|
||
|
// equally loopy, won't work with this simple test. Too bad.
|
||
|
string reference;
|
||
|
reference += VARIABLE_PREFIX;
|
||
|
reference += VARIABLE_OPEN_BRACE;
|
||
|
reference += varname;
|
||
|
reference += VARIABLE_CLOSE_BRACE;
|
||
|
|
||
|
string result;
|
||
|
|
||
|
size_t p = 0;
|
||
|
size_t q = str.find(reference, p);
|
||
|
while (q != string::npos) {
|
||
|
result += str.substr(p, q - p);
|
||
|
p = q;
|
||
|
result += r_expand_variable(str, p, (ExpandedVariable *)NULL);
|
||
|
q = str.find(reference, p);
|
||
|
}
|
||
|
|
||
|
result += str.substr(p);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::push_scope
|
||
|
// Access: Public, Static
|
||
|
// Description: Pushes the indicated scope onto the top of the stack.
|
||
|
// When a variable reference is unresolved in the
|
||
|
// current scope, the scope stack is searched, in LIFO
|
||
|
// order.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
void PPScope::
|
||
|
push_scope(PPScope *scope) {
|
||
|
_scope_stack.push_back(scope);
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::pop_scope
|
||
|
// Access: Public, Static
|
||
|
// Description: Pops another level off the top of the stack. See
|
||
|
// push_scope().
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
PPScope *PPScope::
|
||
|
pop_scope() {
|
||
|
assert(!_scope_stack.empty());
|
||
|
PPScope *back = _scope_stack.back();
|
||
|
_scope_stack.pop_back();
|
||
|
return back;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::get_bottom_scope
|
||
|
// Access: Public, Static
|
||
|
// Description: Returns the scope on the bottom of the stack. This
|
||
|
// was the very first scope ever pushed, e.g. the global
|
||
|
// scope.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
PPScope *PPScope::
|
||
|
get_bottom_scope() {
|
||
|
assert(!_scope_stack.empty());
|
||
|
return _scope_stack.front();
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::get_enclosing_scope
|
||
|
// Access: Public, Static
|
||
|
// Description: Returns the scope n below the top of the stack, or
|
||
|
// the bottom scope if the stack has exactly n or fewer
|
||
|
// scopes.
|
||
|
//
|
||
|
// This will be the scope associated with the nth
|
||
|
// enclosing syntax in the source file.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
PPScope *PPScope::
|
||
|
get_enclosing_scope(int n) {
|
||
|
assert(n >= 0);
|
||
|
if (n >= _scope_stack.size()) {
|
||
|
return get_bottom_scope();
|
||
|
}
|
||
|
return _scope_stack[_scope_stack.size() - 1 - n];
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::tokenize_params
|
||
|
// Access: Public
|
||
|
// Description: Separates a string into tokens based on comma
|
||
|
// delimiters, e.g. for parameters to a function.
|
||
|
// Nested variable references are skipped correctly,
|
||
|
// even if they include commas. Leading and trailing
|
||
|
// whitespace in each token is automatically stripped.
|
||
|
//
|
||
|
// If expand is true, the nested variables are
|
||
|
// automatically expanded as the string is tokenized;
|
||
|
// otherwise, they are left unexpanded.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
void PPScope::
|
||
|
tokenize_params(const string &str, vector<string> &tokens,
|
||
|
bool expand) {
|
||
|
size_t p = 0;
|
||
|
while (p < str.length()) {
|
||
|
// Skip initial whitespace.
|
||
|
while (p < str.length() && isspace(str[p])) {
|
||
|
p++;
|
||
|
}
|
||
|
|
||
|
string token;
|
||
|
while (p < str.length() && str[p] != FUNCTION_PARAMETER_SEPARATOR) {
|
||
|
if (p + 1 < str.length() && str[p] == VARIABLE_PREFIX &&
|
||
|
str[p + 1] == VARIABLE_OPEN_BRACE) {
|
||
|
// Skip a nested variable reference.
|
||
|
if (expand) {
|
||
|
token += r_expand_variable(str, p, (ExpandedVariable *)NULL);
|
||
|
} else {
|
||
|
token += r_scan_variable(str, p);
|
||
|
}
|
||
|
} else {
|
||
|
token += str[p];
|
||
|
p++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Back up past trailing whitespace.
|
||
|
size_t q = token.length();
|
||
|
while (q > 0 && isspace(token[q - 1])) {
|
||
|
q--;
|
||
|
}
|
||
|
|
||
|
tokens.push_back(token.substr(0, q));
|
||
|
p++;
|
||
|
|
||
|
if (p == str.length()) {
|
||
|
// In this case, we have just read past a trailing comma symbol
|
||
|
// at the end of the string, so we have one more empty token.
|
||
|
tokens.push_back(string());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::tokenize_numeric_pair
|
||
|
// Access: Public
|
||
|
// Description: This function is used by all the numeric comparision
|
||
|
// functions, e.g. nne, nlt, etc. It splits the string
|
||
|
// up into two parameters based on commas, and evaluates
|
||
|
// each parameter as a number, into a and b. It returns
|
||
|
// true if successful, or false if there was some user
|
||
|
// error.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
bool PPScope::
|
||
|
tokenize_numeric_pair(const string &str, double &a, double &b) {
|
||
|
vector<string> words;
|
||
|
tokenize_params(str, words, true);
|
||
|
if (words.size() != 2) {
|
||
|
cerr << words.size() << " parameters supplied when two were expected:\n"
|
||
|
<< str << "\n";
|
||
|
errors_occurred = true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
double results[2];
|
||
|
|
||
|
for (int i = 0; i < 2; i++) {
|
||
|
const char *param = words[i].c_str();
|
||
|
char *n;
|
||
|
results[i] = strtod(param, &n);
|
||
|
if (*n != '\0') {
|
||
|
// strtod failed--not a numeric representation.
|
||
|
cerr << "Warning: " << words[i] << " is not a number.\n";
|
||
|
if (n == param) {
|
||
|
results[i] = 0.0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
a = results[0];
|
||
|
b = results[1];
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::tokenize_ints
|
||
|
// Access: Public
|
||
|
// Description: This function is used by the arithmetic functions +,
|
||
|
// -, etc. It separates the string into parameters
|
||
|
// based on the comma, interprets each parameter as an
|
||
|
// integer, and fills up the indicated vector.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
bool PPScope::
|
||
|
tokenize_ints(const string &str, vector<int> &tokens) {
|
||
|
vector<string> words;
|
||
|
tokenize_params(str, words, true);
|
||
|
|
||
|
vector<string>::const_iterator wi;
|
||
|
for (wi = words.begin(); wi != words.end(); ++wi) {
|
||
|
const char *param = (*wi).c_str();
|
||
|
char *n;
|
||
|
int result = strtol(param, &n, 0);
|
||
|
if (*n != '\0') {
|
||
|
// strtol failed--not an integer.
|
||
|
cerr << "Warning: " << param << " is not an integer.\n";
|
||
|
if (n == param) {
|
||
|
result = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
tokens.push_back(result);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::scan_to_whitespace
|
||
|
// Access: Public
|
||
|
// Description: Scans to the end of the first whitespace-delimited
|
||
|
// word in the indicated string, even if it includes a
|
||
|
// nested variable reference (which is itself allowed to
|
||
|
// contain whitespace).
|
||
|
//
|
||
|
// On input, str is a string, and start is the starting
|
||
|
// position within the string of the scan; it should
|
||
|
// point to a non-whitespace character.
|
||
|
//
|
||
|
// The return value is the position within the string of
|
||
|
// the first whitespace character encountered at its
|
||
|
// original position or later, that is not part of a
|
||
|
// variable reference. All variable references are left
|
||
|
// unexpanded.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
size_t PPScope::
|
||
|
scan_to_whitespace(const string &str, size_t start) {
|
||
|
size_t p = start;
|
||
|
while (p < str.length() && !isspace(str[p])) {
|
||
|
string token;
|
||
|
if (p + 1 < str.length() && str[p] == VARIABLE_PREFIX &&
|
||
|
str[p + 1] == VARIABLE_OPEN_BRACE) {
|
||
|
// Skip a nested variable reference.
|
||
|
r_scan_variable(str, p);
|
||
|
|
||
|
} else {
|
||
|
p++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::format_int
|
||
|
// Access: Private, Static
|
||
|
// Description: Formats the indicated integer as a string and returns
|
||
|
// the string.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
format_int(int num) {
|
||
|
char buffer[32];
|
||
|
sprintf(buffer, "%d", num);
|
||
|
return buffer;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::p_set_variable
|
||
|
// Access: Private
|
||
|
// Description: The private implementation of p_set_variable.
|
||
|
// Returns true if the variable's definition is found
|
||
|
// and set, false otherwise.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
bool PPScope::
|
||
|
p_set_variable(const string &varname, const string &definition) {
|
||
|
Variables::iterator vi;
|
||
|
vi = _variables.find(varname);
|
||
|
if (vi != _variables.end()) {
|
||
|
(*vi).second = definition;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (_parent_scope != (PPScope *)NULL) {
|
||
|
return _parent_scope->p_set_variable(varname, definition);
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::p_get_variable
|
||
|
// Access: Private
|
||
|
// Description: The private implementation of get_variable(). This
|
||
|
// checks the local scope only; it does not check the
|
||
|
// stack. It returns true if the variable is defined,
|
||
|
// false otherwise..
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
bool PPScope::
|
||
|
p_get_variable(const string &varname, string &result) {
|
||
|
Variables::const_iterator vi;
|
||
|
vi = _variables.find(varname);
|
||
|
if (vi != _variables.end()) {
|
||
|
result = (*vi).second;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (varname == "RELDIR" &&
|
||
|
_directory != (PPDirectory *)NULL &&
|
||
|
current_output_directory != (PPDirectory *)NULL) {
|
||
|
// $[RELDIR] is a special variable name that evaluates to the
|
||
|
// relative directory of the current scope to the current output
|
||
|
// directory.
|
||
|
result = current_output_directory->get_rel_to(_directory);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (varname == "DEPENDS_INDEX" &&
|
||
|
_directory != (PPDirectory *)NULL) {
|
||
|
// $[DEPENDS_INDEX] is another special variable name that
|
||
|
// evaluates to the numeric sorting index assigned to this
|
||
|
// directory based on its dependency relationship with other
|
||
|
// directories. It's useful primarily for debugging.
|
||
|
char buffer[32];
|
||
|
sprintf(buffer, "%d", _directory->get_depends_index());
|
||
|
result = buffer;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (_parent_scope != (PPScope *)NULL) {
|
||
|
return _parent_scope->p_get_variable(varname, result);
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::r_expand_string
|
||
|
// Access: Private
|
||
|
// Description: The recursive implementation of expand_string().
|
||
|
// This function detects cycles in the variable
|
||
|
// expansion by storing the set of variable names that
|
||
|
// have thus far been expanded in the linked list.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
r_expand_string(const string &str, PPScope::ExpandedVariable *expanded) {
|
||
|
string result;
|
||
|
|
||
|
// Search for a variable reference.
|
||
|
size_t p = 0;
|
||
|
while (p < str.length()) {
|
||
|
if (p + 1 < str.length() && str[p] == VARIABLE_PREFIX &&
|
||
|
str[p + 1] == VARIABLE_OPEN_BRACE) {
|
||
|
// Here's a nested variable! Expand it fully.
|
||
|
result += r_expand_variable(str, p, expanded);
|
||
|
|
||
|
} else {
|
||
|
result += str[p];
|
||
|
p++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::r_scan_variable
|
||
|
// Access: Private
|
||
|
// Description: Scans past a single variable reference without
|
||
|
// expanding it. On input, str is a string containing a
|
||
|
// variable reference (among other stuff), and vp is the
|
||
|
// position within the string of the prefix character at
|
||
|
// the beginning of the variable reference.
|
||
|
//
|
||
|
// On output, vp is set to the position within the
|
||
|
// string of the first character after the variable
|
||
|
// reference's closing bracket. The variable reference
|
||
|
// itself is returned.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
r_scan_variable(const string &str, size_t &vp) {
|
||
|
|
||
|
// Search for the end of the variable name: an unmatched square
|
||
|
// bracket.
|
||
|
size_t start = vp;
|
||
|
size_t p = vp + 2;
|
||
|
while (p < str.length() && str[p] != VARIABLE_CLOSE_BRACE) {
|
||
|
if (p + 1 < str.length() && str[p] == VARIABLE_PREFIX &&
|
||
|
str[p + 1] == VARIABLE_OPEN_BRACE) {
|
||
|
// Here's a nested variable! Scan past it, matching braces
|
||
|
// properly.
|
||
|
r_scan_variable(str, p);
|
||
|
} else {
|
||
|
p++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (p < str.length()) {
|
||
|
assert(str[p] == VARIABLE_CLOSE_BRACE);
|
||
|
p++;
|
||
|
} else {
|
||
|
cerr << "Warning! Unclosed variable reference:\n"
|
||
|
<< str.substr(vp) << "\n";
|
||
|
}
|
||
|
|
||
|
vp = p;
|
||
|
return str.substr(start, vp - start);
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::r_expand_variable
|
||
|
// Access: Private
|
||
|
// Description: Expands a single variable reference. On input, str
|
||
|
// is a string containing a variable reference (among
|
||
|
// other stuff), and vp is the position within the
|
||
|
// string of the prefix character at the beginning of
|
||
|
// the variable reference.
|
||
|
//
|
||
|
// On output, vp is set to the position within the
|
||
|
// string of the first character after the variable
|
||
|
// reference's closing bracket, and the string expansion
|
||
|
// of the variable reference is returned.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
r_expand_variable(const string &str, size_t &vp,
|
||
|
PPScope::ExpandedVariable *expanded) {
|
||
|
string varname;
|
||
|
|
||
|
size_t whitespace_at = 0;
|
||
|
size_t open_nested_at = 0;
|
||
|
|
||
|
// Search for the end of the variable name: an unmatched square
|
||
|
// bracket.
|
||
|
size_t p = vp + 2;
|
||
|
while (p < str.length() && str[p] != VARIABLE_CLOSE_BRACE) {
|
||
|
if (p + 1 < str.length() && str[p] == VARIABLE_PREFIX &&
|
||
|
str[p + 1] == VARIABLE_OPEN_BRACE) {
|
||
|
if (whitespace_at != 0) {
|
||
|
// Once we have encountered whitespace, we don't expand
|
||
|
// variables inline anymore. These are now function
|
||
|
// parameters, and might need to be expanded in some other
|
||
|
// scope.
|
||
|
varname += r_scan_variable(str, p);
|
||
|
} else {
|
||
|
varname += r_expand_variable(str, p, expanded);
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
if (open_nested_at == 0 && str[p] == VARIABLE_OPEN_NESTED) {
|
||
|
open_nested_at = p - (vp + 2);
|
||
|
}
|
||
|
if (open_nested_at == 0 && whitespace_at == 0 && isspace(str[p])) {
|
||
|
whitespace_at = p - (vp + 2);
|
||
|
}
|
||
|
varname += str[p];
|
||
|
p++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (p < str.length()) {
|
||
|
assert(str[p] == VARIABLE_CLOSE_BRACE);
|
||
|
p++;
|
||
|
} else {
|
||
|
cerr << "Warning! Unclosed variable reference:\n"
|
||
|
<< str.substr(vp) << "\n";
|
||
|
}
|
||
|
|
||
|
vp = p;
|
||
|
|
||
|
// Check for a function expansion.
|
||
|
if (whitespace_at != 0) {
|
||
|
string funcname = varname.substr(0, whitespace_at);
|
||
|
p = whitespace_at;
|
||
|
while (p < varname.length() && isspace(varname[p])) {
|
||
|
p++;
|
||
|
}
|
||
|
string params = varname.substr(p);
|
||
|
|
||
|
// Is it a user-defined function?
|
||
|
const PPSubroutine *sub = PPSubroutine::get_func(funcname);
|
||
|
if (sub != (const PPSubroutine *)NULL) {
|
||
|
return expand_function(funcname, sub, params);
|
||
|
}
|
||
|
|
||
|
// Is it a built-in function?
|
||
|
if (funcname == "isfullpath") {
|
||
|
return expand_isfullpath(params);
|
||
|
} else if (funcname == "osfilename") {
|
||
|
return expand_osfilename(params);
|
||
|
} else if (funcname == "unixfilename") {
|
||
|
return expand_unixfilename(params);
|
||
|
} else if (funcname == "unixshortname") {
|
||
|
return expand_unixshortname(params);
|
||
|
} else if (funcname == "cygpath_w") {
|
||
|
// This maps to osfilename for historical reasons.
|
||
|
return expand_osfilename(params);
|
||
|
} else if (funcname == "cygpath_p") {
|
||
|
// This maps to unixfilename for historical reasons.
|
||
|
return expand_unixfilename(params);
|
||
|
} else if (funcname == "wildcard") {
|
||
|
return expand_wildcard(params);
|
||
|
} else if (funcname == "isdir") {
|
||
|
return expand_isdir(params);
|
||
|
} else if (funcname == "isfile") {
|
||
|
return expand_isfile(params);
|
||
|
} else if (funcname == "libtest") {
|
||
|
return expand_libtest(params);
|
||
|
} else if (funcname == "bintest") {
|
||
|
return expand_bintest(params);
|
||
|
} else if (funcname == "shell") {
|
||
|
return expand_shell(params);
|
||
|
} else if (funcname == "standardize") {
|
||
|
return expand_standardize(params);
|
||
|
} else if (funcname == "canonical") {
|
||
|
return expand_canonical(params);
|
||
|
} else if (funcname == "length") {
|
||
|
return expand_length(params);
|
||
|
} else if (funcname == "substr") {
|
||
|
return expand_substr(params);
|
||
|
} else if (funcname == "findstring") {
|
||
|
return expand_findstring(params);
|
||
|
} else if (funcname == "dir") {
|
||
|
return expand_dir(params);
|
||
|
} else if (funcname == "notdir") {
|
||
|
return expand_notdir(params);
|
||
|
} else if (funcname == "suffix") {
|
||
|
return expand_suffix(params);
|
||
|
} else if (funcname == "basename") {
|
||
|
return expand_basename(params);
|
||
|
} else if (funcname == "makeguid") {
|
||
|
return expand_makeguid(params);
|
||
|
} else if (funcname == "word") {
|
||
|
return expand_word(params);
|
||
|
} else if (funcname == "wordlist") {
|
||
|
return expand_wordlist(params);
|
||
|
} else if (funcname == "words") {
|
||
|
return expand_words(params);
|
||
|
} else if (funcname == "firstword") {
|
||
|
return expand_firstword(params);
|
||
|
} else if (funcname == "patsubst") {
|
||
|
return expand_patsubst(params, true);
|
||
|
} else if (funcname == "patsubstw") {
|
||
|
return expand_patsubst(params, false);
|
||
|
} else if (funcname == "subst") {
|
||
|
return expand_subst(params);
|
||
|
} else if (funcname == "wordsubst") {
|
||
|
return expand_wordsubst(params);
|
||
|
} else if (funcname == "filter") {
|
||
|
return expand_filter(params);
|
||
|
} else if (funcname == "filter_out" || funcname == "filter-out") {
|
||
|
return expand_filter_out(params);
|
||
|
} else if (funcname == "join") {
|
||
|
return expand_join(params);
|
||
|
} else if (funcname == "sort") {
|
||
|
return expand_sort(params);
|
||
|
} else if (funcname == "unique") {
|
||
|
return expand_unique(params);
|
||
|
} else if (funcname == "matrix") {
|
||
|
return expand_matrix(params);
|
||
|
} else if (funcname == "if") {
|
||
|
return expand_if(params);
|
||
|
} else if (funcname == "eq") {
|
||
|
return expand_eq(params);
|
||
|
} else if (funcname == "defined") {
|
||
|
return expand_defined(params);
|
||
|
} else if (funcname == "ne") {
|
||
|
return expand_ne(params);
|
||
|
} else if (funcname == "=" || funcname == "==") {
|
||
|
return expand_eqn(params);
|
||
|
} else if (funcname == "!=") {
|
||
|
return expand_nen(params);
|
||
|
} else if (funcname == "<") {
|
||
|
return expand_ltn(params);
|
||
|
} else if (funcname == "<=") {
|
||
|
return expand_len(params);
|
||
|
} else if (funcname == ">") {
|
||
|
return expand_gtn(params);
|
||
|
} else if (funcname == ">=") {
|
||
|
return expand_gen(params);
|
||
|
} else if (funcname == "+") {
|
||
|
return expand_plus(params);
|
||
|
} else if (funcname == "-") {
|
||
|
return expand_minus(params);
|
||
|
} else if (funcname == "*") {
|
||
|
return expand_times(params);
|
||
|
} else if (funcname == "/") {
|
||
|
return expand_divide(params);
|
||
|
} else if (funcname == "%") {
|
||
|
return expand_modulo(params);
|
||
|
} else if (funcname == "not") {
|
||
|
return expand_not(params);
|
||
|
} else if (funcname == "or") {
|
||
|
return expand_or(params);
|
||
|
} else if (funcname == "and") {
|
||
|
return expand_and(params);
|
||
|
} else if (funcname == "upcase") {
|
||
|
return expand_upcase(params);
|
||
|
} else if (funcname == "downcase") {
|
||
|
return expand_downcase(params);
|
||
|
} else if (funcname == "cdefine") {
|
||
|
return expand_cdefine(params);
|
||
|
} else if (funcname == "closure") {
|
||
|
return expand_closure(params);
|
||
|
} else if (funcname == "unmapped") {
|
||
|
return expand_unmapped(params);
|
||
|
} else if (funcname == "dependencies") {
|
||
|
return expand_dependencies(params);
|
||
|
} else if (funcname == "foreach") {
|
||
|
return expand_foreach(params);
|
||
|
} else if (funcname == "forscopes") {
|
||
|
return expand_forscopes(params);
|
||
|
}
|
||
|
|
||
|
// It must be a map variable.
|
||
|
return expand_map_variable(funcname, params);
|
||
|
}
|
||
|
|
||
|
// Now we have the variable name; was it previously expanded?
|
||
|
ExpandedVariable *ev;
|
||
|
for (ev = expanded; ev != (ExpandedVariable *)NULL; ev = ev->_next) {
|
||
|
if (ev->_varname == varname) {
|
||
|
// Yes, this is a cyclical expansion.
|
||
|
cerr << "Ignoring cyclical expansion of " << varname << "\n";
|
||
|
return string();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// And now expand the variable.
|
||
|
|
||
|
string expansion;
|
||
|
|
||
|
// Check for a special inline patsubst operation, like GNU make:
|
||
|
// $[varname:%.c=%.o]
|
||
|
string patsubst;
|
||
|
bool got_patsubst = false;
|
||
|
p = varname.find(variable_patsubst);
|
||
|
if (p != string::npos) {
|
||
|
got_patsubst = true;
|
||
|
patsubst = varname.substr(p + variable_patsubst.length());
|
||
|
varname = varname.substr(0, p);
|
||
|
}
|
||
|
|
||
|
// Check for special scoping operators in the variable name.
|
||
|
p = varname.find(VARIABLE_OPEN_NESTED);
|
||
|
if (p != string::npos && varname[varname.length() - 1] == VARIABLE_CLOSE_NESTED) {
|
||
|
size_t q = varname.length() - 1;
|
||
|
string scope_names = varname.substr(p + 1, q - (p + 1));
|
||
|
varname = varname.substr(0, p);
|
||
|
expansion = expand_variable_nested(varname, scope_names);
|
||
|
|
||
|
} else {
|
||
|
// No special scoping; just expand the variable name.
|
||
|
expansion = get_variable(varname);
|
||
|
}
|
||
|
|
||
|
// Finally, recursively expand any variable references in the
|
||
|
// variable's expansion.
|
||
|
ExpandedVariable new_var;
|
||
|
new_var._varname = varname;
|
||
|
new_var._next = expanded;
|
||
|
string result = r_expand_string(expansion, &new_var);
|
||
|
|
||
|
// And *then* apply any inline patsubst.
|
||
|
if (got_patsubst) {
|
||
|
vector<string> tokens;
|
||
|
tokenize(patsubst, tokens, VARIABLE_PATSUBST_DELIM);
|
||
|
|
||
|
if (tokens.size() != 2) {
|
||
|
cerr << "inline patsubst should be of the form "
|
||
|
<< VARIABLE_PREFIX << VARIABLE_OPEN_BRACE << "varname"
|
||
|
<< VARIABLE_PATSUBST << PATTERN_WILDCARD << ".c"
|
||
|
<< VARIABLE_PATSUBST_DELIM << PATTERN_WILDCARD << ".o"
|
||
|
<< VARIABLE_CLOSE_BRACE << ".\n";
|
||
|
errors_occurred = true;
|
||
|
} else {
|
||
|
PPFilenamePattern from(tokens[0]);
|
||
|
PPFilenamePattern to(tokens[1]);
|
||
|
|
||
|
if (!from.has_wildcard() || !to.has_wildcard()) {
|
||
|
cerr << "The two parameters of inline patsubst must both include "
|
||
|
<< PATTERN_WILDCARD << ".\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
// Split the expansion into tokens based on the spaces.
|
||
|
vector<string> words;
|
||
|
tokenize_whitespace(result, words);
|
||
|
|
||
|
vector<string>::iterator wi;
|
||
|
for (wi = words.begin(); wi != words.end(); ++wi) {
|
||
|
(*wi) = to.transform(*wi, from);
|
||
|
}
|
||
|
|
||
|
result = repaste(words, " ");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_variable_nested
|
||
|
// Access: Private
|
||
|
// Description: Expands a variable reference of the form
|
||
|
// $[varname(scope scope scope)]. This means to
|
||
|
// concatenate the expansions of the variable in all of
|
||
|
// the named scopes.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_variable_nested(const string &varname,
|
||
|
const string &scope_names) {
|
||
|
if (_named_scopes == (PPNamedScopes *)NULL) {
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
vector<string> names;
|
||
|
tokenize_whitespace(scope_names, names);
|
||
|
|
||
|
// Get all of the named scopes.
|
||
|
PPNamedScopes::Scopes scopes;
|
||
|
|
||
|
vector<string>::const_iterator ni;
|
||
|
for (ni = names.begin(); ni != names.end(); ++ni) {
|
||
|
const string &name = (*ni);
|
||
|
_named_scopes->get_scopes(name, scopes);
|
||
|
}
|
||
|
|
||
|
if (scopes.empty()) {
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
// Now go through the scopes and build up the results.
|
||
|
vector<string> results;
|
||
|
|
||
|
PPNamedScopes::Scopes::const_iterator si;
|
||
|
for (si = scopes.begin(); si != scopes.end(); ++si) {
|
||
|
PPScope *scope = (*si);
|
||
|
string nested = scope->expand_variable(varname);
|
||
|
if (!nested.empty()) {
|
||
|
results.push_back(nested);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
string result = repaste(results, " ");
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_isfullpath
|
||
|
// Access: Private
|
||
|
// Description: Expands the "isfullpath" function variable. This
|
||
|
// returns true (actually, the same as its input) if the
|
||
|
// input parameter is a fully-specified path name,
|
||
|
// meaning it begins with a slash for unix_platform, and
|
||
|
// it begins with a slash or backslash, with an optional
|
||
|
// drive leterr, for windows_platform.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_isfullpath(const string ¶ms) {
|
||
|
Filename filename = trim_blanks(expand_string(params));
|
||
|
|
||
|
string result;
|
||
|
if (filename.is_fully_qualified()) {
|
||
|
result = filename;
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_osfilename
|
||
|
// Access: Private
|
||
|
// Description: Expands the "osfilename" function variable. This
|
||
|
// converts the filename from a Unix-style filename
|
||
|
// (e.g. with slash separators) to a platform-specific
|
||
|
// filename.
|
||
|
//
|
||
|
// This follows the same rules of Panda filename
|
||
|
// conversion; i.e. forward slashes become backslashes,
|
||
|
// and $PANDA_ROOT prefixes full pathnames, unless the
|
||
|
// topmost directory name is a single letter.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_osfilename(const string ¶ms) {
|
||
|
// Split the parameter into tokens based on the spaces.
|
||
|
vector<string> words;
|
||
|
tokenize_whitespace(expand_string(params), words);
|
||
|
|
||
|
vector<string>::iterator wi;
|
||
|
for (wi = words.begin(); wi != words.end(); ++wi) {
|
||
|
Filename filename = (*wi);
|
||
|
(*wi) = filename.to_os_specific();
|
||
|
}
|
||
|
|
||
|
string result = repaste(words, " ");
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_unixfilename
|
||
|
// Access: Private
|
||
|
// Description: Expands the "unixfilename" function variable. This
|
||
|
// converts the filename from a platform-specific
|
||
|
// filename to a Unix-style filename (e.g. with slash
|
||
|
// separators).
|
||
|
//
|
||
|
// This follows the rules of Panda filename conversion.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_unixfilename(const string ¶ms) {
|
||
|
// Split the parameter into tokens based on the spaces.
|
||
|
vector<string> words;
|
||
|
tokenize_whitespace(expand_string(params), words);
|
||
|
|
||
|
vector<string>::iterator wi;
|
||
|
for (wi = words.begin(); wi != words.end(); ++wi) {
|
||
|
Filename filename = Filename::from_os_specific(*wi);
|
||
|
(*wi) = filename;
|
||
|
}
|
||
|
|
||
|
string result = repaste(words, " ");
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_unixshortname
|
||
|
// Access: Private
|
||
|
// Description: Expands the "unixshortname" function variable. This
|
||
|
// converts the filename from a platform-specific
|
||
|
// filename to a Unix-style filename (e.g. with slash
|
||
|
// separators), just like the unixfilename variable.
|
||
|
//
|
||
|
// On Windows, this also specifically converts the
|
||
|
// Windows-specific filename to 8.3 convention before
|
||
|
// converting it to Unix style. This can be a cheesy
|
||
|
// way to work around embedded spaces in the filename.
|
||
|
//
|
||
|
// Unlike unixfilename, this parameter accepts only one
|
||
|
// filename. However, the filename may contain embedded
|
||
|
// spaces.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_unixshortname(const string ¶ms) {
|
||
|
Filename filename = Filename::from_os_specific(params);
|
||
|
filename = Filename::from_os_specific(filename.to_os_short_name());
|
||
|
|
||
|
return filename;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_wildcard
|
||
|
// Access: Private
|
||
|
// Description: Expands the "wildcard" function variable. This
|
||
|
// returns the set of files matched by the parameters
|
||
|
// with shell matching characters.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_wildcard(const string ¶ms) {
|
||
|
vector<string> results;
|
||
|
glob_string(expand_string(params), results);
|
||
|
|
||
|
string result = repaste(results, " ");
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_isdir
|
||
|
// Access: Private
|
||
|
// Description: Expands the "isdir" function variable. This
|
||
|
// returns true if the parameter exists and is a
|
||
|
// directory, or false otherwise. This actually expands
|
||
|
// the parameter(s) with shell globbing characters,
|
||
|
// similar to the "wildcard" function, and looks only at
|
||
|
// the first expansion.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_isdir(const string ¶ms) {
|
||
|
vector<string> results;
|
||
|
glob_string(expand_string(params), results);
|
||
|
|
||
|
if (results.empty()) {
|
||
|
// No matching file, too bad.
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
Filename filename = results[0];
|
||
|
if (filename.is_directory()) {
|
||
|
return filename.get_fullpath();
|
||
|
} else {
|
||
|
return string();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_isfile
|
||
|
// Access: Private
|
||
|
// Description: Expands the "isfile" function variable. This
|
||
|
// returns true if the parameter exists and is a
|
||
|
// regular file, or false otherwise. This actually
|
||
|
// expands the parameter(s) with shell globbing
|
||
|
// characters, similar to the "wildcard" function, and
|
||
|
// looks only at the first expansion.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_isfile(const string ¶ms) {
|
||
|
vector<string> results;
|
||
|
glob_string(expand_string(params), results);
|
||
|
|
||
|
if (results.empty()) {
|
||
|
// No matching file, too bad.
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
Filename filename = results[0];
|
||
|
if (filename.is_regular_file()) {
|
||
|
return filename.get_fullpath();
|
||
|
} else {
|
||
|
return string();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_libtest
|
||
|
// Access: Private
|
||
|
// Description: Expands the "libtest" function variable. This
|
||
|
// serves as a poor man's autoconf feature to check to
|
||
|
// see if a library by the given name exists on the
|
||
|
// indicated search path, or on the system search path.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_libtest(const string ¶ms) {
|
||
|
// Get the parameters out based on commas. The first parameter is a
|
||
|
// space-separated set of directories to search, the second
|
||
|
// parameter is a space-separated set of library names.
|
||
|
vector<string> tokens;
|
||
|
tokenize_params(params, tokens, true);
|
||
|
|
||
|
if (tokens.size() != 2) {
|
||
|
cerr << "libtest requires two parameters.\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
DSearchPath directories;
|
||
|
directories.append_path(tokens[0], " \n\t");
|
||
|
|
||
|
// Also add the system directories to the list, whatever we think
|
||
|
// those should be. Here we have to make a few assumptions.
|
||
|
#ifdef WIN32
|
||
|
const char *windir = getenv("WINDIR");
|
||
|
if (windir != (const char *)NULL) {
|
||
|
Filename windir_filename = Filename::from_os_specific(windir);
|
||
|
directories.append_directory(Filename(windir_filename, "System"));
|
||
|
directories.append_directory(Filename(windir_filename, "System32"));
|
||
|
}
|
||
|
|
||
|
const char *lib = getenv("LIB");
|
||
|
if (lib != (const char *)NULL) {
|
||
|
vector<string> lib_dirs;
|
||
|
tokenize(lib, lib_dirs, ";");
|
||
|
vector<string>::const_iterator li;
|
||
|
for (li = lib_dirs.begin(); li != lib_dirs.end(); ++li) {
|
||
|
directories.append_directory(Filename::from_os_specific(*li));
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// We'll also check the Unix standard places, even if we're building
|
||
|
// Windows, since we might be using Cygwin.
|
||
|
|
||
|
// Check LD_LIBRARY_PATH.
|
||
|
const char *ld_library_path = getenv("LD_LIBRARY_PATH");
|
||
|
if (ld_library_path != (const char *)NULL) {
|
||
|
directories.append_path(ld_library_path, ":");
|
||
|
}
|
||
|
|
||
|
directories.append_directory("/lib");
|
||
|
directories.append_directory("/usr/lib");
|
||
|
|
||
|
vector<string> libnames;
|
||
|
tokenize_whitespace(tokens[1], libnames);
|
||
|
|
||
|
if (libnames.empty()) {
|
||
|
// No libraries is a default "false".
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
// We only bother to search for the first library name in the list.
|
||
|
Filename libname = libnames[0];
|
||
|
|
||
|
bool found = false;
|
||
|
|
||
|
#ifdef WIN32
|
||
|
if (libname.get_extension() != string("lib")) {
|
||
|
libname = "lib" + libname.get_basename() + ".lib";
|
||
|
}
|
||
|
found = libname.resolve_filename(directories);
|
||
|
if (!found) {
|
||
|
libname.set_extension("dll");
|
||
|
found = libname.resolve_filename(directories);
|
||
|
}
|
||
|
|
||
|
#else // WIN32
|
||
|
libname = "lib" + libname.get_basename() + ".a";
|
||
|
found = libname.resolve_filename(directories);
|
||
|
if (!found) {
|
||
|
libname.set_extension("so");
|
||
|
found = libname.resolve_filename(directories);
|
||
|
}
|
||
|
#ifdef HAVE_OSX
|
||
|
if (!found) {
|
||
|
libname.set_extension("dylib");
|
||
|
found = libname.resolve_filename(directories);
|
||
|
}
|
||
|
#endif // HAVE_OSX
|
||
|
#endif // WIN32
|
||
|
|
||
|
if (found) {
|
||
|
return libname.get_fullpath();
|
||
|
} else {
|
||
|
return string();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_bintest
|
||
|
// Access: Private
|
||
|
// Description: Expands the "bintest" function variable. This
|
||
|
// serves as a poor man's autoconf feature to check to
|
||
|
// see if an executable program by the given name exists
|
||
|
// on the indicated search path, or on the system search
|
||
|
// path.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_bintest(const string ¶ms) {
|
||
|
// We only have one parameter: the filename of the executable. We
|
||
|
// always search for it on the path.
|
||
|
Filename binname = Filename::from_os_specific(expand_string(params));
|
||
|
|
||
|
if (binname.empty()) {
|
||
|
// No binary, no exist.
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
// An explicit path from the root does not require a search.
|
||
|
if (binname.is_fully_qualified()) {
|
||
|
if (binname.exists()) {
|
||
|
return binname.get_fullpath();
|
||
|
} else {
|
||
|
return string();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const char *path = getenv("PATH");
|
||
|
if (path == (const char *)NULL) {
|
||
|
// If the path is undefined, too bad.
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
string pathvar(path);
|
||
|
|
||
|
DSearchPath directories;
|
||
|
|
||
|
#ifdef WIN32
|
||
|
if (pathvar.find(';') != string::npos) {
|
||
|
// If the path contains semicolons, it's a native Windows-style
|
||
|
// path: split it up based on semicolons, and convert each
|
||
|
// directory from windows form.
|
||
|
vector<string> path_dirs;
|
||
|
tokenize(path, path_dirs, ";");
|
||
|
vector<string>::const_iterator pi;
|
||
|
for (pi = path_dirs.begin(); pi != path_dirs.end(); ++pi) {
|
||
|
directories.append_directory(Filename::from_os_specific(*pi));
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
// Otherwise, assume it's a Cygwin-style path: split it up based
|
||
|
// on colons.
|
||
|
directories.append_path(pathvar, ":");
|
||
|
}
|
||
|
#else
|
||
|
directories.append_path(pathvar, ":");
|
||
|
#endif
|
||
|
|
||
|
#ifdef WIN32
|
||
|
bool found = binname.resolve_filename(directories, "exe");
|
||
|
#else
|
||
|
bool found = binname.resolve_filename(directories);
|
||
|
#endif
|
||
|
|
||
|
if (found) {
|
||
|
return binname.get_fullpath();
|
||
|
} else {
|
||
|
return string();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_shell
|
||
|
// Access: Private
|
||
|
// Description: Expands the "shell" function variable. This executes
|
||
|
// the given command in a subprocess and returns the
|
||
|
// standard output.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_shell(const string ¶ms) {
|
||
|
#ifdef WIN32_VC
|
||
|
cerr << "$[shell] is not presently supported on Win32 without Cygwin.\n";
|
||
|
errors_occurred = true;
|
||
|
string output;
|
||
|
|
||
|
#else // WIN32_VC
|
||
|
// We run $[shell] commands within the directory indicated by
|
||
|
// $[THISDIRPREFIX]. This way, local filenames will be expanded the
|
||
|
// way we expect.
|
||
|
string dirname = trim_blanks(expand_variable("THISDIRPREFIX"));
|
||
|
|
||
|
string command = expand_string(params);
|
||
|
int pid, status;
|
||
|
|
||
|
int pd[2];
|
||
|
if (pipe(pd) < 0) {
|
||
|
// pipe() failed.
|
||
|
perror("pipe");
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
pid = fork();
|
||
|
if (pid < 0) {
|
||
|
// fork() failed.
|
||
|
perror("fork");
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
if (pid == 0) {
|
||
|
// Child.
|
||
|
|
||
|
if (!dirname.empty()) {
|
||
|
// We don't have to restore the directory after we're done,
|
||
|
// because we're doing the chdir() call only within the child
|
||
|
// process.
|
||
|
if (chdir(dirname.c_str()) < 0) {
|
||
|
perror("chdir");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
close(pd[0]);
|
||
|
dup2(pd[1], STDOUT_FILENO);
|
||
|
char *argv[4];
|
||
|
argv[0] = (char *)"sh";
|
||
|
argv[1] = (char *)"-c";
|
||
|
argv[2] = (char *)command.c_str();
|
||
|
argv[3] = (char *)NULL;
|
||
|
execv("/bin/sh", argv);
|
||
|
exit(127);
|
||
|
}
|
||
|
|
||
|
// Parent. Wait for the child to terminate, and read from its
|
||
|
// output while we're waiting.
|
||
|
close(pd[1]);
|
||
|
bool child_done = false;
|
||
|
bool pipe_closed = false;
|
||
|
string output;
|
||
|
|
||
|
while (!child_done && !pipe_closed) {
|
||
|
static const int buffer_size = 1024;
|
||
|
char buffer[buffer_size];
|
||
|
int read_bytes = (int)read(pd[0], buffer, buffer_size);
|
||
|
if (read_bytes < 0) {
|
||
|
perror("read");
|
||
|
} else if (read_bytes == 0) {
|
||
|
pipe_closed = true;
|
||
|
} else {
|
||
|
output += string(buffer, read_bytes);
|
||
|
}
|
||
|
|
||
|
if (!child_done) {
|
||
|
int waitresult = waitpid(pid, &status, WNOHANG);
|
||
|
if (waitresult < 0) {
|
||
|
if (errno != EINTR) {
|
||
|
perror("waitpid");
|
||
|
return string();
|
||
|
}
|
||
|
} else if (waitresult > 0) {
|
||
|
child_done = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
close(pd[0]);
|
||
|
#endif // WIN32_VC
|
||
|
|
||
|
// Now get the output. We split it into words and then reconnect
|
||
|
// it, to simulate the shell's backpop operator.
|
||
|
vector<string> results;
|
||
|
tokenize_whitespace(output, results);
|
||
|
|
||
|
string result = repaste(results, " ");
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_standardize
|
||
|
// Access: Private
|
||
|
// Description: Expands the "standardize" function variable. This
|
||
|
// converts the filename to standard form by removing
|
||
|
// consecutive repeated slashes and collapsing /../
|
||
|
// where possible.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_standardize(const string ¶ms) {
|
||
|
Filename filename = trim_blanks(expand_string(params));
|
||
|
if (filename.empty()) {
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
filename.standardize();
|
||
|
return filename.get_fullpath();
|
||
|
}
|
||
|
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_canonical
|
||
|
// Access: Private
|
||
|
// Description: Expands the "canonical" function variable. This
|
||
|
// converts this filename to a canonical name by
|
||
|
// replacing the directory part with the fully-qualified
|
||
|
// directory part. This is done by changing to that
|
||
|
// directory and calling getcwd().
|
||
|
//
|
||
|
// See filename::make_canonical() for a complete
|
||
|
// explanation of the implications of this and of the
|
||
|
// difference between this and standardize, above.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_canonical(const string ¶ms) {
|
||
|
Filename filename = trim_blanks(expand_string(params));
|
||
|
filename.make_canonical();
|
||
|
return filename.get_fullpath();
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_length
|
||
|
// Access: Private
|
||
|
// Description: Expands the "length" function variable. This returns
|
||
|
// the length of the argument in characters.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_length(const string ¶ms) {
|
||
|
string word = trim_blanks(expand_string(params));
|
||
|
|
||
|
char buffer[32];
|
||
|
sprintf(buffer, "%d", (int) word.length());
|
||
|
string result = buffer;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_substr
|
||
|
// Access: Private
|
||
|
// Description: Expands the "substr" function variable. $[substr
|
||
|
// S,E,string] returns the substring of "string"
|
||
|
// beginning at character S (1-based) and continuing to
|
||
|
// character E, inclusive.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_substr(const string ¶ms) {
|
||
|
// Split the string up into tokens based on the commas.
|
||
|
vector<string> tokens;
|
||
|
tokenize_params(params, tokens, true);
|
||
|
|
||
|
if (tokens.size() != 3) {
|
||
|
cerr << "substr requires three parameters.\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
int start = atoi(tokens[0].c_str());
|
||
|
int end = atoi(tokens[1].c_str());
|
||
|
|
||
|
if (end < start) {
|
||
|
// Following GNU make's convention, we swap start and end if
|
||
|
// they're out of order.
|
||
|
int t = end;
|
||
|
end = start;
|
||
|
start = t;
|
||
|
}
|
||
|
|
||
|
const string &word = tokens[2];
|
||
|
|
||
|
start = max(start, 1);
|
||
|
end = min(end, (int)word.length());
|
||
|
|
||
|
if (end < start) {
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
string result = word.substr(start - 1, end - start + 1);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_dir
|
||
|
// Access: Private
|
||
|
// Description: Expands the "dir" function variable. This returns
|
||
|
// the directory part of its filename argument(s), or ./
|
||
|
// if the words contain no slash.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_dir(const string ¶ms) {
|
||
|
// Split the parameter into tokens based on the spaces.
|
||
|
vector<string> words;
|
||
|
tokenize_whitespace(expand_string(params), words);
|
||
|
|
||
|
vector<string>::iterator wi;
|
||
|
for (wi = words.begin(); wi != words.end(); ++wi) {
|
||
|
string &word = (*wi);
|
||
|
|
||
|
size_t slash = word.rfind('/');
|
||
|
if (slash != string::npos) {
|
||
|
word = word.substr(0, slash + 1);
|
||
|
} else {
|
||
|
word = "./";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
string result = repaste(words, " ");
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_notdir
|
||
|
// Access: Private
|
||
|
// Description: Expands the "notdir" function variable. This returns
|
||
|
// everything following the rightmost slash, or the
|
||
|
// string itself if there is no slash.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_notdir(const string ¶ms) {
|
||
|
// Split the parameter into tokens based on the spaces.
|
||
|
vector<string> words;
|
||
|
tokenize_whitespace(expand_string(params), words);
|
||
|
|
||
|
vector<string>::iterator wi;
|
||
|
for (wi = words.begin(); wi != words.end(); ++wi) {
|
||
|
string &word = (*wi);
|
||
|
|
||
|
size_t slash = word.rfind('/');
|
||
|
if (slash != string::npos) {
|
||
|
word = word.substr(slash + 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
string result = repaste(words, " ");
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_suffix
|
||
|
// Access: Private
|
||
|
// Description: Expands the "suffix" function variable. This returns
|
||
|
// the filename extension, including a dot, if any.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_suffix(const string ¶ms) {
|
||
|
// Split the parameter into tokens based on the spaces.
|
||
|
vector<string> words;
|
||
|
tokenize_whitespace(expand_string(params), words);
|
||
|
|
||
|
vector<string>::iterator wi;
|
||
|
for (wi = words.begin(); wi != words.end(); ++wi) {
|
||
|
string &word = (*wi);
|
||
|
|
||
|
size_t dot = word.rfind('.');
|
||
|
if (dot != string::npos) {
|
||
|
string ext = word.substr(dot);
|
||
|
if (ext.find('/') == string::npos) {
|
||
|
word = ext;
|
||
|
} else {
|
||
|
word = string();
|
||
|
}
|
||
|
} else {
|
||
|
word = string();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
string result = repaste(words, " ");
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_basename
|
||
|
// Access: Private
|
||
|
// Description: Expands the "basename" function variable. This returns
|
||
|
// everything but the filename extension (including the
|
||
|
// directory, if any).
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_basename(const string ¶ms) {
|
||
|
// Split the parameter into tokens based on the spaces.
|
||
|
vector<string> words;
|
||
|
tokenize_whitespace(expand_string(params), words);
|
||
|
|
||
|
vector<string>::iterator wi;
|
||
|
for (wi = words.begin(); wi != words.end(); ++wi) {
|
||
|
string &word = (*wi);
|
||
|
|
||
|
size_t dot = word.rfind('.');
|
||
|
if (dot != string::npos) {
|
||
|
string ext = word.substr(dot);
|
||
|
if (ext.find('/') == string::npos) {
|
||
|
word = word.substr(0, dot);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
string result = repaste(words, " ");
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_makeguid
|
||
|
// Access: Private
|
||
|
// Description: Expands the GUID (global unique identifier) of the
|
||
|
// given name (generally a directory). A GUID looks
|
||
|
// like this: 398F2CC4-C683-26EB-3251-6FC996738F7F
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_makeguid(const string ¶ms) {
|
||
|
// Expand all of the parameters into a single string.
|
||
|
string expansion = trim_blanks(expand_string(params));
|
||
|
|
||
|
if (expansion.size() == 0) {
|
||
|
cerr << "makeguid requires an argument.\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
PP_MD5_CTX context;
|
||
|
unsigned char digest[16];
|
||
|
|
||
|
MD5Init(&context);
|
||
|
MD5Update(&context, reinterpret_cast<const unsigned char *>(expansion.data()),
|
||
|
expansion.size());
|
||
|
MD5Final(digest, &context);
|
||
|
|
||
|
string guid;
|
||
|
char hex[2];
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < 4; i++) {
|
||
|
sprintf(hex, "%02x", digest[i]);
|
||
|
guid.append(hex);
|
||
|
}
|
||
|
guid += "-";
|
||
|
|
||
|
for (i = 4; i < 6; i++) {
|
||
|
sprintf(hex, "%02x", digest[i]);
|
||
|
guid.append(hex);
|
||
|
}
|
||
|
guid += "-";
|
||
|
|
||
|
for (i = 6; i < 8; i++) {
|
||
|
sprintf(hex, "%02x", digest[i]);
|
||
|
guid.append(hex);
|
||
|
}
|
||
|
guid += "-";
|
||
|
|
||
|
for (i = 8; i < 10; i++) {
|
||
|
sprintf(hex, "%02x", digest[i]);
|
||
|
guid.append(hex);
|
||
|
}
|
||
|
guid += "-";
|
||
|
|
||
|
for (i = 10; i < 16; i++) {
|
||
|
sprintf(hex, "%02x", digest[i]);
|
||
|
guid.append(hex);
|
||
|
}
|
||
|
|
||
|
// Convert the entire GUID string to uppercased letters.
|
||
|
string::iterator si;
|
||
|
for (si = guid.begin(); si != guid.end(); ++si) {
|
||
|
(*si) = toupper(*si);
|
||
|
}
|
||
|
|
||
|
return guid;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_word
|
||
|
// Access: Private
|
||
|
// Description: Expands the "word" function variable. This returns
|
||
|
// the nth word, 1-based, of the space-separated list of
|
||
|
// words in the second parameter.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_word(const string ¶ms) {
|
||
|
// Split the string up into tokens based on the commas.
|
||
|
vector<string> tokens;
|
||
|
tokenize_params(params, tokens, true);
|
||
|
|
||
|
if (tokens.size() != 2) {
|
||
|
cerr << "word requires two parameters.\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
int index = atoi(tokens[0].c_str());
|
||
|
|
||
|
// Split the second parameter into tokens based on the spaces.
|
||
|
vector<string> words;
|
||
|
tokenize_whitespace(expand_string(tokens[1]), words);
|
||
|
|
||
|
if (index < 1 || index > (int)words.size()) {
|
||
|
// Out of range.
|
||
|
return string();
|
||
|
}
|
||
|
return words[index - 1];
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_wordlist
|
||
|
// Access: Private
|
||
|
// Description: Expands the "wordlist" function variable. This
|
||
|
// returns a range of words, 1-based, of the
|
||
|
// space-separated list of words in the third parameter.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_wordlist(const string ¶ms) {
|
||
|
// Split the string up into tokens based on the commas.
|
||
|
vector<string> tokens;
|
||
|
tokenize_params(params, tokens, true);
|
||
|
|
||
|
if (tokens.size() != 3) {
|
||
|
cerr << "wordlist requires three parameters.\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
int start = atoi(tokens[0].c_str());
|
||
|
int end = atoi(tokens[1].c_str());
|
||
|
|
||
|
if (end < start) {
|
||
|
// Following GNU make's convention, we swap start and end if
|
||
|
// they're out of order.
|
||
|
int t = end;
|
||
|
end = start;
|
||
|
start = t;
|
||
|
}
|
||
|
|
||
|
// Split the third parameter into tokens based on the spaces.
|
||
|
vector<string> words;
|
||
|
tokenize_whitespace(expand_string(tokens[2]), words);
|
||
|
|
||
|
start = max(start, 1);
|
||
|
end = min(end, (int)words.size() + 1);
|
||
|
|
||
|
if (end < start) {
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
vector<string> results;
|
||
|
results.insert(results.end(),
|
||
|
words.begin() + start - 1,
|
||
|
words.begin() + end - 1);
|
||
|
|
||
|
string result = repaste(results, " ");
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_words
|
||
|
// Access: Private
|
||
|
// Description: Expands the "words" function variable. This
|
||
|
// returns the number of space-separated words in the
|
||
|
// list.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_words(const string ¶ms) {
|
||
|
// Split the parameter into tokens based on the spaces.
|
||
|
vector<string> words;
|
||
|
tokenize_whitespace(expand_string(params), words);
|
||
|
|
||
|
char buffer[32];
|
||
|
sprintf(buffer, "%d", (int) words.size());
|
||
|
string result = buffer;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_firstword
|
||
|
// Access: Private
|
||
|
// Description: Expands the "firstword" function variable. This
|
||
|
// returns the first of several words separated by
|
||
|
// whitespace.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_firstword(const string ¶ms) {
|
||
|
// Split the parameter into tokens based on the spaces.
|
||
|
vector<string> words;
|
||
|
tokenize_whitespace(expand_string(params), words);
|
||
|
|
||
|
if (!words.empty()) {
|
||
|
return words[0];
|
||
|
}
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_patsubst
|
||
|
// Access: Private
|
||
|
// Description: Expands the "patsubst" function variable.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_patsubst(const string ¶ms, bool separate_words) {
|
||
|
// Split the string up into tokens based on the commas.
|
||
|
vector<string> tokens;
|
||
|
tokenize_params(params, tokens, false);
|
||
|
|
||
|
if (tokens.size() < 3) {
|
||
|
cerr << "patsubst requires at least three parameters.\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
if ((tokens.size() % 2) != 1) {
|
||
|
cerr << "patsubst requires an odd number of parameters.\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
// Split the last parameter into tokens based on the spaces--but
|
||
|
// only if separate_words is true.
|
||
|
vector<string> words;
|
||
|
if (separate_words) {
|
||
|
tokenize_whitespace(expand_string(tokens.back()), words);
|
||
|
} else {
|
||
|
words.push_back(expand_string(tokens.back()));
|
||
|
}
|
||
|
|
||
|
// Build up a vector of from/to patterns.
|
||
|
typedef vector<PPFilenamePattern> Patterns;
|
||
|
typedef vector<Patterns> FromPatterns;
|
||
|
FromPatterns from;
|
||
|
Patterns to;
|
||
|
|
||
|
size_t i;
|
||
|
for (i = 0; i < tokens.size() - 1; i += 2) {
|
||
|
// Each "from" pattern might be a collection of patterns separated
|
||
|
// by spaces, and it is expanded immediately.
|
||
|
from.push_back(Patterns());
|
||
|
vector<string> froms;
|
||
|
tokenize_whitespace(expand_string(tokens[i]), froms);
|
||
|
vector<string>::const_iterator fi;
|
||
|
for (fi = froms.begin(); fi != froms.end(); ++fi) {
|
||
|
PPFilenamePattern pattern(*fi);
|
||
|
if (!pattern.has_wildcard()) {
|
||
|
cerr << "All the \"from\" parameters of patsubst must include "
|
||
|
<< PATTERN_WILDCARD << ".\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
from.back().push_back(pattern);
|
||
|
}
|
||
|
|
||
|
// However, the corresponding "to" pattern is just one pattern,
|
||
|
// and it is expanded immediately only if it does not contain a
|
||
|
// wildcard character.
|
||
|
PPFilenamePattern to_pattern(tokens[i + 1]);
|
||
|
if (!to_pattern.has_wildcard()) {
|
||
|
to_pattern = PPFilenamePattern(expand_string(tokens[i + 1]));
|
||
|
}
|
||
|
to.push_back(to_pattern);
|
||
|
}
|
||
|
size_t num_patterns = from.size();
|
||
|
assert(num_patterns == to.size());
|
||
|
|
||
|
vector<string>::iterator wi;
|
||
|
for (wi = words.begin(); wi != words.end(); ++wi) {
|
||
|
bool matched = false;
|
||
|
for (i = 0; i < num_patterns && !matched; i++) {
|
||
|
Patterns::const_iterator pi;
|
||
|
for (pi = from[i].begin(); pi != from[i].end() && !matched; ++pi) {
|
||
|
if ((*pi).matches(*wi)) {
|
||
|
matched = true;
|
||
|
string transformed = to[i].transform(*wi, (*pi));
|
||
|
(*wi) = expand_string(transformed);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
string result = repaste(words, " ");
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_filter
|
||
|
// Access: Private
|
||
|
// Description: Expands the "filter" function variable.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_filter(const string ¶ms) {
|
||
|
// Split the string up into tokens based on the commas.
|
||
|
vector<string> tokens;
|
||
|
tokenize_params(params, tokens, true);
|
||
|
|
||
|
if (tokens.size() != 2) {
|
||
|
cerr << "filter requires two parameters.\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
// Split up the first parameter--the list of patterns to filter
|
||
|
// by--into tokens based on the spaces.
|
||
|
vector<string> pattern_strings;
|
||
|
tokenize_whitespace(tokens[0], pattern_strings);
|
||
|
|
||
|
vector<PPFilenamePattern> patterns;
|
||
|
vector<string>::const_iterator psi;
|
||
|
for (psi = pattern_strings.begin(); psi != pattern_strings.end(); ++psi) {
|
||
|
patterns.push_back(PPFilenamePattern(*psi));
|
||
|
}
|
||
|
|
||
|
// Split up the second parameter--the list of words to filter--into
|
||
|
// tokens based on the spaces.
|
||
|
vector<string> words;
|
||
|
tokenize_whitespace(tokens[1], words);
|
||
|
|
||
|
vector<string>::iterator wi, wnext;
|
||
|
wnext = words.begin();
|
||
|
for (wi = words.begin(); wi != words.end(); ++wi) {
|
||
|
const string &word = (*wi);
|
||
|
|
||
|
bool matches_pattern = false;
|
||
|
vector<PPFilenamePattern>::const_iterator pi;
|
||
|
for (pi = patterns.begin();
|
||
|
pi != patterns.end() && !matches_pattern;
|
||
|
++pi) {
|
||
|
matches_pattern = (*pi).matches(word);
|
||
|
}
|
||
|
|
||
|
if (matches_pattern) {
|
||
|
*wnext++ = word;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
words.erase(wnext, words.end());
|
||
|
|
||
|
string result = repaste(words, " ");
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_filter_out
|
||
|
// Access: Private
|
||
|
// Description: Expands the "filter_out" function variable.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_filter_out(const string ¶ms) {
|
||
|
// Split the string up into tokens based on the commas.
|
||
|
vector<string> tokens;
|
||
|
tokenize_params(params, tokens, true);
|
||
|
|
||
|
if (tokens.size() != 2) {
|
||
|
cerr << "filter-out requires two parameters.\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
// Split up the first parameter--the list of patterns to filter
|
||
|
// by--into tokens based on the spaces.
|
||
|
vector<string> pattern_strings;
|
||
|
tokenize_whitespace(tokens[0], pattern_strings);
|
||
|
|
||
|
vector<PPFilenamePattern> patterns;
|
||
|
vector<string>::const_iterator psi;
|
||
|
for (psi = pattern_strings.begin(); psi != pattern_strings.end(); ++psi) {
|
||
|
patterns.push_back(PPFilenamePattern(*psi));
|
||
|
}
|
||
|
|
||
|
// Split up the second parameter--the list of words to filter--into
|
||
|
// tokens based on the spaces.
|
||
|
vector<string> words;
|
||
|
tokenize_whitespace(tokens[1], words);
|
||
|
|
||
|
vector<string>::iterator wi, wnext;
|
||
|
wnext = words.begin();
|
||
|
for (wi = words.begin(); wi != words.end(); ++wi) {
|
||
|
const string &word = (*wi);
|
||
|
|
||
|
bool matches_pattern = false;
|
||
|
vector<PPFilenamePattern>::const_iterator pi;
|
||
|
for (pi = patterns.begin();
|
||
|
pi != patterns.end() && !matches_pattern;
|
||
|
++pi) {
|
||
|
matches_pattern = (*pi).matches(word);
|
||
|
}
|
||
|
|
||
|
if (!matches_pattern) {
|
||
|
*wnext++ = word;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
words.erase(wnext, words.end());
|
||
|
|
||
|
string result = repaste(words, " ");
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_subst
|
||
|
// Access: Private
|
||
|
// Description: Expands the "subst" function variable.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_subst(const string ¶ms) {
|
||
|
// Split the string up into tokens based on the commas.
|
||
|
vector<string> tokens;
|
||
|
tokenize_params(params, tokens, true);
|
||
|
|
||
|
if (tokens.size() < 3) {
|
||
|
cerr << "subst requires at least three parameters.\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
if ((tokens.size() % 2) != 1) {
|
||
|
cerr << "subst requires an odd number of parameters.\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
// Now substitute each of the substitute strings out for the
|
||
|
// replacement strings.
|
||
|
string str = tokens.back();
|
||
|
for (size_t i = 0; i < tokens.size() - 1; i += 2) {
|
||
|
string new_str;
|
||
|
const string &subst = tokens[i];
|
||
|
const string &repl = tokens[i + 1];
|
||
|
size_t q = 0;
|
||
|
size_t p = str.find(subst, q);
|
||
|
while (p != string::npos) {
|
||
|
new_str += str.substr(q, p - q) + repl;
|
||
|
q = p + subst.length();
|
||
|
p = str.find(subst, q);
|
||
|
}
|
||
|
str = new_str + str.substr(q);
|
||
|
}
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_findstrnig
|
||
|
// Access: Private
|
||
|
// Description: Expands the "findstring" function variable.
|
||
|
// $[findstring a,b] returns b if and only if it is a
|
||
|
// substring of a; otherwise, it returns the empty
|
||
|
// string.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_findstring(const string ¶ms) {
|
||
|
// Split the string up into tokens based on the commas.
|
||
|
vector<string> tokens;
|
||
|
tokenize_params(params, tokens, true);
|
||
|
|
||
|
if (tokens.size() != 2) {
|
||
|
cerr << "findstring requires two parameters.\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
string str = tokens.back();
|
||
|
const string &srchstr = tokens[0];
|
||
|
size_t q = 0;
|
||
|
size_t p = str.find(srchstr, q);
|
||
|
if(p == string::npos)
|
||
|
str = "";
|
||
|
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_wordsubst
|
||
|
// Access: Private
|
||
|
// Description: Expands the "wordsubst" function variable. This is
|
||
|
// like "subst" except it only replaces whole words.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_wordsubst(const string ¶ms) {
|
||
|
// Split the string up into tokens based on the commas.
|
||
|
vector<string> tokens;
|
||
|
tokenize_params(params, tokens, true);
|
||
|
|
||
|
if (tokens.size() < 3) {
|
||
|
cerr << "subst requires at least three parameters.\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
if ((tokens.size() % 2) != 1) {
|
||
|
cerr << "subst requires an odd number of parameters.\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
// Split the last parameter into tokens based on the spaces.
|
||
|
vector<string> words;
|
||
|
tokenize_whitespace(tokens.back(), words);
|
||
|
|
||
|
for (size_t i = 0; i < tokens.size() - 1; i += 2) {
|
||
|
const string &subst = tokens[i];
|
||
|
const string &repl = tokens[i + 1];
|
||
|
vector<string>::iterator wi;
|
||
|
for (wi = words.begin(); wi != words.end(); ++wi) {
|
||
|
if ((*wi) == subst) {
|
||
|
(*wi) = repl;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
string result = repaste(words, " ");
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_join
|
||
|
// Access: Private
|
||
|
// Description: Expands the "join" function variable: joins the list
|
||
|
// of words using the specified separator.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_join(const string ¶ms) {
|
||
|
// Split the string up into tokens based on the spaces.
|
||
|
vector<string> tokens;
|
||
|
tokenize_params(params, tokens, true);
|
||
|
|
||
|
if (tokens.size() != 2) {
|
||
|
cerr << "join requires two parameters.\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
const string &sep = tokens[0];
|
||
|
vector<string> words;
|
||
|
tokenize_whitespace(expand_string(tokens[1]), words);
|
||
|
|
||
|
string result = repaste(words, sep);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_sort
|
||
|
// Access: Private
|
||
|
// Description: Expands the "sort" function variable: sort the words
|
||
|
// into alphabetical order, and also remove duplicates.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_sort(const string ¶ms) {
|
||
|
// Split the string up into tokens based on the spaces.
|
||
|
vector<string> words;
|
||
|
tokenize_whitespace(expand_string(params), words);
|
||
|
|
||
|
sort(words.begin(), words.end());
|
||
|
words.erase(unique(words.begin(), words.end()), words.end());
|
||
|
|
||
|
string result = repaste(words, " ");
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_unique
|
||
|
// Access: Private
|
||
|
// Description: Expands the "unique" function variable: remove
|
||
|
// duplicates from the list of words without changing
|
||
|
// the order. The first appearance of each word
|
||
|
// remains.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_unique(const string ¶ms) {
|
||
|
// Split the string up into tokens based on the spaces.
|
||
|
vector<string> words;
|
||
|
tokenize_whitespace(expand_string(params), words);
|
||
|
|
||
|
vector<string>::iterator win, wout;
|
||
|
set<string> included_words;
|
||
|
|
||
|
win = words.begin();
|
||
|
wout = words.begin();
|
||
|
while (win != words.end()) {
|
||
|
if (included_words.insert(*win).second) {
|
||
|
// This is a unique word so far.
|
||
|
*wout++ = *win;
|
||
|
}
|
||
|
++win;
|
||
|
}
|
||
|
|
||
|
words.erase(wout, words.end());
|
||
|
string result = repaste(words, " ");
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_matrix
|
||
|
// Access: Private
|
||
|
// Description: Expands the "matrix" function variable. This
|
||
|
// combines the different words of the n parameters in
|
||
|
// all possible ways, like the shell {a,b,c} expansion
|
||
|
// characters. For example, $[matrix a b,c,10 20 30]
|
||
|
// expands to ac10 ac20 ac30 bc10 bc20 bc30.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_matrix(const string ¶ms) {
|
||
|
// Split the string up into tokens based on the commas.
|
||
|
vector<string> tokens;
|
||
|
tokenize_params(params, tokens, true);
|
||
|
|
||
|
// Each token gets split up into words based on the spaces.
|
||
|
vector<vector<string> > words;
|
||
|
for (int i = 0; i < (int)tokens.size(); i++) {
|
||
|
words.push_back(vector<string>());
|
||
|
tokenize_whitespace(tokens[i], words.back());
|
||
|
}
|
||
|
|
||
|
// Now synthesize the results recursively.
|
||
|
vector<string> results;
|
||
|
r_expand_matrix(results, words, 0, "");
|
||
|
|
||
|
string result = repaste(results, " ");
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_if
|
||
|
// Access: Private
|
||
|
// Description: Expands the "if" function variable. This evaluates
|
||
|
// the first parameter and returns the second parameter
|
||
|
// if the result is true (i.e. nonempty) and the third
|
||
|
// parameter (if present) if the result is false
|
||
|
// (i.e. empty).
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_if(const string ¶ms) {
|
||
|
// Split the string up into tokens based on the commas.
|
||
|
vector<string> tokens;
|
||
|
tokenize_params(params, tokens, true);
|
||
|
|
||
|
if (tokens.size() == 2) {
|
||
|
if (!tokens[0].empty()) {
|
||
|
return tokens[1];
|
||
|
} else {
|
||
|
return "";
|
||
|
}
|
||
|
} else if (tokens.size() == 3) {
|
||
|
if (!tokens[0].empty()) {
|
||
|
return tokens[1];
|
||
|
} else {
|
||
|
return tokens[2];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cerr << "if requires two or three parameters.\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_eq
|
||
|
// Access: Private
|
||
|
// Description: Expands the "eq" function variable. This tests
|
||
|
// string equivalence.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_eq(const string ¶ms) {
|
||
|
// Split the string up into tokens based on the commas.
|
||
|
vector<string> tokens;
|
||
|
tokenize_params(params, tokens, true);
|
||
|
|
||
|
if (tokens.size() != 2) {
|
||
|
cerr << "eq requires two parameters.\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
string result;
|
||
|
if (tokens[0] == tokens[1]) {
|
||
|
result = "1";
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_ne
|
||
|
// Access: Private
|
||
|
// Description: Expands the "ne" function variable. This tests
|
||
|
// string equivalence.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_ne(const string ¶ms) {
|
||
|
// Split the string up into tokens based on the commas.
|
||
|
vector<string> tokens;
|
||
|
tokenize_params(params, tokens, true);
|
||
|
|
||
|
if (tokens.size() != 2) {
|
||
|
cerr << "ne requires two parameters.\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
string result;
|
||
|
if (!(tokens[0] == tokens[1])) {
|
||
|
result = "1";
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_eqn
|
||
|
// Access: Private
|
||
|
// Description: Expands the "=" function variable. This tests
|
||
|
// numeric equivalence.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_eqn(const string ¶ms) {
|
||
|
double a, b;
|
||
|
if (!tokenize_numeric_pair(params, a, b)) {
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
string result;
|
||
|
if (a == b) {
|
||
|
result = "1";
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_nen
|
||
|
// Access: Private
|
||
|
// Description: Expands the "!=" function variable. This tests
|
||
|
// numeric equivalence.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_nen(const string ¶ms) {
|
||
|
double a, b;
|
||
|
if (!tokenize_numeric_pair(params, a, b)) {
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
string result;
|
||
|
if (a != b) {
|
||
|
result = "1";
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_ltn
|
||
|
// Access: Private
|
||
|
// Description: Expands the "<" function variable. This tests
|
||
|
// numeric relationships.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_ltn(const string ¶ms) {
|
||
|
double a, b;
|
||
|
if (!tokenize_numeric_pair(params, a, b)) {
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
string result;
|
||
|
if (a < b) {
|
||
|
result = "1";
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_len
|
||
|
// Access: Private
|
||
|
// Description: Expands the "<=" function variable. This tests
|
||
|
// numeric relationships.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_len(const string ¶ms) {
|
||
|
double a, b;
|
||
|
if (!tokenize_numeric_pair(params, a, b)) {
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
string result;
|
||
|
if (a <= b) {
|
||
|
result = "1";
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_gtn
|
||
|
// Access: Private
|
||
|
// Description: Expands the ">" function variable. This tests
|
||
|
// numeric relationships.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_gtn(const string ¶ms) {
|
||
|
double a, b;
|
||
|
if (!tokenize_numeric_pair(params, a, b)) {
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
string result;
|
||
|
if (a > b) {
|
||
|
result = "1";
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_gen
|
||
|
// Access: Private
|
||
|
// Description: Expands the ">=" function variable. This tests
|
||
|
// numeric relationships.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_gen(const string ¶ms) {
|
||
|
double a, b;
|
||
|
if (!tokenize_numeric_pair(params, a, b)) {
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
string result;
|
||
|
if (a >= b) {
|
||
|
result = "1";
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_plus
|
||
|
// Access: Private
|
||
|
// Description: Expands the "+" function variable. This operates
|
||
|
// on integer numbers.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_plus(const string ¶ms) {
|
||
|
vector<int> tokens;
|
||
|
if (!tokenize_ints(params, tokens)) {
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
int result = 0;
|
||
|
vector<int>::const_iterator ti;
|
||
|
for (ti = tokens.begin(); ti != tokens.end(); ++ti) {
|
||
|
result += (*ti);
|
||
|
}
|
||
|
|
||
|
return format_int(result);
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_minus
|
||
|
// Access: Private
|
||
|
// Description: Expands the "-" function variable. This operates
|
||
|
// on integer numbers.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_minus(const string ¶ms) {
|
||
|
vector<int> tokens;
|
||
|
if (!tokenize_ints(params, tokens)) {
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
int result = 0;
|
||
|
|
||
|
if (tokens.size() == 1) {
|
||
|
// A special case: unary minus.
|
||
|
result = -tokens[0];
|
||
|
|
||
|
} else if (tokens.size() > 1) {
|
||
|
result = tokens[0];
|
||
|
for (int i = 1; i < (int)tokens.size(); i++) {
|
||
|
result -= tokens[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return format_int(result);
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_times
|
||
|
// Access: Private
|
||
|
// Description: Expands the "*" function variable. This operates
|
||
|
// on integer numbers.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_times(const string ¶ms) {
|
||
|
vector<int> tokens;
|
||
|
if (!tokenize_ints(params, tokens)) {
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
int result = 1;
|
||
|
vector<int>::const_iterator ti;
|
||
|
for (ti = tokens.begin(); ti != tokens.end(); ++ti) {
|
||
|
result *= (*ti);
|
||
|
}
|
||
|
|
||
|
return format_int(result);
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_divide
|
||
|
// Access: Private
|
||
|
// Description: Expands the "/" function variable. This operates
|
||
|
// on integer numbers.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_divide(const string ¶ms) {
|
||
|
vector<int> tokens;
|
||
|
if (!tokenize_ints(params, tokens)) {
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
if (tokens.size() != 2) {
|
||
|
cerr << tokens.size() << " parameters supplied when two were expected:\n"
|
||
|
<< params << "\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
return format_int(tokens[0] / tokens[1]);
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_modulo
|
||
|
// Access: Private
|
||
|
// Description: Expands the "%" function variable. This operates
|
||
|
// on integer numbers.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_modulo(const string ¶ms) {
|
||
|
vector<int> tokens;
|
||
|
if (!tokenize_ints(params, tokens)) {
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
if (tokens.size() != 2) {
|
||
|
cerr << tokens.size() << " parameters supplied when two were expected:\n"
|
||
|
<< params << "\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
return format_int(tokens[0] % tokens[1]);
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_not
|
||
|
// Access: Private
|
||
|
// Description: Expands the "not" function variable. This returns
|
||
|
// nonempty if its argument is empty, empty if its
|
||
|
// argument is nonempty.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_not(const string ¶ms) {
|
||
|
// Split the string up into tokens based on the commas.
|
||
|
vector<string> tokens;
|
||
|
tokenize_params(params, tokens, true);
|
||
|
|
||
|
if (tokens.size() != 1) {
|
||
|
cerr << "not requires one parameter.\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
string result;
|
||
|
if (tokens[0].empty()) {
|
||
|
result = "1";
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_or
|
||
|
// Access: Private
|
||
|
// Description: Expands the "or" function variable. This returns
|
||
|
// nonempty if any of its arguments are nonempty.
|
||
|
// Specifically, it returns the first nonempty argument.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_or(const string ¶ms) {
|
||
|
// Split the string up into tokens based on the commas.
|
||
|
vector<string> tokens;
|
||
|
tokenize_params(params, tokens, true);
|
||
|
|
||
|
vector<string>::const_iterator ti;
|
||
|
for (ti = tokens.begin(); ti != tokens.end(); ++ti) {
|
||
|
if (!(*ti).empty()) {
|
||
|
return (*ti);
|
||
|
}
|
||
|
}
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_and
|
||
|
// Access: Private
|
||
|
// Description: Expands the "and" function variable. This returns
|
||
|
// nonempty if all of its arguments are nonempty.
|
||
|
// Specifically, it returns the last argument.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_and(const string ¶ms) {
|
||
|
// Split the string up into tokens based on the commas.
|
||
|
vector<string> tokens;
|
||
|
tokenize_params(params, tokens, true);
|
||
|
|
||
|
vector<string>::const_iterator ti;
|
||
|
for (ti = tokens.begin(); ti != tokens.end(); ++ti) {
|
||
|
if ((*ti).empty()) {
|
||
|
return string();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
string result = "1";
|
||
|
if (!tokens.empty()) {
|
||
|
result = tokens.back();
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_upcase
|
||
|
// Access: Private
|
||
|
// Description: Expands the "upcase" function variable.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_upcase(const string ¶ms) {
|
||
|
string result = expand_string(params);
|
||
|
string::iterator si;
|
||
|
for (si = result.begin(); si != result.end(); ++si) {
|
||
|
(*si) = toupper(*si);
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_downcase
|
||
|
// Access: Private
|
||
|
// Description: Expands the "downcase" function variable.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_downcase(const string ¶ms) {
|
||
|
string result = expand_string(params);
|
||
|
string::iterator si;
|
||
|
for (si = result.begin(); si != result.end(); ++si) {
|
||
|
(*si) = tolower(*si);
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_cdefine
|
||
|
// Access: Private
|
||
|
// Description: Expands the "cdefine" function variable. This is a
|
||
|
// convenience function to output a C-style #define or
|
||
|
// #undef statement based on the value of the named
|
||
|
// variable. If the named string is a variable whose
|
||
|
// definition is nonempty, this returns "#define varname
|
||
|
// definition". Otherwise, it returns "#undef varname".
|
||
|
// This is particularly useful for building up a
|
||
|
// config.h file.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_cdefine(const string ¶ms) {
|
||
|
string varname = trim_blanks(params);
|
||
|
string expansion = trim_blanks(expand_variable(varname));
|
||
|
|
||
|
string result;
|
||
|
if (expansion.empty()) {
|
||
|
result = "#undef " + varname;
|
||
|
} else {
|
||
|
result = "#define " + varname + " " + expansion;
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_closure
|
||
|
// Access: Private
|
||
|
// Description: Expands the "closure" function variable. This is a
|
||
|
// special function that recursively expands a map
|
||
|
// variable with the given parameter string until all
|
||
|
// definitions have been encountered.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_closure(const string ¶ms) {
|
||
|
// Split the string up into tokens based on the commas.
|
||
|
vector<string> tokens;
|
||
|
tokenize_params(params, tokens, false);
|
||
|
|
||
|
if (tokens.size() != 2 && tokens.size() != 3) {
|
||
|
cerr << "closure requires two or three parameters.\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
// The first parameter is the map variable name, the second
|
||
|
// parameter is the expression to evaluate, and the third parameter
|
||
|
// (if present) is the expression that leads to the recursive
|
||
|
// evaluation of the map variable.
|
||
|
string varname = expand_string(tokens[0]);
|
||
|
string expression = tokens[1];
|
||
|
string close_on = expression;
|
||
|
if (tokens.size() > 2) {
|
||
|
close_on = tokens[2];
|
||
|
}
|
||
|
|
||
|
const MapVariableDefinition &def = find_map_variable(varname);
|
||
|
if (&def == &_null_map_def) {
|
||
|
cerr << "Warning: undefined map variable: " << varname << "\n";
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
// Now evaluate the expression within this scope, and then again
|
||
|
// within each scope indicated by the result, and then within each
|
||
|
// scope indicated by *that* result, and so on. We need to keep
|
||
|
// track of the words we have already evaluated (hence the set), and
|
||
|
// we also need to keep track of all the partial results we have yet
|
||
|
// to evaluate (hence the vector of strings).
|
||
|
set<string> closure;
|
||
|
vector<string> results;
|
||
|
vector<string> next_pass;
|
||
|
|
||
|
// Start off with the expression evaluated within the starting
|
||
|
// scope.
|
||
|
results.push_back(expand_string(expression));
|
||
|
|
||
|
next_pass.push_back(expand_string(close_on));
|
||
|
|
||
|
while (!next_pass.empty()) {
|
||
|
// Pull off one of the partial results (it doesn't matter which
|
||
|
// one), and chop it up into its constituent words.
|
||
|
vector<string> pass;
|
||
|
tokenize_whitespace(next_pass.back(), pass);
|
||
|
next_pass.pop_back();
|
||
|
|
||
|
// And then map each of those words into scopes.
|
||
|
vector<string>::const_iterator wi;
|
||
|
for (wi = pass.begin(); wi != pass.end(); ++wi) {
|
||
|
const string &word = (*wi);
|
||
|
bool inserted = closure.insert(word).second;
|
||
|
if (inserted) {
|
||
|
// This is a new word, which presumably maps to a scope.
|
||
|
MapVariableDefinition::const_iterator di;
|
||
|
di = def.find(word);
|
||
|
if (di != def.end()) {
|
||
|
PPScope *scope = (*di).second;
|
||
|
// Evaluate the expression within this scope.
|
||
|
results.push_back(scope->expand_string(expression));
|
||
|
|
||
|
// What does close_on evaluate to within this scope? That
|
||
|
// points us to the next scope(s).
|
||
|
next_pass.push_back(scope->expand_string(close_on));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Now we have the complete transitive closure of $[mapvar close_on].
|
||
|
string result = repaste(results, " ");
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_unmapped
|
||
|
// Access: Private
|
||
|
// Description: Expands the "closure" function variable. This is a
|
||
|
// special function that returns all the arguments to a
|
||
|
// map variable, unchanged, that did *not* match any of
|
||
|
// the keys in the map.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_unmapped(const string ¶ms) {
|
||
|
// Split the string up into tokens based on the commas.
|
||
|
vector<string> tokens;
|
||
|
tokenize_params(params, tokens, false);
|
||
|
|
||
|
if (tokens.size() != 2) {
|
||
|
cerr << "unmapped requires two parameters.\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
// The first parameter is the map variable name, and the second
|
||
|
// parameter is the space-separated list of arguments to the map.
|
||
|
string varname = expand_string(tokens[0]);
|
||
|
vector<string> keys;
|
||
|
tokenize_whitespace(expand_string(tokens[1]), keys);
|
||
|
|
||
|
const MapVariableDefinition &def = find_map_variable(varname);
|
||
|
if (&def == &_null_map_def) {
|
||
|
cerr << "Warning: undefined map variable: " << varname << "\n";
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
vector<string> results;
|
||
|
vector<string>::const_iterator ki;
|
||
|
for (ki = keys.begin(); ki != keys.end(); ++ki) {
|
||
|
MapVariableDefinition::const_iterator di;
|
||
|
di = def.find(*ki);
|
||
|
if (di == def.end()) {
|
||
|
// This key was undefined.
|
||
|
results.push_back(*ki);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
string result = repaste(results, " ");
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_dependencies
|
||
|
// Access: Private
|
||
|
// Description: Expands the "dependencies" function variable. This
|
||
|
// function returns all of the inter-file dependencies
|
||
|
// that the named file(s) depend on, as defined by the
|
||
|
// #include directives appearing within the files.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_dependencies(const string ¶ms) {
|
||
|
// Split the string up into filenames based on whitespace.
|
||
|
vector<string> filenames;
|
||
|
tokenize_whitespace(expand_string(params), filenames);
|
||
|
|
||
|
PPDirectory *directory = get_directory();
|
||
|
assert(directory != (PPDirectory *)NULL);
|
||
|
|
||
|
vector<string> results;
|
||
|
vector<string>::const_iterator fi;
|
||
|
for (fi = filenames.begin(); fi != filenames.end(); ++fi) {
|
||
|
PPDependableFile *file = directory->get_dependable_file(*fi, false);
|
||
|
assert(file != (PPDependableFile *)NULL);
|
||
|
|
||
|
vector<PPDependableFile *> files;
|
||
|
file->get_complete_dependencies(files);
|
||
|
|
||
|
vector<PPDependableFile *>::const_iterator dfi;
|
||
|
for (dfi = files.begin(); dfi != files.end(); ++dfi) {
|
||
|
PPDependableFile *df = (*dfi);
|
||
|
string rel_filename =
|
||
|
current_output_directory->get_rel_to(df->get_directory()) + "/" +
|
||
|
df->get_filename();
|
||
|
results.push_back(rel_filename);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sort(results.begin(), results.end());
|
||
|
results.erase(unique(results.begin(), results.end()), results.end());
|
||
|
|
||
|
string result = repaste(results, " ");
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_foreach
|
||
|
// Access: Private
|
||
|
// Description: Expands the "foreach" function variable. This
|
||
|
// evaluates an expression once for each word of a list.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_foreach(const string ¶ms) {
|
||
|
// Split the string up into tokens based on the commas.
|
||
|
vector<string> tokens;
|
||
|
tokenize_params(params, tokens, false);
|
||
|
|
||
|
if (tokens.size() != 3) {
|
||
|
cerr << "foreach requires three parameters.\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
// The first parameter is the temporary variable name that holds
|
||
|
// each word as it is expanded; the second parameter is the
|
||
|
// space-separated list of words. The third parameter is the
|
||
|
// expression to evaluate.
|
||
|
string varname = trim_blanks(expand_string(tokens[0]));
|
||
|
vector<string> words;
|
||
|
tokenize_whitespace(expand_string(tokens[1]), words);
|
||
|
|
||
|
vector<string> results;
|
||
|
vector<string>::const_iterator wi;
|
||
|
for (wi = words.begin(); wi != words.end(); ++wi) {
|
||
|
define_variable(varname, *wi);
|
||
|
results.push_back(expand_string(tokens[2]));
|
||
|
}
|
||
|
|
||
|
string result = repaste(results, " ");
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_forscopes
|
||
|
// Access: Private
|
||
|
// Description: Expands the "forscopes" function variable. This
|
||
|
// evaluates an expression once within each of a number
|
||
|
// of named nested scopes.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_forscopes(const string ¶ms) {
|
||
|
// Split the string up into tokens based on the commas.
|
||
|
vector<string> tokens;
|
||
|
tokenize_params(params, tokens, false);
|
||
|
|
||
|
if (tokens.size() != 2) {
|
||
|
cerr << "forscopes requires two parameters.\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
// The first parameter is the space-separated list of nested scope
|
||
|
// names. The second parameter is the expression to evaluate.
|
||
|
vector<string> scope_names;
|
||
|
tokenize_whitespace(expand_string(tokens[0]), scope_names);
|
||
|
|
||
|
if (_named_scopes == (PPNamedScopes *)NULL) {
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
// Now build up the list of scopes with these names.
|
||
|
PPNamedScopes::Scopes scopes;
|
||
|
vector<string>::const_iterator wi;
|
||
|
for (wi = scope_names.begin(); wi != scope_names.end(); ++wi) {
|
||
|
_named_scopes->get_scopes(*wi, scopes);
|
||
|
}
|
||
|
PPNamedScopes::sort_by_dependency(scopes);
|
||
|
|
||
|
// Now evaluate the expression within each scope.
|
||
|
|
||
|
vector<string> results;
|
||
|
PPNamedScopes::Scopes::const_iterator si;
|
||
|
for (si = scopes.begin(); si != scopes.end(); ++si) {
|
||
|
PPScope *scope = *si;
|
||
|
results.push_back(scope->expand_string(tokens[1]));
|
||
|
}
|
||
|
|
||
|
string result = repaste(results, " ");
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_function
|
||
|
// Access: Private
|
||
|
// Description: Expands the user-defined function reference. This
|
||
|
// invokes the nested commands within the function body,
|
||
|
// and returns all the output text as one line. Quite a
|
||
|
// job, really.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_function(const string &funcname,
|
||
|
const PPSubroutine *sub, const string ¶ms) {
|
||
|
PPScope::push_scope((PPScope *)this);
|
||
|
PPScope nested_scope(_named_scopes);
|
||
|
nested_scope.define_formals(funcname, sub->_formals, params);
|
||
|
|
||
|
#ifdef HAVE_SSTREAM
|
||
|
ostringstream ostr;
|
||
|
#else
|
||
|
ostrstream ostr;
|
||
|
#endif
|
||
|
|
||
|
PPCommandFile command(&nested_scope);
|
||
|
command.set_output(&ostr);
|
||
|
|
||
|
command.begin_read();
|
||
|
bool okflag = true;
|
||
|
vector<string>::const_iterator li;
|
||
|
for (li = sub->_lines.begin(); li != sub->_lines.end() && okflag; ++li) {
|
||
|
okflag = command.read_line(*li);
|
||
|
}
|
||
|
if (okflag) {
|
||
|
okflag = command.end_read();
|
||
|
}
|
||
|
// We don't do anything with okflag here. What can we do?
|
||
|
|
||
|
PPScope::pop_scope();
|
||
|
|
||
|
// Now get the output. We split it into words and then reconnect
|
||
|
// it, to replace all whitespace with spaces.
|
||
|
#ifdef HAVE_SSTREAM
|
||
|
string str = ostr.str();
|
||
|
#else
|
||
|
ostr << ends;
|
||
|
char *c_str = ostr.str();
|
||
|
string str = c_str;
|
||
|
delete[] c_str;
|
||
|
#endif
|
||
|
|
||
|
vector<string> results;
|
||
|
tokenize_whitespace(str, results);
|
||
|
|
||
|
string result = repaste(results, " ");
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_map_variable
|
||
|
// Access: Private
|
||
|
// Description: Expands a map variable function reference. This
|
||
|
// looks up the given keys in the map and expands the
|
||
|
// first parameter for each corresponding scope.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_map_variable(const string &varname, const string ¶ms) {
|
||
|
// Split the string up into tokens based on the commas, but don't
|
||
|
// expand the variables yet.
|
||
|
vector<string> tokens;
|
||
|
tokenize_params(params, tokens, false);
|
||
|
|
||
|
if (tokens.size() != 2) {
|
||
|
cerr << "map variable expansions require two parameters: $["
|
||
|
<< varname << " " << params << "]\n";
|
||
|
errors_occurred = true;
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
// Split the second parameter into tokens based on the spaces. This
|
||
|
// is the set of keys.
|
||
|
vector<string> keys;
|
||
|
tokenize_whitespace(expand_string(tokens[1]), keys);
|
||
|
|
||
|
return expand_map_variable(varname, tokens[0], keys);
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::expand_map_variable
|
||
|
// Access: Private
|
||
|
// Description: Expands a map variable function reference. This
|
||
|
// looks up the given keys in the map and expands the
|
||
|
// expression for each corresponding scope.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
string PPScope::
|
||
|
expand_map_variable(const string &varname, const string &expression,
|
||
|
const vector<string> &keys) {
|
||
|
const MapVariableDefinition &def = find_map_variable(varname);
|
||
|
if (&def == &_null_map_def) {
|
||
|
cerr << "Warning: undefined map variable: " << varname << "\n";
|
||
|
return string();
|
||
|
}
|
||
|
|
||
|
vector<string> results;
|
||
|
|
||
|
// Now build up the set of expansions of the expression in the
|
||
|
// various scopes indicated by the keys.
|
||
|
vector<string>::const_iterator wi;
|
||
|
for (wi = keys.begin(); wi != keys.end(); ++wi) {
|
||
|
MapVariableDefinition::const_iterator di;
|
||
|
di = def.find(*wi);
|
||
|
if (di != def.end()) {
|
||
|
PPScope *scope = (*di).second;
|
||
|
string expansion = scope->expand_string(expression);
|
||
|
if (!expansion.empty()) {
|
||
|
results.push_back(expansion);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
string result = repaste(results, " ");
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::r_expand_matrix
|
||
|
// Access: Private
|
||
|
// Description: The recursive implementation of expand_matrix().
|
||
|
// This generates all of the combinations from the
|
||
|
// indicated index into the words array, with the given
|
||
|
// prefix.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
void PPScope::
|
||
|
r_expand_matrix(vector<string> &results, const vector<vector<string> > &words,
|
||
|
int index, const string &prefix) {
|
||
|
if (index >= (int)words.size()) {
|
||
|
// This is the terminal condition.
|
||
|
results.push_back(prefix);
|
||
|
|
||
|
} else {
|
||
|
// Otherwise, tack on the next set of words, and recurse.
|
||
|
const vector<string> &w = words[index];
|
||
|
vector<string>::const_iterator wi;
|
||
|
for (wi = w.begin(); wi != w.end(); ++wi) {
|
||
|
r_expand_matrix(results, words, index + 1, prefix + (*wi));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::p_find_map_variable
|
||
|
// Access: Private
|
||
|
// Description: The implementation of find_map_variable() for a
|
||
|
// particular static scope, without checking the stack.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
PPScope::MapVariableDefinition &PPScope::
|
||
|
p_find_map_variable(const string &varname) {
|
||
|
MapVariables::const_iterator mvi;
|
||
|
mvi = _map_variables.find(varname);
|
||
|
if (mvi != _map_variables.end()) {
|
||
|
return (MapVariableDefinition &)(*mvi).second;
|
||
|
}
|
||
|
|
||
|
if (_parent_scope != (PPScope *)NULL) {
|
||
|
return _parent_scope->p_find_map_variable(varname);
|
||
|
}
|
||
|
|
||
|
return _null_map_def;
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
// Function: PPScope::glob_string
|
||
|
// Access: Private
|
||
|
// Description: Expands the words in the string as if they were a set
|
||
|
// of filenames using the shell globbing characters.
|
||
|
// Fills up the results vector (which the user should
|
||
|
// ensure is empty before calling) with the set of all
|
||
|
// files that actually match the globbing characters.
|
||
|
////////////////////////////////////////////////////////////////////
|
||
|
void PPScope::
|
||
|
glob_string(const string &str, vector<string> &results) {
|
||
|
// The globbing is relative to THISDIRPREFIX, not necessarily the
|
||
|
// current directory.
|
||
|
string dirname = trim_blanks(expand_variable("THISDIRPREFIX"));
|
||
|
|
||
|
vector<string> words;
|
||
|
tokenize_whitespace(str, words);
|
||
|
|
||
|
vector<string>::const_iterator wi;
|
||
|
for (wi = words.begin(); wi != words.end(); ++wi) {
|
||
|
GlobPattern glob(*wi);
|
||
|
glob.match_files(results, dirname);
|
||
|
}
|
||
|
|
||
|
// Sort the results into alphabetical order.
|
||
|
sort(results.begin(), results.end());
|
||
|
}
|