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