You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

751 lines
20 KiB

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