cmStringAlgorithms.cxx 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmStringAlgorithms.h"
  4. #include <algorithm>
  5. #include <cerrno>
  6. #include <cstddef> // IWYU pragma: keep
  7. #include <cstdio>
  8. #include <cstdlib>
  9. #include <iterator>
  10. std::string cmTrimWhitespace(cm::string_view str)
  11. {
  12. // XXX(clang-tidy): This declaration and the next cannot be `const auto*`
  13. // because the qualification of `auto` is platform-dependent.
  14. // NOLINTNEXTLINE(readability-qualified-auto)
  15. auto start = str.begin();
  16. while (start != str.end() && cmIsSpace(*start)) {
  17. ++start;
  18. }
  19. if (start == str.end()) {
  20. return std::string();
  21. }
  22. // NOLINTNEXTLINE(readability-qualified-auto)
  23. auto stop = str.end() - 1;
  24. while (cmIsSpace(*stop)) {
  25. --stop;
  26. }
  27. return std::string(start, stop + 1);
  28. }
  29. std::string cmRemoveQuotes(cm::string_view str)
  30. {
  31. // We process only strings that have two quotes at least.
  32. // Also front() and back() are only defined behavior on non empty strings.
  33. if (str.size() >= 2 && //
  34. str.front() == '"' && //
  35. str.back() == '"') {
  36. // Remove a quote from the front and back
  37. str.remove_prefix(1);
  38. str.remove_suffix(1);
  39. }
  40. return std::string(str);
  41. }
  42. std::string cmEscapeQuotes(cm::string_view str)
  43. {
  44. std::string result;
  45. result.reserve(str.size());
  46. for (const char ch : str) {
  47. if (ch == '"') {
  48. result += '\\';
  49. }
  50. result += ch;
  51. }
  52. return result;
  53. }
  54. std::vector<std::string> cmTokenize(cm::string_view str, cm::string_view sep)
  55. {
  56. std::vector<std::string> tokens;
  57. cm::string_view::size_type tokend = 0;
  58. do {
  59. cm::string_view::size_type tokstart = str.find_first_not_of(sep, tokend);
  60. if (tokstart == cm::string_view::npos) {
  61. break; // no more tokens
  62. }
  63. tokend = str.find_first_of(sep, tokstart);
  64. if (tokend == cm::string_view::npos) {
  65. tokens.emplace_back(str.substr(tokstart));
  66. } else {
  67. tokens.emplace_back(str.substr(tokstart, tokend - tokstart));
  68. }
  69. } while (tokend != cm::string_view::npos);
  70. if (tokens.empty()) {
  71. tokens.emplace_back();
  72. }
  73. return tokens;
  74. }
  75. void cmExpandList(cm::string_view arg, std::vector<std::string>& argsOut,
  76. bool emptyArgs)
  77. {
  78. // If argument is empty, it is an empty list.
  79. if (!emptyArgs && arg.empty()) {
  80. return;
  81. }
  82. // if there are no ; in the name then just copy the current string
  83. if (arg.find(';') == cm::string_view::npos) {
  84. argsOut.emplace_back(arg);
  85. return;
  86. }
  87. std::string newArg;
  88. // Break the string at non-escaped semicolons not nested in [].
  89. int squareNesting = 0;
  90. cm::string_view::iterator last = arg.begin();
  91. cm::string_view::iterator const cend = arg.end();
  92. for (cm::string_view::iterator c = last; c != cend; ++c) {
  93. switch (*c) {
  94. case '\\': {
  95. // We only want to allow escaping of semicolons. Other
  96. // escapes should not be processed here.
  97. cm::string_view::iterator cnext = c + 1;
  98. if ((cnext != cend) && *cnext == ';') {
  99. newArg.append(last, c);
  100. // Skip over the escape character
  101. last = cnext;
  102. c = cnext;
  103. }
  104. } break;
  105. case '[': {
  106. ++squareNesting;
  107. } break;
  108. case ']': {
  109. --squareNesting;
  110. } break;
  111. case ';': {
  112. // Break the string here if we are not nested inside square
  113. // brackets.
  114. if (squareNesting == 0) {
  115. newArg.append(last, c);
  116. // Skip over the semicolon
  117. last = c + 1;
  118. if (!newArg.empty() || emptyArgs) {
  119. // Add the last argument if the string is not empty.
  120. argsOut.push_back(newArg);
  121. newArg.clear();
  122. }
  123. }
  124. } break;
  125. default: {
  126. // Just append this character.
  127. } break;
  128. }
  129. }
  130. newArg.append(last, cend);
  131. if (!newArg.empty() || emptyArgs) {
  132. // Add the last argument if the string is not empty.
  133. argsOut.push_back(std::move(newArg));
  134. }
  135. }
  136. std::vector<std::string> cmExpandedList(cm::string_view arg, bool emptyArgs)
  137. {
  138. std::vector<std::string> argsOut;
  139. cmExpandList(arg, argsOut, emptyArgs);
  140. return argsOut;
  141. }
  142. namespace {
  143. template <std::size_t N, typename T>
  144. inline void MakeDigits(cm::string_view& view, char (&digits)[N],
  145. const char* pattern, T value)
  146. {
  147. int res = std::snprintf(digits, N, pattern, value);
  148. if (res > 0 && res < static_cast<int>(N)) {
  149. view = cm::string_view(digits, static_cast<std::size_t>(res));
  150. }
  151. }
  152. } // unnamed namespace
  153. cmAlphaNum::cmAlphaNum(int val)
  154. {
  155. MakeDigits(this->View_, this->Digits_, "%i", val);
  156. }
  157. cmAlphaNum::cmAlphaNum(unsigned int val)
  158. {
  159. MakeDigits(this->View_, this->Digits_, "%u", val);
  160. }
  161. cmAlphaNum::cmAlphaNum(long int val)
  162. {
  163. MakeDigits(this->View_, this->Digits_, "%li", val);
  164. }
  165. cmAlphaNum::cmAlphaNum(unsigned long int val)
  166. {
  167. MakeDigits(this->View_, this->Digits_, "%lu", val);
  168. }
  169. cmAlphaNum::cmAlphaNum(long long int val)
  170. {
  171. MakeDigits(this->View_, this->Digits_, "%lli", val);
  172. }
  173. cmAlphaNum::cmAlphaNum(unsigned long long int val)
  174. {
  175. MakeDigits(this->View_, this->Digits_, "%llu", val);
  176. }
  177. cmAlphaNum::cmAlphaNum(float val)
  178. {
  179. MakeDigits(this->View_, this->Digits_, "%g", static_cast<double>(val));
  180. }
  181. cmAlphaNum::cmAlphaNum(double val)
  182. {
  183. MakeDigits(this->View_, this->Digits_, "%g", val);
  184. }
  185. std::string cmCatViews(std::initializer_list<cm::string_view> views)
  186. {
  187. std::size_t total_size = 0;
  188. for (cm::string_view const& view : views) {
  189. total_size += view.size();
  190. }
  191. std::string result(total_size, '\0');
  192. std::string::iterator sit = result.begin();
  193. for (cm::string_view const& view : views) {
  194. sit = std::copy_n(view.data(), view.size(), sit);
  195. }
  196. return result;
  197. }
  198. bool cmStrToLong(const char* str, long* value)
  199. {
  200. errno = 0;
  201. char* endp;
  202. *value = strtol(str, &endp, 10);
  203. return (*endp == '\0') && (endp != str) && (errno == 0);
  204. }
  205. bool cmStrToLong(std::string const& str, long* value)
  206. {
  207. return cmStrToLong(str.c_str(), value);
  208. }
  209. bool cmStrToULong(const char* str, unsigned long* value)
  210. {
  211. errno = 0;
  212. char* endp;
  213. while (cmIsSpace(*str)) {
  214. ++str;
  215. }
  216. if (*str == '-') {
  217. return false;
  218. }
  219. *value = strtoul(str, &endp, 10);
  220. return (*endp == '\0') && (endp != str) && (errno == 0);
  221. }
  222. bool cmStrToULong(std::string const& str, unsigned long* value)
  223. {
  224. return cmStrToULong(str.c_str(), value);
  225. }
  226. template <typename Range>
  227. std::size_t getJoinedLength(Range const& rng, cm::string_view separator)
  228. {
  229. std::size_t rangeLength{};
  230. for (auto const& item : rng) {
  231. rangeLength += item.size();
  232. }
  233. auto const separatorsLength = (rng.size() - 1) * separator.size();
  234. return rangeLength + separatorsLength;
  235. }
  236. template <typename Range>
  237. std::string cmJoinImpl(Range const& rng, cm::string_view separator,
  238. cm::string_view initial)
  239. {
  240. if (rng.empty()) {
  241. return { std::begin(initial), std::end(initial) };
  242. }
  243. std::string result;
  244. result.reserve(initial.size() + getJoinedLength(rng, separator));
  245. result.append(std::begin(initial), std::end(initial));
  246. auto begin = std::begin(rng);
  247. auto end = std::end(rng);
  248. result += *begin;
  249. for (++begin; begin != end; ++begin) {
  250. result.append(std::begin(separator), std::end(separator));
  251. result += *begin;
  252. }
  253. return result;
  254. }
  255. std::string cmJoin(std::vector<std::string> const& rng,
  256. cm::string_view separator, cm::string_view initial)
  257. {
  258. return cmJoinImpl(rng, separator, initial);
  259. }
  260. std::string cmJoin(cmStringRange const& rng, cm::string_view separator,
  261. cm::string_view initial)
  262. {
  263. return cmJoinImpl(rng, separator, initial);
  264. }