CernVM-FS  2.9.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
string.cc
Go to the documentation of this file.
1 
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 
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 
67  bool operator()(const std::string::value_type a,
68  const std::string::value_type b) const {
69  return std::tolower(a) == std::tolower(b);
70  }
71 };
72 
73 } // anonymous namespace
74 
75 string StringifyBool(const bool value) { return value ? "yes" : "no"; }
76 
77 string StringifyInt(const int64_t value) {
78  char buffer[48];
79  snprintf(buffer, sizeof(buffer), "%" PRId64, value);
80  return string(buffer);
81 }
82 
83 std::string StringifyUint(const uint64_t value) {
84  char buffer[48];
85  snprintf(buffer, sizeof(buffer), "%" PRIu64, value);
86  return string(buffer);
87 }
88 
89 string StringifyByteAsHex(const unsigned char value) {
90  char buffer[3];
91  snprintf(buffer, sizeof(buffer), "%02x", value);
92  return string(buffer);
93 }
94 
95 string StringifyDouble(const double value) {
96  char buffer[64];
97  snprintf(buffer, sizeof(buffer), "%.03f", value);
98  return string(buffer);
99 }
100 
104 string StringifyTime(const time_t seconds, const bool utc) {
105  struct tm timestamp;
106  if (utc) {
107  localtime_r(&seconds, &timestamp);
108  } else {
109  gmtime_r(&seconds, &timestamp);
110  }
111 
112  const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
113  "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  timestamp.tm_hour, timestamp.tm_min, timestamp.tm_sec);
118 
119  return string(buffer);
120 }
121 
122 
126 std::string RfcTimestamp() {
127  const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
128  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
129  const char *day_of_week[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
130 
131  struct tm timestamp;
132  time_t now = time(NULL);
133  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  timestamp.tm_zone);
141  return string(buffer);
142 }
143 
144 
148 std::string IsoTimestamp() {
149  struct tm timestamp;
150  time_t now = time(NULL);
151  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  timestamp.tm_sec);
161  return string(buffer);
162 }
163 
164 
168 std::string WhitelistTimestamp(time_t when) {
169  struct tm timestamp;
170  gmtime_r(&when, &timestamp);
171 
172  char buffer[15];
173  snprintf(buffer, sizeof(buffer), "%04d%02d%02d%02d%02d%02d",
174  timestamp.tm_year + 1900,
175  timestamp.tm_mon + 1,
176  timestamp.tm_mday,
177  timestamp.tm_hour,
178  timestamp.tm_min,
179  timestamp.tm_sec);
180  return string(buffer);
181 }
182 
183 
184 string StringifyTimeval(const timeval value) {
185  char buffer[64];
186  int64_t msec = value.tv_sec * 1000;
187  msec += value.tv_usec / 1000;
188  snprintf(buffer, sizeof(buffer), "%" PRId64 ".%03d", msec,
189  static_cast<int>(value.tv_usec % 1000));
190  return string(buffer);
191 }
192 
197 time_t IsoTimestamp2UtcTime(const std::string &iso8601) {
198  time_t utc_time = 0;
199  unsigned length = iso8601.length();
200 
201  if (length != 20) return utc_time;
202  if ((iso8601[4] != '-') || (iso8601[7] != '-') || (iso8601[10] != 'T') ||
203  (iso8601[13] != ':') || (iso8601[16] != ':') || (iso8601[19] != 'Z')) {
204  return utc_time;
205  }
206 
207  struct tm tm_wl;
208  memset(&tm_wl, 0, sizeof(struct tm));
209  tm_wl.tm_year = String2Int64(iso8601.substr(0, 4)) - 1900;
210  tm_wl.tm_mon = String2Int64(iso8601.substr(5, 2)) - 1;
211  tm_wl.tm_mday = String2Int64(iso8601.substr(8, 2));
212  tm_wl.tm_hour = String2Int64(iso8601.substr(11, 2));
213  tm_wl.tm_min = String2Int64(iso8601.substr(14, 2));
214  tm_wl.tm_sec = String2Int64(iso8601.substr(17, 2));
215  utc_time = timegm(&tm_wl);
216  if (utc_time < 0) return 0;
217 
218  return utc_time;
219 }
220 
221 int64_t String2Int64(const string &value) {
222  int64_t result;
223  sscanf(value.c_str(), "%" PRId64, &result);
224  return result;
225 }
226 
227 uint64_t String2Uint64(const string &value) {
228  uint64_t result;
229  sscanf(value.c_str(), "%" PRIu64, &result);
230  return result;
231 }
232 
242 bool String2Uint64Parse(const std::string &value, uint64_t *result) {
243  char *endptr = NULL;
244  errno = 0;
245  long long myval = strtoll(value.c_str(), &endptr, 10); // NOLINT
246  if ((value.size() == 0) || (endptr != (value.c_str() + value.size())) ||
247  (myval < 0)) {
248  errno = EINVAL;
249  return false;
250  }
251  if (errno) {
252  return false;
253  }
254  if (result) {
255  *result = myval;
256  }
257  return true;
258 }
259 
260 void String2Uint64Pair(const string &value, uint64_t *a, uint64_t *b) {
261  sscanf(value.c_str(), "%" PRIu64 " %" PRIu64, a, b);
262 }
263 
264 bool HasPrefix(const string &str, const string &prefix,
265  const bool ignore_case) {
266  if (prefix.length() > str.length()) return false;
267 
268  for (unsigned i = 0, l = prefix.length(); i < l; ++i) {
269  if (ignore_case) {
270  if (toupper(str[i]) != toupper(prefix[i])) return false;
271  } else {
272  if (str[i] != prefix[i]) return false;
273  }
274  }
275  return true;
276 }
277 
278 bool HasSuffix(const std::string &str, const std::string &suffix,
279  const bool ignore_case) {
280  if (suffix.size() > str.size()) return false;
281  const IgnoreCaseComperator icmp;
282  return (ignore_case)
283  ? std::equal(suffix.rbegin(), suffix.rend(), str.rbegin(), icmp)
284  : std::equal(suffix.rbegin(), suffix.rend(), str.rbegin());
285 }
286 
287 vector<string> SplitString(const string &str, const char delim,
288  const unsigned max_chunks) {
289  vector<string> result;
290 
291  // edge case... one chunk is always the whole string
292  if (1 == max_chunks) {
293  result.push_back(str);
294  return result;
295  }
296 
297  // split the string
298  const unsigned size = str.size();
299  unsigned marker = 0;
300  unsigned chunks = 1;
301  unsigned i;
302  for (i = 0; i < size; ++i) {
303  if (str[i] == delim) {
304  result.push_back(str.substr(marker, i - marker));
305  marker = i + 1;
306 
307  // we got what we want... good bye
308  if (++chunks == max_chunks) break;
309  }
310  }
311 
312  // push the remainings of the string and return
313  result.push_back(str.substr(marker));
314  return result;
315 }
316 
317 string JoinStrings(const vector<string> &strings, const string &joint) {
318  string result = "";
319  const unsigned size = strings.size();
320 
321  if (size > 0) {
322  result = strings[0];
323  for (unsigned i = 1; i < size; ++i) result += joint + strings[i];
324  }
325 
326  return result;
327 }
328 
329 void ParseKeyvalMem(const unsigned char *buffer, const unsigned buffer_size,
330  map<char, string> *content) {
331  string line;
332  unsigned pos = 0;
333  while (pos < buffer_size) {
334  if (static_cast<char>(buffer[pos]) == '\n') {
335  if (line == "--") return;
336 
337  if (line != "") {
338  const string tail = (line.length() == 1) ? "" : line.substr(1);
339  // Special handling of 'Z' key because it can exist multiple times
340  if (line[0] != 'Z') {
341  (*content)[line[0]] = tail;
342  } else {
343  if (content->find(line[0]) == content->end()) {
344  (*content)[line[0]] = tail;
345  } else {
346  (*content)[line[0]] = (*content)[line[0]] + "|" + tail;
347  }
348  }
349  }
350  line = "";
351  } else {
352  line += static_cast<char>(buffer[pos]);
353  }
354  pos++;
355  }
356 }
357 
358 bool ParseKeyvalPath(const string &filename, map<char, string> *content) {
359  int fd = open(filename.c_str(), O_RDONLY);
360  if (fd < 0) return false;
361 
362  unsigned char buffer[4096];
363  int num_bytes = read(fd, buffer, sizeof(buffer));
364  close(fd);
365 
366  if ((num_bytes <= 0) || (unsigned(num_bytes) >= sizeof(buffer))) return false;
367 
368  ParseKeyvalMem(buffer, unsigned(num_bytes), content);
369  return true;
370 }
371 
372 string GetLineMem(const char *text, const int text_size) {
373  int pos = 0;
374  while ((pos < text_size) && (text[pos] != '\n')) pos++;
375  return string(text, pos);
376 }
377 
378 bool GetLineFile(FILE *f, std::string *line) {
379  int retval;
380  line->clear();
381  while (true) {
382  retval = fgetc(f);
383  if (ferror(f) && (errno == EINTR)) {
384  clearerr(f);
385  continue;
386  } else if (retval == EOF) {
387  break;
388  }
389  char c = retval;
390  if (c == '\n') break;
391  line->push_back(c);
392  }
393  return (retval != EOF) || !line->empty();
394 }
395 
396 bool GetLineFd(const int fd, std::string *line) {
397  int retval;
398  char c;
399  line->clear();
400  while (true) {
401  retval = read(fd, &c, 1);
402  if (retval == 0) {
403  break;
404  }
405  if ((retval == -1) && (errno == EINTR)) {
406  continue;
407  }
408  if (retval == -1) {
409  break;
410  }
411  if (c == '\n') break;
412  line->push_back(c);
413  }
414  return (retval == 1) || !line->empty();
415 }
416 
420 string Trim(const string &raw, bool trim_newline) {
421  if (raw.empty()) return "";
422 
423  unsigned start_pos = 0;
424  for (; (start_pos < raw.length()) &&
425  (raw[start_pos] == ' ' || raw[start_pos] == '\t' ||
426  (trim_newline && (raw[start_pos] == '\n' || raw[start_pos] == '\r')));
427  ++start_pos)
428  {
429  }
430  unsigned end_pos = raw.length() - 1; // at least one character in raw
431  for (;
432  (end_pos >= start_pos) &&
433  (raw[end_pos] == ' ' || raw[end_pos] == '\t' ||
434  (trim_newline && (raw[end_pos] == '\n' || raw[end_pos] == '\r')));
435  --end_pos)
436  {
437  }
438 
439  return raw.substr(start_pos, end_pos - start_pos + 1);
440 }
441 
445 string ToUpper(const string &mixed_case) {
446  string result(mixed_case);
447  for (unsigned i = 0, l = result.length(); i < l; ++i) {
448  result[i] = toupper(result[i]);
449  }
450  return result;
451 }
452 
453 string ReplaceAll(const string &haystack, const string &needle,
454  const string &replace_by) {
455  string result(haystack);
456  size_t pos = 0;
457  const unsigned needle_size = needle.size();
458  if (needle == "") return result;
459 
460  while ((pos = result.find(needle, pos)) != string::npos)
461  result.replace(pos, needle_size, replace_by);
462  return result;
463 }
464 
465 static inline void Base64Block(const unsigned char input[3], const char *table,
466  char output[4]) {
467  output[0] = table[(input[0] & 0xFD) >> 2];
468  output[1] = table[((input[0] & 0x03) << 4) | ((input[1] & 0xF0) >> 4)];
469  output[2] = table[((input[1] & 0x0F) << 2) | ((input[2] & 0xD0) >> 6)];
470  output[3] = table[input[2] & 0x3F];
471 }
472 
473 string Base64(const string &data) {
474  string result;
475  result.reserve((data.length() + 3) * 4 / 3);
476  unsigned pos = 0;
477  const unsigned char *data_ptr =
478  reinterpret_cast<const unsigned char *>(data.data());
479  const unsigned length = data.length();
480  while (pos + 2 < length) {
481  char encoded_block[4];
482  Base64Block(data_ptr + pos, b64_table, encoded_block);
483  result.append(encoded_block, 4);
484  pos += 3;
485  }
486  if (length % 3 != 0) {
487  unsigned char input[3];
488  input[0] = data_ptr[pos];
489  input[1] = ((length % 3) == 2) ? data_ptr[pos + 1] : 0;
490  input[2] = 0;
491  char encoded_block[4];
492  Base64Block(input, b64_table, encoded_block);
493  result.append(encoded_block, 2);
494  result.push_back(((length % 3) == 2) ? encoded_block[2] : '=');
495  result.push_back('=');
496  }
497 
498  return result;
499 }
500 
504 string Base64Url(const string &data) {
505  string base64 = Base64(data);
506  for (unsigned i = 0, l = base64.length(); i < l; ++i) {
507  if (base64[i] == '+')
508  base64[i] = '-';
509  else if (base64[i] == '/')
510  base64[i] = '_';
511  }
512  return base64;
513 }
514 
515 static bool Debase64Block(const unsigned char input[4],
516  const signed char *d_table, unsigned char output[3]) {
517  int32_t dec[4];
518  for (int i = 0; i < 4; ++i) {
519  dec[i] = db64_table[input[i]];
520  if (dec[i] < 0) return false;
521  }
522 
523  output[0] = (dec[0] << 2) | (dec[1] >> 4);
524  output[1] = ((dec[1] & 0x0F) << 4) | (dec[2] >> 2);
525  output[2] = ((dec[2] & 0x03) << 6) | dec[3];
526  return true;
527 }
528 
532 bool Debase64(const string &data, string *decoded) {
533  decoded->clear();
534  decoded->reserve((data.length() + 4) * 3 / 4);
535  unsigned pos = 0;
536  const unsigned char *data_ptr =
537  reinterpret_cast<const unsigned char *>(data.data());
538  const unsigned length = data.length();
539  if (length == 0) return true;
540  if ((length % 4) != 0) return false;
541 
542  while (pos < length) {
543  unsigned char decoded_block[3];
544  bool retval = Debase64Block(data_ptr + pos, db64_table, decoded_block);
545  if (!retval) return false;
546  decoded->append(reinterpret_cast<char *>(decoded_block), 3);
547  pos += 4;
548  }
549 
550  for (int i = 0; i < 2; ++i) {
551  pos--;
552  if (data[pos] == '=') decoded->erase(decoded->length() - 1);
553  }
554  return true;
555 }
556 
560 string Tail(const string &source, unsigned num_lines) {
561  if (source.empty() || (num_lines == 0)) return "";
562 
563  unsigned l = source.length();
564  int i = l - 1;
565  for (; i >= 0; --i) {
566  char c = source.data()[i];
567  if (c == '\n') {
568  if (num_lines == 0) {
569  return source.substr(i + 1);
570  }
571  num_lines--;
572  }
573  }
574  return source;
575 }
576 
583 std::string GetGMTimestamp(std::string format) {
584  struct tm time_ptr;
585  char date_and_time[100];
586  time_t t = time(NULL);
587  gmtime_r(&t, &time_ptr); // take UTC
588  // return empty string if formatting fails
589  if (!strftime(date_and_time, 100, format.c_str(), &time_ptr)) {
590  return "";
591  }
592  std::string timestamp(date_and_time);
593  return timestamp;
594 }
595 
596 #ifdef CVMFS_NAMESPACE_GUARD
597 } // namespace CVMFS_NAMESPACE_GUARD
598 #endif
string GetLineMem(const char *text, const int text_size)
Definition: string.cc:372
vector< string > SplitString(const string &str, const char delim, const unsigned max_chunks)
Definition: string.cc:287
std::string IsoTimestamp()
Definition: string.cc:148
string Trim(const string &raw, bool trim_newline)
Definition: string.cc:420
string ReplaceAll(const string &haystack, const string &needle, const string &replace_by)
Definition: string.cc:453
static void Base64Block(const unsigned char input[3], const char *table, char output[4])
Definition: string.cc:465
string JoinStrings(const vector< string > &strings, const string &joint)
Definition: string.cc:317
static bool Debase64Block(const unsigned char input[4], const signed char *d_table, unsigned char output[3])
Definition: string.cc:515
string Tail(const string &source, unsigned num_lines)
Definition: string.cc:560
string StringifyTime(const time_t seconds, const bool utc)
Definition: string.cc:104
string StringifyDouble(const double value)
Definition: string.cc:95
std::string StringifyUint(const uint64_t value)
Definition: string.cc:83
bool Debase64(const string &data, string *decoded)
Definition: string.cc:532
string StringifyByteAsHex(const unsigned char value)
Definition: string.cc:89
string StringifyBool(const bool value)
Definition: string.cc:75
bool String2Uint64Parse(const std::string &value, uint64_t *result)
Definition: string.cc:242
string Base64Url(const string &data)
Definition: string.cc:504
std::string RfcTimestamp()
Definition: string.cc:126
int64_t String2Int64(const string &value)
Definition: string.cc:221
bool GetLineFile(FILE *f, std::string *line)
Definition: string.cc:378
string ToUpper(const string &mixed_case)
Definition: string.cc:445
const signed char db64_table[]
Definition: string.cc:42
bool HasSuffix(const std::string &str, const std::string &suffix, const bool ignore_case)
Definition: string.cc:278
std::string WhitelistTimestamp(time_t when)
Definition: string.cc:168
string StringifyInt(const int64_t value)
Definition: string.cc:77
bool HasPrefix(const string &str, const string &prefix, const bool ignore_case)
Definition: string.cc:264
time_t IsoTimestamp2UtcTime(const std::string &iso8601)
Definition: string.cc:197
bool GetLineFd(const int fd, std::string *line)
Definition: string.cc:396
string Base64(const string &data)
Definition: string.cc:473
uint64_t String2Uint64(const string &value)
Definition: string.cc:227
bool operator()(const std::string::value_type a, const std::string::value_type b) const
Definition: string.cc:67
bool ParseKeyvalPath(const string &filename, map< char, string > *content)
Definition: string.cc:358
const char b64_table[]
Definition: string.cc:32
string StringifyTimeval(const timeval value)
Definition: string.cc:184
static void size_t size
Definition: smalloc.h:47
void ParseKeyvalMem(const unsigned char *buffer, const unsigned buffer_size, map< char, string > *content)
Definition: string.cc:329
std::string GetGMTimestamp(std::string format)
Definition: string.cc:583
void String2Uint64Pair(const string &value, uint64_t *a, uint64_t *b)
Definition: string.cc:260