GCC Code Coverage Report


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