cmStringCommand.cxx 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842
  1. /*============================================================================
  2. CMake - Cross Platform Makefile Generator
  3. Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
  4. Distributed under the OSI-approved BSD License (the "License");
  5. see accompanying file Copyright.txt for details.
  6. This software is distributed WITHOUT ANY WARRANTY; without even the
  7. implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  8. See the License for more information.
  9. ============================================================================*/
  10. #include "cmStringCommand.h"
  11. #include <cmsys/RegularExpression.hxx>
  12. #include <cmsys/SystemTools.hxx>
  13. #include <stdlib.h> // required for atoi
  14. #include <ctype.h>
  15. #include <time.h>
  16. //----------------------------------------------------------------------------
  17. bool cmStringCommand
  18. ::InitialPass(std::vector<std::string> const& args, cmExecutionStatus &)
  19. {
  20. if(args.size() < 1)
  21. {
  22. this->SetError("must be called with at least one argument.");
  23. return false;
  24. }
  25. const std::string &subCommand = args[0];
  26. if(subCommand == "REGEX")
  27. {
  28. return this->HandleRegexCommand(args);
  29. }
  30. else if(subCommand == "REPLACE")
  31. {
  32. return this->HandleReplaceCommand(args);
  33. }
  34. else if(subCommand == "TOLOWER")
  35. {
  36. return this->HandleToUpperLowerCommand(args, false);
  37. }
  38. else if(subCommand == "TOUPPER")
  39. {
  40. return this->HandleToUpperLowerCommand(args, true);
  41. }
  42. else if(subCommand == "COMPARE")
  43. {
  44. return this->HandleCompareCommand(args);
  45. }
  46. else if(subCommand == "ASCII")
  47. {
  48. return this->HandleAsciiCommand(args);
  49. }
  50. else if(subCommand == "CONFIGURE")
  51. {
  52. return this->HandleConfigureCommand(args);
  53. }
  54. else if(subCommand == "LENGTH")
  55. {
  56. return this->HandleLengthCommand(args);
  57. }
  58. else if(subCommand == "SUBSTRING")
  59. {
  60. return this->HandleSubstringCommand(args);
  61. }
  62. else if(subCommand == "STRIP")
  63. {
  64. return this->HandleStripCommand(args);
  65. }
  66. else if(subCommand == "RANDOM")
  67. {
  68. return this->HandleRandomCommand(args);
  69. }
  70. else if(subCommand == "FIND")
  71. {
  72. return this->HandleFindCommand(args);
  73. }
  74. std::string e = "does not recognize sub-command "+subCommand;
  75. this->SetError(e.c_str());
  76. return false;
  77. }
  78. //----------------------------------------------------------------------------
  79. bool cmStringCommand::HandleToUpperLowerCommand(
  80. std::vector<std::string> const& args, bool toUpper)
  81. {
  82. if ( args.size() < 3 )
  83. {
  84. this->SetError("no output variable specified");
  85. return false;
  86. }
  87. std::string outvar = args[2];
  88. std::string output;
  89. if (toUpper)
  90. {
  91. output = cmSystemTools::UpperCase(args[1]);
  92. }
  93. else
  94. {
  95. output = cmSystemTools::LowerCase(args[1]);
  96. }
  97. // Store the output in the provided variable.
  98. this->Makefile->AddDefinition(outvar.c_str(), output.c_str());
  99. return true;
  100. }
  101. //----------------------------------------------------------------------------
  102. bool cmStringCommand::HandleAsciiCommand(std::vector<std::string> const& args)
  103. {
  104. if ( args.size() < 3 )
  105. {
  106. this->SetError("No output variable specified");
  107. return false;
  108. }
  109. std::string::size_type cc;
  110. std::string outvar = args[args.size()-1];
  111. std::string output = "";
  112. for ( cc = 1; cc < args.size()-1; cc ++ )
  113. {
  114. int ch = atoi(args[cc].c_str());
  115. if ( ch > 0 && ch < 256 )
  116. {
  117. output += static_cast<char>(ch);
  118. }
  119. else
  120. {
  121. std::string error = "Character with code ";
  122. error += args[cc];
  123. error += " does not exist.";
  124. this->SetError(error.c_str());
  125. return false;
  126. }
  127. }
  128. // Store the output in the provided variable.
  129. this->Makefile->AddDefinition(outvar.c_str(), output.c_str());
  130. return true;
  131. }
  132. //----------------------------------------------------------------------------
  133. bool cmStringCommand::HandleConfigureCommand(
  134. std::vector<std::string> const& args)
  135. {
  136. if ( args.size() < 2 )
  137. {
  138. this->SetError("No input string specified.");
  139. return false;
  140. }
  141. else if ( args.size() < 3 )
  142. {
  143. this->SetError("No output variable specified.");
  144. return false;
  145. }
  146. // Parse options.
  147. bool escapeQuotes = false;
  148. bool atOnly = false;
  149. for(unsigned int i = 3; i < args.size(); ++i)
  150. {
  151. if(args[i] == "@ONLY")
  152. {
  153. atOnly = true;
  154. }
  155. else if(args[i] == "ESCAPE_QUOTES")
  156. {
  157. escapeQuotes = true;
  158. }
  159. else
  160. {
  161. cmOStringStream err;
  162. err << "Unrecognized argument \"" << args[i] << "\"";
  163. this->SetError(err.str().c_str());
  164. return false;
  165. }
  166. }
  167. // Configure the string.
  168. std::string output;
  169. this->Makefile->ConfigureString(args[1], output, atOnly, escapeQuotes);
  170. // Store the output in the provided variable.
  171. this->Makefile->AddDefinition(args[2].c_str(), output.c_str());
  172. return true;
  173. }
  174. //----------------------------------------------------------------------------
  175. bool cmStringCommand::HandleRegexCommand(std::vector<std::string> const& args)
  176. {
  177. if(args.size() < 2)
  178. {
  179. this->SetError("sub-command REGEX requires a mode to be specified.");
  180. return false;
  181. }
  182. std::string mode = args[1];
  183. if(mode == "MATCH")
  184. {
  185. if(args.size() < 5)
  186. {
  187. this->SetError("sub-command REGEX, mode MATCH needs "
  188. "at least 5 arguments total to command.");
  189. return false;
  190. }
  191. return this->RegexMatch(args);
  192. }
  193. else if(mode == "MATCHALL")
  194. {
  195. if(args.size() < 5)
  196. {
  197. this->SetError("sub-command REGEX, mode MATCHALL needs "
  198. "at least 5 arguments total to command.");
  199. return false;
  200. }
  201. return this->RegexMatchAll(args);
  202. }
  203. else if(mode == "REPLACE")
  204. {
  205. if(args.size() < 6)
  206. {
  207. this->SetError("sub-command REGEX, mode REPLACE needs "
  208. "at least 6 arguments total to command.");
  209. return false;
  210. }
  211. return this->RegexReplace(args);
  212. }
  213. std::string e = "sub-command REGEX does not recognize mode "+mode;
  214. this->SetError(e.c_str());
  215. return false;
  216. }
  217. //----------------------------------------------------------------------------
  218. bool cmStringCommand::RegexMatch(std::vector<std::string> const& args)
  219. {
  220. //"STRING(REGEX MATCH <regular_expression> <output variable>
  221. // <input> [<input>...])\n";
  222. std::string regex = args[2];
  223. std::string outvar = args[3];
  224. // Concatenate all the last arguments together.
  225. std::string input = args[4];
  226. for(unsigned int i=5; i < args.size(); ++i)
  227. {
  228. input += args[i];
  229. }
  230. this->ClearMatches(this->Makefile);
  231. // Compile the regular expression.
  232. cmsys::RegularExpression re;
  233. if(!re.compile(regex.c_str()))
  234. {
  235. std::string e =
  236. "sub-command REGEX, mode MATCH failed to compile regex \""+regex+"\".";
  237. this->SetError(e.c_str());
  238. return false;
  239. }
  240. // Scan through the input for all matches.
  241. std::string output;
  242. if(re.find(input.c_str()))
  243. {
  244. this->StoreMatches(this->Makefile, re);
  245. std::string::size_type l = re.start();
  246. std::string::size_type r = re.end();
  247. if(r-l == 0)
  248. {
  249. std::string e =
  250. "sub-command REGEX, mode MATCH regex \""+regex+
  251. "\" matched an empty string.";
  252. this->SetError(e.c_str());
  253. return false;
  254. }
  255. output = input.substr(l, r-l);
  256. }
  257. // Store the output in the provided variable.
  258. this->Makefile->AddDefinition(outvar.c_str(), output.c_str());
  259. return true;
  260. }
  261. //----------------------------------------------------------------------------
  262. bool cmStringCommand::RegexMatchAll(std::vector<std::string> const& args)
  263. {
  264. //"STRING(REGEX MATCHALL <regular_expression> <output variable> <input>
  265. // [<input>...])\n";
  266. std::string regex = args[2];
  267. std::string outvar = args[3];
  268. // Concatenate all the last arguments together.
  269. std::string input = args[4];
  270. for(unsigned int i=5; i < args.size(); ++i)
  271. {
  272. input += args[i];
  273. }
  274. this->ClearMatches(this->Makefile);
  275. // Compile the regular expression.
  276. cmsys::RegularExpression re;
  277. if(!re.compile(regex.c_str()))
  278. {
  279. std::string e =
  280. "sub-command REGEX, mode MATCHALL failed to compile regex \""+
  281. regex+"\".";
  282. this->SetError(e.c_str());
  283. return false;
  284. }
  285. // Scan through the input for all matches.
  286. std::string output;
  287. const char* p = input.c_str();
  288. while(re.find(p))
  289. {
  290. this->StoreMatches(this->Makefile, re);
  291. std::string::size_type l = re.start();
  292. std::string::size_type r = re.end();
  293. if(r-l == 0)
  294. {
  295. std::string e = "sub-command REGEX, mode MATCHALL regex \""+
  296. regex+"\" matched an empty string.";
  297. this->SetError(e.c_str());
  298. return false;
  299. }
  300. if(output.length() > 0)
  301. {
  302. output += ";";
  303. }
  304. output += std::string(p+l, r-l);
  305. p += r;
  306. }
  307. // Store the output in the provided variable.
  308. this->Makefile->AddDefinition(outvar.c_str(), output.c_str());
  309. return true;
  310. }
  311. //----------------------------------------------------------------------------
  312. bool cmStringCommand::RegexReplace(std::vector<std::string> const& args)
  313. {
  314. //"STRING(REGEX REPLACE <regular_expression> <replace_expression>
  315. // <output variable> <input> [<input>...])\n"
  316. std::string regex = args[2];
  317. std::string replace = args[3];
  318. std::string outvar = args[4];
  319. // Pull apart the replace expression to find the escaped [0-9] values.
  320. std::vector<RegexReplacement> replacement;
  321. std::string::size_type l = 0;
  322. while(l < replace.length())
  323. {
  324. std::string::size_type r = replace.find("\\", l);
  325. if(r == std::string::npos)
  326. {
  327. r = replace.length();
  328. replacement.push_back(replace.substr(l, r-l));
  329. }
  330. else
  331. {
  332. if(r-l > 0)
  333. {
  334. replacement.push_back(replace.substr(l, r-l));
  335. }
  336. if(r == (replace.length()-1))
  337. {
  338. this->SetError("sub-command REGEX, mode REPLACE: "
  339. "replace-expression ends in a backslash.");
  340. return false;
  341. }
  342. if((replace[r+1] >= '0') && (replace[r+1] <= '9'))
  343. {
  344. replacement.push_back(replace[r+1]-'0');
  345. }
  346. else if(replace[r+1] == 'n')
  347. {
  348. replacement.push_back("\n");
  349. }
  350. else if(replace[r+1] == '\\')
  351. {
  352. replacement.push_back("\\");
  353. }
  354. else
  355. {
  356. std::string e = "sub-command REGEX, mode REPLACE: Unknown escape \"";
  357. e += replace.substr(r, 2);
  358. e += "\" in replace-expression.";
  359. this->SetError(e.c_str());
  360. return false;
  361. }
  362. r += 2;
  363. }
  364. l = r;
  365. }
  366. // Concatenate all the last arguments together.
  367. std::string input = args[5];
  368. for(unsigned int i=6; i < args.size(); ++i)
  369. {
  370. input += args[i];
  371. }
  372. this->ClearMatches(this->Makefile);
  373. // Compile the regular expression.
  374. cmsys::RegularExpression re;
  375. if(!re.compile(regex.c_str()))
  376. {
  377. std::string e =
  378. "sub-command REGEX, mode REPLACE failed to compile regex \""+
  379. regex+"\".";
  380. this->SetError(e.c_str());
  381. return false;
  382. }
  383. // Scan through the input for all matches.
  384. std::string output;
  385. std::string::size_type base = 0;
  386. while(re.find(input.c_str()+base))
  387. {
  388. this->StoreMatches(this->Makefile, re);
  389. std::string::size_type l2 = re.start();
  390. std::string::size_type r = re.end();
  391. // Concatenate the part of the input that was not matched.
  392. output += input.substr(base, l2);
  393. // Make sure the match had some text.
  394. if(r-l2 == 0)
  395. {
  396. std::string e = "sub-command REGEX, mode REPLACE regex \""+
  397. regex+"\" matched an empty string.";
  398. this->SetError(e.c_str());
  399. return false;
  400. }
  401. // Concatenate the replacement for the match.
  402. for(unsigned int i=0; i < replacement.size(); ++i)
  403. {
  404. if(replacement[i].number < 0)
  405. {
  406. // This is just a plain-text part of the replacement.
  407. output += replacement[i].value;
  408. }
  409. else
  410. {
  411. // Replace with part of the match.
  412. int n = replacement[i].number;
  413. std::string::size_type start = re.start(n);
  414. std::string::size_type end = re.end(n);
  415. std::string::size_type len = input.length()-base;
  416. if((start != std::string::npos) && (end != std::string::npos) &&
  417. (start <= len) && (end <= len))
  418. {
  419. output += input.substr(base+start, end-start);
  420. }
  421. else
  422. {
  423. std::string e =
  424. "sub-command REGEX, mode REPLACE: replace expression \""+
  425. replace+"\" contains an out-of-range escape for regex \""+
  426. regex+"\".";
  427. this->SetError(e.c_str());
  428. return false;
  429. }
  430. }
  431. }
  432. // Move past the match.
  433. base += r;
  434. }
  435. // Concatenate the text after the last match.
  436. output += input.substr(base, input.length()-base);
  437. // Store the output in the provided variable.
  438. this->Makefile->AddDefinition(outvar.c_str(), output.c_str());
  439. return true;
  440. }
  441. //----------------------------------------------------------------------------
  442. void cmStringCommand::ClearMatches(cmMakefile* mf)
  443. {
  444. for (unsigned int i=0; i<10; i++)
  445. {
  446. char name[128];
  447. sprintf(name, "CMAKE_MATCH_%d", i);
  448. mf->AddDefinition(name, "");
  449. mf->MarkVariableAsUsed(name);
  450. }
  451. }
  452. //----------------------------------------------------------------------------
  453. void cmStringCommand::StoreMatches(cmMakefile* mf,cmsys::RegularExpression& re)
  454. {
  455. for (unsigned int i=0; i<10; i++)
  456. {
  457. char name[128];
  458. sprintf(name, "CMAKE_MATCH_%d", i);
  459. mf->AddDefinition(name, re.match(i).c_str());
  460. mf->MarkVariableAsUsed(name);
  461. }
  462. }
  463. //----------------------------------------------------------------------------
  464. bool cmStringCommand::HandleFindCommand(std::vector<std::string> const&
  465. args)
  466. {
  467. // check if all required parameters were passed
  468. if(args.size() < 4 || args.size() > 5)
  469. {
  470. this->SetError("sub-command FIND requires 3 or 4 parameters.");
  471. return false;
  472. }
  473. // check if the reverse flag was set or not
  474. bool reverseMode = false;
  475. if(args.size() == 5 && args[4] == "REVERSE")
  476. {
  477. reverseMode = true;
  478. }
  479. // if we have 5 arguments the last one must be REVERSE
  480. if(args.size() == 5 && args[4] != "REVERSE")
  481. {
  482. this->SetError("sub-command FIND: unknown last parameter");
  483. return false;
  484. }
  485. // local parameter names.
  486. const std::string& sstring = args[1];
  487. const std::string& schar = args[2];
  488. const std::string& outvar = args[3];
  489. // ensure that the user cannot accidentally specify REVERSE as a variable
  490. if(outvar == "REVERSE")
  491. {
  492. this->SetError("sub-command FIND does not allow to select REVERSE as "
  493. "the output variable. "
  494. "Maybe you missed the actual output variable?");
  495. return false;
  496. }
  497. // try to find the character and return its position
  498. size_t pos;
  499. if(!reverseMode)
  500. {
  501. pos = sstring.find(schar);
  502. }
  503. else
  504. {
  505. pos = sstring.rfind(schar);
  506. }
  507. if(std::string::npos != pos)
  508. {
  509. cmOStringStream s;
  510. s << pos;
  511. this->Makefile->AddDefinition(outvar.c_str(), s.str().c_str());
  512. return true;
  513. }
  514. // the character was not found, but this is not really an error
  515. this->Makefile->AddDefinition(outvar.c_str(), "-1");
  516. return true;
  517. }
  518. //----------------------------------------------------------------------------
  519. bool cmStringCommand::HandleCompareCommand(std::vector<std::string> const&
  520. args)
  521. {
  522. if(args.size() < 2)
  523. {
  524. this->SetError("sub-command COMPARE requires a mode to be specified.");
  525. return false;
  526. }
  527. std::string mode = args[1];
  528. if((mode == "EQUAL") || (mode == "NOTEQUAL") ||
  529. (mode == "LESS") || (mode == "GREATER"))
  530. {
  531. if(args.size() < 5)
  532. {
  533. std::string e = "sub-command COMPARE, mode ";
  534. e += mode;
  535. e += " needs at least 5 arguments total to command.";
  536. this->SetError(e.c_str());
  537. return false;
  538. }
  539. const std::string& left = args[2];
  540. const std::string& right = args[3];
  541. const std::string& outvar = args[4];
  542. bool result;
  543. if(mode == "LESS")
  544. {
  545. result = (left < right);
  546. }
  547. else if(mode == "GREATER")
  548. {
  549. result = (left > right);
  550. }
  551. else if(mode == "EQUAL")
  552. {
  553. result = (left == right);
  554. }
  555. else // if(mode == "NOTEQUAL")
  556. {
  557. result = !(left == right);
  558. }
  559. if(result)
  560. {
  561. this->Makefile->AddDefinition(outvar.c_str(), "1");
  562. }
  563. else
  564. {
  565. this->Makefile->AddDefinition(outvar.c_str(), "0");
  566. }
  567. return true;
  568. }
  569. std::string e = "sub-command COMPARE does not recognize mode "+mode;
  570. this->SetError(e.c_str());
  571. return false;
  572. }
  573. //----------------------------------------------------------------------------
  574. bool cmStringCommand::HandleReplaceCommand(std::vector<std::string> const&
  575. args)
  576. {
  577. if(args.size() < 5)
  578. {
  579. this->SetError("sub-command REPLACE requires at least four arguments.");
  580. return false;
  581. }
  582. const std::string& matchExpression = args[1];
  583. const std::string& replaceExpression = args[2];
  584. const std::string& variableName = args[3];
  585. std::string input = args[4];
  586. for(unsigned int i=5; i < args.size(); ++i)
  587. {
  588. input += args[i];
  589. }
  590. cmsys::SystemTools::ReplaceString(input, matchExpression.c_str(),
  591. replaceExpression.c_str());
  592. this->Makefile->AddDefinition(variableName.c_str(), input.c_str());
  593. return true;
  594. }
  595. //----------------------------------------------------------------------------
  596. bool cmStringCommand::HandleSubstringCommand(std::vector<std::string> const&
  597. args)
  598. {
  599. if(args.size() != 5)
  600. {
  601. this->SetError("sub-command SUBSTRING requires four arguments.");
  602. return false;
  603. }
  604. const std::string& stringValue = args[1];
  605. int begin = atoi(args[2].c_str());
  606. int end = atoi(args[3].c_str());
  607. const std::string& variableName = args[4];
  608. size_t stringLength = stringValue.size();
  609. int intStringLength = static_cast<int>(stringLength);
  610. if ( begin < 0 || begin > intStringLength )
  611. {
  612. cmOStringStream ostr;
  613. ostr << "begin index: " << begin << " is out of range 0 - "
  614. << stringLength;
  615. this->SetError(ostr.str().c_str());
  616. return false;
  617. }
  618. int leftOverLength = intStringLength - begin;
  619. if ( end < -1 || end > leftOverLength )
  620. {
  621. cmOStringStream ostr;
  622. ostr << "end index: " << end << " is out of range -1 - "
  623. << leftOverLength;
  624. this->SetError(ostr.str().c_str());
  625. return false;
  626. }
  627. this->Makefile->AddDefinition(variableName.c_str(),
  628. stringValue.substr(begin, end).c_str());
  629. return true;
  630. }
  631. //----------------------------------------------------------------------------
  632. bool cmStringCommand
  633. ::HandleLengthCommand(std::vector<std::string> const& args)
  634. {
  635. if(args.size() != 3)
  636. {
  637. this->SetError("sub-command LENGTH requires two arguments.");
  638. return false;
  639. }
  640. const std::string& stringValue = args[1];
  641. const std::string& variableName = args[2];
  642. size_t length = stringValue.size();
  643. char buffer[1024];
  644. sprintf(buffer, "%d", static_cast<int>(length));
  645. this->Makefile->AddDefinition(variableName.c_str(), buffer);
  646. return true;
  647. }
  648. //----------------------------------------------------------------------------
  649. bool cmStringCommand::HandleStripCommand(
  650. std::vector<std::string> const& args)
  651. {
  652. if(args.size() != 3)
  653. {
  654. this->SetError("sub-command STRIP requires two arguments.");
  655. return false;
  656. }
  657. const std::string& stringValue = args[1];
  658. const std::string& variableName = args[2];
  659. size_t inStringLength = stringValue.size();
  660. size_t startPos = inStringLength + 1;
  661. size_t endPos = 0;
  662. const char* ptr = stringValue.c_str();
  663. size_t cc;
  664. for ( cc = 0; cc < inStringLength; ++ cc )
  665. {
  666. if ( !isspace(*ptr) )
  667. {
  668. if ( startPos > inStringLength )
  669. {
  670. startPos = cc;
  671. }
  672. endPos = cc;
  673. }
  674. ++ ptr;
  675. }
  676. size_t outLength = 0;
  677. // if the input string didn't contain any non-space characters, return
  678. // an empty string
  679. if (startPos > inStringLength)
  680. {
  681. outLength = 0;
  682. startPos = 0;
  683. }
  684. else
  685. {
  686. outLength=endPos - startPos + 1;
  687. }
  688. this->Makefile->AddDefinition(variableName.c_str(),
  689. stringValue.substr(startPos, outLength).c_str());
  690. return true;
  691. }
  692. //----------------------------------------------------------------------------
  693. bool cmStringCommand
  694. ::HandleRandomCommand(std::vector<std::string> const& args)
  695. {
  696. if(args.size() < 2 || args.size() == 3 || args.size() == 5)
  697. {
  698. this->SetError("sub-command RANDOM requires at least one argument.");
  699. return false;
  700. }
  701. static bool seeded = false;
  702. bool force_seed = false;
  703. unsigned int seed = 0;
  704. int length = 5;
  705. const char cmStringCommandDefaultAlphabet[] = "qwertyuiopasdfghjklzxcvbnm"
  706. "QWERTYUIOPASDFGHJKLZXCVBNM"
  707. "0123456789";
  708. std::string alphabet;
  709. if ( args.size() > 3 )
  710. {
  711. size_t i = 1;
  712. size_t stopAt = args.size() - 2;
  713. for ( ; i < stopAt; ++i )
  714. {
  715. if ( args[i] == "LENGTH" )
  716. {
  717. ++i;
  718. length = atoi(args[i].c_str());
  719. }
  720. else if ( args[i] == "ALPHABET" )
  721. {
  722. ++i;
  723. alphabet = args[i];
  724. }
  725. else if ( args[i] == "RANDOM_SEED" )
  726. {
  727. ++i;
  728. seed = static_cast<unsigned int>(atoi(args[i].c_str()));
  729. force_seed = true;
  730. }
  731. }
  732. }
  733. if ( !alphabet.size() )
  734. {
  735. alphabet = cmStringCommandDefaultAlphabet;
  736. }
  737. double sizeofAlphabet = static_cast<double>(alphabet.size());
  738. if ( sizeofAlphabet < 1 )
  739. {
  740. this->SetError("sub-command RANDOM invoked with bad alphabet.");
  741. return false;
  742. }
  743. if ( length < 1 )
  744. {
  745. this->SetError("sub-command RANDOM invoked with bad length.");
  746. return false;
  747. }
  748. const std::string& variableName = args[args.size()-1];
  749. std::vector<char> result;
  750. if (!seeded || force_seed)
  751. {
  752. seeded = true;
  753. srand(force_seed? seed : cmSystemTools::RandomSeed());
  754. }
  755. const char* alphaPtr = alphabet.c_str();
  756. int cc;
  757. for ( cc = 0; cc < length; cc ++ )
  758. {
  759. int idx=(int) (sizeofAlphabet* rand()/(RAND_MAX+1.0));
  760. result.push_back(*(alphaPtr + idx));
  761. }
  762. result.push_back(0);
  763. this->Makefile->AddDefinition(variableName.c_str(), &*result.begin());
  764. return true;
  765. }