cmStringAlgorithms.cxx 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  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. std::string cmTrimWhitespace(cm::string_view str)
  10. {
  11. auto start = str.begin();
  12. while (start != str.end() && cmIsSpace(*start)) {
  13. ++start;
  14. }
  15. if (start == str.end()) {
  16. return std::string();
  17. }
  18. auto stop = str.end() - 1;
  19. while (cmIsSpace(*stop)) {
  20. --stop;
  21. }
  22. return std::string(start, stop + 1);
  23. }
  24. std::string cmRemoveQuotes(cm::string_view str)
  25. {
  26. // We process only strings that have two quotes at least.
  27. // Also front() and back() are only defined behavior on non empty strings.
  28. if (str.size() >= 2 && //
  29. str.front() == '"' && //
  30. str.back() == '"') {
  31. // Remove a quote from the front and back
  32. str.remove_prefix(1);
  33. str.remove_suffix(1);
  34. }
  35. return std::string(str);
  36. }
  37. std::string cmEscapeQuotes(cm::string_view str)
  38. {
  39. std::string result;
  40. result.reserve(str.size());
  41. for (const char ch : str) {
  42. if (ch == '"') {
  43. result += '\\';
  44. }
  45. result += ch;
  46. }
  47. return result;
  48. }
  49. std::vector<std::string> cmTokenize(cm::string_view str, cm::string_view sep)
  50. {
  51. std::vector<std::string> tokens;
  52. cm::string_view::size_type tokend = 0;
  53. do {
  54. cm::string_view::size_type tokstart = str.find_first_not_of(sep, tokend);
  55. if (tokstart == cm::string_view::npos) {
  56. break; // no more tokens
  57. }
  58. tokend = str.find_first_of(sep, tokstart);
  59. if (tokend == cm::string_view::npos) {
  60. tokens.emplace_back(str.substr(tokstart));
  61. } else {
  62. tokens.emplace_back(str.substr(tokstart, tokend - tokstart));
  63. }
  64. } while (tokend != cm::string_view::npos);
  65. if (tokens.empty()) {
  66. tokens.emplace_back();
  67. }
  68. return tokens;
  69. }
  70. void cmExpandList(cm::string_view arg, std::vector<std::string>& argsOut,
  71. bool emptyArgs)
  72. {
  73. // If argument is empty, it is an empty list.
  74. if (!emptyArgs && arg.empty()) {
  75. return;
  76. }
  77. // if there are no ; in the name then just copy the current string
  78. if (arg.find(';') == cm::string_view::npos) {
  79. argsOut.emplace_back(arg);
  80. return;
  81. }
  82. std::string newArg;
  83. // Break the string at non-escaped semicolons not nested in [].
  84. int squareNesting = 0;
  85. cm::string_view::iterator last = arg.begin();
  86. cm::string_view::iterator const cend = arg.end();
  87. for (cm::string_view::iterator c = last; c != cend; ++c) {
  88. switch (*c) {
  89. case '\\': {
  90. // We only want to allow escaping of semicolons. Other
  91. // escapes should not be processed here.
  92. cm::string_view::iterator cnext = c + 1;
  93. if ((cnext != cend) && *cnext == ';') {
  94. newArg.append(last, c);
  95. // Skip over the escape character
  96. last = cnext;
  97. c = cnext;
  98. }
  99. } break;
  100. case '[': {
  101. ++squareNesting;
  102. } break;
  103. case ']': {
  104. --squareNesting;
  105. } break;
  106. case ';': {
  107. // Break the string here if we are not nested inside square
  108. // brackets.
  109. if (squareNesting == 0) {
  110. newArg.append(last, c);
  111. // Skip over the semicolon
  112. last = c + 1;
  113. if (!newArg.empty() || emptyArgs) {
  114. // Add the last argument if the string is not empty.
  115. argsOut.push_back(newArg);
  116. newArg.clear();
  117. }
  118. }
  119. } break;
  120. default: {
  121. // Just append this character.
  122. } break;
  123. }
  124. }
  125. newArg.append(last, cend);
  126. if (!newArg.empty() || emptyArgs) {
  127. // Add the last argument if the string is not empty.
  128. argsOut.push_back(std::move(newArg));
  129. }
  130. }
  131. std::vector<std::string> cmExpandedList(cm::string_view arg, bool emptyArgs)
  132. {
  133. std::vector<std::string> argsOut;
  134. cmExpandList(arg, argsOut, emptyArgs);
  135. return argsOut;
  136. }
  137. namespace {
  138. template <std::size_t N, typename T>
  139. inline void MakeDigits(cm::string_view& view, char (&digits)[N],
  140. const char* pattern, T value)
  141. {
  142. int res = std::snprintf(digits, N, pattern, value);
  143. if (res > 0 && res < static_cast<int>(N)) {
  144. view = cm::string_view(digits, static_cast<std::size_t>(res));
  145. }
  146. }
  147. } // unnamed namespace
  148. cmAlphaNum::cmAlphaNum(int val)
  149. {
  150. MakeDigits(View_, Digits_, "%i", val);
  151. }
  152. cmAlphaNum::cmAlphaNum(unsigned int val)
  153. {
  154. MakeDigits(View_, Digits_, "%u", val);
  155. }
  156. cmAlphaNum::cmAlphaNum(long int val)
  157. {
  158. MakeDigits(View_, Digits_, "%li", val);
  159. }
  160. cmAlphaNum::cmAlphaNum(unsigned long int val)
  161. {
  162. MakeDigits(View_, Digits_, "%lu", val);
  163. }
  164. cmAlphaNum::cmAlphaNum(long long int val)
  165. {
  166. MakeDigits(View_, Digits_, "%lli", val);
  167. }
  168. cmAlphaNum::cmAlphaNum(unsigned long long int val)
  169. {
  170. MakeDigits(View_, Digits_, "%llu", val);
  171. }
  172. cmAlphaNum::cmAlphaNum(float val)
  173. {
  174. MakeDigits(View_, Digits_, "%g", static_cast<double>(val));
  175. }
  176. cmAlphaNum::cmAlphaNum(double val)
  177. {
  178. MakeDigits(View_, Digits_, "%g", val);
  179. }
  180. std::string cmCatViews(std::initializer_list<cm::string_view> views)
  181. {
  182. std::size_t total_size = 0;
  183. for (cm::string_view const& view : views) {
  184. total_size += view.size();
  185. }
  186. std::string result(total_size, '\0');
  187. std::string::iterator sit = result.begin();
  188. for (cm::string_view const& view : views) {
  189. sit = std::copy_n(view.data(), view.size(), sit);
  190. }
  191. return result;
  192. }
  193. bool cmIsInternallyOn(cm::string_view val)
  194. {
  195. return (val.size() == 4) && //
  196. (val[0] == 'I' || val[0] == 'i') && //
  197. (val[1] == '_') && //
  198. (val[2] == 'O' || val[2] == 'o') && //
  199. (val[3] == 'N' || val[3] == 'n');
  200. }
  201. bool cmIsNOTFOUND(cm::string_view val)
  202. {
  203. return (val == "NOTFOUND") || cmHasLiteralSuffix(val, "-NOTFOUND");
  204. }
  205. bool cmIsOn(cm::string_view val)
  206. {
  207. switch (val.size()) {
  208. case 1:
  209. return val[0] == '1' || val[0] == 'Y' || val[0] == 'y';
  210. case 2:
  211. return //
  212. (val[0] == 'O' || val[0] == 'o') && //
  213. (val[1] == 'N' || val[1] == 'n');
  214. case 3:
  215. return //
  216. (val[0] == 'Y' || val[0] == 'y') && //
  217. (val[1] == 'E' || val[1] == 'e') && //
  218. (val[2] == 'S' || val[2] == 's');
  219. case 4:
  220. return //
  221. (val[0] == 'T' || val[0] == 't') && //
  222. (val[1] == 'R' || val[1] == 'r') && //
  223. (val[2] == 'U' || val[2] == 'u') && //
  224. (val[3] == 'E' || val[3] == 'e');
  225. default:
  226. break;
  227. }
  228. return false;
  229. }
  230. bool cmIsOff(cm::string_view val)
  231. {
  232. switch (val.size()) {
  233. case 0:
  234. return true;
  235. case 1:
  236. return val[0] == '0' || val[0] == 'N' || val[0] == 'n';
  237. case 2:
  238. return //
  239. (val[0] == 'N' || val[0] == 'n') && //
  240. (val[1] == 'O' || val[1] == 'o');
  241. case 3:
  242. return //
  243. (val[0] == 'O' || val[0] == 'o') && //
  244. (val[1] == 'F' || val[1] == 'f') && //
  245. (val[2] == 'F' || val[2] == 'f');
  246. case 5:
  247. return //
  248. (val[0] == 'F' || val[0] == 'f') && //
  249. (val[1] == 'A' || val[1] == 'a') && //
  250. (val[2] == 'L' || val[2] == 'l') && //
  251. (val[3] == 'S' || val[3] == 's') && //
  252. (val[4] == 'E' || val[4] == 'e');
  253. case 6:
  254. return //
  255. (val[0] == 'I' || val[0] == 'i') && //
  256. (val[1] == 'G' || val[1] == 'g') && //
  257. (val[2] == 'N' || val[2] == 'n') && //
  258. (val[3] == 'O' || val[3] == 'o') && //
  259. (val[4] == 'R' || val[4] == 'r') && //
  260. (val[5] == 'E' || val[5] == 'e');
  261. default:
  262. break;
  263. }
  264. return cmIsNOTFOUND(val);
  265. }
  266. bool cmStrToLong(const char* str, long* value)
  267. {
  268. errno = 0;
  269. char* endp;
  270. *value = strtol(str, &endp, 10);
  271. return (*endp == '\0') && (endp != str) && (errno == 0);
  272. }
  273. bool cmStrToLong(std::string const& str, long* value)
  274. {
  275. return cmStrToLong(str.c_str(), value);
  276. }
  277. bool cmStrToULong(const char* str, unsigned long* value)
  278. {
  279. errno = 0;
  280. char* endp;
  281. while (cmIsSpace(*str)) {
  282. ++str;
  283. }
  284. if (*str == '-') {
  285. return false;
  286. }
  287. *value = strtoul(str, &endp, 10);
  288. return (*endp == '\0') && (endp != str) && (errno == 0);
  289. }
  290. bool cmStrToULong(std::string const& str, unsigned long* value)
  291. {
  292. return cmStrToULong(str.c_str(), value);
  293. }