cmCMakeString.cxx 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file LICENSE.rst or https://cmake.org/licensing for details. */
  3. #include "cmConfigure.h" // IWYU pragma: keep
  4. #include "cmCMakeString.hxx"
  5. #include <cstdio>
  6. #include <cstdlib>
  7. #include <memory>
  8. #include <stdexcept>
  9. #include <vector>
  10. #include "cmsys/RegularExpression.hxx"
  11. #include "cmCryptoHash.h"
  12. #include "cmGeneratorExpression.h"
  13. #include "cmMakefile.h"
  14. #include "cmPolicies.h"
  15. #include "cmStringAlgorithms.h"
  16. #include "cmStringReplaceHelper.h"
  17. #include "cmSystemTools.h"
  18. #include "cmTimestamp.h"
  19. #include "cmUuid.h"
  20. namespace cm {
  21. bool CMakeString::Compare(CompOperator op, cm::string_view other)
  22. {
  23. switch (op) {
  24. case CompOperator::EQUAL:
  25. return this->String_ == other;
  26. case CompOperator::LESS:
  27. return this->String_ < other;
  28. case CompOperator::LESS_EQUAL:
  29. return this->String_ <= other;
  30. case CompOperator::GREATER:
  31. return this->String_ > other;
  32. case CompOperator::GREATER_EQUAL:
  33. return this->String_ >= other;
  34. default:
  35. return false;
  36. }
  37. }
  38. CMakeString& CMakeString::Replace(std::string const& matchExpression,
  39. std::string const& replaceExpression,
  40. Regex regex, cmMakefile* makefile)
  41. {
  42. if (regex == Regex::Yes) {
  43. if (makefile) {
  44. makefile->ClearMatches();
  45. }
  46. cmStringReplaceHelper replaceHelper(matchExpression, replaceExpression,
  47. makefile);
  48. if (!replaceHelper.IsReplaceExpressionValid()) {
  49. throw std::invalid_argument(replaceHelper.GetError());
  50. }
  51. if (!replaceHelper.IsRegularExpressionValid()) {
  52. throw std::invalid_argument(
  53. cmStrCat("Failed to compile regex \"", matchExpression, '"'));
  54. }
  55. std ::string output;
  56. if (!replaceHelper.Replace(this->String_, output)) {
  57. throw std::runtime_error(replaceHelper.GetError());
  58. }
  59. this->String_ = std::move(output);
  60. } else {
  61. std::string output = this->String_.str();
  62. cmsys::SystemTools::ReplaceString(output, matchExpression,
  63. replaceExpression);
  64. this->String_ = std::move(output);
  65. }
  66. return *this;
  67. };
  68. cmList CMakeString::Match(std::string const& matchExpression,
  69. MatchItems matchItems, cmMakefile* makefile) const
  70. {
  71. if (makefile) {
  72. makefile->ClearMatches();
  73. }
  74. // Compile the regular expression.
  75. cmsys::RegularExpression re;
  76. if (!re.compile(matchExpression)) {
  77. throw std::invalid_argument(
  78. cmStrCat("Failed to compile regex \"", matchExpression, '"'));
  79. }
  80. cmList output;
  81. if (matchItems == MatchItems::Once) {
  82. if (re.find(this->String_.data())) {
  83. if (makefile) {
  84. makefile->StoreMatches(re);
  85. }
  86. output = re.match();
  87. }
  88. } else {
  89. unsigned optAnchor = 0;
  90. if (makefile &&
  91. makefile->GetPolicyStatus(cmPolicies::CMP0186) != cmPolicies::NEW) {
  92. optAnchor = cmsys::RegularExpression::BOL_AT_OFFSET;
  93. }
  94. // Scan through the input for all matches.
  95. std::string::size_type base = 0;
  96. unsigned optNonEmpty = 0;
  97. while (re.find(this->String_.data(), base, optAnchor | optNonEmpty)) {
  98. if (makefile) {
  99. makefile->ClearMatches();
  100. makefile->StoreMatches(re);
  101. }
  102. output.push_back(re.match());
  103. base = re.end();
  104. if (re.start() == this->String_.length()) {
  105. break;
  106. }
  107. if (re.start() == re.end()) {
  108. optNonEmpty = cmsys::RegularExpression::NONEMPTY_AT_OFFSET;
  109. } else {
  110. optNonEmpty = 0;
  111. }
  112. }
  113. }
  114. return output;
  115. }
  116. CMakeString CMakeString::Substring(long begin, long count) const
  117. {
  118. if (begin < 0 || static_cast<size_type>(begin) > this->String_.size()) {
  119. throw std::out_of_range(cmStrCat(
  120. "begin index: ", begin, " is out of range 0 - ", this->String_.size()));
  121. }
  122. if (count < -1) {
  123. throw std::out_of_range(
  124. cmStrCat("end index: ", count, " should be -1 or greater"));
  125. }
  126. return this->String_.substr(static_cast<size_type>(begin),
  127. count == -1 ? npos
  128. : static_cast<size_type>(count));
  129. }
  130. CMakeString& CMakeString::Strip(StripItems stripItems)
  131. {
  132. if (stripItems == StripItems::Space) {
  133. this->String_ = cmTrimWhitespace(this->String_);
  134. } else {
  135. this->String_ = cmGeneratorExpression::Preprocess(
  136. this->String_, cmGeneratorExpression::StripAllGeneratorExpressions);
  137. }
  138. return *this;
  139. }
  140. CMakeString& CMakeString::Repeat(size_type count)
  141. {
  142. switch (this->Size()) {
  143. case 0u:
  144. // Nothing to do for zero length input strings
  145. break;
  146. case 1u:
  147. // NOTE If the string to repeat consists of the only character,
  148. // use the appropriate constructor.
  149. this->String_ = std::string(count, this->String_[0]);
  150. break;
  151. default:
  152. std::string result;
  153. auto size = this->Size();
  154. result.reserve(size * count);
  155. for (auto i = 0u; i < count; ++i) {
  156. result.insert(i * size, this->String_.data(), size);
  157. }
  158. this->String_ = std::move(result);
  159. break;
  160. }
  161. return *this;
  162. }
  163. CMakeString& CMakeString::Quote(QuoteItems)
  164. {
  165. std ::string output;
  166. // Escape all regex special characters
  167. cmStringReplaceHelper replaceHelper("([][()+*^.$?|\\\\])", R"(\\\1)");
  168. if (!replaceHelper.Replace(this->String_, output)) {
  169. throw std::runtime_error(replaceHelper.GetError());
  170. }
  171. this->String_ = std::move(output);
  172. return *this;
  173. }
  174. CMakeString& CMakeString::Hash(cm::string_view hashAlgorithm)
  175. {
  176. std::unique_ptr<cmCryptoHash> hash(cmCryptoHash::New(hashAlgorithm));
  177. if (hash) {
  178. this->String_ = hash->HashString(this->String_);
  179. return *this;
  180. }
  181. throw std::invalid_argument(
  182. cmStrCat(hashAlgorithm, ": invalid hash algorithm."));
  183. }
  184. CMakeString& CMakeString::FromASCII(string_range codes)
  185. {
  186. std::string output;
  187. output.reserve(codes.size());
  188. for (auto const& code : codes) {
  189. try {
  190. auto ch = std::stoi(code);
  191. if (ch > 0 && ch < 256) {
  192. output += static_cast<char>(ch);
  193. } else {
  194. throw std::invalid_argument(
  195. cmStrCat("Character with code ", code, " does not exist."));
  196. }
  197. } catch (...) {
  198. throw std::invalid_argument(
  199. cmStrCat("Character with code ", code, " does not exist."));
  200. }
  201. }
  202. this->String_ = std::move(output);
  203. return *this;
  204. }
  205. CMakeString& CMakeString::ToHexadecimal(cm::string_view str)
  206. {
  207. std::string output(str.size() * 2, ' ');
  208. std::string::size_type hexIndex = 0;
  209. for (auto const& c : str) {
  210. std::snprintf(&output[hexIndex], 3, "%.2x", c & 0xFFu);
  211. hexIndex += 2;
  212. }
  213. this->String_ = output;
  214. return *this;
  215. }
  216. cm::string_view const CMakeString::RandomDefaultAlphabet{
  217. "qwertyuiopasdfghjklzxcvbnm"
  218. "QWERTYUIOPASDFGHJKLZXCVBNM"
  219. "0123456789"
  220. };
  221. bool CMakeString::Seeded = false;
  222. CMakeString& CMakeString::Random(unsigned int seed, std::size_t length,
  223. cm::string_view alphabet)
  224. {
  225. if (alphabet.empty()) {
  226. alphabet = RandomDefaultAlphabet;
  227. }
  228. if (length < 1) {
  229. throw std::out_of_range("Invoked with bad length.");
  230. }
  231. if (!this->Seeded) {
  232. this->Seeded = true;
  233. std::srand(seed);
  234. }
  235. double alphabetSize = static_cast<double>(alphabet.size());
  236. std::vector<char> result;
  237. result.reserve(length + 1);
  238. for (std::size_t i = 0; i < length; i++) {
  239. auto index = static_cast<std::string::size_type>(
  240. alphabetSize * std::rand() / (RAND_MAX + 1.0));
  241. result.push_back(alphabet[index]);
  242. }
  243. result.push_back(0);
  244. this->String_ = result.data();
  245. return *this;
  246. }
  247. CMakeString& CMakeString::Timestamp(cm::string_view format, UTC utc)
  248. {
  249. cmTimestamp timestamp;
  250. this->String_ = timestamp.CurrentTime(format, utc == UTC::Yes);
  251. return *this;
  252. }
  253. CMakeString& CMakeString::UUID(cm::string_view nameSpace, cm::string_view name,
  254. UUIDType type, Case uuidCase)
  255. {
  256. #if !defined(CMAKE_BOOTSTRAP)
  257. cmUuid uuidGenerator;
  258. std::vector<unsigned char> uuidNamespace;
  259. std::string uuid;
  260. if (!uuidGenerator.StringToBinary(nameSpace, uuidNamespace)) {
  261. throw std::invalid_argument("malformed NAMESPACE UUID");
  262. }
  263. if (type == UUIDType::MD5) {
  264. uuid = uuidGenerator.FromMd5(uuidNamespace, name);
  265. } else if (type == UUIDType::SHA1) {
  266. uuid = uuidGenerator.FromSha1(uuidNamespace, name);
  267. }
  268. if (uuid.empty()) {
  269. throw std::runtime_error("generation failed");
  270. }
  271. if (uuidCase == Case::Upper) {
  272. uuid = cmSystemTools::UpperCase(uuid);
  273. }
  274. this->String_ = std::move(uuid);
  275. return *this;
  276. #else
  277. throw std::runtime_error("not available during bootstrap");
  278. #endif
  279. }
  280. }