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