diff --git a/ppremake/config_msvc.h b/ppremake/config_msvc.h index 2b372eea7e..87e22adad8 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.05" +#define VERSION "1.1" /**************** UPDATE VERSION NUMBER HERE ****************/ diff --git a/ppremake/configure.in b/ppremake/configure.in index 7b41886f46..a9d2a4cdd8 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.05) +AM_INIT_AUTOMAKE(ppremake, 1.1) dnl **************** UPDATE VERSION NUMBER HERE **************** AM_CONFIG_HEADER(config.h) diff --git a/ppremake/ppCommandFile.cxx b/ppremake/ppCommandFile.cxx index 6f60c30dc5..7f5b245d61 100644 --- a/ppremake/ppCommandFile.cxx +++ b/ppremake/ppCommandFile.cxx @@ -491,6 +491,11 @@ end_read() { cerr << "Unclosed begin " << _block_nesting->_name << "\n"; break; + case BS_while: + case BS_nested_while: + cerr << "Unclosed while " << _block_nesting->_name << "\n"; + break; + case BS_forscopes: case BS_nested_forscopes: cerr << "Unclosed forscopes " << _block_nesting->_name << "\n"; @@ -597,6 +602,12 @@ handle_command(const string &line) { } else if (_command == "begin") { return handle_begin_command(); + } else if (_command == "while") { + return handle_while_command(); + + } else if (_command == "for") { + return handle_for_command(); + } else if (_command == "forscopes") { return handle_forscopes_command(); @@ -645,6 +656,9 @@ handle_command(const string &line) { } else if (_command == "error") { return handle_error_command(); + } else if (_command == "mkdir") { + return handle_mkdir_command(); + } else if (_command == "defer") { return handle_defer_command(); @@ -831,6 +845,70 @@ handle_begin_command() { return true; } +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::handle_while_command +// Access: Protected +// Description: Handles the #while command: repeat a block of +// commands while a condition is true (nonempty). +// Unlike many of the other block commands, this does +// not begin a new scope. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +handle_while_command() { + BlockState state = _in_for ? BS_nested_while : BS_while; + string name = trim_blanks(_params); + BlockNesting *nest = new BlockNesting(state, name); + nest->push(this); + + if (!_in_for) { + _in_for = true; + _saved_lines.clear(); + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::handle_for_command +// Access: Protected +// Description: Handles the #for command: repeat a block of +// commands with a loop variable iterating over a range +// of numeric values. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +handle_for_command() { + // We get the name of the variable out first. + size_t p = _scope->scan_to_whitespace(_params); + string name = _params.substr(0, p); + + if (name.empty()) { + cerr << "#for without varname\n"; + return false; + } + + // The rest is the comma-delimited range of values. + vector words; + _scope->tokenize_params(_params.substr(p), words, true); + if (words.size() != 2 && words.size() != 3) { + cerr << "Invalid numeric range: '" << _params.substr(p) + << "' for #for " << name << "\n"; + return false; + } + + BlockState state = _in_for ? BS_nested_for : BS_for; + BlockNesting *nest = new BlockNesting(state, name); + nest->push(this); + + if (!_in_for) { + _in_for = true; + _saved_lines.clear(); + + nest->_words.swap(words); + } + + return true; +} + //////////////////////////////////////////////////////////////////// // Function: PPCommandFile::handle_forscopes_command // Access: Protected @@ -1034,12 +1112,12 @@ handle_output_command() { return false; } - string prefix = _scope->expand_variable("DIRPREFIX"); if (filename[0] != '/') { + string prefix = _scope->expand_variable("DIRPREFIX"); filename = prefix + filename; } - nest->_filename = filename; + nest->_params = filename; // Generate an in-memory copy of the file first. _write_state = new WriteState(*_write_state); @@ -1083,7 +1161,21 @@ handle_end_command() { return false; } - if (nest->_state == BS_forscopes) { + if (nest->_state == BS_while) { + // Now replay all of the saved lines. + _in_for = false; + if (!replay_while(nest->_name)) { + return false; + } + + } else if (nest->_state == BS_for) { + // Now replay all of the saved lines. + _in_for = false; + if (!replay_for(nest->_name, nest->_words)) { + return false; + } + + } else if (nest->_state == BS_forscopes) { // Now replay all of the saved lines. _in_for = false; if (!replay_forscopes(nest->_name)) { @@ -1126,7 +1218,7 @@ handle_end_command() { } else if (nest->_state == BS_output) { if (!_in_for) { if (!nest->_output) { - cerr << "Error while writing " << nest->_filename << "\n"; + cerr << "Error while writing " << nest->_params << "\n"; return false; } @@ -1135,7 +1227,7 @@ handle_end_command() { nest->_output << ends; const char *generated_file = nest->_output.str(); - if (!compare_output(generated_file, nest->_filename, + if (!compare_output(generated_file, nest->_params, (nest->_flags & OF_notouch) != 0)) { return false; } @@ -1327,6 +1419,34 @@ handle_error_command() { return false; } +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::handle_mkdir_command +// Access: Protected +// Description: Handles the #mkdir command: create a directory or +// directories. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +handle_mkdir_command() { + vector words; + tokenize_whitespace(_scope->expand_string(_params), words); + + if (words.empty()) { + cerr << "No directory name specifed to #mkdir.\n"; + } else { + vector::const_iterator wi; + for (wi = words.begin(); wi != words.end(); ++wi) { + Filename dirname(*wi, "."); + if (!dirname.make_dir()) { + if (!dirname.is_directory()) { + cerr << "Unable to create directory " << *wi << "\n"; + } + } + } + } + + return true; +} + //////////////////////////////////////////////////////////////////// // Function: PPCommandFile::handle_defer_command // Access: Protected @@ -1572,6 +1692,118 @@ include_file(Filename filename) { return true; } +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::replay_while +// Access: Protected +// Description: Replays all the lines that were saved during a +// previous #while..#end block. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +replay_while(const string &name) { + assert(!_in_for); + + bool okflag = true; + + vector lines; + lines.swap(_saved_lines); + + // Remove the #end command. This will fail if someone makes an #end + // command that spans multiple lines. Don't do that. + assert(!lines.empty()); + lines.pop_back(); + + // Now replay all of the saved lines. + BlockNesting *saved_block = _block_nesting; + IfNesting *saved_if = _if_nesting; + + while (!_scope->expand_string(name).empty()) { + vector::const_iterator li; + for (li = lines.begin(); li != lines.end() && okflag; ++li) { + okflag = read_line(*li); + } + } + + if (saved_block != _block_nesting || saved_if != _if_nesting) { + cerr << "Misplaced #end or #endif.\n"; + okflag = false; + } + + return okflag; +} + +//////////////////////////////////////////////////////////////////// +// Function: PPCommandFile::replay_for +// Access: Protected +// Description: Replays all the lines that were saved during a +// previous #for..#end block. +//////////////////////////////////////////////////////////////////// +bool PPCommandFile:: +replay_for(const string &name, const vector &words) { + assert(!_in_for); + + bool okflag = true; + + vector lines; + lines.swap(_saved_lines); + + // Remove the #end command. This will fail if someone makes an #end + // command that spans multiple lines. Don't do that. + assert(!lines.empty()); + lines.pop_back(); + + // Expand the variable name. + string varname = _scope->expand_string(name); + + // Get out the numeric range. + int range[3] = {0, 0, 1}; + assert(words.size() <= 3); + for (int i = 0; i < (int)words.size(); i++) { + const char *param = words[i].c_str(); + char *n; + range[i] = strtol(param, &n, 10); + if (n == param) { + cerr << "Invalid integer in #for: " << param << "\n"; + return false; + } + } + + if (range[2] == 0) { + cerr << "Step by zero in #for " << name << "\n"; + return false; + } + + // Now replay all of the saved lines. + BlockNesting *saved_block = _block_nesting; + IfNesting *saved_if = _if_nesting; + int index_var; + + if (range[2] > 0) { + for (index_var = range[0]; index_var <= range[1]; index_var += range[2]) { + _scope->define_variable(varname, _scope->format_int(index_var)); + vector::const_iterator li; + for (li = lines.begin(); li != lines.end() && okflag; ++li) { + okflag = read_line(*li); + } + } + } else { + for (index_var = range[0]; index_var >= range[1]; index_var += range[2]) { + _scope->define_variable(varname, _scope->format_int(index_var)); + vector::const_iterator li; + for (li = lines.begin(); li != lines.end() && okflag; ++li) { + okflag = read_line(*li); + } + } + } + _scope->define_variable(varname, _scope->format_int(index_var)); + + if (saved_block != _block_nesting || saved_if != _if_nesting) { + cerr << "Misplaced #end or #endif.\n"; + okflag = false; + } + + return okflag; +} + //////////////////////////////////////////////////////////////////// // Function: PPCommandFile::replay_forscopes // Access: Protected diff --git a/ppremake/ppCommandFile.h b/ppremake/ppCommandFile.h index db7a0adac8..a9bd714666 100644 --- a/ppremake/ppCommandFile.h +++ b/ppremake/ppCommandFile.h @@ -45,6 +45,8 @@ protected: bool handle_endif_command(); bool handle_begin_command(); + bool handle_while_command(); + bool handle_for_command(); bool handle_forscopes_command(); bool handle_foreach_command(); bool handle_formap_command(); @@ -59,6 +61,7 @@ protected: bool handle_sinclude_command(); bool handle_call_command(); bool handle_error_command(); + bool handle_mkdir_command(); bool handle_defer_command(); bool handle_define_command(); @@ -68,6 +71,8 @@ protected: bool handle_push_command(); bool include_file(Filename filename); + bool replay_while(const string &name); + bool replay_for(const string &name, const vector &words); bool replay_forscopes(const string &name); bool replay_foreach(const string &varname, const vector &words); bool replay_formap(const string &varname, const string &mapvar); @@ -111,6 +116,10 @@ private: enum BlockState { BS_begin, + BS_while, + BS_nested_while, + BS_for, + BS_nested_for, BS_forscopes, BS_nested_forscopes, BS_foreach, @@ -157,7 +166,7 @@ private: IfNesting *_if; WriteState *_write_state; PPScope *_scope; - string _filename; + string _params; ostrstream _output; vector _words; int _flags; diff --git a/ppremake/ppScope.cxx b/ppremake/ppScope.cxx index 1b156087ef..616cca52c6 100644 --- a/ppremake/ppScope.cxx +++ b/ppremake/ppScope.cxx @@ -2601,7 +2601,7 @@ expand_minus(const string ¶ms) { int num_words = words.size(); if (num_words == 1) { - // Simular unary minus by setting the first operand to 0. + // Simulate unary minus by setting the first operand to 0. results[0] = 0.0; } else if (num_words != 2) {