/src/CMake/Source/cmStringCommand.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | // NOLINTNEXTLINE(bugprone-reserved-identifier) |
4 | | #define _SCL_SECURE_NO_WARNINGS |
5 | | |
6 | | #include "cmStringCommand.h" |
7 | | |
8 | | #include <algorithm> |
9 | | #include <cstdio> |
10 | | #include <cstdlib> |
11 | | #include <exception> |
12 | | #include <limits> |
13 | | #include <memory> |
14 | | #include <stdexcept> |
15 | | #include <utility> |
16 | | |
17 | | #include <cm/iterator> |
18 | | #include <cm/optional> |
19 | | #include <cm/string_view> |
20 | | #include <cmext/string_view> |
21 | | |
22 | | #include <cm3p/json/reader.h> |
23 | | #include <cm3p/json/value.h> |
24 | | #include <cm3p/json/writer.h> |
25 | | |
26 | | #include "cmCMakeString.hxx" |
27 | | #include "cmExecutionStatus.h" |
28 | | #include "cmList.h" |
29 | | #include "cmMakefile.h" |
30 | | #include "cmMessageType.h" |
31 | | #include "cmRange.h" |
32 | | #include "cmStringAlgorithms.h" |
33 | | #include "cmSubcommandTable.h" |
34 | | |
35 | | namespace { |
36 | | |
37 | | bool RegexMatch(std::vector<std::string> const& args, |
38 | | cmExecutionStatus& status); |
39 | | bool RegexMatchAll(std::vector<std::string> const& args, |
40 | | cmExecutionStatus& status); |
41 | | bool RegexReplace(std::vector<std::string> const& args, |
42 | | cmExecutionStatus& status); |
43 | | bool RegexQuote(std::vector<std::string> const& args, |
44 | | cmExecutionStatus& status); |
45 | | |
46 | | bool joinImpl(std::vector<std::string> const& args, std::string const& glue, |
47 | | size_t varIdx, cmMakefile& makefile); |
48 | | |
49 | | bool HandleHashCommand(std::vector<std::string> const& args, |
50 | | cmExecutionStatus& status) |
51 | 0 | { |
52 | 0 | if (args.size() != 3) { |
53 | 0 | status.SetError( |
54 | 0 | cmStrCat(args[0], " requires an output variable and an input string")); |
55 | 0 | return false; |
56 | 0 | } |
57 | | |
58 | 0 | cm::CMakeString data{ args[2] }; |
59 | |
|
60 | 0 | try { |
61 | 0 | data.Hash(args[0]); |
62 | 0 | status.GetMakefile().AddDefinition(args[1], data); |
63 | 0 | return true; |
64 | 0 | } catch (std::exception const& e) { |
65 | 0 | status.SetError(e.what()); |
66 | 0 | return false; |
67 | 0 | } |
68 | 0 | } |
69 | | |
70 | | bool HandleToUpperLowerCommand(std::vector<std::string> const& args, |
71 | | bool toUpper, cmExecutionStatus& status) |
72 | 0 | { |
73 | 0 | if (args.size() < 3) { |
74 | 0 | status.SetError("no output variable specified"); |
75 | 0 | return false; |
76 | 0 | } |
77 | | |
78 | 0 | std::string const& outvar = args[2]; |
79 | 0 | cm::CMakeString data{ args[1] }; |
80 | |
|
81 | 0 | if (toUpper) { |
82 | 0 | data.ToUpper(); |
83 | 0 | } else { |
84 | 0 | data.ToLower(); |
85 | 0 | } |
86 | | |
87 | | // Store the output in the provided variable. |
88 | 0 | status.GetMakefile().AddDefinition(outvar, data); |
89 | 0 | return true; |
90 | 0 | } |
91 | | |
92 | | bool HandleToUpperCommand(std::vector<std::string> const& args, |
93 | | cmExecutionStatus& status) |
94 | 0 | { |
95 | 0 | return HandleToUpperLowerCommand(args, true, status); |
96 | 0 | } |
97 | | |
98 | | bool HandleToLowerCommand(std::vector<std::string> const& args, |
99 | | cmExecutionStatus& status) |
100 | 0 | { |
101 | 0 | return HandleToUpperLowerCommand(args, false, status); |
102 | 0 | } |
103 | | |
104 | | bool HandleAsciiCommand(std::vector<std::string> const& args, |
105 | | cmExecutionStatus& status) |
106 | 0 | { |
107 | 0 | if (args.size() < 3) { |
108 | 0 | status.SetError("No output variable specified"); |
109 | 0 | return false; |
110 | 0 | } |
111 | | |
112 | 0 | try { |
113 | 0 | std::string const& outvar = args.back(); |
114 | 0 | cm::CMakeString data; |
115 | 0 | data.FromASCII(cmMakeRange(args).advance(1).retreat(1)); |
116 | 0 | status.GetMakefile().AddDefinition(outvar, data); |
117 | 0 | return true; |
118 | 0 | } catch (std::exception const& e) { |
119 | 0 | status.SetError(e.what()); |
120 | 0 | return false; |
121 | 0 | } |
122 | 0 | } |
123 | | |
124 | | bool HandleHexCommand(std::vector<std::string> const& args, |
125 | | cmExecutionStatus& status) |
126 | 0 | { |
127 | 0 | if (args.size() != 3) { |
128 | 0 | status.SetError("Incorrect number of arguments"); |
129 | 0 | return false; |
130 | 0 | } |
131 | | |
132 | 0 | auto const& outvar = args[2]; |
133 | 0 | cm::CMakeString data{ args[1] }; |
134 | |
|
135 | 0 | data.ToHexadecimal(); |
136 | |
|
137 | 0 | status.GetMakefile().AddDefinition(outvar, data); |
138 | 0 | return true; |
139 | 0 | } |
140 | | |
141 | | bool HandleConfigureCommand(std::vector<std::string> const& args, |
142 | | cmExecutionStatus& status) |
143 | 0 | { |
144 | 0 | if (args.size() < 2) { |
145 | 0 | status.SetError("No input string specified."); |
146 | 0 | return false; |
147 | 0 | } |
148 | 0 | if (args.size() < 3) { |
149 | 0 | status.SetError("No output variable specified."); |
150 | 0 | return false; |
151 | 0 | } |
152 | | |
153 | | // Parse options. |
154 | 0 | bool escapeQuotes = false; |
155 | 0 | bool atOnly = false; |
156 | 0 | for (unsigned int i = 3; i < args.size(); ++i) { |
157 | 0 | if (args[i] == "@ONLY") { |
158 | 0 | atOnly = true; |
159 | 0 | } else if (args[i] == "ESCAPE_QUOTES") { |
160 | 0 | escapeQuotes = true; |
161 | 0 | } else { |
162 | 0 | status.SetError(cmStrCat("Unrecognized argument \"", args[i], '"')); |
163 | 0 | return false; |
164 | 0 | } |
165 | 0 | } |
166 | | |
167 | | // Configure the string. |
168 | 0 | std::string output; |
169 | 0 | status.GetMakefile().ConfigureString(args[1], output, atOnly, escapeQuotes); |
170 | | |
171 | | // Store the output in the provided variable. |
172 | 0 | status.GetMakefile().AddDefinition(args[2], output); |
173 | |
|
174 | 0 | return true; |
175 | 0 | } |
176 | | |
177 | | bool HandleRegexCommand(std::vector<std::string> const& args, |
178 | | cmExecutionStatus& status) |
179 | 0 | { |
180 | 0 | if (args.size() < 2) { |
181 | 0 | status.SetError("sub-command REGEX requires a mode to be specified."); |
182 | 0 | return false; |
183 | 0 | } |
184 | 0 | std::string const& mode = args[1]; |
185 | 0 | if (mode == "MATCH") { |
186 | 0 | if (args.size() < 5) { |
187 | 0 | status.SetError("sub-command REGEX, mode MATCH needs " |
188 | 0 | "at least 5 arguments total to command."); |
189 | 0 | return false; |
190 | 0 | } |
191 | 0 | return RegexMatch(args, status); |
192 | 0 | } |
193 | 0 | if (mode == "MATCHALL") { |
194 | 0 | if (args.size() < 5) { |
195 | 0 | status.SetError("sub-command REGEX, mode MATCHALL needs " |
196 | 0 | "at least 5 arguments total to command."); |
197 | 0 | return false; |
198 | 0 | } |
199 | 0 | return RegexMatchAll(args, status); |
200 | 0 | } |
201 | 0 | if (mode == "REPLACE") { |
202 | 0 | if (args.size() < 6) { |
203 | 0 | status.SetError("sub-command REGEX, mode REPLACE needs " |
204 | 0 | "at least 6 arguments total to command."); |
205 | 0 | return false; |
206 | 0 | } |
207 | 0 | return RegexReplace(args, status); |
208 | 0 | } |
209 | 0 | if (mode == "QUOTE") { |
210 | 0 | if (args.size() < 4) { |
211 | 0 | status.SetError("sub-command REGEX, mode QUOTE needs " |
212 | 0 | "at least 4 arguments total to command."); |
213 | 0 | return false; |
214 | 0 | } |
215 | 0 | return RegexQuote(args, status); |
216 | 0 | } |
217 | | |
218 | 0 | std::string e = "sub-command REGEX does not recognize mode " + mode; |
219 | 0 | status.SetError(e); |
220 | 0 | return false; |
221 | 0 | } |
222 | | |
223 | | bool RegexMatch(std::vector<std::string> const& args, |
224 | | cmExecutionStatus& status) |
225 | 0 | { |
226 | | //"STRING(REGEX MATCH <regular_expression> <output variable> |
227 | | // <input> [<input>...])\n"; |
228 | 0 | try { |
229 | 0 | std::string const& regex = args[2]; |
230 | 0 | std::string const& outvar = args[3]; |
231 | 0 | cm::CMakeString data{ cmMakeRange(args).advance(4) }; |
232 | |
|
233 | 0 | auto result = data.Match(regex, cm::CMakeString::MatchItems::Once, |
234 | 0 | &status.GetMakefile()); |
235 | | // Store the result in the provided variable. |
236 | 0 | status.GetMakefile().AddDefinition(outvar, result.to_string()); |
237 | 0 | return true; |
238 | 0 | } catch (std::exception const& e) { |
239 | 0 | status.SetError( |
240 | 0 | cmStrCat("sub-command REGEX, mode MATCH: ", e.what(), '.')); |
241 | 0 | return false; |
242 | 0 | } |
243 | 0 | } |
244 | | |
245 | | bool RegexMatchAll(std::vector<std::string> const& args, |
246 | | cmExecutionStatus& status) |
247 | 0 | { |
248 | | //"STRING(REGEX MATCHALL <regular_expression> <output variable> <input> |
249 | | // [<input>...])\n"; |
250 | 0 | try { |
251 | 0 | std::string const& regex = args[2]; |
252 | 0 | std::string const& outvar = args[3]; |
253 | 0 | cm::CMakeString data{ cmMakeRange(args).advance(4) }; |
254 | |
|
255 | 0 | auto result = data.Match(regex, cm::CMakeString::MatchItems::All, |
256 | 0 | &status.GetMakefile()); |
257 | | // Store the result in the provided variable. |
258 | 0 | status.GetMakefile().AddDefinition(outvar, result.to_string()); |
259 | 0 | return true; |
260 | 0 | } catch (std::exception const& e) { |
261 | 0 | status.SetError( |
262 | 0 | cmStrCat("sub-command REGEX, mode MATCHALL: ", e.what(), '.')); |
263 | 0 | return false; |
264 | 0 | } |
265 | 0 | } |
266 | | |
267 | | bool RegexReplace(std::vector<std::string> const& args, |
268 | | cmExecutionStatus& status) |
269 | 0 | { |
270 | | //"STRING(REGEX REPLACE <regular_expression> <replace_expression> |
271 | | // <output variable> <input> [<input>...])\n" |
272 | 0 | std::string const& regex = args[2]; |
273 | 0 | std::string const& replace = args[3]; |
274 | 0 | std::string const& outvar = args[4]; |
275 | |
|
276 | 0 | try { |
277 | 0 | cm::CMakeString data{ cmMakeRange(args).advance(5) }; |
278 | |
|
279 | 0 | data.Replace(regex, replace, cm::CMakeString::Regex::Yes, |
280 | 0 | &status.GetMakefile()); |
281 | | // Store the result in the provided variable. |
282 | 0 | status.GetMakefile().AddDefinition(outvar, data); |
283 | 0 | return true; |
284 | 0 | } catch (std::exception const& e) { |
285 | 0 | status.SetError( |
286 | 0 | cmStrCat("sub-command REGEX, mode REPLACE: ", e.what(), '.')); |
287 | 0 | return false; |
288 | 0 | } |
289 | 0 | } |
290 | | |
291 | | bool RegexQuote(std::vector<std::string> const& args, |
292 | | cmExecutionStatus& status) |
293 | 0 | { |
294 | | //"STRING(REGEX QUOTE <output variable> <input> [<input>...]\n" |
295 | 0 | std::string const& outvar = args[2]; |
296 | 0 | cm::CMakeString data{ cmMakeRange(args).advance(3) }; |
297 | |
|
298 | 0 | try { |
299 | | // Escape all regex special characters |
300 | 0 | data.Quote(); |
301 | | // Store the output in the provided variable. |
302 | 0 | status.GetMakefile().AddDefinition(outvar, data); |
303 | 0 | return true; |
304 | 0 | } catch (std::exception const& e) { |
305 | 0 | status.SetError( |
306 | 0 | cmStrCat("sub-command REGEX, mode QUOTE: ", e.what(), '.')); |
307 | 0 | return false; |
308 | 0 | } |
309 | 0 | } |
310 | | |
311 | | bool HandleFindCommand(std::vector<std::string> const& args, |
312 | | cmExecutionStatus& status) |
313 | 0 | { |
314 | | // check if all required parameters were passed |
315 | 0 | if (args.size() < 4 || args.size() > 5) { |
316 | 0 | status.SetError("sub-command FIND requires 3 or 4 parameters."); |
317 | 0 | return false; |
318 | 0 | } |
319 | | |
320 | | // check if the reverse flag was set or not |
321 | 0 | bool reverseMode = false; |
322 | 0 | if (args.size() == 5 && args[4] == "REVERSE") { |
323 | 0 | reverseMode = true; |
324 | 0 | } |
325 | | |
326 | | // if we have 5 arguments the last one must be REVERSE |
327 | 0 | if (args.size() == 5 && args[4] != "REVERSE") { |
328 | 0 | status.SetError("sub-command FIND: unknown last parameter"); |
329 | 0 | return false; |
330 | 0 | } |
331 | | |
332 | | // local parameter names. |
333 | 0 | std::string const& sstring = args[1]; |
334 | 0 | std::string const& schar = args[2]; |
335 | 0 | std::string const& outvar = args[3]; |
336 | | |
337 | | // ensure that the user cannot accidentally specify REVERSE as a variable |
338 | 0 | if (outvar == "REVERSE") { |
339 | 0 | status.SetError("sub-command FIND does not allow one to select REVERSE as " |
340 | 0 | "the output variable. " |
341 | 0 | "Maybe you missed the actual output variable?"); |
342 | 0 | return false; |
343 | 0 | } |
344 | | |
345 | | // try to find the character and return its position |
346 | 0 | auto pos = cm::CMakeString{ sstring }.Find( |
347 | 0 | schar, |
348 | 0 | reverseMode ? cm::CMakeString::FindFrom::End |
349 | 0 | : cm::CMakeString::FindFrom::Begin); |
350 | |
|
351 | 0 | status.GetMakefile().AddDefinition( |
352 | 0 | outvar, pos != cm::CMakeString::npos ? std::to_string(pos) : "-1"); |
353 | |
|
354 | 0 | return true; |
355 | 0 | } |
356 | | |
357 | | bool HandleCompareCommand(std::vector<std::string> const& args, |
358 | | cmExecutionStatus& status) |
359 | 0 | { |
360 | 0 | if (args.size() < 2) { |
361 | 0 | status.SetError("sub-command COMPARE requires a mode to be specified."); |
362 | 0 | return false; |
363 | 0 | } |
364 | 0 | std::string const& mode = args[1]; |
365 | 0 | if ((mode == "EQUAL") || (mode == "NOTEQUAL") || (mode == "LESS") || |
366 | 0 | (mode == "LESS_EQUAL") || (mode == "GREATER") || |
367 | 0 | (mode == "GREATER_EQUAL")) { |
368 | 0 | if (args.size() < 5) { |
369 | 0 | std::string e = |
370 | 0 | cmStrCat("sub-command COMPARE, mode ", mode, |
371 | 0 | " needs at least 5 arguments total to command."); |
372 | 0 | status.SetError(e); |
373 | 0 | return false; |
374 | 0 | } |
375 | | |
376 | 0 | std::string const& left = args[2]; |
377 | 0 | std::string const& right = args[3]; |
378 | 0 | std::string const& outvar = args[4]; |
379 | 0 | bool result; |
380 | 0 | cm::CMakeString::CompOperator op = cm::CMakeString::CompOperator::EQUAL; |
381 | 0 | if (mode == "LESS") { |
382 | 0 | op = cm::CMakeString::CompOperator::LESS; |
383 | 0 | } else if (mode == "LESS_EQUAL") { |
384 | 0 | op = cm::CMakeString::CompOperator::LESS_EQUAL; |
385 | 0 | } else if (mode == "GREATER") { |
386 | 0 | op = cm::CMakeString::CompOperator::GREATER; |
387 | 0 | } else if (mode == "GREATER_EQUAL") { |
388 | 0 | op = cm::CMakeString::CompOperator::GREATER_EQUAL; |
389 | 0 | } |
390 | 0 | result = cm::CMakeString{ left }.Compare(op, right); |
391 | 0 | if (mode == "NOTEQUAL") { |
392 | 0 | result = !result; |
393 | 0 | } |
394 | |
|
395 | 0 | status.GetMakefile().AddDefinition(outvar, result ? "1" : "0"); |
396 | 0 | return true; |
397 | 0 | } |
398 | 0 | std::string e = "sub-command COMPARE does not recognize mode " + mode; |
399 | 0 | status.SetError(e); |
400 | 0 | return false; |
401 | 0 | } |
402 | | |
403 | | bool HandleReplaceCommand(std::vector<std::string> const& args, |
404 | | cmExecutionStatus& status) |
405 | 0 | { |
406 | 0 | if (args.size() < 5) { |
407 | 0 | status.SetError("sub-command REPLACE requires at least four arguments."); |
408 | 0 | return false; |
409 | 0 | } |
410 | | |
411 | 0 | try { |
412 | 0 | std::string const& matchExpression = args[1]; |
413 | 0 | std::string const& replaceExpression = args[2]; |
414 | 0 | std::string const& variableName = args[3]; |
415 | 0 | cm::CMakeString data{ cmMakeRange(args).advance(4) }; |
416 | |
|
417 | 0 | data.Replace(matchExpression, replaceExpression); |
418 | 0 | status.GetMakefile().AddDefinition(variableName, data); |
419 | 0 | return true; |
420 | 0 | } catch (std::exception const& e) { |
421 | 0 | status.SetError(cmStrCat("sub-command REPLACE: ", e.what(), '.')); |
422 | 0 | return false; |
423 | 0 | } |
424 | 0 | } |
425 | | |
426 | | bool HandleSubstringCommand(std::vector<std::string> const& args, |
427 | | cmExecutionStatus& status) |
428 | 0 | { |
429 | 0 | if (args.size() != 5) { |
430 | 0 | status.SetError("sub-command SUBSTRING requires four arguments."); |
431 | 0 | return false; |
432 | 0 | } |
433 | | |
434 | 0 | try { |
435 | 0 | std::string const& stringValue = args[1]; |
436 | 0 | int begin = atoi(args[2].c_str()); |
437 | 0 | int end = atoi(args[3].c_str()); |
438 | 0 | std::string const& variableName = args[4]; |
439 | |
|
440 | 0 | cm::CMakeString data{ stringValue }; |
441 | 0 | status.GetMakefile().AddDefinition(variableName, |
442 | 0 | data.Substring(begin, end)); |
443 | 0 | } catch (std::exception const& e) { |
444 | 0 | status.SetError(e.what()); |
445 | 0 | return false; |
446 | 0 | } |
447 | 0 | return true; |
448 | 0 | } |
449 | | |
450 | | bool HandleLengthCommand(std::vector<std::string> const& args, |
451 | | cmExecutionStatus& status) |
452 | 0 | { |
453 | 0 | if (args.size() != 3) { |
454 | 0 | status.SetError("sub-command LENGTH requires two arguments."); |
455 | 0 | return false; |
456 | 0 | } |
457 | | |
458 | 0 | std::string const& stringValue = args[1]; |
459 | 0 | std::string const& variableName = args[2]; |
460 | |
|
461 | 0 | status.GetMakefile().AddDefinition( |
462 | 0 | variableName, std::to_string(cm::CMakeString{ stringValue }.Length())); |
463 | 0 | return true; |
464 | 0 | } |
465 | | |
466 | | bool HandleAppendCommand(std::vector<std::string> const& args, |
467 | | cmExecutionStatus& status) |
468 | 0 | { |
469 | 0 | if (args.size() < 2) { |
470 | 0 | status.SetError("sub-command APPEND requires at least one argument."); |
471 | 0 | return false; |
472 | 0 | } |
473 | | |
474 | | // Skip if nothing to append. |
475 | 0 | if (args.size() < 3) { |
476 | 0 | return true; |
477 | 0 | } |
478 | | |
479 | 0 | auto const& variableName = args[1]; |
480 | 0 | cm::CMakeString data{ status.GetMakefile().GetDefinition(variableName) }; |
481 | |
|
482 | 0 | data.Append(cmMakeRange(args).advance(2)); |
483 | 0 | status.GetMakefile().AddDefinition(variableName, data); |
484 | |
|
485 | 0 | return true; |
486 | 0 | } |
487 | | |
488 | | bool HandlePrependCommand(std::vector<std::string> const& args, |
489 | | cmExecutionStatus& status) |
490 | 0 | { |
491 | 0 | if (args.size() < 2) { |
492 | 0 | status.SetError("sub-command PREPEND requires at least one argument."); |
493 | 0 | return false; |
494 | 0 | } |
495 | | |
496 | | // Skip if nothing to prepend. |
497 | 0 | if (args.size() < 3) { |
498 | 0 | return true; |
499 | 0 | } |
500 | | |
501 | 0 | std::string const& variable = args[1]; |
502 | 0 | cm::CMakeString data{ status.GetMakefile().GetDefinition(variable) }; |
503 | |
|
504 | 0 | data.Prepend(cmMakeRange(args).advance(2)); |
505 | 0 | status.GetMakefile().AddDefinition(variable, data); |
506 | 0 | return true; |
507 | 0 | } |
508 | | |
509 | | bool HandleConcatCommand(std::vector<std::string> const& args, |
510 | | cmExecutionStatus& status) |
511 | 0 | { |
512 | 0 | if (args.size() < 2) { |
513 | 0 | status.SetError("sub-command CONCAT requires at least one argument."); |
514 | 0 | return false; |
515 | 0 | } |
516 | | |
517 | 0 | return joinImpl(args, std::string(), 1, status.GetMakefile()); |
518 | 0 | } |
519 | | |
520 | | bool HandleJoinCommand(std::vector<std::string> const& args, |
521 | | cmExecutionStatus& status) |
522 | 0 | { |
523 | 0 | if (args.size() < 3) { |
524 | 0 | status.SetError("sub-command JOIN requires at least two arguments."); |
525 | 0 | return false; |
526 | 0 | } |
527 | | |
528 | 0 | return joinImpl(args, args[1], 2, status.GetMakefile()); |
529 | 0 | } |
530 | | |
531 | | bool joinImpl(std::vector<std::string> const& args, std::string const& glue, |
532 | | size_t const varIdx, cmMakefile& makefile) |
533 | 0 | { |
534 | 0 | std::string const& variableName = args[varIdx]; |
535 | | // NOTE Items to concat/join placed right after the variable for |
536 | | // both `CONCAT` and `JOIN` sub-commands. |
537 | 0 | cm::CMakeString data{ cmMakeRange(args).advance(varIdx + 1), glue }; |
538 | |
|
539 | 0 | makefile.AddDefinition(variableName, data); |
540 | 0 | return true; |
541 | 0 | } |
542 | | |
543 | | bool HandleMakeCIdentifierCommand(std::vector<std::string> const& args, |
544 | | cmExecutionStatus& status) |
545 | 0 | { |
546 | 0 | if (args.size() != 3) { |
547 | 0 | status.SetError("sub-command MAKE_C_IDENTIFIER requires two arguments."); |
548 | 0 | return false; |
549 | 0 | } |
550 | | |
551 | 0 | std::string const& input = args[1]; |
552 | 0 | std::string const& variableName = args[2]; |
553 | |
|
554 | 0 | status.GetMakefile().AddDefinition(variableName, |
555 | 0 | cm::CMakeString{}.MakeCIdentifier(input)); |
556 | 0 | return true; |
557 | 0 | } |
558 | | |
559 | | bool HandleGenexStripCommand(std::vector<std::string> const& args, |
560 | | cmExecutionStatus& status) |
561 | 0 | { |
562 | 0 | if (args.size() != 3) { |
563 | 0 | status.SetError("sub-command GENEX_STRIP requires two arguments."); |
564 | 0 | return false; |
565 | 0 | } |
566 | | |
567 | 0 | cm::CMakeString data{ args[1] }; |
568 | 0 | std::string const& variableName = args[2]; |
569 | |
|
570 | 0 | status.GetMakefile().AddDefinition( |
571 | 0 | variableName, data.Strip(cm::CMakeString::StripItems::Genex)); |
572 | 0 | return true; |
573 | 0 | } |
574 | | |
575 | | bool HandleStripCommand(std::vector<std::string> const& args, |
576 | | cmExecutionStatus& status) |
577 | 0 | { |
578 | 0 | if (args.size() != 3) { |
579 | 0 | status.SetError("sub-command STRIP requires two arguments."); |
580 | 0 | return false; |
581 | 0 | } |
582 | | |
583 | 0 | cm::CMakeString data{ args[1] }; |
584 | 0 | std::string const& variableName = args[2]; |
585 | |
|
586 | 0 | status.GetMakefile().AddDefinition(variableName, data.Strip()); |
587 | 0 | return true; |
588 | 0 | } |
589 | | |
590 | | bool HandleRepeatCommand(std::vector<std::string> const& args, |
591 | | cmExecutionStatus& status) |
592 | 0 | { |
593 | 0 | cmMakefile& makefile = status.GetMakefile(); |
594 | | |
595 | | // `string(REPEAT "<str>" <times> OUTPUT_VARIABLE)` |
596 | 0 | enum ArgPos : std::size_t |
597 | 0 | { |
598 | 0 | SUB_COMMAND, |
599 | 0 | VALUE, |
600 | 0 | TIMES, |
601 | 0 | OUTPUT_VARIABLE, |
602 | 0 | TOTAL_ARGS |
603 | 0 | }; |
604 | |
|
605 | 0 | if (args.size() != ArgPos::TOTAL_ARGS) { |
606 | 0 | makefile.IssueMessage(MessageType::FATAL_ERROR, |
607 | 0 | "sub-command REPEAT requires three arguments."); |
608 | 0 | return true; |
609 | 0 | } |
610 | | |
611 | 0 | unsigned long times; |
612 | 0 | if (!cmStrToULong(args[ArgPos::TIMES], ×)) { |
613 | 0 | makefile.IssueMessage(MessageType::FATAL_ERROR, |
614 | 0 | "repeat count is not a positive number."); |
615 | 0 | return true; |
616 | 0 | } |
617 | | |
618 | 0 | cm::CMakeString data{ args[ArgPos::VALUE] }; |
619 | 0 | data.Repeat(times); |
620 | |
|
621 | 0 | auto const& variableName = args[ArgPos::OUTPUT_VARIABLE]; |
622 | |
|
623 | 0 | makefile.AddDefinition(variableName, data); |
624 | 0 | return true; |
625 | 0 | } |
626 | | |
627 | | bool HandleRandomCommand(std::vector<std::string> const& args, |
628 | | cmExecutionStatus& status) |
629 | 0 | { |
630 | 0 | if (args.size() < 2 || args.size() == 3 || args.size() == 5) { |
631 | 0 | status.SetError("sub-command RANDOM requires at least one argument."); |
632 | 0 | return false; |
633 | 0 | } |
634 | | |
635 | 0 | int length = 5; |
636 | 0 | cm::string_view alphabet; |
637 | 0 | bool force_seed = false; |
638 | 0 | unsigned int seed = 0; |
639 | |
|
640 | 0 | if (args.size() > 3) { |
641 | 0 | size_t i = 1; |
642 | 0 | size_t stopAt = args.size() - 2; |
643 | |
|
644 | 0 | for (; i < stopAt; ++i) { |
645 | 0 | if (args[i] == "LENGTH") { |
646 | 0 | ++i; |
647 | 0 | length = atoi(args[i].c_str()); |
648 | 0 | } else if (args[i] == "ALPHABET") { |
649 | 0 | ++i; |
650 | 0 | alphabet = args[i]; |
651 | 0 | } else if (args[i] == "RANDOM_SEED") { |
652 | 0 | ++i; |
653 | 0 | seed = static_cast<unsigned int>(atoi(args[i].c_str())); |
654 | 0 | force_seed = true; |
655 | 0 | } |
656 | 0 | } |
657 | 0 | } |
658 | |
|
659 | 0 | try { |
660 | 0 | cm::CMakeString data; |
661 | 0 | std::string const& variableName = args.back(); |
662 | |
|
663 | 0 | if (force_seed) { |
664 | 0 | data.Random(seed, length, alphabet); |
665 | 0 | } else { |
666 | 0 | data.Random(length, alphabet); |
667 | 0 | } |
668 | 0 | status.GetMakefile().AddDefinition(variableName, data); |
669 | 0 | return true; |
670 | 0 | } catch (std::exception const& e) { |
671 | 0 | status.SetError(cmStrCat("sub-command RANDOM: ", e.what(), '.')); |
672 | 0 | return false; |
673 | 0 | } |
674 | 0 | } |
675 | | |
676 | | bool HandleTimestampCommand(std::vector<std::string> const& args, |
677 | | cmExecutionStatus& status) |
678 | 0 | { |
679 | 0 | if (args.size() < 2) { |
680 | 0 | status.SetError("sub-command TIMESTAMP requires at least one argument."); |
681 | 0 | return false; |
682 | 0 | } |
683 | 0 | if (args.size() > 4) { |
684 | 0 | status.SetError("sub-command TIMESTAMP takes at most three arguments."); |
685 | 0 | return false; |
686 | 0 | } |
687 | | |
688 | 0 | unsigned int argsIndex = 1; |
689 | |
|
690 | 0 | std::string const& outputVariable = args[argsIndex++]; |
691 | |
|
692 | 0 | cm::string_view formatString; |
693 | 0 | if (args.size() > argsIndex && args[argsIndex] != "UTC") { |
694 | 0 | formatString = args[argsIndex++]; |
695 | 0 | } |
696 | |
|
697 | 0 | cm::CMakeString::UTC utcFlag = cm::CMakeString::UTC::No; |
698 | 0 | if (args.size() > argsIndex) { |
699 | 0 | if (args[argsIndex] == "UTC") { |
700 | 0 | utcFlag = cm::CMakeString::UTC::Yes; |
701 | 0 | } else { |
702 | 0 | std::string e = |
703 | 0 | cmStrCat(" TIMESTAMP sub-command does not recognize option ", |
704 | 0 | args[argsIndex], '.'); |
705 | 0 | status.SetError(e); |
706 | 0 | return false; |
707 | 0 | } |
708 | 0 | } |
709 | | |
710 | 0 | cm::CMakeString data; |
711 | |
|
712 | 0 | status.GetMakefile().AddDefinition(outputVariable, |
713 | 0 | data.Timestamp(formatString, utcFlag)); |
714 | |
|
715 | 0 | return true; |
716 | 0 | } |
717 | | |
718 | | bool HandleUuidCommand(std::vector<std::string> const& args, |
719 | | cmExecutionStatus& status) |
720 | 0 | { |
721 | 0 | #if !defined(CMAKE_BOOTSTRAP) |
722 | 0 | unsigned int argsIndex = 1; |
723 | |
|
724 | 0 | if (args.size() < 2) { |
725 | 0 | status.SetError("UUID sub-command requires an output variable."); |
726 | 0 | return false; |
727 | 0 | } |
728 | | |
729 | 0 | std::string const& outputVariable = args[argsIndex++]; |
730 | |
|
731 | 0 | cm::string_view uuidNamespaceString; |
732 | 0 | cm::string_view uuidName; |
733 | 0 | cm::CMakeString::UUIDType uuidType = cm::CMakeString::UUIDType::MD5; |
734 | 0 | cm::CMakeString::Case uuidCase = cm::CMakeString::Case::Lower; |
735 | |
|
736 | 0 | while (args.size() > argsIndex) { |
737 | 0 | if (args[argsIndex] == "NAMESPACE") { |
738 | 0 | ++argsIndex; |
739 | 0 | if (argsIndex >= args.size()) { |
740 | 0 | status.SetError("UUID sub-command, NAMESPACE requires a value."); |
741 | 0 | return false; |
742 | 0 | } |
743 | 0 | uuidNamespaceString = args[argsIndex++]; |
744 | 0 | } else if (args[argsIndex] == "NAME") { |
745 | 0 | ++argsIndex; |
746 | 0 | if (argsIndex >= args.size()) { |
747 | 0 | status.SetError("UUID sub-command, NAME requires a value."); |
748 | 0 | return false; |
749 | 0 | } |
750 | 0 | uuidName = args[argsIndex++]; |
751 | 0 | } else if (args[argsIndex] == "TYPE") { |
752 | 0 | ++argsIndex; |
753 | 0 | if (argsIndex >= args.size()) { |
754 | 0 | status.SetError("UUID sub-command, TYPE requires a value."); |
755 | 0 | return false; |
756 | 0 | } |
757 | 0 | if (args[argsIndex] == "MD5") { |
758 | 0 | uuidType = cm::CMakeString::UUIDType::MD5; |
759 | 0 | } else if (args[argsIndex] == "SHA1") { |
760 | 0 | uuidType = cm::CMakeString::UUIDType::SHA1; |
761 | 0 | } else { |
762 | 0 | status.SetError( |
763 | 0 | cmStrCat("UUID sub-command, unknown TYPE '", args[argsIndex], "'.")); |
764 | 0 | return false; |
765 | 0 | } |
766 | 0 | argsIndex++; |
767 | 0 | } else if (args[argsIndex] == "UPPER") { |
768 | 0 | ++argsIndex; |
769 | 0 | uuidCase = cm::CMakeString::Case::Upper; |
770 | 0 | } else { |
771 | 0 | std::string e = cmStrCat("UUID sub-command does not recognize option ", |
772 | 0 | args[argsIndex], '.'); |
773 | 0 | status.SetError(e); |
774 | 0 | return false; |
775 | 0 | } |
776 | 0 | } |
777 | | |
778 | 0 | try { |
779 | 0 | cm::CMakeString data; |
780 | |
|
781 | 0 | data.UUID(uuidNamespaceString, uuidName, uuidType, uuidCase); |
782 | 0 | status.GetMakefile().AddDefinition(outputVariable, data); |
783 | 0 | return true; |
784 | 0 | } catch (std::exception const& e) { |
785 | 0 | status.SetError(cmStrCat("UUID sub-command, ", e.what(), '.')); |
786 | 0 | return false; |
787 | 0 | } |
788 | | #else |
789 | | status.SetError("UUID sub-command not available during bootstrap."); |
790 | | return false; |
791 | | #endif |
792 | 0 | } |
793 | | |
794 | | #if !defined(CMAKE_BOOTSTRAP) |
795 | | |
796 | | // Helpers for string(JSON ...) |
797 | | struct Args : cmRange<typename std::vector<std::string>::const_iterator> |
798 | | { |
799 | | using cmRange<typename std::vector<std::string>::const_iterator>::cmRange; |
800 | | |
801 | | auto PopFront(cm::string_view error) -> std::string const&; |
802 | | auto PopBack(cm::string_view error) -> std::string const&; |
803 | | }; |
804 | | |
805 | | class json_error : public std::runtime_error |
806 | | { |
807 | | public: |
808 | | json_error(std::string const& message, |
809 | | cm::optional<Args> errorPath = cm::nullopt) |
810 | 0 | : std::runtime_error(message) |
811 | 0 | , ErrorPath{ |
812 | 0 | std::move(errorPath) // NOLINT(performance-move-const-arg) |
813 | 0 | } |
814 | 0 | { |
815 | 0 | } |
816 | | cm::optional<Args> ErrorPath; |
817 | | }; |
818 | | |
819 | | std::string const& Args::PopFront(cm::string_view error) |
820 | 0 | { |
821 | 0 | if (this->empty()) { |
822 | 0 | throw json_error(std::string(error)); |
823 | 0 | } |
824 | 0 | std::string const& res = *this->begin(); |
825 | 0 | this->advance(1); |
826 | 0 | return res; |
827 | 0 | } |
828 | | |
829 | | std::string const& Args::PopBack(cm::string_view error) |
830 | 0 | { |
831 | 0 | if (this->empty()) { |
832 | 0 | throw json_error(std::string(error)); |
833 | 0 | } |
834 | 0 | std::string const& res = *(this->end() - 1); |
835 | 0 | this->retreat(1); |
836 | 0 | return res; |
837 | 0 | } |
838 | | |
839 | | cm::string_view JsonTypeToString(Json::ValueType type) |
840 | 0 | { |
841 | 0 | switch (type) { |
842 | 0 | case Json::ValueType::nullValue: |
843 | 0 | return "NULL"_s; |
844 | 0 | case Json::ValueType::intValue: |
845 | 0 | case Json::ValueType::uintValue: |
846 | 0 | case Json::ValueType::realValue: |
847 | 0 | return "NUMBER"_s; |
848 | 0 | case Json::ValueType::stringValue: |
849 | 0 | return "STRING"_s; |
850 | 0 | case Json::ValueType::booleanValue: |
851 | 0 | return "BOOLEAN"_s; |
852 | 0 | case Json::ValueType::arrayValue: |
853 | 0 | return "ARRAY"_s; |
854 | 0 | case Json::ValueType::objectValue: |
855 | 0 | return "OBJECT"_s; |
856 | 0 | } |
857 | 0 | throw json_error("invalid JSON type found"); |
858 | 0 | } |
859 | | |
860 | | int ParseIndex( |
861 | | std::string const& str, cm::optional<Args> const& progress = cm::nullopt, |
862 | | Json::ArrayIndex max = std::numeric_limits<Json::ArrayIndex>::max()) |
863 | 0 | { |
864 | 0 | unsigned long lindex; |
865 | 0 | if (!cmStrToULong(str, &lindex)) { |
866 | 0 | throw json_error(cmStrCat("expected an array index, got: '"_s, str, "'"_s), |
867 | 0 | progress); |
868 | 0 | } |
869 | 0 | Json::ArrayIndex index = static_cast<Json::ArrayIndex>(lindex); |
870 | 0 | if (index >= max) { |
871 | 0 | cmAlphaNum sizeStr{ max }; |
872 | 0 | throw json_error(cmStrCat("expected an index less than "_s, sizeStr.View(), |
873 | 0 | " got '"_s, str, "'"_s), |
874 | 0 | progress); |
875 | 0 | } |
876 | 0 | return index; |
877 | 0 | } |
878 | | |
879 | | Json::Value& ResolvePath(Json::Value& json, Args path) |
880 | 0 | { |
881 | 0 | Json::Value* search = &json; |
882 | |
|
883 | 0 | for (auto curr = path.begin(); curr != path.end(); ++curr) { |
884 | 0 | std::string const& field = *curr; |
885 | 0 | Args progress{ path.begin(), curr + 1 }; |
886 | |
|
887 | 0 | if (search->isArray()) { |
888 | 0 | auto index = ParseIndex(field, progress, search->size()); |
889 | 0 | search = &(*search)[index]; |
890 | |
|
891 | 0 | } else if (search->isObject()) { |
892 | 0 | if (!search->isMember(field)) { |
893 | 0 | auto const progressStr = cmJoin(progress, " "_s); |
894 | 0 | throw json_error(cmStrCat("member '"_s, progressStr, "' not found"_s), |
895 | 0 | progress); |
896 | 0 | } |
897 | 0 | search = &(*search)[field]; |
898 | 0 | } else { |
899 | 0 | auto const progressStr = cmJoin(progress, " "_s); |
900 | 0 | throw json_error( |
901 | 0 | cmStrCat("invalid path '"_s, progressStr, |
902 | 0 | "', need element of OBJECT or ARRAY type to lookup '"_s, |
903 | 0 | field, "' got "_s, JsonTypeToString(search->type())), |
904 | 0 | progress); |
905 | 0 | } |
906 | 0 | } |
907 | 0 | return *search; |
908 | 0 | } |
909 | | |
910 | | Json::Value ReadJson(std::string const& jsonstr) |
911 | 0 | { |
912 | 0 | Json::CharReaderBuilder builder; |
913 | 0 | builder["collectComments"] = false; |
914 | 0 | auto jsonReader = std::unique_ptr<Json::CharReader>(builder.newCharReader()); |
915 | 0 | Json::Value json; |
916 | 0 | std::string error; |
917 | 0 | if (!jsonReader->parse(jsonstr.data(), jsonstr.data() + jsonstr.size(), |
918 | 0 | &json, &error)) { |
919 | 0 | throw json_error( |
920 | 0 | cmStrCat("failed parsing json string:\n"_s, jsonstr, '\n', error)); |
921 | 0 | } |
922 | 0 | return json; |
923 | 0 | } |
924 | | std::string WriteJson(Json::Value const& value) |
925 | 0 | { |
926 | 0 | Json::StreamWriterBuilder writer; |
927 | 0 | writer["indentation"] = " "; |
928 | 0 | writer["commentStyle"] = "None"; |
929 | 0 | return Json::writeString(writer, value); |
930 | 0 | } |
931 | | |
932 | | bool JsonPartialMatch(Json::Value const& pattern, Json::Value const& actual) |
933 | 0 | { |
934 | 0 | if (pattern.type() != actual.type()) { |
935 | 0 | return false; |
936 | 0 | } |
937 | 0 | switch (pattern.type()) { |
938 | 0 | case Json::nullValue: |
939 | 0 | case Json::intValue: |
940 | 0 | case Json::uintValue: |
941 | 0 | case Json::realValue: |
942 | 0 | case Json::stringValue: |
943 | 0 | case Json::booleanValue: |
944 | 0 | return pattern == actual; |
945 | | |
946 | 0 | case Json::objectValue: { |
947 | 0 | std::vector<std::string> const keys = pattern.getMemberNames(); |
948 | 0 | return std::all_of(keys.begin(), keys.end(), |
949 | 0 | [&](std::string const& key) { |
950 | 0 | return actual.isMember(key) && |
951 | 0 | JsonPartialMatch(pattern[key], actual[key]); |
952 | 0 | }); |
953 | 0 | } |
954 | | |
955 | 0 | case Json::arrayValue: { |
956 | 0 | if (actual.size() < pattern.size()) { |
957 | 0 | return false; |
958 | 0 | } |
959 | 0 | std::vector<bool> matched(actual.size(), false); |
960 | 0 | for (Json::Value const& p : pattern) { |
961 | 0 | bool found = false; |
962 | 0 | for (Json::ArrayIndex j = 0; j < actual.size(); ++j) { |
963 | 0 | if (matched[j]) { |
964 | 0 | continue; |
965 | 0 | } |
966 | 0 | if (JsonPartialMatch(p, actual[j])) { |
967 | 0 | matched[j] = true; |
968 | 0 | found = true; |
969 | 0 | break; |
970 | 0 | } |
971 | 0 | } |
972 | 0 | if (!found) { |
973 | 0 | return false; |
974 | 0 | } |
975 | 0 | } |
976 | 0 | return true; |
977 | 0 | } |
978 | 0 | } |
979 | 0 | return false; |
980 | 0 | } |
981 | | |
982 | | #endif |
983 | | |
984 | | bool HandleJSONCommand(std::vector<std::string> const& arguments, |
985 | | cmExecutionStatus& status) |
986 | 0 | { |
987 | 0 | #if !defined(CMAKE_BOOTSTRAP) |
988 | |
|
989 | 0 | auto& makefile = status.GetMakefile(); |
990 | 0 | Args args{ arguments.begin() + 1, arguments.end() }; |
991 | |
|
992 | 0 | std::string const* errorVariable = nullptr; |
993 | 0 | std::string const* outputVariable = nullptr; |
994 | 0 | bool success = true; |
995 | |
|
996 | 0 | try { |
997 | 0 | outputVariable = &args.PopFront("missing out-var argument"_s); |
998 | |
|
999 | 0 | if (!args.empty() && *args.begin() == "ERROR_VARIABLE"_s) { |
1000 | 0 | args.PopFront(""); |
1001 | 0 | errorVariable = &args.PopFront("missing error-var argument"_s); |
1002 | 0 | makefile.AddDefinition(*errorVariable, "NOTFOUND"_s); |
1003 | 0 | } |
1004 | |
|
1005 | 0 | auto const& mode = args.PopFront("missing mode argument"_s); |
1006 | 0 | if (mode != "GET"_s && mode != "GET_RAW"_s && mode != "TYPE"_s && |
1007 | 0 | mode != "MEMBER"_s && mode != "LENGTH"_s && mode != "REMOVE"_s && |
1008 | 0 | mode != "SET"_s && mode != "EQUAL"_s && mode != "STRING_ENCODE"_s && |
1009 | 0 | mode != "PARTIAL_EQUAL"_s) { |
1010 | 0 | throw json_error(cmStrCat( |
1011 | 0 | "got an invalid mode '"_s, mode, |
1012 | 0 | "', expected one of GET, GET_RAW, TYPE, MEMBER, LENGTH, REMOVE, SET, " |
1013 | 0 | " EQUAL, PARTIAL_EQUAL, STRING_ENCODE"_s)); |
1014 | 0 | } |
1015 | | |
1016 | 0 | auto const& jsonstr = args.PopFront("missing json string argument"_s); |
1017 | |
|
1018 | 0 | if (mode == "STRING_ENCODE"_s) { |
1019 | 0 | Json::Value json(jsonstr); |
1020 | 0 | makefile.AddDefinition(*outputVariable, WriteJson(json)); |
1021 | 0 | } else { |
1022 | 0 | Json::Value json = ReadJson(jsonstr); |
1023 | |
|
1024 | 0 | if (mode == "GET"_s) { |
1025 | 0 | auto const& value = ResolvePath(json, args); |
1026 | 0 | if (value.isObject() || value.isArray()) { |
1027 | 0 | makefile.AddDefinition(*outputVariable, WriteJson(value)); |
1028 | 0 | } else if (value.isBool()) { |
1029 | 0 | makefile.AddDefinitionBool(*outputVariable, value.asBool()); |
1030 | 0 | } else { |
1031 | 0 | makefile.AddDefinition(*outputVariable, value.asString()); |
1032 | 0 | } |
1033 | |
|
1034 | 0 | } else if (mode == "GET_RAW"_s) { |
1035 | 0 | auto const& value = ResolvePath(json, args); |
1036 | 0 | makefile.AddDefinition(*outputVariable, WriteJson(value)); |
1037 | |
|
1038 | 0 | } else if (mode == "TYPE"_s) { |
1039 | 0 | auto const& value = ResolvePath(json, args); |
1040 | 0 | makefile.AddDefinition(*outputVariable, |
1041 | 0 | JsonTypeToString(value.type())); |
1042 | |
|
1043 | 0 | } else if (mode == "MEMBER"_s) { |
1044 | 0 | auto const& indexStr = args.PopBack("missing member index"_s); |
1045 | 0 | auto const& value = ResolvePath(json, args); |
1046 | 0 | if (!value.isObject()) { |
1047 | 0 | throw json_error( |
1048 | 0 | cmStrCat("MEMBER needs to be called with an element of " |
1049 | 0 | "type OBJECT, got "_s, |
1050 | 0 | JsonTypeToString(value.type())), |
1051 | 0 | args); |
1052 | 0 | } |
1053 | 0 | auto const index = ParseIndex( |
1054 | 0 | indexStr, Args{ args.begin(), args.end() + 1 }, value.size()); |
1055 | 0 | auto const memIt = std::next(value.begin(), index); |
1056 | 0 | makefile.AddDefinition(*outputVariable, memIt.name()); |
1057 | |
|
1058 | 0 | } else if (mode == "LENGTH"_s) { |
1059 | 0 | auto const& value = ResolvePath(json, args); |
1060 | 0 | if (!value.isArray() && !value.isObject()) { |
1061 | 0 | throw json_error(cmStrCat("LENGTH needs to be called with an " |
1062 | 0 | "element of type ARRAY or OBJECT, got "_s, |
1063 | 0 | JsonTypeToString(value.type())), |
1064 | 0 | args); |
1065 | 0 | } |
1066 | | |
1067 | 0 | cmAlphaNum sizeStr{ value.size() }; |
1068 | 0 | makefile.AddDefinition(*outputVariable, sizeStr.View()); |
1069 | |
|
1070 | 0 | } else if (mode == "REMOVE"_s) { |
1071 | 0 | auto const& toRemove = |
1072 | 0 | args.PopBack("missing member or index to remove"_s); |
1073 | 0 | auto& value = ResolvePath(json, args); |
1074 | |
|
1075 | 0 | if (value.isArray()) { |
1076 | 0 | auto const index = ParseIndex( |
1077 | 0 | toRemove, Args{ args.begin(), args.end() + 1 }, value.size()); |
1078 | 0 | Json::Value removed; |
1079 | 0 | value.removeIndex(index, &removed); |
1080 | |
|
1081 | 0 | } else if (value.isObject()) { |
1082 | 0 | Json::Value removed; |
1083 | 0 | value.removeMember(toRemove, &removed); |
1084 | |
|
1085 | 0 | } else { |
1086 | 0 | throw json_error(cmStrCat("REMOVE needs to be called with an " |
1087 | 0 | "element of type ARRAY or OBJECT, got "_s, |
1088 | 0 | JsonTypeToString(value.type())), |
1089 | 0 | args); |
1090 | 0 | } |
1091 | 0 | makefile.AddDefinition(*outputVariable, WriteJson(json)); |
1092 | |
|
1093 | 0 | } else if (mode == "SET"_s) { |
1094 | 0 | auto const& newValueStr = args.PopBack("missing new value remove"_s); |
1095 | 0 | auto const& toAdd = args.PopBack("missing member name to add"_s); |
1096 | 0 | auto& value = ResolvePath(json, args); |
1097 | |
|
1098 | 0 | Json::Value newValue = ReadJson(newValueStr); |
1099 | 0 | if (value.isObject()) { |
1100 | 0 | value[toAdd] = newValue; |
1101 | 0 | } else if (value.isArray()) { |
1102 | 0 | auto const index = |
1103 | 0 | ParseIndex(toAdd, Args{ args.begin(), args.end() + 1 }); |
1104 | 0 | if (value.isValidIndex(index)) { |
1105 | 0 | value[static_cast<int>(index)] = newValue; |
1106 | 0 | } else { |
1107 | 0 | value.append(newValue); |
1108 | 0 | } |
1109 | 0 | } else { |
1110 | 0 | throw json_error(cmStrCat("SET needs to be called with an " |
1111 | 0 | "element of type OBJECT or ARRAY, got "_s, |
1112 | 0 | JsonTypeToString(value.type()))); |
1113 | 0 | } |
1114 | | |
1115 | 0 | makefile.AddDefinition(*outputVariable, WriteJson(json)); |
1116 | |
|
1117 | 0 | } else if (mode == "EQUAL"_s) { |
1118 | 0 | auto const& jsonstr2 = |
1119 | 0 | args.PopFront("missing second json string argument"_s); |
1120 | 0 | Json::Value json2 = ReadJson(jsonstr2); |
1121 | 0 | makefile.AddDefinitionBool(*outputVariable, json == json2); |
1122 | 0 | } else if (mode == "PARTIAL_EQUAL"_s) { |
1123 | 0 | auto const& jsonstr2 = |
1124 | 0 | args.PopFront("missing second json string argument"_s); |
1125 | 0 | Json::Value json2 = ReadJson(jsonstr2); |
1126 | 0 | makefile.AddDefinitionBool(*outputVariable, |
1127 | 0 | JsonPartialMatch(json, json2)); |
1128 | 0 | } |
1129 | 0 | } |
1130 | |
|
1131 | 0 | } catch (json_error const& e) { |
1132 | 0 | if (outputVariable && e.ErrorPath) { |
1133 | 0 | auto const errorPath = cmJoin(*e.ErrorPath, "-"); |
1134 | 0 | makefile.AddDefinition(*outputVariable, |
1135 | 0 | cmStrCat(errorPath, "-NOTFOUND"_s)); |
1136 | 0 | } else if (outputVariable) { |
1137 | 0 | makefile.AddDefinition(*outputVariable, "NOTFOUND"_s); |
1138 | 0 | } |
1139 | |
|
1140 | 0 | if (errorVariable) { |
1141 | 0 | makefile.AddDefinition(*errorVariable, e.what()); |
1142 | 0 | } else { |
1143 | 0 | status.SetError(cmStrCat("sub-command JSON "_s, e.what(), "."_s)); |
1144 | 0 | success = false; |
1145 | 0 | } |
1146 | 0 | } |
1147 | 0 | return success; |
1148 | | #else |
1149 | | status.SetError(cmStrCat(arguments[0], " not available during bootstrap"_s)); |
1150 | | return false; |
1151 | | #endif |
1152 | 0 | } |
1153 | | |
1154 | | } // namespace |
1155 | | |
1156 | | bool cmStringCommand(std::vector<std::string> const& args, |
1157 | | cmExecutionStatus& status) |
1158 | 0 | { |
1159 | 0 | if (args.empty()) { |
1160 | 0 | status.SetError("must be called with at least one argument."); |
1161 | 0 | return false; |
1162 | 0 | } |
1163 | | |
1164 | 0 | static cmSubcommandTable const subcommand{ |
1165 | 0 | { "REGEX"_s, HandleRegexCommand }, |
1166 | 0 | { "REPLACE"_s, HandleReplaceCommand }, |
1167 | 0 | { "MD5"_s, HandleHashCommand }, |
1168 | 0 | { "SHA1"_s, HandleHashCommand }, |
1169 | 0 | { "SHA224"_s, HandleHashCommand }, |
1170 | 0 | { "SHA256"_s, HandleHashCommand }, |
1171 | 0 | { "SHA384"_s, HandleHashCommand }, |
1172 | 0 | { "SHA512"_s, HandleHashCommand }, |
1173 | 0 | { "SHA3_224"_s, HandleHashCommand }, |
1174 | 0 | { "SHA3_256"_s, HandleHashCommand }, |
1175 | 0 | { "SHA3_384"_s, HandleHashCommand }, |
1176 | 0 | { "SHA3_512"_s, HandleHashCommand }, |
1177 | 0 | { "TOLOWER"_s, HandleToLowerCommand }, |
1178 | 0 | { "TOUPPER"_s, HandleToUpperCommand }, |
1179 | 0 | { "COMPARE"_s, HandleCompareCommand }, |
1180 | 0 | { "ASCII"_s, HandleAsciiCommand }, |
1181 | 0 | { "HEX"_s, HandleHexCommand }, |
1182 | 0 | { "CONFIGURE"_s, HandleConfigureCommand }, |
1183 | 0 | { "LENGTH"_s, HandleLengthCommand }, |
1184 | 0 | { "APPEND"_s, HandleAppendCommand }, |
1185 | 0 | { "PREPEND"_s, HandlePrependCommand }, |
1186 | 0 | { "CONCAT"_s, HandleConcatCommand }, |
1187 | 0 | { "JOIN"_s, HandleJoinCommand }, |
1188 | 0 | { "SUBSTRING"_s, HandleSubstringCommand }, |
1189 | 0 | { "STRIP"_s, HandleStripCommand }, |
1190 | 0 | { "REPEAT"_s, HandleRepeatCommand }, |
1191 | 0 | { "RANDOM"_s, HandleRandomCommand }, |
1192 | 0 | { "FIND"_s, HandleFindCommand }, |
1193 | 0 | { "TIMESTAMP"_s, HandleTimestampCommand }, |
1194 | 0 | { "MAKE_C_IDENTIFIER"_s, HandleMakeCIdentifierCommand }, |
1195 | 0 | { "GENEX_STRIP"_s, HandleGenexStripCommand }, |
1196 | 0 | { "UUID"_s, HandleUuidCommand }, |
1197 | 0 | { "JSON"_s, HandleJSONCommand }, |
1198 | 0 | }; |
1199 | |
|
1200 | 0 | return subcommand(args[0], args, status); |
1201 | 0 | } |