diff options
Diffstat (limited to 'Source/cmCommandArgumentParserHelper.cxx')
-rw-r--r-- | Source/cmCommandArgumentParserHelper.cxx | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/Source/cmCommandArgumentParserHelper.cxx b/Source/cmCommandArgumentParserHelper.cxx new file mode 100644 index 0000000..deddba8 --- /dev/null +++ b/Source/cmCommandArgumentParserHelper.cxx @@ -0,0 +1,294 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmCommandArgumentParserHelper.h" + +#include <cstring> +#include <iostream> +#include <sstream> +#include <utility> + +#include <cm/memory> +#include <cm/optional> +#include <cmext/string_view> + +#include "cmCommandArgumentLexer.h" +#include "cmListFileCache.h" +#include "cmMakefile.h" +#include "cmProperty.h" +#include "cmState.h" +#include "cmStringAlgorithms.h" +#include "cmSystemTools.h" + +int cmCommandArgument_yyparse(yyscan_t yyscanner); +// +cmCommandArgumentParserHelper::cmCommandArgumentParserHelper() +{ + this->FileLine = -1; + this->FileName = nullptr; + this->RemoveEmpty = true; + + this->NoEscapeMode = false; + this->ReplaceAtSyntax = false; +} + +cmCommandArgumentParserHelper::~cmCommandArgumentParserHelper() +{ + this->CleanupParser(); +} + +void cmCommandArgumentParserHelper::SetLineFile(long line, const char* file) +{ + this->FileLine = line; + this->FileName = file; +} + +const char* cmCommandArgumentParserHelper::AddString(const std::string& str) +{ + if (str.empty()) { + return ""; + } + auto stVal = cm::make_unique<char[]>(str.size() + 1); + strcpy(stVal.get(), str.c_str()); + this->Variables.push_back(std::move(stVal)); + return this->Variables.back().get(); +} + +const char* cmCommandArgumentParserHelper::ExpandSpecialVariable( + const char* key, const char* var) +{ + if (!key) { + return this->ExpandVariable(var); + } + if (!var) { + return ""; + } + if (strcmp(key, "ENV") == 0) { + std::string str; + if (cmSystemTools::GetEnv(var, str)) { + if (this->EscapeQuotes) { + return this->AddString(cmEscapeQuotes(str)); + } + return this->AddString(str); + } + return ""; + } + if (strcmp(key, "CACHE") == 0) { + if (cmProp c = this->Makefile->GetState()->GetInitializedCacheValue(var)) { + if (this->EscapeQuotes) { + return this->AddString(cmEscapeQuotes(*c)); + } + return this->AddString(*c); + } + return ""; + } + std::ostringstream e; + e << "Syntax $" << key << "{} is not supported. " + << "Only ${}, $ENV{}, and $CACHE{} are allowed."; + this->SetError(e.str()); + return nullptr; +} + +const char* cmCommandArgumentParserHelper::ExpandVariable(const char* var) +{ + if (!var) { + return nullptr; + } + if (this->FileLine >= 0 && strcmp(var, "CMAKE_CURRENT_LIST_LINE") == 0) { + std::string line; + cmListFileContext const& top = this->Makefile->GetBacktrace().Top(); + if (top.DeferId) { + line = cmStrCat("DEFERRED:"_s, *top.DeferId); + } else { + line = std::to_string(this->FileLine); + } + return this->AddString(line); + } + cmProp value = this->Makefile->GetDefinition(var); + if (!value) { + this->Makefile->MaybeWarnUninitialized(var, this->FileName); + if (!this->RemoveEmpty) { + return nullptr; + } + } + if (this->EscapeQuotes && value) { + return this->AddString(cmEscapeQuotes(*value)); + } + return this->AddString(cmToCStrSafe(value)); +} + +const char* cmCommandArgumentParserHelper::ExpandVariableForAt(const char* var) +{ + if (this->ReplaceAtSyntax) { + // try to expand the variable + const char* ret = this->ExpandVariable(var); + // if the return was 0 and we want to replace empty strings + // then return an empty string + if (!ret && this->RemoveEmpty) { + return this->AddString(""); + } + // if the ret was not 0, then return it + if (ret) { + return ret; + } + } + // at this point we want to put it back because of one of these cases: + // - this->ReplaceAtSyntax is false + // - this->ReplaceAtSyntax is true, but this->RemoveEmpty is false, + // and the variable was not defined + std::string ref = cmStrCat('@', var, '@'); + return this->AddString(ref); +} + +const char* cmCommandArgumentParserHelper::CombineUnions(const char* in1, + const char* in2) +{ + if (!in1) { + return in2; + } + if (!in2) { + return in1; + } + size_t len = strlen(in1) + strlen(in2) + 1; + auto out = cm::make_unique<char[]>(len); + strcpy(out.get(), in1); + strcat(out.get(), in2); + this->Variables.push_back(std::move(out)); + return this->Variables.back().get(); +} + +void cmCommandArgumentParserHelper::AllocateParserType( + cmCommandArgumentParserHelper::ParserType* pt, const char* str, int len) +{ + pt->str = nullptr; + if (len == 0) { + len = static_cast<int>(strlen(str)); + } + if (len == 0) { + return; + } + auto out = cm::make_unique<char[]>(len + 1); + memcpy(out.get(), str, len); + out.get()[len] = 0; + pt->str = out.get(); + this->Variables.push_back(std::move(out)); +} + +bool cmCommandArgumentParserHelper::HandleEscapeSymbol( + cmCommandArgumentParserHelper::ParserType* pt, char symbol) +{ + switch (symbol) { + case '\\': + case '"': + case ' ': + case '#': + case '(': + case ')': + case '$': + case '@': + case '^': + this->AllocateParserType(pt, &symbol, 1); + break; + case ';': + this->AllocateParserType(pt, "\\;", 2); + break; + case 't': + this->AllocateParserType(pt, "\t", 1); + break; + case 'n': + this->AllocateParserType(pt, "\n", 1); + break; + case 'r': + this->AllocateParserType(pt, "\r", 1); + break; + case '0': + this->AllocateParserType(pt, "\0", 1); + break; + default: { + std::ostringstream e; + e << "Invalid escape sequence \\" << symbol; + this->SetError(e.str()); + } + return false; + } + return true; +} + +void cmCommandArgument_SetupEscapes(yyscan_t yyscanner, bool noEscapes); + +int cmCommandArgumentParserHelper::ParseString(std::string const& str, + int verb) +{ + if (str.empty()) { + return 0; + } + this->InputSize = str.size(); + this->Verbose = verb; + + this->Result.clear(); + + yyscan_t yyscanner; + cmCommandArgument_yylex_init(&yyscanner); + auto* scanBuf = cmCommandArgument_yy_scan_string(str.c_str(), yyscanner); + cmCommandArgument_yyset_extra(this, yyscanner); + cmCommandArgument_SetupEscapes(yyscanner, this->NoEscapeMode); + int res = cmCommandArgument_yyparse(yyscanner); + cmCommandArgument_yy_delete_buffer(scanBuf, yyscanner); + cmCommandArgument_yylex_destroy(yyscanner); + if (res != 0) { + return 0; + } + + this->CleanupParser(); + + if (this->Verbose) { + std::cerr << "Expanding [" << str << "] produced: [" << this->Result << "]" + << std::endl; + } + return 1; +} + +void cmCommandArgumentParserHelper::CleanupParser() +{ + this->Variables.clear(); +} + +void cmCommandArgumentParserHelper::Error(const char* str) +{ + auto pos = this->InputBufferPos; + auto const isEof = (this->InputSize < this->InputBufferPos); + if (!isEof) { + pos -= this->LastTokenLength; + } + + std::ostringstream ostr; + ostr << str << " (" << pos << ")"; + this->SetError(ostr.str()); +} + +void cmCommandArgumentParserHelper::SetMakefile(const cmMakefile* mf) +{ + this->Makefile = mf; +} + +void cmCommandArgumentParserHelper::SetResult(const char* value) +{ + if (!value) { + this->Result.clear(); + return; + } + this->Result = value; +} + +void cmCommandArgumentParserHelper::SetError(std::string const& msg) +{ + // Keep only the first error. + if (this->ErrorString.empty()) { + this->ErrorString = msg; + } +} + +void cmCommandArgumentParserHelper::UpdateInputPosition(int const tokenLength) +{ + this->InputBufferPos += tokenLength; + this->LastTokenLength = tokenLength; +} |