diff --git a/ppremake/config_msvc.h b/ppremake/config_msvc.h index 05525a52fb..2b372eea7e 100644 --- a/ppremake/config_msvc.h +++ b/ppremake/config_msvc.h @@ -80,5 +80,5 @@ ** Also be sure to change the version number ** ** at the beginning of configure.in. ** **************** ****************/ -#define VERSION "1.04" +#define VERSION "1.05" /**************** UPDATE VERSION NUMBER HERE ****************/ diff --git a/ppremake/configure.in b/ppremake/configure.in index 866ac4d82d..7b41886f46 100644 --- a/ppremake/configure.in +++ b/ppremake/configure.in @@ -5,7 +5,7 @@ dnl **************** UPDATE VERSION NUMBER HERE **************** dnl ** Also be sure to change the version number ** dnl ** at the end of config_msvc.h. ** dnl **************** **************** -AM_INIT_AUTOMAKE(ppremake, 1.04) +AM_INIT_AUTOMAKE(ppremake, 1.05) dnl **************** UPDATE VERSION NUMBER HERE **************** AM_CONFIG_HEADER(config.h) diff --git a/ppremake/filename.cxx b/ppremake/filename.cxx index 495ca9b4a8..97b9683adf 100644 --- a/ppremake/filename.cxx +++ b/ppremake/filename.cxx @@ -59,7 +59,7 @@ // pathnames. #ifdef HAVE_CYGWIN extern "C" void cygwin_conv_to_win32_path(const char *path, char *win32); -//extern "C" void cygwin_conv_to_posix_path(const char *path, char *posix); +extern "C" void cygwin_conv_to_posix_path(const char *path, char *posix); #endif static string @@ -1294,7 +1294,21 @@ bool Filename:: touch() const { #ifdef HAVE_UTIME_H // Most Unix systems can do this explicitly. + string os_specific = to_os_specific(); +#ifdef HAVE_CYGWIN + // In the Cygwin case, it seems we need to be sure to use the + // Cygwin-style name; some broken utime() implementation. That's + // almost the same thing as the original Panda-style name, but not + // exactly, so we first convert the Panda name to a Windows name, + // then convert it back to Cygwin, to ensure we get it exactly right + // by Cygwin rules. + { + char result[4096] = ""; + cygwin_conv_to_posix_path(os_specific.c_str(), result); + os_specific = result; + } +#endif // HAVE_CYGWIN int result = utime(os_specific.c_str(), NULL); if (result < 0) { if (errno == ENOENT) { @@ -1311,14 +1325,14 @@ touch() const { return false; } return true; -#else +#else // HAVE_UTIME_H // Other systems may not have an explicit control over the // modification time. For these systems, we'll just temporarily // open the file in append mode, then close it again (it gets closed // when the ofstream goes out of scope). ofstream file; return open_append(file); -#endif +#endif // HAVE_UTIME_H } //////////////////////////////////////////////////////////////////// diff --git a/ppremake/ppCommandFile.cxx b/ppremake/ppCommandFile.cxx index a4463b8b50..6f60c30dc5 100644 --- a/ppremake/ppCommandFile.cxx +++ b/ppremake/ppCommandFile.cxx @@ -74,7 +74,7 @@ pop(PPCommandFile *file) { //////////////////////////////////////////////////////////////////// PPCommandFile::WriteState:: WriteState() { - _out = &cout; + _out = NULL; _format = WF_collapse; _last_blank = true; } @@ -99,20 +99,28 @@ WriteState(const WriteState ©) : //////////////////////////////////////////////////////////////////// bool PPCommandFile::WriteState:: write_line(const string &line) { - switch (_format) { - case WF_straight: - (*_out) << line << "\n"; + if (_out == (ostream *)NULL || _format == WF_error) { + if (!line.empty()) { + cerr << "Ignoring: " << line << "\n"; + } return true; - case WF_collapse: - return write_collapse_line(line); + } else { + switch (_format) { + case WF_straight: + (*_out) << line << "\n"; + return true; + + case WF_collapse: + return write_collapse_line(line); + + case WF_makefile: + return write_makefile_line(line); + } - case WF_makefile: - return write_makefile_line(line); + cerr << "Unsupported write format: " << (int)_format << "\n"; + return false; } - - cerr << "Unsupported write format: " << (int)_format << "\n"; - return false; } //////////////////////////////////////////////////////////////////// @@ -276,8 +284,8 @@ set_output(ostream *out) { //////////////////////////////////////////////////////////////////// void PPCommandFile:: set_scope(PPScope *scope) { - _scope = scope; _native_scope = scope; + _scope = scope; } //////////////////////////////////////////////////////////////////// @@ -622,6 +630,9 @@ handle_command(const string &line) { } else if (_command == "print") { return handle_print_command(); + } else if (_command == "printvar") { + return handle_printvar_command(); + } else if (_command == "include") { return handle_include_command(); @@ -648,6 +659,9 @@ handle_command(const string &line) { } else if (_command == "addmap") { return handle_addmap_command(); + + } else if (_command == "push") { + return handle_push_command(); } cerr << "Invalid command: " << COMMAND_PREFIX << _command << "\n"; @@ -792,7 +806,7 @@ handle_endif_command() { //////////////////////////////////////////////////////////////////// bool PPCommandFile:: handle_begin_command() { - string name = trim_blanks(_scope->expand_string(_params)); + string name = trim_blanks(_params); BlockNesting *nest = new BlockNesting(BS_begin, name); if (contains_whitespace(name)) { @@ -828,7 +842,7 @@ handle_begin_command() { bool PPCommandFile:: handle_forscopes_command() { BlockState state = _in_for ? BS_nested_forscopes : BS_forscopes; - string name = trim_blanks(_scope->expand_string(_params)); + string name = trim_blanks(_params); BlockNesting *nest = new BlockNesting(state, name); nest->push(this); @@ -985,18 +999,25 @@ handle_defsub_command(bool is_defsub) { //////////////////////////////////////////////////////////////////// bool PPCommandFile:: handle_output_command() { - vector words; - tokenize_whitespace(_scope->expand_string(_params), words); + // We must get the filename out (the first parameter) without + // expanding it, because it might involve a string that has to be + // expanded later. + size_t p = _scope->scan_to_whitespace(_params); + string name = _params.substr(0, p); - if (words.empty()) { + if (name.empty()) { cerr << "#output command requires one parameter.\n"; return false; } - BlockNesting *nest = new BlockNesting(BS_output, words[0]); + // Now get the remaining parameters out; these we will expand immediately. + vector words; + tokenize_whitespace(_scope->expand_string(_params.substr(p)), words); + + BlockNesting *nest = new BlockNesting(BS_output, name); // Also check the output flags. - for (int i = 1; i < (int)words.size(); i++) { + for (int i = 0; i < (int)words.size(); i++) { if (words[i] == "notouch") { nest->_flags |= OF_notouch; } else { @@ -1007,7 +1028,7 @@ handle_output_command() { nest->push(this); if (!_in_for) { - string filename = nest->_name; + string filename = trim_blanks(_scope->expand_string(nest->_name)); if (filename.empty()) { cerr << "Attempt to output to empty filename\n"; return false; @@ -1041,7 +1062,13 @@ handle_end_command() { return false; } - string name = trim_blanks(_scope->expand_string(_params)); + // We don't expand the closing name string, because we didn't expand + // the opening string. (This is especially true for an #output + // statement. On some of the other statements, we might have + // inadvertently expanded this string, but probably that was a + // mistake; and there's no reason for programmers to take advantage + // of an expansion there.) + string name = trim_blanks(_params); if (name != _block_nesting->_name) { cerr << "end " << name << " encountered where end " << _block_nesting->_name << " expected.\n"; @@ -1158,6 +1185,33 @@ handle_print_command() { return true; } +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::handle_printvar_command +// Access: Protected +// Description: Writes the literal contents of the named variable(s) +// (the variables are named directly without enclosing +// $[ ... ] syntax) to cerr, for debugging. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +handle_printvar_command() { + size_t p = 0; + + while (p < _params.length()) { + // Pull off the next varname. + size_t q = _scope->scan_to_whitespace(_params, p); + string varname = trim_blanks(_scope->expand_string(_params.substr(p, q))); + + cerr << varname << " = \"" << _scope->get_variable(varname) + << "\" "; + p = q; + while (p < _params.length() && isspace(_params[p])) { + p++; + } + } + cerr << "\n"; + return true; +} + //////////////////////////////////////////////////////////////////// // Function: PPCommandFile::handle_include_command // Access: Protected @@ -1288,11 +1342,8 @@ handle_error_command() { bool PPCommandFile:: handle_defer_command() { // Pull off the first word and the rest of the params. - size_t p = 0; - while (p < _params.length() && !isspace(_params[p])) { - p++; - } - string varname = _params.substr(0, p); + size_t p = _scope->scan_to_whitespace(_params); + string varname = trim_blanks(_scope->expand_string(_params.substr(0, p))); if (PPSubroutine::get_func(varname) != (const PPSubroutine *)NULL) { cerr << "Warning: variable " << varname @@ -1310,7 +1361,6 @@ handle_defer_command() { // should expand any simple self-reference immediately, to allow for // recursive definitions. def = _scope->expand_self_reference(def, varname); - _scope->define_variable(varname, def); return true; @@ -1327,11 +1377,8 @@ handle_defer_command() { bool PPCommandFile:: handle_define_command() { // Pull off the first word and the rest of the params. - size_t p = 0; - while (p < _params.length() && !isspace(_params[p])) { - p++; - } - string varname = _params.substr(0, p); + size_t p = _scope->scan_to_whitespace(_params); + string varname = trim_blanks(_scope->expand_string(_params.substr(0, p))); if (PPSubroutine::get_func(varname) != (const PPSubroutine *)NULL) { cerr << "Warning: variable " << varname @@ -1366,11 +1413,8 @@ handle_define_command() { bool PPCommandFile:: handle_set_command() { // Pull off the first word and the rest of the params. - size_t p = 0; - while (p < _params.length() && !isspace(_params[p])) { - p++; - } - string varname = _params.substr(0, p); + size_t p = _scope->scan_to_whitespace(_params); + string varname = trim_blanks(_scope->expand_string(_params.substr(0, p))); if (PPSubroutine::get_func(varname) != (const PPSubroutine *)NULL) { cerr << "Warning: variable " << varname @@ -1403,18 +1447,14 @@ handle_set_command() { bool PPCommandFile:: handle_map_command() { // Pull off the first word and the rest of the params. - size_t p = 0; - while (p < _params.length() && !isspace(_params[p])) { - p++; - } - string varname = _params.substr(0, p); + size_t p = _scope->scan_to_whitespace(_params); + string varname = trim_blanks(_scope->expand_string(_params.substr(0, p))); // Skip whitespace between the variable name and its definition. while (p < _params.length() && isspace(_params[p])) { p++; } string def = trim_blanks(_params.substr(p)); - _scope->define_map_variable(varname, def); return true; } @@ -1428,11 +1468,8 @@ handle_map_command() { bool PPCommandFile:: handle_addmap_command() { // Pull off the first word and the rest of the params. - size_t p = 0; - while (p < _params.length() && !isspace(_params[p])) { - p++; - } - string varname = _params.substr(0, p); + size_t p = _scope->scan_to_whitespace(_params); + string varname = trim_blanks(_scope->expand_string(_params.substr(0, p))); // Skip whitespace between the variable name and the key. while (p < _params.length() && isspace(_params[p])) { @@ -1444,6 +1481,61 @@ handle_addmap_command() { return true; } +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::handle_push_command +// Access: Protected +// Description: Handles the #push command: push a variable definition +// out to the enclosing scope. Useful for defining +// variables within a #forscopes block that you want to +// persist longer than the block itself. +// +// Syntax is: +// +// #push n varname [varname2 ... ] +// +// Where n is the number of levels out to push. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +handle_push_command() { + // The first word is the number of levels. + size_t p = _scope->scan_to_whitespace(_params); + string levels_str = trim_blanks(_scope->expand_string(_params.substr(0, p))); + + // Validate the number. + const char *param = levels_str.c_str(); + char *n; + int levels = strtol(param, &n, 10); + if (n == param || levels < 0) { + // Invalid integer. + cerr << "#push with invalid level count: " << levels_str << "\n"; + return false; + } + + PPScope *enclosing_scope = _scope; + if (levels > 0) { + enclosing_scope = _scope->get_enclosing_scope(levels - 1); + } + + // Skip whitespace to the first variable name. + while (p < _params.length() && isspace(_params[p])) { + p++; + } + + while (p < _params.length()) { + // Pull off the next varname. + size_t q = _scope->scan_to_whitespace(_params, p); + string varname = trim_blanks(_scope->expand_string(_params.substr(p, q))); + string def = _scope->get_variable(varname); + enclosing_scope->define_variable(varname, def); + + p = q; + while (p < _params.length() && isspace(_params[p])) { + p++; + } + } + return true; +} + //////////////////////////////////////////////////////////////////// // Function: PPCommandFile::include_file diff --git a/ppremake/ppCommandFile.h b/ppremake/ppCommandFile.h index a183a65134..db7a0adac8 100644 --- a/ppremake/ppCommandFile.h +++ b/ppremake/ppCommandFile.h @@ -54,6 +54,7 @@ protected: bool handle_format_command(); bool handle_print_command(); + bool handle_printvar_command(); bool handle_include_command(); bool handle_sinclude_command(); bool handle_call_command(); @@ -64,6 +65,7 @@ protected: bool handle_set_command(); bool handle_map_command(); bool handle_addmap_command(); + bool handle_push_command(); bool include_file(Filename filename); bool replay_forscopes(const string &name); @@ -121,6 +123,7 @@ private: }; enum WriteFormat { + WF_error, // anything other than whitespace is an error. WF_straight, // write lines directly as they come in WF_collapse, // collapse out consecutive blank lines WF_makefile // fancy makefile formatting diff --git a/ppremake/ppScope.cxx b/ppremake/ppScope.cxx index 5cff0d51f4..e55ad1fd20 100644 --- a/ppremake/ppScope.cxx +++ b/ppremake/ppScope.cxx @@ -571,6 +571,25 @@ get_bottom_scope() { 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 @@ -665,6 +684,42 @@ tokenize_numeric_pair(const string &str, double &a, double &b) { 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::p_set_variable // Access: Private diff --git a/ppremake/ppScope.h b/ppremake/ppScope.h index f73e075cde..4cd2898440 100644 --- a/ppremake/ppScope.h +++ b/ppremake/ppScope.h @@ -56,10 +56,12 @@ public: static void push_scope(PPScope *scope); static PPScope *pop_scope(); static PPScope *get_bottom_scope(); + static PPScope *get_enclosing_scope(int n); void tokenize_params(const string &str, vector &tokens, - bool expand); + bool expand); bool tokenize_numeric_pair(const string &str, double &a, double &b); + size_t scan_to_whitespace(const string &str, size_t start = 0); static MapVariableDefinition _null_map_def;