cmStringCommand.cxx 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #define _SCL_SECURE_NO_WARNINGS
  4. #include "cmStringCommand.h"
  5. #include "cmsys/RegularExpression.hxx"
  6. #include <algorithm>
  7. #include <cctype>
  8. #include <cstdio>
  9. #include <cstdlib>
  10. #include <iterator>
  11. #include <memory>
  12. #include "cm_static_string_view.hxx"
  13. #include "cmAlgorithms.h"
  14. #include "cmCryptoHash.h"
  15. #include "cmExecutionStatus.h"
  16. #include "cmGeneratorExpression.h"
  17. #include "cmMakefile.h"
  18. #include "cmMessageType.h"
  19. #include "cmRange.h"
  20. #include "cmStringAlgorithms.h"
  21. #include "cmStringReplaceHelper.h"
  22. #include "cmSubcommandTable.h"
  23. #include "cmSystemTools.h"
  24. #include "cmTimestamp.h"
  25. #include "cmUuid.h"
  26. namespace {
  27. bool RegexMatch(std::vector<std::string> const& args,
  28. cmExecutionStatus& status);
  29. bool RegexMatchAll(std::vector<std::string> const& args,
  30. cmExecutionStatus& status);
  31. bool RegexReplace(std::vector<std::string> const& args,
  32. cmExecutionStatus& status);
  33. bool joinImpl(std::vector<std::string> const& args, std::string const& glue,
  34. size_t varIdx, cmMakefile& makefile);
  35. bool HandleHashCommand(std::vector<std::string> const& args,
  36. cmExecutionStatus& status)
  37. {
  38. #if !defined(CMAKE_BOOTSTRAP)
  39. if (args.size() != 3) {
  40. status.SetError(
  41. cmStrCat(args[0], " requires an output variable and an input string"));
  42. return false;
  43. }
  44. std::unique_ptr<cmCryptoHash> hash(cmCryptoHash::New(args[0]));
  45. if (hash) {
  46. std::string out = hash->HashString(args[2]);
  47. status.GetMakefile().AddDefinition(args[1], out);
  48. return true;
  49. }
  50. return false;
  51. #else
  52. status.SetError(cmStrCat(args[0], " not available during bootstrap"));
  53. return false;
  54. #endif
  55. }
  56. bool HandleToUpperLowerCommand(std::vector<std::string> const& args,
  57. bool toUpper, cmExecutionStatus& status)
  58. {
  59. if (args.size() < 3) {
  60. status.SetError("no output variable specified");
  61. return false;
  62. }
  63. std::string const& outvar = args[2];
  64. std::string output;
  65. if (toUpper) {
  66. output = cmSystemTools::UpperCase(args[1]);
  67. } else {
  68. output = cmSystemTools::LowerCase(args[1]);
  69. }
  70. // Store the output in the provided variable.
  71. status.GetMakefile().AddDefinition(outvar, output);
  72. return true;
  73. }
  74. bool HandleToUpperCommand(std::vector<std::string> const& args,
  75. cmExecutionStatus& status)
  76. {
  77. return HandleToUpperLowerCommand(args, true, status);
  78. }
  79. bool HandleToLowerCommand(std::vector<std::string> const& args,
  80. cmExecutionStatus& status)
  81. {
  82. return HandleToUpperLowerCommand(args, false, status);
  83. }
  84. bool HandleAsciiCommand(std::vector<std::string> const& args,
  85. cmExecutionStatus& status)
  86. {
  87. if (args.size() < 3) {
  88. status.SetError("No output variable specified");
  89. return false;
  90. }
  91. std::string::size_type cc;
  92. std::string const& outvar = args.back();
  93. std::string output;
  94. for (cc = 1; cc < args.size() - 1; cc++) {
  95. int ch = atoi(args[cc].c_str());
  96. if (ch > 0 && ch < 256) {
  97. output += static_cast<char>(ch);
  98. } else {
  99. std::string error =
  100. cmStrCat("Character with code ", args[cc], " does not exist.");
  101. status.SetError(error);
  102. return false;
  103. }
  104. }
  105. // Store the output in the provided variable.
  106. status.GetMakefile().AddDefinition(outvar, output);
  107. return true;
  108. }
  109. bool HandleConfigureCommand(std::vector<std::string> const& args,
  110. cmExecutionStatus& status)
  111. {
  112. if (args.size() < 2) {
  113. status.SetError("No input string specified.");
  114. return false;
  115. }
  116. if (args.size() < 3) {
  117. status.SetError("No output variable specified.");
  118. return false;
  119. }
  120. // Parse options.
  121. bool escapeQuotes = false;
  122. bool atOnly = false;
  123. for (unsigned int i = 3; i < args.size(); ++i) {
  124. if (args[i] == "@ONLY") {
  125. atOnly = true;
  126. } else if (args[i] == "ESCAPE_QUOTES") {
  127. escapeQuotes = true;
  128. } else {
  129. status.SetError(cmStrCat("Unrecognized argument \"", args[i], "\""));
  130. return false;
  131. }
  132. }
  133. // Configure the string.
  134. std::string output;
  135. status.GetMakefile().ConfigureString(args[1], output, atOnly, escapeQuotes);
  136. // Store the output in the provided variable.
  137. status.GetMakefile().AddDefinition(args[2], output);
  138. return true;
  139. }
  140. bool HandleRegexCommand(std::vector<std::string> const& args,
  141. cmExecutionStatus& status)
  142. {
  143. if (args.size() < 2) {
  144. status.SetError("sub-command REGEX requires a mode to be specified.");
  145. return false;
  146. }
  147. std::string const& mode = args[1];
  148. if (mode == "MATCH") {
  149. if (args.size() < 5) {
  150. status.SetError("sub-command REGEX, mode MATCH needs "
  151. "at least 5 arguments total to command.");
  152. return false;
  153. }
  154. return RegexMatch(args, status);
  155. }
  156. if (mode == "MATCHALL") {
  157. if (args.size() < 5) {
  158. status.SetError("sub-command REGEX, mode MATCHALL needs "
  159. "at least 5 arguments total to command.");
  160. return false;
  161. }
  162. return RegexMatchAll(args, status);
  163. }
  164. if (mode == "REPLACE") {
  165. if (args.size() < 6) {
  166. status.SetError("sub-command REGEX, mode REPLACE needs "
  167. "at least 6 arguments total to command.");
  168. return false;
  169. }
  170. return RegexReplace(args, status);
  171. }
  172. std::string e = "sub-command REGEX does not recognize mode " + mode;
  173. status.SetError(e);
  174. return false;
  175. }
  176. bool RegexMatch(std::vector<std::string> const& args,
  177. cmExecutionStatus& status)
  178. {
  179. //"STRING(REGEX MATCH <regular_expression> <output variable>
  180. // <input> [<input>...])\n";
  181. std::string const& regex = args[2];
  182. std::string const& outvar = args[3];
  183. status.GetMakefile().ClearMatches();
  184. // Compile the regular expression.
  185. cmsys::RegularExpression re;
  186. if (!re.compile(regex.c_str())) {
  187. std::string e =
  188. "sub-command REGEX, mode MATCH failed to compile regex \"" + regex +
  189. "\".";
  190. status.SetError(e);
  191. return false;
  192. }
  193. // Concatenate all the last arguments together.
  194. std::string input = cmJoin(cmMakeRange(args).advance(4), std::string());
  195. // Scan through the input for all matches.
  196. std::string output;
  197. if (re.find(input)) {
  198. status.GetMakefile().StoreMatches(re);
  199. std::string::size_type l = re.start();
  200. std::string::size_type r = re.end();
  201. if (r - l == 0) {
  202. std::string e = "sub-command REGEX, mode MATCH regex \"" + regex +
  203. "\" matched an empty string.";
  204. status.SetError(e);
  205. return false;
  206. }
  207. output = input.substr(l, r - l);
  208. }
  209. // Store the output in the provided variable.
  210. status.GetMakefile().AddDefinition(outvar, output);
  211. return true;
  212. }
  213. bool RegexMatchAll(std::vector<std::string> const& args,
  214. cmExecutionStatus& status)
  215. {
  216. //"STRING(REGEX MATCHALL <regular_expression> <output variable> <input>
  217. // [<input>...])\n";
  218. std::string const& regex = args[2];
  219. std::string const& outvar = args[3];
  220. status.GetMakefile().ClearMatches();
  221. // Compile the regular expression.
  222. cmsys::RegularExpression re;
  223. if (!re.compile(regex.c_str())) {
  224. std::string e =
  225. "sub-command REGEX, mode MATCHALL failed to compile regex \"" + regex +
  226. "\".";
  227. status.SetError(e);
  228. return false;
  229. }
  230. // Concatenate all the last arguments together.
  231. std::string input = cmJoin(cmMakeRange(args).advance(4), std::string());
  232. // Scan through the input for all matches.
  233. std::string output;
  234. const char* p = input.c_str();
  235. while (re.find(p)) {
  236. status.GetMakefile().ClearMatches();
  237. status.GetMakefile().StoreMatches(re);
  238. std::string::size_type l = re.start();
  239. std::string::size_type r = re.end();
  240. if (r - l == 0) {
  241. std::string e = "sub-command REGEX, mode MATCHALL regex \"" + regex +
  242. "\" matched an empty string.";
  243. status.SetError(e);
  244. return false;
  245. }
  246. if (!output.empty()) {
  247. output += ";";
  248. }
  249. output += std::string(p + l, r - l);
  250. p += r;
  251. }
  252. // Store the output in the provided variable.
  253. status.GetMakefile().AddDefinition(outvar, output);
  254. return true;
  255. }
  256. bool RegexReplace(std::vector<std::string> const& args,
  257. cmExecutionStatus& status)
  258. {
  259. //"STRING(REGEX REPLACE <regular_expression> <replace_expression>
  260. // <output variable> <input> [<input>...])\n"
  261. std::string const& regex = args[2];
  262. std::string const& replace = args[3];
  263. std::string const& outvar = args[4];
  264. cmStringReplaceHelper replaceHelper(regex, replace, &status.GetMakefile());
  265. if (!replaceHelper.IsReplaceExpressionValid()) {
  266. status.SetError(
  267. "sub-command REGEX, mode REPLACE: " + replaceHelper.GetError() + ".");
  268. return false;
  269. }
  270. status.GetMakefile().ClearMatches();
  271. if (!replaceHelper.IsRegularExpressionValid()) {
  272. std::string e =
  273. "sub-command REGEX, mode REPLACE failed to compile regex \"" + regex +
  274. "\".";
  275. status.SetError(e);
  276. return false;
  277. }
  278. // Concatenate all the last arguments together.
  279. const std::string input =
  280. cmJoin(cmMakeRange(args).advance(5), std::string());
  281. std::string output;
  282. if (!replaceHelper.Replace(input, output)) {
  283. status.SetError(
  284. "sub-command REGEX, mode REPLACE: " + replaceHelper.GetError() + ".");
  285. return false;
  286. }
  287. // Store the output in the provided variable.
  288. status.GetMakefile().AddDefinition(outvar, output);
  289. return true;
  290. }
  291. bool HandleFindCommand(std::vector<std::string> const& args,
  292. cmExecutionStatus& status)
  293. {
  294. // check if all required parameters were passed
  295. if (args.size() < 4 || args.size() > 5) {
  296. status.SetError("sub-command FIND requires 3 or 4 parameters.");
  297. return false;
  298. }
  299. // check if the reverse flag was set or not
  300. bool reverseMode = false;
  301. if (args.size() == 5 && args[4] == "REVERSE") {
  302. reverseMode = true;
  303. }
  304. // if we have 5 arguments the last one must be REVERSE
  305. if (args.size() == 5 && args[4] != "REVERSE") {
  306. status.SetError("sub-command FIND: unknown last parameter");
  307. return false;
  308. }
  309. // local parameter names.
  310. const std::string& sstring = args[1];
  311. const std::string& schar = args[2];
  312. const std::string& outvar = args[3];
  313. // ensure that the user cannot accidentally specify REVERSE as a variable
  314. if (outvar == "REVERSE") {
  315. status.SetError("sub-command FIND does not allow one to select REVERSE as "
  316. "the output variable. "
  317. "Maybe you missed the actual output variable?");
  318. return false;
  319. }
  320. // try to find the character and return its position
  321. size_t pos;
  322. if (!reverseMode) {
  323. pos = sstring.find(schar);
  324. } else {
  325. pos = sstring.rfind(schar);
  326. }
  327. if (std::string::npos != pos) {
  328. status.GetMakefile().AddDefinition(outvar, std::to_string(pos));
  329. return true;
  330. }
  331. // the character was not found, but this is not really an error
  332. status.GetMakefile().AddDefinition(outvar, "-1");
  333. return true;
  334. }
  335. bool HandleCompareCommand(std::vector<std::string> const& args,
  336. cmExecutionStatus& status)
  337. {
  338. if (args.size() < 2) {
  339. status.SetError("sub-command COMPARE requires a mode to be specified.");
  340. return false;
  341. }
  342. std::string const& mode = args[1];
  343. if ((mode == "EQUAL") || (mode == "NOTEQUAL") || (mode == "LESS") ||
  344. (mode == "LESS_EQUAL") || (mode == "GREATER") ||
  345. (mode == "GREATER_EQUAL")) {
  346. if (args.size() < 5) {
  347. std::string e =
  348. cmStrCat("sub-command COMPARE, mode ", mode,
  349. " needs at least 5 arguments total to command.");
  350. status.SetError(e);
  351. return false;
  352. }
  353. const std::string& left = args[2];
  354. const std::string& right = args[3];
  355. const std::string& outvar = args[4];
  356. bool result;
  357. if (mode == "LESS") {
  358. result = (left < right);
  359. } else if (mode == "LESS_EQUAL") {
  360. result = (left <= right);
  361. } else if (mode == "GREATER") {
  362. result = (left > right);
  363. } else if (mode == "GREATER_EQUAL") {
  364. result = (left >= right);
  365. } else if (mode == "EQUAL") {
  366. result = (left == right);
  367. } else // if(mode == "NOTEQUAL")
  368. {
  369. result = !(left == right);
  370. }
  371. if (result) {
  372. status.GetMakefile().AddDefinition(outvar, "1");
  373. } else {
  374. status.GetMakefile().AddDefinition(outvar, "0");
  375. }
  376. return true;
  377. }
  378. std::string e = "sub-command COMPARE does not recognize mode " + mode;
  379. status.SetError(e);
  380. return false;
  381. }
  382. bool HandleReplaceCommand(std::vector<std::string> const& args,
  383. cmExecutionStatus& status)
  384. {
  385. if (args.size() < 5) {
  386. status.SetError("sub-command REPLACE requires at least four arguments.");
  387. return false;
  388. }
  389. const std::string& matchExpression = args[1];
  390. const std::string& replaceExpression = args[2];
  391. const std::string& variableName = args[3];
  392. std::string input = cmJoin(cmMakeRange(args).advance(4), std::string());
  393. cmsys::SystemTools::ReplaceString(input, matchExpression.c_str(),
  394. replaceExpression.c_str());
  395. status.GetMakefile().AddDefinition(variableName, input);
  396. return true;
  397. }
  398. bool HandleSubstringCommand(std::vector<std::string> const& args,
  399. cmExecutionStatus& status)
  400. {
  401. if (args.size() != 5) {
  402. status.SetError("sub-command SUBSTRING requires four arguments.");
  403. return false;
  404. }
  405. const std::string& stringValue = args[1];
  406. int begin = atoi(args[2].c_str());
  407. int end = atoi(args[3].c_str());
  408. const std::string& variableName = args[4];
  409. size_t stringLength = stringValue.size();
  410. int intStringLength = static_cast<int>(stringLength);
  411. if (begin < 0 || begin > intStringLength) {
  412. status.SetError(
  413. cmStrCat("begin index: ", begin, " is out of range 0 - ", stringLength));
  414. return false;
  415. }
  416. if (end < -1) {
  417. status.SetError(cmStrCat("end index: ", end, " should be -1 or greater"));
  418. return false;
  419. }
  420. status.GetMakefile().AddDefinition(variableName,
  421. stringValue.substr(begin, end));
  422. return true;
  423. }
  424. bool HandleLengthCommand(std::vector<std::string> const& args,
  425. cmExecutionStatus& status)
  426. {
  427. if (args.size() != 3) {
  428. status.SetError("sub-command LENGTH requires two arguments.");
  429. return false;
  430. }
  431. const std::string& stringValue = args[1];
  432. const std::string& variableName = args[2];
  433. size_t length = stringValue.size();
  434. char buffer[1024];
  435. sprintf(buffer, "%d", static_cast<int>(length));
  436. status.GetMakefile().AddDefinition(variableName, buffer);
  437. return true;
  438. }
  439. bool HandleAppendCommand(std::vector<std::string> const& args,
  440. cmExecutionStatus& status)
  441. {
  442. if (args.size() < 2) {
  443. status.SetError("sub-command APPEND requires at least one argument.");
  444. return false;
  445. }
  446. // Skip if nothing to append.
  447. if (args.size() < 3) {
  448. return true;
  449. }
  450. const std::string& variable = args[1];
  451. std::string value;
  452. const char* oldValue = status.GetMakefile().GetDefinition(variable);
  453. if (oldValue) {
  454. value = oldValue;
  455. }
  456. value += cmJoin(cmMakeRange(args).advance(2), std::string());
  457. status.GetMakefile().AddDefinition(variable, value);
  458. return true;
  459. }
  460. bool HandlePrependCommand(std::vector<std::string> const& args,
  461. cmExecutionStatus& status)
  462. {
  463. if (args.size() < 2) {
  464. status.SetError("sub-command PREPEND requires at least one argument.");
  465. return false;
  466. }
  467. // Skip if nothing to prepend.
  468. if (args.size() < 3) {
  469. return true;
  470. }
  471. const std::string& variable = args[1];
  472. std::string value = cmJoin(cmMakeRange(args).advance(2), std::string());
  473. const char* oldValue = status.GetMakefile().GetDefinition(variable);
  474. if (oldValue) {
  475. value += oldValue;
  476. }
  477. status.GetMakefile().AddDefinition(variable, value);
  478. return true;
  479. }
  480. bool HandleConcatCommand(std::vector<std::string> const& args,
  481. cmExecutionStatus& status)
  482. {
  483. if (args.size() < 2) {
  484. status.SetError("sub-command CONCAT requires at least one argument.");
  485. return false;
  486. }
  487. return joinImpl(args, std::string(), 1, status.GetMakefile());
  488. }
  489. bool HandleJoinCommand(std::vector<std::string> const& args,
  490. cmExecutionStatus& status)
  491. {
  492. if (args.size() < 3) {
  493. status.SetError("sub-command JOIN requires at least two arguments.");
  494. return false;
  495. }
  496. return joinImpl(args, args[1], 2, status.GetMakefile());
  497. }
  498. bool joinImpl(std::vector<std::string> const& args, std::string const& glue,
  499. const size_t varIdx, cmMakefile& makefile)
  500. {
  501. std::string const& variableName = args[varIdx];
  502. // NOTE Items to concat/join placed right after the variable for
  503. // both `CONCAT` and `JOIN` sub-commands.
  504. std::string value = cmJoin(cmMakeRange(args).advance(varIdx + 1), glue);
  505. makefile.AddDefinition(variableName, value);
  506. return true;
  507. }
  508. bool HandleMakeCIdentifierCommand(std::vector<std::string> const& args,
  509. cmExecutionStatus& status)
  510. {
  511. if (args.size() != 3) {
  512. status.SetError("sub-command MAKE_C_IDENTIFIER requires two arguments.");
  513. return false;
  514. }
  515. const std::string& input = args[1];
  516. const std::string& variableName = args[2];
  517. status.GetMakefile().AddDefinition(variableName,
  518. cmSystemTools::MakeCidentifier(input));
  519. return true;
  520. }
  521. bool HandleGenexStripCommand(std::vector<std::string> const& args,
  522. cmExecutionStatus& status)
  523. {
  524. if (args.size() != 3) {
  525. status.SetError("sub-command GENEX_STRIP requires two arguments.");
  526. return false;
  527. }
  528. const std::string& input = args[1];
  529. std::string result = cmGeneratorExpression::Preprocess(
  530. input, cmGeneratorExpression::StripAllGeneratorExpressions);
  531. const std::string& variableName = args[2];
  532. status.GetMakefile().AddDefinition(variableName, result);
  533. return true;
  534. }
  535. bool HandleStripCommand(std::vector<std::string> const& args,
  536. cmExecutionStatus& status)
  537. {
  538. if (args.size() != 3) {
  539. status.SetError("sub-command STRIP requires two arguments.");
  540. return false;
  541. }
  542. const std::string& stringValue = args[1];
  543. const std::string& variableName = args[2];
  544. size_t inStringLength = stringValue.size();
  545. size_t startPos = inStringLength + 1;
  546. size_t endPos = 0;
  547. const char* ptr = stringValue.c_str();
  548. size_t cc;
  549. for (cc = 0; cc < inStringLength; ++cc) {
  550. if (!isspace(*ptr)) {
  551. if (startPos > inStringLength) {
  552. startPos = cc;
  553. }
  554. endPos = cc;
  555. }
  556. ++ptr;
  557. }
  558. size_t outLength = 0;
  559. // if the input string didn't contain any non-space characters, return
  560. // an empty string
  561. if (startPos > inStringLength) {
  562. outLength = 0;
  563. startPos = 0;
  564. } else {
  565. outLength = endPos - startPos + 1;
  566. }
  567. status.GetMakefile().AddDefinition(variableName,
  568. stringValue.substr(startPos, outLength));
  569. return true;
  570. }
  571. bool HandleRepeatCommand(std::vector<std::string> const& args,
  572. cmExecutionStatus& status)
  573. {
  574. cmMakefile& makefile = status.GetMakefile();
  575. // `string(REPEAT "<str>" <times> OUTPUT_VARIABLE)`
  576. enum ArgPos : std::size_t
  577. {
  578. SUB_COMMAND,
  579. VALUE,
  580. TIMES,
  581. OUTPUT_VARIABLE,
  582. TOTAL_ARGS
  583. };
  584. if (args.size() != ArgPos::TOTAL_ARGS) {
  585. makefile.IssueMessage(MessageType::FATAL_ERROR,
  586. "sub-command REPEAT requires three arguments.");
  587. return true;
  588. }
  589. unsigned long times;
  590. if (!cmStrToULong(args[ArgPos::TIMES], &times)) {
  591. makefile.IssueMessage(MessageType::FATAL_ERROR,
  592. "repeat count is not a positive number.");
  593. return true;
  594. }
  595. const auto& stringValue = args[ArgPos::VALUE];
  596. const auto& variableName = args[ArgPos::OUTPUT_VARIABLE];
  597. const auto inStringLength = stringValue.size();
  598. std::string result;
  599. switch (inStringLength) {
  600. case 0u:
  601. // Nothing to do for zero length input strings
  602. break;
  603. case 1u:
  604. // NOTE If the string to repeat consists of the only character,
  605. // use the appropriate constructor.
  606. result = std::string(times, stringValue[0]);
  607. break;
  608. default:
  609. result = std::string(inStringLength * times, char{});
  610. for (auto i = 0u; i < times; ++i) {
  611. std::copy(cm::cbegin(stringValue), cm::cend(stringValue),
  612. &result[i * inStringLength]);
  613. }
  614. break;
  615. }
  616. makefile.AddDefinition(variableName, result);
  617. return true;
  618. }
  619. bool HandleRandomCommand(std::vector<std::string> const& args,
  620. cmExecutionStatus& status)
  621. {
  622. if (args.size() < 2 || args.size() == 3 || args.size() == 5) {
  623. status.SetError("sub-command RANDOM requires at least one argument.");
  624. return false;
  625. }
  626. static bool seeded = false;
  627. bool force_seed = false;
  628. unsigned int seed = 0;
  629. int length = 5;
  630. const char cmStringCommandDefaultAlphabet[] = "qwertyuiopasdfghjklzxcvbnm"
  631. "QWERTYUIOPASDFGHJKLZXCVBNM"
  632. "0123456789";
  633. std::string alphabet;
  634. if (args.size() > 3) {
  635. size_t i = 1;
  636. size_t stopAt = args.size() - 2;
  637. for (; i < stopAt; ++i) {
  638. if (args[i] == "LENGTH") {
  639. ++i;
  640. length = atoi(args[i].c_str());
  641. } else if (args[i] == "ALPHABET") {
  642. ++i;
  643. alphabet = args[i];
  644. } else if (args[i] == "RANDOM_SEED") {
  645. ++i;
  646. seed = static_cast<unsigned int>(atoi(args[i].c_str()));
  647. force_seed = true;
  648. }
  649. }
  650. }
  651. if (alphabet.empty()) {
  652. alphabet = cmStringCommandDefaultAlphabet;
  653. }
  654. double sizeofAlphabet = static_cast<double>(alphabet.size());
  655. if (sizeofAlphabet < 1) {
  656. status.SetError("sub-command RANDOM invoked with bad alphabet.");
  657. return false;
  658. }
  659. if (length < 1) {
  660. status.SetError("sub-command RANDOM invoked with bad length.");
  661. return false;
  662. }
  663. const std::string& variableName = args.back();
  664. std::vector<char> result;
  665. if (!seeded || force_seed) {
  666. seeded = true;
  667. srand(force_seed ? seed : cmSystemTools::RandomSeed());
  668. }
  669. const char* alphaPtr = alphabet.c_str();
  670. for (int cc = 0; cc < length; cc++) {
  671. int idx = static_cast<int>(sizeofAlphabet * rand() / (RAND_MAX + 1.0));
  672. result.push_back(*(alphaPtr + idx));
  673. }
  674. result.push_back(0);
  675. status.GetMakefile().AddDefinition(variableName, result.data());
  676. return true;
  677. }
  678. bool HandleTimestampCommand(std::vector<std::string> const& args,
  679. cmExecutionStatus& status)
  680. {
  681. if (args.size() < 2) {
  682. status.SetError("sub-command TIMESTAMP requires at least one argument.");
  683. return false;
  684. }
  685. if (args.size() > 4) {
  686. status.SetError("sub-command TIMESTAMP takes at most three arguments.");
  687. return false;
  688. }
  689. unsigned int argsIndex = 1;
  690. const std::string& outputVariable = args[argsIndex++];
  691. std::string formatString;
  692. if (args.size() > argsIndex && args[argsIndex] != "UTC") {
  693. formatString = args[argsIndex++];
  694. }
  695. bool utcFlag = false;
  696. if (args.size() > argsIndex) {
  697. if (args[argsIndex] == "UTC") {
  698. utcFlag = true;
  699. } else {
  700. std::string e = " TIMESTAMP sub-command does not recognize option " +
  701. args[argsIndex] + ".";
  702. status.SetError(e);
  703. return false;
  704. }
  705. }
  706. cmTimestamp timestamp;
  707. std::string result = timestamp.CurrentTime(formatString, utcFlag);
  708. status.GetMakefile().AddDefinition(outputVariable, result);
  709. return true;
  710. }
  711. bool HandleUuidCommand(std::vector<std::string> const& args,
  712. cmExecutionStatus& status)
  713. {
  714. #if !defined(CMAKE_BOOTSTRAP)
  715. unsigned int argsIndex = 1;
  716. if (args.size() < 2) {
  717. status.SetError("UUID sub-command requires an output variable.");
  718. return false;
  719. }
  720. const std::string& outputVariable = args[argsIndex++];
  721. std::string uuidNamespaceString;
  722. std::string uuidName;
  723. std::string uuidType;
  724. bool uuidUpperCase = false;
  725. while (args.size() > argsIndex) {
  726. if (args[argsIndex] == "NAMESPACE") {
  727. ++argsIndex;
  728. if (argsIndex >= args.size()) {
  729. status.SetError("UUID sub-command, NAMESPACE requires a value.");
  730. return false;
  731. }
  732. uuidNamespaceString = args[argsIndex++];
  733. } else if (args[argsIndex] == "NAME") {
  734. ++argsIndex;
  735. if (argsIndex >= args.size()) {
  736. status.SetError("UUID sub-command, NAME requires a value.");
  737. return false;
  738. }
  739. uuidName = args[argsIndex++];
  740. } else if (args[argsIndex] == "TYPE") {
  741. ++argsIndex;
  742. if (argsIndex >= args.size()) {
  743. status.SetError("UUID sub-command, TYPE requires a value.");
  744. return false;
  745. }
  746. uuidType = args[argsIndex++];
  747. } else if (args[argsIndex] == "UPPER") {
  748. ++argsIndex;
  749. uuidUpperCase = true;
  750. } else {
  751. std::string e =
  752. "UUID sub-command does not recognize option " + args[argsIndex] + ".";
  753. status.SetError(e);
  754. return false;
  755. }
  756. }
  757. std::string uuid;
  758. cmUuid uuidGenerator;
  759. std::vector<unsigned char> uuidNamespace;
  760. if (!uuidGenerator.StringToBinary(uuidNamespaceString, uuidNamespace)) {
  761. status.SetError("UUID sub-command, malformed NAMESPACE UUID.");
  762. return false;
  763. }
  764. if (uuidType == "MD5") {
  765. uuid = uuidGenerator.FromMd5(uuidNamespace, uuidName);
  766. } else if (uuidType == "SHA1") {
  767. uuid = uuidGenerator.FromSha1(uuidNamespace, uuidName);
  768. } else {
  769. std::string e = "UUID sub-command, unknown TYPE '" + uuidType + "'.";
  770. status.SetError(e);
  771. return false;
  772. }
  773. if (uuid.empty()) {
  774. status.SetError("UUID sub-command, generation failed.");
  775. return false;
  776. }
  777. if (uuidUpperCase) {
  778. uuid = cmSystemTools::UpperCase(uuid);
  779. }
  780. status.GetMakefile().AddDefinition(outputVariable, uuid);
  781. return true;
  782. #else
  783. status.SetError(cmStrCat(args[0], " not available during bootstrap"));
  784. return false;
  785. #endif
  786. }
  787. } // namespace
  788. bool cmStringCommand(std::vector<std::string> const& args,
  789. cmExecutionStatus& status)
  790. {
  791. if (args.empty()) {
  792. status.SetError("must be called with at least one argument.");
  793. return false;
  794. }
  795. static cmSubcommandTable const subcommand{
  796. { "REGEX"_s, HandleRegexCommand },
  797. { "REPLACE"_s, HandleReplaceCommand },
  798. { "MD5"_s, HandleHashCommand },
  799. { "SHA1"_s, HandleHashCommand },
  800. { "SHA224"_s, HandleHashCommand },
  801. { "SHA256"_s, HandleHashCommand },
  802. { "SHA384"_s, HandleHashCommand },
  803. { "SHA512"_s, HandleHashCommand },
  804. { "SHA3_224"_s, HandleHashCommand },
  805. { "SHA3_256"_s, HandleHashCommand },
  806. { "SHA3_384"_s, HandleHashCommand },
  807. { "SHA3_512"_s, HandleHashCommand },
  808. { "TOLOWER"_s, HandleToLowerCommand },
  809. { "TOUPPER"_s, HandleToUpperCommand },
  810. { "COMPARE"_s, HandleCompareCommand },
  811. { "ASCII"_s, HandleAsciiCommand },
  812. { "CONFIGURE"_s, HandleConfigureCommand },
  813. { "LENGTH"_s, HandleLengthCommand },
  814. { "APPEND"_s, HandleAppendCommand },
  815. { "PREPEND"_s, HandlePrependCommand },
  816. { "CONCAT"_s, HandleConcatCommand },
  817. { "JOIN"_s, HandleJoinCommand },
  818. { "SUBSTRING"_s, HandleSubstringCommand },
  819. { "STRIP"_s, HandleStripCommand },
  820. { "REPEAT"_s, HandleRepeatCommand },
  821. { "RANDOM"_s, HandleRandomCommand },
  822. { "FIND"_s, HandleFindCommand },
  823. { "TIMESTAMP"_s, HandleTimestampCommand },
  824. { "MAKE_C_IDENTIFIER"_s, HandleMakeCIdentifierCommand },
  825. { "GENEX_STRIP"_s, HandleGenexStripCommand },
  826. { "UUID"_s, HandleUuidCommand },
  827. };
  828. return subcommand(args[0], args, status);
  829. }