cmStringCommand.cxx 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680
  1. /*=========================================================================
  2. Program: CMake - Cross-Platform Makefile Generator
  3. Module: $RCSfile$
  4. Language: C++
  5. Date: $Date$
  6. Version: $Revision$
  7. Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
  8. See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
  9. This software is distributed WITHOUT ANY WARRANTY; without even
  10. the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  11. PURPOSE. See the above copyright notices for more information.
  12. =========================================================================*/
  13. #include "cmStringCommand.h"
  14. #include <cmsys/RegularExpression.hxx>
  15. #include <cmsys/SystemTools.hxx>
  16. #include <stdlib.h> // required for atoi
  17. #include <ctype.h>
  18. //----------------------------------------------------------------------------
  19. bool cmStringCommand::InitialPass(std::vector<std::string> const& args)
  20. {
  21. if(args.size() < 1)
  22. {
  23. this->SetError("must be called with at least one argument.");
  24. return false;
  25. }
  26. const std::string &subCommand = args[0];
  27. if(subCommand == "REGEX")
  28. {
  29. return this->HandleRegexCommand(args);
  30. }
  31. else if(subCommand == "REPLACE")
  32. {
  33. return this->HandleReplaceCommand(args);
  34. }
  35. else if(subCommand == "TOLOWER")
  36. {
  37. return this->HandleToUpperLowerCommand(args, false);
  38. }
  39. else if(subCommand == "TOUPPER")
  40. {
  41. return this->HandleToUpperLowerCommand(args, true);
  42. }
  43. else if(subCommand == "COMPARE")
  44. {
  45. return this->HandleCompareCommand(args);
  46. }
  47. else if(subCommand == "ASCII")
  48. {
  49. return this->HandleAsciiCommand(args);
  50. }
  51. else if(subCommand == "CONFIGURE")
  52. {
  53. return this->HandleConfigureCommand(args);
  54. }
  55. else if(subCommand == "LENGTH")
  56. {
  57. return this->HandleLengthCommand(args);
  58. }
  59. else if(subCommand == "SUBSTRING")
  60. {
  61. return this->HandleSubstringCommand(args);
  62. }
  63. else if(subCommand == "RANDOM")
  64. {
  65. return this->HandleRandomCommand(args);
  66. }
  67. std::string e = "does not recognize sub-command "+subCommand;
  68. this->SetError(e.c_str());
  69. return false;
  70. }
  71. //----------------------------------------------------------------------------
  72. bool cmStringCommand::HandleToUpperLowerCommand(
  73. std::vector<std::string> const& args, bool toUpper)
  74. {
  75. if ( args.size() < 3 )
  76. {
  77. this->SetError("no output variable specified");
  78. return false;
  79. }
  80. std::string outvar = args[2];
  81. std::string output;
  82. if (toUpper)
  83. {
  84. output = cmSystemTools::UpperCase(args[1]);
  85. }
  86. else
  87. {
  88. output = cmSystemTools::LowerCase(args[1]);
  89. }
  90. // Store the output in the provided variable.
  91. this->Makefile->AddDefinition(outvar.c_str(), output.c_str());
  92. return true;
  93. }
  94. //----------------------------------------------------------------------------
  95. bool cmStringCommand::HandleAsciiCommand(std::vector<std::string> const& args)
  96. {
  97. if ( args.size() < 3 )
  98. {
  99. this->SetError("No output variable specified");
  100. return false;
  101. }
  102. std::string::size_type cc;
  103. std::string outvar = args[args.size()-1];
  104. std::string output = "";
  105. for ( cc = 1; cc < args.size()-1; cc ++ )
  106. {
  107. int ch = atoi(args[cc].c_str());
  108. if ( ch > 0 && ch < 256 )
  109. {
  110. output += static_cast<char>(ch);
  111. }
  112. else
  113. {
  114. std::string error = "Character with code ";
  115. error += ch;
  116. error += " does not exist.";
  117. this->SetError(error.c_str());
  118. return false;
  119. }
  120. }
  121. // Store the output in the provided variable.
  122. this->Makefile->AddDefinition(outvar.c_str(), output.c_str());
  123. return true;
  124. }
  125. //----------------------------------------------------------------------------
  126. bool cmStringCommand::HandleConfigureCommand(
  127. std::vector<std::string> const& args)
  128. {
  129. if ( args.size() < 2 )
  130. {
  131. this->SetError("No input string specified.");
  132. return false;
  133. }
  134. else if ( args.size() < 3 )
  135. {
  136. this->SetError("No output variable specified.");
  137. return false;
  138. }
  139. // Parse options.
  140. bool escapeQuotes = false;
  141. bool atOnly = false;
  142. for(unsigned int i = 3; i < args.size(); ++i)
  143. {
  144. if(args[i] == "@ONLY")
  145. {
  146. atOnly = true;
  147. }
  148. else if(args[i] == "ESCAPE_QUOTES")
  149. {
  150. escapeQuotes = true;
  151. }
  152. else
  153. {
  154. cmOStringStream err;
  155. err << "Unrecognized argument \"" << args[i] << "\"";
  156. this->SetError(err.str().c_str());
  157. return false;
  158. }
  159. }
  160. // Configure the string.
  161. std::string output;
  162. this->Makefile->ConfigureString(args[1], output, atOnly, escapeQuotes);
  163. // Store the output in the provided variable.
  164. this->Makefile->AddDefinition(args[2].c_str(), output.c_str());
  165. return true;
  166. }
  167. //----------------------------------------------------------------------------
  168. bool cmStringCommand::HandleRegexCommand(std::vector<std::string> const& args)
  169. {
  170. if(args.size() < 2)
  171. {
  172. this->SetError("sub-command REGEX requires a mode to be specified.");
  173. return false;
  174. }
  175. std::string mode = args[1];
  176. if(mode == "MATCH")
  177. {
  178. if(args.size() < 5)
  179. {
  180. this->SetError("sub-command REGEX, mode MATCH needs "
  181. "at least 5 arguments total to command.");
  182. return false;
  183. }
  184. return this->RegexMatch(args);
  185. }
  186. else if(mode == "MATCHALL")
  187. {
  188. if(args.size() < 5)
  189. {
  190. this->SetError("sub-command REGEX, mode MATCHALL needs "
  191. "at least 5 arguments total to command.");
  192. return false;
  193. }
  194. return this->RegexMatchAll(args);
  195. }
  196. else if(mode == "REPLACE")
  197. {
  198. if(args.size() < 6)
  199. {
  200. this->SetError("sub-command REGEX, mode MATCH needs "
  201. "at least 6 arguments total to command.");
  202. return false;
  203. }
  204. return this->RegexReplace(args);
  205. }
  206. std::string e = "sub-command REGEX does not recognize mode "+mode;
  207. this->SetError(e.c_str());
  208. return false;
  209. }
  210. //----------------------------------------------------------------------------
  211. bool cmStringCommand::RegexMatch(std::vector<std::string> const& args)
  212. {
  213. //"STRING(REGEX MATCH <regular_expression> <output variable>
  214. // <input> [<input>...])\n";
  215. std::string regex = args[2];
  216. std::string outvar = args[3];
  217. // Concatenate all the last arguments together.
  218. std::string input = args[4];
  219. for(unsigned int i=5; i < args.size(); ++i)
  220. {
  221. input += args[i];
  222. }
  223. // Compile the regular expression.
  224. cmsys::RegularExpression re;
  225. if(!re.compile(regex.c_str()))
  226. {
  227. std::string e =
  228. "sub-command REGEX, mode MATCH failed to compile regex \""+regex+"\".";
  229. this->SetError(e.c_str());
  230. return false;
  231. }
  232. // Scan through the input for all matches.
  233. std::string output;
  234. if(re.find(input.c_str()))
  235. {
  236. std::string::size_type l = re.start();
  237. std::string::size_type r = re.end();
  238. if(r-l == 0)
  239. {
  240. std::string e =
  241. "sub-command REGEX, mode MATCH regex \""+regex+
  242. "\" matched an empty string.";
  243. this->SetError(e.c_str());
  244. return false;
  245. }
  246. output = input.substr(l, r-l);
  247. }
  248. // Store the output in the provided variable.
  249. this->Makefile->AddDefinition(outvar.c_str(), output.c_str());
  250. return true;
  251. }
  252. //----------------------------------------------------------------------------
  253. bool cmStringCommand::RegexMatchAll(std::vector<std::string> const& args)
  254. {
  255. //"STRING(REGEX MATCHALL <regular_expression> <output variable> <input>
  256. // [<input>...])\n";
  257. std::string regex = args[2];
  258. std::string outvar = args[3];
  259. // Concatenate all the last arguments together.
  260. std::string input = args[4];
  261. for(unsigned int i=5; i < args.size(); ++i)
  262. {
  263. input += args[i];
  264. }
  265. // Compile the regular expression.
  266. cmsys::RegularExpression re;
  267. if(!re.compile(regex.c_str()))
  268. {
  269. std::string e =
  270. "sub-command REGEX, mode MATCHALL failed to compile regex \""+
  271. regex+"\".";
  272. this->SetError(e.c_str());
  273. return false;
  274. }
  275. // Scan through the input for all matches.
  276. std::string output;
  277. const char* p = input.c_str();
  278. while(re.find(p))
  279. {
  280. std::string::size_type l = re.start();
  281. std::string::size_type r = re.end();
  282. if(r-l == 0)
  283. {
  284. std::string e = "sub-command REGEX, mode MATCHALL regex \""+
  285. regex+"\" matched an empty string.";
  286. this->SetError(e.c_str());
  287. return false;
  288. }
  289. if(output.length() > 0)
  290. {
  291. output += ";";
  292. }
  293. output += std::string(p+l, r-l);
  294. p += r;
  295. }
  296. // Store the output in the provided variable.
  297. this->Makefile->AddDefinition(outvar.c_str(), output.c_str());
  298. return true;
  299. }
  300. //----------------------------------------------------------------------------
  301. bool cmStringCommand::RegexReplace(std::vector<std::string> const& args)
  302. {
  303. //"STRING(REGEX REPLACE <regular_expression> <replace_expression>
  304. // <output variable> <input> [<input>...])\n"
  305. std::string regex = args[2];
  306. std::string replace = args[3];
  307. std::string outvar = args[4];
  308. // Pull apart the replace expression to find the escaped [0-9] values.
  309. std::vector<RegexReplacement> replacement;
  310. std::string::size_type l = 0;
  311. while(l < replace.length())
  312. {
  313. std::string::size_type r = replace.find("\\", l);
  314. if(r == std::string::npos)
  315. {
  316. r = replace.length();
  317. replacement.push_back(replace.substr(l, r-l));
  318. }
  319. else
  320. {
  321. if(r-l > 0)
  322. {
  323. replacement.push_back(replace.substr(l, r-l));
  324. }
  325. if(r == (replace.length()-1))
  326. {
  327. this->SetError("sub-command REGEX, mode REPLACE: "
  328. "replace-expression ends in a backslash.");
  329. return false;
  330. }
  331. if((replace[r+1] >= '0') && (replace[r+1] <= '9'))
  332. {
  333. replacement.push_back(replace[r+1]-'0');
  334. }
  335. else if(replace[r+1] == 'n')
  336. {
  337. replacement.push_back("\n");
  338. }
  339. else if(replace[r+1] == '\\')
  340. {
  341. replacement.push_back("\\");
  342. }
  343. else
  344. {
  345. std::string e = "sub-command REGEX, mode REPLACE: Unknown escape \"";
  346. e += replace.substr(r, 2);
  347. e += "\"in replace-expression.";
  348. this->SetError(e.c_str());
  349. return false;
  350. }
  351. r += 2;
  352. }
  353. l = r;
  354. }
  355. // Concatenate all the last arguments together.
  356. std::string input = args[5];
  357. for(unsigned int i=6; i < args.size(); ++i)
  358. {
  359. input += args[i];
  360. }
  361. // Compile the regular expression.
  362. cmsys::RegularExpression re;
  363. if(!re.compile(regex.c_str()))
  364. {
  365. std::string e =
  366. "sub-command REGEX, mode REPLACE failed to compile regex \""+
  367. regex+"\".";
  368. this->SetError(e.c_str());
  369. return false;
  370. }
  371. // Scan through the input for all matches.
  372. std::string output;
  373. std::string::size_type base = 0;
  374. while(re.find(input.c_str()+base))
  375. {
  376. std::string::size_type l2 = re.start();
  377. std::string::size_type r = re.end();
  378. // Concatenate the part of the input that was not matched.
  379. output += input.substr(base, l2);
  380. // Make sure the match had some text.
  381. if(r-l2 == 0)
  382. {
  383. std::string e = "sub-command REGEX, mode REPLACE regex \""+
  384. regex+"\" matched an empty string.";
  385. this->SetError(e.c_str());
  386. return false;
  387. }
  388. // Concatenate the replacement for the match.
  389. for(unsigned int i=0; i < replacement.size(); ++i)
  390. {
  391. if(replacement[i].number < 0)
  392. {
  393. // This is just a plain-text part of the replacement.
  394. output += replacement[i].value;
  395. }
  396. else
  397. {
  398. // Replace with part of the match.
  399. int n = replacement[i].number;
  400. std::string::size_type start = re.start(n);
  401. std::string::size_type end = re.end(n);
  402. std::string::size_type len = input.length()-base;
  403. if((start != std::string::npos) && (end != std::string::npos) &&
  404. (start <= len) && (end <= len))
  405. {
  406. output += input.substr(base+start, end-start);
  407. }
  408. else
  409. {
  410. std::string e =
  411. "sub-command REGEX, mode REPLACE: replace expression \""+
  412. replace+"\" contains an out-of-range escape for regex \""+
  413. regex+"\".";
  414. this->SetError(e.c_str());
  415. return false;
  416. }
  417. }
  418. }
  419. // Move past the match.
  420. base += r;
  421. }
  422. // Concatenate the text after the last match.
  423. output += input.substr(base, input.length()-base);
  424. // Store the output in the provided variable.
  425. this->Makefile->AddDefinition(outvar.c_str(), output.c_str());
  426. return true;
  427. }
  428. //----------------------------------------------------------------------------
  429. bool cmStringCommand::HandleCompareCommand(std::vector<std::string> const&
  430. args)
  431. {
  432. if(args.size() < 2)
  433. {
  434. this->SetError("sub-command COMPARE requires a mode to be specified.");
  435. return false;
  436. }
  437. std::string mode = args[1];
  438. if((mode == "EQUAL") || (mode == "NOTEQUAL") ||
  439. (mode == "LESS") || (mode == "GREATER"))
  440. {
  441. if(args.size() < 5)
  442. {
  443. std::string e = "sub-command COMPARE, mode ";
  444. e += mode;
  445. e += " needs at least 5 arguments total to command.";
  446. this->SetError(e.c_str());
  447. return false;
  448. }
  449. const std::string& left = args[2];
  450. const std::string& right = args[3];
  451. const std::string& outvar = args[4];
  452. bool result;
  453. if(mode == "LESS")
  454. {
  455. result = (left < right);
  456. }
  457. else if(mode == "GREATER")
  458. {
  459. result = (left > right);
  460. }
  461. else if(mode == "EQUAL")
  462. {
  463. result = (left == right);
  464. }
  465. else // if(mode == "NOTEQUAL")
  466. {
  467. result = !(left == right);
  468. }
  469. if(result)
  470. {
  471. this->Makefile->AddDefinition(outvar.c_str(), "1");
  472. }
  473. else
  474. {
  475. this->Makefile->AddDefinition(outvar.c_str(), "0");
  476. }
  477. return true;
  478. }
  479. std::string e = "sub-command COMPARE does not recognize mode "+mode;
  480. this->SetError(e.c_str());
  481. return false;
  482. }
  483. //----------------------------------------------------------------------------
  484. bool cmStringCommand::HandleReplaceCommand(std::vector<std::string> const&
  485. args)
  486. {
  487. if(args.size() < 5)
  488. {
  489. this->SetError("sub-command REPLACE requires four arguments.");
  490. return false;
  491. }
  492. const std::string& matchExpression = args[1];
  493. const std::string& replaceExpression = args[2];
  494. const std::string& variableName = args[3];
  495. std::string input = args[4];
  496. for(unsigned int i=5; i < args.size(); ++i)
  497. {
  498. input += args[i];
  499. }
  500. cmsys::SystemTools::ReplaceString(input, matchExpression.c_str(),
  501. replaceExpression.c_str());
  502. this->Makefile->AddDefinition(variableName.c_str(), input.c_str());
  503. return true;
  504. }
  505. //----------------------------------------------------------------------------
  506. bool cmStringCommand::HandleSubstringCommand(std::vector<std::string> const&
  507. args)
  508. {
  509. if(args.size() != 5)
  510. {
  511. this->SetError("sub-command REPLACE requires four arguments.");
  512. return false;
  513. }
  514. const std::string& stringValue = args[1];
  515. int begin = atoi(args[2].c_str());
  516. int end = atoi(args[3].c_str());
  517. const std::string& variableName = args[4];
  518. size_t stringLength = stringValue.size();
  519. int intStringLength = static_cast<int>(stringLength);
  520. if ( begin < 0 || begin > intStringLength )
  521. {
  522. cmOStringStream ostr;
  523. ostr << "begin index: " << begin << " is out of range 0 - "
  524. << stringLength;
  525. this->SetError(ostr.str().c_str());
  526. return false;
  527. }
  528. int leftOverLength = intStringLength - begin;
  529. if ( end < 0 || end > leftOverLength )
  530. {
  531. cmOStringStream ostr;
  532. ostr << "end index: " << end << " is out of range " << 0 << " - "
  533. << leftOverLength;
  534. this->SetError(ostr.str().c_str());
  535. return false;
  536. }
  537. this->Makefile->AddDefinition(variableName.c_str(),
  538. stringValue.substr(begin, end).c_str());
  539. return true;
  540. }
  541. //----------------------------------------------------------------------------
  542. bool cmStringCommand
  543. ::HandleLengthCommand(std::vector<std::string> const& args)
  544. {
  545. if(args.size() != 3)
  546. {
  547. this->SetError("sub-command LENGTH requires two arguments.");
  548. return false;
  549. }
  550. const std::string& stringValue = args[1];
  551. const std::string& variableName = args[2];
  552. size_t length = stringValue.size();
  553. char buffer[1024];
  554. sprintf(buffer, "%d", static_cast<int>(length));
  555. this->Makefile->AddDefinition(variableName.c_str(), buffer);
  556. return true;
  557. }
  558. //----------------------------------------------------------------------------
  559. bool cmStringCommand
  560. ::HandleRandomCommand(std::vector<std::string> const& args)
  561. {
  562. if(args.size() < 2 || args.size() == 3 || args.size() == 5)
  563. {
  564. this->SetError("sub-command RANDOM requires at least one argument.");
  565. return false;
  566. }
  567. int length = 5;
  568. const char cmStringCommandDefaultAlphabet[] = "qwertyuiopasdfghjklzxcvbnm"
  569. "QWERTYUIOPASDFGHJKLZXCVBNM"
  570. "0123456789";
  571. std::string alphabet;
  572. if ( args.size() > 3 )
  573. {
  574. size_t i = 1;
  575. size_t stopAt = args.size() - 2;
  576. for ( ; i < stopAt; ++i )
  577. {
  578. if ( args[i] == "LENGTH" )
  579. {
  580. ++i;
  581. length = atoi(args[i].c_str());
  582. }
  583. else if ( args[i] == "ALPHABET" )
  584. {
  585. ++i;
  586. alphabet = args[i];
  587. }
  588. }
  589. }
  590. if ( !alphabet.size() )
  591. {
  592. alphabet = cmStringCommandDefaultAlphabet;
  593. }
  594. double sizeofAlphabet = alphabet.size();
  595. if ( sizeofAlphabet < 1 )
  596. {
  597. this->SetError("sub-command RANDOM invoked with bad alphabet.");
  598. return false;
  599. }
  600. if ( length < 1 )
  601. {
  602. this->SetError("sub-command RANDOM invoked with bad length.");
  603. return false;
  604. }
  605. const std::string& variableName = args[args.size()-1];
  606. std::vector<char> result;
  607. srand((int)time(NULL));
  608. const char* alphaPtr = alphabet.c_str();
  609. int cc;
  610. for ( cc = 0; cc < length; cc ++ )
  611. {
  612. int idx=(int) (sizeofAlphabet* rand()/(RAND_MAX+1.0));
  613. result.push_back(*(alphaPtr + idx));
  614. }
  615. result.push_back(0);
  616. this->Makefile->AddDefinition(variableName.c_str(), &*result.begin());
  617. return true;
  618. }