/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ // regexpr.cpp #include "always.h" #include "regexpr.h" #include "wwstring.h" #include // Pull in the gnu_regex library's definitions. #define __STDC__ 1 extern "C" { #include "gnu_regex.h" } // The regular expression syntax options that RegularExpressionClass uses. // The dirty details of each option are described in "gnu_regex.h" #define OUR_SYNTAX_OPTIONS \ RE_CHAR_CLASSES | /* Support character classes such as [:alpha:] and [:digit:] */ \ RE_CONTEXT_INDEP_ANCHORS | /* ^ and $ are always anchors (outside bracket expressions) */ \ RE_CONTEXT_INDEP_OPS | /* operators such as + * ? are always considered operators */ \ RE_CONTEXT_INVALID_OPS | /* operators are invalid as the first characters in a string */ \ RE_INTERVALS | /* { } are used to define intervals */ \ RE_NO_BK_BRACES | /* { } are interval markers and \{ \} are literals */ \ RE_NO_BK_PARENS | /* ( ) are group markers and \( \) are literals */ \ RE_NO_BK_VBAR | /* | is the OR operator and \| is a literal */ \ RE_NO_EMPTY_RANGES /* [z-a] is an invalid range but [a-z] is valid */ /* ** Definition of private DataStruct for RegularExpressionClass */ struct RegularExpressionClass::DataStruct { DataStruct (void) : IsValid(false) { // Blank out the expression structure. memset(&CompiledExpr, 0, sizeof(CompiledExpr)); } ~DataStruct (void) { ClearExpression(); } void ClearExpression (void) { // If the expression was valid, let the gnu_regex library // deallocate any memory it had allocated for it. if (IsValid) regfree(&CompiledExpr); // Blank out the expression structure. memset(&CompiledExpr, 0, sizeof(CompiledExpr)); // Erase the expression string. ExprString = ""; // No longer a valid compiled expression. IsValid = false; } // The regular expression that has been compiled. StringClass ExprString; // gnu_regex compiled version of the regular expression used // during matching or any form of evaluation regex_t CompiledExpr; // True if CompiledExpr is valid. bool IsValid; }; /* ** RegularExpressionClass Implementation */ RegularExpressionClass::RegularExpressionClass (const char *expression) : Data(0) { // Allocate our private members. Data = new DataStruct; assert(Data); // Compile the expression if we were given one. if (expression) Compile(expression); } RegularExpressionClass::RegularExpressionClass (const RegularExpressionClass ©) : Data(0) { // Allocate our private members. Data = new DataStruct; assert(Data); // Compile the expression if the given object had one. if (copy.Is_Valid()) { Compile(copy.Data->ExprString); assert(Is_Valid()); } } RegularExpressionClass::~RegularExpressionClass (void) { delete Data; Data = 0; } bool RegularExpressionClass::Compile (const char *expression) { assert(Data); assert(expression); // Clear any existing expression data. This makes it safe to // call Compile() twice on one object. Data->ClearExpression(); // Set the regular expression module to the syntax that we // would like to use. reg_syntax_t old_syntax = re_set_syntax(OUR_SYNTAX_OPTIONS); // Compile the given expression. const char *error_str = re_compile_pattern(expression, strlen(expression), &Data->CompiledExpr); // Restore the old syntax setting. re_set_syntax(old_syntax); // If no error string was returned, the expression was good! if (error_str == 0) { Data->IsValid = true; Data->ExprString = expression; return true; } return false; } bool RegularExpressionClass::Is_Valid (void) const { assert(Data); return Data->IsValid; } bool RegularExpressionClass::Match (const char *string) const { assert(Data); // If we have no valid compiled expression, we can't match Jack. if (!Data->IsValid) return false; // Set the regular expression module to the syntax that we // would like to use. reg_syntax_t old_syntax = re_set_syntax(OUR_SYNTAX_OPTIONS); // Try to match the given string with our regular expression. int retval = re_match(&Data->CompiledExpr, string, strlen(string), 0, 0); // Restore the old syntax setting. re_set_syntax(old_syntax); // -1 means no match, -2 means internal gnu_regex lib error, otherwise // re_match returned the number of characters matched. A 0 character // match is valid, and distinctly different than no match at all. if (retval < 0) return false; // The given string matched our regular expression! return true; } /* ** Operators */ RegularExpressionClass & RegularExpressionClass::operator = (const RegularExpressionClass &rhs) { // Check for assignment to self. if (*this == rhs) return *this; // Assign that object to this one. assert(rhs.Data); Compile(rhs.Data->ExprString); assert(Is_Valid()); // Return this object. return *this; } bool RegularExpressionClass::operator == (const RegularExpressionClass &rhs) const { // Two RegularExpressionClass objects are equivalent if they both // have the same validity state, and if that state is 'true' both // of their expressions are the same. // Check validity states for equality. if (Is_Valid() != rhs.Is_Valid()) return false; // If they're valid, check their expressions. if (Is_Valid()) { // The objects are not equivalent if their expression strings // don't match. if (Data->ExprString != rhs.Data->ExprString) return false; } return true; } inline bool RegularExpressionClass::operator != (const RegularExpressionClass &rhs) const { return !(*this == rhs); }