1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmCommandArgumentParserHelper.h"
11 #include <cm/optional>
12 #include <cmext/string_view>
14 #include "cmCommandArgumentLexer.h"
15 #include "cmListFileCache.h"
16 #include "cmMakefile.h"
18 #include "cmStringAlgorithms.h"
19 #include "cmSystemTools.h"
22 int cmCommandArgument_yyparse(yyscan_t yyscanner);
24 cmCommandArgumentParserHelper::cmCommandArgumentParserHelper()
27 this->FileName = nullptr;
28 this->RemoveEmpty = true;
30 this->NoEscapeMode = false;
31 this->ReplaceAtSyntax = false;
34 cmCommandArgumentParserHelper::~cmCommandArgumentParserHelper()
36 this->CleanupParser();
39 void cmCommandArgumentParserHelper::SetLineFile(long line, const char* file)
41 this->FileLine = line;
42 this->FileName = file;
45 const char* cmCommandArgumentParserHelper::AddString(const std::string& str)
50 auto stVal = cm::make_unique<char[]>(str.size() + 1);
51 strcpy(stVal.get(), str.c_str());
52 this->Variables.push_back(std::move(stVal));
53 return this->Variables.back().get();
56 const char* cmCommandArgumentParserHelper::ExpandSpecialVariable(
57 const char* key, const char* var)
60 return this->ExpandVariable(var);
65 if (strcmp(key, "ENV") == 0) {
67 if (cmSystemTools::GetEnv(var, str)) {
68 if (this->EscapeQuotes) {
69 return this->AddString(cmEscapeQuotes(str));
71 return this->AddString(str);
75 if (strcmp(key, "CACHE") == 0) {
77 this->Makefile->GetState()->GetInitializedCacheValue(var)) {
78 if (this->EscapeQuotes) {
79 return this->AddString(cmEscapeQuotes(*c));
81 return this->AddString(*c);
86 e << "Syntax $" << key << "{} is not supported. "
87 << "Only ${}, $ENV{}, and $CACHE{} are allowed.";
88 this->SetError(e.str());
92 const char* cmCommandArgumentParserHelper::ExpandVariable(const char* var)
97 if (this->FileLine >= 0 && strcmp(var, "CMAKE_CURRENT_LIST_LINE") == 0) {
99 cmListFileContext const& top = this->Makefile->GetBacktrace().Top();
101 line = cmStrCat("DEFERRED:"_s, *top.DeferId);
103 line = std::to_string(this->FileLine);
105 return this->AddString(line);
107 cmValue value = this->Makefile->GetDefinition(var);
109 this->Makefile->MaybeWarnUninitialized(var, this->FileName);
110 if (!this->RemoveEmpty) {
114 if (this->EscapeQuotes && value) {
115 return this->AddString(cmEscapeQuotes(*value));
117 return this->AddString(value);
120 const char* cmCommandArgumentParserHelper::ExpandVariableForAt(const char* var)
122 if (this->ReplaceAtSyntax) {
123 // try to expand the variable
124 const char* ret = this->ExpandVariable(var);
125 // if the return was 0 and we want to replace empty strings
126 // then return an empty string
127 if (!ret && this->RemoveEmpty) {
128 return this->AddString("");
130 // if the ret was not 0, then return it
135 // at this point we want to put it back because of one of these cases:
136 // - this->ReplaceAtSyntax is false
137 // - this->ReplaceAtSyntax is true, but this->RemoveEmpty is false,
138 // and the variable was not defined
139 std::string ref = cmStrCat('@', var, '@');
140 return this->AddString(ref);
143 const char* cmCommandArgumentParserHelper::CombineUnions(const char* in1,
152 size_t len = strlen(in1) + strlen(in2) + 1;
153 auto out = cm::make_unique<char[]>(len);
154 strcpy(out.get(), in1);
155 strcat(out.get(), in2);
156 this->Variables.push_back(std::move(out));
157 return this->Variables.back().get();
160 void cmCommandArgumentParserHelper::AllocateParserType(
161 cmCommandArgumentParserHelper::ParserType* pt, const char* str, int len)
165 len = static_cast<int>(strlen(str));
170 auto out = cm::make_unique<char[]>(len + 1);
171 memcpy(out.get(), str, len);
174 this->Variables.push_back(std::move(out));
177 bool cmCommandArgumentParserHelper::HandleEscapeSymbol(
178 cmCommandArgumentParserHelper::ParserType* pt, char symbol)
190 this->AllocateParserType(pt, &symbol, 1);
193 this->AllocateParserType(pt, "\\;", 2);
196 this->AllocateParserType(pt, "\t", 1);
199 this->AllocateParserType(pt, "\n", 1);
202 this->AllocateParserType(pt, "\r", 1);
205 this->AllocateParserType(pt, "\0", 1);
208 std::ostringstream e;
209 e << "Invalid escape sequence \\" << symbol;
210 this->SetError(e.str());
217 void cmCommandArgument_SetupEscapes(yyscan_t yyscanner, bool noEscapes);
219 int cmCommandArgumentParserHelper::ParseString(std::string const& str,
225 this->InputSize = str.size();
226 this->Verbose = verb;
228 this->Result.clear();
231 cmCommandArgument_yylex_init(&yyscanner);
232 auto* scanBuf = cmCommandArgument_yy_scan_string(str.c_str(), yyscanner);
233 cmCommandArgument_yyset_extra(this, yyscanner);
234 cmCommandArgument_SetupEscapes(yyscanner, this->NoEscapeMode);
235 int res = cmCommandArgument_yyparse(yyscanner);
236 cmCommandArgument_yy_delete_buffer(scanBuf, yyscanner);
237 cmCommandArgument_yylex_destroy(yyscanner);
242 this->CleanupParser();
245 std::cerr << "Expanding [" << str << "] produced: [" << this->Result << "]"
251 void cmCommandArgumentParserHelper::CleanupParser()
253 this->Variables.clear();
256 void cmCommandArgumentParserHelper::Error(const char* str)
258 auto pos = this->InputBufferPos;
259 auto const isEof = (this->InputSize < this->InputBufferPos);
261 pos -= this->LastTokenLength;
264 std::ostringstream ostr;
265 ostr << str << " (" << pos << ")";
266 this->SetError(ostr.str());
269 void cmCommandArgumentParserHelper::SetMakefile(const cmMakefile* mf)
274 void cmCommandArgumentParserHelper::SetResult(const char* value)
277 this->Result.clear();
280 this->Result = value;
283 void cmCommandArgumentParserHelper::SetError(std::string const& msg)
285 // Keep only the first error.
286 if (this->ErrorString.empty()) {
287 this->ErrorString = msg;
291 void cmCommandArgumentParserHelper::UpdateInputPosition(int const tokenLength)
293 this->InputBufferPos += tokenLength;
294 this->LastTokenLength = tokenLength;