resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmCommandArgumentParserHelper.cxx
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"
4
5 #include <cstring>
6 #include <iostream>
7 #include <sstream>
8 #include <utility>
9
10 #include <cm/memory>
11 #include <cm/optional>
12 #include <cmext/string_view>
13
14 #include "cmCommandArgumentLexer.h"
15 #include "cmListFileCache.h"
16 #include "cmMakefile.h"
17 #include "cmState.h"
18 #include "cmStringAlgorithms.h"
19 #include "cmSystemTools.h"
20 #include "cmValue.h"
21
22 int cmCommandArgument_yyparse(yyscan_t yyscanner);
23 //
24 cmCommandArgumentParserHelper::cmCommandArgumentParserHelper()
25 {
26   this->FileLine = -1;
27   this->FileName = nullptr;
28   this->RemoveEmpty = true;
29
30   this->NoEscapeMode = false;
31   this->ReplaceAtSyntax = false;
32 }
33
34 cmCommandArgumentParserHelper::~cmCommandArgumentParserHelper()
35 {
36   this->CleanupParser();
37 }
38
39 void cmCommandArgumentParserHelper::SetLineFile(long line, const char* file)
40 {
41   this->FileLine = line;
42   this->FileName = file;
43 }
44
45 const char* cmCommandArgumentParserHelper::AddString(const std::string& str)
46 {
47   if (str.empty()) {
48     return "";
49   }
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();
54 }
55
56 const char* cmCommandArgumentParserHelper::ExpandSpecialVariable(
57   const char* key, const char* var)
58 {
59   if (!key) {
60     return this->ExpandVariable(var);
61   }
62   if (!var) {
63     return "";
64   }
65   if (strcmp(key, "ENV") == 0) {
66     std::string str;
67     if (cmSystemTools::GetEnv(var, str)) {
68       if (this->EscapeQuotes) {
69         return this->AddString(cmEscapeQuotes(str));
70       }
71       return this->AddString(str);
72     }
73     return "";
74   }
75   if (strcmp(key, "CACHE") == 0) {
76     if (cmValue c =
77           this->Makefile->GetState()->GetInitializedCacheValue(var)) {
78       if (this->EscapeQuotes) {
79         return this->AddString(cmEscapeQuotes(*c));
80       }
81       return this->AddString(*c);
82     }
83     return "";
84   }
85   std::ostringstream e;
86   e << "Syntax $" << key << "{} is not supported.  "
87     << "Only ${}, $ENV{}, and $CACHE{} are allowed.";
88   this->SetError(e.str());
89   return nullptr;
90 }
91
92 const char* cmCommandArgumentParserHelper::ExpandVariable(const char* var)
93 {
94   if (!var) {
95     return nullptr;
96   }
97   if (this->FileLine >= 0 && strcmp(var, "CMAKE_CURRENT_LIST_LINE") == 0) {
98     std::string line;
99     cmListFileContext const& top = this->Makefile->GetBacktrace().Top();
100     if (top.DeferId) {
101       line = cmStrCat("DEFERRED:"_s, *top.DeferId);
102     } else {
103       line = std::to_string(this->FileLine);
104     }
105     return this->AddString(line);
106   }
107   cmValue value = this->Makefile->GetDefinition(var);
108   if (!value) {
109     this->Makefile->MaybeWarnUninitialized(var, this->FileName);
110     if (!this->RemoveEmpty) {
111       return nullptr;
112     }
113   }
114   if (this->EscapeQuotes && value) {
115     return this->AddString(cmEscapeQuotes(*value));
116   }
117   return this->AddString(value);
118 }
119
120 const char* cmCommandArgumentParserHelper::ExpandVariableForAt(const char* var)
121 {
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("");
129     }
130     // if the ret was not 0, then return it
131     if (ret) {
132       return ret;
133     }
134   }
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);
141 }
142
143 const char* cmCommandArgumentParserHelper::CombineUnions(const char* in1,
144                                                          const char* in2)
145 {
146   if (!in1) {
147     return in2;
148   }
149   if (!in2) {
150     return in1;
151   }
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();
158 }
159
160 void cmCommandArgumentParserHelper::AllocateParserType(
161   cmCommandArgumentParserHelper::ParserType* pt, const char* str, int len)
162 {
163   pt->str = nullptr;
164   if (len == 0) {
165     len = static_cast<int>(strlen(str));
166   }
167   if (len == 0) {
168     return;
169   }
170   auto out = cm::make_unique<char[]>(len + 1);
171   memcpy(out.get(), str, len);
172   out.get()[len] = 0;
173   pt->str = out.get();
174   this->Variables.push_back(std::move(out));
175 }
176
177 bool cmCommandArgumentParserHelper::HandleEscapeSymbol(
178   cmCommandArgumentParserHelper::ParserType* pt, char symbol)
179 {
180   switch (symbol) {
181     case '\\':
182     case '"':
183     case ' ':
184     case '#':
185     case '(':
186     case ')':
187     case '$':
188     case '@':
189     case '^':
190       this->AllocateParserType(pt, &symbol, 1);
191       break;
192     case ';':
193       this->AllocateParserType(pt, "\\;", 2);
194       break;
195     case 't':
196       this->AllocateParserType(pt, "\t", 1);
197       break;
198     case 'n':
199       this->AllocateParserType(pt, "\n", 1);
200       break;
201     case 'r':
202       this->AllocateParserType(pt, "\r", 1);
203       break;
204     case '0':
205       this->AllocateParserType(pt, "\0", 1);
206       break;
207     default: {
208       std::ostringstream e;
209       e << "Invalid escape sequence \\" << symbol;
210       this->SetError(e.str());
211     }
212       return false;
213   }
214   return true;
215 }
216
217 void cmCommandArgument_SetupEscapes(yyscan_t yyscanner, bool noEscapes);
218
219 int cmCommandArgumentParserHelper::ParseString(std::string const& str,
220                                                int verb)
221 {
222   if (str.empty()) {
223     return 0;
224   }
225   this->InputSize = str.size();
226   this->Verbose = verb;
227
228   this->Result.clear();
229
230   yyscan_t yyscanner;
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);
238   if (res != 0) {
239     return 0;
240   }
241
242   this->CleanupParser();
243
244   if (this->Verbose) {
245     std::cerr << "Expanding [" << str << "] produced: [" << this->Result << "]"
246               << std::endl;
247   }
248   return 1;
249 }
250
251 void cmCommandArgumentParserHelper::CleanupParser()
252 {
253   this->Variables.clear();
254 }
255
256 void cmCommandArgumentParserHelper::Error(const char* str)
257 {
258   auto pos = this->InputBufferPos;
259   auto const isEof = (this->InputSize < this->InputBufferPos);
260   if (!isEof) {
261     pos -= this->LastTokenLength;
262   }
263
264   std::ostringstream ostr;
265   ostr << str << " (" << pos << ")";
266   this->SetError(ostr.str());
267 }
268
269 void cmCommandArgumentParserHelper::SetMakefile(const cmMakefile* mf)
270 {
271   this->Makefile = mf;
272 }
273
274 void cmCommandArgumentParserHelper::SetResult(const char* value)
275 {
276   if (!value) {
277     this->Result.clear();
278     return;
279   }
280   this->Result = value;
281 }
282
283 void cmCommandArgumentParserHelper::SetError(std::string const& msg)
284 {
285   // Keep only the first error.
286   if (this->ErrorString.empty()) {
287     this->ErrorString = msg;
288   }
289 }
290
291 void cmCommandArgumentParserHelper::UpdateInputPosition(int const tokenLength)
292 {
293   this->InputBufferPos += tokenLength;
294   this->LastTokenLength = tokenLength;
295 }