From 28b9f21a54a78b697162104c75cb380bc8d11340 Mon Sep 17 00:00:00 2001 From: David Rose Date: Wed, 25 Oct 2000 01:35:44 +0000 Subject: [PATCH] *** empty log message *** --- ppremake/Makefile.am | 26 +-- ppremake/configure.in | 2 +- ppremake/ppremake.cxx | 55 ++++++- ppremake/sedAddress.cxx | 152 ++++++++++++++++++ ppremake/sedAddress.h | 44 ++++++ ppremake/sedCommand.cxx | 341 ++++++++++++++++++++++++++++++++++++++++ ppremake/sedCommand.h | 57 +++++++ ppremake/sedContext.cxx | 19 +++ ppremake/sedContext.h | 31 ++++ ppremake/sedProcess.cxx | 70 +++++++++ ppremake/sedProcess.h | 30 ++++ ppremake/sedScript.cxx | 97 ++++++++++++ ppremake/sedScript.h | 39 +++++ 13 files changed, 943 insertions(+), 20 deletions(-) create mode 100644 ppremake/sedAddress.cxx create mode 100644 ppremake/sedAddress.h create mode 100644 ppremake/sedCommand.cxx create mode 100644 ppremake/sedCommand.h create mode 100644 ppremake/sedContext.cxx create mode 100644 ppremake/sedContext.h create mode 100644 ppremake/sedProcess.cxx create mode 100644 ppremake/sedProcess.h create mode 100644 ppremake/sedScript.cxx create mode 100644 ppremake/sedScript.h diff --git a/ppremake/Makefile.am b/ppremake/Makefile.am index f1429e9528..a5abbad831 100644 --- a/ppremake/Makefile.am +++ b/ppremake/Makefile.am @@ -1,15 +1,17 @@ bin_PROGRAMS = ppremake -ppremake_SOURCES = \ - check_include.cxx check_include.h filename.cxx filename.h \ - find_searchpath.cxx find_searchpath.h \ - gnu_getopt.c gnu_getopt.h \ - ppCommandFile.cxx ppCommandFile.h ppDependableFile.cxx \ - ppDependableFile.h ppDirectory.cxx \ - ppDirectory.h ppDirectoryTree.cxx ppDirectoryTree.h \ - ppMain.cxx ppMain.h \ - ppFilenamePattern.cxx \ - ppFilenamePattern.h ppNamedScopes.cxx ppNamedScopes.h \ - ppScope.cxx ppScope.h ppSubroutine.cxx ppSubroutine.h \ - ppremake.cxx ppremake.h tokenize.cxx \ +ppremake_SOURCES = \ + check_include.cxx check_include.h filename.cxx filename.h \ + find_searchpath.cxx find_searchpath.h \ + gnu_getopt.c gnu_getopt.h \ + ppCommandFile.cxx ppCommandFile.h ppDependableFile.cxx \ + ppDependableFile.h ppDirectory.cxx \ + ppDirectory.h ppDirectoryTree.cxx ppDirectoryTree.h \ + ppMain.cxx ppMain.h \ + ppFilenamePattern.cxx \ + ppFilenamePattern.h ppNamedScopes.cxx ppNamedScopes.h \ + ppScope.cxx ppScope.h ppSubroutine.cxx ppSubroutine.h \ + ppremake.cxx ppremake.h sedAddress.cxx sedAddress.h sedCommand.cxx \ + sedCommand.h sedContext.cxx sedContext.h sedProcess.cxx \ + sedProcess.h sedScript.cxx sedScript.h tokenize.cxx \ tokenize.h diff --git a/ppremake/configure.in b/ppremake/configure.in index f9efac00bd..504a5f1d4b 100644 --- a/ppremake/configure.in +++ b/ppremake/configure.in @@ -1,6 +1,6 @@ dnl Process this file with autoconf to produce a configure script. AC_INIT(ppremake.cxx) -AM_INIT_AUTOMAKE(ppremake, 0.52) +AM_INIT_AUTOMAKE(ppremake, 0.54) AM_CONFIG_HEADER(config.h) AC_PREFIX_DEFAULT(/usr/local/panda) diff --git a/ppremake/ppremake.cxx b/ppremake/ppremake.cxx index 74a391466c..e6463b6a8e 100644 --- a/ppremake/ppremake.cxx +++ b/ppremake/ppremake.cxx @@ -8,6 +8,7 @@ #include "ppScope.h" #include "check_include.h" #include "tokenize.h" +#include "sedProcess.h" #ifdef HAVE_GETOPT #include @@ -30,6 +31,7 @@ usage() { "\n" "ppremake [opts] subdir-name [subdir-name..]\n" "ppremake\n" + "ppremake -s 'sed-command' output\n" "\n" "This is Panda pre-make: a script preprocessor that scans the source\n" "directory hierarchy containing the current directory, looking for\n" @@ -51,6 +53,11 @@ usage() { "generated. If no parameter is given, then all directories will be\n" "processed.\n\n" + "ppremake -s is a special form of the command that runs as a very limited\n" + "sed. It has nothing to do with building makefiles, but is provided mainly\n" + "so platforms that don't have sed built in can still portably run simple sed\n" + "scripts.\n\n" + "Options:\n\n" " -h Display this page.\n" @@ -66,7 +73,11 @@ usage() { " depend on (need) the named subdirectory. Options -d and\n" " -n may be combined, and you may also name multiple\n" " subdirectories to scan at once.\n\n" - " -p platform Build as if for the indicated platform name.\n\n"; + " -p platform Build as if for the indicated platform name.\n" + " -c config.pp Read the indicated user-level config.pp file after reading\n" + " the system config.pp file. If this is omitted, the value\n" + " given in the environment variable PPREMAKE_CONFIG is used\n" + " instead.\n\n"; } static void @@ -188,13 +199,17 @@ main(int argc, char *argv[]) { string progname = argv[0]; extern char *optarg; extern int optind; - const char *optstr = "hVPD:p:dn"; + const char *optstr = "hVPD:dnp:c:s:"; bool any_d = false; bool dependencies_stale = false; - string platform = PLATFORM; bool report_depends = false; bool report_needs = false; + string platform = PLATFORM; + string ppremake_config; + bool got_ppremake_config = false; + string sed_command; + bool got_sed_command = false; int flag = getopt(argc, argv, optstr); while (flag != EOF) { @@ -220,10 +235,6 @@ main(int argc, char *argv[]) { any_d = true; break; - case 'p': - platform = optarg; - break; - case 'd': report_depends = true; break; @@ -232,6 +243,20 @@ main(int argc, char *argv[]) { report_needs = true; break; + case 'p': + platform = optarg; + break; + + case 'c': + ppremake_config = optarg; + got_ppremake_config = true; + break; + + case 's': + sed_command = optarg; + got_sed_command = true; + break; + default: exit(1); } @@ -241,6 +266,15 @@ main(int argc, char *argv[]) { argc -= (optind-1); argv += (optind-1); + if (got_sed_command) { + SedProcess sp; + if (!sp.add_script_line(sed_command)) { + exit(1); + } + sp.run(cin, cout); + exit(0); + } + // If the user supplied one or more -d parameters, then we should // not continue unless some of the dependencies were stale. if (any_d) { @@ -257,6 +291,13 @@ main(int argc, char *argv[]) { global_scope.define_variable("PACKAGE_FILENAME", PACKAGE_FILENAME); global_scope.define_variable("SOURCE_FILENAME", SOURCE_FILENAME); + if (got_ppremake_config) { + // If this came in on the command line, define a variable as such. + // Otherwise, the system scripts can pull this value in from the + // similarly-named environment variable. + global_scope.define_variable("PPREMAKE_CONFIG", ppremake_config); + } + PPMain ppmain(&global_scope); if (!ppmain.read_source(".")) { exit(1); diff --git a/ppremake/sedAddress.cxx b/ppremake/sedAddress.cxx new file mode 100644 index 0000000000..ede61a14c8 --- /dev/null +++ b/ppremake/sedAddress.cxx @@ -0,0 +1,152 @@ +// Filename: sedAddress.cxx +// Created by: drose (24Oct00) +// +//////////////////////////////////////////////////////////////////// + +#include "sedAddress.h" +#include "sedContext.h" + +#include +#include +#include + +//////////////////////////////////////////////////////////////////// +// Function: SedAddress::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +SedAddress:: +SedAddress() { + _address_type = AT_invalid; +} + +//////////////////////////////////////////////////////////////////// +// Function: SedAddress::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +SedAddress:: +~SedAddress() { + if (_address_type == AT_re) { + regfree(&_re); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SedAddress::parse_address +// Access: Public +// Description: Scans the indicated string beginning at the indicated +// character position for an address specification, +// e.g. a number, $, or a regular expression. If a +// correct address is found, increments p to the first +// non-whitespace character past it and returns true; +// otherwise, returns false. +//////////////////////////////////////////////////////////////////// +bool SedAddress:: +parse_address(const string &line, size_t &p) { + assert(p < line.length()); + if (line[p] == '$') { + p++; + _address_type = AT_last; + + } else if (isdigit(line[p])) { + const char *str = line.c_str() + p; + char *end; + _number = strtol(str, &end, 10); + _address_type = AT_numeric; + p += (end - str); + + } else { + // It must be a regular expression. + size_t p0 = p; + char delimiter = line[p]; + p++; + if (p < line.length() && delimiter == '\\') { + // A backslash might escape the opening character. + delimiter = line[p]; + p++; + } + + size_t begin = p; + while (p < line.length() && line[p] != delimiter) { + if (line[p] == '\\') { + p++; + // A backslash could escape the closing character. + } + p++; + } + + if (p >= line.length()) { + cerr << "Could not find terminating character '" << delimiter + << "' in regular expression: " << line.substr(p0) << "\n"; + return false; + } + + string re = line.substr(begin, p - begin); + p++; + + int error = regcomp(&_re, re.c_str(), REG_NOSUB); + if (error != 0) { + static const int errbuf_size = 512; + char errbuf[errbuf_size]; + regerror(error, &_re, errbuf, errbuf_size); + + cerr << "Invalid regular expression: " << re << "\n" + << errbuf << "\n"; + return false; + } + + _address_type = AT_re; + } + + // Skip whitespace following the address. + while (p < line.length() && isspace(line[p])) { + p++; + } + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: SedAddress::matches +// Access: Public +// Description: Returns true if this address exactly matches the +// current pattern space. +//////////////////////////////////////////////////////////////////// +bool SedAddress:: +matches(const SedContext &context) const { + switch (_address_type) { + case AT_invalid: + cerr << "Internal error!\n"; + assert(false); + return false; + + case AT_numeric: + return (_number == context._line_number); + + case AT_last: + return context._is_last_line; + + case AT_re: + return (regexec(&_re, context._pattern_space.c_str(), 0, (regmatch_t *)NULL, 0) == 0); + } + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: SedAddress::precedes +// Access: Public +// Description: Returns true if this address exactly matches the +// current line or refers to a previous line. This +// never returns true if the address is a regular +// expression type. +//////////////////////////////////////////////////////////////////// +bool SedAddress:: +precedes(const SedContext &context) const { + if (_address_type == AT_numeric) { + return (_number <= context._line_number); + } + + return false; +} + diff --git a/ppremake/sedAddress.h b/ppremake/sedAddress.h new file mode 100644 index 0000000000..ba69496582 --- /dev/null +++ b/ppremake/sedAddress.h @@ -0,0 +1,44 @@ +// Filename: sedAddress.h +// Created by: drose (24Oct00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef SEDADDRESS_H +#define SEDADDRESS_H + +#include "ppremake.h" + +#include +#include + +class SedContext; + +/////////////////////////////////////////////////////////////////// +// Class : SedAddress +// Description : This represents a single address in a sed command, +// something like a line number or a regular expression. +//////////////////////////////////////////////////////////////////// +class SedAddress { +public: + SedAddress(); + ~SedAddress(); + + bool parse_address(const string &line, size_t &p); + + bool matches(const SedContext &context) const; + bool precedes(const SedContext &context) const; + +private: + enum AddressType { + AT_invalid, + AT_numeric, + AT_last, + AT_re, + }; + AddressType _address_type; + + int _number; + regex_t _re; +}; + +#endif diff --git a/ppremake/sedCommand.cxx b/ppremake/sedCommand.cxx new file mode 100644 index 0000000000..03ffbe471c --- /dev/null +++ b/ppremake/sedCommand.cxx @@ -0,0 +1,341 @@ +// Filename: sedCommand.cxx +// Created by: drose (24Oct00) +// +//////////////////////////////////////////////////////////////////// + +#include "sedCommand.h" +#include "sedAddress.h" +#include "sedContext.h" +#include "sedScript.h" + +#include + +//////////////////////////////////////////////////////////////////// +// Function: SedCommand::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +SedCommand:: +SedCommand() { + _addr1 = (SedAddress *)NULL; + _addr2 = (SedAddress *)NULL; + _command = '\0'; + _flags = 0; + _active = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: SedCommand::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +SedCommand:: +~SedCommand() { + if (_addr1 != (SedAddress *)NULL) { + delete _addr1; + } + if (_addr2 != (SedAddress *)NULL) { + delete _addr2; + } + if ((_flags & F_have_re) != 0) { + regfree(&_re); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SedCommand::parse_command +// Access: Public +// Description: Scans the indicated string at the given character +// position for a legal command. If a legal command is +// found, stores it and increments p to the first +// non-whitespace character after the command, returning +// true. Otherwise, returns false. +//////////////////////////////////////////////////////////////////// +bool SedCommand:: +parse_command(const string &line, size_t &p) { + // First, skip initial whitespace. + while (p < line.length() && isspace(line[p])) { + p++; + } + + // Now, check for an address. + if (p < line.length() && + (isdigit(line[p]) || line[p] == '/' || line[p] == '\\')) { + _addr1 = new SedAddress; + if (!_addr1->parse_address(line, p)) { + return false; + } + + if (p < line.length() && line[p] == ',') { + // Another address. + + // Skip the comma and more whitespace. + p++; + while (p < line.length() && isspace(line[p])) { + p++; + } + + _addr2 = new SedAddress; + if (!_addr2->parse_address(line, p)) { + return false; + } + } + } + + if (p >= line.length()) { + // It's a null command, which is acceptable; ignore it. + return true; + } + + _command = line[p]; + + // Skip more whitespace after the command letter. + p++; + while (p < line.length() && isspace(line[p])) { + p++; + } + + // At the moment, we only accept a small subset of sed commands. We + // can add more later as we see the need. + switch (_command) { + case 'd': + // No arguments. + return true; + + case 's': + // /regexp/repl/flags + return parse_s_params(line, p); + + default: + cerr << "Unknown command: " << _command << "\n"; + return false; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SedCommand::run +// Access: Public +// Description: Runs the script command, modifying the context and/or +// the script position as appropriate. +//////////////////////////////////////////////////////////////////// +void SedCommand:: +run(SedScript &script, SedContext &context) { + // First, see if this command matches the pattern space. + bool matches = false; + + if (_addr1 != (SedAddress *)NULL && _addr2 != (SedAddress *)NULL) { + // If the user supplied two addresses, all lines inclusive between + // the lines matched by the two addresses are considered matching. + if (_active) { + // We have previously matched _addr1. Therefore this line is + // in, but are the rest of the lines following this one? + matches = true; + if (_addr2->matches(context)) { + // If this line matches addr2, that's the end of our range for + // next time. + _active = false; + } + } else { + // We have not yet matched _addr1. This line and subsequent + // lines are in only if we match now. + if (_addr1->matches(context)) { + matches = true; + if (!_addr2->precedes(context)) { + _active = true; + } + } + } + + } else if (_addr1 != (SedAddress *)NULL) { + // If the user supplied only one address, only those lines that + // exactly match the address are considered matching. + matches = _addr1->matches(context); + + } else { + // If the user supplied no addresses, all lines are considered + // matching. + matches = true; + } + + if (matches) { + do_command(script, context); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SedCommand::parse_s_params +// Access: Private +// Description: Parses the /regexp/replacement/flags parameters that +// follow the 's' command. +//////////////////////////////////////////////////////////////////// +bool SedCommand:: +parse_s_params(const string &line, size_t &p) { + size_t p0 = p; + char delimiter = line[p]; + p++; + if (p < line.length() && delimiter == '\\') { + // A backslash might escape the opening character. + delimiter = line[p]; + p++; + } + + size_t begin = p; + while (p < line.length() && line[p] != delimiter) { + if (line[p] == '\\') { + p++; + // A backslash could escape the closing character. + } + p++; + } + + if (p >= line.length()) { + cerr << "Could not find terminating character '" << delimiter + << "' in regular expression: " << line.substr(p0) << "\n"; + return false; + } + + string re = line.substr(begin, p - begin); + p++; + + int error = regcomp(&_re, re.c_str(), 0); + if (error != 0) { + static const int errbuf_size = 512; + char errbuf[errbuf_size]; + regerror(error, &_re, errbuf, errbuf_size); + + cerr << "Invalid regular expression: " << re << "\n" + << errbuf << "\n"; + return false; + } + _flags |= F_have_re; + + // Get the replacement string. + begin = p; + while (p < line.length() && line[p] != delimiter) { + if (line[p] == '\\') { + p++; + // A backslash could escape the closing character. + } + p++; + } + + if (p >= line.length()) { + cerr << "Could not find terminating character '" << delimiter + << "' in replacement string: " << line.substr(p0) << "\n"; + return false; + } + + _string2 = line.substr(begin, p - begin); + + // Skip the final delimiter. + p++; + if (p < line.length() && line[p] == 'g') { + // Global flag. + p++; + _flags |= F_g; + } + + // Skip any more whitespace after the parameters. + while (p < line.length() && isspace(line[p])) { + p++; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: SedCommand::do_command +// Access: Private +// Description: Actually invokes the command, once it has been +// determined that the command applied to the current +// pattern space. +//////////////////////////////////////////////////////////////////// +void SedCommand:: +do_command(SedScript &script, SedContext &context) { + switch (_command) { + case '\0': + // Null command. + return; + + case 'd': + // Delete line. + context._deleted = true; + script._next_command = script._commands.end(); + return; + + case 's': + // Substitute. + do_s_command(context); + return; + } + + cerr << "Undefined command: " << _command << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: SedCommand::do_s_command +// Access: Private +// Description: Invokes the s command, which performs a +// pattern/replacement substitution. +//////////////////////////////////////////////////////////////////// +void SedCommand:: +do_s_command(SedContext &context) { + size_t nmatch = _re.re_nsub + 1; + regmatch_t pmatch[nmatch]; + + string result; + const char *str = context._pattern_space.c_str(); + int error = regexec(&_re, str, nmatch, pmatch, 0); + while (error == 0) { + // Here's a match. Determine the replacement. + string repl; + + size_t p = 0; + while (p < _string2.length()) { + if (_string2[p] == '\\') { + p++; + if (p < _string2.length()) { + if (isdigit(_string2[p])) { + // Here's a subexpression reference. + const char *numstr = _string2.c_str() + p; + char *numend; + int ref = strtol(numstr, &numend, 10); + p += (numend - numstr); + if (ref <= 0 || ref >= (int)nmatch) { + cerr << "Invalid subexpression number: " << ref << "\n"; + } else { + repl += string(str + pmatch[ref].rm_so, + pmatch[ref].rm_eo - pmatch[ref].rm_so); + } + } else { + // Here's an escaped character. + repl += _string2[p]; + p++; + } + } + } else { + // Here's a normal character. + repl += _string2[p]; + p++; + } + } + + // Store the result so far. + result += string(str, pmatch[0].rm_so); + result += repl; + str += pmatch[0].rm_eo; + + if ((_flags & F_g) == 0) { + // If we don't have the global flag set, stop after the first iteration. + result += str; + context._pattern_space = result; + return; + } + + error = regexec(&_re, str, nmatch, pmatch, 0); + } + + // All done. + result += str; + context._pattern_space = result; +} diff --git a/ppremake/sedCommand.h b/ppremake/sedCommand.h new file mode 100644 index 0000000000..b1003e7ebc --- /dev/null +++ b/ppremake/sedCommand.h @@ -0,0 +1,57 @@ +// Filename: sedCommand.h +// Created by: drose (24Oct00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef SEDCOMMAND_H +#define SEDCOMMAND_H + +#include "ppremake.h" + +#include +#include + +class SedScript; +class SedContext; +class SedAddress; + +/////////////////////////////////////////////////////////////////// +// Class : SedCommand +// Description : This represents a single command (of several +// possible, separated by semicolons) to a SedProgram. +//////////////////////////////////////////////////////////////////// +class SedCommand { +public: + SedCommand(); + ~SedCommand(); + + bool parse_command(const string &line, size_t &p); + + void run(SedScript &script, SedContext &context); + +private: + bool parse_s_params(const string &line, size_t &p); + void do_command(SedScript &script, SedContext &context); + void do_s_command(SedContext &context); + + SedAddress *_addr1; + SedAddress *_addr2; + char _command; + + string _text; + + regex_t _re; + string _string1; + string _string2; + + enum Flags { + F_have_re = 0x001, + F_g = 0x002, + }; + + int _flags; + + bool _active; +}; + +#endif diff --git a/ppremake/sedContext.cxx b/ppremake/sedContext.cxx new file mode 100644 index 0000000000..90d0fae4a7 --- /dev/null +++ b/ppremake/sedContext.cxx @@ -0,0 +1,19 @@ +// Filename: sedContext.cxx +// Created by: drose (24Oct00) +// +//////////////////////////////////////////////////////////////////// + +#include "sedContext.h" + + +//////////////////////////////////////////////////////////////////// +// Function: SedContext::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +SedContext:: +SedContext(ostream &out) : _out(out) { + _line_number = 0; + _is_last_line = false; + _deleted = false; +} diff --git a/ppremake/sedContext.h b/ppremake/sedContext.h new file mode 100644 index 0000000000..d3425b6d4c --- /dev/null +++ b/ppremake/sedContext.h @@ -0,0 +1,31 @@ +// Filename: sedContext.h +// Created by: drose (24Oct00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef SEDCONTEXT_H +#define SEDCONTEXT_H + +#include "ppremake.h" + +/////////////////////////////////////////////////////////////////// +// Class : SedContext +// Description : This contains the current context of the sed process +// as it is running: the pattern space, the hold space, +// and the current line numbers, etc. It is updated as +// each line is read in and as each command is executed. +//////////////////////////////////////////////////////////////////// +class SedContext { +public: + SedContext(ostream &out); + + int _line_number; + bool _is_last_line; + string _pattern_space; + string _hold_space; + bool _deleted; + + ostream &_out; +}; + +#endif diff --git a/ppremake/sedProcess.cxx b/ppremake/sedProcess.cxx new file mode 100644 index 0000000000..472a9e6f9d --- /dev/null +++ b/ppremake/sedProcess.cxx @@ -0,0 +1,70 @@ +// Filename: sedProcess.cxx +// Created by: drose (24Oct00) +// +//////////////////////////////////////////////////////////////////// + +#include "sedProcess.h" +#include "sedContext.h" + +//////////////////////////////////////////////////////////////////// +// Function: SedProcess::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +SedProcess:: +SedProcess() { +} + +//////////////////////////////////////////////////////////////////// +// Function: SedProcess::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +SedProcess:: +~SedProcess() { +} + + +//////////////////////////////////////////////////////////////////// +// Function: SedProcess::add_script_line +// Access: Public +// Description: Appends the indicated line to the end of the script +// that will be executed for each line of the input +// stream. This may be called as many times as you +// like. +// +// The return value is true if the line was added +// successfully, or false if there was an error in the +// line (in which case, some commands on the line might +// have been added, and others not). +//////////////////////////////////////////////////////////////////// +bool SedProcess:: +add_script_line(const string &line) { + return _script.add_line(line); +} + + +//////////////////////////////////////////////////////////////////// +// Function: SedProcess::run +// Access: Public +// Description: Reads the input stream and executes the script once +// for each line on the input stream. Output is written +// to the indicated output stream. +//////////////////////////////////////////////////////////////////// +void SedProcess:: +run(istream &in, ostream &out) { + SedContext context(out); + + string line; + getline(in, line); + while (!in.fail() && !in.eof()) { + context._pattern_space = line; + context._line_number++; + getline(in, line); + + if (in.eof()) { + context._is_last_line = true; + } + _script.run(context); + } +} diff --git a/ppremake/sedProcess.h b/ppremake/sedProcess.h new file mode 100644 index 0000000000..2459f68a09 --- /dev/null +++ b/ppremake/sedProcess.h @@ -0,0 +1,30 @@ +// Filename: sedProcess.h +// Created by: drose (24Oct00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef SEDPROCESS_H +#define SEDPROCESS_H + +#include "ppremake.h" +#include "sedScript.h" + +/////////////////////////////////////////////////////////////////// +// Class : SedProcess +// Description : This supervises the whole sed process, from beginning +// to end. +//////////////////////////////////////////////////////////////////// +class SedProcess { +public: + SedProcess(); + ~SedProcess(); + + bool add_script_line(const string &line); + + void run(istream &in, ostream &out); + +private: + SedScript _script; +}; + +#endif diff --git a/ppremake/sedScript.cxx b/ppremake/sedScript.cxx new file mode 100644 index 0000000000..16d1c0809c --- /dev/null +++ b/ppremake/sedScript.cxx @@ -0,0 +1,97 @@ +// Filename: sedScript.cxx +// Created by: drose (24Oct00) +// +//////////////////////////////////////////////////////////////////// + +#include "sedScript.h" +#include "sedCommand.h" +#include "sedContext.h" + + +//////////////////////////////////////////////////////////////////// +// Function: SedScript::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +SedScript:: +SedScript() { + _quit = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: SedScript::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +SedScript:: +~SedScript() { + Commands::iterator ci; + for (ci = _commands.begin(); ci != _commands.end(); ++ci) { + delete (*ci); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SedScript::add_line +// Access: Public +// Description: Adds the indicated script line to the script. +// Returns true if it is a valid line, false if there is +// an error. +//////////////////////////////////////////////////////////////////// +bool SedScript:: +add_line(const string &line) { + size_t p = 0; + SedCommand *command = new SedCommand; + if (!command->parse_command(line, p)) { + // That's an invalid command. + delete command; + return false; + } + _commands.push_back(command); + + while (p < line.length()) { + // There's more to the line. + if (line[p] != ';') { + // But it's an error. + cerr << "Invalid character at: " << line.substr(p) << "\n"; + return false; + } + p++; + + command = new SedCommand; + if (!command->parse_command(line, p)) { + // That's an invalid command. + delete command; + return false; + } + _commands.push_back(command); + } + + // Everything parsed ok. + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: SedScript::run +// Access: Public +// Description: Runs the script, modifying the context as +// appropriate. Returns true if the process should +// continue with the next line, or false if we have quit +// and we should terminate. +//////////////////////////////////////////////////////////////////// +bool SedScript:: +run(SedContext &context) { + context._deleted = false; + _next_command = _commands.begin(); + while (!_quit && _next_command != _commands.end()) { + SedCommand *command = (*_next_command); + ++_next_command; + command->run(*this, context); + } + + if (!context._deleted) { + context._out << context._pattern_space << "\n"; + } + + return !_quit; +} diff --git a/ppremake/sedScript.h b/ppremake/sedScript.h new file mode 100644 index 0000000000..1faadc1bdc --- /dev/null +++ b/ppremake/sedScript.h @@ -0,0 +1,39 @@ +// Filename: sedScript.h +// Created by: drose (24Oct00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef SEDSCRIPT_H +#define SEDSCRIPT_H + +#include "ppremake.h" + +#include + +class SedCommand; +class SedContext; + +/////////////////////////////////////////////////////////////////// +// Class : SedScript +// Description : This is a complete sed script: a linear list of +// commands that are to be applied for each line read +// from input. +//////////////////////////////////////////////////////////////////// +class SedScript { +public: + SedScript(); + ~SedScript(); + + bool add_line(const string &line); + + bool run(SedContext &context); + +public: + bool _quit; + + typedef vector Commands; + Commands _commands; + Commands::const_iterator _next_command; +}; + +#endif