/* 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 "cmCommandArgumentLexer.h" #include "cmMakefile.h" #include "cmState.h" #include "cmSystemTools.h" #include "cmake.h" #include #include #include int cmCommandArgument_yyparse(yyscan_t yyscanner); // cmCommandArgumentParserHelper::cmCommandArgumentParserHelper() { this->WarnUninitialized = false; this->CheckSystemVars = false; 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 ""; } char* stVal = new char[str.size() + 1]; strcpy(stVal, str.c_str()); this->Variables.push_back(stVal); return stVal; } 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(cmSystemTools::EscapeQuotes(str)); } return this->AddString(str); } return ""; } if (strcmp(key, "CACHE") == 0) { if (const char* c = this->Makefile->GetState()->GetInitializedCacheValue(var)) { if (this->EscapeQuotes) { return this->AddString(cmSystemTools::EscapeQuotes(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::ostringstream ostr; ostr << this->FileLine; return this->AddString(ostr.str()); } const char* value = this->Makefile->GetDefinition(var); if (!value && !this->RemoveEmpty) { // check to see if we need to print a warning // if strict mode is on and the variable has // not been "cleared"/initialized with a set(foo ) call if (this->WarnUninitialized && !this->Makefile->VariableInitialized(var)) { if (this->CheckSystemVars || (this->FileName && (cmSystemTools::IsSubDirectory( this->FileName, this->Makefile->GetHomeDirectory()) || cmSystemTools::IsSubDirectory( this->FileName, this->Makefile->GetHomeOutputDirectory())))) { std::ostringstream msg; msg << "uninitialized variable \'" << var << "\'"; this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, msg.str()); } } return nullptr; } if (this->EscapeQuotes && value) { return this->AddString(cmSystemTools::EscapeQuotes(value)); } return this->AddString(value ? 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 = "@"; ref += var; ref += "@"; 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; char* out = new char[len]; strcpy(out, in1); strcat(out, in2); this->Variables.push_back(out); return out; } void cmCommandArgumentParserHelper::AllocateParserType( cmCommandArgumentParserHelper::ParserType* pt, const char* str, int len) { pt->str = nullptr; if (len == 0) { len = static_cast(strlen(str)); } if (len == 0) { return; } char* out = new char[len + 1]; memcpy(out, str, len); out[len] = 0; pt->str = out; this->Variables.push_back(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(const char* str, int verb) { if (!str) { return 0; } this->Verbose = verb; this->InputBuffer = str; this->InputBufferPos = 0; this->CurrentLine = 0; this->Result.clear(); yyscan_t yyscanner; cmCommandArgument_yylex_init(&yyscanner); cmCommandArgument_yyset_extra(this, yyscanner); cmCommandArgument_SetupEscapes(yyscanner, this->NoEscapeMode); int res = cmCommandArgument_yyparse(yyscanner); cmCommandArgument_yylex_destroy(yyscanner); if (res != 0) { return 0; } this->CleanupParser(); if (Verbose) { std::cerr << "Expanding [" << str << "] produced: [" << this->Result << "]" << std::endl; } return 1; } void cmCommandArgumentParserHelper::CleanupParser() { std::vector::iterator sit; for (char* var : this->Variables) { delete[] var; } this->Variables.erase(this->Variables.begin(), this->Variables.end()); } int cmCommandArgumentParserHelper::LexInput(char* buf, int maxlen) { if (maxlen < 1) { return 0; } if (this->InputBufferPos < this->InputBuffer.size()) { buf[0] = this->InputBuffer[this->InputBufferPos++]; if (buf[0] == '\n') { this->CurrentLine++; } return (1); } buf[0] = '\n'; return (0); } void cmCommandArgumentParserHelper::Error(const char* str) { unsigned long pos = static_cast(this->InputBufferPos); std::ostringstream ostr; ostr << str << " (" << pos << ")"; this->SetError(ostr.str()); } void cmCommandArgumentParserHelper::SetMakefile(const cmMakefile* mf) { this->Makefile = mf; this->WarnUninitialized = mf->GetCMakeInstance()->GetWarnUninitialized(); this->CheckSystemVars = mf->GetCMakeInstance()->GetCheckSystemVars(); } 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; } }