Directory: | cvmfs/ |
---|---|
File: | cvmfs/util/string.cc |
Date: | 2025-06-01 02:36:00 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 319 | 332 | 96.1% |
Branches: | 273 | 392 | 69.6% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /** | ||
2 | * This file is part of the CernVM File System. | ||
3 | * | ||
4 | * Some common functions. | ||
5 | */ | ||
6 | |||
7 | #ifndef __STDC_FORMAT_MACROS | ||
8 | // NOLINTNEXTLINE | ||
9 | #define __STDC_FORMAT_MACROS | ||
10 | #endif | ||
11 | |||
12 | #include "string.h" | ||
13 | |||
14 | #include <errno.h> | ||
15 | #include <fcntl.h> | ||
16 | #include <inttypes.h> | ||
17 | #include <stdint.h> | ||
18 | #include <unistd.h> | ||
19 | |||
20 | #include <cstdio> | ||
21 | #include <cstdlib> | ||
22 | #include <cstring> | ||
23 | #include <ctime> | ||
24 | #include <string> | ||
25 | |||
26 | using namespace std; // NOLINT | ||
27 | |||
28 | #ifdef CVMFS_NAMESPACE_GUARD | ||
29 | namespace CVMFS_NAMESPACE_GUARD { | ||
30 | #endif | ||
31 | |||
32 | const char b64_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', | ||
33 | 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', | ||
34 | 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', | ||
35 | 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', | ||
36 | 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', | ||
37 | '3', '4', '5', '6', '7', '8', '9', '+', '/'}; | ||
38 | |||
39 | /** | ||
40 | * Decode Base64 and Base64Url | ||
41 | */ | ||
42 | const int8_t db64_table[] = { | ||
43 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | ||
44 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | ||
45 | -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, | ||
46 | 61, -1, -1, -1, 0, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, | ||
47 | 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, | ||
48 | 63, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, | ||
49 | 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, | ||
50 | |||
51 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | ||
52 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | ||
53 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | ||
54 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | ||
55 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | ||
56 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | ||
57 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | ||
58 | }; | ||
59 | |||
60 | namespace { | ||
61 | |||
62 | /** | ||
63 | * Used for cas insensitive HasSuffix | ||
64 | */ | ||
65 | struct IgnoreCaseComperator { | ||
66 | 725 | IgnoreCaseComperator() { } | |
67 | 21 | bool operator()(const std::string::value_type a, | |
68 | const std::string::value_type b) const { | ||
69 | 21 | return std::tolower(a) == std::tolower(b); | |
70 | } | ||
71 | }; | ||
72 | |||
73 | } // anonymous namespace | ||
74 | |||
75 |
3/4✓ Branch 1 taken 4 times.
✓ Branch 2 taken 102 times.
✓ Branch 4 taken 106 times.
✗ Branch 5 not taken.
|
106 | string StringifyBool(const bool value) { return value ? "yes" : "no"; } |
76 | |||
77 | 1231213 | string StringifyInt(const int64_t value) { | |
78 | char buffer[48]; | ||
79 | 1231213 | snprintf(buffer, sizeof(buffer), "%" PRId64, value); | |
80 |
1/2✓ Branch 2 taken 1226552 times.
✗ Branch 3 not taken.
|
1231213 | return string(buffer); |
81 | } | ||
82 | |||
83 | 366627 | std::string StringifyUint(const uint64_t value) { | |
84 | char buffer[48]; | ||
85 | 366627 | snprintf(buffer, sizeof(buffer), "%" PRIu64, value); | |
86 |
1/2✓ Branch 2 taken 366627 times.
✗ Branch 3 not taken.
|
366627 | return string(buffer); |
87 | } | ||
88 | |||
89 | 4 | string StringifyByteAsHex(const unsigned char value) { | |
90 | char buffer[3]; | ||
91 | 4 | snprintf(buffer, sizeof(buffer), "%02x", value); | |
92 |
1/2✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | return string(buffer); |
93 | } | ||
94 | |||
95 | 8 | string StringifyDouble(const double value) { | |
96 | char buffer[64]; | ||
97 | 8 | snprintf(buffer, sizeof(buffer), "%.03f", value); | |
98 |
1/2✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | return string(buffer); |
99 | } | ||
100 | |||
101 | /** | ||
102 | * Converts seconds since UTC 0 into something readable | ||
103 | */ | ||
104 | 75 | string StringifyTime(const time_t seconds, const bool utc) { | |
105 | struct tm timestamp; | ||
106 |
2/2✓ Branch 0 taken 42 times.
✓ Branch 1 taken 33 times.
|
75 | if (utc) { |
107 | 42 | localtime_r(&seconds, ×tamp); | |
108 | } else { | ||
109 | 33 | gmtime_r(&seconds, ×tamp); | |
110 | } | ||
111 | |||
112 | 75 | const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", | |
113 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; | ||
114 | char buffer[21]; | ||
115 | 75 | snprintf(buffer, sizeof(buffer), "%d %s %d %02d:%02d:%02d", timestamp.tm_mday, | |
116 | 75 | months[timestamp.tm_mon], timestamp.tm_year + 1900, | |
117 | timestamp.tm_hour, timestamp.tm_min, timestamp.tm_sec); | ||
118 | |||
119 |
1/2✓ Branch 2 taken 75 times.
✗ Branch 3 not taken.
|
75 | return string(buffer); |
120 | } | ||
121 | |||
122 | /** | ||
123 | * Converts seconds since UTC 0 into something like 12 Sep 14:59:37 CDT | ||
124 | */ | ||
125 | 21 | string StringifyLocalTime(const time_t seconds) { | |
126 | struct tm timestamp; | ||
127 | 21 | localtime_r(&seconds, ×tamp); | |
128 | |||
129 | 21 | const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", | |
130 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; | ||
131 | char buffer[26]; | ||
132 | (void)/* cast to void ignores return and placates clang-tidy */ | ||
133 | 21 | snprintf(buffer, sizeof(buffer), "%d %s %d %02d:%02d:%02d %s", | |
134 | 21 | timestamp.tm_mday, months[timestamp.tm_mon], | |
135 | 21 | timestamp.tm_year + 1900, timestamp.tm_hour, timestamp.tm_min, | |
136 | timestamp.tm_sec, timestamp.tm_zone); | ||
137 | |||
138 |
1/2✓ Branch 2 taken 21 times.
✗ Branch 3 not taken.
|
21 | return string(buffer); |
139 | } | ||
140 | |||
141 | |||
142 | /** | ||
143 | * Current time in format Wed, 01 Mar 2006 12:00:00 GMT | ||
144 | */ | ||
145 | 1399 | std::string RfcTimestamp() { | |
146 | 1399 | const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", | |
147 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; | ||
148 | 1399 | const char *day_of_week[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; | |
149 | |||
150 | struct tm timestamp; | ||
151 | 1399 | time_t now = time(NULL); | |
152 | 1399 | gmtime_r(&now, ×tamp); | |
153 | |||
154 | char buffer[30]; | ||
155 | 1399 | snprintf(buffer, sizeof(buffer), "%s, %02d %s %d %02d:%02d:%02d %s", | |
156 | 1399 | day_of_week[timestamp.tm_wday], timestamp.tm_mday, | |
157 | 1399 | months[timestamp.tm_mon], timestamp.tm_year + 1900, | |
158 | timestamp.tm_hour, timestamp.tm_min, timestamp.tm_sec, | ||
159 | timestamp.tm_zone); | ||
160 |
1/2✓ Branch 2 taken 1399 times.
✗ Branch 3 not taken.
|
1399 | return string(buffer); |
161 | } | ||
162 | |||
163 | |||
164 | /** | ||
165 | * Current time in format YYYYMMDDTHHMMSSZ. Used in AWS4 requests. | ||
166 | */ | ||
167 | 1 | std::string IsoTimestamp() { | |
168 | struct tm timestamp; | ||
169 | 1 | time_t now = time(NULL); | |
170 | 1 | gmtime_r(&now, ×tamp); | |
171 | |||
172 | char buffer[17]; | ||
173 | 1 | snprintf(buffer, sizeof(buffer), "%04d%02d%02dT%02d%02d%02dZ", | |
174 | 1 | timestamp.tm_year + 1900, timestamp.tm_mon + 1, timestamp.tm_mday, | |
175 | timestamp.tm_hour, timestamp.tm_min, timestamp.tm_sec); | ||
176 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | return string(buffer); |
177 | } | ||
178 | |||
179 | |||
180 | /** | ||
181 | * UTC time in format YYYYMMDDHHMMSS. Used in cvmfs whitelists. | ||
182 | */ | ||
183 | 7 | std::string WhitelistTimestamp(time_t when) { | |
184 | struct tm timestamp; | ||
185 | 7 | gmtime_r(&when, ×tamp); | |
186 | |||
187 | char buffer[15]; | ||
188 | 7 | snprintf(buffer, sizeof(buffer), "%04d%02d%02d%02d%02d%02d", | |
189 | 7 | timestamp.tm_year + 1900, timestamp.tm_mon + 1, timestamp.tm_mday, | |
190 | timestamp.tm_hour, timestamp.tm_min, timestamp.tm_sec); | ||
191 |
1/2✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
|
7 | return string(buffer); |
192 | } | ||
193 | |||
194 | |||
195 | 372632 | string StringifyTimeval(const timeval value) { | |
196 | char buffer[64]; | ||
197 | 372632 | int64_t msec = value.tv_sec * 1000; | |
198 | 372632 | msec += value.tv_usec / 1000; | |
199 | 372632 | snprintf(buffer, sizeof(buffer), "%" PRId64 ".%03d", msec, | |
200 | 372632 | static_cast<int>(value.tv_usec % 1000)); | |
201 |
1/2✓ Branch 2 taken 372632 times.
✗ Branch 3 not taken.
|
372632 | return string(buffer); |
202 | } | ||
203 | |||
204 | /** | ||
205 | * Parses a timestamp of the form YYYY-MM-DDTHH:MM:SSZ | ||
206 | * Return 0 on error | ||
207 | */ | ||
208 | 108 | time_t IsoTimestamp2UtcTime(const std::string &iso8601) { | |
209 | 108 | time_t utc_time = 0; | |
210 | 108 | unsigned length = iso8601.length(); | |
211 | |||
212 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 106 times.
|
108 | if (length != 20) |
213 | 2 | return utc_time; | |
214 |
2/4✓ Branch 2 taken 106 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 106 times.
✗ Branch 6 not taken.
|
212 | if ((iso8601[4] != '-') || (iso8601[7] != '-') || (iso8601[10] != 'T') |
215 |
5/10✓ Branch 0 taken 106 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 106 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 106 times.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 106 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 106 times.
|
212 | || (iso8601[13] != ':') || (iso8601[16] != ':') || (iso8601[19] != 'Z')) { |
216 | ✗ | return utc_time; | |
217 | } | ||
218 | |||
219 | struct tm tm_wl; | ||
220 | 106 | memset(&tm_wl, 0, sizeof(struct tm)); | |
221 |
2/4✓ Branch 1 taken 106 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 106 times.
✗ Branch 5 not taken.
|
106 | tm_wl.tm_year = static_cast<int>(String2Int64(iso8601.substr(0, 4))) - 1900; |
222 |
2/4✓ Branch 1 taken 106 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 106 times.
✗ Branch 5 not taken.
|
106 | tm_wl.tm_mon = static_cast<int>(String2Int64(iso8601.substr(5, 2))) - 1; |
223 |
2/4✓ Branch 1 taken 106 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 106 times.
✗ Branch 5 not taken.
|
106 | tm_wl.tm_mday = static_cast<int>(String2Int64(iso8601.substr(8, 2))); |
224 |
2/4✓ Branch 1 taken 106 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 106 times.
✗ Branch 5 not taken.
|
106 | tm_wl.tm_hour = static_cast<int>(String2Int64(iso8601.substr(11, 2))); |
225 |
2/4✓ Branch 1 taken 106 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 106 times.
✗ Branch 5 not taken.
|
106 | tm_wl.tm_min = static_cast<int>(String2Int64(iso8601.substr(14, 2))); |
226 |
2/4✓ Branch 1 taken 106 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 106 times.
✗ Branch 5 not taken.
|
106 | tm_wl.tm_sec = static_cast<int>(String2Int64(iso8601.substr(17, 2))); |
227 | 106 | utc_time = timegm(&tm_wl); | |
228 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 105 times.
|
106 | if (utc_time < 0) |
229 | 1 | return 0; | |
230 | |||
231 | 105 | return utc_time; | |
232 | } | ||
233 | |||
234 | 1442 | int64_t String2Int64(const string &value) { | |
235 | int64_t result; | ||
236 | 1442 | sscanf(value.c_str(), "%" PRId64, &result); | |
237 | 1442 | return result; | |
238 | } | ||
239 | |||
240 | 10438 | uint64_t String2Uint64(const string &value) { | |
241 | uint64_t result; | ||
242 |
2/2✓ Branch 1 taken 10434 times.
✓ Branch 2 taken 4 times.
|
10438 | if (sscanf(value.c_str(), "%" PRIu64, &result) == 1) { |
243 | 10434 | return result; | |
244 | } | ||
245 | 4 | return 0; | |
246 | } | ||
247 | |||
248 | /** | ||
249 | * Parse a string into a a uint64_t. | ||
250 | * | ||
251 | * Unlike String2Uint64, this: | ||
252 | * - Checks to make sure the full string is parsed | ||
253 | * - Can indicate an error occurred. | ||
254 | * | ||
255 | * If an error occurs, this returns false and sets errno appropriately. | ||
256 | */ | ||
257 | 570 | bool String2Uint64Parse(const std::string &value, uint64_t *result) { | |
258 | 570 | char *endptr = NULL; | |
259 | 570 | errno = 0; | |
260 | 570 | long long myval = strtoll(value.c_str(), &endptr, 10); // NOLINT | |
261 |
2/2✓ Branch 3 taken 492 times.
✓ Branch 4 taken 73 times.
|
1135 | if ((value.size() == 0) || (endptr != (value.c_str() + value.size())) |
262 |
6/6✓ Branch 0 taken 565 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 491 times.
✓ Branch 4 taken 79 times.
✓ Branch 5 taken 491 times.
|
1135 | || (myval < 0)) { |
263 | 79 | errno = EINVAL; | |
264 | 79 | return false; | |
265 | } | ||
266 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 491 times.
|
491 | if (errno) { |
267 | ✗ | return false; | |
268 | } | ||
269 |
2/2✓ Branch 0 taken 490 times.
✓ Branch 1 taken 1 times.
|
491 | if (result) { |
270 | 490 | *result = myval; | |
271 | } | ||
272 | 491 | return true; | |
273 | } | ||
274 | |||
275 | 4 | void String2Uint64Pair(const string &value, uint64_t *a, uint64_t *b) { | |
276 | 4 | sscanf(value.c_str(), "%" PRIu64 " %" PRIu64, a, b); | |
277 | 4 | } | |
278 | |||
279 | 8448 | bool HasPrefix(const string &str, const string &prefix, | |
280 | const bool ignore_case) { | ||
281 |
2/2✓ Branch 2 taken 1689 times.
✓ Branch 3 taken 6759 times.
|
8448 | if (prefix.length() > str.length()) |
282 | 1689 | return false; | |
283 | |||
284 |
2/2✓ Branch 1 taken 24437 times.
✓ Branch 2 taken 2143 times.
|
26580 | for (unsigned i = 0, l = prefix.length(); i < l; ++i) { |
285 |
2/2✓ Branch 0 taken 4201 times.
✓ Branch 1 taken 20236 times.
|
24437 | if (ignore_case) { |
286 |
2/2✓ Branch 2 taken 2239 times.
✓ Branch 3 taken 1962 times.
|
4201 | if (toupper(str[i]) != toupper(prefix[i])) |
287 | 2239 | return false; | |
288 | } else { | ||
289 |
2/2✓ Branch 2 taken 2377 times.
✓ Branch 3 taken 17859 times.
|
20236 | if (str[i] != prefix[i]) |
290 | 2377 | return false; | |
291 | } | ||
292 | } | ||
293 | 2143 | return true; | |
294 | } | ||
295 | |||
296 | 735 | bool HasSuffix(const std::string &str, const std::string &suffix, | |
297 | const bool ignore_case) { | ||
298 |
2/2✓ Branch 2 taken 10 times.
✓ Branch 3 taken 725 times.
|
735 | if (suffix.size() > str.size()) |
299 | 10 | return false; | |
300 | 725 | const IgnoreCaseComperator icmp; | |
301 | return (ignore_case) | ||
302 |
3/4✓ Branch 0 taken 13 times.
✓ Branch 1 taken 712 times.
✓ Branch 6 taken 13 times.
✗ Branch 7 not taken.
|
725 | ? std::equal(suffix.rbegin(), suffix.rend(), str.rbegin(), icmp) |
303 |
1/2✓ Branch 4 taken 712 times.
✗ Branch 5 not taken.
|
725 | : std::equal(suffix.rbegin(), suffix.rend(), str.rbegin()); |
304 | } | ||
305 | |||
306 | 1806 | vector<string> SplitString(const string &str, char delim) { | |
307 | 1806 | return SplitStringBounded(0, str, delim); | |
308 | } | ||
309 | |||
310 | 1812 | vector<string> SplitStringBounded(unsigned max_chunks, const string &str, | |
311 | char delim) { | ||
312 | 1812 | vector<string> result; | |
313 | |||
314 | // edge case... one chunk is always the whole string | ||
315 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1811 times.
|
1812 | if (1 == max_chunks) { |
316 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | result.push_back(str); |
317 | 1 | return result; | |
318 | } | ||
319 | |||
320 | // split the string | ||
321 | 1811 | const unsigned size = str.size(); | |
322 | 1811 | unsigned marker = 0; | |
323 | 1811 | unsigned chunks = 1; | |
324 | unsigned i; | ||
325 |
2/2✓ Branch 0 taken 44489 times.
✓ Branch 1 taken 1809 times.
|
46298 | for (i = 0; i < size; ++i) { |
326 |
2/2✓ Branch 1 taken 2689 times.
✓ Branch 2 taken 41800 times.
|
44489 | if (str[i] == delim) { |
327 |
2/4✓ Branch 1 taken 2689 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2689 times.
✗ Branch 5 not taken.
|
2689 | result.push_back(str.substr(marker, i - marker)); |
328 | 2689 | marker = i + 1; | |
329 | |||
330 | // we got what we want... good bye | ||
331 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2687 times.
|
2689 | if (++chunks == max_chunks) |
332 | 2 | break; | |
333 | } | ||
334 | } | ||
335 | |||
336 | // push the remainings of the string and return | ||
337 |
2/4✓ Branch 1 taken 1811 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1811 times.
✗ Branch 5 not taken.
|
1811 | result.push_back(str.substr(marker)); |
338 | 1811 | return result; | |
339 | } | ||
340 | |||
341 | 3 | vector<string> SplitStringMultiChar(const string &str, const string &delim) { | |
342 | 3 | size_t pos_start = 0, pos_end = 0, delim_len = delim.length(); | |
343 | 3 | std::string substring; | |
344 | 3 | std::vector<std::string> result; | |
345 | |||
346 |
2/2✓ Branch 1 taken 11 times.
✓ Branch 2 taken 3 times.
|
14 | while ((pos_end = str.find(delim, pos_start)) != string::npos) { |
347 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | substring = str.substr(pos_start, pos_end - pos_start); |
348 | 11 | pos_start = pos_end + delim_len; | |
349 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | result.push_back(substring); |
350 | } | ||
351 | |||
352 |
2/4✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
|
3 | result.push_back(str.substr(pos_start)); |
353 | 6 | return result; | |
354 | 3 | } | |
355 | |||
356 | 320 | string JoinStrings(const vector<string> &strings, const string &joint) { | |
357 |
1/2✓ Branch 2 taken 320 times.
✗ Branch 3 not taken.
|
320 | string result = ""; |
358 | 320 | const unsigned size = strings.size(); | |
359 | |||
360 |
2/2✓ Branch 0 taken 239 times.
✓ Branch 1 taken 81 times.
|
320 | if (size > 0) { |
361 |
1/2✓ Branch 2 taken 239 times.
✗ Branch 3 not taken.
|
239 | result = strings[0]; |
362 |
2/2✓ Branch 0 taken 32 times.
✓ Branch 1 taken 239 times.
|
271 | for (unsigned i = 1; i < size; ++i) |
363 |
2/4✓ Branch 2 taken 32 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 32 times.
✗ Branch 6 not taken.
|
32 | result += joint + strings[i]; |
364 | } | ||
365 | |||
366 | 320 | return result; | |
367 | } | ||
368 | |||
369 | 113 | void ParseKeyvalMem(const unsigned char *buffer, const unsigned buffer_size, | |
370 | map<char, string> *content) { | ||
371 | 113 | string line; | |
372 | 113 | unsigned pos = 0; | |
373 |
2/2✓ Branch 0 taken 9652 times.
✓ Branch 1 taken 2 times.
|
9654 | while (pos < buffer_size) { |
374 |
2/2✓ Branch 0 taken 750 times.
✓ Branch 1 taken 8902 times.
|
9652 | if (static_cast<char>(buffer[pos]) == '\n') { |
375 |
2/2✓ Branch 1 taken 111 times.
✓ Branch 2 taken 639 times.
|
750 | if (line == "--") |
376 | 111 | return; | |
377 | |||
378 |
1/2✓ Branch 1 taken 639 times.
✗ Branch 2 not taken.
|
639 | if (line != "") { |
379 |
3/11✗ Branch 1 not taken.
✓ Branch 2 taken 639 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 639 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 639 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
|
639 | const string tail = (line.length() == 1) ? "" : line.substr(1); |
380 | // Special handling of 'Z' key because it can exist multiple times | ||
381 |
3/4✓ Branch 1 taken 639 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 635 times.
✓ Branch 4 taken 4 times.
|
639 | if (line[0] != 'Z') { |
382 |
3/6✓ Branch 1 taken 635 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 635 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 635 times.
✗ Branch 8 not taken.
|
635 | (*content)[line[0]] = tail; |
383 | } else { | ||
384 |
4/7✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 2 times.
✓ Branch 9 taken 2 times.
|
4 | if (content->find(line[0]) == content->end()) { |
385 |
3/6✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
|
2 | (*content)[line[0]] = tail; |
386 | } else { | ||
387 |
6/12✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 2 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 2 times.
✗ Branch 17 not taken.
|
2 | (*content)[line[0]] = (*content)[line[0]] + "|" + tail; |
388 | } | ||
389 | } | ||
390 | 639 | } | |
391 |
1/2✓ Branch 1 taken 639 times.
✗ Branch 2 not taken.
|
639 | line = ""; |
392 | } else { | ||
393 |
1/2✓ Branch 1 taken 8902 times.
✗ Branch 2 not taken.
|
8902 | line += static_cast<char>(buffer[pos]); |
394 | } | ||
395 | 9541 | pos++; | |
396 | } | ||
397 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 111 times.
|
113 | } |
398 | |||
399 | 12 | bool ParseKeyvalPath(const string &filename, map<char, string> *content) { | |
400 |
1/2✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
|
12 | int fd = open(filename.c_str(), O_RDONLY); |
401 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 11 times.
|
12 | if (fd < 0) |
402 | 1 | return false; | |
403 | |||
404 | unsigned char buffer[4096]; | ||
405 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | ssize_t num_bytes = read(fd, buffer, sizeof(buffer)); |
406 |
1/2✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
|
11 | close(fd); |
407 | |||
408 |
3/4✓ Branch 0 taken 10 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
|
11 | if ((num_bytes <= 0) || (unsigned(num_bytes) >= sizeof(buffer))) |
409 | 1 | return false; | |
410 | |||
411 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
10 | ParseKeyvalMem(buffer, unsigned(num_bytes), content); |
412 | 10 | return true; | |
413 | } | ||
414 | |||
415 | 7304 | string GetLineMem(const char *text, const int text_size) { | |
416 | 7304 | int pos = 0; | |
417 |
4/4✓ Branch 0 taken 306043 times.
✓ Branch 1 taken 21 times.
✓ Branch 2 taken 298760 times.
✓ Branch 3 taken 7283 times.
|
306064 | while ((pos < text_size) && (text[pos] != '\n')) |
418 | 298760 | pos++; | |
419 |
1/2✓ Branch 2 taken 7304 times.
✗ Branch 3 not taken.
|
7304 | return string(text, pos); |
420 | } | ||
421 | |||
422 | 373772 | bool GetLineFile(FILE *f, std::string *line) { | |
423 | int retval; | ||
424 | 373772 | line->clear(); | |
425 | while (true) { | ||
426 | 26641186 | retval = fgetc(f); | |
427 |
2/6✗ Branch 1 not taken.
✓ Branch 2 taken 26641186 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 26641186 times.
|
26641186 | if (ferror(f) && (errno == EINTR)) { |
428 | ✗ | clearerr(f); | |
429 | ✗ | continue; | |
430 |
2/2✓ Branch 0 taken 215 times.
✓ Branch 1 taken 26640971 times.
|
26641186 | } else if (retval == EOF) { |
431 | 215 | break; | |
432 | } | ||
433 | 26640971 | char c = static_cast<char>(retval); | |
434 |
2/2✓ Branch 0 taken 373557 times.
✓ Branch 1 taken 26267414 times.
|
26640971 | if (c == '\n') |
435 | 373557 | break; | |
436 | 26267414 | line->push_back(c); | |
437 | 26267414 | } | |
438 |
4/4✓ Branch 0 taken 215 times.
✓ Branch 1 taken 373557 times.
✓ Branch 3 taken 37 times.
✓ Branch 4 taken 178 times.
|
373772 | return (retval != EOF) || !line->empty(); |
439 | } | ||
440 | |||
441 | 199 | bool GetLineFd(const int fd, std::string *line) { | |
442 | ssize_t retval; | ||
443 | char c; | ||
444 | 199 | line->clear(); | |
445 | while (true) { | ||
446 |
1/2✓ Branch 1 taken 9032 times.
✗ Branch 2 not taken.
|
9032 | retval = read(fd, &c, 1); |
447 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 9029 times.
|
9032 | if (retval == 0) { |
448 | 3 | break; | |
449 | } | ||
450 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 9029 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
9029 | if ((retval == -1) && (errno == EINTR)) { |
451 | ✗ | continue; | |
452 | } | ||
453 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9029 times.
|
9029 | if (retval == -1) { |
454 | ✗ | break; | |
455 | } | ||
456 |
2/2✓ Branch 0 taken 196 times.
✓ Branch 1 taken 8833 times.
|
9029 | if (c == '\n') |
457 | 196 | break; | |
458 |
1/2✓ Branch 1 taken 8833 times.
✗ Branch 2 not taken.
|
8833 | line->push_back(c); |
459 | } | ||
460 |
4/4✓ Branch 0 taken 3 times.
✓ Branch 1 taken 196 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 1 times.
|
199 | return (retval == 1) || !line->empty(); |
461 | } | ||
462 | |||
463 | /** | ||
464 | * Removes leading and trailing whitespaces. | ||
465 | */ | ||
466 | 975 | string Trim(const string &raw, bool trim_newline) { | |
467 |
2/2✓ Branch 1 taken 83 times.
✓ Branch 2 taken 892 times.
|
975 | if (raw.empty()) |
468 |
1/2✓ Branch 2 taken 83 times.
✗ Branch 3 not taken.
|
83 | return ""; |
469 | |||
470 | 892 | unsigned start_pos = 0; | |
471 | 973 | for (; (start_pos < raw.length()) | |
472 |
7/8✓ Branch 0 taken 961 times.
✓ Branch 1 taken 12 times.
✓ Branch 3 taken 905 times.
✓ Branch 4 taken 56 times.
✓ Branch 6 taken 905 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 81 times.
✓ Branch 9 taken 892 times.
|
1878 | && (raw[start_pos] == ' ' || raw[start_pos] == '\t' |
473 |
2/2✓ Branch 0 taken 106 times.
✓ Branch 1 taken 799 times.
|
905 | || (trim_newline |
474 |
4/4✓ Branch 1 taken 94 times.
✓ Branch 2 taken 12 times.
✓ Branch 4 taken 13 times.
✓ Branch 5 taken 81 times.
|
106 | && (raw[start_pos] == '\n' || raw[start_pos] == '\r'))); |
475 | ++start_pos) { | ||
476 | } | ||
477 | 892 | unsigned end_pos = raw.length() - 1; // at least one character in raw | |
478 | 1096 | for (; | |
479 | (end_pos >= start_pos) | ||
480 |
7/8✓ Branch 0 taken 1084 times.
✓ Branch 1 taken 12 times.
✓ Branch 3 taken 1029 times.
✓ Branch 4 taken 55 times.
✓ Branch 6 taken 1029 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 204 times.
✓ Branch 9 taken 892 times.
|
2125 | && (raw[end_pos] == ' ' || raw[end_pos] == '\t' |
481 |
6/6✓ Branch 0 taken 230 times.
✓ Branch 1 taken 799 times.
✓ Branch 3 taken 145 times.
✓ Branch 4 taken 85 times.
✓ Branch 6 taken 64 times.
✓ Branch 7 taken 81 times.
|
1029 | || (trim_newline && (raw[end_pos] == '\n' || raw[end_pos] == '\r'))); |
482 | --end_pos) { | ||
483 | } | ||
484 | |||
485 | 892 | return raw.substr(start_pos, end_pos - start_pos + 1); | |
486 | } | ||
487 | |||
488 | 4 | std::string TrimString(const std::string &path, | |
489 | const std::string &toTrim, | ||
490 | const int trimMode) { | ||
491 | 4 | std::string trimmed = path; | |
492 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | if (trimmed != toTrim) { |
493 |
3/4✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 2 times.
|
12 | while ((trimMode & kTrimLeading) && HasPrefix(trimmed, toTrim, true) |
494 |
5/6✓ Branch 0 taken 8 times.
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6 times.
✓ Branch 7 taken 4 times.
|
18 | && (trimmed.size() > toTrim.size())) { |
495 |
1/2✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
|
6 | trimmed = trimmed.substr(toTrim.size()); |
496 | } | ||
497 |
3/4✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 2 times.
|
10 | while ((trimMode & kTrimTrailing) && HasSuffix(trimmed, toTrim, true) |
498 |
5/6✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 4 times.
|
14 | && (trimmed.size() > toTrim.size())) { |
499 |
1/2✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
|
4 | trimmed = trimmed.substr(0, trimmed.size() - toTrim.size()); |
500 | } | ||
501 | } | ||
502 | 4 | return trimmed; | |
503 | } | ||
504 | |||
505 | /** | ||
506 | * Converts all characters to upper case | ||
507 | */ | ||
508 | 95 | string ToUpper(const string &mixed_case) { | |
509 | 95 | string result(mixed_case); | |
510 |
2/2✓ Branch 1 taken 240 times.
✓ Branch 2 taken 95 times.
|
335 | for (unsigned i = 0, l = result.length(); i < l; ++i) { |
511 |
2/4✓ Branch 1 taken 240 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 240 times.
✗ Branch 5 not taken.
|
240 | result[i] = static_cast<char>(toupper(result[i])); |
512 | } | ||
513 | 95 | return result; | |
514 | } | ||
515 | |||
516 | 101 | string ReplaceAll(const string &haystack, const string &needle, | |
517 | const string &replace_by) { | ||
518 | 101 | string result(haystack); | |
519 | 101 | size_t pos = 0; | |
520 | 101 | const unsigned needle_size = needle.size(); | |
521 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 100 times.
|
101 | if (needle == "") |
522 | 1 | return result; | |
523 | |||
524 |
2/2✓ Branch 1 taken 68 times.
✓ Branch 2 taken 100 times.
|
168 | while ((pos = result.find(needle, pos)) != string::npos) |
525 |
1/2✓ Branch 1 taken 68 times.
✗ Branch 2 not taken.
|
68 | result.replace(pos, needle_size, replace_by); |
526 | 100 | return result; | |
527 | } | ||
528 | |||
529 | 16052 | static inline void Base64Block(const unsigned char input[3], const char *table, | |
530 | char output[4]) { | ||
531 | 16052 | output[0] = table[(input[0] & 0xFD) >> 2]; | |
532 | 16052 | output[1] = table[((input[0] & 0x03) << 4) | ((input[1] & 0xF0) >> 4)]; | |
533 | 16052 | output[2] = table[((input[1] & 0x0F) << 2) | ((input[2] & 0xD0) >> 6)]; | |
534 | 16052 | output[3] = table[input[2] & 0x3F]; | |
535 | 16052 | } | |
536 | |||
537 | 1878 | string Base64(const string &data) { | |
538 | 1878 | string result; | |
539 |
1/2✓ Branch 2 taken 1878 times.
✗ Branch 3 not taken.
|
1878 | result.reserve((data.length() + 3) * 4 / 3); |
540 | 1878 | unsigned pos = 0; | |
541 | const unsigned char *data_ptr = reinterpret_cast<const unsigned char *>( | ||
542 | 1878 | data.data()); | |
543 | 1878 | const unsigned length = data.length(); | |
544 |
2/2✓ Branch 0 taken 14191 times.
✓ Branch 1 taken 1878 times.
|
16069 | while (pos + 2 < length) { |
545 | char encoded_block[4]; | ||
546 | 14191 | Base64Block(data_ptr + pos, b64_table, encoded_block); | |
547 |
1/2✓ Branch 1 taken 14191 times.
✗ Branch 2 not taken.
|
14191 | result.append(encoded_block, 4); |
548 | 14191 | pos += 3; | |
549 | } | ||
550 |
2/2✓ Branch 0 taken 1861 times.
✓ Branch 1 taken 17 times.
|
1878 | if (length % 3 != 0) { |
551 | unsigned char input[3]; | ||
552 | 1861 | input[0] = data_ptr[pos]; | |
553 |
2/2✓ Branch 0 taken 1243 times.
✓ Branch 1 taken 618 times.
|
1861 | input[1] = ((length % 3) == 2) ? data_ptr[pos + 1] : 0; |
554 | 1861 | input[2] = 0; | |
555 | char encoded_block[4]; | ||
556 | 1861 | Base64Block(input, b64_table, encoded_block); | |
557 |
1/2✓ Branch 1 taken 1861 times.
✗ Branch 2 not taken.
|
1861 | result.append(encoded_block, 2); |
558 |
3/4✓ Branch 0 taken 1243 times.
✓ Branch 1 taken 618 times.
✓ Branch 3 taken 1861 times.
✗ Branch 4 not taken.
|
1861 | result.push_back(((length % 3) == 2) ? encoded_block[2] : '='); |
559 |
1/2✓ Branch 1 taken 1861 times.
✗ Branch 2 not taken.
|
1861 | result.push_back('='); |
560 | } | ||
561 | |||
562 | 1878 | return result; | |
563 | } | ||
564 | |||
565 | /** | ||
566 | * Safe encoding for URIs and path names: replace + by - and / by _ | ||
567 | */ | ||
568 | 5 | string Base64Url(const string &data) { | |
569 | 5 | string base64 = Base64(data); | |
570 |
2/2✓ Branch 1 taken 420 times.
✓ Branch 2 taken 5 times.
|
425 | for (unsigned i = 0, l = base64.length(); i < l; ++i) { |
571 |
3/4✓ Branch 1 taken 420 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 414 times.
|
420 | if (base64[i] == '+') { |
572 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | base64[i] = '-'; |
573 |
3/4✓ Branch 1 taken 414 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 410 times.
|
414 | } else if (base64[i] == '/') { |
574 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | base64[i] = '_'; |
575 | } | ||
576 | } | ||
577 | 5 | return base64; | |
578 | } | ||
579 | |||
580 | 29704 | static bool Debase64Block(const unsigned char input[4], | |
581 | unsigned char output[3]) { | ||
582 | int32_t dec[4]; | ||
583 |
2/2✓ Branch 0 taken 118813 times.
✓ Branch 1 taken 29703 times.
|
148516 | for (int i = 0; i < 4; ++i) { |
584 | 118813 | dec[i] = db64_table[input[i]]; | |
585 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 118812 times.
|
118813 | if (dec[i] < 0) |
586 | 1 | return false; | |
587 | } | ||
588 | |||
589 | 29703 | output[0] = (dec[0] << 2) | (dec[1] >> 4); | |
590 | 29703 | output[1] = ((dec[1] & 0x0F) << 4) | (dec[2] >> 2); | |
591 | 29703 | output[2] = ((dec[2] & 0x03) << 6) | dec[3]; | |
592 | 29703 | return true; | |
593 | } | ||
594 | |||
595 | /** | ||
596 | * Can decode both base64 and base64url | ||
597 | */ | ||
598 | 145 | bool Debase64(const string &data, string *decoded) { | |
599 | 145 | decoded->clear(); | |
600 | 145 | decoded->reserve((data.length() + 4) * 3 / 4); | |
601 | 145 | unsigned pos = 0; | |
602 | const unsigned char *data_ptr = reinterpret_cast<const unsigned char *>( | ||
603 | 145 | data.data()); | |
604 | 145 | const unsigned length = data.length(); | |
605 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 144 times.
|
145 | if (length == 0) |
606 | 1 | return true; | |
607 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 142 times.
|
144 | if ((length % 4) != 0) |
608 | 2 | return false; | |
609 | |||
610 |
2/2✓ Branch 0 taken 29704 times.
✓ Branch 1 taken 141 times.
|
29845 | while (pos < length) { |
611 | unsigned char decoded_block[3]; | ||
612 | 29704 | bool retval = Debase64Block(data_ptr + pos, decoded_block); | |
613 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 29703 times.
|
29704 | if (!retval) |
614 | 1 | return false; | |
615 |
1/2✓ Branch 1 taken 29703 times.
✗ Branch 2 not taken.
|
29703 | decoded->append(reinterpret_cast<char *>(decoded_block), 3); |
616 | 29703 | pos += 4; | |
617 | } | ||
618 | |||
619 |
2/2✓ Branch 0 taken 282 times.
✓ Branch 1 taken 141 times.
|
423 | for (int i = 0; i < 2; ++i) { |
620 | 282 | pos--; | |
621 |
2/2✓ Branch 1 taken 50 times.
✓ Branch 2 taken 232 times.
|
282 | if (data[pos] == '=') |
622 | 50 | decoded->erase(decoded->length() - 1); | |
623 | } | ||
624 | 141 | return true; | |
625 | } | ||
626 | |||
627 | /** | ||
628 | * Assumes that source is terminated by a newline | ||
629 | */ | ||
630 | 7 | string Tail(const string &source, unsigned num_lines) { | |
631 |
6/6✓ Branch 1 taken 5 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 4 times.
|
7 | if (source.empty() || (num_lines == 0)) |
632 |
1/2✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
3 | return ""; |
633 | |||
634 | 4 | int l = static_cast<int>(source.length()); | |
635 | 4 | int i = l - 1; | |
636 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 3 times.
|
19 | for (; i >= 0; --i) { |
637 | 16 | char c = source.data()[i]; | |
638 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 11 times.
|
16 | if (c == '\n') { |
639 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
|
5 | if (num_lines == 0) { |
640 | 1 | return source.substr(i + 1); | |
641 | } | ||
642 | 4 | num_lines--; | |
643 | } | ||
644 | } | ||
645 | 3 | return source; | |
646 | } | ||
647 | |||
648 | /** | ||
649 | * Get UTC Time. | ||
650 | * | ||
651 | * @param format format if timestamp (YYYY-MM-DD HH:MM:SS by default) | ||
652 | * @return a timestamp string on success, empty string on failure | ||
653 | */ | ||
654 | ✗ | std::string GetGMTimestamp(const std::string &format) { | |
655 | struct tm time_ptr; | ||
656 | char date_and_time[100]; | ||
657 | ✗ | time_t t = time(NULL); | |
658 | ✗ | gmtime_r(&t, &time_ptr); // take UTC | |
659 | // return empty string if formatting fails | ||
660 | ✗ | if (!strftime(date_and_time, 100, format.c_str(), &time_ptr)) { | |
661 | ✗ | return ""; | |
662 | } | ||
663 | ✗ | std::string timestamp(date_and_time); | |
664 | ✗ | return timestamp; | |
665 | } | ||
666 | |||
667 | #ifdef CVMFS_NAMESPACE_GUARD | ||
668 | } // namespace CVMFS_NAMESPACE_GUARD | ||
669 | #endif | ||
670 |