GCC Code Coverage Report
Directory: cvmfs/ Exec Total Coverage
File: cvmfs/util/string.cc Lines: 247 259 95.4 %
Date: 2019-02-03 02:48:13 Branches: 158 185 85.4 %

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
#define __STDC_FORMAT_MACROS
9
#endif
10
11
#include "string.h"
12
#include "cvmfs_config.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 signed char 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
165
  IgnoreCaseComperator() {}
67
4
  bool operator()(const std::string::value_type a,
68
                  const std::string::value_type b) const {
69
4
    return std::tolower(a) == std::tolower(b);
70
  }
71
};
72
73
}  // anonymous namespace
74
75
88
string StringifyBool(const bool value) { return value ? "yes" : "no"; }
76
77
9668917
string StringifyInt(const int64_t value) {
78
  char buffer[48];
79
9668917
  snprintf(buffer, sizeof(buffer), "%" PRId64, value);
80
9668917
  return string(buffer);
81
}
82
83
7
std::string StringifyUint(const uint64_t value) {
84
  char buffer[48];
85
7
  snprintf(buffer, sizeof(buffer), "%" PRIu64, value);
86
7
  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
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
8
  return string(buffer);
99
}
100
101
/**
102
 * Converts seconds since UTC 0 into something readable
103
 */
104
82
string StringifyTime(const time_t seconds, const bool utc) {
105
  struct tm timestamp;
106
82
  if (utc) {
107
44
    localtime_r(&seconds, &timestamp);
108
  } else {
109
38
    gmtime_r(&seconds, &timestamp);
110
  }
111
112
  const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
113
82
                          "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
114
  char buffer[21];
115
  snprintf(buffer, sizeof(buffer), "%d %s %d %02d:%02d:%02d", timestamp.tm_mday,
116
           months[timestamp.tm_mon], timestamp.tm_year + 1900,
117
82
           timestamp.tm_hour, timestamp.tm_min, timestamp.tm_sec);
118
119
82
  return string(buffer);
120
}
121
122
123
/**
124
 * Current time in format Wed, 01 Mar 2006 12:00:00 GMT
125
 */
126
6120
std::string RfcTimestamp() {
127
  const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
128
6120
                          "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
129
6120
  const char *day_of_week[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
130
131
  struct tm timestamp;
132
6120
  time_t now = time(NULL);
133
6120
  gmtime_r(&now, &timestamp);
134
135
  char buffer[30];
136
  snprintf(buffer, sizeof(buffer), "%s, %02d %s %d %02d:%02d:%02d %s",
137
           day_of_week[timestamp.tm_wday], timestamp.tm_mday,
138
           months[timestamp.tm_mon], timestamp.tm_year + 1900,
139
           timestamp.tm_hour, timestamp.tm_min, timestamp.tm_sec,
140
6120
           timestamp.tm_zone);
141
6120
  return string(buffer);
142
}
143
144
145
/**
146
 * Current time in format YYYYMMDDTHHMMSSZ.  Used in AWS4 requests.
147
 */
148
1
std::string IsoTimestamp() {
149
  struct tm timestamp;
150
1
  time_t now = time(NULL);
151
1
  gmtime_r(&now, &timestamp);
152
153
  char buffer[17];
154
  snprintf(buffer, sizeof(buffer), "%04d%02d%02dT%02d%02d%02dZ",
155
           timestamp.tm_year + 1900,
156
           timestamp.tm_mon + 1,
157
           timestamp.tm_mday,
158
           timestamp.tm_hour,
159
           timestamp.tm_min,
160
1
           timestamp.tm_sec);
161
1
  return string(buffer);
162
}
163
164
165
5589438
string StringifyTimeval(const timeval value) {
166
  char buffer[64];
167
5589438
  int64_t msec = value.tv_sec * 1000;
168
5589438
  msec += value.tv_usec / 1000;
169
  snprintf(buffer, sizeof(buffer), "%" PRId64 ".%03d", msec,
170
5589438
           static_cast<int>(value.tv_usec % 1000));
171
5589438
  return string(buffer);
172
}
173
174
/**
175
 * Parses a timstamp of the form YYYY-MM-DDTHH:MM:SSZ
176
 * Return 0 on error
177
 */
178
90
time_t IsoTimestamp2UtcTime(const std::string &iso8601) {
179
90
  time_t utc_time = 0;
180
90
  unsigned length = iso8601.length();
181
182
90
  if (length != 20) return utc_time;
183



88
  if ((iso8601[4] != '-') || (iso8601[7] != '-') || (iso8601[10] != 'T') ||
184
      (iso8601[13] != ':') || (iso8601[16] != ':') || (iso8601[19] != 'Z')) {
185
    return utc_time;
186
  }
187
188
  struct tm tm_wl;
189
88
  memset(&tm_wl, 0, sizeof(struct tm));
190
88
  tm_wl.tm_year = String2Int64(iso8601.substr(0, 4)) - 1900;
191
176
  tm_wl.tm_mon = String2Int64(iso8601.substr(5, 2)) - 1;
192
176
  tm_wl.tm_mday = String2Int64(iso8601.substr(8, 2));
193
176
  tm_wl.tm_hour = String2Int64(iso8601.substr(11, 2));
194
176
  tm_wl.tm_min = String2Int64(iso8601.substr(14, 2));
195
176
  tm_wl.tm_sec = String2Int64(iso8601.substr(17, 2));
196
88
  utc_time = timegm(&tm_wl);
197
89
  if (utc_time < 0) return 0;
198
199
87
  return utc_time;
200
}
201
202
3795
int64_t String2Int64(const string &value) {
203
  int64_t result;
204
3795
  sscanf(value.c_str(), "%" PRId64, &result);
205
3795
  return result;
206
}
207
208
9168
uint64_t String2Uint64(const string &value) {
209
  uint64_t result;
210
9168
  sscanf(value.c_str(), "%" PRIu64, &result);
211
9168
  return result;
212
}
213
214
/**
215
 * Parse a string into a a uint64_t.
216
 *
217
 * Unlike String2Uint64, this:
218
 *   - Checks to make sure the full string is parsed
219
 *   - Can indicate an error occurred.
220
 *
221
 * If an error occurs, this returns false and sets errno appropriately.
222
 */
223
11
bool String2Uint64Parse(const std::string &value, uint64_t *result) {
224
11
  char *endptr = NULL;
225
11
  errno = 0;
226
11
  long long myval = strtoll(value.c_str(), &endptr, 10);  // NOLINT
227


11
  if ((value.size() == 0) || (endptr != (value.c_str() + value.size())) ||
228
      (myval < 0)) {
229
4
    errno = EINVAL;
230
4
    return false;
231
  }
232
7
  if (errno) {
233
    return false;
234
  }
235
7
  if (result) {
236
6
    *result = myval;
237
  }
238
7
  return true;
239
}
240
241
4
void String2Uint64Pair(const string &value, uint64_t *a, uint64_t *b) {
242
4
  sscanf(value.c_str(), "%" PRIu64 " %" PRIu64, a, b);
243
4
}
244
245
17935
bool HasPrefix(const string &str, const string &prefix,
246
               const bool ignore_case) {
247
17935
  if (prefix.length() > str.length()) return false;
248
249
73825
  for (unsigned i = 0, l = prefix.length(); i < l; ++i) {
250
66335
    if (ignore_case) {
251
3337
      if (toupper(str[i]) != toupper(prefix[i])) return false;
252
    } else {
253
62998
      if (str[i] != prefix[i]) return false;
254
    }
255
  }
256
7490
  return true;
257
}
258
259
166
bool HasSuffix(const std::string &str, const std::string &suffix,
260
               const bool ignore_case) {
261
166
  if (suffix.size() > str.size()) return false;
262
165
  const IgnoreCaseComperator icmp;
263
  return (ignore_case)
264
             ? std::equal(suffix.rbegin(), suffix.rend(), str.rbegin(), icmp)
265
165
             : std::equal(suffix.rbegin(), suffix.rend(), str.rbegin());
266
}
267
268
6809
vector<string> SplitString(const string &str, const char delim,
269
                           const unsigned max_chunks) {
270
6809
  vector<string> result;
271
272
  // edge case... one chunk is always the whole string
273
6809
  if (1 == max_chunks) {
274
1
    result.push_back(str);
275
1
    return result;
276
  }
277
278
  // split the string
279
6808
  const unsigned size = str.size();
280
6808
  unsigned marker = 0;
281
6808
  unsigned chunks = 1;
282
  unsigned i;
283
1457728
  for (i = 0; i < size; ++i) {
284
1450922
    if (str[i] == delim) {
285
47376
      result.push_back(str.substr(marker, i - marker));
286
47376
      marker = i + 1;
287
288
      // we got what we want... good bye
289
47376
      if (++chunks == max_chunks) break;
290
    }
291
  }
292
293
  // push the remainings of the string and return
294
6808
  result.push_back(str.substr(marker));
295
6808
  return result;
296
}
297
298
114
string JoinStrings(const vector<string> &strings, const string &joint) {
299
114
  string result = "";
300
114
  const unsigned size = strings.size();
301
302
114
  if (size > 0) {
303
105
    result = strings[0];
304
105
    for (unsigned i = 1; i < size; ++i) result += joint + strings[i];
305
  }
306
307
114
  return result;
308
}
309
310
120
void ParseKeyvalMem(const unsigned char *buffer, const unsigned buffer_size,
311
                    map<char, string> *content) {
312
120
  string line;
313
120
  unsigned pos = 0;
314
11258
  while (pos < buffer_size) {
315
11137
    if (static_cast<char>(buffer[pos]) == '\n') {
316
835
      if (line == "--") return;
317
318
716
      if (line != "") {
319

716
        const string tail = (line.length() == 1) ? "" : line.substr(1);
320
        // Special handling of 'Z' key because it can exist multiple times
321
716
        if (line[0] != 'Z') {
322
712
          (*content)[line[0]] = tail;
323
        } else {
324
4
          if (content->find(line[0]) == content->end()) {
325
2
            (*content)[line[0]] = tail;
326
          } else {
327
2
            (*content)[line[0]] = (*content)[line[0]] + "|" + tail;
328
          }
329
        }
330
      }
331
716
      line = "";
332
    } else {
333
10302
      line += static_cast<char>(buffer[pos]);
334
    }
335
11018
    pos++;
336
  }
337
}
338
339
11
bool ParseKeyvalPath(const string &filename, map<char, string> *content) {
340
11
  int fd = open(filename.c_str(), O_RDONLY);
341
11
  if (fd < 0) return false;
342
343
  unsigned char buffer[4096];
344
10
  int num_bytes = read(fd, buffer, sizeof(buffer));
345
10
  close(fd);
346
347

10
  if ((num_bytes <= 0) || (unsigned(num_bytes) >= sizeof(buffer))) return false;
348
349
9
  ParseKeyvalMem(buffer, unsigned(num_bytes), content);
350
9
  return true;
351
}
352
353
7151
string GetLineMem(const char *text, const int text_size) {
354
7151
  int pos = 0;
355

7151
  while ((pos < text_size) && (text[pos] != '\n')) pos++;
356
7151
  return string(text, pos);
357
}
358
359
5593257
bool GetLineFile(FILE *f, std::string *line) {
360
  int retval;
361
5593257
  line->clear();
362
393531876
  while (true) {
363
399125133
    retval = fgetc(f);
364

399125133
    if (ferror(f) && (errno == EINTR)) {
365
      clearerr(f);
366
      continue;
367
399125133
    } else if (retval == EOF) {
368
682
      break;
369
    }
370
399124451
    char c = retval;
371
399124451
    if (c == '\n') break;
372
393531876
    line->push_back(c);
373
  }
374

5593257
  return (retval != EOF) || !line->empty();
375
}
376
377
985
bool GetLineFd(const int fd, std::string *line) {
378
  int retval;
379
  char c;
380
985
  line->clear();
381
66547
  while (true) {
382
67532
    retval = read(fd, &c, 1);
383
67532
    if (retval == 0) {
384
6
      break;
385
    }
386

67526
    if ((retval == -1) && (errno == EINTR)) {
387
      continue;
388
    }
389
67526
    if (retval == -1) {
390
      break;
391
    }
392
67526
    if (c == '\n') break;
393
66547
    line->push_back(c);
394
  }
395

985
  return (retval == 1) || !line->empty();
396
}
397
398
/**
399
 * Removes leading and trailing whitespaces.
400
 */
401
2694
string Trim(const string &raw) {
402
2694
  if (raw.empty()) return "";
403
404
2548
  unsigned start_pos = 0;
405


2548
  for (; (start_pos < raw.length()) &&
406
         (raw[start_pos] == ' ' || raw[start_pos] == '\t');
407
       ++start_pos) {
408
  }
409
2548
  unsigned end_pos = raw.length() - 1;  // at least one character in raw
410


2548
  for (;
411
       (end_pos >= start_pos) && (raw[end_pos] == ' ' || raw[end_pos] == '\t');
412
       --end_pos) {
413
  }
414
415
2548
  return raw.substr(start_pos, end_pos - start_pos + 1);
416
}
417
418
/**
419
 * Converts all characters to upper case
420
 */
421
189
string ToUpper(const string &mixed_case) {
422
189
  string result(mixed_case);
423
670
  for (unsigned i = 0, l = result.length(); i < l; ++i) {
424
481
    result[i] = toupper(result[i]);
425
  }
426
189
  return result;
427
}
428
429
250
string ReplaceAll(const string &haystack, const string &needle,
430
                  const string &replace_by) {
431
250
  string result(haystack);
432
250
  size_t pos = 0;
433
250
  const unsigned needle_size = needle.size();
434
250
  if (needle == "") return result;
435
436
627
  while ((pos = result.find(needle, pos)) != string::npos)
437
129
    result.replace(pos, needle_size, replace_by);
438
249
  return result;
439
}
440
441
102244
static inline void Base64Block(const unsigned char input[3], const char *table,
442
                               char output[4]) {
443
102244
  output[0] = table[(input[0] & 0xFD) >> 2];
444
102244
  output[1] = table[((input[0] & 0x03) << 4) | ((input[1] & 0xF0) >> 4)];
445
102244
  output[2] = table[((input[1] & 0x0F) << 2) | ((input[2] & 0xD0) >> 6)];
446
102244
  output[3] = table[input[2] & 0x3F];
447
102244
}
448
449
9105
string Base64(const string &data) {
450
9105
  string result;
451
9105
  result.reserve((data.length() + 3) * 4 / 3);
452
9105
  unsigned pos = 0;
453
  const unsigned char *data_ptr =
454
9105
      reinterpret_cast<const unsigned char *>(data.data());
455
9105
  const unsigned length = data.length();
456
111392
  while (pos + 2 < length) {
457
    char encoded_block[4];
458
93182
    Base64Block(data_ptr + pos, b64_table, encoded_block);
459
93182
    result.append(encoded_block, 4);
460
93182
    pos += 3;
461
  }
462
9105
  if (length % 3 != 0) {
463
    unsigned char input[3];
464
9062
    input[0] = data_ptr[pos];
465
9062
    input[1] = ((length % 3) == 2) ? data_ptr[pos + 1] : 0;
466
9062
    input[2] = 0;
467
    char encoded_block[4];
468
9062
    Base64Block(input, b64_table, encoded_block);
469
9062
    result.append(encoded_block, 2);
470
9062
    result.push_back(((length % 3) == 2) ? encoded_block[2] : '=');
471
9062
    result.push_back('=');
472
  }
473
474
9105
  return result;
475
}
476
477
/**
478
 * Safe encoding for URIs and path names: replace + by - and / by _
479
 */
480
5
string Base64Url(const string &data) {
481
5
  string base64 = Base64(data);
482
425
  for (unsigned i = 0, l = base64.length(); i < l; ++i) {
483
420
    if (base64[i] == '+')
484
6
      base64[i] = '-';
485
414
    else if (base64[i] == '/')
486
4
      base64[i] = '_';
487
  }
488
5
  return base64;
489
}
490
491
64482
static bool Debase64Block(const unsigned char input[4],
492
                          const signed char *d_table, unsigned char output[3]) {
493
  int32_t dec[4];
494
322406
  for (int i = 0; i < 4; ++i) {
495
257925
    dec[i] = db64_table[input[i]];
496
257925
    if (dec[i] < 0) return false;
497
  }
498
499
64481
  output[0] = (dec[0] << 2) | (dec[1] >> 4);
500
64481
  output[1] = ((dec[1] & 0x0F) << 4) | (dec[2] >> 2);
501
64481
  output[2] = ((dec[2] & 0x03) << 6) | dec[3];
502
64481
  return true;
503
}
504
505
/**
506
 * Can decode both base64 and base64url
507
 */
508
167
bool Debase64(const string &data, string *decoded) {
509
167
  decoded->clear();
510
167
  decoded->reserve((data.length() + 4) * 3 / 4);
511
167
  unsigned pos = 0;
512
  const unsigned char *data_ptr =
513
167
      reinterpret_cast<const unsigned char *>(data.data());
514
167
  const unsigned length = data.length();
515
167
  if (length == 0) return true;
516
166
  if ((length % 4) != 0) return false;
517
518
64809
  while (pos < length) {
519
    unsigned char decoded_block[3];
520
64482
    bool retval = Debase64Block(data_ptr + pos, db64_table, decoded_block);
521
64482
    if (!retval) return false;
522
64481
    decoded->append(reinterpret_cast<char *>(decoded_block), 3);
523
64481
    pos += 4;
524
  }
525
526
489
  for (int i = 0; i < 2; ++i) {
527
326
    pos--;
528
326
    if (data[pos] == '=') decoded->erase(decoded->length() - 1);
529
  }
530
163
  return true;
531
}
532
533
/**
534
 * Assumes that source is terminated by a newline
535
 */
536
7
string Tail(const string &source, unsigned num_lines) {
537

7
  if (source.empty() || (num_lines == 0)) return "";
538
539
4
  unsigned l = source.length();
540
4
  int i = l - 1;
541
19
  for (; i >= 0; --i) {
542
16
    char c = source.data()[i];
543
16
    if (c == '\n') {
544
5
      if (num_lines == 0) {
545
1
        return source.substr(i + 1);
546
      }
547
4
      num_lines--;
548
    }
549
  }
550
3
  return source;
551
}
552
553
/**
554
  * Get UTC Time.
555
  *
556
  * @return a timestamp in "YYYY-MM-DD HH:MM:SS" format
557
  */
558
std::string GetGMTimestamp() {
559
  struct tm time_ptr;
560
  char date_and_time[50];
561
  time_t t = time(NULL);
562
  gmtime_r(&t, &time_ptr);      // take UTC
563
  // timestamp format
564
  strftime(date_and_time, 50, "%Y-%m-%d %H:%M:%S", &time_ptr);
565
  std::string timestamp(date_and_time);
566
  return timestamp;
567
}
568
569
#ifdef CVMFS_NAMESPACE_GUARD
570
}  // namespace CVMFS_NAMESPACE_GUARD
571
#endif